1 5cdb1798 2005-10-29 devnull #include "common.h"
2 5cdb1798 2005-10-29 devnull #include <ctype.h>
3 5cdb1798 2005-10-29 devnull #include <auth.h>
4 5cdb1798 2005-10-29 devnull #include <libsec.h>
6 5cdb1798 2005-10-29 devnull typedef struct Cmd Cmd;
7 5cdb1798 2005-10-29 devnull struct Cmd
9 5cdb1798 2005-10-29 devnull char *name;
10 5cdb1798 2005-10-29 devnull int needauth;
11 5cdb1798 2005-10-29 devnull int (*f)(char*);
14 5cdb1798 2005-10-29 devnull static void hello(void);
15 5cdb1798 2005-10-29 devnull static int apopcmd(char*);
16 5cdb1798 2005-10-29 devnull static int capacmd(char*);
17 5cdb1798 2005-10-29 devnull static int delecmd(char*);
18 5cdb1798 2005-10-29 devnull static int listcmd(char*);
19 5cdb1798 2005-10-29 devnull static int noopcmd(char*);
20 5cdb1798 2005-10-29 devnull static int passcmd(char*);
21 5cdb1798 2005-10-29 devnull static int quitcmd(char*);
22 5cdb1798 2005-10-29 devnull static int rsetcmd(char*);
23 5cdb1798 2005-10-29 devnull static int retrcmd(char*);
24 5cdb1798 2005-10-29 devnull static int statcmd(char*);
25 5cdb1798 2005-10-29 devnull static int stlscmd(char*);
26 5cdb1798 2005-10-29 devnull static int topcmd(char*);
27 5cdb1798 2005-10-29 devnull static int synccmd(char*);
28 5cdb1798 2005-10-29 devnull static int uidlcmd(char*);
29 5cdb1798 2005-10-29 devnull static int usercmd(char*);
30 5cdb1798 2005-10-29 devnull static char *nextarg(char*);
31 5cdb1798 2005-10-29 devnull static int getcrnl(char*, int);
32 5cdb1798 2005-10-29 devnull static int readmbox(char*);
33 5cdb1798 2005-10-29 devnull static void sendcrnl(char*, ...);
34 5cdb1798 2005-10-29 devnull static int senderr(char*, ...);
35 5cdb1798 2005-10-29 devnull static int sendok(char*, ...);
36 5cdb1798 2005-10-29 devnull #pragma varargck argpos sendcrnl 1
37 5cdb1798 2005-10-29 devnull #pragma varargck argpos senderr 1
38 5cdb1798 2005-10-29 devnull #pragma varargck argpos sendok 1
40 5cdb1798 2005-10-29 devnull Cmd cmdtab[] =
42 5cdb1798 2005-10-29 devnull "apop", 0, apopcmd,
43 5cdb1798 2005-10-29 devnull "capa", 0, capacmd,
44 5cdb1798 2005-10-29 devnull "dele", 1, delecmd,
45 5cdb1798 2005-10-29 devnull "list", 1, listcmd,
46 5cdb1798 2005-10-29 devnull "noop", 0, noopcmd,
47 5cdb1798 2005-10-29 devnull "pass", 0, passcmd,
48 5cdb1798 2005-10-29 devnull "quit", 0, quitcmd,
49 5cdb1798 2005-10-29 devnull "rset", 0, rsetcmd,
50 5cdb1798 2005-10-29 devnull "retr", 1, retrcmd,
51 5cdb1798 2005-10-29 devnull "stat", 1, statcmd,
52 5cdb1798 2005-10-29 devnull "stls", 0, stlscmd,
53 5cdb1798 2005-10-29 devnull "sync", 1, synccmd,
54 5cdb1798 2005-10-29 devnull "top", 1, topcmd,
55 5cdb1798 2005-10-29 devnull "uidl", 1, uidlcmd,
56 5cdb1798 2005-10-29 devnull "user", 0, usercmd,
60 5cdb1798 2005-10-29 devnull static Biobuf in;
61 5cdb1798 2005-10-29 devnull static Biobuf out;
62 5cdb1798 2005-10-29 devnull static int passwordinclear;
63 5cdb1798 2005-10-29 devnull static int didtls;
65 5cdb1798 2005-10-29 devnull typedef struct Msg Msg;
66 5cdb1798 2005-10-29 devnull struct Msg
68 5cdb1798 2005-10-29 devnull int upasnum;
69 5cdb1798 2005-10-29 devnull char digest[64];
70 5cdb1798 2005-10-29 devnull int bytes;
71 5cdb1798 2005-10-29 devnull int deleted;
74 5cdb1798 2005-10-29 devnull static int totalbytes;
75 5cdb1798 2005-10-29 devnull static int totalmsgs;
76 5cdb1798 2005-10-29 devnull static Msg *msg;
77 5cdb1798 2005-10-29 devnull static int nmsg;
78 5cdb1798 2005-10-29 devnull static int loggedin;
79 5cdb1798 2005-10-29 devnull static int debug;
80 5cdb1798 2005-10-29 devnull static uchar *tlscert;
81 5cdb1798 2005-10-29 devnull static int ntlscert;
82 5cdb1798 2005-10-29 devnull static char *peeraddr;
83 5cdb1798 2005-10-29 devnull static char tmpaddr[64];
86 5cdb1798 2005-10-29 devnull usage(void)
88 5cdb1798 2005-10-29 devnull fprint(2, "usage: upas/pop3 [-a authmboxfile] [-d debugfile] [-p]\n");
89 5cdb1798 2005-10-29 devnull exits("usage");
93 5cdb1798 2005-10-29 devnull main(int argc, char **argv)
96 5cdb1798 2005-10-29 devnull char *arg, cmdbuf[1024];
99 5cdb1798 2005-10-29 devnull rfork(RFNAMEG);
100 5cdb1798 2005-10-29 devnull Binit(&in, 0, OREAD);
101 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
103 5cdb1798 2005-10-29 devnull ARGBEGIN{
104 5cdb1798 2005-10-29 devnull case 'a':
105 5cdb1798 2005-10-29 devnull loggedin = 1;
106 5cdb1798 2005-10-29 devnull if(readmbox(EARGF(usage())) < 0)
107 5cdb1798 2005-10-29 devnull exits(nil);
109 5cdb1798 2005-10-29 devnull case 'd':
110 5cdb1798 2005-10-29 devnull debug++;
111 5cdb1798 2005-10-29 devnull if((fd = create(EARGF(usage()), OWRITE, 0666)) >= 0 && fd != 2){
112 5cdb1798 2005-10-29 devnull dup(fd, 2);
113 5cdb1798 2005-10-29 devnull close(fd);
116 5cdb1798 2005-10-29 devnull case 'r':
117 5cdb1798 2005-10-29 devnull strecpy(tmpaddr, tmpaddr+sizeof tmpaddr, EARGF(usage()));
118 5cdb1798 2005-10-29 devnull if(arg = strchr(tmpaddr, '!'))
119 5cdb1798 2005-10-29 devnull *arg = '\0';
120 5cdb1798 2005-10-29 devnull peeraddr = tmpaddr;
122 5cdb1798 2005-10-29 devnull case 't':
123 5cdb1798 2005-10-29 devnull tlscert = readcert(EARGF(usage()), &ntlscert);
124 5cdb1798 2005-10-29 devnull if(tlscert == nil){
125 5cdb1798 2005-10-29 devnull senderr("cannot read TLS certificate: %r");
126 5cdb1798 2005-10-29 devnull exits(nil);
129 5cdb1798 2005-10-29 devnull case 'p':
130 5cdb1798 2005-10-29 devnull passwordinclear = 1;
134 5cdb1798 2005-10-29 devnull /* do before TLS */
135 5cdb1798 2005-10-29 devnull if(peeraddr == nil)
136 5cdb1798 2005-10-29 devnull peeraddr = remoteaddr(0,0);
138 5cdb1798 2005-10-29 devnull hello();
140 5cdb1798 2005-10-29 devnull while(Bflush(&out), getcrnl(cmdbuf, sizeof cmdbuf) > 0){
141 5cdb1798 2005-10-29 devnull arg = nextarg(cmdbuf);
142 5cdb1798 2005-10-29 devnull for(c=cmdtab; c->name; c++)
143 5cdb1798 2005-10-29 devnull if(cistrcmp(c->name, cmdbuf) == 0)
145 5cdb1798 2005-10-29 devnull if(c->name == 0){
146 5cdb1798 2005-10-29 devnull senderr("unknown command %s", cmdbuf);
147 5cdb1798 2005-10-29 devnull continue;
149 5cdb1798 2005-10-29 devnull if(c->needauth && !loggedin){
150 5cdb1798 2005-10-29 devnull senderr("%s requires authentication", cmdbuf);
151 5cdb1798 2005-10-29 devnull continue;
153 5cdb1798 2005-10-29 devnull (*c->f)(arg);
155 5cdb1798 2005-10-29 devnull exits(nil);
158 5cdb1798 2005-10-29 devnull /* sort directories in increasing message number order */
159 5cdb1798 2005-10-29 devnull static int
160 5cdb1798 2005-10-29 devnull dircmp(void *a, void *b)
162 5cdb1798 2005-10-29 devnull return atoi(((Dir*)a)->name) - atoi(((Dir*)b)->name);
165 5cdb1798 2005-10-29 devnull static int
166 5cdb1798 2005-10-29 devnull readmbox(char *box)
168 5cdb1798 2005-10-29 devnull int fd, i, n, nd, lines, pid;
169 5cdb1798 2005-10-29 devnull char buf[100], err[ERRMAX];
170 5cdb1798 2005-10-29 devnull char *p;
171 5cdb1798 2005-10-29 devnull Biobuf *b;
172 5cdb1798 2005-10-29 devnull Dir *d, *draw;
174 5cdb1798 2005-10-29 devnull Waitmsg *w;
176 5cdb1798 2005-10-29 devnull unmount(nil, "/mail/fs");
177 5cdb1798 2005-10-29 devnull switch(pid = fork()){
178 5cdb1798 2005-10-29 devnull case -1:
179 5cdb1798 2005-10-29 devnull return senderr("can't fork to start upas/fs");
182 5cdb1798 2005-10-29 devnull close(0);
183 5cdb1798 2005-10-29 devnull close(1);
184 5cdb1798 2005-10-29 devnull open("/dev/null", OREAD);
185 5cdb1798 2005-10-29 devnull open("/dev/null", OWRITE);
186 5cdb1798 2005-10-29 devnull execl("/bin/upas/fs", "upas/fs", "-np", "-f", box, nil);
187 5cdb1798 2005-10-29 devnull snprint(err, sizeof err, "upas/fs: %r");
188 5cdb1798 2005-10-29 devnull _exits(err);
191 5cdb1798 2005-10-29 devnull default:
195 5cdb1798 2005-10-29 devnull if((w = wait()) == nil || w->pid != pid || w->msg[0] != '\0'){
196 5cdb1798 2005-10-29 devnull if(w && w->pid==pid)
197 5cdb1798 2005-10-29 devnull return senderr("%s", w->msg);
199 5cdb1798 2005-10-29 devnull return senderr("can't initialize upas/fs");
201 5cdb1798 2005-10-29 devnull free(w);
203 5cdb1798 2005-10-29 devnull if(chdir("/mail/fs/mbox") < 0)
204 5cdb1798 2005-10-29 devnull return senderr("can't initialize upas/fs: %r");
206 5cdb1798 2005-10-29 devnull if((fd = open(".", OREAD)) < 0)
207 5cdb1798 2005-10-29 devnull return senderr("cannot open /mail/fs/mbox: %r");
208 5cdb1798 2005-10-29 devnull nd = dirreadall(fd, &d);
209 5cdb1798 2005-10-29 devnull close(fd);
210 5cdb1798 2005-10-29 devnull if(nd < 0)
211 5cdb1798 2005-10-29 devnull return senderr("cannot read from /mail/fs/mbox: %r");
213 5cdb1798 2005-10-29 devnull msg = mallocz(sizeof(Msg)*nd, 1);
214 5cdb1798 2005-10-29 devnull if(msg == nil)
215 5cdb1798 2005-10-29 devnull return senderr("out of memory");
217 5cdb1798 2005-10-29 devnull if(nd == 0)
218 5cdb1798 2005-10-29 devnull return 0;
219 5cdb1798 2005-10-29 devnull qsort(d, nd, sizeof(d[0]), dircmp);
221 5cdb1798 2005-10-29 devnull for(i=0; i<nd; i++){
222 5cdb1798 2005-10-29 devnull m = &msg[nmsg];
223 5cdb1798 2005-10-29 devnull m->upasnum = atoi(d[i].name);
224 5cdb1798 2005-10-29 devnull sprint(buf, "%d/digest", m->upasnum);
225 5cdb1798 2005-10-29 devnull if((fd = open(buf, OREAD)) < 0)
226 5cdb1798 2005-10-29 devnull continue;
227 5cdb1798 2005-10-29 devnull n = readn(fd, m->digest, sizeof m->digest - 1);
228 5cdb1798 2005-10-29 devnull close(fd);
229 5cdb1798 2005-10-29 devnull if(n < 0)
230 5cdb1798 2005-10-29 devnull continue;
231 5cdb1798 2005-10-29 devnull m->digest[n] = '\0';
234 5cdb1798 2005-10-29 devnull * We need the number of message lines so that we
235 5cdb1798 2005-10-29 devnull * can adjust the byte count to include \r's.
236 5cdb1798 2005-10-29 devnull * Upas/fs gives us the number of lines in the raw body
237 5cdb1798 2005-10-29 devnull * in the lines file, but we have to count rawheader ourselves.
238 5cdb1798 2005-10-29 devnull * There is one blank line between raw header and raw body.
240 5cdb1798 2005-10-29 devnull sprint(buf, "%d/rawheader", m->upasnum);
241 5cdb1798 2005-10-29 devnull if((b = Bopen(buf, OREAD)) == nil)
242 5cdb1798 2005-10-29 devnull continue;
243 5cdb1798 2005-10-29 devnull lines = 0;
244 5cdb1798 2005-10-29 devnull for(;;){
245 5cdb1798 2005-10-29 devnull p = Brdline(b, '\n');
246 5cdb1798 2005-10-29 devnull if(p == nil){
247 5cdb1798 2005-10-29 devnull if((n = Blinelen(b)) == 0)
249 5cdb1798 2005-10-29 devnull Bseek(b, n, 1);
251 5cdb1798 2005-10-29 devnull lines++;
253 5cdb1798 2005-10-29 devnull Bterm(b);
254 5cdb1798 2005-10-29 devnull lines++;
255 5cdb1798 2005-10-29 devnull sprint(buf, "%d/lines", m->upasnum);
256 5cdb1798 2005-10-29 devnull if((fd = open(buf, OREAD)) < 0)
257 5cdb1798 2005-10-29 devnull continue;
258 5cdb1798 2005-10-29 devnull n = readn(fd, buf, sizeof buf - 1);
259 5cdb1798 2005-10-29 devnull close(fd);
260 5cdb1798 2005-10-29 devnull if(n < 0)
261 5cdb1798 2005-10-29 devnull continue;
262 5cdb1798 2005-10-29 devnull buf[n] = '\0';
263 5cdb1798 2005-10-29 devnull lines += atoi(buf);
265 5cdb1798 2005-10-29 devnull sprint(buf, "%d/raw", m->upasnum);
266 5cdb1798 2005-10-29 devnull if((draw = dirstat(buf)) == nil)
267 5cdb1798 2005-10-29 devnull continue;
268 5cdb1798 2005-10-29 devnull m->bytes = lines+draw->length;
269 5cdb1798 2005-10-29 devnull free(draw);
271 5cdb1798 2005-10-29 devnull totalmsgs++;
272 5cdb1798 2005-10-29 devnull totalbytes += m->bytes;
274 5cdb1798 2005-10-29 devnull return 0;
278 5cdb1798 2005-10-29 devnull * get a line that ends in crnl or cr, turn terminating crnl into a nl
280 5cdb1798 2005-10-29 devnull * return 0 on EOF
282 5cdb1798 2005-10-29 devnull static int
283 5cdb1798 2005-10-29 devnull getcrnl(char *buf, int n)
286 5cdb1798 2005-10-29 devnull char *ep;
287 5cdb1798 2005-10-29 devnull char *bp;
288 5cdb1798 2005-10-29 devnull Biobuf *fp = ∈
290 5cdb1798 2005-10-29 devnull Bflush(&out);
292 5cdb1798 2005-10-29 devnull bp = buf;
293 5cdb1798 2005-10-29 devnull ep = bp + n - 1;
294 5cdb1798 2005-10-29 devnull while(bp != ep){
295 5cdb1798 2005-10-29 devnull c = Bgetc(fp);
296 5cdb1798 2005-10-29 devnull if(debug) {
297 5cdb1798 2005-10-29 devnull seek(2, 0, 2);
298 5cdb1798 2005-10-29 devnull fprint(2, "%c", c);
300 5cdb1798 2005-10-29 devnull switch(c){
301 5cdb1798 2005-10-29 devnull case -1:
302 5cdb1798 2005-10-29 devnull *bp = 0;
303 5cdb1798 2005-10-29 devnull if(bp==buf)
304 5cdb1798 2005-10-29 devnull return 0;
306 5cdb1798 2005-10-29 devnull return bp-buf;
307 5cdb1798 2005-10-29 devnull case '\r':
308 5cdb1798 2005-10-29 devnull c = Bgetc(fp);
309 5cdb1798 2005-10-29 devnull if(c == '\n'){
310 5cdb1798 2005-10-29 devnull if(debug) {
311 5cdb1798 2005-10-29 devnull seek(2, 0, 2);
312 5cdb1798 2005-10-29 devnull fprint(2, "%c", c);
314 5cdb1798 2005-10-29 devnull *bp = 0;
315 5cdb1798 2005-10-29 devnull return bp-buf;
317 5cdb1798 2005-10-29 devnull Bungetc(fp);
318 5cdb1798 2005-10-29 devnull c = '\r';
320 5cdb1798 2005-10-29 devnull case '\n':
321 5cdb1798 2005-10-29 devnull *bp = 0;
322 5cdb1798 2005-10-29 devnull return bp-buf;
324 5cdb1798 2005-10-29 devnull *bp++ = c;
326 5cdb1798 2005-10-29 devnull *bp = 0;
327 5cdb1798 2005-10-29 devnull return bp-buf;
330 5cdb1798 2005-10-29 devnull static void
331 5cdb1798 2005-10-29 devnull sendcrnl(char *fmt, ...)
333 5cdb1798 2005-10-29 devnull char buf[1024];
334 5cdb1798 2005-10-29 devnull va_list arg;
336 5cdb1798 2005-10-29 devnull va_start(arg, fmt);
337 5cdb1798 2005-10-29 devnull vseprint(buf, buf+sizeof(buf), fmt, arg);
338 5cdb1798 2005-10-29 devnull va_end(arg);
339 5cdb1798 2005-10-29 devnull if(debug)
340 5cdb1798 2005-10-29 devnull fprint(2, "-> %s\n", buf);
341 5cdb1798 2005-10-29 devnull Bprint(&out, "%s\r\n", buf);
344 5cdb1798 2005-10-29 devnull static int
345 5cdb1798 2005-10-29 devnull senderr(char *fmt, ...)
347 5cdb1798 2005-10-29 devnull char buf[1024];
348 5cdb1798 2005-10-29 devnull va_list arg;
350 5cdb1798 2005-10-29 devnull va_start(arg, fmt);
351 5cdb1798 2005-10-29 devnull vseprint(buf, buf+sizeof(buf), fmt, arg);
352 5cdb1798 2005-10-29 devnull va_end(arg);
353 5cdb1798 2005-10-29 devnull if(debug)
354 5cdb1798 2005-10-29 devnull fprint(2, "-> -ERR %s\n", buf);
355 5cdb1798 2005-10-29 devnull Bprint(&out, "-ERR %s\r\n", buf);
356 5cdb1798 2005-10-29 devnull return -1;
359 5cdb1798 2005-10-29 devnull static int
360 5cdb1798 2005-10-29 devnull sendok(char *fmt, ...)
362 5cdb1798 2005-10-29 devnull char buf[1024];
363 5cdb1798 2005-10-29 devnull va_list arg;
365 5cdb1798 2005-10-29 devnull va_start(arg, fmt);
366 5cdb1798 2005-10-29 devnull vseprint(buf, buf+sizeof(buf), fmt, arg);
367 5cdb1798 2005-10-29 devnull va_end(arg);
368 5cdb1798 2005-10-29 devnull if(*buf){
369 5cdb1798 2005-10-29 devnull if(debug)
370 5cdb1798 2005-10-29 devnull fprint(2, "-> +OK %s\n", buf);
371 5cdb1798 2005-10-29 devnull Bprint(&out, "+OK %s\r\n", buf);
372 5cdb1798 2005-10-29 devnull } else {
373 5cdb1798 2005-10-29 devnull if(debug)
374 5cdb1798 2005-10-29 devnull fprint(2, "-> +OK\n");
375 5cdb1798 2005-10-29 devnull Bprint(&out, "+OK\r\n");
377 5cdb1798 2005-10-29 devnull return 0;
380 5cdb1798 2005-10-29 devnull static int
381 5cdb1798 2005-10-29 devnull capacmd(char*)
383 5cdb1798 2005-10-29 devnull sendok("");
384 5cdb1798 2005-10-29 devnull sendcrnl("TOP");
385 5cdb1798 2005-10-29 devnull if(passwordinclear || didtls)
386 5cdb1798 2005-10-29 devnull sendcrnl("USER");
387 5cdb1798 2005-10-29 devnull sendcrnl("PIPELINING");
388 5cdb1798 2005-10-29 devnull sendcrnl("UIDL");
389 5cdb1798 2005-10-29 devnull sendcrnl("STLS");
390 5cdb1798 2005-10-29 devnull sendcrnl(".");
391 5cdb1798 2005-10-29 devnull return 0;
394 5cdb1798 2005-10-29 devnull static int
395 5cdb1798 2005-10-29 devnull delecmd(char *arg)
399 5cdb1798 2005-10-29 devnull if(*arg==0)
400 5cdb1798 2005-10-29 devnull return senderr("DELE requires a message number");
402 5cdb1798 2005-10-29 devnull n = atoi(arg)-1;
403 5cdb1798 2005-10-29 devnull if(n < 0 || n >= nmsg || msg[n].deleted)
404 5cdb1798 2005-10-29 devnull return senderr("no such message");
406 5cdb1798 2005-10-29 devnull msg[n].deleted = 1;
407 5cdb1798 2005-10-29 devnull totalmsgs--;
408 5cdb1798 2005-10-29 devnull totalbytes -= msg[n].bytes;
409 5cdb1798 2005-10-29 devnull sendok("message %d deleted", n+1);
410 5cdb1798 2005-10-29 devnull return 0;
413 5cdb1798 2005-10-29 devnull static int
414 5cdb1798 2005-10-29 devnull listcmd(char *arg)
416 5cdb1798 2005-10-29 devnull int i, n;
418 5cdb1798 2005-10-29 devnull if(*arg == 0){
419 5cdb1798 2005-10-29 devnull sendok("+%d message%s (%d octets)", totalmsgs, totalmsgs==1 ? "":"s", totalbytes);
420 5cdb1798 2005-10-29 devnull for(i=0; i<nmsg; i++){
421 5cdb1798 2005-10-29 devnull if(msg[i].deleted)
422 5cdb1798 2005-10-29 devnull continue;
423 5cdb1798 2005-10-29 devnull sendcrnl("%d %d", i+1, msg[i].bytes);
425 5cdb1798 2005-10-29 devnull sendcrnl(".");
427 5cdb1798 2005-10-29 devnull n = atoi(arg)-1;
428 5cdb1798 2005-10-29 devnull if(n < 0 || n >= nmsg || msg[n].deleted)
429 5cdb1798 2005-10-29 devnull return senderr("no such message");
430 5cdb1798 2005-10-29 devnull sendok("%d %d", n+1, msg[n].bytes);
432 5cdb1798 2005-10-29 devnull return 0;
435 5cdb1798 2005-10-29 devnull static int
436 5cdb1798 2005-10-29 devnull noopcmd(char *arg)
438 5cdb1798 2005-10-29 devnull USED(arg);
439 5cdb1798 2005-10-29 devnull sendok("");
440 5cdb1798 2005-10-29 devnull return 0;
443 5cdb1798 2005-10-29 devnull static void
444 5cdb1798 2005-10-29 devnull _synccmd(char*)
446 5cdb1798 2005-10-29 devnull int i, fd;
447 5cdb1798 2005-10-29 devnull char *s;
450 5cdb1798 2005-10-29 devnull if(!loggedin){
451 5cdb1798 2005-10-29 devnull sendok("");
455 5cdb1798 2005-10-29 devnull fmtstrinit(&f);
456 5cdb1798 2005-10-29 devnull fmtprint(&f, "delete mbox");
457 5cdb1798 2005-10-29 devnull for(i=0; i<nmsg; i++)
458 5cdb1798 2005-10-29 devnull if(msg[i].deleted)
459 5cdb1798 2005-10-29 devnull fmtprint(&f, " %d", msg[i].upasnum);
460 5cdb1798 2005-10-29 devnull s = fmtstrflush(&f);
461 5cdb1798 2005-10-29 devnull if(strcmp(s, "delete mbox") != 0){ /* must have something to delete */
462 5cdb1798 2005-10-29 devnull if((fd = open("../ctl", OWRITE)) < 0){
463 5cdb1798 2005-10-29 devnull senderr("open ctl to delete messages: %r");
466 5cdb1798 2005-10-29 devnull if(write(fd, s, strlen(s)) < 0){
467 5cdb1798 2005-10-29 devnull senderr("error deleting messages: %r");
471 5cdb1798 2005-10-29 devnull sendok("");
474 5cdb1798 2005-10-29 devnull static int
475 5cdb1798 2005-10-29 devnull synccmd(char*)
477 5cdb1798 2005-10-29 devnull _synccmd(nil);
478 5cdb1798 2005-10-29 devnull return 0;
481 5cdb1798 2005-10-29 devnull static int
482 5cdb1798 2005-10-29 devnull quitcmd(char*)
484 5cdb1798 2005-10-29 devnull synccmd(nil);
485 5cdb1798 2005-10-29 devnull exits(nil);
486 5cdb1798 2005-10-29 devnull return 0;
489 5cdb1798 2005-10-29 devnull static int
490 5cdb1798 2005-10-29 devnull retrcmd(char *arg)
493 5cdb1798 2005-10-29 devnull Biobuf *b;
494 5cdb1798 2005-10-29 devnull char buf[40], *p;
496 5cdb1798 2005-10-29 devnull if(*arg == 0)
497 5cdb1798 2005-10-29 devnull return senderr("RETR requires a message number");
498 5cdb1798 2005-10-29 devnull n = atoi(arg)-1;
499 5cdb1798 2005-10-29 devnull if(n < 0 || n >= nmsg || msg[n].deleted)
500 5cdb1798 2005-10-29 devnull return senderr("no such message");
501 5cdb1798 2005-10-29 devnull snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
502 5cdb1798 2005-10-29 devnull if((b = Bopen(buf, OREAD)) == nil)
503 5cdb1798 2005-10-29 devnull return senderr("message disappeared");
504 5cdb1798 2005-10-29 devnull sendok("");
505 5cdb1798 2005-10-29 devnull while((p = Brdstr(b, '\n', 1)) != nil){
506 5cdb1798 2005-10-29 devnull if(p[0]=='.')
507 5cdb1798 2005-10-29 devnull Bwrite(&out, ".", 1);
508 5cdb1798 2005-10-29 devnull Bwrite(&out, p, strlen(p));
509 5cdb1798 2005-10-29 devnull Bwrite(&out, "\r\n", 2);
510 5cdb1798 2005-10-29 devnull free(p);
512 5cdb1798 2005-10-29 devnull Bterm(b);
513 5cdb1798 2005-10-29 devnull sendcrnl(".");
514 5cdb1798 2005-10-29 devnull return 0;
517 5cdb1798 2005-10-29 devnull static int
518 5cdb1798 2005-10-29 devnull rsetcmd(char*)
522 5cdb1798 2005-10-29 devnull for(i=0; i<nmsg; i++){
523 5cdb1798 2005-10-29 devnull if(msg[i].deleted){
524 5cdb1798 2005-10-29 devnull msg[i].deleted = 0;
525 5cdb1798 2005-10-29 devnull totalmsgs++;
526 5cdb1798 2005-10-29 devnull totalbytes += msg[i].bytes;
529 5cdb1798 2005-10-29 devnull return sendok("");
532 5cdb1798 2005-10-29 devnull static int
533 5cdb1798 2005-10-29 devnull statcmd(char*)
535 5cdb1798 2005-10-29 devnull return sendok("%d %d", totalmsgs, totalbytes);
538 5cdb1798 2005-10-29 devnull static int
539 5cdb1798 2005-10-29 devnull trace(char *fmt, ...)
541 5cdb1798 2005-10-29 devnull va_list arg;
544 5cdb1798 2005-10-29 devnull va_start(arg, fmt);
545 5cdb1798 2005-10-29 devnull n = vfprint(2, fmt, arg);
546 5cdb1798 2005-10-29 devnull va_end(arg);
547 5cdb1798 2005-10-29 devnull return n;
550 5cdb1798 2005-10-29 devnull static int
551 5cdb1798 2005-10-29 devnull stlscmd(char*)
554 5cdb1798 2005-10-29 devnull TLSconn conn;
556 5cdb1798 2005-10-29 devnull if(didtls)
557 5cdb1798 2005-10-29 devnull return senderr("tls already started");
558 5cdb1798 2005-10-29 devnull if(!tlscert)
559 5cdb1798 2005-10-29 devnull return senderr("don't have any tls credentials");
560 5cdb1798 2005-10-29 devnull sendok("");
561 5cdb1798 2005-10-29 devnull Bflush(&out);
563 5cdb1798 2005-10-29 devnull memset(&conn, 0, sizeof conn);
564 5cdb1798 2005-10-29 devnull conn.cert = tlscert;
565 5cdb1798 2005-10-29 devnull conn.certlen = ntlscert;
566 5cdb1798 2005-10-29 devnull if(debug)
567 5cdb1798 2005-10-29 devnull conn.trace = trace;
568 5cdb1798 2005-10-29 devnull fd = tlsServer(0, &conn);
569 5cdb1798 2005-10-29 devnull if(fd < 0)
570 5cdb1798 2005-10-29 devnull sysfatal("tlsServer: %r");
571 5cdb1798 2005-10-29 devnull dup(fd, 0);
572 5cdb1798 2005-10-29 devnull dup(fd, 1);
573 5cdb1798 2005-10-29 devnull close(fd);
574 5cdb1798 2005-10-29 devnull Binit(&in, 0, OREAD);
575 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
576 5cdb1798 2005-10-29 devnull didtls = 1;
577 5cdb1798 2005-10-29 devnull return 0;
580 5cdb1798 2005-10-29 devnull static int
581 5cdb1798 2005-10-29 devnull topcmd(char *arg)
583 5cdb1798 2005-10-29 devnull int done, i, lines, n;
584 5cdb1798 2005-10-29 devnull char buf[40], *p;
585 5cdb1798 2005-10-29 devnull Biobuf *b;
587 5cdb1798 2005-10-29 devnull if(*arg == 0)
588 5cdb1798 2005-10-29 devnull return senderr("TOP requires a message number");
589 5cdb1798 2005-10-29 devnull n = atoi(arg)-1;
590 5cdb1798 2005-10-29 devnull if(n < 0 || n >= nmsg || msg[n].deleted)
591 5cdb1798 2005-10-29 devnull return senderr("no such message");
592 5cdb1798 2005-10-29 devnull arg = nextarg(arg);
593 5cdb1798 2005-10-29 devnull if(*arg == 0)
594 5cdb1798 2005-10-29 devnull return senderr("TOP requires a line count");
595 5cdb1798 2005-10-29 devnull lines = atoi(arg);
596 5cdb1798 2005-10-29 devnull if(lines < 0)
597 5cdb1798 2005-10-29 devnull return senderr("bad args to TOP");
598 5cdb1798 2005-10-29 devnull snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
599 5cdb1798 2005-10-29 devnull if((b = Bopen(buf, OREAD)) == nil)
600 5cdb1798 2005-10-29 devnull return senderr("message disappeared");
601 5cdb1798 2005-10-29 devnull sendok("");
602 5cdb1798 2005-10-29 devnull while(p = Brdstr(b, '\n', 1)){
603 5cdb1798 2005-10-29 devnull if(p[0]=='.')
604 5cdb1798 2005-10-29 devnull Bputc(&out, '.');
605 5cdb1798 2005-10-29 devnull Bwrite(&out, p, strlen(p));
606 5cdb1798 2005-10-29 devnull Bwrite(&out, "\r\n", 2);
607 5cdb1798 2005-10-29 devnull done = p[0]=='\0';
608 5cdb1798 2005-10-29 devnull free(p);
609 5cdb1798 2005-10-29 devnull if(done)
612 5cdb1798 2005-10-29 devnull for(i=0; i<lines; i++){
613 5cdb1798 2005-10-29 devnull p = Brdstr(b, '\n', 1);
614 5cdb1798 2005-10-29 devnull if(p == nil)
616 5cdb1798 2005-10-29 devnull if(p[0]=='.')
617 5cdb1798 2005-10-29 devnull Bwrite(&out, ".", 1);
618 5cdb1798 2005-10-29 devnull Bwrite(&out, p, strlen(p));
619 5cdb1798 2005-10-29 devnull Bwrite(&out, "\r\n", 2);
620 5cdb1798 2005-10-29 devnull free(p);
622 5cdb1798 2005-10-29 devnull sendcrnl(".");
623 5cdb1798 2005-10-29 devnull Bterm(b);
624 5cdb1798 2005-10-29 devnull return 0;
627 5cdb1798 2005-10-29 devnull static int
628 5cdb1798 2005-10-29 devnull uidlcmd(char *arg)
632 5cdb1798 2005-10-29 devnull if(*arg==0){
633 5cdb1798 2005-10-29 devnull sendok("");
634 5cdb1798 2005-10-29 devnull for(n=0; n<nmsg; n++){
635 5cdb1798 2005-10-29 devnull if(msg[n].deleted)
636 5cdb1798 2005-10-29 devnull continue;
637 5cdb1798 2005-10-29 devnull sendcrnl("%d %s", n+1, msg[n].digest);
639 5cdb1798 2005-10-29 devnull sendcrnl(".");
641 5cdb1798 2005-10-29 devnull n = atoi(arg)-1;
642 5cdb1798 2005-10-29 devnull if(n < 0 || n >= nmsg || msg[n].deleted)
643 5cdb1798 2005-10-29 devnull return senderr("no such message");
644 5cdb1798 2005-10-29 devnull sendok("%d %s", n+1, msg[n].digest);
646 5cdb1798 2005-10-29 devnull return 0;
649 5cdb1798 2005-10-29 devnull static char*
650 5cdb1798 2005-10-29 devnull nextarg(char *p)
652 5cdb1798 2005-10-29 devnull while(*p && *p != ' ' && *p != '\t')
654 5cdb1798 2005-10-29 devnull while(*p == ' ' || *p == '\t')
655 5cdb1798 2005-10-29 devnull *p++ = 0;
656 5cdb1798 2005-10-29 devnull return p;
660 5cdb1798 2005-10-29 devnull * authentication
662 5cdb1798 2005-10-29 devnull Chalstate *chs;
663 5cdb1798 2005-10-29 devnull char user[256];
664 5cdb1798 2005-10-29 devnull char box[256];
665 5cdb1798 2005-10-29 devnull char cbox[256];
667 5cdb1798 2005-10-29 devnull static void
668 5cdb1798 2005-10-29 devnull hello(void)
670 5cdb1798 2005-10-29 devnull fmtinstall('H', encodefmt);
671 5cdb1798 2005-10-29 devnull if((chs = auth_challenge("proto=apop role=server")) == nil){
672 5cdb1798 2005-10-29 devnull senderr("auth server not responding, try later");
673 5cdb1798 2005-10-29 devnull exits(nil);
676 5cdb1798 2005-10-29 devnull sendok("POP3 server ready %s", chs->chal);
679 5cdb1798 2005-10-29 devnull static int
680 5cdb1798 2005-10-29 devnull setuser(char *arg)
682 5cdb1798 2005-10-29 devnull char *p;
684 5cdb1798 2005-10-29 devnull strcpy(box, "/mail/box/");
685 5cdb1798 2005-10-29 devnull strecpy(box+strlen(box), box+sizeof box-7, arg);
686 5cdb1798 2005-10-29 devnull strcpy(cbox, box);
687 5cdb1798 2005-10-29 devnull cleanname(cbox);
688 5cdb1798 2005-10-29 devnull if(strcmp(cbox, box) != 0)
689 5cdb1798 2005-10-29 devnull return senderr("bad mailbox name");
690 5cdb1798 2005-10-29 devnull strcat(box, "/mbox");
692 5cdb1798 2005-10-29 devnull strecpy(user, user+sizeof user, arg);
693 5cdb1798 2005-10-29 devnull if(p = strchr(user, '/'))
694 5cdb1798 2005-10-29 devnull *p = '\0';
695 5cdb1798 2005-10-29 devnull return 0;
698 5cdb1798 2005-10-29 devnull static int
699 5cdb1798 2005-10-29 devnull usercmd(char *arg)
701 5cdb1798 2005-10-29 devnull if(loggedin)
702 5cdb1798 2005-10-29 devnull return senderr("already authenticated");
703 5cdb1798 2005-10-29 devnull if(*arg == 0)
704 5cdb1798 2005-10-29 devnull return senderr("USER requires argument");
705 5cdb1798 2005-10-29 devnull if(setuser(arg) < 0)
706 5cdb1798 2005-10-29 devnull return -1;
707 5cdb1798 2005-10-29 devnull return sendok("");
710 5cdb1798 2005-10-29 devnull static void
711 5cdb1798 2005-10-29 devnull enableaddr(void)
714 5cdb1798 2005-10-29 devnull char buf[64];
716 5cdb1798 2005-10-29 devnull /* hide the peer IP address under a rock in the ratifier FS */
717 5cdb1798 2005-10-29 devnull if(peeraddr == 0 || *peeraddr == 0)
720 5cdb1798 2005-10-29 devnull sprint(buf, "/mail/ratify/trusted/%s#32", peeraddr);
723 5cdb1798 2005-10-29 devnull * if the address is already there and the user owns it,
724 5cdb1798 2005-10-29 devnull * remove it and recreate it to give him a new time quanta.
726 5cdb1798 2005-10-29 devnull if(access(buf, 0) >= 0 && remove(buf) < 0)
729 5cdb1798 2005-10-29 devnull fd = create(buf, OREAD, 0666);
730 5cdb1798 2005-10-29 devnull if(fd >= 0){
731 5cdb1798 2005-10-29 devnull close(fd);
732 5cdb1798 2005-10-29 devnull // syslog(0, "pop3", "ratified %s", peeraddr);
736 5cdb1798 2005-10-29 devnull static int
737 5cdb1798 2005-10-29 devnull dologin(char *response)
739 5cdb1798 2005-10-29 devnull AuthInfo *ai;
740 5cdb1798 2005-10-29 devnull static int tries;
742 5cdb1798 2005-10-29 devnull chs->user = user;
743 5cdb1798 2005-10-29 devnull chs->resp = response;
744 5cdb1798 2005-10-29 devnull chs->nresp = strlen(response);
745 5cdb1798 2005-10-29 devnull if((ai = auth_response(chs)) == nil){
746 5cdb1798 2005-10-29 devnull if(tries++ >= 5){
747 5cdb1798 2005-10-29 devnull senderr("authentication failed: %r; server exiting");
748 5cdb1798 2005-10-29 devnull exits(nil);
750 5cdb1798 2005-10-29 devnull return senderr("authentication failed");
753 5cdb1798 2005-10-29 devnull if(auth_chuid(ai, nil) < 0){
754 5cdb1798 2005-10-29 devnull senderr("chuid failed: %r; server exiting");
755 5cdb1798 2005-10-29 devnull exits(nil);
757 5cdb1798 2005-10-29 devnull auth_freeAI(ai);
758 5cdb1798 2005-10-29 devnull auth_freechal(chs);
759 5cdb1798 2005-10-29 devnull chs = nil;
761 5cdb1798 2005-10-29 devnull loggedin = 1;
762 5cdb1798 2005-10-29 devnull if(newns(user, 0) < 0){
763 5cdb1798 2005-10-29 devnull senderr("newns failed: %r; server exiting");
764 5cdb1798 2005-10-29 devnull exits(nil);
767 5cdb1798 2005-10-29 devnull enableaddr();
768 5cdb1798 2005-10-29 devnull if(readmbox(box) < 0)
769 5cdb1798 2005-10-29 devnull exits(nil);
770 5cdb1798 2005-10-29 devnull return sendok("mailbox is %s", box);
773 5cdb1798 2005-10-29 devnull static int
774 5cdb1798 2005-10-29 devnull passcmd(char *arg)
776 5cdb1798 2005-10-29 devnull DigestState *s;
777 5cdb1798 2005-10-29 devnull uchar digest[MD5dlen];
778 5cdb1798 2005-10-29 devnull char response[2*MD5dlen+1];
780 5cdb1798 2005-10-29 devnull if(passwordinclear==0 && didtls==0)
781 5cdb1798 2005-10-29 devnull return senderr("password in the clear disallowed");
783 5cdb1798 2005-10-29 devnull /* use password to encode challenge */
784 5cdb1798 2005-10-29 devnull if((chs = auth_challenge("proto=apop role=server")) == nil)
785 5cdb1798 2005-10-29 devnull return senderr("couldn't get apop challenge");
787 5cdb1798 2005-10-29 devnull // hash challenge with secret and convert to ascii
788 5cdb1798 2005-10-29 devnull s = md5((uchar*)chs->chal, chs->nchal, 0, 0);
789 5cdb1798 2005-10-29 devnull md5((uchar*)arg, strlen(arg), digest, s);
790 5cdb1798 2005-10-29 devnull snprint(response, sizeof response, "%.*H", MD5dlen, digest);
791 5cdb1798 2005-10-29 devnull return dologin(response);
794 5cdb1798 2005-10-29 devnull static int
795 5cdb1798 2005-10-29 devnull apopcmd(char *arg)
797 5cdb1798 2005-10-29 devnull char *resp;
799 5cdb1798 2005-10-29 devnull resp = nextarg(arg);
800 5cdb1798 2005-10-29 devnull if(setuser(arg) < 0)
801 5cdb1798 2005-10-29 devnull return -1;
802 5cdb1798 2005-10-29 devnull return dologin(resp);