/* * 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); }
/* * 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; }
struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822) { struct CtdlMessage *msg; const char *pos, *beg, *end, *totalend; int done, alldone = 0; int converted; StrBuf *OtherHeaders; msg = malloc(sizeof(struct CtdlMessage)); if (msg == NULL) return msg; memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; /* self check */ msg->cm_anon_type = 0; /* never anonymous */ msg->cm_format_type = FMT_RFC822; /* internet message */ pos = ChrPtr(*rfc822); totalend = pos + StrLength(*rfc822); done = 0; OtherHeaders = NewStrBufPlain(NULL, StrLength(*rfc822)); while (!alldone) { /* Locate beginning and end of field, keeping in mind that * some fields might be multiline */ end = beg = pos; while ((end < totalend) && (end == beg) && (done == 0) ) { if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) ) { end = pos; } /* done with headers? */ if ((*pos=='\n') && ( (*(pos+1)=='\n') || (*(pos+1)=='\r')) ) { alldone = 1; } if (pos >= (totalend - 1) ) { end = pos; done = 1; } ++pos; } /* At this point we have a field. Are we interested in it? */ converted = convert_field(msg, beg, end); /* Strip the field out of the RFC822 header if we used it */ if (!converted) { StrBufAppendBufPlain(OtherHeaders, beg, end - beg, 0); StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); } /* If we've hit the end of the message, bail out */ if (pos >= totalend) alldone = 1; } StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); if (pos < totalend) StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0); FreeStrBuf(rfc822); CM_SetAsFieldSB(msg, eMesageText, &OtherHeaders); /* Follow-up sanity checks... */ /* If there's no timestamp on this message, set it to now. */ if (CM_IsEmpty(msg, eTimestamp)) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); } /* If a W (references, or rather, Wefewences) field is present, we * have to convert it from RFC822 format to Citadel format. */ if (!CM_IsEmpty(msg, eWeferences)) { /// todo: API! convert_references_to_wefewences(msg->cm_fields[eWeferences]); } return msg; }
/* * convert_field() is a helper function for convert_internet_message(). * Given start/end positions for an rfc822 field, it converts it to a Citadel * field if it wants to, and unfolds it if necessary. * * Returns 1 if the field was converted and inserted into the Citadel message * structure, implying that the source field should be removed from the * message text. */ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) { char *key, *value, *valueend; long len; const char *pos; int i; const char *colonpos = NULL; int processed = 0; char user[1024]; char node[1024]; char name[1024]; char addr[1024]; time_t parsed_date; long valuelen; for (pos = end; pos >= beg; pos--) { if (*pos == ':') colonpos = pos; } if (colonpos == NULL) return(0); /* no colon? not a valid header line */ len = end - beg; key = malloc(len + 2); memcpy(key, beg, len + 1); key[len] = '\0'; valueend = key + len; * ( key + (colonpos - beg) ) = '\0'; value = &key[(colonpos - beg) + 1]; /* printf("Header: [%s]\nValue: [%s]\n", key, value); */ unfold_rfc822_field(&value, &valueend); valuelen = valueend - value + 1; /* printf("UnfoldedValue: [%s]\n", value); */ /* * Here's the big rfc822-to-citadel loop. */ /* Date/time is converted into a unix timestamp. If the conversion * fails, we replace it with the time the message arrived locally. */ if (!strcasecmp(key, "Date")) { parsed_date = parsedate(value); if (parsed_date < 0L) parsed_date = time(NULL); if (CM_IsEmpty(msg, eTimestamp)) CM_SetFieldLONG(msg, eTimestamp, parsed_date); processed = 1; } else if (!strcasecmp(key, "From")) { process_rfc822_addr(value, user, node, name); syslog(LOG_DEBUG, "Converted to <%s@%s> (%s)\n", user, node, name); snprintf(addr, sizeof(addr), "%s@%s", user, node); if (CM_IsEmpty(msg, eAuthor) && !IsEmptyStr(name)) CM_SetField(msg, eAuthor, name, strlen(name)); if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr)) CM_SetField(msg, erFc822Addr, addr, strlen(addr)); processed = 1; } else if (!strcasecmp(key, "Subject")) { if (CM_IsEmpty(msg, eMsgSubject)) CM_SetField(msg, eMsgSubject, value, valuelen); processed = 1; } else if (!strcasecmp(key, "List-ID")) { if (CM_IsEmpty(msg, eListID)) CM_SetField(msg, eListID, value, valuelen); processed = 1; } else if (!strcasecmp(key, "To")) { if (CM_IsEmpty(msg, eRecipient)) CM_SetField(msg, eRecipient, value, valuelen); processed = 1; } else if (!strcasecmp(key, "CC")) { if (CM_IsEmpty(msg, eCarbonCopY)) CM_SetField(msg, eCarbonCopY, value, valuelen); processed = 1; } else if (!strcasecmp(key, "Message-ID")) { if (!CM_IsEmpty(msg, emessageId)) { syslog(LOG_WARNING, "duplicate message id\n"); } else { char *pValue; long pValueLen; pValue = value; pValueLen = valuelen; /* Strip angle brackets */ while (haschar(pValue, '<') > 0) { pValue ++; pValueLen --; } for (i = 0; i <= pValueLen; ++i) if (pValue[i] == '>') { pValueLen = i; break; } CM_SetField(msg, emessageId, pValue, pValueLen); } processed = 1; } else if (!strcasecmp(key, "Return-Path")) { if (CM_IsEmpty(msg, eMessagePath)) CM_SetField(msg, eMessagePath, value, valuelen); processed = 1; } else if (!strcasecmp(key, "Envelope-To")) { if (CM_IsEmpty(msg, eenVelopeTo)) CM_SetField(msg, eenVelopeTo, value, valuelen); processed = 1; } else if (!strcasecmp(key, "References")) { CM_SetField(msg, eWeferences, value, valuelen); processed = 1; } else if (!strcasecmp(key, "Reply-To")) { CM_SetField(msg, eReplyTo, value, valuelen); processed = 1; } else if (!strcasecmp(key, "In-reply-to")) { if (CM_IsEmpty(msg, eWeferences)) /* References: supersedes In-reply-to: */ CM_SetField(msg, eWeferences, value, valuelen); processed = 1; } /* Clean up and move on. */ free(key); /* Don't free 'value', it's actually the same buffer */ return processed; }