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-08-01 20:22:45 +02:00
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
#import <Foundation/NSEnumerator.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
#import <Foundation/NSUserDefaults.h>
|
|
|
|
|
|
|
|
#import <NGObjWeb/NSException+HTTP.h>
|
|
|
|
#import <NGObjWeb/SoObject+SoDAV.h>
|
|
|
|
#import <NGObjWeb/WOContext.h>
|
|
|
|
#import <NGObjWeb/WOResponse.h>
|
|
|
|
#import <NGExtensions/NSNull+misc.h>
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
|
|
|
#import <NGExtensions/NGBase64Coding.h>
|
2007-12-06 17:03:53 +01:00
|
|
|
#import <NGExtensions/NGQuotedPrintableCoding.h>
|
2007-08-01 20:22:45 +02:00
|
|
|
#import <NGImap4/NGImap4Connection.h>
|
|
|
|
|
2007-11-10 01:02:30 +01:00
|
|
|
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
|
|
|
|
|
2007-08-01 20:22:45 +02:00
|
|
|
#import "SOGoMailObject.h"
|
|
|
|
#import "SOGoMailManager.h"
|
|
|
|
|
|
|
|
#import "SOGoMailBodyPart.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@implementation SOGoMailBodyPart
|
|
|
|
|
|
|
|
static NSString *mailETag = nil;
|
|
|
|
static BOOL debugOn = NO;
|
|
|
|
|
2007-07-10 16:22:33 +02:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
|
|
if (![[ud objectForKey:@"SOGoMailDisableETag"] boolValue]) {
|
|
|
|
mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%d_%d_%03d\"",
|
|
|
|
UIX_MAILER_MAJOR_VERSION,
|
|
|
|
UIX_MAILER_MINOR_VERSION,
|
|
|
|
UIX_MAILER_SUBMINOR_VERSION];
|
|
|
|
NSLog(@"Note(SOGoMailBodyPart): using constant etag for mail parts: '%@'",
|
|
|
|
mailETag);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
NSLog(@"Note(SOGoMailBodyPart): etag caching disabled!");
|
|
|
|
}
|
|
|
|
|
2007-12-10 23:34:12 +01:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
asAttachment = NO;
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2007-12-17 21:24:07 +01:00
|
|
|
[partInfo release];
|
|
|
|
[identifier release];
|
|
|
|
[pathToPart release];
|
2006-06-15 21:34:10 +02:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2007-12-10 23:34:12 +01:00
|
|
|
- (void) setAsAttachment
|
|
|
|
{
|
|
|
|
asAttachment = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* hierarchy */
|
|
|
|
|
|
|
|
- (SOGoMailObject *)mailObject {
|
|
|
|
return [[self container] mailObject];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IMAP4 */
|
|
|
|
|
|
|
|
- (NSString *)bodyPartName {
|
|
|
|
NSString *s;
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
s = [self nameInContainer];
|
|
|
|
r = [s rangeOfString:@"."]; /* strip extensions */
|
|
|
|
if (r.length == 0)
|
|
|
|
return s;
|
|
|
|
return [s substringToIndex:r.location];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *)bodyPartPath {
|
|
|
|
NSMutableArray *p;
|
|
|
|
id obj;
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
if (pathToPart != nil)
|
|
|
|
return [pathToPart isNotNull] ? pathToPart : nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
p = [[NSMutableArray alloc] initWithCapacity:8];
|
|
|
|
for (obj = self; [obj isKindOfClass:[SOGoMailBodyPart class]];
|
|
|
|
obj = [obj container]) {
|
|
|
|
[p insertObject:[obj bodyPartName] atIndex:0];
|
|
|
|
}
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
pathToPart = [p copy];
|
2006-06-15 21:34:10 +02:00
|
|
|
[p release];
|
2007-12-17 21:24:07 +01:00
|
|
|
return pathToPart;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)bodyPartIdentifier {
|
2007-12-17 21:24:07 +01:00
|
|
|
if (identifier != nil)
|
|
|
|
return [identifier isNotNull] ? identifier : nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
identifier =
|
2006-06-15 21:34:10 +02:00
|
|
|
[[[self bodyPartPath] componentsJoinedByString:@"."] copy];
|
2007-12-17 21:24:07 +01:00
|
|
|
return identifier;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSURL *)imap4URL {
|
|
|
|
/* reuse URL of message */
|
|
|
|
return [[self mailObject] imap4URL];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* part info */
|
|
|
|
|
|
|
|
- (id)partInfo {
|
2007-12-17 21:24:07 +01:00
|
|
|
if (partInfo != nil)
|
|
|
|
return [partInfo isNotNull] ? partInfo : nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
partInfo =
|
2006-06-15 21:34:10 +02:00
|
|
|
[[[self mailObject] lookupInfoForBodyPart:[self bodyPartPath]] retain];
|
2007-12-17 21:24:07 +01:00
|
|
|
return partInfo;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* name lookup */
|
|
|
|
|
2007-08-20 22:43:08 +02:00
|
|
|
- (id) lookupImap4BodyPartKey: (NSString *) _key
|
|
|
|
inContext: (id) _ctx
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: we might want to check for existence prior controller creation
|
|
|
|
Class clazz;
|
|
|
|
|
|
|
|
clazz = [SOGoMailBodyPart bodyPartClassForKey:_key inContext:_ctx];
|
2007-08-20 22:43:08 +02:00
|
|
|
|
|
|
|
return [clazz objectWithName: _key inContainer: self];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
- (NSString *) filename
|
|
|
|
{
|
|
|
|
NSString *filename;
|
|
|
|
NSDictionary *parameters;
|
|
|
|
|
|
|
|
[self partInfo];
|
|
|
|
|
|
|
|
filename = [[partInfo objectForKey: @"parameterList"]
|
|
|
|
objectForKey: @"name"];
|
|
|
|
if (!filename)
|
|
|
|
{
|
|
|
|
parameters = [[partInfo objectForKey: @"disposition"]
|
|
|
|
objectForKey: @"parameterList"];
|
|
|
|
filename = [parameters objectForKey: @"filename"];
|
|
|
|
}
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
2007-12-03 15:13:10 +01:00
|
|
|
/* We overwrite the super's class method in order to make sure
|
|
|
|
we aren't dealing with our actual filename as the _key. That
|
|
|
|
could lead to problems if we weren't doing this as our filename
|
|
|
|
could start with a digit, leading to a wrong assumption in
|
|
|
|
the super class
|
|
|
|
*/
|
2007-12-17 21:24:07 +01:00
|
|
|
// - (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx
|
|
|
|
// {
|
|
|
|
// NSString *s;
|
2007-12-03 15:13:10 +01:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
// s = [self filename];
|
|
|
|
// if (s && [s isEqualToString: _key]) return NO;
|
2007-12-03 15:13:10 +01:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
// return [super isBodyPartKey: _key inContext: _ctx];
|
|
|
|
// }
|
2007-12-03 15:13:10 +01:00
|
|
|
|
2007-08-20 22:43:08 +02:00
|
|
|
- (id) lookupName: (NSString *) _key
|
|
|
|
inContext: (id) _ctx
|
|
|
|
acquire: (BOOL) _flag
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
id obj;
|
|
|
|
|
|
|
|
/* first check attributes directly bound to the application */
|
2007-08-20 22:43:08 +02:00
|
|
|
obj = [super lookupName:_key inContext:_ctx acquire:NO];
|
|
|
|
if (!obj)
|
|
|
|
{
|
|
|
|
/* lookup body part */
|
|
|
|
if ([self isBodyPartKey:_key inContext:_ctx])
|
|
|
|
obj = [self lookupImap4BodyPartKey:_key inContext:_ctx];
|
2007-12-10 23:34:12 +01:00
|
|
|
else if ([_key isEqualToString: @"asAttachment"])
|
|
|
|
[self setAsAttachment];
|
2007-08-20 22:43:08 +02:00
|
|
|
/* should check whether such a filename exist in the attached names */
|
|
|
|
if (!obj)
|
|
|
|
obj = self;
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* fetch */
|
|
|
|
|
2007-11-13 15:58:40 +01:00
|
|
|
- (NSData *) fetchBLOB
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, TEXT
|
|
|
|
NSString *enc;
|
|
|
|
NSData *data;
|
|
|
|
|
|
|
|
data = [[self imap4Connection] fetchContentOfBodyPart:
|
|
|
|
[self bodyPartIdentifier]
|
|
|
|
atURL:[self imap4URL]];
|
|
|
|
if (data == nil) return nil;
|
|
|
|
|
|
|
|
/* check for content encodings */
|
2007-11-19 01:47:07 +01:00
|
|
|
enc = [[self partInfo] valueForKey: @"encoding"];
|
|
|
|
|
|
|
|
/* if we haven't found one, check out the main message's encoding
|
|
|
|
as we could be trying to fetch the message's content as a part */
|
|
|
|
if (!enc)
|
|
|
|
enc = [[[[self mailObject] fetchCoreInfos] valueForKey: @"body"]
|
|
|
|
valueForKey: @"encoding"];
|
|
|
|
|
|
|
|
if (enc)
|
|
|
|
{
|
2007-12-06 17:03:53 +01:00
|
|
|
enc = [enc lowercaseString];
|
2007-11-19 01:47:07 +01:00
|
|
|
|
2007-12-06 17:03:53 +01:00
|
|
|
if ([enc isEqualToString: @"base64"])
|
2007-11-19 01:47:07 +01:00
|
|
|
data = [data dataByDecodingBase64];
|
2007-12-06 17:03:53 +01:00
|
|
|
else if ([enc isEqualToString: @"quoted-printable"])
|
|
|
|
data = [data dataByDecodingQuotedPrintableTransferEncoding];
|
|
|
|
else if ([enc isEqualToString: @"7bit"]
|
|
|
|
|| [enc isEqualToString: @"8bit"])
|
2007-11-19 01:47:07 +01:00
|
|
|
; /* keep data as is */ // TODO: do we need to change encodings?
|
|
|
|
else
|
2007-12-06 17:03:53 +01:00
|
|
|
{
|
|
|
|
data = nil;
|
|
|
|
[self errorWithFormat: @"unsupported encoding: %@", enc];
|
|
|
|
}
|
2007-11-19 01:47:07 +01:00
|
|
|
}
|
2007-12-06 17:03:53 +01:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* WebDAV */
|
|
|
|
|
|
|
|
- (NSString *)contentTypeForBodyPartInfo:(id)_info {
|
|
|
|
NSMutableString *type;
|
|
|
|
NSString *mt, *st;
|
|
|
|
NSDictionary *parameters;
|
|
|
|
NSEnumerator *ke;
|
|
|
|
NSString *pn;
|
|
|
|
|
|
|
|
if (![_info isNotNull])
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
mt = [_info valueForKey:@"type"]; if (![mt isNotNull]) return nil;
|
|
|
|
st = [_info valueForKey:@"subtype"]; if (![st isNotNull]) return nil;
|
|
|
|
|
|
|
|
type = [NSMutableString stringWithCapacity:16];
|
|
|
|
[type appendString:[mt lowercaseString]];
|
|
|
|
[type appendString:@"/"];
|
|
|
|
[type appendString:[st lowercaseString]];
|
|
|
|
|
|
|
|
parameters = [_info valueForKey:@"parameterList"];
|
|
|
|
ke = [parameters keyEnumerator];
|
|
|
|
while ((pn = [ke nextObject]) != nil) {
|
|
|
|
[type appendString:@"; "];
|
|
|
|
[type appendString:pn];
|
|
|
|
[type appendString:@"=\""];
|
|
|
|
[type appendString:[[parameters objectForKey:pn] stringValue]];
|
|
|
|
[type appendString:@"\""];
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2007-11-13 15:58:40 +01:00
|
|
|
- (NSString *) contentTypeForPathExtension: (NSString *) pe
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
if ([pe length] == 0)
|
|
|
|
return @"application/octet-stream";
|
|
|
|
|
|
|
|
/* TODO: add some map */
|
|
|
|
if ([pe isEqualToString:@"gif"]) return @"image/gif";
|
|
|
|
if ([pe isEqualToString:@"png"]) return @"image/png";
|
|
|
|
if ([pe isEqualToString:@"jpg"]) return @"image/jpeg";
|
|
|
|
if ([pe isEqualToString:@"txt"]) return @"text/plain";
|
|
|
|
|
|
|
|
return @"application/octet-stream";
|
|
|
|
}
|
|
|
|
|
2007-10-29 22:00:35 +01:00
|
|
|
- (NSString *) davContentType
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: what about the content-type and other headers?
|
|
|
|
// => we could pass them in as the extension? (eg generate 1.gif!)
|
2007-10-29 22:00:35 +01:00
|
|
|
NSString *parts, *contentType, *extension;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
/* try type from body structure info */
|
|
|
|
|
2007-12-10 23:34:12 +01:00
|
|
|
if (asAttachment)
|
|
|
|
contentType = @"application/octet-stream";
|
|
|
|
else {
|
|
|
|
parts = [self contentTypeForBodyPartInfo: [self partInfo]];
|
|
|
|
contentType = [[parts componentsSeparatedByString: @";"] objectAtIndex: 0];
|
|
|
|
|
|
|
|
if (![contentType length])
|
|
|
|
{
|
|
|
|
extension = [[self nameInContainer] pathExtension];
|
|
|
|
contentType = [self contentTypeForPathExtension: extension];
|
|
|
|
}
|
|
|
|
}
|
2007-10-29 22:00:35 +01:00
|
|
|
|
|
|
|
return contentType;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* actions */
|
|
|
|
|
|
|
|
- (id)GETAction:(id)_ctx {
|
|
|
|
NSException *error;
|
|
|
|
WOResponse *r;
|
|
|
|
NSData *data;
|
2007-12-10 23:34:12 +01:00
|
|
|
NSString *etag, *mimeType, *fileName;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
if ((error = [self matchesRequestConditionInContext:_ctx]) != nil) {
|
|
|
|
// TODO: currently we fetch the body structure to get here - check this!
|
|
|
|
/* check whether the mail still exists */
|
|
|
|
if (![[self mailObject] doesMailExist]) {
|
|
|
|
return [NSException exceptionWithHTTPStatus:404 /* Not Found */
|
|
|
|
reason:@"mail was deleted"];
|
|
|
|
}
|
|
|
|
return error; /* return 304 or 416 */
|
|
|
|
}
|
|
|
|
|
|
|
|
[self debugWithFormat:@"should fetch body part: %@",
|
|
|
|
[self bodyPartIdentifier]];
|
|
|
|
|
|
|
|
if ((data = [self fetchBLOB]) == nil) {
|
|
|
|
return [NSException exceptionWithHTTPStatus:404 /* not found */
|
|
|
|
reason:@"did not find body part"];
|
|
|
|
}
|
|
|
|
|
|
|
|
[self debugWithFormat:@" fetched %d bytes: %@", [data length],
|
|
|
|
[self partInfo]];
|
|
|
|
|
|
|
|
// TODO: wrong, could be encoded
|
|
|
|
r = [(WOContext *)_ctx response];
|
2007-10-26 17:50:27 +02:00
|
|
|
mimeType = [self davContentType];
|
|
|
|
if ([mimeType isEqualToString: @"application/x-xpinstall"])
|
|
|
|
mimeType = @"application/octet-stream";
|
|
|
|
|
|
|
|
[r setHeader: mimeType forKey:@"content-type"];
|
|
|
|
[r setHeader: [NSString stringWithFormat:@"%d", [data length]]
|
|
|
|
forKey: @"content-length"];
|
2007-12-10 23:34:12 +01:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
if (asAttachment)
|
|
|
|
{
|
|
|
|
fileName = [self filename];
|
|
|
|
if ([fileName length])
|
|
|
|
[r setHeader: [NSString stringWithFormat: @"attachment; filename=%@", fileName]
|
|
|
|
forKey: @"content-disposition"];
|
|
|
|
}
|
2007-10-26 17:50:27 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
if ((etag = [self davEntityTag]) != nil)
|
|
|
|
[r setHeader:etag forKey:@"etag"];
|
|
|
|
|
|
|
|
[r setContent:data];
|
2007-10-26 17:50:27 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* factory */
|
|
|
|
|
2007-11-10 01:02:30 +01:00
|
|
|
+ (Class) bodyPartClassForKey: (NSString *) _key
|
|
|
|
inContext: (id) _ctx
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSString *pe;
|
|
|
|
|
|
|
|
pe = [_key pathExtension];
|
|
|
|
if (![pe isNotNull] || [pe length] == 0)
|
|
|
|
return self;
|
|
|
|
|
|
|
|
/* hard coded for now */
|
|
|
|
|
|
|
|
switch ([pe length]) {
|
|
|
|
case 3:
|
|
|
|
if ([pe isEqualToString:@"gif"] ||
|
|
|
|
[pe isEqualToString:@"png"] ||
|
|
|
|
[pe isEqualToString:@"jpg"])
|
|
|
|
return NSClassFromString(@"SOGoImageMailBodyPart");
|
|
|
|
if ([pe isEqualToString:@"ics"])
|
|
|
|
return NSClassFromString(@"SOGoCalendarMailBodyPart");
|
|
|
|
if ([pe isEqualToString:@"vcf"])
|
|
|
|
return NSClassFromString(@"SOGoVCardMailBodyPart");
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if ([pe isEqualToString:@"mail"])
|
|
|
|
return NSClassFromString(@"SOGoMessageMailBodyPart");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2007-11-10 01:02:30 +01:00
|
|
|
+ (Class) bodyPartClassForMimeType: (NSString *) mimeType
|
|
|
|
inContext: (id) _ctx
|
|
|
|
{
|
|
|
|
NSString *classString;
|
|
|
|
Class klazz;
|
|
|
|
|
|
|
|
if ([mimeType isEqualToString: @"image/gif"]
|
|
|
|
|| [mimeType isEqualToString: @"image/png"]
|
2007-11-16 22:15:38 +01:00
|
|
|
|| [mimeType isEqualToString: @"image/jpg"]
|
|
|
|
|| [mimeType isEqualToString: @"image/jpeg"])
|
2007-11-10 01:02:30 +01:00
|
|
|
classString = @"SOGoImageMailBodyPart";
|
2007-11-22 18:32:44 +01:00
|
|
|
else if ([mimeType isEqualToString: @"text/calendar"]
|
|
|
|
|| [mimeType isEqualToString: @"application/ics"])
|
2007-11-10 01:02:30 +01:00
|
|
|
classString = @"SOGoCalendarMailBodyPart";
|
|
|
|
else if ([mimeType isEqualToString: @"text/x-vcard"])
|
|
|
|
classString = @"SOGoVCardMailBodyPart";
|
|
|
|
else if ([mimeType isEqualToString: @"message/rfc822"])
|
|
|
|
classString = @"SOGoMessageMailBodyPart";
|
|
|
|
else
|
|
|
|
{
|
2007-11-21 16:04:24 +01:00
|
|
|
classString = @"SOGoMailBodyPart";
|
|
|
|
// NSLog (@"unhandled mime type: '%@'", mimeType);
|
2007-11-10 01:02:30 +01:00
|
|
|
}
|
|
|
|
|
2007-11-21 16:04:24 +01:00
|
|
|
klazz = NSClassFromString (classString);
|
2007-11-10 01:02:30 +01:00
|
|
|
|
|
|
|
return klazz;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* etag support */
|
|
|
|
|
|
|
|
- (id)davEntityTag {
|
|
|
|
return mailETag;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* debugging */
|
|
|
|
|
|
|
|
- (BOOL)isDebuggingEnabled {
|
|
|
|
return debugOn;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* SOGoMailBodyPart */
|