Back to index

citadel  8.12
internet_addressing.c
Go to the documentation of this file.
00001 /*
00002  * This file contains functions which handle the mapping of Internet addresses
00003  * to users on the Citadel system.
00004  */
00005 
00006 #include "sysdep.h"
00007 #include <stdlib.h>
00008 #include <unistd.h>
00009 #include <stdio.h>
00010 #include <fcntl.h>
00011 #include <ctype.h>
00012 #include <signal.h>
00013 #include <pwd.h>
00014 #include <errno.h>
00015 #include <sys/types.h>
00016 
00017 #if TIME_WITH_SYS_TIME
00018 # include <sys/time.h>
00019 # include <time.h>
00020 #else
00021 # if HAVE_SYS_TIME_H
00022 #  include <sys/time.h>
00023 # else
00024 #  include <time.h>
00025 # endif
00026 #endif
00027 
00028 #include <sys/wait.h>
00029 #include <string.h>
00030 #include <limits.h>
00031 #include <libcitadel.h>
00032 #include "citadel.h"
00033 #include "server.h"
00034 #include "sysdep_decls.h"
00035 #include "citserver.h"
00036 #include "support.h"
00037 #include "config.h"
00038 #include "msgbase.h"
00039 #include "internet_addressing.h"
00040 #include "user_ops.h"
00041 #include "room_ops.h"
00042 #include "parsedate.h"
00043 #include "database.h"
00044 
00045 #include "ctdl_module.h"
00046 
00047 #ifndef HAVE_SNPRINTF
00048 #include "snprintf.h"
00049 #endif
00050 
00051 
00052 #ifdef HAVE_ICONV
00053 #include <iconv.h>
00054 
00055 #if 0
00056 /* This is the non-define version in case of s.b. needing to debug */
00057 inline void FindNextEnd (char *bptr, char *end)
00058 {
00059        /* Find the next ?Q? */
00060        end = strchr(bptr + 2, '?');
00061        if (end == NULL) return NULL;
00062        if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
00063            (*(end + 2) == '?')) {
00064               /* skip on to the end of the cluster, the next ?= */
00065               end = strstr(end + 3, "?=");
00066        }
00067        else
00068               /* sort of half valid encoding, try to find an end. */
00069               end = strstr(bptr, "?=");
00070 }
00071 #endif
00072 
00073 #define FindNextEnd(bptr, end) { \
00074        end = strchr(bptr + 2, '?'); \
00075        if (end != NULL) { \
00076               if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && (*(end + 2) == '?')) { \
00077                      end = strstr(end + 3, "?="); \
00078               } else end = strstr(bptr, "?="); \
00079        } \
00080 }
00081 
00082 /*
00083  * Handle subjects with RFC2047 encoding such as:
00084  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
00085  */
00086 void utf8ify_rfc822_string(char *buf) {
00087        char *start, *end, *next, *nextend, *ptr;
00088        char newbuf[1024];
00089        char charset[128];
00090        char encoding[16];
00091        char istr[1024];
00092        iconv_t ic = (iconv_t)(-1) ;
00093        char *ibuf;                 
00094        char *obuf;                 
00095        size_t ibuflen;                    
00096        size_t obuflen;                    
00097        char *isav;                 
00098        char *osav;                 
00099        int passes = 0;
00100        int i, len, delta;
00101        int illegal_non_rfc2047_encoding = 0;
00102 
00103        /* Sometimes, badly formed messages contain strings which were simply
00104         *  written out directly in some foreign character set instead of
00105         *  using RFC2047 encoding.  This is illegal but we will attempt to
00106         *  handle it anyway by converting from a user-specified default
00107         *  charset to UTF-8 if we see any nonprintable characters.
00108         */
00109        len = strlen(buf);
00110        for (i=0; i<len; ++i) {
00111               if ((buf[i] < 32) || (buf[i] > 126)) {
00112                      illegal_non_rfc2047_encoding = 1;
00113                      i = len; 
00114               }
00115        }
00116        if (illegal_non_rfc2047_encoding) {
00117               const char *default_header_charset = "iso-8859-1";
00118               if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) {
00119                      ctdl_iconv_open("UTF-8", default_header_charset, &ic);
00120                      if (ic != (iconv_t)(-1) ) {
00121                             ibuf = malloc(1024);
00122                             isav = ibuf;
00123                             safestrncpy(ibuf, buf, 1024);
00124                             ibuflen = strlen(ibuf);
00125                             obuflen = 1024;
00126                             obuf = (char *) malloc(obuflen);
00127                             osav = obuf;
00128                             iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
00129                             osav[1024-obuflen] = 0;
00130                             strcpy(buf, osav);
00131                             free(osav);
00132                             iconv_close(ic);
00133                             free(isav);
00134                      }
00135               }
00136        }
00137 
00138        /* pre evaluate the first pair */
00139        nextend = end = NULL;
00140        len = strlen(buf);
00141        start = strstr(buf, "=?");
00142        if (start != NULL) 
00143               FindNextEnd (start, end);
00144 
00145        while ((start != NULL) && (end != NULL))
00146        {
00147               next = strstr(end, "=?");
00148               if (next != NULL)
00149                      FindNextEnd(next, nextend);
00150               if (nextend == NULL)
00151                      next = NULL;
00152 
00153               /* did we find two partitions */
00154               if ((next != NULL) && 
00155                   ((next - end) > 2))
00156               {
00157                      ptr = end + 2;
00158                      while ((ptr < next) && 
00159                             (isspace(*ptr) ||
00160                             (*ptr == '\r') ||
00161                             (*ptr == '\n') || 
00162                             (*ptr == '\t')))
00163                             ptr ++;
00164                      /* did we find a gab just filled with blanks? */
00165                      if (ptr == next)
00166                      {
00167                             memmove (end + 2,
00168                                     next,
00169                                     len - (next - start));
00170 
00171                             /* now terminate the gab at the end */
00172                             delta = (next - end) - 2;
00173                             len -= delta;
00174                             buf[len] = '\0';
00175 
00176                             /* move next to its new location. */
00177                             next -= delta;
00178                             nextend -= delta;
00179                      }
00180               }
00181               /* our next-pair is our new first pair now. */
00182               start = next;
00183               end = nextend;
00184        }
00185 
00186        /* Now we handle foreign character sets properly encoded
00187         * in RFC2047 format.
00188         */
00189        start = strstr(buf, "=?");
00190        FindNextEnd((start != NULL)? start : buf, end);
00191        while (start != NULL && end != NULL && end > start)
00192        {
00193               extract_token(charset, start, 1, '?', sizeof charset);
00194               extract_token(encoding, start, 2, '?', sizeof encoding);
00195               extract_token(istr, start, 3, '?', sizeof istr);
00196 
00197               ibuf = malloc(1024);
00198               isav = ibuf;
00199               if (!strcasecmp(encoding, "B")) {  
00200                      ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
00201               }
00202               else if (!strcasecmp(encoding, "Q")) {    
00203                      size_t len;
00204                      long pos;
00205                      
00206                      len = strlen(istr);
00207                      pos = 0;
00208                      while (pos < len)
00209                      {
00210                             if (istr[pos] == '_') istr[pos] = ' ';
00211                             pos++;
00212                      }
00213 
00214                      ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len);
00215               }
00216               else {
00217                      strcpy(ibuf, istr);         
00218                      ibuflen = strlen(istr);
00219               }
00220 
00221               ctdl_iconv_open("UTF-8", charset, &ic);
00222               if (ic != (iconv_t)(-1) ) {
00223                      obuflen = 1024;
00224                      obuf = (char *) malloc(obuflen);
00225                      osav = obuf;
00226                      iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
00227                      osav[1024-obuflen] = 0;
00228 
00229                      end = start;
00230                      end++;
00231                      strcpy(start, "");
00232                      remove_token(end, 0, '?');
00233                      remove_token(end, 0, '?');
00234                      remove_token(end, 0, '?');
00235                      remove_token(end, 0, '?');
00236                      strcpy(end, &end[1]);
00237 
00238                      snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
00239                      strcpy(buf, newbuf);
00240                      free(osav);
00241                      iconv_close(ic);
00242               }
00243               else {
00244                      end = start;
00245                      end++;
00246                      strcpy(start, "");
00247                      remove_token(end, 0, '?');
00248                      remove_token(end, 0, '?');
00249                      remove_token(end, 0, '?');
00250                      remove_token(end, 0, '?');
00251                      strcpy(end, &end[1]);
00252 
00253                      snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
00254                      strcpy(buf, newbuf);
00255               }
00256 
00257               free(isav);
00258 
00259               /*
00260                * Since spammers will go to all sorts of absurd lengths to get their
00261                * messages through, there are LOTS of corrupt headers out there.
00262                * So, prevent a really badly formed RFC2047 header from throwing
00263                * this function into an infinite loop.
00264                */
00265               ++passes;
00266               if (passes > 20) return;
00267 
00268               start = strstr(buf, "=?");
00269               FindNextEnd((start != NULL)? start : buf, end);
00270        }
00271 
00272 }
00273 #else
00274 inline void utf8ify_rfc822_string(char *a){};
00275 
00276 #endif
00277 
00278 
00279 
00280 struct trynamebuf {
00281        char buffer1[SIZ];
00282        char buffer2[SIZ];
00283 };
00284 
00285 char *inetcfg = NULL;
00286 struct spamstrings_t *spamstrings = NULL;
00287 
00288 
00289 /*
00290  * Return nonzero if the supplied name is an alias for this host.
00291  */
00292 int CtdlHostAlias(char *fqdn) {
00293        int config_lines;
00294        int i;
00295        char buf[256];
00296        char host[256], type[256];
00297        int found = 0;
00298 
00299        if (fqdn == NULL) return(hostalias_nomatch);
00300        if (IsEmptyStr(fqdn)) return(hostalias_nomatch);
00301        if (!strcasecmp(fqdn, "localhost")) return(hostalias_localhost);
00302        if (!strcasecmp(fqdn, config.c_fqdn)) return(hostalias_localhost);
00303        if (!strcasecmp(fqdn, config.c_nodename)) return(hostalias_localhost);
00304        if (inetcfg == NULL) return(hostalias_nomatch);
00305 
00306        config_lines = num_tokens(inetcfg, '\n');
00307        for (i=0; i<config_lines; ++i) {
00308               extract_token(buf, inetcfg, i, '\n', sizeof buf);
00309               extract_token(host, buf, 0, '|', sizeof host);
00310               extract_token(type, buf, 1, '|', sizeof type);
00311 
00312               found = 0;
00313 
00314               /* Process these in a specific order, in case there are multiple matches.
00315                * We want directory to override masq, for example.
00316                */
00317 
00318               if ( (!strcasecmp(type, "masqdomain")) && (!strcasecmp(fqdn, host))) {
00319                      found = hostalias_masq;
00320               }
00321               if ( (!strcasecmp(type, "localhost")) && (!strcasecmp(fqdn, host))) {
00322                      found = hostalias_localhost;
00323               }
00324               if ( (!strcasecmp(type, "directory")) && (!strcasecmp(fqdn, host))) {
00325                      found = hostalias_directory;
00326               }
00327 
00328               if (found) return(found);
00329        }
00330 
00331        return(hostalias_nomatch);
00332 }
00333 
00334 
00335 
00336 
00337 
00338 
00339 
00340 /*
00341  * Return 0 if a given string fuzzy-matches a Citadel user account
00342  *
00343  * FIXME ... this needs to be updated to handle aliases.
00344  */
00345 int fuzzy_match(struct ctdluser *us, char *matchstring) {
00346        int a;
00347        long len;
00348 
00349        if ( (!strncasecmp(matchstring, "cit", 3)) 
00350           && (atol(&matchstring[3]) == us->usernum)) {
00351               return 0;
00352        }
00353 
00354        len = strlen(matchstring);
00355        for (a=0; !IsEmptyStr(&us->fullname[a]); ++a) {
00356               if (!strncasecmp(&us->fullname[a],
00357                  matchstring, len)) {
00358                      return 0;
00359               }
00360        }
00361        return -1;
00362 }
00363 
00364 
00365 /*
00366  * Unfold a multi-line field into a single line, removing multi-whitespaces
00367  */
00368 void unfold_rfc822_field(char **field, char **FieldEnd) 
00369 {
00370        int quote = 0;
00371        char *pField = *field;
00372        char *sField;
00373        char *pFieldEnd = *FieldEnd;
00374 
00375        while (isspace(*pField))
00376               pField++;
00377        /* remove leading/trailing whitespace */
00378        ;
00379 
00380        while (isspace(*pFieldEnd))
00381               pFieldEnd --;
00382 
00383        *FieldEnd = pFieldEnd;
00384        /* convert non-space whitespace to spaces, and remove double blanks */
00385        for (sField = *field = pField; 
00386             sField < pFieldEnd; 
00387             pField++, sField++)
00388        {
00389               if ((*sField=='\r') || (*sField=='\n')) {
00390                   sField++;
00391                   if  (*sField == '\n')
00392                      sField++;
00393                   *pField = *sField;
00394               }
00395               else {
00396                      if (*sField=='\"') quote = 1 - quote;
00397                      if (!quote) {
00398                             if (isspace(*sField))
00399                             {
00400                                    *pField = ' ';
00401                                    pField++;
00402                                    sField++;
00403                                    
00404                                    while ((sField < pFieldEnd) && 
00405                                           isspace(*sField))
00406                                           sField++;
00407                                    *pField = *sField;
00408                             }
00409                             else *pField = *sField;
00410                      }
00411                      else *pField = *sField;
00412               }
00413        }
00414        *pField = '\0';
00415        *FieldEnd = pField - 1;
00416 }
00417 
00418 
00419 
00420 /*
00421  * Split an RFC822-style address into userid, host, and full name
00422  *
00423  */
00424 void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name)
00425 {
00426        int a;
00427 
00428        strcpy(user, "");
00429        strcpy(node, config.c_fqdn);
00430        strcpy(name, "");
00431 
00432        if (rfc822 == NULL) return;
00433 
00434        /* extract full name - first, it's From minus <userid> */
00435        strcpy(name, rfc822);
00436        stripout(name, '<', '>');
00437 
00438        /* strip anything to the left of a bang */
00439        while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0))
00440               strcpy(name, &name[1]);
00441 
00442        /* and anything to the right of a @ or % */
00443        for (a = 0; a < strlen(name); ++a) {
00444               if (name[a] == '@')
00445                      name[a] = 0;
00446               if (name[a] == '%')
00447                      name[a] = 0;
00448        }
00449 
00450        /* but if there are parentheses, that changes the rules... */
00451        if ((haschar(rfc822, '(') == 1) && (haschar(rfc822, ')') == 1)) {
00452               strcpy(name, rfc822);
00453               stripallbut(name, '(', ')');
00454        }
00455 
00456        /* but if there are a set of quotes, that supersedes everything */
00457        if (haschar(rfc822, 34) == 2) {
00458               strcpy(name, rfc822);
00459               while ((!IsEmptyStr(name)) && (name[0] != 34)) {
00460                      strcpy(&name[0], &name[1]);
00461               }
00462               strcpy(&name[0], &name[1]);
00463               for (a = 0; a < strlen(name); ++a)
00464                      if (name[a] == 34)
00465                             name[a] = 0;
00466        }
00467        /* extract user id */
00468        strcpy(user, rfc822);
00469 
00470        /* first get rid of anything in parens */
00471        stripout(user, '(', ')');
00472 
00473        /* if there's a set of angle brackets, strip it down to that */
00474        if ((haschar(user, '<') == 1) && (haschar(user, '>') == 1)) {
00475               stripallbut(user, '<', '>');
00476        }
00477 
00478        /* strip anything to the left of a bang */
00479        while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0))
00480               strcpy(user, &user[1]);
00481 
00482        /* and anything to the right of a @ or % */
00483        for (a = 0; a < strlen(user); ++a) {
00484               if (user[a] == '@')
00485                      user[a] = 0;
00486               if (user[a] == '%')
00487                      user[a] = 0;
00488        }
00489 
00490 
00491        /* extract node name */
00492        strcpy(node, rfc822);
00493 
00494        /* first get rid of anything in parens */
00495        stripout(node, '(', ')');
00496 
00497        /* if there's a set of angle brackets, strip it down to that */
00498        if ((haschar(node, '<') == 1) && (haschar(node, '>') == 1)) {
00499               stripallbut(node, '<', '>');
00500        }
00501 
00502        /* If no node specified, tack ours on instead */
00503        if (
00504               (haschar(node, '@')==0)
00505               && (haschar(node, '%')==0)
00506               && (haschar(node, '!')==0)
00507        ) {
00508               strcpy(node, config.c_nodename);
00509        }
00510 
00511        else {
00512 
00513               /* strip anything to the left of a @ */
00514               while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0))
00515                      strcpy(node, &node[1]);
00516        
00517               /* strip anything to the left of a % */
00518               while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0))
00519                      strcpy(node, &node[1]);
00520        
00521               /* reduce multiple system bang paths to node!user */
00522               while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1))
00523                      strcpy(node, &node[1]);
00524        
00525               /* now get rid of the user portion of a node!user string */
00526               for (a = 0; a < strlen(node); ++a)
00527                      if (node[a] == '!')
00528                             node[a] = 0;
00529        }
00530 
00531        /* strip leading and trailing spaces in all strings */
00532        striplt(user);
00533        striplt(node);
00534        striplt(name);
00535 
00536        /* If we processed a string that had the address in angle brackets
00537         * but no name outside the brackets, we now have an empty name.  In
00538         * this case, use the user portion of the address as the name.
00539         */
00540        if ((IsEmptyStr(name)) && (!IsEmptyStr(user))) {
00541               strcpy(name, user);
00542        }
00543 }
00544 
00545 
00546 
00547 /*
00548  * convert_field() is a helper function for convert_internet_message().
00549  * Given start/end positions for an rfc822 field, it converts it to a Citadel
00550  * field if it wants to, and unfolds it if necessary.
00551  *
00552  * Returns 1 if the field was converted and inserted into the Citadel message
00553  * structure, implying that the source field should be removed from the
00554  * message text.
00555  */
00556 int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
00557        char *key, *value, *valueend;
00558        long len;
00559        const char *pos;
00560        int i;
00561        const char *colonpos = NULL;
00562        int processed = 0;
00563        char buf[SIZ];
00564        char user[1024];
00565        char node[1024];
00566        char name[1024];
00567        char addr[1024];
00568        time_t parsed_date;
00569        long valuelen;
00570 
00571        for (pos = end; pos >= beg; pos--) {
00572               if (*pos == ':') colonpos = pos;
00573        }
00574 
00575        if (colonpos == NULL) return(0);   /* no colon? not a valid header line */
00576 
00577        len = end - beg;
00578        key = malloc(len + 2);
00579        memcpy(key, beg, len + 1);
00580        key[len] = '\0';
00581        valueend = key + len;
00582        * ( key + (colonpos - beg) ) = '\0';
00583        value = &key[(colonpos - beg) + 1];
00584 /*     printf("Header: [%s]\nValue: [%s]\n", key, value); */
00585        unfold_rfc822_field(&value, &valueend);
00586        valuelen = valueend - value + 1;
00587 /*     printf("UnfoldedValue: [%s]\n", value); */
00588 
00589        /*
00590         * Here's the big rfc822-to-citadel loop.
00591         */
00592 
00593        /* Date/time is converted into a unix timestamp.  If the conversion
00594         * fails, we replace it with the time the message arrived locally.
00595         */
00596        if (!strcasecmp(key, "Date")) {
00597               parsed_date = parsedate(value);
00598               if (parsed_date < 0L) parsed_date = time(NULL);
00599               snprintf(buf, sizeof buf, "%ld", (long)parsed_date );
00600               if (msg->cm_fields['T'] == NULL)
00601                      msg->cm_fields['T'] = strdup(buf);
00602               processed = 1;
00603        }
00604 
00605        else if (!strcasecmp(key, "From")) {
00606               process_rfc822_addr(value, user, node, name);
00607               syslog(LOG_DEBUG, "Converted to <%s@%s> (%s)\n", user, node, name);
00608               snprintf(addr, sizeof addr, "%s@%s", user, node);
00609               if (msg->cm_fields['A'] == NULL)
00610                      msg->cm_fields['A'] = strdup(name);
00611               if (msg->cm_fields['F'] == NULL)
00612                      msg->cm_fields['F'] = strdup(addr);
00613               processed = 1;
00614        }
00615 
00616        else if (!strcasecmp(key, "Subject")) {
00617               if (msg->cm_fields['U'] == NULL)
00618                      msg->cm_fields['U'] = strndup(value, valuelen);
00619               processed = 1;
00620        }
00621 
00622        else if (!strcasecmp(key, "List-ID")) {
00623               if (msg->cm_fields['L'] == NULL)
00624                      msg->cm_fields['L'] = strndup(value, valuelen);
00625               processed = 1;
00626        }
00627 
00628        else if (!strcasecmp(key, "To")) {
00629               if (msg->cm_fields['R'] == NULL)
00630                      msg->cm_fields['R'] = strndup(value, valuelen);
00631               processed = 1;
00632        }
00633 
00634        else if (!strcasecmp(key, "CC")) {
00635               if (msg->cm_fields['Y'] == NULL)
00636                      msg->cm_fields['Y'] = strndup(value, valuelen);
00637               processed = 1;
00638        }
00639 
00640        else if (!strcasecmp(key, "Message-ID")) {
00641               if (msg->cm_fields['I'] != NULL) {
00642                      syslog(LOG_WARNING, "duplicate message id\n");
00643               }
00644 
00645               if (msg->cm_fields['I'] == NULL) {
00646                      msg->cm_fields['I'] = strndup(value, valuelen);
00647 
00648                      /* Strip angle brackets */
00649                      while (haschar(msg->cm_fields['I'], '<') > 0) {
00650                             strcpy(&msg->cm_fields['I'][0],
00651                                    &msg->cm_fields['I'][1]);
00652                      }
00653                      for (i = 0; i<strlen(msg->cm_fields['I']); ++i)
00654                             if (msg->cm_fields['I'][i] == '>')
00655                                    msg->cm_fields['I'][i] = 0;
00656               }
00657 
00658               processed = 1;
00659        }
00660 
00661        else if (!strcasecmp(key, "Return-Path")) {
00662               if (msg->cm_fields['P'] == NULL)
00663                      msg->cm_fields['P'] = strndup(value, valuelen);
00664               processed = 1;
00665        }
00666 
00667        else if (!strcasecmp(key, "Envelope-To")) {
00668               if (msg->cm_fields['V'] == NULL)
00669                      msg->cm_fields['V'] = strndup(value, valuelen);
00670               processed = 1;
00671        }
00672 
00673        else if (!strcasecmp(key, "References")) {
00674               if (msg->cm_fields['W'] != NULL) {
00675                      free(msg->cm_fields['W']);
00676               }
00677               msg->cm_fields['W'] = strndup(value, valuelen);
00678               processed = 1;
00679        }
00680 
00681        else if (!strcasecmp(key, "Reply-To")) {
00682               if (msg->cm_fields['K'] != NULL) {
00683                      free(msg->cm_fields['K']);
00684               }
00685               msg->cm_fields['K'] = strndup(value, valuelen);
00686               processed = 1;
00687        }
00688 
00689        else if (!strcasecmp(key, "In-reply-to")) {
00690               if (msg->cm_fields['W'] == NULL) {        /* References: supersedes In-reply-to: */
00691                      msg->cm_fields['W'] = strndup(value, valuelen);
00692               }
00693               processed = 1;
00694        }
00695 
00696 
00697 
00698        /* Clean up and move on. */
00699        free(key);    /* Don't free 'value', it's actually the same buffer */
00700        return(processed);
00701 }
00702 
00703 
00704 /*
00705  * Convert RFC822 references format (References) to Citadel references format (Weferences)
00706  */
00707 void convert_references_to_wefewences(char *str) {
00708        int bracket_nesting = 0;
00709        char *ptr = str;
00710        char *moveptr = NULL;
00711        char ch;
00712 
00713        while(*ptr) {
00714               ch = *ptr;
00715               if (ch == '>') {
00716                      --bracket_nesting;
00717                      if (bracket_nesting < 0) bracket_nesting = 0;
00718               }
00719               if ((ch == '>') && (bracket_nesting == 0) && (*(ptr+1)) && (ptr>str) ) {
00720                      *ptr = '|';
00721                      ++ptr;
00722               }
00723               else if (bracket_nesting > 0) {
00724                      ++ptr;
00725               }
00726               else {
00727                      moveptr = ptr;
00728                      while (*moveptr) {
00729                             *moveptr = *(moveptr+1);
00730                             ++moveptr;
00731                      }
00732               }
00733               if (ch == '<') ++bracket_nesting;
00734        }
00735 
00736 }
00737 
00738 
00739 /*
00740  * Convert an RFC822 message (headers + body) to a CtdlMessage structure.
00741  * NOTE: the supplied buffer becomes part of the CtdlMessage structure, and
00742  * will be deallocated when CtdlFreeMessage() is called.  Therefore, the
00743  * supplied buffer should be DEREFERENCED.  It should not be freed or used
00744  * again.
00745  */
00746 struct CtdlMessage *convert_internet_message(char *rfc822) {
00747        StrBuf *RFCBuf = NewStrBufPlain(rfc822, -1);
00748        free (rfc822);
00749        return convert_internet_message_buf(&RFCBuf);
00750 }
00751 
00752 
00753 
00754 struct CtdlMessage *convert_internet_message_buf(StrBuf **rfc822)
00755 {
00756        struct CtdlMessage *msg;
00757        const char *pos, *beg, *end, *totalend;
00758        int done, alldone = 0;
00759        char buf[SIZ];
00760        int converted;
00761        StrBuf *OtherHeaders;
00762 
00763        msg = malloc(sizeof(struct CtdlMessage));
00764        if (msg == NULL) return msg;
00765 
00766        memset(msg, 0, sizeof(struct CtdlMessage));
00767        msg->cm_magic = CTDLMESSAGE_MAGIC; /* self check */
00768        msg->cm_anon_type = 0;                    /* never anonymous */
00769        msg->cm_format_type = FMT_RFC822;  /* internet message */
00770 
00771        pos = ChrPtr(*rfc822);
00772        totalend = pos + StrLength(*rfc822);
00773        done = 0;
00774        OtherHeaders = NewStrBufPlain(NULL, StrLength(*rfc822));
00775 
00776        while (!alldone) {
00777 
00778               /* Locate beginning and end of field, keeping in mind that
00779                * some fields might be multiline
00780                */
00781               end = beg = pos;
00782 
00783               while ((end < totalend) && 
00784                      (end == beg) && 
00785                      (done == 0) ) 
00786               {
00787 
00788                      if ( (*pos=='\n') && ((*(pos+1))!=0x20) && ((*(pos+1))!=0x09) )
00789                      {
00790                             end = pos;
00791                      }
00792 
00793                      /* done with headers? */
00794                      if ((*pos=='\n') &&
00795                          ( (*(pos+1)=='\n') ||
00796                            (*(pos+1)=='\r')) ) 
00797                      {
00798                             alldone = 1;
00799                      }
00800 
00801                      if (pos >= (totalend - 1) )
00802                      {
00803                             end = pos;
00804                             done = 1;
00805                      }
00806 
00807                      ++pos;
00808 
00809               }
00810 
00811               /* At this point we have a field.  Are we interested in it? */
00812               converted = convert_field(msg, beg, end);
00813 
00814               /* Strip the field out of the RFC822 header if we used it */
00815               if (!converted) {
00816                      StrBufAppendBufPlain(OtherHeaders, beg, end - beg, 0);
00817                      StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0);
00818               }
00819 
00820               /* If we've hit the end of the message, bail out */
00821               if (pos >= totalend)
00822                      alldone = 1;
00823        }
00824        StrBufAppendBufPlain(OtherHeaders, HKEY("\n"), 0);
00825        if (pos < totalend)
00826               StrBufAppendBufPlain(OtherHeaders, pos, totalend - pos, 0);
00827        FreeStrBuf(rfc822);
00828        msg->cm_fields['M'] = SmashStrBuf(&OtherHeaders);
00829 
00830        /* Follow-up sanity checks... */
00831 
00832        /* If there's no timestamp on this message, set it to now. */
00833        if (msg->cm_fields['T'] == NULL) {
00834               snprintf(buf, sizeof buf, "%ld", (long)time(NULL));
00835               msg->cm_fields['T'] = strdup(buf);
00836        }
00837 
00838        /* If a W (references, or rather, Wefewences) field is present, we
00839         * have to convert it from RFC822 format to Citadel format.
00840         */
00841        if (msg->cm_fields['W'] != NULL) {
00842               convert_references_to_wefewences(msg->cm_fields['W']);
00843        }
00844 
00845        return msg;
00846 }
00847 
00848 
00849 
00850 /*
00851  * Look for a particular header field in an RFC822 message text.  If the
00852  * requested field is found, it is unfolded (if necessary) and returned to
00853  * the caller.  The field name is stripped out, leaving only its contents.
00854  * The caller is responsible for freeing the returned buffer.  If the requested
00855  * field is not present, or anything else goes wrong, it returns NULL.
00856  */
00857 char *rfc822_fetch_field(const char *rfc822, const char *fieldname) {
00858        char *fieldbuf = NULL;
00859        const char *end_of_headers;
00860        const char *field_start;
00861        const char *ptr;
00862        char *cont;
00863        char fieldhdr[SIZ];
00864 
00865        /* Should never happen, but sometimes we get stupid */
00866        if (rfc822 == NULL) return(NULL);
00867        if (fieldname == NULL) return(NULL);
00868 
00869        snprintf(fieldhdr, sizeof fieldhdr, "%s:", fieldname);
00870 
00871        /* Locate the end of the headers, so we don't run past that point */
00872        end_of_headers = cbmstrcasestr(rfc822, "\n\r\n");
00873        if (end_of_headers == NULL) {
00874               end_of_headers = cbmstrcasestr(rfc822, "\n\n");
00875        }
00876        if (end_of_headers == NULL) return (NULL);
00877 
00878        field_start = cbmstrcasestr(rfc822, fieldhdr);
00879        if (field_start == NULL) return(NULL);
00880        if (field_start > end_of_headers) return(NULL);
00881 
00882        fieldbuf = malloc(SIZ);
00883        strcpy(fieldbuf, "");
00884 
00885        ptr = field_start;
00886        ptr = cmemreadline(ptr, fieldbuf, SIZ-strlen(fieldbuf) );
00887        while ( (isspace(ptr[0])) && (ptr < end_of_headers) ) {
00888               strcat(fieldbuf, " ");
00889               cont = &fieldbuf[strlen(fieldbuf)];
00890               ptr = cmemreadline(ptr, cont, SIZ-strlen(fieldbuf) );
00891               striplt(cont);
00892        }
00893 
00894        strcpy(fieldbuf, &fieldbuf[strlen(fieldhdr)]);
00895        striplt(fieldbuf);
00896 
00897        return(fieldbuf);
00898 }
00899 
00900 
00901 
00902 /*****************************************************************************
00903  *                      DIRECTORY MANAGEMENT FUNCTIONS                       *
00904  *****************************************************************************/
00905 
00906 /*
00907  * Generate the index key for an Internet e-mail address to be looked up
00908  * in the database.
00909  */
00910 void directory_key(char *key, char *addr) {
00911        int i;
00912        int keylen = 0;
00913 
00914        for (i=0; !IsEmptyStr(&addr[i]); ++i) {
00915               if (!isspace(addr[i])) {
00916                      key[keylen++] = tolower(addr[i]);
00917               }
00918        }
00919        key[keylen++] = 0;
00920 
00921        syslog(LOG_DEBUG, "Directory key is <%s>\n", key);
00922 }
00923 
00924 
00925 
00926 /* Return nonzero if the supplied address is in a domain we keep in
00927  * the directory
00928  */
00929 int IsDirectory(char *addr, int allow_masq_domains) {
00930        char domain[256];
00931        int h;
00932 
00933        extract_token(domain, addr, 1, '@', sizeof domain);
00934        striplt(domain);
00935 
00936        h = CtdlHostAlias(domain);
00937 
00938        if ( (h == hostalias_masq) && allow_masq_domains)
00939               return(1);
00940        
00941        if ( (h == hostalias_localhost) || (h == hostalias_directory) ) {
00942               return(1);
00943        }
00944        else {
00945               return(0);
00946        }
00947 }
00948 
00949 
00950 /*
00951  * Initialize the directory database (erasing anything already there)
00952  */
00953 void CtdlDirectoryInit(void) {
00954        cdb_trunc(CDB_DIRECTORY);
00955 }
00956 
00957 
00958 /*
00959  * Add an Internet e-mail address to the directory for a user
00960  */
00961 int CtdlDirectoryAddUser(char *internet_addr, char *citadel_addr) {
00962        char key[SIZ];
00963 
00964        if (IsDirectory(internet_addr, 0) == 0) 
00965               return 0;
00966        syslog(LOG_DEBUG, "Create directory entry: %s --> %s\n", internet_addr, citadel_addr);
00967        directory_key(key, internet_addr);
00968        cdb_store(CDB_DIRECTORY, key, strlen(key), citadel_addr, strlen(citadel_addr)+1 );
00969        return 1;
00970 }
00971 
00972 
00973 /*
00974  * Delete an Internet e-mail address from the directory.
00975  *
00976  * (NOTE: we don't actually use or need the citadel_addr variable; it's merely
00977  * here because the callback API expects to be able to send it.)
00978  */
00979 int CtdlDirectoryDelUser(char *internet_addr, char *citadel_addr) {
00980        char key[SIZ];
00981 
00982        syslog(LOG_DEBUG, "Delete directory entry: %s --> %s\n", internet_addr, citadel_addr);
00983        directory_key(key, internet_addr);
00984        return cdb_delete(CDB_DIRECTORY, key, strlen(key) ) == 0;
00985 }
00986 
00987 
00988 /*
00989  * Look up an Internet e-mail address in the directory.
00990  * On success: returns 0, and Citadel address stored in 'target'
00991  * On failure: returns nonzero
00992  */
00993 int CtdlDirectoryLookup(char *target, char *internet_addr, size_t targbuflen) {
00994        struct cdbdata *cdbrec;
00995        char key[SIZ];
00996 
00997        /* Dump it in there unchanged, just for kicks */
00998        safestrncpy(target, internet_addr, targbuflen);
00999 
01000        /* Only do lookups for addresses with hostnames in them */
01001        if (num_tokens(internet_addr, '@') != 2) return(-1);
01002 
01003        /* Only do lookups for domains in the directory */
01004        if (IsDirectory(internet_addr, 0) == 0) return(-1);
01005 
01006        directory_key(key, internet_addr);
01007        cdbrec = cdb_fetch(CDB_DIRECTORY, key, strlen(key) );
01008        if (cdbrec != NULL) {
01009               safestrncpy(target, cdbrec->ptr, targbuflen);
01010               cdb_free(cdbrec);
01011               return(0);
01012        }
01013 
01014        return(-1);
01015 }
01016 
01017 
01018 /*
01019  * Harvest any email addresses that someone might want to have in their
01020  * "collected addresses" book.
01021  */
01022 char *harvest_collected_addresses(struct CtdlMessage *msg) {
01023        char *coll = NULL;
01024        char addr[256];
01025        char user[256], node[256], name[256];
01026        int is_harvestable;
01027        int i, j, h;
01028        int field = 0;
01029 
01030        if (msg == NULL) return(NULL);
01031 
01032        is_harvestable = 1;
01033        strcpy(addr, "");    
01034        if (msg->cm_fields['A'] != NULL) {
01035               strcat(addr, msg->cm_fields['A']);
01036        }
01037        if (msg->cm_fields['F'] != NULL) {
01038               strcat(addr, " <");
01039               strcat(addr, msg->cm_fields['F']);
01040               strcat(addr, ">");
01041               if (IsDirectory(msg->cm_fields['F'], 0)) {
01042                      is_harvestable = 0;
01043               }
01044        }
01045 
01046        if (is_harvestable) {
01047               coll = strdup(addr);
01048        }
01049        else {
01050               coll = strdup("");
01051        }
01052 
01053        if (coll == NULL) return(NULL);
01054 
01055        /* Scan both the R (To) and Y (CC) fields */
01056        for (i = 0; i < 2; ++i) {
01057               if (i == 0) field = 'R' ;
01058               if (i == 1) field = 'Y' ;
01059 
01060               if (msg->cm_fields[field] != NULL) {
01061                      for (j=0; j<num_tokens(msg->cm_fields[field], ','); ++j) {
01062                             extract_token(addr, msg->cm_fields[field], j, ',', sizeof addr);
01063                             if (strstr(addr, "=?") != NULL)
01064                                    utf8ify_rfc822_string(addr);
01065                             process_rfc822_addr(addr, user, node, name);
01066                             h = CtdlHostAlias(node);
01067                             if ( (h != hostalias_localhost) && (h != hostalias_directory) ) {
01068                                    coll = realloc(coll, strlen(coll) + strlen(addr) + 4);
01069                                    if (coll == NULL) return(NULL);
01070                                    if (!IsEmptyStr(coll)) {
01071                                           strcat(coll, ",");
01072                                    }
01073                                    striplt(addr);
01074                                    strcat(coll, addr);
01075                             }
01076                      }
01077               }
01078        }
01079 
01080        if (IsEmptyStr(coll)) {
01081               free(coll);
01082               return(NULL);
01083        }
01084        return(coll);
01085 }