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