]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1147 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   /* Note: we even have the line count in the body-info! */
169   NSString *charset;
170   NSString *s;
171   NSData   *content;
172
173   content = [self decodedFlatContent];
174   if (content)
175     {
176       charset = [[bodyInfo objectForKey:@"parameterList"]
177                   objectForKey: @"charset"];
178
179       // TODO: properly decode charset, might need to handle encoding?
180   
181       if ([charset length] > 0)
182         s = [NSString stringWithData: content
183                       usingEncodingNamed: [charset lowercaseString]];
184       else
185         {
186           s = [[NSString alloc] initWithData: content
187                                 encoding: NSUTF8StringEncoding];
188           [s autorelease];
189         }
190
191       if (!s)
192         {
193           /* 
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?).
197           */
198           [self errorWithFormat:@"could not convert content to text, charset: '%@'",
199                 charset];
200           if ([self fallbackStringEncoding] > 0)
201             {
202               s = [[NSString alloc] initWithData:content 
203                                     encoding: [self fallbackStringEncoding]];
204               if (s)
205                 [s autorelease];
206               else
207                 [self errorWithFormat:
208                         @"an attempt to use fallback encoding failed to."];
209             }
210         }
211     }
212   else
213     {
214       [self errorWithFormat:@"got no text content: %@", 
215             [partPath componentsJoinedByString:@"."]];
216       s = nil;
217     }
218
219   return s;
220 }
221
222 /* path extension */
223
224 - (NSString *) pathExtensionForType: (NSString *) _mt
225                             subtype: (NSString *) _st
226 {
227   // TODO: support /etc/mime.types
228   
229   if (![_mt isNotNull] || ![_st isNotNull])
230     return nil;
231   if ([_mt length] == 0) return nil;
232   if ([_st length] == 0) return nil;
233   _mt = [_mt lowercaseString];
234   _st = [_st lowercaseString];
235   
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";
240   }
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";
246   }
247   else if ([_mt isEqualToString:@"message"]) {
248     if ([_st isEqualToString:@"rfc822"]) return @"mail";
249   }
250   else if ([_mt isEqualToString:@"application"]) {
251     if ([_st isEqualToString:@"pdf"]) return @"pdf";
252   }
253   return nil;
254 }
255
256 - (NSString *) preferredPathExtension
257 {
258   return [self pathExtensionForType: [bodyInfo valueForKey:@"type"]
259                subtype: [bodyInfo valueForKey:@"subtype"]];
260 }
261
262 - (NSString *) filename
263 {
264   NSDictionary *parameters;
265   NSString *filename;
266
267   filename = nil;
268   parameters = [bodyInfo valueForKey: @"parameterList"];
269   if (parameters)
270     filename = [parameters valueForKey: @"name"];
271
272   if (!filename)
273     {
274       parameters = [[bodyInfo valueForKey: @"disposition"]
275                      valueForKey: @"parameterList"];
276       filename = [parameters valueForKey: @"filename"];
277     }
278
279   return filename;
280 }
281
282 - (NSString *) filenameForDisplay
283 {
284   NSString *s;
285   
286   if ((s = [self filename]) != nil)
287     return s;
288   
289   s = [partPath componentsJoinedByString:@"-"];
290   return ([s length] > 0)
291     ? [@"untitled-" stringByAppendingString:s]
292     : @"untitled";
293 }
294
295 - (NSFormatter *) sizeFormatter
296 {
297   return [UIxMailSizeFormatter sharedMailSizeFormatter];
298 }
299
300 /* URL generation */
301
302 - (NSString *) pathToAttachmentObject
303 {
304   /* this points to the SoObject representing the part, no modifications */
305   NSString *url, *n, *pext;
306
307   /* path to mail controller object */
308   
309   url = [[self clientObject] baseURLInContext:context];
310   if (![url hasSuffix: @"/"])
311     url = [url stringByAppendingString: @"/"];
312   
313   /* mail relative path to body-part */
314   
315   /* eg this was nil for a draft containing an HTML message */
316   if ([(n = [partPath componentsJoinedByString:@"/"]) isNotNull])
317     url = [url stringByAppendingString:n];
318   
319   /* we currently NEED the extension for SoObject lookup (should be fixed) */
320   
321   pext = [self preferredPathExtension];
322   if ([pext isNotNull] && [pext length] > 0)
323     {
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)];
328       }
329       url = [url stringByAppendingString:@"."];
330       url = [url stringByAppendingString:pext];
331     }
332   
333   return url;
334 }
335
336 - (NSString *) pathToAttachment
337 {
338   /* this generates a more beautiful 'download' URL for a part */
339   NSString *url, *fn;
340
341   fn   = [self filename];
342   
343   if (![fn isNotNull] || ([fn length] == 0))
344     fn = nil;
345
346   /* get basic URL */
347
348   url = [self pathToAttachmentObject];
349   
350   /* 
351      If we have an attachment name, we attach it, this is properly handled by
352      SOGoMailBodyPart.
353   */
354   
355   if (fn)
356     {
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]];
362     
363     // TODO: should we check for a proper extension?
364     }
365
366   return url;
367 }
368
369 @end /* UIxMailPartViewer */