X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Finternet_addressing.c;h=7a9f4b07816810967e106731f4e2781ddb675c36;hb=6ab0a033e52ed0e31feadec942f3d2c4dc8040b1;hp=5cb35968164b728065dd132df93235bd30d334bc;hpb=25dc8d8676bc59a71f66c2c61bc87712c7abed6c;p=citadel.git diff --git a/citadel/internet_addressing.c b/citadel/internet_addressing.c index 5cb359681..7a9f4b078 100644 --- a/citadel/internet_addressing.c +++ b/citadel/internet_addressing.c @@ -5,10 +5,6 @@ * to users on the Citadel system. */ -#ifdef DLL_EXPORT -#define IN_LIBCIT -#endif - #include "sysdep.h" #include #include @@ -34,33 +30,262 @@ #include #include #include +#include #include "citadel.h" #include "server.h" -#include "dynloader.h" #include "sysdep_decls.h" #include "citserver.h" #include "support.h" #include "config.h" -#include "tools.h" #include "msgbase.h" #include "internet_addressing.h" #include "user_ops.h" #include "room_ops.h" #include "parsedate.h" +#include "database.h" +#include "ctdl_module.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif +#ifdef HAVE_ICONV +#include + +#if 0 +/* This is the non-define version in case of s.b. needing to debug */ +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 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; + 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 + + + struct trynamebuf { char buffer1[SIZ]; char buffer2[SIZ]; }; char *inetcfg = NULL; - +struct spamstrings_t *spamstrings = NULL; /* @@ -69,31 +294,40 @@ char *inetcfg = NULL; int CtdlHostAlias(char *fqdn) { int config_lines; int i; - char buf[SIZ]; - char host[SIZ], type[SIZ]; + char buf[256]; + char host[256], type[256]; + int found = 0; + if (fqdn == NULL) return(hostalias_nomatch); + if (IsEmptyStr(fqdn)) return(hostalias_nomatch); + if (!strcasecmp(fqdn, "localhost")) return(hostalias_localhost); if (!strcasecmp(fqdn, config.c_fqdn)) return(hostalias_localhost); if (!strcasecmp(fqdn, config.c_nodename)) return(hostalias_localhost); if (inetcfg == NULL) return(hostalias_nomatch); config_lines = num_tokens(inetcfg, '\n'); for (i=0; iusernum)) { return 0; } - - for (a=0; afullname); ++a) { + len = strlen(matchstring); + for (a=0; !IsEmptyStr(&us->fullname[a]); ++a) { if (!strncasecmp(&us->fullname[a], - matchstring, strlen(matchstring))) { + matchstring, len)) { return 0; } } @@ -132,22 +367,53 @@ int fuzzy_match(struct usersupp *us, char *matchstring) { /* * Unfold a multi-line field into a single line, removing multi-whitespaces */ -void unfold_rfc822_field(char *field) { - int i; +void unfold_rfc822_field(char **field, char **FieldEnd) +{ int quote = 0; + char *pField = *field; + char *sField; + char *pFieldEnd = *FieldEnd; + + while (isspace(*pField)) + pField++; + /* remove leading/trailing whitespace */ + ; - striplt(field); /* remove leading/trailing whitespace */ + while (isspace(*pFieldEnd)) + pFieldEnd --; + *FieldEnd = pFieldEnd; /* convert non-space whitespace to spaces, and remove double blanks */ - for (i=0; i */ strcpy(name, rfc822); stripout(name, '<', '>'); /* strip anything to the left of a bang */ - while ((strlen(name) > 0) && (haschar(name, '!') > 0)) + while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0)) strcpy(name, &name[1]); /* and anything to the right of a @ or % */ @@ -189,7 +457,7 @@ void process_rfc822_addr(char *rfc822, char *user, char *node, char *name) /* but if there are a set of quotes, that supersedes everything */ if (haschar(rfc822, 34) == 2) { strcpy(name, rfc822); - while ((strlen(name) > 0) && (name[0] != 34)) { + while ((!IsEmptyStr(name)) && (name[0] != 34)) { strcpy(&name[0], &name[1]); } strcpy(&name[0], &name[1]); @@ -209,7 +477,7 @@ void process_rfc822_addr(char *rfc822, char *user, char *node, char *name) } /* strip anything to the left of a bang */ - while ((strlen(user) > 0) && (haschar(user, '!') > 0)) + while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0)) strcpy(user, &user[1]); /* and anything to the right of a @ or % */ @@ -244,15 +512,15 @@ void process_rfc822_addr(char *rfc822, char *user, char *node, char *name) else { /* strip anything to the left of a @ */ - while ((strlen(node) > 0) && (haschar(node, '@') > 0)) + while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0)) strcpy(node, &node[1]); /* strip anything to the left of a % */ - while ((strlen(node) > 0) && (haschar(node, '%') > 0)) + while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0)) strcpy(node, &node[1]); /* reduce multiple system bang paths to node!user */ - while ((strlen(node) > 0) && (haschar(node, '!') > 1)) + while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1)) strcpy(node, &node[1]); /* now get rid of the user portion of a node!user string */ @@ -265,122 +533,18 @@ void process_rfc822_addr(char *rfc822, char *user, char *node, char *name) striplt(user); striplt(node); striplt(name); -} - - -/* - * Back end for convert_internet_address() - * (Compares an internet name [buffer1] and stores in [buffer2] if found) - */ -void try_name(struct usersupp *us, void *data) { - struct passwd *pw; - struct trynamebuf *tnb; - tnb = (struct trynamebuf *)data; - - if (!strncasecmp(tnb->buffer1, "cit", 3)) - if (atol(&tnb->buffer1[3]) == us->usernum) - strcpy(tnb->buffer2, us->fullname); - - if (!collapsed_strcmp(tnb->buffer1, us->fullname)) - strcpy(tnb->buffer2, us->fullname); - - if (us->uid != BBSUID) { - pw = getpwuid(us->uid); - if (pw != NULL) { - if (!strcasecmp(tnb->buffer1, pw->pw_name)) { - strcpy(tnb->buffer2, us->fullname); - } - } - } -} - - -/* - * Convert an Internet email address to a Citadel user/host combination - */ -int convert_internet_address(char *destuser, char *desthost, char *source) -{ - char user[SIZ]; - char node[SIZ]; - char name[SIZ]; - struct quickroom qrbuf; - int i; - int hostalias; - struct trynamebuf tnb; - char buf[SIZ]; - int passes = 0; - char sourcealias[1024]; - - safestrncpy(sourcealias, source, sizeof(sourcealias) ); - alias(sourcealias); - -REALIAS: - /* Split it up */ - process_rfc822_addr(sourcealias, user, node, name); - lprintf(9, "process_rfc822_addr() converted to <%s@%s> (%s)\n", - user, node, name); - - /* Map the FQDN to a Citadel node 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 + * this case, use the user portion of the address as the name. */ - hostalias = CtdlHostAlias(node); - switch(hostalias) { - case hostalias_localhost: - strcpy(node, config.c_nodename); - break; - - case hostalias_gatewaydomain: - extract_token(buf, node, 0, '.'); - safestrncpy(node, buf, sizeof buf); + if ((IsEmptyStr(name)) && (!IsEmptyStr(user))) { + strcpy(name, user); } - - /* Now try to resolve the name - * FIXME ... do the multiple-addresses thing - */ - if (!strcasecmp(node, config.c_nodename)) { - - - /* First, see if we hit an alias. Don't do this more than - * a few times, in case we accidentally hit an alias loop - */ - strcpy(sourcealias, user); - alias(user); - if ( (strcasecmp(user, sourcealias)) && (++passes < 3) ) - goto REALIAS; - - /* Try all local rooms */ - if (!strncasecmp(user, "room_", 5)) { - strcpy(name, &user[5]); - for (i=0; icm_fields['M']; /* M field contains rfc822 text */ - for (i = end; i >= beg; --i) { - if (rfc822[i] == ':') colonpos = i; + for (pos = end; pos >= beg; pos--) { + if (*pos == ':') colonpos = pos; } - if (colonpos < 0) return(0); /* no colon? not a valid header line */ + if (colonpos == NULL) return(0); /* no colon? not a valid header line */ - key = mallok((end - beg) + 2); - safestrncpy(key, &rfc822[beg], (end-beg)+1); - key[colonpos - beg] = 0; + 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]; - unfold_rfc822_field(value); +/* 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. @@ -428,35 +599,53 @@ int convert_field(struct CtdlMessage *msg, int beg, int end) { if (parsed_date < 0L) parsed_date = time(NULL); snprintf(buf, sizeof buf, "%ld", (long)parsed_date ); if (msg->cm_fields['T'] == NULL) - msg->cm_fields['T'] = strdoop(buf); + msg->cm_fields['T'] = strdup(buf); processed = 1; } else if (!strcasecmp(key, "From")) { process_rfc822_addr(value, user, node, name); - lprintf(9, "Converted to <%s@%s> (%s)\n", user, node, name); + CtdlLogPrintf(CTDL_DEBUG, "Converted to <%s@%s> (%s)\n", user, node, name); snprintf(addr, sizeof addr, "%s@%s", user, node); if (msg->cm_fields['A'] == NULL) - msg->cm_fields['A'] = strdoop(name); + msg->cm_fields['A'] = strdup(name); processed = 1; if (msg->cm_fields['F'] == NULL) - msg->cm_fields['F'] = strdoop(addr); + msg->cm_fields['F'] = strdup(addr); processed = 1; } else if (!strcasecmp(key, "Subject")) { if (msg->cm_fields['U'] == NULL) - msg->cm_fields['U'] = strdoop(value); + msg->cm_fields['U'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "List-ID")) { + if (msg->cm_fields['L'] == NULL) + msg->cm_fields['L'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "To")) { + if (msg->cm_fields['R'] == NULL) + msg->cm_fields['R'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "CC")) { + if (msg->cm_fields['Y'] == NULL) + msg->cm_fields['Y'] = strndup(value, valuelen); processed = 1; } else if (!strcasecmp(key, "Message-ID")) { if (msg->cm_fields['I'] != NULL) { - lprintf(5, "duplicate message id\n"); + CtdlLogPrintf(CTDL_WARNING, "duplicate message id\n"); } if (msg->cm_fields['I'] == NULL) { - msg->cm_fields['I'] = strdoop(value); + msg->cm_fields['I'] = strndup(value, valuelen); /* Strip angle brackets */ while (haschar(msg->cm_fields['I'], '<') > 0) { @@ -471,12 +660,76 @@ int convert_field(struct CtdlMessage *msg, int beg, int end) { processed = 1; } + else if (!strcasecmp(key, "Return-Path")) { + if (msg->cm_fields['P'] == NULL) + msg->cm_fields['P'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "Envelope-To")) { + if (msg->cm_fields['V'] == NULL) + msg->cm_fields['V'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "References")) { + if (msg->cm_fields['W'] != NULL) { + free(msg->cm_fields['W']); + } + msg->cm_fields['W'] = strndup(value, valuelen); + processed = 1; + } + + else if (!strcasecmp(key, "In-reply-to")) { + if (msg->cm_fields['W'] == NULL) { /* References: supersedes In-reply-to: */ + msg->cm_fields['W'] = strndup(value, valuelen); + } + processed = 1; + } + + + /* Clean up and move on. */ - phree(key); /* Don't free 'value', it's actually the same buffer */ + free(key); /* Don't free 'value', it's actually the same buffer */ return(processed); } +/* + * Convert RFC822 references format (References) to Citadel references format (Weferences) + */ +void convert_references_to_wefewences(char *str) { + int bracket_nesting = 0; + char *ptr = str; + char *moveptr = NULL; + char ch; + + while(*ptr) { + ch = *ptr; + if (ch == '>') { + --bracket_nesting; + if (bracket_nesting < 0) bracket_nesting = 0; + } + if ((ch == '>') && (bracket_nesting == 0) && (*(ptr+1)) && (ptr>str) ) { + *ptr = '|'; + ++ptr; + } + else if (bracket_nesting > 0) { + ++ptr; + } + else { + moveptr = ptr; + while (*moveptr) { + *moveptr = *(moveptr+1); + ++moveptr; + } + } + if (ch == '<') ++bracket_nesting; + } + +} + + /* * Convert an RFC822 message (headers + body) to a CtdlMessage structure. * NOTE: the supplied buffer becomes part of the CtdlMessage structure, and @@ -485,52 +738,63 @@ int convert_field(struct CtdlMessage *msg, int beg, int end) { * again. */ struct CtdlMessage *convert_internet_message(char *rfc822) { + StrBuf *RFCBuf = NewStrBufPlain(rfc822, -1); + free (rfc822); + return convert_internet_message_buf(&RFCBuf); +} + + +struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822) +{ struct CtdlMessage *msg; - int pos, beg, end, msglen; - int done; + const char *pos, *beg, *end, *totalend; + int done, alldone = 0; char buf[SIZ]; int converted; + StrBuf *OtherHeaders; - msg = mallok(sizeof(struct CtdlMessage)); + 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 */ - msg->cm_fields['M'] = rfc822; - lprintf(9, "Unconverted RFC822 message length = %ld\n", (long)strlen(rfc822)); - pos = 0; + pos = ChrPtr(*rfc822); + totalend = pos + StrLength(*rfc822); done = 0; + OtherHeaders = NewStrBufPlain(NULL, StrLength(*rfc822)); - while (!done) { + while (!alldone) { /* Locate beginning and end of field, keeping in mind that * some fields might be multiline */ - beg = pos; - end = (-1); + end = beg = pos; - msglen = strlen(rfc822); - while ( (end < 0) && (done == 0) ) { + while ((end < totalend) && + (end == beg) && + (done == 0) ) + { - if ((rfc822[pos]=='\n') - && (!isspace(rfc822[pos+1]))) { + if ((*pos=='\n') && + (!isspace(*(pos+1)))) + { end = pos; } /* done with headers? */ - if ( ((rfc822[pos]=='\n') - ||(rfc822[pos]=='\r') ) - && ( (rfc822[pos+1]=='\n') - ||(rfc822[pos+1]=='\r')) ) { - end = pos; - done = 1; + if ((*pos=='\n') && + ( (*(pos+1)=='\n') || + (*(pos+1)=='\r')) ) + { + alldone = 1; } - if (pos >= (msglen-1) ) { + if (pos >= (totalend - 1) ) + { end = pos; done = 1; } @@ -543,25 +807,36 @@ struct CtdlMessage *convert_internet_message(char *rfc822) { converted = convert_field(msg, beg, end); /* Strip the field out of the RFC822 header if we used it */ - if (converted) { - strcpy(&rfc822[beg], &rfc822[pos]); - pos = beg; + 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 > strlen(rfc822)) done = 1; + if (pos >= totalend) + alldone = 1; } + StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0); + if (pos < totalend) + StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0); + FreeStrBuf(rfc822); + msg->cm_fields['M'] = SmashStrBuf(&OtherHeaders); /* Follow-up sanity checks... */ /* If there's no timestamp on this message, set it to now. */ if (msg->cm_fields['T'] == NULL) { snprintf(buf, sizeof buf, "%ld", (long)time(NULL)); - msg->cm_fields['T'] = strdoop(buf); + msg->cm_fields['T'] = strdup(buf); + } + + /* If a W (references, or rather, Wefewences) field is present, we + * have to convert it from RFC822 format to Citadel format. + */ + if (msg->cm_fields['W'] != NULL) { + convert_references_to_wefewences(msg->cm_fields['W']); } - lprintf(9, "RFC822 length remaining after conversion = %ld\n", - (long)strlen(rfc822)); return msg; } @@ -574,61 +849,230 @@ struct CtdlMessage *convert_internet_message(char *rfc822) { * 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(char *rfc822, char *fieldname) { - int pos = 0; - int beg, end; - int done = 0; - int colonpos, i; +char *rfc822_fetch_field(const char *rfc822, const char *fieldname) { char *fieldbuf = NULL; + const char *end_of_headers; + const char *field_start; + const char *ptr; + char *cont; + char fieldhdr[SIZ]; /* Should never happen, but sometimes we get stupid */ if (rfc822 == NULL) return(NULL); if (fieldname == NULL) return(NULL); - while (!done) { + snprintf(fieldhdr, sizeof fieldhdr, "%s:", fieldname); - /* Locate beginning and end of field, keeping in mind that - * some fields might be multiline - */ - beg = pos; - end = (-1); - for (pos=beg; ((pos<=strlen(rfc822))&&(end<0)); ++pos) { - if ((rfc822[pos]=='\n') - && (!isspace(rfc822[pos+1]))) { - end = pos; - } - if ( (rfc822[pos]=='\n') /* done w. headers? */ - && ( (rfc822[pos+1]=='\n') - ||(rfc822[pos+1]=='\r'))) { - end = pos; - done = 1; - } + /* 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"); + } + if (end_of_headers == NULL) return (NULL); + + field_start = cbmstrcasestr(rfc822, fieldhdr); + if (field_start == NULL) return(NULL); + if (field_start > end_of_headers) return(NULL); + + fieldbuf = malloc(SIZ); + strcpy(fieldbuf, ""); + + ptr = field_start; + ptr = cmemreadline(ptr, fieldbuf, SIZ-strlen(fieldbuf) ); + while ( (isspace(ptr[0])) && (ptr < end_of_headers) ) { + strcat(fieldbuf, " "); + cont = &fieldbuf[strlen(fieldbuf)]; + ptr = cmemreadline(ptr, cont, SIZ-strlen(fieldbuf) ); + striplt(cont); + } + + strcpy(fieldbuf, &fieldbuf[strlen(fieldhdr)]); + striplt(fieldbuf); + + return(fieldbuf); +} + + + +/***************************************************************************** + * DIRECTORY MANAGEMENT FUNCTIONS * + *****************************************************************************/ +/* + * 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; + + for (i=0; !IsEmptyStr(&addr[i]); ++i) { + if (!isspace(addr[i])) { + key[keylen++] = tolower(addr[i]); } + } + key[keylen++] = 0; - /* At this point we have a field. Is it The One? */ - if (end > beg) { - fieldbuf = mallok((end-beg)+3); - if (fieldbuf == NULL) return(NULL); - safestrncpy(fieldbuf, &rfc822[beg], (end-beg)+1); - unfold_rfc822_field(fieldbuf); - colonpos = (-1); - for (i = strlen(fieldbuf); i >= 0; --i) { - if (fieldbuf[i] == ':') colonpos = i; - } - if (colonpos > 0) { - fieldbuf[colonpos] = 0; - if (!strcasecmp(fieldbuf, fieldname)) { - strcpy(fieldbuf, &fieldbuf[colonpos+1]); - striplt(fieldbuf); - return(fieldbuf); + CtdlLogPrintf(CTDL_DEBUG, "Directory key is <%s>\n", key); +} + + + +/* Return nonzero if the supplied address is in a domain we keep in + * the directory + */ +int IsDirectory(char *addr, int allow_masq_domains) { + char domain[256]; + int h; + + extract_token(domain, addr, 1, '@', sizeof domain); + striplt(domain); + + h = CtdlHostAlias(domain); + + if ( (h == hostalias_masq) && allow_masq_domains) + return(1); + + if ( (h == hostalias_localhost) || (h == hostalias_directory) ) { + return(1); + } + else { + return(0); + } +} + + +/* + * Initialize the directory database (erasing anything already there) + */ +void CtdlDirectoryInit(void) { + cdb_trunc(CDB_DIRECTORY); +} + + +/* + * Add an Internet e-mail address to the directory for a user + */ +void CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr) { + char key[SIZ]; + + if (IsDirectory(internet_addr, 0) == 0) return; + CtdlLogPrintf(CTDL_DEBUG, "Create directory entry: %s --> %s\n", internet_addr, citadel_addr); + directory_key(key, internet_addr); + cdb_store(CDB_DIRECTORY, key, strlen(key), citadel_addr, strlen(citadel_addr)+1 ); +} + + +/* + * 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.) + */ +void CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr) { + char key[SIZ]; + + CtdlLogPrintf(CTDL_DEBUG, "Delete directory entry: %s --> %s\n", internet_addr, citadel_addr); + directory_key(key, internet_addr); + cdb_delete(CDB_DIRECTORY, key, strlen(key) ); +} + + +/* + * 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; + char key[SIZ]; + + /* Dump it in there unchanged, just for kicks */ + safestrncpy(target, internet_addr, targbuflen); + + /* 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 */ + if (IsDirectory(internet_addr, 0) == 0) return(-1); + + directory_key(key, internet_addr); + cdbrec = cdb_fetch(CDB_DIRECTORY, key, strlen(key) ); + if (cdbrec != NULL) { + safestrncpy(target, cdbrec->ptr, targbuflen); + cdb_free(cdbrec); + return(0); + } + + return(-1); +} + + +/* + * 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]; + char user[256], node[256], name[256]; + int is_harvestable; + int i, j, h; + int field = 0; + + if (msg == NULL) return(NULL); + + is_harvestable = 1; + strcpy(addr, ""); + if (msg->cm_fields['A'] != NULL) { + strcat(addr, msg->cm_fields['A']); + } + if (msg->cm_fields['F'] != NULL) { + strcat(addr, " <"); + strcat(addr, msg->cm_fields['F']); + strcat(addr, ">"); + if (IsDirectory(msg->cm_fields['F'], 0)) { + is_harvestable = 0; + } + } + + if (is_harvestable) { + coll = strdup(addr); + } + else { + coll = strdup(""); + } + + if (coll == NULL) return(NULL); + + /* Scan both the R (To) and Y (CC) fields */ + for (i = 0; i < 2; ++i) { + if (i == 0) field = 'R' ; + if (i == 1) field = 'Y' ; + + if (msg->cm_fields[field] != NULL) { + for (j=0; jcm_fields[field], ','); ++j) { + extract_token(addr, msg->cm_fields[field], j, ',', sizeof addr); + if (strstr(addr, "=?") != NULL) + utf8ify_rfc822_string(addr); + process_rfc822_addr(addr, user, node, name); + h = CtdlHostAlias(node); + if ( (h != hostalias_localhost) && (h != hostalias_directory) ) { + coll = realloc(coll, strlen(coll) + strlen(addr) + 4); + if (coll == NULL) return(NULL); + if (!IsEmptyStr(coll)) { + strcat(coll, ","); + } + striplt(addr); + strcat(coll, addr); } } - phree(fieldbuf); } + } - /* If we've hit the end of the message, bail out */ - if (pos > strlen(rfc822)) done = 1; + if (IsEmptyStr(coll)) { + free(coll); + return(NULL); } - return(NULL); + return(coll); }