/* * 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, ""); } }
void headers_brief_filter(long msgnum, void *userdata) { long i, l; struct CtdlMessage *msg; msg_filter *flt = (msg_filter*) userdata; l = GetCount(flt->Filter); msg = CtdlFetchMessage(msgnum, 0, 1); StrBufPrintf(flt->buffer, "%ld", msgnum); if (msg == NULL) { for (i = 0; i < l; i++) { StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0); } } else { const char *k; long len; void *v; RewindHashPos(flt->Filter, flt->p, 0); while (GetNextHashPos(flt->Filter, flt->p, &len, &k, &v)) { eMsgField f = (eMsgField) v; StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0); if (!CM_IsEmpty(msg, f)) { StrBufAppendBufPlain(flt->buffer, CM_KEY(msg, f), 0); } } } StrBufAppendBufPlain(flt->buffer, HKEY("\n"), 0); cputbuf(flt->buffer); }
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); } }
void dspam_do_msg(long msgnum, void *userdata) { char *msgtext; DSPAM_CTX *CTX; /* DSPAM Context */ struct CtdlMessage *msg; struct _ds_spam_signature SIG; /* signature */ CTX = *(DSPAM_CTX**) userdata; msg = CtdlFetchMessage(msgnum, 0); if (msg == NULL) return; /* Message */ CC->redirect_buffer = malloc(SIZ); CC->redirect_len = 0; CC->redirect_alloc = SIZ; CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0); msgtext = CC->redirect_buffer; // don't need? msglen = CC->redirect_len; CC->redirect_buffer = NULL; CC->redirect_len = 0; CC->redirect_alloc = 0; /* Call DSPAM's processor with the message text */ if (dspam_process (CTX, msgtext) != 0) { free(msgtext); syslog(LOG_CRIT, "ERROR: dspam_process failed"); return; } if (CTX->signature == NULL) { syslog(LOG_CRIT,"No signature provided\n"); } else { /* Copy to a safe place */ // TODO: len -> cm_fields? msg->cm_fields[eErrorMsg] = malloc (CTX->signature->length * 2); size_t len = CtdlEncodeBase64(msg->cm_fields[eErrorMsg], CTX->signature->data, CTX->signature->length, 0); if (msg->cm_fields[eErrorMsg][len - 1] == '\n') { msg->cm_fields[eErrorMsg][len - 1] = '\0'; } } free(msgtext); SIG.length = CTX->signature->length; /* Print processing results */ syslog(LOG_DEBUG, "Probability: %2.4f Confidence: %2.4f, Result: %s\n", CTX->probability, CTX->confidence, (CTX->result == DSR_ISSPAM) ? "Spam" : "Innocent"); //// todo: put signature into the citadel message //// todo: save message; destroy message. }
/* * 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); }
/* * 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; }
/* * 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 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); }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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; }
/* * 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); } }
/* * 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 } }