static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result) { DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict; int len = strlen(name); int buflen; /* * We won't get integer overflows in 2*len + 1, because Postfix input * keys have reasonable size limits, better safe than sorry. */ if (len > (INT_MAX - VSTRING_LEN(result) - 1) / 2) msg_panic("dict_mysql_quote: integer overflow in %lu+2*%d+1", (unsigned long) VSTRING_LEN(result), len); buflen = 2 * len + 1; VSTRING_SPACE(result, buflen); #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 if (dict_mysql->active_host) mysql_real_escape_string(dict_mysql->active_host->db, vstring_end(result), name, len); else #endif mysql_escape_string(vstring_end(result), name, len); VSTRING_SKIP(result); }
ARGV *mail_addr_crunch(const char *string, const char *extension) { VSTRING *extern_addr = vstring_alloc(100); VSTRING *canon_addr = vstring_alloc(100); ARGV *argv = argv_alloc(1); TOK822 *tree; TOK822 **addr_list; TOK822 **tpp; char *ratsign; ssize_t extlen; if (extension) extlen = strlen(extension); #define STR(x) vstring_str(x) /* * Parse the string, rewrite each address to canonical form, and convert * the result to external (quoted) form. Optionally apply the extension * to each address found. * * XXX Workaround for the null address. This works for envelopes but * produces ugly results for message headers. */ if (*string == 0 || strcmp(string, "<>") == 0) string = "\"\""; tree = tok822_parse(string); addr_list = tok822_grep(tree, TOK822_ADDR); for (tpp = addr_list; *tpp; tpp++) { tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL); canon_addr_external(canon_addr, STR(extern_addr)); if (extension) { VSTRING_SPACE(canon_addr, extlen + 1); if ((ratsign = strrchr(STR(canon_addr), '@')) == 0) { vstring_strcat(canon_addr, extension); } else { memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1); memcpy(ratsign, extension, extlen); VSTRING_SKIP(canon_addr); } } argv_add(argv, STR(canon_addr), ARGV_END); } argv_terminate(argv); myfree((char *) addr_list); tok822_free_tree(tree); vstring_free(canon_addr); vstring_free(extern_addr); return (argv); }
int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr) { const char *myname = "deliver_resolve_tree"; RESOLVE_REPLY reply; int status; ssize_t ext_len; char *ratsign; int rcpt_delim; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * Initialize. */ resolve_clnt_init(&reply); /* * Rewrite the address to canonical form, just like the cleanup service * does. Then, resolve the address to (transport, nexhop, recipient), * just like the queue manager does. The only part missing here is the * virtual address substitution. Message forwarding fixes most of that. */ tok822_rewrite(addr, REWRITE_CANON); tok822_resolve(addr, &reply); /* * First, a healthy portion of error handling. */ if (reply.flags & RESOLVE_FLAG_FAIL) { dsb_simple(state.msg_attr.why, "4.3.0", "address resolver failure"); status = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else if (reply.flags & RESOLVE_FLAG_ERROR) { dsb_simple(state.msg_attr.why, "5.1.3", "bad recipient address syntax: %s", STR(reply.recipient)); status = bounce_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else { /* * Splice in the optional unmatched address extension. */ if (state.msg_attr.unmatched) { rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)]; if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { VSTRING_ADDCH(reply.recipient, rcpt_delim); vstring_strcat(reply.recipient, state.msg_attr.unmatched); } else { ext_len = strlen(state.msg_attr.unmatched); VSTRING_SPACE(reply.recipient, ext_len + 2); if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) msg_panic("%s: recipient @ botch", myname); memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1); *ratsign = rcpt_delim; memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len); VSTRING_SKIP(reply.recipient); } } state.msg_attr.rcpt.address = STR(reply.recipient); /* * Delivery to a local or non-local address. For a while there was * some ugly code to force local recursive alias expansions on a host * with no authority over the local domain, but that code was just * too unclean. */ if (strcmp(state.msg_attr.relay, STR(reply.transport)) == 0) { status = deliver_recipient(state, usr_attr); } else { status = deliver_indirect(state); } } /* * Cleanup. */ resolve_clnt_free(&reply); return (status); }
static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result) { DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; HOST *active_host = dict_pgsql->active_host; char *myname = "dict_pgsql_quote"; size_t len = strlen(name); size_t buflen = 2 * len + 1; int err = 1; if (active_host == 0) msg_panic("%s: bogus dict_pgsql->active_host", myname); /* * We won't get arithmetic overflows in 2*len + 1, because Postfix input * keys have reasonable size limits, better safe than sorry. */ if (buflen <= len) msg_panic("%s: arithmetic overflow in 2*%lu+1", myname, (unsigned long) len); /* * XXX Workaround: stop further processing when PQescapeStringConn() * (below) fails. A more proper fix requires invasive changes, not * suitable for a stable release. */ if (active_host->stat == STATFAIL) return; /* * Escape the input string, using PQescapeStringConn(), because the older * PQescapeString() is not safe anymore, as stated by the documentation. * * From current libpq (8.1.4) documentation: * * PQescapeStringConn writes an escaped version of the from string to the to * buffer, escaping special characters so that they cannot cause any * harm, and adding a terminating zero byte. * * ... * * The parameter from points to the first character of the string that is to * be escaped, and the length parameter gives the number of bytes in this * string. A terminating zero byte is not required, and should not be * counted in length. * * ... * * (The parameter) to shall point to a buffer that is able to hold at least * one more byte than twice the value of length, otherwise the behavior * is undefined. * * ... * * If the error parameter is not NULL, then *error is set to zero on * success, nonzero on error ... The output string is still generated on * error, but it can be expected that the server will reject it as * malformed. On error, a suitable message is stored in the conn object, * whether or not error is NULL. */ VSTRING_SPACE(result, buflen); PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err); if (err == 0) { VSTRING_SKIP(result); } else { /* * PQescapeStringConn() failed. According to the docs, we still have * a valid, null-terminated output string, but we need not rely on * this behavior. */ msg_warn("dict pgsql: (host %s) cannot escape input string: %s", active_host->hostname, PQerrorMessage(active_host->db)); active_host->stat = STATFAIL; VSTRING_TERMINATE(result); } }
const char *mail_date(time_t when) { static VSTRING *vp; struct tm *lt; struct tm gmt; int gmtoff; /* * As if strftime() isn't expensive enough, we're dynamically adjusting * the size for the result, so we won't be surprised by long names etc. */ if (vp == 0) vp = vstring_alloc(100); else VSTRING_RESET(vp); /* * POSIX does not require that struct tm has a tm_gmtoff field, so we * must compute the time offset from UTC by hand. * * Starting with the difference in hours/minutes between 24-hour clocks, * adjust for differences in years, in yeardays, and in (leap) seconds. * * Assume 0..23 hours in a day, 0..59 minutes in an hour. The library spec * has changed: we can no longer assume that there are 0..59 seconds in a * minute. */ gmt = *gmtime(&when); lt = localtime(&when); gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min; if (lt->tm_year < gmt.tm_year) gmtoff -= DAY_MIN; else if (lt->tm_year > gmt.tm_year) gmtoff += DAY_MIN; else if (lt->tm_yday < gmt.tm_yday) gmtoff -= DAY_MIN; else if (lt->tm_yday > gmt.tm_yday) gmtoff += DAY_MIN; if (lt->tm_sec <= gmt.tm_sec - MIN_SEC) gmtoff -= 1; else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC) gmtoff += 1; /* * First, format the date and wall-clock time. XXX The %e format (day of * month, leading zero replaced by blank) isn't in my POSIX book, but * many vendors seem to support it. */ #ifdef MISSING_STRFTIME_E #define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S " #else #define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S " #endif while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0) VSTRING_SPACE(vp, 100); VSTRING_SKIP(vp); /* * Then, add the UTC offset. */ if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN) msg_panic("UTC time offset %d is larger than one day", gmtoff); vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN), (int) (abs(gmtoff) % HOUR_MIN)); /* * Finally, add the time zone name. */ while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0) VSTRING_SPACE(vp, vstring_avail(vp) + 100); VSTRING_SKIP(vp); return (vstring_str(vp)); }