Hi,
This is to (re)initiate discussion about implementing newaliases/makemap
as smtpctl subcommands (issue #354). This diff proposes a way to
track the tables used for alias and then generate the db. The
implementation is borrowed from makemap.c minus the global state.
Makemap isn't done yet as I would like to know if this the right
way to proceed.
diff --git a/smtpd/enqueue.c b/smtpd/enqueue.c
index d97620f..ed73a9b 100644
--- a/smtpd/enqueue.c
+++ b/smtpd/enqueue.c
@@ -97,10 +97,10 @@ struct {
#define WSP(c) (c == ' ' || c == '\t')
-int verbose = 0;
-char host[SMTPD_MAXHOSTNAMELEN];
-char *user = NULL;
-time_t timestamp;
+int verbose = 0;
+static char host[SMTPD_MAXHOSTNAMELEN];
+char *user = NULL;
+time_t timestamp;
struct {
int fd;
diff --git a/smtpd/parse.y b/smtpd/parse.y
index 887e864..718aeb8 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -759,6 +759,7 @@ tables : tablenew { $$ =
$1; }
;
alias : ALIAS tables {
+ char *association;
struct table *t = $2;
if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
@@ -767,6 +768,9 @@ alias : ALIAS tables {
YYERROR;
}
+ association = xmalloc(32 * sizeof(char), "parse_alias");
+ strlcpy(association, "alias", sizeof(association));
+ dict_xset(&t->t_dict, "association", association);
$$ = t;
}
;
diff --git a/smtpd/smtpctl.c b/smtpd/smtpctl.c
index ee2aafc..52eb3ba 100644
--- a/smtpd/smtpctl.c
+++ b/smtpd/smtpctl.c
@@ -23,28 +23,35 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/un.h>
#include <sys/wait.h>
+#include <ctype.h>
+#include <db.h>
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <fcntl.h>
#include <fts.h>
#include <imsg.h>
#include <inttypes.h>
+#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <util.h>
#include <unistd.h>
#include "smtpd.h"
#include "parser.h"
#include "log.h"
+#define CONF_FILE "/etc/mail/smtpd.conf"
#define PATH_GZCAT "/usr/bin/gzcat"
#define PATH_CAT "/bin/cat"
#define PATH_QUEUE "/queue"
@@ -63,6 +70,10 @@ static int is_gzip_fp(FILE *);
static int is_encrypted_fp(FILE *);
static int is_encrypted_buffer(const char *);
static int is_gzip_buffer(const char *);
+static int parse_table(struct table *, struct stat *);
+static int parse_file(DB *, const char *);
+static int parse_entry(DB *, const char *, char *, size_t , size_t);
+static int make_aliases(DBT *, char *);
extern char *__progname;
int sendmail;
@@ -405,6 +416,39 @@ do_log_verbose(int argc, struct parameter *argv)
}
static int
+do_newaliases(int argc, struct parameter *argv)
+{
+ struct smtpd smtpd;
+ struct stat sb;
+ int nentries = 0;
+ struct table *t;
+ const char *conf = CONF_FILE, *key = "association";
+ char *val;
+ void *iter_tbl = NULL;
+
+ if (argc)
+ conf = argv[0].u.u_str;
+
+ if (stat(conf, &sb) == -1)
+ err(1, "stat: %s", conf);
+
+ env = &smtpd;
+ if (parse_config(env, conf, 0))
+ errx(1, "failed to parse config file %s", conf);
+
+ while (dict_iter(env->sc_tables_dict, &iter_tbl, NULL, (void **)&t)) {
+ if (dict_check(&t->t_dict, key))
+ if ((val = dict_get(&t->t_dict, key)) != NULL &&
+ !strcmp(val, "alias")) {
+ nentries = parse_table(t, &sb);
+ printf("%s: %d aliases\n", t->t_name, nentries);
+ }
+
+ }
+ return (0);
+}
+
+static int
do_monitor(int argc, struct parameter *argv)
{
struct stat_digest last, digest;
@@ -881,6 +925,8 @@ main(int argc, char **argv)
cmd_install("log brief", do_log_brief);
cmd_install("log verbose", do_log_verbose);
cmd_install("monitor", do_monitor);
+ cmd_install("newaliases", do_newaliases);
+ cmd_install("newaliases <str>", do_newaliases);
cmd_install("pause envelope <evpid>", do_pause_envelope);
cmd_install("pause envelope <msgid>", do_pause_envelope);
cmd_install("pause mda", do_pause_mda);
@@ -1207,3 +1253,193 @@ end:
fseek(fp, 0, SEEK_SET);
return ret;
}
+
+static int
+parse_table(struct table *t, struct stat *sb)
+{
+ char dbname[SMTPD_MAXPATHLEN], *p;
+ char target[SMTPD_MAXPATHLEN];
+ int nentries = 0;
+ DB *db;
+ const char *path;
+ mode_t omode;
+
+ path = xstrdup(t->t_config, "parse_table");
+ p = strstr(path, ".db");
+ if (p && strcmp(p, ".db") == 0)
+ *p = '\0';
+
+ if (! bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", path))
+ errx(1, "path too long");
+
+ omode = umask(7077);
+ if (mkstemp(dbname) == -1)
+ err(1, "mkstemp");
+ umask(omode);
+
+ db = dbopen(dbname, O_EXLOCK|O_RDWR|O_SYNC, 0644, DB_HASH, NULL);
+ if (db == NULL)
+ err(1, "dbopen");
+
+ if (fchmod(db->fd(db), sb->st_mode) == -1 ||
+ fchown(db->fd(db), sb->st_uid, sb->st_gid) == -1)
+ errx(1, "couldn't carry ownership and perms to %s", dbname);
+
+ if (! (nentries = parse_file(db, path)))
+ errx(1, "failed to parse %s", path);
+
+ if (db->close(db) == -1) {
+ warn("dbclose: %s", dbname);
+ goto bad;
+ }
+
+ snprintf(target, sizeof(target), "%s.db", path);
+ if (rename(dbname, target) == -1) {
+ warn("rename");
+ goto bad;
+ }
+
+bad:
+ unlink(dbname);
+
+ return (nentries);
+}
+
+static int
+parse_file(DB *db, const char *filename)
+{
+ FILE *fp;
+ char *line;
+ int entries, nentries = 0;
+ size_t len;
+ size_t lineno = 0;
+ char delim[] = { '\\', 0, 0 };
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ warn("%s", filename);
+ return (0);
+ }
+
+ if (!isatty(fileno(fp)) && flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) {
+ if (errno == EWOULDBLOCK)
+ warnx("%s is locked", filename);
+ else
+ warn("%s: flock", filename);
+ fclose(fp);
+ return (0);
+ }
+
+ while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
+ if (! (entries = parse_entry(db, filename,
+ line, len, lineno))) {
+ free(line);
+ fclose(fp);
+ return (0);
+ }
+ nentries += entries;
+ free(line);
+ }
+
+ fclose(fp);
+ return (nentries);
+}
+
+int
+parse_entry(DB *db, const char *source, char *line, size_t len, size_t lineno)
+{
+ DBT key;
+ DBT val;
+ int dbputs = 0;
+ char *keyp;
+ char *valp;
+
+ keyp = line;
+ while (isspace((unsigned char)*keyp))
+ keyp++;
+ if (*keyp == '\0' || *keyp == '#')
+ return (1);
+
+ valp = keyp;
+ strsep(&valp, " \t:");
+ if (valp == NULL || valp == keyp)
+ goto bad;
+ while (*valp == ':' || isspace((unsigned char)*valp))
+ valp++;
+ if (*valp == '\0' || *valp == '#')
+ goto bad;
+
+ /* Check for dups. */
+ key.data = keyp;
+ key.size = strlen(keyp) + 1;
+
+ xlowercase(key.data, key.data, strlen(key.data) + 1);
+ if (db->get(db, &key, &val, 0) == 0) {
+ warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
+ return (0);
+ }
+
+ if (! make_aliases(&val, valp))
+ goto bad;
+
+ if (db->put(db, &key, &val, 0) == -1) {
+ warn("dbput");
+ return (0);
+ }
+
+ dbputs++;
+ free(val.data);
+ return (dbputs);
+
+bad:
+ warnx("%s:%zd: invalid entry", source, lineno);
+ return (0);
+}
+
+static int
+make_aliases(DBT *val, char *text)
+{
+ struct expandnode xn;
+ char *subrcpt;
+ char *endp;
+ char *origtext;
+
+ val->data = NULL;
+ val->size = 0;
+
+ origtext = xstrdup(text, "make_aliases");
+
+ while ((subrcpt = strsep(&text, ",")) != NULL) {
+ /* subrcpt: strip initial whitespace. */
+ while (isspace((unsigned char)*subrcpt))
+ ++subrcpt;
+ if (*subrcpt == '\0')
+ goto error;
+
+ /* subrcpt: strip trailing whitespace. */
+ endp = subrcpt + strlen(subrcpt) - 1;
+ while (subrcpt < endp && isspace((unsigned char)*endp))
+ *endp-- = '\0';
+
+ if (! text_to_expandnode(&xn, subrcpt))
+ goto error;
+ }
+
+ val->data = origtext;
+ val->size = strlen(origtext) + 1;
+ return (val->size);
+
+error:
+ free(origtext);
+
+ return (0);
+}
+
+/*
+ * Stub function so that smtpctl compiles using minimum object files.
+ */
+void
+purge_config(uint8_t what)
+{
+ memset(env, 0, sizeof(struct smtpd));
+}
diff --git a/smtpd/smtpctl/Makefile b/smtpd/smtpctl/Makefile
index 44eef46..d358d97 100644
--- a/smtpd/smtpctl/Makefile
+++ b/smtpd/smtpctl/Makefile
@@ -31,6 +31,13 @@ SRCS+= to.c
SRCS+= expand.c
SRCS+= tree.c
SRCS+= dict.c
+SRCS+= table.c
+SRCS+= parse.y
+SRCS+= limit.c
+SRCS+= table_static.c
+SRCS+= table_db.c
+SRCS+= table_getpwnam.c
+SRCS+= table_proc.c
LDADD+= -lutil -lz -lcrypto
DPADD+= ${LIBUTIL} ${LIBZ} ${LIBCRYPTO}
--
You received this mail because you are subscribed to [email protected]
To unsubscribe, send a mail to: [email protected]