X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Finternet_addressing.c;h=db9e776ad27b17933b6f86c86aafa7ea37987a63;hb=2b5449a35c6ae9b76bee5bb1b2fe0f5e17393c38;hp=33d70451a6f36406c36056d8b91954ce0e728f4e;hpb=50ff39d933b7178377b3a67f8edc31ffa42ffd28;p=citadel.git diff --git a/citadel/internet_addressing.c b/citadel/internet_addressing.c index 33d70451a..db9e776ad 100644 --- a/citadel/internet_addressing.c +++ b/citadel/internet_addressing.c @@ -2,7 +2,7 @@ * This file contains functions which handle the mapping of Internet addresses * to users on the Citadel system. * - * Copyright (c) 1987-2018 by the citadel.org team + * Copyright (c) 1987-2021 by the citadel.org team * * This program is open source software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3. @@ -134,8 +134,7 @@ void utf8ify_rfc822_string(char *buf) { if (start != NULL) FindNextEnd (start, end); - while ((start != NULL) && (end != NULL)) - { + while ((start != NULL) && (end != NULL)) { next = strstr(end, "=?"); if (next != NULL) FindNextEnd(next, nextend); @@ -143,9 +142,7 @@ void utf8ify_rfc822_string(char *buf) { next = NULL; /* did we find two partitions */ - if ((next != NULL) && - ((next - end) > 2)) - { + if ((next != NULL) && ((next - end) > 2)) { ptr = end + 2; while ((ptr < next) && (isspace(*ptr) || @@ -175,13 +172,10 @@ void utf8ify_rfc822_string(char *buf) { end = nextend; } - /* Now we handle foreign character sets properly encoded - * in RFC2047 format. - */ + // 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) - { + 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); @@ -197,12 +191,10 @@ void utf8ify_rfc822_string(char *buf) { len = strlen(istr); pos = 0; - while (pos < len) - { + while (pos < len) { if (istr[pos] == '_') istr[pos] = ' '; pos++; } - ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len); } else { @@ -268,11 +260,6 @@ inline void utf8ify_rfc822_string(char *a){}; #endif -struct trynamebuf { - char buffer1[SIZ]; - char buffer2[SIZ]; -}; - char *inetcfg = NULL; /* @@ -326,9 +313,8 @@ int CtdlHostAlias(char *fqdn) { /* * Determine whether a given Internet address belongs to the current user */ -int CtdlIsMe(char *addr, int addr_buf_len) -{ - recptypes *recp; +int CtdlIsMe(char *addr, int addr_buf_len) { + struct recptypes *recp; int i; recp = validate_recipients(addr, NULL, 0); @@ -353,7 +339,7 @@ int CtdlIsMe(char *addr, int addr_buf_len) /* If the last item in a list of recipients was truncated to a partial address, - * remove it completely in order to avoid choking libSieve + * remove it completely in order to avoid choking library functions. */ void sanitize_truncated_recipient(char *str) { @@ -380,37 +366,66 @@ void sanitize_truncated_recipient(char *str) * This function is self explanatory. * (What can I say, I'm in a weird mood today...) */ -void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name) -{ - unsigned int i; - - for (i = 0; i < strlen(name); ++i) { - if (name[i] == '@') { - while (isspace(name[i - 1]) && i > 0) { - strcpy(&name[i - 1], &name[i]); - --i; - } - while (isspace(name[i + 1])) { - strcpy(&name[i + 1], &name[i + 2]); - } +void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name) { + char *ptr; + if (!name) return; + + for (ptr=name; *ptr; ++ptr) { + while ( (isspace(*ptr)) && (*(ptr+1)=='@') ) { + strcpy(ptr, ptr+1); + if (ptr > name) --ptr; + } + while ( (*ptr=='@') && (*(ptr+1)!=0) && (isspace(*(ptr+1))) ) { + strcpy(ptr+1, ptr+2); } } } -/* - * Aliasing for network mail. - */ -int alias(char *name) -{ /* process alias and routing info for mail */ +// values that can be returned by expand_aliases() +enum { + EA_ERROR, // Can't send message due to bad address + EA_MULTIPLE, // Alias expanded into multiple recipients -- run me again! + EA_LOCAL, // Local message, do no network processing + EA_INTERNET // Convert msg and send as Internet mail +}; + + +// Process alias and routing info for email addresses +int expand_aliases(char *name) { int a; char aaa[SIZ]; int at = 0; char node[64]; + char *t; + + syslog(LOG_DEBUG, "internet_addressing: \x1b[34mexpand_aliases(%s)\x1b[0m", name); + + char *aliases = CtdlGetSysConfig(GLOBAL_ALIASES); // First hit the Global Alias Table + if (aliases) { + char *aptr = aliases; + while ((t = strtok_r(aptr, "\n", &aptr))) { + char *bar = strchr(t, '|'); + if (bar) { + bar[0] = 0; + ++bar; + if (!strcasecmp(name, t)) { + syslog(LOG_DEBUG, "internet_addressing: global alias <%s> to <%s>", name, bar); + strcpy(name, bar); + } + } + } + + free(aliases); + if (strchr(name, ',')) { + return(EA_MULTIPLE); + } + } - char original_name[256]; + char original_name[256]; // Now go for the regular aliases safestrncpy(original_name, name, sizeof original_name); + // should these checks still be here, or maybe move them to split_recps() ? striplt(name); remove_any_whitespace_to_the_left_or_right_of_at_symbol(name); stripallbut(name, '<', '>'); @@ -421,7 +436,7 @@ int alias(char *name) } if (strcasecmp(original_name, name)) { - syslog(LOG_INFO, "internet_addressing: %s is being forwarded to %s", original_name, name); + syslog(LOG_INFO, "internet_addressing: directory alias <%s> to <%s>", original_name, name); } /* Change "user @ xxx" to "user" if xxx is an alias for this host */ @@ -429,7 +444,7 @@ int alias(char *name) if (name[a] == '@') { if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) { name[a] = 0; - syslog(LOG_DEBUG, "internet_addressing: changed to <%s>", name); + syslog(LOG_DEBUG, "internet_addressing: host is local, recipient is <%s>", name); break; } } @@ -437,8 +452,8 @@ int alias(char *name) /* determine local or remote type, see citadel.h */ at = haschar(name, '@'); - if (at == 0) return(MES_LOCAL); /* no @'s - local address */ - if (at > 1) return(MES_ERROR); /* >1 @'s - invalid address */ + if (at == 0) return(EA_LOCAL); /* no @'s - local address */ + if (at > 1) return(EA_ERROR); /* >1 @'s - invalid address */ remove_any_whitespace_to_the_left_or_right_of_at_symbol(name); /* figure out the delivery mode */ @@ -448,50 +463,84 @@ int alias(char *name) * is an FQDN and will attempt SMTP delivery to the Internet. */ if (haschar(node, '.') > 0) { - return(MES_INTERNET); + return(EA_INTERNET); } /* If we get to this point it's an invalid node name */ - return (MES_ERROR); + return (EA_ERROR); } -/* - * Validate recipients, count delivery types and errors, and handle aliasing - * FIXME check for dupes!!!!! - * - * Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses - * were specified, or the number of addresses found invalid. - * - * Caller needs to free the result using free_recipients() - */ -recptypes *validate_recipients(const char *supplied_recipients, const char *RemoteIdentifier, int Flags) -{ - struct CitContext *CCC = CC; - recptypes *ret; +// Return a supplied list of email addresses as an array, removing superfluous information and syntax. +Array *split_recps(char *addresses) { + + // Copy the supplied address list into our own memory space, because we are going to mangle it. + char *a = malloc(strlen(addresses)); + a[0] = 0; + + // Strip out anything in double quotes + int toggle = 0; + int pos = 0; + char *t; + for (t=addresses; t[0]; ++t) { + if (t[0] == '\"') { + toggle = 1 - toggle; + } + else if (!toggle) { + a[pos++] = t[0]; + a[pos] = 0; + } + } + + // Transform all qualifying delimiters to commas + for (t=a; t[0]; ++t) { + if ((t[0]==';') || (t[0]=='|')) { + t[0]=','; + } + } + + // Tokenize the recipients into an array + Array *recipients_array = array_new(256); // no single recipient should be bigger than 256 bytes + char *r = a; + while ((t = strtok_r(r, ",", &r))) { + striplt(t); // strip leading and trailing whitespace + stripout(t, '(', ')'); // remove any portion in parentheses + stripallbut(t, '<', '>'); // if angle brackets are present, keep only what is inside them + array_append(recipients_array, t); + } + + free(a); // We don't need this buffer anymore. + return(recipients_array); // Return the completed array to the caller. +} + + +// Validate recipients, count delivery types and errors, and handle aliasing +// FIXME check for dupes!!!!! +// +// Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses +// were specified, or the number of addresses found invalid. +// +// Caller needs to free the result using free_recipients() +// +struct recptypes *validate_recipients(char *supplied_recipients, const char *RemoteIdentifier, int Flags) { + struct recptypes *ret; char *recipients = NULL; - char *org_recp; - char this_recp[256]; - char this_recp_cooked[256]; char append[SIZ]; long len; - int num_recps = 0; - int i, j; int mailtype; int invalid; struct ctdluser tempUS; - struct ctdlroom tempQR; - struct ctdlroom tempQR2; + struct ctdlroom original_room; int err = 0; char errmsg[SIZ]; - int in_quotes = 0; + char *org_recp; + char this_recp[256]; - /* Initialize */ - ret = (recptypes *) malloc(sizeof(recptypes)); - if (ret == NULL) return(NULL); + syslog(LOG_DEBUG, "internet_addressing: \x1b[32mvalidate_recipients(%s) \x1b[0m", supplied_recipients); - /* Set all strings to null and numeric values to zero */ - memset(ret, 0, sizeof(recptypes)); + ret = (struct recptypes *) malloc(sizeof(struct recptypes)); // Initialize + if (ret == NULL) return(NULL); + memset(ret, 0, sizeof(struct recptypes)); // set all values to null/zero if (supplied_recipients == NULL) { recipients = strdup(""); @@ -500,18 +549,13 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo recipients = strdup(supplied_recipients); } - /* Allocate some memory. Yes, this allocates 500% more memory than we will - * actually need, but it's healthier for the heap than doing lots of tiny - * realloc() calls instead. - */ - len = strlen(recipients) + 1024; + len = strlen(recipients) + 1024; // allocate memory ret->errormsg = malloc(len); ret->recp_local = malloc(len); ret->recp_internet = malloc(len); ret->recp_room = malloc(len); ret->display_recp = malloc(len); ret->recp_orgroom = malloc(len); - org_recp = malloc(len); ret->errormsg[0] = 0; ret->recp_local[0] = 0; @@ -519,58 +563,44 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo ret->recp_room[0] = 0; ret->recp_orgroom[0] = 0; ret->display_recp[0] = 0; - ret->recptypes_magic = RECPTYPES_MAGIC; - /* Change all valid separator characters to commas */ - for (i=0; !IsEmptyStr(&recipients[i]); ++i) { - if ((recipients[i] == ';') || (recipients[i] == '|')) { - recipients[i] = ','; - } - } - - /* Now start extracting recipients... */ - - while (!IsEmptyStr(recipients)) { - for (i=0; i<=strlen(recipients); ++i) { - if (recipients[i] == '\"') in_quotes = 1 - in_quotes; - if ( ( (recipients[i] == ',') && (!in_quotes) ) || (recipients[i] == 0) ) { - safestrncpy(this_recp, recipients, i+1); - this_recp[i] = 0; - if (recipients[i] == ',') { - strcpy(recipients, &recipients[i+1]); - } - else { - strcpy(recipients, ""); + Array *recp_array = split_recps(supplied_recipients); + int original_array_len = array_len(recp_array); + for (int r=0; rnum_room; strcpy(this_recp, CtdlGetConfigStr("c_aideroom")); @@ -579,44 +609,55 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo } strcat(ret->recp_room, this_recp); } - else if ( (!strncasecmp(this_recp, "room_", 5)) - && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) { - /* Save room so we can restore it later */ - tempQR2 = CCC->room; - CCC->room = tempQR; - - /* Check permissions to send mail to this room */ - err = CtdlDoIHavePermissionToPostInThisRoom( - errmsg, - sizeof errmsg, - RemoteIdentifier, - Flags, - 0 /* 0 = not a reply */ + // This handles rooms which can receive posts via email. + else if (!strncasecmp(this_recp, "room_", 5)) { + original_room = CC->room; // Remember where we parked + + char mail_to_room[ROOMNAMELEN]; + char *m; + strncpy(mail_to_room, &this_recp[5], sizeof mail_to_room); + for (m = mail_to_room; *m; ++m) { + if (m[0] == '_') m[0]=' '; + } + if (!CtdlGetRoom(&CC->room, mail_to_room)) { // Find the room they asked for + + err = CtdlDoIHavePermissionToPostInThisRoom( // check for write permissions to room + errmsg, + sizeof errmsg, + RemoteIdentifier, + Flags, + 0 // 0 means "this is not a reply" ); - if (err) - { + if (err) { + ++ret->num_error; + invalid = 1; + } + else { + ++ret->num_room; + if (!IsEmptyStr(ret->recp_room)) { + strcat(ret->recp_room, "|"); + } + strcat(ret->recp_room, &this_recp[5]); + + if (!IsEmptyStr(ret->recp_orgroom)) { + strcat(ret->recp_orgroom, "|"); + } + strcat(ret->recp_orgroom, org_recp); + + } + } + else { // no such room exists ++ret->num_error; invalid = 1; - } - else { - ++ret->num_room; - if (!IsEmptyStr(ret->recp_room)) { - strcat(ret->recp_room, "|"); - } - strcat(ret->recp_room, &this_recp_cooked[5]); - - if (!IsEmptyStr(ret->recp_orgroom)) { - strcat(ret->recp_orgroom, "|"); - } - strcat(ret->recp_orgroom, org_recp); - } - - /* Restore room in case something needs it */ - CCC->room = tempQR2; + + // Restore this session's original room location. + CC->room = original_room; } + + // This handles the most common case, which is mail to a user's inbox. else if (CtdlGetUser(&tempUS, this_recp) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); @@ -625,26 +666,17 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo } strcat(ret->recp_local, this_recp); } - else if (CtdlGetUser(&tempUS, this_recp_cooked) == 0) { - ++ret->num_local; - strcpy(this_recp, tempUS.fullname); - if (!IsEmptyStr(ret->recp_local)) { - strcat(ret->recp_local, "|"); - } - strcat(ret->recp_local, this_recp); - } + + // No match for this recipient else { ++ret->num_error; invalid = 1; } break; - case MES_INTERNET: - /* Yes, you're reading this correctly: if the target - * domain points back to the local system, - * the address is invalid. That's - * because if the address were valid, we would have - * already translated it to a local address by now. - */ + case EA_INTERNET: + // Yes, you're reading this correctly: if the target domain points back to the local system, + // the address is invalid. That's because if the address were valid, we would have + // already translated it to a local address by now. if (IsDirectory(this_recp, 0)) { ++ret->num_error; invalid = 1; @@ -657,7 +689,7 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo strcat(ret->recp_internet, this_recp); } break; - case MES_ERROR: + case EA_ERROR: ++ret->num_error; invalid = 1; break; @@ -688,10 +720,8 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo } } } - free(org_recp); - if ( (ret->num_local + ret->num_internet + ret->num_room + ret->num_error) == 0) - { + if ( (ret->num_local + ret->num_internet + ret->num_room + ret->num_error) == 0) { ret->num_error = (-1); strcpy(ret->errormsg, "No recipients specified."); } @@ -701,6 +731,8 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo ); free(recipients); + array_free(recp_array); + return(ret); } @@ -708,7 +740,7 @@ recptypes *validate_recipients(const char *supplied_recipients, const char *Remo /* * Destructor for recptypes */ -void free_recipients(recptypes *valid) { +void free_recipients(struct recptypes *valid) { if (valid == NULL) { return; @@ -732,8 +764,7 @@ void free_recipients(recptypes *valid) { } -char *qp_encode_email_addrs(char *source) -{ +char *qp_encode_email_addrs(char *source) { char *user, *node, *name; const char headerStr[] = "=?UTF-8?Q?"; char *Encoded; @@ -768,20 +799,19 @@ char *qp_encode_email_addrs(char *source) free (AddrPtr), AddrPtr = ptr; ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2); - memset(&ptr[nAddrPtrMax], 0, - sizeof (long) * nAddrPtrMax); + memset(&ptr[nAddrPtrMax], 0, sizeof (long) * nAddrPtrMax); memcpy (ptr, AddrUtf8, sizeof (long) * nAddrPtrMax); free (AddrUtf8), AddrUtf8 = ptr; nAddrPtrMax *= 2; } - if (((unsigned char) source[i] < 32) || - ((unsigned char) source[i] > 126)) { + if (((unsigned char) source[i] < 32) || ((unsigned char) source[i] > 126)) { need_to_encode = 1; AddrUtf8[nColons] = 1; } - if (source[i] == '"') + if (source[i] == '"') { InQuotes = !InQuotes; + } if (!InQuotes && source[i] == ',') { AddrPtr[nColons] = i; nColons++; @@ -811,28 +841,19 @@ char *qp_encode_email_addrs(char *source) for (i = 0; i < nColons && nPtr != NULL; i++) { nmax = EncodedMaxLen - (nPtr - Encoded); if (AddrUtf8[i]) { - process_rfc822_addr(&source[AddrPtr[i]], - user, - node, - name); + process_rfc822_addr(&source[AddrPtr[i]], user, node, name); /* TODO: libIDN here ! */ if (IsEmptyStr(name)) { - n = snprintf(nPtr, nmax, - (i==0)?"%s@%s" : ",%s@%s", - user, node); + n = snprintf(nPtr, nmax, (i==0)?"%s@%s" : ",%s@%s", user, node); } else { EncodedName = rfc2047encode(name, strlen(name)); - n = snprintf(nPtr, nmax, - (i==0)?"%s <%s@%s>" : ",%s <%s@%s>", - EncodedName, user, node); + n = snprintf(nPtr, nmax, (i==0)?"%s <%s@%s>" : ",%s <%s@%s>", EncodedName, user, node); free(EncodedName); } } else { - n = snprintf(nPtr, nmax, - (i==0)?"%s" : ",%s", - &source[AddrPtr[i]]); + n = snprintf(nPtr, nmax, (i==0)?"%s" : ",%s", &source[AddrPtr[i]]); } if (n > 0 ) nPtr += n; @@ -884,20 +905,17 @@ void unfold_rfc822_field(char **field, char **FieldEnd) { if ((*sField=='\r') || (*sField=='\n')) { - int Offset = 1; - while (((*(sField + Offset) == '\r') || - (*(sField + Offset) == '\n') || - (isspace(*(sField + Offset)))) && - (sField + Offset < pFieldEnd)) - Offset ++; - sField += Offset; + int offset = 1; + while ( ( (*(sField + offset) == '\r') || (*(sField + offset) == '\n' )) && (sField + offset < pFieldEnd) ) { + offset ++; + } + sField += offset; *pField = *sField; } else { if (*sField=='\"') quote = 1 - quote; if (!quote) { - if (isspace(*sField)) - { + if (isspace(*sField)) { *pField = ' '; pField++; sField++; @@ -921,8 +939,7 @@ void unfold_rfc822_field(char **field, char **FieldEnd) * Split an RFC822-style address into userid, host, and full name * */ -void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) -{ +void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) { int a; strcpy(user, ""); @@ -1017,7 +1034,6 @@ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) ) { strcpy(node, CtdlGetConfigStr("c_nodename")); } - else { /* strip anything to the left of a @ */ @@ -1116,10 +1132,12 @@ int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) { process_rfc822_addr(value, user, node, name); 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, strlen(name)); - if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr)) - CM_SetField(msg, erFc822Addr, addr, strlen(addr)); + if (CM_IsEmpty(msg, eAuthor) && !IsEmptyStr(name)) { + CM_SetField(msg, eAuthor, name, -1); + } + if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr)) { + CM_SetField(msg, erFc822Addr, addr, -1); + } processed = 1; } @@ -1428,7 +1446,8 @@ void directory_key(char *key, char *addr) { } -/* 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]; @@ -1588,58 +1607,23 @@ char *harvest_collected_addresses(struct CtdlMessage *msg) { /* * Helper function for CtdlRebuildDirectoryIndex() - * - * Call this function as a ForEachUser backend in order to queue up - * user names, or call it with a null user to make it do the processing. - * This allows us to maintain the list as a static instead of passing - * pointers around. */ -void CtdlRebuildDirectoryIndex_backend(struct ctdluser *usbuf, void *data) { - - struct crdib { - char name[64]; - char emails[512]; - }; +void CtdlRebuildDirectoryIndex_backend(char *username, void *data) { - static struct crdib *e = NULL; - static int num_e = 0; - static int alloc_e = 0; - - /* this is the calling mode where we add a user */ + int j = 0; + struct ctdluser usbuf; - if (usbuf != NULL) { - if (num_e >= alloc_e) { - if (alloc_e == 0) { - alloc_e = 100; - e = malloc(sizeof(struct crdib) * alloc_e); - } - else { - alloc_e *= 2; - e = realloc(e, (sizeof(struct crdib) * alloc_e)); - } - } - strcpy(e[num_e].name, usbuf->fullname); - strcpy(e[num_e].emails, usbuf->emailaddrs); - ++num_e; + if (CtdlGetUser(&usbuf, username) != 0) { return; } - /* this is the calling mode where we do the processing */ - - int i, j; - for (i=0; i lookup returned <%d>", synthetic_email_addr, u); } CtdlSetEmailAddressesForUser(user->fullname, synthetic_email_addr);