Back to index

php5  5.3.10
sendmail.c
Go to the documentation of this file.
00001 /* 
00002  *    PHP Sendmail for Windows.
00003  *
00004  *  This file is rewriten specificly for PHPFI.  Some functionality
00005  *  has been removed (MIME and file attachments).  This code was 
00006  *  modified from code based on code writen by Jarle Aase.
00007  *
00008  *  This class is based on the original code by Jarle Aase, see bellow:
00009  *  wSendmail.cpp  It has been striped of some functionality to match
00010  *  the requirements of phpfi.
00011  *
00012  *  Very simple SMTP Send-mail program for sending command-line level
00013  *  emails and CGI-BIN form response for the Windows platform.
00014  *
00015  *  The complete wSendmail package with source code can be located
00016  *  from http://www.jgaa.com
00017  *
00018  */
00019 
00020 /* $Id: sendmail.c 287783 2009-08-26 21:59:54Z pajoye $ */
00021 
00022 #include "php.h"                          /*php specific */
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #ifndef NETWARE
00026 #include <winsock2.h>
00027 #include "time.h"
00028 #else  /* NETWARE */
00029 #include <netware/sendmail_nw.h>
00030 #endif /* NETWARE */
00031 #include <string.h>
00032 #include <math.h>
00033 #ifndef NETWARE
00034 #include <malloc.h>
00035 #include <memory.h>
00036 #include <winbase.h>
00037 #endif /* NETWARE */
00038 #include "sendmail.h"
00039 #include "php_ini.h"
00040 #include "inet.h"
00041 
00042 #if HAVE_PCRE || HAVE_BUNDLED_PCRE
00043 #include "ext/pcre/php_pcre.h"
00044 #endif
00045 
00046 #include "ext/standard/php_string.h"
00047 #include "ext/date/php_date.h"
00048 
00049 /*enum
00050    {
00051    DO_CONNECT = WM_USER +1
00052    };
00053  */
00054 
00055 /* '*error_message' has to be passed around from php_mail() */
00056 #define SMTP_ERROR_RESPONSE_SPEC   "SMTP server response: %s"
00057 /* Convinient way to handle error messages from the SMTP server.
00058    response is ecalloc()d in Ack() itself and efree()d here
00059    because the content is in *error_message now */
00060 #define SMTP_ERROR_RESPONSE(response)     { \
00061                                                                              if (response && error_message) { \
00062                                                                                     if (NULL != (*error_message = ecalloc(1, sizeof(SMTP_ERROR_RESPONSE_SPEC) + strlen(response)))) { \
00063                                                                                            snprintf(*error_message, sizeof(SMTP_ERROR_RESPONSE_SPEC) + strlen(response), SMTP_ERROR_RESPONSE_SPEC, response); \
00064                                                                                     } \
00065                                                                                     efree(response); \
00066                                                                              } \
00067                                                                       }
00068 #define SMTP_SKIP_SPACE(str)       { while (isspace(*str)) { str++; } }
00069 
00070 
00071 #ifndef THREAD_SAFE
00072 char Buffer[MAIL_BUFFER_SIZE];
00073 
00074 /* socket related data */
00075 SOCKET sc;
00076 #ifndef NETWARE
00077 WSADATA Data;
00078 struct hostent *adr;
00079 int WinsockStarted;
00080 /* values set by the constructor */
00081 char *AppName;
00082 #endif /* NETWARE */
00083 SOCKADDR_IN sock_in;
00084 char MailHost[HOST_NAME_LEN];
00085 char LocalHost[HOST_NAME_LEN];
00086 #endif
00087 char seps[] = " ,\t\n";
00088 #ifndef NETWARE
00089 char *php_mailer = "PHP 5 WIN32";
00090 #else
00091 char *php_mailer = "PHP 5 NetWare";
00092 #endif /* NETWARE */
00093 
00094 /* Error messages */
00095 static char *ErrorMessages[] =
00096 {
00097        {"Success"}, /* 0 */
00098        {"Bad arguments from form"}, /* 1 */
00099        {"Unable to open temporary mailfile for read"},
00100        {"Failed to Start Sockets"},
00101        {"Failed to Resolve Host"},
00102        {"Failed to obtain socket handle"}, /* 5 */
00103        {"Failed to connect to mailserver, verify your \"SMTP\" setting in php.ini"},
00104        {"Failed to Send"},
00105        {"Failed to Receive"},
00106        {"Server Error"},
00107        {"Failed to resolve the host IP name"}, /* 10 */
00108        {"Out of memory"},
00109        {"Unknown error"},
00110        {"Bad Message Contents"},
00111        {"Bad Message Subject"},
00112        {"Bad Message destination"}, /* 15 */
00113        {"Bad Message Return Path"},
00114        {"Bad Mail Host"},
00115        {"Bad Message File"},
00116        {"\"sendmail_from\" not set in php.ini or custom \"From:\" header missing"},
00117        {"Mailserver rejected our \"sendmail_from\" setting"}, /* 20 */
00118        {"Error while trimming mail header with PCRE, please file a bug report at http://bugs.php.net/"} /* 21 */
00119 };
00120 
00121 /* This pattern converts all single occurences of \n (Unix)
00122  * withour a leading \r to \r\n and all occurences of \r (Mac)
00123  * without a trailing \n to \r\n
00124  * Thx to Nibbler from ircnet/#linuxger
00125  */
00126 #define PHP_WIN32_MAIL_UNIFY_PATTERN      "/(\r\n?)|\n/"
00127 #define PHP_WIN32_MAIL_UNIFY_REPLACE      "\r\n"
00128 
00129 /* This pattern removes \r\n from the start of the string,
00130  * \r\n from the end of the string and also makes sure every line
00131  * is only wrapped with a single \r\n (thus reduces multiple
00132  * occurences of \r\n between lines to a single \r\n) */
00133 #define PHP_WIN32_MAIL_RMVDBL_PATTERN     "/^\r\n|(\r\n)+$/m"
00134 #define PHP_WIN32_MAIL_RMVDBL_REPLACE     ""
00135 
00136 /* This pattern escapes \n. inside the message body. It prevents
00137  * premature end of message if \n.\n or \r\n.\r\n is encountered
00138  * and ensures that \n. sequences are properly displayed in the
00139  * message body. */
00140 #define PHP_WIN32_MAIL_DOT_PATTERN "\n."
00141 #define PHP_WIN32_MAIL_DOT_REPLACE "\n.."
00142 
00143 /* This function is meant to unify the headers passed to to mail()
00144  * This means, use PCRE to transform single occurences of \n or \r in \r\n
00145  * As a second step we also eleminate all \r\n occurences which are:
00146  * 1) At the start of the header
00147  * 2) At the end of the header
00148  * 3) Two or more occurences in the header are removed so only one is left
00149  *
00150  * Returns NULL on error, or the new char* buffer on success.
00151  * You have to take care and efree() the buffer on your own.
00152  */
00153 static char *php_win32_mail_trim_header(char *header TSRMLS_DC)
00154 {
00155 
00156 #if HAVE_PCRE || HAVE_BUNDLED_PCRE
00157        
00158        char *result, *result2;
00159        int result_len;
00160        zval *replace;
00161 
00162        if (!header) {
00163               return NULL;
00164        }
00165 
00166        MAKE_STD_ZVAL(replace);
00167        ZVAL_STRING(replace, PHP_WIN32_MAIL_UNIFY_REPLACE, 0);
00168 
00169        result = php_pcre_replace(PHP_WIN32_MAIL_UNIFY_PATTERN, sizeof(PHP_WIN32_MAIL_UNIFY_PATTERN)-1,
00170                                                    header, strlen(header),
00171                                                    replace,
00172                                                    0,
00173                                                    &result_len,
00174                                                    -1,
00175                                                    NULL TSRMLS_CC);
00176        if (NULL == result) {
00177               FREE_ZVAL(replace);
00178               return NULL;
00179        }
00180 
00181        ZVAL_STRING(replace, PHP_WIN32_MAIL_RMVDBL_REPLACE, 0);
00182 
00183        result2 = php_pcre_replace(PHP_WIN32_MAIL_RMVDBL_PATTERN, sizeof(PHP_WIN32_MAIL_RMVDBL_PATTERN)-1,
00184                                                     result, result_len,
00185                                                     replace,
00186                                                     0,
00187                                                     &result_len,
00188                                                     -1,
00189                                                     NULL TSRMLS_CC);
00190        efree(result);
00191        FREE_ZVAL(replace);
00192        return result2;
00193 #else
00194        /* In case we don't have PCRE support (for whatever reason...) simply do nothing and return the unmodified header */
00195        return estrdup(header);
00196 #endif
00197 }
00198 
00199 /*********************************************************************
00200 // Name:  TSendMail
00201 // Input:   1) host:    Name of the mail host where the SMTP server resides
00202 //                      max accepted length of name = 256
00203 //          2) appname: Name of the application to use in the X-mailer
00204 //                      field of the message. if NULL is given the application
00205 //                      name is used as given by the GetCommandLine() function
00206 //                      max accespted length of name = 100
00207 // Output:  1) error:   Returns the error code if something went wrong or
00208 //                      SUCCESS otherwise.
00209 //
00210 //  See SendText() for additional args!
00211 //********************************************************************/
00212 PHPAPI int TSendMail(char *host, int *error, char **error_message,
00213                        char *headers, char *Subject, char *mailTo, char *data,
00214                        char *mailCc, char *mailBcc, char *mailRPath TSRMLS_DC)
00215 {
00216        int ret;
00217        char *RPath = NULL;
00218        char *headers_lc = NULL; /* headers_lc is only created if we've a header at all */
00219        char *pos1 = NULL, *pos2 = NULL;
00220 
00221 #ifndef NETWARE
00222        WinsockStarted = FALSE;
00223 #endif
00224 
00225        if (host == NULL) {
00226               *error = BAD_MAIL_HOST;
00227               return FAILURE;
00228        } else if (strlen(host) >= HOST_NAME_LEN) {
00229               *error = BAD_MAIL_HOST;
00230               return FAILURE;
00231        } else {
00232               strcpy(MailHost, host);
00233        }
00234 
00235        if (headers) {
00236               char *pos = NULL;
00237               size_t i;
00238 
00239               /* Use PCRE to trim the header into the right format */
00240               if (NULL == (headers = php_win32_mail_trim_header(headers TSRMLS_CC))) {
00241                      *error = W32_SM_PCRE_ERROR;
00242                      return FAILURE;
00243               }
00244 
00245               /* Create a lowercased header for all the searches so we're finally case
00246                * insensitive when searching for a pattern. */
00247               if (NULL == (headers_lc = estrdup(headers))) {
00248                      efree(headers);
00249                      *error = OUT_OF_MEMORY;
00250                      return FAILURE;
00251               }
00252               for (i = 0; i < strlen(headers_lc); i++) {
00253                      headers_lc[i] = tolower(headers_lc[i]);
00254               }
00255        }
00256  
00257        /* Fall back to sendmail_from php.ini setting */
00258        if (mailRPath && *mailRPath) {
00259               RPath = estrdup(mailRPath);
00260        } else if (INI_STR("sendmail_from")) {
00261               RPath = estrdup(INI_STR("sendmail_from"));
00262        } else if (   headers_lc &&
00263                             (pos1 = strstr(headers_lc, "from:")) &&
00264                             ((pos1 == headers_lc) || (*(pos1-1) == '\n'))
00265        ) {
00266               /* Real offset is memaddress from the original headers + difference of
00267                * string found in the lowercase headrs + 5 characters to jump over   
00268                * the from: */
00269               pos1 = headers + (pos1 - headers_lc) + 5;
00270               if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
00271                      RPath = estrndup(pos1, strlen(pos1));
00272               } else {
00273                      RPath = estrndup(pos1, pos2 - pos1);
00274               }
00275        } else {
00276               if (headers) {
00277                      efree(headers);
00278                      efree(headers_lc);
00279               }
00280               *error = W32_SM_SENDMAIL_FROM_NOT_SET;
00281               return FAILURE;
00282        }
00283 
00284        /* attempt to connect with mail host */
00285        *error = MailConnect();
00286        if (*error != 0) {
00287               if (RPath) {
00288                      efree(RPath);
00289               }
00290               if (headers) {
00291                      efree(headers);
00292                      efree(headers_lc);
00293               }
00294               /* 128 is safe here, the specifier in snprintf isn't longer than that */
00295               if (NULL == (*error_message = ecalloc(1, HOST_NAME_LEN + 128))) {
00296                      return FAILURE;
00297               }
00298               snprintf(*error_message, HOST_NAME_LEN + 128,
00299                      "Failed to connect to mailserver at \"%s\" port %d, verify your \"SMTP\" "
00300                      "and \"smtp_port\" setting in php.ini or use ini_set()",
00301                      MailHost, !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port"));
00302               return FAILURE;
00303        } else {
00304               ret = SendText(RPath, Subject, mailTo, mailCc, mailBcc, data, headers, headers_lc, error_message TSRMLS_CC);
00305               TSMClose();
00306               if (RPath) {
00307                      efree(RPath);
00308               }
00309               if (headers) {
00310                      efree(headers);
00311                      efree(headers_lc);
00312               }
00313               if (ret != SUCCESS) {
00314                      *error = ret;
00315                      return FAILURE;
00316               }
00317               return SUCCESS;
00318        }
00319 }
00320 
00321 //********************************************************************
00322 // Name:  TSendMail::~TSendMail
00323 // Input:
00324 // Output:
00325 // Description: DESTRUCTOR
00326 // Author/Date:  jcar 20/9/96
00327 // History:
00328 //********************************************************************/
00329 PHPAPI void TSMClose()
00330 {
00331        Post("QUIT\r\n");
00332        Ack(NULL);
00333        /* to guarantee that the cleanup is not made twice and 
00334           compomise the rest of the application if sockets are used
00335           elesewhere 
00336        */
00337 
00338        shutdown(sc, 0); 
00339        closesocket(sc);
00340 }
00341 
00342 
00343 /*********************************************************************
00344 // Name:  char *GetSMErrorText
00345 // Input:   Error index returned by the menber functions
00346 // Output:  pointer to a string containing the error description
00347 // Description:
00348 // Author/Date:  jcar 20/9/96
00349 // History:
00350 //*******************************************************************/
00351 PHPAPI char *GetSMErrorText(int index)
00352 {
00353        if (MIN_ERROR_INDEX <= index && index < MAX_ERROR_INDEX) {
00354               return (ErrorMessages[index]);
00355 
00356        } else {
00357               return (ErrorMessages[UNKNOWN_ERROR]);
00358 
00359        }
00360 }
00361 
00362 
00363 /*********************************************************************
00364 // Name:  SendText
00365 // Input:       1) RPath:   return path of the message
00366 //                                  Is used to fill the "Return-Path" and the
00367 //                                  "X-Sender" fields of the message.
00368 //                  2) Subject: Subject field of the message. If NULL is given
00369 //                                  the subject is set to "No Subject"
00370 //                  3) mailTo:  Destination address
00371 //                  4) data:        Null terminated string containing the data to be send.
00372 //                  5,6) headers of the message. Note that the second
00373 //                  parameter, headers_lc, is actually a lowercased version of
00374 //                  headers. The should match exactly (in terms of length),
00375 //                  only differ in case
00376 // Output:      Error code or SUCCESS
00377 // Description:
00378 // Author/Date:  jcar 20/9/96
00379 // History:
00380 //*******************************************************************/
00381 static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char *mailBcc, char *data, 
00382                       char *headers, char *headers_lc, char **error_message TSRMLS_DC)
00383 {
00384        int res;
00385        char *p;
00386        char *tempMailTo, *token, *pos1, *pos2;
00387        char *server_response = NULL;
00388        char *stripped_header  = NULL;
00389        char *data_cln;
00390        int data_cln_len;
00391 
00392        /* check for NULL parameters */
00393        if (data == NULL)
00394               return (BAD_MSG_CONTENTS);
00395        if (mailTo == NULL)
00396               return (BAD_MSG_DESTINATION);
00397        if (RPath == NULL)
00398               return (BAD_MSG_RPATH);
00399 
00400        /* simple checks for the mailto address */
00401        /* have ampersand ? */
00402        /* mfischer, 20020514: I commented this out because it really
00403           seems bogus. Only a username for example may still be a
00404           valid address at the destination system.
00405        if (strchr(mailTo, '@') == NULL)
00406               return (BAD_MSG_DESTINATION);
00407        */
00408 
00409        snprintf(Buffer, sizeof(Buffer), "HELO %s\r\n", LocalHost);
00410 
00411        /* in the beggining of the dialog */
00412        /* attempt reconnect if the first Post fail */
00413        if ((res = Post(Buffer)) != SUCCESS) {
00414               MailConnect();
00415               if ((res = Post(Buffer)) != SUCCESS) {
00416                      return (res);
00417               }
00418        }
00419        if ((res = Ack(&server_response)) != SUCCESS) {
00420               SMTP_ERROR_RESPONSE(server_response);
00421               return (res);
00422        }
00423 
00424        SMTP_SKIP_SPACE(RPath);
00425        FormatEmailAddress(Buffer, RPath, "MAIL FROM:<%s>\r\n");
00426        if ((res = Post(Buffer)) != SUCCESS) {
00427               return (res);
00428        }
00429        if ((res = Ack(&server_response)) != SUCCESS) {
00430               SMTP_ERROR_RESPONSE(server_response);
00431               return W32_SM_SENDMAIL_FROM_MALFORMED;
00432        }
00433 
00434        tempMailTo = estrdup(mailTo);
00435        /* Send mail to all rcpt's */
00436        token = strtok(tempMailTo, ",");
00437        while (token != NULL)
00438        {
00439               SMTP_SKIP_SPACE(token);
00440               FormatEmailAddress(Buffer, token, "RCPT TO:<%s>\r\n");
00441               if ((res = Post(Buffer)) != SUCCESS) {
00442                      efree(tempMailTo);
00443                      return (res);
00444               }
00445               if ((res = Ack(&server_response)) != SUCCESS) {
00446                      SMTP_ERROR_RESPONSE(server_response);
00447                      efree(tempMailTo);
00448                      return (res);
00449               }
00450               token = strtok(NULL, ",");
00451        }
00452        efree(tempMailTo);
00453 
00454        if (mailCc && *mailCc) {
00455               tempMailTo = estrdup(mailCc);
00456               /* Send mail to all rcpt's */
00457               token = strtok(tempMailTo, ",");
00458               while (token != NULL)
00459               {
00460                      SMTP_SKIP_SPACE(token);
00461                      FormatEmailAddress(Buffer, token, "RCPT TO:<%s>\r\n");
00462                      if ((res = Post(Buffer)) != SUCCESS) {
00463                             efree(tempMailTo);
00464                             return (res);
00465                      }
00466                      if ((res = Ack(&server_response)) != SUCCESS) {
00467                             SMTP_ERROR_RESPONSE(server_response);
00468                             efree(tempMailTo);
00469                             return (res);
00470                      }
00471                      token = strtok(NULL, ",");
00472               }
00473               efree(tempMailTo);
00474        }
00475        /* Send mail to all Cc rcpt's */
00476        else if (headers && (pos1 = strstr(headers_lc, "cc:")) && ((pos1 == headers_lc) || (*(pos1-1) == '\n'))) {
00477               /* Real offset is memaddress from the original headers + difference of
00478                * string found in the lowercase headrs + 3 characters to jump over
00479                * the cc: */
00480               pos1 = headers + (pos1 - headers_lc) + 3;
00481               if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
00482                      tempMailTo = estrndup(pos1, strlen(pos1));
00483               } else {
00484                      tempMailTo = estrndup(pos1, pos2 - pos1);
00485               }
00486 
00487               token = strtok(tempMailTo, ",");
00488               while (token != NULL)
00489               {
00490                      SMTP_SKIP_SPACE(token);
00491                      FormatEmailAddress(Buffer, token, "RCPT TO:<%s>\r\n");
00492                      if ((res = Post(Buffer)) != SUCCESS) {
00493                             efree(tempMailTo);
00494                             return (res);
00495                      }
00496                      if ((res = Ack(&server_response)) != SUCCESS) {
00497                             SMTP_ERROR_RESPONSE(server_response);
00498                             efree(tempMailTo);
00499                             return (res);
00500                      }
00501                      token = strtok(NULL, ",");
00502               }
00503               efree(tempMailTo);
00504        }
00505 
00506        /* Send mail to all Bcc rcpt's
00507           This is basically a rip of the Cc code above.
00508           Just don't forget to remove the Bcc: from the header afterwards. */
00509        if (mailBcc && *mailBcc) {
00510               tempMailTo = estrdup(mailBcc);
00511               /* Send mail to all rcpt's */
00512               token = strtok(tempMailTo, ",");
00513               while (token != NULL)
00514               {
00515                      SMTP_SKIP_SPACE(token);
00516                      FormatEmailAddress(Buffer, token, "RCPT TO:<%s>\r\n");
00517                      if ((res = Post(Buffer)) != SUCCESS) {
00518                             efree(tempMailTo);
00519                             return (res);
00520                      }
00521                      if ((res = Ack(&server_response)) != SUCCESS) {
00522                             SMTP_ERROR_RESPONSE(server_response);
00523                             efree(tempMailTo);
00524                             return (res);
00525                      }
00526                      token = strtok(NULL, ",");
00527               }
00528               efree(tempMailTo);
00529        }
00530        else if (headers) {
00531               if (pos1 = strstr(headers_lc, "bcc:")) {
00532                      /* Real offset is memaddress from the original headers + difference of
00533                       * string found in the lowercase headrs + 4 characters to jump over
00534                       * the bcc: */
00535                      pos1 = headers + (pos1 - headers_lc) + 4;
00536                      if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
00537                             tempMailTo = estrndup(pos1, strlen(pos1));
00538                             /* Later, when we remove the Bcc: out of the
00539                                header we know it was the last thing. */
00540                             pos2 = pos1;
00541                      } else {
00542                             tempMailTo = estrndup(pos1, pos2 - pos1);
00543                      }
00544 
00545                      token = strtok(tempMailTo, ",");
00546                      while (token != NULL)
00547                      {
00548                             SMTP_SKIP_SPACE(token);
00549                             FormatEmailAddress(Buffer, token, "RCPT TO:<%s>\r\n");
00550                             if ((res = Post(Buffer)) != SUCCESS) {
00551                                    efree(tempMailTo);
00552                                    return (res);
00553                             }
00554                             if ((res = Ack(&server_response)) != SUCCESS) {
00555                                    SMTP_ERROR_RESPONSE(server_response);
00556                                    efree(tempMailTo);
00557                                    return (res);
00558                             }
00559                             token = strtok(NULL, ",");
00560                      }
00561                      efree(tempMailTo);
00562 
00563                      /* Now that we've identified that we've a Bcc list,
00564                         remove it from the current header. */
00565                      if (NULL == (stripped_header = ecalloc(1, strlen(headers)))) {
00566                             return OUT_OF_MEMORY;
00567                      }
00568                      /* headers = point to string start of header
00569                         pos1    = pointer IN headers where the Bcc starts
00570                         '4'     = Length of the characters 'bcc:'
00571                         Because we've added +4 above for parsing the Emails
00572                         we've to substract them here. */
00573                      memcpy(stripped_header, headers, pos1 - headers - 4);
00574                      if (pos1 != pos2) {
00575                             /* if pos1 != pos2 , pos2 points to the rest of the headers.
00576                                Since pos1 != pos2 if "\r\n" was found, we know those characters
00577                                are there and so we jump over them (else we would generate a new header
00578                                which would look like "\r\n\r\n". */
00579                             memcpy(stripped_header + (pos1 - headers - 4), pos2 + 2, strlen(pos2) - 2);
00580                      }
00581               }
00582        }
00583 
00584        /* Simplify the code that we create a copy of stripped_header no matter if
00585           we actually strip something or not. So we've a single efree() later. */
00586        if (headers && !stripped_header) {
00587               if (NULL == (stripped_header = estrndup(headers, strlen(headers)))) {
00588                      return OUT_OF_MEMORY;
00589               }
00590        }
00591 
00592        if ((res = Post("DATA\r\n")) != SUCCESS) {
00593               if (stripped_header) {
00594                      efree(stripped_header);
00595               }
00596               return (res);
00597        }
00598        if ((res = Ack(&server_response)) != SUCCESS) {
00599               SMTP_ERROR_RESPONSE(server_response);
00600               if (stripped_header) {
00601                      efree(stripped_header);
00602               }
00603               return (res);
00604        }
00605 
00606        /* send message header */
00607        if (Subject == NULL) {
00608               res = PostHeader(RPath, "No Subject", mailTo, stripped_header TSRMLS_CC);
00609        } else {
00610               res = PostHeader(RPath, Subject, mailTo, stripped_header TSRMLS_CC);
00611        }
00612        if (stripped_header) {
00613               efree(stripped_header);
00614        }
00615        if (res != SUCCESS) {
00616               return (res);
00617        }
00618 
00619        /* Escape \n. sequences
00620         * We use php_str_to_str() and not php_str_replace_in_subject(), since the latter
00621         * uses ZVAL as it's parameters */
00622        data_cln = php_str_to_str(data, strlen(data), PHP_WIN32_MAIL_DOT_PATTERN, sizeof(PHP_WIN32_MAIL_DOT_PATTERN) - 1,
00623                                    PHP_WIN32_MAIL_DOT_REPLACE, sizeof(PHP_WIN32_MAIL_DOT_REPLACE) - 1, &data_cln_len);
00624        if (!data_cln) {
00625               data_cln = estrdup("");
00626               data_cln_len = 1;           
00627        }
00628 
00629        /* send message contents in 1024 chunks */
00630        {
00631               char c, *e2, *e = data_cln + data_cln_len;
00632               p = data_cln;
00633 
00634               while (e - p > 1024) {
00635                      e2 = p + 1024;
00636                      c = *e2;
00637                      *e2 = '\0';
00638                      if ((res = Post(p)) != SUCCESS) {
00639                             efree(data_cln);
00640                             return(res);
00641                      }
00642                      *e2 = c;
00643                      p = e2;
00644               }
00645               if ((res = Post(p)) != SUCCESS) {
00646                      efree(data_cln);
00647                      return(res);
00648               }
00649        }
00650 
00651        efree(data_cln);
00652 
00653        /*send termination dot */
00654        if ((res = Post("\r\n.\r\n")) != SUCCESS)
00655               return (res);
00656        if ((res = Ack(&server_response)) != SUCCESS) {
00657               SMTP_ERROR_RESPONSE(server_response);
00658               return (res);
00659        }
00660 
00661        return (SUCCESS);
00662 }
00663 
00664 static int addToHeader(char **header_buffer, const char *specifier, char *string)
00665 {
00666        if (NULL == (*header_buffer = erealloc(*header_buffer, strlen(*header_buffer) + strlen(specifier) + strlen(string) + 1))) {
00667               return 0;
00668        }
00669        sprintf(*header_buffer + strlen(*header_buffer), specifier, string);
00670        return 1;
00671 }
00672 
00673 /*********************************************************************
00674 // Name:  PostHeader
00675 // Input:       1) return path
00676 //              2) Subject
00677 //              3) destination address
00678 //              4) headers
00679 // Output:      Error code or Success
00680 // Description:
00681 // Author/Date:  jcar 20/9/96
00682 // History:
00683 //********************************************************************/
00684 static int PostHeader(char *RPath, char *Subject, char *mailTo, char *xheaders TSRMLS_DC)
00685 {
00686        /* Print message header according to RFC 822 */
00687        /* Return-path, Received, Date, From, Subject, Sender, To, cc */
00688 
00689        int res;
00690        char *header_buffer;
00691        char *headers_lc = NULL;
00692        size_t i;
00693 
00694        if (xheaders) {
00695               if (NULL == (headers_lc = estrdup(xheaders))) {
00696                      return OUT_OF_MEMORY;
00697               }
00698               for (i = 0; i < strlen(headers_lc); i++) {
00699                      headers_lc[i] = tolower(headers_lc[i]);
00700               }
00701        }
00702 
00703        header_buffer = ecalloc(1, MAIL_BUFFER_SIZE);
00704 
00705        if (!xheaders || !strstr(headers_lc, "date:")) {
00706               time_t tNow = time(NULL);
00707               char *dt = php_format_date("r", 1, tNow, 1 TSRMLS_CC);
00708 
00709               snprintf(header_buffer, MAIL_BUFFER_SIZE, "Date: %s\r\n", dt);
00710               efree(dt);
00711        }
00712 
00713        if (!headers_lc || !strstr(headers_lc, "from:")) {
00714               if (!addToHeader(&header_buffer, "From: %s\r\n", RPath)) {
00715                      goto PostHeader_outofmem;
00716               }
00717        }
00718        if (!addToHeader(&header_buffer, "Subject: %s\r\n", Subject)) {
00719               goto PostHeader_outofmem;
00720        }
00721 
00722        /* Only add the To: field from the $to parameter if isn't in the custom headers */
00723        if ((headers_lc && (!strstr(headers_lc, "\r\nto:") && (strncmp(headers_lc, "to:", 3) != 0))) || !headers_lc) {
00724               if (!addToHeader(&header_buffer, "To: %s\r\n", mailTo)) {
00725                      goto PostHeader_outofmem;
00726               }
00727        }
00728        if (xheaders) {
00729               if (!addToHeader(&header_buffer, "%s\r\n", xheaders)) {
00730                      goto PostHeader_outofmem;
00731               }
00732        }
00733 
00734        if (headers_lc) {
00735               efree(headers_lc);
00736        }
00737        if ((res = Post(header_buffer)) != SUCCESS) {
00738               efree(header_buffer);
00739               return (res);
00740        }
00741        efree(header_buffer);
00742 
00743        if ((res = Post("\r\n")) != SUCCESS) {
00744               return (res);
00745        }
00746 
00747        return (SUCCESS);
00748 
00749 PostHeader_outofmem:
00750        if (headers_lc) {
00751               efree(headers_lc);
00752        }
00753        return OUT_OF_MEMORY;
00754 }
00755 
00756 
00757 
00758 /*********************************************************************
00759 // Name:  MailConnect
00760 // Input:   None
00761 // Output:  None
00762 // Description: Connect to the mail host and receive the welcome message.
00763 // Author/Date:  jcar 20/9/96
00764 // History:
00765 //********************************************************************/
00766 static int MailConnect()
00767 {
00768 
00769        int res, namelen;
00770        short portnum;
00771        struct hostent *ent;
00772        IN_ADDR addr;
00773 #ifdef HAVE_IPV6
00774        IN6_ADDR addr6;
00775 #endif
00776 
00777        /* Create Socket */
00778        if ((sc = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
00779               return (FAILED_TO_OBTAIN_SOCKET_HANDLE);
00780        }
00781 
00782        /* Get our own host name */
00783        if (gethostname(LocalHost, HOST_NAME_LEN)) {
00784               return (FAILED_TO_GET_HOSTNAME);
00785        }
00786 
00787        ent = gethostbyname(LocalHost);
00788 
00789        if (!ent) {
00790               return (FAILED_TO_GET_HOSTNAME);
00791        }
00792 
00793        namelen = strlen(ent->h_name);
00794 
00795 #ifdef HAVE_IPV6
00796        if (inet_pton(AF_INET, ent->h_name, &addr) == 1 || inet_pton(AF_INET6, ent->h_name, &addr6) == 1)
00797 #else
00798        if (inet_pton(AF_INET, ent->h_name, &addr) == 1)
00799 #endif
00800        {
00801               if (namelen + 2 >= HOST_NAME_LEN) {
00802                      return (FAILED_TO_GET_HOSTNAME);
00803               }
00804 
00805               strcpy(LocalHost, "[");
00806               strcpy(LocalHost + 1, ent->h_name);
00807               strcpy(LocalHost + namelen + 1, "]");
00808        } else {
00809               if (namelen >= HOST_NAME_LEN) {
00810                      return (FAILED_TO_GET_HOSTNAME);
00811               }
00812 
00813               strcpy(LocalHost, ent->h_name);
00814        }
00815 
00816        /* Resolve the servers IP */
00817        /*
00818        if (!isdigit(MailHost[0])||!gethostbyname(MailHost))
00819        {
00820               return (FAILED_TO_RESOLVE_HOST);
00821        }
00822        */
00823 
00824        portnum = (short) INI_INT("smtp_port");
00825        if (!portnum) {
00826               portnum = 25;
00827        }
00828 
00829        /* Connect to server */
00830        sock_in.sin_family = AF_INET;
00831        sock_in.sin_port = htons(portnum);
00832        sock_in.sin_addr.S_un.S_addr = GetAddr(MailHost);
00833 
00834        if (connect(sc, (LPSOCKADDR) & sock_in, sizeof(sock_in))) {
00835               return (FAILED_TO_CONNECT);
00836        }
00837 
00838        /* receive Server welcome message */
00839        res = Ack(NULL);
00840        return (res);
00841 }
00842 
00843 
00844 /*********************************************************************
00845 // Name:  Post
00846 // Input:
00847 // Output:
00848 // Description:
00849 // Author/Date:  jcar 20/9/96
00850 // History:
00851 //********************************************************************/
00852 static int Post(LPCSTR msg)
00853 {
00854        int len = strlen(msg);
00855        int slen;
00856        int index = 0;
00857 
00858        while (len > 0) {
00859               if ((slen = send(sc, msg + index, len, 0)) < 1)
00860                      return (FAILED_TO_SEND);
00861               len -= slen;
00862               index += slen;
00863        }
00864        return (SUCCESS);
00865 }
00866 
00867 
00868 
00869 /*********************************************************************
00870 // Name:  Ack
00871 // Input:
00872 // Output:
00873 // Description:
00874 // Get the response from the server. We only want to know if the
00875 // last command was successful.
00876 // Author/Date:  jcar 20/9/96
00877 // History:
00878 //********************************************************************/
00879 static int Ack(char **server_response)
00880 {
00881        static char buf[MAIL_BUFFER_SIZE];
00882        int rlen;
00883        int Index = 0;
00884        int Received = 0;
00885 
00886 again:
00887 
00888        if ((rlen = recv(sc, buf + Index, ((MAIL_BUFFER_SIZE) - 1) - Received, 0)) < 1) {
00889               return (FAILED_TO_RECEIVE);
00890        }
00891        Received += rlen;
00892        buf[Received] = 0;
00893        /*err_msg   fprintf(stderr,"Received: (%d bytes) %s", rlen, buf + Index); */
00894 
00895        /* Check for newline */
00896        Index += rlen;
00897        
00898        /* SMPT RFC says \r\n is the only valid line ending, who are we to argue ;)
00899         * The response code must contain at least 5 characters ex. 220\r\n */
00900        if (Received < 5 || buf[Received - 1] != '\n' || buf[Received - 2] != '\r') {
00901               goto again;
00902        }
00903 
00904        if (buf[0] > '3') {
00905               /* If we've a valid pointer, return the SMTP server response so the error message contains more information */
00906               if (server_response) {
00907                      int dec = 0;
00908                      /* See if we have something like \r, \n, \r\n or \n\r at the end of the message and chop it off */
00909                      if (Received > 2) {
00910                             if (buf[Received-1] == '\n' || buf[Received-1] == '\r') {
00911                                    dec++;
00912                                    if (buf[Received-2] == '\r' || buf[Received-2] == '\n') {
00913                                           dec++;
00914                                    }
00915                             }
00916 
00917                      }
00918                      *server_response = estrndup(buf, Received - dec);
00919               }
00920               return (SMTP_SERVER_ERROR);
00921        }
00922 
00923        return (SUCCESS);
00924 }
00925 
00926 
00927 /*********************************************************************
00928 // Name:  unsigned long GetAddr (LPSTR szHost)
00929 // Input:
00930 // Output:
00931 // Description: Given a string, it will return an IP address.
00932 //   - first it tries to convert the string directly
00933 //   - if that fails, it tries o resolve it as a hostname
00934 //
00935 // WARNING: gethostbyname() is a blocking function
00936 // Author/Date:  jcar 20/9/96
00937 // History:
00938 //********************************************************************/
00939 static unsigned long GetAddr(LPSTR szHost)
00940 {
00941        LPHOSTENT lpstHost;
00942        u_long lAddr = INADDR_ANY;
00943 
00944        /* check that we have a string */
00945        if (*szHost) {
00946 
00947               /* check for a dotted-IP address string */
00948               lAddr = inet_addr(szHost);
00949 
00950               /* If not an address, then try to resolve it as a hostname */
00951               if ((lAddr == INADDR_NONE) && (strcmp(szHost, "255.255.255.255"))) {
00952 
00953                      lpstHost = gethostbyname(szHost);
00954                      if (lpstHost) {             /* success */
00955                             lAddr = *((u_long FAR *) (lpstHost->h_addr));
00956                      } else {
00957                             lAddr = INADDR_ANY;         /* failure */
00958                      }
00959               }
00960        }
00961        return (lAddr);
00962 } /* end GetAddr() */
00963 
00964 
00965 /*********************************************************************
00966 // Name:  int FormatEmailAddress
00967 // Input: 
00968 // Output:
00969 // Description: Formats the email address to remove any content ouside
00970 //   of the angle brackets < > as per RFC 2821.
00971 //
00972 //   Returns the invalidly formatted mail address if the < > are 
00973 //   unbalanced (the SMTP server should reject it if it's out of spec.)
00974 //  
00975 // Author/Date:  garretts 08/18/2009
00976 // History:
00977 //********************************************************************/
00978 static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString) {
00979        char *tmpAddress1, *tmpAddress2;
00980        int result;
00981 
00982        if( (tmpAddress1 = strchr(EmailAddress, '<')) && (tmpAddress2 = strchr(tmpAddress1, '>'))  ) {
00983               *tmpAddress2 = 0; // terminate the string temporarily.
00984               result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString , tmpAddress1+1);
00985               *tmpAddress2 = '>'; // put it back the way it was.
00986               return result;
00987        } 
00988        return snprintf(Buf, MAIL_BUFFER_SIZE , FormatString , EmailAddress );
00989 } /* end FormatEmailAddress() */