static void ListCalculateSubject(struct CtdlMessage *msg) { struct CitContext *CCC = CC; StrBuf *Subject, *FlatSubject; int rlen; char *pCh; if (CM_IsEmpty(msg, eMsgSubject)) { Subject = NewStrBufPlain(HKEY("(no subject)")); } else { Subject = NewStrBufPlain(CM_KEY(msg, eMsgSubject)); } FlatSubject = NewStrBufPlain(NULL, StrLength(Subject)); StrBuf_RFC822_to_Utf8(FlatSubject, Subject, NULL, NULL); rlen = strlen(CCC->room.QRname); pCh = strstr(ChrPtr(FlatSubject), CCC->room.QRname); if ((pCh == NULL) || (*(pCh + rlen) != ']') || (pCh == ChrPtr(FlatSubject)) || (*(pCh - 1) != '[') ) { StrBuf *tmp; StrBufPlain(Subject, HKEY("[")); StrBufAppendBufPlain(Subject, CCC->room.QRname, rlen, 0); StrBufAppendBufPlain(Subject, HKEY("] "), 0); StrBufAppendBuf(Subject, FlatSubject, 0); /* so we can free the right one swap them */ tmp = Subject; Subject = FlatSubject; FlatSubject = tmp; StrBufRFC2047encode(&Subject, FlatSubject); } CM_SetAsFieldSB(msg, eMsgSubject, &Subject); FreeStrBuf(&FlatSubject); }
/* * Called by JournalRunQueue() to send an individual message. */ void JournalRunQueueMsg(struct jnlq *jmsg) { struct CtdlMessage *journal_msg = NULL; recptypes *journal_recps = NULL; StrBuf *message_text = NULL; char mime_boundary[256]; long mblen; long rfc822len; char recipient[256]; char inetemail[256]; static int seq = 0; int i; if (jmsg == NULL) return; journal_recps = validate_recipients(config.c_journal_dest, NULL, 0); if (journal_recps != NULL) { if ( (journal_recps->num_local > 0) || (journal_recps->num_internet > 0) || (journal_recps->num_ignet > 0) || (journal_recps->num_room > 0) ) { /* * Construct journal message. * Note that we are transferring ownership of some of the memory here. */ journal_msg = malloc(sizeof(struct CtdlMessage)); memset(journal_msg, 0, sizeof(struct CtdlMessage)); journal_msg->cm_magic = CTDLMESSAGE_MAGIC; journal_msg->cm_anon_type = MES_NORMAL; journal_msg->cm_format_type = FMT_RFC822; CM_SetField(journal_msg, eJournal, HKEY("is journal")); CM_SetField(journal_msg, eAuthor, jmsg->from, strlen(jmsg->from)); CM_SetField(journal_msg, eNodeName, jmsg->node, strlen(jmsg->node)); CM_SetField(journal_msg, erFc822Addr, jmsg->rfca, strlen(jmsg->rfca)); CM_SetField(journal_msg, eMsgSubject, jmsg->subj, strlen(jmsg->subj)); mblen = snprintf(mime_boundary, sizeof(mime_boundary), "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq); rfc822len = strlen(jmsg->rfc822); message_text = NewStrBufPlain(NULL, rfc822len + sizeof(recptypes) + 1024); /* * Here is where we begin to compose the journalized message. * NOTE: the superfluous "Content-Identifer: ExJournalReport" header was * requested by a paying customer, and yes, it is intentionally * spelled wrong. Do NOT remove or change it. */ StrBufAppendBufPlain( message_text, HKEY("Content-type: multipart/mixed; boundary=\""), 0); StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); StrBufAppendBufPlain( message_text, HKEY("\"\r\n" "Content-Identifer: ExJournalReport\r\n" "MIME-Version: 1.0\r\n" "\n" "--"), 0); StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); StrBufAppendBufPlain( message_text, HKEY("\r\n" "Content-type: text/plain\r\n" "\r\n" "Sender: "), 0); if (CM_IsEmpty(journal_msg, eAuthor)) StrBufAppendBufPlain( message_text, journal_msg->cm_fields[eAuthor], -1, 0); else StrBufAppendBufPlain( message_text, HKEY("(null)"), 0); if (!CM_IsEmpty(journal_msg, erFc822Addr)) { StrBufAppendPrintf(message_text, " <%s>", journal_msg->cm_fields[erFc822Addr]); } else if (!CM_IsEmpty(journal_msg, eNodeName)) { StrBufAppendPrintf(message_text, " @ %s", journal_msg->cm_fields[eNodeName]); } else StrBufAppendBufPlain( message_text, HKEY(" "), 0); StrBufAppendBufPlain( message_text, HKEY("\r\n" "Message-ID: <"), 0); StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0); StrBufAppendBufPlain( message_text, HKEY(">\r\n" "Recipients:\r\n"), 0); if (jmsg->recps.num_local > 0) { for (i=0; i<jmsg->recps.num_local; ++i) { extract_token(recipient, jmsg->recps.recp_local, i, '|', sizeof recipient); local_to_inetemail(inetemail, recipient, sizeof inetemail); StrBufAppendPrintf(message_text, " %s <%s>\r\n", recipient, inetemail); } } if (jmsg->recps.num_ignet > 0) { for (i=0; i<jmsg->recps.num_ignet; ++i) { extract_token(recipient, jmsg->recps.recp_ignet, i, '|', sizeof recipient); StrBufAppendPrintf(message_text, " %s\r\n", recipient); } } if (jmsg->recps.num_internet > 0) { for (i=0; i<jmsg->recps.num_internet; ++i) { extract_token(recipient, jmsg->recps.recp_internet, i, '|', sizeof recipient); StrBufAppendPrintf(message_text, " %s\r\n", recipient); } } StrBufAppendBufPlain( message_text, HKEY("\r\n" "--"), 0); StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); StrBufAppendBufPlain( message_text, HKEY("\r\n" "Content-type: message/rfc822\r\n" "\r\n"), 0); StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0); StrBufAppendBufPlain( message_text, HKEY("--"), 0); StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0); StrBufAppendBufPlain( message_text, HKEY("--\r\n"), 0); CM_SetAsFieldSB(journal_msg, eMesageText, &message_text); free(jmsg->rfc822); free(jmsg->msgn); jmsg->rfc822 = NULL; jmsg->msgn = NULL; /* Submit journal message */ CtdlSubmitMsg(journal_msg, journal_recps, "", 0); CM_Free(journal_msg); } free_recipients(journal_recps); } /* We are responsible for freeing this memory. */ free(jmsg); }
/* * 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"); }
/* * Before allowing a wiki page save to execute, we have to perform version control. * This involves fetching the old version of the page if it exists. */ int wiki_upload_beforesave(struct CtdlMessage *msg, recptypes *recp) { struct CitContext *CCC = CC; long old_msgnum = (-1L); struct CtdlMessage *old_msg = NULL; long history_msgnum = (-1L); struct CtdlMessage *history_msg = NULL; char diff_old_filename[PATH_MAX]; char diff_new_filename[PATH_MAX]; char diff_out_filename[PATH_MAX]; char diff_cmd[PATH_MAX]; FILE *fp; int rv; char history_page[1024]; long history_page_len; char boundary[256]; char prefixed_boundary[258]; char buf[1024]; char *diffbuf = NULL; size_t diffbuf_len = 0; char *ptr = NULL; long newmsgid; StrBuf *msgidbuf; if (!CCC->logged_in) return(0); /* Only do this if logged in. */ /* Is this a room with a Wiki in it, don't run this hook. */ if ((CCC->room.QRdefaultview != VIEW_WIKI) && (CCC->room.QRdefaultview != VIEW_WIKIMD)) { return(0); } /* If this isn't a MIME message, don't bother. */ if (msg->cm_format_type != 4) return(0); /* If there's no EUID we can't do this. Reject the post. */ if (CM_IsEmpty(msg, eExclusiveID)) return(1); newmsgid = get_new_message_number(); msgidbuf = NewStrBuf(); StrBufPrintf(msgidbuf, "%08lX-%08lX@%s/%s", (long unsigned int) time(NULL), (long unsigned int) newmsgid, CtdlGetConfigStr("c_fqdn"), msg->cm_fields[eExclusiveID] ); CM_SetAsFieldSB(msg, emessageId, &msgidbuf); history_page_len = snprintf(history_page, sizeof history_page, "%s_HISTORY_", msg->cm_fields[eExclusiveID]); /* Make sure we're saving a real wiki page rather than a wiki history page. * This is important in order to avoid recursing infinitely into this hook. */ if ( (msg->cm_lengths[eExclusiveID] >= 9) && (!strcasecmp(&msg->cm_fields[eExclusiveID][msg->cm_lengths[eExclusiveID]-9], "_HISTORY_")) ) { syslog(LOG_DEBUG, "History page not being historied\n"); return(0); } /* If there's no message text, obviously this is all b0rken and shouldn't happen at all */ if (CM_IsEmpty(msg, eMesageText)) return(0); /* Set the message subject identical to the page name */ CM_CopyField(msg, eMsgSubject, eExclusiveID); /* See if we can retrieve the previous version. */ old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CCC->room); if (old_msgnum > 0L) { old_msg = CtdlFetchMessage(old_msgnum, 1, 1); } else { old_msg = NULL; } if ((old_msg != NULL) && (CM_IsEmpty(old_msg, eMesageText))) { /* old version is corrupt? */ CM_Free(old_msg); old_msg = NULL; } /* If no changes were made, don't bother saving it again */ if ((old_msg != NULL) && (!strcmp(msg->cm_fields[eMesageText], old_msg->cm_fields[eMesageText]))) { CM_Free(old_msg); return(1); } /* * Generate diffs */ CtdlMakeTempFileName(diff_old_filename, sizeof diff_old_filename); CtdlMakeTempFileName(diff_new_filename, sizeof diff_new_filename); CtdlMakeTempFileName(diff_out_filename, sizeof diff_out_filename); if (old_msg != NULL) { fp = fopen(diff_old_filename, "w"); rv = fwrite(old_msg->cm_fields[eMesageText], old_msg->cm_lengths[eMesageText], 1, fp); fclose(fp); CM_Free(old_msg); } fp = fopen(diff_new_filename, "w"); rv = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); snprintf(diff_cmd, sizeof diff_cmd, DIFF " -u %s %s >%s", diff_new_filename, ((old_msg != NULL) ? diff_old_filename : "/dev/null"), diff_out_filename ); syslog(LOG_DEBUG, "diff cmd: %s", diff_cmd); rv = system(diff_cmd); syslog(LOG_DEBUG, "diff cmd returned %d", rv); diffbuf_len = 0; diffbuf = NULL; fp = fopen(diff_out_filename, "r"); if (fp == NULL) { fp = fopen("/dev/null", "r"); } if (fp != NULL) { fseek(fp, 0L, SEEK_END); diffbuf_len = ftell(fp); fseek(fp, 0L, SEEK_SET); diffbuf = malloc(diffbuf_len + 1); fread(diffbuf, diffbuf_len, 1, fp); diffbuf[diffbuf_len] = '\0'; fclose(fp); } syslog(LOG_DEBUG, "diff length is "SIZE_T_FMT" bytes", diffbuf_len); unlink(diff_old_filename); unlink(diff_new_filename); unlink(diff_out_filename); /* Determine whether this was a bogus (empty) edit */ if ((diffbuf_len = 0) && (diffbuf != NULL)) { free(diffbuf); diffbuf = NULL; } if (diffbuf == NULL) { return(1); /* No changes at all? Abandon the post entirely! */ } /* Now look for the existing edit history */ history_msgnum = CtdlLocateMessageByEuid(history_page, &CCC->room); history_msg = NULL; if (history_msgnum > 0L) { history_msg = CtdlFetchMessage(history_msgnum, 1, 1); } /* Create a new history message if necessary */ if (history_msg == NULL) { char *buf; long len; history_msg = malloc(sizeof(struct CtdlMessage)); memset(history_msg, 0, sizeof(struct CtdlMessage)); history_msg->cm_magic = CTDLMESSAGE_MAGIC; history_msg->cm_anon_type = MES_NORMAL; history_msg->cm_format_type = FMT_RFC822; CM_SetField(history_msg, eAuthor, HKEY("Citadel")); if (!IsEmptyStr(CCC->room.QRname)){ CM_SetField(history_msg, eRecipient, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(history_msg, eExclusiveID, history_page, history_page_len); CM_SetField(history_msg, eMsgSubject, history_page, history_page_len); CM_SetField(history_msg, eSuppressIdx, HKEY("1")); /* suppress full text indexing */ snprintf(boundary, sizeof boundary, "Citadel--Multipart--%04x--%08lx", getpid(), time(NULL)); buf = (char*) malloc(1024); len = snprintf(buf, 1024, "Content-type: multipart/mixed; boundary=\"%s\"\n\n" "This is a Citadel wiki history encoded as multipart MIME.\n" "Each part is comprised of a diff script representing one change set.\n" "\n" "--%s--\n", boundary, boundary ); CM_SetAsField(history_msg, eMesageText, &buf, len); } /* Update the history message (regardless of whether it's new or existing) */ /* Remove the Message-ID from the old version of the history message. This will cause a brand * new one to be generated, avoiding an uninitentional hit of the loop zapper when we replicate. */ CM_FlushField(history_msg, emessageId); /* Figure out the boundary string. We do this even when we generated the * boundary string in the above code, just to be safe and consistent. */ *boundary = '\0'; ptr = history_msg->cm_fields[eMesageText]; do { ptr = memreadline(ptr, buf, sizeof buf); if (*ptr != 0) { striplt(buf); if (!IsEmptyStr(buf) && (!strncasecmp(buf, "Content-type:", 13))) { if ( (bmstrcasestr(buf, "multipart") != NULL) && (bmstrcasestr(buf, "boundary=") != NULL) ) { safestrncpy(boundary, bmstrcasestr(buf, "\""), sizeof boundary); char *qu; qu = strchr(boundary, '\"'); if (qu) { strcpy(boundary, ++qu); } qu = strchr(boundary, '\"'); if (qu) { *qu = 0; } } } } } while ( (IsEmptyStr(boundary)) && (*ptr != 0) ); /* * Now look for the first boundary. That is where we need to insert our fun. */ if (!IsEmptyStr(boundary)) { char *MsgText; long MsgTextLen; time_t Now = time(NULL); snprintf(prefixed_boundary, sizeof(prefixed_boundary), "--%s", boundary); CM_GetAsField(history_msg, eMesageText, &MsgText, &MsgTextLen); ptr = bmstrcasestr(MsgText, prefixed_boundary); if (ptr != NULL) { StrBuf *NewMsgText; char uuid[64]; char memo[512]; long memolen; char encoded_memo[1024]; NewMsgText = NewStrBufPlain(NULL, MsgTextLen + diffbuf_len + 1024); generate_uuid(uuid); memolen = snprintf(memo, sizeof(memo), "%s|%ld|%s|%s", uuid, Now, CCC->user.fullname, CtdlGetConfigStr("c_nodename")); memolen = CtdlEncodeBase64(encoded_memo, memo, memolen, 0); StrBufAppendBufPlain(NewMsgText, HKEY("--"), 0); StrBufAppendBufPlain(NewMsgText, boundary, -1, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\n" "Content-type: text/plain\n" "Content-Disposition: inline; filename=\""), 0); StrBufAppendBufPlain(NewMsgText, encoded_memo, memolen, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\"\n" "Content-Transfer-Encoding: 8bit\n" "\n"), 0); StrBufAppendBufPlain(NewMsgText, diffbuf, diffbuf_len, 0); StrBufAppendBufPlain(NewMsgText, HKEY("\n"), 0); StrBufAppendBufPlain(NewMsgText, ptr, MsgTextLen - (ptr - MsgText), 0); free(MsgText); CM_SetAsFieldSB(history_msg, eMesageText, &NewMsgText); } else { CM_SetAsField(history_msg, eMesageText, &MsgText, MsgTextLen); } CM_SetFieldLONG(history_msg, eTimestamp, Now); CtdlSubmitMsg(history_msg, NULL, "", 0); } else { syslog(LOG_ALERT, "Empty boundary string in history message. No history!\n"); } free(diffbuf); CM_Free(history_msg); return(0); }
/****************************************************************************** * So, we're finished with sending (regardless of success or failure) * * This Message might be referenced by several Queue-Items, if we're the last,* * we need to free the memory and send bounce messages (on terminal failure) * * else we just free our SMTP-Message struct. * ******************************************************************************/ eNextState FinalizeMessageSend_DB(AsyncIO *IO) { const char *Status; SmtpOutMsg *Msg = IO->Data; StrBuf *StatusMessage; if (Msg->MyQEntry->AllStatusMessages != NULL) StatusMessage = Msg->MyQEntry->AllStatusMessages; else StatusMessage = Msg->MyQEntry->StatusMessage; if (Msg->MyQEntry->Status == 2) { SetSMTPState(IO, eSTMPfinished); Status = "Delivery successful."; } else if (Msg->MyQEntry->Status == 5) { SetSMTPState(IO, eSMTPFailTotal); Status = "Delivery failed permanently; giving up."; } else { SetSMTPState(IO, eSMTPFailTemporary); Status = "Delivery failed temporarily; will retry later."; } EVS_syslog(LOG_INFO, "%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n", Status, Msg->IO.Now - Msg->IO.StartIO, Msg->user, Msg->node, Msg->name, ChrPtr(StatusMessage)); Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem); Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem, 0); if (Msg->MyQEntry->Active && !Msg->MyQEntry->StillActive && CheckQEntryIsBounce(Msg->MyQEntry)) { /* are we casue for a bounce mail? */ Msg->MyQItem->SendBounceMail |= (1<<Msg->MyQEntry->Status); } if ((Msg->nRemain > 0) || Msg->IDestructQueItem) Msg->QMsgData = SerializeQueueItem(Msg->MyQItem); else Msg->QMsgData = NULL; /* * Uncompleted delivery instructions remain, so delete the old * instructions and replace with the updated ones. */ EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID); CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, ""); Msg->MyQItem->QueMsgID = -1; if (Msg->IDestructQueItem) smtpq_do_bounce(Msg->MyQItem, Msg->msgtext, Msg->pCurrRelay); if (Msg->nRemain > 0) { struct CtdlMessage *msg; msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; msg->cm_format_type = FMT_RFC822; CM_SetAsFieldSB(msg, eMesageText, &Msg->QMsgData); CM_SetField(msg, eMsgSubject, HKEY("QMSG")); Msg->MyQItem->QueMsgID = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID); CM_Free(msg); } else { CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->MessageID, 1, ""); FreeStrBuf(&Msg->QMsgData); } RemoveContext(Msg->IO.CitContext); return eAbort; }
/* * This is the back end for flush_conversations_to_disk() * At this point we've isolated a single conversation (struct imlog) * and are ready to write it to disk. */ void flush_individual_conversation(struct imlog *im) { struct CtdlMessage *msg; long msgnum = 0; char roomname[ROOMNAMELEN]; StrBuf *MsgBuf, *FullMsgBuf; StrBufAppendBufPlain(im->conversation, HKEY( "</body>\r\n" "</html>\r\n" ), 0 ); MsgBuf = StrBufRFC2047encodeMessage(im->conversation); FlushStrBuf(im->conversation); FullMsgBuf = NewStrBufPlain(NULL, StrLength(im->conversation) + 100); StrBufAppendBufPlain(FullMsgBuf, HKEY( "Content-type: text/html; charset=UTF-8\r\n" "Content-Transfer-Encoding: quoted-printable\r\n" "\r\n" ), 0); StrBufAppendBuf (FullMsgBuf, MsgBuf, 0); FreeStrBuf(&MsgBuf); msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; msg->cm_format_type = FMT_RFC822; if (!IsEmptyStr(im->usernames[0])) { CM_SetField(msg, eAuthor, im->usernames[0], strlen(im->usernames[0])); } else { CM_SetField(msg, eAuthor, HKEY("Citadel")); } if (!IsEmptyStr(im->usernames[1])) { CM_SetField(msg, eRecipient, im->usernames[1], strlen(im->usernames[1])); } CM_SetField(msg, eOriginalRoom, HKEY(PAGELOGROOM)); CM_SetField(msg, eNodeName, CFG_KEY(c_nodename)); CM_SetAsFieldSB(msg, eMesageText, &FullMsgBuf); /* we own this memory now */ /* Start with usernums[1] because it's guaranteed to be higher than usernums[0], * so if there's only one party, usernums[0] will be zero but usernums[1] won't. * Create the room if necessary. Note that we create as a type 5 room rather * than 4, which indicates that it's a personal room but we've already supplied * the namespace prefix. * * In the unlikely event that usernums[1] is zero, a room with an invalid namespace * prefix will be created. That's ok because the auto-purger will clean it up later. */ snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[1], PAGELOGROOM); CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS); msgnum = CtdlSubmitMsg(msg, NULL, roomname, 0); CM_Free(msg); /* If there is a valid user number in usernums[0], save a copy for them too. */ if (im->usernums[0] > 0) { snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[0], PAGELOGROOM); CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS); CtdlSaveMsgPointerInRoom(roomname, msgnum, 0, NULL); } /* Finally, if we're logging instant messages globally, do that now. */ if (!IsEmptyStr(config.c_logpages)) { CtdlCreateRoom(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS); CtdlSaveMsgPointerInRoom(config.c_logpages, msgnum, 0, NULL); } }
/* * 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 smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt, ParsedURL *Relay) { static int seq = 0; struct CtdlMessage *bmsg = NULL; StrBuf *boundary; StrBuf *Msg = NULL; StrBuf *BounceMB; recptypes *valid; time_t now; HashPos *It; void *vQE; long len; const char *Key; int first_attempt = 0; int successful_bounce = 0; int num_bounces = 0; int give_up = 0; SMTPCM_syslog(LOG_DEBUG, "smtp_do_bounce() called\n"); if (MyQItem->SendBounceMail == 0) return; now = time (NULL); //ev_time(); if ( (now - MyQItem->Submitted) > SMTP_GIVE_UP ) { give_up = 1; } if (MyQItem->Retry == SMTP_RETRY_INTERVAL) { first_attempt = 1; } /* * Now go through the instructions checking for stuff. */ Msg = NewStrBufPlain(NULL, 1024); It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { MailQEntry *ThisItem = vQE; if ((ThisItem->Active && (ThisItem->Status == 5)) || /* failed now? */ ((give_up == 1) && (ThisItem->Status != 2)) || ((first_attempt == 1) && (ThisItem->Status != 2))) /* giving up after failed attempts... */ { ++num_bounces; StrBufAppendBufPlain(Msg, HKEY(" "), 0); StrBufAppendBuf(Msg, ThisItem->Recipient, 0); StrBufAppendBufPlain(Msg, HKEY(": "), 0); if (ThisItem->AllStatusMessages != NULL) StrBufAppendBuf(Msg, ThisItem->AllStatusMessages, 0); else StrBufAppendBuf(Msg, ThisItem->StatusMessage, 0); StrBufAppendBufPlain(Msg, HKEY("\r\n"), 0); } } DeleteHashPos(&It); /* Deliver the bounce if there's anything worth mentioning */ SMTPC_syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces); if (num_bounces == 0) { FreeStrBuf(&Msg); return; } if ((StrLength(MyQItem->SenderRoom) == 0) && MyQItem->HaveRelay) { const char *RelayUrlStr = "[not found]"; /* one message that relaying is broken is enough; no extra room error message. */ StrBuf *RelayDetails = NewStrBuf(); if (Relay != NULL) RelayUrlStr = ChrPtr(Relay->URL); StrBufPrintf(RelayDetails, "Relaying via %s failed permanently. \n Reason:\n%s\n Revalidate your relay configuration.", RelayUrlStr, ChrPtr(Msg)); CtdlAideMessage(ChrPtr(RelayDetails), "Relaying Failed"); FreeStrBuf(&RelayDetails); } boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq); /* Start building our bounce message; go shopping for memory first. */ BounceMB = NewStrBufPlain( NULL, 1024 + /* mime stuff.... */ StrLength(Msg) + /* the bounce information... */ StrLength(OMsgTxt)); /* the original message */ if (BounceMB == NULL) { FreeStrBuf(&boundary); SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n"); return; } bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); if (bmsg == NULL) { FreeStrBuf(&boundary); FreeStrBuf(&BounceMB); SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n"); return; } memset(bmsg, 0, sizeof(struct CtdlMessage)); 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\n" "due 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); StrBufAppendBuf(BounceMB, Msg, 0); FreeStrBuf(&Msg); if (StrLength(MyQItem->SenderRoom) > 0) { StrBufAppendBufPlain( BounceMB, HKEY("The message was originaly posted in: "), 0); StrBufAppendBuf(BounceMB, MyQItem->SenderRoom, 0); StrBufAppendBufPlain( BounceMB, HKEY("\n"), 0); } /* Attach the original message */ StrBufAppendBufPlain(BounceMB, HKEY("\r\n--"), 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); StrBufAppendBuf(BounceMB, OMsgTxt, 0); /* Close the multipart MIME scope */ StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); bmsg->cm_magic = CTDLMESSAGE_MAGIC; bmsg->cm_anon_type = MES_NORMAL; bmsg->cm_format_type = FMT_RFC822; CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM)); CM_SetField(bmsg, eAuthor, HKEY("Citadel")); CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)")); CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB); /* First try the user who sent the message */ if (StrLength(MyQItem->BounceTo) == 0) { SMTPCM_syslog(LOG_ERR, "No bounce address specified\n"); } else { SMTPC_syslog(LOG_DEBUG, "bounce to user? <%s>\n", ChrPtr(MyQItem->BounceTo)); } /* Can we deliver the bounce to the original sender? */ valid = validate_recipients(ChrPtr(MyQItem->BounceTo), NULL, 0); if ((valid != NULL) && (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 */ free_recipients(valid); FreeStrBuf(&boundary); CM_Free(bmsg); SMTPCM_syslog(LOG_DEBUG, "Done processing bounces\n"); }
int smtp_aftersave(struct CtdlMessage *msg, recptypes *recps) { /* For internet mail, generate delivery instructions. * Yes, this is recursive. Deal with it. Infinite recursion does * not happen because the delivery instructions message does not * contain a recipient. */ if ((recps != NULL) && (recps->num_internet > 0)) { struct CtdlMessage *imsg = NULL; char recipient[SIZ]; CitContext *CCC = MyContext(); StrBuf *SpoolMsg = NewStrBuf(); long nTokens; int i; MSGM_syslog(LOG_DEBUG, "Generating delivery instructions\n"); StrBufPrintf(SpoolMsg, "Content-type: "SPOOLMIME"\n" "\n" "msgid|%s\n" "submitted|%ld\n" "bounceto|%s\n", msg->cm_fields[eVltMsgNum], (long)time(NULL), recps->bounce_to); if (recps->envelope_from != NULL) { StrBufAppendBufPlain(SpoolMsg, HKEY("envelope_from|"), 0); StrBufAppendBufPlain(SpoolMsg, recps->envelope_from, -1, 0); StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0); } if (recps->sending_room != NULL) { StrBufAppendBufPlain(SpoolMsg, HKEY("source_room|"), 0); StrBufAppendBufPlain(SpoolMsg, recps->sending_room, -1, 0); StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0); } nTokens = num_tokens(recps->recp_internet, '|'); for (i = 0; i < nTokens; i++) { long len; len = extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient); if (len > 0) { StrBufAppendBufPlain(SpoolMsg, HKEY("remote|"), 0); StrBufAppendBufPlain(SpoolMsg, recipient, len, 0); StrBufAppendBufPlain(SpoolMsg, HKEY("|0||\n"), 0); } } imsg = malloc(sizeof(struct CtdlMessage)); memset(imsg, 0, sizeof(struct CtdlMessage)); imsg->cm_magic = CTDLMESSAGE_MAGIC; imsg->cm_anon_type = MES_NORMAL; imsg->cm_format_type = FMT_RFC822; CM_SetField(imsg, eMsgSubject, HKEY("QMSG")); CM_SetField(imsg, eAuthor, HKEY("Citadel")); CM_SetField(imsg, eJournal, HKEY("do not journal")); CM_SetAsFieldSB(imsg, eMesageText, &SpoolMsg); CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); CM_Free(imsg); } return 0; }
struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822) { struct CtdlMessage *msg; const char *pos, *beg, *end, *totalend; int done, alldone = 0; int converted; StrBuf *OtherHeaders; msg = malloc(sizeof(struct CtdlMessage)); if (msg == NULL) return msg; memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; /* self check */ msg->cm_anon_type = 0; /* never anonymous */ msg->cm_format_type = FMT_RFC822; /* internet message */ pos = ChrPtr(*rfc822); totalend = pos + StrLength(*rfc822); done = 0; OtherHeaders = NewStrBufPlain(NULL, StrLength(*rfc822)); while (!alldone) { /* Locate beginning and end of field, keeping in mind that * some fields might be multiline */ end = beg = pos; while ((end < totalend) && (end == beg) && (done == 0) ) { if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) ) { end = pos; } /* done with headers? */ if ((*pos=='\n') && ( (*(pos+1)=='\n') || (*(pos+1)=='\r')) ) { alldone = 1; } if (pos >= (totalend - 1) ) { end = pos; done = 1; } ++pos; } /* At this point we have a field. Are we interested in it? */ converted = convert_field(msg, beg, end); /* Strip the field out of the RFC822 header if we used it */ if (!converted) { StrBufAppendBufPlain(OtherHeaders, beg, end - beg, 0); StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); } /* If we've hit the end of the message, bail out */ if (pos >= totalend) alldone = 1; } StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); if (pos < totalend) StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0); FreeStrBuf(rfc822); CM_SetAsFieldSB(msg, eMesageText, &OtherHeaders); /* Follow-up sanity checks... */ /* If there's no timestamp on this message, set it to now. */ if (CM_IsEmpty(msg, eTimestamp)) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); } /* If a W (references, or rather, Wefewences) field is present, we * have to convert it from RFC822 format to Citadel format. */ if (!CM_IsEmpty(msg, eWeferences)) { /// todo: API! convert_references_to_wefewences(msg->cm_fields[eWeferences]); } return msg; }