]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1129 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailPartViewers / UIxMailPartViewer.m
1 /*
2   Copyright (C) 2004-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 #import <Foundation/NSDictionary.h>
23
24 #import <NGExtensions/NGBase64Coding.h>
25 #import <NGExtensions/NSNull+misc.h>
26 #import <NGExtensions/NSObject+Logs.h>
27 #import <NGExtensions/NGQuotedPrintableCoding.h>
28 #import <NGExtensions/NSString+Encoding.h>
29 #import <NGExtensions/NSString+misc.h>
30 #import <SoObjects/SOGo/NSString+Utilities.h>
31
32 #import "UI/MailerUI/WOContext+UIxMailer.h"
33 #import "UIxMailRenderingContext.h"
34 #import "UIxMailSizeFormatter.h"
35
36 #import "UIxMailPartViewer.h"
37
38 @implementation UIxMailPartViewer
39
40 - (void)dealloc {
41   [flatContent release];
42   [bodyInfo    release];
43   [partPath    release];
44   [super dealloc];
45 }
46
47 /* caches */
48
49 - (void)resetPathCaches {
50   /* this is called when -setPartPath: is called */
51   [flatContent release]; flatContent = nil;
52 }
53 - (void)resetBodyInfoCaches {
54 }
55
56 /* notifications */
57
58 - (void)sleep {
59   [self resetPathCaches];
60   [self resetBodyInfoCaches];
61   [partPath release]; partPath = nil;
62   [bodyInfo release]; bodyInfo = nil;
63   [super sleep];
64 }
65
66 /* accessors */
67
68 - (void)setPartPath:(NSArray *)_path {
69   if ([_path isEqual:partPath])
70     return;
71   
72   ASSIGN(partPath, _path);
73   [self resetPathCaches];
74 }
75 - (NSArray *)partPath {
76   return partPath;
77 }
78
79 - (void)setBodyInfo:(id)_info {
80   ASSIGN(bodyInfo, _info);
81 }
82 - (id)bodyInfo {
83   return bodyInfo;
84 }
85
86 - (NSData *)flatContent {
87   if (flatContent != nil)
88     return [flatContent isNotNull] ? flatContent : nil;
89   
90   flatContent = 
91     [[[context mailRenderingContext] flatContentForPartPath:
92                                               [self partPath]] retain];
93   return flatContent;
94 }
95
96 - (NSData *)decodedFlatContent {
97   NSString *enc;
98   
99   enc = [[(NSDictionary *)[self bodyInfo] 
100                           objectForKey:@"encoding"] lowercaseString];
101   
102   if ([enc isEqualToString:@"7bit"])
103     return [self flatContent];
104   
105   if ([enc isEqualToString:@"8bit"]) // TODO: correct?
106     return [self flatContent];
107   
108   if ([enc isEqualToString:@"base64"])
109     return [[self flatContent] dataByDecodingBase64];
110
111   if ([enc isEqualToString:@"quoted-printable"])
112     return [[self flatContent] dataByDecodingQuotedPrintable];
113   
114   [self errorWithFormat:@"unsupported MIME encoding: %@", enc];
115   return [self flatContent];
116 }
117
118 - (NSStringEncoding)fallbackStringEncoding {
119   return 0;
120 }
121 - (NSString *)flatContentAsString {
122   /* Note: we even have the line count in the body-info! */
123   NSString *charset;
124   NSString *s;
125   NSData   *content;
126   
127   if ((content = [self decodedFlatContent]) == nil) {
128     [self errorWithFormat:@"got no text content: %@", 
129             [[self partPath] componentsJoinedByString:@"."]];
130     return nil;
131   }
132   
133   charset = [(NSDictionary *)
134               [(NSDictionary *)[self bodyInfo] objectForKey:@"parameterList"]
135               objectForKey:@"charset"];
136   charset = [charset lowercaseString];
137
138   // TODO: properly decode charset, might need to handle encoding?
139   
140   if ([charset length] > 0) {
141     s = [NSString stringWithData: content usingEncodingNamed: charset];
142   }
143   else {
144     s = [[NSString alloc] initWithData: content encoding: NSUTF8StringEncoding];
145     s = [s autorelease];
146   }
147   
148   if (s == nil) {
149     /* 
150        Note: this can happend with iCalendar invitations sent by Outlook 2002.
151              It will mark the content as UTF-8 but actually deliver it as
152              Latin-1 (or Windows encoding?).
153     */
154     [self errorWithFormat:@"could not convert content to text, charset: '%@'",
155             charset];
156     if ([self fallbackStringEncoding] > 0) {
157       s = [[NSString alloc] initWithData:content 
158                             encoding:[self fallbackStringEncoding]];
159       s = [s autorelease];
160       
161       if (s == nil) {
162         [self errorWithFormat:
163                 @"  an attempt to use fallback encoding failed to."];
164       }
165     }
166   }
167   return s;
168 }
169
170 /* path extension */
171
172 - (NSString *)pathExtensionForType:(NSString *)_mt subtype:(NSString *)_st {
173   // TODO: support /etc/mime.types
174   
175   if (![_mt isNotNull] || ![_st isNotNull])
176     return nil;
177   if ([_mt length] == 0) return nil;
178   if ([_st length] == 0) return nil;
179   _mt = [_mt lowercaseString];
180   _st = [_st lowercaseString];
181   
182   if ([_mt isEqualToString:@"image"]) {
183     if ([_st isEqualToString:@"gif"])  return @"gif";
184     if ([_st isEqualToString:@"jpeg"]) return @"jpg";
185     if ([_st isEqualToString:@"png"])  return @"png";
186   }
187   else if ([_mt isEqualToString:@"text"]) {
188     if ([_st isEqualToString:@"plain"])    return @"txt";
189     if ([_st isEqualToString:@"xml"])      return @"xml";
190     if ([_st isEqualToString:@"calendar"]) return @"ics";
191     if ([_st isEqualToString:@"x-vcard"])  return @"vcf";
192   }
193   else if ([_mt isEqualToString:@"message"]) {
194     if ([_st isEqualToString:@"rfc822"]) return @"mail";
195   }
196   else if ([_mt isEqualToString:@"application"]) {
197     if ([_st isEqualToString:@"pdf"]) return @"pdf";
198   }
199   return nil;
200 }
201
202 - (NSString *)preferredPathExtension {
203   return [self pathExtensionForType:[[self bodyInfo] valueForKey:@"type"]
204                subtype:[[self bodyInfo] valueForKey:@"subtype"]];
205 }
206
207 - (NSString *)filename {
208   id tmp;
209   
210   tmp = [[self bodyInfo] valueForKey:@"parameterList"];
211   if (![tmp isNotNull])
212     return nil;
213   
214   tmp = [tmp valueForKey:@"name"];
215   if (![tmp isNotNull])
216     return nil;
217   if ([tmp length] == 0)
218     return nil;
219   
220   return tmp;
221 }
222
223 - (NSString *)filenameForDisplay {
224   NSString *s;
225   
226   if ((s = [self filename]) != nil)
227     return s;
228   
229   s = [[self partPath] componentsJoinedByString:@"-"];
230   return ([s length] > 0)
231     ? [@"untitled-" stringByAppendingString:s]
232     : @"untitled";
233 }
234
235 - (NSFormatter *)sizeFormatter {
236   return [UIxMailSizeFormatter sharedMailSizeFormatter];
237 }
238
239 /* URL generation */
240
241 - (NSString *)pathToAttachmentObject {
242   /* this points to the SoObject representing the part, no modifications */
243   NSString *url, *n, *pext;
244
245   /* path to mail controller object */
246   
247   url = [[self clientObject] baseURLInContext:context];
248   if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
249   
250   /* mail relative path to body-part */
251   
252   if ([(n = [[self partPath] componentsJoinedByString:@"/"]) isNotNull]) {
253     /* eg this was nil for a draft containing an HTML message */
254     url = [url stringByAppendingString:n];
255   }
256   
257   /* we currently NEED the extension for SoObject lookup (should be fixed) */
258   
259   pext = [self preferredPathExtension];
260   if ([pext isNotNull] && [pext length] > 0) {
261     /* attach extension */
262     if ([url hasSuffix:@"/"]) {
263       /* this happens if the part is the root-content of the mail */
264       url = [url substringToIndex:([url length] - 1)];
265     }
266     url = [url stringByAppendingString:@"."];
267     url = [url stringByAppendingString:pext];
268   }
269   
270   return url;
271 }
272
273 - (NSString *)pathToAttachment {
274   /* this generates a more beautiful 'download' URL for a part */
275   NSString *url, *fn;
276
277   fn   = [self filename];
278   
279   if (![fn isNotNull] || ([fn length] == 0))
280     fn = nil;
281
282   /* get basic URL */
283
284   url = [self pathToAttachmentObject];
285   
286   /* 
287      If we have an attachment name, we attach it, this is properly handled by
288      SOGoMailBodyPart.
289   */
290   
291   if (fn != nil) {
292     if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
293     if (isdigit([fn characterAtIndex:0]))
294       url = [url stringByAppendingString:@"fn-"];
295     url = [url stringByAppendingString:[fn stringByEscapingURL]];
296     
297     // TODO: should we check for a proper extension?
298   }
299   
300   return url;
301 }
302
303 @end /* UIxMailPartViewer */