Newer
Older
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */
/* $Id$ */
// vi: tabstop=4
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 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 <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 "utf8.h"
#include "mime.h"
#include "md5.h"
#include "base64.h"
#include "ini_file.h"
#include "netwrap.h" /* getNameServerList() */
#include "xpendian.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "ssl.h"
#include "cryptlib.h"
static const char* server_name="Synchronet Mail Server";
#define FORWARD "forward:"
#define NO_FORWARD "local:"
#define NO_SPAM "#nospam"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
#define pop_err "-ERR"
#define ok_rsp "250 OK"
#define auth_ok "235 User Authenticated"
#define sys_error "421 System error"
#define sys_unavail "421 System unavailable, try again later"
#define insuf_stor "452 Insufficient system storage"
#define badarg_rsp "501 Bad argument"
#define badseq_rsp "503 Bad sequence of commands"
#define badauth_rsp "535 Authentication failure"
#define badrsp_err "%s replied with:\r\n\"%s\"\r\ninstead of the expected reply:\r\n\"%s ...\""
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define DNSBL_THROTTLE_VALUE 1000 /* Milliseconds */
#define STATUS_WFC "Listening"
static mail_startup_t* startup=NULL;
static scfg_t scfg;
static struct xpms_set *mail_set=NULL;
static BOOL terminated=FALSE;
static protected_uint32_t active_clients;
static protected_uint32_t thread_count;
static volatile int active_sendmail=0;
static volatile BOOL sendmail_running=FALSE;
static volatile BOOL terminate_server=FALSE;
static volatile BOOL terminate_sendmail=FALSE;
static sem_t sendmail_wakeup_sem;
static char revision[16];
static volatile time_t uptime;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static int mailproc_count;
static js_server_props_t js_server_props;
struct {
volatile ulong sockets;
volatile ulong errors;
volatile ulong crit_errors;
volatile ulong connections_ignored;
volatile ulong connections_refused;
volatile ulong connections_served;
volatile ulong pop3_served;
volatile ulong smtp_served;
/* SMTP: */
volatile ulong sessions_refused;
volatile ulong msgs_ignored;
volatile ulong msgs_refused;
volatile ulong msgs_received;
} stats;
struct mailproc {
char cmdline[INI_MAX_VALUE_LEN];
str_list_t to;
str_list_t from;
BOOL passthru;
BOOL native;
BOOL ignore_on_error; /* Ignore mail message if cmdline fails */
BOOL disabled;
BOOL process_spam;
BOOL process_dnsbl;
uint8_t* ar;
ulong handled; /* counter (for stats display) */
} *mailproc_list;
typedef struct {
SOCKET socket;
BOOL tls_port;
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
lprintf(GCES_level, "%04d %s/TLS %s", sock, server, GCES_estr); \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
lprintf(GCES_level, "%04d %s/TLS [%s] %s", sock, server, host, GCES_estr); \
#if defined(__GNUC__) // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
static int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
if(level <= LOG_ERR) {
char errmsg[sizeof(sbuf)+16];
SAFEPRINTF(errmsg, "mail %s", sbuf);
errorlog(&scfg,startup==NULL ? NULL:startup->host_name,errmsg), stats.errors++;
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata,level,errmsg);
}
if(level <= LOG_CRIT)
stats.crit_errors++;
if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
return(0);
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return(0);
#endif
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_DEBUG,"%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,protected_uint32_value(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)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE,setuid);
static int32_t thread_down(void)
int32_t count = protected_uint32_adjust(&thread_count,-1);
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);
return count;
char error[256];
char section[128];
startup->socket_open(startup->cbdata,TRUE);
SAFEPRINTF(section,"mail|%s",protocol);
if(set_socket_options(&scfg, sock, section, error, sizeof(error)))
lprintf(LOG_ERR,"%04d %s !ERROR %s", sock, protocol, error);
stats.sockets++;
}
void mail_close_socket_cb(SOCKET sock, void* cb_protocol)
{
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;
int mail_close_socket(SOCKET *sock, int *sess)
if (*sess != -1) {
cryptDestroySession(*sess);
*sess = -1;
}
if(*sock==INVALID_SOCKET)
return(-1);
shutdown(*sock,SHUT_RDWR); /* required on Unix */
result=closesocket(*sock);
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",*sock,stats.sockets);
*sock = -1;
return(result);
}
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
int sockprintf(SOCKET sock, const char* prot, CRYPT_SESSION sess, 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 %s TX: %.*s", sock, prot, len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockprintf", prot);
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 %s !TIMEOUT selecting socket for send"
,sock, prot);
lprintf(LOG_NOTICE,"%04d %s !ERROR %d selecting socket for send"
,sock, prot, ERROR_VALUE);
return(0);
}
if (sess != -1) {
int tls_sent;
int sent = 0;
while (sent < len) {
result = cryptPushData(sess, sbuf+sent, len-sent, &tls_sent);
if (result == CRYPT_OK)
sent += tls_sent;
GCES(result, prot, sock, sess, "pushing data");
if ((result = cryptFlushData(sess)) != CRYPT_OK) {
GCES(result, prot, sock, sess, "flushing data");
}
else {
// It looks like this could stutter on partial sends -- Deuce
while((result=sendsocket(sock,sbuf,len))!=len) {
if(result==SOCKET_ERROR) {
if(ERROR_VALUE==EWOULDBLOCK) {
YIELD();
continue;
}
if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on send",sock,prot);
lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on send",sock, prot);
lprintf(LOG_NOTICE,"%04d %s !ERROR %d sending on socket",sock,prot,ERROR_VALUE);
lprintf(LOG_WARNING,"%04d %s !ERROR: short send on socket: %d instead of %d",sock,prot,result,len);
}
}
return(len);
}
static void sockerror(SOCKET socket, const char* prot, int rd, const char* action)
lprintf(LOG_NOTICE,"%04d %s Socket closed by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s !SOCKET ERROR %d on %s"
,socket, prot, ERROR_VALUE, action);
lprintf(LOG_WARNING,"%04d %s !SOCKET ERROR: unexpected return value %d from %s"
,socket, prot, rd, action);
static int sock_recvbyte(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *buf, time_t start)
{
int len=0;
fd_set socket_set;
struct timeval tv;
int ret;
int i;
if (sess > -1) {
while (1) {
ret = cryptPopData(sess, buf, 1, &len);
GCES(ret, prot, sock, sess, "popping data");
switch(ret) {
case CRYPT_OK:
break;
case CRYPT_ERROR_TIMEOUT:
return -1;
case CRYPT_ERROR_COMPLETE:
return 0;
default:
if (ret < -1)
return ret;
return -2;
}
if (len)
return len;
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, prot, startup->max_inactivity);
return(-1);
}
}
}
else {
tv.tv_sec=startup->max_inactivity;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,&socket_set,NULL,NULL,&tv);

rswindell
committed
if(i<1) {
if(i==0) {
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, prot, startup->max_inactivity);
return(-1);
}
return 0;
}
sockerror(sock,prot,i,"select");
return 0;
}
i=recv(sock, buf, 1, 0);
if(i<1)
sockerror(sock,prot,i,"receive");
static int sockreadline(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* buf, int len)
{
char ch;
int i,rd=0;
time_t start;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockreadline", prot);
return(-1);

rswindell
committed
lprintf(LOG_WARNING,"%04d %s !ABORTING sockreadline",socket, prot);
i = sock_recvbyte(socket, prot, sess, &ch, start);

rswindell
committed
return(-1);
if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
if(rd>0 && buf[rd-1]=='\r')
rd--;
buf[rd]=0;
static BOOL sockgetrsp(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *buf, int len)
{
int rd;
while(1) {
rd = sockreadline(socket, prot, sess, buf, len);
if(rd<1) {
if(rd==0 && rsp != NULL)
lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
if(buf[3]=='-') { /* Multi-line response */
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
static int sockgetrsp_opt(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *opt, char *buf, int len)
{
int rd;
int ret = 0;
size_t moptlen;
char *mopt;
moptlen = strlen(rsp)+strlen(opt) + 1;
mopt = malloc(moptlen+1);
if (mopt == NULL)
return -1;
sprintf(mopt, "%s-%s", rsp, opt);
while(1) {
rd = sockreadline(socket, prot, sess, buf, len);
lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
free(mopt);
return(-1);
}
if(buf[3]=='-') { /* Multi-line response */
if (strncmp(buf, mopt, moptlen) == 0)
ret = 1;
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
continue;
}
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
free(mopt);
return(-1);
}
break;
}
mopt[strlen(rsp)] = ' ';
if (strncmp(buf, mopt, moptlen) == 0)
free(mopt);
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
/* non-standard, but documented (mostly) in draft-newman-msgheader-originfo-05 */
void originator_info(SOCKET socket, const char* protocol, CRYPT_SESSION sess, smbmsg_t* msg)
{
char* user = msg->from_ext;
char* login = smb_get_hfield(msg,SENDERUSERID,NULL);
char* server = smb_get_hfield(msg,SENDERSERVER,NULL);
char* client = smb_get_hfield(msg,SENDERHOSTNAME,NULL);
char* addr = smb_get_hfield(msg,SENDERIPADDR,NULL);
char* prot = smb_get_hfield(msg,SENDERPROTOCOL,NULL);
char* port = smb_get_hfield(msg,SENDERPORT,NULL);
char* time = smb_get_hfield(msg,SENDERTIME,NULL);
if(user || login || server || client || addr || prot || port || time)
sockprintf(socket, protocol, sess
,"X-Originator-Info: account=%s; login-id=%s; server=%s; client=%s; addr=%s; prot=%s; port=%s; time=%s"
,user
,login
,server
,client
,addr
,prot
,port
,time
);
}
/* RFC822: The maximum total length of a text line including the
<CRLF> is 1000 characters (but not counting the leading
dot duplicated for transparency).
POP3 (RFC1939) actually calls for a 512 byte line length limit!
*/
#define MAX_LINE_LEN 998
static ulong sockmimetext(SOCKET socket, const char* prot, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines
,str_list_t file_list, char* mime_boundary)
char toaddr[256]="";
char fromaddr[256]="";
char fromhost[256];
uchar* p;
char* content_type=NULL;
int len,tlen;
/* HEADERS (in recommended order per RFC822 4.1) */
if(msg->reverse_path!=NULL)
if(!sockprintf(socket,prot,sess,"Return-Path: %s", msg->reverse_path))
return(0);
for(i=0;i<msg->total_hfields;i++)
if(msg->hfield[i].type == SMTPRECEIVED && msg->hfield_dat[i]!=NULL)
if(!sockprintf(socket,prot,sess,"Received: %s", (char*)msg->hfield_dat[i]))
return(0);
if(!sockprintf(socket,prot,sess,"Date: %s",msgdate(msg->hdr.when_written,date)))
if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
s=sockprintf(socket,prot,sess,"From: %s",p); /* use original RFC822 header field */
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 if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL) {
faddr_t* faddr = (faddr_t *)msg->from_net.addr;
char faddrstr[128];
SAFEPRINTF2(fromname,"\"%s\" (%s)", msg->from, smb_faddrtoa(faddr, NULL));
if(faddr->point)
SAFEPRINTF4(faddrstr,"p%hu.f%hu.n%hu.z%hu"FIDO_TLD
,faddr->point, faddr->node, faddr->net, faddr->zone);
else
SAFEPRINTF3(faddrstr,"f%hu.n%hu.z%hu"FIDO_TLD
,faddr->node, faddr->net, faddr->zone);
SAFEPRINTF2(fromaddr,"%s@%s", usermailaddr(NULL,fromhost,msg->from), faddrstr);
} else if(msg->from_net.type!=NET_NONE && msg->from_net.addr!=NULL)
SAFECOPY(fromaddr,(char*)msg->from_net.addr);
else
usermailaddr(&scfg,fromaddr,msg->from);
if(fromaddr[0]=='<')
s=sockprintf(socket,prot,sess,"From: %s %s",fromname,fromaddr);
else
s=sockprintf(socket,prot,sess,"From: %s <%s>",fromname,fromaddr);
}
if(!s)
return(0);
if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
if(!sockprintf(socket,prot,sess,"Organization: %s"
,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
return(0);
if(!sockprintf(socket,prot,sess,"Subject: %s",msg->subj))
if(msg->to_list != NULL)
s=sockprintf(socket,prot,sess,"To: %s", msg->to_list); /* use original RFC822 header field */
else {
if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
s=sockprintf(socket,prot,sess,"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,prot,sess,"To: %s",(char*)msg->to_net.addr);
else
s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
s=sockprintf(socket,prot,sess,"To: \"%s\" (%s)",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
} else {
usermailaddr(&scfg,toaddr,msg->to);
s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(!s)
return(0);
if(msg->cc_list != NULL)
if(!sockprintf(socket,prot,sess,"Cc: %s", msg->cc_list))
return(0);
if((p = (uchar*)msg->replyto_list) == NULL) {
if(msg->replyto_net.type==NET_INTERNET)
if(p!=NULL && strchr((char*)p, '@') != NULL) {
s=sockprintf(socket,prot,sess,"Reply-To: \"%s\" <%s>",np,p);
s=sockprintf(socket,prot,sess,"Reply-To: %s",p);
if(!s)
return(0);
if(!sockprintf(socket,prot,sess,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg,msgid,sizeof(msgid))))
if(msg->reply_id!=NULL)
if(!sockprintf(socket,prot,sess,"In-Reply-To: %s",msg->reply_id))
if(msg->hdr.priority != SMB_PRIORITY_UNSPECIFIED)
if(!sockprintf(socket,prot,sess,"X-Priority: %u", (uint)msg->hdr.priority))
return(0);
originator_info(socket, prot, sess, msg);
/* Include all possible FidoNet header fields here */
for(i=0;i<msg->total_hfields;i++) {
switch(msg->hfield[i].type) {
case FIDOCTRL:
case FIDOAREA:
case FIDOSEENBY:
case FIDOPATH:
case FIDOMSGID:
case FIDOREPLYID:
case FIDOPID:
case FIDOFLAGS:
case FIDOTID:
if(!sockprintf(socket, prot, sess, "%s: %s", smb_hfieldtype(msg->hfield[i].type), (char*)msg->hfield_dat[i]))
return(0);
break;
}
}
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,prot, sess,"%s",(char*)msg->hfield_dat[i]))
/* Default MIME Content-Type for non-Internet messages */
if(msg->from_net.type!=NET_INTERNET && content_type==NULL && startup->default_charset[0]) {
/* No content-type specified, so assume IBM code-page 437 (full ex-ASCII) */
sockprintf(socket,prot,sess,"Content-Type: text/plain; charset=%s", startup->default_charset);
sockprintf(socket,prot,sess,"Content-Transfer-Encoding: 8bit");
}
if(strListCount(file_list)) { /* File attachments */
mimeheaders(socket,prot,sess,mime_boundary);
sockprintf(socket,prot,sess,"");
mimeblurb(socket,prot,sess,mime_boundary);
sockprintf(socket,prot,sess,"");
mimetextpartheader(socket,prot,sess,mime_boundary, startup->default_charset);
if(!sockprintf(socket,prot,sess,"")) /* Header Terminator */
np=msgtxt;
while(*np && lines<maxlines) {
len=0;
while(len<MAX_LINE_LEN && *(np+len)!=0 && *(np+len)!='\n')
len++;
tlen=len;
while(tlen && *(np+(tlen-1))<=' ') /* Takes care of '\r' or spaces */
tlen--;
if(!sockprintf(socket,prot,sess, "%s%.*s", *np=='.' ? ".":"", tlen, np))
if(*(np+len)=='\r')
len++;
if(*(np+len)=='\n')
len++;
/* release time-slices every x lines */
if(startup->lines_per_yield
&& !(lines%startup->lines_per_yield))
YIELD();
if((lines%100) == 0)
lprintf(LOG_DEBUG,"%04d %s sent %lu lines (%ld bytes) of body text"
,socket, prot, lines, (long)(np-msgtxt));
lprintf(LOG_DEBUG,"%04d %s sent %lu lines (%ld bytes) of body text"
,socket, prot, lines, (long)(np-msgtxt));
if(file_list!=NULL) {
for(i=0;file_list[i];i++) {
sockprintf(socket,prot,sess,"");
lprintf(LOG_INFO,"%04u %s MIME Encoding and sending %s", socket, prot, file_list[i]);
if(!mimeattach(socket,prot,sess,mime_boundary,file_list[i]))
lprintf(LOG_ERR,"%04u %s !ERROR opening/encoding/sending %s", socket, prot, file_list[i]);
endmime(socket,prot,sess,mime_boundary);
if(msg->hdr.auxattr&MSG_KILLFILE)
if(remove(file_list[i])!=0)
lprintf(LOG_WARNING,"%04u %s !ERROR %d (%s) removing %s", socket, prot, errno, strerror(errno), file_list[i]);
sockprintf(socket,prot,sess,"."); /* End of text */
static ulong sockmsgtxt(SOCKET socket, const char* prot, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines)
{
char filepath[MAX_PATH+1];
ulong retval;
char* boundary=NULL;
unsigned i;
str_list_t file_list=NULL;
str_list_t split;
if(msg->hdr.auxattr&MSG_FILEATTACH) {
boundary = mimegetboundary();
file_list = strListInit();
/* Parse subject (if necessary) */
if(!strListCount(file_list)) { /* filename(s) stored in subject */
split=strListSplitCopy(NULL,msg->subj," ");
if(split!=NULL) {
for(i=0;split[i];i++) {
if(msg->idx.to!=0)
SAFEPRINTF3(filepath,"%sfile/%04u.in/%s"
,scfg.data_dir,msg->idx.to,getfname(truncsp(split[i])));
else
SAFEPRINTF3(filepath,"%sfile/%04u.out/%s"
,scfg.data_dir,msg->idx.from,getfname(truncsp(split[i])));
strListPush(&file_list,filepath);
}
strListFree(&split);
}
}
}
retval = sockmimetext(socket,prot,sess,msg,msgtxt,maxlines,file_list,boundary);
strListFree(&file_list);
if(boundary!=NULL)
free(boundary);
return(retval);
}
static u_long resolve_ip(const 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((uchar)*p))
break;
if(!(*p))
if((host=gethostbyname(inaddr))==NULL)
return((u_long)INADDR_NONE);
return(*((ulong*)host->h_addr_list[0]));
}
/****************************************************************************/
/* Consecutive failed login (possible password hack) attempt tracking */
/****************************************************************************/
/* Counter is global so it is tracked between multiple connections. */
/* Failed consecutive login attempts > 10 will generate a hacklog entry and */
/* immediately disconnect (after the usual failed-login delay). */
/* A failed login from a different host resets the counter. */
/* A successful login from the same host resets the counter. */
/****************************************************************************/
static void badlogin(SOCKET sock, CRYPT_SESSION sess, const char* prot, const char* resp, char* user, char* passwd, char* host, union xp_sockaddr* addr)
{
char reason[128];
ulong count;
if(user == NULL)
user = "<unspecified>";
if(addr!=NULL) {
SAFEPRINTF(reason,"%s LOGIN", prot);
count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
hacklog(&scfg, reason, user, passwd, host, addr);
if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
SAFEPRINTF(reason, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS (%lu)", count);
filter_ip(&scfg, (char*)prot, reason ,host, ip, user, /* fname: */NULL);
}
}
sockprintf(sock,prot,sess, "%s", resp);
}
static void pop3_thread(void* arg)
{
char* p;
char str[128];
char buf[512];
char host_name[128];
char username[128];
char password[128];
uchar digest[MD5_DIGEST_SIZE];
char* response="";
char* msgtxt;
int i;
int rd;
long lm_mode = 0;
uint32_t msgnum;
ulong bytes;
SOCKET socket;
smb_t smb;
smbmsg_t msg;
user_t user;
client_t client;
mail_t* mail;
pop3_t pop3=*(pop3_t*)arg;
CRYPT_SESSION session = -1;
BOOL nodelay=TRUE;
ulong nb = 0;
char *estr;
int level;
int stat;
SetThreadName("sbbs/pop3");
thread_up(TRUE /* setuid */);
free(arg);
socket=pop3.socket;
client.protocol = pop3.tls_port ? "POP3S" : "POP3";
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s session thread started", socket, client.protocol);
if(startup->pop3_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
,socket, client.protocol, host_ip, inet_addrport(&pop3.client_addr));
SAFECOPY(host_name, STR_NO_HOSTNAME);
if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
getnameinfo(&pop3.client_addr.addr, pop3.client_addr_len, host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s Hostname: %s [%s]", socket, client.protocol, host_name, host_ip);
if (pop3.tls_port) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s !Failure getting certificate: %s", socket, client.protocol, estr);
free_crypt_attrstr(estr);
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, CRYPT_UNUSED, "creating session");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "disabling certificate verification");
mail_close_socket(&socket, &session);
thread_down();
return;
}
lock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting private key");
mail_close_socket(&socket, &session);
thread_down();
return;
}
nodelay = TRUE;
setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(socket,FIONBIO,&nb);
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session socket");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session active");
mail_close_socket(&socket, &session);
thread_down();
return;
}
unlock_ssl_cert();
if (startup->max_inactivity) {
if (cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "setting read timeout");
mail_close_socket(&socket, &session);
thread_down();
return;
}
}
}
ulong banned = loginBanned(&scfg, startup->login_attempt_list, socket, host_name, startup->login_attempt, &attempted);
if(banned || trashcan(&scfg,host_ip,"ip")) {
if(banned) {
char ban_duration[128];
lprintf(LOG_NOTICE, "%04d %s !TEMPORARY BAN of %s (%lu login attempts, last: %s) - remaining: %s"
,socket, client.protocol, host_ip, attempted.count-attempted.dupes, attempted.user, seconds_to_str(banned, ban_duration));
lprintf(LOG_NOTICE,"%04d %s !CLIENT IP ADDRESS BLOCKED: %s",socket, client.protocol, host_ip);
sockprintf(socket,client.protocol,session,"-ERR Access denied.");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf(LOG_NOTICE,"%04d %s !CLIENT HOSTNAME BLOCKED: %s"
,socket, client.protocol, host_name);
sockprintf(socket,client.protocol,session,"-ERR Access denied.");
mail_close_socket(&socket, &session);
thread_down();
return;
}
protected_uint32_adjust(&active_clients, 1);
update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time32(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.user=STR_UNKNOWN_USER;
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF2(str,"%s: %s", client.protocol, host_ip);
&& (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) {
lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%lu login attempts)"
,socket, client.protocol, host_ip, login_attempts);
mswait(login_attempts*startup->login_attempt.throttle);
}
do {
memset(&smb,0,sizeof(smb));
memset(&msg,0,sizeof(msg));

rswindell
committed
memset(&user,0,sizeof(user));
password[0]=0;
srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId())); /* 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,client.protocol,session,"+OK Synchronet %s Server %s-%s Ready %s"
,client.protocol, revision,PLATFORM_DESC,challenge);
/* Requires USER or APOP command first */
for(i=5;i;i--) {
if(!sockgetrsp(socket,client.protocol,session,NULL,buf,sizeof(buf)))
break;
if(!strnicmp(buf,"USER ",5))
break;
break;
else if (!stricmp(buf, "CAPA")) {
// Capabilities
sockprintf(socket,client.protocol,session, "+OK Capability list follows");
sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s-%s\r\n%s.", revision, PLATFORM_DESC, (session != -1 || get_ssl_cert(&scfg, NULL, NULL) == -1) ? "" : "STLS\r\n");
i++;
}
else if (!stricmp(buf, "STLS")) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s !TLS Failure getting certificate: %s", socket, client.protocol, estr);
free_crypt_attrstr(estr);
}
sockprintf(socket,client.protocol,session,"-ERR STLS command not supported");
sockprintf(socket,client.protocol,session,"+OK Begin TLS negotiation");
if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, CRYPT_UNUSED, "creating session");
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "disabling certificate verification");
lock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting private key");
buf[0] = 0;
break;
}
nodelay = TRUE;
setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(socket,FIONBIO,&nb);
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting network socket");
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session active");
unlock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "setting read timeout");
buf[0] = 0;
break;
}
}
i++;
client.protocol = "POP3S";
sockprintf(socket,client.protocol,session,"-ERR USER, APOP, CAPA, or STLS 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((p = strstr(username, NO_SPAM)) != NULL) {
*p = 0;
lm_mode = LM_NOSPAM;
} else
lm_mode = 0;
sockprintf(socket,client.protocol,session,"+OK");
if(!sockgetrsp(socket,client.protocol,session,"PASS ",buf,sizeof(buf))) {
sockprintf(socket,client.protocol,session,"-ERR PASS command expected");
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/);

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_NOTICE,"%04d %s [%s] !UNKNOWN USER: '%s' (password: %s)"
,socket, client.protocol, host_ip, username, password);

rswindell
committed
else
lprintf(LOG_NOTICE,"%04d %s [%s] !UNKNOWN USER: '%s'"
,socket, client.protocol, host_ip, username);
badlogin(socket, session, client.protocol, pop_err, username, password, host_name, &pop3.client_addr);
break;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf(LOG_ERR,"%04d %s [%s] !ERROR %d getting data on user (%s)"
,socket, client.protocol, host_ip, i, username);
badlogin(socket, session, client.protocol, pop_err, NULL, NULL, NULL, NULL);
break;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_NOTICE,"%04d %s [%s] !DELETED or INACTIVE user #%u (%s)"
,socket, client.protocol, host_ip, user.number, username);
badlogin(socket, session, client.protocol, pop_err, NULL, NULL, NULL, NULL);
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((BYTE*)str,digest);
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED APOP authentication: %s"
,socket, client.protocol, host_ip, username);
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);
badlogin(socket, session, client.protocol, pop_err, username, response, host_name, &pop3.client_addr);
break;
}
} else if(stricmp(password,user.pass)) {

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED Password attempt for user %s: '%s' expected '%s'"
,socket, client.protocol, host_ip, username, password, user.pass);

rswindell
committed
else
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED Password attempt for user %s"
,socket, client.protocol, host_ip, username);
badlogin(socket, session, client.protocol, pop_err, username, password, host_name, &pop3.client_addr);
if(user.pass[0])
loginSuccess(startup->login_attempt_list, &pop3.client_addr);
putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
/* Update client display */
client.user=user.alias;
client_on(socket,&client,TRUE /* update */);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s [%s] %s logged in %s", socket, client.protocol, host_ip, user.alias, apop ? "via APOP":"");
SAFEPRINTF2(str,"%s: %s", client.protocol, user.alias);
SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d %s <%s> !MAIL BASE LOCKED: %s",socket, client.protocol, user.alias, smb.last_error);
sockprintf(socket,client.protocol,session,"-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 %s <%s> !ERROR %d (%s) opening %s", socket, client.protocol, user.alias, i, smb.last_error,smb.file);
sockprintf(socket,client.protocol,session,"-ERR %d opening %s",i,smb.file);
break;
}
mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,lm_mode);
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 %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
bytes+=smb_getmsgtxtlen(&msg);
sockprintf(socket,client.protocol,session,"-ERR message #%d: %d (%s)"
,mail[l].number,i,smb.last_error);
break;
}
sockprintf(socket,client.protocol,session,"+OK %u messages (%lu bytes)",msgs,bytes);
while(1) { /* TRANSACTION STATE */
rd = sockreadline(socket, client.protocol, session, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s RX: %s", socket, client.protocol, buf);
sockprintf(socket,client.protocol,session,"+OK");
continue;
}
if(!stricmp(buf, "QUIT")) {
sockprintf(socket,client.protocol,session,"+OK");
break;
}
if(!stricmp(buf, "STAT")) {
sockprintf(socket,client.protocol,session,"+OK %u %lu",msgs,bytes);
continue;
}
if(!stricmp(buf, "RSET")) {
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d locking message base",i);
continue;
}
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
break;
}
msg.hdr.attr=mail[l].attr;
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) updating message index"
,socket, client.protocol, user.alias, i, smb.last_error);
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
}
smb_unlocksmbhdr(&smb);
sockprintf(socket,client.protocol,session,"-ERR %d messages reset (ERROR: %d)",l,i);
sockprintf(socket,client.protocol,session,"+OK %u messages (%lu bytes)",msgs,bytes);
continue;
}
if(!strnicmp(buf, "LIST",4) || !strnicmp(buf,"UIDL",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(isdigit((uchar)*p)) {
msgnum=strtoul(p, NULL, 10);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
break;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPT to list DELETED message"
,socket, client.protocol, user.alias);
sockprintf(socket,client.protocol,session,"-ERR message deleted");
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-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 %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
continue;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,client.protocol,session,"+OK %" PRIu32 " %lu",msgnum,smb_getmsgtxtlen(&msg));
sockprintf(socket,client.protocol,session,"+OK %" PRIu32 " %u",msgnum,msg.hdr.number);
smb_freemsgmem(&msg);
continue;
}
/* List ALL messages */
sockprintf(socket,client.protocol,session,"+OK %u 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 %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
break;
}
if(msg.idx.attr&MSG_DELETE)
continue;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
break;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,client.protocol,session,"%u %lu",l+1,smb_getmsgtxtlen(&msg));
sockprintf(socket,client.protocol,session,"%u %u",l+1,msg.hdr.number);
smb_freemsgmem(&msg);
}
sockprintf(socket,client.protocol,session,".");
activity=TRUE;
if(!strnicmp(buf, "RETR ",5) || !strnicmp(buf,"TOP ",4)) {
SAFEPRINTF2(str,"%s: %s", client.protocol, user.alias);
status(str);
lines=-1;
p=buf+4;
SKIP_WHITESPACE(p);
msgnum=strtoul(p, NULL, 10);
if(!strnicmp(buf,"TOP ",4)) {
SKIP_DIGIT(p);
SKIP_WHITESPACE(p);
lines=atol(p);
}
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPTED to retrieve an INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d %s <%s> retrieving message #%u with command: %s"
,socket, client.protocol, user.alias, msg.hdr.number, buf);
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
continue;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPT to retrieve DELETED message"
,socket, client.protocol, user.alias);
sockprintf(socket,client.protocol,session,"-ERR message deleted");
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR %d locking message header",i);
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR (%s) retrieving message #%u text"
,socket, client.protocol, user.alias, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR retrieving message text");
remove_ctrl_a(msgtxt, msgtxt);
if(lines > 0 /* Works around BlackBerry mail server */
&& lines >= strlen(msgtxt)) /* which requests the number of bytes (instead of lines) using TOP */
lines=-1;
sockprintf(socket,client.protocol,session,"+OK message follows");
lprintf(LOG_DEBUG,"%04d %s <%s> sending message text (%lu bytes)"
,socket, client.protocol, user.alias, (ulong)strlen(msgtxt));
lines_sent=sockmsgtxt(socket, client.protocol, session, &msg, msgtxt, lines);
/* if(startup->options&MAIL_OPT_DEBUG_POP3) */
if(lines!=-1 && lines_sent<lines) /* could send *more* lines */
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR sending message text (sent %ld of %ld lines)"
,socket, client.protocol, user.alias, lines_sent, lines);
lprintf(LOG_DEBUG,"%04d %s <%s> message transfer complete (%lu lines)"
,socket, client.protocol, user.alias, lines_sent);
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
} else {
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
} else {
msg.hdr.attr|=MSG_READ;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) marking message #%u as read"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
smb_unlockmsghdr(&smb,&msg);
}
smb_unlocksmbhdr(&smb);
}
smb_freemsgmem(&msg);
smb_freemsgtxt(msgtxt);
continue;
}
if(!strnicmp(buf, "DELE ",5)) {
p=buf+5;
SKIP_WHITESPACE(p);
msgnum=strtoul(p, NULL, 10);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPTED to delete an INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d %s <%s> deleting message #%u"
,socket, client.protocol, user.alias, msg.hdr.number);
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d locking message base",i);
continue;
}
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR %d locking message header",i);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
continue;
}
msg.hdr.attr|=MSG_DELETE;
if((i=smb_putmsg(&smb,&msg))==SMB_SUCCESS && msg.hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,&msg);
smb_unlockmsghdr(&smb,&msg);
smb_unlocksmbhdr(&smb);
smb_freemsgmem(&msg);
if(i!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) marking message as read"
, socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d marking message for deletion",i);
sockprintf(socket,client.protocol,session,"+OK");
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s <%s> message deleted", socket, client.protocol, user.alias);
lprintf(LOG_NOTICE,"%04d %s <%s> !UNSUPPORTED COMMAND: '%s'"
,socket, client.protocol, user.alias, buf);
sockprintf(socket,client.protocol,session,"-ERR UNSUPPORTED COMMAND: %s",buf);
if(user.number) {
if(!logoutuserdat(&scfg,&user,time(NULL),client.time))
lprintf(LOG_ERR,"%04d %s <%s> !ERROR in logoutuserdat", socket, client.protocol, user.alias);
if(activity) {
if(user.number)
lprintf(LOG_INFO,"%04d %s <%s> logged out from port %u on %s [%s]"
,socket, client.protocol, user.alias, inet_addrport(&pop3.client_addr), host_name, host_ip);
lprintf(LOG_INFO,"%04d %s client disconnected from port %u on %s [%s]"
,socket, client.protocol, inet_addrport(&pop3.client_addr), host_name, host_ip);
status(STATUS_WFC);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
smb_freemsgmem(&msg);
smb_close(&smb);
protected_uint32_adjust(&active_clients, -1);
update_clients();
{
int32_t remain = thread_down();
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s session thread terminated (%u threads remain, %lu clients served)"
,socket, client.protocol, remain, ++stats.pop3_served);
/* Must be last */
mail_close_socket(&socket, &session);
static ulong rblchk(SOCKET sock, const char* prot, union xp_sockaddr *addr, const char* rbl_addr)
{
char name[256];
DWORD mail_addr;
HOSTENT* host;
struct in_addr dnsbl_result;
unsigned char *addr6;
switch(addr->addr.sa_family) {
case AF_INET:
mail_addr=ntohl(addr->in.sin_addr.s_addr);
safe_snprintf(name,sizeof(name),"%lu.%lu.%lu.%lu.%.128s"
,(ulong)(mail_addr&0xff)
,(ulong)(mail_addr>>8)&0xff
,(ulong)(mail_addr>>16)&0xff
,(ulong)(mail_addr>>24)&0xff
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
,rbl_addr
);
break;
case AF_INET6:
addr6 = (unsigned char *)&addr->in6.sin6_addr;
safe_snprintf(name,sizeof(name),"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%.128s"
,addr6[15]&0x0f
,addr6[15]>>4
,addr6[14]&0x0f
,addr6[14]>>4
,addr6[13]&0x0f
,addr6[13]>>4
,addr6[12]&0x0f
,addr6[12]>>4
,addr6[11]&0x0f
,addr6[11]>>4
,addr6[10]&0x0f
,addr6[10]>>4
,addr6[9]&0x0f
,addr6[9]>>4
,addr6[8]&0x0f
,addr6[8]>>4
,addr6[7]&0x0f
,addr6[7]>>4
,addr6[6]&0x0f
,addr6[6]>>4
,addr6[5]&0x0f
,addr6[5]>>4
,addr6[4]&0x0f
,addr6[4]>>4
,addr6[3]&0x0f
,addr6[3]>>4
,addr6[2]&0x0f
,addr6[2]>>4
,addr6[1]&0x0f
,addr6[1]>>4
,addr6[0]&0x0f
,addr6[0]>>4
,rbl_addr
);
break;
default:
return 0;
}
lprintf(LOG_DEBUG,"%04d %s DNSBL Query: %s",sock,prot,name);
if((host=gethostbyname(name))==NULL)
return(0);
dnsbl_result.s_addr = *((ulong*)host->h_addr_list[0]);
lprintf(LOG_INFO,"%04d %s DNSBL Query: %s resolved to: %s"
,sock,prot,name,inet_ntoa(dnsbl_result));
return(dnsbl_result.s_addr);
static ulong dns_blacklisted(SOCKET sock, const char* prot, union xp_sockaddr *addr, char* host_name, char* list, char* dnsbl_ip)
{
char fname[MAX_PATH+1];
char str[256];
char* p;
char* tp;
FILE* fp;
ulong found=0;
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if(findstr(host_name,fname))
return(FALSE);
SAFEPRINTF(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir);
if((fp=fopen(fname,"r"))==NULL)
return(FALSE);
while(!feof(fp) && !found) {
if(fgets(str,sizeof(str),fp)==NULL)
break;
truncsp(str);
p=str;
SKIP_WHITESPACE(p);
if(*p==';' || *p==0) /* comment or blank line */
continue;
sprintf(list,"%.100s",p);
/* terminate */
tp = p;
FIND_WHITESPACE(tp);
*tp=0;
found = rblchk(sock, prot, addr, p);
}
fclose(fp);
if(found)
return(found);
}
static BOOL chk_email_addr(SOCKET socket, const char* prot, char* p, char* host_name, char* host_ip
,char* to, char* from, char* source)
{
char addr[64];
char tmp[128];
SKIP_WHITESPACE(p);
char* lt = strchr(p, '<');
if(lt!= NULL)
p = lt+1;
truncstr(addr,">( ");
if(!trashcan(&scfg,addr,"email"))
return(TRUE);
lprintf(LOG_NOTICE,"%04d %s !BLOCKED %s e-mail address: %s"
,socket, prot, source, addr);
SAFEPRINTF2(tmp,"Blocked %s e-mail address: %s", source, addr);
spamlog(&scfg, (char*)prot, "REFUSED", tmp, host_name, host_ip, to, from);
static BOOL email_addr_is_exempt(const char* addr)
{
char fname[MAX_PATH+1];
char netmail[128];
char* p;
if(*addr==0 || strcmp(addr,"<>")==0)
return FALSE;
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if(findstr((char*)addr,fname))
return TRUE;
SAFECOPY(netmail, addr);
if(*(p=netmail)=='<')
p++;
truncstr(p,">");
return userdatdupe(&scfg, 0, U_NETMAIL, LEN_NETMAIL, p, /* del */FALSE, /* next */FALSE, NULL, NULL);
}
static void exempt_email_addr(const char* comment
,const char* fromname, const char* fromext, const char* fromaddr
,const char* toaddr)
{
char fname[MAX_PATH+1];
char to[128];
char tmp[128];
FILE* fp;
if(*toaddr == '<')
SAFECOPY(to, toaddr);
else
SAFEPRINTF(to,"<%s>",toaddr);
if(!email_addr_is_exempt(to)) {
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if((fp=fopen(fname,"a"))==NULL)
lprintf(LOG_ERR,"0000 !Error opening file: %s", fname);
else {
lprintf(LOG_INFO,"0000 %s: %s", comment, to);
fprintf(fp,"\n;%s from \"%s\""
,comment, fromname);
if(fromext!=NULL)
fprintf(fp,"%s",fromext);
fprintf(fp," %s on %s\n%s\n"
,fromaddr, timestr(&scfg,time32(NULL),tmp), to);
fclose(fp);
}
}
}
{
int file;
if(scfg.smtpmail_sem[0]==0)
return; /* do nothing */
if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC,DEFFILEMODE))!=-1)
close(file);
}
/*****************************************************************************/
/* Returns command line generated from instr with %c replacements */
/*****************************************************************************/
static char* mailcmdstr(char* instr, char* msgpath, char* newpath, char* logpath
,char* lstpath, char* errpath
,char* host, char* ip, uint usernum
,char* rcpt_addr
,char* sender, char* sender_addr, char* reverse_path, char* cmd)
{
char str[1024];
int i,j,len;
len=strlen(instr);
for(i=j=0;i<len;i++) {
if(instr[i]=='%') {
i++;
cmd[j]=0;
switch(toupper(instr[i])) {
case 'D':
strcat(cmd,logpath);
break;
case 'E':
strcat(cmd,errpath);
break;
case 'H':
strcat(cmd,host);
break;
case 'I':
strcat(cmd,ip);
break;
case 'G': /* Temp directory */
strcat(cmd,scfg.temp_dir);
break;
case 'J':
strcat(cmd,scfg.data_dir);
break;
case 'K':
strcat(cmd,scfg.ctrl_dir);
break;
case 'L':
strcat(cmd,lstpath);
break;
case 'F':
case 'M':
strcat(cmd,msgpath);
break;
case 'N':
strcat(cmd,newpath);
break;
case 'O': /* SysOp */
strcat(cmd,scfg.sys_op);
break;
case 'Q': /* QWK ID */
strcat(cmd,scfg.sys_id);
break;
case 'R': /* reverse path */
strcat(cmd,reverse_path);
break;
case 'S': /* sender name */
strcat(cmd,sender);
break;
case 'T': /* recipient */
strcat(cmd,rcpt_addr);
break;
case 'A': /* sender address */
strcat(cmd,sender_addr);
break;
case 'V': /* Synchronet Version */
SAFEPRINTF2(str,"%s%c",VERSION,REVISION);
strcat(cmd,str);
break;
case 'Z':
strcat(cmd,scfg.text_dir);
break;
case '!': /* EXEC Directory */
strcat(cmd,scfg.exec_dir);
break;
case '@': /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
strcat(cmd,scfg.exec_dir);
#endif
break;
case '%': /* %% for percent sign */
strcat(cmd,"%");
break;
case '?': /* Platform */
#ifdef __OS2__
SAFECOPY(str,"OS2");
#else
SAFECOPY(str,PLATFORM_DESC);
#endif
strlwr(str);
strcat(cmd,str);
break;
case 'U': /* User number */
strcat(cmd,str);
break;
default: /* unknown specification */
break;
}
j=strlen(cmd);
}
else
cmd[j++]=instr[i];
}
cmd[j]=0;
return(cmd);
}
#ifdef JAVASCRIPT
typedef struct {
SOCKET sock;
const char* log_prefix;
const char* proc_name;
} private_t;
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
char line[64];
char file[MAX_PATH+1];
char* warning;
jsrefcount rc;
int log_level;
return;
if(report==NULL) {
lprintf(LOG_ERR,"%04d %s %s !JavaScript: %s"
, p->sock, p->log_prefix, p->proc_name, message);
return;
}
if(report->filename)
SAFEPRINTF(file," %s",report->filename);
else
file[0]=0;
if(report->lineno)
SAFEPRINTF(line," line %u",report->lineno);
else
line[0]=0;
if(JSREPORT_IS_WARNING(report->flags)) {
if(JSREPORT_IS_STRICT(report->flags))
warning="strict warning";
else
warning="warning";
log_level=LOG_WARNING;
} else {
log_level=LOG_ERR;
warning="";
rc=JS_SUSPENDREQUEST(cx);
lprintf(log_level,"%04d %s %s !JavaScript %s%s%s: %s"
,p->sock, p->log_prefix, p->proc_name
,warning ,file, line, message);
JS_RESUMEREQUEST(cx, rc);
static JSBool
js_log(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
uintN i=0;
int32 level=LOG_INFO;
jsrefcount rc;
char *lstr=NULL;
size_t lstr_sz=0;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
return(JS_FALSE);
if(JSVAL_IS_NUMBER(argv[i])) {
if(!JS_ValueToInt32(cx,argv[i++],&level))
return JS_FALSE;
}
for(; i<argc; i++) {
JSVALUE_TO_RASTRING(cx, argv[i], lstr, &lstr_sz, NULL);
HANDLE_PENDING(cx, lstr);
return(JS_TRUE);
rc=JS_SUSPENDREQUEST(cx);
lprintf(level,"%04d %s %s %s"
JS_SET_RVAL(cx, arglist, argv[i]);
JS_RESUMEREQUEST(cx, rc);
}
return(JS_TRUE);
}
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
static JSBool
js_alert(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
private_t* p;
jsrefcount rc;
char *line;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((p=(private_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
if(line==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
lprintf(LOG_ERR,"%04d %s %s %s"
,p->sock, p->log_prefix, p->proc_name, line);
free(line);
JS_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist, argv[0]);
return(JS_TRUE);
}
static JSFunctionSpec js_global_functions[] = {
{"write", js_log, 0},
{"writeln", js_log, 0},
{"print", js_log, 0},
{"log", js_log, 0},
{"alert", js_alert, 1},
{0}
};
static BOOL
js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailproc
,char* cmdline
,char* msgtxt_fname, char* newtxt_fname, char* logtxt_fname
,char* rcpt_addr
,char* rcptlst_fname, char* proc_err_fname
,char* sender, char* sender_addr, char* reverse_path, char* hello_name
,JSRuntime** js_runtime
,JSContext** js_cx
,JSObject** js_glob
,const char* log_prefix
)
{
char* p;
char fname[MAX_PATH+1];
char path[MAX_PATH+1];
char arg[MAX_PATH+1];
BOOL success=FALSE;
JSObject* argv;
jsuint argc;
JSObject* js_script;
js_callback_t js_callback;
jsval val;
jsval rval=JSVAL_VOID;
ZERO_VAR(js_callback);
SAFECOPY(fname,cmdline);
truncstr(fname," \t");
if(getfext(fname)==NULL) /* No extension specified, assume '.js' */
strcat(fname,".js");
SAFECOPY(path,fname);

rswindell
committed
if(getfname(path)==path) { /* No path specified, assume mods or exec dir */
SAFEPRINTF2(path,"%s%s",scfg.mods_dir,fname);

rswindell
committed
if(scfg.mods_dir[0]==0 || !fexist(path))
SAFEPRINTF2(path,"%s%s",scfg.exec_dir,fname);

rswindell
committed
}
*result = 0;
if(*js_runtime==NULL) {
lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes\n"
,sock, log_prefix, startup->js.max_bytes);
if((*js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
return FALSE;
if(*js_cx==NULL) {
lprintf(LOG_DEBUG,"%04d %s JavaScript: Initializing context (stack: %lu bytes)\n"
,sock, log_prefix, startup->js.cx_stack);
if((*js_cx = JS_NewContext(*js_runtime, startup->js.cx_stack))==NULL)
return FALSE;
Loading
Loading full blame...