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