rmtsmtp.cpp 24 KB

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