2006-06-15 21:34:10 +02:00
|
|
|
/*
|
|
|
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
|
|
|
|
|
|
|
This file is part of OpenGroupware.org.
|
|
|
|
|
|
|
|
OGo is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
later version.
|
|
|
|
|
|
|
|
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with OGo; see the file COPYING. If not, write to the
|
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2007-02-20 17:15:37 +01:00
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
|
|
|
|
#import "UIxMailPartViewer.h"
|
|
|
|
#import "UIxMailRenderingContext.h"
|
|
|
|
#import "UIxMailSizeFormatter.h"
|
|
|
|
#import "../MailerUI/WOContext+UIxMailer.h"
|
|
|
|
#import <NGExtensions/NSString+Encoding.h>
|
|
|
|
#import "common.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@implementation UIxMailPartViewer
|
|
|
|
|
|
|
|
- (void)dealloc {
|
|
|
|
[self->flatContent release];
|
|
|
|
[self->bodyInfo release];
|
|
|
|
[self->partPath release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* caches */
|
|
|
|
|
|
|
|
- (void)resetPathCaches {
|
|
|
|
/* this is called when -setPartPath: is called */
|
|
|
|
[self->flatContent release]; self->flatContent = nil;
|
|
|
|
}
|
|
|
|
- (void)resetBodyInfoCaches {
|
|
|
|
}
|
|
|
|
|
|
|
|
/* notifications */
|
|
|
|
|
|
|
|
- (void)sleep {
|
|
|
|
[self resetPathCaches];
|
|
|
|
[self resetBodyInfoCaches];
|
|
|
|
[self->partPath release]; self->partPath = nil;
|
|
|
|
[self->bodyInfo release]; self->bodyInfo = nil;
|
|
|
|
[super sleep];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
|
|
|
- (void)setPartPath:(NSArray *)_path {
|
|
|
|
if ([_path isEqual:self->partPath])
|
|
|
|
return;
|
|
|
|
|
|
|
|
ASSIGN(self->partPath, _path);
|
|
|
|
[self resetPathCaches];
|
|
|
|
}
|
|
|
|
- (NSArray *)partPath {
|
|
|
|
return self->partPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setBodyInfo:(id)_info {
|
|
|
|
ASSIGN(self->bodyInfo, _info);
|
|
|
|
}
|
|
|
|
- (id)bodyInfo {
|
|
|
|
return self->bodyInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData *)flatContent {
|
|
|
|
if (self->flatContent != nil)
|
|
|
|
return [self->flatContent isNotNull] ? self->flatContent : nil;
|
|
|
|
|
|
|
|
self->flatContent =
|
|
|
|
[[[[self context] mailRenderingContext] flatContentForPartPath:
|
|
|
|
[self partPath]] retain];
|
|
|
|
return self->flatContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData *)decodedFlatContent {
|
|
|
|
NSString *enc;
|
|
|
|
|
|
|
|
enc = [[(NSDictionary *)[self bodyInfo]
|
|
|
|
objectForKey:@"encoding"] lowercaseString];
|
|
|
|
|
|
|
|
if ([enc isEqualToString:@"7bit"])
|
|
|
|
return [self flatContent];
|
|
|
|
|
|
|
|
if ([enc isEqualToString:@"8bit"]) // TODO: correct?
|
|
|
|
return [self flatContent];
|
|
|
|
|
|
|
|
if ([enc isEqualToString:@"base64"])
|
|
|
|
return [[self flatContent] dataByDecodingBase64];
|
|
|
|
|
|
|
|
if ([enc isEqualToString:@"quoted-printable"])
|
|
|
|
return [[self flatContent] dataByDecodingQuotedPrintable];
|
|
|
|
|
|
|
|
[self errorWithFormat:@"unsupported MIME encoding: %@", enc];
|
|
|
|
return [self flatContent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSStringEncoding)fallbackStringEncoding {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
- (NSString *)flatContentAsString {
|
|
|
|
/* Note: we even have the line count in the body-info! */
|
|
|
|
NSString *charset;
|
|
|
|
NSString *s;
|
|
|
|
NSData *content;
|
|
|
|
|
|
|
|
if ((content = [self decodedFlatContent]) == nil) {
|
|
|
|
[self errorWithFormat:@"got no text content: %@",
|
|
|
|
[[self partPath] componentsJoinedByString:@"."]];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
charset = [(NSDictionary *)
|
2007-02-15 22:06:46 +01:00
|
|
|
[(NSDictionary *)[self bodyInfo] objectForKey:@"parameterList"]
|
|
|
|
objectForKey:@"charset"];
|
2006-06-15 21:34:10 +02:00
|
|
|
charset = [charset lowercaseString];
|
2007-02-15 22:06:46 +01:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: properly decode charset, might need to handle encoding?
|
|
|
|
|
|
|
|
if ([charset length] > 0) {
|
2007-02-15 22:06:46 +01:00
|
|
|
s = [NSString stringWithData: content usingEncodingNamed: charset];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2007-02-15 22:06:46 +01:00
|
|
|
s = [[NSString alloc] initWithData: content encoding: NSUTF8StringEncoding];
|
2006-06-15 21:34:10 +02:00
|
|
|
s = [s autorelease];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s == nil) {
|
|
|
|
/*
|
|
|
|
Note: this can happend with iCalendar invitations sent by Outlook 2002.
|
|
|
|
It will mark the content as UTF-8 but actually deliver it as
|
|
|
|
Latin-1 (or Windows encoding?).
|
|
|
|
*/
|
|
|
|
[self errorWithFormat:@"could not convert content to text, charset: '%@'",
|
|
|
|
charset];
|
|
|
|
if ([self fallbackStringEncoding] > 0) {
|
|
|
|
s = [[NSString alloc] initWithData:content
|
|
|
|
encoding:[self fallbackStringEncoding]];
|
|
|
|
s = [s autorelease];
|
|
|
|
|
|
|
|
if (s == nil) {
|
|
|
|
[self errorWithFormat:
|
|
|
|
@" an attempt to use fallback encoding failed to."];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* path extension */
|
|
|
|
|
|
|
|
- (NSString *)pathExtensionForType:(NSString *)_mt subtype:(NSString *)_st {
|
|
|
|
// TODO: support /etc/mime.types
|
|
|
|
|
|
|
|
if (![_mt isNotNull] || ![_st isNotNull])
|
|
|
|
return nil;
|
|
|
|
if ([_mt length] == 0) return nil;
|
|
|
|
if ([_st length] == 0) return nil;
|
|
|
|
_mt = [_mt lowercaseString];
|
|
|
|
_st = [_st lowercaseString];
|
|
|
|
|
|
|
|
if ([_mt isEqualToString:@"image"]) {
|
|
|
|
if ([_st isEqualToString:@"gif"]) return @"gif";
|
|
|
|
if ([_st isEqualToString:@"jpeg"]) return @"jpg";
|
|
|
|
if ([_st isEqualToString:@"png"]) return @"png";
|
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"text"]) {
|
|
|
|
if ([_st isEqualToString:@"plain"]) return @"txt";
|
|
|
|
if ([_st isEqualToString:@"xml"]) return @"xml";
|
|
|
|
if ([_st isEqualToString:@"calendar"]) return @"ics";
|
|
|
|
if ([_st isEqualToString:@"x-vcard"]) return @"vcf";
|
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"message"]) {
|
|
|
|
if ([_st isEqualToString:@"rfc822"]) return @"mail";
|
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"application"]) {
|
|
|
|
if ([_st isEqualToString:@"pdf"]) return @"pdf";
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)preferredPathExtension {
|
|
|
|
return [self pathExtensionForType:[[self bodyInfo] valueForKey:@"type"]
|
|
|
|
subtype:[[self bodyInfo] valueForKey:@"subtype"]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)filename {
|
|
|
|
id tmp;
|
|
|
|
|
|
|
|
tmp = [[self bodyInfo] valueForKey:@"parameterList"];
|
|
|
|
if (![tmp isNotNull])
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
tmp = [tmp valueForKey:@"name"];
|
|
|
|
if (![tmp isNotNull])
|
|
|
|
return nil;
|
|
|
|
if ([tmp length] == 0)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)filenameForDisplay {
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
if ((s = [self filename]) != nil)
|
|
|
|
return s;
|
|
|
|
|
|
|
|
s = [[self partPath] componentsJoinedByString:@"-"];
|
|
|
|
return ([s length] > 0)
|
|
|
|
? [@"untitled-" stringByAppendingString:s]
|
|
|
|
: @"untitled";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFormatter *)sizeFormatter {
|
|
|
|
return [UIxMailSizeFormatter sharedMailSizeFormatter];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* URL generation */
|
|
|
|
|
|
|
|
- (NSString *)pathToAttachmentObject {
|
|
|
|
/* this points to the SoObject representing the part, no modifications */
|
|
|
|
NSString *url, *n, *pext;
|
|
|
|
|
|
|
|
/* path to mail controller object */
|
|
|
|
|
|
|
|
url = [[self clientObject] baseURLInContext:[self context]];
|
|
|
|
if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
|
|
|
|
|
|
|
|
/* mail relative path to body-part */
|
|
|
|
|
|
|
|
if ([(n = [[self partPath] componentsJoinedByString:@"/"]) isNotNull]) {
|
|
|
|
/* eg this was nil for a draft containing an HTML message */
|
|
|
|
url = [url stringByAppendingString:n];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we currently NEED the extension for SoObject lookup (should be fixed) */
|
|
|
|
|
|
|
|
pext = [self preferredPathExtension];
|
|
|
|
if ([pext isNotNull] && [pext length] > 0) {
|
|
|
|
/* attach extension */
|
|
|
|
if ([url hasSuffix:@"/"]) {
|
|
|
|
/* this happens if the part is the root-content of the mail */
|
|
|
|
url = [url substringToIndex:([url length] - 1)];
|
|
|
|
}
|
|
|
|
url = [url stringByAppendingString:@"."];
|
|
|
|
url = [url stringByAppendingString:pext];
|
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)pathToAttachment {
|
|
|
|
/* this generates a more beautiful 'download' URL for a part */
|
|
|
|
NSString *url, *fn;
|
|
|
|
|
|
|
|
fn = [self filename];
|
|
|
|
|
|
|
|
if (![fn isNotNull] || ([fn length] == 0))
|
|
|
|
fn = nil;
|
|
|
|
|
|
|
|
/* get basic URL */
|
|
|
|
|
|
|
|
url = [self pathToAttachmentObject];
|
|
|
|
|
|
|
|
/*
|
|
|
|
If we have an attachment name, we attach it, this is properly handled by
|
|
|
|
SOGoMailBodyPart.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (fn != nil) {
|
|
|
|
if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
|
|
|
|
if (isdigit([fn characterAtIndex:0]))
|
|
|
|
url = [url stringByAppendingString:@"fn-"];
|
|
|
|
url = [url stringByAppendingString:[fn stringByEscapingURL]];
|
|
|
|
|
|
|
|
// TODO: should we check for a proper extension?
|
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* UIxMailPartViewer */
|