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>