/* 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 *)¶m,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, ¶m); 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, ¶m); 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 ."); 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" 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 3 <1085101c9d5d0$09b15420$16f9280a@phx.gbl> 4 5 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, ¶m); 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; sockptr->Timeout = 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, ¶m); 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" // To: // 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; }