-/* $OpenBSD: table_static.c,v 1.29 2018/12/27 15:04:59 eric Exp $ */
+/* $OpenBSD: table_static.c,v 1.30 2018/12/28 10:42:18 eric Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
#include <arpa/inet.h>
#include <ctype.h>
-#include <err.h>
+#include <errno.h>
+
#include <event.h>
#include <fcntl.h>
#include <imsg.h>
#include "smtpd.h"
#include "log.h"
+struct table_static_priv {
+ int type;
+ struct dict dict;
+ void *iter;
+};
+
/* static backend */
static int table_static_config(struct table *);
static int table_static_add(struct table *, const char *, const char *);
};
+static void
+table_static_priv_free(struct table_static_priv *priv)
+{
+ void *p;
+
+ while (dict_poproot(&priv->dict, (void **)&p))
+ if (p != priv)
+ free(p);
+ free(priv);
+}
+
static int
-table_static_config(struct table *t)
+table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
+{
+ char lkey[1024];
+ void *old, *new = NULL;
+
+ if (!lowercase(lkey, key, sizeof lkey)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ if (val) {
+ new = strdup(val);
+ if (new == NULL)
+ return (-1);
+ }
+
+ /* use priv if value is null, so we can detect duplicate entries */
+ old = dict_set(&priv->dict, lkey, new ? new : priv);
+ if (old) {
+ if (old != priv)
+ free(old);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+table_static_priv_load(struct table_static_priv *priv, const char *path)
{
FILE *fp;
char *buf = NULL, *p;
ssize_t flen;
char *keyp;
char *valp;
- size_t ret = 0;
-
- /* no config ? ok */
- if (*t->t_config == '\0')
- return 1;
+ int ret = 0;
- if ((fp = fopen(t->t_config, "r")) == NULL) {
- log_warn("warn: Table \"%s\"", t->t_config);
+ if ((fp = fopen(path, "r")) == NULL) {
+ log_warn("%s: fopen", path);
return 0;
}
while (isspace((unsigned char)keyp[flen - 1]))
keyp[--flen] = '\0';
if (*keyp == '#') {
- if (t->t_type == T_NONE) {
+ if (priv->type == T_NONE) {
keyp++;
while (isspace((unsigned char)*keyp))
++keyp;
if (!strcmp(keyp, "@list"))
- t->t_type = T_LIST;
+ priv->type = T_LIST;
}
continue;
}
- if (t->t_type == T_NONE) {
+ if (priv->type == T_NONE) {
for (p = keyp; *p; p++) {
if (*p == ' ' || *p == '\t' || *p == ':') {
- t->t_type = T_HASH;
+ priv->type = T_HASH;
break;
}
}
- if (t->t_type == T_NONE)
- t->t_type = T_LIST;
+ if (priv->type == T_NONE)
+ priv->type = T_LIST;
}
- if (t->t_type == T_LIST) {
- table_add(t, keyp, NULL);
+ if (priv->type == T_LIST) {
+ table_static_priv_add(priv, keyp, NULL);
continue;
}
valp = NULL;
}
if (valp == NULL) {
- log_warnx("%s: invalid map entry line %d", t->t_config,
- lineno);
+ log_warnx("%s: invalid map entry line %d",
+ path, lineno);
goto end;
}
- table_add(t, keyp, valp);
+ table_static_priv_add(priv, keyp, valp);
}
if (ferror(fp)) {
- log_warn("%s: getline", t->t_config);
+ log_warn("%s: getline", path);
goto end;
}
/* Accept empty alias files; treat them as hashes */
- if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS)
- t->t_type = T_HASH;
+ if (priv->type == T_NONE)
+ priv->type = T_HASH;
ret = 1;
end:
}
static int
-table_static_add(struct table *table, const char *key, const char *val)
+table_static_config(struct table *t)
{
- char lkey[1024], *old;
+ struct table_static_priv *priv, *old;
- if (!lowercase(lkey, key, sizeof lkey)) {
- log_warnx("warn: lookup key too long: %s", key);
+ /* already up, and no config file? ok */
+ if (t->t_handle && *t->t_config == '\0')
+ return 1;
+
+ /* new config */
+ priv = calloc(1, sizeof(*priv));
+ if (priv == NULL)
return 0;
+ priv->type = t->t_type;
+ dict_init(&priv->dict);
+
+ if (*t->t_config) {
+ /* load the config file */
+ if (table_static_priv_load(priv, t->t_config) == 0) {
+ table_static_priv_free(priv);
+ return 0;
+ }
}
- old = dict_set(&table->t_dict, lkey, val ? xstrdup(val) : NULL);
- if (old) {
- log_warnx("warn: duplicate key \"%s\" in static table \"%s\"",
- lkey, table->t_name);
- free(old);
+ if ((old = t->t_handle))
+ table_static_priv_free(old);
+ t->t_handle = priv;
+ t->t_type = priv->type;
+
+ return 1;
+}
+
+static int
+table_static_add(struct table *table, const char *key, const char *val)
+{
+ struct table_static_priv *priv = table->t_handle;
+ int r;
+
+ /* cannot add to a table read from a file */
+ if (*table->t_config)
+ return 0;
+
+ if (priv == NULL) {
+ if (table_static_config(table) == 0)
+ return 0;
+ priv = table->t_handle;
}
+ r = table_static_priv_add(priv, key, val);
+ if (r == -1)
+ return 0;
return 1;
}
static void
table_static_dump(struct table *table)
{
+ struct table_static_priv *priv = table->t_handle;
const char *key;
char *value;
void *iter;
iter = NULL;
- while (dict_iter(&table->t_dict, &iter, &key, (void**)&value)) {
+ while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
if (value)
log_debug(" \"%s\" -> \"%s\"", key, value);
else
static int
table_static_update(struct table *table)
{
- struct table *t;
- void *p = NULL;
-
- /* no config ? ok */
- if (table->t_config[0] == '\0')
- goto ok;
-
- t = table_create(env, "static", table->t_name, "update", table->t_config);
- if (!table_config(t))
- goto err;
-
- /* replace former table, frees t */
- while (dict_poproot(&table->t_dict, (void **)&p))
- free(p);
- dict_merge(&table->t_dict, &t->t_dict);
- table_destroy(env, t);
-
-ok:
- log_info("info: Table \"%s\" successfully updated", table->t_name);
- return 1;
+ if (table_static_config(table) == 1) {
+ log_info("info: Table \"%s\" successfully updated", table->t_name);
+ return 1;
+ }
-err:
- table_destroy(env, t);
log_info("info: Failed to update table \"%s\"", table->t_name);
return 0;
}
static int
table_static_open(struct table *table)
{
- table->t_handle = table;
+ if (table->t_handle == NULL)
+ return table_static_config(table);
return 1;
}
static void
table_static_close(struct table *table)
{
+ struct table_static_priv *priv = table->t_handle;
+
+ if (priv)
+ table_static_priv_free(priv);
table->t_handle = NULL;
}
static int
-table_static_lookup(struct table *m, enum table_service service, const char *key,
+table_static_lookup(struct table *table, enum table_service service, const char *key,
char **dst)
{
+ struct table_static_priv *priv = table->t_handle;
char *line;
int ret;
int (*match)(const char *, const char *) = NULL;
line = NULL;
iter = NULL;
ret = 0;
- while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) {
+ while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
if (match) {
if (match(key, k)) {
line = v;
static int
table_static_fetch(struct table *t, enum table_service service, char **dst)
{
+ struct table_static_priv *priv = t->t_handle;
const char *k;
- if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) {
- t->t_iter = NULL;
- if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL))
+ if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
+ priv->iter = NULL;
+ if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
return 0;
}