|
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 } |