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.

501 lines
12 KiB

27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 years ago
27 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 downloaded
  16. * from http://virtual.icr.com.au:80/jgaa/cgi-bin.htm
  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 3.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\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. //********************************************************************
  148. // Name: char *GetSMErrorText
  149. // Input: Error index returned by the menber functions
  150. // Output: pointer to a string containing the error description
  151. // Description:
  152. // Author/Date: jcar 20/9/96
  153. // History:
  154. //********************************************************************
  155. char *GetSMErrorText(int index)
  156. {
  157. if ((index > MAX_ERROR_INDEX) || (index < MIN_ERROR_INDEX))
  158. return (ErrorMessages[UNKNOWN_ERROR]);
  159. else
  160. return (ErrorMessages[index]);
  161. }
  162. //********************************************************************
  163. // Name: TSendText
  164. // Input: 1) RPath: return path of the message
  165. // Is used to fill the "Return-Path" and the
  166. // "X-Sender" fields of the message.
  167. // 2) Subject: Subject field of the message. If NULL is given
  168. // the subject is set to "No Subject"
  169. // 3) mailTo: Destination address
  170. // 4) data: Null terminated string containing the data to be send.
  171. // Output: Error code or SUCCESS
  172. // Description:
  173. // Author/Date: jcar 20/9/96
  174. // History:
  175. //********************************************************************
  176. int SendText(char *RPath, char *Subject, char *mailTo, char *data, char *headers)
  177. {
  178. int res, i;
  179. char *p;
  180. // check for NULL parameters
  181. if (data == NULL)
  182. return (BAD_MSG_CONTENTS);
  183. if (mailTo == NULL)
  184. return (BAD_MSG_DESTINATION);
  185. if (RPath == NULL)
  186. return (BAD_MSG_RPATH);
  187. // simple checks for the mailto address
  188. // have ampersand ?
  189. if (strchr(mailTo, '@') == NULL)
  190. return (BAD_MSG_DESTINATION);
  191. sprintf(Buffer, "HELO %s\n", LocalHost);
  192. // in the beggining of the dialog
  193. // attempt reconnect if the first Post fail
  194. if ((res = Post(Buffer)) != SUCCESS) {
  195. MailConnect();
  196. if ((res = Post(Buffer)) != SUCCESS)
  197. return (res);
  198. }
  199. if ((res = Ack()) != SUCCESS)
  200. return (res);
  201. sprintf(Buffer, "MAIL FROM:<%s>\n", RPath);
  202. if ((res = Post(Buffer)) != SUCCESS)
  203. return (res);
  204. if ((res = Ack()) != SUCCESS)
  205. return (res);
  206. sprintf(Buffer, "RCPT TO:<%s>\n", mailTo);
  207. if ((res = Post(Buffer)) != SUCCESS)
  208. return (res);
  209. if ((res = Ack()) != SUCCESS)
  210. return (res);
  211. if ((res = Post("DATA\n")) != SUCCESS)
  212. return (res);
  213. if ((res = Ack()) != SUCCESS)
  214. return (res);
  215. // send message header
  216. if (Subject == NULL)
  217. res = PostHeader(RPath, "No Subject", mailTo, headers);
  218. else
  219. res = PostHeader(RPath, Subject, mailTo, headers);
  220. if (res != SUCCESS)
  221. return (res);
  222. // send message contents in 1024 chunks
  223. if (strlen(data) <= 1024) {
  224. if ((res = Post(data)) != SUCCESS)
  225. return (res);
  226. } else {
  227. p = data;
  228. while (1) {
  229. if (*p == '\0')
  230. break;
  231. if (strlen(p) >= 1024)
  232. i = 1024;
  233. else
  234. i = strlen(p);
  235. // put next chunk in buffer
  236. strncpy(Buffer, p, i);
  237. Buffer[i] = '\0';
  238. p += i;
  239. // send chunk
  240. if ((res = Post(Buffer)) != SUCCESS)
  241. return (res);
  242. }
  243. }
  244. //send termination dot
  245. if ((res = Post("\r\n.\r\n")) != SUCCESS)
  246. return (res);
  247. if ((res = Ack()) != SUCCESS)
  248. return (res);
  249. return (SUCCESS);
  250. }
  251. //********************************************************************
  252. // Name: PostHeader
  253. // Input: 1) return path
  254. // 2) Subject
  255. // 3) destination address
  256. // 4) DoMime flag
  257. // Output: Error code or Success
  258. // Description:
  259. // Author/Date: jcar 20/9/96
  260. // History:
  261. //********************************************************************
  262. int PostHeader(char *RPath, char *Subject, char *mailTo, char *xheaders)
  263. {
  264. // Print message header according to RFC 822
  265. // Return-path, Received, Date, From, Subject, Sender, To, cc
  266. time_t tNow = time(NULL);
  267. struct tm *tm = localtime(&tNow);
  268. int zoneh = abs(_timezone);
  269. int zonem, res;
  270. char *p;
  271. p = Buffer;
  272. zoneh /= (60 * 60);
  273. zonem = (abs(_timezone) / 60) - (zoneh * 60);
  274. if(!xheaders || !strstr(xheaders, "Date:")){
  275. p += sprintf(p, "Date: %s, %02d %s %04d %02d:%02d:%02d %s%02d%02d\r\n",
  276. days[tm->tm_wday],
  277. tm->tm_mday,
  278. months[tm->tm_mon],
  279. tm->tm_year + 1900,
  280. tm->tm_hour,
  281. tm->tm_min,
  282. tm->tm_sec,
  283. (_timezone > 0) ? "+" : (_timezone < 0) ? "-" : "",
  284. zoneh,
  285. zonem);
  286. }
  287. if(!xheaders || !strstr(xheaders, "From:")){
  288. p += sprintf(p, "From: %s\r\n", RPath);
  289. }
  290. p += sprintf(p, "Subject: %s\r\n", Subject);
  291. p += sprintf(p, "To: %s\r\n", mailTo);
  292. if(xheaders){
  293. p += sprintf(p, "%s\r\n", xheaders);
  294. }
  295. if ((res = Post(Buffer)) != SUCCESS)
  296. return (res);
  297. if ((res = Post("\r\n")) != SUCCESS)
  298. return (res);
  299. return (SUCCESS);
  300. }
  301. //********************************************************************
  302. // Name: MailConnect
  303. // Input: None
  304. // Output: None
  305. // Description: Connect to the mail host and receive the welcome message.
  306. // Author/Date: jcar 20/9/96
  307. // History:
  308. //********************************************************************
  309. int MailConnect()
  310. {
  311. int res;
  312. // Create Socket
  313. if ((sc = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
  314. return (FAILED_TO_OBTAIN_SOCKET_HANDLE);
  315. // Get our own host name
  316. if (gethostname(LocalHost, HOST_NAME_LEN))
  317. return (FAILED_TO_GET_HOSTNAME);
  318. // Resolve the servers IP
  319. //if (!isdigit(MailHost[0])||!gethostbyname(MailHost))
  320. //{
  321. // return (FAILED_TO_RESOLVE_HOST);
  322. //}
  323. // Connect to server
  324. sock_in.sin_family = AF_INET;
  325. sock_in.sin_port = htons(25);
  326. sock_in.sin_addr.S_un.S_addr = GetAddr(MailHost);
  327. if (connect(sc, (LPSOCKADDR) & sock_in, sizeof(sock_in)))
  328. return (FAILED_TO_CONNECT);
  329. // receive Server welcome message
  330. res = Ack();
  331. return (res);
  332. }
  333. //********************************************************************
  334. // Name: Post
  335. // Input:
  336. // Output:
  337. // Description:
  338. // Author/Date: jcar 20/9/96
  339. // History:
  340. //********************************************************************
  341. int Post(LPCSTR msg)
  342. {
  343. int len = strlen(msg);
  344. int slen;
  345. int index = 0;
  346. while (len > 0) {
  347. if ((slen = send(sc, msg + index, len, 0)) < 1)
  348. return (FAILED_TO_SEND);
  349. len -= slen;
  350. index += slen;
  351. }
  352. return (SUCCESS);
  353. }
  354. //********************************************************************
  355. // Name: Ack
  356. // Input:
  357. // Output:
  358. // Description:
  359. // Get the response from the server. We only want to know if the
  360. // last command was successful.
  361. // Author/Date: jcar 20/9/96
  362. // History:
  363. //********************************************************************
  364. int Ack()
  365. {
  366. static char *buf;
  367. int rlen;
  368. int Index = 0;
  369. int Received = 0;
  370. if (!buf)
  371. if ((buf = (char *) malloc(1024 * 4)) == NULL)
  372. return (OUT_OF_MEMORY);
  373. again:
  374. if ((rlen = recv(sc, buf + Index, ((1024 * 4) - 1) - Received, 0)) < 1)
  375. return (FAILED_TO_RECEIVE);
  376. Received += rlen;
  377. buf[Received] = 0;
  378. //err_msg fprintf(stderr,"Received: (%d bytes) %s", rlen, buf + Index);
  379. // Check for newline
  380. Index += rlen;
  381. if ((buf[Received - 2] != '\r') || (buf[Received - 1] != '\n'))
  382. // err_msg fprintf(stderr,"Incomplete server message. Awaiting CRLF\n");
  383. goto again; // Incomplete data. Line must be terminated by CRLF
  384. if (buf[0] > '3')
  385. return (SMTP_SERVER_ERROR);
  386. return (SUCCESS);
  387. }
  388. //********************************************************************
  389. // Name: unsigned long GetAddr (LPSTR szHost)
  390. // Input:
  391. // Output:
  392. // Description: Given a string, it will return an IP address.
  393. // - first it tries to convert the string directly
  394. // - if that fails, it tries o resolve it as a hostname
  395. //
  396. // WARNING: gethostbyname() is a blocking function
  397. // Author/Date: jcar 20/9/96
  398. // History:
  399. //********************************************************************
  400. unsigned long GetAddr(LPSTR szHost)
  401. {
  402. LPHOSTENT lpstHost;
  403. u_long lAddr = INADDR_ANY;
  404. /* check that we have a string */
  405. if (*szHost) {
  406. /* check for a dotted-IP address string */
  407. lAddr = inet_addr(szHost);
  408. /* If not an address, then try to resolve it as a hostname */
  409. if ((lAddr == INADDR_NONE) && (strcmp(szHost, "255.255.255.255"))) {
  410. lpstHost = gethostbyname(szHost);
  411. if (lpstHost) { /* success */
  412. lAddr = *((u_long FAR *) (lpstHost->h_addr));
  413. } else {
  414. lAddr = INADDR_ANY; /* failure */
  415. }
  416. }
  417. }
  418. return (lAddr);
  419. } /* end GetAddr() */