/***************************************************************************** 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 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 "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 *****************************************************************/