Annotation of parser3/src/types/pa_vmail.C, revision 1.40.2.12
1.1 paf 1: /** @file
2: Parser: @b mail class.
3: relies on gmime library, by Jeffrey Stedfast <fejj@helixcode.com>
4:
1.40.2.3 paf 5: Copyright(c) 2001-2003 ArtLebedev Group(http://www.artlebedev.com)
1.1 paf 6: Author: Alexandr Petrosian <paf@design.ru>(http://paf.design.ru)
7: */
1.13 paf 8:
1.40.2.12! paf 9: static const char* IDENT_VMAIL_C="$Date: 2003/03/08 12:30:28 $";
1.1 paf 10:
11: #include "pa_sapi.h"
12: #include "pa_vmail.h"
13: #include "pa_vstring.h"
14: #include "pa_request.h"
15: #include "pa_common.h"
16: #include "pa_charset.h"
17: #include "pa_charsets.h"
18: #include "pa_vdate.h"
19: #include "pa_vfile.h"
20: #include "pa_uue.h"
21:
1.3 paf 22: #ifdef WITH_MAILRECEIVE
1.4 paf 23: extern "C" {
1.1 paf 24: #include "gmime.h"
1.4 paf 25: }
1.40.2.12! paf 26:
! 27: #include "pa_charsets.h"
1.1 paf 28: #endif
29:
30: // defines
31:
32: #define RAW_NAME "raw"
33:
34: // internals
35:
36: enum PartType {
37: P_TEXT,
38: P_HTML,
39: P_FILE,
40: P_MESSAGE,
41:
42: P_TYPES_COUNT
43: };
44:
1.40.2.3 paf 45: static const char* const part_name_starts[P_TYPES_COUNT]={"text", "html", "file", "message"};
1.1 paf 46:
1.40.2.5 paf 47: // defines for statics
48:
49: #define FORMAT_NAME "format"
50: #define CHARSET_NAME "charset"
51:
52: // statics
53:
54: static StringPtr format_name(new String(FORMAT_NAME));
55: static StringPtr charset_name(new String(CHARSET_NAME));
56:
1.1 paf 57: // VMail
58:
1.40.2.7 paf 59: extern MethodedPtr mail_base_class;
1.1 paf 60:
1.40.2.5 paf 61: VMail::VMail(): VStateless_class(StringPtr(0), mail_base_class.get()) {}
1.1 paf 62:
1.3 paf 63: #ifdef WITH_MAILRECEIVE
1.1 paf 64:
1.40.2.12! paf 65: static StringPtr maybeUpperCase(Pool& pool, Charset& source_charset,
! 66: StringPtr src, bool toUpperCase) {
! 67: return toUpperCase?src->change_case(pool, source_charset, String::CC_UPPER):src;
1.1 paf 68: }
69:
1.40.2.12! paf 70: static void UTF8toSource(Pool& pool, Charset& source_charset,
! 71: const char* source_body, size_t source_content_length,
1.1 paf 72: const void *& dest_body, size_t& dest_content_length) {
73: if(source_body) {
74: if(!source_content_length)
75: source_content_length=strlen(source_body);
1.40.2.12! paf 76: Charset::transcode(pool,
! 77: *UTF8_charset, source_body, source_content_length,
! 78: source_charset, dest_body, dest_content_length);
1.1 paf 79: } else {
80: dest_body=0;
81: dest_content_length=0;
82: }
83: }
84:
1.40.2.12! paf 85: static void putReceived(Pool& pool, Charset& source_charset,
! 86: HashStringValue& received,
! 87: const char* name, ValuePtr value,
! 88: bool nameToUpperCase=false) {
! 89:
1.1 paf 90: if(name && value) {
91: received.put(
1.40.2.12! paf 92: maybeUpperCase(pool, source_charset,
! 93: StringPtr(new String(pool.copy(name), 0, true/*tainted*/)), nameToUpperCase),
1.1 paf 94: value);
95: }
96: }
97:
1.40.2.12! paf 98: static void putReceived(Pool& pool, Charset& source_charset,
! 99: HashStringValue& received,
! 100: const char* name, const char* value, size_t value_size=0,
! 101: bool nameToUpperCase=false) {
1.1 paf 102: if(value) {
103: const void *value_dest_body;
104: size_t value_dest_content_length;
1.21 paf 105: // UTF8toSource(pool, value, value_size, value_dest_body, value_dest_content_length);
106: value_dest_body=value;
107: value_dest_content_length=value_size;
1.1 paf 108:
1.40.2.12! paf 109: putReceived(pool, source_charset,
! 110: received,
! 111: name,
! 112: ValuePtr(new VString(StringPtr(new String(
! 113: pool.copy((const char* )value_dest_body), value_dest_content_length, true/*tainted*/))))
! 114: );
1.1 paf 115: }
116: }
117:
1.40.2.12! paf 118: static void putReceived(Pool& pool, HashStringValue& received, const char* name, time_t value) {
1.1 paf 119: if(name)
1.40.2.12! paf 120: received.put(
! 121: StringPtr(new String(pool.copy(name), 0, true/*tainted*/)),
! 122: ValuePtr(new VDate(value))
! 123: );
1.1 paf 124: }
125:
1.40.2.12! paf 126: #ifndef DOXYGEN
! 127: struct MimeHeaderField2received_info {
! 128: Pool* pool;
! 129: Charset* source_charset;
! 130: HashStringValue* received;
! 131: };
! 132: #endif
1.40.2.3 paf 133: static void MimeHeaderField2received(const char* name, const char* value, gpointer data) {
1.40.2.12! paf 134: MimeHeaderField2received_info& info=*static_cast<MimeHeaderField2received_info *>(data);
1.1 paf 135:
1.40.2.12! paf 136: putReceived(*info.pool, *info.source_charset,
! 137: *info.received,
! 138: name, value, 0, true/*nameInUpperCase*/);
1.1 paf 139: }
140:
1.40.2.12! paf 141: static void parse(Request& r, GMimeStream *stream, HashStringValue& received);
1.1 paf 142:
143: #ifndef DOXYGEN
1.40.2.12! paf 144: struct MimePart2body_info {
! 145: Request* r;
! 146: HashStringValue* body;
1.1 paf 147: int partCounts[P_TYPES_COUNT];
148: };
149: #endif
1.27 paf 150: /// @test why no copy to global in P_HTML putReceived?
1.1 paf 151: static void MimePart2body(GMimePart *part,
152: gpointer data) {
1.40.2.12! paf 153: MimePart2body_info& info=*static_cast<MimePart2body_info *>(data);
! 154: Pool& pool=info.r->pool();
! 155: Charset& source_charset=info.r->charsets.source();
1.1 paf 156:
157: if(const GMimeContentType *type=g_mime_part_get_content_type(part)) {
158: if(g_mime_content_type_is_type(type, "multipart", "*"))
159: return; // skipping frames
160:
161: PartType partType;
162: if(g_mime_content_type_is_type(type, "text", "plain"))
163: partType=P_TEXT;
164: else if(g_mime_content_type_is_type(type, "text", "html"))
165: partType=P_HTML;
166: else if(g_mime_content_type_is_type(type, "message", "*"))
167: partType=P_MESSAGE;
168: else
169: partType=P_FILE;
170:
171: // partName
1.40.2.3 paf 172: const char* partName;
1.1 paf 173: char partNameBuf[MAX_STRING];
1.40.2.3 paf 174: const char* partNameStart=part_name_starts[partType];
1.40.2.12! paf 175: if(int partNo=info.partCounts[partType]++) {
1.1 paf 176: snprintf(partNameBuf, MAX_STRING, "%s%d", partNameStart, partNo);
177: partName=partNameBuf;
178: } else
179: partName=partNameStart;
180:
181: // $.partX[
1.40.2.12! paf 182: VHashPtr vpartX(new VHash); putReceived(pool, *info.body, partName, vpartX);
! 183: HashStringValue& partX=vpartX->hash(Exception::undefined_source);
1.1 paf 184: {
185: // $.raw[
1.40.2.12! paf 186: VHashPtr vraw(new VHash); putReceived(pool, partX, RAW_NAME, vraw);
! 187: MimeHeaderField2received_info hfr_info={
! 188: &pool, &source_charset, &vraw->hash(Exception::undefined_source)};
! 189: g_mime_header_foreach(part->headers, MimeHeaderField2received, &hfr_info);
1.1 paf 190: }
1.40.2.3 paf 191: const char* content_filename=0;
1.1 paf 192: {
193: // $.content-type[
1.40.2.12! paf 194: VHashPtr vcontent_type(new VHash); putReceived(pool, partX, "content-type", vcontent_type);
! 195: HashStringValue& content_type=vcontent_type->hash(Exception::undefined_source);
1.1 paf 196: {
197: // $.value[text/plain]
198: char value[MAX_STRING];
199: snprintf(value, MAX_STRING, "%s/%s",
200: type->type?type->type:"x-unknown",
201: type->subtype?type->subtype:"x-unknown");
1.40.2.12! paf 202: putReceived(pool, source_charset, content_type, VALUE_NAME, value);
1.1 paf 203: }
204: GMimeParam *param=type->params;
205: while(param) {
206: // $.charset[windows-1251] && co
1.40.2.12! paf 207: putReceived(pool, source_charset, content_type, param->name, param->value, true);
1.1 paf 208: if(strcasecmp(param->name, "name")==0)
209: content_filename=param->value;
210: param=param->next;
211: }
212: }
213: // $.description
1.40.2.12! paf 214: putReceived(pool, source_charset, partX, "description", part->description);
1.1 paf 215: // $.content-id
1.40.2.12! paf 216: putReceived(pool, source_charset, partX, "content-id", part->content_id);
1.1 paf 217: // $.content-md5
1.40.2.12! paf 218: putReceived(pool, source_charset, partX, "content-md5", part->content_md5);
1.1 paf 219: // $.content-location
1.40.2.12! paf 220: putReceived(pool, source_charset, partX, "content-location", part->content_location);
1.1 paf 221:
222: // todo GMimePart:
223: // GMimePartEncodingType encoding;
224: // GMimeDisposition *disposition;
225: if(part->disposition) {
226: GMimeParam *param=part->disposition->params;
227: while(param) {
228: // $.charset[windows-1251] && co
229: if(strcasecmp(param->name, "filename")==0)
230: content_filename=param->value;
231: param=param->next;
232: }
233: }
234:
235: // MESSAGE
236: if(partType==P_MESSAGE) {
237: if(part->content)
238: if(GMimeStream *stream=part->content->stream)
1.40.2.12! paf 239: parse(*info.r, stream, partX);
1.1 paf 240: } else {
241: // $.value[string|file]
242: size_t buf_len;
1.40.2.12! paf 243: const char* local_buf=(const char*)g_mime_part_get_content(part, &buf_len);
1.1 paf 244: if(partType==P_FILE) {
1.40.2.12! paf 245: VFilePtr vfile(new VFile);
! 246: char *pooled_buf=pool.copy(local_buf, buf_len);
! 247: VStringPtr vcontent_type(content_filename?
! 248: new VString(info.r->mime_type_of(content_filename)):0);
! 249: vfile->set(pool, true/*tainted*/, pooled_buf, buf_len, content_filename, vcontent_type);
! 250: putReceived(pool, partX, VALUE_NAME, vfile);
1.1 paf 251: } else {
252: // P_TEXT, P_HTML
1.40.2.12! paf 253: putReceived(pool, source_charset, partX, VALUE_NAME,(const char*)local_buf, buf_len);
1.1 paf 254: }
255: }
256: }
257: }
258:
1.30 paf 259: int gmt_offset() {
260: #if defined(HAVE_TIMEZONE) && defined(HAVE_DAYLIGHT)
1.31 paf 261: return timezone+(daylight?60*60*(timezone<0?-1:timezone>0?+1:0):0);
1.30 paf 262: #else
263: time_t t=time(0);
264: tm *tm=localtime(&t);
265: #if defined(HAVE_TM_GMTOFF)
266: return tm->tm_gmtoff;
267: #elif defined(HAVE_TM_TZADJ)
268: return tm->tm_tzadj;
269: #else
270: #error neither HAVE_TIMEZONE&HAVE_DAYIGHT nor HAVE_TM_GMTOFF nor HAVE_TM_TZADJ defined
271: #endif
272: #endif
273: }
274:
1.40.2.12! paf 275: static void parse(Request& r, GMimeStream *stream, HashStringValue& received) {
! 276: Pool& pool=r.pool();
! 277: Charset& source_charset=r.charsets.source();
1.1 paf 278:
279: GMimeMessage *message=g_mime_parser_construct_message(stream);
280: try {
281: const GMimeMessageHeader *messageHeader=message->header;
282: if(!messageHeader)
283: return;
284:
285: // firstly user-defined strings go
286: // user headers
287: {
288: // $.raw[
1.40.2.12! paf 289: VHashPtr vraw(new VHash); putReceived(pool, received, RAW_NAME, vraw);
! 290: MimeHeaderField2received_info hfr_info={
! 291: &pool, &source_charset, &vraw->hash(Exception::undefined_source)};
! 292: g_mime_header_foreach(messageHeader->headers, MimeHeaderField2received, &hfr_info);
1.1 paf 293: }
294:
295: // maybe-todo-recipients
296: // x(messageHeader->recipients)
297:
298: // secondly standard headers&body go
299: // standard header
300: // .from
1.40.2.12! paf 301: putReceived(pool, source_charset, received, "from", messageHeader->from);
1.1 paf 302: // .reply-to
1.40.2.12! paf 303: putReceived(pool, source_charset, received, "reply-to", messageHeader->reply_to);
1.1 paf 304: // .to
305: // todo: messageHeader->recipients
306: // .subject
1.40.2.12! paf 307: putReceived(pool, source_charset, received, "subject", messageHeader->subject);
1.1 paf 308: // .date(date+gmt_offset)
309: int tt_offset =
310: ((messageHeader->gmt_offset / 100) *(60 * 60))
311: +(messageHeader->gmt_offset % 100) * 60;
1.30 paf 312:
1.40.2.12! paf 313: putReceived(pool, received, "date",
1.1 paf 314: messageHeader->date // local sender
315: -tt_offset // move local sender to GMT sender
1.30 paf 316: -gmt_offset() // move GMT sender to our local time
1.1 paf 317: );
318: // .message-id
1.40.2.12! paf 319: putReceived(pool, source_charset, received, "message-id", messageHeader->message_id);
1.1 paf 320:
321: // .body[part/parts
322: GMimePart *part=message->mime_part;
323: const GMimeContentType *type=g_mime_part_get_content_type(part);
1.40.2.12! paf 324: MimePart2body_info info={&r, &received};
1.1 paf 325: g_mime_part_foreach(part, MimePart2body, &info);
326:
327: // normal unref
328: g_mime_object_unref(GMIME_OBJECT(message));
329: } catch(...) {
330: // abnormal unref
331: g_mime_object_unref(GMIME_OBJECT(message));
332: }
333: }
334: #endif
335:
336:
337:
1.40.2.12! paf 338: void VMail::fill_received(Request& r) {
1.1 paf 339: // store letter to received
1.3 paf 340: #ifdef WITH_MAILRECEIVE
1.40.2.12! paf 341: if(r.request_info.mail_received) {
1.1 paf 342: // init
343: g_mime_init(GMIME_INIT_FLAG_UTF8);
344:
345: // create stream with CRLF filter
346: GMimeStream *stream = g_mime_stream_fs_new(fileno(stdin));
347: GMimeStream *istream = g_mime_stream_filter_new_with_stream(stream);
348: GMimeFilter *filter = g_mime_filter_crlf_new(GMIME_FILTER_CRLF_DECODE, GMIME_FILTER_CRLF_MODE_CRLF_ONLY);
349: g_mime_stream_filter_add(GMIME_STREAM_FILTER(istream), filter);
350: g_mime_stream_unref(stream);
351: stream = istream;
352: try {
353: // parse incoming stream
1.40.2.12! paf 354: parse(r, stream, vreceived->hash(Exception::undefined_source));
1.1 paf 355: // normal stream free
356: g_mime_stream_unref(stream);
357: } catch(...) {
358: // abnormal stream free
359: g_mime_stream_unref(stream);
360: }
361: }
362: #endif
363: }
364:
1.9 paf 365: typedef int (*string_contains_char_which_check)(int);
1.40.2.3 paf 366: static bool string_contains_char_which(const char* string, string_contains_char_which_check check) {
1.9 paf 367: while(char c=*string++) {
368: if(check(c))
369: return true;
370: }
371: return false;
372: }
1.32 paf 373: static char *trimBoth(char *s) {
374: // sanity check
375: if(!s)
376: return 0;
377:
378: // trim head whitespace
379: while(*s && isspace(*s))
380: s++;
381: // trim tail whitespace
382: char *tail=s+strlen(s);
383: if(tail>s) {
384: do {
385: --tail;
386: if(isspace(*tail))
387: *tail=0;
388: } while(tail>s);
389: }
390: // return it
391: return s;
392: }
1.40.2.5 paf 393: static void extractEmail(StringPtr result, char *email, StringPtr origin_string) {
1.32 paf 394: email=trimBoth(email);
1.40.2.5 paf 395: result->APPEND_TAINTED(email, 0, origin_string->origin().file, origin_string->origin().line);
1.9 paf 396:
397: /*
398: http://www.faqs.org/rfcs/rfc822.html
399:
400: addr-spec = local-part "@" domain ; global address
401:
402: local-part = word *("." word) ; uninterpreted case-preserved
403: word = atom / quoted-string
404:
405: domain = sub-domain *("." sub-domain)
406: sub-domain = domain-ref / domain-literal
407: domain-ref = atom ; symbolic reference
408:
409: domain-literal << ignoring for now
410: quoted-string in word << ignoring for now
411:
412: atom = 1*<any CHAR except specials, SPACE and CTLs> << the ONLY to check
413:
414: specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted-
415: / "," / ";" / ":" / "\" / <"> ; string, to use
416: / "." / "[" / "]" ; within a word.
417:
418: */
1.40.2.3 paf 419: const char* exception_type="email.format";
1.9 paf 420: if(strpbrk(email, "()<>,;:\\\"[]"/*specials minus @ and . */))
1.17 paf 421: throw Exception(exception_type,
1.40.2.5 paf 422: result,
1.32 paf 423: "email contains bad characters (specials)");
1.9 paf 424: if(string_contains_char_which(email, (string_contains_char_which_check)isspace))
1.17 paf 425: throw Exception(exception_type,
1.40.2.5 paf 426: result,
1.32 paf 427: "email contains bad characters (whitespace)");
1.9 paf 428: if(string_contains_char_which(email, (string_contains_char_which_check)iscntrl))
1.17 paf 429: throw Exception(exception_type,
1.40.2.5 paf 430: result,
1.32 paf 431: "email contains bad characters (control)");
1.40.2.5 paf 432: if(result->is_empty())
1.17 paf 433: throw Exception(exception_type,
1.40.2.5 paf 434: origin_string,
1.16 paf 435: "email is empty");
1.39 paf 436: }
437:
1.40.2.5 paf 438: /*nonstatic*/StringPtr extractEmails(Pool& pool, StringPtr string) { // used in classes/mail.C, which supported for backward compatibility
439: char *emails=string->cstr(pool);
440: StringPtr result(new String);
1.39 paf 441: while(char *email=lsplit(&emails, ',')) {
442: rsplit(email, '>');
443: if(char *in_brackets=lsplit(email, '<'))
444: email=in_brackets;
1.40.2.5 paf 445: if(!result->is_empty())
446: *result<<",";
1.39 paf 447: extractEmail(result, email, string);
448: }
1.9 paf 449:
450: return result;
451: }
1.40.2.5 paf 452:
453: #ifndef DOXYGEN
454: struct Store_message_element_info {
1.40.2.10 paf 455: Pool& pool;
456: Request_charsets& charsets;
457: StringPtr& header;
458: StringPtr& from;
459: StringPtr& to;
460: bool extract_to; StringPtr errors_to;
1.40.2.5 paf 461: bool mime_version_specified;
462: ArrayValuePtr parts[P_TYPES_COUNT];
463: int parts_count;
464: bool has_content_type;
1.40.2.10 paf 465:
466: Store_message_element_info(
467: Pool& apool,
468: Request_charsets& acharsets,
469: StringPtr& aheader,
470: StringPtr& afrom,
471: bool aextract_to, StringPtr& ato
472: ):
473: pool(apool),
474: charsets(acharsets),
475: header(aheader),
476: from(afrom),
477: extract_to(aextract_to), to(ato),
478: mime_version_specified(false),
479: parts_count(0),
480: has_content_type(false) {
481: }
1.40.2.5 paf 482: };
483: #endif
484: static void store_message_element(
485: HashStringValue::key_type raw_element_name,
486: HashStringValue::value_type element_value,
487: Store_message_element_info *info) {
1.40.2.10 paf 488: StringPtr low_element_name=raw_element_name->change_case(info->pool,
489: info->charsets.source(), String::CC_LOWER);
1.1 paf 490:
491: // exclude internals
1.40.2.5 paf 492: if(*low_element_name==CHARSET_NAME
493: || *low_element_name==VALUE_NAME
494: || *low_element_name==RAW_NAME
495: || *low_element_name=="date")
1.1 paf 496: return;
497:
498: // grep parts
499: for(int pt=0; pt<P_TYPES_COUNT; pt++) {
1.40.2.5 paf 500: if(low_element_name->starts_with(part_name_starts[pt])) {
1.29 paf 501: // check that $.message# '#' is digit
502: size_t start_len=strlen(part_name_starts[pt]);
1.40.2.5 paf 503: if(low_element_name->size()>start_len) {
504: CharPtr at_num=low_element_name->mid(start_len, start_len+1)->cstr();
1.29 paf 505: if(!isdigit(*at_num))
506: continue;
507: }
1.40.2.5 paf 508: *info->parts[pt]+=element_value;
509: info->parts_count++;
1.1 paf 510: return;
511: }
512: }
513:
1.39 paf 514: bool skip=false;
515:
1.10 paf 516: // fetch some special headers
1.40.2.5 paf 517: if(*low_element_name=="from")
1.40.2.10 paf 518: info->from=extractEmails(info->pool, element_value->as_string(&info->pool));
519: if(info->extract_to) { // defined only on WIN32, see mail.C [collecting info for RCPT to-s]
1.40.2.5 paf 520: bool is_to=*low_element_name=="to" ;
521: bool is_cc=*low_element_name=="cc" ;
522: bool is_bcc=*low_element_name=="bcc" ;
1.39 paf 523: if(is_to||is_cc||is_bcc) {
1.40.2.5 paf 524: if(!info->to)
525: info->to=StringPtr(new String);
1.39 paf 526: else
1.40.2.5 paf 527: *info->to << ",";
1.40.2.10 paf 528: *info->to << extractEmails(info->pool, element_value->as_string(&info->pool));
1.39 paf 529: }
530:
531: if(is_bcc) // blinding it
532: skip=true;
533: }
1.40.2.5 paf 534: if(*low_element_name=="errors-to")
1.40.2.10 paf 535: info->errors_to=extractEmails(info->pool, element_value->as_string(&info->pool));
1.40.2.5 paf 536: if(*low_element_name=="mime-version")
537: info->mime_version_specified=true;
1.1 paf 538:
1.39 paf 539: if(!skip) {
1.40.2.10 paf 540: StringPtr source_line=attributed_meaning_to_string(info->pool, element_value);
541: const char *source_line_cstr=source_line->cstr(info->pool);
1.40.2.5 paf 542: const void *mail_ptr; size_t mail_size;
1.40.2.10 paf 543: Charset::transcode(info->pool,
544: info->charsets.source(), source_line_cstr, source_line->size(),
545: info->charsets.mail(), mail_ptr, mail_size);
1.40.2.5 paf 546: StringPtr mail_line(new String);
547: mail_line->APPEND((const char*)mail_ptr, mail_size, String::UL_MAIL_HEADER,
548: source_line->origin().file,
549: source_line->origin().line
550: );
1.40.2.1 paf 551:
1.40.2.5 paf 552: // append header line
553: *info->header
554: << raw_element_name
1.40.2.10 paf 555: << ":" << mail_line->cstr(info->pool, String::UL_UNSPECIFIED, 0, &info->charsets)
1.40.2.5 paf 556: << "\n";
1.39 paf 557: }
1.1 paf 558:
559: // has content type?
1.40.2.5 paf 560: if(*low_element_name==CONTENT_TYPE_NAME)
561: info->has_content_type=true;
1.1 paf 562: }
563:
1.40.2.5 paf 564: static StringPtr file_value_to_string(Request& r, StringPtr source,
565: ValuePtr send_value) {
1.1 paf 566: Pool& pool=r.pool();
1.40.2.5 paf 567: VFilePtr vfile;
568: StringPtr file_name(0);
569: ValuePtr vformat(0);
570: if(HashStringValue *send_hash=send_value->get_hash(source)) { // hash
1.1 paf 571: // $.value
1.40.2.5 paf 572: if(ValuePtr value=send_hash->get(value_name))
573: vfile=value->as_vfile(pool, String::UL_AS_IS);
1.1 paf 574: else
575: throw Exception("parser.runtime",
576: source,
577: "file part has no $value");
578:
579: // $.format
1.40.2.5 paf 580: vformat=send_hash->get(format_name);
1.1 paf 581:
1.6 paf 582: // $.name
1.40.2.5 paf 583: if(ValuePtr vfile_name=send_hash->get(name_name)) // $name specified
584: file_name=vfile_name->as_string(&pool);
1.28 paf 585: } else // must be VFile then
1.40.2.5 paf 586: vfile=send_value->as_vfile(pool, String::UL_AS_IS);
1.28 paf 587:
588: if(!file_name)
1.40.2.5 paf 589: file_name=vfile->fields().get(name_name)->as_string(&pool);
1.28 paf 590:
1.40.2.3 paf 591: const char* file_name_cstr=file_name->cstr();
1.1 paf 592:
1.40.2.5 paf 593: StringPtr result(new String);
1.1 paf 594:
595: // content-type: application/octet-stream
1.40.2.5 paf 596: *result << "content-type: " << r.mime_type_of(file_name_cstr)
1.1 paf 597: << "; name=\"" << file_name_cstr << "\"\n";
598: // content-disposition: attachment; filename="user_file_name"
1.40.2.5 paf 599: *result <<
1.35 paf 600: CONTENT_DISPOSITION_NAME ": "CONTENT_DISPOSITION_VALUE"; "
601: CONTENT_DISPOSITION_FILENAME_NAME"=\"" << file_name_cstr << "\"\n";
1.1 paf 602:
1.40.2.5 paf 603: StringPtr type=vformat?vformat->as_string(&pool):StringPtr(0);
1.1 paf 604: if(!type/*default = uue*/ || *type=="uue") {
1.40.2.5 paf 605: pa_uuencode(pool, *result, file_name, *vfile);
1.1 paf 606: } else // for now
607: throw Exception("parser.runtime",
608: type,
609: "unknown attachment encode format");
610:
611: return result;
612: }
613:
1.40.2.5 paf 614: static StringPtr text_value_to_string(Request& r, StringPtr source,
615: PartType pt, ValuePtr send_value,
1.1 paf 616: Store_message_element_info& info) {
617: Pool& pool=r.pool();
1.40.2.5 paf 618: StringPtr result(new String);
1.1 paf 619:
1.40.2.5 paf 620: ValuePtr text_value;
621: if(HashStringValue* send_hash=send_value->get_hash(source)) {
1.1 paf 622: // $.USER-HEADERS
623: info.has_content_type=false; // reset
624: send_hash->for_each(store_message_element, &info);
625: // $.value
1.40.2.5 paf 626: text_value=send_hash->get(value_name);
1.1 paf 627: if(!text_value)
628: throw Exception("parser.runtime",
629: source,
630: "%s part has no $" VALUE_NAME, part_name_starts[pt]);
631: } else
1.40.2.5 paf 632: text_value=send_value;
1.1 paf 633:
634: if(!info.has_content_type) {
1.40.2.5 paf 635: *result
636: << "content-type: text/" << (pt==P_TEXT?"plain":"html")
1.40.2.10 paf 637: << "; charset=" << info.charsets.mail().name()
1.40.2.5 paf 638: << "\n";
1.1 paf 639: }
640:
641: // header|body separator
1.40.2.5 paf 642: *result << "\n";
1.1 paf 643:
644: // body
1.40.2.11 paf 645: StringPtr body;
1.1 paf 646: switch(pt) {
647: case P_TEXT:
1.40.2.11 paf 648: body=text_value->as_string(&pool);
1.1 paf 649: break;
650: case P_HTML:
651: {
1.25 paf 652: Temp_lang temp_lang(r, String::UL_HTML | String::UL_OPTIMIZE_BIT);
1.40.2.5 paf 653: if(JunctionPtr junction=text_value->get_junction())
1.40.2.11 paf 654: body=r.process_to_string(text_value);
1.26 paf 655: else
1.1 paf 656: throw Exception("parser.runtime",
657: source,
658: "html part value must be code");
659:
660: break;
661: }
1.40.2.11 paf 662: }
663: if(body) {
664: CharPtr body_ptr=body->cstr(String::UL_UNSPECIFIED); // body
665: size_t body_size=strlen(body_ptr); // body
666: const void *mail_ptr;
667: size_t mail_size;
668: Charset::transcode(pool,
669: r.charsets.source(), body_ptr, body_size,
670: r.charsets.mail()/*always set - either mail.charset or $request:charset*/, mail_ptr, mail_size);
671: result->APPEND_CLEAN((const char*)mail_ptr, mail_size, 0, 0);
1.1 paf 672: }
673:
674: return result;
675: };
676:
677: /// @todo files and messages in order (file, file2, ...)
1.40.2.5 paf 678: StringPtr VMail::message_hash_to_string(Request& r, StringPtr source,
679: HashStringValue* message_hash, int level,
1.40.2.10 paf 680: StringPtr& from, bool extract_to, StringPtr& to) {
1.34 paf 681: Pool& pool=r.pool();
682:
1.1 paf 683: if(!message_hash)
684: throw Exception("parser.runtime",
685: source,
686: "message must be hash");
687:
1.40.2.5 paf 688: StringPtr result(new String);
1.1 paf 689:
1.40.2.5 paf 690: if(ValuePtr vrecodecharset_name=message_hash->get(charset_name))
691: r.charsets.set_mail(charsets.get(vrecodecharset_name->as_string(&pool)));
1.1 paf 692: else
1.40.2.5 paf 693: r.charsets.set_mail(r.charsets.source());
694: // no big deal that we leave it set. they wont miss this point which would reset it
1.1 paf 695:
1.40.2.10 paf 696: Store_message_element_info info(pool, r.charsets,
697: result, from, extract_to, to);
1.1 paf 698: {
699: for(int pt=0; pt<P_TYPES_COUNT; pt++)
1.40.2.5 paf 700: info.parts[pt]=ArrayValuePtr(new ArrayValue(1));
1.1 paf 701: message_hash->for_each(store_message_element, &info);
1.10 paf 702: if(!info.errors_to)
1.40.2.5 paf 703: *result << "errors-to: postmaster\n"; // errors-to: default
1.37 paf 704: if(!info.mime_version_specified)
1.40.2.5 paf 705: *result << "MIME-Version: 1.0\n"; // MIME-Version: default
1.1 paf 706: }
707:
1.40.2.5 paf 708: int textCount=info.parts[P_TEXT]->count();
1.1 paf 709: if(textCount>1)
710: throw Exception("parser.runtime",
711: source,
712: "multiple text parts not supported, use file part");
1.40.2.5 paf 713: int htmlCount=info.parts[P_HTML]->count();
1.1 paf 714: if(htmlCount>1)
715: throw Exception("parser.runtime",
716: source,
717: "multiple html parts not supported, use file part");
718:
719:
720: bool multipart=info.parts_count>1;
721: bool alternative=textCount && htmlCount;
722: // header
723: char *boundary=0;
724: if(multipart) {
1.40.2.9 paf 725: boundary=new(pool) char[MAX_NUMBER];
1.1 paf 726: snprintf(boundary, MAX_NUMBER-5/*lEvEl*/, "lEvEl%d", level);
727: // multi-part
1.40.2.5 paf 728: *result
729: << "content-type: multipart/mixed; boundary=\""
730: << boundary << "\"\n"
731: "\n"
732: "This is a multi-part message in MIME format.";
1.1 paf 733: }
734:
735: // alternative or not
736: {
737: if(alternative) {
1.40.2.5 paf 738: *result << "\n\n--" << boundary << "\n" // intermediate boundary
739: "content-type: multipart/alternative; boundary=\"ALT" << boundary << "\"\n";
1.1 paf 740: }
741: for(int i=0; i<2; i++) {
742: PartType pt=i==0?P_TEXT:P_HTML;
1.40.2.5 paf 743: if(info.parts[pt]->count()) {
1.1 paf 744: if(alternative)
1.40.2.5 paf 745: *result << "\n\n--ALT" << boundary << "\n"; // intermediate boundary
1.1 paf 746: else if(boundary)
1.40.2.5 paf 747: *result << "\n\n--" << boundary << "\n"; // intermediate boundary
748: *result << *text_value_to_string(r, source, pt, info.parts[pt]->get(0), info);
1.1 paf 749: }
750: }
751: if(alternative)
1.40.2.5 paf 752: *result << "\n\n--ALT" << boundary << "--\n";
1.1 paf 753: }
754:
755: // files
756: {
1.40.2.5 paf 757: ArrayValue& files=*info.parts[P_FILE];
758: for(int i=0; i<files.count(); i++) {
1.1 paf 759: if(boundary)
1.40.2.5 paf 760: *result << "\n\n--" << boundary << "\n"; // intermediate boundary
761: *result << file_value_to_string(r, source, files.get(i));
1.1 paf 762: }
763: }
764:
765: // messages
766: {
1.40.2.5 paf 767: ArrayValue& messages=*info.parts[P_MESSAGE];
768: for(int i=0; i<messages.count(); i++) {
1.1 paf 769: if(boundary)
1.40.2.5 paf 770: *result << "\n\n--" << boundary << "\n"; // intermediate boundary
1.1 paf 771:
1.40.2.5 paf 772: StringPtr dummy_from;
773: StringPtr dummy_to;
774: *result << message_hash_to_string(r, source, messages.get(i)->get_hash(source), level+1,
1.40.2.10 paf 775: dummy_from, false, dummy_to);
1.1 paf 776: }
777: }
778:
779: // tailer
780: if(boundary)
1.40.2.5 paf 781: *result << "\n\n--" << boundary << "--\n"; // finish boundary
1.1 paf 782:
783: // return
784: return result;
785: }
786:
787:
1.40.2.5 paf 788: ValuePtr VMail::get_element(StringPtr aname, Value& aself, bool looking_up) {
1.1 paf 789: // $fields
1.3 paf 790: #ifdef WITH_MAILRECEIVE
1.40.2.5 paf 791: if(*aname==MAIL_RECEIVED_ELEMENT_NAME)
792: return vreceived;
1.1 paf 793: #endif
794:
795: // $CLASS,$method
1.40.2.5 paf 796: if(ValuePtr result=VStateless_class::get_element(aname, aself, looking_up))
1.1 paf 797: return result;
798:
1.40.2.6 paf 799: return ValuePtr(0);
1.1 paf 800: }
801:
1.3 paf 802: #if defined(WITH_MAILRECEIVE) && defined(_MSC_VER)
1.1 paf 803: # define GNOME_LIBS "/parser3project/win32mailreceive/win32/gnome"
804: # pragma comment(lib, GNOME_LIBS "/glib/lib/libglib-1.3-11.lib")
805: # ifdef _DEBUG
806: # pragma comment(lib, GNOME_LIBS "/gmime-x.x.x/Debug/libgmime.lib")
807: # else
808: # pragma comment(lib, GNOME_LIBS "/gmime-x.x.x/Release/libgmime.lib")
809: # endif
810: #endif
E-mail: