1 static const char rcsid[] = "$Id: pop3d.c,v 1.25 2005/05/06 12:53:33 dengxf Exp $";
2
3 /*
4 * Copyright (c) 2003-2004 Deng XueFeng <dsnofe@hotmail.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Deng XueFeng.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY DENG XUEFENG AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35
36 #include <dirent.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <netinet/in.h>
44 #include <sys/mman.h>
45 #include <sys/stat.h>
46 #include <sys/event.h>
47 #include <sys/socket.h>
48 #include <sys/uio.h>
49 #include <sys/types.h>
50 #include <arpa/inet.h>
51
52 #include "pop3d.h"
53 #include "const.h"
54 #include "auth.h"
55 #include "plcat.h"
56 #include "mylog.h"
57
58 #ifdef EV_SET
59 /* If event.h is old than 1.22, EV_SET will do the wrong things */
60 #undef EV_SET
61 #define EV_SET(kevp_, a, b, c, d, e, f) do { \
62 struct kevent *kevp = (kevp_); \
63 (kevp)->ident = (a); \
64 (kevp)->filter = (b); \
65 (kevp)->flags = (c); \
66 (kevp)->fflags = (d); \
67 (kevp)->data = (e); \
68 (kevp)->udata = (f); \
69 } while(0)
70
71 #endif
72 #define Free(x) if (x != NULL) {free(x);x = NULL;}
73
74 //#define DEF_MPS_DEBUG 1
75
76 static int kq = 0;
77 static int tsession = 0;
78 static int online = 0;
79 static int used_afds = 0;
80 static int all_cudb = 128;
81 static int used_audb = 0;
82 static int used_cudb = 0;
83 static struct kevent *afds = NULL;
84 static struct kevent *aevs = NULL;
85 static UDB *audb = NULL;
86 static UDB **cudb = NULL;
87
88 extern int multi_process;
89 extern int max_errs;
90 extern int max_client;
91 extern int max_timeout;
92 extern int pop3d_fd;
93
kq_init(void)94 static int kq_init(void)
95 {
96 kq = kqueue();
97 if (kq == -1) {
98 INFO("[-ERR]: kq_init: kqueue(2): %s\n", strerror(errno));
99 return (-1);
100 }
101 afds = malloc(max_client * sizeof(struct kevent));
102 aevs = malloc(max_client * sizeof(struct kevent));
103 audb = malloc(max_client * sizeof(UDB));
104 cudb = malloc(all_cudb * sizeof(UDB *));
105 if ((afds == NULL) || (aevs == NULL) || (audb == NULL) || (cudb == NULL)) {
106 INFO("[-ERR]: kq_init: malloc(3): %s\n", strerror(errno));
107 return (-1);
108 }
109 memset(afds, 0, max_client * sizeof(struct kevent));
110 memset(aevs, 0, max_client * sizeof(struct kevent));
111 memset(audb, 0, max_client * sizeof(UDB));
112 memset(cudb, 0, all_cudb * sizeof(UDB *));
113 return (0);
114 }
115
exitfree(void)116 static void exitfree(void)
117 {
118 Free(afds);
119 Free(aevs);
120 Free(audb);
121 Free(cudb);
122 }
123
update_udb(UDB * const addr)124 static void update_udb(UDB *const addr)
125 {
126 UDB **p;
127
128 if (used_cudb == all_cudb) {
129 all_cudb <<= 1;
130 p = realloc(cudb, all_cudb * sizeof(UDB *));
131 if (p == NULL) {
132 INFO("[-ERR]: update_udb: realloc(3): %s\n", strerror(errno));
133 exitfree();
134 _exit(EXIT_FAILURE);
135 }
136 cudb = p;
137 }
138 *(cudb + used_cudb++) = addr;
139 }
140
Write(const int fd,const void * const buf,const size_t n)141 static ssize_t Write(const int fd, const void *const buf, const size_t n)
142 {
143 ssize_t nwritten;
144
145 nwritten = write(fd, buf, n);
146 if (nwritten != (int)n) {
147 INFO("[-ERR]: Write: write(2): %s\n", strerror(errno));
148 return (-1);
149 }
150 return (nwritten);
151 }
152
Awrite(const int fd,const void * const buf,const size_t n)153 static ssize_t Awrite(const int fd, const void *const buf, const size_t n)
154 {
155 ssize_t nleft = n;
156 ssize_t nwritten;
157 const char *ptr = buf;
158
159 while (nleft > 0) {
160 if ((nwritten = write(fd, ptr, nleft)) <= 0) {
161 INFO("[-ERR]: Awrite: write(2): %s\n", strerror(errno));
162 return (-1);
163 }
164 nleft -= nwritten;
165 ptr += nwritten;
166 }
167 return (n);
168 }
169
myfree(const struct kevent * const kdp)170 static void myfree(const struct kevent *const kdp)
171 {
172 UDB *t_udb = kdp->udata;
173
174 #ifdef DEF_MPS_DEBUG
175 INFO("[DEBUG]: myfree() flag = %d refer = %d\n", t_udb->flag, t_udb->refer);
176 #endif
177 t_udb->cmdlock = DEF_CMD_FREE;
178 if (t_udb->flag == DEF_UDB_USED) {
179 Free(t_udb->c_all);
180 Free(t_udb->c_updated);
181 } else if (t_udb->flag == DEF_UDB_INIT) {
182 INFO("[EXIT]: myfree() flag = %d refer = %d\n", t_udb->flag, t_udb->refer);
183 _exit(EXIT_FAILURE);
184 }
185 t_udb->flag = DEF_UDB_FREE;
186 if (t_udb->refer > 0)
187 return;
188 close(kdp->ident);
189 t_udb->flag = DEF_UDB_INIT;
190 update_udb(t_udb);
191 online--;
192 INFO("[INFO]: Disconnect %s.%u\n",
193 inet_ntoa(t_udb->address.sin_addr), htons(t_udb->address.sin_port));
194 }
195
check_errs(const struct kevent * const kdp)196 static void check_errs(const struct kevent *const kdp)
197 {
198 UDB *t_udb = kdp->udata;
199
200 if (t_udb->curerrs >= max_errs) {
201 INFO("[-ERR]: Disconnect %s.%u : Too Many Errors\n",
202 inet_ntoa(t_udb->address.sin_addr), htons(t_udb->address.sin_port));
203 Awrite(kdp->ident, msg_max_err, sizeof(msg_max_err) - 1);
204 myfree(kdp);
205 } else {
206 EV_SET(&afds[used_afds++], kdp->ident, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, t_udb);
207 EV_SET(&afds[used_afds++], kdp->ident, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, max_timeout, t_udb);
208 t_udb->cmdlock = DEF_CMD_FREE;
209 t_udb->refer += 2;
210 }
211 }
212
wel_cmd(const struct kevent * const kdp)213 static void wel_cmd(const struct kevent *const kdp)
214 {
215 int n;
216 UDB *t_udb = kdp->udata;
217
218 n = Awrite(kdp->ident, msg_wel, sizeof(msg_wel) - 1);
219 if (n == -1) {
220 myfree(kdp);
221 return;
222 }
223 t_udb->welmsg = 1;
224 check_errs(kdp);
225 }
226
unknown_cmd(const struct kevent * const kdp)227 static void unknown_cmd(const struct kevent *const kdp)
228 {
229 int n;
230 UDB *t_udb = kdp->udata;
231
232 n = Awrite(kdp->ident, msg_unknown, sizeof(msg_unknown) - 1);
233 if (n == -1) {
234 myfree(kdp);
235 return;
236 }
237 t_udb->curerrs++;
238 t_udb->cmd = 0;
239 t_udb->cmderr = 0;
240 check_errs(kdp);
241 }
242
user_cmd(const struct kevent * const kdp)243 static void user_cmd(const struct kevent *const kdp)
244 {
245 int n;
246 UDB *t_udb = kdp->udata;
247
248 if (t_udb->cmderr != 0) {
249 n = Awrite(kdp->ident, msg_user_err, sizeof(msg_user_err) - 1);
250 t_udb->curerrs++;
251 } else
252 n = Awrite(kdp->ident, msg_user_ok, sizeof(msg_user_ok) - 1);
253 if (n == -1) {
254 myfree(kdp);
255 return;
256 }
257 t_udb->cmd = 0;
258 t_udb->cmderr = 0;
259 check_errs(kdp);
260 }
261
pass_cmd(const struct kevent * const kdp)262 static void pass_cmd(const struct kevent *const kdp)
263 {
264 int n;
265 UDB *t_udb = kdp->udata;
266
267 if (t_udb->cmderr == 0)
268 n = Awrite(kdp->ident, msg_pass_ok, sizeof(msg_pass_ok) - 1);
269 else if (t_udb->cmderr == 1) {
270 n = Awrite(kdp->ident, msg_pass_no_user, sizeof(msg_pass_no_user) - 1);
271 t_udb->curerrs++;
272 } else if (t_udb->cmderr == 2) {
273 n = Awrite(kdp->ident, msg_pass_no_pass, sizeof(msg_pass_no_pass) - 1);
274 t_udb->curerrs++;
275 } else {
276 n = Awrite(kdp->ident, msg_pass_err, sizeof(msg_pass_err) - 1);
277 t_udb->curerrs++;
278 }
279 if (n == -1) {
280 myfree(kdp);
281 return;
282 }
283 t_udb->cmd = 0;
284 t_udb->cmderr = 0;
285 check_errs(kdp);
286 }
287
noop_cmd(const struct kevent * const kdp)288 static void noop_cmd(const struct kevent *const kdp)
289 {
290 int n;
291 UDB *t_udb = kdp->udata;
292
293 n = Awrite(kdp->ident, msg_noop, sizeof(msg_noop) - 1);
294 if (n == -1) {
295 myfree(kdp);
296 return;
297 }
298 t_udb->cmd = 0;
299 t_udb->cmderr = 0;
300 check_errs(kdp);
301 }
302
quit_cmd(const struct kevent * const kdp)303 static void quit_cmd(const struct kevent *const kdp)
304 {
305 Awrite(kdp->ident, msg_quit, sizeof(msg_quit) - 1);
306 myfree(kdp);
307 }
308
list_cmd(const struct kevent * const kdp)309 static void list_cmd(const struct kevent *const kdp)
310 {
311 int i = 0;
312 int n;
313 int usiz;
314 int tmp;
315 char outbuf[1024];
316 UDB *t_udb = kdp->udata;
317 MINFO *t_cache = t_udb->c_all;
318 struct iovec iov[2];
319
320 tmp = t_udb->a_id;
321 if (tmp == -1) {
322 n = Awrite(kdp->ident, msg_id_err, sizeof(msg_id_err) - 1);
323 t_udb->cmderr++;
324 } else if (tmp > t_udb->maxid) {
325 n = Awrite(kdp->ident, msg_id_no, sizeof(msg_id_no) - 1);
326 t_udb->cmderr++;
327 } else if (t_udb->maxid == 0) {
328 n = Awrite(kdp->ident, msg_list_no, sizeof(msg_list_no) - 1);
329 t_udb->curerrs++;
330 } else if (tmp == 0) {
331 strlcpy(outbuf, msg_list_all, sizeof(outbuf));
332 usiz = sizeof(msg_list_all) - 1;
333 while (i < t_udb->maxid) {
334 if ((*(t_cache+i)).f_del != 1)
335 usiz = plcat(outbuf, usiz, sizeof(outbuf), "%u %u\r\n", i + 1, (*(t_cache+i)).size);
336 if (usiz < 0) {
337 n = Awrite(kdp->ident, outbuf, -usiz);
338 if (n == -1) {
339 myfree(kdp);
340 return;
341 }
342 usiz = 0;
343 i--;
344 }
345 i++;
346 }
347 iov[0].iov_base = outbuf;
348 iov[0].iov_len = usiz;
349 iov[1].iov_base = (char *)crlfend;
350 iov[1].iov_len = sizeof(crlfend) - 1;
351 n = writev(kdp->ident, (struct iovec *) &iov, 2);
352 } else if ((*(t_cache+tmp-1)).f_del == 1) {
353 usiz = plcat(outbuf, 0, sizeof(outbuf), "-ERR message %u is marked as deleted\r\n", tmp);
354 n = Awrite(kdp->ident, outbuf, usiz);
355 t_udb->cmderr++;
356 } else {
357 usiz = plcat(outbuf, 0, sizeof(outbuf), "+OK %u %u\r\n", tmp, (*(t_cache+tmp-1)).size);
358 n = Awrite(kdp->ident, outbuf, usiz);
359 }
360 if (n == -1) {
361 myfree(kdp);
362 return;
363 }
364 t_udb->cmd = 0;
365 t_udb->cmderr = 0;
366 check_errs(kdp);
367 }
368
uidl_cmd(const struct kevent * const kdp)369 static void uidl_cmd(const struct kevent *const kdp)
370 {
371 int i = 0;
372 int n;
373 int usiz;
374 int tmp;
375 char outbuf[1024];
376 UDB *t_udb = kdp->udata;
377 MINFO *t_cache = t_udb->c_all;
378 struct iovec iov[2];
379
380 tmp = t_udb->a_id;
381 if (tmp == -1) {
382 n = Awrite(kdp->ident, msg_id_err, sizeof(msg_id_err) - 1);
383 t_udb->cmderr++;
384 } else if (tmp > t_udb->maxid) {
385 n = Awrite(kdp->ident, msg_id_no, sizeof(msg_id_no) - 1);
386 t_udb->cmderr++;
387 } else if (t_udb->maxid == 0) {
388 n = Awrite(kdp->ident, msg_uidl_no, sizeof(msg_uidl_no) - 1);
389 t_udb->curerrs++;
390 } else if (tmp == 0) {
391 strlcpy(outbuf, msg_ok, sizeof(outbuf));
392 usiz = sizeof(msg_ok) - 1;
393 while (i < t_udb->maxid) {
394 if ((*(t_cache+i)).f_del != 1)
395 usiz = plcat(outbuf, usiz, sizeof(outbuf), "%u %s\r\n", i + 1, (*(t_cache+i)).name);
396 if (usiz < 0) {
397 n = Awrite(kdp->ident, outbuf, -usiz);
398 if (n == -1) {
399 myfree(kdp);
400 return;
401 }
402 usiz = 0;
403 i--;
404 }
405 i++;
406 }
407 iov[0].iov_base = outbuf;
408 iov[0].iov_len = usiz;
409 iov[1].iov_base = (char *) crlfend;
410 iov[1].iov_len = sizeof(crlfend) - 1;
411 n = writev(kdp->ident, (struct iovec *) &iov, 2);
412 } else if ((*(t_cache+tmp-1)).f_del == 1) {
413 usiz = plcat(outbuf, 0, sizeof(outbuf), "-ERR message %u is marked as deleted\r\n", tmp);
414 n = Awrite(kdp->ident, outbuf, usiz);
415 t_udb->cmderr++;
416 } else {
417 usiz = plcat(outbuf, 0, sizeof(outbuf), "+OK %u %s\r\n", tmp, (*(t_cache+tmp-1)).name);
418 n = Awrite(kdp->ident, outbuf, usiz);
419 }
420 if (n == -1) {
421 myfree(kdp);
422 return;
423 }
424 t_udb->cmd = 0;
425 t_udb->cmderr = 0;
426 check_errs(kdp);
427 }
428
cache_update(UDB * const t_udb,MINFO * const id_updated)429 static int cache_update(UDB *const t_udb, MINFO *const id_updated)
430 {
431 int i = 0;
432 MINFO **p_updated;
433
434 if (t_udb->upid == t_udb->ucsize) {
435 t_udb->ucsize += 16;
436 p_updated = realloc(t_udb->c_updated, t_udb->ucsize * sizeof(MINFO *));
437 if (p_updated == NULL) {
438 INFO("[-ERR]: cache_update: realloc(3): %s\n", strerror(errno));
439 return (-1);
440 }
441 memset(p_updated+t_udb->upid, 0, 16 * sizeof(MINFO *));
442 t_udb->c_updated = p_updated;
443 }
444 p_updated = t_udb->c_updated;
445 while (i < t_udb->upid) {
446 if (*(p_updated+i) == id_updated) /* Already Marked */
447 return (0);
448 i++;
449 }
450 *(p_updated + t_udb->upid++) = id_updated;
451 return (0);
452 }
453
dele_cmd(const struct kevent * const kdp)454 static void dele_cmd(const struct kevent *const kdp)
455 {
456 int n;
457 int tmp;
458 char outbuf[256];
459 UDB *t_udb = kdp->udata;
460 MINFO *t_cache = t_udb->c_all;
461
462 tmp = t_udb->a_id;
463 if (tmp == -1) {
464 n = Awrite(kdp->ident, msg_id_err, sizeof(msg_id_err) - 1);
465 t_udb->cmderr++;
466 } else if ((tmp > t_udb->maxid) || (t_udb->maxid == 0)) {
467 n = Awrite(kdp->ident, msg_id_no, sizeof(msg_id_no) - 1);
468 t_udb->cmderr++;
469 } else if (tmp == 0) {
470 n = Awrite(kdp->ident, msg_no_arg, sizeof(msg_no_arg) - 1);
471 t_udb->cmderr++;
472 } else if ((*(t_cache+tmp-1)).f_del == 1) {
473 n = plcat(outbuf, 0, sizeof(outbuf), "-ERR message %u already marked as deleted\r\n", tmp);
474 n = Awrite(kdp->ident, outbuf, n);
475 t_udb->cmderr++;
476 } else {
477 (*(t_cache+tmp-1)).f_del = 1;
478 n = cache_update(t_udb, t_cache+tmp-1);
479 if (n == -1) {
480 Awrite(kdp->ident, msg_dele_err, sizeof(msg_dele_err) - 1);
481 myfree(kdp);
482 return;
483 }
484 n = plcat(outbuf, 0, sizeof(outbuf), "+OK message %u marked as deleted\r\n", tmp);
485 n = Awrite(kdp->ident, outbuf, n);
486 }
487 if (n == -1) {
488 myfree(kdp);
489 return;
490 }
491 t_udb->cmd = 0;
492 t_udb->cmderr = 0;
493 check_errs(kdp);
494 }
495
rset_cmd(const struct kevent * const kdp)496 static void rset_cmd(const struct kevent *const kdp)
497 {
498 int i = 0;
499 int n;
500 UDB *t_udb = kdp->udata;
501 MINFO **p_updated;
502
503 if (t_udb->c_updated != NULL) {
504 p_updated = t_udb->c_updated;
505 while (i < t_udb->upid) {
506 (*(p_updated+i))->f_read = 0;
507 (*(p_updated+i))->f_del = 0;
508 i++;
509 }
510 free(t_udb->c_updated);
511 t_udb->c_updated = NULL;
512 }
513 t_udb->ucsize = 0;
514 t_udb->upid = 0;
515 n = Awrite(kdp->ident, msg_rset, sizeof(msg_rset) - 1);
516 if (n == -1) {
517 myfree(kdp);
518 return;
519 }
520 t_udb->cmd = 0;
521 t_udb->cmderr = 0;
522 check_errs(kdp);
523 }
524
stat_cmd(const struct kevent * const kdp)525 static void stat_cmd(const struct kevent *const kdp)
526 {
527 int i;
528 int curmail = 0;
529 int cursize = 0;
530 char outbuf[256];
531 UDB *t_udb = kdp->udata;
532 MINFO *t_cache = t_udb->c_all;
533
534 if (t_udb->maxid != 0) {
535 for (i = 0; i < t_udb->maxid; i++) {
536 if ((*(t_cache+i)).f_del != 1) {
537 cursize += (*(t_cache+i)).size;
538 curmail++;
539 }
540 }
541 }
542 i = plcat(outbuf, 0, sizeof(outbuf), "+OK %u %u\r\n", curmail, cursize);
543 i = Awrite(kdp->ident, outbuf, i);
544 if (i == -1) {
545 myfree(kdp);
546 return;
547 }
548 t_udb->cmd = 0;
549 t_udb->cmderr = 0;
550 check_errs(kdp);
551 }
552
retr_cmd(const struct kevent * const kdp)553 static void retr_cmd(const struct kevent *const kdp)
554 {
555 int n;
556 int tmp;
557 int ofd;
558 int send_siz;
559 int mail_siz;
560 char *psend;
561 char *mail;
562 char sendbuf[def_send_psiz];
563 char tmpbuf[256];
564 UDB *t_udb = kdp->udata;
565 MINFO *t_cache = t_udb->c_all;
566
567 tmp = t_udb->a_id;
568 send_siz = (kdp->data > def_send_psiz) ? def_send_psiz : kdp->data;
569 if (tmp == -1) {
570 n = Awrite(kdp->ident, msg_id_err, sizeof(msg_id_err) - 1);
571 t_udb->cmderr++;
572 t_udb->cmd = 0;
573 } else if (tmp > t_udb->maxid) {
574 n = Awrite(kdp->ident, msg_id_no, sizeof(msg_id_no) - 1);
575 t_udb->cmderr++;
576 t_udb->cmd = 0;
577 } else if (tmp == 0) {
578 n = Awrite(kdp->ident, msg_no_arg, sizeof(msg_no_arg) - 1);
579 t_udb->cmderr++;
580 t_udb->cmd = 0;
581 } else if ((*(t_cache+tmp-1)).f_del == 1) {
582 n = plcat(tmpbuf, 0, sizeof(tmpbuf), "-ERR message %u already marked as deleted\r\n", tmp);
583 n = Awrite(kdp->ident, tmpbuf, n);
584 t_udb->cmderr++;
585 t_udb->cmd = 0;
586 } else {
587 (*(t_cache+tmp-1)).f_read = 1;
588 n = cache_update(t_udb, t_cache+tmp-1);
589 if (n == -1) {
590 myfree(kdp);
591 return;
592 }
593 if ((t_udb->offset == 0L) && (t_udb->mail == NULL)){
594 n = plcat(tmpbuf, 0, sizeof(tmpbuf), "+OK %u octets\r\n", (*(t_cache+tmp-1)).size);
595 n = Awrite(kdp->ident, tmpbuf, n);
596 if (n == -1) {
597 myfree(kdp);
598 return;
599 }
600 }
601 if (t_udb->mail == NULL) {
602 if ((*(t_cache+tmp-1)).f_new == 1)
603 plcat(tmpbuf, 0, sizeof(tmpbuf), "%s/new/%s", t_udb->maildir, (*(t_cache+tmp-1)).name);
604 else
605 plcat(tmpbuf, 0, sizeof(tmpbuf), "%s/cur/%s", t_udb->maildir, (*(t_cache+tmp-1)).name);
606 ofd = open(tmpbuf, O_RDONLY | O_NONBLOCK | O_DIRECT);
607 if (ofd < 0) {
608 INFO("[-ERR]: retr_cmd: open(2) %s: %s\n", tmpbuf, strerror(errno));
609 Awrite(kdp->ident, msg_open_err, sizeof(msg_open_err) - 1);
610 myfree(kdp);
611 return;
612 }
613 t_udb->mail = mmap(0, (*(t_cache+tmp-1)).size, PROT_READ, MAP_PRIVATE, ofd, 0L);
614 close(ofd);
615 if (t_udb->mail == MAP_FAILED) {
616 INFO("[-ERR]: retr_cmd: mmap(2): %s\n", strerror(errno));
617 Awrite(kdp->ident, msg_mmap_err, sizeof(msg_mmap_err) - 1);
618 myfree(kdp);
619 return;
620 }
621 }
622 psend = sendbuf;
623 mail = t_udb->mail + t_udb->offset;
624 mail_siz = (*(t_cache+tmp-1)).size;
625 while ((psend - sendbuf + 1) <= send_siz - 1) {
626 if (mail >= t_udb->mail + mail_siz)
627 break;
628 if (*mail == '\n') {
629 *psend++ = '\r';
630 *psend++ = '\n';
631 } else if ((*mail == '.') && (*(mail-1) == '\n')) {
632 *psend++ = '.';
633 *psend++ = '.';
634 } else
635 *psend++ = *mail;
636 mail++;
637 }
638 if (((psend - sendbuf) < send_siz) && (mail < t_udb->mail + mail_siz)) {
639 if (*mail == '\n')
640 *psend = '\0';
641 else if ((*mail == '.') && (*(mail-1) == '\n'))
642 *psend = '\0';
643 else
644 *psend++ = *mail;
645 mail++;
646 }
647 n = Write(kdp->ident, sendbuf, psend - sendbuf);
648 if (n == -1) {
649 myfree(kdp);
650 return;
651 }
652 t_udb->offset = mail - t_udb->mail;
653 if (t_udb->offset == mail_siz) {
654 n = Awrite(kdp->ident, crlfend, sizeof(crlfend) - 1);
655 n = munmap(t_udb->mail, mail_siz);
656 if (n == -1) {
657 INFO("[-ERR]: retr_cmd: munmap(2): %s\n", strerror(errno));
658 Awrite(kdp->ident, msg_munmap_err, sizeof(msg_munmap_err) - 1);
659 myfree(kdp);
660 return;
661 }
662 t_udb->cmd = 0;
663 t_udb->mail = NULL;
664 t_udb->offset = 0L;
665 }
666 }
667 if (n == -1) {
668 myfree(kdp);
669 return;
670 }
671 if (t_udb->cmd == 0) {
672 t_udb->cmderr = 0;
673 check_errs(kdp);
674 } else {
675 EV_SET(&afds[used_afds++], kdp->ident, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, t_udb);
676 t_udb->refer++;
677 }
678 }
679
exit_cmd(const struct kevent * const kdp)680 static void exit_cmd(const struct kevent *const kdp)
681 {
682 int i;
683 int res;
684 char fdir[256];
685 char tdir[256];
686 UDB *t_udb = kdp->udata;
687 MINFO **p_updated;
688
689 if (t_udb->ucsize != 0) {
690 p_updated = t_udb->c_updated;
691 for (i=0; i<t_udb->upid; i++) {
692 if ((*(p_updated+i))->f_del == 1) {
693 if ((*(p_updated+i))->f_new == 1)
694 plcat(fdir, 0, sizeof(fdir), "%s/new/%s", t_udb->maildir, (*(p_updated+i))->name);
695 else
696 plcat(fdir, 0, sizeof(fdir), "%s/cur/%s", t_udb->maildir, (*(p_updated+i))->name);
697 res = unlink(fdir);
698 if (res != 0) {
699 INFO("[-ERR]: exit_cmd: unlink(2) %s: %s\n", fdir, strerror(errno));
700 Awrite(kdp->ident, msg_unlink_err, sizeof(msg_unlink_err) - 1);
701 myfree(kdp);
702 return;
703 }
704 } else if ((*(p_updated+i))->f_read == 1) {
705 if ((*(p_updated+i))->f_new == 1) {
706 plcat(fdir, 0, sizeof(fdir), "%s/new/%s", t_udb->maildir, (*(p_updated+i))->name);
707 plcat(tdir, 0, sizeof(tdir), "%s/cur/%s", t_udb->maildir, (*(p_updated+i))->name);
708 res = rename(fdir, tdir);
709 if (res != 0) {
710 INFO("[-ERR]: exit_cmd: rename(2) %s => %s: %s\n", fdir, tdir, strerror(errno));
711 Awrite(kdp->ident, msg_rename_err, sizeof(msg_rename_err) - 1);
712 myfree(kdp);
713 return;
714 }
715 }
716 }
717 }
718 }
719 i = Awrite(kdp->ident, msg_quit, sizeof(msg_quit) - 1);
720 if (i == -1) {
721 myfree(kdp);
722 return;
723 }
724 myfree(kdp);
725 }
726
tmperr_cmd(const struct kevent * const kdp)727 static void tmperr_cmd(const struct kevent *const kdp)
728 {
729 int n;
730 UDB *t_udb = kdp->udata;
731
732 n = Awrite(kdp->ident, msg_opendir_err, sizeof(msg_opendir_err) - 1);
733 if (n == -1) {
734 myfree(kdp);
735 return;
736 }
737 t_udb->cmd = 0;
738 t_udb->cmderr = 0;
739 check_errs(kdp);
740 }
741
cmd_write(const struct kevent * const kdp)742 static void cmd_write(const struct kevent *const kdp)
743 {
744 UDB *w_udb = kdp->udata;
745
746 w_udb->refer--;
747 if (w_udb->flag == DEF_UDB_FREE) {
748 myfree(kdp);
749 return;
750 }
751 if (w_udb->welmsg == 0)
752 wel_cmd(kdp);
753 else if (w_udb->cmd == DEF_CMDNAME_UNKNOWN)
754 unknown_cmd(kdp);
755 else if (w_udb->cmd == DEF_CMDNAME_USER)
756 user_cmd(kdp);
757 else if (w_udb->cmd == DEF_CMDNAME_PASS)
758 pass_cmd(kdp);
759 else if (w_udb->cmd == DEF_CMDNAME_QUIT)
760 quit_cmd(kdp);
761 else if (w_udb->cmd == DEF_CMDNAME_RSET)
762 rset_cmd(kdp);
763 else if (w_udb->cmd == DEF_CMDNAME_NOOP)
764 noop_cmd(kdp);
765 else if (w_udb->cmd == DEF_CMDNAME_EXIT)
766 exit_cmd(kdp);
767 else if (w_udb->cache_status == DEF_CACHE_UNDONE)
768 tmperr_cmd(kdp);
769 else if (w_udb->cmd == DEF_CMDNAME_LIST)
770 list_cmd(kdp);
771 else if (w_udb->cmd == DEF_CMDNAME_UIDL)
772 uidl_cmd(kdp);
773 else if (w_udb->cmd == DEF_CMDNAME_DELE)
774 dele_cmd(kdp);
775 else if (w_udb->cmd == DEF_CMDNAME_RETR)
776 retr_cmd(kdp);
777 else if (w_udb->cmd == DEF_CMDNAME_STAT)
778 stat_cmd(kdp);
779 else
780 unknown_cmd(kdp);
781 }
782
upch(char * const str)783 static char *upch(char *const str)
784 {
785 char *tmp;
786
787 tmp = str;
788 while(*tmp != '\0') {
789 if ((*tmp > 'z') || (*tmp < 'A'))
790 return (NULL);
791 else if ((*tmp > 'Z') && (*tmp < 'a'))
792 return (NULL);
793 else if ((*tmp >= 'a') && (*tmp <= 'z'))
794 *tmp -= 32;
795 tmp++;
796 }
797 return (str);
798 }
799
aldigit(char * const data,const int maxn)800 static int aldigit(char *const data, const int maxn)
801 {
802 int i;
803 char *t;
804
805 if (*data == '\0')
806 return (0);
807 t = data;
808 while (*t != '\0') {
809 if ((*t < '0') || (*t > '9'))
810 return (-1);
811 t++;
812 }
813 i = atoi(data);
814 return (i == 0) ? (maxn + 3) : (i);
815 }
816
cmd_parse(CMDTBL * const p_cmd,char * cmdbuf)817 static int cmd_parse(CMDTBL *const p_cmd, char *cmdbuf)
818 {
819 int flags;
820 int values;
821 char *tmp;
822
823 tmp = cmdbuf + 4;
824 if (*tmp != '\r' && *tmp != ' ')
825 return (0);
826 *tmp++ = '\0';
827 cmdbuf = upch(cmdbuf);
828 if (cmdbuf == NULL)
829 return (0);
830 strcpy(p_cmd->name, cmdbuf);
831 if (*tmp == '\n')
832 return (1);
833 flags = 0;
834 values = 0;
835 while(*tmp != '\0') {
836 if ((*tmp == '\r') && (*(tmp + 1) == '\n')) {
837 *tmp = '\0';
838 flags = 1;
839 break;
840 } else if ((values == 0) && (*tmp != ' '))
841 values = 1;
842 tmp++;
843 }
844 if (flags == 0)
845 return (-1);
846 if (values == 1) {
847 strcpy(p_cmd->value, cmdbuf + 5);
848 return (2);
849 } else
850 return (1);
851 }
852
md_cache(const char * dir,int siz,const int new,UDB * const m_udb)853 static int md_cache(const char *dir, int siz, const int new, UDB *const m_udb)
854 {
855 int id;
856 char tdir[256];
857 struct dirent *dp;
858 struct stat stat_buf;
859 DIR *dirp;
860 MINFO *p;
861
862 if (m_udb->c_all == NULL) {
863 p = malloc(siz * sizeof(MINFO));
864 if (p == NULL) {
865 INFO("[-ERR]: md_cache: malloc(3): %s\n", strerror(errno));
866 return (-1);
867 }
868 m_udb->c_all = p;
869 memset(p, 0, siz * sizeof(MINFO));
870 }
871 if ((dirp = opendir(dir)) == NULL) {
872 INFO("[-ERR]: md_cache: opendir(2) %s: %s\n", dir, strerror(errno));
873 return (-1);
874 }
875 p = m_udb->c_all;
876 id = m_udb->maxid;
877 while ((dp = readdir(dirp)) != NULL) {
878 if (id == (siz - 1)) {
879 siz <<= 1;
880 p = realloc(m_udb->c_all, siz * sizeof(MINFO));
881 if (p == NULL) {
882 INFO("[-ERR]: md_cache: realloc(3): %s\n", strerror(errno));
883 closedir(dirp);
884 return (-1);
885 }
886 m_udb->c_all = p;
887 }
888 if (((strcmp(".", dp->d_name)) == 0) || ((strcmp("..", dp->d_name)) == 0))
889 continue;
890 plcat(tdir, 0, sizeof(tdir), "%s/%s", dir, dp->d_name);
891 if ((stat(tdir, &stat_buf)) == -1) {
892 INFO("[-ERR]: md_cache: stat(2) %s: %s\n", tdir, strerror(errno));
893 closedir(dirp);
894 return (-1);
895 }
896 (*(p+id)).id = id + 1;
897 (*(p+id)).f_del = 0;
898 (*(p+id)).f_read = 0;
899 (*(p+id)).f_new = new;
900 strlcpy((*(p+id)).name, dp->d_name, sizeof(p->name));
901 (*(p+id)).size = stat_buf.st_size;
902 id++;
903 }
904 m_udb->maxid = id;
905 if ((closedir(dirp)) != 0) {
906 INFO("[-ERR]: md_cache: closedir(3) %s: %s\n", dirp, strerror(errno));
907 return (-1);
908 }
909 return (siz);
910 }
911
init_cache(UDB * const g_udb)912 static void init_cache(UDB *const g_udb)
913 {
914 int res;
915 char tmpdir[256];
916
917 plcat(tmpdir, 0, sizeof(tmpdir), "%s/new", g_udb->maildir);
918 res = md_cache(tmpdir, 32, 1, g_udb);
919 if (res == -1)
920 return;
921 plcat(tmpdir, 0, sizeof(tmpdir), "%s/cur", g_udb->maildir);
922 res = md_cache(tmpdir, res, 0, g_udb);
923 if (res == -1)
924 return;
925 g_udb->cache_status = DEF_CACHE_DONE;
926 }
927
cmd_read(const struct kevent * const kdp)928 static void cmd_read(const struct kevent *const kdp)
929 {
930 int n;
931 char inbuf[256];
932 UDB *r_udb = kdp->udata;
933 CMDTBL r_cmd;
934
935 EV_SET(&afds[used_afds++], kdp->ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
936 r_udb->cmdlock = DEF_CMD_LOCK;
937 r_udb->refer -= 2;
938 if (r_udb->flag == DEF_UDB_FREE) {
939 myfree(kdp);
940 return;
941 }
942 n = read(kdp->ident, &inbuf, sizeof(inbuf)-1);
943 if (n <= 0) {
944 INFO("[-ERR]: cmd_read: read(2): %s\n", strerror(errno));
945 myfree(kdp);
946 return;
947 }
948 *(inbuf+n) = '\0';
949 memset(&r_cmd, 0, sizeof(CMDTBL));
950 n = cmd_parse(&r_cmd, inbuf);
951 if (n == -1) {
952 Awrite(kdp->ident, msg_over, sizeof(msg_over) - 1);
953 myfree(kdp);
954 return;
955 } else if (n == 0) {
956 r_udb->cmd = DEF_CMDNAME_UNKNOWN;
957 r_udb->cmderr = 0;
958 } else if (r_udb->login == 0) {
959 if (strcmp(r_cmd.name, "USER") == 0) {
960 r_udb->cmd = DEF_CMDNAME_USER;
961 if (n == 2) {
962 plcat(r_udb->username, 0, sizeof(r_udb->username), "%s", r_cmd.value);
963 r_udb->cmderr = 0;
964 } else
965 r_udb->cmderr = 1;
966 } else if (strcmp(r_cmd.name, "PASS") == 0) {
967 r_udb->cmd = DEF_CMDNAME_PASS;
968 if (r_udb->username[0] == '\0')
969 r_udb->cmderr = 1;
970 else if (n == 1)
971 r_udb->cmderr = 2;
972 else {
973 n = auth_system(r_udb->username, r_udb->maildir, sizeof(r_udb->maildir), r_cmd.value);
974 memset(&r_cmd, 0, sizeof(CMDTBL));
975 if (n == 1) {
976 r_udb->cmderr = 0;
977 r_udb->login = 1;
978 } else {
979 INFO("[-ERR]: Auth Error FROM: %s.%u : user: %s\n",
980 inet_ntoa(r_udb->address.sin_addr), htons(r_udb->address.sin_port), r_udb->username);
981 r_udb->cmderr = 3;
982 }
983 }
984 } else if (strcmp(r_cmd.name, "QUIT") == 0) {
985 r_udb->cmd = DEF_CMDNAME_QUIT;
986 r_udb->cmderr = 0;
987 } else
988 r_udb->cmd = 0;
989 } else {
990 if (strcmp(r_cmd.name, "LIST") == 0) {
991 r_udb->cmd = DEF_CMDNAME_LIST;
992 r_udb->a_id = aldigit(r_cmd.value, r_udb->maxid);
993 } else if (strcmp(r_cmd.name, "UIDL") == 0) {
994 r_udb->cmd = DEF_CMDNAME_UIDL;
995 r_udb->a_id = aldigit(r_cmd.value, r_udb->maxid);
996 } else if (strcmp(r_cmd.name, "DELE") == 0) {
997 r_udb->cmd = DEF_CMDNAME_DELE;
998 r_udb->a_id = aldigit(r_cmd.value, r_udb->maxid);
999 } else if (strcmp(r_cmd.name, "RETR") == 0) {
1000 r_udb->cmd = DEF_CMDNAME_RETR;
1001 r_udb->a_id = aldigit(r_cmd.value, r_udb->maxid);
1002 } else if (strcmp(r_cmd.name, "STAT") == 0) {
1003 r_udb->cmd = DEF_CMDNAME_STAT;
1004 r_udb->cmderr = 0;
1005 } else if (strcmp(r_cmd.name, "RSET") == 0) {
1006 r_udb->cmd = DEF_CMDNAME_RSET;
1007 r_udb->cmderr = 0;
1008 } else if (strcmp(r_cmd.name, "NOOP") == 0) {
1009 r_udb->cmd = DEF_CMDNAME_NOOP;
1010 r_udb->cmderr = 0;
1011 } else if (strcmp(r_cmd.name, "QUIT") == 0) {
1012 r_udb->cmd = DEF_CMDNAME_EXIT;
1013 r_udb->cmderr = 0;
1014 } else {
1015 r_udb->cmd = DEF_CMDNAME_UNKNOWN;
1016 r_udb->cmderr = 0;
1017 }
1018 if ((r_udb->cmd < DEF_CMDNAME_RSET) && (r_udb->cache_status == DEF_CACHE_UNDONE))
1019 init_cache(r_udb);
1020 }
1021 EV_SET(&afds[used_afds++], kdp->ident, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, r_udb);
1022 r_udb->refer++;
1023 }
1024
checktm(const struct kevent * const kdp)1025 static void checktm(const struct kevent *const kdp)
1026 {
1027 UDB *t_udb = kdp->udata;
1028
1029 if (t_udb->cmdlock == DEF_CMD_LOCK)
1030 return;
1031 t_udb->refer--;
1032 if (t_udb->flag == DEF_UDB_USED)
1033 Awrite(kdp->ident, msg_timeout, sizeof(msg_timeout) - 1);
1034 shutdown(kdp->ident, SHUT_RD);
1035 myfree(kdp);
1036 }
1037
cli_accept(const struct kevent * const kdp)1038 static void cli_accept(const struct kevent *const kdp)
1039 {
1040 int cli_fd;
1041 UDB *udbp;
1042 socklen_t tmpaddrlen;
1043 struct sockaddr_in address;
1044
1045 tmpaddrlen = sizeof(struct sockaddr_in);
1046 cli_fd = accept(kdp->ident, (struct sockaddr *) &address, &tmpaddrlen);
1047 if (cli_fd < 0) {
1048 INFO("[-ERR]: cli_accept: accept(2): %s\n", strerror(errno));
1049 return;
1050 } else {
1051 tsession++;
1052 if (online == max_client) {
1053 INFO("[-ERR]: Total:%d Online: %d ACCEPT FROM: %s.%u : Too Many Online\n",
1054 tsession, online, inet_ntoa(address.sin_addr), htons(address.sin_port));
1055 Write(cli_fd, msg_max_online, sizeof(msg_max_online) - 1);
1056 close(cli_fd);
1057 return;
1058 }
1059 online++;
1060 INFO("%d [INFO]: Total:%d Online: %d ACCEPT FROM: %s.%u\n",
1061 getpid(), tsession, online, inet_ntoa(address.sin_addr), htons(address.sin_port));
1062 udbp = (used_cudb == 0) ? &audb[used_audb++] : cudb[--used_cudb];
1063 memset(udbp, 0, sizeof(UDB));
1064 udbp->flag = DEF_UDB_USED;
1065 memcpy(&udbp->address, &address, sizeof(address));
1066 EV_SET(&afds[used_afds++], cli_fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, udbp);
1067 udbp->refer = 1;
1068 }
1069 }
1070
myloop(int id)1071 static void myloop(int id)
1072 {
1073 int myid;
1074 int res;
1075 struct kevent *kdp;
1076 UDB *t_udb = NULL;
1077
1078 myid = 0;
1079 while (1) {
1080 errno = 0;
1081 res = kevent(kq, afds, used_afds, aevs, max_client, NULL);
1082 if (res == -1) {
1083 INFO("[-ERR]: myloop: kevent(2): %s\n", strerror(errno));
1084 continue;
1085 }
1086 if (res == 0) {
1087 INFO("[EXIT]: myloop: NO Event received\n");
1088 exitfree();
1089 _exit(EXIT_FAILURE);
1090 }
1091 used_afds = 0;
1092 for (kdp = aevs; kdp < &aevs[res]; kdp++) {
1093 if ((int)kdp->ident != pop3d_fd)
1094 t_udb = kdp->udata;
1095 if ((int)kdp->ident == pop3d_fd) {
1096 if (kdp->filter == EVFILT_READ)
1097 cli_accept(kdp);
1098 else {
1099 if (myid == id)
1100 EV_SET(&afds[used_afds++], pop3d_fd, EVFILT_READ, EV_ENABLE, 0, 0, NULL);
1101 else
1102 EV_SET(&afds[used_afds++], pop3d_fd, EVFILT_READ, EV_DISABLE, 0, 0, NULL);
1103 if (myid == multi_process-1)
1104 myid = 0;
1105 else
1106 myid++;
1107 }
1108 } else if (kdp->flags & EV_ERROR) {
1109 if ((int)kdp->ident != pop3d_fd) {
1110 t_udb->refer--;
1111 myfree(kdp);
1112 }
1113 } else if (kdp->flags & EV_EOF) {
1114 if ((int)kdp->ident != pop3d_fd) {
1115 t_udb->refer--;
1116 myfree(kdp);
1117 }
1118 } else if (kdp->filter == EVFILT_TIMER)
1119 checktm(kdp);
1120 else if (kdp->filter == EVFILT_READ)
1121 cmd_read(kdp);
1122 else if (kdp->filter == EVFILT_WRITE)
1123 cmd_write(kdp);
1124 else {
1125 INFO("[-ERR]: myloop: UNKNOWN\n");
1126 exitfree();
1127 _exit(EXIT_FAILURE);
1128 }
1129 }
1130 }
1131 }
1132
pop3d(int id)1133 void pop3d(int id)
1134 {
1135 if (kq_init() == -1) {
1136 exitfree();
1137 _exit(EXIT_FAILURE);
1138 }
1139 if (multi_process == 1)
1140 EV_SET(&afds[used_afds++], pop3d_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
1141 else {
1142 EV_SET(&afds[used_afds++], pop3d_fd, EVFILT_READ, EV_ADD|EV_DISABLE, 0, 0, NULL);
1143 EV_SET(&afds[used_afds++], pop3d_fd, EVFILT_TIMER, EV_ADD, 0, 500, NULL);
1144 }
1145 myloop(id);
1146 }
1147