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