/*****************************************************************************
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 <jochen@remote.org>
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 <config.h>
#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 <n> to the client. It will send the whole
header, an empty line, and <lines> lines from the body. If <lines> 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 <line> 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 *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1