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