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