/* Copyright (C) 2007-2017 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. SOGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import "MailerUI/WOContext+UIxMailer.h" #import "UIxMailRenderingContext.h" #import "UIxMailSizeFormatter.h" #import "UIxMailPartViewer.h" @implementation UIxMailPartViewer - (id) init { if ((self = [super init])) { attachmentIds = nil; flatContent = nil; decodedContent = nil; } return self; } - (void) dealloc { [flatContent release]; [decodedContent release]; [bodyInfo release]; [partPath release]; [super dealloc]; } /* caches */ // // This is called when -setPartPath: is called // - (void) resetPathCaches { DESTROY(flatContent); DESTROY(decodedContent); } - (void) resetBodyInfoCaches { } /* notifications */ - (void) sleep { [self resetPathCaches]; [self resetBodyInfoCaches]; [partPath release]; [bodyInfo release]; partPath = nil; bodyInfo = nil; [super sleep]; } /* accessors */ - (void) setPartPath: (NSArray *) _path { if ([_path isEqual: partPath]) return; ASSIGN(partPath, _path); [self resetPathCaches]; } - (NSArray *) partPath { return partPath; } - (void) setBodyInfo: (id) _info { ASSIGN(bodyInfo, _info); } - (id) bodyInfo { return bodyInfo; } - (NSData *) flatContent { if (flatContent != nil) return [flatContent isNotNull] ? (id)flatContent : nil; flatContent = [[[context mailRenderingContext] flatContentForPartPath: partPath] retain]; return flatContent; } - (void) setFlatContent: (NSData *) theData { ASSIGN(flatContent, theData); } // // This methods decodes quoted-printable or // based64 encoded data coming straight // from the IMAP server. // - (id) decodedFlatContent { NSString *enc; if (decodedContent != nil) return [decodedContent isNotNull] ? (id)decodedContent : nil; enc = [[bodyInfo objectForKey:@"encoding"] lowercaseString]; decodedContent = [[[self flatContent] bodyDataFromEncoding: enc] retain]; return decodedContent; } - (void) setDecodedContent: (id) theData { ASSIGN(decodedContent, theData); } - (SOGoMailBodyPart *) clientPart { id currentObject; NSString *currentPart; NSEnumerator *parts; currentObject = [self clientObject]; parts = [partPath objectEnumerator]; while ((currentPart = [parts nextObject])) currentObject = [currentObject lookupName: currentPart inContext: context acquire: NO]; return currentObject; } - (id) renderedPart { NSString *type; type = [NSString stringWithFormat: @"%@/%@", [bodyInfo objectForKey: @"type"], [bodyInfo objectForKey: @"subtype"]]; return [NSDictionary dictionaryWithObjectsAndKeys: [self className], @"type", type, @"contentType", [[self generateResponse] contentAsString], @"content", [self filenameForDisplay], @"filename", [self preferredPathExtension], @"extension", [[self sizeFormatter] stringForObjectValue: [bodyInfo objectForKey: @"size"]], @"size", [self pathToAttachment], @"viewURL", [self pathForDownload], @"downloadURL", nil]; } - (NSDictionary *) attachmentIds { return attachmentIds; } // // Attachment IDs are used to replace CID from HTML content // with their MIME parts when viewing an HTML mail with // embedded images, defined as CID. // - (void) setAttachmentIds: (NSDictionary *) newAttachmentIds { attachmentIds = newAttachmentIds; } - (NSString *) flatContentAsString { NSString *charset, *s; NSData *content; content = [self decodedFlatContent]; if (content) { // We handle special cases for S/MIME encrypted message // as we won't deal with IMAP body structure objects here // but rather directly with NGMime objects. if ([content isKindOfClass: [NSData class]]) { charset = [[bodyInfo objectForKey: @"parameterList"] objectForKey: @"charset"]; s = [content bodyStringFromCharset: charset]; } else if ([content isKindOfClass: [NGMimeBodyPart class]] || [content isKindOfClass: [NGMimeMultipartBody class]]) s = [content body]; else s = (id)content; } else { [self errorWithFormat:@"got no text content: %@", [partPath componentsJoinedByString:@"."]]; s = nil; } return s; } /* path extension */ - (NSString *) pathExtensionForType: (NSString *) _mt subtype: (NSString *) _st { // TODO: support /etc/mime.types if (![_mt isNotNull] || ![_st isNotNull]) return nil; if ([_mt length] == 0) return nil; if ([_st length] == 0) return nil; _mt = [_mt lowercaseString]; _st = [_st lowercaseString]; if ([_mt isEqualToString:@"image"]) { if ([_st isEqualToString:@"gif"]) return @"gif"; if ([_st isEqualToString:@"jpeg"]) return @"jpg"; if ([_st isEqualToString:@"png"]) return @"png"; if ([_st isEqualToString:@"svg+xml"]) return @"svg"; } else if ([_mt isEqualToString:@"text"]) { if ([_st isEqualToString:@"plain"]) return @"txt"; if ([_st isEqualToString:@"xml"]) return @"xml"; if ([_st isEqualToString:@"calendar"]) return @"ics"; if ([_st isEqualToString:@"x-vcard"]) return @"vcf"; } else if ([_mt isEqualToString:@"message"]) { if ([_st isEqualToString:@"rfc822"]) return @"eml"; } else if ([_mt isEqualToString:@"application"]) { if ([_st isEqualToString:@"pdf"]) return @"pdf"; } return nil; } - (NSString *) preferredPathExtension { return [self pathExtensionForType: [bodyInfo valueForKey:@"type"] subtype: [bodyInfo valueForKey:@"subtype"]]; } - (NSString *) filename { return [bodyInfo filename]; } - (NSString *) filenameForDisplay { NSString *s; if ((s = [self filename]) != nil) return s; s = [partPath componentsJoinedByString:@"-"]; return ([s length] > 0) ? (id)[@"untitled-" stringByAppendingString:s] : (id)@"untitled"; } - (NSFormatter *) sizeFormatter { return [UIxMailSizeFormatter sharedMailSizeFormatter]; } /* URL generation */ - (NSString *) _filenameForAttachment: (SOGoMailBodyPart *) bodyPart { NSMutableString *filename; NSString *extension; if ([[self filename] length]) // We replace any slash by a dash since Apache won't allow encoded slashes by default. // See http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes filename = [NSMutableString stringWithString: [[self filename] stringByReplacingString: @"/" withString: @"-"]]; else filename = [NSMutableString stringWithFormat: @"%@-%@", [self labelForKey: @"Untitled"], [bodyPart nameInContainer]]; if (![[filename pathExtension] length]) { extension = [self preferredPathExtension]; if (extension) [filename appendFormat: @".%@", extension]; } return [filename stringByEscapingURL]; } - (NSString *) _pathForAttachmentOrDownload: (BOOL) forDownload { SOGoMailBodyPart *bodyPart; NSString *st, *s, *attachment; NSMutableString *url; st = [[bodyInfo valueForKey:@"subtype"] lowercaseString]; if (!forDownload && [st isEqualToString: @"svg+xml"]) return nil; bodyPart = [self clientPart]; s = [[self clientObject] baseURLInContext: [self context]]; url = [NSMutableString stringWithString: s]; if (![url hasSuffix: @"/"]) [url appendString: @"/"]; if ([self partPath]) { [url appendString: [[self partPath] componentsJoinedByString: @"/"]]; [url appendString: @"/"]; } if ([bodyPart isKindOfClass: [SOGoMailBodyPart class]] || [bodyPart isKindOfClass: [SOGoMailObject class]]) attachment = [self _filenameForAttachment: bodyPart]; else if ([[self decodedFlatContent] isKindOfClass: [NGMimeBodyPart class]]) attachment = [[[self decodedFlatContent] headerForKey: @"content-disposition"] filename]; else attachment = @"0"; if (forDownload) [url appendString: @"asAttachment/"]; [url appendString: attachment]; return url; } // // Used by UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox // - (NSString *) pathToAttachmentFromMessage { NSMutableArray *parts; SOGoMailBodyPart *bodyPart; bodyPart = [self clientPart]; if ([bodyPart isKindOfClass: [SOGoMailBodyPart class]]) { parts = [NSMutableArray arrayWithObject: [self _filenameForAttachment: bodyPart]]; do { [parts insertObject: [bodyPart nameInContainer] atIndex: 0]; bodyPart = [bodyPart container]; } while ([bodyPart isKindOfClass: [SOGoMailBodyPart class]]); return [parts componentsJoinedByString: @"/"]; } return @"0"; } - (NSString *) pathToAttachment { return [self _pathForAttachmentOrDownload: NO]; } - (NSString *) pathForDownload { return [self _pathForAttachmentOrDownload: YES]; } @end /* UIxMailPartViewer */