Newer
Older
/* mailsrvr.c */
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <io.h> /* open/close */
#include <share.h> /* share open flags */
#include <process.h> /* _beginthread */
#include <windows.h> /* required for mmsystem.h */
#include <mmsystem.h> /* SND_ASYNC */
#include <stdlib.h> /* ltoa in GNU C lib */
#include <stdarg.h> /* va_list */
#include <string.h> /* strrchr */
#include <ctype.h> /* isdigit */
#include <fcntl.h> /* Open flags */
#include <errno.h> /* errno */
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "mime.h"
#include "md5.h"
#include "base64.h"
#include "ini_file.h"
#define FORWARD "forward:"
#define NO_FORWARD "local:"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
static char* ok_rsp = "250 OK";
static char* auth_ok = "235 User Authenticated";
static char* sys_error = "421 System error";
static char* sys_unavail= "421 System unavailable, try again later";
static char* badarg_rsp = "501 Bad argument";
static char* badseq_rsp = "503 Bad sequence of commands";
static char* badauth_rsp= "535 Authentication failure";
static char* badrsp_err = "%s replied with:\r\n\"%s\"\r\n"
"instead of the expected reply:\r\n\"%s ...\"";
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define DNSBL_THROTTLE_VALUE 5000 /* Milliseconds */
#define STATUS_WFC "Listening"
static mail_startup_t* startup=NULL;
static scfg_t scfg;
static SOCKET server_socket=INVALID_SOCKET;
static SOCKET pop3_socket=INVALID_SOCKET;
static DWORD active_clients=0;
static DWORD thread_count=0;
static BOOL sendmail_running=FALSE;
static DWORD sockets=0;
static DWORD served=0;
static BOOL terminate_server=FALSE;
static BOOL terminate_sendmail=FALSE;
static sem_t sendmail_wakeup_sem;
static char revision[16];
static time_t uptime;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static int mailproc_count;
struct mailproc {
char cmdline[INI_MAX_VALUE_LEN];
str_list_t to;
BOOL passthru;
BOOL native;
} *mailproc_list;
typedef struct {
SOCKET socket;
SOCKADDR_IN client_addr;
} smtp_t,pop3_t;
static int lprintf(int level, char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
if(startup==NULL || startup->lputs==NULL)
return(0);
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
return(startup->lputs(startup->cbdata,level,sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define SOCKLIB_DESC NULL
#endif
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,active_clients+active_sendmail);
static void client_on(SOCKET sock, client_t* client, BOOL update)
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
thread_count++;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE,setuid);
}
static void thread_down(void)
{
if(thread_count>0)
thread_count--;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);

rswindell
committed
SOCKET mail_open_socket(int type)
char error[256];
SOCKET sock;
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
if(set_socket_options(&scfg, sock,error))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
sockets++;
#if 0 /*def _DEBUG */
lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,sockets);
#endif
}
return(sock);
}

rswindell
committed
int mail_close_socket(SOCKET sock)
if(sock==INVALID_SOCKET)
return(-1);
shutdown(sock,SHUT_RDWR); /* required on Unix */
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_ERR,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,sockets);
#endif
return(result);
}
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
int sockprintf(SOCKET sock, char *fmt, ...)
int maxlen;
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
va_end(argptr);
if(len<0 || len > maxlen) /* format error or output truncated */
len=maxlen;
if(startup->options&MAIL_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d TX: %.*s", sock, len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
return(0);
}
/* Check socket for writability (using select) */
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
lprintf(LOG_NOTICE,"%04d !TIMEOUT selecting socket for send"
lprintf(LOG_NOTICE,"%04d !ERROR %d selecting socket for send"
,sock, ERROR_VALUE);
return(0);
}
while((result=sendsocket(sock,sbuf,len))!=len) {
YIELD();
lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
lprintf(LOG_NOTICE,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
lprintf(LOG_WARNING,"%04d !ERROR: short send on socket: %d instead of %d",sock,result,len);
}
return(len);
}
static time_t checktime(void)
{
struct tm tm;
memset(&tm,0,sizeof(tm));
tm.tm_year=94;
tm.tm_mday=1;

rswindell
committed
return(mktime(&tm)-0x2D24BD00L);

rswindell
committed
static void recverror(SOCKET socket, int rd, int line)
lprintf(LOG_NOTICE,"%04d Socket closed by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %d)"

rswindell
committed
,socket, ERROR_VALUE, line);
lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %d)"

rswindell
committed
,socket, rd, line);

rswindell
committed
static int sockreadline(SOCKET socket, char* buf, int len)
{
char ch;
int i,rd=0;

rswindell
committed
fd_set socket_set;
struct timeval tv;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockreadline");
return(-1);

rswindell
committed
if(server_socket==INVALID_SOCKET || terminate_server) {
lprintf(LOG_WARNING,"%04d !ABORTING sockreadline",socket);
tv.tv_sec=startup->max_inactivity;

rswindell
committed
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(socket,&socket_set);
i=select(socket+1,&socket_set,NULL,NULL,&tv);

rswindell
committed
if(i==0) {
if((time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d !SOCKET INACTIVE",socket);
return(-1);

rswindell
committed
recverror(socket,i,__LINE__);
return(-1);

rswindell
committed
}
i=recv(socket, &ch, 1, 0);
if(i<1) {
recverror(socket,i,__LINE__);
return(-1);
if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
break;
}
buf[rd++]=ch;
}
if(rd>0 && buf[rd-1]=='\r')
rd--;
buf[rd]=0;
return(rd);
}
static BOOL sockgetrsp(SOCKET socket, char* rsp, char *buf, int len)
{
int rd;
while(1) {
rd = sockreadline(socket, buf, len);
if(rd<1)
return(FALSE);
if(buf[3]=='-') { /* Multi-line response */
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d !INVALID RESPONSE: '%s' Expected: '%s'", socket, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
return(TRUE);
}
#define MAX_LINE_LEN 1000
static ulong sockmsgtxt(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxlines)
char toaddr[256]="";
char fromaddr[256]="";
char fromhost[256];
char filepath[MAX_PATH+1]="";
char* boundary=NULL;
char* content_type=NULL;
/* HEADERS (in recommended order per RFC822 4.1) */
if(!sockprintf(socket,"Date: %s",msgdate(msg->hdr.when_written,date)))
return(0);
if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
s=sockprintf(socket,"From: %s",p); /* use original RFC822 header field */
else {
if(msg->from_net.type==NET_INTERNET && msg->from_net.addr!=NULL)
else if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
SAFEPRINTF2(fromaddr,"%s!%s"
,(char*)msg->from_net.addr
,usermailaddr(&scfg,fromhost,msg->from));
else
usermailaddr(&scfg,fromaddr,msg->from);
if(fromaddr[0]=='<')
s=sockprintf(socket,"From: \"%s\" %s",msg->from,fromaddr);
else
s=sockprintf(socket,"From: \"%s\" <%s>",msg->from,fromaddr);
}
if(!s)
return(0);
if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
if(!sockprintf(socket,"Organization: %s"
,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
return(0);
if(!sockprintf(socket,"Subject: %s",msg->subj))
return(0);
if((p=smb_get_hfield(msg,RFC822TO,NULL))!=NULL)
s=sockprintf(socket,"To: %s",p); /* use original RFC822 header field */
else {
if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
s=sockprintf(socket,"To: %s",msg->to); /* Avoid double-@ */
else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
if(strchr((char*)msg->to_net.addr,'<')!=NULL)
s=sockprintf(socket,"To: %s",(char*)msg->to_net.addr);
else
s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
} else {
usermailaddr(&scfg,toaddr,msg->to);
s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(!s)
return(0);
if((p=smb_get_hfield(msg,RFC822REPLYTO,NULL))==NULL) {
if(msg->replyto_net.type==NET_INTERNET)
else if(msg->replyto!=NULL)
p=msg->replyto;
}
if(p!=NULL)
s=sockprintf(socket,"Reply-To: %s",p); /* use original RFC822 header field */
if(!s)
return(0);
if(!sockprintf(socket,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg)))
if(msg->reply_id!=NULL)
if(!sockprintf(socket,"In-Reply-To: %s",msg->reply_id))
return(0);
for(i=0;i<msg->total_hfields;i++) {
if(msg->hfield[i].type==RFC822HEADER) {
if(strnicmp((char*)msg->hfield_dat[i],"Content-Type:",13)==0)
content_type=msg->hfield_dat[i];
if(!sockprintf(socket,"%s",(char*)msg->hfield_dat[i]))
return(0);
} else if(msg->hdr.auxattr&MSG_FILEATTACH && msg->hfield[i].type==FILEATTACH)
strncpy(filepath,(char*)msg->hfield_dat[i],sizeof(filepath)-1);
}
/* Default MIME Content-Type for non-Internet messages */
if(msg->from_net.type!=NET_INTERNET && content_type==NULL) {
/* No content-type specified, so assume IBM code-page 437 (full ex-ASCII) */
sockprintf(socket,"Content-Type: text/plain; charset=IBM437");
sockprintf(socket,"Content-Transfer-Encoding: 8bit");
}
if(msg->hdr.auxattr&MSG_FILEATTACH) {
if(filepath[0]==0) { /* filename stored in subject */
if(msg->idx.to!=0)
snprintf(filepath,sizeof(filepath)-1,"%sfile/%04u.in/%s"
,scfg.data_dir,msg->idx.to,msg->subj);
else
snprintf(filepath,sizeof(filepath)-1,"%sfile/%04u.out/%s"
,scfg.data_dir,msg->idx.from,msg->subj);
}
boundary=mimegetboundary();
mimeheaders(socket,boundary);
sockprintf(socket,"");
mimeblurb(socket,boundary);
sockprintf(socket,"");
mimetextpartheader(socket,boundary);
}
if(!sockprintf(socket,"")) /* Header Terminator */
return(0);
#if 0 /* This is now handled in smb_getmsgtxt() */
for(i=0;i<msg->total_hfields;i++) { /* delivery failure notification? */
if(msg->hfield[i].type==SMTPSYSMSG || msg->hfield[i].type==SMB_COMMENT) {
if(!sockprintf(socket,"%s",(char*)msg->hfield_dat[i]))
return(0);
lines++;
}
}
#endif
while(*p && lines<maxlines) {
tp=strchr(p,'\n');
if(tp) {
if(tp-p>MAX_LINE_LEN)
tp=p+MAX_LINE_LEN;
*tp=0;
}
truncsp(p); /* Takes care of '\r' or spaces */
if(*p=='.')
i=sockprintf(socket,".%.*s",MAX_LINE_LEN,p);
else
i=sockprintf(socket,"%.*s",MAX_LINE_LEN,p);
if(!i)
break;
if(tp==NULL)
break;
p=tp+1;
/* release time-slices every x lines */
if(startup->lines_per_yield
&& !(lines%startup->lines_per_yield))
YIELD();
if(msg->hdr.auxattr&MSG_FILEATTACH) {
sockprintf(socket,"");
lprintf(LOG_INFO,"%04u MIME Encoding and sending %s",socket,filepath);
if(!mimeattach(socket,boundary,filepath))
lprintf(LOG_ERR,"%04u !ERROR opening/encoding/sending %s",socket,filepath);
else {
endmime(socket,boundary);
if(msg->hdr.auxattr&MSG_KILLFILE)
if(remove(filepath)!=0)
lprintf(LOG_WARNING,"%04u !ERROR %d removing %s",socket,filepath);
}
sockprintf(socket,"."); /* End of text */
if(boundary) FREE(boundary);
static u_long resolve_ip(char *inaddr)
char* p;
char* addr;
char buf[128];
HOSTENT* host;
SAFECOPY(buf,inaddr);
addr=buf;
if(*addr=='[' && *(p=lastchar(addr))==']') { /* Support [ip_address] notation */
addr++;
*p=0;
}
if(*addr==0)
return((u_long)INADDR_NONE);
for(p=addr;*p;p++)
if(*p!='.' && !isdigit(*p))
break;
if(!(*p))
if((host=gethostbyname(inaddr))==NULL) {
lprintf(LOG_WARNING,"0000 !ERROR resolving hostname: %s",inaddr);
return((u_long)INADDR_NONE);
}
return(*((ulong*)host->h_addr_list[0]));
}
static void pop3_thread(void* arg)
{
char* p;
char str[128];
char buf[512];
char host_name[128];
char host_ip[64];
char username[LEN_ALIAS+1];
char password[LEN_PASS+1];
char challenge[256];
char digest[MD5_DIGEST_SIZE];
char* response="";
char* msgtxt;
int i;
int rd;
BOOL activity=FALSE;
long l;
long msgs,msgnum;
ulong bytes;
SOCKET socket;
HOSTENT* host;
smb_t smb;
smbmsg_t msg;
user_t user;
client_t client;
mail_t* mail;
pop3_t pop3=*(pop3_t*)arg;
thread_up(TRUE /* setuid */);
free(arg);
socket=pop3.socket;
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 session thread started", socket);
if(startup->pop3_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME);
SAFECOPY(host_ip,inet_ntoa(pop3.client_addr.sin_addr));
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 connection accepted from: %s port %u"
,socket, host_ip, ntohs(pop3.client_addr.sin_port));
if(startup->options&MAIL_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr((char *)&pop3.client_addr.sin_addr
,sizeof(pop3.client_addr.sin_addr),AF_INET);
if(host!=NULL && host->h_name!=NULL)
else
strcpy(host_name,"<no name>");
if(startup->options&MAIL_OPT_DEBUG_POP3
&& !(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name);
for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
lprintf(LOG_INFO,"%04d POP3 HostAlias: %s", socket, host->h_aliases[i]);
if(trashcan(&scfg,host_ip,"ip")) {
lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT IP ADDRESS: %s"
,socket, host_ip);
sockprintf(socket,"-ERR Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT HOSTNAME: %s"
,socket, host_name);
sockprintf(socket,"-ERR Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
active_clients++, update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.port=ntohs(pop3.client_addr.sin_port);
client.protocol="POP3";
client.user="<unknown>";
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF(str,"POP3: %s", host_ip);
do {
memset(&smb,0,sizeof(smb));
memset(&msg,0,sizeof(msg));

rswindell
committed
memset(&user,0,sizeof(user));
password[0]=0;
srand(time(NULL)); /* seed random number generator */
rand(); /* throw-away first result */
safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>"
,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
sockprintf(socket,"+OK Synchronet POP3 Server %s-%s Ready %s"
,revision,PLATFORM_DESC,challenge);
/* Requires USER command first */
for(i=3;i;i--) {
if(!sockgetrsp(socket,NULL,buf,sizeof(buf)))
break;
if(!strnicmp(buf,"USER ",5))
break;
if(!strnicmp(buf,"APOP ",5)) {
apop=TRUE;
break;
}
sockprintf(socket,"-ERR USER or APOP command expected");
if(!i || buf[0]==0) /* no USER or APOP command received */
break;
SKIP_WHITESPACE(p);
if(apop) {
if((response=strrchr(p,' '))!=NULL)
*(response++)=0;
else
response=p;
}
if(!apop) {
sockprintf(socket,"+OK");
if(!sockgetrsp(socket,"PASS ",buf,sizeof(buf))) {
sockprintf(socket,"-ERR PASS command expected");
break;
}
p=buf+5;
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/);

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s (password: %s)"

rswindell
committed
,socket, username, password);
else
lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s"

rswindell
committed
,socket, username);
break;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d getting data on user (%s)"
break;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !POP3 DELETED or INACTIVE user #%u (%s)"
,socket, user.number, username);
if(apop) {
strlwr(user.pass); /* this is case-sensitive, so convert to lowercase */
strcat(challenge,user.pass);
MD5_calc(digest,challenge,strlen(challenge));
MD5_hex(str,digest);
if(strcmp(str,response)) {
lprintf(LOG_WARNING,"%04d !POP3 %s FAILED APOP authentication"
lprintf(LOG_DEBUG,"%04d !POP3 digest data: %s",socket,challenge);
lprintf(LOG_DEBUG,"%04d !POP3 calc digest: %s",socket,str);
lprintf(LOG_DEBUG,"%04d !POP3 resp digest: %s",socket,response);
#endif
sockprintf(socket,pop_err);
break;
}
} else if(stricmp(password,user.pass)) {

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s: '%s' expected '%s'"

rswindell
committed
,socket, username, password, user.pass);
else
lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s"

rswindell
committed
,socket, username);
putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
/* Update client display */
client.user=user.alias;
client_on(socket,&client,TRUE /* update */);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 %s logged in %s", socket, user.alias, apop ? "via APOP":"");
SAFEPRINTF(str,"POP3: %s",user.alias);
sprintf(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d !POP3 MAIL BASE LOCKED: %s",socket,smb.last_error);
sockprintf(socket,"-ERR database locked, try again later");
break;
}
smb.retry_time=scfg.smb_retry_time;
smb.subnum=INVALID_SUB;
if((i=smb_open(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) opening %s",socket,i,smb.last_error,smb.file);
sockprintf(socket,"-ERR %d opening %s",i,smb.file);
break;
}
mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,0);
for(l=bytes=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
bytes+=smb_getmsgtxtlen(&msg);
if(l<msgs) {
sockprintf(socket,"-ERR message #%d: %d (%s)"
,mail[l].number,i,smb.last_error);
break;
}
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
while(1) { /* TRANSACTION STATE */
rd = sockreadline(socket, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 RX: %s", socket, buf);
if(!stricmp(buf, "NOOP")) {
sockprintf(socket,"+OK");
continue;
}
if(!stricmp(buf, "QUIT")) {
sockprintf(socket,"+OK");
break;
}
if(!stricmp(buf, "STAT")) {
sockprintf(socket,"+OK %lu %lu",msgs,bytes);
continue;
}
if(!stricmp(buf, "RSET")) {
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
msg.hdr.attr=mail[l].attr;
msg.idx.attr=msg.hdr.attr;
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) updating message index"
,socket, i, smb.last_error);
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
}
if(l<msgs)
sockprintf(socket,"-ERR %d messages reset (ERROR: %d)",l,i);
else
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
continue;
}
if(!strnicmp(buf, "LIST",4) || !strnicmp(buf,"UIDL",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(isdigit(*p)) {
msgnum=atol(p);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_WARNING,"%04d !POP3 INVALID message #%ld"
,socket, msgnum);
sockprintf(socket,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
sockprintf(socket,"-ERR %d getting message index",i);
break;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_WARNING,"%04d !POP3 ATTEMPT to list DELETED message"
,socket);
sockprintf(socket,"-ERR message deleted");
continue;
}
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d locking message header",i);
continue;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d getting message header",i);
continue;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,"+OK %lu %lu",msgnum,smb_getmsgtxtlen(&msg));
} else /* UIDL */
sockprintf(socket,"+OK %lu %lu",msgnum,msg.hdr.number);
smb_freemsgmem(&msg);
continue;
}
/* List ALL messages */
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
break;
}
if(msg.idx.attr&MSG_DELETE)
continue;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);