PageRenderTime 71ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/ext_mailparse.cpp

https://github.com/tstarling/hiphop-php
C++ | 454 lines | 350 code | 56 blank | 48 comment | 98 complexity | 9599b7c5c166158b8b647b969d564efc MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. | Copyright (c) 1997-2010 The PHP Group |
  7. +----------------------------------------------------------------------+
  8. | This source file is subject to version 3.01 of the PHP license, |
  9. | that is bundled with this package in the file LICENSE, and is |
  10. | available through the world-wide-web at the following url: |
  11. | http://www.php.net/license/3_01.txt |
  12. | If you did not receive a copy of the PHP license and are unable to |
  13. | obtain it through the world-wide-web, please send a note to |
  14. | license@php.net so we can mail you a copy immediately. |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/ext_mailparse.h"
  18. #include "hphp/runtime/base/runtime-option.h"
  19. #include "hphp/runtime/base/runtime-error.h"
  20. #include "hphp/runtime/base/temp-file.h"
  21. #include "hphp/runtime/ext/ext_process.h"
  22. #include "hphp/runtime/ext/mailparse/mime.h"
  23. #include "hphp/runtime/ext/mailparse/rfc822.h"
  24. #include "hphp/runtime/base/zend-string.h"
  25. namespace HPHP {
  26. ///////////////////////////////////////////////////////////////////////////////
  27. // utility functions
  28. /**
  29. * Removes whitespaces from the end, and replaces control characters with ' '
  30. * from the beginning.
  31. */
  32. static String php_trim(const String& str) {
  33. std::string s(str.c_str());
  34. unsigned int l = s.length();
  35. while (l > 0 && isspace((unsigned char)s[l - 1])) {
  36. l--;
  37. }
  38. for (unsigned int i = 0; i < l; i++) {
  39. if (iscntrl((unsigned char)s[i])) {
  40. if (i + 2 < l && s[i] == '\r' && s[i + 1] == '\n' &&
  41. (s[i + 2] == ' ' || s[i + 2] == '\t')) {
  42. i += 2;
  43. while (i + 1 < l && (s[i + 1] == ' ' || s[i + 1] == '\t')) {
  44. i++;
  45. }
  46. continue;
  47. }
  48. s[i] = ' ';
  49. }
  50. }
  51. return s.substr(0, l);
  52. }
  53. ///////////////////////////////////////////////////////////////////////////////
  54. bool php_mail(const String& to, const String& subject, const String& message,
  55. const String& headers, const String& extra_cmd) {
  56. // assumes we always have sendmail installed
  57. always_assert(!RuntimeOption::SendmailPath.empty());
  58. std::ostringstream os;
  59. os << RuntimeOption::SendmailPath;
  60. if (!extra_cmd.empty()) {
  61. os << ' ' << extra_cmd.c_str();
  62. }
  63. errno = 0;
  64. FILE *sendmail = popen(os.str().c_str(), "w");
  65. if (sendmail == NULL || EACCES == errno) {
  66. raise_warning("Unable to execute %s",
  67. RuntimeOption::SendmailPath.c_str());
  68. return false;
  69. }
  70. fprintf(sendmail, "To: %s\n", to.c_str());
  71. fprintf(sendmail, "Subject: %s\n", subject.c_str());
  72. if (!headers.empty()) {
  73. fprintf(sendmail, "%s\n", headers.c_str());
  74. }
  75. fprintf(sendmail, "\n%s\n", message.c_str());
  76. int ret = pclose(sendmail);
  77. return (!ret);
  78. }
  79. const StaticString
  80. s_zero(LITSTR_INIT("\0")),
  81. s_space(" ");
  82. bool f_mail(const String& to, const String& subject, const String& message,
  83. const String& additional_headers /* = null_string */,
  84. const String& additional_parameters /* = null_string */) {
  85. // replace \0 with spaces
  86. String to2 = string_replace(to, s_zero, s_space);
  87. String subject2 = string_replace(subject, s_zero, s_space);
  88. String message2 = string_replace(message, s_zero, s_space);
  89. String headers2;
  90. if (!additional_headers.empty()) {
  91. headers2 = string_replace(additional_headers, s_zero, s_space);
  92. }
  93. String params2;
  94. if (!additional_parameters.empty()) {
  95. params2 = string_replace(additional_parameters, s_zero, s_space);
  96. }
  97. to2 = php_trim(to2);
  98. subject2 = php_trim(subject2);
  99. if (!RuntimeOption::MailForceExtraParameters.empty()) {
  100. params2 = f_escapeshellcmd(RuntimeOption::MailForceExtraParameters);
  101. } else {
  102. params2 = f_escapeshellcmd(params2);
  103. }
  104. return php_mail(to2, subject2, message2, headers2, params2);
  105. }
  106. int64_t f_ezmlm_hash(const String& addr) {
  107. unsigned long h = 5381L;
  108. int str_len = addr.length();
  109. for (int i = 0; i < str_len; i++) {
  110. h = (h + (h << 5)) ^
  111. ((unsigned long)(unsigned char)tolower(addr.charAt(i)));
  112. }
  113. h = (h % 53);
  114. return (int)h;
  115. }
  116. ///////////////////////////////////////////////////////////////////////////////
  117. // mailparse
  118. Resource f_mailparse_msg_create() {
  119. return NEWOBJ(MimePart)();
  120. }
  121. bool f_mailparse_msg_free(const Resource& mimemail) {
  122. return true;
  123. }
  124. Variant f_mailparse_msg_parse_file(const String& filename) {
  125. Resource resource = File::Open(filename, "rb");
  126. File *f = resource.getTyped<File>(true);
  127. if (!f) return false;
  128. MimePart *p = NEWOBJ(MimePart)();
  129. Resource ret(p);
  130. while (!f->eof()) {
  131. String line = f->readLine();
  132. if (!line.isNull()) {
  133. if (!MimePart::ProcessLine(p, line)) {
  134. return false;
  135. }
  136. }
  137. }
  138. return ret;
  139. }
  140. bool f_mailparse_msg_parse(const Resource& mimemail, const String& data) {
  141. return mimemail.getTyped<MimePart>()->parse(data.data(), data.size());
  142. }
  143. Variant f_mailparse_msg_extract_part_file(const Resource& mimemail, const Variant& filename,
  144. const Variant& callbackfunc /* = "" */) {
  145. return mimemail.getTyped<MimePart>()->
  146. extract(filename, callbackfunc,
  147. MimePart::Decode8Bit | MimePart::DecodeNoHeaders, true);
  148. }
  149. Variant f_mailparse_msg_extract_whole_part_file(const Resource& mimemail,
  150. const Variant& filename,
  151. const Variant& callbackfunc /* = "" */) {
  152. return mimemail.getTyped<MimePart>()->
  153. extract(filename, callbackfunc, MimePart::DecodeNone, true);
  154. }
  155. Variant f_mailparse_msg_extract_part(const Resource& mimemail, const Variant& msgbody,
  156. const Variant& callbackfunc /* = "" */) {
  157. return mimemail.getTyped<MimePart>()->
  158. extract(msgbody, callbackfunc,
  159. MimePart::Decode8Bit | MimePart::DecodeNoHeaders, false);
  160. }
  161. Array f_mailparse_msg_get_part_data(const Resource& mimemail) {
  162. return mimemail.getTyped<MimePart>()->getPartData().toArray();
  163. }
  164. Variant f_mailparse_msg_get_part(const Resource& mimemail, const String& mimesection) {
  165. Resource part =
  166. mimemail.getTyped<MimePart>()->findByName(mimesection.c_str());
  167. if (part.isNull()) {
  168. raise_warning("cannot find section %s in message", mimesection.data());
  169. return false;
  170. }
  171. return part;
  172. }
  173. Array f_mailparse_msg_get_structure(const Resource& mimemail) {
  174. return mimemail.getTyped<MimePart>()->getStructure();
  175. }
  176. const StaticString
  177. s_display("display"),
  178. s_address("address"),
  179. s_is_group("is_group");
  180. Array f_mailparse_rfc822_parse_addresses(const String& addresses) {
  181. php_rfc822_tokenized_t *toks =
  182. php_mailparse_rfc822_tokenize(addresses.data(), 1);
  183. php_rfc822_addresses_t *addrs = php_rfc822_parse_address_tokens(toks);
  184. Array ret = Array::Create();
  185. for (int i = 0; i < addrs->naddrs; i++) {
  186. Array item = Array::Create();
  187. if (addrs->addrs[i].name) {
  188. item.set(s_display, String(addrs->addrs[i].name, CopyString));
  189. }
  190. if (addrs->addrs[i].address) {
  191. item.set(s_address, String(addrs->addrs[i].address, CopyString));
  192. }
  193. item.set(s_is_group, (bool)addrs->addrs[i].is_group);
  194. ret.append(item);
  195. }
  196. php_rfc822_free_addresses(addrs);
  197. php_rfc822_tokenize_free(toks);
  198. return ret;
  199. }
  200. static int mailparse_stream_output(int c, void *stream) {
  201. char buf[2];
  202. buf[0] = c;
  203. buf[1] = '\0';
  204. return ((File*)stream)->write(buf, 1);
  205. }
  206. static int mailparse_stream_flush(void *stream) {
  207. return ((File*)stream)->flush() ? 1 : 0;
  208. }
  209. bool f_mailparse_stream_encode(const Resource& sourcefp, const Resource& destfp,
  210. const String& encoding) {
  211. File *srcstream = sourcefp.getTyped<File>(true, true);
  212. File *deststream = destfp.getTyped<File>(true, true);
  213. if (srcstream == NULL || deststream == NULL) {
  214. return false;
  215. }
  216. enum mbfl_no_encoding enc = mbfl_name2no_encoding(encoding.data());
  217. if (enc == mbfl_no_encoding_invalid) {
  218. raise_warning("Unknown encoding \"%s\"", encoding.data());
  219. return false;
  220. }
  221. mbfl_convert_filter *conv =
  222. mbfl_convert_filter_new(mbfl_no_encoding_8bit, enc,
  223. mailparse_stream_output, mailparse_stream_flush,
  224. deststream);
  225. if (enc == mbfl_no_encoding_qprint) {
  226. /* If the qp encoded section is going to be digitally signed,
  227. * it is a good idea to make sure that lines that begin "From "
  228. * have the letter F encoded, so that MTAs do not stick a > character
  229. * in front of it and invalidate the content/signature */
  230. while (!srcstream->eof()) {
  231. String line = srcstream->readLine();
  232. if (!line.isNull()) {
  233. int i;
  234. if (strncmp(line.data(), "From ", 5) == 0) {
  235. mbfl_convert_filter_flush(conv);
  236. deststream->write("=46rom ", 7);
  237. i = 5;
  238. } else {
  239. i = 0;
  240. }
  241. const char *p = line.data();
  242. for (; i < line.size(); i++) {
  243. mbfl_convert_filter_feed(p[i], conv);
  244. }
  245. }
  246. }
  247. } else {
  248. while (!srcstream->eof()) {
  249. String data = srcstream->read();
  250. if (!data.empty()) {
  251. const char *p = data.data();
  252. for (int i = 0; i < data.size(); i++) {
  253. mbfl_convert_filter_feed(p[i], conv);
  254. }
  255. }
  256. }
  257. }
  258. mbfl_convert_filter_flush(conv);
  259. mbfl_convert_filter_delete(conv);
  260. return true;
  261. }
  262. #define UUDEC(c) (char)(((c)-' ')&077)
  263. #define UU_NEXT(v) \
  264. if (line[x] == '\0' || line[x] == '\r' || line[x] == '\n') break; \
  265. v = line[x++]; v = UUDEC(v)
  266. static size_t mailparse_do_uudecode(File *instream, File *outstream) {
  267. int A, B, C, D, n;
  268. size_t file_size = 0;
  269. if (outstream) {
  270. /* write to outstream */
  271. while (!instream->eof()) {
  272. String line = instream->readLine(128);
  273. if (line.isNull()) break;
  274. int x = 0;
  275. UU_NEXT(n);
  276. while (n) {
  277. UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
  278. if (n-- > 0) {
  279. file_size++;
  280. outstream->putc((A << 2) | (B >> 4));
  281. }
  282. if (n-- > 0) {
  283. file_size++;
  284. outstream->putc((B << 4) | (C >> 2));
  285. }
  286. if (n-- > 0) {
  287. file_size++;
  288. outstream->putc((C << 6) | D);
  289. }
  290. }
  291. }
  292. } else {
  293. /* skip (and measure) the data, but discard it.
  294. * This is separated from the version above to speed it up by a few cycles
  295. */
  296. while (!instream->eof()) {
  297. String line = instream->readLine(128);
  298. if (line.isNull()) break;
  299. int x = 0;
  300. UU_NEXT(n);
  301. while (line[x] && n != 0) {
  302. UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
  303. if (n-- > 0) file_size++;
  304. if (n-- > 0) file_size++;
  305. if (n-- > 0) file_size++;
  306. }
  307. }
  308. }
  309. return file_size;
  310. }
  311. const StaticString
  312. s_filename("filename"),
  313. s_origfilename("origfilename");
  314. Variant f_mailparse_uudecode_all(const Resource& fp) {
  315. File *instream = fp.getTyped<File>();
  316. instream->rewind();
  317. File *outstream = NEWOBJ(TempFile)(false);
  318. Resource deleter(outstream);
  319. Array return_value;
  320. int nparts = 0;
  321. while (!instream->eof()) {
  322. String line = instream->readLine();
  323. if (line.isNull()) break;
  324. /* Look for the "begin " sequence that identifies a uuencoded file */
  325. if (strncmp(line.data(), "begin ", 6) == 0) {
  326. /* parse out the file name.
  327. * The next 4 bytes are an octal number for perms; ignore it */
  328. // TODO: Update gcc and get rid of this dumb workaround.
  329. char *origfilename = (char *)((size_t)line.data() + (10 * sizeof(char)));
  330. /* NUL terminate the filename */
  331. int len = strlen(origfilename);
  332. while (isspace(origfilename[len-1])) {
  333. origfilename[--len] = '\0';
  334. }
  335. /* make the return an array */
  336. if (nparts == 0) {
  337. return_value = Array::Create();
  338. /* create an initial item representing the file with all uuencoded
  339. parts removed */
  340. Array item = Array::Create();
  341. item.set(s_filename, String(((TempFile*)outstream)->getName()));
  342. return_value.append(item);
  343. }
  344. /* add an item */
  345. Array item = Array::Create();
  346. item.set(s_origfilename, String(origfilename, CopyString));
  347. /* create a temp file for the data */
  348. File *partstream = NEWOBJ(TempFile)(false);
  349. Resource deleter(partstream);
  350. if (partstream) {
  351. nparts++;
  352. item.set(s_filename, String(((TempFile*)partstream)->getName()));
  353. return_value.append(item);
  354. /* decode it */
  355. mailparse_do_uudecode(instream, partstream);
  356. }
  357. } else {
  358. /* write to the output file */
  359. outstream->write(line);
  360. }
  361. }
  362. instream->rewind();
  363. if (nparts == 0) {
  364. return false;
  365. }
  366. return return_value;
  367. }
  368. Variant f_mailparse_determine_best_xfer_encoding(const Resource& fp) {
  369. File *stream = fp.getTyped<File>();
  370. stream->rewind();
  371. int linelen = 0;
  372. enum mbfl_no_encoding bestenc = mbfl_no_encoding_7bit;
  373. bool longline = false;
  374. while (!stream->eof()) {
  375. int c = stream->getc();
  376. if (c > 0x80) {
  377. bestenc = mbfl_no_encoding_8bit;
  378. } else if (c == 0) {
  379. bestenc = mbfl_no_encoding_base64;
  380. longline = false;
  381. break;
  382. }
  383. if (c == '\n') {
  384. linelen = 0;
  385. } else if (++linelen > 200) {
  386. longline = true;
  387. }
  388. }
  389. if (longline) bestenc = mbfl_no_encoding_qprint;
  390. stream->rewind();
  391. char * name = (char *)mbfl_no2preferred_mime_name(bestenc);
  392. if (name) {
  393. return String(name, CopyString);
  394. }
  395. return false;
  396. }
  397. ///////////////////////////////////////////////////////////////////////////////
  398. }