PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/runtime/ext/ext_mailparse.cpp

https://github.com/openparallel/hiphop-php
C++ | 438 lines | 336 code | 55 blank | 47 comment | 98 complexity | 2d1d27e9e883f452acd493631266d0bc MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010 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 <runtime/ext/ext_mailparse.h>
  18. #include <runtime/base/runtime_option.h>
  19. #include <runtime/base/runtime_error.h>
  20. #include <runtime/base/file/temp_file.h>
  21. #include <runtime/ext/ext_process.h>
  22. #include <runtime/ext/mailparse/mime.h>
  23. #include <runtime/ext/mailparse/rfc822.h>
  24. using namespace std;
  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(CStrRef str) {
  33. 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(CStrRef to, CStrRef subject, CStrRef message, CStrRef headers,
  55. CStrRef extra_cmd) {
  56. // assumes we always have sendmail installed
  57. assert(!RuntimeOption::SendmailPath.empty());
  58. 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. static const StaticString zero(LITSTR_INIT("\0"));
  80. bool f_mail(CStrRef to, CStrRef subject, CStrRef message, CStrRef additional_headers /* = null_string */, CStrRef additional_parameters /* = null_string */) {
  81. // replace \0 with spaces
  82. String to2 = to.replace(zero, " ");
  83. String subject2 = subject.replace(zero, " ");
  84. String message2 = message.replace(zero, " ");
  85. String headers2;
  86. if (!additional_headers.empty()) {
  87. headers2 = additional_headers.replace(zero, " ");
  88. }
  89. String params2;
  90. if (!additional_parameters.empty()) {
  91. params2 = additional_parameters.replace(zero, " ");
  92. }
  93. to2 = php_trim(to2);
  94. subject2 = php_trim(subject2);
  95. if (!RuntimeOption::MailForceExtraParameters.empty()) {
  96. params2 = f_escapeshellcmd(RuntimeOption::MailForceExtraParameters);
  97. } else {
  98. params2 = f_escapeshellcmd(params2);
  99. }
  100. return php_mail(to2, subject2, message2, headers2, params2);
  101. }
  102. int f_ezmlm_hash(CStrRef addr) {
  103. unsigned long h = 5381L;
  104. int str_len = addr.length();
  105. for (int i = 0; i < str_len; i++) {
  106. h = (h + (h << 5)) ^
  107. ((unsigned long)(unsigned char)tolower(addr.charAt(i)));
  108. }
  109. h = (h % 53);
  110. return (int)h;
  111. }
  112. ///////////////////////////////////////////////////////////////////////////////
  113. // mailparse
  114. Object f_mailparse_msg_create() {
  115. return NEW(MimePart)();
  116. }
  117. bool f_mailparse_msg_free(CObjRef mimemail) {
  118. return true;
  119. }
  120. Variant f_mailparse_msg_parse_file(CStrRef filename) {
  121. Variant stream = File::Open(filename, "rb", Array());
  122. if (same(stream, false)) return false;
  123. File *f = stream.toObject().getTyped<File>();
  124. MimePart *p = NEW(MimePart)();
  125. Object ret(p);
  126. while (!f->eof()) {
  127. String line = f->readLine();
  128. if (!line.isNull()) {
  129. if (!MimePart::ProcessLine(p, line)) {
  130. return false;
  131. }
  132. }
  133. }
  134. return ret;
  135. }
  136. bool f_mailparse_msg_parse(CObjRef mimemail, CStrRef data) {
  137. return mimemail.getTyped<MimePart>()->parse(data.data(), data.size());
  138. }
  139. Variant f_mailparse_msg_extract_part_file(CObjRef mimemail, CVarRef filename,
  140. CVarRef callbackfunc /* = "" */) {
  141. return mimemail.getTyped<MimePart>()->
  142. extract(filename, callbackfunc,
  143. MimePart::Decode8Bit | MimePart::DecodeNoHeaders, true);
  144. }
  145. Variant f_mailparse_msg_extract_whole_part_file(CObjRef mimemail,
  146. CVarRef filename,
  147. CVarRef callbackfunc /* = "" */) {
  148. return mimemail.getTyped<MimePart>()->
  149. extract(filename, callbackfunc, MimePart::DecodeNone, true);
  150. }
  151. Variant f_mailparse_msg_extract_part(CObjRef mimemail, CVarRef msgbody,
  152. CVarRef callbackfunc /* = "" */) {
  153. return mimemail.getTyped<MimePart>()->
  154. extract(msgbody, callbackfunc,
  155. MimePart::Decode8Bit | MimePart::DecodeNoHeaders, false);
  156. }
  157. Array f_mailparse_msg_get_part_data(CObjRef mimemail) {
  158. return mimemail.getTyped<MimePart>()->getPartData();
  159. }
  160. Variant f_mailparse_msg_get_part(CObjRef mimemail, CStrRef mimesection) {
  161. Object part = mimemail.getTyped<MimePart>()->findByName(mimesection);
  162. if (part.isNull()) {
  163. raise_warning("cannot find section %s in message", mimesection.data());
  164. return false;
  165. }
  166. return part;
  167. }
  168. Array f_mailparse_msg_get_structure(CObjRef mimemail) {
  169. return mimemail.getTyped<MimePart>()->getStructure();
  170. }
  171. Array f_mailparse_rfc822_parse_addresses(CStrRef addresses) {
  172. php_rfc822_tokenized_t *toks =
  173. php_mailparse_rfc822_tokenize(addresses.data(), 1);
  174. php_rfc822_addresses_t *addrs = php_rfc822_parse_address_tokens(toks);
  175. Array ret = Array::Create();
  176. for (int i = 0; i < addrs->naddrs; i++) {
  177. Array item = Array::Create();
  178. if (addrs->addrs[i].name) {
  179. item.set("display", String(addrs->addrs[i].name, CopyString));
  180. }
  181. if (addrs->addrs[i].address) {
  182. item.set("address", String(addrs->addrs[i].address, CopyString));
  183. }
  184. item.set("is_group", (bool)addrs->addrs[i].is_group);
  185. ret.append(item);
  186. }
  187. php_rfc822_free_addresses(addrs);
  188. php_rfc822_tokenize_free(toks);
  189. return ret;
  190. }
  191. static int mailparse_stream_output(int c, void *stream) {
  192. char buf = c;
  193. return ((File*)stream)->write(&buf, 1);
  194. }
  195. static int mailparse_stream_flush(void *stream) {
  196. return ((File*)stream)->flush() ? 1 : 0;
  197. }
  198. bool f_mailparse_stream_encode(CObjRef sourcefp, CObjRef destfp,
  199. CStrRef encoding) {
  200. File *srcstream = sourcefp.getTyped<File>(true, true);
  201. File *deststream = destfp.getTyped<File>(true, true);
  202. if (srcstream == NULL || deststream == NULL) {
  203. return false;
  204. }
  205. enum mbfl_no_encoding enc = mbfl_name2no_encoding(encoding.data());
  206. if (enc == mbfl_no_encoding_invalid) {
  207. raise_warning("Unknown encoding \"%s\"", encoding.data());
  208. return false;
  209. }
  210. mbfl_convert_filter *conv =
  211. mbfl_convert_filter_new(mbfl_no_encoding_8bit, enc,
  212. mailparse_stream_output, mailparse_stream_flush,
  213. deststream);
  214. if (enc == mbfl_no_encoding_qprint) {
  215. /* If the qp encoded section is going to be digitally signed,
  216. * it is a good idea to make sure that lines that begin "From "
  217. * have the letter F encoded, so that MTAs do not stick a > character
  218. * in front of it and invalidate the content/signature */
  219. while (!srcstream->eof()) {
  220. String line = srcstream->readLine();
  221. if (!line.isNull()) {
  222. int i;
  223. if (strncmp(line.data(), "From ", 5) == 0) {
  224. mbfl_convert_filter_flush(conv);
  225. deststream->write("=46rom ", 7);
  226. i = 5;
  227. } else {
  228. i = 0;
  229. }
  230. const char *p = line.data();
  231. for (; i < line.size(); i++) {
  232. mbfl_convert_filter_feed(p[i], conv);
  233. }
  234. }
  235. }
  236. } else {
  237. while (!srcstream->eof()) {
  238. String data = srcstream->read();
  239. if (!data.empty()) {
  240. const char *p = data.data();
  241. for (int i = 0; i < data.size(); i++) {
  242. mbfl_convert_filter_feed(p[i], conv);
  243. }
  244. }
  245. }
  246. }
  247. mbfl_convert_filter_flush(conv);
  248. mbfl_convert_filter_delete(conv);
  249. return true;
  250. }
  251. #define UUDEC(c) (char)(((c)-' ')&077)
  252. #define UU_NEXT(v) \
  253. if (line[x] == '\0' || line[x] == '\r' || line[x] == '\n') break; \
  254. v = line[x++]; v = UUDEC(v)
  255. static size_t mailparse_do_uudecode(File *instream, File *outstream) {
  256. int A, B, C, D, n;
  257. size_t file_size = 0;
  258. if (outstream) {
  259. /* write to outstream */
  260. while (!instream->eof()) {
  261. String line = instream->readLine(128);
  262. if (line.isNull()) break;
  263. int x = 0;
  264. UU_NEXT(n);
  265. while (n) {
  266. UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
  267. if (n-- > 0) {
  268. file_size++;
  269. outstream->putc((A << 2) | (B >> 4));
  270. }
  271. if (n-- > 0) {
  272. file_size++;
  273. outstream->putc((B << 4) | (C >> 2));
  274. }
  275. if (n-- > 0) {
  276. file_size++;
  277. outstream->putc((C << 6) | D);
  278. }
  279. }
  280. }
  281. } else {
  282. /* skip (and measure) the data, but discard it.
  283. * This is separated from the version above to speed it up by a few cycles
  284. */
  285. while (!instream->eof()) {
  286. String line = instream->readLine(128);
  287. if (line.isNull()) break;
  288. int x = 0;
  289. UU_NEXT(n);
  290. while (line[x] && n != 0) {
  291. UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
  292. if (n-- > 0) file_size++;
  293. if (n-- > 0) file_size++;
  294. if (n-- > 0) file_size++;
  295. }
  296. }
  297. }
  298. return file_size;
  299. }
  300. Variant f_mailparse_uudecode_all(CObjRef fp) {
  301. File *instream = fp.getTyped<File>();
  302. instream->rewind();
  303. File *outstream = NEW(TempFile)(false);
  304. Object deleter(outstream);
  305. Array return_value;
  306. int nparts = 0;
  307. while (!instream->eof()) {
  308. String line = instream->readLine();
  309. if (line.isNull()) break;
  310. /* Look for the "begin " sequence that identifies a uuencoded file */
  311. if (strncmp(line.data(), "begin ", 6) == 0) {
  312. /* parse out the file name.
  313. * The next 4 bytes are an octal number for perms; ignore it */
  314. char *origfilename = (char*)line.data() + 10;
  315. /* NUL terminate the filename */
  316. int len = strlen(origfilename);
  317. while (isspace(origfilename[len-1])) {
  318. origfilename[--len] = '\0';
  319. }
  320. /* make the return an array */
  321. if (nparts == 0) {
  322. return_value = Array::Create();
  323. /* create an initial item representing the file with all uuencoded
  324. parts removed */
  325. Array item = Array::Create();
  326. item.set("filename", String(((TempFile*)outstream)->getName()));
  327. return_value.append(item);
  328. }
  329. /* add an item */
  330. Array item = Array::Create();
  331. item.set("origfilename", String(origfilename, CopyString));
  332. /* create a temp file for the data */
  333. File *partstream = NEW(TempFile)(false);
  334. Object deleter(partstream);
  335. if (partstream) {
  336. nparts++;
  337. item.set("filename", String(((TempFile*)partstream)->getName()));
  338. return_value.append(item);
  339. /* decode it */
  340. mailparse_do_uudecode(instream, partstream);
  341. }
  342. } else {
  343. /* write to the output file */
  344. outstream->write(line);
  345. }
  346. }
  347. instream->rewind();
  348. if (nparts == 0) {
  349. return false;
  350. }
  351. return return_value;
  352. }
  353. Variant f_mailparse_determine_best_xfer_encoding(CObjRef fp) {
  354. File *stream = fp.getTyped<File>();
  355. stream->rewind();
  356. int linelen = 0;
  357. enum mbfl_no_encoding bestenc = mbfl_no_encoding_7bit;
  358. bool longline = false;
  359. while (!stream->eof()) {
  360. int c = stream->getc();
  361. if (c > 0x80) {
  362. bestenc = mbfl_no_encoding_8bit;
  363. } else if (c == 0) {
  364. bestenc = mbfl_no_encoding_base64;
  365. longline = false;
  366. break;
  367. }
  368. if (c == '\n') {
  369. linelen = 0;
  370. } else if (++linelen > 200) {
  371. longline = true;
  372. }
  373. }
  374. if (longline) bestenc = mbfl_no_encoding_qprint;
  375. stream->rewind();
  376. char * name = (char *)mbfl_no2preferred_mime_name(bestenc);
  377. if (name) {
  378. return String(name, CopyString);
  379. }
  380. return false;
  381. }
  382. ///////////////////////////////////////////////////////////////////////////////
  383. }