Beispiel #1
0
MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...)
{
    MATCH_LIST *list;
    char   *saved_patterns;
    va_list ap;
    int     i;

    if (flags & ~MATCH_FLAG_ALL)
	msg_panic("match_list_init: bad flags 0x%x", flags);

    list = (MATCH_LIST *) mymalloc(sizeof(*list));
    list->flags = flags;
    list->match_count = match_count;
    list->match_func =
	(MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN));
    list->match_args =
	(const char **) mymalloc(match_count * sizeof(const char *));
    va_start(ap, match_count);
    for (i = 0; i < match_count; i++)
	list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
    va_end(ap);
    list->error = 0;

#define DO_MATCH	1

    saved_patterns = mystrdup(patterns);
    list->patterns = match_list_parse(argv_alloc(1), saved_patterns, DO_MATCH);
    argv_terminate(list->patterns);
    myfree(saved_patterns);
    return (list);
}
Beispiel #2
0
ARGV   *dict_mapnames()
{
    HTABLE_INFO **ht_info;
    HTABLE_INFO **ht;
    DICT_OPEN_INFO *dp;
    ARGV   *mapnames;
#ifndef NO_DYNAMIC_MAPS
    DLINFO *dlp;
#endif

    if (dict_open_hash == 0)
	dict_open_init();
    mapnames = argv_alloc(dict_open_hash->used + 1);
    for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
	dp = (DICT_OPEN_INFO *) ht[0]->value;
	argv_add(mapnames, dp->type, ARGV_END);
    }
#ifndef NO_DYNAMIC_MAPS
    if (!dict_dlinfo)
	msg_fatal("dlinfo==NULL");
    for (dlp=dict_dlinfo; dlp->pattern; dlp++) {
	argv_add(mapnames, dlp->pattern, ARGV_END);
    }
#endif
    qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]),
	  dict_sort_alpha_cpp);
    myfree((char *) ht_info);
    argv_terminate(mapnames);
    return mapnames;
}
Beispiel #3
0
void    smtp_chat_notify(SMTP_SESSION *session)
{
    const char *myname = "smtp_chat_notify";
    VSTREAM *notice;
    char  **cpp;

    /*
     * Sanity checks.
     */
    if (session->history == 0)
	msg_panic("%s: no conversation history", myname);
    if (msg_verbose)
	msg_info("%s: notify postmaster", myname);

    /*
     * Construct a message for the postmaster, explaining what this is all
     * about. This is junk mail: don't send it when the mail posting service
     * is unavailable, and use the double bounce sender address, to prevent
     * mail bounce wars. Always prepend one space to message content that we
     * generate from untrusted data.
     */
#define NULL_TRACE_FLAGS	0
#define NO_QUEUE_ID		((VSTRING *) 0)
#define LENGTH	78
#define INDENT	4

    notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
				    var_error_rcpt,
				    MAIL_SRC_MASK_NOTIFY, NULL_TRACE_FLAGS,
				    SMTPUTF8_FLAG_NONE, NO_QUEUE_ID);
    if (notice == 0) {
	msg_warn("postmaster notify: %m");
	return;
    }
    post_mail_fprintf(notice, "From: %s (Mail Delivery System)",
		      mail_addr_mail_daemon());
    post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt);
    post_mail_fprintf(notice, "Subject: %s %s client: errors from %s",
		      var_mail_name, smtp_mode ? "SMTP" : "LMTP",
		      session->namaddrport);
    post_mail_fputs(notice, "");
    post_mail_fprintf(notice, "Unexpected response from %s.",
		      session->namaddrport);
    post_mail_fputs(notice, "");
    post_mail_fputs(notice, "Transcript of session follows.");
    post_mail_fputs(notice, "");
    argv_terminate(session->history);
    for (cpp = session->history->argv; *cpp; cpp++)
	line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
		  (void *) notice);
    post_mail_fputs(notice, "");
    post_mail_fprintf(notice, "For other details, see the local mail logfile");
    (void) post_mail_fclose(notice);
}
Beispiel #4
0
ARGV   *mbox_lock_names(void)
{
    const NAME_MASK *np;
    ARGV   *argv;

    argv = argv_alloc(2);
    for (np = mbox_mask; np->name != 0; np++)
	argv_add(argv, np->name, ARGV_END);
    argv_terminate(argv);
    return (argv);
}
Beispiel #5
0
ARGV   *match_service_init_argv(char **patterns)
{
    ARGV   *list = argv_alloc(1);
    char  **cpp;

    for (cpp = patterns; *cpp; cpp++)
	argv_add(list, *cpp, (char *) 0);
    argv_terminate(list);
    match_service_compat(list);
    return (list);
}
Beispiel #6
0
ARGV   *argv_split_append(ARGV *argvp, const char *string, const char *delim)
{
    char   *saved_string = mystrdup(string);
    char   *bp = saved_string;
    char   *arg;

    while ((arg = mystrtok(&bp, delim)) != 0)
	argv_add(argvp, arg, (char *) 0);
    argv_terminate(argvp);
    myfree(saved_string);
    return (argvp);
}
Beispiel #7
0
ARGV   *match_service_init(const char *patterns)
{
    const char *delim = " ,\t\r\n";
    ARGV   *list = argv_alloc(1);
    char   *saved_patterns = mystrdup(patterns);
    char   *bp = saved_patterns;
    const char *item;

    while ((item = mystrtok(&bp, delim)) != 0)
        argv_add(list, item, (char *) 0);
    argv_terminate(list);
    myfree(saved_patterns);
    return (list);
}
Beispiel #8
0
void    smtpd_chat_notify(SMTPD_STATE *state)
{
    char   *myname = "smtpd_chat_notify";
    VSTREAM *notice;
    char  **cpp;

    /*
     * Sanity checks.
     */
    if (state->history == 0)
	msg_panic("%s: no conversation history", myname);
    if (msg_verbose)
	msg_info("%s: notify postmaster", myname);

    /*
     * Construct a message for the postmaster, explaining what this is all
     * about. This is junk mail: don't send it when the mail posting service
     * is unavailable, and use the double bounce sender address to prevent
     * mail bounce wars. Always prepend one space to message content that we
     * generate from untrusted data.
     */
#define NULL_TRACE_FLAGS	0
#define LENGTH	78
#define INDENT	4

    notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
				    var_error_rcpt,
				    CLEANUP_FLAG_MASK_INTERNAL,
				    NULL_TRACE_FLAGS);
    if (notice == 0) {
	msg_warn("postmaster notify: %m");
	return;
    }
    post_mail_fprintf(notice, "From: %s (Mail Delivery System)",
		      mail_addr_mail_daemon());
    post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt);
    post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s[%s]",
		      var_mail_name, state->name, state->addr);
    post_mail_fputs(notice, "");
    post_mail_fputs(notice, "Transcript of session follows.");
    post_mail_fputs(notice, "");
    argv_terminate(state->history);
    for (cpp = state->history->argv; *cpp; cpp++)
	line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
		  (char *) notice);
    post_mail_fputs(notice, "");
    if (state->reason)
	post_mail_fprintf(notice, "Session aborted, reason: %s", state->reason);
    (void) post_mail_fclose(notice);
}
Beispiel #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);
}
Beispiel #10
0
ARGV   *argv_split_count(const char *string, const char *delim, ssize_t count)
{
    ARGV   *argvp = argv_alloc(1);
    char   *saved_string = mystrdup(string);
    char   *bp = saved_string;
    char   *arg;

    if (count < 1)
	msg_panic("argv_split_count: bad count: %ld", (long) count);
    while (count-- > 1 && (arg = mystrtok(&bp, delim)) != 0)
	argv_add(argvp, arg, (char *) 0);
    if (*bp)
	bp += strspn(bp, delim);
    if (*bp)
	argv_add(argvp, bp, (char *) 0);
    argv_terminate(argvp);
    myfree(saved_string);
    return (argvp);
}
Beispiel #11
0
ARGV   *dict_mapnames()
{
    HTABLE_INFO **ht_info;
    HTABLE_INFO **ht;
    DICT_OPEN_INFO *dp;
    ARGV   *mapnames;

    if (dict_open_hash == 0)
	dict_open_init();
    mapnames = argv_alloc(dict_open_hash->used + 1);
    for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
	dp = (DICT_OPEN_INFO *) ht[0]->value;
	argv_add(mapnames, dp->type, ARGV_END);
    }
    qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]),
	  dict_sort_alpha_cpp);
    myfree((char *) ht_info);
    argv_terminate(mapnames);
    return mapnames;
}
Beispiel #12
0
SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
{
    char   *saved_acl = mystrdup(extern_acl);
    SERVER_ACL *intern_acl = argv_alloc(1);
    char   *bp = saved_acl;
    char   *acl;

#define STREQ(x,y) ((*x) == (*y) && strcasecmp((x), (y)) == 0)
#define STRNE(x,y) ((*x) != (*y) || strcasecmp((x), (y)) != 0)

    /*
     * Nested tables are not allowed. Tables are opened before entering the
     * chroot jail, while access lists are evaluated after entering the
     * chroot jail.
     */
    while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) {
	if (strchr(acl, ':') != 0) {
	    if (strchr(origin, ':') != 0) {
		msg_warn("table %s: lookup result \"%s\" is not allowed"
			 " -- ignoring remainder of access list",
			 origin, acl);
		argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0);
		break;
	    } else {
		if (dict_handle(acl) == 0)
		    dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
						 | DICT_FLAG_FOLD_FIX));
	    }
	}
	argv_add(intern_acl, acl, (char *) 0);
    }
    argv_terminate(intern_acl);

    /*
     * Cleanup.
     */
    myfree(saved_acl);
    return (intern_acl);
}
Beispiel #13
0
static void show_queue(void)
{
    const char *errstr;
    char    buf[VSTREAM_BUFSIZE];
    VSTREAM *showq;
    int     n;
    uid_t   uid = getuid();

    if (uid != 0 && uid != var_owner_uid
	&& (errstr = check_user_acl_byuid(VAR_SHOWQ_ACL, var_showq_acl,
					  uid)) != 0)
	msg_fatal_status(EX_NOPERM,
		       "User %s(%ld) is not allowed to view the mail queue",
			 errstr, (long) uid);

    /*
     * Connect to the show queue service. Terminate silently when piping into
     * a program that terminates early.
     */
    if ((showq = mail_connect(MAIL_CLASS_PUBLIC, var_showq_service, BLOCKING)) != 0) {
	while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0) {
	    if (vstream_fwrite(VSTREAM_OUT, buf, n) != n
		|| vstream_fflush(VSTREAM_OUT) != 0) {
		if (errno == EPIPE)
		    break;
		msg_fatal("write error: %m");
	    }
	}
	if (vstream_fclose(showq) && errno != EPIPE)
	    msg_warn("close: %m");
    }

    /*
     * Don't assume that the mail system is down when the user has
     * insufficient permission to access the showq socket.
     */
    else if (errno == EACCES) {
	msg_fatal_status(EX_SOFTWARE,
			 "Connect to the %s %s service: %m",
			 var_mail_name, var_showq_service);
    }

    /*
     * When the mail system is down, the superuser can still access the queue
     * directly. Just run the showq program in stand-alone mode.
     */
    else if (geteuid() == 0) {
	ARGV   *argv;
	int     stat;

	msg_warn("Mail system is down -- accessing queue directly");
	argv = argv_alloc(6);
	argv_add(argv, var_showq_service, "-u", "-S", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(argv, "-v", (char *) 0);
	argv_terminate(argv);
	stat = mail_run_foreground(var_daemon_dir, argv->argv);
	argv_free(argv);
	if (stat != 0)
	    msg_fatal_status(stat < 0 ? EX_OSERR : EX_SOFTWARE,
			     "Error running %s/%s",
			     var_daemon_dir, argv->argv[0]);
    }

    /*
     * When the mail system is down, unprivileged users are stuck, because by
     * design the mail system contains no set_uid programs. The only way for
     * an unprivileged user to cross protection boundaries is to talk to the
     * showq daemon.
     */
    else {
	msg_fatal_status(EX_UNAVAILABLE,
			 "Queue report unavailable - mail system is down");
    }
}
Beispiel #14
0
int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command)
{
    const char *myname = "deliver_command";
    DSN_BUF *why = state.msg_attr.why;
    int     cmd_status;
    int     deliver_status;
    ARGV   *env;
    int     copy_flags;
    char  **cpp;
    char   *cp;
    ARGV   *export_env;
    VSTRING *exec_dir;
    int     expand_status;

    /*
     * Make verbose logging easier to understand.
     */
    state.level++;
    if (msg_verbose)
	MSG_LOG_STATE(myname, state);

    /*
     * DUPLICATE ELIMINATION
     * 
     * Skip this command if it was already delivered to as this user.
     */
    if (been_here(state.dup_filter, "command %s:%ld %s",
		  state.msg_attr.user, (long) usr_attr.uid, command))
	return (0);

    /*
     * Don't deliver a trace-only request.
     */
    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
	dsb_simple(why, "2.0.0", "delivers to command: %s", command);
	return (sent(BOUNCE_FLAGS(state.request),
		     SENT_ATTR(state.msg_attr)));
    }

    /*
     * DELIVERY RIGHTS
     * 
     * Choose a default uid and gid when none have been selected (i.e. values
     * are still zero).
     */
    if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0)
	msg_panic("privileged default user id");
    if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0)
	msg_panic("privileged default group id");

    /*
     * Deliver.
     */
    copy_flags = MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH
	| MAIL_COPY_ORIG_RCPT;
    if (local_deliver_hdr_mask & DELIVER_HDR_CMD)
	copy_flags |= MAIL_COPY_DELIVERED;

    if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
	msg_fatal("%s: seek queue file %s: %m",
		  myname, VSTREAM_PATH(state.msg_attr.fp));

    /*
     * Pass additional environment information. XXX This should be
     * configurable. However, passing untrusted information via environment
     * parameters opens up a whole can of worms. Lesson from web servers:
     * don't let any network data even near a shell. It causes trouble.
     */
    env = argv_alloc(1);
    if (usr_attr.home)
	argv_add(env, "HOME", usr_attr.home, ARGV_END);
    argv_add(env,
	     "LOGNAME", state.msg_attr.user,
	     "USER", state.msg_attr.user,
	     "SENDER", state.msg_attr.sender,
	     "RECIPIENT", state.msg_attr.rcpt.address,
	     "LOCAL", state.msg_attr.local,
	     ARGV_END);
    if (usr_attr.shell)
	argv_add(env, "SHELL", usr_attr.shell, ARGV_END);
    if (state.msg_attr.domain)
	argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END);
    if (state.msg_attr.extension)
	argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END);
    if (state.msg_attr.rcpt.orig_addr && state.msg_attr.rcpt.orig_addr[0])
	argv_add(env, "ORIGINAL_RECIPIENT", state.msg_attr.rcpt.orig_addr,
		 ARGV_END);

#define EXPORT_REQUEST(name, value) \
	if ((value)[0]) argv_add(env, (name), (value), ARGV_END);

    EXPORT_REQUEST("CLIENT_HOSTNAME", state.msg_attr.request->client_name);
    EXPORT_REQUEST("CLIENT_ADDRESS", state.msg_attr.request->client_addr);
    EXPORT_REQUEST("CLIENT_HELO", state.msg_attr.request->client_helo);
    EXPORT_REQUEST("CLIENT_PROTOCOL", state.msg_attr.request->client_proto);
    EXPORT_REQUEST("SASL_METHOD", state.msg_attr.request->sasl_method);
    EXPORT_REQUEST("SASL_SENDER", state.msg_attr.request->sasl_sender);
    EXPORT_REQUEST("SASL_USERNAME", state.msg_attr.request->sasl_username);

    argv_terminate(env);

    /*
     * Censor out undesirable characters from exported data.
     */
    for (cpp = env->argv; *cpp; cpp += 2)
	for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;)
	    *cp++ = '_';

    /*
     * Evaluate the command execution directory. Defer delivery if expansion
     * fails.
     */
    export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ);
    exec_dir = vstring_alloc(10);
    expand_status = local_expand(exec_dir, var_exec_directory,
				 &state, &usr_attr, var_exec_exp_filter);

    if (expand_status & MAC_PARSE_ERROR) {
	cmd_status = PIPE_STAT_DEFER;
	dsb_simple(why, "4.3.5", "mail system configuration error");
	msg_warn("bad parameter value syntax for %s: %s",
		 VAR_EXEC_DIRECTORY, var_exec_directory);
    } else {
	cmd_status = pipe_command(state.msg_attr.fp, why,
				  PIPE_CMD_UID, usr_attr.uid,
				  PIPE_CMD_GID, usr_attr.gid,
				  PIPE_CMD_COMMAND, command,
				  PIPE_CMD_COPY_FLAGS, copy_flags,
				  PIPE_CMD_SENDER, state.msg_attr.sender,
			  PIPE_CMD_ORIG_RCPT, state.msg_attr.rcpt.orig_addr,
			       PIPE_CMD_DELIVERED, state.msg_attr.delivered,
				  PIPE_CMD_TIME_LIMIT, var_command_maxtime,
				  PIPE_CMD_ENV, env->argv,
				  PIPE_CMD_EXPORT, export_env->argv,
				  PIPE_CMD_SHELL, var_local_cmd_shell,
				  PIPE_CMD_CWD, *STR(exec_dir) ?
				  STR(exec_dir) : (char *) 0,
				  PIPE_CMD_END);
    }
    vstring_free(exec_dir);
    argv_free(export_env);
    argv_free(env);

    /*
     * Depending on the result, bounce or defer the message.
     */
    switch (cmd_status) {
    case PIPE_STAT_OK:
	dsb_simple(why, "2.0.0", "delivered to command: %s", command);
	deliver_status = sent(BOUNCE_FLAGS(state.request),
			      SENT_ATTR(state.msg_attr));
	break;
    case PIPE_STAT_BOUNCE:
    case PIPE_STAT_DEFER:
	/* Account for possible owner- sender address override. */
	deliver_status = bounce_workaround(state);
	break;
    case PIPE_STAT_CORRUPT:
	deliver_status = DEL_STAT_DEFER;
	break;
    default:
	msg_panic("%s: bad status %d", myname, cmd_status);
	/* NOTREACHED */
    }

    return (deliver_status);
}
Beispiel #15
0
ARGV   *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr,
			               MAPS *maps, int propagate)
{
    ARGV   *argv;
    ARGV   *lookup;
    int     count;
    int     i;
    int     arg;
    BH_TABLE *been_here;
    char   *saved_lhs;

    /*
     * Initialize.
     */
    argv = argv_alloc(1);
    argv_add(argv, addr, ARGV_END);
    argv_terminate(argv);
    been_here = been_here_init(0, BH_FLAG_FOLD);

    /*
     * Rewrite the address vector in place. With each map lookup result,
     * split it into separate addresses, then rewrite and flatten each
     * address, and repeat the process. Beware: argv is being changed, so we
     * must index the array explicitly, instead of running along it with a
     * pointer.
     */
#define UPDATE(ptr,new)	do { \
	if (ptr) myfree(ptr); ptr = mystrdup(new); \
    } while (0)
#define STR	vstring_str
#define RETURN(x) do { \
	been_here_free(been_here); return (x); \
    } while (0)
#define UNEXPAND(argv, addr) do { \
	argv_truncate((argv), 0); argv_add((argv), (addr), (char *) 0); \
    } while (0)

    for (arg = 0; arg < argv->argc; arg++) {
	if (argv->argc > var_virt_expan_limit) {
	    msg_warn("%s: unreasonable %s map expansion size for %s -- "
		     "message not accepted, try again later",
		     state->queue_id, maps->title, addr);
	    state->errs |= CLEANUP_STAT_DEFER;
	    UPDATE(state->reason, "4.6.0 Alias expansion error");
	    UNEXPAND(argv, addr);
	    RETURN(argv);
	}
	for (count = 0; /* void */ ; count++) {

	    /*
	     * Don't expand an address that already expanded into itself.
	     */
	    if (been_here_check_fixed(been_here, argv->argv[arg]) != 0)
		break;
	    if (count >= var_virt_recur_limit) {
		msg_warn("%s: unreasonable %s map nesting for %s -- "
			 "message not accepted, try again later",
			 state->queue_id, maps->title, addr);
		state->errs |= CLEANUP_STAT_DEFER;
		UPDATE(state->reason, "4.6.0 Alias expansion error");
		UNEXPAND(argv, addr);
		RETURN(argv);
	    }
	    quote_822_local(state->temp1, argv->argv[arg]);
	    if ((lookup = mail_addr_map(maps, STR(state->temp1), propagate)) != 0) {
		saved_lhs = mystrdup(argv->argv[arg]);
		for (i = 0; i < lookup->argc; i++) {
		    unquote_822_local(state->temp1, lookup->argv[i]);
		    if (i == 0) {
			UPDATE(argv->argv[arg], STR(state->temp1));
		    } else {
			argv_add(argv, STR(state->temp1), ARGV_END);
			argv_terminate(argv);
		    }

		    /*
		     * Allow an address to expand into itself once.
		     */
		    if (strcasecmp(saved_lhs, STR(state->temp1)) == 0)
			been_here_fixed(been_here, saved_lhs);
		}
		myfree(saved_lhs);
		argv_free(lookup);
	    } else if (maps->error != 0) {
		msg_warn("%s: %s map lookup problem for %s -- "
			 "message not accepted, try again later",
			 state->queue_id, maps->title, addr);
		state->errs |= CLEANUP_STAT_WRITE;
		UPDATE(state->reason, "4.6.0 Alias expansion error");
		UNEXPAND(argv, addr);
		RETURN(argv);
	    } else {
		break;
	    }
	}
    }
    RETURN(argv);
}
Beispiel #16
0
int     main(int argc, char **argv)
{
    static char *full_name = 0;		/* sendmail -F */
    struct stat st;
    char   *slash;
    char   *sender = 0;			/* sendmail -f */
    int     c;
    int     fd;
    int     mode;
    ARGV   *ext_argv;
    int     debug_me = 0;
    int     err;
    int     n;
    int     flags = SM_FLAG_DEFAULT;
    char   *site_to_flush = 0;
    char   *id_to_flush = 0;
    char   *encoding = 0;
    char   *qtime = 0;
    const char *errstr;
    uid_t   uid;
    const char *rewrite_context = MAIL_ATTR_RWR_LOCAL;
    int     dsn_notify = 0;
    int     dsn_ret = 0;
    const char *dsn_envid = 0;
    int     saved_optind;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    /*
     * Be consistent with file permissions.
     */
    umask(022);

    /*
     * To minimize confusion, make sure that the standard file descriptors
     * are open before opening anything else. XXX Work around for 44BSD where
     * fstat can return EBADF on an open file descriptor.
     */
    for (fd = 0; fd < 3; fd++)
	if (fstat(fd, &st) == -1
	    && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
	    msg_fatal_status(EX_OSERR, "open /dev/null: %m");

    /*
     * The CDE desktop calendar manager leaks a parent file descriptor into
     * the child process. For the sake of sendmail compatibility we have to
     * close the file descriptor otherwise mail notification will hang.
     */
    for ( /* void */ ; fd < 100; fd++)
	(void) close(fd);

    /*
     * Process environment options as early as we can. We might be called
     * from a set-uid (set-gid) program, so be careful with importing
     * environment variables.
     */
    if (safe_getenv(CONF_ENV_VERB))
	msg_verbose = 1;
    if (safe_getenv(CONF_ENV_DEBUG))
	debug_me = 1;

    /*
     * Initialize. Set up logging, read the global configuration file and
     * extract configuration information. Set up signal handlers so that we
     * can clean up incomplete output.
     */
    if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
	argv[0] = slash + 1;
    msg_vstream_init(argv[0], VSTREAM_ERR);
    msg_cleanup(tempfail);
    msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY);
    set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * Some sites mistakenly install Postfix sendmail as set-uid root. Drop
     * set-uid privileges only when root, otherwise some systems will not
     * reset the saved set-userid, which would be a security vulnerability.
     */
    if (geteuid() == 0 && getuid() != 0) {
	msg_warn("the Postfix sendmail command has set-uid root file permissions");
	msg_warn("or the command is run from a set-uid root process");
	msg_warn("the Postfix sendmail command must be installed without set-uid root file permissions");
	set_ugid(getuid(), getgid());
    }

    /*
     * Further initialization. Load main.cf first, so that command-line
     * options can override main.cf settings. Pre-scan the argument list so
     * that we load the right main.cf file.
     */
#define GETOPT_LIST "A:B:C:F:GIL:N:O:R:UV:X:b:ce:f:h:imno:p:r:q:tvx"

    saved_optind = optind;
    while (argv[OPTIND] != 0) {
	if (strcmp(argv[OPTIND], "-q") == 0) {	/* not getopt compatible */
	    optind++;
	    continue;
	}
	if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0)
	    break;
	if (c == 'C') {
	    VSTRING *buf = vstring_alloc(1);

	    if (setenv(CONF_ENV_PATH,
		   strcmp(sane_basename(buf, optarg), MAIN_CONF_FILE) == 0 ?
		       sane_dirname(buf, optarg) : optarg, 1) < 0)
		msg_fatal_status(EX_UNAVAILABLE, "out of memory");
	    vstring_free(buf);
	}
    }
    optind = saved_optind;
    mail_conf_read();
    /* Re-evaluate mail_task() after reading main.cf. */
    msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY);
    get_mail_conf_str_table(str_table);

    if (chdir(var_queue_dir))
	msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);

    signal(SIGPIPE, SIG_IGN);

    /*
     * Optionally start the debugger on ourself. This must be done after
     * reading the global configuration file, because that file specifies
     * what debugger command to execute.
     */
    if (debug_me)
	debug_process();

    /*
     * The default mode of operation is determined by the process name. It
     * can, however, be changed via command-line options (for example,
     * "newaliases -bp" will show the mail queue).
     */
    if (strcmp(argv[0], "mailq") == 0) {
	mode = SM_MODE_MAILQ;
    } else if (strcmp(argv[0], "newaliases") == 0) {
	mode = SM_MODE_NEWALIAS;
    } else if (strcmp(argv[0], "smtpd") == 0) {
	mode = SM_MODE_DAEMON;
    } else {
	mode = SM_MODE_ENQUEUE;
    }

    /*
     * Parse JCL. Sendmail has been around for a long time, and has acquired
     * a large number of options in the course of time. Some options such as
     * -q are not parsable with GETOPT() and get special treatment.
     */
#define OPTIND  (optind > 0 ? optind : 1)

    while (argv[OPTIND] != 0) {
	if (strcmp(argv[OPTIND], "-q") == 0) {
	    if (mode == SM_MODE_DAEMON)
		msg_warn("ignoring -q option in daemon mode");
	    else
		mode = SM_MODE_FLUSHQ;
	    optind++;
	    continue;
	}
	if (strcmp(argv[OPTIND], "-V") == 0
	    && argv[OPTIND + 1] != 0 && strlen(argv[OPTIND + 1]) == 2) {
	    msg_warn("option -V is deprecated with Postfix 2.3; "
		     "specify -XV instead");
	    argv[OPTIND] = "-XV";
	}
	if (strncmp(argv[OPTIND], "-V", 2) == 0 && strlen(argv[OPTIND]) == 4) {
	    msg_warn("option %s is deprecated with Postfix 2.3; "
		     "specify -X%s instead",
		     argv[OPTIND], argv[OPTIND] + 1);
	    argv[OPTIND] = concatenate("-X", argv[OPTIND] + 1, (char *) 0);
	}
	if (strcmp(argv[OPTIND], "-XV") == 0) {
	    verp_delims = var_verp_delims;
	    optind++;
	    continue;
	}
	if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0)
	    break;
	switch (c) {
	default:
	    if (msg_verbose)
		msg_info("-%c option ignored", c);
	    break;
	case 'n':
	    msg_fatal_status(EX_USAGE, "-%c option not supported", c);
	case 'B':
	    if (strcmp(optarg, "8BITMIME") == 0)/* RFC 1652 */
		encoding = MAIL_ATTR_ENC_8BIT;
	    else if (strcmp(optarg, "7BIT") == 0)	/* RFC 1652 */
		encoding = MAIL_ATTR_ENC_7BIT;
	    else
		msg_fatal_status(EX_USAGE, "-B option needs 8BITMIME or 7BIT");
	    break;
	case 'F':				/* full name */
	    full_name = optarg;
	    break;
	case 'G':				/* gateway submission */
	    rewrite_context = MAIL_ATTR_RWR_REMOTE;
	    break;
	case 'I':				/* newaliases */
	    mode = SM_MODE_NEWALIAS;
	    break;
	case 'N':
	    if ((dsn_notify = dsn_notify_mask(optarg)) == 0)
		msg_warn("bad -N option value -- ignored");
	    break;
	case 'R':
	    if ((dsn_ret = dsn_ret_code(optarg)) == 0)
		msg_warn("bad -R option value -- ignored");
	    break;
	case 'V':				/* DSN, was: VERP */
	    if (strlen(optarg) > 100)
		msg_warn("too long -V option value -- ignored");
	    else if (!allprint(optarg))
		msg_warn("bad syntax in -V option value -- ignored");
	    else
		dsn_envid = optarg;
	    break;
	case 'X':
	    switch (*optarg) {
	    default:
		msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
	    case 'V':				/* VERP */
		if (verp_delims_verify(optarg + 1) != 0)
		    msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
				     var_verp_filter);
		verp_delims = optarg + 1;
		break;
	    }
	    break;
	case 'b':
	    switch (*optarg) {
	    default:
		msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
	    case 'd':				/* daemon mode */
	    case 'l':				/* daemon mode */
		if (mode == SM_MODE_FLUSHQ)
		    msg_warn("ignoring -q option in daemon mode");
		mode = SM_MODE_DAEMON;
		break;
	    case 'h':				/* print host status */
	    case 'H':				/* flush host status */
		mode = SM_MODE_IGNORE;
		break;
	    case 'i':				/* newaliases */
		mode = SM_MODE_NEWALIAS;
		break;
	    case 'm':				/* deliver mail */
		mode = SM_MODE_ENQUEUE;
		break;
	    case 'p':				/* mailq */
		mode = SM_MODE_MAILQ;
		break;
	    case 's':				/* stand-alone mode */
		mode = SM_MODE_USER;
		break;
	    case 'v':				/* expand recipients */
		flags |= DEL_REQ_FLAG_USR_VRFY;
		break;
	    }
	    break;
	case 'f':
	    sender = optarg;
	    break;
	case 'i':
	    flags &= ~SM_FLAG_AEOF;
	    break;
	case 'o':
	    switch (*optarg) {
	    default:
		if (msg_verbose)
		    msg_info("-%c%c option ignored", c, *optarg);
		break;
	    case 'A':
		if (optarg[1] == 0)
		    msg_fatal_status(EX_USAGE, "-oA requires pathname");
		myfree(var_alias_db_map);
		var_alias_db_map = mystrdup(optarg + 1);
		set_mail_conf_str(VAR_ALIAS_DB_MAP, var_alias_db_map);
		break;
	    case '7':
	    case '8':
		break;
	    case 'i':
		flags &= ~SM_FLAG_AEOF;
		break;
	    case 'm':
		break;
	    }
	    break;
	case 'r':				/* obsoleted by -f */
	    sender = optarg;
	    break;
	case 'q':
	    if (ISDIGIT(optarg[0])) {
		qtime = optarg;
	    } else if (optarg[0] == 'R') {
		site_to_flush = optarg + 1;
		if (*site_to_flush == 0)
		    msg_fatal_status(EX_USAGE, "specify: -qRsitename");
	    } else if (optarg[0] == 'I') {
		id_to_flush = optarg + 1;
		if (*id_to_flush == 0)
		    msg_fatal_status(EX_USAGE, "specify: -qIqueueid");
	    } else {
		msg_fatal_status(EX_USAGE, "-q%c is not implemented",
				 optarg[0]);
	    }
	    break;
	case 't':
	    flags |= SM_FLAG_XRCPT;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	case '?':
	    msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]);
	}
    }

    /*
     * Look for conflicting options and arguments.
     */
    if ((flags & SM_FLAG_XRCPT) && mode != SM_MODE_ENQUEUE)
	msg_fatal_status(EX_USAGE, "-t can be used only in delivery mode");

    if (site_to_flush && mode != SM_MODE_ENQUEUE)
	msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode");

    if (id_to_flush && mode != SM_MODE_ENQUEUE)
	msg_fatal_status(EX_USAGE, "-qI can be used only in delivery mode");

    if (flags & DEL_REQ_FLAG_USR_VRFY) {
	if (flags & SM_FLAG_XRCPT)
	    msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv");
	if (dsn_notify)
	    msg_fatal_status(EX_USAGE, "-N option cannot be used with -bv");
	if (dsn_ret)
	    msg_fatal_status(EX_USAGE, "-R option cannot be used with -bv");
	if (msg_verbose == 1)
	    msg_fatal_status(EX_USAGE, "-v option cannot be used with -bv");
    }

    /*
     * The -v option plays double duty. One requests verbose delivery, more
     * than one requests verbose logging.
     */
    if (msg_verbose == 1 && mode == SM_MODE_ENQUEUE) {
	msg_verbose = 0;
	flags |= DEL_REQ_FLAG_RECORD;
    }

    /*
     * Start processing. Everything is delegated to external commands.
     */
    if (qtime && mode != SM_MODE_DAEMON)
	exit(0);
    switch (mode) {
    default:
	msg_panic("unknown operation mode: %d", mode);
	/* NOTREACHED */
    case SM_MODE_ENQUEUE:
	if (site_to_flush) {
	    if (argv[OPTIND])
		msg_fatal_status(EX_USAGE, "flush site requires no recipient");
	    ext_argv = argv_alloc(2);
	    argv_add(ext_argv, "postqueue", "-s", site_to_flush, (char *) 0);
	    for (n = 0; n < msg_verbose; n++)
		argv_add(ext_argv, "-v", (char *) 0);
	    argv_terminate(ext_argv);
	    mail_run_replace(var_command_dir, ext_argv->argv);
	    /* NOTREACHED */
	} else if (id_to_flush) {
	    if (argv[OPTIND])
		msg_fatal_status(EX_USAGE, "flush queue_id requires no recipient");
	    ext_argv = argv_alloc(2);
	    argv_add(ext_argv, "postqueue", "-i", id_to_flush, (char *) 0);
	    for (n = 0; n < msg_verbose; n++)
		argv_add(ext_argv, "-v", (char *) 0);
	    argv_terminate(ext_argv);
	    mail_run_replace(var_command_dir, ext_argv->argv);
	    /* NOTREACHED */
	} else {
	    enqueue(flags, encoding, dsn_envid, dsn_ret, dsn_notify,
		    rewrite_context, sender, full_name, argv + OPTIND);
	    exit(0);
	    /* NOTREACHED */
	}
	break;
    case SM_MODE_MAILQ:
	if (argv[OPTIND])
	    msg_fatal_status(EX_USAGE,
			     "display queue mode requires no recipient");
	ext_argv = argv_alloc(2);
	argv_add(ext_argv, "postqueue", "-p", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(ext_argv, "-v", (char *) 0);
	argv_terminate(ext_argv);
	mail_run_replace(var_command_dir, ext_argv->argv);
	/* NOTREACHED */
    case SM_MODE_FLUSHQ:
	if (argv[OPTIND])
	    msg_fatal_status(EX_USAGE,
			     "flush queue mode requires no recipient");
	ext_argv = argv_alloc(2);
	argv_add(ext_argv, "postqueue", "-f", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(ext_argv, "-v", (char *) 0);
	argv_terminate(ext_argv);
	mail_run_replace(var_command_dir, ext_argv->argv);
	/* NOTREACHED */
    case SM_MODE_DAEMON:
	if (argv[OPTIND])
	    msg_fatal_status(EX_USAGE, "daemon mode requires no recipient");
	ext_argv = argv_alloc(2);
	argv_add(ext_argv, "postfix", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(ext_argv, "-v", (char *) 0);
	argv_add(ext_argv, "start", (char *) 0);
	argv_terminate(ext_argv);
	err = (mail_run_background(var_command_dir, ext_argv->argv) < 0);
	argv_free(ext_argv);
	exit(err);
	break;
    case SM_MODE_NEWALIAS:
	if (argv[OPTIND])
	    msg_fatal_status(EX_USAGE,
			 "alias initialization mode requires no recipient");
	if (*var_alias_db_map == 0)
	    return (0);
	ext_argv = argv_alloc(2);
	argv_add(ext_argv, "postalias", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(ext_argv, "-v", (char *) 0);
	argv_split_append(ext_argv, var_alias_db_map, CHARS_COMMA_SP);
	argv_terminate(ext_argv);
	mail_run_replace(var_command_dir, ext_argv->argv);
	/* NOTREACHED */
    case SM_MODE_USER:
	if (argv[OPTIND])
	    msg_fatal_status(EX_USAGE,
			     "stand-alone mode requires no recipient");
	/* The actual enforcement happens in the postdrop command. */
	if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl,
					   uid = getuid())) != 0)
	    msg_fatal_status(EX_NOPERM,
			     "User %s(%ld) is not allowed to submit mail",
			     errstr, (long) uid);
	ext_argv = argv_alloc(2);
	argv_add(ext_argv, "smtpd", "-S", (char *) 0);
	for (n = 0; n < msg_verbose; n++)
	    argv_add(ext_argv, "-v", (char *) 0);
	argv_terminate(ext_argv);
	mail_run_replace(var_daemon_dir, ext_argv->argv);
	/* NOTREACHED */
    case SM_MODE_IGNORE:
	exit(0);
	/* NOTREACHED */
    }
}
Beispiel #17
0
/* mysqlname_parse - parse mysql configuration file */
static MYSQL_NAME *mysqlname_parse(const char *mysqlcf)
{
    const char *myname = "mysqlname_parse";
    int     i;
    char   *hosts;
    MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
    ARGV   *hosts_argv;

    /* parser */
    name->parser = cfg_parser_alloc(mysqlcf);

    /* username */
    name->username = cfg_get_str(name->parser, "user", "", 0, 0);

    /* password */
    name->password = cfg_get_str(name->parser, "password", "", 0, 0);

    /* database name */
    name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);

    /* table name */
    name->table = cfg_get_str(name->parser, "table", "", 1, 0);

    /* select field */
    name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0);

    /* where field */
    name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0);

    /* additional conditions */
    name->additional_conditions = cfg_get_str(name->parser,
                                  "additional_conditions",
                                  "", 0, 0);

    /* mysql server hosts */
    hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);

    /* coo argv interface */
    hosts_argv = argv_split(hosts, " ,\t\r\n");
    if (hosts_argv->argc == 0) {
        /* no hosts specified,
        					 * default to 'localhost' */
        if (msg_verbose)
            msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
                     myname, mysqlcf);
        argv_add(hosts_argv, "localhost", ARGV_END);
        argv_terminate(hosts_argv);
    }
    name->len_hosts = hosts_argv->argc;
    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
    i = 0;
    for (i = 0; hosts_argv->argv[i] != NULL; i++) {
        name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
        if (msg_verbose)
            msg_info("%s: %s: adding host '%s' to list of mysql server hosts",
                     myname, mysqlcf, name->hostnames[i]);
    }
    myfree(hosts);
    argv_free(hosts_argv);
    return name;
}
Beispiel #18
0
static ARGV *expand_argv(const char *service, char **argv,
			         RECIPIENT_LIST *rcpt_list, int flags)
{
    VSTRING *buf = vstring_alloc(100);
    ARGV   *result;
    char  **cpp;
    PIPE_STATE state;
    int     i;
    char   *ext;
    char   *dom;

    /*
     * This appears to be simple operation (replace $name by its expansion).
     * However, it becomes complex because a command-line argument that
     * references $recipient must expand to as many command-line arguments as
     * there are recipients (that's wat programs called by sendmail expect).
     * So we parse each command-line argument, and depending on what we find,
     * we either expand the argument just once, or we expand it once for each
     * recipient. In either case we end up parsing the command-line argument
     * twice. The amount of CPU time wasted will be negligible.
     * 
     * Note: we can't use recursive macro expansion here, because recursion
     * would screw up mail addresses that contain $ characters.
     */
#define NO	0
#define EARLY_RETURN(x) { argv_free(result); vstring_free(buf); return (x); }

    result = argv_alloc(1);
    for (cpp = argv; *cpp; cpp++) {
	state.service = service;
	state.expand_flag = 0;
	if (mac_parse(*cpp, parse_callback, (char *) &state) & MAC_PARSE_ERROR)
	    EARLY_RETURN(0);
	if (state.expand_flag == 0) {		/* no $recipient etc. */
	    argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
	} else {				/* contains $recipient etc. */
	    for (i = 0; i < rcpt_list->len; i++) {

		/*
		 * This argument contains $recipient.
		 */
		if (state.expand_flag & PIPE_FLAG_RCPT) {
		    morph_recipient(buf, rcpt_list->info[i].address, flags);
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
		}

		/*
		 * This argument contains $original_recipient.
		 */
		if (state.expand_flag & PIPE_FLAG_ORIG_RCPT) {
		    morph_recipient(buf, rcpt_list->info[i].orig_addr, flags);
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_ORIG_RCPT, STR(buf));
		}

		/*
		 * This argument contains $user. Extract the plain user name.
		 * Either anything to the left of the extension delimiter or,
		 * in absence of the latter, anything to the left of the
		 * rightmost @.
		 * 
		 * Beware: if the user name is blank (e.g. +user@host), the
		 * argument is suppressed. This is necessary to allow for
		 * cyrus bulletin-board (global mailbox) delivery. XXX But,
		 * skipping empty user parts will also prevent other
		 * expansions of this specific command-line argument.
		 */
		if (state.expand_flag & PIPE_FLAG_USER) {
		    morph_recipient(buf, rcpt_list->info[i].address,
				    flags & PIPE_OPT_FOLD_ALL);
		    if (split_at_right(STR(buf), '@') == 0)
			msg_warn("no @ in recipient address: %s",
				 rcpt_list->info[i].address);
		    if (*var_rcpt_delim)
			split_addr(STR(buf), var_rcpt_delim);
		    if (*STR(buf) == 0)
			continue;
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
		}

		/*
		 * This argument contains $extension. Extract the recipient
		 * extension: anything between the leftmost extension
		 * delimiter and the rightmost @. The extension may be blank.
		 */
		if (state.expand_flag & PIPE_FLAG_EXTENSION) {
		    morph_recipient(buf, rcpt_list->info[i].address,
				    flags & PIPE_OPT_FOLD_ALL);
		    if (split_at_right(STR(buf), '@') == 0)
			msg_warn("no @ in recipient address: %s",
				 rcpt_list->info[i].address);
		    if (*var_rcpt_delim == 0
			|| (ext = split_addr(STR(buf), var_rcpt_delim)) == 0)
			ext = "";		/* insert null arg */
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
		}

		/*
		 * This argument contains $mailbox. Extract the mailbox name:
		 * anything to the left of the rightmost @.
		 */
		if (state.expand_flag & PIPE_FLAG_MAILBOX) {
		    morph_recipient(buf, rcpt_list->info[i].address,
				    flags & PIPE_OPT_FOLD_ALL);
		    if (split_at_right(STR(buf), '@') == 0)
			msg_warn("no @ in recipient address: %s",
				 rcpt_list->info[i].address);
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
		}

		/*
		 * This argument contains $domain. Extract the domain name:
		 * anything to the right of the rightmost @.
		 */
		if (state.expand_flag & PIPE_FLAG_DOMAIN) {
		    morph_recipient(buf, rcpt_list->info[i].address,
				    flags & PIPE_OPT_FOLD_ALL);
		    dom = split_at_right(STR(buf), '@');
		    if (dom == 0) {
			msg_warn("no @ in recipient address: %s",
				 rcpt_list->info[i].address);
			dom = "";		/* insert null arg */
		    }
		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_DOMAIN, dom);
		}

		/*
		 * Done.
		 */
		argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
	    }
	}
    }
    argv_terminate(result);
    vstring_free(buf);
    return (result);
}
Beispiel #19
0
static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
{
    const char *myname = "pgsql_parse_config";
    CFG_PARSER *p = dict_pgsql->parser;
    char   *hosts;
    VSTRING *query;
    char   *select_function;

    dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
    dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
    dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
    dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);

    /*
     * XXX: The default should be non-zero for safety, but that is not
     * backwards compatible.
     */
    dict_pgsql->expansion_limit = cfg_get_int(dict_pgsql->parser,
					      "expansion_limit", 0, 0, 0);

    if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {

	/*
	 * No query specified -- fallback to building it from components (
	 * old style "select %s from %s where %s" )
	 */
	query = vstring_alloc(64);
	select_function = cfg_get_str(p, "select_function", 0, 0, 0);
	if (select_function != 0) {
	    vstring_sprintf(query, "SELECT %s('%%s')", select_function);
	    myfree(select_function);
	} else
	    db_common_sql_build_query(query, p);
	dict_pgsql->query = vstring_export(query);
    }

    /*
     * Must parse all templates before we can use db_common_expand()
     */
    dict_pgsql->ctx = 0;
    (void) db_common_parse(&dict_pgsql->dict, &dict_pgsql->ctx,
			   dict_pgsql->query, 1);
    (void) db_common_parse(0, &dict_pgsql->ctx, dict_pgsql->result_format, 0);
    db_common_parse_domain(p, dict_pgsql->ctx);

    /*
     * Maps that use substring keys should only be used with the full input
     * key.
     */
    if (db_common_dict_partial(dict_pgsql->ctx))
	dict_pgsql->dict.flags |= DICT_FLAG_PATTERN;
    else
	dict_pgsql->dict.flags |= DICT_FLAG_FIXED;
    if (dict_pgsql->dict.flags & DICT_FLAG_FOLD_FIX)
	dict_pgsql->dict.fold_buf = vstring_alloc(10);

    hosts = cfg_get_str(p, "hosts", "", 0, 0);

    dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n");
    if (dict_pgsql->hosts->argc == 0) {
	argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
	argv_terminate(dict_pgsql->hosts);
	if (msg_verbose)
	    msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
		     myname, pgsqlcf, dict_pgsql->hosts->argv[0]);
    }
    myfree(hosts);
}
Beispiel #20
0
static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
{
    const char *myname = "mysql_parse_config";
    CFG_PARSER *p = dict_mysql->parser;
    VSTRING *buf;
    char   *hosts;

    dict_mysql->username = cfg_get_str(p, "user", "", 0, 0);
    dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
    dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
    dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
    dict_mysql->option_file = cfg_get_str(p, "option_file", NULL, 0, 0);
    dict_mysql->option_group = cfg_get_str(p, "option_group", NULL, 0, 0);
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
    dict_mysql->tls_key_file = cfg_get_str(p, "tls_key_file", NULL, 0, 0);
    dict_mysql->tls_cert_file = cfg_get_str(p, "tls_cert_file", NULL, 0, 0);
    dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
    dict_mysql->tls_CApath = cfg_get_str(p, "tls_CApath", NULL, 0, 0);
    dict_mysql->tls_ciphers = cfg_get_str(p, "tls_ciphers", NULL, 0, 0);
#if MYSQL_VERSION_ID >= 50023
    dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
#endif
#endif

    /*
     * XXX: The default should be non-zero for safety, but that is not
     * backwards compatible.
     */
    dict_mysql->expansion_limit = cfg_get_int(dict_mysql->parser,
					      "expansion_limit", 0, 0, 0);

    if ((dict_mysql->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) {

	/*
	 * No query specified -- fallback to building it from components (old
	 * style "select %s from %s where %s")
	 */
	buf = vstring_alloc(64);
	db_common_sql_build_query(buf, p);
	dict_mysql->query = vstring_export(buf);
    }

    /*
     * Must parse all templates before we can use db_common_expand()
     */
    dict_mysql->ctx = 0;
    (void) db_common_parse(&dict_mysql->dict, &dict_mysql->ctx,
			   dict_mysql->query, 1);
    (void) db_common_parse(0, &dict_mysql->ctx, dict_mysql->result_format, 0);
    db_common_parse_domain(p, dict_mysql->ctx);

    /*
     * Maps that use substring keys should only be used with the full input
     * key.
     */
    if (db_common_dict_partial(dict_mysql->ctx))
	dict_mysql->dict.flags |= DICT_FLAG_PATTERN;
    else
	dict_mysql->dict.flags |= DICT_FLAG_FIXED;
    if (dict_mysql->dict.flags & DICT_FLAG_FOLD_FIX)
	dict_mysql->dict.fold_buf = vstring_alloc(10);

    hosts = cfg_get_str(p, "hosts", "", 0, 0);

    dict_mysql->hosts = argv_split(hosts, " ,\t\r\n");
    if (dict_mysql->hosts->argc == 0) {
	argv_add(dict_mysql->hosts, "localhost", ARGV_END);
	argv_terminate(dict_mysql->hosts);
	if (msg_verbose)
	    msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
		     myname, mysqlcf, dict_mysql->hosts->argv[0]);
    }
    myfree(hosts);
}