Annotation of parser3/src/types/pa_vmail.C, revision 1.1

1.1     ! paf         1: 
        !             2: /**    @file
        !             3:        Parser: @b mail class.
        !             4:        relies on gmime library, by Jeffrey Stedfast <fejj@helixcode.com>
        !             5: 
        !             6:        Copyright(c) 2001, 2002 ArtLebedev Group(http://www.artlebedev.com)
        !             7:        Author: Alexandr Petrosian <paf@design.ru>(http://paf.design.ru)
        !             8:        
        !             9:        $Id: pa_vmail.C,v 1.56 2002/06/10 13:27:40 paf Exp $
        !            10: */
        !            11: 
        !            12: #include "pa_sapi.h"
        !            13: #include "pa_vmail.h"
        !            14: #include "pa_vstring.h"
        !            15: #include "pa_request.h"
        !            16: #include "pa_common.h"
        !            17: #include "pa_charset.h"
        !            18: #include "pa_charsets.h"
        !            19: #include "pa_vdate.h"
        !            20: #include "pa_vfile.h"
        !            21: #include "pa_uue.h"
        !            22: 
        !            23: #ifdef WITH_MAIL_RECEIVE
        !            24: #include "gmime-config.h"
        !            25: #include "gmime.h"
        !            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: 
        !            43: static const char * const part_name_starts[P_TYPES_COUNT]={"text", "html", "file", "message"};
        !            44: 
        !            45: // VMail
        !            46: 
        !            47: extern Methoded *mail_base_class;
        !            48: 
        !            49: VMail::VMail(Pool& apool) : VStateless_class(apool, 0, mail_base_class),
        !            50:        vreceived(apool) {
        !            51: }
        !            52: 
        !            53: #ifdef WITH_MAIL_RECEIVE
        !            54: 
        !            55: static const String& maybeUpperCase(Pool& pool, const String& src, bool toUpperCase) {
        !            56:        return toUpperCase?src.change_case(pool, String::CC_UPPER):src;
        !            57: }
        !            58: 
        !            59: static void UTF8toSource(Pool& pool, const char *source_body, size_t source_content_length,
        !            60:                                                 const void *& dest_body, size_t& dest_content_length) {
        !            61:        if(source_body) {
        !            62:                if(!source_content_length)
        !            63:                        source_content_length=strlen(source_body);
        !            64:                Charset::transcode(pool,
        !            65:                                                                           *utf8_charset, source_body, source_content_length,
        !            66:                                                                           pool.get_source_charset(), dest_body, dest_content_length);
        !            67:        } else {
        !            68:                dest_body=0;
        !            69:                dest_content_length=0;
        !            70:        }
        !            71: }
        !            72: 
        !            73: static void putReceived(Hash& received, const char *name, Value *value, bool nameToUpperCase=false) {
        !            74:        Pool& pool=received.pool();
        !            75:        if(name && value) {
        !            76:                received.put(
        !            77:                        maybeUpperCase(pool, String::OnPool(pool, name, 0, true/*tainted*/), nameToUpperCase),
        !            78:                        value);
        !            79:        }
        !            80: }
        !            81: 
        !            82: static void putReceived(Hash& received, const char *name, const char *value, size_t value_size=0, bool nameToUpperCase=false) {
        !            83:        if(value) {
        !            84:                Pool& pool=received.pool();
        !            85: 
        !            86:                const void *value_dest_body;
        !            87:                size_t value_dest_content_length;
        !            88:                UTF8toSource(pool, value, value_size, value_dest_body, value_dest_content_length);
        !            89:                
        !            90:                putReceived(received, name, 
        !            91:                        new(pool) VString(
        !            92:                                String::OnPool(pool,(const char *)value_dest_body, value_dest_content_length, true/*tainted*/)));
        !            93:        }
        !            94: }
        !            95: 
        !            96: static void putReceived(Hash& received, const char *name, time_t value) {
        !            97:        Pool& pool=received.pool();
        !            98:        if(name)
        !            99:                received.put(String::OnPool(pool, name, 0, true/*tainted*/), new(pool) VDate(pool, value));
        !           100: }
        !           101: 
        !           102: static void MimeHeaderField2received(const char *name, const char *value, gpointer data) {
        !           103:        Hash& received=*static_cast<Hash *>(data);
        !           104: 
        !           105:        putReceived(received, name, value, 0, true/*nameInUpperCase*/);
        !           106: }
        !           107: 
        !           108: static void parse(GMimeStream *stream, Hash& received);
        !           109: 
        !           110: #ifndef DOXYGEN
        !           111: struct MimePart2bodyInfo {
        !           112:        Hash *body;
        !           113:        int partCounts[P_TYPES_COUNT];
        !           114: };
        !           115: #endif
        !           116: static void MimePart2body(GMimePart *part,
        !           117:                                                  gpointer data) {
        !           118:        MimePart2bodyInfo& i=*static_cast<MimePart2bodyInfo *>(data);
        !           119:        Pool& pool=i.body->pool();
        !           120: 
        !           121:        if(const GMimeContentType *type=g_mime_part_get_content_type(part)) {
        !           122:                if(g_mime_content_type_is_type(type, "multipart", "*"))
        !           123:                        return; // skipping frames
        !           124: 
        !           125:                PartType partType;
        !           126:                if(g_mime_content_type_is_type(type, "text", "plain"))
        !           127:                        partType=P_TEXT;
        !           128:                else if(g_mime_content_type_is_type(type, "text", "html"))
        !           129:                        partType=P_HTML;
        !           130:                else if(g_mime_content_type_is_type(type, "message", "*"))
        !           131:                        partType=P_MESSAGE;
        !           132:                else
        !           133:                        partType=P_FILE;
        !           134:                
        !           135:                // partName
        !           136:                const char *partName;
        !           137:                char partNameBuf[MAX_STRING];
        !           138:                const char *partNameStart=part_name_starts[partType];
        !           139:                if(int partNo=i.partCounts[partType]++) {
        !           140:                        snprintf(partNameBuf, MAX_STRING, "%s%d", partNameStart, partNo);
        !           141:                        partName=partNameBuf;
        !           142:                } else
        !           143:                        partName=partNameStart;
        !           144:                
        !           145:                // $.partX[ 
        !           146:                VHash& vpartX=*new(pool) VHash(pool);  putReceived(*i.body, partName, &vpartX);
        !           147:                Hash& partX=vpartX.hash(0);                     
        !           148:                {
        !           149:                        // $.raw[
        !           150:                        VHash& vraw=*new(pool) VHash(pool);  putReceived(partX, RAW_NAME, &vraw);
        !           151:                        g_mime_header_foreach(part->headers, MimeHeaderField2received, &vraw.hash(0));
        !           152:                }
        !           153:                const char *content_filename=0;
        !           154:                {
        !           155:                        // $.content-type[ 
        !           156:                        VHash& vcontent_type=*new(pool) VHash(pool);  putReceived(partX, "content-type", &vcontent_type);
        !           157:                        Hash& content_type=vcontent_type.hash(0)        !           158:                        {
        !           159:                                // $.value[text/plain] 
        !           160:                                char value[MAX_STRING];
        !           161:                                snprintf(value, MAX_STRING, "%s/%s", 
        !           162:                                        type->type?type->type:"x-unknown",
        !           163:                                        type->subtype?type->subtype:"x-unknown");
        !           164:                                putReceived(content_type, VALUE_NAME, value);
        !           165:                        }
        !           166:                        GMimeParam *param=type->params;
        !           167:                        while(param) {
        !           168:                                // $.charset[windows-1251]  && co
        !           169:                                putReceived(content_type, param->name, param->value, true);
        !           170:                                if(strcasecmp(param->name, "name")==0)
        !           171:                                        content_filename=param->value;
        !           172:                                param=param->next;
        !           173:                        }
        !           174:                }
        !           175:                // $.description
        !           176:                putReceived(partX, "description", part->description);
        !           177:                // $.content-id
        !           178:                putReceived(partX, "content-id", part->content_id);
        !           179:                // $.content-md5
        !           180:                putReceived(partX, "content-md5", part->content_md5);
        !           181:                // $.content-location
        !           182:                putReceived(partX, "content-location", part->content_location);
        !           183:                
        !           184:                // todo GMimePart:
        !           185:                //   GMimePartEncodingType encoding;
        !           186:                //   GMimeDisposition *disposition;
        !           187:                if(part->disposition) {
        !           188:                        GMimeParam *param=part->disposition->params;
        !           189:                        while(param) {
        !           190:                                // $.charset[windows-1251]  && co
        !           191:                                if(strcasecmp(param->name, "filename")==0)
        !           192:                                        content_filename=param->value;
        !           193:                                param=param->next;
        !           194:                        }
        !           195:                }
        !           196: 
        !           197:                // MESSAGE
        !           198:                if(partType==P_MESSAGE) {
        !           199:                        if(part->content)
        !           200:                                if(GMimeStream *stream=part->content->stream)
        !           201:                                        parse(stream, partX);
        !           202:                } else {
        !           203:                        // $.value[string|file]
        !           204:                        size_t buf_len;
        !           205:                        const void *buf=g_mime_part_get_content(part, &buf_len);
        !           206:                        if(partType==P_FILE) {
        !           207:                                VFile& vfile=*new(pool) VFile(pool);
        !           208:                                vfile.set(true/*tainted*/, buf, buf_len, content_filename);
        !           209:                                putReceived(partX, VALUE_NAME, &vfile);
        !           210:                        } else {
        !           211:                                // P_TEXT, P_HTML
        !           212:                                putReceived(partX, VALUE_NAME,(const char*)buf, buf_len);
        !           213:                        }
        !           214:                }
        !           215:        }
        !           216: }
        !           217: 
        !           218: static void parse(GMimeStream *stream, Hash& received) {
        !           219:        Pool& pool=received.pool();
        !           220: 
        !           221:        GMimeMessage *message=g_mime_parser_construct_message(stream);
        !           222:        try {
        !           223:                const GMimeMessageHeader *messageHeader=message->header;
        !           224:                if(!messageHeader)
        !           225:                        return;
        !           226: 
        !           227:                // firstly user-defined strings go
        !           228:                //  user headers
        !           229:                {
        !           230:                        // $.raw[
        !           231:                        VHash& vraw=*new(pool) VHash(pool);  putReceived(received, "raw", &vraw);
        !           232:                        g_mime_header_foreach(messageHeader->headers, MimeHeaderField2received, &vraw.hash(0));
        !           233:                }
        !           234: 
        !           235:                // maybe-todo-recipients
        !           236:                // x(messageHeader->recipients)
        !           237: 
        !           238:                //  secondly standard headers&body go
        !           239:                //  standard header
        !           240:                // .from
        !           241:                putReceived(received, "from", messageHeader->from);
        !           242:                // .reply-to
        !           243:                putReceived(received, "reply-to", messageHeader->reply_to);
        !           244:                // .to
        !           245:                // todo: messageHeader->recipients
        !           246:                // .subject
        !           247:                putReceived(received, "subject", messageHeader->subject);
        !           248:                // .date(date+gmt_offset)
        !           249:                int tt_offset = 
        !           250:                        ((messageHeader->gmt_offset / 100) *(60 * 60)) 
        !           251:                        +(messageHeader->gmt_offset % 100) * 60;                        
        !           252:                putReceived(received, "date", 
        !           253:                        messageHeader->date // local sender
        !           254:                        -tt_offset // move local sender to GMT sender
        !           255:                        -(timezone+(daylight?60*60*sign(timezone):0)) // move GMT sender to our local time
        !           256:                );
        !           257:                // .message-id
        !           258:                putReceived(received, "message-id", messageHeader->message_id);
        !           259: 
        !           260:                // .body[part/parts
        !           261:                GMimePart *part=message->mime_part;
        !           262:                const GMimeContentType *type=g_mime_part_get_content_type(part);
        !           263:                MimePart2bodyInfo info={&received};
        !           264:                g_mime_part_foreach(part, MimePart2body, &info);
        !           265: 
        !           266:                // normal unref
        !           267:                g_mime_object_unref(GMIME_OBJECT(message));
        !           268:        } catch(...) {
        !           269:                // abnormal unref
        !           270:                g_mime_object_unref(GMIME_OBJECT(message));
        !           271:        }
        !           272: }
        !           273: #endif
        !           274: 
        !           275: 
        !           276: 
        !           277: void VMail::fill_received(Request& request) {
        !           278: int a=timezone;
        !           279: int b=sign(timezone);
        !           280:        // store letter to received
        !           281: #ifdef WITH_MAIL_RECEIVE
        !           282:        if(request.info.mail_received) {
        !           283:                // init
        !           284:                g_mime_init(GMIME_INIT_FLAG_UTF8);
        !           285: 
        !           286:                // create stream with CRLF filter
        !           287:                GMimeStream *stream = g_mime_stream_fs_new(fileno(stdin));
        !           288:                GMimeStream *istream = g_mime_stream_filter_new_with_stream(stream);
        !           289:                GMimeFilter *filter = g_mime_filter_crlf_new(GMIME_FILTER_CRLF_DECODE, GMIME_FILTER_CRLF_MODE_CRLF_ONLY);
        !           290:                g_mime_stream_filter_add(GMIME_STREAM_FILTER(istream), filter);
        !           291:                g_mime_stream_unref(stream);
        !           292:                stream = istream;
        !           293:                try {
        !           294:                        // parse incoming stream
        !           295:                        parse(stream, vreceived.hash(0));
        !           296:                        // normal stream free 
        !           297:                        g_mime_stream_unref(stream);
        !           298:                } catch(...) {
        !           299:                        // abnormal stream free 
        !           300:                        g_mime_stream_unref(stream);
        !           301:                }
        !           302:        }
        !           303: #endif
        !           304: }
        !           305: 
        !           306: #ifndef DOXYGEN
        !           307: struct Store_message_element_info {
        !           308:        Charset *charset;
        !           309:        String *header;
        !           310:        const String **from, **to;
        !           311:        Array *parts[P_TYPES_COUNT];
        !           312:        int parts_count;
        !           313:        bool has_content_type;
        !           314: };
        !           315: #endif
        !           316: static void store_message_element(const Hash::Key& element_name, Hash::Val *aelement_value, 
        !           317:                                                                  void *info) {
        !           318:        Value& element_value=*static_cast<Value *>(aelement_value);
        !           319:        Store_message_element_info& i=*static_cast<Store_message_element_info *>(info);
        !           320: 
        !           321:        // exclude internals
        !           322:        if(element_name==CHARSET_NAME
        !           323:                || element_name==VALUE_NAME
        !           324:                || element_name==RAW_NAME)              
        !           325:                return;
        !           326: 
        !           327:        // grep parts
        !           328:        for(int pt=0; pt<P_TYPES_COUNT; pt++) {
        !           329:                if(element_name.starts_with(part_name_starts[pt])) {
        !           330:                        *i.parts[pt]+=&element_value;
        !           331:                        i.parts_count++;
        !           332:                        return;
        !           333:                }
        !           334:        }
        !           335: 
        !           336:        // fetch from & to from header for SMTP
        !           337:        if(i.from && element_name=="from")
        !           338:                *i.from=&element_value.as_string();
        !           339:        if(i.to && element_name=="to")
        !           340:                *i.to=&element_value.as_string();
        !           341: 
        !           342:        // append header line
        !           343:        *i.header << 
        !           344:                element_name << ":" << 
        !           345:                attributed_meaning_to_string(element_value, String::UL_MAIL_HEADER).
        !           346:                        cstr(String::UL_UNSPECIFIED, 0, i.charset, i.charset?i.charset->name().cstr():0) << 
        !           347:                "\n";
        !           348: 
        !           349:        // has content type?
        !           350:        if(element_name.change_case(element_name.pool(), String::CC_LOWER)==CONTENT_TYPE_NAME)
        !           351:                i.has_content_type=true;
        !           352: }
        !           353: 
        !           354: static const String& file_value_to_string(Request& r, const String *source, 
        !           355:                                                                                 Value& send_value) {
        !           356:        Pool& pool=r.pool();
        !           357:        const VFile *vfile;
        !           358:        const String *file_name;
        !           359:        Value *vformat;
        !           360:        if(Hash *send_hash=send_value.get_hash(source)) { // hash
        !           361:                // $.value
        !           362:                if(Value *value=static_cast<Value *>(send_hash->get(*value_name)))
        !           363:                        vfile=value->as_vfile(String::UL_AS_IS);
        !           364:                else
        !           365:                        throw Exception("parser.runtime",
        !           366:                                source,
        !           367:                                "file part has no $value");
        !           368: 
        !           369:                // $.format
        !           370:                vformat=static_cast<Value *>(send_hash->get(*new(pool) String(pool, "format")));
        !           371: 
        !           372:                // $.file-name
        !           373:                if(Value *vfile_name=static_cast<Value *>(send_hash->get(
        !           374:                        *new(pool) String(pool, "file-name")))) // specified $file-name
        !           375:                        file_name=&vfile_name->as_string();
        !           376:        } else {  // must be VFile
        !           377:                vfile=send_value.as_vfile(String::UL_AS_IS);
        !           378:                file_name=&static_cast<Value *>(vfile->fields().get(*name_name))->as_string();
        !           379:                vformat=0;
        !           380:        }
        !           381:        const char *file_name_cstr=file_name->cstr();
        !           382: 
        !           383:        String& result=*new(pool) String(pool);
        !           384: 
        !           385:        // content-type: application/octet-stream
        !           386:        result << "content-type: " << r.mime_type_of(file_name_cstr) 
        !           387:                << "; name=\"" << file_name_cstr << "\"\n";
        !           388:        // content-disposition: attachment; filename="user_file_name"
        !           389:        result << "content-disposition: attachment; filename=\"" << file_name_cstr << "\"\n";
        !           390: 
        !           391:        const String *type=vformat?&vformat->as_string():0;
        !           392:        if(!type/*default = uue*/ || *type=="uue") {
        !           393:                pa_uuencode(result, file_name_cstr, *vfile);
        !           394:        } else // for now
        !           395:                throw Exception("parser.runtime",
        !           396:                        type,
        !           397:                        "unknown attachment encode format");
        !           398:        
        !           399:        return result;
        !           400: }
        !           401: 
        !           402: static const String& text_value_to_string(Request& r, const String *source, 
        !           403:                                                                 PartType pt, Value& send_value,
        !           404:                                                                 Store_message_element_info& info) {
        !           405:        Pool& pool=r.pool();
        !           406:        String& result=*new(pool) String(pool);
        !           407: 
        !           408:        Value *text_value;
        !           409:        bool has_content_type;
        !           410:        if(Hash *send_hash=send_value.get_hash(source)) {
        !           411:                // $.USER-HEADERS
        !           412:                info.has_content_type=false; // reset
        !           413:                send_hash->for_each(store_message_element, &info);
        !           414:                // $.value
        !           415:                text_value=static_cast<Value *>(send_hash->get(*value_name));
        !           416:                if(!text_value)
        !           417:                        throw Exception("parser.runtime",
        !           418:                                source,
        !           419:                                "%s part has no $" VALUE_NAME, part_name_starts[pt]);
        !           420:        } else
        !           421:                text_value=&send_value;
        !           422: 
        !           423:        if(!info.has_content_type) {
        !           424:                result << "content-type: text/" << (pt==P_TEXT?"plain":"html");
        !           425:                if(info.charset)
        !           426:                        result << "; charset=" << info.charset->name();
        !           427:                result << "\n";
        !           428:        }
        !           429: 
        !           430:        // header|body separator
        !           431:        result << "\n"; 
        !           432: 
        !           433:        // body
        !           434:        switch(pt) {
        !           435:        case P_TEXT:
        !           436:                result.append(text_value->as_string(), String::UL_AS_IS, true /* forced */);
        !           437:                break;
        !           438:        case P_HTML: 
        !           439:                {
        !           440:                        Temp_lang temp_lang(r, String::UL_HTML);
        !           441:                        if(Junction *junction=text_value->get_junction()) {
        !           442:                                // execution of found $.html{code} must be in context of ^send[...]
        !           443:                                // setting code context, would execute in ^.send[>>context<<]
        !           444:                                //junction->change_context(?.get_junction());
        !           445:                                junction->root=r.root;
        !           446:                                junction->rcontext=r.rcontext;
        !           447:                                junction->wcontext=r.wcontext;
        !           448:                                
        !           449:                                result << r.process_to_string(*text_value);
        !           450:                        } else
        !           451:                                throw Exception("parser.runtime",
        !           452:                                        source,
        !           453:                                        "html part value must be code");
        !           454: 
        !           455:                        break;
        !           456:                }
        !           457:        }
        !           458: 
        !           459:        return result;
        !           460: };
        !           461: 
        !           462: /// @todo files and messages in order (file, file2, ...)
        !           463: const String& VMail::message_hash_to_string(Request& r, const String *source,
        !           464:                                                                                        Hash *message_hash, int level, 
        !           465:                                                                                        const String **from, const String **to) {
        !           466:        if(!message_hash)
        !           467:                throw Exception("parser.runtime",
        !           468:                        source,
        !           469:                        "message must be hash");
        !           470: 
        !           471:        String& result=*NEW String(pool());
        !           472: 
        !           473:        Charset *charset;
        !           474:        if(Value *vrecodecharset_name=static_cast<Value *>(message_hash->get(*charset_name)))
        !           475:                charset=&charsets->get_charset(vrecodecharset_name->as_string());
        !           476:        else
        !           477:                charset=&pool().get_source_charset();
        !           478: 
        !           479:        Store_message_element_info info={
        !           480:                charset,
        !           481:                &result,
        !           482:                from, to
        !           483:        };
        !           484:        {
        !           485:                if(from)
        !           486:                        *from=0;
        !           487:                if(to)
        !           488:                        *to=0;
        !           489:                for(int pt=0; pt<P_TYPES_COUNT; pt++)
        !           490:                        info.parts[pt]=NEW Array(pool());
        !           491:                message_hash->for_each(store_message_element, &info);
        !           492:        }
        !           493: 
        !           494:        int textCount=info.parts[P_TEXT]->size();
        !           495:        if(textCount>1)
        !           496:                throw Exception("parser.runtime",
        !           497:                        source,
        !           498:                        "multiple text parts not supported, use file part");
        !           499:        int htmlCount=info.parts[P_HTML]->size();
        !           500:        if(htmlCount>1)
        !           501:                throw Exception("parser.runtime",
        !           502:                        source,
        !           503:                        "multiple html parts not supported, use file part");
        !           504: 
        !           505: 
        !           506:        bool multipart=info.parts_count>1;
        !           507:        bool alternative=textCount && htmlCount;
        !           508:        // header
        !           509:        char *boundary=0;
        !           510:        if(multipart) {
        !           511:                boundary=(char *)malloc(MAX_NUMBER);
        !           512:                snprintf(boundary, MAX_NUMBER-5/*lEvEl*/, "lEvEl%d", level);
        !           513:                // multi-part
        !           514:                result << "content-type: multipart/mixed; boundary=\"" << boundary << "\"\n";
        !           515:                result << "\n" 
        !           516:                        "This is a multi-part message in MIME format.";
        !           517:        }
        !           518: 
        !           519:        // alternative or not
        !           520:        {
        !           521:                if(alternative) {
        !           522:                        result << "\n\n--" << boundary << "\n"; // intermediate boundary
        !           523:                        result << "content-type: multipart/alternative; boundary=\"ALT" << boundary << "\"\n";
        !           524:                } 
        !           525:                for(int i=0; i<2; i++) {
        !           526:                        PartType pt=i==0?P_TEXT:P_HTML;
        !           527:                        if(info.parts[pt]->size()) {
        !           528:                                if(alternative)
        !           529:                                        result << "\n\n--ALT" << boundary << "\n"; // intermediate boundary
        !           530:                                else if(boundary)
        !           531:                                        result << "\n\n--" << boundary << "\n";  // intermediate boundary
        !           532:                                result << text_value_to_string(r, source, pt, 
        !           533:                                        *static_cast<Value *>(info.parts[pt]->get(0)), info);
        !           534:                        }
        !           535:                }
        !           536:                if(alternative)
        !           537:                        result << "\n\n--ALT" << boundary << "--\n";
        !           538:        }
        !           539: 
        !           540:        // files
        !           541:        {
        !           542:                Array& files=*info.parts[P_FILE];
        !           543:                for(int i=0; i<files.size(); i++) {
        !           544:                        if(boundary)
        !           545:                                result << "\n\n--" << boundary << "\n";  // intermediate boundary
        !           546:                        result << file_value_to_string(r, source, *static_cast<Value *>(files.get(i)));
        !           547:                }
        !           548:        }
        !           549: 
        !           550:        // messages
        !           551:        {
        !           552:                Array& messages=*info.parts[P_MESSAGE];
        !           553:                for(int i=0; i<messages.size(); i++) {
        !           554:                        if(boundary)
        !           555:                                result << "\n\n--" << boundary << "\n";  // intermediate boundary
        !           556:                        
        !           557:                        result << message_hash_to_string(r, source,
        !           558:                                static_cast<Value *>(messages.get(i))->get_hash(source), 
        !           559:                                level+1);
        !           560:                }
        !           561:        }
        !           562:        
        !           563:        // tailer
        !           564:        if(boundary)
        !           565:                result << "\n\n--" << boundary << "--\n"; // finish boundary
        !           566: 
        !           567:        // return
        !           568:        return result;
        !           569: }
        !           570: 
        !           571: 
        !           572: Value *VMail::get_element(const String& aname) {
        !           573:        // $fields
        !           574: #ifdef WITH_MAIL_RECEIVE
        !           575:        if(aname==MAIL_RECEIVED_ELEMENT_NAME)
        !           576:                return &vreceived;
        !           577: #endif
        !           578: 
        !           579:        // $CLASS,$method
        !           580:        if(Value *result=VStateless_class::get_element(aname))
        !           581:                return result;
        !           582: 
        !           583:        return 0;
        !           584: }
        !           585: 
        !           586: #if defined(WITH_MAIL_RECEIVE) && defined(_MSC_VER)
        !           587: #      define GNOME_LIBS "/parser3project/win32mailreceive/win32/gnome"
        !           588: #      pragma comment(lib, GNOME_LIBS "/glib/lib/libglib-1.3-11.lib")
        !           589: #      ifdef _DEBUG
        !           590: #              pragma comment(lib, GNOME_LIBS "/gmime-x.x.x/Debug/libgmime.lib")
        !           591: #      else
        !           592: #              pragma comment(lib, GNOME_LIBS "/gmime-x.x.x/Release/libgmime.lib")
        !           593: #      endif
        !           594: #endif

E-mail: