Annotation of parser3/src/types/pa_vmath.C, revision 1.1.2.1

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

E-mail: