/** * this one has to have the context for loading the message via the redirect buffer... */ StrBuf *smtp_load_msg(OneQueItem *MyQItem, int n, char **Author, char **Address) { CitContext *CCC=CC; StrBuf *SendMsg; CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputMsg(MyQItem->MessageID, MT_RFC822, HEADERS_ALL, 0, 1, NULL, (ESC_DOT|SUPPRESS_ENV_TO), Author, Address, NULL); SendMsg = CCC->redirect_buffer; CCC->redirect_buffer = NULL; if ((StrLength(SendMsg) > 0) && ChrPtr(SendMsg)[StrLength(SendMsg) - 1] != '\n') { SMTPC_syslog(LOG_WARNING, "[%d] Possible problem: message did not " "correctly terminate. (expecting 0x10, got 0x%02x)\n", MsgCount, //yes uncool, but best choice here... ChrPtr(SendMsg)[StrLength(SendMsg) - 1] ); StrBufAppendBufPlain(SendMsg, HKEY("\r\n"), 0); } return SendMsg; }
// // 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); }
/* * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery * instructions for "5" codes (permanent fatal errors) and produce/deliver * a "bounce" message (delivery status notification). */ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt) { int i; int lines; int status; char buf[1024]; char key[1024]; char addr[1024]; char dsn[1024]; char bounceto[1024]; StrBuf *boundary; int num_bounces = 0; int bounce_this = 0; time_t submitted = 0L; struct CtdlMessage *bmsg = NULL; int give_up = 0; recptypes *valid; int successful_bounce = 0; static int seq = 0; StrBuf *BounceMB; long omsgid = (-1); syslog(LOG_DEBUG, "smtp_do_bounce() called\n"); strcpy(bounceto, ""); boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq); lines = num_tokens(instr, '\n'); /* See if it's time to give up on delivery of this message */ for (i=0; i<lines; ++i) { extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); extract_token(addr, buf, 1, '|', sizeof addr); if (!strcasecmp(key, "submitted")) { submitted = atol(addr); } } if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) { give_up = 1; } /* Start building our bounce message */ bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); if (bmsg == NULL) return; memset(bmsg, 0, sizeof(struct CtdlMessage)); BounceMB = NewStrBufPlain(NULL, 1024); bmsg->cm_magic = CTDLMESSAGE_MAGIC; bmsg->cm_anon_type = MES_NORMAL; bmsg->cm_format_type = FMT_RFC822; CM_SetField(bmsg, eAuthor, HKEY("Citadel")); CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM)); CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)")); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("\r\nThis is a multipart message in MIME format." "\r\n\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0); if (give_up) { StrBufAppendBufPlain( BounceMB, HKEY("A message you sent could not be delivered " "to some or all of its recipients\ndue to " "prolonged unavailability of its destination(s).\n" "Giving up on the following addresses:\n\n"), 0); } else { StrBufAppendBufPlain( BounceMB, HKEY("A message you sent could not be delivered " "to some or all of its recipients.\n" "The following addresses were undeliverable:\n\n" ), 0); } /* * Now go through the instructions checking for stuff. */ for (i=0; i<lines; ++i) { long addrlen; long dsnlen; extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); addrlen = extract_token(addr, buf, 1, '|', sizeof addr); status = extract_int(buf, 2); dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn); bounce_this = 0; syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n", key, addr, status, dsn); if (!strcasecmp(key, "bounceto")) { strcpy(bounceto, addr); } if (!strcasecmp(key, "msgid")) { omsgid = atol(addr); } if (!strcasecmp(key, "remote")) { if (status == 5) bounce_this = 1; if (give_up) bounce_this = 1; } if (bounce_this) { ++num_bounces; StrBufAppendBufPlain(BounceMB, addr, addrlen, 0); StrBufAppendBufPlain(BounceMB, HKEY(": "), 0); StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); remove_token(instr, i, '\n'); --i; --lines; } } /* Attach the original message */ if (omsgid >= 0) { StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-Disposition: inline\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); if (OMsgTxt == NULL) { CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, NULL, NULL); StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0); FreeStrBuf(&CC->redirect_buffer); } else { StrBufAppendBuf(BounceMB, OMsgTxt, 0); } } /* Close the multipart MIME scope */ StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB); /* Deliver the bounce if there's anything worth mentioning */ syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces); if (num_bounces > 0) { /* First try the user who sent the message */ if (IsEmptyStr(bounceto)) syslog(LOG_ERR, "No bounce address specified\n"); else syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto); /* Can we deliver the bounce to the original sender? */ valid = validate_recipients(bounceto, smtp_get_Recipients (), 0); if (valid != NULL) { if (valid->num_error == 0) { CtdlSubmitMsg(bmsg, valid, "", QP_EADDR); successful_bounce = 1; } } /* If not, post it in the Aide> room */ if (successful_bounce == 0) { CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR); } /* Free up the memory we used */ if (valid != NULL) { free_recipients(valid); } } FreeStrBuf(&boundary); CM_Free(bmsg); syslog(LOG_DEBUG, "Done processing bounces\n"); }
// // Implements the ARTICLE, HEAD, BODY, and STAT commands. // (These commands all accept the same parameters; they differ only in how they output the retrieved message.) // void nntp_article(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; char requested_article[256]; long requested_msgnum = 0; char *lb, *rb = NULL; 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, BODY, STAT }; extract_token(which_command, cmd, 0, ' ', sizeof which_command); if (!strcasecmp(which_command, "article")) { acmd = ARTICLE; } else if (!strcasecmp(which_command, "head")) { acmd = HEAD; } else if (!strcasecmp(which_command, "body")) { acmd = BODY; } else if (!strcasecmp(which_command, "stat")) { acmd = STAT; } else { cprintf("500 I'm afraid I can't do that.\r\n"); return; } // Which NNTP command was issued, determines whether we will fetch headers, body, or both. int headers_only = HEADERS_ALL; if (acmd == HEAD) headers_only = HEADERS_FAST; else if (acmd == BODY) headers_only = HEADERS_NONE; else if (acmd == STAT) headers_only = HEADERS_FAST; // now figure out what the client is asking for. extract_token(requested_article, cmd, 1, ' ', sizeof requested_article); lb = strchr(requested_article, '<'); rb = strchr(requested_article, '>'); requested_msgnum = atol(requested_article); // If no article number or message-id is specified, the client wants the "currently selected article" if (IsEmptyStr(requested_article)) { if (nntpstate->current_article_number < 1) { cprintf("420 No current article selected\r\n"); return; } requested_msgnum = nntpstate->current_article_number; must_change_currently_selected_article = 1; // got it -- now fall through and keep going } // If the requested article is numeric, it maps directly to a message number. Good. else if (requested_msgnum > 0) { must_change_currently_selected_article = 1; // good -- fall through and keep going } // If the requested article has angle brackets, the client wants a specific message-id. // We don't know how to do that yet. else if ( (lb != NULL) && (rb != NULL) && (lb < rb) ) { must_change_currently_selected_article = 0; cprintf("500 I don't know how to fetch by message-id yet.\r\n"); // FIXME return; } // Anything else is noncompliant gobbledygook and should die in a car fire. else { must_change_currently_selected_article = 0; cprintf("500 syntax error\r\n"); return; } // At this point we know the message number of the "article" being requested. // We have an awesome API call that does all the heavy lifting for us. char *fetched_message_id = NULL; CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); int fetch = CtdlOutputMsg(requested_msgnum, MT_RFC822, // output in RFC822 format ... sort of headers_only, // 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; if (fetch != om_ok) { cprintf("423 no article with that number\r\n"); FreeStrBuf(&msgtext); return; } // RFC3977 6.2.1.2 specifes conditions under which the "currently selected article" // MUST or MUST NOT be set to the message we just referenced. if (must_change_currently_selected_article) { nntpstate->current_article_number = requested_msgnum; } // Now give the client what it asked for. if (acmd == ARTICLE) { cprintf("220 %ld <%s>\r\n", requested_msgnum, fetched_message_id); } if (acmd == HEAD) { cprintf("221 %ld <%s>\r\n", requested_msgnum, fetched_message_id); } if (acmd == BODY) { cprintf("222 %ld <%s>\r\n", requested_msgnum, fetched_message_id); } if (acmd == STAT) { cprintf("223 %ld <%s>\r\n", requested_msgnum, fetched_message_id); FreeStrBuf(&msgtext); return; } client_write(SKEY(msgtext)); cprintf(".\r\n"); // this protocol uses a dot terminator FreeStrBuf(&msgtext); if (fetched_message_id) free(fetched_message_id); }