/***************************************************************************** POPular -- A POP3 server and proxy for large mail systems $Id: pserv_child.c,v 1.6 2002/09/15 12:27:16 sqrt Exp $ http://www.remote.org/jochen/mail/popular/ ****************************************************************************** Copyright (C) 1999-2002 Jochen Topf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA *****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include "popular.h" #include "pserv.h" #include "daemon.h" /* capability list */ extern struct pserv_capa capa; extern struct pservconfig conf; extern int debug; /* session this child is handling */ static struct backend_session *cs; /* number of messages in mailbox and size of alle messages */ static long box_size; /* this is for the list of messages */ struct msg { struct msg *next; unsigned long size; int deleted; int new; int read; char name[MAXLEN_MAILFILE+1]; }; static struct msg **msglist; /***************************************************************************** readmsgdir() *****************************************************************************/ struct msg * readmsgdir(int newflag, const char *dirname, struct msg *m) { struct dirent *dirent; struct msg *msg, *last=m; struct stat st; char *p; DIR *dir = opendir(dirname); if (! dir) { /* XLOG-DOC:ADM:0006:dir_access_failed * The opendir() system call on a mailbox directory failed. This probably * means that something is wrong with permissions in the pop spool. */ xlog_printf(xlog_adm, 0x0006, "dir_access_failed dir='%s' errno=%d errmsg='%s'", dirname, errno, strerror(errno)); return NULL; } while ((dirent = readdir(dir))) { int n; if (dirent->d_name[0] == '.') continue; /* no dot files */ msg = malloc(sizeof(struct msg)); /* XLOG-DOC:SOS:0007:out_of_memory * Pserv couldn't allocate memory to hold a mail message structure. */ if (! msg) { xlog_printf(xlog_sos, 0x0007, "out_of_memory"); exit(RCODE_ERR); } msg->deleted = 0; msg->read = 0; msg->size = 0; msg->new = newflag; (void) strlcpy(msg->name, dirname, sizeof(msg->name)); (void) strlcat(msg->name, "/", sizeof(msg->name)); n = strlcat(msg->name, dirent->d_name, sizeof(msg->name)); if (n >= sizeof(msg->name)) { /* XLOG-DOC:ADM:0008:name_too_long * The name of a mailbox directory and file is too long. */ xlog_printf(xlog_adm, 0x0008, "name_too_long"); free(msg); continue; } /* try to get message size from file name. The format must be as follows: The name must end in a _ (underscore) and the file size. */ if ((p = strrchr(msg->name, '_'))) { int l = get_int(p+1, 0, INT_MAX, -1, -1, -1); if (l >= 0) msg->size = l; } /* if we don't have a message size yet, call stat() */ if (msg->size == 0) { if (stat(msg->name, &st) == 0) { msg->size = st.st_size; } else { /* XLOG-DOC:ADM:0009:stat_failed * The stat() system call failed for a mail message file. The message * will not be listed. This should never happen and probably means * that somebody tinkered around with a mailbox file by hand and * got the permission wrong. */ xlog_printf(xlog_adm, 0x0009, "stat_failed file='%s'", dirent->d_name); free(msg); continue; } } if (msg->size == 0) { /* XLOG-DOC:ADM:0162:msg_size_zero * A mail message has size zero. It will not be listed. */ xlog_printf(xlog_adm, 0x0162, "msg_size_zero file='%s'", dirent->d_name); free(msg); continue; } cs->msgnum++; if (newflag) cs->msgnew++; msg->next = last; last = msg; /* add 12 to the size of mailbox, if mail has been read, for "Status: RO\r\n" header */ if (conf.statusheader && ! newflag) msg->size += 12; box_size += msg->size; } closedir(dir); return last; } /***************************************************************************** compare_msg() Is called from sort() to compare two messages by name. We start comparing at the 5th char, because of the leading "new/" or "cur/". *****************************************************************************/ int compare_msg(const void *a, const void *b) { return strtol(&((*(struct msg **)a)->name[4]), (char **)NULL, 10) - strtol(&((*(struct msg **)b)->name[4]), (char **)NULL, 10); } /***************************************************************************** readmaillist() Current directory must be the mailbox directory, when this function is called. *****************************************************************************/ int readmaillist() { struct msg *first; int count; first = readmsgdir(1, "new", NULL); first = readmsgdir(0, "cur", first); msglist = malloc(cs->msgnum * sizeof(struct msg *)); if (! msglist) { /* XLOG-DOC:SOS:0014:out_of_memory * Pserv can't allocate memory for a msglist structure. */ xlog_printf(xlog_sos, 0x0014, "out_of_memory"); return 0; } for (count = cs->msgnum; count; count--) { msglist[count-1] = first; first = first->next; } qsort(msglist, cs->msgnum, sizeof(struct msg *), compare_msg); return 1; } /***************************************************************************** send_dot_stuffed() *****************************************************************************/ int send_dot_stuffed(struct io_ctx *ioc, char *buf, size_t count) { char *p=buf; char *q; if (count <= 0) return 0; if (buf[0] == '.') { if (io_syswrite(ioc, ".", 1) < 0) return -1; } while ((q = search_string(p, "\r\n.", count-(p-buf)))) { if (io_syswrite(ioc, p, (q-p)+2) < 0) return -1; p = q+1; *p = '.'; } if (io_syswrite(ioc, p, (buf+count)-p) < 0) return -1; return count; } /***************************************************************************** sendmailmsg(int n, int lines) This function sends message to the client. It will send the whole header, an empty line, and lines from the body. If is -1 it will send the whole body. The message *must* have the proper line endings (\r\n) already, they will *not* be changed. If a line starts with a dot, another dot is added at the beginning as required by the POP3 protocol. If the message has already been read it will add a "Status: RO" header at the end of the headers. *****************************************************************************/ char * sendmailmsg(struct io_ctx *ioc, int n, int lines) { int file; char *m, *p; int size = msglist[n]->size; cs->state = sstRead; cs->statetime = time(NULL); DEBUG2(DG_POP, "sendmailmsg", "started msg=%d lines=%d", n, lines); if (conf.statusheader && ! msglist[n]->new) size -= 12; if ((lines < 0) && (! msglist[n]->read)) { /* RETR x */ /* count this as a read and remember that this mail has been read */ cs->msgread++; msglist[n]->read = 1; } file = open(msglist[n]->name, O_RDONLY); if (file < 0) { if (errno == ENOENT) { /* XLOG-DOC:ERR:0158:mailfile_does_not_exist * A mail message file does not exist. This probably means that the * mail has been deleted by another POP session running at the same * time. */ xlog_printf(xlog_err, 0x0158, "mailfile_does_not_exist name='%s'", msglist[n]->name); return "-ERR no such message"; } else { /* XLOG-DOC:ADM:0159:mailfile_open_failed * Opening a mail message file for reading failed. */ xlog_printf(xlog_adm, 0x0159, "mailfile_open_failed name='%s' errno=%d errmsg='%s'", msglist[n]->name, errno, strerror(errno)); return "-ERR internal error"; } } m = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, file, 0); if (m == MAP_FAILED) { /* XLOG-DOC:SOS:015a:mailfile_mmap_failed * The mmap () system call on the mail message file failed. */ xlog_printf(xlog_sos, 0x015a, "mailfile_mmap_failed msgnum=%d errno=%d errmsg='%s'", n, errno, strerror(errno)); return "-ERR internal error"; } DEBUG0(DG_POP, "sendmailmsg", "file is mmaped"); if (m[0] == '\0') return "-ERR internal error"; io_buf_printf(ioc, "+OK %ld octets\r\n", msglist[n]->size); io_buf_flush(ioc); if (m[0] == '\r' && m[1] == '\n') { /* CRLF at beginning: no header */ p = m; } else { /* find end of header */ p = search_string(m, "\r\n\r\n", size); if (p) p+=2; } if (p) { /* found body, send header */ if (send_dot_stuffed(ioc, m, p-m) < 0) { /* XLOG-DOC:ERR:015d:write_error * Error writing mail message to proxy socket. */ xlog_printf(xlog_err, 0x015d, "write_error errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_OK); } } else { /* no body, send header */ if (send_dot_stuffed(ioc, m, size) < 0) { /* XLOG-DOC:ERR:015e:write_error * Error writing mail message to proxy socket. */ xlog_printf(xlog_err, 0x015e, "write_error errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_OK); } } if (conf.statusheader && ! msglist[n]->new) io_buf_writeln(ioc, "Status: RO"); if (p) { if (lines == 0) { io_buf_writeln(ioc, ""); } else if (lines == -1) { io_buf_flush(ioc); if (send_dot_stuffed(ioc, p, (m+size)-p) < 0) { /* XLOG-DOC:ERR:015f:write_error * Error writing mail message to proxy socket. */ xlog_printf(xlog_err, 0x015f, "write_error errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_OK); } } else { int i; char *q, *r; DEBUG1(DG_POP, "sendmailmsg", "top lines=%d", lines); io_buf_flush(ioc); for (i=0, q=p; i<=lines; i++, q=r+2) { r = search_string(q, "\r\n", size-(q-m)); DEBUG1(DG_POP, "sendmailmsg", "top r=%lx", r); if (!r) { q = m+size; break; } } DEBUG1(DG_POP, "sendmailmsg", "top q-p=%d", q-p); if (send_dot_stuffed(ioc, p, q-p) < 0) { /* XLOG-DOC:ERR:0161:write_error * Error writing mail message to proxy socket. */ xlog_printf(xlog_err, 0x0161, "write_error errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_OK); } } } /* if the message is not terminated by CRLF, send one */ if (m[size-2] != '\r' || m[size-1] != '\n') { io_buf_writeln(ioc, ""); } DEBUG0(DG_POP, "sendmailmsg", "finished sending"); if (munmap(m, size) != 0) { /* XLOG-DOC:SOS:015b:mailfile_munmap_failed * The unmap () system call on the mail message file failed. */ xlog_printf(xlog_sos, 0x015b, "mailfile_munmap_failed msgnum=%d errno=%d errmsg='%s'", n, errno, strerror(errno)); } close(file); return "."; } /***************************************************************************** list_or_uidl() LIST and UIDL command *****************************************************************************/ void list_or_uidl(struct io_ctx *ioc, const char *s, int uidl) { if (s) { /* argument given, list one message only */ int num = get_int(s, 1, cs->msgnum, -1, -1, -1); if (num < 1) { io_writeln(ioc, "-ERR no such message"); } else if (msglist[num-1]->deleted) { io_writeln(ioc, "-ERR message is deleted"); } else { if (uidl) { io_buf_printf(ioc, "+OK %d %s\r\n", num, (msglist[num-1]->name)+4); } else { io_buf_printf(ioc, "+OK %d %lu\r\n", num, msglist[num-1]->size); } io_buf_flush(ioc); } } else { /* no argument, list all messages */ int i; io_buf_writeln(ioc, "+OK"); for (i=0; i < cs->msgnum; i++) { if (msglist[i]->deleted) continue; if (uidl) { io_buf_printf(ioc, "%d %s\r\n", i+1, (msglist[i]->name)+4); } else { io_buf_printf(ioc, "%d %lu\r\n", i+1, msglist[i]->size); } } io_writeln(ioc, "."); } } /***************************************************************************** do_command(struct io_ctx *ioc, char *line) Parse as POP command and do whatever the command says. *****************************************************************************/ int do_command(struct io_ctx *ioc, char *line) { char *s; char buf[MAXBUF]; /* find arguments and if there are any separate them from the command */ s = strchr(line, ' '); if (s) { *s = 0; s++; } if (! strcasecmp(line, "QUIT")) { io_writeln(ioc, "+OK"); return 0; } else if (! strcasecmp(line, "NOOP")) { io_writeln(ioc, "+OK"); } else if (! strcasecmp(line, "RSET")) { int i; for (i=0; i < cs->msgnum; i++) { msglist[i]->deleted = 0; msglist[i]->read = 0; } cs->msgread = 0; cs->msgdel = 0; io_writeln(ioc, "+OK"); } else if (! strcasecmp(line, "STAT")) { int i, n=0; unsigned long sum=0; for (i=0; i < cs->msgnum; i++) { if (msglist[i]->deleted) continue; n++; sum += msglist[i]->size; } snprintf(buf, sizeof(buf), "+OK %d %lu", n, sum); io_writeln(ioc, buf); } else if (! strcasecmp(line, "LIST")) { list_or_uidl(ioc, s, 0); } else if (! strcasecmp(line, "UIDL")) { list_or_uidl(ioc, s, 1); } else if (! strcasecmp(line, "RETR")) { if (s) { int num = get_int(s, 1, cs->msgnum, -1, -1, -1); if (num < 1) { io_writeln(ioc, "-ERR no such message"); } else if (msglist[num-1]->deleted) { io_writeln(ioc, "-ERR message is deleted"); } else { io_writeln(ioc, sendmailmsg(ioc, num-1, -1)); } } else { io_writeln(ioc, "-ERR no such message"); } } else if (! strcasecmp(line, "TOP")) { if (s) { char *t = strchr(s, ' '); if (t) { int num; *t = '\0'; t++; num = get_int(s, 1, cs->msgnum, -1, -1, -1); if (num < 1) { io_writeln(ioc, "-ERR no such message"); } else if (msglist[num-1]->deleted) { io_writeln(ioc, "-ERR message is deleted"); } else { int c = get_int(t, 0, INT_MAX, -1, -1, -1); if (c < 0) { io_writeln(ioc, "-ERR parse error"); } else { io_writeln(ioc, sendmailmsg(ioc, num-1, c)); } } } else { io_writeln(ioc, "-ERR parse error"); } } else { io_writeln(ioc, "-ERR no such message"); } } else if (! strcasecmp(line, "DELE")) { if (s) { int num = get_int(s, 1, cs->msgnum, -1, -1, -1); if (num < 1) { io_writeln(ioc, "-ERR no such message"); } else if (msglist[num-1]->deleted) { io_writeln(ioc, "-ERR message is deleted"); } else { msglist[num-1]->deleted = 1; cs->msgdel++; io_writeln(ioc, "+OK"); } } else { io_writeln(ioc, "-ERR no message number given"); } } else if (! strcasecmp(line, "CAPA")) { /* RFC 2449 */ switch (capa.capa_type) { case capa_error: io_writeln(ioc, "-ERR unknown command"); break; case capa_none: io_writeln(ioc, "+OK capability list follows\r\n."); break; case capa_default: io_writeln(ioc, "+OK capability list follows\r\n" DEFAULT_CAPA_LIST "."); break; case capa_user: io_buf_writeln(ioc, "+OK capability list follows"); io_buf_write(ioc, capa.text); io_writeln(ioc, "."); break; default: io_writeln(ioc, "-ERR unknown command"); break; } #if 0 } else if (! strcasecmp(line, "LAST")) { /* The LAST command was defined in older POP3 RFCs. It is obsolete now but some clients need it, so we send a fake response. */ io_writeln(ioc, "+OK 0"); #endif } else if (! strcasecmp(line, "LAST")) { /* check this special case, because we don't need to log this */ /* this command was in old POP RFCs, but is not mentioned any more by * RFC 1939 */ io_writeln(ioc, "-ERR unknown command"); } else if (! strcasecmp(line, "FTRQ")) { /* check this special case, because we don't need to log this */ /* this command seems to be used by the FTGate mail server, although * I don't know what it is supposed to do. See http://www.ftgate.com/ */ io_writeln(ioc, "-ERR unknown command"); } else if (! strcasecmp(line, "XSENDER")) { /* check this special case, because we don't need to log this */ /* Netscape extension. */ io_writeln(ioc, "-ERR unknown command"); } else { /* XLOG-DOC:ERR:0018:unknown_command * The client sent an unknown POP command. */ xlog_printf(xlog_err, 0x0018, "unknown_command cmd='%s'", line); io_writeln(ioc, "-ERR unknown command"); } return 1; } /***************************************************************************** child_main() *****************************************************************************/ int child_main(int fd, struct backend_session *this_session, int slot) { struct sockaddr_in sin; struct ip_address { unsigned char d[4]; } *ipa; unsigned int dummy = sizeof(sin); char peer[30]; char mailbox[256], id[256], flags[256], *rl; int flag_master=0; int i, len; struct io_ctx *ioc; cs = this_session; cs->starttime = time(NULL); signal_init_child(); alarm(conf.sessiontimeout); if (set_keepalive(fd) < 0) { DEBUG2(DG_NET, "child", "setting TCP keepalive failed errno=%d errmsg='%s'", errno, strerror(errno)); } ioc = io_init(iot_plain, fd, "to proxy", 1, conf.idletimeout, NULL); if (! ioc) { /* XLOG-DOC:SOS:0022:out_of_memory * There is no memory to allocate an io context struct. The program is * immediately terminated. */ xlog_printf(xlog_sos, 0x0022, "out_of_memory"); exit(RCODE_ERR); } /* find out to whom we are talking */ if (getpeername(fd, (struct sockaddr *) &sin, &dummy) < 0) { if (errno == ENOTSOCK) { (void) strlcpy(peer, "LOCAL", sizeof(peer)); } else { /* XLOG-DOC:ERR:0023:getpeername_failed * The getpeername() call failed. This probably means that the client * and subsequently the proxy has closed the connection. */ xlog_printf(xlog_err, 0x0023, "getpeername_failed errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_OK); } } else { ipa = (struct ip_address *) &sin.sin_addr; snprintf(peer, sizeof(peer), "%d.%d.%d.%d", ipa->d[0], ipa->d[1], ipa->d[2], ipa->d[3]); } memcpy(&(cs->sin_proxy), &sin, sizeof(struct sockaddr_in)); /* read mailbox name */ rl = io_readln(ioc); if (! rl) exit(RCODE_ERR); len = strlcpy(mailbox, rl, sizeof(mailbox)); if (len >= sizeof(mailbox)) { /* XLOG-DOC:ADM:0024:mailbox_too_long * The mailbox name that the proxy sent to the pserv is too long. */ xlog_printf(xlog_adm, 0x0024, "mailbox_too_long from='%s' mailbox='%s'", peer, rl); goto internal_error; } len = strlcpy(cs->mailbox, rl, sizeof(cs->mailbox)); if (len >= sizeof(cs->mailbox)) { /* XLOG-DOC:ADM:0025:mailbox_too_long * The mailbox name that the proxy sent to the pserv is too long. */ xlog_printf(xlog_adm, 0x0025, "mailbox_too_long from='%s' mailbox='%s'", peer, rl); goto internal_error; } /* empty mailbox */ if (mailbox[0] == '\0') { io_writeln(ioc, "-ERR missing mailbox"); exit(RCODE_OK); } /* check mailbox */ if (mailbox[0] == '/') { /* XLOG-DOC:ADM:0026:bad_mailbox * The mailbox name that the proxy sent to the pserv has a leading '/'. */ xlog_printf(xlog_adm, 0x0026, "bad_mailbox from='%s' mailbox='%s'", peer, mailbox); goto internal_error; } for (i=0; mailbox[i]; i++) { if ( !(isalnum((int)(mailbox[i])) || mailbox[i] == '.' || mailbox[i] == '_' || mailbox[i] == '-' || mailbox[i] == '+' || mailbox[i] == '/' || mailbox[i] == '%' || mailbox[i] == '=' ) || (mailbox[i] == '.' && mailbox[i+1] == '.') ) { /* XLOG-DOC:ADM:0027:bad_mailbox * The mailbox name that the proxy sent to the pserv has funny * chars in it. Only the following chars are allowed: a-z, A-Z, 0-9, * '.', '_', '-', '+', '/', '=', '%'. Two adjacent dots ("..") are not * allowed. */ xlog_printf(xlog_adm, 0x0027, "bad_mailbox from='%s' mailbox='%s'", peer, mailbox); goto internal_error; } } /* read id */ rl = io_readln(ioc); if (! rl) exit(RCODE_ERR); len = strlcpy(id, rl, sizeof(id)); if (len >= sizeof(id)) { /* XLOG-DOC:ADM:0028:id_too_long * The id that the proxy sent to the pserv is too long. */ xlog_printf(xlog_adm, 0x0028, "id_too_long from='%s' id='%s'", peer, rl); goto internal_error; } len = strlcpy(cs->id, rl, sizeof(cs->id)); if (len >= sizeof(cs->id)) { /* XLOG-DOC:ADM:0029:id_too_long * The id that the proxy sent to the pserv is too long. */ xlog_printf(xlog_adm, 0x0029, "id_too_long from='%s' id='%s'", peer, rl); goto internal_error; } /* check id */ for (i=0; id[i]; i++) { if (!(isalnum((int)(id[i])) || id[i] == '.' || id[i] == '_' || id[i] == '-')) { /* XLOG-DOC:ADM:0030:bad_id * The id that the proxy sent to the pserv has funny chars in it. Only * the following chars are allowed: a-z, A-Z, 0-9, '.', '_', '-'. */ xlog_printf(xlog_adm, 0x0030, "bad_id from='%s' id='%s'", peer, id); goto internal_error; } } xlog_set_id(id); /* read flags */ rl = io_readln(ioc); if (! rl) exit(RCODE_ERR); len = strlcpy(flags, rl, sizeof(flags)); if (len >= sizeof(flags)) { /* XLOG-DOC:ADM:0031:flags_too_long * The flags that the proxy sent to the pserv are too long. */ xlog_printf(xlog_adm, 0x0031, "flags_too_long from='%s' flags='%s'", peer, rl); goto internal_error; } len = strlcpy(cs->flags, rl, sizeof(cs->flags)); if (len >= sizeof(cs->flags)) { /* XLOG-DOC:ADM:0032:flags_too_long * The flags that the proxy sent to the pserv are too long. */ xlog_printf(xlog_adm, 0x0032, "flags_too_long from='%s' flags='%s'", peer, rl); goto internal_error; } /* check flags */ for (i=0; flags[i]; i++) { if (flags[i] == '-') continue; /* this is for compatibility with older version, that used a hyphen to indicate a cleared flag */ if (!isalpha((int)(flags[i]))) { /* XLOG-DOC:ADM:0033:bad_flags * The flags that the proxy sent to the pserv have funny chars in it. * Only a-z, A-Z, 0-9 are allowed. */ xlog_printf(xlog_adm, 0x0033, "bad_flags from='%s' flags='%s'", peer, flags); goto internal_error; } if (flags[i] == 'M') flag_master=1; } /* XLOG-DOC:INF:0034:login * A new connection from the proxy is coming in and the mailbox name, the * logging id and the flags have been received. */ xlog_printf(xlog_inf, 0x0034, "login slot=%d peer='%s' mailbox='%s' flags='%s'", slot, peer, mailbox, flags); /* prepare mailbox directory */ { char mbdir[strlen(conf.popdir) + strlen(mailbox) + 2]; snprintf(mbdir, sizeof(mbdir), "%s/%s", conf.popdir, mailbox); if (! mailbox_prepdir(mbdir)) goto internal_error; } /* read list of mails into memory */ if (! readmaillist()) goto internal_error; io_writeln(ioc, "+OK"); /* main loop, only a QUIT will get you out of here */ do { cs->state = sstIdle; cs->statetime = time(NULL); rl = io_readln(ioc); if (!rl) exit(RCODE_OK); } while (do_command(ioc, rl)); for (i=0; i < cs->msgnum; i++) { if (msglist[i]->deleted) { DEBUG1(DG_MAIN, "child", "deleting message %d", i+1); if (unlink(msglist[i]->name) != 0) { if (errno == ENOENT) { /* XLOG-DOC:ERR:0148:msg_unlink_failed * Deleting of a mail message failed. Probably another pserv process * was faster. */ xlog_printf(xlog_err, 0x0148, "msg_unlink_failed mailbox='%s' msg='%s' errno=%d errmsg='%s'", cs->mailbox, msglist[i]->name, errno, strerror(errno)); } else { /* XLOG-DOC:ADM:0037:msg_unlink_failed * After the QUIT command has been received, the server tried to * delete a message but failed. */ xlog_printf(xlog_adm, 0x0037, "msg_unlink_failed mailbox='%s' msg='%s' errno=%d errmsg='%s'", cs->mailbox, msglist[i]->name, errno, strerror(errno)); } } if (conf.logeachmsg) { /* XLOG-DOC:INF:017d:msg_info * A message was deleted. */ xlog_printf(xlog_inf, 0x0036, "msg_info mailbox='%s' msg='%s' size=%d op=del", cs->mailbox, msglist[i]->name, msglist[i]->size); } } else if (!flag_master && msglist[i]->read && msglist[i]->new) { /* only mark msg as read if master_flag is not set */ char rbuf[256] = "cur/"; DEBUG1(DG_MAIN, "child", "moving message %d", i+1); strlcat(rbuf, (msglist[i]->name)+4, sizeof(rbuf)); if (rename(msglist[i]->name, rbuf) != 0) { if (errno == ENOENT) { /* XLOG-DOC:ERR:0147:msg_rename_failed * Renaming of a mail file failed, because it is not there. Another * pserv process probably got to this file before we did. */ xlog_printf(xlog_err, 0x0147, "msg_rename_failed mailbox='%s' msg='%s' errno=%d errmsg='%s'", cs->mailbox, msglist[i]->name, errno, strerror(errno)); } else { /* XLOG-DOC:ADM:0038:msg_rename_failed * After the QUIT command has been received, the server tried to * rename a message from the 'new' into the 'cur' directory but * failed. */ xlog_printf(xlog_adm, 0x0038, "msg_rename_failed mailbox='%s' msg='%s' errno=%d errmsg='%s'", cs->mailbox, msglist[i]->name, errno, strerror(errno)); } } if (conf.logeachmsg) { /* XLOG-DOC:INF:017e:msg_info * A message was moved from new to cur. */ xlog_printf(xlog_inf, 0x0036, "msg_info mailbox='%s' msg='%s' size=%d op=mv", cs->mailbox, msglist[i]->name, msglist[i]->size); } } } cs->state = sstConnQuit; cs->statetime = time(NULL); /* XLOG-DOC:INF:0039:quit * This message is logged after the client send a QUIT. It contains * statistics about the number of mails in mailbox, the number of new * mails, the number of read mails, and the number of deleted mails. */ xlog_printf(xlog_inf, 0x0039, "quit time=%d mails=%d new=%d retr=%d del=%d", time(NULL) - cs->starttime, cs->msgnum, cs->msgnew, cs->msgread, cs->msgdel); exit(RCODE_OK); internal_error: io_writeln(ioc, "-ERR internal error"); exit(RCODE_ERR); } /** THE END *****************************************************************/