chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[qmail] / qmail-pop3d.c
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
20 void die() { _exit(0); }
21
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
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
44 void put(buf,len) char *buf; int len;
45 {
46   substdio_put(&ssout,buf,len);
47 }
48 void puts(s) char *s;
49 {
50   substdio_puts(&ssout,s);
51 }
52 void flush()
53 {
54   substdio_flush(&ssout);
55 }
56 void err(s) char *s;
57 {
58   puts("-ERR ");
59   puts(s);
60   puts("\r\n");
61   flush();
62 }
63
64 void die_nomem() { err("out of memory"); die(); }
65 void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
66 void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
67
68 void err_syntax() { err("syntax error"); }
69 void err_unimpl() { err("unimplemented"); }
70 void err_deleted() { err("already deleted"); }
71 void err_nozero() { err("messages are counted from 1"); }
72 void err_toobig() { err("not that many messages"); }
73 void err_nosuch() { err("unable to open that message"); }
74 void err_nounlink() { err("unable to unlink all deleted messages"); }
75
76 void okay() { puts("+OK \r\n"); flush(); }
77
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
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
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
149 void pop3_stat()
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
164 void pop3_rset()
165 {
166   int i;
167   for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
168   last = 0;
169   okay();
170 }
171
172 void pop3_last()
173 {
174   puts("+OK ");
175   put(strnum,fmt_uint(strnum,last));
176   puts("\r\n");
177   flush();
178 }
179
180 void pop3_quit()
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();
196   die();
197 }
198
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
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();
218 }
219
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
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();
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
250 void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
251 void pop3_list(arg) char *arg; { dolisting(arg,0); }
252
253 substdio ssmsg; char ssmsgbuf[1024];
254
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();
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
290 void 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();
303   commands(&ssin,pop3commands);
304   die();
305 }