/*****************************************************************************
POPular -- A POP3 server and proxy for large mail systems
$Id: pproxy_child.c,v 1.45 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 "pproxy.h"
#include "daemon.h"
#include "net.h"
extern struct pproxyconfig conf;
/* session this child is handling */
static struct proxy_session *cs;
/*****************************************************************************
connect_to_backend()
Connect to backend.
*****************************************************************************/
int
connect_to_backend(void)
{
int s;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
/* XLOG-DOC:SOS:0096:backend_socket_call_failed
* Creating a socket for a connection to the backend failed. */
xlog_printf(xlog_sos, 0x0096, "backend_socket_call_failed errno=%d errmsg='%s'", errno, strerror(errno));
return -1;
}
if (connect(s, (struct sockaddr *) &(cs->backend.backend_addr), sizeof(struct sockaddr_in)) < 0) {
/* XLOG-DOC:SOS:0097:connect_to_backend_failed
* The connect to a backend failed. */
xlog_printf(xlog_sos, 0x0097, "connect_to_backend_failed id=%s errno=%d errmsg='%s'", cs->backend.id, errno, strerror(errno));
return -1;
}
if (set_keepalive(s) < 0) {
DEBUG2(DG_NET, "connect_to_backend", "setting TCP keepalive failed errno=%d errmsg='%s'", errno, strerror(errno));
}
return s;
}
/*****************************************************************************
do_pop3_auth()
returns: 0 - connection or protocol error
1 - access denied
2 - ok
*****************************************************************************/
int
do_pop3_auth(struct io_ctx *ioc, const char *user, const char *pass)
{
char *line;
char buf[1024];
/***************************************************************************
Read greeting banner and return error if it doesn't start with +OK.
***************************************************************************/
line = io_readln(ioc);
if (line == NULL) {
/* XLOG-DOC:SOS:0098:backend_read_err
* A read from a POP3 backend failed. */
xlog_printf(xlog_sos, 0x0098, "backend_read_err");
return 0;
}
if (strncmp(line, "+OK", 3)) {
/* XLOG-DOC:SOS:0099:backend_not_ready
* A POP3 backend sent a -ERR reply on connection. */
xlog_printf(xlog_sos, 0x0099, "backend_not_ready msg='%s'", line);
return 0;
}
/***************************************************************************
Send USER command.
***************************************************************************/
(void) strlcpy(buf, "USER ", sizeof(buf));
(void) strlcat(buf, user, sizeof(buf));
if (io_writeln(ioc, buf) < 0) {
/* XLOG-DOC:SOS:009a:backend_read_err
* A write to a POP3 backend failed. */
xlog_printf(xlog_sos, 0x009a, "backend_write_err");
return 0;
}
/***************************************************************************
Read answer to USER command and return error if it doesn't start
with +OK.
***************************************************************************/
line = io_readln(ioc);
if (line == NULL) {
/* XLOG-DOC:SOS:009b:backend_read_err
* A read from a POP3 backend failed. */
xlog_printf(xlog_sos, 0x009b, "backend_read_err");
return 0;
}
if (strncmp(line, "+OK", 3)) {
/* XLOG-DOC:ADM:009c:login_failed
* The login to a POP3 backend server failed. The answer to the USER
* command was -ERR. */
xlog_printf(xlog_adm, 0x009c, "login_failed msg='%s'", line);
return 1;
}
/***************************************************************************
Send PASS command.
***************************************************************************/
(void) strlcpy(buf, "PASS ", sizeof(buf));
(void) strlcat(buf, pass, sizeof(buf));
if (io_writeln(ioc, buf) < 0) {
/* XLOG-DOC:SOS:009d:backend_read_err
* A write to a POP3 backend failed. */
xlog_printf(xlog_sos, 0x009d, "backend_write_err");
return 0;
}
/***************************************************************************
Read answer to PASS command and return error if it doesn't start
with +OK.
***************************************************************************/
line = io_readln(ioc);
if (line == NULL) {
/* XLOG-DOC:SOS:009e:backend_read_err
* A read from a POP3 backend failed. */
xlog_printf(xlog_sos, 0x009e, "backend_read_err");
return 0;
}
if (strncmp(line, "+OK", 3)) {
/* XLOG-DOC:ADM:009f:login_failed
* The login to a POP3 backend server failed. The answer to the PASS
* command was -ERR. */
xlog_printf(xlog_adm, 0x009f, "login_failed msg='%s'", line);
return 1;
}
/***************************************************************************
User is succesfully authenticated.
***************************************************************************/
return 2;
}
/*****************************************************************************
time_event()
returns diff in milliseconds (1/1000th of a second)
*****************************************************************************/
#ifndef timersub
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
long
time_event(int start)
{
static struct timeval tv_start;
struct timeval tv_end, tv_diff;
if (start) {
if (gettimeofday(&tv_start, NULL) != 0) return -1;
return 0;
} else {
if (gettimeofday(&tv_end, NULL) != 0) return -1;
timersub(&tv_end, &tv_start, &tv_diff);
return tv_diff.tv_sec * 1000 + tv_diff.tv_usec / 1000;
}
}
/*****************************************************************************
mailcheck()
Sends UDP packet to backend to find out, whether the given mailbox is
empty.
*****************************************************************************/
mailcheck_result_t
mailcheck(const char *mbfile)
{
int s;
char buf[MAXLEN_MAILCHECK];
fd_set fdset;
int try;
DEBUG1(DG_NET, "mailcheck", "start mbfile='%s'", mbfile);
s = net_open_udp_socket(&(cs->backend.backend_addr), conf.checkport);
if (s < 0) return mcrError;
/* Send mailbox file name */
(void) strlcpy(buf, "*M", sizeof(buf));
if (strlcat(buf, mbfile, sizeof(buf)) >= sizeof(buf)) {
/* XLOG-DOC:SOS:0102:buffer_to_small
* A buffer for sending a mailbox name to the pcheckd is too small. */
xlog_printf(xlog_sos, 0x0102, "buffer_too_small");
return mcrError;
}
time_event(1); /* start timer */
for (try=0; try < MAILCHECK_MAX_REQUESTS; try++) {
int r;
if (send(s, buf, strlen(buf), 0) != strlen(buf)) {
/* XLOG-DOC:SOS:0103:send_failed
* The send() call sending a request to the pcheckd failed. */
xlog_printf(xlog_sos, 0x0103, "send_failed errno=%d errmsg='%s'", errno, strerror(errno));
return mcrError;
}
FD_ZERO(&fdset);
FD_SET(s, &fdset);
r = rel_select(&fdset, conf.checktimeout, 0);
if (r < 0) {
/* XLOG-DOC:SOS:0104:select_failed
* The select () call waiting for answer from pcheckd failed in an
* unexpected way. */
xlog_printf(xlog_sos, 0x0104, "select_failed errno=%d errmsg='%s'", errno, strerror(errno));
return mcrError;
}
if (FD_ISSET(s, &fdset)) {
goto got_answer;
}
}
cs->mailcheck_time = time_event(0); /* read timer */
cs->mailcheck_retry = try;
/* XLOG-DOC:ERR:0106:check_udp_timeout
* Pproxy timed out waiting for an answer from pcheckd. */
xlog_printf(xlog_err, 0x0106, "check_udp_timeout");
close(s);
return mcrTimeout;
got_answer:
cs->mailcheck_time = time_event(0); /* read timer */
cs->mailcheck_retry = try;
DEBUG2(DG_NET, "mailcheck", "mailcheck retries=%d msecs=%ld", cs->mailcheck_retry, cs->mailcheck_time);
memset(buf, 0, sizeof(buf));
if ((recv(s, buf, sizeof(buf)-1, 0) < 0)) {
/* XLOG-DOC:SOS:0105:recv_failed
* Recv() call failed when reading answer from pcheckd. */
xlog_printf(xlog_sos, 0x0105, "recv_failed errno=%d errmsg='%s'", errno, strerror(errno));
close(s);
return mcrError;
} else {
close(s);
if (! strncmp("+OK 3", buf, 5)) return mcrLoadTooHigh;
if (! strncmp("+OK 4", buf, 5)) return mcrMaxSession;
if (! strncmp("+OK 0", buf, 5)) return mcrEmpty;
return mcrMail;
}
}
/*****************************************************************************
proxy_data()
Copy data between two file descriptors with timeout.
*****************************************************************************/
void
proxy_data(struct io_ctx *ioc1, struct io_ctx *ioc2)
{
fd_set readset;
char buffer[1024*16];
int n;
int fd1 = ioc1->io_fd;
int fd2 = ioc2->io_fd;
DEBUG0(DG_NET, "proxy_data", "start");
io_buf_flush(ioc2);
io_buf_flush(ioc1);
while (1) {
FD_ZERO(&readset);
if ( (!io_pending(ioc1)) && (!io_pending(ioc2)) ) {
int r;
FD_SET(fd1, &readset);
FD_SET(fd2, &readset);
r = rel_select(&readset, conf.proxytimeout, 0);
if (r == 0) {
/* XLOG-DOC:ERR:0107:timeout
* A timeout occurred while proxying data. */
xlog_printf(xlog_err, 0x0107, "timeout");
break;
} else if (r < 0) {
/* XLOG-DOC:SOS:0145:select_error
* An error occurred in the select () system call while proxying
* data. */
xlog_printf(xlog_sos, 0x0145, "select_error errno=%d errmsg='%s'", errno, strerror(errno));
break;
}
}
if (io_pending(ioc1) || FD_ISSET(fd1, &readset)) {
if ((n = io_read(ioc1, buffer, sizeof(buffer))) <= 0) {
DEBUG1(DG_NET, "proxy_data", "read returned (to client): %d", n);
break;
}
if (io_syswrite(ioc2, buffer, n) < 0) break;
cs->bytesin += n;
}
if (io_pending(ioc2) || FD_ISSET(fd2, &readset)) {
if ((n = io_read(ioc2, buffer, sizeof(buffer))) <= 0) {
DEBUG1(DG_NET, "proxy_data", "read returned (to backend): %d", n);
break;
}
if (io_syswrite(ioc1, buffer, n) < 0) break;
cs->bytesout += n;
}
}
/* XLOG-DOC:INF:0108:quit
* The proxy child shuts down after its work is done. The proxied
* bytes in both directions are reported. */
xlog_printf(xlog_inf, 0x0108, "quit reason=shutdown time=%d bytesin=%d bytesout=%d", time(NULL) - cs->starttime, cs->bytesin, cs->bytesout);
}
/*****************************************************************************
method_fakepop()
Fakes POP backend with no mails
*****************************************************************************/
void
method_fakepop(struct io_ctx *ioc, session_state_t state) {
int rc;
cs->state = state;
cs->statetime = time(NULL);
DEBUG1(DG_NET, "method_fakepop", "faking pop server with state=%d", state);
rc = pop3_fake(ioc, &(cs->vs));
if (rc == 1) {
/* XLOG-DOC:INF:0109:quit
* The client closed the connection while faking a POP3 dialog. */
xlog_printf(xlog_inf, 0x0109, "quit reason=client_shutdown time=%d", time(NULL) - cs->starttime);
} else {
/* XLOG-DOC:INF:010a:quit
* The client send a quit while faking a POP3 dialog. */
xlog_printf(xlog_inf, 0x010a, "quit reason=quit time=%d", time(NULL) - cs->starttime);
}
cs->state = sstDone;
cs->statetime = time(NULL);
}
/*****************************************************************************
method_proxy_xpop()
*****************************************************************************/
void
method_proxy_xpop(struct io_ctx *ioc, const char *mailbox, const char *id, int flags)
{
int socket;
struct io_ctx *backend_ioc;
DEBUG0(DG_NET, "method_proxy_xpop", "start");
/***************************************************************************
Now connecting to backend.
***************************************************************************/
cs->state = sstConnecting;
cs->statetime = time(NULL);
socket = connect_to_backend();
if (socket < 0) {
method_fakepop(ioc, sstBackendUnreachable);
return;
}
DEBUG0(DG_NET, "method_proxy_xpop", "connected to backend");
backend_ioc = io_init(iot_plain, socket, "to backend", 0, conf.idletimeout, NULL);
if (! backend_ioc) {
/* XLOG-DOC:SOS:010c:out_of_memory
* There is not enought memory to allocate an IO context. */
xlog_printf(xlog_sos, 0x010c, "out_of_memory");
method_fakepop(ioc, sstBackendUnreachable);
close(socket);
return;
}
/***************************************************************************
Write 3 lines to backend: mailboxfile, id for logging and flags. If this
failes fake POP server.
***************************************************************************/
if (io_writeln(backend_ioc, mailbox) < 0) goto server_err;
if (io_writeln(backend_ioc, id) < 0) goto server_err;
if (io_writeln(backend_ioc, flags == PP_FLAGS_M ? "M" : "") < 0) goto server_err;
DEBUG0(DG_NET, "method_proxy_xpop", "data sent to backend");
/***************************************************************************
And the rest is proxying.
***************************************************************************/
cs->state = sstProxy;
cs->statetime = time(NULL);
proxy_data(ioc, backend_ioc);
return;
server_err:
/* XLOG-DOC:SOS:010d:write_to_backend_failed
* A write call to the backend failed. */
xlog_printf(xlog_sos, 0x010d, "write_to_backend_failed");
method_fakepop(ioc, sstBackendUnreachable);
}
/*****************************************************************************
method_proxy_pop()
*****************************************************************************/
void
method_proxy_pop(struct io_ctx *ioc, int fail_on_error, const char *user, const char *pass)
{
int socket;
struct io_ctx *backend_ioc;
DEBUG0(DG_NET, "method_proxy_pop", "start");
/***************************************************************************
Now connecting to backend.
***************************************************************************/
cs->state = sstConnecting;
cs->statetime = time(NULL);
socket = connect_to_backend();
if (socket < 0) goto failed;
DEBUG0(DG_NET, "method_proxy_pop", "connected to backend");
backend_ioc = io_init(iot_plain, socket, "to backend", 0, conf.idletimeout, NULL);
if (! backend_ioc) {
close(socket);
/* XLOG-DOC:SOS:010f:out_of_memory
* There is not enought memory to allocate an IO context. */
xlog_printf(xlog_sos, 0x010f, "out_of_memory");
goto failed;
return;
}
/***************************************************************************
Authenticate user on backend and do the right thing.
***************************************************************************/
switch (do_pop3_auth(backend_ioc, user, pass)) {
case 0:
DEBUG0(DG_NET, "method_proxy_pop", "backend error");
goto failed;
case 1:
io_writeln(ioc, "-ERR access denied");
break;
case 2:
io_writeln(ioc, "+OK");
DEBUG0(DG_NET, "method_proxy_pop", "authenticated on backend");
cs->state = sstProxy;
cs->statetime = time(NULL);
proxy_data(ioc, backend_ioc);
break;
default:
/* XLOG-DOC:BUG:0110:illegal_value
* The do_pop3_auth() function returned an illegal value. */
xlog_printf(xlog_bug, 0x0110, "illegal_value returned by do_pop3_auth");
exit(RCODE_ERR);
}
return;
failed:
if (fail_on_error) {
io_writeln(ioc, "-ERR temporarily unavailable");
} else {
method_fakepop(ioc, sstBackendUnreachable);
}
}
/*****************************************************************************
child_main()
This function never returns.
*****************************************************************************/
void
child_main(struct proxy_session *this_session, int slot, struct virt_serv *vs)
{
struct pdm_request ar;
struct pdm_data ard;
int fd;
char peerbuf[16];
pdm_result_t result;
struct io_ctx *ioc;
int use_fallback=0;
signal_init_child();
alarm(conf.authtimeout);
/***************************************************************************
Setup session.
***************************************************************************/
cs = this_session;
cs->starttime = time(NULL);
cs->pid = getpid();
memcpy(&(cs->vs), vs, sizeof(struct virt_serv));
fd = cs->fd_client;
if (set_keepalive(fd) < 0) {
DEBUG2(DG_NET, "child_main", "setting TCP keepalive failed errno=%d errmsg='%s'", errno, strerror(errno));
}
/***************************************************************************
Create unique session ID from sidprefix, timestamp and process id.
***************************************************************************/
snprintf(cs->id, sizeof(cs->id), "%s.%d.%d", conf.sidprefix, (int)cs->starttime, (int)cs->pid);
xlog_set_id(cs->id);
DEBUG0(DG_AUTH, "child_main", "start");
/***************************************************************************
Get own address and peer IP address from socket.
***************************************************************************/
{
unsigned int len = sizeof(struct sockaddr_in);
/* own address is in vserv struct, too. maybe we should get it from
* there. This is needed for pstatus here */
if (getsockname(fd, (struct sockaddr *) &cs->sin_client_local, &len) < 0) {
/* XLOG-DOC:ERR:0111:getsockname_error
* getsockname() returned an error. This probably means that the
* client disconnected before we could look at the socket. */
xlog_printf(xlog_err, 0x0111, "getsockname_error errno=%d errmsg='%s'", errno, strerror(errno));
exit(RCODE_OK);
}
len = sizeof(struct sockaddr_in);
if (getpeername(fd, (struct sockaddr *) &cs->sin_client_remote, &len) < 0) {
/* XLOG-DOC:ERR:0112:getpeername_error
* getpeername() returned an error. This probably means that the
* client disconnected before we could look at the socket. */
xlog_printf(xlog_err, 0x0112, "getpeername_error errno=%d errmsg='%s'", errno, strerror(errno));
exit(RCODE_OK);
}
}
/***************************************************************************
Log peer and virt_serv.
***************************************************************************/
{
/* XLOG-DOC:INF:0113:connect
* Incoming connection from client. */
xlog_printf(xlog_inf, 0x0113, "connect vserv=%s remote=%s/%d ns=%s slot=%d",
vs->id,
print_ip(&(cs->sin_client_remote), NULL),
ntohs(cs->sin_client_remote.sin_port),
vs->namespace,
slot);
}
/***************************************************************************
Setup IO context.
***************************************************************************/
{
struct protocol_info *pi = find_protocol_info(vs->prot, NULL);
if (! pi) {
/* XLOG-DOC:BUG:0114:unknown_protocol
* No protocol information for this virtual server is found. This
* should never happen. */
xlog_printf(xlog_bug, 0x0114, "unknown_protocol");
exit(RCODE_ERR);
}
ioc = io_init(pi->tls, fd, "to client", 1, conf.idletimeout, vs);
}
if (! ioc) {
/* XLOG-DOC:SOS:0116:out_of_memory
* There is not enought memory to allocate an IO context. */
xlog_printf(xlog_sos, 0x0116, "out_of_memory");
exit(RCODE_ERR);
}
cs->ioc_client = ioc;
/***************************************************************************
If virtual server is disabled, send error banner to user and shutdown
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "Check is virtual server is disabled");
if (vs->state == vsstDisabled) {
/* XLOG-DOC:INF:0117:virt_serv_disabled
* The virtual server is disabled */
xlog_printf(xlog_inf, 0x0117, "virt_serv_disabled");
if (pop3_banner(ioc, 0, vs->banner_err) < 0) {
/* XLOG-DOC:ERR:0118:write_error
* A write to the client failed. */
xlog_printf(xlog_err, 0x0118, "write_error");
}
exit(RCODE_OK);
}
/***************************************************************************
Output POP3 greeting banner.
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "Output greeting banner");
if (pop3_banner(ioc, 1, vs->banner_ok) < 0) {
/* XLOG-DOC:ERR:0119:write_error
* A write to the client failed. */
xlog_printf(xlog_err, 0x0119, "write_error");
exit(RCODE_OK);
}
/***************************************************************************
Get username and password from client.
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "Get username and password from client");
{
int rc = pop3_authphase(ioc, vs, cs->username, cs->password);
if (rc == -1) { /* error */
exit(RCODE_OK);
} else if (rc == 0) { /* QUIT received */
/* XLOG-DOC:ERR:011b:quit
* Client sent QUIT command in authentication phase. */
xlog_printf(xlog_inf, 0x011b, "quit reason=quit time=%d", time(NULL) - cs->starttime);
exit(RCODE_OK);
}
}
alarm(conf.sessiontimeout);
/***************************************************************************
If virtual server is to be faked, do this.
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "If virtual server state is Fake, do it");
if (vs->state == vsstFake) {
/* XLOG-DOC:INF:011c:virt_serv_fake
* The virtual server is to be faked. */
xlog_printf(xlog_inf, 0x011c, "virt_serv_fake");
method_fakepop(ioc, sstVirtServFake);
exit(RCODE_OK);
}
/***************************************************************************
There is one special case, if the namespace is 'USER' the real
namespace is contained in the username (after a '=' char). We find
the real namespace and put it in the variable 'rns' and modify the
username by throwing away the namespace.
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "Check for USER namespace");
if (!strcmp(vs->namespace, "USER")) {
char *p = strrchr(cs->username, '=');
DEBUG0(DG_AUTH, "child_main", "namespace is USER");
if (p) *p++ = 0;
else p = conf.defaultns;
(void) strlcpy(cs->namespace, p, sizeof(cs->namespace));
} else {
(void) strlcpy(cs->namespace, vs->namespace, sizeof(cs->namespace));
}
/***************************************************************************
Authenticate user.
***************************************************************************/
DEBUG0(DG_MAIN, "child_main", "Authenticate user");
print_ip(&(cs->sin_client_remote), peerbuf),
ar.user = cs->username;
ar.pass = cs->password;
ar.peer = peerbuf;
ar.namespace = cs->namespace;
DEBUG3(DG_AUTH, "child_main", "calling pdm_auth_user() with peer='%s' ns='%s' user='%s'", ar.peer, ar.namespace, ar.user);
result = pdm_auth_user(&ar, &ard);
DEBUG1(DG_AUTH, "child_main", "pdm_auth_user() returned: %d", result);
/* User is authenticated */
cs->state = sstAuthPhase;
cs->statetime = time(NULL);
switch (result) {
case pdmAccept:
break;
case pdmUnknown:
if (conf.fallback[0]) {
use_fallback = 1;
break;
}
io_writeln(ioc, "-ERR access denied");
/* XLOG-DOC:ERR:0177:auth_fail
* The authorisation of the user failed because the user is unknown. */
xlog_printf(xlog_err, 0x0177, "auth_fail reason=user_unknown user='%s'", ar.user);
exit(RCODE_OK);
case pdmFail:
io_writeln(ioc, "-ERR access denied");
switch (ard.reason) {
case pdmFailIP:
/* XLOG-DOC:ERR:011d:auth_fail
* The authorisation of the user failed because of the client
* IP number. */
xlog_printf(xlog_err, 0x011d, "auth_fail reason=IP user='%s' IP=%s", ar.user, ar.peer);
exit(RCODE_OK);
case pdmFailProtocol:
/* XLOG-DOC:ERR:011e:auth_fail
* The authorisation of the user failed because of the protocol
* used. */
xlog_printf(xlog_err, 0x011e, "auth_fail reason=protocol user='%s'", ar.user);
exit(RCODE_OK);
case pdmFailPassword:
/* XLOG-DOC:ERR:011f:auth_fail
* The authorisation of the user failed because the wrong password
* was given. */
xlog_printf(xlog_err, 0x011f, "auth_fail reason=password user='%s'", ar.user);
exit(RCODE_OK);
case pdmFailUnknown:
/* fallthrough */
default:
/* XLOG-DOC:ERR:0120:auth_fail
* The authorisation of the user failed for unknown reasons. */
xlog_printf(xlog_err, 0x0120, "auth_fail reason=unspecified user='%s'", ar.user);
exit(RCODE_OK);
}
case pdmError: /* fallthrough */
default:
io_writeln(ioc, "-ERR internal error");
/* XLOG-DOC:BUG:0121:illegal_auth_result
* A PDM module returned an illegal result code. */
xlog_printf(xlog_bug, 0x0121, "illegal_auth_result");
exit(RCODE_OK);
}
/***************************************************************************
Find backend and do the right thing...
***************************************************************************/
{
struct backend *be;
char *use_backend = use_fallback ? conf.fallback : ard.backend;
be = get_backend(use_backend, 0);
if (be == NULL) {
/* XLOG-DOC:SOS:0122:unknown_backend
* A PDM module told us which backend to use. Unfortunately this
* backend is not configured. */
xlog_printf(xlog_sos, 0x0122, "unknown_backend backend=%s", use_backend);
if (use_fallback) {
io_writeln(ioc, "-ERR internal error");
exit(RCODE_OK);
} else {
method_fakepop(ioc, sstUnknownBackend);
exit(RCODE_OK);
}
}
memcpy(&(cs->backend), be, sizeof(struct backend));
}
memcpy(&(cs->mailbox), ard.user, sizeof(cs->mailbox));
memcpy(&(cs->sin_backend_remote), &(cs->backend.backend_addr), sizeof(struct sockaddr_in));
/* XLOG-DOC:INF:0123:user_auth
* User was authenticated. */
xlog_printf(xlog_inf, 0x0123, "user_auth user='%s' backend=%s host=%s/%d mailbox='%s'", ar.user, cs->backend.id, print_ip(&(cs->backend.backend_addr), NULL), ntohs(cs->backend.backend_addr.sin_port), ard.user);
switch (cs->backend.state) {
case bstFake:
if (! use_fallback) {
/* XLOG-DOC:INF:0124:operation
* The backend is configured to be faked. */
xlog_printf(xlog_inf, 0x0124, "operation do=fake reason=backend");
method_fakepop(ioc, sstBackendFake);
break;
}
/* else fallthrough, because we can't fake for an unauthenticated user */
case bstOffline:
/* XLOG-DOC:INF:0125:quit
* The backend is configured to be offline. */
xlog_printf(xlog_inf, 0x0125, "quit reason=backend_offline time=%d", time(NULL) - cs->starttime);
io_writeln(ioc, "-ERR temporarily unavailable");
break;
case bstOnline:
switch (cs->backend.prot) {
case ptPOP3:
/* XLOG-DOC:INF:0126:operation
* The backend uses POP3 protocol. Proxying will now be started. */
xlog_printf(xlog_inf, 0x0126, "operation do=proxy prot=pop3");
if (use_fallback) {
method_proxy_pop(ioc, use_fallback, ar.user, ar.pass);
} else {
method_proxy_pop(ioc, use_fallback, ard.user, ard.pass);
}
break;
case ptCXPOP:
{
static char *mailcheck_result_name[] = {
"mail", "empty", "timeout", "load", "maxsession"
};
mailcheck_result_t mr = mailcheck(ard.user);
if (mr > 0) {
/* XLOG-DOC:INF:0127:operation
* The backend pcheckd told us that the mailbox is empty, the
* load on the storage server is too high, or the maximum number
* of sessions is reached on the storage server or a timeout
* occured while waiting for the answer from the pcheckd. The
* POP connection will be faked locally. */
xlog_printf(xlog_inf, 0x0127, "operation do=fake reason=%s retries=%d time=%ld host=%s", mailcheck_result_name[mr], cs->mailcheck_retry, cs->mailcheck_time, print_ip(&(cs->backend.backend_addr), NULL));
method_fakepop(ioc, sstMailboxEmpty); /* XXX */
break;
}
}
/* XLOG-DOC:INF:0179:operation
* The backend uses XPOP protocol. Proxying will now be started. */
xlog_printf(xlog_inf, 0x0179, "operation do=proxy prot=xpop retries=%d time=%ld", cs->mailcheck_retry, cs->mailcheck_time);
method_proxy_xpop(ioc, ard.user, cs->id, ard.flags);
break;
case ptXPOP:
/* XLOG-DOC:INF:0128:operation
* The backend uses XPOP protocol. Proxying will now be started. */
xlog_printf(xlog_inf, 0x0128, "operation do=proxy prot=xpop");
method_proxy_xpop(ioc, ard.user, cs->id, ard.flags);
break;
default:
/* XLOG-DOC:BUG:0129:invalid_value
* Invalid value for the backend protocol. */
xlog_printf(xlog_bug, 0x0129, "invalid_value");
break;
}
break;
default:
/* XLOG-DOC:BUG:012a:invalid_value
* Invalid value for the backend state. */
xlog_printf(xlog_bug, 0x012a, "invalid_value");
break;
}
exit(RCODE_OK);
}
/** THE END *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1