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