summaryrefslogtreecommitdiffstats
path: root/usr.sbin/smtpd/queue_backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/smtpd/queue_backend.c')
-rw-r--r--usr.sbin/smtpd/queue_backend.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/usr.sbin/smtpd/queue_backend.c b/usr.sbin/smtpd/queue_backend.c
new file mode 100644
index 00000000000..f07481e0ec2
--- /dev/null
+++ b/usr.sbin/smtpd/queue_backend.c
@@ -0,0 +1,330 @@
+/* $OpenBSD: queue_backend.c,v 1.1 2010/05/31 23:38:56 jacekm Exp $ */
+
+/*
+ * Copyright (c) 2010 Jacek Masiulaniec <jacekm@dobremiasto.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "queue_backend.h"
+
+static char path[PATH_MAX];
+
+static char *idchars = "ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+int
+queue_be_content_create(u_int64_t *content_id)
+{
+ int c, fd;
+
+ c = idchars[arc4random_uniform(61)];
+ snprintf(path, sizeof path, "content/%c/%cXXXXXXX", c, c);
+ fd = mkstemp(path);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ return -1;
+ if (mkdir(dirname(path), 0700) < 0)
+ return -1;
+ fd = mkstemp(path);
+ if (fd < 0)
+ return -1;
+ }
+ close(fd);
+ *content_id = queue_be_encode(path + 10);
+ return 0;
+}
+
+int
+queue_be_content_open(u_int64_t content_id, int wr)
+{
+ char *id;
+
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "content/%c/%s", id[0], id);
+ return open(path, wr ? O_RDWR|O_APPEND|O_EXLOCK : O_RDONLY|O_SHLOCK);
+}
+
+void
+queue_be_content_delete(u_int64_t content_id)
+{
+ char *id;
+
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "content/%c/%s", id[0], id);
+ unlink(path);
+}
+
+int
+queue_be_action_new(u_int64_t content_id, u_int64_t *action_id, char *aux)
+{
+ FILE *fp;
+ char *id;
+ int fd;
+
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "action/%c/%s,XXXXXXXX", id[0], id);
+ fd = mkstemp(path);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ return -1;
+ if (mkdir(dirname(path), 0700) < 0)
+ return -1;
+ fd = mkstemp(path);
+ if (fd < 0)
+ return -1;
+ }
+ fp = fdopen(fd, "w+");
+ if (fp == NULL) {
+ unlink(path);
+ return -1;
+ }
+ fprintf(fp, "%s\n", aux);
+ if (fclose(fp) == EOF) {
+ unlink(path);
+ return -1;
+ }
+ *action_id = queue_be_encode(path + 18);
+ return 0;
+}
+
+int
+queue_be_action_read(struct action_be *a, u_int64_t content_id, u_int64_t action_id)
+{
+ static char status[2048];
+ static char aux[2048];
+ struct stat sb_status, sb_content;
+ char *id;
+ FILE *fp;
+
+ bzero(a, sizeof *a);
+ a->content_id = content_id;
+ a->action_id = action_id;
+
+ /*
+ * Auxillary params for mta and mda.
+ */
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "action/%c/%s,", id[0], id);
+ strlcat(path, queue_be_decode(action_id), sizeof path);
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ return -1;
+ if (fgets(aux, sizeof aux, fp) == NULL) {
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+ aux[strcspn(aux, "\n")] = '\0';
+ a->aux = aux;
+
+ /*
+ * Error status message.
+ */
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "status/%c/%s,", id[0], id);
+ strlcat(path, queue_be_decode(action_id), sizeof path);
+ fp = fopen(path, "r");
+ if (fp) {
+ if (fgets(status, sizeof status, fp) != NULL)
+ status[strcspn(status, "\n")] = '\0';
+ else
+ status[0] = '\0';
+ if (fstat(fileno(fp), &sb_status) < 0) {
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+ } else
+ status[0] = '\0';
+ a->status = status;
+
+ /*
+ * Message birth time.
+ *
+ * For bounces, use mtime of the status file.
+ * For non-bounces, use mtime of the content file.
+ */
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "content/%c/%s", id[0], id);
+ if (stat(path, &sb_content) < 0)
+ return -1;
+ if (sb_content.st_mode & S_IWUSR)
+ a->birth = 0;
+ else if (status[0] == '5' || status[0] == '6')
+ a->birth = sb_status.st_mtime;
+ else
+ a->birth = sb_content.st_mtime;
+
+ return 0;
+}
+
+int
+queue_be_action_status(u_int64_t content_id, u_int64_t action_id, char *status)
+{
+ FILE *fp;
+ char *id;
+
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "status/%c/%s,", id[0], id);
+ strlcat(path, queue_be_decode(action_id), sizeof path);
+ fp = fopen(path, "w+");
+ if (fp == NULL) {
+ if (errno != ENOENT)
+ return -1;
+ mkdir(dirname(path), 0700);
+ fp = fopen(path, "w+");
+ if (fp == NULL)
+ return -1;
+ }
+ if (fprintf(fp, "%s\n", status) == -1) {
+ fclose(fp);
+ return -1;
+ }
+ if (fclose(fp) == EOF)
+ return -1;
+ return 0;
+}
+
+void
+queue_be_action_delete(u_int64_t content_id, u_int64_t action_id)
+{
+ char *id, *dir[] = { "action", "status" };
+ u_int i;
+
+ for (i = 0; i < 2; i++) {
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "%s/%c/%s,", dir[i], id[0], id);
+ id = queue_be_decode(action_id);
+ strlcat(path, id, sizeof path);
+ unlink(path);
+ }
+}
+
+int
+queue_be_commit(u_int64_t content_id)
+{
+ char *id;
+
+ id = queue_be_decode(content_id);
+ snprintf(path, sizeof path, "content/%c/%s", id[0], id);
+ if (utimes(path, NULL) < 0 || chmod(path, 0400) < 0)
+ return -1;
+ return 0;
+}
+
+int
+queue_be_getnext(struct action_be *a)
+{
+ static FTS *fts;
+ static FTSENT *fe;
+ char *dir[] = { "action", NULL };
+
+ if (fts == NULL) {
+ fts = fts_open(dir, FTS_PHYSICAL|FTS_NOCHDIR, NULL);
+ if (fts == NULL)
+ return -1;
+ }
+
+ for (;;) {
+ fe = fts_read(fts);
+ if (fe == NULL) {
+ if (errno) {
+ fts_close(fts);
+ return -1;
+ } else {
+ if (fts_close(fts) < 0)
+ return -1;
+ a->content_id = 0;
+ a->action_id = 0;
+ return 0;
+ }
+ }
+ switch (fe->fts_info) {
+ case FTS_F:
+ break;
+ case FTS_D:
+ case FTS_DP:
+ continue;
+ default:
+ fts_close(fts);
+ return -1;
+ }
+ break;
+ }
+
+ if (fe->fts_namelen != 17 || fe->fts_name[8] != ',') {
+ fts_close(fts);
+ return -1;
+ }
+ a->content_id = queue_be_encode(fe->fts_name);
+ a->action_id = queue_be_encode(fe->fts_name + 9);
+ if (queue_be_action_read(a, a->content_id, a->action_id) < 0)
+ return -2;
+
+ return 0;
+}
+
+char *
+queue_be_decode(u_int64_t id)
+{
+ static char txt[9];
+
+ memcpy(txt, &id, sizeof id);
+ txt[8] = '\0';
+ return txt;
+}
+
+u_int64_t
+queue_be_encode(const char *txt)
+{
+ u_int64_t id;
+
+ if (strlen(txt) < sizeof id)
+ id = INVALID_ID;
+ else
+ memcpy(&id, txt, sizeof id);
+
+ return id;
+}
+
+int
+queue_be_init(char *prefix, uid_t uid, gid_t gid)
+{
+ char *dir[] = { "action", "content", "status" };
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ snprintf(path, sizeof path, "%s/%s", prefix, dir[i]);
+ if (mkdir(path, 0700) < 0 && errno != EEXIST)
+ return -1;
+ if (chmod(path, 0700) < 0)
+ return -1;
+ if (chown(path, uid, gid) < 0)
+ return -1;
+ }
+ return 0;
+}