validate_recipients() remove extra copy of recipients
[citadel.git] / citadel / server / internet_addressing.c
index 7b8c2963aead5099e1f95127e6cc0a83b2d02956..0bc7507043ca955a99fbfe271a076cf35e294641 100644 (file)
@@ -1,11 +1,9 @@
 // This file contains functions which handle the mapping of Internet addresses
 // to users on the Citadel system.
 //
-// Copyright (c) 1987-2022 by the citadel.org team
+// Copyright (c) 1987-2024 by the citadel.org team
 //
-// This program is open source software.  Use, duplication, or disclosure
-// is subject to the terms of the GNU General Public License, version 3.
-// The program is distributed without any warranty, expressed or implied.
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License version 3.
 
 #include "sysdep.h"
 #include <stdlib.h>
@@ -22,7 +20,7 @@
 #include <string.h>
 #include <limits.h>
 #include <libcitadel.h>
-#include "citadel.h"
+#include "citadel_defs.h"
 #include "server.h"
 #include "sysdep_decls.h"
 #include "citserver.h"
 #include "parsedate.h"
 #include "database.h"
 #include "ctdl_module.h"
-#ifdef HAVE_ICONV
-#include <iconv.h>
-
-// This is the non-define version in case it is needed for debugging
-#if 0
-inline void FindNextEnd (char *bptr, char *end)
-{
-       /* Find the next ?Q? */
-       end = strchr(bptr + 2, '?');
-       if (end == NULL) return NULL;
-       if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
-           (*(end + 2) == '?')) {
-               /* skip on to the end of the cluster, the next ?= */
-               end = strstr(end + 3, "?=");
-       }
-       else
-               /* sort of half valid encoding, try to find an end. */
-               end = strstr(bptr, "?=");
-}
-#endif
-
-#define FindNextEnd(bptr, end) { \
-       end = strchr(bptr + 2, '?'); \
-       if (end != NULL) { \
-               if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && (*(end + 2) == '?')) { \
-                       end = strstr(end + 3, "?="); \
-               } else end = strstr(bptr, "?="); \
-       } \
-}
-
-// Handle subjects with RFC2047 encoding such as:
-// =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
-void utf8ify_rfc822_string(char *buf) {
-       char *start, *end, *next, *nextend, *ptr;
-       char newbuf[1024];
-       char charset[128];
-       char encoding[16];
-       char istr[1024];
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                     // Buffer of characters to be converted
-       char *obuf;                     // Buffer for converted characters
-       size_t ibuflen;                 // Length of input buffer
-       size_t obuflen;                 // Length of output buffer
-       char *isav;                     // Saved pointer to input buffer
-       char *osav;                     // Saved pointer to output buffer
-       int passes = 0;
-       int i, len, delta;
-       int illegal_non_rfc2047_encoding = 0;
-
-       // Sometimes, badly formed messages contain strings which were simply
-       // written out directly in some foreign character set instead of
-       // using RFC2047 encoding.  This is illegal but we will attempt to
-       // handle it anyway by converting from a user-specified default
-       // charset to UTF-8 if we see any nonprintable characters.
-       len = strlen(buf);
-       for (i=0; i<len; ++i) {
-               if ((buf[i] < 32) || (buf[i] > 126)) {
-                       illegal_non_rfc2047_encoding = 1;
-                       i = len;        // take a shortcut, it won't be more than one.
-               }
-       }
-       if (illegal_non_rfc2047_encoding) {
-               const char *default_header_charset = "iso-8859-1";
-               if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) {
-                       ctdl_iconv_open("UTF-8", default_header_charset, &ic);
-                       if (ic != (iconv_t)(-1) ) {
-                               ibuf = malloc(1024);
-                               isav = ibuf;
-                               safestrncpy(ibuf, buf, 1024);
-                               ibuflen = strlen(ibuf);
-                               obuflen = 1024;
-                               obuf = (char *) malloc(obuflen);
-                               osav = obuf;
-                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                               osav[1024-obuflen] = 0;
-                               strcpy(buf, osav);
-                               free(osav);
-                               iconv_close(ic);
-                               free(isav);
-                       }
-               }
-       }
-
-       // pre evaluate the first pair
-       nextend = end = NULL;
-       len = strlen(buf);
-       start = strstr(buf, "=?");
-       if (start != NULL) 
-               FindNextEnd (start, end);
-
-       while ((start != NULL) && (end != NULL)) {
-               next = strstr(end, "=?");
-               if (next != NULL)
-                       FindNextEnd(next, nextend);
-               if (nextend == NULL)
-                       next = NULL;
-
-               // did we find two partitions
-               if ((next != NULL) && ((next - end) > 2)) {
-                       ptr = end + 2;
-                       while ((ptr < next) && 
-                              (isspace(*ptr) ||
-                               (*ptr == '\r') ||
-                               (*ptr == '\n') || 
-                               (*ptr == '\t')))
-                               ptr ++;
-                       // did we find a gab just filled with blanks?
-                       if (ptr == next) {
-                               memmove(end + 2, next, len - (next - start));
-
-                               // now terminate the gab at the end
-                               delta = (next - end) - 2;
-                               len -= delta;
-                               buf[len] = '\0';
-
-                               // move next to its new location.
-                               next -= delta;
-                               nextend -= delta;
-                       }
-               }
-               // our next-pair is our new first pair now.
-               start = next;
-               end = nextend;
-       }
-
-       // Now we handle foreign character sets properly encoded in RFC2047 format.
-       start = strstr(buf, "=?");
-       FindNextEnd((start != NULL)? start : buf, end);
-       while (start != NULL && end != NULL && end > start) {
-               extract_token(charset, start, 1, '?', sizeof charset);
-               extract_token(encoding, start, 2, '?', sizeof encoding);
-               extract_token(istr, start, 3, '?', sizeof istr);
-
-               ibuf = malloc(1024);
-               isav = ibuf;
-               if (!strcasecmp(encoding, "B")) {       // base64
-                       ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
-               }
-               else if (!strcasecmp(encoding, "Q")) {  // quoted-printable
-                       size_t len;
-                       unsigned long pos;
-                       
-                       len = strlen(istr);
-                       pos = 0;
-                       while (pos < len) {
-                               if (istr[pos] == '_') istr[pos] = ' ';
-                               pos++;
-                       }
-                       ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len);
-               }
-               else {
-                       strcpy(ibuf, istr);             // unknown encoding
-                       ibuflen = strlen(istr);
-               }
-
-               ctdl_iconv_open("UTF-8", charset, &ic);
-               if (ic != (iconv_t)(-1) ) {
-                       obuflen = 1024;
-                       obuf = (char *) malloc(obuflen);
-                       osav = obuf;
-                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                       osav[1024-obuflen] = 0;
-
-                       end = start;
-                       end++;
-                       strcpy(start, "");
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       strcpy(end, &end[1]);
-
-                       snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
-                       strcpy(buf, newbuf);
-                       free(osav);
-                       iconv_close(ic);
-               }
-               else {
-                       end = start;
-                       end++;
-                       strcpy(start, "");
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       strcpy(end, &end[1]);
-
-                       snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
-                       strcpy(buf, newbuf);
-               }
-
-               free(isav);
-
-               // Since spammers will go to all sorts of absurd lengths to get their
-               // messages through, there are LOTS of corrupt headers out there.
-               // So, prevent a really badly formed RFC2047 header from throwing
-               // this function into an infinite loop.
-               ++passes;
-               if (passes > 20) return;
-
-               start = strstr(buf, "=?");
-               FindNextEnd((start != NULL)? start : buf, end);
-       }
-
-}
-#else
-inline void utf8ify_rfc822_string(char *a){};
-
-#endif
 
 
 char *inetcfg = NULL;
@@ -383,8 +172,8 @@ int expand_aliases(char *name, char *aliases) {
                        if (bar) {
                                bar[0] = 0;
                                ++bar;
-                               striplt(aaa);
-                               striplt(bar);
+                               string_trim(aaa);
+                               string_trim(bar);
                                if ( (!IsEmptyStr(aaa)) && (!strcasecmp(name, aaa)) ) {
                                        syslog(LOG_DEBUG, "internet_addressing: global alias <%s> to <%s>", name, bar);
                                        strcpy(name, bar);
@@ -400,7 +189,7 @@ int expand_aliases(char *name, char *aliases) {
        safestrncpy(original_name, name, sizeof original_name);
 
        // should these checks still be here, or maybe move them to split_recps() ?
-       striplt(name);
+       string_trim(name);
        remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
        stripallbut(name, '<', '>');
 
@@ -486,7 +275,7 @@ Array *split_recps(char *addresses, Array *append_to) {
        for (i=0; i<num_addresses; ++i) {
                char this_address[256];
                extract_token(this_address, a, i, ',', sizeof this_address);
-               striplt(this_address);                          // strip leading and trailing whitespace
+               string_trim(this_address);                              // strip leading and trailing whitespace
                stripout(this_address, '(', ')');               // remove any portion in parentheses
                stripallbut(this_address, '<', '>');            // if angle brackets are present, keep only what is inside them
                if (!IsEmptyStr(this_address)) {
@@ -506,9 +295,8 @@ Array *split_recps(char *addresses, Array *append_to) {
 //
 // Caller needs to free the result using free_recipients()
 //
-struct recptypes *validate_recipients(char *supplied_recipients, const char *RemoteIdentifier, int Flags) {
+struct recptypes *validate_recipients(char *recipients_in, const char *REMOVE_THIS_UNUSED_VARIABLE, int Flags) {
        struct recptypes *ret;
-       char *recipients = NULL;
        char append[SIZ];
        long len;
        int mailtype;
@@ -524,14 +312,7 @@ struct recptypes *validate_recipients(char *supplied_recipients, const char *Rem
        if (ret == NULL) return(NULL);
        memset(ret, 0, sizeof(struct recptypes));                                       // set all values to null/zero
 
-       if (supplied_recipients == NULL) {
-               recipients = strdup("");
-       }
-       else {
-               recipients = strdup(supplied_recipients);
-       }
-
-       len = strlen(recipients) + 1024;                                                // allocate memory
+       len = strlen(recipients_in) + 1024;                                             // allocate memory
        ret->errormsg = malloc(len);
        ret->recp_local = malloc(len);
        ret->recp_internet = malloc(len);
@@ -547,7 +328,7 @@ struct recptypes *validate_recipients(char *supplied_recipients, const char *Rem
        ret->display_recp[0] = 0;
        ret->recptypes_magic = RECPTYPES_MAGIC;
 
-       Array *recp_array = split_recps(supplied_recipients, NULL);
+       Array *recp_array = split_recps(recipients_in, NULL);
 
        char *aliases = CtdlGetSysConfig(GLOBAL_ALIASES);                               // First hit the Global Alias Table
 
@@ -638,7 +419,8 @@ struct recptypes *validate_recipients(char *supplied_recipients, const char *Rem
                        }
 
                        // This handles the most common case, which is mail to a user's inbox.
-                       else if (CtdlGetUser(&tempUS, this_recp) == 0) {
+                       // (the next line depends on left-to-right evaluation)
+                       else if ((CtdlGetUser(&tempUS, this_recp) == 0) && (tempUS.usernum > 0)) {
                                ++ret->num_local;
                                strcpy(this_recp, tempUS.fullname);
                                if (!IsEmptyStr(ret->recp_local)) {
@@ -718,7 +500,6 @@ struct recptypes *validate_recipients(char *supplied_recipients, const char *Rem
                ret->num_local, ret->num_room, ret->num_internet, ret->num_error
        );
 
-       free(recipients);
        if (recp_array) {
                array_free(recp_array);
        }
@@ -736,7 +517,7 @@ void free_recipients(struct recptypes *valid) {
 
        if (valid->recptypes_magic != RECPTYPES_MAGIC) {
                syslog(LOG_ERR, "internet_addressing: attempt to call free_recipients() on some other data type!");
-               abort();
+               exit(CTDLEXIT_BAD_MAGIC);
        }
 
        if (valid->errormsg != NULL)            free(valid->errormsg);
@@ -922,9 +703,6 @@ void unfold_rfc822_field(char **field, char **FieldEnd)
 
 
 // Split an RFC822-style address into userid, host, and full name
-//
-// Note: This still handles obsolete address syntaxes such as user%node@node and ...node!user
-//       We should probably remove that.
 void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) {
        int a;
 
@@ -938,20 +716,12 @@ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name)
        strcpy(name, rfc822);
        stripout(name, '<', '>');
 
-       // strip anything to the left of a bang
-       while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0))
-               strcpy(name, &name[1]);
-
-       // and anything to the right of a @ or %
+       // and anything to the right of a @
        for (a = 0; name[a] != '\0'; ++a) {
                if (name[a] == '@') {
                        name[a] = 0;
                        break;
                }
-               if (name[a] == '%') {
-                       name[a] = 0;
-                       break;
-               }
        }
 
        // but if there are parentheses, that changes the rules...
@@ -984,23 +754,14 @@ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name)
                stripallbut(user, '<', '>');
        }
 
-       // strip anything to the left of a bang
-       while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0))
-               strcpy(user, &user[1]);
-
-       // and anything to the right of a @ or %
+       // and anything to the right of a @
        for (a = 0; user[a] != '\0'; ++a) {
                if (user[a] == '@') {
                        user[a] = 0;
                        break;
                }
-               if (user[a] == '%') {
-                       user[a] = 0;
-                       break;
-               }
        }
 
-
        // extract node name
        strcpy(node, rfc822);
 
@@ -1013,39 +774,20 @@ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name)
        }
 
        // If no node specified, tack ours on instead
-       if (
-               (haschar(node, '@')==0)
-               && (haschar(node, '%')==0)
-               && (haschar(node, '!')==0)
-       ) {
+       if (haschar(node, '@') == 0) {
                strcpy(node, CtdlGetConfigStr("c_nodename"));
        }
        else {
-
                // strip anything to the left of a @
-               while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0))
-                       strcpy(node, &node[1]);
-       
-               // strip anything to the left of a %
-               while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0))
+               while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0)) {
                        strcpy(node, &node[1]);
-       
-               // reduce multiple system bang paths to node!user
-               while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1))
-                       strcpy(node, &node[1]);
-       
-               // now get rid of the user portion of a node!user string
-               for (a = 0; node[a] != '\0'; ++a)
-                       if (node[a] == '!') {
-                               node[a] = 0;
-                               break;
-                       }
+               }
        }
 
        // strip leading and trailing spaces in all strings
-       striplt(user);
-       striplt(node);
-       striplt(name);
+       string_trim(user);
+       string_trim(node);
+       string_trim(name);
 
        // If we processed a string that had the address in angle brackets
        // but no name outside the brackets, we now have an empty name.  In
@@ -1081,7 +823,7 @@ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
                if (*pos == ':') colonpos = pos;
        }
 
-       if (colonpos == NULL) return(0);        /* no colon? not a valid header line */
+       if (colonpos == NULL) return(0);        // no colon? not a valid header line
 
        len = end - beg;
        key = malloc(len + 2);
@@ -1113,35 +855,35 @@ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
                syslog(LOG_DEBUG, "internet_addressing: converted to <%s@%s> (%s)", user, node, name);
                snprintf(addr, sizeof(addr), "%s@%s", user, node);
                if (CM_IsEmpty(msg, eAuthor) && !IsEmptyStr(name)) {
-                       CM_SetField(msg, eAuthor, name, -1);
+                       CM_SetField(msg, eAuthor, name);
                }
                if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr)) {
-                       CM_SetField(msg, erFc822Addr, addr, -1);
+                       CM_SetField(msg, erFc822Addr, addr);
                }
                processed = 1;
        }
 
        else if (!strcasecmp(key, "Subject")) {
                if (CM_IsEmpty(msg, eMsgSubject))
-                       CM_SetField(msg, eMsgSubject, value, valuelen);
+                       CM_SetField(msg, eMsgSubject, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "List-ID")) {
                if (CM_IsEmpty(msg, eListID))
-                       CM_SetField(msg, eListID, value, valuelen);
+                       CM_SetField(msg, eListID, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "To")) {
                if (CM_IsEmpty(msg, eRecipient))
-                       CM_SetField(msg, eRecipient, value, valuelen);
+                       CM_SetField(msg, eRecipient, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "CC")) {
                if (CM_IsEmpty(msg, eCarbonCopY))
-                       CM_SetField(msg, eCarbonCopY, value, valuelen);
+                       CM_SetField(msg, eCarbonCopY, value);
                processed = 1;
        }
 
@@ -1167,7 +909,7 @@ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
                                        break;
                                }
 
-                       CM_SetField(msg, emessageId, pValue, pValueLen);
+                       CM_SetField(msg, emessageId, pValue);
                }
 
                processed = 1;
@@ -1175,29 +917,29 @@ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
 
        else if (!strcasecmp(key, "Return-Path")) {
                if (CM_IsEmpty(msg, eMessagePath))
-                       CM_SetField(msg, eMessagePath, value, valuelen);
+                       CM_SetField(msg, eMessagePath, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "Envelope-To")) {
                if (CM_IsEmpty(msg, eenVelopeTo))
-                       CM_SetField(msg, eenVelopeTo, value, valuelen);
+                       CM_SetField(msg, eenVelopeTo, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "References")) {
-               CM_SetField(msg, eWeferences, value, valuelen);
+               CM_SetField(msg, eWeferences, value);
                processed = 1;
        }
 
        else if (!strcasecmp(key, "Reply-To")) {
-               CM_SetField(msg, eReplyTo, value, valuelen);
+               CM_SetField(msg, eReplyTo, value);
                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);
+                       CM_SetField(msg, eWeferences, value);
                processed = 1;
        }
 
@@ -1254,8 +996,7 @@ struct CtdlMessage *convert_internet_message(char *rfc822) {
 }
 
 
-struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
-{
+struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822) {
        struct CtdlMessage *msg;
        const char *pos, *beg, *end, *totalend;
        int done, alldone = 0;
@@ -1277,31 +1018,22 @@ struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
 
        while (!alldone) {
 
-               /* Locate beginning and end of field, keeping in mind that
-                * some fields might be multiline
-                */
+               // 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) ) 
-               {
+               while ((end < totalend) && (end == beg) && (done == 0) ) {
 
-                       if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) )
-                       {
+                       if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) ) {
                                end = pos;
                        }
 
-                       /* done with headers? */
-                       if ((*pos=='\n') &&
-                           ( (*(pos+1)=='\n') ||
-                             (*(pos+1)=='\r')) ) 
-                       {
+                       // done with headers?
+                       if ((*pos=='\n') && ( (*(pos+1)=='\n') || (*(pos+1)=='\r')) ) {
                                alldone = 1;
                        }
 
-                       if (pos >= (totalend - 1) )
-                       {
+                       if (pos >= (totalend - 1) ) {
                                end = pos;
                                done = 1;
                        }
@@ -1310,16 +1042,16 @@ struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
 
                }
 
-               /* At this point we have a field.  Are we interested in it? */
+               // 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 */
+               // 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 we've hit the end of the message, bail out
                if (pos >= totalend)
                        alldone = 1;
        }
@@ -1327,18 +1059,17 @@ struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
        if (pos < totalend)
                StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0);
        FreeStrBuf(rfc822);
-       CM_SetAsFieldSB(msg, eMesageText, &OtherHeaders);
+       CM_SetAsFieldSB(msg, eMessageText, &OtherHeaders);
 
-       /* Follow-up sanity checks... */
+       // Follow-up sanity checks...
 
-       /* If there's no timestamp on this message, set it to now. */
+       // 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 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]);
@@ -1348,13 +1079,11 @@ struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
 }
 
 
-/*
- * Look for a particular header field in an RFC822 message text.  If the
- * requested field is found, it is unfolded (if necessary) and returned to
- * the caller.  The field name is stripped out, leaving only its contents.
- * The caller is responsible for freeing the returned buffer.  If the requested
- * field is not present, or anything else goes wrong, it returns NULL.
- */
+// Look for a particular header field in an RFC822 message text.  If the
+// requested field is found, it is unfolded (if necessary) and returned to
+// the caller.  The field name is stripped out, leaving only its contents.
+// The caller is responsible for freeing the returned buffer.  If the requested
+// field is not present, or anything else goes wrong, it returns NULL.
 char *rfc822_fetch_field(const char *rfc822, const char *fieldname) {
        char *fieldbuf = NULL;
        const char *end_of_headers;
@@ -1363,13 +1092,13 @@ char *rfc822_fetch_field(const char *rfc822, const char *fieldname) {
        char *cont;
        char fieldhdr[SIZ];
 
-       /* Should never happen, but sometimes we get stupid */
+       // Should never happen, but sometimes we get stupid
        if (rfc822 == NULL) return(NULL);
        if (fieldname == NULL) return(NULL);
 
        snprintf(fieldhdr, sizeof fieldhdr, "%s:", fieldname);
 
-       /* Locate the end of the headers, so we don't run past that point */
+       // Locate the end of the headers, so we don't run past that point
        end_of_headers = cbmstrcasestr(rfc822, "\n\r\n");
        if (end_of_headers == NULL) {
                end_of_headers = cbmstrcasestr(rfc822, "\n\n");
@@ -1389,24 +1118,22 @@ char *rfc822_fetch_field(const char *rfc822, const char *fieldname) {
                strcat(fieldbuf, " ");
                cont = &fieldbuf[strlen(fieldbuf)];
                ptr = cmemreadline(ptr, cont, SIZ-strlen(fieldbuf) );
-               striplt(cont);
+               string_trim(cont);
        }
 
        strcpy(fieldbuf, &fieldbuf[strlen(fieldhdr)]);
-       striplt(fieldbuf);
+       string_trim(fieldbuf);
 
        return(fieldbuf);
 }
 
 
-/*****************************************************************************
- *                      DIRECTORY MANAGEMENT FUNCTIONS                       *
- *****************************************************************************/
+//     *****************************************************************************
+//                          DIRECTORY MANAGEMENT FUNCTIONS                       
+//     *****************************************************************************
 
-/*
- * Generate the index key for an Internet e-mail address to be looked up
- * in the database.
- */
+// Generate the index key for an Internet e-mail address to be looked up
+// in the database.
 void directory_key(char *key, char *addr) {
        int i;
        int keylen = 0;
@@ -1417,20 +1144,17 @@ void directory_key(char *key, char *addr) {
                }
        }
        key[keylen++] = 0;
-
        syslog(LOG_DEBUG, "internet_addressing: directory key is <%s>", key);
 }
 
 
-/*
- * Return nonzero if the supplied address is in one of "our" domains
- */
+// Return nonzero if the supplied address is in one of "our" domains
 int IsDirectory(char *addr, int allow_masq_domains) {
        char domain[256];
        int h;
 
        extract_token(domain, addr, 1, '@', sizeof domain);
-       striplt(domain);
+       string_trim(domain);
 
        h = CtdlHostAlias(domain);
 
@@ -1446,9 +1170,7 @@ int IsDirectory(char *addr, int allow_masq_domains) {
 }
 
 
-/*
- * Add an Internet e-mail address to the directory for a user
- */
+// Add an Internet e-mail address to the directory for a user
 int CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr) {
        char key[SIZ];
 
@@ -1462,12 +1184,10 @@ int CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr) {
 }
 
 
-/*
- * Delete an Internet e-mail address from the directory.
- *
- * (NOTE: we don't actually use or need the citadel_addr variable; it's merely
- * here because the callback API expects to be able to send it.)
- */
+// Delete an Internet e-mail address from the directory.
+//
+// (NOTE: we don't actually use or need the citadel_addr variable; it's merely
+// here because the callback API expects to be able to send it.)
 int CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr) {
        char key[SIZ];
        
@@ -1477,33 +1197,30 @@ int CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr) {
 }
 
 
-/*
- * Look up an Internet e-mail address in the directory.
- * On success: returns 0, and Citadel address stored in 'target'
- * On failure: returns nonzero
- */
+// Look up an Internet e-mail address in the directory.
+// On success: returns 0, and Citadel address stored in 'target'
+// On failure: returns nonzero
 int CtdlDirectoryLookup(char *target, char *internet_addr, size_t targbuflen) {
-       struct cdbdata *cdbrec;
+       struct cdbdata cdbrec;
        char key[SIZ];
 
-       /* Dump it in there unchanged, just for kicks */
+       // Dump it in there unchanged, just for kicks
        if (target != NULL) {
                safestrncpy(target, internet_addr, targbuflen);
        }
 
-       /* Only do lookups for addresses with hostnames in them */
+       // Only do lookups for addresses with hostnames in them
        if (num_tokens(internet_addr, '@') != 2) return(-1);
 
-       /* Only do lookups for domains in the directory */
+       // Only do lookups for domains in the directory
        if (IsDirectory(internet_addr, 0) == 0) return(-1);
 
        directory_key(key, internet_addr);
-       cdbrec = cdb_fetch(CDB_DIRECTORY, key, strlen(key) );
-       if (cdbrec != NULL) {
+       cdbrec = cdb_fetch(CDB_DIRECTORY, key, strlen(key));
+       if (cdbrec.ptr != NULL) {
                if (target != NULL) {
-                       safestrncpy(target, cdbrec->ptr, targbuflen);
+                       safestrncpy(target, cdbrec.ptr, targbuflen);
                }
-               cdb_free(cdbrec);
                return(0);
        }
 
@@ -1511,10 +1228,8 @@ int CtdlDirectoryLookup(char *target, char *internet_addr, size_t targbuflen) {
 }
 
 
-/*
- * Harvest any email addresses that someone might want to have in their
- * "collected addresses" book.
- */
+// Harvest any email addresses that someone might want to have in their
+// "collected addresses" book.
 char *harvest_collected_addresses(struct CtdlMessage *msg) {
        char *coll = NULL;
        char addr[256];
@@ -1548,7 +1263,7 @@ char *harvest_collected_addresses(struct CtdlMessage *msg) {
 
        if (coll == NULL) return(NULL);
 
-       /* Scan both the R (To) and Y (CC) fields */
+       // Scan both the R (To) and Y (CC) fields
        for (i = 0; i < 2; ++i) {
                if (i == 0) field = eRecipient;
                if (i == 1) field = eCarbonCopY;
@@ -1556,8 +1271,9 @@ char *harvest_collected_addresses(struct CtdlMessage *msg) {
                if (!CM_IsEmpty(msg, field)) {
                        for (j=0; j<num_tokens(msg->cm_fields[field], ','); ++j) {
                                extract_token(addr, msg->cm_fields[field], j, ',', sizeof addr);
-                               if (strstr(addr, "=?") != NULL)
+                               if (strstr(addr, "=?") != NULL) {
                                        utf8ify_rfc822_string(addr);
+                               }
                                process_rfc822_addr(addr, user, node, name);
                                h = CtdlHostAlias(node);
                                if (h != hostalias_localhost) {
@@ -1566,7 +1282,7 @@ char *harvest_collected_addresses(struct CtdlMessage *msg) {
                                        if (!IsEmptyStr(coll)) {
                                                strcat(coll, ",");
                                        }
-                                       striplt(addr);
+                                       string_trim(addr);
                                        strcat(coll, addr);
                                }
                        }
@@ -1581,9 +1297,7 @@ char *harvest_collected_addresses(struct CtdlMessage *msg) {
 }
 
 
-/*
- * Helper function for CtdlRebuildDirectoryIndex()
- */
+// Helper function for CtdlRebuildDirectoryIndex()
 void CtdlRebuildDirectoryIndex_backend(char *username, void *data) {
 
        int j = 0;
@@ -1603,9 +1317,7 @@ void CtdlRebuildDirectoryIndex_backend(char *username, void *data) {
 }
 
 
-/*
- * Initialize the directory database (erasing anything already there)
- */
+// Initialize the directory database (erasing anything already there)
 void CtdlRebuildDirectoryIndex(void) {
        syslog(LOG_INFO, "internet_addressing: rebuilding email address directory index");
        cdb_trunc(CDB_DIRECTORY);
@@ -1643,9 +1355,7 @@ void CtdlSetEmailAddressesForUser(char *requested_user, char *new_emailaddrs) {
 }
 
 
-/*
- * Auto-generate an Internet email address for a user account
- */
+// Auto-generate an Internet email address for a user account
 void AutoGenerateEmailAddressForUser(struct ctdluser *user) {
        char synthetic_email_addr[1024];
        int i, j;