19a747e4fSDavid du Colombier #include "common.h"
29a747e4fSDavid du Colombier #include <ctype.h>
39a747e4fSDavid du Colombier #include <auth.h>
49a747e4fSDavid du Colombier #include <libsec.h>
59a747e4fSDavid du Colombier
69a747e4fSDavid du Colombier typedef struct Cmd Cmd;
79a747e4fSDavid du Colombier struct Cmd
89a747e4fSDavid du Colombier {
99a747e4fSDavid du Colombier char *name;
109a747e4fSDavid du Colombier int needauth;
119a747e4fSDavid du Colombier int (*f)(char*);
129a747e4fSDavid du Colombier };
139a747e4fSDavid du Colombier
149a747e4fSDavid du Colombier static void hello(void);
159a747e4fSDavid du Colombier static int apopcmd(char*);
169a747e4fSDavid du Colombier static int capacmd(char*);
179a747e4fSDavid du Colombier static int delecmd(char*);
189a747e4fSDavid du Colombier static int listcmd(char*);
199a747e4fSDavid du Colombier static int noopcmd(char*);
209a747e4fSDavid du Colombier static int passcmd(char*);
219a747e4fSDavid du Colombier static int quitcmd(char*);
229a747e4fSDavid du Colombier static int rsetcmd(char*);
239a747e4fSDavid du Colombier static int retrcmd(char*);
249a747e4fSDavid du Colombier static int statcmd(char*);
259a747e4fSDavid du Colombier static int stlscmd(char*);
269a747e4fSDavid du Colombier static int topcmd(char*);
2739734e7eSDavid du Colombier static int synccmd(char*);
289a747e4fSDavid du Colombier static int uidlcmd(char*);
299a747e4fSDavid du Colombier static int usercmd(char*);
309a747e4fSDavid du Colombier static char *nextarg(char*);
319a747e4fSDavid du Colombier static int getcrnl(char*, int);
329a747e4fSDavid du Colombier static int readmbox(char*);
339a747e4fSDavid du Colombier static void sendcrnl(char*, ...);
349a747e4fSDavid du Colombier static int senderr(char*, ...);
359a747e4fSDavid du Colombier static int sendok(char*, ...);
369a747e4fSDavid du Colombier #pragma varargck argpos sendcrnl 1
379a747e4fSDavid du Colombier #pragma varargck argpos senderr 1
389a747e4fSDavid du Colombier #pragma varargck argpos sendok 1
399a747e4fSDavid du Colombier
409a747e4fSDavid du Colombier Cmd cmdtab[] =
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier "apop", 0, apopcmd,
439a747e4fSDavid du Colombier "capa", 0, capacmd,
449a747e4fSDavid du Colombier "dele", 1, delecmd,
459a747e4fSDavid du Colombier "list", 1, listcmd,
469a747e4fSDavid du Colombier "noop", 0, noopcmd,
479a747e4fSDavid du Colombier "pass", 0, passcmd,
489a747e4fSDavid du Colombier "quit", 0, quitcmd,
499a747e4fSDavid du Colombier "rset", 0, rsetcmd,
509a747e4fSDavid du Colombier "retr", 1, retrcmd,
519a747e4fSDavid du Colombier "stat", 1, statcmd,
529a747e4fSDavid du Colombier "stls", 0, stlscmd,
5339734e7eSDavid du Colombier "sync", 1, synccmd,
549a747e4fSDavid du Colombier "top", 1, topcmd,
559a747e4fSDavid du Colombier "uidl", 1, uidlcmd,
569a747e4fSDavid du Colombier "user", 0, usercmd,
579a747e4fSDavid du Colombier 0, 0, 0,
589a747e4fSDavid du Colombier };
599a747e4fSDavid du Colombier
609a747e4fSDavid du Colombier static Biobuf in;
619a747e4fSDavid du Colombier static Biobuf out;
629a747e4fSDavid du Colombier static int passwordinclear;
639a747e4fSDavid du Colombier static int didtls;
649a747e4fSDavid du Colombier
659a747e4fSDavid du Colombier typedef struct Msg Msg;
669a747e4fSDavid du Colombier struct Msg
679a747e4fSDavid du Colombier {
689a747e4fSDavid du Colombier int upasnum;
699a747e4fSDavid du Colombier char digest[64];
709a747e4fSDavid du Colombier int bytes;
719a747e4fSDavid du Colombier int deleted;
729a747e4fSDavid du Colombier };
739a747e4fSDavid du Colombier
749a747e4fSDavid du Colombier static int totalbytes;
759a747e4fSDavid du Colombier static int totalmsgs;
769a747e4fSDavid du Colombier static Msg *msg;
779a747e4fSDavid du Colombier static int nmsg;
789a747e4fSDavid du Colombier static int loggedin;
799a747e4fSDavid du Colombier static int debug;
809a747e4fSDavid du Colombier static uchar *tlscert;
819a747e4fSDavid du Colombier static int ntlscert;
829a747e4fSDavid du Colombier static char *peeraddr;
839a747e4fSDavid du Colombier static char tmpaddr[64];
849a747e4fSDavid du Colombier
859a747e4fSDavid du Colombier void
usage(void)869a747e4fSDavid du Colombier usage(void)
879a747e4fSDavid du Colombier {
88c6569576SDavid du Colombier fprint(2, "usage: upas/pop3 [-a authmboxfile] [-d debugfile] [-p] "
89c6569576SDavid du Colombier "[-r remote] [-t cert]\n");
909a747e4fSDavid du Colombier exits("usage");
919a747e4fSDavid du Colombier }
929a747e4fSDavid du Colombier
939a747e4fSDavid du Colombier void
main(int argc,char ** argv)949a747e4fSDavid du Colombier main(int argc, char **argv)
959a747e4fSDavid du Colombier {
969a747e4fSDavid du Colombier int fd;
979a747e4fSDavid du Colombier char *arg, cmdbuf[1024];
989a747e4fSDavid du Colombier Cmd *c;
999a747e4fSDavid du Colombier
1009a747e4fSDavid du Colombier rfork(RFNAMEG);
1019a747e4fSDavid du Colombier Binit(&in, 0, OREAD);
1029a747e4fSDavid du Colombier Binit(&out, 1, OWRITE);
1039a747e4fSDavid du Colombier
1049a747e4fSDavid du Colombier ARGBEGIN{
1059a747e4fSDavid du Colombier case 'a':
1069a747e4fSDavid du Colombier loggedin = 1;
1079a747e4fSDavid du Colombier if(readmbox(EARGF(usage())) < 0)
1089a747e4fSDavid du Colombier exits(nil);
1099a747e4fSDavid du Colombier break;
1109a747e4fSDavid du Colombier case 'd':
1119a747e4fSDavid du Colombier debug++;
1129a747e4fSDavid du Colombier if((fd = create(EARGF(usage()), OWRITE, 0666)) >= 0 && fd != 2){
1139a747e4fSDavid du Colombier dup(fd, 2);
1149a747e4fSDavid du Colombier close(fd);
1159a747e4fSDavid du Colombier }
1169a747e4fSDavid du Colombier break;
117c6569576SDavid du Colombier case 'p':
118c6569576SDavid du Colombier passwordinclear = 1;
119c6569576SDavid du Colombier break;
1209a747e4fSDavid du Colombier case 'r':
1219a747e4fSDavid du Colombier strecpy(tmpaddr, tmpaddr+sizeof tmpaddr, EARGF(usage()));
1229a747e4fSDavid du Colombier if(arg = strchr(tmpaddr, '!'))
1239a747e4fSDavid du Colombier *arg = '\0';
1249a747e4fSDavid du Colombier peeraddr = tmpaddr;
1259a747e4fSDavid du Colombier break;
1269a747e4fSDavid du Colombier case 't':
1279a747e4fSDavid du Colombier tlscert = readcert(EARGF(usage()), &ntlscert);
1289a747e4fSDavid du Colombier if(tlscert == nil){
1299a747e4fSDavid du Colombier senderr("cannot read TLS certificate: %r");
1309a747e4fSDavid du Colombier exits(nil);
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier break;
1339a747e4fSDavid du Colombier }ARGEND
1349a747e4fSDavid du Colombier
1359a747e4fSDavid du Colombier /* do before TLS */
1369a747e4fSDavid du Colombier if(peeraddr == nil)
1379a747e4fSDavid du Colombier peeraddr = remoteaddr(0,0);
1389a747e4fSDavid du Colombier
1399a747e4fSDavid du Colombier hello();
1409a747e4fSDavid du Colombier
1419a747e4fSDavid du Colombier while(Bflush(&out), getcrnl(cmdbuf, sizeof cmdbuf) > 0){
1429a747e4fSDavid du Colombier arg = nextarg(cmdbuf);
1439a747e4fSDavid du Colombier for(c=cmdtab; c->name; c++)
1449a747e4fSDavid du Colombier if(cistrcmp(c->name, cmdbuf) == 0)
1459a747e4fSDavid du Colombier break;
1469a747e4fSDavid du Colombier if(c->name == 0){
1479a747e4fSDavid du Colombier senderr("unknown command %s", cmdbuf);
1489a747e4fSDavid du Colombier continue;
1499a747e4fSDavid du Colombier }
1509a747e4fSDavid du Colombier if(c->needauth && !loggedin){
1519a747e4fSDavid du Colombier senderr("%s requires authentication", cmdbuf);
1529a747e4fSDavid du Colombier continue;
1539a747e4fSDavid du Colombier }
1549a747e4fSDavid du Colombier (*c->f)(arg);
1559a747e4fSDavid du Colombier }
1569a747e4fSDavid du Colombier exits(nil);
1579a747e4fSDavid du Colombier }
1589a747e4fSDavid du Colombier
1599a747e4fSDavid du Colombier /* sort directories in increasing message number order */
1609a747e4fSDavid du Colombier static int
dircmp(void * a,void * b)1619a747e4fSDavid du Colombier dircmp(void *a, void *b)
1629a747e4fSDavid du Colombier {
1639a747e4fSDavid du Colombier return atoi(((Dir*)a)->name) - atoi(((Dir*)b)->name);
1649a747e4fSDavid du Colombier }
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier static int
readmbox(char * box)1679a747e4fSDavid du Colombier readmbox(char *box)
1689a747e4fSDavid du Colombier {
1699a747e4fSDavid du Colombier int fd, i, n, nd, lines, pid;
170*21887c0bSDavid du Colombier char buf[100], err[Errlen];
1719a747e4fSDavid du Colombier char *p;
1729a747e4fSDavid du Colombier Biobuf *b;
1739a747e4fSDavid du Colombier Dir *d, *draw;
1749a747e4fSDavid du Colombier Msg *m;
1759a747e4fSDavid du Colombier Waitmsg *w;
1769a747e4fSDavid du Colombier
1779a747e4fSDavid du Colombier unmount(nil, "/mail/fs");
1789a747e4fSDavid du Colombier switch(pid = fork()){
1799a747e4fSDavid du Colombier case -1:
1809a747e4fSDavid du Colombier return senderr("can't fork to start upas/fs");
1819a747e4fSDavid du Colombier
1829a747e4fSDavid du Colombier case 0:
1839a747e4fSDavid du Colombier close(0);
1849a747e4fSDavid du Colombier close(1);
1859a747e4fSDavid du Colombier open("/dev/null", OREAD);
1869a747e4fSDavid du Colombier open("/dev/null", OWRITE);
1879a747e4fSDavid du Colombier execl("/bin/upas/fs", "upas/fs", "-np", "-f", box, nil);
1889a747e4fSDavid du Colombier snprint(err, sizeof err, "upas/fs: %r");
1899a747e4fSDavid du Colombier _exits(err);
1909a747e4fSDavid du Colombier break;
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombier default:
1939a747e4fSDavid du Colombier break;
1949a747e4fSDavid du Colombier }
1959a747e4fSDavid du Colombier
1969a747e4fSDavid du Colombier if((w = wait()) == nil || w->pid != pid || w->msg[0] != '\0'){
1979a747e4fSDavid du Colombier if(w && w->pid==pid)
1989a747e4fSDavid du Colombier return senderr("%s", w->msg);
1999a747e4fSDavid du Colombier else
2009a747e4fSDavid du Colombier return senderr("can't initialize upas/fs");
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier free(w);
2039a747e4fSDavid du Colombier
2049a747e4fSDavid du Colombier if(chdir("/mail/fs/mbox") < 0)
2059a747e4fSDavid du Colombier return senderr("can't initialize upas/fs: %r");
2069a747e4fSDavid du Colombier
2079a747e4fSDavid du Colombier if((fd = open(".", OREAD)) < 0)
2089a747e4fSDavid du Colombier return senderr("cannot open /mail/fs/mbox: %r");
2099a747e4fSDavid du Colombier nd = dirreadall(fd, &d);
2109a747e4fSDavid du Colombier close(fd);
2119a747e4fSDavid du Colombier if(nd < 0)
2129a747e4fSDavid du Colombier return senderr("cannot read from /mail/fs/mbox: %r");
2139a747e4fSDavid du Colombier
2149a747e4fSDavid du Colombier msg = mallocz(sizeof(Msg)*nd, 1);
2159a747e4fSDavid du Colombier if(msg == nil)
2169a747e4fSDavid du Colombier return senderr("out of memory");
2179a747e4fSDavid du Colombier
2189a747e4fSDavid du Colombier if(nd == 0)
2199a747e4fSDavid du Colombier return 0;
2209a747e4fSDavid du Colombier qsort(d, nd, sizeof(d[0]), dircmp);
2219a747e4fSDavid du Colombier
2229a747e4fSDavid du Colombier for(i=0; i<nd; i++){
2239a747e4fSDavid du Colombier m = &msg[nmsg];
2249a747e4fSDavid du Colombier m->upasnum = atoi(d[i].name);
2259a747e4fSDavid du Colombier sprint(buf, "%d/digest", m->upasnum);
2269a747e4fSDavid du Colombier if((fd = open(buf, OREAD)) < 0)
2279a747e4fSDavid du Colombier continue;
2289a747e4fSDavid du Colombier n = readn(fd, m->digest, sizeof m->digest - 1);
2299a747e4fSDavid du Colombier close(fd);
2309a747e4fSDavid du Colombier if(n < 0)
2319a747e4fSDavid du Colombier continue;
2329a747e4fSDavid du Colombier m->digest[n] = '\0';
2339a747e4fSDavid du Colombier
2349a747e4fSDavid du Colombier /*
2359a747e4fSDavid du Colombier * We need the number of message lines so that we
2369a747e4fSDavid du Colombier * can adjust the byte count to include \r's.
2379a747e4fSDavid du Colombier * Upas/fs gives us the number of lines in the raw body
2389a747e4fSDavid du Colombier * in the lines file, but we have to count rawheader ourselves.
2399a747e4fSDavid du Colombier * There is one blank line between raw header and raw body.
2409a747e4fSDavid du Colombier */
2419a747e4fSDavid du Colombier sprint(buf, "%d/rawheader", m->upasnum);
2429a747e4fSDavid du Colombier if((b = Bopen(buf, OREAD)) == nil)
2439a747e4fSDavid du Colombier continue;
2449a747e4fSDavid du Colombier lines = 0;
2459a747e4fSDavid du Colombier for(;;){
2469a747e4fSDavid du Colombier p = Brdline(b, '\n');
2479a747e4fSDavid du Colombier if(p == nil){
2481f3c807eSDavid du Colombier if((n = Blinelen(b)) == 0)
2499a747e4fSDavid du Colombier break;
2501f3c807eSDavid du Colombier Bseek(b, n, 1);
2519a747e4fSDavid du Colombier }else
2529a747e4fSDavid du Colombier lines++;
2539a747e4fSDavid du Colombier }
2549a747e4fSDavid du Colombier Bterm(b);
2559a747e4fSDavid du Colombier lines++;
2569a747e4fSDavid du Colombier sprint(buf, "%d/lines", m->upasnum);
2579a747e4fSDavid du Colombier if((fd = open(buf, OREAD)) < 0)
2589a747e4fSDavid du Colombier continue;
2599a747e4fSDavid du Colombier n = readn(fd, buf, sizeof buf - 1);
2609a747e4fSDavid du Colombier close(fd);
2619a747e4fSDavid du Colombier if(n < 0)
2629a747e4fSDavid du Colombier continue;
2639a747e4fSDavid du Colombier buf[n] = '\0';
2649a747e4fSDavid du Colombier lines += atoi(buf);
2659a747e4fSDavid du Colombier
2669a747e4fSDavid du Colombier sprint(buf, "%d/raw", m->upasnum);
2679a747e4fSDavid du Colombier if((draw = dirstat(buf)) == nil)
2689a747e4fSDavid du Colombier continue;
2699a747e4fSDavid du Colombier m->bytes = lines+draw->length;
2709a747e4fSDavid du Colombier free(draw);
2719a747e4fSDavid du Colombier nmsg++;
2729a747e4fSDavid du Colombier totalmsgs++;
2739a747e4fSDavid du Colombier totalbytes += m->bytes;
2749a747e4fSDavid du Colombier }
2759a747e4fSDavid du Colombier return 0;
2769a747e4fSDavid du Colombier }
2779a747e4fSDavid du Colombier
2789a747e4fSDavid du Colombier /*
2799a747e4fSDavid du Colombier * get a line that ends in crnl or cr, turn terminating crnl into a nl
2809a747e4fSDavid du Colombier *
2819a747e4fSDavid du Colombier * return 0 on EOF
2829a747e4fSDavid du Colombier */
2839a747e4fSDavid du Colombier static int
getcrnl(char * buf,int n)2849a747e4fSDavid du Colombier getcrnl(char *buf, int n)
2859a747e4fSDavid du Colombier {
2869a747e4fSDavid du Colombier int c;
2879a747e4fSDavid du Colombier char *ep;
2889a747e4fSDavid du Colombier char *bp;
2899a747e4fSDavid du Colombier Biobuf *fp = ∈
2909a747e4fSDavid du Colombier
2919a747e4fSDavid du Colombier Bflush(&out);
2929a747e4fSDavid du Colombier
2939a747e4fSDavid du Colombier bp = buf;
2949a747e4fSDavid du Colombier ep = bp + n - 1;
2959a747e4fSDavid du Colombier while(bp != ep){
2969a747e4fSDavid du Colombier c = Bgetc(fp);
2979a747e4fSDavid du Colombier if(debug) {
2989a747e4fSDavid du Colombier seek(2, 0, 2);
2999a747e4fSDavid du Colombier fprint(2, "%c", c);
3009a747e4fSDavid du Colombier }
3019a747e4fSDavid du Colombier switch(c){
3029a747e4fSDavid du Colombier case -1:
3039a747e4fSDavid du Colombier *bp = 0;
3049a747e4fSDavid du Colombier if(bp==buf)
3059a747e4fSDavid du Colombier return 0;
3069a747e4fSDavid du Colombier else
3079a747e4fSDavid du Colombier return bp-buf;
3089a747e4fSDavid du Colombier case '\r':
3099a747e4fSDavid du Colombier c = Bgetc(fp);
3109a747e4fSDavid du Colombier if(c == '\n'){
3119a747e4fSDavid du Colombier if(debug) {
3129a747e4fSDavid du Colombier seek(2, 0, 2);
3139a747e4fSDavid du Colombier fprint(2, "%c", c);
3149a747e4fSDavid du Colombier }
3159a747e4fSDavid du Colombier *bp = 0;
3169a747e4fSDavid du Colombier return bp-buf;
3179a747e4fSDavid du Colombier }
3189a747e4fSDavid du Colombier Bungetc(fp);
3199a747e4fSDavid du Colombier c = '\r';
3209a747e4fSDavid du Colombier break;
3219a747e4fSDavid du Colombier case '\n':
3229a747e4fSDavid du Colombier *bp = 0;
3239a747e4fSDavid du Colombier return bp-buf;
3249a747e4fSDavid du Colombier }
3259a747e4fSDavid du Colombier *bp++ = c;
3269a747e4fSDavid du Colombier }
3279a747e4fSDavid du Colombier *bp = 0;
3289a747e4fSDavid du Colombier return bp-buf;
3299a747e4fSDavid du Colombier }
3309a747e4fSDavid du Colombier
3319a747e4fSDavid du Colombier static void
sendcrnl(char * fmt,...)3329a747e4fSDavid du Colombier sendcrnl(char *fmt, ...)
3339a747e4fSDavid du Colombier {
3349a747e4fSDavid du Colombier char buf[1024];
3359a747e4fSDavid du Colombier va_list arg;
3369a747e4fSDavid du Colombier
3379a747e4fSDavid du Colombier va_start(arg, fmt);
3389a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg);
3399a747e4fSDavid du Colombier va_end(arg);
3409a747e4fSDavid du Colombier if(debug)
3419a747e4fSDavid du Colombier fprint(2, "-> %s\n", buf);
3429a747e4fSDavid du Colombier Bprint(&out, "%s\r\n", buf);
3439a747e4fSDavid du Colombier }
3449a747e4fSDavid du Colombier
3459a747e4fSDavid du Colombier static int
senderr(char * fmt,...)3469a747e4fSDavid du Colombier senderr(char *fmt, ...)
3479a747e4fSDavid du Colombier {
3489a747e4fSDavid du Colombier char buf[1024];
3499a747e4fSDavid du Colombier va_list arg;
3509a747e4fSDavid du Colombier
3519a747e4fSDavid du Colombier va_start(arg, fmt);
3529a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg);
3539a747e4fSDavid du Colombier va_end(arg);
3549a747e4fSDavid du Colombier if(debug)
3559a747e4fSDavid du Colombier fprint(2, "-> -ERR %s\n", buf);
3569a747e4fSDavid du Colombier Bprint(&out, "-ERR %s\r\n", buf);
3579a747e4fSDavid du Colombier return -1;
3589a747e4fSDavid du Colombier }
3599a747e4fSDavid du Colombier
3609a747e4fSDavid du Colombier static int
sendok(char * fmt,...)3619a747e4fSDavid du Colombier sendok(char *fmt, ...)
3629a747e4fSDavid du Colombier {
3639a747e4fSDavid du Colombier char buf[1024];
3649a747e4fSDavid du Colombier va_list arg;
3659a747e4fSDavid du Colombier
3669a747e4fSDavid du Colombier va_start(arg, fmt);
3679a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg);
3689a747e4fSDavid du Colombier va_end(arg);
3699a747e4fSDavid du Colombier if(*buf){
3709a747e4fSDavid du Colombier if(debug)
3719a747e4fSDavid du Colombier fprint(2, "-> +OK %s\n", buf);
3729a747e4fSDavid du Colombier Bprint(&out, "+OK %s\r\n", buf);
3739a747e4fSDavid du Colombier } else {
3749a747e4fSDavid du Colombier if(debug)
3759a747e4fSDavid du Colombier fprint(2, "-> +OK\n");
3769a747e4fSDavid du Colombier Bprint(&out, "+OK\r\n");
3779a747e4fSDavid du Colombier }
3789a747e4fSDavid du Colombier return 0;
3799a747e4fSDavid du Colombier }
3809a747e4fSDavid du Colombier
3819a747e4fSDavid du Colombier static int
capacmd(char *)3829a747e4fSDavid du Colombier capacmd(char*)
3839a747e4fSDavid du Colombier {
3849a747e4fSDavid du Colombier sendok("");
3859a747e4fSDavid du Colombier sendcrnl("TOP");
3869a747e4fSDavid du Colombier if(passwordinclear || didtls)
3879a747e4fSDavid du Colombier sendcrnl("USER");
3889a747e4fSDavid du Colombier sendcrnl("PIPELINING");
3899a747e4fSDavid du Colombier sendcrnl("UIDL");
3909a747e4fSDavid du Colombier sendcrnl("STLS");
3919a747e4fSDavid du Colombier sendcrnl(".");
3929a747e4fSDavid du Colombier return 0;
3939a747e4fSDavid du Colombier }
3949a747e4fSDavid du Colombier
3959a747e4fSDavid du Colombier static int
delecmd(char * arg)3969a747e4fSDavid du Colombier delecmd(char *arg)
3979a747e4fSDavid du Colombier {
3989a747e4fSDavid du Colombier int n;
3999a747e4fSDavid du Colombier
4009a747e4fSDavid du Colombier if(*arg==0)
4019a747e4fSDavid du Colombier return senderr("DELE requires a message number");
4029a747e4fSDavid du Colombier
4039a747e4fSDavid du Colombier n = atoi(arg)-1;
4049a747e4fSDavid du Colombier if(n < 0 || n >= nmsg || msg[n].deleted)
4059a747e4fSDavid du Colombier return senderr("no such message");
4069a747e4fSDavid du Colombier
4079a747e4fSDavid du Colombier msg[n].deleted = 1;
4089a747e4fSDavid du Colombier totalmsgs--;
4099a747e4fSDavid du Colombier totalbytes -= msg[n].bytes;
4109a747e4fSDavid du Colombier sendok("message %d deleted", n+1);
4119a747e4fSDavid du Colombier return 0;
4129a747e4fSDavid du Colombier }
4139a747e4fSDavid du Colombier
4149a747e4fSDavid du Colombier static int
listcmd(char * arg)4159a747e4fSDavid du Colombier listcmd(char *arg)
4169a747e4fSDavid du Colombier {
4179a747e4fSDavid du Colombier int i, n;
4189a747e4fSDavid du Colombier
4199a747e4fSDavid du Colombier if(*arg == 0){
4209a747e4fSDavid du Colombier sendok("+%d message%s (%d octets)", totalmsgs, totalmsgs==1 ? "":"s", totalbytes);
4219a747e4fSDavid du Colombier for(i=0; i<nmsg; i++){
4229a747e4fSDavid du Colombier if(msg[i].deleted)
4239a747e4fSDavid du Colombier continue;
4249a747e4fSDavid du Colombier sendcrnl("%d %d", i+1, msg[i].bytes);
4259a747e4fSDavid du Colombier }
4269a747e4fSDavid du Colombier sendcrnl(".");
4279a747e4fSDavid du Colombier }else{
4289a747e4fSDavid du Colombier n = atoi(arg)-1;
4299a747e4fSDavid du Colombier if(n < 0 || n >= nmsg || msg[n].deleted)
4309a747e4fSDavid du Colombier return senderr("no such message");
4319a747e4fSDavid du Colombier sendok("%d %d", n+1, msg[n].bytes);
4329a747e4fSDavid du Colombier }
4339a747e4fSDavid du Colombier return 0;
4349a747e4fSDavid du Colombier }
4359a747e4fSDavid du Colombier
4369a747e4fSDavid du Colombier static int
noopcmd(char * arg)4379a747e4fSDavid du Colombier noopcmd(char *arg)
4389a747e4fSDavid du Colombier {
4399a747e4fSDavid du Colombier USED(arg);
4409a747e4fSDavid du Colombier sendok("");
4419a747e4fSDavid du Colombier return 0;
4429a747e4fSDavid du Colombier }
4439a747e4fSDavid du Colombier
44439734e7eSDavid du Colombier static void
_synccmd(char *)44539734e7eSDavid du Colombier _synccmd(char*)
4469a747e4fSDavid du Colombier {
4479a747e4fSDavid du Colombier int i, fd;
4489a747e4fSDavid du Colombier char *s;
4499a747e4fSDavid du Colombier Fmt f;
4509a747e4fSDavid du Colombier
4519a747e4fSDavid du Colombier if(!loggedin){
4529a747e4fSDavid du Colombier sendok("");
45339734e7eSDavid du Colombier return;
4549a747e4fSDavid du Colombier }
4559a747e4fSDavid du Colombier
4569a747e4fSDavid du Colombier fmtstrinit(&f);
4579a747e4fSDavid du Colombier fmtprint(&f, "delete mbox");
4589a747e4fSDavid du Colombier for(i=0; i<nmsg; i++)
4599a747e4fSDavid du Colombier if(msg[i].deleted)
4609a747e4fSDavid du Colombier fmtprint(&f, " %d", msg[i].upasnum);
4619a747e4fSDavid du Colombier s = fmtstrflush(&f);
4629a747e4fSDavid du Colombier if(strcmp(s, "delete mbox") != 0){ /* must have something to delete */
4639a747e4fSDavid du Colombier if((fd = open("../ctl", OWRITE)) < 0){
4649a747e4fSDavid du Colombier senderr("open ctl to delete messages: %r");
46539734e7eSDavid du Colombier return;
4669a747e4fSDavid du Colombier }
4679a747e4fSDavid du Colombier if(write(fd, s, strlen(s)) < 0){
4689a747e4fSDavid du Colombier senderr("error deleting messages: %r");
46939734e7eSDavid du Colombier return;
4709a747e4fSDavid du Colombier }
4719a747e4fSDavid du Colombier }
4729a747e4fSDavid du Colombier sendok("");
47339734e7eSDavid du Colombier }
47439734e7eSDavid du Colombier
47539734e7eSDavid du Colombier static int
synccmd(char *)47639734e7eSDavid du Colombier synccmd(char*)
47739734e7eSDavid du Colombier {
47839734e7eSDavid du Colombier _synccmd(nil);
47939734e7eSDavid du Colombier return 0;
48039734e7eSDavid du Colombier }
48139734e7eSDavid du Colombier
48239734e7eSDavid du Colombier static int
quitcmd(char *)48339734e7eSDavid du Colombier quitcmd(char*)
48439734e7eSDavid du Colombier {
48539734e7eSDavid du Colombier synccmd(nil);
4869a747e4fSDavid du Colombier exits(nil);
4879a747e4fSDavid du Colombier return 0;
4889a747e4fSDavid du Colombier }
4899a747e4fSDavid du Colombier
4909a747e4fSDavid du Colombier static int
retrcmd(char * arg)4919a747e4fSDavid du Colombier retrcmd(char *arg)
4929a747e4fSDavid du Colombier {
4939a747e4fSDavid du Colombier int n;
4949a747e4fSDavid du Colombier Biobuf *b;
4959a747e4fSDavid du Colombier char buf[40], *p;
4969a747e4fSDavid du Colombier
4979a747e4fSDavid du Colombier if(*arg == 0)
4989a747e4fSDavid du Colombier return senderr("RETR requires a message number");
4999a747e4fSDavid du Colombier n = atoi(arg)-1;
5009a747e4fSDavid du Colombier if(n < 0 || n >= nmsg || msg[n].deleted)
5019a747e4fSDavid du Colombier return senderr("no such message");
5029a747e4fSDavid du Colombier snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
5039a747e4fSDavid du Colombier if((b = Bopen(buf, OREAD)) == nil)
5049a747e4fSDavid du Colombier return senderr("message disappeared");
5059a747e4fSDavid du Colombier sendok("");
5069a747e4fSDavid du Colombier while((p = Brdstr(b, '\n', 1)) != nil){
5079a747e4fSDavid du Colombier if(p[0]=='.')
5089a747e4fSDavid du Colombier Bwrite(&out, ".", 1);
5099a747e4fSDavid du Colombier Bwrite(&out, p, strlen(p));
5109a747e4fSDavid du Colombier Bwrite(&out, "\r\n", 2);
5119a747e4fSDavid du Colombier free(p);
5129a747e4fSDavid du Colombier }
5139a747e4fSDavid du Colombier Bterm(b);
5149a747e4fSDavid du Colombier sendcrnl(".");
5159a747e4fSDavid du Colombier return 0;
5169a747e4fSDavid du Colombier }
5179a747e4fSDavid du Colombier
5189a747e4fSDavid du Colombier static int
rsetcmd(char *)5199a747e4fSDavid du Colombier rsetcmd(char*)
5209a747e4fSDavid du Colombier {
5219a747e4fSDavid du Colombier int i;
5229a747e4fSDavid du Colombier
5239a747e4fSDavid du Colombier for(i=0; i<nmsg; i++){
5249a747e4fSDavid du Colombier if(msg[i].deleted){
5259a747e4fSDavid du Colombier msg[i].deleted = 0;
5269a747e4fSDavid du Colombier totalmsgs++;
5279a747e4fSDavid du Colombier totalbytes += msg[i].bytes;
5289a747e4fSDavid du Colombier }
5299a747e4fSDavid du Colombier }
5309a747e4fSDavid du Colombier return sendok("");
5319a747e4fSDavid du Colombier }
5329a747e4fSDavid du Colombier
5339a747e4fSDavid du Colombier static int
statcmd(char *)5349a747e4fSDavid du Colombier statcmd(char*)
5359a747e4fSDavid du Colombier {
5369a747e4fSDavid du Colombier return sendok("%d %d", totalmsgs, totalbytes);
5379a747e4fSDavid du Colombier }
5389a747e4fSDavid du Colombier
5399a747e4fSDavid du Colombier static int
trace(char * fmt,...)5409a747e4fSDavid du Colombier trace(char *fmt, ...)
5419a747e4fSDavid du Colombier {
5429a747e4fSDavid du Colombier va_list arg;
5439a747e4fSDavid du Colombier int n;
5449a747e4fSDavid du Colombier
5459a747e4fSDavid du Colombier va_start(arg, fmt);
5469a747e4fSDavid du Colombier n = vfprint(2, fmt, arg);
5479a747e4fSDavid du Colombier va_end(arg);
5489a747e4fSDavid du Colombier return n;
5499a747e4fSDavid du Colombier }
5509a747e4fSDavid du Colombier
5519a747e4fSDavid du Colombier static int
stlscmd(char *)5529a747e4fSDavid du Colombier stlscmd(char*)
5539a747e4fSDavid du Colombier {
5549a747e4fSDavid du Colombier int fd;
5559a747e4fSDavid du Colombier TLSconn conn;
5569a747e4fSDavid du Colombier
5579a747e4fSDavid du Colombier if(didtls)
5589a747e4fSDavid du Colombier return senderr("tls already started");
5599a747e4fSDavid du Colombier if(!tlscert)
5609a747e4fSDavid du Colombier return senderr("don't have any tls credentials");
5619a747e4fSDavid du Colombier sendok("");
5629a747e4fSDavid du Colombier Bflush(&out);
5639a747e4fSDavid du Colombier
5649a747e4fSDavid du Colombier memset(&conn, 0, sizeof conn);
5659a747e4fSDavid du Colombier conn.cert = tlscert;
5669a747e4fSDavid du Colombier conn.certlen = ntlscert;
5679a747e4fSDavid du Colombier if(debug)
5689a747e4fSDavid du Colombier conn.trace = trace;
5699a747e4fSDavid du Colombier fd = tlsServer(0, &conn);
5709a747e4fSDavid du Colombier if(fd < 0)
5719a747e4fSDavid du Colombier sysfatal("tlsServer: %r");
5729a747e4fSDavid du Colombier dup(fd, 0);
5739a747e4fSDavid du Colombier dup(fd, 1);
5749a747e4fSDavid du Colombier close(fd);
5759a747e4fSDavid du Colombier Binit(&in, 0, OREAD);
5769a747e4fSDavid du Colombier Binit(&out, 1, OWRITE);
5779a747e4fSDavid du Colombier didtls = 1;
5789a747e4fSDavid du Colombier return 0;
5799a747e4fSDavid du Colombier }
5809a747e4fSDavid du Colombier
5819a747e4fSDavid du Colombier static int
topcmd(char * arg)5829a747e4fSDavid du Colombier topcmd(char *arg)
5839a747e4fSDavid du Colombier {
5845d459b5aSDavid du Colombier int done, i, lines, n;
5859a747e4fSDavid du Colombier char buf[40], *p;
5865d459b5aSDavid du Colombier Biobuf *b;
5879a747e4fSDavid du Colombier
5889a747e4fSDavid du Colombier if(*arg == 0)
5899a747e4fSDavid du Colombier return senderr("TOP requires a message number");
5909a747e4fSDavid du Colombier n = atoi(arg)-1;
5919a747e4fSDavid du Colombier if(n < 0 || n >= nmsg || msg[n].deleted)
5929a747e4fSDavid du Colombier return senderr("no such message");
5939a747e4fSDavid du Colombier arg = nextarg(arg);
5949a747e4fSDavid du Colombier if(*arg == 0)
5959a747e4fSDavid du Colombier return senderr("TOP requires a line count");
5969a747e4fSDavid du Colombier lines = atoi(arg);
5979a747e4fSDavid du Colombier if(lines < 0)
5989a747e4fSDavid du Colombier return senderr("bad args to TOP");
5999a747e4fSDavid du Colombier snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
6009a747e4fSDavid du Colombier if((b = Bopen(buf, OREAD)) == nil)
6019a747e4fSDavid du Colombier return senderr("message disappeared");
6029a747e4fSDavid du Colombier sendok("");
6035d459b5aSDavid du Colombier while(p = Brdstr(b, '\n', 1)){
6045d459b5aSDavid du Colombier if(p[0]=='.')
6055d459b5aSDavid du Colombier Bputc(&out, '.');
6065d459b5aSDavid du Colombier Bwrite(&out, p, strlen(p));
6075d459b5aSDavid du Colombier Bwrite(&out, "\r\n", 2);
6085d459b5aSDavid du Colombier done = p[0]=='\0';
6095d459b5aSDavid du Colombier free(p);
6105d459b5aSDavid du Colombier if(done)
6115d459b5aSDavid du Colombier break;
6125d459b5aSDavid du Colombier }
6139a747e4fSDavid du Colombier for(i=0; i<lines; i++){
6149a747e4fSDavid du Colombier p = Brdstr(b, '\n', 1);
6159a747e4fSDavid du Colombier if(p == nil)
6169a747e4fSDavid du Colombier break;
6179a747e4fSDavid du Colombier if(p[0]=='.')
6189a747e4fSDavid du Colombier Bwrite(&out, ".", 1);
6199a747e4fSDavid du Colombier Bwrite(&out, p, strlen(p));
6209a747e4fSDavid du Colombier Bwrite(&out, "\r\n", 2);
6219a747e4fSDavid du Colombier free(p);
6229a747e4fSDavid du Colombier }
6239a747e4fSDavid du Colombier sendcrnl(".");
6249a747e4fSDavid du Colombier Bterm(b);
6259a747e4fSDavid du Colombier return 0;
6269a747e4fSDavid du Colombier }
6279a747e4fSDavid du Colombier
6289a747e4fSDavid du Colombier static int
uidlcmd(char * arg)6299a747e4fSDavid du Colombier uidlcmd(char *arg)
6309a747e4fSDavid du Colombier {
6319a747e4fSDavid du Colombier int n;
6329a747e4fSDavid du Colombier
6339a747e4fSDavid du Colombier if(*arg==0){
6349a747e4fSDavid du Colombier sendok("");
6359a747e4fSDavid du Colombier for(n=0; n<nmsg; n++){
6369a747e4fSDavid du Colombier if(msg[n].deleted)
6379a747e4fSDavid du Colombier continue;
6389a747e4fSDavid du Colombier sendcrnl("%d %s", n+1, msg[n].digest);
6399a747e4fSDavid du Colombier }
6409a747e4fSDavid du Colombier sendcrnl(".");
6419a747e4fSDavid du Colombier }else{
6429a747e4fSDavid du Colombier n = atoi(arg)-1;
6439a747e4fSDavid du Colombier if(n < 0 || n >= nmsg || msg[n].deleted)
6449a747e4fSDavid du Colombier return senderr("no such message");
6459a747e4fSDavid du Colombier sendok("%d %s", n+1, msg[n].digest);
6469a747e4fSDavid du Colombier }
6479a747e4fSDavid du Colombier return 0;
6489a747e4fSDavid du Colombier }
6499a747e4fSDavid du Colombier
6509a747e4fSDavid du Colombier static char*
nextarg(char * p)6519a747e4fSDavid du Colombier nextarg(char *p)
6529a747e4fSDavid du Colombier {
6539a747e4fSDavid du Colombier while(*p && *p != ' ' && *p != '\t')
6549a747e4fSDavid du Colombier p++;
6559a747e4fSDavid du Colombier while(*p == ' ' || *p == '\t')
6569a747e4fSDavid du Colombier *p++ = 0;
6579a747e4fSDavid du Colombier return p;
6589a747e4fSDavid du Colombier }
6599a747e4fSDavid du Colombier
6609a747e4fSDavid du Colombier /*
6619a747e4fSDavid du Colombier * authentication
6629a747e4fSDavid du Colombier */
6639a747e4fSDavid du Colombier Chalstate *chs;
6649a747e4fSDavid du Colombier char user[256];
6659a747e4fSDavid du Colombier char box[256];
6669a747e4fSDavid du Colombier char cbox[256];
6679a747e4fSDavid du Colombier
6689a747e4fSDavid du Colombier static void
hello(void)6699a747e4fSDavid du Colombier hello(void)
6709a747e4fSDavid du Colombier {
6719a747e4fSDavid du Colombier fmtinstall('H', encodefmt);
6723a4824e1SDavid du Colombier if((chs = auth_challenge("proto=apop role=server")) == nil){
6739a747e4fSDavid du Colombier senderr("auth server not responding, try later");
6743a4824e1SDavid du Colombier exits(nil);
6753a4824e1SDavid du Colombier }
6769a747e4fSDavid du Colombier
6779a747e4fSDavid du Colombier sendok("POP3 server ready %s", chs->chal);
6789a747e4fSDavid du Colombier }
6799a747e4fSDavid du Colombier
6809a747e4fSDavid du Colombier static int
setuser(char * arg)6819a747e4fSDavid du Colombier setuser(char *arg)
6829a747e4fSDavid du Colombier {
6839a747e4fSDavid du Colombier char *p;
6849a747e4fSDavid du Colombier
6859a747e4fSDavid du Colombier strcpy(box, "/mail/box/");
6869a747e4fSDavid du Colombier strecpy(box+strlen(box), box+sizeof box-7, arg);
6879a747e4fSDavid du Colombier strcpy(cbox, box);
6889a747e4fSDavid du Colombier cleanname(cbox);
6899a747e4fSDavid du Colombier if(strcmp(cbox, box) != 0)
6909a747e4fSDavid du Colombier return senderr("bad mailbox name");
6919a747e4fSDavid du Colombier strcat(box, "/mbox");
6929a747e4fSDavid du Colombier
6939a747e4fSDavid du Colombier strecpy(user, user+sizeof user, arg);
6949a747e4fSDavid du Colombier if(p = strchr(user, '/'))
6959a747e4fSDavid du Colombier *p = '\0';
6969a747e4fSDavid du Colombier return 0;
6979a747e4fSDavid du Colombier }
6989a747e4fSDavid du Colombier
6999a747e4fSDavid du Colombier static int
usercmd(char * arg)7009a747e4fSDavid du Colombier usercmd(char *arg)
7019a747e4fSDavid du Colombier {
7029a747e4fSDavid du Colombier if(loggedin)
7039a747e4fSDavid du Colombier return senderr("already authenticated");
7049a747e4fSDavid du Colombier if(*arg == 0)
7059a747e4fSDavid du Colombier return senderr("USER requires argument");
7069a747e4fSDavid du Colombier if(setuser(arg) < 0)
7079a747e4fSDavid du Colombier return -1;
7089a747e4fSDavid du Colombier return sendok("");
7099a747e4fSDavid du Colombier }
7109a747e4fSDavid du Colombier
7119a747e4fSDavid du Colombier static void
enableaddr(void)7129a747e4fSDavid du Colombier enableaddr(void)
7139a747e4fSDavid du Colombier {
7149a747e4fSDavid du Colombier int fd;
7159a747e4fSDavid du Colombier char buf[64];
7169a747e4fSDavid du Colombier
7179a747e4fSDavid du Colombier /* hide the peer IP address under a rock in the ratifier FS */
7189a747e4fSDavid du Colombier if(peeraddr == 0 || *peeraddr == 0)
7199a747e4fSDavid du Colombier return;
7209a747e4fSDavid du Colombier
7219a747e4fSDavid du Colombier sprint(buf, "/mail/ratify/trusted/%s#32", peeraddr);
7229a747e4fSDavid du Colombier
7239a747e4fSDavid du Colombier /*
7249a747e4fSDavid du Colombier * if the address is already there and the user owns it,
7259a747e4fSDavid du Colombier * remove it and recreate it to give him a new time quanta.
7269a747e4fSDavid du Colombier */
7279a747e4fSDavid du Colombier if(access(buf, 0) >= 0 && remove(buf) < 0)
7289a747e4fSDavid du Colombier return;
7299a747e4fSDavid du Colombier
7309a747e4fSDavid du Colombier fd = create(buf, OREAD, 0666);
7319a747e4fSDavid du Colombier if(fd >= 0){
7329a747e4fSDavid du Colombier close(fd);
7339a747e4fSDavid du Colombier // syslog(0, "pop3", "ratified %s", peeraddr);
7349a747e4fSDavid du Colombier }
7359a747e4fSDavid du Colombier }
7369a747e4fSDavid du Colombier
7379a747e4fSDavid du Colombier static int
dologin(char * response)7389a747e4fSDavid du Colombier dologin(char *response)
7399a747e4fSDavid du Colombier {
7409a747e4fSDavid du Colombier AuthInfo *ai;
7419a747e4fSDavid du Colombier static int tries;
742c6569576SDavid du Colombier static ulong delaysecs = 5;
7439a747e4fSDavid du Colombier
7449a747e4fSDavid du Colombier chs->user = user;
7459a747e4fSDavid du Colombier chs->resp = response;
7469a747e4fSDavid du Colombier chs->nresp = strlen(response);
7479a747e4fSDavid du Colombier if((ai = auth_response(chs)) == nil){
748c6569576SDavid du Colombier if(tries >= 20){
7499a747e4fSDavid du Colombier senderr("authentication failed: %r; server exiting");
7509a747e4fSDavid du Colombier exits(nil);
7519a747e4fSDavid du Colombier }
752c6569576SDavid du Colombier if(++tries == 3)
753c6569576SDavid du Colombier syslog(0, "pop3", "likely password guesser from %s",
754c6569576SDavid du Colombier peeraddr);
755c6569576SDavid du Colombier delaysecs *= 2;
756c6569576SDavid du Colombier if (delaysecs > 30*60)
757c6569576SDavid du Colombier delaysecs = 30*60; /* half-hour max. */
758c6569576SDavid du Colombier sleep(delaysecs * 1000); /* prevent beating on our auth server */
7599a747e4fSDavid du Colombier return senderr("authentication failed");
7609a747e4fSDavid du Colombier }
7619a747e4fSDavid du Colombier
7629a747e4fSDavid du Colombier if(auth_chuid(ai, nil) < 0){
7639a747e4fSDavid du Colombier senderr("chuid failed: %r; server exiting");
7649a747e4fSDavid du Colombier exits(nil);
7659a747e4fSDavid du Colombier }
7669a747e4fSDavid du Colombier auth_freeAI(ai);
7679a747e4fSDavid du Colombier auth_freechal(chs);
7689a747e4fSDavid du Colombier chs = nil;
7699a747e4fSDavid du Colombier
7709a747e4fSDavid du Colombier loggedin = 1;
7719a747e4fSDavid du Colombier if(newns(user, 0) < 0){
7729a747e4fSDavid du Colombier senderr("newns failed: %r; server exiting");
7739a747e4fSDavid du Colombier exits(nil);
7749a747e4fSDavid du Colombier }
7756d1e5220SDavid du Colombier syslog(0, "pop3", "user %s logged in", user);
7769a747e4fSDavid du Colombier enableaddr();
7779a747e4fSDavid du Colombier if(readmbox(box) < 0)
7789a747e4fSDavid du Colombier exits(nil);
7799a747e4fSDavid du Colombier return sendok("mailbox is %s", box);
7809a747e4fSDavid du Colombier }
7819a747e4fSDavid du Colombier
7829a747e4fSDavid du Colombier static int
passcmd(char * arg)7839a747e4fSDavid du Colombier passcmd(char *arg)
7849a747e4fSDavid du Colombier {
7859a747e4fSDavid du Colombier DigestState *s;
7869a747e4fSDavid du Colombier uchar digest[MD5dlen];
7879a747e4fSDavid du Colombier char response[2*MD5dlen+1];
7889a747e4fSDavid du Colombier
7899a747e4fSDavid du Colombier if(passwordinclear==0 && didtls==0)
7909a747e4fSDavid du Colombier return senderr("password in the clear disallowed");
7919a747e4fSDavid du Colombier
7929a747e4fSDavid du Colombier /* use password to encode challenge */
7939a747e4fSDavid du Colombier if((chs = auth_challenge("proto=apop role=server")) == nil)
7949a747e4fSDavid du Colombier return senderr("couldn't get apop challenge");
7959a747e4fSDavid du Colombier
796c6569576SDavid du Colombier /* hash challenge with secret and convert to ascii */
7979a747e4fSDavid du Colombier s = md5((uchar*)chs->chal, chs->nchal, 0, 0);
7989a747e4fSDavid du Colombier md5((uchar*)arg, strlen(arg), digest, s);
7999a747e4fSDavid du Colombier snprint(response, sizeof response, "%.*H", MD5dlen, digest);
8009a747e4fSDavid du Colombier return dologin(response);
8019a747e4fSDavid du Colombier }
8029a747e4fSDavid du Colombier
8039a747e4fSDavid du Colombier static int
apopcmd(char * arg)8049a747e4fSDavid du Colombier apopcmd(char *arg)
8059a747e4fSDavid du Colombier {
8069a747e4fSDavid du Colombier char *resp;
8079a747e4fSDavid du Colombier
8089a747e4fSDavid du Colombier resp = nextarg(arg);
8099a747e4fSDavid du Colombier if(setuser(arg) < 0)
8109a747e4fSDavid du Colombier return -1;
8119a747e4fSDavid du Colombier return dologin(resp);
8129a747e4fSDavid du Colombier }
8139a747e4fSDavid du Colombier
814