1 #include "mailfront.h"
2
3 #include <bglibs/sysdeps.h>
4 #include <bglibs/systime.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <sys/stat.h>
8
9 #include <bglibs/iobuf.h>
10 #include <bglibs/path.h>
11 #include <bglibs/str.h>
12 #include <bglibs/wrap.h>
13
14 static str destpath;
15 static str temppath;
16 static str destname;
17 static str tempname;
18 static int tmpfd;
19 static obuf msgbuf;
20
21 static const char* env_prefix;
22 static str env_tmpdir;
23 static str env_destdir;
24 static str env_nosync;
25
26 static RESPONSE(createerr, 451, "4.3.0 Error creating queue file");
27 static RESPONSE(writeerr, 451, "4.3.0 Error writing queue file");
28 static RESPONSE(configerr, 451, "4.3.0 Missing backend configuration parameter");
29
queuedir_init(const char * prefix)30 void queuedir_init(const char* prefix)
31 {
32 env_prefix = prefix;
33 wrap_str(str_copy2s(&env_tmpdir, prefix, "_TMP"));
34 wrap_str(str_copy2s(&env_destdir, prefix, "_DEST"));
35 wrap_str(str_copy2s(&env_nosync, prefix, "_NOSYNC"));
36 }
37
queuedir_reset(void)38 const response* queuedir_reset(void)
39 {
40 if (tempname.len)
41 unlink(tempname.s);
42 if (destname.len)
43 unlink(destname.s);
44 tempname.len = destname.len = 0;
45 return 0;
46 }
47
make_filename(str * s,const struct timeval * tv,pid_t pid)48 static int make_filename(str* s, const struct timeval* tv, pid_t pid)
49 {
50 return str_copyf(s, "d{.}06d{.}d", tv->tv_sec, tv->tv_usec, pid);
51 }
52
make_filenames(void)53 static const response* make_filenames(void)
54 {
55 static str filename;
56 pid_t pid = getpid();
57 for (;;) {
58 struct timeval tv;
59 struct stat st;
60
61 gettimeofday(&tv, 0);
62
63 if (!make_filename(&filename, &tv, pid))
64 return &resp_oom;
65 if (!str_copyf(&tempname, "S{/}S", &temppath, &filename))
66 return &resp_oom;
67 if (lstat(tempname.s, &st) == 0) continue;
68 if (errno != ENOENT) return &resp_internal;
69
70 if (!str_copyf(&destname, "S{/}S", &destpath, &filename))
71 return &resp_oom;
72 if (lstat(destname.s, &st) != 0) {
73 if (errno != ENOENT) return &resp_internal;
74 return 0;
75 }
76 sleep(1);
77 }
78 }
79
queuedir_sender(str * address,str * params)80 const response* queuedir_sender(str* address, str* params)
81 {
82 const response* r;
83 const char* destdir = session_getenv(env_prefix);
84 const char* tempsubdir = session_getenv(env_tmpdir.s);
85 const char* destsubdir = session_getenv(env_destdir.s);
86
87 if (destdir == 0)
88 return &resp_configerr;
89 if (tempsubdir == 0)
90 tempsubdir = "tmp";
91 if (destsubdir == 0)
92 destsubdir = "new";
93 if (!str_copyf(&destpath, "s{/}s", destdir, destsubdir)
94 || !str_copyf(&temppath, "s{/}s", destdir, tempsubdir))
95 return &resp_oom;
96 if ((r = make_filenames()) != 0)
97 return r;
98 obuf_close(&msgbuf);
99 if (!obuf_open(&msgbuf, tempname.s, OBUF_CREATE | OBUF_EXCLUSIVE, 0666, 0))
100 return &resp_createerr;
101 if (!obuf_putstr(&msgbuf, address) || !obuf_putc(&msgbuf, 0)) {
102 queuedir_reset();
103 return &resp_writeerr;
104 }
105 return 0;
106 (void)params;
107 }
108
queuedir_recipient(str * address,str * params)109 const response* queuedir_recipient(str* address, str* params)
110 {
111 if (!obuf_putstr(&msgbuf, address) || !obuf_putc(&msgbuf, 0))
112 return &resp_writeerr;
113 return 0;
114 (void)params;
115 }
116
queuedir_data_start(int fd)117 const response* queuedir_data_start(int fd)
118 {
119 /* Sender hasn't been sent, so save the message to a temporary file. */
120 if (destname.len == 0) {
121 if ((tmpfd = scratchfile()) < 0)
122 return &resp_writeerr;
123 }
124 else {
125 tmpfd = 0;
126 if (!obuf_putc(&msgbuf, 0))
127 return &resp_writeerr;
128 }
129 return 0;
130 (void)fd;
131 }
132
queuedir_data_block(const char * bytes,unsigned long len)133 const response* queuedir_data_block(const char* bytes, unsigned long len)
134 {
135 if (tmpfd > 0) {
136 if ((unsigned long)write(tmpfd, bytes, len) != len)
137 return &resp_writeerr;
138 }
139 else
140 if (!obuf_write(&msgbuf, bytes, len))
141 return &resp_writeerr;
142 return 0;
143 }
144
queuedir_message_end(int fd)145 const response* queuedir_message_end(int fd)
146 {
147 int dosync = session_getenv(env_nosync.s) == 0;
148 /* If using a temporary file, copy it to the output. */
149 if (tmpfd > 0) {
150 if (lseek(tmpfd, SEEK_SET, 0) != 0 || !obuf_copyfromfd(tmpfd, &msgbuf)) {
151 close(tmpfd);
152 tmpfd = 0;
153 return &resp_writeerr;
154 }
155 close(tmpfd);
156 tmpfd = 0;
157 }
158 /* Flush and close the output. */
159 if ((dosync && !obuf_sync(&msgbuf))
160 || !obuf_close(&msgbuf)) {
161 queuedir_reset();
162 return &resp_writeerr;
163 }
164 if (link(tempname.s, destname.s) != 0) {
165 queuedir_reset();
166 return &resp_writeerr;
167 }
168 if (dosync) {
169 if ((fd = open(destpath.s, O_DIRECTORY | O_RDONLY)) < 0) {
170 queuedir_reset();
171 return &resp_internal;
172 }
173 if (fsync(fd) != 0) {
174 queuedir_reset();
175 return &resp_writeerr;
176 }
177 close(fd);
178 }
179 unlink(tempname.s);
180 tempname.len = 0;
181 destname.len = 0;
182 return 0;
183 }
184