Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 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 2000 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 */
#elif defined(__unix__)
#include <signal.h> /* signal/SIGPIPE */
#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 */
#include "sbbs.h"
#include "mime.h"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
#define SMTP_OK "250 OK"
#define SMTP_BADSEQ "503 Bad sequence of commands"

rswindell
committed
#define TIMEOUT_THREAD_WAIT 30 /* Seconds */
#define MAX_RECIPIENTS 1000 /* 0xffff = abs max */
#define LINES_PER_YIELD 100 /* Yield every this many lines of text */
#define STATUS_WFC "Listening"
static const char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
,"Jul","Aug","Sep","Oct","Nov","Dec"};
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 BOOL recycle_server=FALSE;
typedef struct {
SOCKET socket;
SOCKADDR_IN client_addr;
} smtp_t,pop3_t;
static int lprintf(char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
if(startup==NULL || startup->lputs==NULL)
return(0);
#if defined(_WIN32) && defined(_DEBUG)
if(IsBadCodePtr((FARPROC)startup->lputs)) {
DebugBreak();

rswindell
committed
return(0);

rswindell
committed
#endif
va_start(argptr,fmt);
vsprintf(sbuf,fmt,argptr);
va_end(argptr);
return(startup->lputs(sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf("%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
return (TRUE);
}
lprintf("!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#endif
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(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(TRUE,sock,client,update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
thread_count++;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(TRUE);
if(setuid && startup!=NULL && startup->setuid!=NULL)
startup->setuid();
}
static void thread_down(void)
{
if(thread_count>0)
thread_count--;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(FALSE);
}

rswindell
committed
SOCKET mail_open_socket(int type)
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(TRUE);
if(sock!=INVALID_SOCKET) {
sockets++;
#if 0 /*def _DEBUG */
lprintf("%04d Socket opened (%d sockets in use)",sock,sockets);
#endif
}
return(sock);

rswindell
committed
int mail_close_socket(SOCKET sock)
shutdown(sock,SHUT_RDWR); /* required on Unix */
result=closesocket(sock);
if(/* result==0 && */ startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(FALSE);
sockets--;
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf("%04d !ERROR %d closing socket",sock, ERROR_VALUE);
}
lprintf("%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(str);
}
int sockprintf(SOCKET sock, char *fmt, ...)
{
int len;
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
va_start(argptr,fmt);
len=vsprintf(sbuf,fmt,argptr);
if(startup->options&MAIL_OPT_DEBUG_TX)
lprintf("%04d TX: %s", sock, sbuf);
strcat(sbuf,"\r\n");
len+=2;
va_end(argptr);
/* Check socket for writability (using select) */
tv.tv_sec=60;
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("%04d !ERROR %d (%d) selecting socket for send"
,sock, result, ERROR_VALUE, sock);
return(0);
}
while((result=sendsocket(sock,sbuf,len))!=len) {
lprintf("%04d Connection reset by peer on send",sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf("%04d Connection aborted by peer on send",sock);
lprintf("%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
lprintf("%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)

rswindell
committed
lprintf("%04d Socket closed by peer on receive (line %d)"
,socket, line);

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

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

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

rswindell
committed
lprintf("%04d !ERROR: recv on socket returned unexpected value: %d (line %d)"
,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;
time_t start;
start=time(NULL);
while(rd<len-1) {

rswindell
committed
tv.tv_sec=0;
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) {

rswindell
committed
recverror(socket,i,__LINE__);
return(i);
}
i=recv(socket, &ch, 1, 0);
if(i<1) {
recverror(socket,i,__LINE__);
return(i);
}
if(ch=='\n' && rd>=1) {
break;
}
buf[rd++]=ch;
}
buf[rd-1]=0;
return(rd);
}
/************************************************/
/* Truncates white-space chars off end of 'str' */
/************************************************/
static void truncsp(char *str)
{
uint c;
c=strlen(str);
while(c && (uchar)str[c-1]<=' ') c--;
str[c]=0;
}
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("%04d RX: %s",socket,buf);
continue;
}
if(strnicmp(buf,rsp,strlen(rsp))) {
lprintf("%04d !INVALID RESPONSE: '%s' Expected: '%s'", socket, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf("%04d RX: %s",socket,buf);
return(TRUE);
}
#define MAX_LINE_LEN 1000
static ulong sockmsgtxt(SOCKET socket, smbmsg_t* msg, char* msgtxt, char* fromaddr
,ulong maxlines)
{
char toaddr[256];
char date[64];
char filepath[MAX_PATH+1]="";
char* boundary=NULL;
/* HEADERS */
sockprintf(socket,"Date: %s",msgdate(msg->hdr.when_written,date));
if(fromaddr[0]=='<')
sockprintf(socket,"From: \"%s\" %s",msg->from,fromaddr);
else
sockprintf(socket,"From: \"%s\" <%s>",msg->from,fromaddr);
sockprintf(socket,"Subject: %s",msg->subj);
if(strchr(msg->to,'@')!=NULL)
sockprintf(socket,"To: %s",msg->to); /* Avoid double-@ */
else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
if(*((char*)msg->to_net.addr)=='<')
sockprintf(socket,"To: \"%s\" %s",msg->to,(char*)msg->to_net.addr);
else
sockprintf(socket,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
} else {
usermailaddr(&scfg,toaddr,msg->to);
sockprintf(socket,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(msg->replyto_net.type==NET_INTERNET)
sockprintf(socket,"Reply-To: %s",msg->replyto_net.addr);
else
sockprintf(socket,"Reply-To: %s",fromaddr);
if(msg->id!=NULL)
sockprintf(socket,"Message-ID: %s",msg->id);
else
sockprintf(socket,"Message-ID: <%08lX.%lu@%s>"
,msg->hdr.when_written.time,msg->idx.number,scfg.sys_inetaddr);
if(msg->reply_id!=NULL)
sockprintf(socket,"In-Reply-To: %s",msg->reply_id);
for(i=0;i<msg->total_hfields;i++) {
Loading
Loading full blame...