Remove String::operator const char*().
[hiphop-php.git] / hphp / runtime / ext / ext_mailparse.cpp
blobeab1d3de8b99e27bf72930c8c6e4e18c322e522e
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 +----------------------------------------------------------------------+
18 #include <runtime/ext/ext_mailparse.h>
19 #include <runtime/base/runtime_option.h>
20 #include <runtime/base/runtime_error.h>
21 #include <runtime/base/file/temp_file.h>
22 #include <runtime/ext/ext_process.h>
23 #include <runtime/ext/mailparse/mime.h>
24 #include <runtime/ext/mailparse/rfc822.h>
26 namespace HPHP {
27 ///////////////////////////////////////////////////////////////////////////////
28 // utility functions
30 /**
31 * Removes whitespaces from the end, and replaces control characters with ' '
32 * from the beginning.
34 static String php_trim(CStrRef str) {
35 string s(str.c_str());
36 unsigned int l = s.length();
37 while (l > 0 && isspace((unsigned char)s[l - 1])) {
38 l--;
40 for (unsigned int i = 0; i < l; i++) {
41 if (iscntrl((unsigned char)s[i])) {
42 if (i + 2 < l && s[i] == '\r' && s[i + 1] == '\n' &&
43 (s[i + 2] == ' ' || s[i + 2] == '\t')) {
44 i += 2;
45 while (i + 1 < l && (s[i + 1] == ' ' || s[i + 1] == '\t')) {
46 i++;
48 continue;
50 s[i] = ' ';
53 return s.substr(0, l);
56 ///////////////////////////////////////////////////////////////////////////////
58 bool php_mail(CStrRef to, CStrRef subject, CStrRef message, CStrRef headers,
59 CStrRef extra_cmd) {
60 // assumes we always have sendmail installed
61 always_assert(!RuntimeOption::SendmailPath.empty());
63 std::ostringstream os;
64 os << RuntimeOption::SendmailPath;
65 if (!extra_cmd.empty()) {
66 os << ' ' << extra_cmd.c_str();
69 errno = 0;
70 FILE *sendmail = popen(os.str().c_str(), "w");
71 if (sendmail == NULL || EACCES == errno) {
72 raise_warning("Unable to execute %s",
73 RuntimeOption::SendmailPath.c_str());
74 return false;
77 fprintf(sendmail, "To: %s\n", to.c_str());
78 fprintf(sendmail, "Subject: %s\n", subject.c_str());
79 if (!headers.empty()) {
80 fprintf(sendmail, "%s\n", headers.c_str());
82 fprintf(sendmail, "\n%s\n", message.c_str());
84 int ret = pclose(sendmail);
85 return (!ret);
88 static const StaticString zero(LITSTR_INIT("\0"));
90 bool f_mail(CStrRef to, CStrRef subject, CStrRef message, CStrRef additional_headers /* = null_string */, CStrRef additional_parameters /* = null_string */) {
91 // replace \0 with spaces
92 String to2 = to.replace(zero, " ");
93 String subject2 = subject.replace(zero, " ");
94 String message2 = message.replace(zero, " ");
95 String headers2;
96 if (!additional_headers.empty()) {
97 headers2 = additional_headers.replace(zero, " ");
99 String params2;
100 if (!additional_parameters.empty()) {
101 params2 = additional_parameters.replace(zero, " ");
104 to2 = php_trim(to2);
105 subject2 = php_trim(subject2);
107 if (!RuntimeOption::MailForceExtraParameters.empty()) {
108 params2 = f_escapeshellcmd(RuntimeOption::MailForceExtraParameters);
109 } else {
110 params2 = f_escapeshellcmd(params2);
113 return php_mail(to2, subject2, message2, headers2, params2);
116 int64_t f_ezmlm_hash(CStrRef addr) {
117 unsigned long h = 5381L;
118 int str_len = addr.length();
119 for (int i = 0; i < str_len; i++) {
120 h = (h + (h << 5)) ^
121 ((unsigned long)(unsigned char)tolower(addr.charAt(i)));
123 h = (h % 53);
124 return (int)h;
127 ///////////////////////////////////////////////////////////////////////////////
128 // mailparse
130 Object f_mailparse_msg_create() {
131 return NEWOBJ(MimePart)();
134 bool f_mailparse_msg_free(CObjRef mimemail) {
135 return true;
138 Variant f_mailparse_msg_parse_file(CStrRef filename) {
139 Variant stream = File::Open(filename, "rb");
140 if (same(stream, false)) return false;
141 File *f = stream.toObject().getTyped<File>();
143 MimePart *p = NEWOBJ(MimePart)();
144 Object ret(p);
145 while (!f->eof()) {
146 String line = f->readLine();
147 if (!line.isNull()) {
148 if (!MimePart::ProcessLine(p, line)) {
149 return false;
153 return ret;
156 bool f_mailparse_msg_parse(CObjRef mimemail, CStrRef data) {
157 return mimemail.getTyped<MimePart>()->parse(data.data(), data.size());
160 Variant f_mailparse_msg_extract_part_file(CObjRef mimemail, CVarRef filename,
161 CVarRef callbackfunc /* = "" */) {
162 return mimemail.getTyped<MimePart>()->
163 extract(filename, callbackfunc,
164 MimePart::Decode8Bit | MimePart::DecodeNoHeaders, true);
167 Variant f_mailparse_msg_extract_whole_part_file(CObjRef mimemail,
168 CVarRef filename,
169 CVarRef callbackfunc /* = "" */) {
170 return mimemail.getTyped<MimePart>()->
171 extract(filename, callbackfunc, MimePart::DecodeNone, true);
174 Variant f_mailparse_msg_extract_part(CObjRef mimemail, CVarRef msgbody,
175 CVarRef callbackfunc /* = "" */) {
176 return mimemail.getTyped<MimePart>()->
177 extract(msgbody, callbackfunc,
178 MimePart::Decode8Bit | MimePart::DecodeNoHeaders, false);
181 Array f_mailparse_msg_get_part_data(CObjRef mimemail) {
182 return mimemail.getTyped<MimePart>()->getPartData();
185 Variant f_mailparse_msg_get_part(CObjRef mimemail, CStrRef mimesection) {
186 Object part = mimemail.getTyped<MimePart>()->findByName(mimesection.c_str());
187 if (part.isNull()) {
188 raise_warning("cannot find section %s in message", mimesection.data());
189 return false;
191 return part;
194 Array f_mailparse_msg_get_structure(CObjRef mimemail) {
195 return mimemail.getTyped<MimePart>()->getStructure();
198 static const StaticString s_display("display");
199 static const StaticString s_address("address");
200 static const StaticString s_is_group("is_group");
202 Array f_mailparse_rfc822_parse_addresses(CStrRef addresses) {
203 php_rfc822_tokenized_t *toks =
204 php_mailparse_rfc822_tokenize(addresses.data(), 1);
205 php_rfc822_addresses_t *addrs = php_rfc822_parse_address_tokens(toks);
207 Array ret = Array::Create();
208 for (int i = 0; i < addrs->naddrs; i++) {
209 Array item = Array::Create();
210 if (addrs->addrs[i].name) {
211 item.set(s_display, String(addrs->addrs[i].name, CopyString));
213 if (addrs->addrs[i].address) {
214 item.set(s_address, String(addrs->addrs[i].address, CopyString));
216 item.set(s_is_group, (bool)addrs->addrs[i].is_group);
217 ret.append(item);
220 php_rfc822_free_addresses(addrs);
221 php_rfc822_tokenize_free(toks);
222 return ret;
225 static int mailparse_stream_output(int c, void *stream) {
226 char buf[2];
227 buf[0] = c;
228 buf[1] = '\0';
229 return ((File*)stream)->write(buf, 1);
231 static int mailparse_stream_flush(void *stream) {
232 return ((File*)stream)->flush() ? 1 : 0;
235 bool f_mailparse_stream_encode(CObjRef sourcefp, CObjRef destfp,
236 CStrRef encoding) {
237 File *srcstream = sourcefp.getTyped<File>(true, true);
238 File *deststream = destfp.getTyped<File>(true, true);
239 if (srcstream == NULL || deststream == NULL) {
240 return false;
243 enum mbfl_no_encoding enc = mbfl_name2no_encoding(encoding.data());
244 if (enc == mbfl_no_encoding_invalid) {
245 raise_warning("Unknown encoding \"%s\"", encoding.data());
246 return false;
249 mbfl_convert_filter *conv =
250 mbfl_convert_filter_new(mbfl_no_encoding_8bit, enc,
251 mailparse_stream_output, mailparse_stream_flush,
252 deststream);
254 if (enc == mbfl_no_encoding_qprint) {
255 /* If the qp encoded section is going to be digitally signed,
256 * it is a good idea to make sure that lines that begin "From "
257 * have the letter F encoded, so that MTAs do not stick a > character
258 * in front of it and invalidate the content/signature */
259 while (!srcstream->eof()) {
260 String line = srcstream->readLine();
261 if (!line.isNull()) {
262 int i;
263 if (strncmp(line.data(), "From ", 5) == 0) {
264 mbfl_convert_filter_flush(conv);
265 deststream->write("=46rom ", 7);
266 i = 5;
267 } else {
268 i = 0;
270 const char *p = line.data();
271 for (; i < line.size(); i++) {
272 mbfl_convert_filter_feed(p[i], conv);
277 } else {
278 while (!srcstream->eof()) {
279 String data = srcstream->read();
280 if (!data.empty()) {
281 const char *p = data.data();
282 for (int i = 0; i < data.size(); i++) {
283 mbfl_convert_filter_feed(p[i], conv);
289 mbfl_convert_filter_flush(conv);
290 mbfl_convert_filter_delete(conv);
291 return true;
294 #define UUDEC(c) (char)(((c)-' ')&077)
295 #define UU_NEXT(v) \
296 if (line[x] == '\0' || line[x] == '\r' || line[x] == '\n') break; \
297 v = line[x++]; v = UUDEC(v)
299 static size_t mailparse_do_uudecode(File *instream, File *outstream) {
300 int A, B, C, D, n;
301 size_t file_size = 0;
302 if (outstream) {
303 /* write to outstream */
304 while (!instream->eof()) {
305 String line = instream->readLine(128);
306 if (line.isNull()) break;
308 int x = 0;
309 UU_NEXT(n);
310 while (n) {
311 UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
312 if (n-- > 0) {
313 file_size++;
314 outstream->putc((A << 2) | (B >> 4));
316 if (n-- > 0) {
317 file_size++;
318 outstream->putc((B << 4) | (C >> 2));
320 if (n-- > 0) {
321 file_size++;
322 outstream->putc((C << 6) | D);
326 } else {
327 /* skip (and measure) the data, but discard it.
328 * This is separated from the version above to speed it up by a few cycles
330 while (!instream->eof()) {
331 String line = instream->readLine(128);
332 if (line.isNull()) break;
334 int x = 0;
335 UU_NEXT(n);
336 while (line[x] && n != 0) {
337 UU_NEXT(A); UU_NEXT(B); UU_NEXT(C); UU_NEXT(D);
338 if (n-- > 0) file_size++;
339 if (n-- > 0) file_size++;
340 if (n-- > 0) file_size++;
344 return file_size;
347 static const StaticString s_filename("filename");
348 static const StaticString s_origfilename("origfilename");
350 Variant f_mailparse_uudecode_all(CObjRef fp) {
351 File *instream = fp.getTyped<File>();
352 instream->rewind();
354 File *outstream = NEWOBJ(TempFile)(false);
355 Object deleter(outstream);
357 Array return_value;
358 int nparts = 0;
359 while (!instream->eof()) {
360 String line = instream->readLine();
361 if (line.isNull()) break;
363 /* Look for the "begin " sequence that identifies a uuencoded file */
364 if (strncmp(line.data(), "begin ", 6) == 0) {
365 /* parse out the file name.
366 * The next 4 bytes are an octal number for perms; ignore it */
367 // TODO: Update gcc and get rid of this dumb workaround.
368 char *origfilename = (char *)((size_t)line.data() + (10 * sizeof(char)));
369 /* NUL terminate the filename */
370 int len = strlen(origfilename);
371 while (isspace(origfilename[len-1])) {
372 origfilename[--len] = '\0';
375 /* make the return an array */
376 if (nparts == 0) {
377 return_value = Array::Create();
378 /* create an initial item representing the file with all uuencoded
379 parts removed */
380 Array item = Array::Create();
381 item.set(s_filename, String(((TempFile*)outstream)->getName()));
382 return_value.append(item);
385 /* add an item */
386 Array item = Array::Create();
387 item.set(s_origfilename, String(origfilename, CopyString));
389 /* create a temp file for the data */
390 File *partstream = NEWOBJ(TempFile)(false);
391 Object deleter(partstream);
392 if (partstream) {
393 nparts++;
394 item.set(s_filename, String(((TempFile*)partstream)->getName()));
395 return_value.append(item);
397 /* decode it */
398 mailparse_do_uudecode(instream, partstream);
400 } else {
401 /* write to the output file */
402 outstream->write(line);
406 instream->rewind();
407 if (nparts == 0) {
408 return false;
410 return return_value;
413 Variant f_mailparse_determine_best_xfer_encoding(CObjRef fp) {
414 File *stream = fp.getTyped<File>();
415 stream->rewind();
417 int linelen = 0;
418 enum mbfl_no_encoding bestenc = mbfl_no_encoding_7bit;
419 bool longline = false;
420 while (!stream->eof()) {
421 int c = stream->getc();
422 if (c > 0x80) {
423 bestenc = mbfl_no_encoding_8bit;
424 } else if (c == 0) {
425 bestenc = mbfl_no_encoding_base64;
426 longline = false;
427 break;
429 if (c == '\n') {
430 linelen = 0;
431 } else if (++linelen > 200) {
432 longline = true;
435 if (longline) bestenc = mbfl_no_encoding_qprint;
436 stream->rewind();
438 char * name = (char *)mbfl_no2preferred_mime_name(bestenc);
439 if (name) {
440 return String(name, CopyString);
442 return false;
445 ///////////////////////////////////////////////////////////////////////////////