/* * Convert FN (Friendly Name) into N (Name) * * vname Supplied friendly-name * n Target buffer to store Name * vname_size Size of buffer */ void vcard_fn_to_n(char *vname, char *n, size_t vname_size) { char lastname[256]; char firstname[256]; char middlename[256]; char honorific_prefixes[256]; char honorific_suffixes[256]; char buf[256]; safestrncpy(buf, n, sizeof buf); /* Try to intelligently convert the screen name to a * fully expanded vCard name based on the number of * words in the name */ safestrncpy(lastname, "", sizeof lastname); safestrncpy(firstname, "", sizeof firstname); safestrncpy(middlename, "", sizeof middlename); safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes); safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes); /* Honorific suffixes */ if (num_tokens(buf, ',') > 1) { extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',', sizeof honorific_suffixes); remove_token(buf, (num_tokens(buf, ',') - 1), ','); } /* Find a last name */ extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname); remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); /* Find honorific prefixes */ if (num_tokens(buf, ' ') > 2) { extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes); remove_token(buf, 0, ' '); } /* Find a middle name */ if (num_tokens(buf, ' ') > 1) { extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename); remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); } /* Anything left is probably the first name */ safestrncpy(firstname, buf, sizeof firstname); striplt(firstname); /* Compose the structured name */ snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename, honorific_prefixes, honorific_suffixes); }
/* * Remove the "charset=" attribute from a vCard property name * */ void remove_charset_attribute(char *strbuf) { int i, t; char compare[256]; t = num_tokens(strbuf, ';'); for (i=0; i<t; ++i) { extract_token(compare, strbuf, i, ';', sizeof compare); striplt(compare); if (!strncasecmp(compare, "charset=", 8)) { remove_token(strbuf, i, ';'); } } if (!IsEmptyStr(strbuf)) { if (strbuf[strlen(strbuf)-1] == ';') { strbuf[strlen(strbuf)-1] = 0; } } }
/* * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery * instructions for "5" codes (permanent fatal errors) and produce/deliver * a "bounce" message (delivery status notification). */ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt) { int i; int lines; int status; char buf[1024]; char key[1024]; char addr[1024]; char dsn[1024]; char bounceto[1024]; StrBuf *boundary; int num_bounces = 0; int bounce_this = 0; time_t submitted = 0L; struct CtdlMessage *bmsg = NULL; int give_up = 0; recptypes *valid; int successful_bounce = 0; static int seq = 0; StrBuf *BounceMB; long omsgid = (-1); syslog(LOG_DEBUG, "smtp_do_bounce() called\n"); strcpy(bounceto, ""); boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq); lines = num_tokens(instr, '\n'); /* See if it's time to give up on delivery of this message */ for (i=0; i<lines; ++i) { extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); extract_token(addr, buf, 1, '|', sizeof addr); if (!strcasecmp(key, "submitted")) { submitted = atol(addr); } } if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) { give_up = 1; } /* Start building our bounce message */ bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); if (bmsg == NULL) return; memset(bmsg, 0, sizeof(struct CtdlMessage)); BounceMB = NewStrBufPlain(NULL, 1024); bmsg->cm_magic = CTDLMESSAGE_MAGIC; bmsg->cm_anon_type = MES_NORMAL; bmsg->cm_format_type = FMT_RFC822; CM_SetField(bmsg, eAuthor, HKEY("Citadel")); CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM)); CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)")); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("\r\nThis is a multipart message in MIME format." "\r\n\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0); if (give_up) { StrBufAppendBufPlain( BounceMB, HKEY("A message you sent could not be delivered " "to some or all of its recipients\ndue to " "prolonged unavailability of its destination(s).\n" "Giving up on the following addresses:\n\n"), 0); } else { StrBufAppendBufPlain( BounceMB, HKEY("A message you sent could not be delivered " "to some or all of its recipients.\n" "The following addresses were undeliverable:\n\n" ), 0); } /* * Now go through the instructions checking for stuff. */ for (i=0; i<lines; ++i) { long addrlen; long dsnlen; extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); addrlen = extract_token(addr, buf, 1, '|', sizeof addr); status = extract_int(buf, 2); dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn); bounce_this = 0; syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n", key, addr, status, dsn); if (!strcasecmp(key, "bounceto")) { strcpy(bounceto, addr); } if (!strcasecmp(key, "msgid")) { omsgid = atol(addr); } if (!strcasecmp(key, "remote")) { if (status == 5) bounce_this = 1; if (give_up) bounce_this = 1; } if (bounce_this) { ++num_bounces; StrBufAppendBufPlain(BounceMB, addr, addrlen, 0); StrBufAppendBufPlain(BounceMB, HKEY(": "), 0); StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); remove_token(instr, i, '\n'); --i; --lines; } } /* Attach the original message */ if (omsgid >= 0) { StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0); StrBufAppendBufPlain( BounceMB, HKEY("Content-Disposition: inline\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); if (OMsgTxt == NULL) { CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, NULL, NULL); StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0); FreeStrBuf(&CC->redirect_buffer); } else { StrBufAppendBuf(BounceMB, OMsgTxt, 0); } } /* Close the multipart MIME scope */ StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB); /* Deliver the bounce if there's anything worth mentioning */ syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces); if (num_bounces > 0) { /* First try the user who sent the message */ if (IsEmptyStr(bounceto)) syslog(LOG_ERR, "No bounce address specified\n"); else syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto); /* Can we deliver the bounce to the original sender? */ valid = validate_recipients(bounceto, smtp_get_Recipients (), 0); if (valid != NULL) { if (valid->num_error == 0) { CtdlSubmitMsg(bmsg, valid, "", QP_EADDR); successful_bounce = 1; } } /* If not, post it in the Aide> room */ if (successful_bounce == 0) { CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR); } /* Free up the memory we used */ if (valid != NULL) { free_recipients(valid); } } FreeStrBuf(&boundary); CM_Free(bmsg); syslog(LOG_DEBUG, "Done processing bounces\n"); }
/* * 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); } }