root/win32/sendmail.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_win32_mail_trim_header
  2. TSendMail
  3. TSMClose
  4. GetSMErrorText
  5. SendText
  6. addToHeader
  7. PostHeader
  8. MailConnect
  9. Post
  10. Ack
  11. GetAddr
  12. FormatEmailAddress

   1 /*
   2  *    PHP Sendmail for Windows.
   3  *
   4  *  This file is rewriten specificly for PHPFI.  Some functionality
   5  *  has been removed (MIME and file attachments).  This code was
   6  *  modified from code based on code written by Jarle Aase.
   7  *
   8  *  This class is based on the original code by Jarle Aase, see bellow:
   9  *  wSendmail.cpp  It has been striped of some functionality to match
  10  *  the requirements of phpfi.
  11  *
  12  *  Very simple SMTP Send-mail program for sending command-line level
  13  *  emails and CGI-BIN form response for the Windows platform.
  14  *
  15  *  The complete wSendmail package with source code can be located
  16  *  from http://www.jgaa.com
  17  *
  18  */
  19 
  20 /* $Id$ */
  21 
  22 #include "php.h"                                /*php specific */
  23 #include <stdio.h>
  24 #include <stdlib.h>
  25 #ifndef NETWARE
  26 #include <winsock2.h>
  27 #include "time.h"
  28 # include <Ws2tcpip.h>
  29 #else   /* NETWARE */
  30 #include <netware/sendmail_nw.h>
  31 #endif  /* NETWARE */
  32 #include <string.h>
  33 #include <math.h>
  34 #ifndef NETWARE
  35 #include <malloc.h>
  36 #include <memory.h>
  37 #include <winbase.h>
  38 #endif  /* NETWARE */
  39 #include "sendmail.h"
  40 #include "php_ini.h"
  41 #include "inet.h"
  42 
  43 #include "php_win32_globals.h"
  44 
  45 #if HAVE_PCRE || HAVE_BUNDLED_PCRE
  46 #include "ext/pcre/php_pcre.h"
  47 #endif
  48 
  49 #include "ext/standard/php_string.h"
  50 #include "ext/date/php_date.h"
  51 
  52 /*enum
  53    {
  54    DO_CONNECT = WM_USER +1
  55    };
  56  */
  57 
  58 /* '*error_message' has to be passed around from php_mail() */
  59 #define SMTP_ERROR_RESPONSE_SPEC        "SMTP server response: %s"
  60 /* Convinient way to handle error messages from the SMTP server.
  61    response is ecalloc()d in Ack() itself and efree()d here
  62    because the content is in *error_message now */
  63 #define SMTP_ERROR_RESPONSE(response)   { \
  64                                                                                         if (response && error_message) { \
  65                                                                                                 if (NULL != (*error_message = ecalloc(1, sizeof(SMTP_ERROR_RESPONSE_SPEC) + strlen(response)))) { \
  66                                                                                                         snprintf(*error_message, sizeof(SMTP_ERROR_RESPONSE_SPEC) + strlen(response), SMTP_ERROR_RESPONSE_SPEC, response); \
  67                                                                                                 } \
  68                                                                                                 efree(response); \
  69                                                                                         } \
  70                                                                                 }
  71 #define SMTP_SKIP_SPACE(str)    { while (isspace(*str)) { str++; } }
  72 
  73 
  74 char seps[] = " ,\t\n";
  75 #ifndef NETWARE
  76 char *php_mailer = "PHP 7 WIN32";
  77 #else
  78 char *php_mailer = "PHP 7 NetWare";
  79 #endif  /* NETWARE */
  80 
  81 /* Error messages */
  82 static char *ErrorMessages[] =
  83 {
  84         {"Success"}, /* 0 */
  85         {"Bad arguments from form"}, /* 1 */
  86         {"Unable to open temporary mailfile for read"},
  87         {"Failed to Start Sockets"},
  88         {"Failed to Resolve Host"},
  89         {"Failed to obtain socket handle"}, /* 5 */
  90         {"Failed to connect to mailserver, verify your \"SMTP\" setting in php.ini"},
  91         {"Failed to Send"},
  92         {"Failed to Receive"},
  93         {"Server Error"},
  94         {"Failed to resolve the host IP name"}, /* 10 */
  95         {"Out of memory"},
  96         {"Unknown error"},
  97         {"Bad Message Contents"},
  98         {"Bad Message Subject"},
  99         {"Bad Message destination"}, /* 15 */
 100         {"Bad Message Return Path"},
 101         {"Bad Mail Host"},
 102         {"Bad Message File"},
 103         {"\"sendmail_from\" not set in php.ini or custom \"From:\" header missing"},
 104         {"Mailserver rejected our \"sendmail_from\" setting"}, /* 20 */
 105         {"Error while trimming mail header with PCRE, please file a bug report at http://bugs.php.net/"} /* 21 */
 106 };
 107 
 108 /* This pattern converts all single occurrences of \n (Unix)
 109  * withour a leading \r to \r\n and all occurrences of \r (Mac)
 110  * without a trailing \n to \r\n
 111  * Thx to Nibbler from ircnet/#linuxger
 112  */
 113 #define PHP_WIN32_MAIL_UNIFY_PATTERN    "/(\r\n?)|\n/"
 114 #define PHP_WIN32_MAIL_UNIFY_REPLACE    "\r\n"
 115 
 116 /* This pattern removes \r\n from the start of the string,
 117  * \r\n from the end of the string and also makes sure every line
 118  * is only wrapped with a single \r\n (thus reduces multiple
 119  * occurrences of \r\n between lines to a single \r\n) */
 120 #define PHP_WIN32_MAIL_RMVDBL_PATTERN   "/^\r\n|(\r\n)+$/m"
 121 #define PHP_WIN32_MAIL_RMVDBL_REPLACE   ""
 122 
 123 /* This pattern escapes \n. inside the message body. It prevents
 124  * premature end of message if \n.\n or \r\n.\r\n is encountered
 125  * and ensures that \n. sequences are properly displayed in the
 126  * message body. */
 127 #define PHP_WIN32_MAIL_DOT_PATTERN      "\n."
 128 #define PHP_WIN32_MAIL_DOT_REPLACE      "\n.."
 129 
 130 /* This function is meant to unify the headers passed to to mail()
 131  * This means, use PCRE to transform single occurrences of \n or \r in \r\n
 132  * As a second step we also eleminate all \r\n occurrences which are:
 133  * 1) At the start of the header
 134  * 2) At the end of the header
 135  * 3) Two or more occurrences in the header are removed so only one is left
 136  *
 137  * Returns NULL on error, or the new char* buffer on success.
 138  * You have to take care and efree() the buffer on your own.
 139  */
 140 static zend_string *php_win32_mail_trim_header(char *header)
 141 {
 142 
 143 #if HAVE_PCRE || HAVE_BUNDLED_PCRE
 144 
 145         zend_string *result, *result2;
 146         zval replace;
 147         zend_string *regex;
 148 
 149         if (!header) {
 150                 return NULL;
 151         }
 152 
 153         ZVAL_STRINGL(&replace, PHP_WIN32_MAIL_UNIFY_REPLACE, strlen(PHP_WIN32_MAIL_UNIFY_REPLACE));
 154         regex = zend_string_init(PHP_WIN32_MAIL_UNIFY_PATTERN, sizeof(PHP_WIN32_MAIL_UNIFY_PATTERN)-1, 0);
 155 
 156         result = php_pcre_replace(regex,
 157                                   NULL, header, (int)strlen(header),
 158                                   &replace,
 159                                   0,
 160                                   -1,
 161                                   NULL);
 162 
 163         zval_ptr_dtor(&replace);
 164         zend_string_release(regex);
 165 
 166         if (NULL == result) {
 167                 return NULL;
 168         }
 169 
 170         ZVAL_STRING(&replace, PHP_WIN32_MAIL_RMVDBL_PATTERN);
 171         regex = zend_string_init(PHP_WIN32_MAIL_RMVDBL_PATTERN, sizeof(PHP_WIN32_MAIL_RMVDBL_PATTERN)-1, 0);
 172 
 173         result2 = php_pcre_replace(regex,
 174                                    result, result->val, (int)result->len,
 175                                    &replace,
 176                                   0,
 177                                   -1,
 178                                   NULL);
 179         zval_ptr_dtor(&replace);
 180         zend_string_release(regex);
 181         zend_string_release(result);
 182 
 183         return result2;
 184 #else
 185         /* In case we don't have PCRE support (for whatever reason...) simply do nothing and return the unmodified header */
 186         return estrdup(header);
 187 #endif
 188 }
 189 
 190 /*********************************************************************
 191 // Name:  TSendMail
 192 // Input:   1) host:    Name of the mail host where the SMTP server resides
 193 //                      max accepted length of name = 256
 194 //          2) appname: Name of the application to use in the X-mailer
 195 //                      field of the message. if NULL is given the application
 196 //                      name is used as given by the GetCommandLine() function
 197 //                      max accespted length of name = 100
 198 // Output:  1) error:   Returns the error code if something went wrong or
 199 //                      SUCCESS otherwise.
 200 //
 201 //  See SendText() for additional args!
 202 //********************************************************************/
 203 PHPAPI int TSendMail(char *host, int *error, char **error_message,
 204                           char *headers, char *Subject, char *mailTo, char *data,
 205                           char *mailCc, char *mailBcc, char *mailRPath)
 206 {
 207         int ret;
 208         char *RPath = NULL;
 209         zend_string *headers_lc = NULL; /* headers_lc is only created if we've a header at all */
 210         char *pos1 = NULL, *pos2 = NULL;
 211 
 212         if (host == NULL) {
 213                 *error = BAD_MAIL_HOST;
 214                 return FAILURE;
 215         } else if (strlen(host) >= HOST_NAME_LEN) {
 216                 *error = BAD_MAIL_HOST;
 217                 return FAILURE;
 218         } else {
 219                 strcpy(PW32G(mail_host), host);
 220         }
 221 
 222         if (headers) {
 223                 char *pos = NULL;
 224                 size_t i;
 225 
 226                 /* Use PCRE to trim the header into the right format */
 227                 if (NULL == (headers_lc = php_win32_mail_trim_header(headers))) {
 228                         *error = W32_SM_PCRE_ERROR;
 229                         return FAILURE;
 230                 }
 231 
 232                 /* Create a lowercased header for all the searches so we're finally case
 233                  * insensitive when searching for a pattern. */
 234                 for (i = 0; i < headers_lc->len; i++) {
 235                         headers_lc->val[i] = tolower(headers_lc->val[i]);
 236                 }
 237         }
 238 
 239         /* Fall back to sendmail_from php.ini setting */
 240         if (mailRPath && *mailRPath) {
 241                 RPath = estrdup(mailRPath);
 242         } else if (INI_STR("sendmail_from")) {
 243                 RPath = estrdup(INI_STR("sendmail_from"));
 244         } else if (headers_lc) {
 245                 int found = 0;
 246                 char *lookup = headers_lc->val;
 247 
 248                 while (lookup) {
 249                         pos1 = strstr(lookup, "from:");
 250 
 251                         if (!pos1) {
 252                                 break;
 253                         } else if (pos1 != headers_lc->val && *(pos1-1) != '\n') {
 254                                 if (strlen(pos1) >= sizeof("from:")) {
 255                                         lookup = pos1 + sizeof("from:");
 256                                         continue;
 257                                 } else {
 258                                         break;
 259                                 }
 260                         }
 261 
 262                         found = 1;
 263 
 264                         /* Real offset is memaddress from the original headers + difference of
 265                          * string found in the lowercase headrs + 5 characters to jump over
 266                          * the from: */
 267                         pos1 = headers + (pos1 - lookup) + 5;
 268                         if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
 269                                 RPath = estrndup(pos1, strlen(pos1));
 270                         } else {
 271                                 RPath = estrndup(pos1, pos2 - pos1);
 272                         }
 273 
 274                         break;
 275                 }
 276 
 277                 if (!found) {
 278                         if (headers_lc) {
 279                                 zend_string_free(headers_lc);
 280                         }
 281                         *error = W32_SM_SENDMAIL_FROM_NOT_SET;
 282                         return FAILURE;
 283                 }
 284         }
 285 
 286         /* attempt to connect with mail host */
 287         *error = MailConnect();
 288         if (*error != 0) {
 289                 if (RPath) {
 290                         efree(RPath);
 291                 }
 292                 if (headers) {
 293                         zend_string_free(headers_lc);
 294                 }
 295                 /* 128 is safe here, the specifier in snprintf isn't longer than that */
 296                 if (NULL == (*error_message = ecalloc(1, HOST_NAME_LEN + 128))) {
 297                         return FAILURE;
 298                 }
 299                 snprintf(*error_message, HOST_NAME_LEN + 128,
 300                         "Failed to connect to mailserver at \"%s\" port %d, verify your \"SMTP\" "
 301                         "and \"smtp_port\" setting in php.ini or use ini_set()",
 302                         PW32G(mail_host), !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port"));
 303                 return FAILURE;
 304         } else {
 305                 ret = SendText(RPath, Subject, mailTo, mailCc, mailBcc, data, headers, headers_lc->val, error_message);
 306                 TSMClose();
 307                 if (RPath) {
 308                         efree(RPath);
 309                 }
 310                 if (headers) {
 311                         zend_string_free(headers_lc);
 312                 }
 313                 if (ret != SUCCESS) {
 314                         *error = ret;
 315                         return FAILURE;
 316                 }
 317                 return SUCCESS;
 318         }
 319 }
 320 
 321 //********************************************************************
 322 // Name:  TSendMail::~TSendMail
 323 // Input:
 324 // Output:
 325 // Description: DESTRUCTOR
 326 // Author/Date:  jcar 20/9/96
 327 // History:
 328 //********************************************************************/
 329 PHPAPI void TSMClose()
 330 {
 331         Post("QUIT\r\n");
 332         Ack(NULL);
 333         /* to guarantee that the cleanup is not made twice and
 334            compomise the rest of the application if sockets are used
 335            elesewhere
 336         */
 337 
 338         shutdown(PW32G(mail_socket), 0);
 339         closesocket(PW32G(mail_socket));
 340 }
 341 
 342 
 343 /*********************************************************************
 344 // Name:  char *GetSMErrorText
 345 // Input:   Error index returned by the menber functions
 346 // Output:  pointer to a string containing the error description
 347 // Description:
 348 // Author/Date:  jcar 20/9/96
 349 // History:
 350 //*******************************************************************/
 351 PHPAPI char *GetSMErrorText(int index)
 352 {
 353         if (MIN_ERROR_INDEX <= index && index < MAX_ERROR_INDEX) {
 354                 return (ErrorMessages[index]);
 355 
 356         } else {
 357                 return (ErrorMessages[UNKNOWN_ERROR]);
 358 
 359         }
 360 }
 361 
 362 PHPAPI zend_string *php_str_to_str(char *haystack, size_t length, char *needle,
 363                 size_t needle_len, char *str, size_t str_len);
 364 
 365 
 366 /*********************************************************************
 367 // Name:  SendText
 368 // Input:       1) RPath:   return path of the message
 369 //                                  Is used to fill the "Return-Path" and the
 370 //                                  "X-Sender" fields of the message.
 371 //                  2) Subject: Subject field of the message. If NULL is given
 372 //                                  the subject is set to "No Subject"
 373 //                  3) mailTo:  Destination address
 374 //                  4) data:        Null terminated string containing the data to be send.
 375 //                  5,6) headers of the message. Note that the second
 376 //                  parameter, headers_lc, is actually a lowercased version of
 377 //                  headers. The should match exactly (in terms of length),
 378 //                  only differ in case
 379 // Output:      Error code or SUCCESS
 380 // Description:
 381 // Author/Date:  jcar 20/9/96
 382 // History:
 383 //*******************************************************************/
 384 static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char *mailBcc, char *data,
 385                          char *headers, char *headers_lc, char **error_message)
 386 {
 387         int res;
 388         char *p;
 389         char *tempMailTo, *token, *pos1, *pos2;
 390         char *server_response = NULL;
 391         char *stripped_header  = NULL;
 392         zend_string *data_cln;
 393 
 394         /* check for NULL parameters */
 395         if (data == NULL)
 396                 return (BAD_MSG_CONTENTS);
 397         if (mailTo == NULL)
 398                 return (BAD_MSG_DESTINATION);
 399         if (RPath == NULL)
 400                 return (BAD_MSG_RPATH);
 401 
 402         /* simple checks for the mailto address */
 403         /* have ampersand ? */
 404         /* mfischer, 20020514: I commented this out because it really
 405            seems bogus. Only a username for example may still be a
 406            valid address at the destination system.
 407         if (strchr(mailTo, '@') == NULL)
 408                 return (BAD_MSG_DESTINATION);
 409         */
 410 
 411         snprintf(PW32G(mail_buffer), sizeof(PW32G(mail_buffer)), "HELO %s\r\n", PW32G(mail_local_host));
 412 
 413         /* in the beginning of the dialog */
 414         /* attempt reconnect if the first Post fail */
 415         if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 416                 int err = MailConnect();
 417                 if (0 != err) {
 418                         return (FAILED_TO_SEND);
 419                 }
 420 
 421                 if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 422                         return (res);
 423                 }
 424         }
 425         if ((res = Ack(&server_response)) != SUCCESS) {
 426                 SMTP_ERROR_RESPONSE(server_response);
 427                 return (res);
 428         }
 429 
 430         SMTP_SKIP_SPACE(RPath);
 431         FormatEmailAddress(PW32G(mail_buffer), RPath, "MAIL FROM:<%s>\r\n");
 432         if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 433                 return (res);
 434         }
 435         if ((res = Ack(&server_response)) != SUCCESS) {
 436                 SMTP_ERROR_RESPONSE(server_response);
 437                 return W32_SM_SENDMAIL_FROM_MALFORMED;
 438         }
 439 
 440         tempMailTo = estrdup(mailTo);
 441         /* Send mail to all rcpt's */
 442         token = strtok(tempMailTo, ",");
 443         while (token != NULL)
 444         {
 445                 SMTP_SKIP_SPACE(token);
 446                 FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n");
 447                 if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 448                         efree(tempMailTo);
 449                         return (res);
 450                 }
 451                 if ((res = Ack(&server_response)) != SUCCESS) {
 452                         SMTP_ERROR_RESPONSE(server_response);
 453                         efree(tempMailTo);
 454                         return (res);
 455                 }
 456                 token = strtok(NULL, ",");
 457         }
 458         efree(tempMailTo);
 459 
 460         if (mailCc && *mailCc) {
 461                 tempMailTo = estrdup(mailCc);
 462                 /* Send mail to all rcpt's */
 463                 token = strtok(tempMailTo, ",");
 464                 while (token != NULL)
 465                 {
 466                         SMTP_SKIP_SPACE(token);
 467                         FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n");
 468                         if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 469                                 efree(tempMailTo);
 470                                 return (res);
 471                         }
 472                         if ((res = Ack(&server_response)) != SUCCESS) {
 473                                 SMTP_ERROR_RESPONSE(server_response);
 474                                 efree(tempMailTo);
 475                                 return (res);
 476                         }
 477                         token = strtok(NULL, ",");
 478                 }
 479                 efree(tempMailTo);
 480         }
 481         /* Send mail to all Cc rcpt's */
 482         else if (headers && (pos1 = strstr(headers_lc, "cc:")) && ((pos1 == headers_lc) || (*(pos1-1) == '\n'))) {
 483                 /* Real offset is memaddress from the original headers + difference of
 484                  * string found in the lowercase headrs + 3 characters to jump over
 485                  * the cc: */
 486                 pos1 = headers + (pos1 - headers_lc) + 3;
 487                 if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
 488                         tempMailTo = estrndup(pos1, strlen(pos1));
 489                 } else {
 490                         tempMailTo = estrndup(pos1, pos2 - pos1);
 491                 }
 492 
 493                 token = strtok(tempMailTo, ",");
 494                 while (token != NULL)
 495                 {
 496                         SMTP_SKIP_SPACE(token);
 497                         FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n");
 498                         if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 499                                 efree(tempMailTo);
 500                                 return (res);
 501                         }
 502                         if ((res = Ack(&server_response)) != SUCCESS) {
 503                                 SMTP_ERROR_RESPONSE(server_response);
 504                                 efree(tempMailTo);
 505                                 return (res);
 506                         }
 507                         token = strtok(NULL, ",");
 508                 }
 509                 efree(tempMailTo);
 510         }
 511 
 512         /* Send mail to all Bcc rcpt's
 513            This is basically a rip of the Cc code above.
 514            Just don't forget to remove the Bcc: from the header afterwards. */
 515         if (mailBcc && *mailBcc) {
 516                 tempMailTo = estrdup(mailBcc);
 517                 /* Send mail to all rcpt's */
 518                 token = strtok(tempMailTo, ",");
 519                 while (token != NULL)
 520                 {
 521                         SMTP_SKIP_SPACE(token);
 522                         FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n");
 523                         if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 524                                 efree(tempMailTo);
 525                                 return (res);
 526                         }
 527                         if ((res = Ack(&server_response)) != SUCCESS) {
 528                                 SMTP_ERROR_RESPONSE(server_response);
 529                                 efree(tempMailTo);
 530                                 return (res);
 531                         }
 532                         token = strtok(NULL, ",");
 533                 }
 534                 efree(tempMailTo);
 535         }
 536         else if (headers) {
 537                 if (pos1 = strstr(headers_lc, "bcc:")) {
 538                         /* Real offset is memaddress from the original headers + difference of
 539                          * string found in the lowercase headrs + 4 characters to jump over
 540                          * the bcc: */
 541                         pos1 = headers + (pos1 - headers_lc) + 4;
 542                         if (NULL == (pos2 = strstr(pos1, "\r\n"))) {
 543                                 tempMailTo = estrndup(pos1, strlen(pos1));
 544                                 /* Later, when we remove the Bcc: out of the
 545                                    header we know it was the last thing. */
 546                                 pos2 = pos1;
 547                         } else {
 548                                 tempMailTo = estrndup(pos1, pos2 - pos1);
 549                         }
 550 
 551                         token = strtok(tempMailTo, ",");
 552                         while (token != NULL)
 553                         {
 554                                 SMTP_SKIP_SPACE(token);
 555                                 FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n");
 556                                 if ((res = Post(PW32G(mail_buffer))) != SUCCESS) {
 557                                         efree(tempMailTo);
 558                                         return (res);
 559                                 }
 560                                 if ((res = Ack(&server_response)) != SUCCESS) {
 561                                         SMTP_ERROR_RESPONSE(server_response);
 562                                         efree(tempMailTo);
 563                                         return (res);
 564                                 }
 565                                 token = strtok(NULL, ",");
 566                         }
 567                         efree(tempMailTo);
 568 
 569                         /* Now that we've identified that we've a Bcc list,
 570                            remove it from the current header. */
 571                         if (NULL == (stripped_header = ecalloc(1, strlen(headers)))) {
 572                                 return OUT_OF_MEMORY;
 573                         }
 574                         /* headers = point to string start of header
 575                            pos1    = pointer IN headers where the Bcc starts
 576                            '4'     = Length of the characters 'bcc:'
 577                            Because we've added +4 above for parsing the Emails
 578                            we've to subtract them here. */
 579                         memcpy(stripped_header, headers, pos1 - headers - 4);
 580                         if (pos1 != pos2) {
 581                                 /* if pos1 != pos2 , pos2 points to the rest of the headers.
 582                                    Since pos1 != pos2 if "\r\n" was found, we know those characters
 583                                    are there and so we jump over them (else we would generate a new header
 584                                    which would look like "\r\n\r\n". */
 585                                 memcpy(stripped_header + (pos1 - headers - 4), pos2 + 2, strlen(pos2) - 2);
 586                         }
 587                 }
 588         }
 589 
 590         /* Simplify the code that we create a copy of stripped_header no matter if
 591            we actually strip something or not. So we've a single efree() later. */
 592         if (headers && !stripped_header) {
 593                 if (NULL == (stripped_header = estrndup(headers, strlen(headers)))) {
 594                         return OUT_OF_MEMORY;
 595                 }
 596         }
 597 
 598         if ((res = Post("DATA\r\n")) != SUCCESS) {
 599                 if (stripped_header) {
 600                         efree(stripped_header);
 601                 }
 602                 return (res);
 603         }
 604         if ((res = Ack(&server_response)) != SUCCESS) {
 605                 SMTP_ERROR_RESPONSE(server_response);
 606                 if (stripped_header) {
 607                         efree(stripped_header);
 608                 }
 609                 return (res);
 610         }
 611 
 612         /* send message header */
 613         if (Subject == NULL) {
 614                 res = PostHeader(RPath, "No Subject", mailTo, stripped_header);
 615         } else {
 616                 res = PostHeader(RPath, Subject, mailTo, stripped_header);
 617         }
 618         if (stripped_header) {
 619                 efree(stripped_header);
 620         }
 621         if (res != SUCCESS) {
 622                 return (res);
 623         }
 624 
 625         /* Escape \n. sequences
 626          * We use php_str_to_str() and not php_str_replace_in_subject(), since the latter
 627          * uses ZVAL as it's parameters */
 628         data_cln = php_str_to_str(data, strlen(data), PHP_WIN32_MAIL_DOT_PATTERN, sizeof(PHP_WIN32_MAIL_DOT_PATTERN) - 1,
 629                                         PHP_WIN32_MAIL_DOT_REPLACE, sizeof(PHP_WIN32_MAIL_DOT_REPLACE) - 1);
 630         if (!data_cln) {
 631                 data_cln = ZSTR_EMPTY_ALLOC();
 632         }
 633 
 634         /* send message contents in 1024 chunks */
 635         {
 636                 char c, *e2, *e = data_cln->val + data_cln->len;
 637                 p = data_cln->val;
 638 
 639                 while (e - p > 1024) {
 640                         e2 = p + 1024;
 641                         c = *e2;
 642                         *e2 = '\0';
 643                         if ((res = Post(p)) != SUCCESS) {
 644                                 zend_string_free(data_cln);
 645                                 return(res);
 646                         }
 647                         *e2 = c;
 648                         p = e2;
 649                 }
 650                 if ((res = Post(p)) != SUCCESS) {
 651                         zend_string_free(data_cln);
 652                         return(res);
 653                 }
 654         }
 655 
 656         zend_string_free(data_cln);
 657 
 658         /*send termination dot */
 659         if ((res = Post("\r\n.\r\n")) != SUCCESS)
 660                 return (res);
 661         if ((res = Ack(&server_response)) != SUCCESS) {
 662                 SMTP_ERROR_RESPONSE(server_response);
 663                 return (res);
 664         }
 665 
 666         return (SUCCESS);
 667 }
 668 
 669 static int addToHeader(char **header_buffer, const char *specifier, char *string)
 670 {
 671         if (NULL == (*header_buffer = erealloc(*header_buffer, strlen(*header_buffer) + strlen(specifier) + strlen(string) + 1))) {
 672                 return 0;
 673         }
 674         sprintf(*header_buffer + strlen(*header_buffer), specifier, string);
 675         return 1;
 676 }
 677 
 678 /*********************************************************************
 679 // Name:  PostHeader
 680 // Input:       1) return path
 681 //              2) Subject
 682 //              3) destination address
 683 //              4) headers
 684 // Output:      Error code or Success
 685 // Description:
 686 // Author/Date:  jcar 20/9/96
 687 // History:
 688 //********************************************************************/
 689 static int PostHeader(char *RPath, char *Subject, char *mailTo, char *xheaders)
 690 {
 691         /* Print message header according to RFC 822 */
 692         /* Return-path, Received, Date, From, Subject, Sender, To, cc */
 693 
 694         int res;
 695         char *header_buffer;
 696         char *headers_lc = NULL;
 697         size_t i;
 698 
 699         if (xheaders) {
 700                 if (NULL == (headers_lc = estrdup(xheaders))) {
 701                         return OUT_OF_MEMORY;
 702                 }
 703                 for (i = 0; i < strlen(headers_lc); i++) {
 704                         headers_lc[i] = tolower(headers_lc[i]);
 705                 }
 706         }
 707 
 708         header_buffer = ecalloc(1, MAIL_BUFFER_SIZE);
 709 
 710         if (!xheaders || !strstr(headers_lc, "date:")) {
 711                 time_t tNow = time(NULL);
 712                 zend_string *dt = php_format_date("r", 1, tNow, 1);
 713 
 714                 snprintf(header_buffer, MAIL_BUFFER_SIZE, "Date: %s\r\n", dt->val);
 715                 zend_string_free(dt);
 716         }
 717 
 718         if (!headers_lc || !strstr(headers_lc, "from:")) {
 719                 if (!addToHeader(&header_buffer, "From: %s\r\n", RPath)) {
 720                         goto PostHeader_outofmem;
 721                 }
 722         }
 723         if (!addToHeader(&header_buffer, "Subject: %s\r\n", Subject)) {
 724                 goto PostHeader_outofmem;
 725         }
 726 
 727         /* Only add the To: field from the $to parameter if isn't in the custom headers */
 728         if ((headers_lc && (!strstr(headers_lc, "\r\nto:") && (strncmp(headers_lc, "to:", 3) != 0))) || !headers_lc) {
 729                 if (!addToHeader(&header_buffer, "To: %s\r\n", mailTo)) {
 730                         goto PostHeader_outofmem;
 731                 }
 732         }
 733         if (xheaders) {
 734                 if (!addToHeader(&header_buffer, "%s\r\n", xheaders)) {
 735                         goto PostHeader_outofmem;
 736                 }
 737         }
 738 
 739         if (headers_lc) {
 740                 efree(headers_lc);
 741         }
 742         if ((res = Post(header_buffer)) != SUCCESS) {
 743                 efree(header_buffer);
 744                 return (res);
 745         }
 746         efree(header_buffer);
 747 
 748         if ((res = Post("\r\n")) != SUCCESS) {
 749                 return (res);
 750         }
 751 
 752         return (SUCCESS);
 753 
 754 PostHeader_outofmem:
 755         if (headers_lc) {
 756                 efree(headers_lc);
 757         }
 758         return OUT_OF_MEMORY;
 759 }
 760 
 761 
 762 
 763 /*********************************************************************
 764 // Name:  MailConnect
 765 // Input:   None
 766 // Output:  None
 767 // Description: Connect to the mail host and receive the welcome message.
 768 // Author/Date:  jcar 20/9/96
 769 // History:
 770 //********************************************************************/
 771 static int MailConnect()
 772 {
 773 
 774         int res, namelen;
 775         short portnum;
 776         struct hostent *ent;
 777         IN_ADDR addr;
 778 #ifdef HAVE_IPV6
 779         IN6_ADDR addr6;
 780 #endif
 781         SOCKADDR_IN sock_in;
 782 
 783         /* Create Socket */
 784         if ((PW32G(mail_socket) = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
 785                 return (FAILED_TO_OBTAIN_SOCKET_HANDLE);
 786         }
 787 
 788         /* Get our own host name */
 789         if (gethostname(PW32G(mail_local_host), HOST_NAME_LEN)) {
 790                 closesocket(PW32G(mail_socket));
 791                 return (FAILED_TO_GET_HOSTNAME);
 792         }
 793 
 794         ent = gethostbyname(PW32G(mail_local_host));
 795 
 796         if (!ent) {
 797                 closesocket(PW32G(mail_socket));
 798                 return (FAILED_TO_GET_HOSTNAME);
 799         }
 800 
 801         namelen = (int)strlen(ent->h_name);
 802 
 803 #ifdef HAVE_IPV6
 804         if (inet_pton(AF_INET, ent->h_name, &addr) == 1 || inet_pton(AF_INET6, ent->h_name, &addr6) == 1)
 805 #else
 806         if (inet_pton(AF_INET, ent->h_name, &addr) == 1)
 807 #endif
 808         {
 809                 if (namelen + 2 >= HOST_NAME_LEN) {
 810                         closesocket(PW32G(mail_socket));
 811                         return (FAILED_TO_GET_HOSTNAME);
 812                 }
 813 
 814                 strcpy(PW32G(mail_local_host), "[");
 815                 strcpy(PW32G(mail_local_host) + 1, ent->h_name);
 816                 strcpy(PW32G(mail_local_host) + namelen + 1, "]");
 817         } else {
 818                 if (namelen >= HOST_NAME_LEN) {
 819                         closesocket(PW32G(mail_socket));
 820                         return (FAILED_TO_GET_HOSTNAME);
 821                 }
 822 
 823                 strcpy(PW32G(mail_local_host), ent->h_name);
 824         }
 825 
 826         /* Resolve the servers IP */
 827         /*
 828         if (!isdigit(PW32G(mail_host)[0])||!gethostbyname(PW32G(mail_host)))
 829         {
 830                 return (FAILED_TO_RESOLVE_HOST);
 831         }
 832         */
 833 
 834         portnum = (short) INI_INT("smtp_port");
 835         if (!portnum) {
 836                 portnum = 25;
 837         }
 838 
 839         /* Connect to server */
 840         sock_in.sin_family = AF_INET;
 841         sock_in.sin_port = htons(portnum);
 842         sock_in.sin_addr.S_un.S_addr = GetAddr(PW32G(mail_host));
 843 
 844         if (connect(PW32G(mail_socket), (LPSOCKADDR) & sock_in, sizeof(sock_in))) {
 845                 closesocket(PW32G(mail_socket));
 846                 return (FAILED_TO_CONNECT);
 847         }
 848 
 849         /* receive Server welcome message */
 850         res = Ack(NULL);
 851         return (res);
 852 }
 853 
 854 
 855 /*********************************************************************
 856 // Name:  Post
 857 // Input:
 858 // Output:
 859 // Description:
 860 // Author/Date:  jcar 20/9/96
 861 // History:
 862 //********************************************************************/
 863 static int Post(LPCSTR msg)
 864 {
 865         int len = (int)strlen(msg);
 866         int slen;
 867         int index = 0;
 868 
 869         while (len > 0) {
 870                 if ((slen = send(PW32G(mail_socket), msg + index, len, 0)) < 1)
 871                         return (FAILED_TO_SEND);
 872                 len -= slen;
 873                 index += slen;
 874         }
 875         return (SUCCESS);
 876 }
 877 
 878 
 879 
 880 /*********************************************************************
 881 // Name:  Ack
 882 // Input:
 883 // Output:
 884 // Description:
 885 // Get the response from the server. We only want to know if the
 886 // last command was successful.
 887 // Author/Date:  jcar 20/9/96
 888 // History:
 889 //********************************************************************/
 890 static int Ack(char **server_response)
 891 {
 892         ZEND_TLS char buf[MAIL_BUFFER_SIZE];
 893         int rlen;
 894         int Index = 0;
 895         int Received = 0;
 896 
 897 again:
 898 
 899         if ((rlen = recv(PW32G(mail_socket), buf + Index, ((MAIL_BUFFER_SIZE) - 1) - Received, 0)) < 1) {
 900                 return (FAILED_TO_RECEIVE);
 901         }
 902         Received += rlen;
 903         buf[Received] = 0;
 904         /*err_msg   fprintf(stderr,"Received: (%d bytes) %s", rlen, buf + Index); */
 905 
 906         /* Check for newline */
 907         Index += rlen;
 908 
 909         /* SMPT RFC says \r\n is the only valid line ending, who are we to argue ;)
 910          * The response code must contain at least 5 characters ex. 220\r\n */
 911         if (Received < 5 || buf[Received - 1] != '\n' || buf[Received - 2] != '\r') {
 912                 goto again;
 913         }
 914 
 915         if (buf[0] > '3') {
 916                 /* If we've a valid pointer, return the SMTP server response so the error message contains more information */
 917                 if (server_response) {
 918                         int dec = 0;
 919                         /* See if we have something like \r, \n, \r\n or \n\r at the end of the message and chop it off */
 920                         if (Received > 2) {
 921                                 if (buf[Received-1] == '\n' || buf[Received-1] == '\r') {
 922                                         dec++;
 923                                         if (buf[Received-2] == '\r' || buf[Received-2] == '\n') {
 924                                                 dec++;
 925                                         }
 926                                 }
 927 
 928                         }
 929                         *server_response = estrndup(buf, Received - dec);
 930                 }
 931                 return (SMTP_SERVER_ERROR);
 932         }
 933 
 934         return (SUCCESS);
 935 }
 936 
 937 
 938 /*********************************************************************
 939 // Name:  unsigned long GetAddr (LPSTR szHost)
 940 // Input:
 941 // Output:
 942 // Description: Given a string, it will return an IP address.
 943 //   - first it tries to convert the string directly
 944 //   - if that fails, it tries o resolve it as a hostname
 945 //
 946 // WARNING: gethostbyname() is a blocking function
 947 // Author/Date:  jcar 20/9/96
 948 // History:
 949 //********************************************************************/
 950 static unsigned long GetAddr(LPSTR szHost)
 951 {
 952         LPHOSTENT lpstHost;
 953         u_long lAddr = INADDR_ANY;
 954 
 955         /* check that we have a string */
 956         if (*szHost) {
 957 
 958                 /* check for a dotted-IP address string */
 959                 lAddr = inet_addr(szHost);
 960 
 961                 /* If not an address, then try to resolve it as a hostname */
 962                 if ((lAddr == INADDR_NONE) && (strcmp(szHost, "255.255.255.255"))) {
 963 
 964                         lpstHost = gethostbyname(szHost);
 965                         if (lpstHost) {         /* success */
 966                                 lAddr = *((u_long FAR *) (lpstHost->h_addr));
 967                         } else {
 968                                 lAddr = INADDR_ANY;             /* failure */
 969                         }
 970                 }
 971         }
 972         return (lAddr);
 973 } /* end GetAddr() */
 974 
 975 
 976 /*********************************************************************
 977 // Name:  int FormatEmailAddress
 978 // Input:
 979 // Output:
 980 // Description: Formats the email address to remove any content ouside
 981 //   of the angle brackets < > as per RFC 2821.
 982 //
 983 //   Returns the invalidly formatted mail address if the < > are
 984 //   unbalanced (the SMTP server should reject it if it's out of spec.)
 985 //
 986 // Author/Date:  garretts 08/18/2009
 987 // History:
 988 //********************************************************************/
 989 static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString) {
 990         char *tmpAddress1, *tmpAddress2;
 991         int result;
 992 
 993         if( (tmpAddress1 = strchr(EmailAddress, '<')) && (tmpAddress2 = strchr(tmpAddress1, '>'))  ) {
 994                 *tmpAddress2 = 0; // terminate the string temporarily.
 995                 result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString , tmpAddress1+1);
 996                 *tmpAddress2 = '>'; // put it back the way it was.
 997                 return result;
 998         }
 999         return snprintf(Buf, MAIL_BUFFER_SIZE , FormatString , EmailAddress );
1000 } /* end FormatEmailAddress() */
1001 
1002 /*
1003  * Local variables:
1004  * tab-width: 4
1005  * c-basic-offset: 4
1006  * End:
1007  * vim600: sw=4 ts=4 fdm=marker
1008  * vim<600: sw=4 ts=4
1009  */

/* [<][>][^][v][top][bottom][index][help] */