Annotation of embedaddon/php/win32/sendmail.c, revision 1.1.1.4

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: 
1.1.1.2   misho      20: /* $Id$ */
1.1       misho      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: 
1.1.1.3   misho     121: /* This pattern converts all single occurrences of \n (Unix)
                    122:  * withour a leading \r to \r\n and all occurrences of \r (Mac)
1.1       misho     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
1.1.1.3   misho     132:  * occurrences of \r\n between lines to a single \r\n) */
1.1       misho     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()
1.1.1.3   misho     144:  * This means, use PCRE to transform single occurrences of \n or \r in \r\n
                    145:  * As a second step we also eleminate all \r\n occurrences which are:
1.1       misho     146:  * 1) At the start of the header
                    147:  * 2) At the end of the header
1.1.1.3   misho     148:  * 3) Two or more occurrences in the header are removed so only one is left
1.1       misho     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: 
1.1.1.4 ! misho     411:        /* in the beginning of the dialog */
1.1       misho     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>