chiark / gitweb /
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 }