2 +----------------------------------------------------------------------+
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>
27 ///////////////////////////////////////////////////////////////////////////////
31 * Removes whitespaces from the end, and replaces control characters with ' '
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])) {
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')) {
45 while (i
+ 1 < l
&& (s
[i
+ 1] == ' ' || s
[i
+ 1] == '\t')) {
53 return s
.substr(0, l
);
56 ///////////////////////////////////////////////////////////////////////////////
58 bool php_mail(CStrRef to
, CStrRef subject
, CStrRef message
, CStrRef headers
,
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();
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());
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
);
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
, " ");
96 if (!additional_headers
.empty()) {
97 headers2
= additional_headers
.replace(zero
, " ");
100 if (!additional_parameters
.empty()) {
101 params2
= additional_parameters
.replace(zero
, " ");
105 subject2
= php_trim(subject2
);
107 if (!RuntimeOption::MailForceExtraParameters
.empty()) {
108 params2
= f_escapeshellcmd(RuntimeOption::MailForceExtraParameters
);
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
++) {
121 ((unsigned long)(unsigned char)tolower(addr
.charAt(i
)));
127 ///////////////////////////////////////////////////////////////////////////////
130 Object
f_mailparse_msg_create() {
131 return NEWOBJ(MimePart
)();
134 bool f_mailparse_msg_free(CObjRef mimemail
) {
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
)();
146 String line
= f
->readLine();
147 if (!line
.isNull()) {
148 if (!MimePart::ProcessLine(p
, line
)) {
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
,
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());
188 raise_warning("cannot find section %s in message", mimesection
.data());
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
);
220 php_rfc822_free_addresses(addrs
);
221 php_rfc822_tokenize_free(toks
);
225 static int mailparse_stream_output(int c
, void *stream
) {
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
,
237 File
*srcstream
= sourcefp
.getTyped
<File
>(true, true);
238 File
*deststream
= destfp
.getTyped
<File
>(true, true);
239 if (srcstream
== NULL
|| deststream
== NULL
) {
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());
249 mbfl_convert_filter
*conv
=
250 mbfl_convert_filter_new(mbfl_no_encoding_8bit
, enc
,
251 mailparse_stream_output
, mailparse_stream_flush
,
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()) {
263 if (strncmp(line
.data(), "From ", 5) == 0) {
264 mbfl_convert_filter_flush(conv
);
265 deststream
->write("=46rom ", 7);
270 const char *p
= line
.data();
271 for (; i
< line
.size(); i
++) {
272 mbfl_convert_filter_feed(p
[i
], conv
);
278 while (!srcstream
->eof()) {
279 String data
= srcstream
->read();
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
);
294 #define UUDEC(c) (char)(((c)-' ')&077)
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
) {
301 size_t file_size
= 0;
303 /* write to outstream */
304 while (!instream
->eof()) {
305 String line
= instream
->readLine(128);
306 if (line
.isNull()) break;
311 UU_NEXT(A
); UU_NEXT(B
); UU_NEXT(C
); UU_NEXT(D
);
314 outstream
->putc((A
<< 2) | (B
>> 4));
318 outstream
->putc((B
<< 4) | (C
>> 2));
322 outstream
->putc((C
<< 6) | D
);
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;
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
++;
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
>();
354 File
*outstream
= NEWOBJ(TempFile
)(false);
355 Object
deleter(outstream
);
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 */
377 return_value
= Array::Create();
378 /* create an initial item representing the file with all uuencoded
380 Array item
= Array::Create();
381 item
.set(s_filename
, String(((TempFile
*)outstream
)->getName()));
382 return_value
.append(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
);
394 item
.set(s_filename
, String(((TempFile
*)partstream
)->getName()));
395 return_value
.append(item
);
398 mailparse_do_uudecode(instream
, partstream
);
401 /* write to the output file */
402 outstream
->write(line
);
413 Variant
f_mailparse_determine_best_xfer_encoding(CObjRef fp
) {
414 File
*stream
= fp
.getTyped
<File
>();
418 enum mbfl_no_encoding bestenc
= mbfl_no_encoding_7bit
;
419 bool longline
= false;
420 while (!stream
->eof()) {
421 int c
= stream
->getc();
423 bestenc
= mbfl_no_encoding_8bit
;
425 bestenc
= mbfl_no_encoding_base64
;
431 } else if (++linelen
> 200) {
435 if (longline
) bestenc
= mbfl_no_encoding_qprint
;
438 char * name
= (char *)mbfl_no2preferred_mime_name(bestenc
);
440 return String(name
, CopyString
);
445 ///////////////////////////////////////////////////////////////////////////////