"Fossies" - the Fresh Open Source Software Archive

Member "opengroupware-5.5rc3/WebUI/Mailer/OGoMailViewers/LSWMimePartViewer.m" (5 Dec 2015, 21089 Bytes) of package /linux/privat/opengroupware-5.5rc3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "LSWMimePartViewer.m" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.5rc2_vs_5.5rc3.

    1 /*
    2   Copyright (C) 2000-2005 SKYRIX Software AG
    3 
    4   This file is part of OpenGroupware.org.
    5 
    6   OGo is free software; you can redistribute it and/or modify it under
    7   the terms of the GNU Lesser General Public License as published by the
    8   Free Software Foundation; either version 2, or (at your option) any
    9   later version.
   10 
   11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
   12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
   14   License for more details.
   15 
   16   You should have received a copy of the GNU Lesser General Public
   17   License along with OGo; see the file COPYING.  If not, write to the
   18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   19   02111-1307, USA.
   20 */
   21 
   22 #include "LSWMimePartViewer.h"
   23 #include "LSWPartBodyViewer.h"
   24 #include "common.h"
   25 #include <NGObjWeb/WEClientCapabilities.h>
   26 #include <NGMime/NGMimeBodyPart.h>
   27 #include <NGMime/NGConcreteMimeType.h>
   28 #include <NGMime/NGMimeFileData.h>
   29 #include <NGMail/NGMimeMessage.h>
   30 #include <NGMail/NGMimeMessageGenerator.h>
   31 #include "SkyDecodeWrapperData.h"
   32 
   33 @interface NSObject(Private)
   34 - (NGImap4Context *)imapContext;
   35 - (void)setData:(NSData *)_data;
   36 + (NGImap4Context *)sessionImapContext:(id)_ses;
   37 @end /* Private */
   38 
   39 @interface LSWMimePartViewer(Private)
   40 - (NSString *)url;
   41 - (NSString *)mimeTypeString;
   42 - (NSString *)encodingString;
   43 - (NSString *)partUrl;
   44 - (NSNumber *)showBodyDepSizeVar;
   45 @end /* LSWMimePartViewer(Private) */
   46 
   47 @implementation WOComponent(Download)
   48 
   49 - (BOOL)isDownloadable {
   50   return NO;
   51 }
   52 
   53 @end /* WOComponent(Download) */
   54 
   55 @implementation LSWMimePartViewer
   56 
   57 static int CreateMailDownloadFileNamesDisable = -1;
   58 static int ShowBodyDependingSizeDisable = -1;
   59 static int ShowBodySize = -1;
   60 
   61 + (void)initialize {
   62   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   63   
   64   CreateMailDownloadFileNamesDisable =
   65     [ud boolForKey:@"CreateMailDownloadFileNamesDisable"] ? 1 : 0;
   66   ShowBodyDependingSizeDisable =
   67     [ud boolForKey:@"ShowBodySizeDisable"] ? 1 : 0;
   68   
   69   ShowBodySize = [ud integerForKey:@"ShowBodySize"];
   70   if (ShowBodySize < 1000) ShowBodySize = 100000;
   71 }
   72 
   73 - (id)init {
   74   if ((self = [super init])) {
   75     self->showBody  = YES;
   76     self->printMode = NO;
   77   }
   78   return self;
   79 }
   80 
   81 - (void)dealloc {
   82   [self->bodyViewer release];
   83   [self->part       release];
   84   [self->source     release];
   85   [super dealloc];
   86 }
   87 
   88 /* notifications */
   89 
   90 - (void)sleep {
   91   [super sleep];
   92   ASSIGN(self->bodyViewer, nil);
   93   ASSIGN(self->part,       nil);
   94   ASSIGN(self->source,     nil);
   95 }
   96 
   97 /* accessors */
   98 
   99 - (BOOL)isDownloadable {
  100   return NO;
  101 }
  102 
  103 /* response generation */
  104 
  105 - (void)appendToResponse:(WOResponse *)_resp
  106   inContext:(WOContext *)_ctx
  107 {
  108   NSMutableArray *array;
  109   
  110   [super appendToResponse:_resp inContext:_ctx];
  111 
  112   if (![(LSWPartBodyViewer *)[self bodyViewerComponent] isDownloadable])
  113     return;
  114 
  115   array = [[self session] valueForKey:@"displayedAttachmentDownloadUrls"];
  116   [array addObject:
  117            [(LSWPartBodyViewer *)[self bodyViewerComponent] bodyDescription]];
  118 }
  119 
  120 
  121 - (void)setNestingDepth:(int)_depth {
  122   self->nestingDepth = _depth;
  123 }
  124 - (int)nestingDepth {
  125   return self->nestingDepth;
  126 }
  127 - (int)nextNestingDepth {
  128   return (self->nestingDepth + 1);
  129 }
  130 
  131 - (void)setPart:(id)_part {
  132   ASSIGN(self->part, _part);
  133 }
  134 - (id)part {
  135   return self->part;
  136 }
  137 
  138 - (void)setShowHeaders:(BOOL)_flag {
  139   self->showHeaders = _flag;
  140 }
  141 - (BOOL)showHeaders {
  142   return self->showHeaders;
  143 }
  144 
  145 /* body */
  146 
  147 - (id)body {
  148   return [self->part body];
  149 }
  150 
  151 - (NSData *)contentForURL:(NSURL *)_url {
  152   NGImap4Context *imapCtx;
  153   NGImap4Folder  *folder;
  154   NSString       *path, *encoding;
  155   NSData         *data;
  156   
  157   encoding = [[self encodingString] lowercaseString];
  158   imapCtx  = [NSClassFromString(@"SkyImapContextHandler")
  159                                sessionImapContext:[self session]];
  160   path     = [_url path];
  161 
  162   folder = [imapCtx folderWithName:
  163               [path stringByDeletingLastPathComponent]];
  164   if (folder == nil) {
  165     [self logWithFormat:
  166         @"%s: could not find folder at path %@", __PRETTY_FUNCTION__,
  167             [path stringByDeletingLastPathComponent]];
  168     return [[[NSData alloc] init] autorelease];
  169   }
  170     
  171   data = [folder blobForUid:[[path lastPathComponent] intValue]
  172          part:[[[_url query] componentsSeparatedByString:@"="]
  173                               lastObject]];
  174   if (data == nil) {
  175     [self logWithFormat:
  176         @"%s: could not fetch blob for folder %@ path %@ uid %d part %@",
  177             __PRETTY_FUNCTION__, folder, path, 
  178         [[path lastPathComponent] intValue],
  179             [[[_url query] componentsSeparatedByString:@"="] lastObject]];
  180     return [[[NSData alloc] init] autorelease];
  181   }
  182   
  183   if ([encoding isEqualToString:@"base64"])
  184     return [data dataByDecodingBase64];
  185 
  186   if ([encoding isEqualToString:@"quoted-printable"])
  187     return [data dataByDecodingQuotedPrintable];
  188   
  189   return data;
  190 }
  191 
  192 /* header fields */
  193 
  194 // Correct content-type if possible. We're using
  195 // the "content-type" field for getting the filename.
  196 // A second filename may be exists in "content-disposition".
  197 
  198 - (NGMimeType *)correctedContentType {
  199   NSString     *ext, *fn;
  200   NSArray      *e, *a;
  201   NSDictionary *mimeTypes = nil;
  202   NSString     *mt;
  203   id ct;
  204 
  205   ct = [self->part contentType];
  206   if (!([[[(NGMimeType *)ct type]
  207                      lowercaseString] isEqualToString:@"application"] &&
  208       [[[(NGMimeType *)ct subType] lowercaseString]
  209     isEqualToString:@"octet-stream"]))
  210     return ct;
  211 
  212   fn  = [[ct parametersAsDictionary] objectForKey:@"name"];
  213     
  214   if (![fn isNotNull])
  215     return ct;
  216 
  217   ext = @"";
  218   e = [fn componentsSeparatedByString:@"."];
  219   if ([e count] > 0)
  220     ext = [e lastObject];
  221 
  222   mimeTypes = [[[self session] userDefaults] dictionaryForKey:@"LSMimeTypes"];
  223   if (!([ext length] > 0 && [mimeTypes isNotNull]))
  224     return ct;
  225 
  226   if ((mt = [mimeTypes valueForKey:ext]) == nil)
  227     return ct;
  228   
  229 
  230   a = [mt componentsSeparatedByString:@"/"];
  231   if ([a count] == 2) {
  232     NGMimeType *mimeType = nil;
  233 
  234     mimeType = [NGMimeType mimeType:[a objectAtIndex:0]
  235                subType:[a objectAtIndex:1]
  236                parameters:[ct parametersAsDictionary]];
  237     return mimeType;
  238   }
  239   return ct;
  240 }
  241 
  242 - (NGMimeType *)contentType {
  243   //return [self->part contentType];
  244   return [self correctedContentType];
  245 }
  246 - (NSString *)contentId {
  247   return [self->part contentId];
  248 }
  249 - (NSArray *)contentLanguage {
  250   return [self->part contentLanguage];
  251 }
  252 - (NSString *)contentMd5 {
  253   return [self->part contentMd5];
  254 }
  255 - (NSString *)encoding {
  256   return [self->part encoding];
  257 }
  258 - (NSString *)contentDescription {
  259   return [self->part contentDescription];
  260 }
  261 
  262 - (NSString *)contentLength {
  263   id len;
  264   len = [self->part valuesOfHeaderFieldWithName:@"content-length"];
  265   len = [len nextObject];
  266   return [len stringValue];
  267 }
  268 - (NSString *)contentDisposition {
  269   id v;
  270   v = [self->part valuesOfHeaderFieldWithName:@"content-disposition"];
  271   v = [v nextObject];
  272   return [v stringValue];
  273 }
  274 - (NSString *)contentTransferEncoding {
  275   id v;
  276   v = [self->part valuesOfHeaderFieldWithName:@"content-transfer-encoding"];
  277   v = [v nextObject];
  278   return [v stringValue];
  279 }
  280 
  281 - (NSString *)downloadIconName {
  282   id cfg = nil;
  283   NGMimeType *contentType;
  284   
  285   contentType = [self contentType];
  286 #if DEBUG
  287   NSAssert1(contentType == nil ||
  288             [contentType isKindOfClass:[NGMimeType class]],
  289             @"invalid content-type class '%@'", contentType);
  290 #endif
  291   
  292   cfg = [[self config] valueForKey:@"typeIcons"];
  293   
  294   if (contentType == nil) {
  295     return [cfg valueForKey:@"unknown"];
  296   }
  297   else {
  298     cfg = [cfg valueForKey:[contentType type]];
  299     cfg = [cfg valueForKey:[contentType subType]];
  300     return cfg
  301       ? cfg
  302       : [[[self config] valueForKey:@"typeIcons"] valueForKey:@"unknown"];
  303   }
  304 }
  305 
  306 - (NSString *)downloadType {
  307   return [NSString stringWithFormat:@"%@/%@", [[self contentType] type],
  308                    [[self contentType] subType]];
  309 }
  310 
  311 - (NSString *)downloadTarget {
  312 #if 0  
  313   NSString *t = [[self contentType] subType];
  314 
  315   return ([t isEqualToString:@"plain"]
  316           || [t isEqualToString:@"html"]
  317           || [t isEqualToString:@"gif"]
  318           || [t isEqualToString:@"jpeg"])
  319     ? [[self context] contextID]
  320     : @"";
  321 #else
  322   return [[self context] contextID];
  323 #endif  
  324 }
  325 
  326 /* actions */
  327 
  328 - (NSString *)_checkFor8Bit:(NSString *)_str {
  329   unsigned char *c;
  330   int           len;
  331   int           i;
  332   BOOL          changed;
  333 
  334   changed = NO;
  335   len     = [_str length];
  336   c       = calloc(len + 6 /* be defensive */, sizeof(id));
  337   
  338   [_str getCString:(char *)c];
  339   for (i = 0; i < len; i++) {
  340     if (c[i] > 127) {
  341       c[i] = '_';
  342       changed = YES;
  343     }
  344   }
  345   if (changed) {
  346 #if LIB_FOUNDATION_LIBRARY
  347     _str = [NSString stringWithCStringNoCopy:(char *)c length:len
  348              freeWhenDone:YES];
  349 #else
  350     _str = [NSString stringWithCString:(char *)c length:len];
  351     if (c != NULL) free(c); c = NULL;
  352 #endif
  353   }
  354   else
  355     if (c) free(c); c = NULL;
  356   
  357   return _str;
  358 }
  359 
  360 - (id)downloadPart {
  361   // TODO: split up method
  362   WOResponse *response;
  363   id         content;
  364   NSString   *disposition, *transferEncoding, *fileName, *type;
  365   
  366   disposition       = [self contentDisposition];
  367   transferEncoding  = [self contentTransferEncoding];
  368   fileName          = [[[self contentType] parametersAsDictionary]
  369                               objectForKey:@"name"];
  370   if (![fileName length])
  371     fileName = nil;
  372   
  373   response = [WOResponse responseWithRequest:[[self context] request]];
  374   [response setStatus:200];
  375 
  376   [response setHeader:[self _checkFor8Bit:[[self contentType] stringValue]]
  377             forKey:@"content-type"];
  378   
  379   content = [self body];
  380   
  381   if ([content isKindOfClass:[NSData class]]) {
  382   }
  383   else if ([content isKindOfClass:[NSString class]]) {
  384     BOOL     useUTF8;
  385     NSString *ct;
  386     WORequest *req;
  387 
  388     req     = [[self context] request];
  389     useUTF8 = [[req clientCapabilities] doesSupportUTF8Encoding];
  390 
  391     if (!useUTF8) {
  392       NSRange r;
  393       
  394       r = [[req headerForKey:@"accept-charset"] rangeOfString:@"utf-8"];
  395       useUTF8 = (r.length > 0) ? YES : NO;
  396     }
  397     if (useUTF8) {
  398       content = [content dataUsingEncoding:NSUTF8StringEncoding
  399                          allowLossyConversion:YES];
  400       ct      = @"text/plain; charset=utf-8";
  401     }
  402     else {
  403       content = [content dataUsingEncoding:NSISOLatin1StringEncoding
  404                          allowLossyConversion:YES];
  405       ct      = @"text/plain; charset=iso-8859-1";
  406     }
  407     [response setHeader:ct forKey:@"content-type"];
  408   }
  409   else if ([content isKindOfClass:[NSURL class]]) {
  410     content = [self contentForURL:content];
  411   }
  412   else {
  413     [(id)[[self context] page]
  414             setErrorString:
  415               @"couldn't provide downloadable representation of body"];
  416     [self logWithFormat:
  417             @"couldn't provide downloadable representation of body"];
  418     return nil;
  419   }
  420   type = [[self contentType] type];
  421   if ([type isEqualToString:@"text"] || [type isEqualToString:@"image"]) {
  422     if (disposition) {
  423       [response setHeader:[self _checkFor8Bit:[disposition stringValue]]
  424                 forKey:@"content-disposition"];
  425     }
  426   }
  427   else {
  428     static Class DispClass = Nil;
  429     id tmp;
  430 
  431     if (DispClass == Nil)
  432       DispClass = [NGMimeContentDispositionHeaderField class];
  433 
  434     if (![disposition isKindOfClass:DispClass]) {
  435       disposition =
  436         [[[DispClass alloc] initWithString:[disposition stringValue]]
  437                      autorelease];
  438     }
  439     tmp = [(NGMimeContentDispositionHeaderField *)disposition filename];
  440 
  441     if (![tmp length])
  442       tmp = fileName;
  443     
  444     if (tmp != nil) {
  445       tmp = [[tmp componentsSeparatedByString:@"\\"] lastObject];
  446       tmp = [NSString stringWithFormat:@"attachment; filename=\"%@\"", tmp];
  447     }
  448     else
  449       tmp = @"attachment";
  450     
  451       [response setHeader:[self _checkFor8Bit:tmp]
  452                 forKey:@"content-disposition"];
  453   }
  454   [response setHeader:[NSString stringWithFormat:@"%"PRIuPTR, [content length]]
  455             forKey:@"content-length"];
  456 
  457   if (transferEncoding) {
  458     if ([transferEncoding isEqualToString:@"base64"])
  459       content = [content dataByDecodingBase64];
  460     else if ([transferEncoding isEqualToString:@"quoted-printable"])
  461       content = [content dataByDecodingQuotedPrintable];
  462     else {
  463       [self logWithFormat:@"Unknown content-transfer-encoding: %@",
  464               transferEncoding];
  465     }
  466   }
  467   [response setHeader:@"identity" forKey:@"content-encoding"];
  468 
  469   [response setContent:content];
  470   return response;
  471 }
  472 
  473 - (id)buildDocumentBodyForPath {
  474   id body;
  475   
  476   body = [self body];
  477   if ([body isKindOfClass:[NSString class]])
  478     body = [body dataUsingEncoding:[NSString defaultCStringEncoding]];
  479   else if ([body isKindOfClass:[NSURL class]])
  480     body = [self contentForURL:body];
  481   return body;
  482 }
  483 
  484 - (NSString *)buildDocumentPathForPart {
  485   NSString *path;
  486   
  487   path = [self contentDisposition];
  488   if ([path length] > 0) {
  489     NGMimeContentDispositionHeaderField *cd;
  490     
  491     cd = [[NGMimeContentDispositionHeaderField alloc]
  492        initWithString:[self contentDisposition]];
  493     path = [[[cd filename] copy] autorelease];
  494     [cd release]; cd = nil;
  495   }
  496   else
  497     path = nil;
  498   
  499   if (path == nil) {
  500     NGMimeType *ct;
  501     NSString   *subject;
  502     
  503     ct   = [self contentType];
  504     path = [[ct parametersAsDictionary] objectForKey:@"name"];
  505     
  506     if (path == nil) {
  507       if ([[ct type] isEqualToString:@"text"]) {
  508     path = ([[ct subType] isEqualToString:@"html"])
  509       ? @".html" : @".txt";
  510       }
  511       else
  512     path = [@"." stringByAppendingString:[ct subType]];;
  513       
  514       if ((subject = [[self source] valueForKey:@"subject"]) == nil)
  515     subject = @"<unknown>";
  516       
  517       path = [subject stringByAppendingString:path];
  518     }
  519   }
  520 #if LIB_FOUNDATION_LIBRARY
  521   if ([path isKindOfClass:[NSInlineUTF16String class]]) {
  522     path = [NSString stringWithFormat:@"%@", path];
  523     /* hack to avoid utf16 string confusings */
  524   }
  525 #endif
  526   return path;
  527 }
  528 
  529 - (NSString *)buildDocumentTitleForPart {
  530   id       tmp;
  531   NSString *subject;
  532 
  533   tmp = [[self source] valueForKey:@"sendDate"];
  534   if ([tmp respondsToSelector:@selector(descriptionWithCalendarFormat:)])
  535     tmp = [tmp descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M"];
  536   else
  537     tmp = @"<unknown>";
  538       
  539   if ([tmp length] == 0)
  540     tmp = @"<unknown>";
  541   
  542   subject = [[self source] valueForKey:@"subject"];
  543   if (![subject isNotNull])
  544     subject = @"<unknown>";
  545   
  546   return [NSString stringWithFormat:@"%@ [%@]", subject, tmp];
  547 }
  548 
  549 - (id)toDoc {
  550   OGoContentPage *page;
  551   
  552 #if 1
  553   page = [self pageWithName:@"OGoDocumentImport"];
  554 #else
  555   // TODO: shouldn't we use a separate page for imports?
  556   page = [self pageWithName:@"SkyProject4DocumentEditor"];
  557   [page takeValue:[NSNumber numberWithBool:YES]    forKey:@"isImport"];
  558 #endif
  559   [page takeValue:[self buildDocumentBodyForPath]  forKey:@"blob"];
  560   [page takeValue:[self buildDocumentPathForPart]  forKey:@"fileName"];
  561   [page takeValue:[self buildDocumentTitleForPart] forKey:@"subject"];
  562   
  563   // TODO: is this required?
  564   [[[[self context] valueForKey:@"page"] navigation] enterPage:page];
  565   return page;
  566 }
  567 
  568 /* viewer */
  569 
  570 - (WOComponent *)bodyViewerComponent {
  571   /* TODO: split up this method */
  572   if (self->bodyViewer)
  573     return self->bodyViewer;
  574 
  575   self->bodyViewer =
  576     [[[self session] instantiateComponentForCommand:@"mailview"
  577                     type:[self contentType]] retain];
  578   if (self->bodyViewer == nil) {
  579     NGMimeType *appOctet;
  580 
  581     appOctet = [NGMimeType mimeType:@"application/octet-stream"];
  582     
  583     self->bodyViewer = [[[self session]
  584                                instantiateComponentForCommand:@"mailview"
  585                                type:appOctet] retain];
  586   }
  587   {
  588     NSData *data;
  589 
  590     data = [self body];
  591 
  592     if ([data isKindOfClass:[NGMimeFileData class]]) {
  593       
  594       data = [(SkyDecodeWrapperData *)[SkyDecodeWrapperData alloc]
  595                                       initWithData:data
  596                                       encoding:[[self part]
  597                          valueForKey:@"encoding"]];
  598       [data autorelease];
  599     }
  600     [(id)self->bodyViewer setBody:data];
  601     [(id)self->bodyViewer setPartOfBody:[self part]];
  602     [(id)self->bodyViewer setSource:[self source]];
  603   }
  604   
  605   if ([[[self contentType] type] isEqualToString:@"eo-pkey"]) {
  606     if ([(id)self->bodyViewer object] == nil) {
  607       ASSIGN(self->bodyViewer, nil);
  608       self->bodyViewer =
  609         [[[self session] instantiateComponentForCommand:@"mailview"
  610                          type:[NGMimeType mimeType:@"eo" subType:@"deleted"]]
  611                 retain];
  612     }
  613   }
  614   return self->bodyViewer;
  615 }
  616 
  617 - (BOOL)showBody {
  618   return self->showBody;
  619 }
  620 - (void)setShowBody:(BOOL)_body {
  621   self->showBody = _body;
  622 }
  623 
  624 - (BOOL)printMode {
  625   return self->printMode;
  626 }
  627 - (void)setPrintMode:(BOOL)_print {
  628   self->printMode = _print;
  629 }
  630 
  631 - (void)setSource:(id)_source {
  632   ASSIGN(self->source, _source);
  633 }
  634 - (id)source {
  635   return self->source;
  636 }
  637 
  638 - (BOOL)hasUrl {
  639   return [[self body] isKindOfClass:[NSURL class]];
  640 }
  641 
  642 - (NSString *)downloadPartActionName {
  643   /* 
  644      This appends a filename to the download action, this helps with browser
  645      detection of filenames and filetypes in case the browser does not
  646      properly work on the content disposition field.
  647   */
  648   // TBD: DUP in LSWPartBodyViewer?
  649   NSString *name;
  650 
  651   /* first check whether we are supposed to generate custom names */
  652 
  653   if (CreateMailDownloadFileNamesDisable)
  654     return @"get";
  655 
  656   /* check whether an attachment has a name assigned */
  657   
  658   name = [[[self->part contentType] parametersAsDictionary]
  659                    objectForKey:@"name"];
  660   if ([name isNotEmpty]) {
  661     /* be sure to escape the URL */
  662     name = [[name stringValue] stringByEscapingURL];
  663     return [@"get/" stringByAppendingString:name];
  664   }
  665   
  666   
  667   /* no escaping, a MIME subtype should always be urlsafe */
  668   
  669   name = [[self->part contentType] subType];
  670   return [@"get/download." stringByAppendingString:name];
  671 }
  672 
  673 - (NSString *)url {
  674   if ([[self body] isKindOfClass:[NSURL class]])
  675     return [[self body] absoluteString];
  676   return nil;
  677 }
  678 - (NSString *)mimeTypeString {
  679   return [[self->part contentType] stringValue];
  680 }
  681 
  682 - (NSString *)encodingString {
  683   return [[self->part encoding] stringValue];
  684 }
  685 
  686 - (NSString *)partUrl {
  687   return [[self body] query];
  688 }
  689 
  690 - (BOOL)showBodyDepSize {
  691   if (ShowBodyDependingSizeDisable)
  692     return YES;
  693   
  694   if ([self showBodyDepSizeVar])
  695     return [[self showBodyDepSizeVar] boolValue];
  696   
  697   if ([[[self->part contentType] type] isEqualToString:@"application"])
  698     return NO;
  699   
  700   if (([[self contentLength] intValue] > ShowBodySize))
  701     return NO;
  702   
  703   return YES;
  704 }
  705 
  706 - (BOOL)isImageViewerComponent:(WOComponent *)_component {
  707   return [_component isKindOfClass:[LSWImageBodyViewer class]];
  708 }
  709 
  710 - (BOOL)showBodyDepSizeEnabled {
  711   BOOL       viewImageInline;
  712   
  713   viewImageInline =
  714     [[[self session] userDefaults] boolForKey:@"mail_viewImagesInline"];
  715   
  716   if ([self isImageViewerComponent:[self bodyViewerComponent]]
  717       && !viewImageInline) {
  718     return NO;
  719   }
  720   return ShowBodyDependingSizeDisable ? NO : YES;
  721 }
  722 
  723 - (NSString *)partKey {
  724   NSString *key;
  725   
  726   if ((key = [self url]) == nil) {
  727     char buf[32];
  728     
  729     // TODO: better use some part hier-id?
  730     sprintf(buf, "%p", [self body]); 
  731     key = [NSString stringWithCString:buf];
  732   }
  733   return key;
  734 }
  735 
  736 - (NSNumber *)showBodyDepSizeVar {
  737   NSMutableDictionary *cache;
  738   
  739   cache = [[self session] valueForKey:@"ShowBodyPartsCache"];
  740   if (cache == nil) {
  741     cache = [NSMutableDictionary dictionaryWithCapacity:64];
  742     [[self session] takeValue:cache forKey:@"ShowBodyPartsCache"];
  743   }
  744   return [cache objectForKey:[self partKey]];
  745 }
  746 
  747 - (void)setShowBodyDepSizeVar:(NSNumber *)_n {
  748   NSMutableDictionary *cache;
  749 
  750   cache = [[self session] valueForKey:@"ShowBodyPartsCache"];
  751 
  752   if (!cache) {
  753     cache = [NSMutableDictionary dictionaryWithCapacity:64];
  754     [[self session] takeValue:cache forKey:@"ShowBodyPartsCache"];
  755   }
  756   [cache takeValue:_n forKey:[self partKey]];
  757 }
  758 
  759 - (id)alternateShowBody {
  760   NSNumber *n;
  761   
  762   n = ((n = [self showBodyDepSizeVar]) != nil)
  763     ? [NSNumber numberWithBool:[n boolValue]          ? NO : YES]
  764     : [NSNumber numberWithBool:[self showBodyDepSize] ? NO : YES];
  765   
  766   [self setShowBodyDepSizeVar:n];
  767   return nil;
  768 }
  769 
  770 @end /* LSWMimePartViewer */
  771 
  772 /* categories */
  773 
  774 @implementation NSObject(ViewerSelection)
  775 
  776 - (NSString *)lswPartViewer {
  777   return @"LSWMimePartViewer";
  778 }
  779 
  780 @end /* NSObject(ViewerSelection) */
  781 
  782 @implementation NGMimeBodyPart(ViewerSelection)
  783 
  784 - (NSString *)lswPartViewer {
  785   return @"LSWMimeBodyPartViewer";
  786 }
  787 
  788 @end /* NGMimeBodyPart(ViewerSelection) */
  789 
  790 @implementation NGMimeMessage(ViewerSelection)
  791 
  792 - (NSString *)lswPartViewer {
  793   //return @"LSWMimeMessageViewer";
  794   //return @"LSWMimeBodyPartViewer";
  795   return @"SkyMessageRfc822Viewer";
  796 }
  797 
  798 @end /* NGMimeMessage(ViewerSelection) */
  799 
  800 @implementation WOSession(ViewerSelection)
  801 
  802 - (NSString *)viewerComponentForPart:(id)_part {
  803   return [_part lswPartViewer];
  804 }
  805 
  806 @end /* WOSession(ViewerSelection) */