sendmail.c 28 KB

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