/* Libsmtp - (C)opyright 2000-2001 Kristian Van Der Vliet This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #ifdef __SMTP_DEBUG # include #endif /* __SMTP_DEBUG */ /* Socket *has* to be a global */ int __smtpGlobalSocket; /* Timeout must also be global */ unsigned long __smtpTimeout = 30; unsigned char __smtpTimedOut = 0; /* 0==FALSE 1==TRUE */ #define __SMTP_ALARM_SET \ signal(SIGALRM, smtp_alarm); \ (void) alarm(__smtpTimeout); #define __SMTP_ALARM_CLEAR \ signal(SIGALRM, SIG_DFL); \ (void) alarm(0); int smtp_clear_buf(int nSocket); void smtp_alarm(int unused); int smtp_connect(const char* zServer, int nPort) { struct sockaddr_in sSocketAddr; /* Socket connection details */ struct hostent* sServer; /* Host details */ char nRecvBuf[3]; /* Receive buffer */ int nBytesRecv,nBytesSend; /* Number of bytes read by recv & send*/ char zHostName[255]; /* Hostname of this computer */ char* nHeloMsg; /* Message sent when we HELO with the server */ #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Server is \"%s\", port %i\n", zServer,nPort); #endif /* __SMTP_DEBUG */ /* Lookup the hostname of the server */ sServer=gethostbyname(zServer); if(sServer==NULL) return(-SMTP_UNKNOWN_SERVER); #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: IP address %s\n", inet_ntoa(*((struct in_addr *)sServer->h_addr))); #endif /* __SMTP_DEBUG */ /* Now attempt to open a socket to the server */ __smtpGlobalSocket=socket(PF_INET,SOCK_STREAM,0); sSocketAddr.sin_family=AF_INET; sSocketAddr.sin_port=htons(nPort); /* FIXME: Don't even ask. The code is ugly. It does it ass */ /* backwards. It sucks. But it works. Want to fix it? */ /* Please do :) */ if(inet_aton( inet_ntoa(*((struct in_addr *)sServer->h_addr)), &(sSocketAddr.sin_addr))==0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Connect failed at inet_ntoa()\n"); #endif /* __SMTP_DEBUG */ return(-SMTP_CONNECT_FAILED); } memset(&(sSocketAddr.sin_zero),'\0',8); /* Set the timeout alarm */ __SMTP_ALARM_SET if(connect(__smtpGlobalSocket, (struct sockaddr*)&sSocketAddr, sizeof(struct sockaddr))==-1) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Connect failed at connect()\n"); #endif /* __SMTP_DEBUG */ return(-SMTP_CONNECT_FAILED); } /* Clear the timeout alarm & see if we timedout */ __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) return(-SMTP_TIMED_OUT); #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Connected\n"); #endif /* __SMTP_DEBUG */ /* We should now have a socket connection to the server, on the requested port The SMTP server should send a 220 Service Ready response */ nBytesRecv=recv(__smtpGlobalSocket,(void*)&nRecvBuf,3,0); if(nBytesRecv<3) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Server did not respond correctly!\n"); #endif /* __SMTP_DEBUG */ return(-SMTP_CONNECT_FAILED); } nRecvBuf[3]='\0'; /* Terminate after the first 3 bytes, which will be just the return code, as we use strcmp on the buffer */ if(strcmp(nRecvBuf,"220")!=0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Server responded %s\n", nRecvBuf); #endif /* __SMTP_DEBUG */ return(-SMTP_CONNECT_FAILED); } /* Clear out the rest of the buffer */ smtp_clear_buf(__smtpGlobalSocket); /* Create our "HELO" message */ gethostname(zHostName, sizeof(zHostName)); nHeloMsg=(char*)malloc(8+strlen(zHostName)); nHeloMsg=(char*)memset(nHeloMsg,'\0',8+strlen(zHostName)); nHeloMsg=strcpy(nHeloMsg,"HELO "); nHeloMsg=strcat(nHeloMsg,zHostName); nHeloMsg=strcat(nHeloMsg,"\r\n\0"); #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connectt: HELO message is %s\n",nHeloMsg); #endif /* __SMTP_DEBUG */ /* Now send the HELO line & wait for the response */ #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Sending HELO\n"); #endif /* __SMTP_DEBUG */ nBytesSend=send(__smtpGlobalSocket,nHeloMsg,strlen(nHeloMsg),0); if(nBytesSend!=strlen(nHeloMsg)) #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Didn't send entire HELO\n"); #endif /* __SMTP_DEBUG */ /* Get server response, which should be 250 if we're good to go */ /* Set the timeout alarm */ __SMTP_ALARM_SET nBytesRecv=recv(__smtpGlobalSocket,(void*)&nRecvBuf,3,0); if(nBytesRecv<3) #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Recieved less than 3 bytes\n"); #endif /* __SMTP_DEBUG */ /* Clear the timeout alarm & see if we timedout */ __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) return(-SMTP_TIMED_OUT); nRecvBuf[3]='\0'; /* Terminate after the first 3 bytes, which will be just the return code, as we use strcmp on the buffer */ if(strcmp(nRecvBuf,"250")!=0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: HELO failed\n"); printf("libsmtp: smtp_connect: Server responded %s\n", nRecvBuf); #endif /* __SMTP_DEBUG */ return(-SMTP_CONNECT_FAILED); } /* The server is ready to recieve email from us */ #ifdef __SMTP_DEBUG printf("libsmtp: smtp_connect: Ready to send\n"); #endif /* __SMTP_DEBUG */ /* Clear out the rest of the buffer */ smtp_clear_buf(__smtpGlobalSocket); /* The connection is open and the server waiting for us */ return(SMTP_OK); } int smtp_sendmail(const char* zTo, const char* zFrom, const char* zBody) { /* Send the mail zBody FROM: zFrom, TO: zTo */ int nLength; /* Used to calculate line lengths etc */ int nBytesRecv,nBytesSend; /* Returns from send() & recv() */ char nRecvBuf[3]; /* Return code recieve buffer */ char *nFromMsg, *nRcptMsg; /* Mail from & rcpt to messages */ /* Build our "MAIL FROM:" line */ nLength=strlen(zFrom); nLength+=14; /* 14 is the length of the string "MAIL FROM: " + \r\n\0 */ nFromMsg=(char*)malloc(nLength); nFromMsg=strcpy(nFromMsg,"MAIL FROM: "); nFromMsg=strcat(nFromMsg,zFrom); nFromMsg=strcat(nFromMsg,"\r\n\0"); /* Send the MAIL FROM: line */ nBytesSend=send(__smtpGlobalSocket,nFromMsg,strlen(nFromMsg),0); /* If we're O.K, server should respond with a 250 */ /* Set the timeout alarm */ __SMTP_ALARM_SET nBytesRecv=recv(__smtpGlobalSocket,(void*)&nRecvBuf,3,0); if(nBytesRecv<3) #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: Recieved less than 3 bytes\n"); #endif /* __SMTP_DEBUG */ /* Clear the timeout alarm & see if we timedout */ __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) return(-SMTP_TIMED_OUT); nRecvBuf[3]='\0'; /* Terminate after the first 3 bytes, which will be just the return code, as we use strcmp on the buffer */ if(strcmp(nRecvBuf,"250")!=0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: MAIL FROM: failed\n"); printf("libsmtp: smtp_sendmail: Server responded %s\n", nRecvBuf); #endif /* __SMTP_DEBUG */ return(-SMTP_INVALID_FROM); } /* Clear out the rest of the buffer */ smtp_clear_buf(__smtpGlobalSocket); /* Now send the RCPT TO: */ /* Build our "RCPT TO:" line */ nLength=strlen(zTo); nLength+=12; /* 12 is the length of the string "RCPT TO: " + \r\n\0 */ nRcptMsg=(char*)malloc(nLength); nRcptMsg=strcpy(nRcptMsg,"RCPT TO: "); nRcptMsg=strcat(nRcptMsg,zTo); nRcptMsg=strcat(nRcptMsg,"\r\n\0"); /* Send the RCPT TO: line */ nBytesSend=send(__smtpGlobalSocket,nRcptMsg,strlen(nRcptMsg),0); /* If we're O.K, server should respond with a 250 */ /* Set the timeout alarm */ __SMTP_ALARM_SET nBytesRecv=recv(__smtpGlobalSocket,(void*)&nRecvBuf,3,0); if(nBytesRecv<3) #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: Recieved less than 3 bytes\n"); #endif /* __SMTP_DEBUG */ /* Clear the timeout alarm & see if we timedout */ __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) return(-SMTP_TIMED_OUT); nRecvBuf[3]='\0'; /* Terminate after the first 3 bytes, which will be just the return code, as we use strcmp on the buffer */ if(strcmp(nRecvBuf,"250")!=0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: RCPT TO: failed\n"); printf("libsmtp: smtp_sendmail: Server responded %s\n", nRecvBuf); #endif /* __SMTP_DEBUG */ return(-SMTP_INVALID_TO); } /* Clear out the rest of the buffer */ smtp_clear_buf(__smtpGlobalSocket); /* Now send DATA & the message */ nBytesSend=send(__smtpGlobalSocket,"DATA\r\n",6,0); /* DATA should respond with a 354*/ /* Set the timeout alarm */ __SMTP_ALARM_SET nBytesRecv=recv(__smtpGlobalSocket,(void*)&nRecvBuf,3,0); if(nBytesRecv<3) #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: Recieved less than 3 bytes\n"); #endif /* __SMTP_DEBUG */ /* Clear the timeout alarm & see if we timedout */ __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) return(-SMTP_TIMED_OUT); nRecvBuf[3]='\0'; /* Terminate after the first 3 bytes, which will be just the return code, as we use strcmp on the buffer */ if(strcmp(nRecvBuf,"354")!=0) { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_sendmail: DATA failed\n"); printf("libsmtp: smtp_sendmail: Server responded %s\n", nRecvBuf); #endif /* __SMTP_DEBUG */ return(-SMTP_SEND_FAILED); } /* Clear out the rest of the buffer */ smtp_clear_buf(__smtpGlobalSocket); /* Send the message */ nBytesSend=send(__smtpGlobalSocket,zBody,strlen(zBody),0); if(nBytesSend0) { if(nBufByte=='\r') /* Chomp the \n */ { recv(nSocket,(void*)&nBufByte,1,0); __SMTP_ALARM_CLEAR if(__smtpTimedOut==1) /* See if we timed out */ break; return(SMTP_OK); } } return(-1); } void smtp_alarm(int unused) /* Callback function for alarm() */ { #ifdef __SMTP_DEBUG printf("libsmtp: smtp_alarm: Timed out!\n"); #endif __smtpTimedOut=1; return; } #if defined(SELF) || defined(SELFTEST) || defined(SELF_SMTP) #include int main(int argc, char **argv) { register int port = SMTP_PORT; char *server = "localhost"; char message[] = "From: frodo@bagend.com\r\n" "To: fred@foo.com, jane@baz.org\r\n" "Subject: Libsmtp example\r\n\r\n" "This is a message to show libsmtp in action\r\n"; if (argc < 2) { fprintf(stderr, "Usage: %s " " [ [ ]]\n", argv[0]); return 1; } if (argc > 2) { server = argv[2]; } if (argc > 3) { port = atoi(argv[3]); if (port == 0) port = SMTP_PORT; } /* Obviously you can build your message dynamically. This message does not include all the headers required by RFC2822, only the bare minimum */ printf("Connecting to server\n"); if(smtp_connect(server, port)<0) { printf("Failed to connect to server!\n"); return(1); } printf("Connected\nSending mail\n"); if (smtp_sendmail(argv[1], "user@localhost", message) < 0) { printf("Failed to send mail\n"); /* Attempt to disconnect from the server */ smtp_disconnect(); return(1); } printf("Mail sent successfully\nDisconnecting\n"); smtp_disconnect(); printf("All done!\n"); return(0); } #endif