1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include "commands.h"
4 #include "sig.h"
5 #include "getln.h"
6 #include "stralloc.h"
7 #include "substdio.h"
8 #include "alloc.h"
9 #include "open.h"
10 #include "prioq.h"
11 #include "scan.h"
12 #include "fmt.h"
13 #include "str.h"
14 #include "exit.h"
15 #include "maildir.h"
16 #include "readwrite.h"
17 #include "timeoutread.h"
18 #include "timeoutwrite.h"
19 
die()20 void die() { _exit(0); }
21 
saferead(fd,buf,len)22 int saferead(fd,buf,len) int fd; char *buf; int len;
23 {
24   int r;
25   r = timeoutread(1200,fd,buf,len);
26   if (r <= 0) die();
27   return r;
28 }
29 
safewrite(fd,buf,len)30 int safewrite(fd,buf,len) int fd; char *buf; int len;
31 {
32   int r;
33   r = timeoutwrite(1200,fd,buf,len);
34   if (r <= 0) die();
35   return r;
36 }
37 
38 char ssoutbuf[1024];
39 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
40 
41 char ssinbuf[128];
42 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
43 
put(buf,len)44 void put(buf,len) char *buf; int len;
45 {
46   substdio_put(&ssout,buf,len);
47 }
puts(s)48 void puts(s) char *s;
49 {
50   substdio_puts(&ssout,s);
51 }
flush()52 void flush()
53 {
54   substdio_flush(&ssout);
55 }
err(s)56 void err(s) char *s;
57 {
58   puts("-ERR ");
59   puts(s);
60   puts("\r\n");
61   flush();
62 }
63 
die_nomem()64 void die_nomem() { err("out of memory"); die(); }
die_nomaildir()65 void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
die_scan()66 void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
67 
err_syntax()68 void err_syntax() { err("syntax error"); }
err_unimpl(arg)69 void err_unimpl(arg) char *arg; { err("unimplemented"); }
err_deleted()70 void err_deleted() { err("already deleted"); }
err_nozero()71 void err_nozero() { err("messages are counted from 1"); }
err_toobig()72 void err_toobig() { err("not that many messages"); }
err_nosuch()73 void err_nosuch() { err("unable to open that message"); }
err_nounlink()74 void err_nounlink() { err("unable to unlink all deleted messages"); }
75 
okay(arg)76 void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
77 
printfn(fn)78 void printfn(fn) char *fn;
79 {
80   fn += 4;
81   put(fn,str_chr(fn,':'));
82 }
83 
84 char strnum[FMT_ULONG];
85 stralloc line = {0};
86 
blast(ssfrom,limit)87 void blast(ssfrom,limit)
88 substdio *ssfrom;
89 unsigned long limit;
90 {
91   int match;
92   int inheaders = 1;
93 
94   for (;;) {
95     if (getln(ssfrom,&line,&match,'\n') != 0) die();
96     if (!match && !line.len) break;
97     if (match) --line.len; /* no way to pass this info over POP */
98     if (limit) if (!inheaders) if (!--limit) break;
99     if (!line.len)
100       inheaders = 0;
101     else
102       if (line.s[0] == '.')
103         put(".",1);
104     put(line.s,line.len);
105     put("\r\n",2);
106     if (!match) break;
107   }
108   put("\r\n.\r\n",5);
109   flush();
110 }
111 
112 stralloc filenames = {0};
113 prioq pq = {0};
114 
115 struct message {
116   int flagdeleted;
117   unsigned long size;
118   char *fn;
119 } *m;
120 int numm;
121 
122 int last = 0;
123 
getlist()124 void getlist()
125 {
126   struct prioq_elt pe;
127   struct stat st;
128   int i;
129 
130   maildir_clean(&line);
131   if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
132 
133   numm = pq.p ? pq.len : 0;
134   m = (struct message *) alloc(numm * sizeof(struct message));
135   if (!m) die_nomem();
136 
137   for (i = 0;i < numm;++i) {
138     if (!prioq_min(&pq,&pe)) { numm = i; break; }
139     prioq_delmin(&pq);
140     m[i].fn = filenames.s + pe.id;
141     m[i].flagdeleted = 0;
142     if (stat(m[i].fn,&st) == -1)
143       m[i].size = 0;
144     else
145       m[i].size = st.st_size;
146   }
147 }
148 
pop3_stat(arg)149 void pop3_stat(arg) char *arg;
150 {
151   int i;
152   unsigned long total;
153 
154   total = 0;
155   for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
156   puts("+OK ");
157   put(strnum,fmt_uint(strnum,numm));
158   puts(" ");
159   put(strnum,fmt_ulong(strnum,total));
160   puts("\r\n");
161   flush();
162 }
163 
pop3_rset(arg)164 void pop3_rset(arg) char *arg;
165 {
166   int i;
167   for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
168   last = 0;
169   okay(0);
170 }
171 
pop3_last(arg)172 void pop3_last(arg) char *arg;
173 {
174   puts("+OK ");
175   put(strnum,fmt_uint(strnum,last));
176   puts("\r\n");
177   flush();
178 }
179 
pop3_quit(arg)180 void pop3_quit(arg) char *arg;
181 {
182   int i;
183   for (i = 0;i < numm;++i)
184     if (m[i].flagdeleted) {
185       if (unlink(m[i].fn) == -1) err_nounlink();
186     }
187     else
188       if (str_start(m[i].fn,"new/")) {
189 	if (!stralloc_copys(&line,"cur/")) die_nomem();
190 	if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
191 	if (!stralloc_cats(&line,":2,")) die_nomem();
192 	if (!stralloc_0(&line)) die_nomem();
193 	rename(m[i].fn,line.s); /* if it fails, bummer */
194       }
195   okay(0);
196   die();
197 }
198 
msgno(arg)199 int msgno(arg) char *arg;
200 {
201   unsigned long u;
202   if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
203   if (!u) { err_nozero(); return -1; }
204   --u;
205   if (u >= numm) { err_toobig(); return -1; }
206   if (m[u].flagdeleted) { err_deleted(); return -1; }
207   return u;
208 }
209 
pop3_dele(arg)210 void pop3_dele(arg) char *arg;
211 {
212   int i;
213   i = msgno(arg);
214   if (i == -1) return;
215   m[i].flagdeleted = 1;
216   if (i + 1 > last) last = i + 1;
217   okay(0);
218 }
219 
list(i,flaguidl)220 void list(i,flaguidl)
221 int i;
222 int flaguidl;
223 {
224   put(strnum,fmt_uint(strnum,i + 1));
225   puts(" ");
226   if (flaguidl) printfn(m[i].fn);
227   else put(strnum,fmt_ulong(strnum,m[i].size));
228   puts("\r\n");
229 }
230 
dolisting(arg,flaguidl)231 void dolisting(arg,flaguidl) char *arg; int flaguidl;
232 {
233   unsigned int i;
234   if (*arg) {
235     i = msgno(arg);
236     if (i == -1) return;
237     puts("+OK ");
238     list(i,flaguidl);
239   }
240   else {
241     okay(0);
242     for (i = 0;i < numm;++i)
243       if (!m[i].flagdeleted)
244 	list(i,flaguidl);
245     puts(".\r\n");
246   }
247   flush();
248 }
249 
pop3_uidl(arg)250 void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
pop3_list(arg)251 void pop3_list(arg) char *arg; { dolisting(arg,0); }
252 
253 substdio ssmsg; char ssmsgbuf[1024];
254 
pop3_top(arg)255 void pop3_top(arg) char *arg;
256 {
257   int i;
258   unsigned long limit;
259   int fd;
260 
261   i = msgno(arg);
262   if (i == -1) return;
263 
264   arg += scan_ulong(arg,&limit);
265   while (*arg == ' ') ++arg;
266   if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
267 
268   fd = open_read(m[i].fn);
269   if (fd == -1) { err_nosuch(); return; }
270   okay(0);
271   substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
272   blast(&ssmsg,limit);
273   close(fd);
274 }
275 
276 struct commands pop3commands[] = {
277   { "quit", pop3_quit, 0 }
278 , { "stat", pop3_stat, 0 }
279 , { "list", pop3_list, 0 }
280 , { "uidl", pop3_uidl, 0 }
281 , { "dele", pop3_dele, 0 }
282 , { "retr", pop3_top, 0 }
283 , { "rset", pop3_rset, 0 }
284 , { "last", pop3_last, 0 }
285 , { "top", pop3_top, 0 }
286 , { "noop", okay, 0 }
287 , { 0, err_unimpl, 0 }
288 } ;
289 
main(argc,argv)290 int main(argc,argv)
291 int argc;
292 char **argv;
293 {
294   sig_alarmcatch(die);
295   sig_pipeignore();
296 
297   if (!argv[1]) die_nomaildir();
298   if (chdir(argv[1]) == -1) die_nomaildir();
299 
300   getlist();
301 
302   okay(0);
303   commands(&ssin,pop3commands);
304   die();
305 }
306