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