コード例 #1
0
ファイル: tok822_tree.cpp プロジェクト: 1514louluo/acl
TOK822 *tok822_free_tree(TOK822 *tp)
{
	if (tp) {
		if (tp->next)
			tok822_free_tree(tp->next);
		if (tp->head)
			tok822_free_tree(tp->head);
		tok822_free(tp);
	}
	return (0);
}
コード例 #2
0
ファイル: mime_state_parse.cpp プロジェクト: aaronshang/acl
static void mail_rcpt(MIME_NODE *node, const HEADER_OPTS *header_info)
{
	TOK822 *tree;
	TOK822 **addr_list;
	TOK822 **tpp;
	ACL_VSTRING *temp;
	const char *cp;

	temp = acl_vstring_alloc(10);
	cp = STR(node->buffer) + strlen(header_info->name) + 1;
	tree = tok822_parse(cp);
	addr_list = tok822_grep(tree, TOK822_ADDR);
	for (tpp = addr_list; *tpp; tpp++) {
		tok822_internalize(temp, tpp[0]->head, TOK822_STR_DEFL);
		if (header_info->type == HDR_TO) {
			node->header_to_list =
				mail_addr_add(node->header_to_list, STR(temp));
		} else if (header_info->type == HDR_CC) {
			node->header_cc_list =
				mail_addr_add(node->header_cc_list, STR(temp));
		} else if (header_info->type == HDR_BCC) {
			node->header_bcc_list =
				mail_addr_add(node->header_bcc_list, STR(temp));
		}
	}

	acl_myfree(addr_list);
	tok822_free_tree(tree);
	acl_vstring_free(temp);
}
コード例 #3
0
ファイル: rfc822.cpp プロジェクト: FlowSea/acl
const std::list<rfc822_addr*>& rfc822::parse_addrs(const char* in,
	const char* to_charset /* = "utf-8" */)
{
	reset();

	if (to_charset == NULL)
		to_charset = "gb18030";

	if (in == NULL || *in == 0)
	{
		logger_error("input invalid");
		return (addrs_);
	}
	TOK822 *tree = tok822_parse(in);
	if (tree == NULL)
	{
		logger_error("tok822_parse(%s) error", in);
		return (addrs_);
	}

	const ACL_VSTRING* comment_prev = NULL;
	string buf;

	for (TOK822 *tp = tree; tp; tp = tp->next)
	{
		if (tp->type == TOK822_ATOM
			|| tp->type == TOK822_COMMENT
			|| tp->type == TOK822_QSTRING
			|| tp->vstr != NULL)
		{
			comment_prev = tp->vstr;
		}

		if (tp->type != TOK822_ADDR || tp->head == NULL)
			continue;

		ACL_VSTRING* addrp = acl_vstring_alloc(32);
		(void) tok822_internalize(addrp, tp->head, TOK822_STR_DEFL);
		rfc822_addr* addr = (rfc822_addr*)
			acl_mymalloc(sizeof(rfc822_addr));
		addr->addr = acl_vstring_export(addrp);
		if (comment_prev)
		{
			buf.clear();
			rfc2047::decode(STR(comment_prev),
				(int) LEN(comment_prev), &buf, to_charset);
			addr->comment = acl_mystrdup(buf.c_str());
			comment_prev = NULL;
		}
		else
			addr->comment = NULL;
		addrs_.push_back(addr);
	}

	tok822_free_tree(tree);
	return (addrs_);
}
コード例 #4
0
ファイル: resolve.c プロジェクト: ajinkya93/netbsd-src
int     deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
{
    TOK822 *tree;
    int     result;

    tree = tok822_scan_addr(addr);
    result = deliver_resolve_tree(state, usr_attr, tree);
    tok822_free_tree(tree);
    return (result);
}
コード例 #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
ファイル: cleanup_rewrite.c プロジェクト: TonyChengTW/Rmail
void    cleanup_rewrite_tree(TOK822 *tree)
{
    VSTRING *dst = vstring_alloc(100);
    VSTRING *src = vstring_alloc(100);

    tok822_externalize(src, tree->head, TOK822_STR_DEFL);
    cleanup_rewrite_external(dst, STR(src));
    tok822_free_tree(tree->head);
    tree->head = tok822_scan(STR(dst), &tree->tail);
    vstring_free(dst);
    vstring_free(src);
}
コード例 #7
0
ファイル: rewrite.c プロジェクト: terbolous/postfix
int     rewrite_proto(VSTREAM *stream)
{
    RWR_CONTEXT *context;
    TOK822 *tree;

    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_STR, MAIL_ATTR_RULE, ruleset,
		  ATTR_TYPE_STR, MAIL_ATTR_ADDR, address,
		  ATTR_TYPE_END) != 2)
	return (-1);

    if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_LOCAL) == 0)
	context = &local_context;
    else if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_REMOTE) == 0)
	context = &remote_context;
    else {
	msg_warn("unknown context: %s", vstring_str(ruleset));
	return (-1);
    }

    /*
     * Sanity check. An address is supposed to be in externalized form.
     */
    if (*vstring_str(address) == 0) {
	msg_warn("rewrite_addr: null address");
	vstring_strcpy(result, vstring_str(address));
    }

    /*
     * Convert the address from externalized (quoted) form to token list,
     * rewrite it, and convert back.
     */
    else {
	tree = tok822_scan_addr(vstring_str(address));
	rewrite_tree(context, tree);
	tok822_externalize(result, tree, TOK822_STR_DEFL);
	tok822_free_tree(tree);
    }
    if (msg_verbose)
	msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset),
		 vstring_str(address), vstring_str(result));

    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_INT, MAIL_ATTR_FLAGS, server_flags,
	       ATTR_TYPE_STR, MAIL_ATTR_ADDR, vstring_str(result),
	       ATTR_TYPE_END);

    if (vstream_fflush(stream) != 0) {
	msg_warn("write rewrite reply: %m");
	return (-1);
    }
    return (0);
}
コード例 #8
0
ファイル: smtp_map11.c プロジェクト: hiroya/postfix
int     smtp_map11_tree(TOK822 *tree, MAPS *maps, int propagate)
{
    VSTRING *temp = vstring_alloc(100);
    int     ret;

    tok822_externalize(temp, tree->head, TOK822_STR_DEFL);
    ret = smtp_map11_external(temp, maps, propagate);
    tok822_free_tree(tree->head);
    tree->head = tok822_scan(STR(temp), &tree->tail);
    vstring_free(temp);
    return (ret);
}
コード例 #9
0
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);
}
コード例 #10
0
ファイル: cleanup_rewrite.c プロジェクト: DabeDotCom/postfix
int    cleanup_rewrite_tree(const char *context_name, TOK822 *tree)
{
    VSTRING *dst = vstring_alloc(100);
    VSTRING *src = vstring_alloc(100);
    int     did_rewrite;

    tok822_externalize(src, tree->head, TOK822_STR_DEFL);
    did_rewrite = cleanup_rewrite_external(context_name, dst, STR(src));
    tok822_free_tree(tree->head);
    tree->head = tok822_scan(STR(dst), &tree->tail);
    vstring_free(dst);
    vstring_free(src);
    return (did_rewrite);
}
コード例 #11
0
ファイル: cleanup_map11.c プロジェクト: TonyChengTW/Rmail
void    cleanup_map11_tree(CLEANUP_STATE *state, TOK822 *tree,
			           MAPS *maps, int propagate)
{
    VSTRING *temp = vstring_alloc(100);

    /*
     * Produce sensible output even in the face of a recoverable error. This
     * simplifies error recovery considerably because we can do delayed error
     * checking in one place, instead of having error handling code all over
     * the place.
     */
    tok822_externalize(temp, tree->head, TOK822_STR_DEFL);
    cleanup_map11_external(state, temp, maps, propagate);
    tok822_free_tree(tree->head);
    tree->head = tok822_scan(STR(temp), &tree->tail);
    vstring_free(temp);
}
コード例 #12
0
ファイル: mime_state_parse.cpp プロジェクト: aaronshang/acl
static void mail_from(MIME_NODE *node, const HEADER_OPTS *header_info)
{
	//MIME_STATE *state = node->state;
	TOK822 *tree;
	TOK822 **addr_list;
	TOK822 **tpp;
	ACL_VSTRING *temp;
	const char *cp;

	temp = acl_vstring_alloc(10);
	cp = STR(node->buffer) + strlen(header_info->name) + 1;
	tree = tok822_parse(cp);
	addr_list = tok822_grep(tree, TOK822_ADDR);
	for (tpp = addr_list; *tpp; tpp++) {
		tok822_internalize(temp, tpp[0]->head, TOK822_STR_DEFL);
		if (header_info->type == HDR_SENDER) {
			if (node->header_sender)
				acl_myfree(node->header_sender);
			node->header_sender = acl_mystrdup(STR(temp));
			break;
		} else if (header_info->type == HDR_FROM) {
			if (node->header_from)
				acl_myfree(node->header_from);
			node->header_from = acl_mystrdup(STR(temp));
			break;
		} else if (header_info->type == HDR_REPLY_TO) {
			if (node->header_replyto)
				acl_myfree(node->header_replyto);
			node->header_replyto = acl_mystrdup(STR(temp));
			break;
		} else if (header_info->type == HDR_RETURN_PATH) {
			if (node->header_returnpath)
				acl_myfree(node->header_returnpath);
			node->header_returnpath = acl_mystrdup(STR(temp));
		}
	}

	acl_myfree(addr_list);
	
	tok822_free_tree(tree);
	acl_vstring_free(temp);
}
コード例 #13
0
ファイル: tok822_parse.cpp プロジェクト: 10jschen/acl
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);
}
コード例 #14
0
ファイル: sendmail.c プロジェクト: DabeDotCom/postfix
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
ファイル: postalias.c プロジェクト: Jingeun/tongsu_smtp
static void postalias(char *map_type, char *path_name, int postalias_flags,
		              int open_flags, int dict_flags)
{
    VSTREAM *NOCLOBBER source_fp;
    VSTRING *line_buffer;
    MKMAP  *mkmap;
    int     lineno;
    int     last_line;
    VSTRING *key_buffer;
    VSTRING *value_buffer;
    TOK822 *tok_list;
    TOK822 *key_list;
    TOK822 *colon;
    TOK822 *value_list;
    struct stat st;
    mode_t  saved_mask;

    /*
     * Initialize.
     */
    line_buffer = vstring_alloc(100);
    key_buffer = vstring_alloc(100);
    value_buffer = vstring_alloc(100);
    if ((open_flags & O_TRUNC) == 0) {
	/* Incremental mode. */
	source_fp = VSTREAM_IN;
	vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END);
    } else {
	/* Create database. */
	if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
	    msg_fatal("can't create maps via the proxy service");
	dict_flags |= DICT_FLAG_BULK_UPDATE;
	if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
	    msg_fatal("open %s: %m", path_name);
    }
    if (fstat(vstream_fileno(source_fp), &st) < 0)
	msg_fatal("fstat %s: %m", path_name);

    /*
     * Turn off group/other read permissions as indicated in the source file.
     */
    if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	saved_mask = umask(022 | (~st.st_mode & 077));

    /*
     * If running as root, run as the owner of the source file, so that the
     * result shows proper ownership, and so that a bug in postalias does not
     * allow privilege escalation.
     */
    if ((postalias_flags & POSTALIAS_FLAG_AS_OWNER) && getuid() == 0
	&& (st.st_uid != geteuid() || st.st_gid != getegid()))
	set_eugid(st.st_uid, st.st_gid);

    /*
     * Open the database, create it when it does not exist, truncate it when
     * it does exist, and lock out any spectators.
     */
    mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);

    /*
     * And restore the umask, in case it matters.
     */
    if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
	umask(saved_mask);

    /*
     * Trap "exceptions" so that we can restart a bulk-mode update after a
     * recoverable error.
     */
    for (;;) {
	if (dict_isjmp(mkmap->dict) != 0
	    && dict_setjmp(mkmap->dict) != 0
	    && vstream_fseek(source_fp, SEEK_SET, 0) < 0)
	    msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));

	/*
	 * Add records to the database.
	 */
	last_line = 0;
	while (readllines(line_buffer, source_fp, &last_line, &lineno)) {

	    /*
	     * First some UTF-8 checks sans casefolding.
	     */
	    if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
		&& !allascii(STR(line_buffer))
		&& !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
		msg_warn("%s, line %d: non-UTF-8 input \"%s\""
			 " -- ignoring this line",
			 VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
		continue;
	    }

	    /*
	     * Tokenize the input, so that we do the right thing when a
	     * quoted localpart contains special characters such as "@", ":"
	     * and so on.
	     */
	    if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0)
		continue;

	    /*
	     * Enforce the key:value format. Disallow missing keys,
	     * multi-address keys, or missing values. In order to specify an
	     * empty string or value, enclose it in double quotes.
	     */
	    if ((colon = tok822_find_type(tok_list, ':')) == 0
		|| colon->prev == 0 || colon->next == 0
		|| tok822_rfind_type(colon, ',')) {
		msg_warn("%s, line %d: need name:value pair",
			 VSTREAM_PATH(source_fp), lineno);
		tok822_free_tree(tok_list);
		continue;
	    }

	    /*
	     * Key must be local. XXX We should use the Postfix rewriting and
	     * resolving services to handle all address forms correctly.
	     * However, we can't count on the mail system being up when the
	     * alias database is being built, so we're guessing a bit.
	     */
	    if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) {
		msg_warn("%s, line %d: name must be local",
			 VSTREAM_PATH(source_fp), lineno);
		tok822_free_tree(tok_list);
		continue;
	    }

	    /*
	     * Split the input into key and value parts, and convert from
	     * token representation back to string representation. Convert
	     * the key to internal (unquoted) form, because the resolver
	     * produces addresses in internal form. Convert the value to
	     * external (quoted) form, because it will have to be re-parsed
	     * upon lookup. Discard the token representation when done.
	     */
	    key_list = tok_list;
	    tok_list = 0;
	    value_list = tok822_cut_after(colon);
	    tok822_unlink(colon);
	    tok822_free(colon);

	    tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL);
	    tok822_free_tree(key_list);

	    tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL);
	    tok822_free_tree(value_list);

	    /*
	     * Store the value under a case-insensitive key.
	     */
	    mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
	    if (mkmap->dict->error)
		msg_fatal("table %s:%s: write error: %m",
			  mkmap->dict->type, mkmap->dict->name);
	}
	break;
    }

    /*
     * Update or append sendmail and NIS signatures.
     */
    if ((open_flags & O_TRUNC) == 0)
	mkmap->dict->flags |= DICT_FLAG_DUP_REPLACE;

    /*
     * Sendmail compatibility: add the @:@ signature to indicate that the
     * database is complete. This might be needed by NIS clients running
     * sendmail.
     */
    mkmap_append(mkmap, "@", "@");
    if (mkmap->dict->error)
	msg_fatal("table %s:%s: write error: %m",
		  mkmap->dict->type, mkmap->dict->name);

    /*
     * NIS compatibility: add time and master info. Unlike other information,
     * this information MUST be written without a trailing null appended to
     * key or value.
     */
    mkmap->dict->flags &= ~DICT_FLAG_TRY1NULL;
    mkmap->dict->flags |= DICT_FLAG_TRY0NULL;
    vstring_sprintf(value_buffer, "%010ld", (long) time((time_t *) 0));
#if (defined(HAS_NIS) || defined(HAS_NISPLUS))
    mkmap->dict->flags &= ~DICT_FLAG_FOLD_FIX;
    mkmap_append(mkmap, "YP_LAST_MODIFIED", STR(value_buffer));
    mkmap_append(mkmap, "YP_MASTER_NAME", var_myhostname);
#endif

    /*
     * Close the alias database, and release the lock.
     */
    mkmap_close(mkmap);

    /*
     * Cleanup. We're about to terminate, but it is a good sanity check.
     */
    vstring_free(value_buffer);
    vstring_free(key_buffer);
    vstring_free(line_buffer);
    if (source_fp != VSTREAM_IN)
	vstream_fclose(source_fp);
}
コード例 #16
0
ファイル: resolve.c プロジェクト: TonyChengTW/Rmail
static void resolve_addr(RES_CONTEXT *rp, char *addr,
			         VSTRING *channel, VSTRING *nexthop,
			         VSTRING *nextrcpt, int *flags)
{
    char   *myname = "resolve_addr";
    VSTRING *addr_buf = vstring_alloc(100);
    TOK822 *tree = 0;
    TOK822 *saved_domain = 0;
    TOK822 *domain = 0;
    char   *destination;
    const char *blame = 0;
    const char *rcpt_domain;
    int     addr_len;
    int     loop_count;
    int     loop_max;
    char   *local;
    char   *oper;
    char   *junk;

    *flags = 0;
    vstring_strcpy(channel, "CHANNEL NOT UPDATED");
    vstring_strcpy(nexthop, "NEXTHOP NOT UPDATED");
    vstring_strcpy(nextrcpt, "NEXTRCPT NOT UPDATED");

    /*
     * The address is in internalized (unquoted) form.
     * 
     * In an ideal world we would parse the externalized address form as given
     * to us by the sender.
     * 
     * However, in the real world we have to look for routing characters like
     * %@! in the address local-part, even when that information is quoted
     * due to the presence of special characters or whitespace. Although
     * technically incorrect, this is needed to stop user@domain@domain relay
     * attempts when forwarding mail to a Sendmail MX host.
     * 
     * This suggests that we parse the address in internalized (unquoted) form.
     * Unfortunately, if we do that, the unparser generates incorrect white
     * space between adjacent non-operator tokens. Example: ``first last''
     * needs white space, but ``stuff[stuff]'' does not. This is is not a
     * problem when unparsing the result from parsing externalized forms,
     * because the parser/unparser were designed for valid externalized forms
     * where ``stuff[stuff]'' does not happen.
     * 
     * As a workaround we start with the quoted form and then dequote the
     * local-part only where needed. This will do the right thing in most
     * (but not all) cases.
     */
    addr_len = strlen(addr);
    quote_822_local(addr_buf, addr);
    tree = tok822_scan_addr(vstring_str(addr_buf));

    /*
     * Let the optimizer replace multiple expansions of this macro by a GOTO
     * to a single instance.
     */
#define FREE_MEMORY_AND_RETURN { \
	if (saved_domain) \
	    tok822_free_tree(saved_domain); \
	if(tree) \
	    tok822_free_tree(tree); \
	if (addr_buf) \
	    vstring_free(addr_buf); \
	return; \
    }

    /*
     * Preliminary resolver: strip off all instances of the local domain.
     * Terminate when no destination domain is left over, or when the
     * destination domain is remote.
     * 
     * XXX To whom it may concern. If you change the resolver loop below, or
     * quote_822_local.c, or tok822_parse.c, be sure to re-run the tests
     * under "make resolve_clnt_test" in the global directory.
     */
#define RESOLVE_LOCAL(domain) \
    resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))

    dict_errno = 0;

    for (loop_count = 0, loop_max = addr_len + 100; /* void */ ; loop_count++) {

	/*
	 * Grr. resolve_local() table lookups may fail. It may be OK for
	 * local file lookup code to abort upon failure, but with
	 * network-based tables it is preferable to return an error
	 * indication to the requestor.
	 */
	if (dict_errno) {
	    *flags |= RESOLVE_FLAG_FAIL;
	    FREE_MEMORY_AND_RETURN;
	}

	/*
	 * XXX Should never happen, but if this happens with some
	 * pathological address, then that is not sufficient reason to
	 * disrupt the operation of an MTA.
	 */
	if (loop_count > loop_max) {
	    msg_warn("resolve_addr: <%s>: giving up after %d iterations",
		     addr, loop_count);
	    break;
	}

	/*
	 * Strip trailing dot at end of domain, but not dot-dot or at-dot.
	 * This merely makes diagnostics more accurate by leaving bogus
	 * addresses alone.
	 */
	if (tree->tail
	    && tree->tail->type == '.'
	    && tok822_rfind_type(tree->tail, '@') != 0
	    && tree->tail->prev->type != '.'
	    && tree->tail->prev->type != '@')
	    tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));

	/*
	 * Strip trailing @.
	 */
	if (var_resolve_nulldom
	    && tree->tail
	    && tree->tail->type == '@')
	    tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));

	/*
	 * Strip (and save) @domain if local.
	 */
	if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
	    if (domain->next && RESOLVE_LOCAL(domain->next) == 0)
		break;
	    tok822_sub_keep_before(tree, domain);
	    if (saved_domain)
		tok822_free_tree(saved_domain);
	    saved_domain = domain;
	    domain = 0;				/* safety for future change */
	}

	/*
	 * After stripping the local domain, if any, replace foo%bar by
	 * foo@bar, site!user by user@site, rewrite to canonical form, and
	 * retry.
	 */
	if (tok822_rfind_type(tree->tail, '@')
	    || (var_swap_bangpath && tok822_rfind_type(tree->tail, '!'))
	    || (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) {
	    rewrite_tree(REWRITE_CANON, tree);
	    continue;
	}

	/*
	 * If the local-part is a quoted string, crack it open when we're
	 * permitted to do so and look for routing operators. This is
	 * technically incorrect, but is needed to stop relaying problems.
	 * 
	 * XXX Do another feeble attempt to keep local-part info quoted.
	 */
	if (var_resolve_dequoted
	    && tree->head && tree->head == tree->tail
	    && tree->head->type == TOK822_QSTRING
	    && ((oper = strrchr(local = STR(tree->head->vstr), '@')) != 0
		|| (var_percent_hack && (oper = strrchr(local, '%')) != 0)
	     || (var_swap_bangpath && (oper = strrchr(local, '!')) != 0))) {
	    if (*oper == '%')
		*oper = '@';
	    tok822_internalize(addr_buf, tree->head, TOK822_STR_DEFL);
	    if (*oper == '@') {
		junk = mystrdup(STR(addr_buf));
		quote_822_local(addr_buf, junk);
		myfree(junk);
	    }
	    tok822_free(tree->head);
	    tree->head = tok822_scan(STR(addr_buf), &tree->tail);
	    rewrite_tree(REWRITE_CANON, tree);
	    continue;
	}

	/*
	 * An empty local-part or an empty quoted string local-part becomes
	 * the local MAILER-DAEMON, for consistency with our own From:
	 * message headers.
	 */
	if (tree->head && tree->head == tree->tail
	    && tree->head->type == TOK822_QSTRING
	    && VSTRING_LEN(tree->head->vstr) == 0) {
	    tok822_free(tree->head);
	    tree->head = 0;
	}
	/* XXX must be localpart only, not user@domain form. */
	if (tree->head == 0)
	    tree->head = tok822_scan(var_empty_addr, &tree->tail);

	/*
	 * We're done. There are no domains left to strip off the address,
	 * and all null local-part information is sanitized.
	 */
	domain = 0;
	break;
    }

    vstring_free(addr_buf);
    addr_buf = 0;

    /*
     * Make sure the resolved envelope recipient has the user@domain form. If
     * no domain was specified in the address, assume the local machine. See
     * above for what happens with an empty address.
     */
    if (domain == 0) {
	if (saved_domain) {
	    tok822_sub_append(tree, saved_domain);
	    saved_domain = 0;
	} else {
	    tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
	    tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
	}
    }

    /*
     * Transform the recipient address back to internal form.
     * 
     * XXX This may produce incorrect results if we cracked open a quoted
     * local-part with routing operators; see discussion above at the top of
     * the big loop.
     */
    tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
    rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
    if (*rcpt_domain == '[' ? !valid_hostliteral(rcpt_domain, DONT_GRIPE) :
	(!valid_hostname(rcpt_domain, DONT_GRIPE)
	 || valid_hostaddr(rcpt_domain, DONT_GRIPE)))
	*flags |= RESOLVE_FLAG_ERROR;
    tok822_free_tree(tree);
    tree = 0;

    /*
     * XXX Short-cut invalid address forms.
     */
    if (*flags & RESOLVE_FLAG_ERROR) {
	*flags |= RESOLVE_CLASS_DEFAULT;
	FREE_MEMORY_AND_RETURN;
    }

    /*
     * Recognize routing operators in the local-part, even when we do not
     * recognize ! or % as valid routing operators locally. This is needed to
     * prevent backup MX hosts from relaying third-party destinations through
     * primary MX hosts, otherwise the backup host could end up on black
     * lists. Ignore local swap_bangpath and percent_hack settings because we
     * can't know how the next MX host is set up.
     */
    if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain))
	*flags |= RESOLVE_FLAG_ROUTED;

    /*
     * With local, virtual, relay, or other non-local destinations, give the
     * highest precedence to transport associated nexthop information.
     * 
     * Otherwise, with relay or other non-local destinations, the relayhost
     * setting overrides the destination domain name.
     * 
     * XXX Nag if the recipient domain is listed in multiple domain lists. The
     * result is implementation defined, and may break when internals change.
     * 
     * For now, we distinguish only a fixed number of address classes.
     * Eventually this may become extensible, so that new classes can be
     * configured with their own domain list, delivery transport, and
     * recipient table.
     */
#define STREQ(x,y) (strcmp((x), (y)) == 0)

    dict_errno = 0;
    if (domain != 0) {

	/*
	 * Virtual alias domain.
	 */
	if (virt_alias_doms
	    && string_list_match(virt_alias_doms, rcpt_domain)) {
	    if (var_helpful_warnings) {
		if (virt_mailbox_doms
		    && string_list_match(virt_mailbox_doms, rcpt_domain))
		    msg_warn("do not list domain %s in BOTH %s and %s",
			     rcpt_domain, VAR_VIRT_ALIAS_DOMS,
			     VAR_VIRT_MAILBOX_DOMS);
		if (relay_domains
		    && domain_list_match(relay_domains, rcpt_domain))
		    msg_warn("do not list domain %s in BOTH %s and %s",
			     rcpt_domain, VAR_VIRT_ALIAS_DOMS,
			     VAR_RELAY_DOMAINS);
#if 0
		if (strcasecmp(rcpt_domain, var_myorigin) == 0)
		    msg_warn("do not list $%s (%s) in %s",
			   VAR_MYORIGIN, var_myorigin, VAR_VIRT_ALIAS_DOMS);
#endif
	    }
	    vstring_strcpy(channel, MAIL_SERVICE_ERROR);
	    vstring_sprintf(nexthop, "User unknown%s",
			    var_show_unk_rcpt_table ?
			    " in virtual alias table" : "");
	    *flags |= RESOLVE_CLASS_ALIAS;
	} else if (dict_errno != 0) {
	    msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS);
	    *flags |= RESOLVE_FLAG_FAIL;
	    FREE_MEMORY_AND_RETURN;
	}

	/*
	 * Virtual mailbox domain.
	 */
	else if (virt_mailbox_doms
		 && string_list_match(virt_mailbox_doms, rcpt_domain)) {
	    if (var_helpful_warnings) {
		if (relay_domains
		    && domain_list_match(relay_domains, rcpt_domain))
		    msg_warn("do not list domain %s in BOTH %s and %s",
			     rcpt_domain, VAR_VIRT_MAILBOX_DOMS,
			     VAR_RELAY_DOMAINS);
	    }
	    vstring_strcpy(channel, RES_PARAM_VALUE(rp->virt_transport));
	    vstring_strcpy(nexthop, rcpt_domain);
	    blame = rp->virt_transport_name;
	    *flags |= RESOLVE_CLASS_VIRTUAL;
	} else if (dict_errno != 0) {
	    msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS);
	    *flags |= RESOLVE_FLAG_FAIL;
	    FREE_MEMORY_AND_RETURN;
	} else {

	    /*
	     * Off-host relay destination.
	     */
	    if (relay_domains
		&& domain_list_match(relay_domains, rcpt_domain)) {
		vstring_strcpy(channel, RES_PARAM_VALUE(rp->relay_transport));
		blame = rp->relay_transport_name;
		*flags |= RESOLVE_CLASS_RELAY;
	    } else if (dict_errno != 0) {
		msg_warn("%s lookup failure", VAR_RELAY_DOMAINS);
		*flags |= RESOLVE_FLAG_FAIL;
		FREE_MEMORY_AND_RETURN;
	    }

	    /*
	     * Other off-host destination.
	     */
	    else {
		vstring_strcpy(channel, RES_PARAM_VALUE(rp->def_transport));
		blame = rp->def_transport_name;
		*flags |= RESOLVE_CLASS_DEFAULT;
	    }

	    /*
	     * With off-host delivery, relayhost overrides recipient domain.
	     */
	    if (*RES_PARAM_VALUE(rp->relayhost))
		vstring_strcpy(nexthop, RES_PARAM_VALUE(rp->relayhost));
	    else
		vstring_strcpy(nexthop, rcpt_domain);
	}
    }

    /*
     * Local delivery.
     * 
     * XXX Nag if the domain is listed in multiple domain lists. The effect is
     * implementation defined, and may break when internals change.
     */
    else {
	if (var_helpful_warnings) {
	    if (virt_alias_doms
		&& string_list_match(virt_alias_doms, rcpt_domain))
		msg_warn("do not list domain %s in BOTH %s and %s",
			 rcpt_domain, VAR_MYDEST, VAR_VIRT_ALIAS_DOMS);
	    if (virt_mailbox_doms
		&& string_list_match(virt_mailbox_doms, rcpt_domain))
		msg_warn("do not list domain %s in BOTH %s and %s",
			 rcpt_domain, VAR_MYDEST, VAR_VIRT_MAILBOX_DOMS);
	}
	vstring_strcpy(channel, RES_PARAM_VALUE(rp->local_transport));
	vstring_strcpy(nexthop, rcpt_domain);
	blame = rp->local_transport_name;
	*flags |= RESOLVE_CLASS_LOCAL;
    }

    /*
     * An explicit main.cf transport:nexthop setting overrides the nexthop.
     * 
     * XXX We depend on this mechanism to enforce per-recipient concurrencies
     * for local recipients. With "local_transport = local:$myhostname" we
     * force mail for any domain in $mydestination/${proxy,inet}_interfaces
     * to share the same queue.
     */
    if ((destination = split_at(STR(channel), ':')) != 0 && *destination)
	vstring_strcpy(nexthop, destination);

    /*
     * Sanity checks.
     */
    if (*STR(channel) == 0) {
	if (blame == 0)
	    msg_panic("%s: null blame", myname);
	msg_warn("file %s/%s: parameter %s: null transport is not allowed",
		 var_config_dir, MAIN_CONF_FILE, blame);
	*flags |= RESOLVE_FLAG_FAIL;
	FREE_MEMORY_AND_RETURN;
    }
    if (*STR(nexthop) == 0)
	msg_panic("%s: null nexthop", myname);

    /*
     * The transport map can selectively override any transport and/or
     * nexthop host info that is set up above. Unfortunately, the syntax for
     * nexthop information is transport specific. We therefore need sane and
     * intuitive semantics for transport map entries that specify a channel
     * but no nexthop.
     * 
     * With non-error transports, the initial nexthop information is the
     * recipient domain. However, specific main.cf transport definitions may
     * specify a transport-specific destination, such as a host + TCP socket,
     * or the pathname of a UNIX-domain socket. With less precedence than
     * main.cf transport definitions, a main.cf relayhost definition may also
     * override nexthop information for off-host deliveries.
     * 
     * With the error transport, the nexthop information is free text that
     * specifies the reason for non-delivery.
     * 
     * Because nexthop syntax is transport specific we reset the nexthop
     * information to the recipient domain when the transport table specifies
     * a transport without also specifying the nexthop information.
     * 
     * Subtle note: reset nexthop even when the transport table does not change
     * the transport. Otherwise it is hard to get rid of main.cf specified
     * nexthop information.
     * 
     * XXX Don't override the virtual alias class (error:User unknown) result.
     */
    if (rp->transport_info && !(*flags & RESOLVE_CLASS_ALIAS)) {
	if (transport_lookup(rp->transport_info, STR(nextrcpt),
			     rcpt_domain, channel, nexthop) == 0
	    && dict_errno != 0) {
	    msg_warn("%s lookup failure", rp->transport_maps_name);
	    *flags |= RESOLVE_FLAG_FAIL;
	    FREE_MEMORY_AND_RETURN;
	}
    }

    /*
     * Bounce recipients that have moved, regardless of domain address class.
     * We do this last, in anticipation of transport maps that can override
     * the recipient address.
     * 
     * The downside of not doing this in delivery agents is that this table has
     * no effect on local alias expansion results. Such mail will have to
     * make almost an entire iteration through the mail system.
     */
#define IGNORE_ADDR_EXTENSION   ((char **) 0)

    if (relocated_maps != 0) {
	const char *newloc;

	if ((newloc = mail_addr_find(relocated_maps, STR(nextrcpt),
				     IGNORE_ADDR_EXTENSION)) != 0) {
	    vstring_strcpy(channel, MAIL_SERVICE_ERROR);
	    vstring_sprintf(nexthop, "User has moved to %s", newloc);
	} else if (dict_errno != 0) {
	    msg_warn("%s lookup failure", VAR_RELOCATED_MAPS);
	    *flags |= RESOLVE_FLAG_FAIL;
	    FREE_MEMORY_AND_RETURN;
	}
    }

    /*
     * Clean up.
     */
    FREE_MEMORY_AND_RETURN;
}
コード例 #17
0
ファイル: rewrite.c プロジェクト: terbolous/postfix
void    rewrite_tree(RWR_CONTEXT *context, TOK822 *tree)
{
    TOK822 *colon;
    TOK822 *domain;
    TOK822 *bang;
    TOK822 *local;
    VSTRING *vstringval;

    /*
     * XXX If you change this module, quote_822_local.c, or tok822_parse.c,
     * be sure to re-run the tests under "make rewrite_clnt_test" and "make
     * resolve_clnt_test" in the global directory.
     */

    /*
     * Sanity check.
     */
    if (tree->head == 0)
	msg_panic("rewrite_tree: empty tree");

    /*
     * An empty address is a special case.
     */
    if (tree->head == tree->tail
	&& tree->tail->type == TOK822_QSTRING
	&& VSTRING_LEN(tree->tail->vstr) == 0)
	return;

    /*
     * Treat a lone @ as if it were an empty address.
     */
    if (tree->head == tree->tail
	&& tree->tail->type == '@') {
	tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
	tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, ""));
	return;
    }

    /*
     * Strip source route.
     */
    if (tree->head->type == '@'
	&& (colon = tok822_find_type(tree->head, ':')) != 0
	&& colon != tree->tail)
	tok822_free_tree(tok822_sub_keep_after(tree, colon));

    /*
     * Optionally, transform address forms without @.
     */
    if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) {

	/*
	 * Swap domain!user to user@domain.
	 */
	if (var_swap_bangpath != 0
	    && (bang = tok822_find_type(tree->head, '!')) != 0) {
	    tok822_sub_keep_before(tree, bang);
	    local = tok822_cut_after(bang);
	    tok822_free(bang);
	    tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0));
	    if (local)
		tok822_sub_prepend(tree, local);
	}

	/*
	 * Promote user%domain to user@domain.
	 */
	else if (var_percent_hack != 0
		 && (domain = tok822_rfind_type(tree->tail, '%')) != 0) {
	    domain->type = '@';
	}

	/*
	 * Append missing @origin
	 */
	else if (var_append_at_myorigin != 0
		 && REW_PARAM_VALUE(context->origin) != 0
		 && REW_PARAM_VALUE(context->origin)[0] != 0) {
	    domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
	    tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin),
						(TOK822 **) 0));
	}
    }

    /*
     * Append missing .domain, but leave broken forms ending in @ alone. This
     * merely makes diagnostics more accurate by leaving bogus addresses
     * alone.
     * 
     * Backwards-compatibility warning: warn for "user@localhost" when there is
     * no "localhost" in mydestination or in any other address class with an
     * explicit domain list.
     */
    if (var_append_dot_mydomain != 0
	&& REW_PARAM_VALUE(context->domain) != 0
	&& REW_PARAM_VALUE(context->domain)[0] != 0
	&& (domain = tok822_rfind_type(tree->tail, '@')) != 0
	&& domain != tree->tail
	&& tok822_find_type(domain, TOK822_DOMLIT) == 0
	&& tok822_find_type(domain, '.') == 0) {
	if (warn_compat_break_app_dot_mydomain
	    && (vstringval = domain->next->vstr) != 0) {
	    if (strcasecmp(vstring_str(vstringval), "localhost") != 0) {
		msg_info("using backwards-compatible default setting "
			 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to "
			 "\"%s.%s\"", vstring_str(vstringval),
			 vstring_str(vstringval), var_mydomain);
	    } else if (resolve_class("localhost") == RESOLVE_CLASS_DEFAULT) {
		msg_info("using backwards-compatible default setting "
			 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to "
			 "\"%s.%s\"; please add \"localhost\" to "
			 "mydestination or other address class",
			 vstring_str(vstringval), vstring_str(vstringval),
			 var_mydomain);
	    }
	}
	tok822_sub_append(tree, tok822_alloc('.', (char *) 0));
	tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain),
					    (TOK822 **) 0));
    }

    /*
     * Strip trailing dot at end of domain, but not dot-dot or @-dot. This
     * merely makes diagnostics more accurate by leaving bogus addresses
     * alone.
     */
    if (tree->tail->type == '.'
	&& tree->tail->prev
	&& tree->tail->prev->type != '.'
	&& tree->tail->prev->type != '@')
	tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
}
コード例 #18
0
ファイル: sendmail.c プロジェクト: DabeDotCom/postfix
static void output_header(void *context, int header_class,
			          const HEADER_OPTS *header_info,
			          VSTRING *buf, off_t offset)
{
    SM_STATE *state = (SM_STATE *) context;
    TOK822 *tree;
    TOK822 **addr_list;
    TOK822 **tpp;
    ARGV   *rcpt;
    char   *start;
    char   *line;
    char   *next_line;
    ssize_t len;

    /*
     * Parse the header line, and save copies of recipient addresses in the
     * appropriate place.
     */
    if (header_class == MIME_HDR_PRIMARY
	&& header_info
	&& (header_info->flags & HDR_OPT_RECIP)
	&& (header_info->flags & HDR_OPT_EXTRACT)
	&& (state->resent == 0 || (header_info->flags & HDR_OPT_RR))) {
	if (header_info->flags & HDR_OPT_RR) {
	    rcpt = state->resent_recip;
	    if (state->resent == 0)
		state->resent = 1;
	} else
	    rcpt = state->recipients;
	tree = tok822_parse(STR(buf) + strlen(header_info->name) + 1);
	addr_list = tok822_grep(tree, TOK822_ADDR);
	for (tpp = addr_list; *tpp; tpp++) {
	    tok822_internalize(state->temp, tpp[0]->head, TOK822_STR_DEFL);
	    argv_add(rcpt, STR(state->temp), (char *) 0);
	}
	myfree((void *) addr_list);
	tok822_free_tree(tree);
    }

    /*
     * Pipe the unmodified message header through the header line folding
     * routine, and ensure that long lines are chopped appropriately.
     */
    for (line = start = STR(buf); line; line = next_line) {
	next_line = split_at(line, '\n');
	len = next_line ? next_line - line - 1 : strlen(line);
	do {
	    if (len > var_line_limit) {
		output_text(context, REC_TYPE_CONT, line, var_line_limit, offset);
		line += var_line_limit;
		len -= var_line_limit;
		offset += var_line_limit;
	    } else {
		output_text(context, REC_TYPE_NORM, line, len, offset);
		offset += len;
		break;
	    }
	} while (len > 0);
	offset += 1;
    }
}