/* * PHP Sendmail for Windows. * * This file is rewriten specificly for PHPFI. Some functionality * has been removed (MIME and file attachments). This code was * modified from code based on code writen by Jarle Aase. * * This class is based on the original code by Jarle Aase, see bellow: * wSendmail.cpp It has been striped of some functionality to match * the requirements of phpfi. * * Very simple SMTP Send-mail program for sending command-line level * emails and CGI-BIN form response for the Windows platform. * * The complete wSendmail package with source code can be downloaded * from http://virtual.icr.com.au:80/jgaa/cgi-bin.htm * */ #include "php.h" /*php specific */ #include #include #include #include "time.h" #include #include #include #include #include "sendmail.h" #include "php_ini.h" /* extern int _daylight; extern long _timezone; */ /*enum { DO_CONNECT = WM_USER +1 }; */ static char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; #ifndef THREAD_SAFE char Buffer[MAIL_BUFFER_SIZE]; // socket related data SOCKET sc; WSADATA Data; struct hostent *adr; SOCKADDR_IN sock_in; int WinsockStarted; // values set by the constructor char *AppName; char MailHost[HOST_NAME_LEN]; char LocalHost[HOST_NAME_LEN]; #endif char seps[] = " ,\t\n"; char *php_mailer = "PHP 3.0 WIN32"; char *get_header(char *h, char *headers); // Error messages static char *ErrorMessages[] = { {"Success"}, {"Bad arguments from form"}, {"Unable to open temporary mailfile for read"}, {"Failed to Start Sockets"}, {"Failed to Resolve Host"}, {"Failed to obtain socket handle"}, {"Failed to Connect"}, {"Failed to Send"}, {"Failed to Receive"}, {"Server Error"}, {"Failed to resolve the host IP name"}, {"Out of memory"}, {"Unknown error"}, {"Bad Message Contents"}, {"Bad Message Subject"}, {"Bad Message destination"}, {"Bad Message Return Path"}, {"Bad Mail Host"}, {"Bad Message File"}, {"PHP Internal error: php.ini sendmail from variable not set!"} }; //******************************************************************** // Name: TSendMail // Input: 1) host: Name of the mail host where the SMTP server resides // max accepted length of name = 256 // 2) appname: Name of the application to use in the X-mailer // field of the message. if NULL is given the application // name is used as given by the GetCommandLine() function // max accespted length of name = 100 // Output: 1) error: Returns the error code if something went wrong or // SUCCESS otherwise. // // See SendText() for additional args! //******************************************************************** int TSendMail(char *host, int *error, char *headers, char *Subject, char *mailTo, char *data) { int ret; char *RPath = NULL; WinsockStarted = FALSE; if (host == NULL) { *error = BAD_MAIL_HOST; return BAD_MAIL_HOST; } else if (strlen(host) >= HOST_NAME_LEN) { *error = BAD_MAIL_HOST; return BAD_MAIL_HOST; } else { strcpy(MailHost, host); } if (INI_STR("sendmail_from")){ RPath = estrdup(INI_STR("sendmail_from")); } else { return 19; } // attempt to connect with mail host *error = MailConnect(); if (*error != 0) { if(RPath)efree(RPath); return *error; } else { ret = SendText(RPath, Subject, mailTo, data, headers); TSMClose(); if (ret != SUCCESS) { *error = ret; } if(RPath)efree(RPath); return ret; } } //******************************************************************** // Name: TSendMail::~TSendMail // Input: // Output: // Description: DESTRUCTOR // Author/Date: jcar 20/9/96 // History: //******************************************************************** void TSMClose() { Post("QUIT\n"); Ack(); // to guarantee that the cleanup is not made twice and // compomise the rest of the application if sockets are used // elesewhere } //******************************************************************** // Name: char *GetSMErrorText // Input: Error index returned by the menber functions // Output: pointer to a string containing the error description // Description: // Author/Date: jcar 20/9/96 // History: //******************************************************************** char *GetSMErrorText(int index) { if ((index > MAX_ERROR_INDEX) || (index < MIN_ERROR_INDEX)) return (ErrorMessages[UNKNOWN_ERROR]); else return (ErrorMessages[index]); } //******************************************************************** // Name: TSendText // Input: 1) RPath: return path of the message // Is used to fill the "Return-Path" and the // "X-Sender" fields of the message. // 2) Subject: Subject field of the message. If NULL is given // the subject is set to "No Subject" // 3) mailTo: Destination address // 4) data: Null terminated string containing the data to be send. // Output: Error code or SUCCESS // Description: // Author/Date: jcar 20/9/96 // History: //******************************************************************** int SendText(char *RPath, char *Subject, char *mailTo, char *data, char *headers) { int res, i; char *p; // check for NULL parameters if (data == NULL) return (BAD_MSG_CONTENTS); if (mailTo == NULL) return (BAD_MSG_DESTINATION); if (RPath == NULL) return (BAD_MSG_RPATH); // simple checks for the mailto address // have ampersand ? if (strchr(mailTo, '@') == NULL) return (BAD_MSG_DESTINATION); sprintf(Buffer, "HELO %s\n", LocalHost); // in the beggining of the dialog // attempt reconnect if the first Post fail if ((res = Post(Buffer)) != SUCCESS) { MailConnect(); if ((res = Post(Buffer)) != SUCCESS) return (res); } if ((res = Ack()) != SUCCESS) return (res); sprintf(Buffer, "MAIL FROM:<%s>\n", RPath); if ((res = Post(Buffer)) != SUCCESS) return (res); if ((res = Ack()) != SUCCESS) return (res); sprintf(Buffer, "RCPT TO:<%s>\n", mailTo); if ((res = Post(Buffer)) != SUCCESS) return (res); if ((res = Ack()) != SUCCESS) return (res); if ((res = Post("DATA\n")) != SUCCESS) return (res); if ((res = Ack()) != SUCCESS) return (res); // send message header if (Subject == NULL) res = PostHeader(RPath, "No Subject", mailTo, headers); else res = PostHeader(RPath, Subject, mailTo, headers); if (res != SUCCESS) return (res); // send message contents in 1024 chunks if (strlen(data) <= 1024) { if ((res = Post(data)) != SUCCESS) return (res); } else { p = data; while (1) { if (*p == '\0') break; if (strlen(p) >= 1024) i = 1024; else i = strlen(p); // put next chunk in buffer strncpy(Buffer, p, i); Buffer[i] = '\0'; p += i; // send chunk if ((res = Post(Buffer)) != SUCCESS) return (res); } } //send termination dot if ((res = Post("\r\n.\r\n")) != SUCCESS) return (res); if ((res = Ack()) != SUCCESS) return (res); return (SUCCESS); } //******************************************************************** // Name: PostHeader // Input: 1) return path // 2) Subject // 3) destination address // 4) DoMime flag // Output: Error code or Success // Description: // Author/Date: jcar 20/9/96 // History: //******************************************************************** int PostHeader(char *RPath, char *Subject, char *mailTo, char *xheaders) { // Print message header according to RFC 822 // Return-path, Received, Date, From, Subject, Sender, To, cc time_t tNow = time(NULL); struct tm *tm = localtime(&tNow); int zoneh = abs(_timezone); int zonem, res; char *p; p = Buffer; zoneh /= (60 * 60); zonem = (abs(_timezone) / 60) - (zoneh * 60); if(!xheaders || !strstr(xheaders, "Date:")){ p += sprintf(p, "Date: %s, %02d %s %04d %02d:%02d:%02d %s%02d%02d\r\n", days[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, (_timezone > 0) ? "+" : (_timezone < 0) ? "-" : "", zoneh, zonem); } if(!xheaders || !strstr(xheaders, "From:")){ p += sprintf(p, "From: %s\r\n", RPath); } p += sprintf(p, "Subject: %s\r\n", Subject); p += sprintf(p, "To: %s\r\n", mailTo); if(xheaders){ p += sprintf(p, "%s\r\n", xheaders); } if ((res = Post(Buffer)) != SUCCESS) return (res); if ((res = Post("\r\n")) != SUCCESS) return (res); return (SUCCESS); } //******************************************************************** // Name: MailConnect // Input: None // Output: None // Description: Connect to the mail host and receive the welcome message. // Author/Date: jcar 20/9/96 // History: //******************************************************************** int MailConnect() { int res; // Create Socket if ((sc = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) return (FAILED_TO_OBTAIN_SOCKET_HANDLE); // Get our own host name if (gethostname(LocalHost, HOST_NAME_LEN)) return (FAILED_TO_GET_HOSTNAME); // Resolve the servers IP //if (!isdigit(MailHost[0])||!gethostbyname(MailHost)) //{ // return (FAILED_TO_RESOLVE_HOST); //} // Connect to server sock_in.sin_family = AF_INET; sock_in.sin_port = htons(25); sock_in.sin_addr.S_un.S_addr = GetAddr(MailHost); if (connect(sc, (LPSOCKADDR) & sock_in, sizeof(sock_in))) return (FAILED_TO_CONNECT); // receive Server welcome message res = Ack(); return (res); } //******************************************************************** // Name: Post // Input: // Output: // Description: // Author/Date: jcar 20/9/96 // History: //******************************************************************** int Post(LPCSTR msg) { int len = strlen(msg); int slen; int index = 0; while (len > 0) { if ((slen = send(sc, msg + index, len, 0)) < 1) return (FAILED_TO_SEND); len -= slen; index += slen; } return (SUCCESS); } //******************************************************************** // Name: Ack // Input: // Output: // Description: // Get the response from the server. We only want to know if the // last command was successful. // Author/Date: jcar 20/9/96 // History: //******************************************************************** int Ack() { static char *buf; int rlen; int Index = 0; int Received = 0; if (!buf) if ((buf = (char *) malloc(1024 * 4)) == NULL) return (OUT_OF_MEMORY); again: if ((rlen = recv(sc, buf + Index, ((1024 * 4) - 1) - Received, 0)) < 1) return (FAILED_TO_RECEIVE); Received += rlen; buf[Received] = 0; //err_msg fprintf(stderr,"Received: (%d bytes) %s", rlen, buf + Index); // Check for newline Index += rlen; if ((buf[Received - 2] != '\r') || (buf[Received - 1] != '\n')) // err_msg fprintf(stderr,"Incomplete server message. Awaiting CRLF\n"); goto again; // Incomplete data. Line must be terminated by CRLF if (buf[0] > '3') return (SMTP_SERVER_ERROR); return (SUCCESS); } //******************************************************************** // Name: unsigned long GetAddr (LPSTR szHost) // Input: // Output: // Description: Given a string, it will return an IP address. // - first it tries to convert the string directly // - if that fails, it tries o resolve it as a hostname // // WARNING: gethostbyname() is a blocking function // Author/Date: jcar 20/9/96 // History: //******************************************************************** unsigned long GetAddr(LPSTR szHost) { LPHOSTENT lpstHost; u_long lAddr = INADDR_ANY; /* check that we have a string */ if (*szHost) { /* check for a dotted-IP address string */ lAddr = inet_addr(szHost); /* If not an address, then try to resolve it as a hostname */ if ((lAddr == INADDR_NONE) && (strcmp(szHost, "255.255.255.255"))) { lpstHost = gethostbyname(szHost); if (lpstHost) { /* success */ lAddr = *((u_long FAR *) (lpstHost->h_addr)); } else { lAddr = INADDR_ANY; /* failure */ } } } return (lAddr); } /* end GetAddr() */