Upstream qmail 1.01
[qmail] / qmail-pop3d.c
CommitLineData
2117e02e
MW
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
21int timeout = 1200;
22
23char ssoutbuf[1024];
24substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf));
25
26int 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
35char ssinbuf[128];
36substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
37
38
39void die() { _exit(0); }
40void puts(s) char *s;
41{
42 if (substdio_puts(&ssout,s) == -1) die();
43}
44void flush()
45{
46 if (substdio_flush(&ssout) == -1) die();
47}
48void err(s) char *s;
49{
50 puts("-ERR ");
51 puts(s);
52 puts("\r\n");
53 if (substdio_flush(&ssout) == -1) die();
54}
55void die_nomem() { err("out of memory"); die(); }
56void die_prot() { err("protection problem"); die(); }
57void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
58
59void err_syntax() { err("syntax error"); }
60void err_unimpl() { err("unimplemented"); }
61void err_deleted() { err("already deleted"); }
62void err_nozero() { err("messages are counted from 1"); }
63void err_toobig() { err("not that many messages"); }
64void err_nosuch() { err("unable to open that message"); }
65void err_nounlink() { err("unable to unlink all deleted messages"); }
66
67void okay() { puts("+OK \r\n"); flush(); }
68void pop3_last() { puts("+OK 0\r\n"); flush(); }
69
70
71stralloc dataline = {0};
72
73stralloc filenames = {0};
74prioq pq = {0};
75stralloc newname = {0};
76
77struct message
78 {
79 int flagdeleted;
80 unsigned long size;
81 char *fn;
82 }
83*m;
84int numm;
85
86substdio ssmsg; char ssmsgbuf[1024];
87
88
89void blast(ssfrom,limit)
90substdio *ssfrom;
91unsigned 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
115void 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
205char foo[FMT_ULONG];
206
207void printint(u) unsigned int u;
208{
209 foo[fmt_uint(foo,u)] = 0;
210 puts(foo);
211 puts(" ");
212}
213
214void printlong(u) unsigned long u;
215{
216 foo[fmt_uint(foo,u)] = 0;
217 puts(foo);
218 puts("\r\n");
219}
220
221void printfn(fn) char *fn;
222{
223 puts(fn + 4);
224 puts("\r\n");
225}
226
227void 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
240void pop3_rset()
241{
242 int i;
243 for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
244 okay();
245}
246
247void 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
257int 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
269void 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
279void 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
306void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
307void pop3_list(arg) char *arg; { dolisting(arg,0); }
308
309void 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
330static 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
344void doit(cmd)
345char *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
370void main(argc,argv)
371int argc;
372char **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}