rmtsmtp.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "platform.h"
  15. #include "jlib.hpp"
  16. #include "jlog.hpp"
  17. #include "jsocket.hpp"
  18. #include "jbuff.hpp"
  19. #include "rmtsmtp.hpp"
  20. class CSMTPValidator
  21. {
  22. public:
  23. CSMTPValidator() : scanlist(false), value(NULL), finger(NULL), label(NULL) {}
  24. void validateValue(char const * _value, char const * _label)
  25. {
  26. value = finger = _value;
  27. label = _label;
  28. while(*finger)
  29. {
  30. if(badChar(*finger))
  31. fail("illegal character");
  32. ++finger;
  33. }
  34. }
  35. void validateAddress(char const * _address, char const * _label)
  36. {
  37. value = finger = _address;
  38. label = _label;
  39. scanlist = false;
  40. validateLocalPart();
  41. validateDomain();
  42. }
  43. void validateDomain(char const * _domain, char const * _label)
  44. {
  45. value = finger = _domain;
  46. label = _label;
  47. scanlist = false;
  48. validateDomain();
  49. }
  50. void scanAddressListStart(char const * _addrlist, char const * _label)
  51. {
  52. value = finger = _addrlist;
  53. label = _label;
  54. if(!skipListSep())
  55. fail("empty address list");
  56. scanlist = true;
  57. }
  58. bool scanAddressListNext(StringBuffer & out)
  59. {
  60. if(!scanlist)
  61. return false;
  62. char const * start = finger;
  63. validateLocalPart();
  64. scanlist = validateDomain();
  65. out.append(finger-start, start);
  66. if(scanlist)
  67. scanlist = skipListSep();
  68. return true;
  69. }
  70. void escapeQuoted(char const * in, StringBuffer & out, char const * _label)
  71. {
  72. value = finger = in;
  73. label = _label;
  74. while(*finger)
  75. {
  76. if(badChar(*finger))
  77. fail("illegal character");
  78. else if((*finger == '"') || (*finger == '\\'))
  79. {
  80. if(finger>in)
  81. out.append(finger-in, in);
  82. out.append('\\');
  83. in = finger;
  84. }
  85. ++finger;
  86. }
  87. if(finger>in)
  88. out.append(finger-in, in);
  89. }
  90. private:
  91. bool skipListSep()
  92. {
  93. while(*finger && isListSep(*finger))
  94. ++finger;
  95. return (*finger != 0);
  96. }
  97. void validateLocalPart()
  98. {
  99. if(*finger == '"')
  100. validateQuotedLocal();
  101. else
  102. validateDotStringLocal();
  103. }
  104. bool validateDomain()
  105. {
  106. if(*finger == '[')
  107. return validateAddressLiteral();
  108. else
  109. return validateNamedDomain();
  110. }
  111. void validateQuotedLocal()
  112. {
  113. ++finger;
  114. while(*finger != '"')
  115. {
  116. if(*finger == '\\')
  117. {
  118. ++finger;
  119. if(!*finger)
  120. fail("unexpected end-of-string in quoted local part");
  121. else if(badChar(*finger))
  122. fail("illegal escaped character in quoted local part");
  123. }
  124. else if(!*finger)
  125. fail("unexpected end-of-string in quoted local part");
  126. else if(badQuotedChar(*finger))
  127. fail("illegal character in quoted local part (may need escaping)");
  128. ++finger;
  129. }
  130. ++finger;
  131. if(!*finger)
  132. fail("address had quoted local part but no domain (reached end-of-string)");
  133. else if(*finger != '@')
  134. fail("quoted local part was not followed by @");
  135. ++finger;
  136. }
  137. void validateDotStringLocal()
  138. {
  139. enum { Start, StartAtom, Main };
  140. unsigned mode = Start;
  141. while(*finger != '@')
  142. {
  143. if(*finger == '.')
  144. switch(mode)
  145. {
  146. case Start:
  147. fail("illegal . at start of local part");
  148. case StartAtom:
  149. fail("illegal .. in local part");
  150. case Main:
  151. mode = StartAtom;
  152. break;
  153. }
  154. else if(!*finger)
  155. if(mode == Start)
  156. fail("blank address (reached end-of-string)");
  157. else
  158. fail("address had dotted-atom-string local part but no domain (reached end-of-string)");
  159. else if(scanlist && isListSep(*finger))
  160. if(mode == Start)
  161. fail("blank address (reached comma/semicolon/space indicating next address in list)");
  162. else
  163. fail("address had dotted-atom-string local part but no domain (reached comma/semicolon/space indicating next address in list)");
  164. else if(badAtomChar(*finger))
  165. fail("illegal character in dotted-atom-string local part (may need quoting)");
  166. else
  167. mode = Main;
  168. ++finger;
  169. }
  170. switch(mode)
  171. {
  172. case Start:
  173. fail("empty local part");
  174. case StartAtom:
  175. fail("illegal . at end of local part");
  176. }
  177. ++finger;
  178. }
  179. bool validateAddressLiteral()
  180. {
  181. ++finger;
  182. unsigned digitcount = 0;
  183. unsigned groupcount = 0;
  184. while(*finger != ']')
  185. {
  186. if(isdigit(*finger))
  187. if(digitcount == 3)
  188. fail("more than three digits in octet in address literal");
  189. else
  190. ++digitcount;
  191. else if(*finger == '.')
  192. if(digitcount == 0)
  193. {
  194. fail("empty octet in address literal");
  195. }
  196. else
  197. {
  198. digitcount = 0;
  199. ++groupcount;
  200. if(groupcount == 4)
  201. fail("too many octets in address literal (sorry, only IPv4 supported)");
  202. }
  203. else if(!*finger)
  204. fail("unexpected end-of-string in address literal");
  205. else
  206. fail("illegal character in address literal (sorry, only IPv4 supported)");
  207. ++finger;
  208. }
  209. if(digitcount == 0)
  210. fail("empty octet in address literal");
  211. digitcount = 0;
  212. ++groupcount;
  213. if(groupcount < 4)
  214. fail("too few octets in address literal");
  215. ++finger;
  216. if(scanlist && isListSep(*finger))
  217. return true;
  218. if(*finger)
  219. fail("unexpected character after end of address literal");
  220. return false;
  221. }
  222. bool validateNamedDomain()
  223. {
  224. unsigned subcount = 0;
  225. unsigned charcount = 0;
  226. bool ret = false;
  227. while(*finger)
  228. {
  229. if(isalnum(*finger))
  230. ++charcount;
  231. else if(*finger == '_')
  232. if(charcount == 0)
  233. fail("illegal _ at start of subdomain");
  234. else if(!*(finger+1) || (*(finger+1) == '.') || (scanlist && isListSep(*(finger+1))))
  235. fail("illegal _ at end of subdomain");
  236. else
  237. ++charcount;
  238. else if(*finger == '.')
  239. if(charcount == 0)
  240. if(subcount == 0)
  241. fail("illegal . at start of domain");
  242. else
  243. fail("illegal .. in domain");
  244. else
  245. {
  246. ++subcount;
  247. charcount = 0;
  248. }
  249. else if(scanlist && isListSep(*finger))
  250. {
  251. ret = true;
  252. break;
  253. }
  254. else
  255. fail("illegal character in domain");
  256. ++finger;
  257. }
  258. if(charcount == 0)
  259. if(subcount == 0)
  260. fail("empty domain");
  261. else
  262. fail("illegal . at end of domain");
  263. ++subcount;
  264. if(subcount < 2)
  265. fail("domain has only 1 subdomain");
  266. return ret;
  267. }
  268. void fail(char const * msg)
  269. {
  270. throw MakeStringException(0, "bad %s (%s at character %u): %s", label, msg, (unsigned) (finger-value), value);
  271. }
  272. bool badAtomChar(char c)
  273. {
  274. if((c<33) || (c>126)) return true;
  275. switch(c)
  276. {
  277. case '"':
  278. case '(':
  279. case ')':
  280. case ',':
  281. case '.':
  282. case ':':
  283. case ';':
  284. case '<':
  285. case '>':
  286. case '@':
  287. case '[':
  288. case '\\':
  289. case ']':
  290. return true;
  291. }
  292. return false;
  293. }
  294. bool badQuotedChar(char c)
  295. {
  296. if((c < 1) || (c>126)) return true;
  297. switch(c)
  298. {
  299. case '\t':
  300. case '\r':
  301. case '\n':
  302. case ' ':
  303. case '"':
  304. case '\\':
  305. return true;
  306. }
  307. return false;
  308. }
  309. bool badChar(char c)
  310. {
  311. if((c < 1) || (c>126)) return true;
  312. switch(c)
  313. {
  314. case '\r':
  315. case '\n':
  316. return true;
  317. }
  318. return false;
  319. }
  320. bool isListSep(char c)
  321. {
  322. switch(c)
  323. {
  324. case ',':
  325. case ';':
  326. case ' ':
  327. return true;
  328. }
  329. return false;
  330. }
  331. private:
  332. char const * value;
  333. char const * finger;
  334. char const * label;
  335. bool scanlist;
  336. };
  337. // escapes text for mail transfer, returns true if quoted-printable encoding was required
  338. bool mailEncode(char const * in, StringBuffer & out)
  339. {
  340. bool esc = false;
  341. size32_t outlinelen = 0;
  342. char const * finger = in;
  343. while(*finger)
  344. {
  345. //max line length 76, use soft line break =\r\n to split (RFC 1521 section 5.1 rule #5)
  346. if(outlinelen+finger-in == 75)
  347. {
  348. out.append(finger-in, in).append("=\r\n");
  349. outlinelen = 0;
  350. in = finger;
  351. esc = true;
  352. }
  353. //printable chars except = and - and . are left alone (RFC 1521 section 5.1 rule #2)
  354. if((*finger >= 33) && (*finger <= 126) && (*finger != '=') && (*finger != '-') && (*finger != '.'))
  355. {
  356. ++finger;
  357. continue;
  358. }
  359. //- is left alone, except for -- at start of line to protect multipart boundary (RFC 1341 section 7.2.1)
  360. if(*finger == '-')
  361. {
  362. if((outlinelen != 0) || (*(finger+1) != '-'))
  363. {
  364. ++finger;
  365. continue;
  366. }
  367. }
  368. //. is left alone, except that an extra . is added when at start of line to protect SMTP 'end of data' signal (RFC 8211 section 4.5.2)
  369. if(*finger == '.')
  370. {
  371. if(outlinelen == 0)
  372. {
  373. out.append('.');
  374. ++outlinelen;
  375. }
  376. ++finger;
  377. continue;
  378. }
  379. //tab and space are left alone except at EOL (RFC 1521 section 5.1 rule #3)
  380. if((*finger == '\t') || (*finger == ' '))
  381. {
  382. char nxt = *(finger+1);
  383. if(nxt && (nxt != 10) && (nxt != 13))
  384. {
  385. ++finger;
  386. continue;
  387. }
  388. }
  389. //CR, LF, and CRLF are all converted to CRLF (RFC 1521 section 5.1 rule #4)
  390. if(*finger == 10)
  391. {
  392. if(finger>in)
  393. out.append(finger-in, in);
  394. ++finger;
  395. if(*finger == 13)
  396. ++finger;
  397. out.append("\r\n");
  398. outlinelen = 0;
  399. in = finger;
  400. continue;
  401. }
  402. if(*finger == 13)
  403. {
  404. if(finger>in)
  405. out.append(finger-in, in);
  406. ++finger;
  407. out.append("\r\n");
  408. outlinelen = 0;
  409. in = finger;
  410. continue;
  411. }
  412. //everything else is escaped (RFC 1521 section 5.1 rule #1)
  413. if(finger>in)
  414. out.append(finger-in, in);
  415. if(outlinelen+finger-in > 72)
  416. {
  417. out.append("=\r\n");
  418. outlinelen = 3;
  419. }
  420. else
  421. {
  422. outlinelen += (finger-in)+3;
  423. }
  424. out.appendf("=%02X", (unsigned char)*finger);
  425. in = ++finger;
  426. esc = true;
  427. }
  428. if(finger > in)
  429. out.append(finger-in, in);
  430. return esc;
  431. }
  432. //#define SMTP_TRACE
  433. class CMailInfo
  434. {
  435. StringArray *warnings;
  436. StringArray recipients;
  437. StringBuffer to;
  438. StringAttr subject;
  439. StringAttr mailServer;
  440. unsigned port;
  441. StringAttr sender;
  442. Owned<ISocket> socket;
  443. StringBuffer lastAction;
  444. char inbuff[200];
  445. unsigned inlen;
  446. static char const * toHeader;
  447. static char const * subjectHeader;
  448. static char const * senderHeader;
  449. public:
  450. CMailInfo(char const * _to, char const * _subject, char const * _mailServer, unsigned _port, char const * _sender, StringArray *_warnings)
  451. : subject(_subject), mailServer(_mailServer), port(_port), sender(_sender), lastAction("process initialization"), inlen(0)
  452. {
  453. warnings = _warnings;
  454. CSMTPValidator validator;
  455. validator.validateDomain(mailServer.get(), "mail server address");
  456. if(strlen(senderHeader) + sender.length() > 998)
  457. throw MakeStringException(0, "email sender address too long: %u characters", sender.length());
  458. validator.validateAddress(sender.get(), "email sender address");
  459. getRecipients(validator, _to);
  460. if(strlen(toHeader) + to.length() > 998)
  461. throw MakeStringException(0, "Email recipient address list too long: %u characters", to.length());
  462. if(strlen(subjectHeader) + subject.length() > 998)
  463. throw MakeStringException(0, "Email subject too long: %u characters", subject.length());
  464. validator.validateValue(subject.get(), "email subject");
  465. }
  466. void open()
  467. {
  468. SocketEndpoint address(mailServer.get());
  469. if (address.isNull())
  470. throw MakeStringException(MSGAUD_operator, 0, "Could not resolve mail server address %s in SendEmail*", mailServer.get());
  471. address.port = port;
  472. try
  473. {
  474. socket.setown(ISocket::connect(address));
  475. }
  476. catch(IException *E)
  477. {
  478. E->Release();
  479. throw MakeStringException(MSGAUD_operator, 0, "Failed to connect to mail server at %s:%u in SendEmail*", mailServer.get(), port);
  480. }
  481. lastAction.clear().append("connection to server");
  482. }
  483. void write(char const * out, size32_t len, char const * action = NULL)
  484. {
  485. if(action)
  486. lastAction.clear().append(action);
  487. else
  488. lastAction.clear().append(len, out).clip();
  489. try
  490. {
  491. socket->write(out, len);
  492. #ifdef SMTP_TRACE
  493. DBGLOG("SMTP write: [%s]", out);
  494. #endif
  495. }
  496. catch(IException * e)
  497. {
  498. int code = e->errorCode();
  499. StringBuffer buff;
  500. e->errorMessage(buff);
  501. e->Release();
  502. throw MakeStringException(MSGAUD_operator, 0, "Exception %d (%s) in SendEmail* while writing %s to mail server %s:%u", code, buff.str(), lastAction.str(), mailServer.get(), port);
  503. }
  504. }
  505. void read()
  506. {
  507. try
  508. {
  509. socket->read(inbuff,1,sizeof(inbuff),inlen);
  510. //MORE: the following is somewhat primitive and not RFC compliant (see bug 25951) - but it is a lot better than nothing
  511. if((*inbuff == '4') || (*inbuff == '5'))
  512. {
  513. StringBuffer b;
  514. b.append("Negative reply from mail server at ").append(mailServer.get()).append(":").append(port).append(" after writing ").append(lastAction.str()).append(" in SendEmail*: ").append(inlen, inbuff).clip();
  515. WARNLOG("%s", b.str());
  516. if (warnings)
  517. warnings->append(b.str());
  518. }
  519. #ifdef SMTP_TRACE
  520. else
  521. {
  522. StringBuffer b(inlen, inbuff);
  523. b.clip();
  524. DBGLOG("SMTP read: [%s]", b.str());
  525. }
  526. #endif
  527. }
  528. catch(IException * e)
  529. {
  530. int code = e->errorCode();
  531. StringBuffer buff;
  532. e->errorMessage(buff);
  533. e->Release();
  534. throw MakeStringException(MSGAUD_operator, 0, "Exception %d (%s) in SendEmail* while reading from mail server %s:%u following %s", code, buff.str(), mailServer.get(), port, lastAction.str());
  535. }
  536. }
  537. void getHeader(StringBuffer & header) const
  538. {
  539. header.append(senderHeader).append(sender.get()).append("\r\n");
  540. header.append(toHeader).append(to.str()).append("\r\n");
  541. header.append(subjectHeader).append(subject.get()).append("\r\n");
  542. header.append("MIME-Version: 1.0\r\n");
  543. }
  544. void getHelo(StringBuffer & out) const
  545. {
  546. out.append("HELO ").append(mailServer.get()).append("\r\n");
  547. }
  548. void getMailFrom(StringBuffer & out) const
  549. {
  550. out.append("MAIL FROM:<").append(sender.get()).append(">\r\n");
  551. }
  552. unsigned numRecipients() const
  553. {
  554. return recipients.ordinality();
  555. }
  556. void getRecipient(unsigned i, StringBuffer & out) const
  557. {
  558. char const * rcpt = recipients.item(i);
  559. out.append("RCPT TO:<").append(rcpt).append(">\r\n");
  560. }
  561. private:
  562. void getRecipients(CSMTPValidator & validator, char const * _to)
  563. {
  564. StringBuffer rcpt;
  565. validator.scanAddressListStart(_to, "recipient email address list");
  566. while(validator.scanAddressListNext(rcpt.clear()))
  567. {
  568. if(recipients.ordinality())
  569. to.append(",");
  570. to.append(rcpt.str());
  571. recipients.append(rcpt.str());
  572. }
  573. }
  574. };
  575. char const * CMailInfo::toHeader = "To: ";
  576. char const * CMailInfo::subjectHeader = "Subject: ";
  577. char const * CMailInfo::senderHeader = "From: ";
  578. class CMailPart
  579. {
  580. public:
  581. CMailPart(char const * mimeType, char const * filename)
  582. {
  583. if(strlen(mimeTypeHeader) + strlen(mimeType) > 998)
  584. throw MakeStringException(0, "Email attachment mime type too long: %u characters", (unsigned) strlen(mimeType));
  585. CSMTPValidator validator;
  586. validator.validateValue(mimeType, "email attachment mime type");
  587. mime.append(mimeType);
  588. if(filename)
  589. {
  590. StringBuffer qfilename;
  591. validator.escapeQuoted(filename, qfilename, "email attachment filename");
  592. if(strlen(dispositionHeader) + strlen("attachment; filename=\"\"") + qfilename.length() > 998)
  593. throw MakeStringException(0, "Email attachment filename too long: %u characters", (unsigned) strlen(filename));
  594. disposition.append("attachment; filename=\"").append(qfilename.str()).append("\"");
  595. }
  596. else
  597. {
  598. disposition.append("inline");
  599. }
  600. encoding = NULL;
  601. }
  602. void getHeader(StringBuffer & header) const
  603. {
  604. header.append(mimeTypeHeader).append(mime.str()).append("\r\n");
  605. header.append(dispositionHeader).append(disposition).append("\r\n");
  606. if(encoding)
  607. header.append(encodingHeader).append(encoding).append("\r\n");
  608. }
  609. virtual void write(CMailInfo & info) const = 0;
  610. protected:
  611. char const * encoding;
  612. StringBuffer mime;
  613. StringBuffer disposition;
  614. private:
  615. static char const * mimeTypeHeader;
  616. static char const * dispositionHeader;
  617. static char const * encodingHeader;
  618. };
  619. char const * CMailPart::mimeTypeHeader = "Content-Type: ";
  620. char const * CMailPart::dispositionHeader = "Content-Disposition: ";
  621. char const * CMailPart::encodingHeader = "Content-Transfer-Encoding: ";
  622. class CTextMailPart : public CMailPart
  623. {
  624. public:
  625. CTextMailPart(char const * text, char const * mimeType, char const * filename) : CMailPart(mimeType, filename)
  626. {
  627. if(mailEncode(text, buff))
  628. encoding = "quoted-printable";
  629. }
  630. void write(CMailInfo & info) const
  631. {
  632. info.write(buff.str(), buff.length(), "mail body");
  633. }
  634. private:
  635. StringBuffer buff;
  636. };
  637. class CDataMailPart : public CMailPart
  638. {
  639. public:
  640. CDataMailPart(size32_t len, const void * data, char const * mimeType, char const * filename) : CMailPart(mimeType, filename)
  641. {
  642. JBASE64_Encode(data, len, buff);
  643. encoding = "base64";
  644. }
  645. void write(CMailInfo & info) const
  646. {
  647. info.write(buff.str(), buff.length(), "mail body");
  648. }
  649. private:
  650. StringBuffer buff;
  651. };
  652. class CMultiMailPart : public CMailPart
  653. {
  654. public:
  655. CMultiMailPart(CMailPart const & _inlined, CMailPart const & _attachment) : CMailPart("multipart/mixed", NULL), inlined(_inlined), attachment(_attachment)
  656. {
  657. unsigned char rndm[12];
  658. for(unsigned i=0; i<12; ++i)
  659. rndm[i] = getRandom() % 256;
  660. JBASE64_Encode(rndm, 12, boundary);
  661. mime.append("; boundary=\"").append(boundary).append("\"");
  662. }
  663. void write(CMailInfo & info) const
  664. {
  665. writePart(inlined, info);
  666. writePart(attachment, info);
  667. writePartEnd(info);
  668. }
  669. private:
  670. void writePart(CMailPart const & part, CMailInfo & info) const
  671. {
  672. StringBuffer outbuff;
  673. outbuff.append("\r\n").append("--").append(boundary).append("\r\n");
  674. part.getHeader(outbuff);
  675. outbuff.append("\r\n");
  676. info.write(outbuff.str(), outbuff.length(), "mail body");
  677. part.write(info);
  678. }
  679. void writePartEnd(CMailInfo & info) const
  680. {
  681. StringBuffer outbuff;
  682. outbuff.append("\r\n").append("--").append(boundary).append("--").append("\r\n");
  683. info.write(outbuff.str(), outbuff.length(), "mail body");
  684. }
  685. private:
  686. StringBuffer boundary;
  687. CMailPart const & inlined;
  688. CMailPart const & attachment;
  689. };
  690. static const char *data="DATA\r\n";
  691. static const char *endMail="\r\n\r\n.\r\n";
  692. static const char *quit="QUIT\r\n";
  693. static void doSendEmail(CMailInfo & info, CMailPart const & part)
  694. {
  695. info.open();
  696. StringBuffer outbuff;
  697. info.read();
  698. info.getHelo(outbuff);
  699. info.write(outbuff.str(), outbuff.length());
  700. info.read();
  701. info.getMailFrom(outbuff.clear());
  702. info.write(outbuff.str(), outbuff.length());
  703. info.read();
  704. unsigned numRcpt = info.numRecipients();
  705. for(unsigned i=0; i<numRcpt; ++i)
  706. {
  707. info.getRecipient(i, outbuff.clear());
  708. info.write(outbuff.str(), outbuff.length());
  709. info.read();
  710. }
  711. info.write(data, strlen(data));
  712. info.read();
  713. info.getHeader(outbuff.clear());
  714. part.getHeader(outbuff);
  715. outbuff.append("\r\n");
  716. info.write(outbuff.str(), outbuff.length(), "mail header");
  717. part.write(info);
  718. info.write(endMail, strlen(endMail), "end of mail body");
  719. info.read();
  720. info.write(quit, strlen(quit));
  721. info.read();
  722. }
  723. void sendEmail(const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings)
  724. {
  725. CMailInfo info(to, subject, mailServer, port, sender, warnings);
  726. CTextMailPart bodyPart(body, "text/plain; charset=ISO-8859-1", NULL);
  727. doSendEmail(info, bodyPart);
  728. }
  729. void sendEmailAttachText(const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings)
  730. {
  731. CMailInfo info(to, subject, mailServer, port, sender, warnings);
  732. CTextMailPart inlinedPart(body, "text/plain; charset=ISO-8859-1", NULL);
  733. CTextMailPart attachmentPart(attachment, mimeType, attachmentName);
  734. CMultiMailPart multiPart(inlinedPart, attachmentPart);
  735. doSendEmail(info, multiPart);
  736. }
  737. void sendEmailAttachData(const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings)
  738. {
  739. CMailInfo info(to, subject, mailServer, port, sender, warnings);
  740. CTextMailPart inlinedPart(body, "text/plain; charset=ISO-8859-1", NULL);
  741. CDataMailPart attachmentPart(lenAttachment, attachment, mimeType, attachmentName);
  742. CMultiMailPart multiPart(inlinedPart, attachmentPart);
  743. doSendEmail(info, multiPart);
  744. }