debian/rules: Use `git' potty wrapper.
[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 }