linbpq/MailTCP.c

4139 lines
84 KiB
C

/*
Copyright 2001-2018 John Wiseman G8BPQ
This file is part of LinBPQ/BPQ32.
LinBPQ/BPQ32 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 3 of the License, or
(at your option) any later version.
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
*/
// Mail and Chat Server for BPQ32 Packet Switch
//
// TCP access module - POP and SMTP
#include "bpqmail.h"
VOID ReleaseSock(SOCKET sock);
void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len);
#define MaxSockets 64
SocketConn * Sockets = NULL;
int CurrentConnections;
int CurrentSockets=0;
#define MAX_PENDING_CONNECTS 4
#define VERSION_MAJOR 2
#define VERSION_MINOR 0
static SOCKADDR_IN local_sin; /* Local socket - internet style */
static PSOCKADDR_IN psin;
SOCKET smtpsock, pop3sock;
char szBuff[80];
int SMTPInPort;
int POP3InPort;
BOOL RemoteEmail; // Set to listen on INADDR_ANY rather than LOCALHOST
BOOL ISP_Gateway_Enabled;
char MyDomain[50]; // Mail domain for BBS<>Internet Mapping
char ISPSMTPName[50];
char ISPEHLOName[50] = "";
int ISPSMTPPort;
char ISPPOP3Name[50];
int ISPPOP3Port;
char ISPAccountName[50];
char ISPAccountPass[50];
char EncryptedISPAccountPass[100];
int EncryptedPassLen;
BOOL SMTPAuthNeeded;
BOOL GMailMode = FALSE;
char GMailName[50];
int POP3Timer=9999; // Run on startup
int ISPPOP3Interval;
BOOL SMTPMsgCreated=FALSE; // Set to cause SMTP client to send messages to ISP
BOOL SMTPActive=FALSE; // SO we don't try every 10 secs!
char mycd64[256];
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
char sockTypes[6][12] = {"Undefined", "SMTPServer", "POP3Server", "SMTPClient", "POP3Client", "NNTPServer"};
void decodeblock( unsigned char in[4], unsigned char out[3] );
VOID FormatTime(char * Time, time_t cTime);
static int Socket_Accept(SOCKET SocketId);
int SendSock(SocketConn * sockptr, char * msg)
{
int len = (int)strlen(msg), sent;
char * newmsg = malloc(len+10);
WriteLogLine(NULL, '>',msg, len, LOG_TCP);
strcpy(newmsg, msg);
strcat(newmsg, "\r\n");
len+=2;
if (sockptr->SendBuffer)
{
// Already queued, so add to end
if ((sockptr->SendSize + len) > sockptr->SendBufferSize)
{
sockptr->SendBufferSize += (10000 + len);
sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize);
}
memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len);
sockptr->SendSize += len;
free (newmsg);
return len;
}
sent = send(sockptr->socket, newmsg, len, 0);
if (sent < len)
{
int error, remains;
// Not all could be sent - queue rest
if (sent == SOCKET_ERROR)
{
error = WSAGetLastError();
if (error == WSAEWOULDBLOCK)
sent=0;
// What else??
}
remains = len - sent;
sockptr->SendBufferSize += (10000 + remains);
sockptr->SendBuffer = malloc(sockptr->SendBufferSize);
memcpy(sockptr->SendBuffer, &newmsg[sent], remains);
sockptr->SendSize = remains;
sockptr->SendPtr = 0;
}
free (newmsg);
return sent;
}
VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...)
{
// printf to a socket
char buff[1000];
va_list(arglist);
va_start(arglist, format);
vsprintf(buff, format, arglist);
SendSock(sockptr, buff);
}
extern int SMTPMsgs;
fd_set ListenSet;
SOCKET ListenMax = 0;
extern SOCKET nntpsock;
int NNTP_Read(SocketConn * sockptr, SOCKET sock);
VOID SetupListenSet()
{
// Set up master set of fd's for checking for incoming calls
fd_set * readfd = &ListenSet;
SOCKET sock;
FD_ZERO(readfd);
sock = nntpsock;
if (sock)
{
FD_SET(sock, readfd);
if (sock > ListenMax)
ListenMax = sock;
}
sock = smtpsock;
if (sock)
{
FD_SET(sock, readfd);
if (sock > ListenMax)
ListenMax = sock;
}
sock = pop3sock;
if (sock)
{
FD_SET(sock, readfd);
if (sock > ListenMax)
ListenMax = sock;
}
}
VOID Socket_Connected(SocketConn * sockptr, int error)
{
SOCKET sock = sockptr->socket;
if (error)
{
Logprintf(LOG_TCP, NULL, '|', "Connect Failed");
if (sockptr->Type == SMTPClient)
SMTPActive = FALSE;
ReleaseSock(sock);
return;
}
sockptr->State = WaitingForGreeting;
if (sockptr->Type == NNTPServer)
SendSock(sockptr, "200 BPQMail NNTP Server ready");
else if (sockptr->Type == SMTPServer)
SendSock(sockptr, "220 BPQMail SMTP Server ready");
else if (sockptr->Type == POP3SLAVE)
{
SendSock(sockptr, "+OK POP3 server ready");
sockptr->State = GettingUser;
}
}
VOID TCPFastTimer()
{
// we now poll for incoming connections and data
fd_set readfd, writefd, exceptfd;
struct timeval timeout;
int retval;
SocketConn * sockptr = Sockets;
SOCKET sock;
int Active = 0;
SOCKET maxsock;
timeout.tv_sec = 0;
timeout.tv_usec = 0; // poll
if (ListenMax)
{
memcpy(&readfd, &ListenSet, sizeof(fd_set));
retval = select((int)ListenMax + 1, &readfd, NULL, NULL, &timeout);
if (retval == -1)
{
retval = 0;
perror("Listen select");
}
if (retval)
{
sock = pop3sock;
if (sock)
if (FD_ISSET(sock, &readfd))
Socket_Accept(sock);
sock = smtpsock;
if (sock)
if (FD_ISSET(sock, &readfd))
Socket_Accept(sock);
sock = nntpsock;
if (sock)
if (FD_ISSET(sock, &readfd))
NNTP_Accept(sock);
}
}
// look for data on any active sockets
maxsock = 0;
FD_ZERO(&readfd);
FD_ZERO(&writefd);
FD_ZERO(&exceptfd);
sockptr=Sockets;
while (sockptr)
{
sockptr->Timeout++;
if (sockptr->Timeout > 1200) // 2 mins
{
Logprintf(LOG_TCP, NULL, '|', "Timeout on Socket = %d", sockptr->socket);
shutdown(sockptr->socket, 0);
closesocket(sockptr->socket);
ReleaseSock(sockptr->socket);
return; // We've messed with chain
}
if (sockptr->State & Connecting)
{
// look for complete or failed
FD_SET(sockptr->socket, &writefd);
FD_SET(sockptr->socket, &exceptfd);
}
else
FD_SET(sockptr->socket, &readfd);
Active++;
if (sockptr->socket > maxsock)
maxsock = sockptr->socket;
sockptr = sockptr->Next;
}
if (Active == 0)
return;
retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout);
if (retval == -1)
{
perror("select");
// we need to do something or the error will recur.
// As there are unlikely to be a lot of open tcp connections perhaps
// simplest is to close all
sockptr = Sockets;
while (sockptr)
{
Debugprintf("MAILTCP Select Failed Active %s Socket", sockTypes[sockptr->Type]);
shutdown(sockptr->socket, 0);
closesocket(sockptr->socket);
ReleaseSock(sockptr->socket);
sockptr = Sockets; // We've messed with chain
}
}
else
{
if (retval)
{
sockptr = Sockets;
// see who has data
while (sockptr)
{
sock = sockptr->socket;
if (FD_ISSET(sock, &readfd))
{
sockptr->Timeout = 0;
if (sockptr->Type == NNTPServer)
{
if (NNTP_Read(sockptr, sock) == 0)
break; // We've messed with the chain
}
else
{
if (DataSocket_Read(sockptr, sock) == 0)
break; // We've messed with the chain
}
}
if (FD_ISSET(sockptr->socket, &writefd))
Socket_Connected(sockptr, 0);
if (FD_ISSET(sockptr->socket, &exceptfd))
{
Socket_Connected(sockptr, 1);
return;
}
sockptr = sockptr->Next;
}
}
}
}
VOID TCPTimer()
{
POP3Timer+=10;
// Debugprintf("POP3 Debug Timer = %d Interval = %d Port %d Enabled %d",
// POP3Timer, ISPPOP3Interval, ISPPOP3Port, ISP_Gateway_Enabled);
if (POP3Timer > ISPPOP3Interval) // 5 mins
{
POP3Timer=0;
if ((ISPSMTPPort && ISP_Gateway_Enabled))
SendtoISP();
if (ISPPOP3Port && ISP_Gateway_Enabled)
{
// Debugprintf("Calling POP3 Connect");
POP3Connect(ISPPOP3Name, ISPPOP3Port);
}
if (SMTPMsgs && ISPSMTPPort && ISP_Gateway_Enabled)
SendtoISP();
}
else
{
if (SMTPMsgCreated && ISPSMTPPort && ISP_Gateway_Enabled)
SendtoISP();
}
}
BOOL InitialiseTCP()
{
int Error; // catches return value of WSAStartup
#ifdef WIN32
WORD VersionRequested; // passed to WSAStartup
WSADATA WsaData; // receives data from WSAStartup
#endif
int i,j;
for (i=0;i<64; i++)
{
j=cb64[i];
mycd64[j]=i;
}
#ifdef WIN32
VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR);
Error = WSAStartup(VersionRequested, &WsaData);
if (Error)
{
#ifndef LINBPQ
MessageBox(NULL,
"Could not find high enough version of WinSock",
"BPQMailChat", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
#else
printf("Could not find high enough version of WinSock\n");
#endif
return FALSE;
}
#endif
// Create listening sockets
if (SMTPInPort)
smtpsock = CreateListeningSocket(SMTPInPort);
if (POP3InPort)
pop3sock = CreateListeningSocket(POP3InPort);
if (ISP_Gateway_Enabled)
{
// See if using GMail
char * ptr = strchr(ISPAccountName, '@');
if (ptr)
{
if (_stricmp(&ptr[1], "gmail.com") == 0 || _stricmp(&ptr[1], "googlemail.com") == 0)
{
strcpy(GMailName, ISPAccountName);
strlop(GMailName, '@');
GMailMode = TRUE;
SMTPAuthNeeded = TRUE;
}
}
}
return TRUE;
}
SOCKET CreateListeningSocket(int Port)
{
SOCKET sock;
unsigned int param = 1;
sock = socket( AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
sprintf(szBuff, "socket() failed error %d", WSAGetLastError());
#ifdef LINBPQ
perror(szBuff);
#else
MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
return FALSE;
}
setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)&param,4);
psin=&local_sin;
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = htonl(RemoteEmail ? INADDR_ANY : INADDR_LOOPBACK); // Local Host Olny
psin->sin_port = htons(Port); /* Convert to network ordering */
if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR)
{
sprintf(szBuff, "bind(%d) failed Error %d", Port, WSAGetLastError());
#ifdef LINBPQ
perror(szBuff);
#else
MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
closesocket( sock );
return FALSE;
}
if (listen( sock, MAX_PENDING_CONNECTS ) < 0)
{
sprintf(szBuff, "listen(%d) failed Error %d", Port, WSAGetLastError());
#ifdef LINBPQ
perror(szBuff);
#else
MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
closesocket( sock );
return FALSE;
}
ioctl(sock, FIONBIO, &param);
return sock;
}
static int Socket_Accept(SOCKET SocketId)
{
int addrlen;
SocketConn * sockptr;
SOCKET sock;
unsigned int param = 1;
addrlen=sizeof(struct sockaddr);
// Allocate a Socket entry
sockptr = malloc(sizeof(SocketConn));
memset(sockptr, 0, sizeof (SocketConn));
sockptr->Next = Sockets;
Sockets = sockptr;
sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen);
if (sock == INVALID_SOCKET)
{
Logprintf(LOG_TCP, NULL, '|', " accept() failed Error %d", WSAGetLastError());
// get rid of socket record
Sockets = sockptr->Next;
free(sockptr);
return FALSE;
}
ioctl(sock, FIONBIO, &param);
sockptr->socket = sock;
if (SocketId == pop3sock)
{
sockptr->Type = POP3SLAVE;
SendSock(sockptr, "+OK POP3 server ready");
sockptr->State = GettingUser;
Logprintf(LOG_TCP, NULL, '|', "Incoming POP3 Connect Socket = %d", sock);
}
else
{
sockptr->Type = SMTPServer;
sockptr->State = WaitingForGreeting;
SendSock(sockptr, "220 BPQMail SMTP Server ready");
Logprintf(LOG_TCP, NULL, '|', "Incoming SMTP Connect Socket = %d", sock);
}
return 0;
}
VOID ReleaseSock(SOCKET sock)
{
// remove and free the socket record
SocketConn * sockptr, * lastptr;
sockptr=Sockets;
lastptr=NULL;
while (sockptr)
{
if (sockptr->socket == sock)
{
if (lastptr)
lastptr->Next=sockptr->Next;
else
Sockets=sockptr->Next;
if (sockptr->POP3User)
sockptr->POP3User->POP3Locked = FALSE;
if (sockptr->State == WaitingForGreeting)
{
Logprintf(LOG_TCP, NULL, '|', "Premature Close on Socket %d", sock);
if (sockptr->Type == SMTPClient)
SMTPActive = FALSE;
}
else
Logprintf(LOG_TCP, NULL, '|', "Socket %d Closed", sock);
free(sockptr);
return;
}
else
{
lastptr=sockptr;
sockptr=sockptr->Next;
}
}
}
/*
int Socket_Data(int sock, int error, int eventcode)
{
SocketConn * sockptr;
// Find Connection Record
sockptr=Sockets;
while (sockptr)
{
if (sockptr->socket == sock)
{
switch (eventcode)
{
case FD_READ:
return DataSocket_Read(sockptr,sock);
case FD_WRITE:
// Either Just connected, or flow contorl cleared
if (sockptr->SendBuffer)
// Data Queued
SendFromQueue(sockptr);
else
{
if (sockptr->Type == SMTPServer)
SendSock(sockptr, "220 BPQMail SMTP Server ready");
else
{
if (sockptr->Type == POP3SLAVE)
{
SendSock(sockptr, "+OK POP3 server ready");
sockptr->State = GettingUser;
}
}
}
return 0;
case FD_OOB:
return 0;
case FD_ACCEPT:
return 0;
case FD_CONNECT:
return 0;
case FD_CLOSE:
closesocket(sock);
ReleaseSock(sock);
return 0;
}
return 0;
}
else
sockptr=sockptr->Next;
}
return 0;
}
*/
int DataSocket_Read(SocketConn * sockptr, SOCKET sock)
{
int InputLen, MsgLen;
char * ptr, * ptr2;
char Buffer[2000];
// May have several messages per packet, or message split over packets
if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode
{
sockptr->InputLen=0;
}
InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0);
if (InputLen <= 0)
{
int x = WSAGetLastError();
closesocket(sock);
ReleaseSock(sock);
return 0; // Does this mean closed?
}
sockptr->InputLen += InputLen;
loop:
ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen);
if (ptr) // CR in buffer
{
ptr2 = &sockptr->TCPBuffer[sockptr->InputLen];
ptr++; // Assume LF Follows CR
if (ptr == ptr2)
{
// Usual Case - single meg in buffer
if (sockptr->Type == SMTPServer)
ProcessSMTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
else
if (sockptr->Type == POP3SLAVE)
ProcessPOP3ServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
else
if (sockptr->Type == SMTPClient)
ProcessSMTPClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
else
if (sockptr->Type == POP3Client)
ProcessPOP3ClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
sockptr->InputLen=0;
}
else
{
// buffer contains more that 1 message
MsgLen = sockptr->InputLen - (int)(ptr2-ptr);
memcpy(Buffer, sockptr->TCPBuffer, MsgLen);
if (sockptr->Type == SMTPServer)
ProcessSMTPServerMessage(sockptr, Buffer, MsgLen);
else
if (sockptr->Type == POP3SLAVE)
ProcessPOP3ServerMessage(sockptr, Buffer, MsgLen);
else
if (sockptr->Type == SMTPClient)
ProcessSMTPClientMessage(sockptr, Buffer, MsgLen);
else
if (sockptr->Type == POP3Client)
ProcessPOP3ClientMessage(sockptr, Buffer, MsgLen);
memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen);
sockptr->InputLen -= MsgLen;
goto loop;
}
}
return TRUE;
}
char * FindPart(char ** Msg, char * Boundary, int * PartLen)
{
char * ptr = *Msg, * ptr2;
char * Msgptr = *Msg;
int BLen = (int)strlen(Boundary);
char * Part;
while(*ptr) // Just in case we run off end
{
ptr2 = strchr(ptr, 10); // Find LF
if (ptr2 == NULL) return NULL;
if (*ptr == '-' && *(ptr+1) == '-')
{
if (memcmp(&ptr[2], Boundary, BLen) == 0)
{
// Found Boundary
int Partlen = (int)(ptr - Msgptr);
Part = malloc(Partlen + 1);
memcpy(Part, Msgptr, Partlen);
Part[Partlen] = 0;
*Msg = ++ptr2;
*PartLen = Partlen;
return Part;
}
}
ptr = ++ptr2;
}
return NULL;
}
BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen) // Will reformat message if necessary.
{
int i;
char * ptr, * ptr2, * ptr3, * ptr4;
char Boundary[1000];
BOOL Multipart = FALSE;
BOOL ALT = FALSE;
int Partlen;
char * Save;
BOOL Base64 = FALSE;
BOOL QuotedP = FALSE;
char FileName[100][250] = {""};
int FileLen[100];
char * FileBody[100];
char * MallocSave[100];
UCHAR * NewMsg;
int Files = 0;
if (*MsgLen > 5000000)
{
int xxx = 1;
}
ptr = Msg;
while(*ptr != 13)
{
ptr2 = strchr(ptr, 10); // Find CR
while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
{
ptr2 = strchr(&ptr2[1], 10); // Find CR
}
// Content-Type: multipart/mixed;
// boundary="----=_NextPart_000_025B_01CAA004.84449180"
// 7.2.2 The Multipart/mixed (primary) subtype
// 7.2.3 The Multipart/alternative subtype
if (_memicmp(ptr, "Content-Type: ", 14) == 0)
{
char Line[1000] = "";
char lcLine[1000] = "";
char * ptr3;
memcpy(Line, &ptr[14], ptr2-ptr-14);
memcpy(lcLine, &ptr[14], ptr2-ptr-14);
_strlwr(lcLine);
if (_memicmp(Line, "Multipart/", 10) == 0)
{
Multipart = TRUE;
if (_memicmp(&Line[10], "alternative", 11) == 0)
{
ALT = TRUE;
}
ptr3 = strstr(Line, "boundary");
if (ptr3)
{
ptr3+=9;
if ((*ptr3) == '"')
ptr3++;
strcpy(Boundary, ptr3);
ptr3 = strchr(Boundary, '"');
if (ptr3) *ptr3 = 0;
ptr3 = strchr(Boundary, 13); // CR
if (ptr3) *ptr3 = 0;
}
else
return FALSE; // Can't do anything without a boundary ??
}
}
else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
{
if (strstr(&ptr[26], "base64"))
Base64 = TRUE;
else
if (strstr(&ptr[26], "quoted-printable"))
QuotedP = TRUE;
}
ptr = ptr2;
ptr++;
}
if (Multipart == FALSE)
{
// We only have one part, but it could have an odd encoding
if (Base64)
{
int i = 0, Len = *MsgLen, NewLen;
char * ptr2;
char * End;
ptr = ptr2 = *Body;
End = ptr + Len;
while (ptr < End)
{
while (*ptr < 33)
{ptr++;}
*ptr2++ = *ptr++;
}
*ptr2 = 0;
ptr = *Body;
Len = (int)(ptr2 - ptr - 1);
ptr2 = ptr;
while (Len > 0)
{
decodeblock(ptr, ptr2);
ptr += 4;
ptr2 += 3;
Len -= 4;
}
NewLen = (int)(ptr2 - *Body);
if (*(ptr-1) == '=')
NewLen--;
if (*(ptr-2) == '=')
NewLen--;
*MsgLen = NewLen;
}
else if (QuotedP)
{
int i = 0, Len = *MsgLen;
char * ptr2;
char * End;
ptr = ptr2 =*Body;
End = ptr + Len;
while (ptr < End)
{
if ((*ptr) == '=')
{
char c = *(++ptr);
char d;
c = c - 48;
if (c < 0)
{
// = CRLF as a soft break
ptr += 2;
continue;
}
if (c > 9)
c -= 7;
d = *(++ptr);
d = d - 48;
if (d > 9)
d -= 7;
*(ptr2) = c << 4 | d;
ptr2++;
ptr++;
}
else
{
*ptr2++ = *ptr++;
}
}
*ptr2 = 0;
*MsgLen = (int)(ptr2 - *Body);
}
return FALSE;
}
// FindPart Returns Next Part of Message, Updates Input Pointer
// Skip to first Boundary (over the non MIME Alt Part)
ptr = FindPart(Body, Boundary, &Partlen);
if (ptr == NULL)
return FALSE; // Couldn't find separator
free(ptr);
if (ALT)
{
// Assume HTML and Plain Text Versions of the same single body.
ptr = FindPart(Body, Boundary, &Partlen);
Save = ptr; // For free();
// Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text)
// Skip any headers
while(*ptr != 13)
{
if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
{
if (strstr(&ptr[26], "base64"))
Base64 = TRUE;
else
if (strstr(&ptr[26], "quoted-printable"))
QuotedP = TRUE;
}
ptr2 = strchr(ptr, 10); // Find CR
while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
{
ptr2 = strchr(&ptr2[1], 10); // Find CR
}
ptr = ++ptr2;
}
ptr += 2; // Skip rerminating line
// Should now have a plain text body to return;
// But could be an odd encoding
if (Base64)
{
int i = 0, Len = (int)strlen(ptr), NewLen;
char * ptr2;
char * End;
char * Save = ptr;
ptr2 = ptr;
End = ptr + Len;
while (ptr < End)
{
while (*ptr < 33)
{ptr++;}
*ptr2++ = *ptr++;
}
*ptr2 = 0;
ptr = Save;
Len = (int)(ptr2 - ptr - 1);
ptr2 = *Body;
while (Len > 0)
{
decodeblock(ptr, ptr2);
ptr += 4;
ptr2 += 3;
Len -= 4;
}
NewLen = (int)(ptr2 - *Body);
if (*(ptr-1) == '=')
NewLen--;
if (*(ptr-2) == '=')
NewLen--;
*MsgLen = NewLen;
}
else if (QuotedP)
{
int i = 0, Len = (int)strlen(ptr);
char * ptr2;
char * End;
char * Save = ptr;
ptr2 = *Body;
End = ptr + Len;
while (ptr < End)
{
if ((*ptr) == '=')
{
char c = *(++ptr);
char d;
c = c - 48;
if (c < 0)
{
// = CRLF as a soft break
ptr += 2;
continue;
}
if (c > 9)
c -= 7;
d = *(++ptr);
d = d - 48;
if (d > 9)
d -= 7;
*(ptr2) = c << 4 | d;
ptr2++;
ptr++;
}
else
{
*ptr2++ = *ptr++;
}
}
*ptr2 = 0;
*MsgLen = (int)(ptr2 - *Body);
}
else
{
strcpy(*Body, ptr);
*MsgLen = (int)strlen(ptr);
}
free(Save);
return FALSE;
}
// Assume Multipart/Mixed - Message with attachments
ptr = FindPart(Body, Boundary, &Partlen);
if (ptr == NULL)
return FALSE; // Couldn't find separator
while (ptr)
{
BOOL Base64 = FALSE;
BOOL QuotedP = FALSE;
MallocSave[Files] = ptr; // For free();
// Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text)
// Process headers - looking for Content-Disposition: attachment;
// The first could also be a Content-Type: multipart/alternative; - if so, feed back to mime handler
while(*ptr != 13)
{
char lcLine[1000] = "";
ptr2 = strchr(ptr, 10); // Find CR
if (ptr2 == 0)
return FALSE;
while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
{
ptr2 = strchr(&ptr2[1], 10); // Find CR
}
memcpy(lcLine, ptr, ptr2-ptr-1);
_strlwr(lcLine);
ptr = lcLine;
if (_memicmp(ptr, "Content-Type: Multipart/alternative", 30) == 0)
{
// Feed Back
int MsgLen;
char * Text = malloc(Partlen+1);
memcpy(Text, MallocSave[Files], Partlen);
free(MallocSave[Files]);
MallocSave[Files] = Text;
CheckforMIME(sockptr, Text, &Text, &MsgLen);
FileName[Files][0] = 0;
FileBody[Files] = Text;
FileLen[Files++] = MsgLen;
goto NextPart;
}
else if (_memicmp(ptr, "Content-Disposition: ", 21) == 0)
{
ptr3 = strstr(&ptr[21], "filename");
if (ptr3)
{
ptr3 += 9;
if (*ptr3 == '"') ptr3++;
ptr4 = strchr(ptr3, '"');
if (ptr4) *ptr4 = 0;
strcpy(FileName[Files], ptr3);
}
}
else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
{
if (strstr(&ptr[26], "base64"))
Base64 = TRUE;
else
if (strstr(&ptr[26], "quoted-printable"))
QuotedP = TRUE;
}
ptr = ++ptr2;
}
ptr += 2;
// Should now have file or plain text. If file is Base64 encoded, decode it.
FileBody[Files] = ptr;
FileLen[Files] = (int)(Partlen - 2 - (ptr - MallocSave[Files]));
if (Base64)
{
int i = 0, Len = FileLen[Files], NewLen;
char * ptr2 = ptr;
char * End;
End = ptr + FileLen[Files];
while (ptr < End)
{
while (*ptr < 33)
{ptr++;}
*ptr2++ = *ptr++;
}
*ptr2 = 0;
ptr = FileBody[Files];
Len = (int)(ptr2 - ptr - 1);
ptr2 = ptr;
while (Len > 0)
{
decodeblock(ptr, ptr2);
ptr += 4;
ptr2 += 3;
Len -= 4;
}
NewLen = (int)(ptr2 - FileBody[Files]);
if (*(ptr-1) == '=')
NewLen--;
if (*(ptr-2) == '=')
NewLen--;
FileLen[Files] = NewLen;
}
else if (QuotedP)
{
int i = 0, Len = FileLen[Files], NewLen;
char * ptr2 = ptr;
char * End;
End = ptr + FileLen[Files];
while (ptr < End)
{
if ((*ptr) == '=')
{
char c = *(++ptr);
char d;
c = c - 48;
if (c < 0)
{
// = CRLF as a soft break
ptr += 2;
continue;
}
if (c > 9)
c -= 7;
d = *(++ptr);
d = d - 48;
if (d > 9)
d -= 7;
*(ptr2) = c << 4 | d;
ptr2++;
ptr++;
}
else
{
*ptr2++ = *ptr++;
}
}
*ptr2 = 0;
NewLen = (int)(ptr2 - FileBody[Files]);
FileLen[Files] = NewLen;
}
Files++;
NextPart:
ptr = FindPart(Body, Boundary, &Partlen);
}
// Now have all the parts - build a B2 Message. Leave the first part of header for later,
// as we may have multiple recipients. Start with the Body: Line.
// We need to add the first part of header later, so start message part way down buffer.
// Make sure buffer is big enough.
if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize)
{
sockptr->MailBufferSize += 2000;
sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
if (sockptr->MailBuffer == NULL)
{
CriticalErrorHandler("Failed to extend Message Buffer");
shutdown(sockptr->socket, 0);
return FALSE;
}
}
NewMsg = sockptr->MailBuffer + 1000;
NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]);
for (i = 1; i < Files; i++)
{
NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]);
}
NewMsg += sprintf(NewMsg, "\r\n");
for (i = 0; i < Files; i++)
{
memcpy(NewMsg, FileBody[i], FileLen[i]);
NewMsg += FileLen[i];
free(MallocSave[i]);
NewMsg += sprintf(NewMsg, "\r\n");
}
*MsgLen = (int)(NewMsg - (sockptr->MailBuffer + 1000));
*Body = sockptr->MailBuffer + 1000;
return TRUE; // B2 Message
}
VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len)
{
SOCKET sock;
int i;
time_t Date = 0;
sock=sockptr->socket;
WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);
if (sockptr->Flags == GETTINGMESSAGE)
{
if(memcmp(Buffer, ".\r\n", 3) == 0)
{
char * ptr1, * ptr2;
int linelen, MsgLen;
char Msgtitle[62];
BOOL B2Flag;
int ToLen = 0;
char * ToString;
char * Via;
// Scan headers for a Subject: or Date: Line (Headers end at blank line)
ptr1 = sockptr->MailBuffer;
Loop:
ptr2 = strchr(ptr1, '\r');
if (ptr2 == NULL)
{
SendSock(sockptr, "500 Eh");
return;
}
linelen = (int)(ptr2 - ptr1);
if (_memicmp(ptr1, "Subject:", 8) == 0)
{
if (linelen > 68) linelen = 68;
memcpy(Msgtitle, &ptr1[9], linelen-9);
Msgtitle[linelen-9]=0;
}
if (_memicmp(ptr1, "Date:", 5) == 0)
{
struct tm rtime;
char * Context;
char seps[] = " ,\t\r";
char Offset[10] = "";
int i, HH, MM;
char Copy[500]="";
// Copy message, so original isn't messed up by strtok
memcpy(Copy, ptr1, linelen);
ptr1 = Copy;
memset(&rtime, 0, sizeof(struct tm));
// Date: Tue, 9 Jun 2009 20:54:55 +0100
ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day
ptr1 = strtok_s(NULL, seps, &Context); // Day
rtime.tm_mday = atoi(ptr1);
ptr1 = strtok_s(NULL, seps, &Context); // Month
for (i=0; i < 12; i++)
{
if (strcmp(month[i], ptr1) == 0)
{
rtime.tm_mon = i;
break;
}
}
sscanf(Context, "%04d %02d:%02d:%02d%s",
&rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset);
rtime.tm_year -= 1900;
Date = mktime(&rtime) - (time_t)_MYTIMEZONE;
if (Date == (time_t)-1)
Date = 0;
else
{
if ((Offset[0] == '+') || (Offset[0] == '-'))
{
MM = atoi(&Offset[3]);
Offset[3] = 0;
HH = atoi(&Offset[1]);
MM = MM + (60 * HH);
if (Offset[0] == '+')
Date -= (60*MM);
else
Date += (60*MM);
}
}
}
ptr1 = ptr2 + 2; // Skip crlf
if (linelen) // Not Null line
{
goto Loop;
}
ptr2 = ptr1;
ptr1 = sockptr->MailBuffer;
MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1));
// We Just want the from call, not the full address.
TidyString(sockptr->MailFrom);
// Examine Message to look for html formatting and attachments.
B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen); // Will reformat message if necessary.
// If any recipients are via RMS, create one message for them, and separate messages for all others
ToString = zalloc(sockptr->Recipients * 100);
for (i=0; i < sockptr->Recipients; i++)
{
char Addr[256]; // Need copy, as we may change it then decide it isn't for RMS
strcpy(Addr, sockptr->RecpTo[i]);
Debugprintf("To Addr %s", Addr);
TidyString(Addr);
Debugprintf("To Addr after Tidy %s", Addr);
if ((_memicmp (Addr, "RMS:", 4) == 0) |(_memicmp (Addr, "RMS/", 4) == 0))
{
// Add to B2 Message for RMS
_strlwr(Addr);
Via = strlop(&Addr[4], '@');
if (Via && _stricmp(Via, "winlink.org") == 0)
{
if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here
continue;
ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, &Addr[4]);
*sockptr->RecpTo[i] = 0; // So we dont create individual one later
continue;
}
ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, &Addr[4], Via);
*sockptr->RecpTo[i] = 0; // So we dont create individual one later
continue;
}
_strupr(Addr);
Debugprintf("To Addr after strupr %s", Addr);
Via = strlop(Addr, '@');
Debugprintf("Via %s", Via);
if (Via && _stricmp(Via, "winlink.org") == 0)
{
if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here
continue;
ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr);
*sockptr->RecpTo[i] = 0; // So we dont create individual one later
continue;
}
}
if (ToLen) // Have some RMS Addresses
{
char B2Hddr[1000];
int B2HddrLen;
char DateString[80];
char * NewBody;
struct tm * tm;
struct MsgInfo * Msg;
BIDRec * BIDRec;
Msg = AllocateMsgRecord();
// Set number here so they remain in sequence
Msg->number = ++LatestMsg;
MsgnotoMsg[Msg->number] = Msg;
Msg->length = MsgLen;
sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);
Msg->type = 'P';
Msg->status = 'N';
strcpy(Msg->to, "RMS");
strlop(sockptr->MailFrom, '@');
if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0;
strcpy(Msg->from, sockptr->MailFrom);
strcpy(Msg->title, Msgtitle);
BIDRec = AllocateBIDRecord();
strcpy(BIDRec->BID, Msg->bid);
BIDRec->mode = Msg->type;
BIDRec->u.msgno = LOWORD(Msg->number);
BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
if (Date)
Msg->datecreated = Date;
tm = gmtime(&Date);
sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
if (B2Flag) // Message has attachments, so Body: line is present
{
Msg->B2Flags = B2Msg | Attachments;
B2HddrLen = sprintf(B2Hddr,
"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\n",
Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName);
}
else
{
Msg->B2Flags = B2Msg;
B2HddrLen = sprintf(B2Hddr,
"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n",
Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName, Msg->length);
}
NewBody = ptr2 - B2HddrLen;
memcpy(NewBody, B2Hddr, B2HddrLen);
Msg->length += B2HddrLen;
free(ToString);
// Set up forwarding bitmap
MatchMessagetoBBSList(Msg, 0);
CreateSMTPMessageFile(NewBody, Msg);
}
for (i=0; i < sockptr->Recipients; i++)
{
if (*sockptr->RecpTo[i]) // not already sent to RMS?
CreateSMTPMessage(sockptr, i, Msgtitle, Date, ptr2, MsgLen, B2Flag);
else
free(sockptr->RecpTo[i]);
}
free(sockptr->RecpTo);
sockptr->RecpTo = NULL;
free(sockptr->MailFrom);
free(sockptr->MailBuffer);
sockptr->MailBufferSize=0;
sockptr->MailBuffer=0;
sockptr->MailSize = 0;
sockptr->Flags = 0;
sockptr->Recipients = 0;
SendSock(sockptr, "250 Ok");
return;
}
if ((sockptr->MailSize + Len) > sockptr->MailBufferSize)
{
sockptr->MailBufferSize += 10000;
sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
if (sockptr->MailBuffer == NULL)
{
CriticalErrorHandler("Failed to extend Message Buffer");
shutdown(sock, 0);
return;
}
}
memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len);
sockptr->MailSize += Len;
return;
}
if (sockptr->State == GettingUser)
{
char Out[30];
Buffer[Len-2]=0;
decodeblock(Buffer, Out);
decodeblock(&Buffer[4], &Out[3]);
decodeblock(&Buffer[8], &Out[6]);
decodeblock(&Buffer[12], &Out[9]);
if (strlen(Out) > 10) Out[10] = 0;
strcpy(sockptr->CallSign, Out);
sockptr->State = GettingPass;
SendSock(sockptr, "334 UGFzc3dvcmQ6");
return;
}
if (sockptr->State == GettingPass)
{
struct UserInfo * user = NULL;
char Out[30];
Buffer[Len-2]=0;
decodeblock(Buffer, Out);
decodeblock(&Buffer[4], &Out[3]);
decodeblock(&Buffer[8], &Out[6]);
decodeblock(&Buffer[12], &Out[9]);
decodeblock(&Buffer[16], &Out[12]);
decodeblock(&Buffer[20], &Out[15]);
user = LookupCall(sockptr->CallSign);
if (user)
{
if (strcmp(user->pass, Out) == 0)
{
sockptr->State = Authenticated;
SendSock(sockptr, "235 2.0.0 OK Authenticated"); //535 authorization failed
return;
}
}
SendSock(sockptr, "535 authorization failed");
sockptr->State = 0;
return;
}
/*AUTH LOGIN
334 VXNlcm5hbWU6
a4msl9ux
334 UGFzc3dvcmQ6
ZvVx9G1hcg==
235 2.0.0 OK Authenticated
*/
if(memcmp(Buffer, "AUTH LOGIN", 10) == 0)
{
sockptr->State = GettingUser;
SendSock(sockptr, "334 VXNlcm5hbWU6");
return;
}
if(memcmp(Buffer, "EHLO",4) == 0)
{
SendSock(sockptr, "250-BPQ Mail Server");
SendSock(sockptr, "250 AUTH LOGIN");
//250-8BITMIME
return;
}
if(memcmp(Buffer, "AUTH LOGIN", 10) == 0)
{
sockptr->State = GettingUser;
SendSock(sockptr, "334 VXNlcm5hbWU6");
return;
}
if(memcmp(Buffer, "HELO",4) == 0)
{
SendSock(sockptr, "250 Ok");
return;
}
if(_memicmp(Buffer, "MAIL FROM:", 10) == 0)
{
if (sockptr->State != Authenticated)
{
// Accept if from 44/8 and ends in ampr.org
if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 &&
(sockptr->sin.sin_addr.s_addr & 0xff) == 44)
{
}
else
{
SendSock(sockptr, "530 Authentication required");
return;
}
}
sockptr->MailFrom = zalloc(Len);
memcpy(sockptr->MailFrom, &Buffer[10], Len-12);
SendSock(sockptr, "250 Ok");
return;
}
if(_memicmp(Buffer, "RCPT TO:", 8) == 0)
{
if (sockptr->State != Authenticated)
{
// Accept if from 44/8 and ends in ampr.org
if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 &&
(sockptr->sin.sin_addr.s_addr & 0xff) == 44)
{
}
else
{
SendSock(sockptr, "530 Authentication required");
return;
}
}
sockptr->RecpTo=realloc(sockptr->RecpTo, (sockptr->Recipients+1) * sizeof(void *));
sockptr->RecpTo[sockptr->Recipients] = zalloc(Len);
memcpy(sockptr->RecpTo[sockptr->Recipients++], &Buffer[8], Len-10);
SendSock(sockptr, "250 Ok");
return;
}
if(memcmp(Buffer, "DATA\r\n", 6) == 0)
{
sockptr->MailBuffer=malloc(10000);
sockptr->MailBufferSize=10000;
if (sockptr->MailBuffer == NULL)
{
CriticalErrorHandler("Failed to create SMTP Message Buffer");
SendSock(sockptr, "250 Failed");
shutdown(sock, 0);
return;
}
sockptr->Flags |= GETTINGMESSAGE;
SendSock(sockptr, "354 End data with <CR><LF>.<CR><LF>");
return;
}
if(memcmp(Buffer, "QUIT\r\n", 6) == 0)
{
SendSock(sockptr, "221 OK");
Sleep(500);
shutdown(sock, 0);
return;
}
if(memcmp(Buffer, "RSET\r\n", 6) == 0)
{
SendSock(sockptr, "250 Ok");
// This cancelled AUTH which I think is wrong
//sockptr->State = 0;
if (sockptr->State != Authenticated)
sockptr->State = 0;
sockptr->Recipients = 0;
return;
}
return;
}
int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag)
{
struct MsgInfo * Msg;
BIDRec * BIDRec;
char * To;
char * via;
// Allocate a message Record slot
Msg = AllocateMsgRecord();
// Set number here so they remain in sequence
Msg->number = ++LatestMsg;
MsgnotoMsg[Msg->number] = Msg;
Msg->length = MsgLen;
sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);
Msg->type = 'P';
Msg->status = 'N';
BIDRec = AllocateBIDRecord();
strcpy(BIDRec->BID, Msg->bid);
BIDRec->mode = Msg->type;
BIDRec->u.msgno = LOWORD(Msg->number);
BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
if (Date)
Msg->datecreated = Date;
To = sockptr->RecpTo[i];
Debugprintf("To %s", To);
TidyString(To);
Debugprintf("To after tidy %s", To);
if (_memicmp(To, "bull/", 5) == 0)
{
Msg->type = 'B';
memmove(To, &To[5], strlen(&To[4]));
}
if ((_memicmp(To, "nts/", 4) == 0) ||(_memicmp(To, "nts:", 4) == 0) ||
(_memicmp(To, "nts.", 4) == 0))
{
Msg->type = 'T';
memmove(To, &To[4], strlen(&To[3]));
}
if (_memicmp(To, "rms:", 4) == 0)
{
via = _strlwr(strlop(To, ':'));
}
else if (_memicmp(To, "rms/", 4) == 0)
{
via = _strlwr(strlop(To, '/'));
}
else if (_memicmp(To, "rms.", 4) == 0)
{
via = _strlwr(strlop(To, '.'));
}
else if (_memicmp(To, "smtp:", 5) == 0)
{
via = _strlwr(strlop(To, ':'));
To[0] = 0;
}
else if (_memicmp(To, "smtp/", 5) == 0)
{
via = _strlwr(strlop(To, '/'));
To[0] = 0;
}
else
{
via = strlop(To, '@');
}
Debugprintf("via %s", via);
if (via)
{
int toLen;
if (strlen(via) > 40) via[40] = 0;
strcpy(Msg->via, via); // Save before messing with it
// if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR
toLen = (int)strlen(via);
if (_memicmp(&via[toLen - 8], "ampr.org", 8) == 0)
{
// if our domain keep here.
// if not, and SendAMPRDirect set, set as ISP,
// else set as RMS
if (_stricmp(via, AMPRDomain) == 0)
{
// Our Message- dont forward
}
else
{
// AMPR but not us
if (SendAMPRDirect)
{
sprintf(Msg->via,"%s@%s", To, via);
strcpy(To, "AMPR");
}
else
{
sprintf(Msg->via,"%s@%s", To, via);
strcpy(To, "RMS");
}
}
}
else
{
strlop(via, '.'); // Get first part of address
if (_stricmp(via, BBSName) == 0)
{
// sent via us - clear the name
Msg->via[0] = 0;
}
}
}
if (strlen(To) > 6) To[6]=0;
strcpy(Msg->to, To);
if (strchr(sockptr->MailFrom, '@'))
{
char * FromHA = strlop(sockptr->MailFrom, '@');
Msg->emailfrom[0] = '@';
strcpy(&Msg->emailfrom[1], FromHA);
}
if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0;
strcpy(Msg->from, sockptr->MailFrom);
strcpy(Msg->title, MsgTitle);
if(Msg->to[0] == 0)
SMTPMsgCreated=TRUE;
// If NTS message (TO is numeric and AT is NTSxx or NTSxx.NTS - Outlook won't accept x@y)
if (isdigits(Msg->to) && memcmp(Msg->via, "NTS", 3) == 0)
{
if (Msg->via[5] == 0 || strcmp(&Msg->via[5], ".NTS") == 0)
{
Msg->type = 'T';
Msg->via[5] = 0;
}
}
Debugprintf("Msg->Via %s", Msg->via);
if (B2Flag)
{
char B2Hddr[1000];
int B2HddrLen;
char B2To[80];
char * NewBody;
char DateString[80];
char * TypeString;
struct tm * tm;
tm = gmtime(&Date);
sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
if (strcmp(Msg->to, "RMS") == 0) // Address is in via
strcpy(B2To, Msg->via);
else
if (Msg->via[0])
sprintf(B2To, "%s@%s", Msg->to, Msg->via);
else
strcpy(B2To, Msg->to);
Msg->B2Flags = B2Msg | Attachments;
if (Msg->type == 'P')
TypeString = "Private" ;
else if (Msg->type == 'B')
TypeString = "Bulletin";
else if (Msg->type == 'T')
TypeString = "Traffic";
B2HddrLen = sprintf(B2Hddr,
"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
Msg->bid, DateString, TypeString,
Msg->from, B2To, Msg->title, BBSName);
NewBody = MsgBody - B2HddrLen;
memcpy(NewBody, B2Hddr, B2HddrLen);
Msg->length += B2HddrLen;
free(To);
// Set up forwarding bitmap
MatchMessagetoBBSList(Msg, 0);
if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
Msg->status = '$'; // Has forwarding
return CreateSMTPMessageFile(NewBody, Msg);
}
free(To);
// Set up forwarding bitmap
MatchMessagetoBBSList(Msg, 0);
if (Msg->type == 'B' && memcmp( Msg->fbbs, zeros, NBMASK) != 0)
Msg->status = '$'; // Has forwarding
return CreateSMTPMessageFile(MsgBody, Msg);
}
BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg)
{
char MsgFile[250];
FILE * hFile;
int WriteLen=0;
char Mess[255];
int len;
struct UserInfo * ToUser = LookupCall(Msg->to);
if (ToUser && ToUser->flags & F_HOLDMAIL)
{
int Length=0;
char * MailBuffer = malloc(100);
char Title[100];
Msg->status = 'H';
Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number);
sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set");
SendMessageToSYSOP(Title, MailBuffer, Length);
}
sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
hFile = fopen(MsgFile, "wb");
if (hFile)
{
WriteLen = (int)fwrite(Message, 1, Msg->length, hFile);
fclose(hFile);
}
if (WriteLen != Msg->length)
{
len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r");
CriticalErrorHandler(Mess);
return FALSE;
}
SaveMessageDatabase();
SaveBIDDatabase();
return TRUE;
}
int TidyString(char * Address)
{
// Cleans up a From: or To: Address
// May have leading or trailing spaces, or be enclosed by <>, or have a " " part
// From: "John Wiseman" <john.wiseman@ntlworld.com>
char * ptr1, * ptr2;
size_t len;
_strupr(Address);
Debugprintf(Address);
ptr1 = strchr(Address, '<');
if (ptr1)
{
ptr1++;
ptr2 = strlop(ptr1, '>');
len = (int)strlen(ptr1);
memmove(Address, ptr1, len);
Address[len] = 0;
// Could have surrounding "" ""
if (Address[0] == '"')
{
int len = (int)strlen(Address) - 1;
if (Address[len] == '"')
{
Address[len] = 0;
memmove(Address, &Address[1], len);
return 0;
}
// Thunderbird can put "" round part of address "rms:john.wiseman"@cantab.net
ptr2 = strchr(&Address[1], '"');
if (ptr2)
{
memmove(Address, &Address[1], ptr2 - &Address[1]);
memmove(ptr2 - 1, ptr2 + 1, strlen(ptr2 + 1) + 1);
}
}
return 0;
}
ptr1 = Address;
while (*ptr1 == ' ') ptr1++;
if (*ptr1 == '"')
{
ptr1++;
ptr1=strlop(ptr1, '"');
ptr2=strlop(ptr1, ' ');
ptr1=ptr2;
}
if (ptr1 == 0)
return 0;
if (*ptr1 == '<') ptr1++;
ptr2 = strlop(ptr1, '>');
strlop(ptr1, ' ');
len = strlen(ptr1);
memmove(Address, ptr1, len);
Address[len] = 0;
return 0;
}
/*
+OK POP3 server ready
USER john.wiseman
+OK please send PASS command
PASS gb7bpq
+OK john.wiseman is welcome here
STAT
+OK 6 115834
UIDL
+OK 6 messages
1 <4A0DC6E0.5020504@hb9bza.net>
2 <gul8gb+of9r@eGroups.com>
3 <1085101c9d5d0$09b15420$16f9280a@phx.gbl>
4 <gul9ms+qkht@eGroups.com>
5 <B0139742084@email.bigvalley.net>
6 <20090516011401.53DB013804@panix1.panix.com>
.
LIST
+OK 6 messages
1 7167
2 10160
3 52898
4 4746
5 20218
6 20645
.
*/
VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len)
{
SOCKET sock;
int i;
struct MsgInfo * Msg;
sock=sockptr->socket;
WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);
if(memcmp(Buffer, "CAPA",4) == 0)
{
SendSock(sockptr, "+OK Capability list follows");
SendSock(sockptr, "UIDL");
SendSock(sockptr, "TOP");
SendSock(sockptr, "EXPIRE 30");
SendSock(sockptr, ".");
return;
}
if(memcmp(Buffer, "AUTH",4) == 0)
{
SendSock(sockptr, "-ERR");
return;
}
if (sockptr->State == GettingUser)
{
Buffer[Len-2]=0;
if (Len > 15) Buffer[15]=0;
strcpy(sockptr->CallSign, &Buffer[5]);
sockptr->State = GettingPass;
SendSock(sockptr, "+OK please send PASS command");
return;
}
if (sockptr->State == GettingPass)
{
struct UserInfo * user = NULL;
Buffer[Len-2]=0;
user = LookupCall(sockptr->CallSign);
if (user)
{
if (strcmp(user->pass, &Buffer[5]) == 0)
{
if (user->POP3Locked)
{
SendSock(sockptr, "-ERR Mailbox Locked");
sockptr->State = 0;
return;
}
sockptr->State = Authenticated;
SendSock(sockptr, "+OK Authenticated");
sockptr->POP3User = user;
user->POP3Locked = TRUE;
// Get Message List
for (i=0; i<=NumberofMessages; i++)
{
Msg = MsgHddrPtr[i];
if ((_stricmp(Msg->to, sockptr->CallSign) == 0) ||
((_stricmp(Msg->to, "SYSOP") == 0) && (user->flags & F_SYSOP) && (Msg->type == 'P')))
{
if (Msg->status != 'K' && Msg->status != 'H')
{
sockptr->POP3Msgs = realloc(sockptr->POP3Msgs, (sockptr->POP3MsgCount+1) * sizeof(void *));
sockptr->POP3Msgs[sockptr->POP3MsgCount++] = MsgHddrPtr[i];
}
}
}
return;
}
}
SendSock(sockptr, "-ERR Authentication failed");
sockptr->State = 0;
return;
}
if (memcmp(Buffer, "QUIT",4) == 0)
{
SendSock(sockptr, "+OK Finished");
if (sockptr->POP3User)
sockptr->POP3User->POP3Locked = FALSE;
return;
}
if (memcmp(Buffer, "NOOP",4) == 0)
{
SendSock(sockptr, "+OK ");
return;
}
// if (memcmp(Buffer, "LAST",4) == 0)
// {
// SendSock(sockptr, "+OK 0");
// return;
// }
if (sockptr->State != Authenticated)
{
SendSock(sockptr, "-ERR Need Authentication");
sockptr->State = 0;
return;
}
if (memcmp(Buffer, "STAT",4) == 0)
{
char reply[40];
int i, size=0;
for (i=0; i< sockptr->POP3MsgCount; i++)
{
size+=sockptr->POP3Msgs[i]->length;
}
sprintf_s(reply, sizeof(reply), "+OK %d %d", sockptr->POP3MsgCount, size);
SendSock(sockptr, reply);
return;
}
if (memcmp(Buffer, "UIDL",4) == 0)
{
char reply[40];
int i, count=0, size=0;
int MsgNo=1;
SendSock(sockptr, "+OK ");
for (i=0; i< sockptr->POP3MsgCount; i++)
{
sprintf_s(reply, sizeof(reply), "%d %s", i+1, sockptr->POP3Msgs[i]->bid);
SendSock(sockptr, reply);
}
SendSock(sockptr, ".");
return;
}
if (memcmp(Buffer, "LIST", 4) == 0)
{
char reply[40];
int i, count=0, size=0;
int MsgNo = atoi(&Buffer[4]);
if (Buffer[4] == 13) // CR
MsgNo = 0;
Debugprintf("%s %d", Buffer, MsgNo);
if (MsgNo)
{
if (MsgNo > sockptr->POP3MsgCount)
sprintf(reply, "-ERR no such message, only %d messages in maildrop", sockptr->POP3MsgCount);
else
sprintf(reply, "+OK %d %d", MsgNo, sockptr->POP3Msgs[MsgNo - 1]->length);
SendSock(sockptr, reply);
return;
}
SendSock(sockptr, "+OK ");
for (i=0; i< sockptr->POP3MsgCount; i++)
{
sprintf_s(reply, sizeof(reply), "%d %d", i+1, sockptr->POP3Msgs[i]->length);
SendSock(sockptr, reply);
}
SendSock(sockptr, ".");
return;
}
if (memcmp(Buffer, "RETR", 4) == 0 || memcmp(Buffer, "TOP", 3) == 0)
{
char * ptr;
char Header[120];
int i, count=0, size=0;
int MsgNo=1;
char * msgbytes;
struct MsgInfo * Msg;
char B2From[80];
struct UserInfo * FromUser;
char TimeString[64];
BOOL TOP = FALSE;
int Len;
if (memcmp(Buffer, "TOP", 3) == 0)
TOP = TRUE;
ptr=strlop(Buffer, ' '); // Get Number
i=atoi(ptr);
if ((i > sockptr->POP3MsgCount) || (i == 0))
{
SendSock(sockptr, "-ERR no such message");
return;
}
Msg = sockptr->POP3Msgs[i-1];
msgbytes = ReadMessageFile(Msg->number);
if (msgbytes == NULL)
{
SendSock(sockptr, "-ERR no such message");
return;
}
SendSock(sockptr, "+OK ");
// Build an RFC822 ish header
//Received: from [69.147.65.148] by n15.bullet.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000
//Received: from [69.147.108.192] by t11.bullet.mail.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000
FormatTime(TimeString, (time_t)Msg->datecreated);
sprintf_s(Header, sizeof(Header), "Date: %s", TimeString);
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "To: %s", Msg->to);
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "Message-ID: %s", Msg->bid);
SendSock(sockptr, Header);
if (_stricmp(Msg->from, "smtp:") == 0)
{
sprintf_s(Header, sizeof(Header), "From: smtp/%s", Msg->emailfrom);
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "Replyto: smtp/%s", Msg->emailfrom);
}
else
{
if (_stricmp(Msg->from, "rms:") == 0)
{
sprintf_s(Header, sizeof(Header), "From: RMS/%s", Msg->emailfrom);
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "Replyto: RMS/%s", Msg->emailfrom);
}
else
{
// If there is an adddress in Msg->emailfrom use it
if (Msg->emailfrom[0])
{
strcpy(B2From, Msg->from);
strcat(B2From, Msg->emailfrom);
}
else
{
// Packet Address. Mail client will need more than just a call to respond to
strcpy(B2From, Msg->from);
if (strcmp(Msg->from, "SMTP:") == 0) // Address is in via
strcpy(B2From, Msg->emailfrom);
else
{
FromUser = LookupCall(Msg->from);
if (FromUser)
{
if (FromUser->HomeBBS[0])
sprintf(B2From, "%s@%s", Msg->from, FromUser->HomeBBS);
else
sprintf(B2From, "%s@%s", Msg->from, BBSName);
}
else
{
WPRecP WP = LookupWP(Msg->from);
if (WP)
sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs);
}
}
}
sprintf_s(Header, sizeof(Header), "From: %s", B2From);
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "Replyto: %s", B2From);
}
}
SendSock(sockptr, Header);
sprintf_s(Header, sizeof(Header), "Subject: %s", Msg->title);
SendSock(sockptr, Header);
if ((Msg->B2Flags & Attachments) && TOP == FALSE)
{
// B2 Message with Attachments. Create a Mime-Encoded Multipart message
SendMultiPartMessage(sockptr, Msg, msgbytes);
return;
}
if (TOP)
{
// Get first i lines of message
char * ptr1, * ptr2;
ptr = strlop(ptr, ' '); // Get Number of lines
i = atoi(ptr);
ptr1 = msgbytes;
ptr2 = --ptr1; // Point both to char before message
while(i--)
{
ptr2 = strchr(++ptr1, 10);
if (ptr2 == 0) // No more lines
i = 0;
ptr1 = ptr2;
}
if (ptr2)
*(ptr2 + 1) = 0;
}
// If message has characters above 7F convert to UFT8 if necessary and send as Base64
Len = (int)strlen(msgbytes);
if (Is8Bit(msgbytes, Len))
{
// 8 Bit. Will send as UFT8
if (WebIsUTF8(msgbytes, Len) == FALSE)
{
// Must be some other coding
int code = TrytoGuessCode(msgbytes, Len);
UCHAR * UTF = malloc(Len * 3);
if (code == 437)
Len = Convert437toUTF8(msgbytes, Len, UTF);
else if (code == 1251)
Len = Convert1251toUTF8(msgbytes, Len, UTF);
else
Len = Convert1252toUTF8(msgbytes, Len, UTF);
free(msgbytes);
msgbytes = UTF;
}
SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\"");
SendSock(sockptr, "Content-Transfer-Encoding: base64");
SendSock(sockptr, "Content-Disposition: inline");
SendSock(sockptr, ""); // Blank line before body
Base64EncodeAndSend(sockptr, msgbytes, Len);
}
else
{
// send as USASCII
SendSock(sockptr, ""); // Blank line before body
SendSock(sockptr, msgbytes);
}
SendSock(sockptr, "");
SendSock(sockptr, ".");
free(msgbytes);
return;
}
if (memcmp(Buffer, "DELE",4) == 0)
{
char * ptr;
int i;
struct MsgInfo * Msg;
ptr=strlop(Buffer, ' '); // Get Number
i=atoi(ptr);
if ((i > sockptr->POP3MsgCount) || (i == 0))
{
SendSock(sockptr, "-ERR no such message");
return;
}
Msg = sockptr->POP3Msgs[i-1];
FlagAsKilled(Msg, TRUE);
SendSock(sockptr, "+OK ");
return;
}
if (memcmp(Buffer, "QUIT",4) == 0)
{
SendSock(sockptr, "+OK Finished");
if (sockptr->POP3User)
sockptr->POP3User->POP3Locked = FALSE;
return;
}
SendSock(sockptr, "-ERR Unrecognised Command");
}
/* jer:
* This is the original file, my mods were only to change the name/semantics on the b64decode function
* and remove some dependencies.
*/
/*
LibCGI base64 manipulation functions is extremly based on the work of Bob Tower,
from its projec http://base64.sourceforge.net. The functions were a bit modicated.
Above is the MIT license from b64.c original code:
LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall
be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
*/
void encodeblock( unsigned char in[3], unsigned char out[4], int len )
{
out[0] = cb64[ in[0] >> 2 ];
out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}
void decodeblock( unsigned char in[4], unsigned char out[3])
{
char Block[5];
Block[0]=mycd64[in[0]];
Block[1]=mycd64[in[1]];
Block[2]=mycd64[in[2]];
Block[3]=mycd64[in[3]];
out[0] = (unsigned char ) (Block[0] << 2 | Block[1] >> 4);
out[1] = (unsigned char ) (Block[1] << 4 | Block[2] >> 2);
out[2] = (unsigned char ) (((Block[2] << 6) & 0xc0) | Block[3]);
}
/**
* @ingroup libcgi_string
* @{
*/
/**
* Encodes a given tring to its base64 form.
*
* @param *str String to convert
* @return Base64 encoded String
* @see str_base64_decode
**/
char *str_base64_encode(char *str)
{
unsigned int i = 0, j = 0, len = (int)strlen(str);
char *tmp = str;
char *result = (char *)zalloc((len+1) * sizeof(void *));
if (!result)
return NULL;
while (len > 2 )
{
encodeblock(&str[i], &result[j],3);
i+=3;
j+=4;
len -=3;
}
if (len)
{
encodeblock(&str[i], &result[j], len);
}
return result;
}
SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody)
{
int err;
u_long param=1;
BOOL bcopt=TRUE;
SocketConn * sockptr;
SOCKADDR_IN sinx;
SOCKADDR_IN destaddr;
int addrlen=sizeof(sinx);
struct hostent * HostEnt;
// Resolve Name if needed
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(Port);
destaddr.sin_addr.s_addr = inet_addr(Host);
if (destaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
HostEnt = gethostbyname (Host);
if (!HostEnt)
{
Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for SMTP Server %s", Host);
SMTPActive = FALSE;
return FALSE; // Resolve failed
}
memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
// Allocate a Socket entry
sockptr=malloc(sizeof(SocketConn));
memset(sockptr, 0, sizeof (SocketConn));
sockptr->Next=Sockets;
Sockets=sockptr;
sockptr->socket=socket(AF_INET,SOCK_STREAM,0);
if (sockptr->socket == INVALID_SOCKET)
{
return FALSE;
}
sockptr->Type = SMTPClient;
sockptr->AMPR = AMPR;
if (AMPR)
strcpy(sockptr->FromDomain, AMPRDomain);
else
strcpy(sockptr->FromDomain, MyDomain);
sockptr->SMTPMsg = Msg;
sockptr->MailBuffer = MsgBody;
ioctlsocket (sockptr->socket, FIONBIO, &param);
setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = 0;
if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 )
{
//
// Bind Failed
//
return FALSE;
}
if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
{
//
// Connected successful
//
sockptr->State = WaitingForGreeting;
return sockptr;
}
else
{
err=WSAGetLastError();
if (err == WSAEWOULDBLOCK || err == 115 || err == 36)
{
//
// Connect in Progress
//
sockptr->State = Connecting;
return sockptr;
}
else
{
//
// Connect failed
//
printf("SMTP Connect failed immediately\n");
closesocket(sockptr->socket);
ReleaseSock(sockptr->socket);
return FALSE;
return FALSE;
}
}
return FALSE;
}
int TryHELO = 0; // Not thread safe but taking the chance..
VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len)
{
SOCKET sock;
sock=sockptr->socket;
WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);
Buffer[Len] = 0;
if (sockptr->State == WaitingForGreeting)
{
if (memcmp(Buffer, "220 ",4) == 0)
{
TryHELO = 0;
if (sockptr->AMPR)
sockprintf(sockptr, "EHLO %s", AMPRDomain);
else if (ISPEHLOName[0])
sockprintf(sockptr, "EHLO %s", ISPEHLOName);
else
sockprintf(sockptr, "EHLO %s", BBSName);
sockptr->State = WaitingForHELOResponse;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = 0;
}
return;
}
if (sockptr->State == WaitingForHELOResponse)
{
/*
if (memcmp(Buffer, "500 ",4) == 0 && TryHELO == 0)
{
TryHELO = 1;
if (sockptr->AMPR)
sockprintf(sockptr, "HELO %s", AMPRDomain);
else if (ISPEHLOName[0])
sockprintf(sockptr, "HELO %s", ISPEHLOName);
else
sockprintf(sockptr, "HELO %s", BBSName);
return;
}
*/
if (memcmp(Buffer, "250-",4) == 0)
return;
if (memcmp(Buffer, "250 ",4) == 0)
{
if (SMTPAuthNeeded && sockptr->AMPR == FALSE)
{
sockprintf(sockptr, "AUTH LOGIN");
sockptr->State = WaitingForAUTHResponse;
}
else
{
sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain);
sockptr->State = WaitingForFROMResponse;
}
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
}
return;
}
if (sockptr->State == WaitingForAUTHResponse)
{
if (memcmp(Buffer, "334 VXN", 7) == 0)
{
char * Msg = str_base64_encode(ISPAccountName);
SendSock(sockptr, Msg);
free(Msg);
return;
}
else if (memcmp(Buffer, "334 UGF", 7) == 0)
{
char * Msg = str_base64_encode(ISPAccountPass);
SendSock(sockptr, Msg);
free(Msg);
return;
}
else if (memcmp(Buffer, "235 ", 4) == 0)
{
sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain);
// sockprintf(sockptr, "MAIL FROM: <%s@%s.%s>", sockptr->SMTPMsg->from, BBSName, HRoute);
sockptr->State = WaitingForFROMResponse;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
}
return;
}
if (sockptr->State == WaitingForFROMResponse)
{
if (memcmp(Buffer, "250 ",4) == 0)
{
sockprintf(sockptr, "RCPT TO: <%s>", sockptr->SMTPMsg->via);
sockptr->State = WaitingForTOResponse;
}
else
{
sockptr->SMTPMsg->status = 'H'; // Hold for review
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
}
return;
}
if (sockptr->State == WaitingForTOResponse)
{
if (memcmp(Buffer, "250 ",4) == 0)
{
SendSock(sockptr, "DATA");
sockptr->State = WaitingForDATAResponse;
}
else
{
sockptr->SMTPMsg->status = 'H'; // Hold for review
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
}
return;
}
if (sockptr->State == WaitingForDATAResponse)
{
int Len;
UCHAR * UTF;
if (memcmp(Buffer, "354 ",4) == 0)
{
sockprintf(sockptr, "To: %s", sockptr->SMTPMsg->via);
sockprintf(sockptr, "From: %s <%s@%s>", sockptr->SMTPMsg->from, sockptr->SMTPMsg->from, sockptr->FromDomain);
sockprintf(sockptr, "Sender: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain);
if (GMailMode && sockptr->AMPR == FALSE)
sockprintf(sockptr, "Reply-To: %s+%s@%s", GMailName, sockptr->SMTPMsg->from, sockptr->FromDomain);
else
sockprintf(sockptr, "Reply-To: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain);
sockprintf(sockptr, "Subject: %s", sockptr->SMTPMsg->title);
sockptr->State = WaitingForBodyResponse;
if (sockptr->SMTPMsg->B2Flags & Attachments)
{
// B2 Message with Attachments. Create a Mime-Encoded Multipart message
SendMultiPartMessage(sockptr, sockptr->SMTPMsg, sockptr->MailBuffer);
return;
}
// If message has characters above 7F convert to UFT8 if necessary and send as Base64
Len = (int)strlen(sockptr->MailBuffer);
if (Is8Bit(sockptr->MailBuffer, Len))
{
// 8 Bit. Will send as UFT8
SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\"");
SendSock(sockptr, "Content-Transfer-Encoding: base64");
SendSock(sockptr, "Content-Disposition: inline");
SendSock(sockptr, ""); // Blank line before body
if (WebIsUTF8(sockptr->MailBuffer, Len) == FALSE)
{
// Must be some other coding
int code = TrytoGuessCode(sockptr->MailBuffer, Len);
UTF = malloc(Len * 3);
if (code == 437)
Len = Convert437toUTF8(sockptr->MailBuffer, Len, UTF);
else if (code == 1251)
Len = Convert1251toUTF8(sockptr->MailBuffer, Len, UTF);
else
Len = Convert1252toUTF8(sockptr->MailBuffer, Len, UTF); // Default
Base64EncodeAndSend(sockptr, UTF, Len);
free(UTF);
}
else
Base64EncodeAndSend(sockptr, sockptr->MailBuffer, Len);
}
else
{
// send as USASCII
SendSock(sockptr, ""); // Blank line before body
SendSock(sockptr, sockptr->MailBuffer);
}
SendSock(sockptr, ".");
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
}
return;
}
if (sockptr->State == WaitingForBodyResponse)
{
struct MsgInfo * Msg = sockptr->SMTPMsg;
if (memcmp(Buffer, "250 ", 4) == 0)
{
// if AMPR, clear forwarding bitmap
if (sockptr->AMPR)
{
// Mark mail as sent, and look for more
struct UserInfo * bbs = sockptr->bbs;
clear_fwd_bit(Msg->fbbs, bbs->BBSNumber);
set_fwd_bit(Msg->forw, bbs->BBSNumber);
// Only mark as forwarded if sent to all BBSs that should have it
if (memcmp(Msg->fbbs, zeros, NBMASK) == 0)
{
Msg->status = 'F'; // Mark as forwarded
Msg->datechanged=time(NULL);
}
bbs->ForwardingInfo->MsgCount--;
bbs->ForwardingInfo->Forwarding = 0;
// See if any more
if (bbs->ForwardingInfo->MsgCount)
bbs->ForwardingInfo->FwdTimer = bbs->ForwardingInfo->FwdInterval; // Reschdul send
}
else
{
Msg->status = 'F';
SMTPActive = FALSE;
SMTPMsgCreated=TRUE; // See if any more
}
}
SendSock(sockptr, "QUIT");
sockptr->State = 0;
SMTPActive = FALSE;
SMTPMsgCreated=TRUE; // See if any more
return;
}
}
BOOL SendtoAMPR(CIRCUIT * conn)
{
struct MsgInfo * Msg = conn->FwdMsg;
SocketConn * sockptr;
char * Body;
int toLen;
char * tocopy;
char * Host;
// Make sure message exists
Body = ReadMessageFile(Msg->number);
if (Body == NULL)
{
FlagAsKilled(Msg, TRUE);
return FALSE;
}
toLen = (int)strlen(Msg->via);
tocopy = _strdup(Msg->via);
Host = strlop(tocopy, '@');
if (Host == NULL)
{
Logprintf(LOG_TCP, NULL, '|', "AMPR Forward - Host Name missing from VIA %s for Msg %d", Msg->via, Msg->number);
free(tocopy);
return FALSE;
}
Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", Host, Msg->number);
sockptr = SMTPConnect(Host, 25, TRUE, Msg, Body);
free(tocopy);
if (sockptr)
{
sockptr->bbs = conn->UserPointer;
return TRUE;
}
return FALSE;
}
BOOL SendtoISP()
{
// Find a message intended for the Internet and send it
int m = NumberofMessages;
char * Body;
struct MsgInfo * Msg;
if (SMTPActive)
return FALSE;
do
{
Msg=MsgHddrPtr[m];
if ((Msg->status == 'N') && (Msg->to[0] == 0) && (Msg->from[0] != 0))
{
// Make sure message exists
Body = ReadMessageFile(Msg->number);
if (Body == NULL)
{
FlagAsKilled(Msg, TRUE);
return FALSE;
}
Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", ISPSMTPName, Msg->number);
SMTPMsgCreated=FALSE; // Stop any more attempts
SMTPConnect(ISPSMTPName, ISPSMTPPort, FALSE, Msg, Body);
SMTPActive = TRUE;
return TRUE;
}
m--;
} while (m> 0);
return FALSE;
}
BOOL POP3Connect(char * Host, int Port)
{
int err;
u_long param=1;
BOOL bcopt=TRUE;
SocketConn * sockptr;
SOCKADDR_IN sinx;
SOCKADDR_IN destaddr;
int addrlen=sizeof(sinx);
struct hostent * HostEnt;
Logprintf(LOG_TCP, NULL, '|', "Connecting to POP3 Server %s", Host);
// Resolve Name if needed
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(Port);
destaddr.sin_addr.s_addr = inet_addr(Host);
if (destaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
HostEnt = gethostbyname (Host);
if (!HostEnt)
{
Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for POP3 Server %s", Host);
return FALSE; // Resolve failed
}
memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
// Allocate a Socket entry
sockptr = malloc(sizeof(SocketConn));
memset(sockptr, 0, sizeof (SocketConn));
sockptr->Next = Sockets;
Sockets = sockptr;
sockptr->socket = socket(AF_INET,SOCK_STREAM,0);
if (sockptr->socket == INVALID_SOCKET)
{
return FALSE;
}
sockptr->Type = POP3Client;
ioctlsocket (sockptr->socket, FIONBIO, &param);
setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = 0;
if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 )
{
//
// Bind Failed
//
return FALSE;
}
if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
{
//
// Connected successful
//
sockptr->State = WaitingForGreeting;
return TRUE;
}
else
{
err=WSAGetLastError();
if (err == WSAEWOULDBLOCK || err == 115 || err == 36)
{
//
// Connect in Progressing
//
sockptr->State = Connecting;
return TRUE;
}
else
{
//
// Connect failed
//
printf("Connect failed immediately %d\n", err);
perror("POP Connect");
closesocket(sockptr->socket);
ReleaseSock(sockptr->socket);
return FALSE;
}
}
return FALSE;
}
VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len)
{
SOCKET sock;
time_t Date;
BOOL B2Flag;
sock=sockptr->socket;
WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);
if (sockptr->Flags == GETTINGMESSAGE)
{
if(memcmp(Buffer, ".\r\n", 3) == 0)
{
// File Message
char * ptr1, * ptr2;
int linelen, MsgLen;
char MsgFrom[62], MsgTo[100], Msgtitle[62];
// Scan headers for From: To: and Subject: Line (Headers end at blank line)
ptr1 = sockptr->MailBuffer;
Loop:
ptr2 = strchr(ptr1, '\r');
if (ptr2 == NULL)
{
SendSock(sockptr, "500 Eh");
return;
}
linelen = (int)(ptr2 - ptr1);
// From: "John Wiseman" <john.wiseman@ntlworld.com>
// To: <G8BPQ@g8bpq.org.uk>
//<To: <gm8bpq+g8bpq@googlemail.com>
if (_memicmp(ptr1, "From:", 5) == 0)
{
if (linelen > 65) linelen = 65;
memcpy(MsgFrom, &ptr1[5], linelen-5);
MsgFrom[linelen-5]=0;
}
else
if (_memicmp(ptr1, "To:", 3) == 0)
{
if (linelen > 99) linelen = 99;
memcpy(MsgTo, &ptr1[4], linelen-4);
MsgTo[linelen-4]=0;
}
else
if (_memicmp(ptr1, "Subject:", 8) == 0)
{
if (linelen > 68) linelen = 68;
memcpy(Msgtitle, &ptr1[9], linelen-9);
Msgtitle[linelen-9]=0;
}
else
if (_memicmp(ptr1, "Date:", 5) == 0)
{
struct tm rtime;
char * Context;
char seps[] = " ,\t\r";
char Offset[10] = "";
int i, HH, MM;
char Copy[500]="";
// Copy message, so original isn't messed up by strtok
memcpy(Copy, ptr1, linelen);
ptr1 = Copy;
memset(&rtime, 0, sizeof(struct tm));
// Date: Tue, 9 Jun 2009 20:54:55 +0100
ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day
ptr1 = strtok_s(NULL, seps, &Context); // Day
rtime.tm_mday = atoi(ptr1);
ptr1 = strtok_s(NULL, seps, &Context); // Month
for (i=0; i < 12; i++)
{
if (strcmp(month[i], ptr1) == 0)
{
rtime.tm_mon = i;
break;
}
}
sscanf(Context, "%04d %02d:%02d:%02d%s",
&rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset);
rtime.tm_year -= 1900;
Date = mktime(&rtime) - (time_t)_MYTIMEZONE;
if (Date == (time_t)-1)
Date = 0;
else
{
if ((Offset[0] == '+') || (Offset[0] == '-'))
{
MM = atoi(&Offset[3]);
Offset[3] = 0;
HH = atoi(&Offset[1]);
MM = MM + (60 * HH);
if (Offset[0] == '+')
Date -= (60*MM);
else
Date += (60*MM);
}
}
}
if (linelen) // Not Null line
{
ptr1 = ptr2 + 2; // Skip crlf
goto Loop;
}
ptr1 = sockptr->MailBuffer;
TidyString(MsgFrom);
_strlwr(MsgFrom);
MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1));
B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen); // Will reformat message if necessary.
CreatePOP3Message(MsgFrom, MsgTo, Msgtitle, Date, ptr2, MsgLen, B2Flag);
free(sockptr->MailBuffer);
sockptr->MailBufferSize=0;
sockptr->MailBuffer=0;
sockptr->MailSize = 0;
sockptr->Flags &= ~GETTINGMESSAGE;
if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++)
{
sockprintf(sockptr, "RETR %d", sockptr->POP3MsgNum);
sockptr->State = WaitingForRETRResponse;
}
else
{
sockptr->POP3MsgNum = 1;
sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);;
sockptr->State = WaitingForDELEResponse;
}
return;
}
if ((sockptr->MailSize + Len) > sockptr->MailBufferSize)
{
sockptr->MailBufferSize += 10000;
sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
if (sockptr->MailBuffer == NULL)
{
CriticalErrorHandler("Failed to extend Message Buffer");
shutdown(sock, 0);
return;
}
}
memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len);
sockptr->MailSize += Len;
return;
}
if (sockptr->State == WaitingForGreeting)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
sockprintf(sockptr, "USER %s", ISPAccountName);
sockptr->State = WaitingForUSERResponse;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = 0;
}
return;
}
if (sockptr->State == WaitingForUSERResponse)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
sockprintf(sockptr, "PASS %s", ISPAccountPass);
sockptr->State = WaitingForPASSResponse;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = WaitingForQUITResponse;
}
return;
}
if (sockptr->State == WaitingForPASSResponse)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
SendSock(sockptr, "STAT");
sockptr->State = WaitingForSTATResponse;
}
else
{
shutdown(sock, 0);
sockptr->State = 0;
}
return;
}
if (sockptr->State == WaitingForSTATResponse)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
int Msgs = atoi(&Buffer[3]);
if (Msgs > 0)
{
sockptr->POP3MsgCount = Msgs;
sockptr->POP3MsgNum = 1;
SendSock(sockptr, "RETR 1");
sockptr->State = WaitingForRETRResponse;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = WaitingForQUITResponse;
}
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = WaitingForQUITResponse;
}
return;
}
if (sockptr->State == WaitingForRETRResponse)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
sockptr->MailBuffer=malloc(10000);
sockptr->MailBufferSize=10000;
if (sockptr->MailBuffer == NULL)
{
CriticalErrorHandler("Failed to create POP3 Message Buffer");
SendSock(sockptr, "QUIT");
sockptr->State = WaitingForQUITResponse;
shutdown(sock, 0);
return;
}
sockptr->Flags |= GETTINGMESSAGE;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->State = WaitingForQUITResponse;
}
return;
}
if (sockptr->State == WaitingForDELEResponse)
{
if (memcmp(Buffer, "+OK", 3) == 0)
{
if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++)
{
sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);;
}
else
{
SendSock(sockptr, "QUIT");
sockptr->Flags = WaitingForQUITResponse;
}
}
else
{
shutdown(sock,0);
sockptr->State = 0;
}
return;
}
if (sockptr->State == WaitingForQUITResponse)
{
shutdown(sock,0);
sockptr->State = 0;
return;
}
SendSock(sockptr, "QUIT");
shutdown(sock,0);
sockptr->State = 0;
}
static char Winlink[] = "WINLINK.ORG";
int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag)
{
struct MsgInfo * Msg;
BIDRec * BIDRec;
// Allocate a message Record slot
Msg = AllocateMsgRecord();
// Set number here so they remain in sequence
Msg->number = ++LatestMsg;
MsgnotoMsg[Msg->number] = Msg;
Msg->length = MsgLen;
sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);
Msg->type = 'P';
Msg->status = 'N';
Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
if (Date)
Msg->datecreated = Date;
BIDRec = AllocateBIDRecord();
strcpy(BIDRec->BID, Msg->bid);
BIDRec->mode = Msg->type;
BIDRec->u.msgno = LOWORD(Msg->number);
BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
TidyString(To);
strlop(To, '@');
// Could have surrounding "" ""
if (To[0] == '"')
{
int len = (int)strlen(To) - 1;
if (To[len] == '"')
{
To[len] = 0;
memmove(To, &To[1], len);
}
}
if (GMailMode)
{
// + separates our address and the target user
char * GMailto;
GMailto = strlop(To,'+');
if (GMailto)
{
char * GmailVia = NULL;
strcpy(To, GMailto);
GmailVia = strlop(To, '|');
if (GmailVia)
strcpy(Msg->via, GmailVia);
}
else
{
// Someone has sent to the GMAIL account without a +.
// This should go to the BBS Call
strcpy(To, BBSName);
}
}
if ((_memicmp(To, "bull/", 5) == 0) || (_memicmp(To, "bull.", 5) == 0)
|| (_memicmp(To, "bull:", 5) == 0))
{
Msg->type = 'B';
memmove(To, &To[5], strlen(&To[4]));
}
if ((_memicmp(To, "nts/", 4) == 0) || (_memicmp(To, "nts.", 4) == 0)
|| (_memicmp(To, "nts:", 4) == 0))
{
Msg->type = 'T';
memmove(To, &To[4], strlen(&To[3]));
}
if (Msg->type == 'P' && Msg->via[0] == 0)
{
// No via - add one from HomeBBS or WP
struct UserInfo * ToUser = LookupCall(To);
if (ToUser)
{
// Local User. If Home BBS is specified, use it
if (ToUser->flags & F_RMSREDIRECT)
{
// sent to Winlink
strcpy(Msg->via, Winlink);
}
else if (ToUser->HomeBBS[0])
strcpy(Msg->via, ToUser->HomeBBS);
}
else
{
WPRec * WP = LookupWP(To);
if (WP)
strcpy(Msg->via, WP->first_homebbs);
}
}
/* if (_memicmp(To, "rms:", 4) == 0)
{
via = _strlwr(strlop(To, ':'));
}
else if (_memicmp(To, "rms/", 4) == 0)
{
via = _strlwr(strlop(To, '/'));
}
else if (_memicmp(To, "rms.", 4) == 0)
{
via = _strlwr(strlop(To, '.'));
}
*/
if (strlen(To) > 6) To[6]=0;
strcpy(Msg->to, To);
strcpy(Msg->from, "smtp:");
strcpy(Msg->emailfrom, From);
strcpy(Msg->title, MsgTitle);
if(Msg->to[0] == 0)
SMTPMsgCreated=TRUE;
if (B2Flag)
{
char B2Hddr[1000];
int B2HddrLen;
char B2To[80];
char * NewBody;
char DateString[80];
struct tm * tm;
char Type[16] = "Private";
// Get Type
if (Msg->type == 'B')
strcpy(Type, "Bulletin");
else if (Msg->type == 'T')
strcpy(Type, "Traffic");
tm = gmtime(&Date);
sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
if (strcmp(Msg->to, "RMS") == 0) // Address is in via
strcpy(B2To, Msg->via);
else
if (Msg->via[0])
sprintf(B2To, "%s@%s", Msg->to, Msg->via);
else
strcpy(B2To, Msg->to);
Msg->B2Flags = B2Msg | Attachments;
B2HddrLen = sprintf(B2Hddr,
"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
Msg->bid, DateString, Type,
Msg->from, B2To, Msg->title, BBSName);
NewBody = MsgBody - B2HddrLen;
memcpy(NewBody, B2Hddr, B2HddrLen);
Msg->length += B2HddrLen;
// Set up forwarding bitmap
MatchMessagetoBBSList(Msg, 0);
return CreateSMTPMessageFile(NewBody, Msg);
}
// Set up forwarding bitmap
MatchMessagetoBBSList(Msg, 0);
return CreateSMTPMessageFile(MsgBody, Msg);
}
VOID base64_encode(char *str, char * result, int len)
{
unsigned int i = 0, j = 0;
char *tmp = str;
while (len > 2 )
{
encodeblock(&str[i], &result[j],3);
i+=3;
j+=4;
len -=3;
}
if (len)
{
encodeblock(&str[i], &result[j], len);
}
return;
}
void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len)
{
char Base64Line[80];
int i = Len;
int j = 0;
Base64Line[76] = 13;
Base64Line[77] = 10;
Base64Line[78] = 0;
// Need to encode in 57 byte chunks to give 76 char lines.
while(i > 57)
{
base64_encode(&Msg[j], Base64Line, 57);
SendSock(sockptr, Base64Line);
j += 57;
i -= 57;
}
memset(Base64Line, 0, 79);
base64_encode(&Msg[j], Base64Line, i);
SendSock(sockptr, Base64Line);
SendSock(sockptr, "");
}
VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes)
{
char * ptr;
char Header[120];
char Separator[33]="";
char FileName[100][250] = {""};
int FileLen[100];
int Files = 0;
int BodyLen;
int i;
CreateOneTimePassword(&Separator[0], "Key", 0);
CreateOneTimePassword(&Separator[16], "Key", 1);
SendSock(sockptr, "MIME-Version: 1.0");
sprintf_s(Header, sizeof(Header), "Content-Type: multipart/mixed; boundary=\"%s\"", Separator);
SendSock(sockptr, Header);
SendSock(sockptr, ""); // Blank line before body
// Get Part Sizes and Filenames
ptr = msgbytes;
while(*ptr != 13)
{
char * ptr2 = strchr(ptr, 10); // Find CR
if (memcmp(ptr, "Body: ", 6) == 0)
{
BodyLen = atoi(&ptr[6]);
}
if (memcmp(ptr, "File: ", 6) == 0)
{
char * ptr1 = strchr(&ptr[6], ' '); // Find Space
FileLen[Files] = atoi(&ptr[6]);
memcpy(FileName[Files++], &ptr1[1], (ptr2-ptr1 - 2));
}
ptr = ptr2;
ptr++;
}
ptr += 2; // Over Blank Line
// Write the none-Mime Part
SendSock(sockptr, "This is a multi-part message in MIME format.");
SendSock(sockptr, "");
// Write the Body as the first part.
sprintf_s(Header, sizeof(Header), "--%s", Separator);
SendSock(sockptr, Header);
SendSock(sockptr, "Content-Type: text/plain");
SendSock(sockptr, "");
ptr[BodyLen] = 0;
SendSock(sockptr, ptr);
ptr += BodyLen; // to first file
ptr += 2; // Over Blank Line
// Write Each Attachment
for (i = 0; i < Files; i++)
{
sprintf_s(Header, sizeof(Header), "--%s", Separator);
SendSock(sockptr, Header);
// Content-Type: image/png; name="UserParams.png"
SendSock(sockptr, "Content-Transfer-Encoding: base64");
sprintf_s(Header, sizeof(Header), "Content-Disposition: attachment; filename=\"%s\"", FileName[i]);
SendSock(sockptr, Header);
SendSock(sockptr, "");
// base64 encode and send file
Base64EncodeAndSend(sockptr, ptr, FileLen[i]);
ptr += FileLen[i];
ptr +=2; // Over separator
}
sprintf_s(Header, sizeof(Header), "--%s--", Separator);
SendSock(sockptr, Header);
SendSock(sockptr, "");
SendSock(sockptr, ".");
free(msgbytes);
return;
}
BOOL SendAMPRSMTP(CIRCUIT * conn)
{
struct UserInfo * bbs = conn->UserPointer;
while (FindMessagestoForward(conn))
{
if (SendtoAMPR(conn))
{
bbs->ForwardingInfo->Forwarding = TRUE;
return TRUE;
}
}
bbs->ForwardingInfo->Forwarding = FALSE;
return FALSE;
}