void SV_ClearLastLevel(void) { Menu_Reset(); Z_TagFree(TAG_G_ALLOC); Z_TagFree(TAG_UI_ALLOC); G_FreeRoffs(); R_ModelFree(); Music_Free(); Sys_IORequestQueueClear(); AS_FreePartial(); G_ASPreCacheFree(); Ghoul2InfoArray_Free(); G2_FreeRag(); ClearAllNavStructures(); ClearModelsAlreadyDone(); CL_FreeServerCommands(); CL_FreeReliableCommands(); CM_Free(); ShaderEntryPtrs_Clear(); numVehicles = 0; if (svs.clients) { SV_FreeClient( svs.clients ); } }
/* * Spools out one message from the list. */ void network_spool_msg(long msgnum, void *userdata) { struct CitContext *CCC = CC; struct CtdlMessage *msg = NULL; long delete_after_send = 0; /* Set to 1 to delete after spooling */ SpoolControl *sc; sc = (SpoolControl *)userdata; msg = CtdlFetchMessage(msgnum, 1); if (msg == NULL) { QN_syslog(LOG_ERR, "failed to load Message <%ld> from disk\n", msgnum); return; } network_process_list(sc, msg, &delete_after_send); network_process_digest(sc, msg, &delete_after_send); network_process_participate(sc, msg, &delete_after_send); network_process_ignetpush(sc, msg, &delete_after_send); CM_Free(msg); /* update lastsent */ sc->lastsent = msgnum; /* Delete this message if delete-after-send is set */ if (delete_after_send) { CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, ""); } }
/* * Deliver digest messages */ void network_deliver_digest(SpoolControl *sc) { struct CitContext *CCC = CC; long len; char buf[SIZ]; char *pbuf; struct CtdlMessage *msg = NULL; long msglen; recptypes *valid; char bounce_to[256]; if (sc->Users[digestrecp] == NULL) return; msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_format_type = FMT_RFC822; msg->cm_anon_type = MES_NORMAL; CM_SetFieldLONG(msg, eTimestamp, time(NULL)); CM_SetField(msg, eAuthor, CCC->room.QRname, strlen(CCC->room.QRname)); len = snprintf(buf, sizeof buf, "[%s]", CCC->room.QRname); CM_SetField(msg, eMsgSubject, buf, len); CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias])); CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias])); /* Set the 'List-ID' header */ CM_SetField(msg, eListID, SKEY(sc->ListID)); /* * Go fetch the contents of the digest */ fseek(sc->digestfp, 0L, SEEK_END); msglen = ftell(sc->digestfp); pbuf = malloc(msglen + 1); fseek(sc->digestfp, 0L, SEEK_SET); fread(pbuf, (size_t)msglen, 1, sc->digestfp); pbuf[msglen] = '\0'; CM_SetAsField(msg, eMesageText, &pbuf, msglen); /* Now generate the delivery instructions */ /* Where do we want bounces and other noise to be heard? * Surely not the list members! */ snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn); /* Now submit the message */ valid = validate_recipients(ChrPtr(sc->Users[digestrecp]), NULL, 0); if (valid != NULL) { valid->bounce_to = strdup(bounce_to); valid->envelope_from = strdup(bounce_to); CtdlSubmitMsg(msg, valid, NULL, 0); } CM_Free(msg); free_recipients(valid); }
void inetcfg_init_backend(long msgnum, void *userdata) { struct CtdlMessage *msg; msg = CtdlFetchMessage(msgnum, 1, 1); if (msg != NULL) { inetcfg_setTo(msg); CM_Free(msg); } }
/* * Fetch a list of revisions for a particular wiki page */ void wiki_history(char *pagename) { int r; char history_page_name[270]; long msgnum; struct CtdlMessage *msg; r = CtdlDoIHavePermissionToReadMessagesInThisRoom(); if (r != om_ok) { if (r == om_not_logged_in) { cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); } else { cprintf("%d An unknown error has occurred.\n", ERROR); } return; } snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename); msgnum = CtdlLocateMessageByEuid(history_page_name, &CC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Revision history for '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } cprintf("%d Revision history for '%s'\n", LISTING_FOLLOWS, pagename); mime_parser(CM_RANGE(msg, eMesageText), *wiki_history_callback, NULL, NULL, NULL, 0); cprintf("000\n"); CM_Free(msg); return; }
void network_process_participate(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send) { struct CtdlMessage *msg = NULL; int ok_to_participate = 0; StrBuf *Buf = NULL; recptypes *valid; /* * Process client-side list participations for this room */ if (sc->Users[participate] == NULL) return; msg = CM_Duplicate(omsg); /* Only send messages which originated on our own * Citadel network, otherwise we'll end up sending the * remote mailing list's messages back to it, which * is rude... */ ok_to_participate = 0; if (!CM_IsEmpty(msg, eNodeName)) { if (!strcasecmp(msg->cm_fields[eNodeName], config.c_nodename)) { ok_to_participate = 1; } Buf = NewStrBufPlain(CM_KEY(msg, eNodeName)); if (CtdlIsValidNode(NULL, NULL, Buf, sc->working_ignetcfg, sc->the_netmap) == 0) { ok_to_participate = 1; } } if (ok_to_participate) { /* Replace the Internet email address of the * actual author with the email address of the * room itself, so the remote listserv doesn't * reject us. */ CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias])); valid = validate_recipients(ChrPtr(sc->Users[participate]) , NULL, 0); CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias])); CtdlSubmitMsg(msg, valid, "", 0); free_recipients(valid); } FreeStrBuf(&Buf); CM_Free(msg); }
/* * Purge the EUID Index of old records. * */ int PurgeEuidIndexTable(void) { int purged = 0; struct cdbdata *cdbei; struct EPurgeList *el = NULL; struct EPurgeList *eptr; long msgnum; struct CtdlMessage *msg = NULL; /* Phase 1: traverse through the table, discovering old records... */ syslog(LOG_DEBUG, "Purge EUID index: phase 1"); cdb_rewind(CDB_EUIDINDEX); while(cdbei = cdb_next_item(CDB_EUIDINDEX), cdbei != NULL) { memcpy(&msgnum, cdbei->ptr, sizeof(long)); msg = CtdlFetchMessage(msgnum, 0); if (msg != NULL) { CM_Free(msg); /* it still exists, so do nothing */ } else { eptr = (struct EPurgeList *) malloc(sizeof(struct EPurgeList)); if (eptr != NULL) { eptr->next = el; eptr->ep_keylen = cdbei->len - sizeof(long); eptr->ep_key = malloc(cdbei->len); memcpy(eptr->ep_key, &cdbei->ptr[sizeof(long)], eptr->ep_keylen); el = eptr; } ++purged; } cdb_free(cdbei); } /* Phase 2: delete the records */ syslog(LOG_DEBUG, "Purge euid index: phase 2"); while (el != NULL) { cdb_delete(CDB_EUIDINDEX, el->ep_key, el->ep_keylen); free(el->ep_key); eptr = el->next; free(el); el = eptr; } syslog(LOG_DEBUG, "Purge euid index: finished (purged %d records)", purged); return(purged); }
/* * Back end for the MSGS command: output EUID header. */ void headers_euid(long msgnum, void *userdata) { struct CtdlMessage *msg; msg = CtdlFetchMessage(msgnum, 0, 1); if (msg == NULL) { cprintf("%ld||\n", msgnum); return; } cprintf("%ld|%s|%s\n", msgnum, (!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""), (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0")); CM_Free(msg); }
/* * Back end for the MSGS command: output header summary. */ void headers_listing(long msgnum, void *userdata) { struct CtdlMessage *msg; msg = CtdlFetchMessage(msgnum, 0, 1); if (msg == NULL) { cprintf("%ld|0|||||\n", msgnum); return; } cprintf("%ld|%s|%s|%s|%s|%s|\n", msgnum, (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"), (!CM_IsEmpty(msg, eAuthor) ? msg->cm_fields[eAuthor] : ""), (!CM_IsEmpty(msg, eNodeName) ? msg->cm_fields[eNodeName] : ""), (!CM_IsEmpty(msg, erFc822Addr) ? msg->cm_fields[erFc822Addr] : ""), (!CM_IsEmpty(msg, eMsgSubject) ? msg->cm_fields[eMsgSubject] : "") ); CM_Free(msg); }
// // back end for the XOVER command , called for each message number // void nntp_xover_backend(long msgnum, void *userdata) { struct listgroup_range *lr = (struct listgroup_range *)userdata; // check range if supplied if (msgnum < lr->lo) return; if ((lr->hi != 0) && (msgnum > lr->hi)) return; struct CtdlMessage *msg = CtdlFetchMessage(msgnum, 0, 1); if (msg == NULL) { return; } // Teh RFC says we need: // ------------------------- // Subject header content // From header content // Date header content // Message-ID header content // References header content // :bytes metadata item // :lines metadata item time_t msgtime = atol(msg->cm_fields[eTimestamp]); char strtimebuf[26]; ctime_r(&msgtime, strtimebuf); // here we go -- print the line o'data cprintf("%ld\t%s\t%s <%s>\t%s\t%s\t%s\t100\t10\r\n", msgnum, msg->cm_fields[eMsgSubject], msg->cm_fields[eAuthor], msg->cm_fields[erFc822Addr], strtimebuf, msg->cm_fields[emessageId], msg->cm_fields[eWeferences] ); CM_Free(msg); }
void network_process_list(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send) { struct CtdlMessage *msg = NULL; /* * Process mailing list recipients */ if (sc->Users[listrecp] == NULL) return; /* create our own copy of the message. * We're going to need to modify it * in order to insert the [list name] in it, etc. */ msg = CM_Duplicate(omsg); CM_SetField(msg, eReplyTo, SKEY(sc->Users[roommailalias])); /* if there is no other recipient, Set the recipient * of the list message to the email address of the * room itself. */ if (CM_IsEmpty(msg, eRecipient)) { CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias])); } /* Set the 'List-ID' header */ CM_SetField(msg, eListID, SKEY(sc->ListID)); /* Prepend "[List name]" to the subject */ ListCalculateSubject(msg); /* Handle delivery */ network_deliver_list(msg, sc, CC->room.QRname); CM_Free(msg); }
eNextState POP3C_SaveMsg(AsyncIO *IO) { long msgnum; pop3aggr *RecvMsg = (pop3aggr *) IO->Data; /* Do Something With It (tm) */ msgnum = CtdlSubmitMsg(RecvMsg->CurrMsg->Msg, NULL, ChrPtr(RecvMsg->RoomName), 0); if (msgnum > 0L) { /* Message has been committed to the store * write the uidl to the use table * so we don't fetch this message again */ } CM_Free(RecvMsg->CurrMsg->Msg); RecvMsg->count ++; return NextDBOperation(&RecvMsg->IO, POP3C_StoreMsgRead); }
void xmpp_fetch_mortuary_backend(long msgnum, void *userdata) { HashList *mortuary = (HashList *) userdata; struct CtdlMessage *msg; char *ptr = NULL; char *lasts = NULL; msg = CtdlFetchMessage(msgnum, 1); if (msg == NULL) { return; } /* now add anyone we find into the hashlist */ /* skip past the headers */ ptr = strstr(msg->cm_fields[eMesageText], "\n\n"); if (ptr != NULL) { ptr += 2; } else { ptr = strstr(msg->cm_fields[eMesageText], "\n\r\n"); if (ptr != NULL) { ptr += 3; } } /* the remaining lines are addresses */ if (ptr != NULL) { ptr = strtok_r(ptr, "\n", &lasts); while (ptr != NULL) { char *pch = strdup(ptr); Put(mortuary, pch, strlen(pch), pch, NULL); ptr = strtok_r(NULL, "\n", &lasts); } } CM_Free(msg); }
/* * First phase of message purge -- gather the locations of messages which * qualify for purging and write them to a temp file. */ void GatherPurgeMessages(struct ctdlroom *qrbuf, void *data) { struct ExpirePolicy epbuf; long delnum; time_t xtime, now; struct CtdlMessage *msg = NULL; int a; struct cdbdata *cdbfr; long *msglist = NULL; int num_msgs = 0; FILE *purgelist; purgelist = (FILE *)data; fprintf(purgelist, "r=%s\n", qrbuf->QRname); time(&now); GetExpirePolicy(&epbuf, qrbuf); /* If the room is set to never expire messages ... do nothing */ if (epbuf.expire_mode == EXPIRE_NEXTLEVEL) return; if (epbuf.expire_mode == EXPIRE_MANUAL) return; /* Don't purge messages containing system configuration, dumbass. */ if (!strcasecmp(qrbuf->QRname, SYSCONFIGROOM)) return; /* Ok, we got this far ... now let's see what's in the room */ cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long)); if (cdbfr != NULL) { msglist = malloc(cdbfr->len); memcpy(msglist, cdbfr->ptr, cdbfr->len); num_msgs = cdbfr->len / sizeof(long); cdb_free(cdbfr); } /* Nothing to do if there aren't any messages */ if (num_msgs == 0) { if (msglist != NULL) free(msglist); return; } /* If the room is set to expire by count, do that */ if (epbuf.expire_mode == EXPIRE_NUMMSGS) { if (num_msgs > epbuf.expire_value) { for (a=0; a<(num_msgs - epbuf.expire_value); ++a) { fprintf(purgelist, "m=%ld\n", msglist[a]); ++messages_purged; } } } /* If the room is set to expire by age... */ if (epbuf.expire_mode == EXPIRE_AGE) { for (a=0; a<num_msgs; ++a) { delnum = msglist[a]; msg = CtdlFetchMessage(delnum, 0); /* dont need body */ if (msg != NULL) { xtime = atol(msg->cm_fields[eTimestamp]); CM_Free(msg); } else { xtime = 0L; } if ((xtime > 0L) && (now - xtime > (time_t)(epbuf.expire_value * 86400L))) { fprintf(purgelist, "m=%ld\n", delnum); ++messages_purged; } } } if (msglist != NULL) free(msglist); }
/* * 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); }
/* * imap_do_search() calls imap_do_search_msg() to search an individual * message after it has been fetched from the disk. This function returns * nonzero if there is a match. * * supplied_msg MAY be used to pass a pointer to the message in memory, * if for some reason it's already been loaded. If not, the message will * be loaded only if one or more search criteria require it. */ int imap_do_search_msg(int seq, struct CtdlMessage *supplied_msg, int num_items, ConstStr *itemlist, int is_uid) { citimap *Imap = IMAP; int match = 0; int is_not = 0; int is_or = 0; int pos = 0; int i; char *fieldptr; struct CtdlMessage *msg = NULL; int need_to_free_msg = 0; if (num_items == 0) { return(0); } msg = supplied_msg; /* Initially we start at the beginning. */ pos = 0; /* Check for the dreaded NOT criterion. */ if (!strcasecmp(itemlist[0].Key, "NOT")) { is_not = 1; pos = 1; } /* Check for the dreaded OR criterion. */ if (!strcasecmp(itemlist[0].Key, "OR")) { is_or = 1; pos = 1; } /* Now look for criteria. */ if (!strcasecmp(itemlist[pos].Key, "ALL")) { match = 1; ++pos; } else if (!strcasecmp(itemlist[pos].Key, "ANSWERED")) { if (Imap->flags[seq-1] & IMAP_ANSWERED) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "BCC")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Bcc"); if (fieldptr != NULL) { if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) { match = 1; } free(fieldptr); } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "BEFORE")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) < 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "BODY")) { /* If fulltext indexing is active, on this server, * all messages have already been qualified. */ if (config.c_enable_fulltext) { match = 1; } /* Otherwise, we have to do a slow search. */ else { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (bmstrcasestr(msg->cm_fields[eMesageText], itemlist[pos+1].Key)) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "CC")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { fieldptr = msg->cm_fields[eCarbonCopY]; if (fieldptr != NULL) { if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) { match = 1; } } else { fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Cc"); if (fieldptr != NULL) { if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) { match = 1; } free(fieldptr); } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "DELETED")) { if (Imap->flags[seq-1] & IMAP_DELETED) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "DRAFT")) { if (Imap->flags[seq-1] & IMAP_DRAFT) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "FLAGGED")) { if (Imap->flags[seq-1] & IMAP_FLAGGED) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "FROM")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (bmstrcasestr(msg->cm_fields[eAuthor], itemlist[pos+1].Key)) { match = 1; } if (bmstrcasestr(msg->cm_fields[erFc822Addr], itemlist[pos+1].Key)) { match = 1; } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "HEADER")) { /* We've got to do a slow search for this because the client * might be asking for an RFC822 header field that has not been * converted into a Citadel header field. That requires * examining the message body. */ if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_FAST, 0, 1, 0); fieldptr = rfc822_fetch_field(ChrPtr(CC->redirect_buffer), itemlist[pos+1].Key); if (fieldptr != NULL) { if (bmstrcasestr(fieldptr, itemlist[pos+2].Key)) { match = 1; } free(fieldptr); } FreeStrBuf(&CC->redirect_buffer); } pos += 3; /* Yes, three */ } else if (!strcasecmp(itemlist[pos].Key, "KEYWORD")) { /* not implemented */ pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "LARGER")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (msg->cm_lengths[eMesageText] > atoi(itemlist[pos+1].Key)) { match = 1; } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "NEW")) { if ( (Imap->flags[seq-1] & IMAP_RECENT) && (!(Imap->flags[seq-1] & IMAP_SEEN))) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "OLD")) { if (!(Imap->flags[seq-1] & IMAP_RECENT)) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "ON")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) == 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "RECENT")) { if (Imap->flags[seq-1] & IMAP_RECENT) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "SEEN")) { if (Imap->flags[seq-1] & IMAP_SEEN) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "SENTBEFORE")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) < 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "SENTON")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) == 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "SENTSINCE")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) >= 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "SINCE")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (!CM_IsEmpty(msg, eTimestamp)) { if (imap_datecmp(itemlist[pos+1].Key, atol(msg->cm_fields[eTimestamp])) >= 0) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "SMALLER")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (msg->cm_lengths[eMesageText] < atoi(itemlist[pos+1].Key)) { match = 1; } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "SUBJECT")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (bmstrcasestr(msg->cm_fields[eMsgSubject], itemlist[pos+1].Key)) { match = 1; } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "TEXT")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { for (i='A'; i<='Z'; ++i) { if (bmstrcasestr(msg->cm_fields[i], itemlist[pos+1].Key)) { match = 1; } } } pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "TO")) { if (msg == NULL) { msg = CtdlFetchMessage(Imap->msgids[seq-1], 1); need_to_free_msg = 1; } if (msg != NULL) { if (bmstrcasestr(msg->cm_fields[eRecipient], itemlist[pos+1].Key)) { match = 1; } } pos += 2; } /* FIXME this is b0rken. fix it. */ else if (imap_is_message_set(itemlist[pos].Key)) { if (is_msg_in_sequence_set(itemlist[pos].Key, seq)) { match = 1; } pos += 1; } /* FIXME this is b0rken. fix it. */ else if (!strcasecmp(itemlist[pos].Key, "UID")) { if (is_msg_in_sequence_set(itemlist[pos+1].Key, Imap->msgids[seq-1])) { match = 1; } pos += 2; } /* Now here come the 'UN' criteria. Why oh why do we have to * implement *both* the 'UN' criteria *and* the 'NOT' keyword? Why * can't there be *one* way to do things? More gratuitous complexity. */ else if (!strcasecmp(itemlist[pos].Key, "UNANSWERED")) { if ((Imap->flags[seq-1] & IMAP_ANSWERED) == 0) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "UNDELETED")) { if ((Imap->flags[seq-1] & IMAP_DELETED) == 0) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "UNDRAFT")) { if ((Imap->flags[seq-1] & IMAP_DRAFT) == 0) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "UNFLAGGED")) { if ((Imap->flags[seq-1] & IMAP_FLAGGED) == 0) { match = 1; } ++pos; } else if (!strcasecmp(itemlist[pos].Key, "UNKEYWORD")) { /* FIXME */ pos += 2; } else if (!strcasecmp(itemlist[pos].Key, "UNSEEN")) { if ((Imap->flags[seq-1] & IMAP_SEEN) == 0) { match = 1; } ++pos; } /* Remember to negate if we were told to */ if (is_not) { match = !match; } /* Keep going if there are more criteria! */ if (pos < num_items) { if (is_or) { match = (match || imap_do_search_msg(seq, msg, num_items - pos, &itemlist[pos], is_uid)); } else { match = (match && imap_do_search_msg(seq, msg, num_items - pos, &itemlist[pos], is_uid)); } } if (need_to_free_msg) { CM_Free(msg); } return(match); }
/* * 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"); }
void network_process_digest(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send) { struct CtdlMessage *msg = NULL; if (sc->Users[digestrecp] == NULL) return; /* If there are digest recipients, we have to build a digest */ if (sc->digestfp == NULL) { sc->digestfp = create_digest_file(&sc->room, 1); if (sc->digestfp == NULL) return; sc->haveDigest = ftell(sc->digestfp) > 0; if (!sc->haveDigest) { fprintf(sc->digestfp, "Content-type: text/plain\n\n"); } sc->haveDigest = 1; } msg = CM_Duplicate(omsg); if (msg != NULL) { sc->haveDigest = 1; fprintf(sc->digestfp, " -----------------------------------" "------------------------------------" "-------\n"); fprintf(sc->digestfp, "From: "); if (!CM_IsEmpty(msg, eAuthor)) { fprintf(sc->digestfp, "%s ", msg->cm_fields[eAuthor]); } if (!CM_IsEmpty(msg, erFc822Addr)) { fprintf(sc->digestfp, "<%s> ", msg->cm_fields[erFc822Addr]); } else if (!CM_IsEmpty(msg, eNodeName)) { fprintf(sc->digestfp, "@%s ", msg->cm_fields[eNodeName]); } fprintf(sc->digestfp, "\n"); if (!CM_IsEmpty(msg, eMsgSubject)) { fprintf(sc->digestfp, "Subject: %s\n", msg->cm_fields[eMsgSubject]); } CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); safestrncpy(CC->preferred_formats, "text/plain", sizeof CC->preferred_formats); CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0, 0); StrBufTrim(CC->redirect_buffer); fwrite(HKEY("\n"), 1, sc->digestfp); fwrite(SKEY(CC->redirect_buffer), 1, sc->digestfp); fwrite(HKEY("\n"), 1, sc->digestfp); FreeStrBuf(&CC->redirect_buffer); sc->num_msgs_spooled += 1; CM_Free(msg); } }
/* * smtp_do_procmsg() * * Called by smtp_do_queue() to handle an individual message. */ void smtp_do_procmsg(long msgnum, void *userdata) { time_t now; int mynumsessions = num_sessions; struct CtdlMessage *msg = NULL; char *Author = NULL; char *Address = NULL; char *instr = NULL; StrBuf *PlainQItem; OneQueItem *MyQItem; char *pch; HashPos *It; void *vQE; long len; const char *Key; int HaveBuffers = 0; StrBuf *Msg =NULL; if (mynumsessions > max_sessions_for_outbound_smtp) { SMTPC_syslog(LOG_INFO, "skipping because of num jobs %d > %d max_sessions_for_outbound_smtp", mynumsessions, max_sessions_for_outbound_smtp); } SMTPC_syslog(LOG_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum); ///strcpy(envelope_from, ""); msg = CtdlFetchMessage(msgnum, 1, 1); if (msg == NULL) { SMTPC_syslog(LOG_ERR, "tried %ld but no such message!\n", msgnum); return; } pch = instr = msg->cm_fields[eMesageText]; /* Strip out the headers (no not amd any other non-instruction) line */ while (pch != NULL) { pch = strchr(pch, '\n'); if ((pch != NULL) && ((*(pch + 1) == '\n') || (*(pch + 1) == '\r'))) { instr = pch + 2; pch = NULL; } } PlainQItem = NewStrBufPlain(instr, -1); CM_Free(msg); MyQItem = DeserializeQueueItem(PlainQItem, msgnum); FreeStrBuf(&PlainQItem); if (MyQItem == NULL) { SMTPC_syslog(LOG_ERR, "Msg No %ld: already in progress!\n", msgnum); return; /* s.b. else is already processing... */ } /* * Postpone delivery if we've already tried recently. */ now = time(NULL); if ((MyQItem->ReattemptWhen != 0) && (now < MyQItem->ReattemptWhen) && (run_queue_now == 0)) { SMTPC_syslog(LOG_DEBUG, "Retry time not yet reached. %ld seconds left.", MyQItem->ReattemptWhen - now); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? DeleteHashPos(&It); return; } /* * Bail out if there's no actual message associated with this */ if (MyQItem->MessageID < 0L) { SMTPCM_syslog(LOG_ERR, "no 'msgid' directive found!\n"); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); DeleteHashPos(&It); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? return; } It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { MailQEntry *ThisItem = vQE; SMTPC_syslog(LOG_DEBUG, "SMTP Queue: Task: <%s> %d\n", ChrPtr(ThisItem->Recipient), ThisItem->Active); } DeleteHashPos(&It); MyQItem->NotYetShutdownDeliveries = MyQItem->ActiveDeliveries = CountActiveQueueEntries(MyQItem, 1); /* failsafe against overload: * will we exceed the limit set? */ if ((MyQItem->ActiveDeliveries + mynumsessions > max_sessions_for_outbound_smtp) && /* if yes, did we reach more than half of the quota? */ ((mynumsessions * 2) > max_sessions_for_outbound_smtp) && /* if... would we ever fit into half of the quota?? */ (((MyQItem->ActiveDeliveries * 2) < max_sessions_for_outbound_smtp))) { /* abort delivery for another time. */ SMTPC_syslog(LOG_INFO, "SMTP Queue: skipping because of num jobs %d + %ld > %d max_sessions_for_outbound_smtp", mynumsessions, MyQItem->ActiveDeliveries, max_sessions_for_outbound_smtp); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); return; } if (MyQItem->ActiveDeliveries > 0) { ParsedURL *RelayUrls = NULL; int nActivated = 0; int n = MsgCount++; int m = MyQItem->ActiveDeliveries; int i = 1; It = GetNewHashPos(MyQItem->MailQEntries, 0); Msg = smtp_load_msg(MyQItem, n, &Author, &Address); RelayUrls = LoadRelayUrls(MyQItem, Author, Address); if ((RelayUrls == NULL) && MyQItem->HaveRelay) { while ((i <= m) && (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))) { int KeepBuffers = (i == m); MailQEntry *ThisItem = vQE; StrBufPrintf(ThisItem->StatusMessage, "No relay configured matching %s / %s", (Author != NULL)? Author : "", (Address != NULL)? Address : ""); ThisItem->Status = 5; nActivated++; if (i > 1) n = MsgCount++; SMTPC_syslog(LOG_INFO, "SMTPC: giving up on <%ld> <%s> %d / %d \n", MyQItem->MessageID, ChrPtr(ThisItem->Recipient), i, m); (*((int*) userdata)) ++; smtp_try_one_queue_entry(MyQItem, ThisItem, Msg, KeepBuffers, n, RelayUrls); if (KeepBuffers) HaveBuffers++; i++; } if (Author != NULL) free (Author); if (Address != NULL) free (Address); DeleteHashPos(&It); return; } if (Author != NULL) free (Author); if (Address != NULL) free (Address); while ((i <= m) && (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))) { MailQEntry *ThisItem = vQE; if (ThisItem->Active == 1) { int KeepBuffers = (i == m); nActivated++; if (nActivated % ndelay_count == 0) usleep(delay_msec); if (i > 1) n = MsgCount++; SMTPC_syslog(LOG_DEBUG, "SMTPC: Trying <%ld> <%s> %d / %d \n", MyQItem->MessageID, ChrPtr(ThisItem->Recipient), i, m); (*((int*) userdata)) ++; smtp_try_one_queue_entry(MyQItem, ThisItem, Msg, KeepBuffers, n, RelayUrls); if (KeepBuffers) HaveBuffers++; i++; } } DeleteHashPos(&It); } else { It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } else { long len; const char* Key; void *VData; SMTPC_syslog(LOG_WARNING, "unable to find QItem with ID[%ld]", MyQItem->MessageID); while (GetNextHashPos(ActiveQItems, It, &len, &Key, &VData)) { SMTPC_syslog(LOG_WARNING, "have: ID[%ld]", ((OneQueItem *)VData)->MessageID); } } } pthread_mutex_unlock(&ActiveQItemsLock); DeleteHashPos(&It); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? // TODO: bounce & delete? } if (!HaveBuffers) { FreeStrBuf (&Msg); // TODO : free RelayUrls } }
/* * Fetch a specific revision of a wiki page. The "operation" string may be set to "fetch" in order * to simply fetch the desired revision and store it in a temporary location for viewing, or "revert" * to revert the currently active page to that revision. */ void wiki_rev(char *pagename, char *rev, char *operation) { struct CitContext *CCC = CC; int r; char history_page_name[270]; long msgnum; char temp[PATH_MAX]; struct CtdlMessage *msg; FILE *fp; struct HistoryEraserCallBackData hecbd; long len = 0L; int rv; r = CtdlDoIHavePermissionToReadMessagesInThisRoom(); if (r != om_ok) { if (r == om_not_logged_in) { cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); } else { cprintf("%d An unknown error has occurred.\n", ERROR); } return; } if (!strcasecmp(operation, "revert")) { r = CtdlDoIHavePermissionToPostInThisRoom(temp, sizeof temp, NULL, POST_LOGGED_IN, 0); if (r != 0) { cprintf("%d %s\n", r, temp); return; } } /* Begin by fetching the current version of the page. We're going to patch * backwards through the diffs until we get the one we want. */ msgnum = CtdlLocateMessageByEuid(pagename, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Page '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Output it to a temporary file */ CtdlMakeTempFileName(temp, sizeof temp); fp = fopen(temp, "w"); if (fp != NULL) { r = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); } else { syslog(LOG_ALERT, "Cannot open %s: %s\n", temp, strerror(errno)); } CM_Free(msg); /* Get the revision history */ snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename); msgnum = CtdlLocateMessageByEuid(history_page_name, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Revision history for '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Start patching backwards (newest to oldest) through the revision history until we * hit the revision uuid requested by the user. (The callback will perform each one.) */ memset(&hecbd, 0, sizeof(struct HistoryEraserCallBackData)); hecbd.tempfilename = temp; hecbd.stop_when = rev; striplt(hecbd.stop_when); mime_parser(CM_RANGE(msg, eMesageText), *wiki_rev_callback, NULL, NULL, (void *)&hecbd, 0); CM_Free(msg); /* Were we successful? */ if (hecbd.done == 0) { cprintf("%d Revision '%s' of page '%s' was not found.\n", ERROR + MESSAGE_NOT_FOUND, rev, pagename ); } /* We have the desired revision on disk. Now do something with it. */ else if ( (!strcasecmp(operation, "fetch")) || (!strcasecmp(operation, "revert")) ) { 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; fp = fopen(temp, "r"); if (fp) { char *msgbuf; fseek(fp, 0L, SEEK_END); len = ftell(fp); fseek(fp, 0L, SEEK_SET); msgbuf = malloc(len + 1); rv = fread(msgbuf, len, 1, fp); syslog(LOG_DEBUG, "did %d blocks of %ld bytes\n", rv, len); msgbuf[len] = '\0'; CM_SetAsField(msg, eMesageText, &msgbuf, len); fclose(fp); } if (len <= 0) { msgnum = (-1L); } else if (!strcasecmp(operation, "fetch")) { CM_SetField(msg, eAuthor, HKEY("Citadel")); CtdlCreateRoom(wwm, 5, "", 0, 1, 1, VIEW_BBS); /* Not an error if already exists */ msgnum = CtdlSubmitMsg(msg, NULL, wwm, 0); /* Store the revision here */ /* * WARNING: VILE SLEAZY HACK * This will avoid the 'message xxx is not in this room' security error, * but only if the client fetches the message we just generated immediately * without first trying to perform other fetch operations. */ if (CCC->cached_msglist != NULL) { free(CCC->cached_msglist); CCC->cached_msglist = NULL; CCC->cached_num_msgs = 0; } CCC->cached_msglist = malloc(sizeof(long)); if (CCC->cached_msglist != NULL) { CCC->cached_num_msgs = 1; CCC->cached_msglist[0] = msgnum; } } else if (!strcasecmp(operation, "revert")) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); if (!IsEmptyStr(CCC->user.fullname)) { CM_SetField(msg, eAuthor, CCC->user.fullname, strlen(CCC->user.fullname)); } if (!IsEmptyStr(CCC->cs_inet_email)) { CM_SetField(msg, erFc822Addr, CCC->cs_inet_email, strlen(CCC->cs_inet_email)); } if (!IsEmptyStr(CCC->room.QRname)) { CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(msg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); if (!IsEmptyStr(pagename)) { CM_SetField(msg, eExclusiveID, pagename, strlen(pagename)); } msgnum = CtdlSubmitMsg(msg, NULL, "", 0); /* Replace the current revision */ } else { /* Theoretically it is impossible to get here, but throw an error anyway */ msgnum = (-1L); } CM_Free(msg); if (msgnum >= 0L) { cprintf("%d %ld\n", CIT_OK, msgnum); /* Give the client a msgnum */ } else { cprintf("%d Error %ld has occurred.\n", ERROR+INTERNAL_ERROR, msgnum); } } /* We did all this work for nothing. Express anguish to the caller. */ else { cprintf("%d An unknown operation was requested.\n", ERROR+CMD_NOT_SUPPORTED); } unlink(temp); return; }
/* * 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"); }
void network_process_ignetpush(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send) { StrBuf *Recipient; StrBuf *RemoteRoom; const char *Pos = NULL; struct CtdlMessage *msg = NULL; struct CitContext *CCC = CC; struct ser_ret sermsg; char buf[SIZ]; char filename[PATH_MAX]; FILE *fp; StrBuf *Buf = NULL; int i; int bang = 0; int send = 1; if (sc->Users[ignet_push_share] == NULL) return; /* * Process IGnet push shares */ msg = CM_Duplicate(omsg); /* Prepend our node name to the Path field whenever * sending a message to another IGnet node */ Netmap_AddMe(msg, HKEY("username")); /* * Determine if this message is set to be deleted * after sending out on the network */ if (!CM_IsEmpty(msg, eSpecialField)) { if (!strcasecmp(msg->cm_fields[eSpecialField], "CANCEL")) { *delete_after_send = 1; } } /* Now send it to every node */ Recipient = NewStrBufPlain(NULL, StrLength(sc->Users[ignet_push_share])); RemoteRoom = NewStrBufPlain(NULL, StrLength(sc->Users[ignet_push_share])); while ((Pos != StrBufNOTNULL) && StrBufExtract_NextToken(Recipient, sc->Users[ignet_push_share], &Pos, ',')) { StrBufExtract_NextToken(RemoteRoom, sc->Users[ignet_push_share], &Pos, ','); send = 1; NewStrBufDupAppendFlush(&Buf, Recipient, NULL, 1); /* Check for valid node name */ if (CtdlIsValidNode(NULL, NULL, Buf, sc->working_ignetcfg, sc->the_netmap) != 0) { QN_syslog(LOG_ERR, "Invalid node <%s>\n", ChrPtr(Recipient)); send = 0; } /* Check for split horizon */ QN_syslog(LOG_DEBUG, "Path is %s\n", msg->cm_fields[eMessagePath]); bang = num_tokens(msg->cm_fields[eMessagePath], '!'); if (bang > 1) { for (i=0; i<(bang-1); ++i) { extract_token(buf, msg->cm_fields[eMessagePath], i, '!', sizeof buf); QN_syslog(LOG_DEBUG, "Compare <%s> to <%s>\n", buf, ChrPtr(Recipient)) ; if (!strcasecmp(buf, ChrPtr(Recipient))) { send = 0; break; } } QN_syslog(LOG_INFO, " %sSending to %s\n", (send)?"":"Not ", ChrPtr(Recipient)); } /* Send the message */ if (send == 1) { /* * Force the message to appear in the correct * room on the far end by setting the C field * correctly */ if (StrLength(RemoteRoom) > 0) { CM_SetField(msg, eRemoteRoom, SKEY(RemoteRoom)); } else { CM_SetField(msg, eRemoteRoom, CCC->room.QRname, strlen(CCC->room.QRname)); } /* serialize it for transmission */ CtdlSerializeMessage(&sermsg, msg); if (sermsg.len > 0) { /* write it to a spool file */ snprintf(filename, sizeof(filename), "%s/%s@%lx%x", ctdl_netout_dir, ChrPtr(Recipient), time(NULL), rand() ); QN_syslog(LOG_DEBUG, "Appending to %s\n", filename); fp = fopen(filename, "ab"); if (fp != NULL) { fwrite(sermsg.ser, sermsg.len, 1, fp); fclose(fp); } else { QN_syslog(LOG_ERR, "%s: %s\n", filename, strerror(errno)); } /* free the serialized version */ free(sermsg.ser); } } } FreeStrBuf(&Buf); FreeStrBuf(&Recipient); FreeStrBuf(&RemoteRoom); CM_Free(msg); }
void R_Init( void ) { int err; int i; //VID_Printf( PRINT_ALL, "----- R_Init -----\n" ); #ifdef _XBOX extern qboolean vidRestartReloadMap; if (!vidRestartReloadMap) { Hunk_Clear(); extern void CM_Free(void); CM_Free(); void CM_CleanLeafCache(void); CM_CleanLeafCache(); } #endif ShaderEntryPtrs_Clear(); #ifdef _XBOX //Save visibility info as it has already been set. SPARC<byte> *vis = tr.externalVisData; #endif // clear all our internal state memset( &tr, 0, sizeof( tr ) ); memset( &backEnd, 0, sizeof( backEnd ) ); memset( &tess, 0, sizeof( tess ) ); #ifdef _XBOX //Restore visibility info. tr.externalVisData = vis; #endif Swap_Init(); #ifndef FINAL_BUILD if ( (int)tess.xyz & 15 ) { Com_Printf( "WARNING: tess.xyz not 16 byte aligned (%x)\n",(int)tess.xyz & 15 ); } #endif // // init function tables // for ( i = 0; i < FUNCTABLE_SIZE; i++ ) { tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; tr.inverseSawToothTable[i] = 1.0 - tr.sawToothTable[i]; if ( i < FUNCTABLE_SIZE / 2 ) { if ( i < FUNCTABLE_SIZE / 4 ) { tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); } else { tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; } } else { tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; } } R_InitFogTable(); R_NoiseInit(); R_Register(); backEndData = (backEndData_t *) Hunk_Alloc( sizeof( backEndData_t ), qtrue ); R_ToggleSmpFrame(); //r_smp const color4ub_t color = {0xff, 0xff, 0xff, 0xff}; for(i=0;i<MAX_LIGHT_STYLES;i++) { RE_SetLightStyle(i, *(int*)color); } InitOpenGL(); R_InitImages(); R_InitShaders(); R_InitSkins(); #ifndef _XBOX R_TerrainInit(); #endif R_ModelInit(); // R_InitWorldEffects(); R_InitFonts(); err = qglGetError(); if ( err != GL_NO_ERROR ) VID_Printf (PRINT_ALL, "glGetError() = 0x%x\n", err); //VID_Printf( PRINT_ALL, "----- finished R_Init -----\n" ); }
/* * 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); } }
/* * 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); }
/* * Back end for cmd_auto() */ void hunt_for_autocomplete(long msgnum, char *search_string) { struct CtdlMessage *msg; struct vCard *v; char *value = NULL; char *value2 = NULL; int i = 0; char *nnn = NULL; msg = CtdlFetchMessage(msgnum, 1); if (msg == NULL) return; v = vcard_load(msg->cm_fields[eMesageText]); CM_Free(msg); /* * Try to match from a friendly name (the "fn" field). If there is * a match, return the entry in the form of: * Display Name <*****@*****.**> */ value = vcard_get_prop(v, "fn", 0, 0, 0); if (value != NULL) if (bmstrcasestr(value, search_string)) { value2 = vcard_get_prop(v, "email", 1, 0, 0); if (value2 == NULL) value2 = ""; cprintf("%s <%s>\n", value, value2); vcard_free(v); return; } /* * Try to match from a structured name (the "n" field). If there is * a match, return the entry in the form of: * Display Name <*****@*****.**> */ value = vcard_get_prop(v, "n", 0, 0, 0); if (value != NULL) if (bmstrcasestr(value, search_string)) { value2 = vcard_get_prop(v, "email", 1, 0, 0); if (value2 == NULL) value2 = ""; nnn = n_to_fn(value); cprintf("%s <%s>\n", nnn, value2); free(nnn); vcard_free(v); return; } /* * Try a partial match on all listed email addresses. */ i = 0; while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) { if (bmstrcasestr(value, search_string)) { if (vcard_get_prop(v, "fn", 0, 0, 0)) { cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value); } else if (vcard_get_prop(v, "n", 0, 0, 0)) { nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0)); cprintf("%s <%s>\n", nnn, value); free(nnn); } else { cprintf("%s\n", value); } vcard_free(v); return; } } vcard_free(v); }
/* * SV_ShutdownGame * * Called when each game quits */ void SV_ShutdownGame( const char *finalmsg, qboolean reconnect ) { if( !svs.initialized ) return; if( svs.demo.file ) SV_Demo_Stop_f(); if( svs.clients ) SV_FinalMessage( finalmsg, reconnect ); SV_ShutdownGameProgs(); // SV_MM_Shutdown(); NET_CloseSocket( &svs.socket_loopback ); NET_CloseSocket( &svs.socket_udp ); NET_CloseSocket( &svs.socket_udp6 ); #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer ) NET_CloseSocket( &svs.socket_tcp ); #endif // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); if( svs.clients ) { Mem_Free( svs.clients ); svs.clients = NULL; } if( svs.client_entities.entities ) { Mem_Free( svs.client_entities.entities ); memset( &svs.client_entities, 0, sizeof( svs.client_entities ) ); } if( svs.cms ) { CM_Free( svs.cms ); svs.cms = NULL; } memset( &sv, 0, sizeof( sv ) ); Com_SetServerState( sv.state ); Com_FreePureList( &svs.purelist ); if( svs.motd ) { Mem_Free( svs.motd ); svs.motd = NULL; } if( sv_mempool ) Mem_EmptyPool( sv_mempool ); memset( &svs, 0, sizeof( svs ) ); svs.initialized = qfalse; }
/* * Index or de-index a message. (op == 1 to index, 0 to de-index) */ void ft_index_message(long msgnum, int op) { int num_tokens = 0; int *tokens = NULL; int i, j; struct cdbdata *cdb_bucket; StrBuf *msgtext; char *txt; int tok; struct CtdlMessage *msg = NULL; msg = CtdlFetchMessage(msgnum, 1, 1); if (msg == NULL) { syslog(LOG_ERR, "ft_index_message() could not load msg %ld", msgnum); return; } if (!CM_IsEmpty(msg, eSuppressIdx)) { syslog(LOG_DEBUG, "ft_index_message() excluded msg %ld", msgnum); CM_Free(msg); return; } syslog(LOG_DEBUG, "ft_index_message() %s msg %ld", (op ? "adding" : "removing") , msgnum); /* Output the message as text before indexing it, so we don't end up * indexing a bunch of encoded base64, etc. */ CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_ALL, 0, 1, 0); CM_Free(msg); msgtext = CC->redirect_buffer; CC->redirect_buffer = NULL; if (msgtext != NULL) { syslog(LOG_DEBUG, "Wordbreaking message %ld (%d bytes)", msgnum, StrLength(msgtext)); } txt = SmashStrBuf(&msgtext); wordbreaker(txt, &num_tokens, &tokens); free(txt); syslog(LOG_DEBUG, "Indexing message %ld [%d tokens]", msgnum, num_tokens); if (num_tokens > 0) { for (i=0; i<num_tokens; ++i) { /* Add the message to the relevant token bucket */ /* search for tokens[i] */ tok = tokens[i]; if ( (tok >= 0) && (tok <= 65535) ) { /* fetch the bucket, Liza */ if (ftc_msgs[tok] == NULL) { cdb_bucket = cdb_fetch(CDB_FULLTEXT, &tok, sizeof(int)); if (cdb_bucket != NULL) { ftc_num_msgs[tok] = cdb_bucket->len / sizeof(long); ftc_msgs[tok] = (long *)cdb_bucket->ptr; cdb_bucket->ptr = NULL; cdb_free(cdb_bucket); } else { ftc_num_msgs[tok] = 0; ftc_msgs[tok] = malloc(sizeof(long)); } } if (op == 1) { /* add to index */ ++ftc_num_msgs[tok]; ftc_msgs[tok] = realloc(ftc_msgs[tok], ftc_num_msgs[tok]*sizeof(long)); ftc_msgs[tok][ftc_num_msgs[tok] - 1] = msgnum; } if (op == 0) { /* remove from index */ if (ftc_num_msgs[tok] >= 1) { for (j=0; j<ftc_num_msgs[tok]; ++j) { if (ftc_msgs[tok][j] == msgnum) { memmove(&ftc_msgs[tok][j], &ftc_msgs[tok][j+1], ((ftc_num_msgs[tok] - j - 1)*sizeof(long))); --ftc_num_msgs[tok]; --j; } } } } } else { syslog(LOG_ALERT, "Invalid token %d !!", tok); } } free(tokens); } }
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; }
/****************************************************************************** * 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; }