예제 #1
0
const char *cleanup_strflags(unsigned flags)
{
    static VSTRING *result;
    unsigned i;

    if (flags == 0)
	return ("none");

    if (result == 0)
	result = vstring_alloc(20);
    else
	VSTRING_RESET(result);

    for (i = 0; i < sizeof(cleanup_flag_map) / sizeof(cleanup_flag_map[0]); i++) {
	if (cleanup_flag_map[i].flag & flags) {
	    vstring_sprintf_append(result, "%s ", cleanup_flag_map[i].text);
	    flags &= ~cleanup_flag_map[i].flag;
	}
    }

    if (flags != 0 || VSTRING_LEN(result) == 0)
	msg_panic("cleanup_strflags: unrecognized flag value(s) 0x%x", flags);

    vstring_truncate(result, VSTRING_LEN(result) - 1);
    VSTRING_TERMINATE(result);

    return (vstring_str(result));
}
예제 #2
0
int     memcache_get(VSTREAM *stream, VSTRING *vp, ssize_t bound)
{
    int     last_char;
    int     next_char;

    last_char = (bound == 0 ? vstring_get(vp, stream) :
		 vstring_get_bound(vp, stream, bound));

    switch (last_char) {

	/*
	 * Do some repair in the rare case that we stopped reading in the
	 * middle of the CRLF record terminator.
	 */
    case '\r':
	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
	    VSTRING_ADDCH(vp, '\n');
	    /* FALLTRHOUGH */
	} else {
	    if (next_char != VSTREAM_EOF)
		vstream_ungetc(stream, next_char);

	    /*
	     * Input too long, or EOF
	     */
    default:
	    if (msg_verbose)
		msg_info("%s got %s", VSTREAM_PATH(stream),
			 LEN(vp) < bound ? "EOF" : "input too long");
	    return (-1);
	}

	/*
	 * Strip off the record terminator: either CRLF or just bare LF.
	 */
    case '\n':
	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	if (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	VSTRING_TERMINATE(vp);
	if (msg_verbose)
	    msg_info("%s got: %s", VSTREAM_PATH(stream), STR(vp));
	return (0);
    }
}
예제 #3
0
VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno)
{
    int     ch;
    int     next;
    int     start;
    char   *cp;

    VSTRING_RESET(buf);

    /*
     * Ignore comment lines, all whitespace lines, and empty lines. Terminate
     * at EOF or at the beginning of the next logical line.
     */
    for (;;) {
	/* Read one line, possibly not newline terminated. */
	start = LEN(buf);
	while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
	    VSTRING_ADDCH(buf, ch);
	if (ch == '\n' && lineno != 0)
	    *lineno += 1;
	/* Ignore comment line, all whitespace line, or empty line. */
	for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++)
	     /* void */ ;
	if (cp == END(buf) || *cp == '#')
	    vstring_truncate(buf, start);
	/* Terminate at EOF or at the beginning of the next logical line. */
	if (ch == VSTREAM_EOF)
	    break;
	if (LEN(buf) > 0) {
	    if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF)
		vstream_ungetc(fp, next);
	    if (next != '#' && !ISSPACE(next))
		break;
	}
    }
    VSTRING_TERMINATE(buf);

    /*
     * Invalid input: continuing text without preceding text. Allowing this
     * would complicate "postconf -e", which implements its own multi-line
     * parsing routine. Do not abort, just warn, so that critical programs
     * like postmap do not leave behind a truncated table.
     */
    if (LEN(buf) > 0 && ISSPACE(*STR(buf))) {
	msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"",
		 VSTREAM_PATH(fp), STR(buf),
		 LEN(buf) > 30 ? "..." : "");
	return (readlline(buf, fp, lineno));
    }

    /*
     * Done.
     */
    return (LEN(buf) > 0 ? buf : 0);
}
예제 #4
0
static void mime_state_downgrade(MIME_STATE *state, int rec_type,
				         const char *text, int len)
{
    static char hexchars[] = "0123456789ABCDEF";
    const unsigned char *cp;
    int     ch;

#define QP_ENCODE(buffer, ch) { \
	VSTRING_ADDCH(buffer, '='); \
	VSTRING_ADDCH(buffer, hexchars[(ch >> 4) & 0xff]); \
	VSTRING_ADDCH(buffer, hexchars[ch & 0xf]); \
    }

    /*
     * Insert a soft line break when the output reaches a critical length
     * before we reach a hard line break.
     */
    for (cp = CU_CHAR_PTR(text); cp < CU_CHAR_PTR(text + len); cp++) {
	/* Critical length before hard line break. */
	if (LEN(state->output_buffer) > 72) {
	    VSTRING_ADDCH(state->output_buffer, '=');
	    VSTRING_TERMINATE(state->output_buffer);
	    BODY_OUT(state, REC_TYPE_NORM,
		     STR(state->output_buffer),
		     LEN(state->output_buffer));
	    VSTRING_RESET(state->output_buffer);
	}
	/* Append the next character. */
	ch = *cp;
	if ((ch < 32 && ch != '\t') || ch == '=' || ch > 126) {
	    QP_ENCODE(state->output_buffer, ch);
	} else {
	    VSTRING_ADDCH(state->output_buffer, ch);
	}
    }

    /*
     * Flush output after a hard line break (i.e. the end of a REC_TYPE_NORM
     * record). Fix trailing whitespace as per the RFC: in the worst case,
     * the output length will grow from 73 characters to 75 characters.
     */
    if (rec_type == REC_TYPE_NORM) {
	if (LEN(state->output_buffer) > 0
	    && ((ch = END(state->output_buffer)[-1]) == ' ' || ch == '\t')) {
	    vstring_truncate(state->output_buffer,
			     LEN(state->output_buffer) - 1);
	    QP_ENCODE(state->output_buffer, ch);
	}
	VSTRING_TERMINATE(state->output_buffer);
	BODY_OUT(state, REC_TYPE_NORM,
		 STR(state->output_buffer),
		 LEN(state->output_buffer));
	VSTRING_RESET(state->output_buffer);
    }
}
예제 #5
0
static void cleanup_rewrite_recip(CLEANUP_STATE *state,
				          const HEADER_OPTS *hdr_opts,
				          VSTRING *header_buf)
{
    TOK822 *tree;
    TOK822 **addr_list;
    TOK822 **tpp;
    int     did_rewrite = 0;

    if (msg_verbose)
	msg_info("rewrite_recip: %s", hdr_opts->name);

    /*
     * Parse the header line, rewrite each address found, and regenerate the
     * header line. Finally, pipe the result through the header line folding
     * routine.
     */
    tree = tok822_parse_limit(vstring_str(header_buf)
			      + strlen(hdr_opts->name) + 1,
			      var_token_limit);
    addr_list = tok822_grep(tree, TOK822_ADDR);
    for (tpp = addr_list; *tpp; tpp++) {
	did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp);
	if (state->flags & CLEANUP_FLAG_MAP_OK) {
	    if (cleanup_rcpt_canon_maps
		&& (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT))
		did_rewrite |=
		    cleanup_map11_tree(state, *tpp, cleanup_rcpt_canon_maps,
				cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
	    if (cleanup_comm_canon_maps
		&& (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT))
		did_rewrite |=
		    cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps,
				cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
	    if (cleanup_masq_domains
		&& (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_RCPT))
		did_rewrite |=
		    cleanup_masquerade_tree(state, *tpp, cleanup_masq_domains);
	}
    }
    if (did_rewrite) {
	vstring_truncate(header_buf, strlen(hdr_opts->name));
	vstring_strcat(header_buf, ": ");
	tok822_externalize(header_buf, tree, TOK822_STR_HEAD);
    }
    myfree((char *) addr_list);
    tok822_free_tree(tree);
    if ((hdr_opts->flags & HDR_OPT_DROP) == 0) {
	if (did_rewrite)
	    cleanup_fold_header(state, header_buf);
	else
	    cleanup_out_header(state, header_buf);
    }
}
예제 #6
0
int     psc_send_reply(PSC_STATE *state, const char *text)
{
    ssize_t start;
    int     ret;
    const char *footer;
    ssize_t text_len = strlen(text) - 2;

    if (msg_verbose)
	msg_info("> [%s]:%s: %.*s", state->smtp_client_addr,
		 state->smtp_client_port, (int) text_len, text);

    /*
     * Append the new text to earlier text that could not be sent because the
     * output was throttled.
     */
    start = VSTRING_LEN(state->send_buf);
    vstring_strcat(state->send_buf, text);

    /*
     * For soft_bounce support, we also fix the REJECT logging before the
     * dummy SMTP engine calls the psc_send_reply() output routine. We do
     * some double work, but it is for debugging only.
     */
    if (var_soft_bounce) {
	if (text[0] == '5')
	    STR(state->send_buf)[start + 0] = '4';
	if (text[4] == '5')
	    STR(state->send_buf)[start + 4] = '4';
    }

    /*
     * Append the optional reply footer.
     */
    if ((*text == '4' || *text == '5')
	&& ((psc_rej_ftr_maps != 0
	     && (footer = psc_get_footer(text, text_len)) != 0)
	    || *(footer = var_psc_rej_footer) != 0))
	smtp_reply_footer(state->send_buf, start, footer,
			  STR(psc_expand_filter), psc_expand_lookup,
			  (void *) state);

    /*
     * Do a best effort sending text, but don't block when the output is
     * throttled by a hostile peer.
     */
    ret = write(vstream_fileno(state->smtp_client_stream),
		STR(state->send_buf), LEN(state->send_buf));
    if (ret > 0)
	vstring_truncate(state->send_buf, ret - LEN(state->send_buf));
    if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET)
	msg_warn("write [%s]:%s: %m", state->smtp_client_addr,
		 state->smtp_client_port);
    return (ret < 0 && errno != EAGAIN);
}
예제 #7
0
파일: name_mask.c 프로젝트: aosm/postfix
const char *str_long_name_mask_opt(VSTRING *buf, const char *context,
				           const LONG_NAME_MASK * table,
				           long mask, int flags)
{
    const char *myname = "name_mask";
    int     len;
    static VSTRING *my_buf = 0;
    int     delim = (flags & NAME_MASK_COMMA ? ',' :
		     (flags & NAME_MASK_PIPE ? '|' : ' '));
    const LONG_NAME_MASK *np;

    if ((flags & STR_NAME_MASK_REQUIRED) == 0)
	msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag",
		  myname);

    if (buf == 0) {
	if (my_buf == 0)
	    my_buf = vstring_alloc(1);
	buf = my_buf;
    }
    VSTRING_RESET(buf);

    for (np = table; mask != 0; np++) {
	if (np->name == 0) {
	    if (flags & NAME_MASK_NUMBER) {
		vstring_sprintf_append(buf, "0x%lx%c", mask, delim);
	    } else if (flags & NAME_MASK_FATAL) {
		msg_fatal("%s: unknown %s bit in mask: 0x%lx",
			  myname, context, mask);
	    } else if (flags & NAME_MASK_RETURN) {
		msg_warn("%s: unknown %s bit in mask: 0x%lx",
			 myname, context, mask);
		return (0);
	    } else if (flags & NAME_MASK_WARN) {
		msg_warn("%s: unknown %s bit in mask: 0x%lx",
			 myname, context, mask);
	    }
	    break;
	}
	if (mask & np->mask) {
	    mask &= ~np->mask;
	    vstring_sprintf_append(buf, "%s%c", np->name, delim);
	}
    }
    if ((len = VSTRING_LEN(buf)) > 0)
	vstring_truncate(buf, len - 1);
    VSTRING_TERMINATE(buf);

    return (STR(buf));
}
예제 #8
0
static void qmqpd_copy_sender(QMQPD_STATE *state)
{
    char   *end_prefix;
    char   *end_origin;
    int     verp_requested;
    static char verp_delims[] = "-=";

    /*
     * If the sender address looks like prefix@origin-@[], then request
     * variable envelope return path delivery, with an envelope sender
     * address of prefi@origin, and with VERP delimiters of x and =. This
     * way, the recipients will see envelope sender addresses that look like:
     * prefixuser=domain@origin.
     */
    state->where = "receiving sender address";
    netstring_get(state->client, state->buf, var_line_limit);
    VSTRING_TERMINATE(state->buf);
    verp_requested =
	((end_origin = vstring_end(state->buf) - 4) > STR(state->buf)
	 && strcmp(end_origin, "-@[]") == 0
	 && (end_prefix = strchr(STR(state->buf), '@')) != 0	/* XXX */
	 && --end_prefix < end_origin - 2	/* non-null origin */
	 && end_prefix > STR(state->buf));	/* non-null prefix */
    if (verp_requested) {
	verp_delims[0] = end_prefix[0];
	if (verp_delims_verify(verp_delims) != 0) {
	    state->err |= CLEANUP_STAT_CONT;	/* XXX */
	    vstring_sprintf(state->why_rejected, "Invalid VERP delimiters: \"%s\". Need two characters from \"%s\"",
			    verp_delims, var_verp_filter);
	}
	memmove(end_prefix, end_prefix + 1, end_origin - end_prefix - 1);
	vstring_truncate(state->buf, end_origin - STR(state->buf) - 1);
    }
    if (state->err == CLEANUP_STAT_OK
	&& REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0)
	state->err = CLEANUP_STAT_WRITE;
    if (verp_requested)
	if (state->err == CLEANUP_STAT_OK
	    && rec_put(state->cleanup, REC_TYPE_VERP, verp_delims, 2) < 0)
	    state->err = CLEANUP_STAT_WRITE;
    state->sender = mystrndup(STR(state->buf), LEN(state->buf));
}
예제 #9
0
static void strip_address(VSTRING *vp, int start, TOK822 *addr)
{
    VSTRING *tmp;

    /*
     * Emit plain <address>. Discard any comments or phrases.
     */
    msg_warn("stripping too many comments from address: %.100s...",
	     printable(vstring_str(vp) + start, '?'));
    vstring_truncate(vp, start);
    VSTRING_ADDCH(vp, '<');
    if (addr) {
	tmp = vstring_alloc(100);
	tok822_internalize(tmp, addr, TOK822_STR_TERM);
	quote_822_local_flags(vp, vstring_str(tmp),
			      QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND);
	vstring_free(tmp);
    }
    VSTRING_ADDCH(vp, '>');
}
예제 #10
0
const HEADER_OPTS *header_opts_find(const char *string)
{
    const char *cp;

    if (header_hash == 0)
	header_opts_init();

    /*
     * Look up the lower-cased version of the header name.
     */
    VSTRING_RESET(header_key);
    for (cp = string; *cp != ':'; cp++) {
	if (*cp == 0)
	    msg_panic("header_opts_find: no colon in header: %.30s", string);
	VSTRING_ADDCH(header_key, TOLOWER(*cp));
    }
    vstring_truncate(header_key,
		     trimblanks(vstring_str(header_key), cp - string)
		     - vstring_str(header_key));
    VSTRING_TERMINATE(header_key);
    return ((const HEADER_OPTS *) htable_find(header_hash, vstring_str(header_key)));
}
예제 #11
0
int     main(int unused_argc, char **unused_argv)
{
	VSTRING *vp = vstring_alloc(100);
	TOK822 *list;
	VSTRING *buf = vstring_alloc(100);

#define TEST_TOKEN_LIMIT 20

	while (readlline(buf, VSTREAM_IN, (int *) 0)) {
		while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\n') {
			vstring_end(buf)[-1] = 0;
			vstring_truncate(buf, VSTRING_LEN(buf) - 1);
		}
		if (!isatty(vstream_fileno(VSTREAM_IN)))
			vstream_printf(">>>%s<<<\n\n", vstring_str(buf));
		list = tok822_parse_limit(vstring_str(buf), TEST_TOKEN_LIMIT);
		vstream_printf("Parse tree:\n");
		tok822_print(list, 0);
		vstream_printf("\n");

		vstream_printf("Internalized:\n%s\n\n",
				vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL)));
		vstream_fflush(VSTREAM_OUT);
		vstream_printf("Externalized, no newlines inserted:\n%s\n\n",
				vstring_str(tok822_externalize(vp, list,
						TOK822_STR_DEFL | TOK822_STR_TRNC)));
		vstream_fflush(VSTREAM_OUT);
		vstream_printf("Externalized, newlines inserted:\n%s\n\n",
				vstring_str(tok822_externalize(vp, list,
						TOK822_STR_DEFL | TOK822_STR_LINE | TOK822_STR_TRNC)));
		vstream_fflush(VSTREAM_OUT);
		tok822_free_tree(list);
	}
	vstring_free(vp);
	vstring_free(buf);
	return (0);
}
예제 #12
0
int     smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
{
    int     last_char;
    int     next_char;

    /*
     * It's painful to do I/O with records that may span multiple buffers.
     * Allow for partial long lines (we will read the remainder later) and
     * allow for lines ending in bare LF. The idea is to be liberal in what
     * we accept, strict in what we send.
     * 
     * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize
     * bare LF as record terminator.
     */
    smtp_timeout_reset(stream);
    last_char = (bound == 0 ? vstring_get(vp, stream) :
		 vstring_get_bound(vp, stream, bound));

    switch (last_char) {

	/*
	 * Do some repair in the rare case that we stopped reading in the
	 * middle of the CRLF record terminator.
	 */
    case '\r':
	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
	    VSTRING_ADDCH(vp, '\n');
	    last_char = '\n';
	    /* FALLTRHOUGH */
	} else {
	    if (next_char != VSTREAM_EOF)
		vstream_ungetc(stream, next_char);
	    break;
	}

	/*
	 * Strip off the record terminator: either CRLF or just bare LF.
	 * 
	 * XXX RFC 2821 disallows sending bare CR everywhere. We remove bare CR
	 * if received before CRLF, and leave it alone otherwise.
	 */
    case '\n':
	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	VSTRING_TERMINATE(vp);

	/*
	 * Partial line: just read the remainder later. If we ran into EOF,
	 * the next test will deal with it.
	 */
    default:
	break;
    }
    smtp_timeout_detect(stream);

    /*
     * EOF is bad, whether or not it happens in the middle of a record. Don't
     * allow data that was truncated because of EOF.
     */
    if (vstream_feof(stream) || vstream_ferror(stream)) {
	if (msg_verbose)
	    msg_info("smtp_get: EOF");
	vstream_longjmp(stream, SMTP_ERR_EOF);
    }
    return (last_char);
}
예제 #13
0
static void qmgr_message_resolve(QMGR_MESSAGE *message)
{
    static ARGV *defer_xport_argv;
    RECIPIENT_LIST list = message->rcpt_list;
    RECIPIENT *recipient;
    QMGR_TRANSPORT *transport = 0;
    QMGR_QUEUE *queue = 0;
    RESOLVE_REPLY reply;
    VSTRING *queue_name;
    char   *at;
    char  **cpp;
    char   *nexthop;
    ssize_t len;
    int     status;
    DSN     dsn;
    MSG_STATS stats;
    DSN    *saved_dsn;

#define STREQ(x,y)	(strcmp(x,y) == 0)
#define STR		vstring_str
#define LEN		VSTRING_LEN

    resolve_clnt_init(&reply);
    queue_name = vstring_alloc(1);
    for (recipient = list.info; recipient < list.info + list.len; recipient++) {

	/*
	 * Redirect overrides all else. But only once (per entire message).
	 * For consistency with the remainder of Postfix, rewrite the address
	 * to canonical form before resolving it.
	 */
	if (message->redirect_addr) {
	    if (recipient > list.info) {
		recipient->u.queue = 0;
		continue;
	    }
	    message->rcpt_offset = 0;
	    message->rcpt_unread = 0;

	    rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
				  reply.recipient);
	    RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Content filtering overrides the address resolver.
	 * 
	 * XXX Bypass content_filter inspection for user-generated probes
	 * (sendmail -bv). MTA-generated probes never have the "please filter
	 * me" bits turned on, but we handle them here anyway for the sake of
	 * future proofing.
	 */
#define FILTER_WITHOUT_NEXTHOP(filter, next) \
	(((next) = split_at((filter), ':')) == 0 || *(next) == 0)

#define RCPT_WITHOUT_DOMAIN(rcpt, next) \
	((next = strrchr(rcpt, '@')) == 0 || *++(next) == 0)

	else if (message->filter_xport
		 && (message->tflags & DEL_REQ_TRACE_ONLY_MASK) == 0) {
	    reply.flags = 0;
	    vstring_strcpy(reply.transport, message->filter_xport);
	    if (FILTER_WITHOUT_NEXTHOP(STR(reply.transport), nexthop)
		&& *(nexthop = var_def_filter_nexthop) == 0
		&& RCPT_WITHOUT_DOMAIN(recipient->address, nexthop))
		nexthop = var_myhostname;
	    vstring_strcpy(reply.nexthop, nexthop);
	    vstring_strcpy(reply.recipient, recipient->address);
	}

	/*
	 * Resolve the destination to (transport, nexthop, address). The
	 * result address may differ from the one specified by the sender.
	 */
	else {
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Bounce null recipients. This should never happen, but is most
	 * likely the result of a fault in a different program, so aborting
	 * the queue manager process does not help.
	 */
	if (recipient->address[0] == 0) {
	    QMGR_REDIRECT(&reply, MAIL_SERVICE_ERROR,
			  "5.1.3 null recipient address");
	}

	/*
	 * Discard mail to the local double bounce address here, so this
	 * system can run without a local delivery agent. They'd still have
	 * to configure something for mail directed to the local postmaster,
	 * though, but that is an RFC requirement anyway.
	 * 
	 * XXX This lookup should be done in the resolver, and the mail should
	 * be directed to a general-purpose null delivery agent.
	 */
	if (reply.flags & RESOLVE_CLASS_LOCAL) {
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
			    len) == 0
		&& !var_double_bounce_sender[len]) {
		status = sent(message->tflags, message->queue_id,
			      QMGR_MSG_STATS(&stats, message), recipient,
			      "none", DSN_SIMPLE(&dsn, "2.0.0",
			"undeliverable postmaster notification discarded"));
		if (status == 0) {
		    deliver_completed(message->fp, recipient->offset);
#if 0
		    /* It's the default verification probe sender address. */
		    msg_warn("%s: undeliverable postmaster notification discarded",
			     message->queue_id);
#endif
		} else
		    message->flags |= status;
		continue;
	    }
	}

	/*
	 * Optionally defer deliveries over specific transports, unless the
	 * restriction is lifted temporarily.
	 */
	if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
	    if (defer_xport_argv == 0)
		defer_xport_argv = argv_split(var_defer_xports, CHARS_COMMA_SP);
	    for (cpp = defer_xport_argv->argv; *cpp; cpp++)
		if (strcmp(*cpp, STR(reply.transport)) == 0)
		    break;
	    if (*cpp) {
		QMGR_REDIRECT(&reply, MAIL_SERVICE_RETRY,
			      "4.3.2 deferred transport");
	    }
	}

	/*
	 * Look up or instantiate the proper transport.
	 */
	if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
	    if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
		transport = qmgr_transport_create(STR(reply.transport));
	    queue = 0;
	}

	/*
	 * This message is being flushed. If need-be unthrottle the
	 * transport.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_TRANSPORT_THROTTLED(transport))
	    qmgr_transport_unthrottle(transport);

	/*
	 * This transport is dead. Defer delivery to this recipient.
	 */
	if (QMGR_TRANSPORT_THROTTLED(transport)) {
	    saved_dsn = transport->dsn;
	    if ((transport = qmgr_error_transport(MAIL_SERVICE_RETRY)) != 0) {
		nexthop = qmgr_error_nexthop(saved_dsn);
		vstring_strcpy(reply.nexthop, nexthop);
		myfree(nexthop);
		queue = 0;
	    } else {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * The nexthop destination provides the default name for the
	 * per-destination queue. When the delivery agent accepts only one
	 * recipient per delivery, give each recipient its own queue, so that
	 * deliveries to different recipients of the same message can happen
	 * in parallel, and so that we can enforce per-recipient concurrency
	 * limits and prevent one recipient from tying up all the delivery
	 * agent resources. We use recipient@nexthop as queue name rather
	 * than the actual recipient domain name, so that one recipient in
	 * multiple equivalent domains cannot evade the per-recipient
	 * concurrency limit. Split the address on the recipient delimiter if
	 * one is defined, so that extended addresses don't get extra
	 * delivery slots.
	 * 
	 * Fold the result to lower case so that we don't have multiple queues
	 * for the same name.
	 * 
	 * Important! All recipients in a queue must have the same nexthop
	 * value. It is OK to have multiple queues with the same nexthop
	 * value, but only when those queues are named after recipients.
	 * 
	 * The single-recipient code below was written for local(8) like
	 * delivery agents, and assumes that all domains that deliver to the
	 * same (transport + nexthop) are aliases for $nexthop. Delivery
	 * concurrency is changed from per-domain into per-recipient, by
	 * changing the queue name from nexthop into localpart@nexthop.
	 * 
	 * XXX This assumption is incorrect when different destinations share
	 * the same (transport + nexthop). In reality, such transports are
	 * rarely configured to use single-recipient deliveries. The fix is
	 * to decouple the per-destination recipient limit from the
	 * per-destination concurrency.
	 */
	vstring_strcpy(queue_name, STR(reply.nexthop));
	if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
	    && strcmp(transport->name, MAIL_SERVICE_RETRY) != 0
	    && transport->recipient_limit == 1) {
	    /* Copy the recipient localpart. */
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    vstring_strncpy(queue_name, STR(reply.recipient), len);
	    /* Remove the address extension from the recipient localpart. */
	    if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim))
		vstring_truncate(queue_name, strlen(STR(queue_name)));
	    /* Assume the recipient domain is equivalent to nexthop. */
	    vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
	}
	lowercase(STR(queue_name));

	/*
	 * This transport is alive. Find or instantiate a queue for this
	 * recipient.
	 */
	if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
	    if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
		queue = qmgr_queue_create(transport, STR(queue_name),
					  STR(reply.nexthop));
	}

	/*
	 * This message is being flushed. If need-be unthrottle the queue.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_QUEUE_THROTTLED(queue))
	    qmgr_queue_unthrottle(queue);

	/*
	 * This queue is dead. Defer delivery to this recipient.
	 */
	if (QMGR_QUEUE_THROTTLED(queue)) {
	    saved_dsn = queue->dsn;
	    if ((queue = qmgr_error_queue(MAIL_SERVICE_RETRY, saved_dsn)) == 0) {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * This queue is alive. Bind this recipient to this queue instance.
	 */
	recipient->u.queue = queue;
    }
    resolve_clnt_free(&reply);
    vstring_free(queue_name);
}
예제 #14
0
static void enqueue(const int flags, const char *encoding,
		         const char *dsn_envid, int dsn_ret, int dsn_notify,
		            const char *rewrite_context, const char *sender,
		            const char *full_name, char **recipients)
{
    VSTRING *buf;
    VSTREAM *dst;
    char   *saved_sender;
    char  **cpp;
    int     type;
    char   *start;
    int     skip_from_;
    TOK822 *tree;
    TOK822 *tp;
    int     rcpt_count = 0;
    enum {
	STRIP_CR_DUNNO, STRIP_CR_DO, STRIP_CR_DONT, STRIP_CR_ERROR
    }       strip_cr;
    MAIL_STREAM *handle;
    VSTRING *postdrop_command;
    uid_t   uid = getuid();
    int     status;
    int     naddr;
    int     prev_type;
    MIME_STATE *mime_state = 0;
    SM_STATE state;
    int     mime_errs;
    const char *errstr;
    int     addr_count;
    int     level;
    static NAME_CODE sm_fix_eol_table[] = {
	SM_FIX_EOL_ALWAYS, STRIP_CR_DO,
	SM_FIX_EOL_STRICT, STRIP_CR_DUNNO,
	SM_FIX_EOL_NEVER, STRIP_CR_DONT,
	0, STRIP_CR_ERROR,
    };

    /*
     * Access control is enforced in the postdrop command. The code here
     * merely produces a more user-friendly interface.
     */
    if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL,
				       var_submit_acl, uid)) != 0)
	msg_fatal_status(EX_NOPERM,
	  "User %s(%ld) is not allowed to submit mail", errstr, (long) uid);

    /*
     * Initialize.
     */
    buf = vstring_alloc(100);

    /*
     * Stop run-away process accidents by limiting the queue file size. This
     * is not a defense against DOS attack.
     */
    if (var_message_limit > 0 && get_file_limit() > var_message_limit)
	set_file_limit((off_t) var_message_limit);

    /*
     * The sender name is provided by the user. In principle, the mail pickup
     * service could deduce the sender name from queue file ownership, but:
     * pickup would not be able to run chrooted, and it may not be desirable
     * to use login names at all.
     */
    if (sender != 0) {
	VSTRING_RESET(buf);
	VSTRING_TERMINATE(buf);
	tree = tok822_parse(sender);
	for (naddr = 0, tp = tree; tp != 0; tp = tp->next)
	    if (tp->type == TOK822_ADDR && naddr++ == 0)
		tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
	tok822_free_tree(tree);
	saved_sender = mystrdup(STR(buf));
	if (naddr > 1)
	    msg_warn("-f option specified malformed sender: %s", sender);
    } else {
	if ((sender = username()) == 0)
	    msg_fatal_status(EX_OSERR, "no login name found for user ID %lu",
			     (unsigned long) uid);
	saved_sender = mystrdup(sender);
    }

    /*
     * Let the postdrop command open the queue file for us, and sanity check
     * the content. XXX Make postdrop a manifest constant.
     */
    errno = 0;
    postdrop_command = vstring_alloc(1000);
    vstring_sprintf(postdrop_command, "%s/postdrop -r", var_command_dir);
    for (level = 0; level < msg_verbose; level++)
	vstring_strcat(postdrop_command, " -v");
    if ((handle = mail_stream_command(STR(postdrop_command))) == 0)
	msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
			 saved_sender, (long) uid, STR(postdrop_command));
    vstring_free(postdrop_command);
    dst = handle->stream;

    /*
     * First, write envelope information to the output stream.
     * 
     * For sendmail compatibility, parse each command-line recipient as if it
     * were an RFC 822 message header; some MUAs specify comma-separated
     * recipient lists; and some MUAs even specify "word word <address>".
     * 
     * Sort-uniq-ing the recipient list is done after address canonicalization,
     * before recipients are written to queue file. That's cleaner than
     * having the queue manager nuke duplicate recipient status records.
     * 
     * XXX Should limit the size of envelope records.
     * 
     * With "sendmail -N", instead of a per-message NOTIFY record we store one
     * per recipient so that we can simplify the implementation somewhat.
     */
    if (dsn_envid)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
		    MAIL_ATTR_DSN_ENVID, dsn_envid);
    if (dsn_ret)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
		    MAIL_ATTR_DSN_RET, dsn_ret);
    rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
		MAIL_ATTR_RWR_CONTEXT, rewrite_context);
    if (full_name || (full_name = fullname()) != 0)
	rec_fputs(dst, REC_TYPE_FULL, full_name);
    rec_fputs(dst, REC_TYPE_FROM, saved_sender);
    if (verp_delims && *saved_sender == 0)
	msg_fatal_status(EX_USAGE,
		      "%s(%ld): -V option requires non-null sender address",
			 saved_sender, (long) uid);
    if (encoding)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ENCODING, encoding);
    if (DEL_REQ_TRACE_FLAGS(flags))
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_TRACE_FLAGS,
		    DEL_REQ_TRACE_FLAGS(flags));
    if (verp_delims)
	rec_fputs(dst, REC_TYPE_VERP, verp_delims);
    if (recipients) {
	for (cpp = recipients; *cpp != 0; cpp++) {
	    tree = tok822_parse(*cpp);
	    for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) {
		if (tp->type == TOK822_ADDR) {
		    tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
		    if (dsn_notify)
			rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
				    MAIL_ATTR_DSN_NOTIFY, dsn_notify);
		    if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
			msg_fatal_status(EX_TEMPFAIL,
				    "%s(%ld): error writing queue file: %m",
					 saved_sender, (long) uid);
		    ++rcpt_count;
		    ++addr_count;
		}
	    }
	    tok822_free_tree(tree);
	    if (addr_count == 0) {
		if (rec_put(dst, REC_TYPE_RCPT, "", 0) < 0)
		    msg_fatal_status(EX_TEMPFAIL,
				     "%s(%ld): error writing queue file: %m",
				     saved_sender, (long) uid);
		++rcpt_count;
	    }
	}
    }

    /*
     * Append the message contents to the queue file. Write chunks of at most
     * 1kbyte. Internally, we use different record types for data ending in
     * LF and for data that doesn't, so we can actually be binary transparent
     * for local mail. Unfortunately, SMTP has no record continuation
     * convention, so there is no guarantee that arbitrary data will be
     * delivered intact via SMTP. Strip leading From_ lines. For the benefit
     * of UUCP environments, also get rid of leading >>>From_ lines.
     */
    rec_fputs(dst, REC_TYPE_MESG, "");
    if (DEL_REQ_TRACE_ONLY(flags) != 0) {
	if (flags & SM_FLAG_XRCPT)
	    msg_fatal_status(EX_USAGE, "%s(%ld): -t option cannot be used with -bv",
			     saved_sender, (long) uid);
	if (*saved_sender)
	    rec_fprintf(dst, REC_TYPE_NORM, "From: %s", saved_sender);
	rec_fprintf(dst, REC_TYPE_NORM, "Subject: probe");
	if (recipients) {
	    rec_fprintf(dst, REC_TYPE_CONT, "To:");
	    for (cpp = recipients; *cpp != 0; cpp++) {
		rec_fprintf(dst, REC_TYPE_NORM, "	%s%s",
			    *cpp, cpp[1] ? "," : "");
	    }
	}
    } else {

	/*
	 * Initialize the MIME processor and set up the callback context.
	 */
	if (flags & SM_FLAG_XRCPT) {
	    state.dst = dst;
	    state.recipients = argv_alloc(2);
	    state.resent_recip = argv_alloc(2);
	    state.resent = 0;
	    state.saved_sender = saved_sender;
	    state.uid = uid;
	    state.temp = vstring_alloc(10);
	    mime_state = mime_state_alloc(MIME_OPT_DISABLE_MIME
					  | MIME_OPT_REPORT_TRUNC_HEADER,
					  output_header,
					  (MIME_STATE_ANY_END) 0,
					  output_text,
					  (MIME_STATE_ANY_END) 0,
					  (MIME_STATE_ERR_PRINT) 0,
					  (void *) &state);
	}

	/*
	 * Process header/body lines.
	 */
	skip_from_ = 1;
	strip_cr = name_code(sm_fix_eol_table, NAME_CODE_FLAG_STRICT_CASE,
			     var_sm_fix_eol);
	if (strip_cr == STRIP_CR_ERROR)
	    msg_fatal_status(EX_USAGE,
		    "invalid %s value: %s", VAR_SM_FIX_EOL, var_sm_fix_eol);
	for (prev_type = 0; (type = rec_streamlf_get(VSTREAM_IN, buf, var_line_limit))
	     != REC_TYPE_EOF; prev_type = type) {
	    if (strip_cr == STRIP_CR_DUNNO && type == REC_TYPE_NORM) {
		if (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
		    strip_cr = STRIP_CR_DO;
		else
		    strip_cr = STRIP_CR_DONT;
	    }
	    if (skip_from_) {
		if (type == REC_TYPE_NORM) {
		    start = STR(buf);
		    if (strncmp(start + strspn(start, ">"), "From ", 5) == 0)
			continue;
		}
		skip_from_ = 0;
	    }
	    if (strip_cr == STRIP_CR_DO && type == REC_TYPE_NORM)
		while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
		    vstring_truncate(buf, VSTRING_LEN(buf) - 1);
	    if ((flags & SM_FLAG_AEOF) && prev_type != REC_TYPE_CONT
		&& VSTRING_LEN(buf) == 1 && *STR(buf) == '.')
		break;
	    if (mime_state) {
		mime_errs = mime_state_update(mime_state, type, STR(buf),
					      VSTRING_LEN(buf));
		if (mime_errs)
		    msg_fatal_status(EX_DATAERR,
				"%s(%ld): unable to extract recipients: %s",
				     saved_sender, (long) uid,
				     mime_state_error(mime_errs));
	    } else {
		if (REC_PUT_BUF(dst, type, buf) < 0)
		    msg_fatal_status(EX_TEMPFAIL,
				     "%s(%ld): error writing queue file: %m",
				     saved_sender, (long) uid);
	    }
	}
    }

    /*
     * Finish MIME processing. We need a final mime_state_update() call in
     * order to flush text that is still buffered. That can happen when the
     * last line did not end in newline.
     */
    if (mime_state) {
	mime_errs = mime_state_update(mime_state, REC_TYPE_EOF, "", 0);
	if (mime_errs)
	    msg_fatal_status(EX_DATAERR,
			     "%s(%ld): unable to extract recipients: %s",
			     saved_sender, (long) uid,
			     mime_state_error(mime_errs));
	mime_state = mime_state_free(mime_state);
    }

    /*
     * Append recipient addresses that were extracted from message headers.
     */
    rec_fputs(dst, REC_TYPE_XTRA, "");
    if (flags & SM_FLAG_XRCPT) {
	for (cpp = state.resent ? state.resent_recip->argv :
	     state.recipients->argv; *cpp; cpp++) {
	    if (dsn_notify)
		rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
			    MAIL_ATTR_DSN_NOTIFY, dsn_notify);
	    if (rec_put(dst, REC_TYPE_RCPT, *cpp, strlen(*cpp)) < 0)
		msg_fatal_status(EX_TEMPFAIL,
				 "%s(%ld): error writing queue file: %m",
				 saved_sender, (long) uid);
	    ++rcpt_count;
	}
	argv_free(state.recipients);
	argv_free(state.resent_recip);
	vstring_free(state.temp);
    }
    if (rcpt_count == 0)
	msg_fatal_status(EX_USAGE, (flags & SM_FLAG_XRCPT) ?
		 "%s(%ld): No recipient addresses found in message header" :
			 "%s(%ld): Recipient addresses must be specified on"
			 " the command line or via the -t option",
			 saved_sender, (long) uid);

    /*
     * Identify the end of the queue file.
     */
    rec_fputs(dst, REC_TYPE_END, "");

    /*
     * Make sure that the message makes it to the file system. Once we have
     * terminated with successful exit status we cannot lose the message due
     * to "frivolous reasons". If all goes well, prevent the run-time error
     * handler from removing the file.
     */
    if (vstream_ferror(VSTREAM_IN))
	msg_fatal_status(EX_DATAERR, "%s(%ld): error reading input: %m",
			 saved_sender, (long) uid);
    if ((status = mail_stream_finish(handle, (VSTRING *) 0)) != 0)
	msg_fatal_status((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
			 (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
			 EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
			 (long) uid, cleanup_strerror(status));

    /*
     * Don't leave them in the dark.
     */
    if (DEL_REQ_TRACE_FLAGS(flags)) {
	vstream_printf("Mail Delivery Status Report will be mailed to <%s>.\n",
		       saved_sender);
	vstream_fflush(VSTREAM_OUT);
    }

    /*
     * Cleanup. Not really necessary as we're about to exit, but good for
     * debugging purposes.
     */
    vstring_free(buf);
    myfree(saved_sender);
}
예제 #15
0
파일: pipe.c 프로젝트: teonsystems/postfix
static int eval_command_status(int command_status, char *service,
			          DELIVER_REQUEST *request, PIPE_ATTR *attr,
			               DSN_BUF *why)
{
    RECIPIENT *rcpt;
    int     status;
    int     result = 0;
    int     n;
    char   *saved_text;

    /*
     * Depending on the result, bounce or defer the message, and mark the
     * recipient as done where appropriate.
     */
    switch (command_status) {
    case PIPE_STAT_OK:
	/* Save the command output before dsb_update() clobbers it. */
	vstring_truncate(why->reason, trimblanks(STR(why->reason),
			      VSTRING_LEN(why->reason)) - STR(why->reason));
	if (VSTRING_LEN(why->reason) > 0) {
	    VSTRING_TERMINATE(why->reason);
	    saved_text =
		vstring_export(vstring_sprintf(
				    vstring_alloc(VSTRING_LEN(why->reason)),
					    " (%.100s)", STR(why->reason)));
	} else
	    saved_text = mystrdup("");		/* uses shared R/O storage */
	dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ?
		   "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
		   "delivered via %s service%s", service, saved_text);
	myfree(saved_text);
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
			  request->queue_id, &request->msg_stats, rcpt,
			  service, &why->dsn);
	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_BOUNCE:
    case PIPE_STAT_DEFER:
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    /* XXX Maybe encapsulate this with ndr_append(). */
	    status = (STR(why->status)[0] != '4' ?
		      bounce_append : defer_append)
		(DEL_REQ_TRACE_FLAGS(request->flags),
		 request->queue_id,
		 &request->msg_stats, rcpt,
		 service, &why->dsn);
	    if (status == 0)
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_CORRUPT:
	/* XXX DSN should we send something? */
	result |= DEL_STAT_DEFER;
	break;
    default:
	msg_panic("eval_command_status: bad status %d", command_status);
	/* NOTREACHED */
    }

    return (result);
}
예제 #16
0
const char *make_verify_sender_addr(void)
{
    static VSTRING *verify_sender_buf;	/* the complete sender address */
    static VSTRING *my_epoch_buf;	/* scratch space */
    char   *my_at_domain;

    /*
     * The null sender is always time-independent.
     */
    if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0)
	return ("");

    /*
     * Sanity check.
     */
    if (*var_verify_sender == '@')
	msg_fatal("parameter %s: value \"%s\" must not start with '@'",
		  VAR_VERIFY_SENDER, var_verify_sender);
    if ((my_at_domain = strchr(var_verify_sender, '@')) != 0 && my_at_domain[1] == 0)
	msg_fatal("parameter %s: value \"%s\" must not end with '@'",
		  VAR_VERIFY_SENDER, var_verify_sender);

    /*
     * One-time initialization.
     */
    if (verify_sender_buf == 0) {
	verify_sender_buf = vstring_alloc(10);
	my_epoch_buf = vstring_alloc(10);
    }

    /*
     * Start with the bare sender address.
     */
    vstring_strcpy(verify_sender_buf, var_verify_sender);

    /*
     * Append the time stamp to the address localpart, encoded in some
     * non-decimal form for obscurity.
     * 
     * XXX It would be nice to have safe_ultostr() append-only support.
     */
    if (var_verify_sender_ttl > 0) {
	/* Strip the @domain portion, if applicable. */
	if (my_at_domain != 0)
	    vstring_truncate(verify_sender_buf,
			     (ssize_t) (my_at_domain - var_verify_sender));
	/* Append the time stamp to the address localpart. */
	vstring_sprintf_append(verify_sender_buf, "%s",
			       safe_ultostr(my_epoch_buf,
					    VERIFY_SENDER_ADDR_EPOCH(),
					    VERIFY_BASE, 0, 0));
	/* Add back the @domain, if applicable. */
	if (my_at_domain != 0)
	    vstring_sprintf_append(verify_sender_buf, "%s", my_at_domain);
    }

    /*
     * Rewrite the address to canonical form.
     */
    rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(verify_sender_buf),
			  verify_sender_buf);

    return (STR(verify_sender_buf));
}
예제 #17
0
ARGV   *mail_addr_map(MAPS *path, const char *address, int propagate)
{
    VSTRING *buffer = 0;
    const char *myname = "mail_addr_map";
    const char *string;
    char   *ratsign;
    char   *extension = 0;
    ARGV   *argv = 0;
    int     i;

    /*
     * Look up the full address; if no match is found, look up the address
     * with the extension stripped off, and remember the unmatched extension.
     */
    if ((string = mail_addr_find(path, address, &extension)) != 0) {

	/*
	 * Prepend the original user to @otherdomain, but do not propagate
	 * the unmatched address extension.
	 */
	if (*string == '@') {
	    buffer = vstring_alloc(100);
	    if ((ratsign = strrchr(address, '@')) != 0)
		vstring_strncpy(buffer, address, ratsign - address);
	    else
		vstring_strcpy(buffer, address);
	    if (extension)
		vstring_truncate(buffer, LEN(buffer) - strlen(extension));
	    vstring_strcat(buffer, string);
	    string = STR(buffer);
	}

	/*
	 * Canonicalize and externalize the result, and propagate the
	 * unmatched extension to each address found.
	 */
	argv = mail_addr_crunch(string, propagate ? extension : 0);
	if (buffer)
	    vstring_free(buffer);
	if (msg_verbose)
	    for (i = 0; i < argv->argc; i++)
		msg_info("%s: %s -> %d: %s", myname, address, i, argv->argv[i]);
	if (argv->argc == 0) {
	    msg_warn("%s lookup of %s returns non-address result \"%s\"",
		     path->title, address, string);
	    argv = argv_free(argv);
	    path->error = DICT_ERR_RETRY;
	}
    }

    /*
     * No match found.
     */
    else {
	if (msg_verbose)
	    msg_info("%s: %s -> %s", myname, address,
		     path->error ? "(try again)" : "(not found)");
    }

    /*
     * Cleanup.
     */
    if (extension)
	myfree(extension);

    return (argv);
}