2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #import <Foundation/NSArray.h>
23 #import <Foundation/NSDictionary.h>
25 #import <NGExtensions/NGBase64Coding.h>
26 #import <NGExtensions/NSNull+misc.h>
27 #import <NGExtensions/NSObject+Logs.h>
28 #import <NGExtensions/NGQuotedPrintableCoding.h>
29 #import <NGExtensions/NSString+Encoding.h>
30 #import <NGExtensions/NSString+misc.h>
32 #import <SoObjects/SOGo/NSString+Utilities.h>
33 #import <SoObjects/Mailer/SOGoMailBodyPart.h>
35 #import "UI/MailerUI/WOContext+UIxMailer.h"
36 #import "UIxMailRenderingContext.h"
37 #import "UIxMailSizeFormatter.h"
39 #import "UIxMailPartViewer.h"
41 @implementation UIxMailPartViewer
45 [flatContent release];
53 - (void) resetPathCaches
55 /* this is called when -setPartPath: is called */
56 [flatContent release]; flatContent = nil;
59 - (void) resetBodyInfoCaches
67 [self resetPathCaches];
68 [self resetBodyInfoCaches];
78 - (void) setPartPath: (NSArray *) _path
80 if ([_path isEqual: partPath])
83 ASSIGN(partPath, _path);
85 [self resetPathCaches];
88 - (NSArray *) partPath
93 - (void) setBodyInfo: (id) _info
95 ASSIGN(bodyInfo, _info);
103 - (NSData *) flatContent
105 if (flatContent != nil)
106 return [flatContent isNotNull] ? flatContent : nil;
109 [[[context mailRenderingContext] flatContentForPartPath:
114 - (NSData *) decodedFlatContent
118 enc = [[bodyInfo objectForKey:@"encoding"] lowercaseString];
120 if ([enc isEqualToString:@"7bit"])
121 return [self flatContent];
123 if ([enc isEqualToString:@"8bit"]) // TODO: correct?
124 return [self flatContent];
126 if ([enc isEqualToString:@"base64"])
127 return [[self flatContent] dataByDecodingBase64];
129 if ([enc isEqualToString:@"quoted-printable"])
130 return [[self flatContent] dataByDecodingQuotedPrintable];
132 [self errorWithFormat:@"unsupported MIME encoding: %@", enc];
134 return [self flatContent];
142 NSString *currentPart;
146 currentObject = [self clientObject];
147 parts = [partPath objectEnumerator];
148 currentPart = [parts nextObject];
151 currentObject = [currentObject lookupName: currentPart
154 currentPart = [parts nextObject];
157 content = [currentObject fetchBLOB];
162 - (NSStringEncoding) fallbackStringEncoding
167 - (NSString *)flatContentAsString {
168 /* Note: we even have the line count in the body-info! */
173 content = [self decodedFlatContent];
176 charset = [[bodyInfo objectForKey:@"parameterList"]
177 objectForKey: @"charset"];
179 // TODO: properly decode charset, might need to handle encoding?
181 if ([charset length] > 0)
182 s = [NSString stringWithData: content
183 usingEncodingNamed: [charset lowercaseString]];
186 s = [[NSString alloc] initWithData: content
187 encoding: NSUTF8StringEncoding];
194 Note: this can happend with iCalendar invitations sent by Outlook 2002.
195 It will mark the content as UTF-8 but actually deliver it as
196 Latin-1 (or Windows encoding?).
198 [self errorWithFormat:@"could not convert content to text, charset: '%@'",
200 if ([self fallbackStringEncoding] > 0)
202 s = [[NSString alloc] initWithData:content
203 encoding: [self fallbackStringEncoding]];
207 [self errorWithFormat:
208 @"an attempt to use fallback encoding failed to."];
214 [self errorWithFormat:@"got no text content: %@",
215 [partPath componentsJoinedByString:@"."]];
224 - (NSString *) pathExtensionForType: (NSString *) _mt
225 subtype: (NSString *) _st
227 // TODO: support /etc/mime.types
229 if (![_mt isNotNull] || ![_st isNotNull])
231 if ([_mt length] == 0) return nil;
232 if ([_st length] == 0) return nil;
233 _mt = [_mt lowercaseString];
234 _st = [_st lowercaseString];
236 if ([_mt isEqualToString:@"image"]) {
237 if ([_st isEqualToString:@"gif"]) return @"gif";
238 if ([_st isEqualToString:@"jpeg"]) return @"jpg";
239 if ([_st isEqualToString:@"png"]) return @"png";
241 else if ([_mt isEqualToString:@"text"]) {
242 if ([_st isEqualToString:@"plain"]) return @"txt";
243 if ([_st isEqualToString:@"xml"]) return @"xml";
244 if ([_st isEqualToString:@"calendar"]) return @"ics";
245 if ([_st isEqualToString:@"x-vcard"]) return @"vcf";
247 else if ([_mt isEqualToString:@"message"]) {
248 if ([_st isEqualToString:@"rfc822"]) return @"mail";
250 else if ([_mt isEqualToString:@"application"]) {
251 if ([_st isEqualToString:@"pdf"]) return @"pdf";
256 - (NSString *) preferredPathExtension
258 return [self pathExtensionForType: [bodyInfo valueForKey:@"type"]
259 subtype: [bodyInfo valueForKey:@"subtype"]];
262 - (NSString *) filename
264 NSDictionary *parameters;
268 parameters = [bodyInfo valueForKey: @"parameterList"];
270 filename = [parameters valueForKey: @"name"];
274 parameters = [[bodyInfo valueForKey: @"disposition"]
275 valueForKey: @"parameterList"];
276 filename = [parameters valueForKey: @"filename"];
282 - (NSString *) filenameForDisplay
286 if ((s = [self filename]) != nil)
289 s = [partPath componentsJoinedByString:@"-"];
290 return ([s length] > 0)
291 ? [@"untitled-" stringByAppendingString:s]
295 - (NSFormatter *) sizeFormatter
297 return [UIxMailSizeFormatter sharedMailSizeFormatter];
302 - (NSString *) pathToAttachmentObject
304 /* this points to the SoObject representing the part, no modifications */
305 NSString *url, *n, *pext;
307 /* path to mail controller object */
309 url = [[self clientObject] baseURLInContext:context];
310 if (![url hasSuffix: @"/"])
311 url = [url stringByAppendingString: @"/"];
313 /* mail relative path to body-part */
315 /* eg this was nil for a draft containing an HTML message */
316 if ([(n = [partPath componentsJoinedByString:@"/"]) isNotNull])
317 url = [url stringByAppendingString:n];
319 /* we currently NEED the extension for SoObject lookup (should be fixed) */
321 pext = [self preferredPathExtension];
322 if ([pext isNotNull] && [pext length] > 0)
324 /* attach extension */
325 if ([url hasSuffix:@"/"]) {
326 /* this happens if the part is the root-content of the mail */
327 url = [url substringToIndex:([url length] - 1)];
329 url = [url stringByAppendingString:@"."];
330 url = [url stringByAppendingString:pext];
336 - (NSString *) pathToAttachment
338 /* this generates a more beautiful 'download' URL for a part */
341 fn = [self filename];
343 if (![fn isNotNull] || ([fn length] == 0))
348 url = [self pathToAttachmentObject];
351 If we have an attachment name, we attach it, this is properly handled by
357 if (![url hasSuffix: @"/"])
358 url = [url stringByAppendingString: @"/"];
359 if (isdigit([fn characterAtIndex:0]))
360 url = [url stringByAppendingString: @"fn-"];
361 url = [url stringByAppendingString: [fn stringByEscapingURL]];
363 // TODO: should we check for a proper extension?
369 @end /* UIxMailPartViewer */