X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fnntp%2Fserv_nntp.c;h=e4897d6c3f3bbfe160729cc252debb3da5d91718;hb=0adb29d5fa73df9c3760478405aaf71fa37054c4;hp=8b86178270ff7ece54f7d253271b33ef31b8ee7b;hpb=0363b91d8430842e8fee04df031c95ce4a39301d;p=citadel.git diff --git a/citadel/modules/nntp/serv_nntp.c b/citadel/modules/nntp/serv_nntp.c index 8b8617827..e4897d6c3 100644 --- a/citadel/modules/nntp/serv_nntp.c +++ b/citadel/modules/nntp/serv_nntp.c @@ -238,6 +238,7 @@ void nntp_capabilities(void) cprintf("READER\r\n"); cprintf("MODE-READER\r\n"); cprintf("LIST ACTIVE NEWSGROUPS\r\n"); + cprintf("OVER\r\n"); #ifdef HAVE_OPENSSL cprintf("STARTTLS\r\n"); #endif @@ -429,13 +430,8 @@ void nntp_newgroups_backend(struct ctdlroom *qrbuf, void *data) // Implements the NEWGROUPS command // void nntp_newgroups(const char *cmd) { - /* - * HACK: this works because the 5XX series error codes from citadel - * protocol will also be considered error codes by an NNTP client - */ if (CtdlAccessCheck(ac_logged_in_or_guest)) return; - char stringy_date[16]; char stringy_time[16]; char stringy_gmt[16]; @@ -492,10 +488,6 @@ void nntp_list_backend(struct ctdlroom *qrbuf, void *data) // Implements the LIST commands // void nntp_list(const char *cmd) { - // - // HACK: this works because the 5XX series error codes from citadel - // protocol will also be considered error codes by an NNTP client - // if (CtdlAccessCheck(ac_logged_in_or_guest)) return; char list_format[64]; @@ -518,11 +510,30 @@ void nntp_list(const char *cmd) { else if (!strcasecmp(list_format, "NEWSGROUPS")) { nld.list_format = NNTP_LIST_NEWSGROUPS; } + else if (!strcasecmp(list_format, "OVERVIEW.FMT")) { + nld.list_format = NNTP_LIST_OVERVIEW_FMT; + } else { cprintf("501 syntax error , unsupported list format\r\n"); return; } + // OVERVIEW.FMT delivers a completely different type of data than all of the + // other LIST commands. It's a stupid place to put it. But that's how it's + // written into RFC3977, so we have to handle it here. + if (nld.list_format == NNTP_LIST_OVERVIEW_FMT) { + cprintf("215 Order of fields in overview database.\r\n"); + cprintf("Subject:\r\n"); + cprintf("From:\r\n"); + cprintf("Date:\r\n"); + cprintf("Message-ID:\r\n"); + cprintf("References:\r\n"); + cprintf(":bytes\r\n"); + cprintf(":lines\r\n"); + cprintf(".\r\n"); + return; + } + cprintf("215 list of newsgroups follows\r\n"); CtdlGetUser(&CC->user, CC->curr_user); CtdlForEachRoom(nntp_list_backend, &nld); @@ -540,6 +551,25 @@ void nntp_help(void) { } +// +// Implement DATE command. +// +void nntp_date(void) { + time_t now; + struct tm nowLocal; + struct tm nowUtc; + char tsFromUtc[32]; + + now = time(NULL); + localtime_r(&now, &nowLocal); + gmtime_r(&now, &nowUtc); + + strftime(tsFromUtc, sizeof(tsFromUtc), "%Y%m%d%H%M%S", &nowUtc); + + cprintf("111 %s\r\n", tsFromUtc); +} + + // // back end for the LISTGROUP command , called for each message number // @@ -559,10 +589,6 @@ void nntp_listgroup_backend(long msgnum, void *userdata) { // Implements the GROUP and LISTGROUP commands // void nntp_group(const char *cmd) { - // - // HACK: this works because the 5XX series error codes from citadel - // protocol will also be considered error codes by an NNTP client - // if (CtdlAccessCheck(ac_logged_in_or_guest)) return; citnntp *nntpstate = (citnntp *) CC->session_specific_data; @@ -668,10 +694,6 @@ void nntp_mode(const char *cmd) { // (These commands all accept the same parameters; they differ only in how they output the retrieved message.) // void nntp_article(const char *cmd) { - /* - * HACK: this works because the 5XX series error codes from citadel - * protocol will also be considered error codes by an NNTP client - */ if (CtdlAccessCheck(ac_logged_in_or_guest)) return; citnntp *nntpstate = (citnntp *) CC->session_specific_data; @@ -683,6 +705,7 @@ void nntp_article(const char *cmd) { int must_change_currently_selected_article = 0; // We're going to store one of these values in the variable 'acmd' so that + // we can quickly check later which version of the output we want. enum { ARTICLE, HEAD, @@ -806,6 +829,175 @@ void nntp_article(const char *cmd) { } +// +// Utility function for nntp_last_next() that turns a msgnum into a message ID. +// The memory for the returned string is pwnz0red by the caller. +// +char *message_id_from_msgnum(long msgnum) { + + char *fetched_message_id = NULL; + CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); + CtdlOutputMsg(msgnum, + MT_RFC822, // output in RFC822 format ... sort of + HEADERS_FAST, // headers, body, or both? + 0, // don't do Citadel protocol responses + 1, // CRLF newlines + NULL, // teh whole thing, not just a section + 0, // no flags yet ... maybe new ones for Path: etc ? + NULL, + NULL, + &fetched_message_id // extract the message ID from the message as we go... + ); + StrBuf *msgtext = CC->redirect_buffer; + CC->redirect_buffer = NULL; + + FreeStrBuf(&msgtext); + return(fetched_message_id); +} + + +// +// The LAST and NEXT commands are so similar that they are handled by a single function. +// +void nntp_last_next(const char *cmd) { + if (CtdlAccessCheck(ac_logged_in_or_guest)) return; + + citnntp *nntpstate = (citnntp *) CC->session_specific_data; + char which_command[16]; + int acmd = 0; + + // We're going to store one of these values in the variable 'acmd' so that + // we can quickly check later which version of the output we want. + enum { + NNTP_LAST, + NNTP_NEXT + }; + + extract_token(which_command, cmd, 0, ' ', sizeof which_command); + + if (!strcasecmp(which_command, "last")) { + acmd = NNTP_LAST; + } + else if (!strcasecmp(which_command, "next")) { + acmd = NNTP_NEXT; + } + else { + cprintf("500 I'm afraid I can't do that.\r\n"); + return; + } + + // ok, here we go ... fetch the msglist so we can figure out our place in the universe + struct nntp_msglist nm; + int i = 0; + long selected_msgnum = 0; + char *message_id = NULL; + + nm = nntp_fetch_msglist(&CC->room); + if ((nm.num_msgs < 0) || (nm.msgnums == NULL)) { + cprintf("500 something bad happened\r\n"); + return; + } + + if ( (acmd == NNTP_LAST) && (nm.num_msgs == 0) ) { + cprintf("422 no previous article in this group\r\n"); // nothing here + } + + else if ( (acmd == NNTP_LAST) && (nntpstate->current_article_number <= nm.msgnums[0]) ) { + cprintf("422 no previous article in this group\r\n"); // already at the beginning + } + + else if (acmd == NNTP_LAST) { + for (i=0; ((i= nntpstate->current_article_number) && (i > 0) ) { + selected_msgnum = nm.msgnums[i-1]; + } + } + if (selected_msgnum > 0) { + nntpstate->current_article_number = selected_msgnum; + message_id = message_id_from_msgnum(nntpstate->current_article_number); + cprintf("223 %ld <%s>\r\n", nntpstate->current_article_number, message_id); + if (message_id) free(message_id); + } + else { + cprintf("422 no previous article in this group\r\n"); + } + } + + else if ( (acmd == NNTP_NEXT) && (nm.num_msgs == 0) ) { + cprintf("421 no next article in this group\r\n"); // nothing here + } + + else if ( (acmd == NNTP_NEXT) && (nntpstate->current_article_number >= nm.msgnums[nm.num_msgs-1]) ) { + cprintf("421 no next article in this group\r\n"); // already at the end + } + + else if (acmd == NNTP_NEXT) { + for (i=0; ((i nntpstate->current_article_number) { + selected_msgnum = nm.msgnums[i]; + } + } + if (selected_msgnum > 0) { + nntpstate->current_article_number = selected_msgnum; + message_id = message_id_from_msgnum(nntpstate->current_article_number); + cprintf("223 %ld <%s>\r\n", nntpstate->current_article_number, message_id); + if (message_id) free(message_id); + } + else { + cprintf("421 no next article in this group\r\n"); + } + } + + // should never get here + else { + cprintf("500 internal error\r\n"); + } + + if (nm.msgnums != NULL) { + free(nm.msgnums); + } + +} + + +// +// XOVER is used by some clients, even if we don't offer it +// +void nntp_xover(const char *cmd) { + if (CtdlAccessCheck(ac_logged_in_or_guest)) return; + + citnntp *nntpstate = (citnntp *) CC->session_specific_data; + char range[256]; + long lowest = (-1) ; + long highest = (-1) ; + + extract_token(range, cmd, 1, ' ', sizeof range); + lowest = atol(range); + if (lowest <= 0) { + lowest = nntpstate->current_article_number; + highest = nntpstate->current_article_number; + } + else { + char *dash = strchr(range, '-'); + if (dash != NULL) { + ++dash; + highest = atol(dash); + if (highest == 0) { + highest = LONG_MAX; + } + if (highest < lowest) { + highest = lowest; + } + } + else { + highest = lowest; + } + } + + cprintf("500 not implemented yet FIXME lowest=%ld highest=%ld\r\n", lowest, highest); +} + + // // Main command loop for NNTP server sessions. // @@ -834,6 +1026,10 @@ void nntp_command_loop(void) nntp_help(); } + else if (!strcasecmp(cmdname, "date")) { + nntp_date(); + } + else if (!strcasecmp(cmdname, "capabilities")) { nntp_capabilities(); } @@ -871,10 +1067,27 @@ void nntp_command_loop(void) || (!strcasecmp(cmdname, "head")) || (!strcasecmp(cmdname, "body")) || (!strcasecmp(cmdname, "stat")) - ) { + ) + { nntp_article(ChrPtr(Cmd)); } + else if ( + (!strcasecmp(cmdname, "last")) + || (!strcasecmp(cmdname, "next")) + ) + { + nntp_last_next(ChrPtr(Cmd)); + } + + else if ( + (!strcasecmp(cmdname, "xover")) + || (!strcasecmp(cmdname, "over")) + ) + { + nntp_xover(ChrPtr(Cmd)); + } + else { cprintf("500 I'm afraid I can't do that.\r\n"); }