Upstream qmail 1.01
[qmail] / qmail-pop3d.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include "direntry.h"
4 #include "sig.h"
5 #include "getln.h"
6 #include "stralloc.h"
7 #include "substdio.h"
8 #include "alloc.h"
9 #include "datetime.h"
10 #include "prot.h"
11 #include "open.h"
12 #include "prioq.h"
13 #include "scan.h"
14 #include "fmt.h"
15 #include "error.h"
16 #include "str.h"
17 #include "exit.h"
18 #include "now.h"
19 #include "readwrite.h"
20
21 int timeout = 1200;
22
23 char ssoutbuf[1024];
24 substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf));
25
26 int timeoutread(fd,buf,n) int fd; char *buf; int n;
27 {
28 int r; int saveerrno;
29 alarm(timeout);
30 r = read(fd,buf,n); saveerrno = errno;
31 alarm(0);
32 errno = saveerrno; return r;
33 }
34
35 char ssinbuf[128];
36 substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
37
38
39 void die() { _exit(0); }
40 void puts(s) char *s;
41 {
42 if (substdio_puts(&ssout,s) == -1) die();
43 }
44 void flush()
45 {
46 if (substdio_flush(&ssout) == -1) die();
47 }
48 void err(s) char *s;
49 {
50 puts("-ERR ");
51 puts(s);
52 puts("\r\n");
53 if (substdio_flush(&ssout) == -1) die();
54 }
55 void die_nomem() { err("out of memory"); die(); }
56 void die_prot() { err("protection problem"); die(); }
57 void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
58
59 void err_syntax() { err("syntax error"); }
60 void err_unimpl() { err("unimplemented"); }
61 void err_deleted() { err("already deleted"); }
62 void err_nozero() { err("messages are counted from 1"); }
63 void err_toobig() { err("not that many messages"); }
64 void err_nosuch() { err("unable to open that message"); }
65 void err_nounlink() { err("unable to unlink all deleted messages"); }
66
67 void okay() { puts("+OK \r\n"); flush(); }
68 void pop3_last() { puts("+OK 0\r\n"); flush(); }
69
70
71 stralloc dataline = {0};
72
73 stralloc filenames = {0};
74 prioq pq = {0};
75 stralloc newname = {0};
76
77 struct message
78 {
79 int flagdeleted;
80 unsigned long size;
81 char *fn;
82 }
83 *m;
84 int numm;
85
86 substdio ssmsg; char ssmsgbuf[1024];
87
88
89 void blast(ssfrom,limit)
90 substdio *ssfrom;
91 unsigned long limit;
92 {
93 int match;
94 int inheaders = 1;
95
96 for (;;)
97 {
98 if (getln(ssfrom,&dataline,&match,'\n') != 0) die();
99 if (!match && !dataline.len) break;
100 if (match) --dataline.len; /* no way to pass this info over POP */
101 if (limit) if (!inheaders) if (!--limit) break;
102 if (!dataline.len)
103 inheaders = 0;
104 else
105 if (dataline.s[0] == '.')
106 substdio_put(&ssout,".",1);
107 if (substdio_put(&ssout,dataline.s,dataline.len) == -1) die();
108 if (substdio_put(&ssout,"\r\n",2) == -1) die();
109 if (!match) break;
110 }
111 if (substdio_put(&ssout,"\r\n.\r\n",5) == -1) die();
112 if (substdio_flush(&ssout) == -1) die();
113 }
114
115 void getlist()
116 {
117 unsigned long pos;
118 datetime_sec time;
119 DIR *dir;
120 direntry *d;
121 struct prioq_elt pe;
122 struct stat st;
123 int i;
124
125 numm = 0;
126
127 time = now();
128
129 if (dir = opendir("tmp"))
130 {
131 while (d = readdir(dir))
132 {
133 if (str_equal(d->d_name,".")) continue;
134 if (str_equal(d->d_name,"..")) continue;
135 if (!stralloc_copys(&newname,"tmp/")) die_nomem();
136 if (!stralloc_cats(&newname,d->d_name)) die_nomem();
137 if (!stralloc_0(&newname)) die_nomem();
138 if (stat(newname.s,&st) == 0)
139 if (time > st.st_atime + 129600)
140 unlink(newname.s);
141 }
142 closedir(dir);
143 }
144
145 if (!stralloc_copys(&filenames,"")) die_nomem();
146
147 if (dir = opendir("new"))
148 {
149 while (d = readdir(dir))
150 {
151 if (str_equal(d->d_name,".")) continue;
152 if (str_equal(d->d_name,"..")) continue;
153 pos = filenames.len;
154 if (!stralloc_cats(&filenames,"new/")) die_nomem();
155 if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
156 if (!stralloc_0(&filenames)) die_nomem();
157 if (stat(filenames.s + pos,&st) == 0)
158 {
159 pe.dt = st.st_mtime;
160 pe.id = pos;
161 if (!prioq_insert(&pq,&pe)) die_nomem();
162 ++numm;
163 }
164 }
165 closedir(dir);
166 }
167
168 if (dir = opendir("cur"))
169 {
170 while (d = readdir(dir))
171 {
172 if (str_equal(d->d_name,".")) continue;
173 if (str_equal(d->d_name,"..")) continue;
174 pos = filenames.len;
175 if (!stralloc_cats(&filenames,"cur/")) die_nomem();
176 if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
177 if (!stralloc_0(&filenames)) die_nomem();
178 if (stat(filenames.s + pos,&st) == 0)
179 {
180 pe.dt = st.st_mtime;
181 pe.id = pos;
182 if (!prioq_insert(&pq,&pe)) die_nomem();
183 ++numm;
184 }
185 }
186 closedir(dir);
187 }
188
189 m = (struct message *) alloc(numm * sizeof(struct message));
190 if (!m) die_nomem();
191
192 for (i = 0;i < numm;++i)
193 {
194 if (!prioq_min(&pq,&pe)) { numm = i; break; }
195 prioq_delmin(&pq);
196 m[i].fn = filenames.s + pe.id;
197 m[i].flagdeleted = 0;
198 if (stat(m[i].fn,&st) == -1)
199 m[i].size = 0;
200 else
201 m[i].size = st.st_size;
202 }
203 }
204
205 char foo[FMT_ULONG];
206
207 void printint(u) unsigned int u;
208 {
209 foo[fmt_uint(foo,u)] = 0;
210 puts(foo);
211 puts(" ");
212 }
213
214 void printlong(u) unsigned long u;
215 {
216 foo[fmt_uint(foo,u)] = 0;
217 puts(foo);
218 puts("\r\n");
219 }
220
221 void printfn(fn) char *fn;
222 {
223 puts(fn + 4);
224 puts("\r\n");
225 }
226
227 void pop3_stat()
228 {
229 int i;
230 unsigned long total;
231
232 total = 0;
233 for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
234 puts("+OK ");
235 printint(numm);
236 printlong(total);
237 flush();
238 }
239
240 void pop3_rset()
241 {
242 int i;
243 for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
244 okay();
245 }
246
247 void pop3_quit()
248 {
249 int i;
250 for (i = 0;i < numm;++i)
251 if (m[i].flagdeleted)
252 if (unlink(m[i].fn) == -1) err_nounlink();
253 okay();
254 die();
255 }
256
257 int msgno(arg) char *arg;
258 {
259 unsigned long u;
260 if (!arg) { err_syntax(); return -1; }
261 if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
262 if (!u) { err_nozero(); return -1; }
263 --u;
264 if (u >= numm) { err_toobig(); return -1; }
265 if (m[u].flagdeleted) { err_deleted(); return -1; }
266 return u;
267 }
268
269 void pop3_dele(arg) char *arg;
270 {
271 int i;
272
273 i = msgno(arg);
274 if (i == -1) return;
275 m[i].flagdeleted = 1;
276 okay();
277 }
278
279 void dolisting(arg,flaguidl) char *arg; int flaguidl;
280 {
281 unsigned int i;
282
283 if (arg)
284 {
285 i = msgno(arg);
286 if (i == -1) return;
287 puts("+OK ");
288 printint(i + 1);
289 if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
290 }
291 else
292 {
293 okay();
294
295 for (i = 0;i < numm;++i)
296 if (!m[i].flagdeleted)
297 {
298 printint(i + 1);
299 if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
300 }
301 puts(".\r\n");
302 }
303 flush();
304 }
305
306 void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
307 void pop3_list(arg) char *arg; { dolisting(arg,0); }
308
309 void pop3_top(arg) char *arg;
310 {
311 int i;
312 unsigned long limit;
313 int fd;
314
315 i = msgno(arg);
316 if (i == -1) return;
317
318 arg += scan_ulong(arg,&limit);
319 while (*arg == ' ') ++arg;
320 if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
321
322 fd = open_read(m[i].fn);
323 if (fd == -1) { err_nosuch(); return; }
324 okay();
325 substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
326 blast(&ssmsg,limit);
327 close(fd);
328 }
329
330 static struct { void (*fun)(); char *text; } pop3cmd[] = {
331 { pop3_quit, "quit" }
332 , { pop3_stat, "stat" }
333 , { pop3_list, "list" }
334 , { pop3_uidl, "uidl" }
335 , { pop3_dele, "dele" }
336 , { pop3_top, "retr" }
337 , { pop3_rset, "rset" }
338 , { pop3_last, "last" }
339 , { pop3_top, "top" }
340 , { okay, "noop" }
341 , { 0, 0 }
342 };
343
344 void doit(cmd)
345 char *cmd;
346 {
347 int i;
348 int j;
349 char ch;
350
351 for (i = 0;pop3cmd[i].fun;++i)
352 {
353 for (j = 0;ch = pop3cmd[i].text[j];++j)
354 if ((cmd[j] != ch) && (cmd[j] != ch - 32))
355 break;
356 if (!ch)
357 if (!cmd[j] || (cmd[j] == ' '))
358 {
359 while (cmd[j] == ' ') ++j;
360 if (!cmd[j])
361 pop3cmd[i].fun((char *) 0);
362 else
363 pop3cmd[i].fun(cmd + j);
364 return;
365 }
366 }
367 err_unimpl();
368 }
369
370 void main(argc,argv)
371 int argc;
372 char **argv;
373 {
374 static stralloc cmd = {0};
375 int match;
376
377 sig_alarmcatch(die);
378 sig_pipeignore();
379
380 if (!argv[1]) die_nomaildir();
381 if (chdir(argv[1]) == -1) die_nomaildir();
382
383 getlist();
384
385 okay();
386
387 for (;;)
388 {
389 if (getln(&ssin,&cmd,&match,'\n') == -1) die();
390 if (!match) die();
391 if (cmd.len == 0) die();
392 if (cmd.s[--cmd.len] != '\n') die();
393 if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len;
394 cmd.s[cmd.len++] = 0;
395 doit(cmd.s);
396 }
397 }