2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #include "SOGoMailObject.h"
23 #include "SOGoMailManager.h"
24 #include "SOGoMailBodyPart.h"
25 #include <NGImap4/NGImap4Envelope.h>
26 #include <NGImap4/NGImap4EnvelopeAddress.h>
29 @implementation SOGoMailObject
31 static NSArray *coreInfoKeys = nil;
32 static BOOL heavyDebug = NO;
35 /* Note: see SOGoMailManager.m for allowed IMAP4 keys */
36 /* Note: "BODY" actually returns the structure! */
37 coreInfoKeys = [[NSArray alloc] initWithObjects:
38 @"FLAGS", @"ENVELOPE", @"BODY",
40 // not yet supported: @"INTERNALDATE",
45 [self->coreInfos release];
51 - (NSString *)relativeImap4Name {
52 return [[self nameInContainer] stringByDeletingPathExtension];
57 - (SOGoMailObject *)mailObject {
63 - (NSString *)keyExtensionForPart:(id)_partInfo {
69 mt = [_partInfo valueForKey:@"type"];
70 st = [[_partInfo valueForKey:@"subtype"] lowercaseString];
71 if ([mt isEqualToString:@"text"]) {
72 if ([st isEqualToString:@"plain"])
74 if ([st isEqualToString:@"html"])
77 else if ([mt isEqualToString:@"image"])
78 return [@"." stringByAppendingString:st];
83 - (NSArray *)relationshipKeysWithParts:(BOOL)_withParts {
84 /* should return non-multipart children */
89 parts = [[self bodyStructure] valueForKey:@"parts"];
90 if (![parts isNotNull])
92 if ((count = [parts count]) == 0)
95 for (i = 0, ma = nil; i < count; i++) {
100 part = [parts objectAtIndex:i];
101 hasParts = [part valueForKey:@"parts"] != nil ? YES:NO;
102 if ((hasParts && !_withParts) || (_withParts && !hasParts))
106 ma = [NSMutableArray arrayWithCapacity:count - i];
108 ext = [self keyExtensionForPart:part];
109 key = [[NSString alloc] initWithFormat:@"%d%@", i + 1, ext?ext:@""];
116 - (NSArray *)toOneRelationshipKeys {
117 return [self relationshipKeysWithParts:NO];
119 - (NSArray *)toManyRelationshipKeys {
120 return [self relationshipKeysWithParts:YES];
125 - (id)fetchParts:(NSArray *)_parts {
126 return [[self mailManager] fetchURL:[self imap4URL] parts:_parts
127 password:[self imap4Password]];
132 - (id)fetchCoreInfos {
135 if (self->coreInfos != nil)
136 return [self->coreInfos isNotNull] ? self->coreInfos : nil;
138 msgs = [[self clientObject] fetchParts:coreInfoKeys]; // returns dict
139 if (heavyDebug) [self logWithFormat:@"M: %@", msgs];
140 msgs = [msgs valueForKey:@"fetch"];
141 if ([msgs count] == 0)
144 self->coreInfos = [[msgs objectAtIndex:0] retain];
145 return self->coreInfos;
148 - (id)bodyStructure {
149 return [[self fetchCoreInfos] valueForKey:@"body"];
152 - (NGImap4Envelope *)envelope {
153 return [[self fetchCoreInfos] valueForKey:@"envelope"];
155 - (NSString *)subject {
156 return [[self envelope] subject];
158 - (NSCalendarDate *)date {
159 return [[self envelope] date];
161 - (NGImap4EnvelopeAddress *)fromEnvelopeAddress {
162 return [[self envelope] from];
164 - (NSArray *)toEnvelopeAddresses {
165 return [[self envelope] to];
167 - (NSArray *)ccEnvelopeAddresses {
168 return [[self envelope] cc];
171 - (id)lookupInfoForBodyPart:(NSArray *)_path {
176 if ((info = [self bodyStructure]) == nil) {
177 [self errorWithFormat:@"got no body part structure!"];
181 /* for each path component, eg 1,1,3 */
182 pe = [_path objectEnumerator];
183 while ((p = [pe nextObject]) != nil && [info isNotNull]) {
187 [self logWithFormat:@"check PATH: %@", p];
188 idx = [p intValue] - 1;
190 parts = [info valueForKey:@"parts"];
191 if (idx >= [parts count]) {
192 [self errorWithFormat:@"body part index out of bounds(%d vs %d): %@",
193 (idx + 1), [parts count], info];
196 info = [parts objectAtIndex:idx];
198 return [info isNotNull] ? info : nil;
203 - (NSData *)content {
205 id result, fullResult;
207 fullResult = [self fetchParts:[NSArray arrayWithObject:@"RFC822"]];
208 if (fullResult == nil)
211 if ([fullResult isKindOfClass:[NSException class]])
214 /* extract fetch result */
216 result = [fullResult valueForKey:@"fetch"];
217 if (![result isKindOfClass:[NSArray class]]) {
219 @"ERROR: unexpected IMAP4 result (missing 'fetch'): %@",
221 return [NSException exceptionWithHTTPStatus:500 /* server error */
222 reason:@"unexpected IMAP4 result"];
224 if ([result count] == 0)
227 result = [result objectAtIndex:0];
229 /* extract message */
231 if ((content = [result valueForKey:@"message"]) == nil) {
233 @"ERROR: unexpected IMAP4 result (missing 'message'): %@",
235 return [NSException exceptionWithHTTPStatus:500 /* server error */
236 reason:@"unexpected IMAP4 result"];
239 return [[content copy] autorelease];
242 - (NSString *)contentAsString {
246 if ((content = [self content]) == nil)
248 if ([content isKindOfClass:[NSException class]])
251 s = [[NSString alloc] initWithData:content
252 encoding:NSISOLatin1StringEncoding];
255 @"ERROR: could not convert data of length %d to string",
259 return [s autorelease];
264 - (NSException *)addFlags:(id)_flags {
265 return [[self mailManager] addFlags:_flags toURL:[self imap4URL]
266 password:[self imap4Password]];
268 - (NSException *)removeFlags:(id)_flags {
269 return [[self mailManager] removeFlags:_flags toURL:[self imap4URL]
270 password:[self imap4Password]];
275 - (id)lookupImap4BodyPartKey:(NSString *)_key inContext:(id)_ctx {
276 // TODO: we might want to check for existence prior controller creation
277 return [[[SOGoMailBodyPart alloc] initWithName:_key
278 inContainer:self] autorelease];
281 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
284 /* first check attributes directly bound to the application */
285 if ((obj = [super lookupName:_key inContext:_ctx acquire:NO]) != nil)
288 /* lookup body part */
290 if ([self isBodyPartKey:_key inContext:_ctx]) {
291 if ((obj = [self lookupImap4BodyPartKey:_key inContext:_ctx]) != nil)
295 /* return 404 to stop acquisition */
296 return [NSException exceptionWithHTTPStatus:404 /* Not Found */];
301 - (BOOL)davIsCollection {
302 /* while a mail has child objects, it should appear as a file in WebDAV */
306 - (id)davContentLength {
307 return [[self fetchCoreInfos] valueForKey:@"size"];
310 - (NSDate *)davCreationDate {
311 // TODO: use INTERNALDATE once NGImap4 supports that
314 - (NSDate *)davLastModified {
315 return [self davCreationDate];
320 - (id)GETAction:(WOContext *)_ctx {
324 content = [self content];
325 if ([content isKindOfClass:[NSException class]])
327 if (content == nil) {
328 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
329 reason:@"did not find IMAP4 message"];
333 [r setHeader:@"message/rfc822" forKey:@"content-type"];
334 [r setContent:content];
338 @end /* SOGoMailObject */