/* * conf.c * * This file is part of msmtp, an SMTP client. * * Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007 * Martin Lambers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "gettext.h" #include "xalloc.h" #include "xvasprintf.h" #include "list.h" #include "smtp.h" #include "tools.h" #include "conf.h" /* buffer size for configuration file lines */ #define LINEBUFSIZE 501 /* * account_new() * * see conf.h */ account_t *account_new(const char *conffile, const char *id) { account_t *a; a = xmalloc(sizeof(account_t)); a->id = id ? xstrdup(id) : NULL; a->conffile = conffile ? xstrdup(conffile) : NULL; a->mask = 0; a->host = NULL; a->port = 0; /* this must be set later */ a->timeout = 0; a->protocol = SMTP_PROTO_SMTP; a->domain = xstrdup("localhost"); a->auto_from = 0; a->from = NULL; a->maildomain = NULL; a->dsn_return = NULL; a->dsn_notify = NULL; a->keepbcc = 0; a->auth_mech = NULL; a->username = NULL; a->password = NULL; a->ntlmdomain = NULL; a->tls = 0; a->tls_nostarttls = 0; a->tls_key_file = NULL; a->tls_cert_file = NULL; a->tls_trust_file = NULL; a->tls_nocertcheck = 0; a->tls_force_sslv3 = 0; a->logfile = NULL; a->syslog = NULL; return a; } /* * account_copy() * * see conf.h */ account_t *account_copy(account_t *acc) { account_t *a = NULL; if (acc) { a = xmalloc(sizeof(account_t)); a->id = acc->id ? xstrdup(acc->id) : NULL; a->conffile = acc->conffile ? xstrdup(acc->conffile) : NULL; a->mask = acc->mask; a->host = acc->host ? xstrdup(acc->host) : NULL; a->port = acc->port; a->timeout = acc->timeout; a->protocol = acc->protocol; a->domain = acc->domain ? xstrdup(acc->domain) : NULL; a->auto_from = acc->auto_from; a->from = acc->from ? xstrdup(acc->from) : NULL; a->maildomain = acc->maildomain ? xstrdup(acc->maildomain) : NULL; a->dsn_return = acc->dsn_return ? xstrdup(acc->dsn_return) : NULL; a->dsn_notify = acc->dsn_notify ? xstrdup(acc->dsn_notify) : NULL; a->keepbcc = acc->keepbcc; a->auth_mech = acc->auth_mech ? xstrdup(acc->auth_mech) : NULL; a->username = acc->username ? xstrdup(acc->username) : NULL; a->password = acc->password ? xstrdup(acc->password) : NULL; a->ntlmdomain = acc->ntlmdomain ? xstrdup(acc->ntlmdomain) : NULL; a->tls = acc->tls; a->tls_nostarttls = acc->tls_nostarttls; a->tls_key_file = acc->tls_key_file ? xstrdup(acc->tls_key_file) : NULL; a->tls_cert_file = acc->tls_cert_file ? xstrdup(acc->tls_cert_file) : NULL; a->tls_trust_file = acc->tls_trust_file ? xstrdup(acc->tls_trust_file) : NULL; a->tls_nocertcheck = acc->tls_nocertcheck; a->tls_force_sslv3 = acc->tls_force_sslv3; a->logfile = acc->logfile ? xstrdup(acc->logfile) : NULL; a->syslog = acc->syslog ? xstrdup(acc->syslog) : NULL; } return a; } /* * account_free() * * see conf.h */ void account_free(void *a) { account_t *p = a; if (p) { free(p->id); free(p->conffile); free(p->host); free(p->domain); free(p->from); free(p->maildomain); free(p->auth_mech); free(p->username); free(p->password); free(p->ntlmdomain); free(p->tls_key_file); free(p->tls_cert_file); free(p->tls_trust_file); free(p->dsn_return); free(p->dsn_notify); free(p->logfile); free(p->syslog); free(p); } } /* * find_account() * * see conf.h */ account_t *find_account(list_t *acc_list, const char *id) { account_t *a = NULL; char *acc_id; while (!list_is_empty(acc_list)) { acc_list = acc_list->next; acc_id = ((account_t *)(acc_list->data))->id; if (acc_id && strcmp(id, acc_id) == 0) { a = acc_list->data; break; } } return a; } /* * find_account_by_envelope_from() * * see conf.h */ account_t *find_account_by_envelope_from(list_t *acc_list, const char *from) { account_t *a = NULL; char *acc_from; while (!list_is_empty(acc_list)) { acc_list = acc_list->next; acc_from = ((account_t *)(acc_list->data))->from; if (acc_from && strcasecmp(from, acc_from) == 0) { a = acc_list->data; break; } } return a; } /* * is_on(), is_off() * * see conf.h */ int is_on(char *s) { return (strcmp(s, "on") == 0); } int is_off(char *s) { return (strcmp(s, "off") == 0); } /* * get_pos_int() * * see conf.h */ int get_pos_int(const char *s) { long x; char *p; errno = 0; x = strtol(s, &p, 0); if (p == s || x <= 0 || (x == LONG_MAX && errno == ERANGE) || x > INT_MAX) { x = -1; } else if (*p != '\0') { /* trailing garbage */ x = -1; } return x; } /* * check_auth_arg() * * see conf.h */ int check_auth_arg(char *arg) { size_t l, i; if (*arg == '\0') { return 0; } else if (strcmp(arg, "plain") == 0 || strcmp(arg, "cram-md5") == 0 || strcmp(arg, "digest-md5") == 0 || strcmp(arg, "gssapi") == 0 || strcmp(arg, "external") == 0 || strcmp(arg, "login") == 0 || strcmp(arg, "ntlm") == 0) { l = strlen(arg); for (i = 0; i < l; i++) { arg[i] = toupper((unsigned char)arg[i]); } return 0; } else { return 1; } } /* * check_dsn_notify_arg() * * see conf.h */ int check_dsn_notify_arg(char *arg) { int count; size_t i; size_t l; if (strcmp(arg, "never") != 0) { l = 0; count = 0; if (strstr(arg, "failure")) { count++; l += 7; } if (strstr(arg, "delay")) { count++; l += 5; } if (strstr(arg, "success")) { count++; l += 7; } if (count == 0 || (strlen(arg) != l + count - 1) || (count == 2 && !strchr(arg, ',')) || (count == 3 && !(strchr(arg, ',') && strchr(strchr(arg, ',') + 1, ',')))) { return 1; } } l = strlen(arg); for (i = 0; i < l; i++) { arg[i] = toupper((unsigned char)arg[i]); } return 0; } /* * check_syslog_arg() * * see conf.h */ int check_syslog_arg(const char *arg) { if (strcmp(arg, "LOG_USER") == 0 || strcmp(arg, "LOG_MAIL") == 0 || (strncmp(arg, "LOG_LOCAL", 9) == 0 && strlen(arg) == 10 && (arg[9] == '0' || arg[9] == '1' || arg[9] == '2' || arg[9] == '3' || arg[9] == '4' || arg[9] == '5' || arg[9] == '6' || arg[9] == '7'))) { return 0; } else { return 1; } } /* * get_default_syslog_facility() * * Returns a pointer to an allocated string containing the default syslog * facility. */ char *get_default_syslog_facility(void) { return xstrdup("LOG_USER"); } /* * override_account() * * see conf.h */ void override_account(account_t *acc1, account_t *acc2) { if (acc2->conffile) { free(acc1->conffile); acc1->conffile = xstrdup(acc2->conffile); } if (acc2->mask & ACC_HOST) { free(acc1->host); acc1->host = acc2->host ? xstrdup(acc2->host) : NULL; } if (acc2->mask & ACC_PORT) { acc1->port = acc2->port; } if (acc2->mask & ACC_TIMEOUT) { acc1->timeout = acc2->timeout; } if (acc2->mask & ACC_PROTOCOL) { acc1->protocol = acc2->protocol; } if (acc2->mask & ACC_DOMAIN) { free(acc1->domain); acc1->domain = acc2->domain ? xstrdup(acc2->domain) : NULL; } if (acc2->mask & ACC_AUTO_FROM) { acc1->auto_from = acc2->auto_from; } if (acc2->mask & ACC_FROM) { free(acc1->from); acc1->from = acc2->from ? xstrdup(acc2->from) : NULL; } if (acc2->mask & ACC_MAILDOMAIN) { free(acc1->maildomain); acc1->maildomain = acc2->maildomain ? xstrdup(acc2->maildomain) : NULL; } if (acc2->mask & ACC_AUTH_MECH) { free(acc1->auth_mech); acc1->auth_mech = acc2->auth_mech ? xstrdup(acc2->auth_mech) : NULL; } if (acc2->mask & ACC_USERNAME) { free(acc1->username); acc1->username = acc2->username ? xstrdup(acc2->username) : NULL; } if (acc2->mask & ACC_PASSWORD) { free(acc1->password); acc1->password = acc2->password ? xstrdup(acc2->password) : NULL; } if (acc2->mask & ACC_NTLMDOMAIN) { free(acc1->ntlmdomain); acc1->ntlmdomain = acc2->ntlmdomain ? xstrdup(acc2->ntlmdomain) : NULL; } if (acc2->mask & ACC_TLS) { acc1->tls = acc2->tls; } if (acc2->mask & ACC_TLS_NOSTARTTLS) { acc1->tls_nostarttls = acc2->tls_nostarttls; } if (acc2->mask & ACC_TLS_KEY_FILE) { free(acc1->tls_key_file); acc1->tls_key_file = acc2->tls_key_file ? xstrdup(acc2->tls_key_file) : NULL; } if (acc2->mask & ACC_TLS_CERT_FILE) { free(acc1->tls_cert_file); acc1->tls_cert_file = acc2->tls_cert_file ? xstrdup(acc2->tls_cert_file) : NULL; } if (acc2->mask & ACC_TLS_TRUST_FILE) { free(acc1->tls_trust_file); acc1->tls_trust_file = acc2->tls_trust_file ? xstrdup(acc2->tls_trust_file) : NULL; } if (acc2->mask & ACC_TLS_FORCE_SSLV3) { acc1->tls_force_sslv3 = acc2->tls_force_sslv3; } if (acc2->mask & ACC_TLS_NOCERTCHECK) { acc1->tls_nocertcheck = acc2->tls_nocertcheck; } if (acc2->mask & ACC_DSN_RETURN) { free(acc1->dsn_return); acc1->dsn_return = acc2->dsn_return ? xstrdup(acc2->dsn_return) : NULL; } if (acc2->mask & ACC_DSN_NOTIFY) { free(acc1->dsn_notify); acc1->dsn_notify = acc2->dsn_notify ? xstrdup(acc2->dsn_notify) : NULL; } if (acc2->mask & ACC_KEEPBCC) { acc1->keepbcc = acc2->keepbcc; } if (acc2->mask & ACC_LOGFILE) { free(acc1->logfile); acc1->logfile = acc2->logfile ? xstrdup(acc2->logfile) : NULL; } if (acc2->mask & ACC_SYSLOG) { free(acc1->syslog); acc1->syslog = acc2->syslog ? xstrdup(acc2->syslog) : NULL; } acc1->mask |= acc2->mask; } /* * check_account() * * see conf.h */ int check_account(account_t *acc, int sendmail_mode, char **errstr) { if (!acc->host) { *errstr = xasprintf(_("host not set")); return CONF_ESYNTAX; } if (acc->port == 0) { *errstr = xasprintf(_("port not set")); return CONF_ESYNTAX; } if (sendmail_mode && !acc->from) { *errstr = xasprintf(_("envelope-from address is missing")); return CONF_ESYNTAX; } if (acc->tls_key_file && !acc->tls_cert_file) { *errstr = xasprintf(_("tls_key_file requires tls_cert_file")); return CONF_ESYNTAX; } if (!acc->tls_key_file && acc->tls_cert_file) { *errstr = xasprintf(_("tls_cert_file requires tls_key_file")); return CONF_ESYNTAX; } if (acc->tls_nocertcheck && acc->tls_trust_file) { *errstr = xasprintf( _("cannot use tls_trust_file with tls_certcheck turned off")); return CONF_ESYNTAX; } if (acc->tls && !acc->tls_trust_file && !acc->tls_nocertcheck) { *errstr = xasprintf( _("tls requires either tls_trust_file (highly recommended) or " "a disabled tls_certcheck")); return CONF_ESYNTAX; } return CONF_EOK; } /* * some small helper functions */ int is_blank(int c) { return (c == ' ' || c == '\t'); } int skip_blanks(const char *s, int i) { while (is_blank(s[i])) { i++; } return i; } int get_cmd_length(const char *s) { int i = 0; while (s[i] != '\0' && !is_blank(s[i])) { i++; } return i; } /* get index of last non-blank character. -1 means there is none. */ int get_last_nonblank(const char *s) { int i; i = (int)strlen(s) - 1; while (i >= 0 && is_blank(s[i])) { i--; } return i; } /* Return string without whitespace at beginning and end. If the string is * enclosed in double quotes, remove these, too. String is allocated. */ char *trim_string(const char *s) { char *t; int i; int l; i = skip_blanks(s, 0); l = get_last_nonblank(s + i); if (l >= 1 && s[i] == '"' && s[i + l] == '"') { t = xmalloc(l * sizeof(char)); strncpy(t, s + i + 1, (size_t)l - 1); t[l - 1] = '\0'; } else { t = xmalloc((l + 2) * sizeof(char)); strncpy(t, s + i, (size_t)l + 1); t[l + 1] = '\0'; } return t; } /* * get_next_cmd() * * Read a line from 'f'. Split it in a command part (first word after * whitespace) and an argument part (the word after the command). * Whitespace is ignored. * Sets the flag 'empty_line' if the line is empty. * Sets the flag 'eof' if EOF occured. * On errors, 'empty_line' and 'eof', 'cmd' and 'arg' NULL. * On success, 'cmd' and 'arg' are allocated strings. * Used error codes: CONF_EIO, CONF_EPARSE */ int get_next_cmd(FILE *f, char **cmd, char **arg, int *empty_line, int *eof, char **errstr) { char line[LINEBUFSIZE]; char *p; int i; int l; *eof = 0; *empty_line = 0; *cmd = NULL; *arg = NULL; if (!fgets(line, (int)sizeof(line), f)) { if (ferror(f)) { *errstr = xasprintf(_("input error")); return CONF_EIO; } else /* EOF */ { *eof = 1; return CONF_EOK; } } /* Kill '\n'. Beware: sometimes the last line of a file has no '\n' */ if ((p = strchr(line, '\n'))) { *p = '\0'; /* Kill '\r' (if CRLF line endings are used) */ if (p > line && *(p - 1) == '\r') { *(p - 1) = '\0'; } } else if (strlen(line) == LINEBUFSIZE - 1) { *errstr = xasprintf(_("line longer than %d characters"), LINEBUFSIZE - 1); return CONF_EPARSE; } i = skip_blanks(line, 0); if (line[i] == '#' || line[i] == '\0') { *empty_line = 1; return CONF_EOK; } l = get_cmd_length(line + i); *cmd = xmalloc((l + 1) * sizeof(char)); strncpy(*cmd, line + i, (size_t)l); (*cmd)[l] = '\0'; *arg = trim_string(line + i + l); return CONF_EOK; } /* * read_account_list() * * Helper function for the account command: For every account name in the comma * separated string 's' search the account in 'acc_list' and add a pointer to * it to 'l'. */ int read_account_list(int line, list_t *acc_list, char *s, list_t *l, char **errstr) { list_t *lp = l; char *comma; char *acc_id; account_t *acc; for (;;) { comma = strchr(s, ','); if (comma) { *comma = '\0'; } acc_id = trim_string(s); if (*acc_id == '\0') { free(acc_id); *errstr = xasprintf(_("line %d: missing account name"), line); return CONF_ESYNTAX; } if (!(acc = find_account(acc_list, acc_id))) { *errstr = xasprintf(_("line %d: account %s not (yet) defined"), line, acc_id); free(acc_id); return CONF_ESYNTAX; } free(acc_id); list_insert(lp, acc); lp = lp->next; if (comma) { s = comma + 1; } else { break; } } return CONF_EOK; } /* * read_conffile() * * Read configuration data from 'f' and store it in 'acc_list'. * The name of the configuration file, 'conffile', will be stored in the * "conffile" field of each account. * Unless an error code is returned, 'acc_list' will always be a new list; * it may be empty if no accounts were found. * Used error codes: CONF_EIO, CONF_EPARSE, CONF_ESYNTAX */ int read_conffile(const char *conffile, FILE *f, list_t **acc_list, char **errstr) { int e; list_t *p; account_t *defaults; account_t *acc; int line; char *cmd; char *arg; int empty_line; int eof; /* for the account command: */ char *acc_id; char *t; list_t *copy_from; list_t *lp; defaults = account_new(NULL, NULL); *acc_list = list_new(); p = *acc_list; acc = NULL; e = CONF_EOK; for (line = 1; ; line++) { if ((e = get_next_cmd(f, &cmd, &arg, &empty_line, &eof, errstr)) != CONF_EOK) { break; } if (empty_line) { continue; } if (eof) { break; } /* compatibility with 1.2.x: if no account command is given, the first * account will be named "default". */ if (!acc && strcmp(cmd, "account") != 0 && strcmp(cmd, "defaults") != 0) { acc = account_copy(defaults); acc->id = xstrdup("default"); acc->conffile = xstrdup(conffile); acc->mask = 0; list_insert(p, acc); p = p->next; } if (strcmp(cmd, "defaults") == 0) { if (*arg != '\0') { *errstr = xasprintf( _("line %d: command %s does not take an argument"), line, cmd); e = CONF_ESYNTAX; break; } acc = defaults; } else if (strcmp(cmd, "account") == 0) { copy_from = list_new(); if ((t = strchr(arg, ':'))) { if ((e = read_account_list(line, *acc_list, t + 1, copy_from, errstr)) != CONF_EOK) { list_free(copy_from); break; } *t = '\0'; acc_id = trim_string(arg); } else { acc_id = xstrdup(arg); } if (*acc_id == '\0') { list_free(copy_from); *errstr = xasprintf(_("line %d: missing account name"), line); e = CONF_ESYNTAX; break; } if (strchr(acc_id, ':') || strchr(acc_id, ',')) { list_free(copy_from); *errstr = xasprintf(_("line %d: an account name must not " "contain colons or commas"), line); e = CONF_ESYNTAX; break; } if (find_account(*acc_list, acc_id)) { list_free(copy_from); *errstr = xasprintf( _("line %d: account %s was already defined " "in this file"), line, arg); e = CONF_ESYNTAX; break; } acc = account_copy(defaults); acc->id = acc_id; acc->conffile = xstrdup(conffile); acc->mask = 0; list_insert(p, acc); p = p->next; lp = copy_from; while (!list_is_empty(lp)) { lp = lp->next; override_account(acc, lp->data); } list_free(copy_from); } else if (strcmp(cmd, "host") == 0) { acc->mask |= ACC_HOST; if (*arg == '\0') { *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { free(acc->host); acc->host = xstrdup(arg); } } else if (strcmp(cmd, "port") == 0) { acc->mask |= ACC_PORT; if (*arg == '\0') { /* We should go back to the default, which is to call * get_default_port(), but we cannot since the account is not * complete yet. So demand an argument here. */ *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { acc->port = get_pos_int(arg); if (acc->port < 1 || acc->port > 65535) { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } } else if (strcmp(cmd, "timeout") == 0 || strcmp(cmd, "connect_timeout") == 0) { /* For compatibility with versions <= 1.4.1, connect_timeout is * accepted as an alias for timeout, though it had a slightly * different meaning. */ acc->mask |= ACC_TIMEOUT; if (*arg == '\0') { *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { if (is_off(arg)) { acc->timeout = 0; } else { acc->timeout = get_pos_int(arg); if (acc->timeout < 1) { *errstr = xasprintf(_("line %d: invalid argument %s " "for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } } } else if (strcmp(cmd, "protocol") == 0) { acc->mask |= ACC_PROTOCOL; if (*arg == '\0') { *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { if (strcmp(arg, "smtp") == 0) { acc->protocol = SMTP_PROTO_SMTP; } else if (strcmp(arg, "lmtp") == 0) { acc->protocol = SMTP_PROTO_LMTP; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } } else if (strcmp(cmd, "domain") == 0) { acc->mask |= ACC_DOMAIN; free(acc->domain); acc->domain = xstrdup(arg); } else if (strcmp(cmd, "auto_from") == 0) { acc->mask |= ACC_AUTO_FROM; if (*arg == '\0' || is_on(arg)) { acc->auto_from = 1; } else if (is_off(arg)) { acc->auto_from = 0; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "from") == 0) { acc->mask |= ACC_FROM; free(acc->from); acc->from = xstrdup(arg); } else if (strcmp(cmd, "maildomain") == 0) { acc->mask |= ACC_MAILDOMAIN; free(acc->maildomain); acc->maildomain = xstrdup(arg); } else if (strcmp(cmd, "auth") == 0) { acc->mask |= ACC_AUTH_MECH; free(acc->auth_mech); if (*arg == '\0' || is_on(arg)) { acc->auth_mech = xstrdup(""); } else if (is_off(arg)) { acc->auth_mech = NULL; } else if (check_auth_arg(arg) == 0) { acc->auth_mech = xstrdup(arg); } else { acc->auth_mech = NULL; *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "user") == 0) { acc->mask |= ACC_USERNAME; free(acc->username); acc->username = (*arg == '\0') ? NULL : xstrdup(arg); } else if (strcmp(cmd, "password") == 0) { acc->mask |= ACC_PASSWORD; free(acc->password); acc->password = (*arg == '\0') ? NULL : xstrdup(arg); } else if (strcmp(cmd, "ntlmdomain") == 0) { acc->mask |= ACC_NTLMDOMAIN; free(acc->ntlmdomain); acc->ntlmdomain = (*arg == '\0') ? NULL : xstrdup(arg); } else if (strcmp(cmd, "tls") == 0) { acc->mask |= ACC_TLS; if (*arg == '\0' || is_on(arg)) { acc->tls = 1; } else if (is_off(arg)) { acc->tls = 0; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "tls_starttls") == 0) { acc->mask |= ACC_TLS_NOSTARTTLS; if (*arg == '\0' || is_on(arg)) { acc->tls_nostarttls = 0; } else if (is_off(arg)) { acc->tls_nostarttls = 1; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "tls_key_file") == 0) { acc->mask |= ACC_TLS_KEY_FILE; free(acc->tls_key_file); if (*arg == '\0') { acc->tls_key_file = NULL; } else { acc->tls_key_file = expand_tilde(arg); } } else if (strcmp(cmd, "tls_cert_file") == 0) { acc->mask |= ACC_TLS_CERT_FILE; free(acc->tls_cert_file); if (*arg == '\0') { acc->tls_cert_file = NULL; } else { acc->tls_cert_file = expand_tilde(arg); } } else if (strcmp(cmd, "tls_trust_file") == 0) { acc->mask |= ACC_TLS_TRUST_FILE; free(acc->tls_trust_file); if (*arg == '\0') { acc->tls_trust_file = NULL; } else { acc->tls_trust_file = expand_tilde(arg); } } else if (strcmp(cmd, "tls_certcheck") == 0) { acc->mask |= ACC_TLS_NOCERTCHECK; if (*arg == '\0' || is_on(arg)) { acc->tls_nocertcheck = 0; } else if (is_off(arg)) { acc->tls_nocertcheck = 1; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "tls_force_sslv3") == 0) { acc->mask |= ACC_TLS_FORCE_SSLV3; if (*arg == '\0' || is_on(arg)) { acc->tls_force_sslv3 = 1; } else if (is_off(arg)) { acc->tls_force_sslv3 = 0; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "dsn_return") == 0) { acc->mask |= ACC_DSN_RETURN; free(acc->dsn_return); if (*arg == '\0') { *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { if (is_off(arg)) { acc->dsn_return = NULL; } else if (strcmp(arg, "headers") == 0) { acc->dsn_return = xstrdup("HDRS"); } else if (strcmp(arg, "full") == 0) { acc->dsn_return = xstrdup("FULL"); } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } } else if (strcmp(cmd, "dsn_notify") == 0) { acc->mask |= ACC_DSN_NOTIFY; free(acc->dsn_notify); if (*arg == '\0') { *errstr = xasprintf(_("line %d: command %s needs an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { if (is_off(arg)) { acc->dsn_notify = NULL; } else if (check_dsn_notify_arg(arg) == 0) { acc->dsn_notify = xstrdup(arg); } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } } else if (strcmp(cmd, "keepbcc") == 0) { acc->mask |= ACC_KEEPBCC; if (*arg == '\0' || is_on(arg)) { acc->keepbcc = 1; } else if (is_off(arg)) { acc->keepbcc = 0; } else { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } } else if (strcmp(cmd, "logfile") == 0) { acc->mask |= ACC_LOGFILE; free(acc->logfile); if (*arg == '\0') { acc->logfile = NULL; } else { acc->logfile = expand_tilde(arg); } } else if (strcmp(cmd, "syslog") == 0) { acc->mask |= ACC_SYSLOG; free(acc->syslog); if (*arg == '\0' || is_on(arg)) { acc->syslog = get_default_syslog_facility(); } else if (is_off(arg)) { acc->syslog = NULL; } else { if (check_syslog_arg(arg) != 0) { *errstr = xasprintf( _("line %d: invalid argument %s for command %s"), line, arg, cmd); e = CONF_ESYNTAX; break; } acc->syslog = xstrdup(arg); } } else if (strcmp(cmd, "tls_nocertcheck") == 0) { /* compatibility with 1.2.x */ acc->mask |= ACC_TLS_NOCERTCHECK; if (*arg != '\0') { *errstr = xasprintf( _("line %d: command %s does not take an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { acc->tls_nocertcheck = 1; } } else if (strcmp(cmd, "tls_nostarttls") == 0) { /* compatibility with 1.2.x */ acc->mask |= ACC_TLS_NOSTARTTLS; if (*arg != '\0') { *errstr = xasprintf( _("line %d: command %s does not take an argument"), line, cmd); e = CONF_ESYNTAX; break; } else { acc->tls_nostarttls = 1; } } else { *errstr = xasprintf(_("line %d: unknown command %s"), line, cmd); e = CONF_ESYNTAX; break; } free(cmd); free(arg); } free(cmd); free(arg); if (e != CONF_EOK) { list_xfree(*acc_list, account_free); *acc_list = NULL; } account_free(defaults); return e; } /* * get_conf() * * see conf.h */ int get_conf(const char *conffile, int securitycheck, list_t **acc_list, char **errstr) { FILE *f; int e; if (!(f = fopen(conffile, "r"))) { *errstr = xasprintf("%s", strerror(errno)); return CONF_ECANTOPEN; } if (securitycheck) { switch (check_secure(conffile)) { case 1: *errstr = xasprintf(_("must be owned by you")); e = CONF_EINSECURE; break; case 2: *errstr = xasprintf(_("must have no more than user " "read/write permissions")); e = CONF_EINSECURE; break; case 3: *errstr = xasprintf("%s", strerror(errno)); e = CONF_EIO; break; default: e = CONF_EOK; } if (e != CONF_EOK) { fclose(f); return e; } } if ((e = read_conffile(conffile, f, acc_list, errstr)) != CONF_EOK) { fclose(f); return e; } fclose(f); return CONF_EOK; }