You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

545 lines
13 KiB

28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
26 years ago
28 years ago
28 years ago
28 years ago
28 years ago
26 years ago
26 years ago
26 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
28 years ago
  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. #include "php.h" /*php specific */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <winsock.h>
  23. #include "time.h"
  24. #include <string.h>
  25. #include <malloc.h>
  26. #include <memory.h>
  27. #include <winbase.h>
  28. #include "sendmail.h"
  29. #include "php_ini.h"
  30. /*
  31. extern int _daylight;
  32. extern long _timezone;
  33. */
  34. /*enum
  35. {
  36. DO_CONNECT = WM_USER +1
  37. };
  38. */
  39. static char *days[] =
  40. {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  41. static char *months[] =
  42. {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  43. #ifndef THREAD_SAFE
  44. char Buffer[MAIL_BUFFER_SIZE];
  45. /* socket related data */
  46. SOCKET sc;
  47. WSADATA Data;
  48. struct hostent *adr;
  49. SOCKADDR_IN sock_in;
  50. int WinsockStarted;
  51. /* values set by the constructor */
  52. char *AppName;
  53. char MailHost[HOST_NAME_LEN];
  54. char LocalHost[HOST_NAME_LEN];
  55. #endif
  56. char seps[] = " ,\t\n";
  57. char *php_mailer = "PHP 4.0 WIN32";
  58. char *get_header(char *h, char *headers);
  59. /* Error messages */
  60. static char *ErrorMessages[] =
  61. {
  62. {"Success"},
  63. {"Bad arguments from form"},
  64. {"Unable to open temporary mailfile for read"},
  65. {"Failed to Start Sockets"},
  66. {"Failed to Resolve Host"},
  67. {"Failed to obtain socket handle"},
  68. {"Failed to Connect"},
  69. {"Failed to Send"},
  70. {"Failed to Receive"},
  71. {"Server Error"},
  72. {"Failed to resolve the host IP name"},
  73. {"Out of memory"},
  74. {"Unknown error"},
  75. {"Bad Message Contents"},
  76. {"Bad Message Subject"},
  77. {"Bad Message destination"},
  78. {"Bad Message Return Path"},
  79. {"Bad Mail Host"},
  80. {"Bad Message File"},
  81. {"PHP Internal error: php.ini sendmail from variable not set!"}
  82. };
  83. /*********************************************************************
  84. // Name: TSendMail
  85. // Input: 1) host: Name of the mail host where the SMTP server resides
  86. // max accepted length of name = 256
  87. // 2) appname: Name of the application to use in the X-mailer
  88. // field of the message. if NULL is given the application
  89. // name is used as given by the GetCommandLine() function
  90. // max accespted length of name = 100
  91. // Output: 1) error: Returns the error code if something went wrong or
  92. // SUCCESS otherwise.
  93. //
  94. // See SendText() for additional args!
  95. //********************************************************************/
  96. int TSendMail(char *host, int *error,
  97. char *headers, char *Subject, char *mailTo, char *data)
  98. {
  99. int ret;
  100. char *RPath = NULL;
  101. WinsockStarted = FALSE;
  102. if (host == NULL) {
  103. *error = BAD_MAIL_HOST;
  104. return BAD_MAIL_HOST;
  105. } else if (strlen(host) >= HOST_NAME_LEN) {
  106. *error = BAD_MAIL_HOST;
  107. return BAD_MAIL_HOST;
  108. } else {
  109. strcpy(MailHost, host);
  110. }
  111. if (INI_STR("sendmail_from")){
  112. RPath = estrdup(INI_STR("sendmail_from"));
  113. } else {
  114. return 19;
  115. }
  116. /* attempt to connect with mail host */
  117. *error = MailConnect();
  118. if (*error != 0) {
  119. if(RPath)efree(RPath);
  120. return *error;
  121. } else {
  122. ret = SendText(RPath, Subject, mailTo, data, headers);
  123. TSMClose();
  124. if (ret != SUCCESS) {
  125. *error = ret;
  126. }
  127. if(RPath)efree(RPath);
  128. return ret;
  129. }
  130. }
  131. //********************************************************************
  132. // Name: TSendMail::~TSendMail
  133. // Input:
  134. // Output:
  135. // Description: DESTRUCTOR
  136. // Author/Date: jcar 20/9/96
  137. // History:
  138. //********************************************************************/
  139. void TSMClose()
  140. {
  141. Post("QUIT\r\n");
  142. Ack();
  143. /* to guarantee that the cleanup is not made twice and
  144. compomise the rest of the application if sockets are used
  145. elesewhere
  146. */
  147. shutdown(sc, 0);
  148. closesocket(sc);
  149. }
  150. /*********************************************************************
  151. // Name: char *GetSMErrorText
  152. // Input: Error index returned by the menber functions
  153. // Output: pointer to a string containing the error description
  154. // Description:
  155. // Author/Date: jcar 20/9/96
  156. // History:
  157. //*******************************************************************/
  158. char *GetSMErrorText(int index)
  159. {
  160. if ((index > MAX_ERROR_INDEX) || (index < MIN_ERROR_INDEX))
  161. return (ErrorMessages[UNKNOWN_ERROR]);
  162. else
  163. return (ErrorMessages[index]);
  164. }
  165. /*********************************************************************
  166. // Name: TSendText
  167. // Input: 1) RPath: return path of the message
  168. // Is used to fill the "Return-Path" and the
  169. // "X-Sender" fields of the message.
  170. // 2) Subject: Subject field of the message. If NULL is given
  171. // the subject is set to "No Subject"
  172. // 3) mailTo: Destination address
  173. // 4) data: Null terminated string containing the data to be send.
  174. // Output: Error code or SUCCESS
  175. // Description:
  176. // Author/Date: jcar 20/9/96
  177. // History:
  178. //*******************************************************************/
  179. int SendText(char *RPath, char *Subject, char *mailTo, char *data, char *headers)
  180. {
  181. int res, i;
  182. char *p;
  183. char *tempMailTo, *token, *pos1, *pos2;
  184. /* check for NULL parameters */
  185. if (data == NULL)
  186. return (BAD_MSG_CONTENTS);
  187. if (mailTo == NULL)
  188. return (BAD_MSG_DESTINATION);
  189. if (RPath == NULL)
  190. return (BAD_MSG_RPATH);
  191. /* simple checks for the mailto address */
  192. /* have ampersand ? */
  193. if (strchr(mailTo, '@') == NULL)
  194. return (BAD_MSG_DESTINATION);
  195. sprintf(Buffer, "HELO %s\r\n", LocalHost);
  196. /* in the beggining of the dialog */
  197. /* attempt reconnect if the first Post fail */
  198. if ((res = Post(Buffer)) != SUCCESS) {
  199. MailConnect();
  200. if ((res = Post(Buffer)) != SUCCESS)
  201. return (res);
  202. }
  203. if ((res = Ack()) != SUCCESS)
  204. return (res);
  205. sprintf(Buffer, "MAIL FROM:<%s>\r\n", RPath);
  206. if ((res = Post(Buffer)) != SUCCESS)
  207. return (res);
  208. if ((res = Ack()) != SUCCESS)
  209. return (res);
  210. tempMailTo = estrdup(mailTo);
  211. /* Send mail to all rcpt's */
  212. token = strtok(tempMailTo, ",");
  213. while(token != NULL)
  214. {
  215. sprintf(Buffer, "RCPT TO:<%s>\r\n", token);
  216. if ((res = Post(Buffer)) != SUCCESS)
  217. return (res);
  218. if ((res = Ack()) != SUCCESS)
  219. return (res);
  220. token = strtok(NULL, ",");
  221. }
  222. /* Send mail to all Cc rcpt's */
  223. efree(tempMailTo);
  224. if (headers && (pos1 = strstr(headers, "Cc:"))) {
  225. pos2 = strstr(pos1, "\r\n");
  226. tempMailTo = estrndup(pos1, pos2-pos1);
  227. token = strtok(tempMailTo, ",");
  228. while(token != NULL)
  229. {
  230. sprintf(Buffer, "RCPT TO:<%s>\r\n", token);
  231. if ((res = Post(Buffer)) != SUCCESS)
  232. return (res);
  233. if ((res = Ack()) != SUCCESS)
  234. return (res);
  235. token = strtok(NULL, ",");
  236. }
  237. efree(tempMailTo);
  238. }
  239. if ((res = Post("DATA\r\n")) != SUCCESS)
  240. return (res);
  241. if ((res = Ack()) != SUCCESS)
  242. return (res);
  243. /* send message header */
  244. if (Subject == NULL)
  245. res = PostHeader(RPath, "No Subject", mailTo, headers);
  246. else
  247. res = PostHeader(RPath, Subject, mailTo, headers);
  248. if (res != SUCCESS)
  249. return (res);
  250. /* send message contents in 1024 chunks */
  251. if (strlen(data) <= 1024) {
  252. if ((res = Post(data)) != SUCCESS)
  253. return (res);
  254. } else {
  255. p = data;
  256. while (1) {
  257. if (*p == '\0')
  258. break;
  259. if (strlen(p) >= 1024)
  260. i = 1024;
  261. else
  262. i = strlen(p);
  263. /* put next chunk in buffer */
  264. strncpy(Buffer, p, i);
  265. Buffer[i] = '\0';
  266. p += i;
  267. /* send chunk */
  268. if ((res = Post(Buffer)) != SUCCESS)
  269. return (res);
  270. }
  271. }
  272. /*send termination dot */
  273. if ((res = Post("\r\n.\r\n")) != SUCCESS)
  274. return (res);
  275. if ((res = Ack()) != SUCCESS)
  276. return (res);
  277. return (SUCCESS);
  278. }
  279. /*********************************************************************
  280. // Name: PostHeader
  281. // Input: 1) return path
  282. // 2) Subject
  283. // 3) destination address
  284. // 4) DoMime flag
  285. // Output: Error code or Success
  286. // Description:
  287. // Author/Date: jcar 20/9/96
  288. // History:
  289. //********************************************************************/
  290. int PostHeader(char *RPath, char *Subject, char *mailTo, char *xheaders)
  291. {
  292. /* Print message header according to RFC 822 */
  293. /* Return-path, Received, Date, From, Subject, Sender, To, cc */
  294. time_t tNow = time(NULL);
  295. struct tm *tm = localtime(&tNow);
  296. int zoneh = abs(_timezone);
  297. int zonem, res;
  298. char *p;
  299. p = Buffer;
  300. zoneh /= (60 * 60);
  301. zonem = (abs(_timezone) / 60) - (zoneh * 60);
  302. if(!xheaders || !strstr(xheaders, "Date:")){
  303. p += sprintf(p, "Date: %s, %02d %s %04d %02d:%02d:%02d %s%02d%02d\r\n",
  304. days[tm->tm_wday],
  305. tm->tm_mday,
  306. months[tm->tm_mon],
  307. tm->tm_year + 1900,
  308. tm->tm_hour,
  309. tm->tm_min,
  310. tm->tm_sec,
  311. (_timezone > 0) ? "+" : (_timezone < 0) ? "-" : "",
  312. zoneh,
  313. zonem);
  314. }
  315. if(!xheaders || !strstr(xheaders, "From:")){
  316. p += sprintf(p, "From: %s\r\n", RPath);
  317. }
  318. p += sprintf(p, "Subject: %s\r\n", Subject);
  319. if(!xheaders || !strstr(xheaders, "To:")){
  320. p += sprintf(p, "To: %s\r\n", mailTo);
  321. }
  322. if(xheaders){
  323. p += sprintf(p, "%s\r\n", xheaders);
  324. }
  325. if ((res = Post(Buffer)) != SUCCESS)
  326. return (res);
  327. if ((res = Post("\r\n")) != SUCCESS)
  328. return (res);
  329. return (SUCCESS);
  330. }
  331. /*********************************************************************
  332. // Name: MailConnect
  333. // Input: None
  334. // Output: None
  335. // Description: Connect to the mail host and receive the welcome message.
  336. // Author/Date: jcar 20/9/96
  337. // History:
  338. //********************************************************************/
  339. int MailConnect()
  340. {
  341. int res;
  342. short portnum;
  343. /* Create Socket */
  344. if ((sc = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
  345. return (FAILED_TO_OBTAIN_SOCKET_HANDLE);
  346. /* Get our own host name */
  347. if (gethostname(LocalHost, HOST_NAME_LEN))
  348. return (FAILED_TO_GET_HOSTNAME);
  349. /* Resolve the servers IP */
  350. /*
  351. if (!isdigit(MailHost[0])||!gethostbyname(MailHost))
  352. {
  353. return (FAILED_TO_RESOLVE_HOST);
  354. }
  355. */
  356. portnum = (short) INI_INT("sendmail_port");
  357. if (!portnum) {
  358. portnum = 25;
  359. }
  360. /* Connect to server */
  361. sock_in.sin_family = AF_INET;
  362. sock_in.sin_port = htons(portnum);
  363. sock_in.sin_addr.S_un.S_addr = GetAddr(MailHost);
  364. if (connect(sc, (LPSOCKADDR) & sock_in, sizeof(sock_in)))
  365. return (FAILED_TO_CONNECT);
  366. /* receive Server welcome message */
  367. res = Ack();
  368. return (res);
  369. }
  370. /*********************************************************************
  371. // Name: Post
  372. // Input:
  373. // Output:
  374. // Description:
  375. // Author/Date: jcar 20/9/96
  376. // History:
  377. //********************************************************************/
  378. int Post(LPCSTR msg)
  379. {
  380. int len = strlen(msg);
  381. int slen;
  382. int index = 0;
  383. while (len > 0) {
  384. if ((slen = send(sc, msg + index, len, 0)) < 1)
  385. return (FAILED_TO_SEND);
  386. len -= slen;
  387. index += slen;
  388. }
  389. return (SUCCESS);
  390. }
  391. /*********************************************************************
  392. // Name: Ack
  393. // Input:
  394. // Output:
  395. // Description:
  396. // Get the response from the server. We only want to know if the
  397. // last command was successful.
  398. // Author/Date: jcar 20/9/96
  399. // History:
  400. //********************************************************************/
  401. int Ack()
  402. {
  403. static char *buf;
  404. int rlen;
  405. int Index = 0;
  406. int Received = 0;
  407. if (!buf)
  408. if ((buf = (char *) malloc(1024 * 4)) == NULL)
  409. return (OUT_OF_MEMORY);
  410. again:
  411. if ((rlen = recv(sc, buf + Index, ((1024 * 4) - 1) - Received, 0)) < 1)
  412. return (FAILED_TO_RECEIVE);
  413. Received += rlen;
  414. buf[Received] = 0;
  415. /*err_msg fprintf(stderr,"Received: (%d bytes) %s", rlen, buf + Index); */
  416. /* Check for newline */
  417. Index += rlen;
  418. if ((buf[Received - 4] == ' ' && buf[Received - 3] == '-') ||
  419. (buf[Received - 2] != '\r') || (buf[Received - 1] != '\n'))
  420. /* err_msg fprintf(stderr,"Incomplete server message. Awaiting CRLF\n"); */
  421. goto again; /* Incomplete data. Line must be terminated by CRLF
  422. And not contain a space followed by a '-' */
  423. if (buf[0] > '3')
  424. return (SMTP_SERVER_ERROR);
  425. return (SUCCESS);
  426. }
  427. /*********************************************************************
  428. // Name: unsigned long GetAddr (LPSTR szHost)
  429. // Input:
  430. // Output:
  431. // Description: Given a string, it will return an IP address.
  432. // - first it tries to convert the string directly
  433. // - if that fails, it tries o resolve it as a hostname
  434. //
  435. // WARNING: gethostbyname() is a blocking function
  436. // Author/Date: jcar 20/9/96
  437. // History:
  438. //********************************************************************/
  439. unsigned long GetAddr(LPSTR szHost)
  440. {
  441. LPHOSTENT lpstHost;
  442. u_long lAddr = INADDR_ANY;
  443. /* check that we have a string */
  444. if (*szHost) {
  445. /* check for a dotted-IP address string */
  446. lAddr = inet_addr(szHost);
  447. /* If not an address, then try to resolve it as a hostname */
  448. if ((lAddr == INADDR_NONE) && (strcmp(szHost, "255.255.255.255"))) {
  449. lpstHost = gethostbyname(szHost);
  450. if (lpstHost) { /* success */
  451. lAddr = *((u_long FAR *) (lpstHost->h_addr));
  452. } else {
  453. lAddr = INADDR_ANY; /* failure */
  454. }
  455. }
  456. }
  457. return (lAddr);
  458. } /* end GetAddr() */