/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | | Copyright (c) 1997-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include #include #include #include #include namespace HPHP { /////////////////////////////////////////////////////////////////////////////// // utility functions /** * Removes whitespaces from the end, and replaces control characters with ' ' * from the beginning. */ static String php_trim(CStrRef str) { string s(str.c_str()); unsigned int l = s.length(); while (l > 0 && isspace((unsigned char)s[l - 1])) { l--; } for (unsigned int i = 0; i < l; i++) { if (iscntrl((unsigned char)s[i])) { if (i + 2 < l && s[i] == '\r' && s[i + 1] == '\n' && (s[i + 2] == ' ' || s[i + 2] == '\t')) { i += 2; while (i + 1 < l && (s[i + 1] == ' ' || s[i + 1] == '\t')) { i++; } continue; } s[i] = ' '; } } return s.substr(0, l); } /////////////////////////////////////////////////////////////////////////////// bool php_mail(CStrRef to, CStrRef subject, CStrRef message, CStrRef headers, CStrRef extra_cmd) { // assumes we always have sendmail installed always_assert(!RuntimeOption::SendmailPath.empty()); std::ostringstream os; os << RuntimeOption::SendmailPath; if (!extra_cmd.empty()) { os << ' ' << extra_cmd.c_str(); } errno = 0; FILE *sendmail = popen(os.str().c_str(), "w"); if (sendmail == NULL || EACCES == errno) { raise_warning("Unable to execute %s", RuntimeOption::SendmailPath.c_str()); return false; } fprintf(sendmail, "To: %s\n", to.c_str()); fprintf(sendmail, "Subject: %s\n", subject.c_str()); if (!headers.empty()) { fprintf(sendmail, "%s\n", headers.c_str()); } fprintf(sendmail, "\n%s\n", message.c_str()); int ret = pclose(sendmail); return (!ret); } static const StaticString zero(LITSTR_INIT("\0")); bool f_mail(CStrRef to, CStrRef subject, CStrRef message, CStrRef additional_headers /* = null_string */, CStrRef additional_parameters /* = null_string */) { // replace \0 with spaces String to2 = to.replace(zero, " "); String subject2 = subject.replace(zero, " "); String message2 = message.replace(zero, " "); String headers2; if (!additional_headers.empty()) { headers2 = additional_headers.replace(zero, " "); } String params2; if (!additional_parameters.empty()) { params2 = additional_parameters.replace(zero, " "); } to2 = php_trim(to2); subject2 = php_trim(subject2); if (!RuntimeOption::MailForceExtraParameters.empty()) { params2 = f_escapeshellcmd(RuntimeOption::MailForceExtraParameters); } else { params2 = f_escapeshellcmd(params2); } return php_mail(to2, subject2, message2, headers2, params2); } int64_t f_ezmlm_hash(CStrRef addr) { unsigned long h = 5381L; int str_len = addr.length(); for (int i = 0; i < str_len; i++) { h = (h + (h << 5)) ^ ((unsigned long)(unsigned char)tolower(addr.charAt(i))); } h = (h % 53); return (int)h; } /////////////////////////////////////////////////////////////////////////////// // mailparse Object f_mailparse_msg_create() { return NEWOBJ(MimePart)(); } bool f_mailparse_msg_free(CObjRef mimemail) { return true; } Variant f_mailparse_msg_parse_file(CStrRef filename) { Variant stream = File::Open(filename, "rb"); if (same(stream, false)) return false; File *f = stream.toObject().getTyped(); MimePart *p = NEWOBJ(MimePart)(); Object ret(p); while (!f->eof()) { String line = f->readLine(); if (!line.isNull()) { if (!MimePart::ProcessLine(p, line)) { return false; } } } return ret; } bool f_mailparse_msg_parse(CObjRef mimemail, CStrRef data) { return mimemail.getTyped()->parse(data.data(), data.size()); } Variant f_mailparse_msg_extract_part_file(CObjRef mimemail, CVarRef filename, CVarRef callbackfunc /* = "" */) { return mimemail.getTyped()-> extract(filename, callbackfunc, MimePart::Decode8Bit | MimePart::DecodeNoHeaders, true); } Variant f_mailparse_msg_extract_whole_part_file(CObjRef mimemail, CVarRef filename, CVarRef callbackfunc /* = "" */) { return mimemail.getTyped()-> extract(filename, callbackfunc, MimePart::DecodeNone, true); } Variant f_mailparse_msg_extract_part(CObjRef mimemail, CVarRef msgbody, CVarRef callbackfunc /* = "" */) { return mimemail.getTyped()-> extract(msgbody, callbackfunc, MimePart::Decode8Bit | MimePart::DecodeNoHeaders, false); } Array f_mailparse_msg_get_part_data(CObjRef mimemail) { return mimemail.getTyped()->getPartData(); } Variant f_mailparse_msg_get_part(CObjRef mimemail, CStrRef mimesection) { Object part = mimemail.getTyped()->findByName(mimesection); if (part.isNull()) { raise_warning("cannot find section %s in message", mimesection.data()); return false; } return part; } Array f_mailparse_msg_get_structure(CObjRef mimemail) { return mimemail.getTyped()->getStructure(); } static const StaticString s_display("display"); static const StaticString s_address("address"); static const StaticString s_is_group("is_group"); Array f_mailparse_rfc822_parse_addresses(CStrRef addresses) { php_rfc822_tokenized_t *toks = php_mailparse_rfc822_tokenize(addresses.data(), 1); php_rfc822_addresses_t *addrs = php_rfc822_parse_address_tokens(toks); Array ret = Array::Create(); for (int i = 0; i < addrs->naddrs; i++) { Array item = Array::Create(); if (addrs->addrs[i].name) { item.set(s_display, String(addrs->addrs[i].name, CopyString)); } if (addrs->addrs[i].address) { item.set(s_address, String(addrs->addrs[i].address, CopyString)); } item.set(s_is_group, (bool)addrs->addrs[i].is_group); ret.append(item); } php_rfc822_free_addresses(addrs); php_rfc822_tokenize_free(toks); return ret; } static int mailparse_stream_output(int c, void *stream) { char buf[2]; buf[0] = c; buf[1] = '\0'; return ((File*)stream)->write(buf, 1); } static int mailparse_stream_flush(void *stream) { return ((File*)stream)->flush() ? 1 : 0; } bool f_mailparse_stream_encode(CObjRef sourcefp, CObjRef destfp, CStrRef encoding) { File *srcstream = sourcefp.getTyped(true, true); File *deststream = destfp.getTyped(true, true); if (srcstream == NULL || deststream == NULL) { return false; } enum mbfl_no_encoding enc = mbfl_name2no_encoding(encoding.data()); if (enc == mbfl_no_encoding_invalid) { raise_warning("Unknown encoding \"%s\"", encoding.data()); return false; } mbfl_convert_filter *conv = mbfl_convert_filter_new(mbfl_no_encoding_8bit, enc, mailparse_stream_output, mailparse_stream_flush, deststream); if (enc == mbfl_no_encoding_qprint) { /* If the qp encoded section is going to be digitally signed, * it is a good idea to make sure that lines that begin "From " * have the letter F encoded, so that MTAs do not stick a > character * in front of it and invalidate the content/signature */ while (!srcstream->eof()) { String line = srcstream->readLine(); if (!line.isNull()) { int i; if (strncmp(line.data(), "From ", 5) == 0) { mbfl_convert_filter_flush(conv); deststream->write("=46rom ", 7); i = 5; } else { i = 0; } const char *p = line.data(); for (; i < line.size(); i++) { mbfl_convert_filter_feed(p[i], conv); } } } } else { while (!srcstream->eof()) { String data = srcstream->read(); if (!data.empty()) { const char *p = data.data(); for (int i = 0; i < data.size(); i++) { mbfl_convert_filter_feed(p[i], conv); } } } } mbfl_convert_filter_flush(conv); mbfl_convert_filter_delete(conv); return true; } #define UUDEC(c) (char)(((c)-' ')&077) #define UU_NEXT(v) \ if (line[x] == '\0' || line[x] == '\r' || line[x] == '\n') break; \ v = line[x++]; v = UUDEC(v) static size_t mailparse_do_uudecode(File *instream, File *outstream) { int A, B, C, D, n; size_t file_size = 0; if (outstream) { /* write to outstream */ while (!instream->eof()) { String line = instream->readLine(128); if (line.isNull()) break; int x = 0; UU_NEXT(n); while (n) { UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D); if (n-- > 0) { file_size++; outstream->putc((A << 2) | (B >> 4)); } if (n-- > 0) { file_size++; outstream->putc((B << 4) | (C >> 2)); } if (n-- > 0) { file_size++; outstream->putc((C << 6) | D); } } } } else { /* skip (and measure) the data, but discard it. * This is separated from the version above to speed it up by a few cycles */ while (!instream->eof()) { String line = instream->readLine(128); if (line.isNull()) break; int x = 0; UU_NEXT(n); while (line[x] && n != 0) { UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D); if (n-- > 0) file_size++; if (n-- > 0) file_size++; if (n-- > 0) file_size++; } } } return file_size; } static const StaticString s_filename("filename"); static const StaticString s_origfilename("origfilename"); Variant f_mailparse_uudecode_all(CObjRef fp) { File *instream = fp.getTyped(); instream->rewind(); File *outstream = NEWOBJ(TempFile)(false); Object deleter(outstream); Array return_value; int nparts = 0; while (!instream->eof()) { String line = instream->readLine(); if (line.isNull()) break; /* Look for the "begin " sequence that identifies a uuencoded file */ if (strncmp(line.data(), "begin ", 6) == 0) { /* parse out the file name. * The next 4 bytes are an octal number for perms; ignore it */ // TODO: Update gcc and get rid of this dumb workaround. char *origfilename = (char *)((size_t)line.data() + (10 * sizeof(char))); /* NUL terminate the filename */ int len = strlen(origfilename); while (isspace(origfilename[len-1])) { origfilename[--len] = '\0'; } /* make the return an array */ if (nparts == 0) { return_value = Array::Create(); /* create an initial item representing the file with all uuencoded parts removed */ Array item = Array::Create(); item.set(s_filename, String(((TempFile*)outstream)->getName())); return_value.append(item); } /* add an item */ Array item = Array::Create(); item.set(s_origfilename, String(origfilename, CopyString)); /* create a temp file for the data */ File *partstream = NEWOBJ(TempFile)(false); Object deleter(partstream); if (partstream) { nparts++; item.set(s_filename, String(((TempFile*)partstream)->getName())); return_value.append(item); /* decode it */ mailparse_do_uudecode(instream, partstream); } } else { /* write to the output file */ outstream->write(line); } } instream->rewind(); if (nparts == 0) { return false; } return return_value; } Variant f_mailparse_determine_best_xfer_encoding(CObjRef fp) { File *stream = fp.getTyped(); stream->rewind(); int linelen = 0; enum mbfl_no_encoding bestenc = mbfl_no_encoding_7bit; bool longline = false; while (!stream->eof()) { int c = stream->getc(); if (c > 0x80) { bestenc = mbfl_no_encoding_8bit; } else if (c == 0) { bestenc = mbfl_no_encoding_base64; longline = false; break; } if (c == '\n') { linelen = 0; } else if (++linelen > 200) { longline = true; } } if (longline) bestenc = mbfl_no_encoding_qprint; stream->rewind(); char * name = (char *)mbfl_no2preferred_mime_name(bestenc); if (name) { return String(name, CopyString); } return false; } /////////////////////////////////////////////////////////////////////////////// }