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