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 #import "UIxMailEditorAction.h"
24 @interface UIxMailReplyAction : UIxMailEditorAction
27 #import <SoObjects/Mailer/SOGoMailObject.h>
28 #import <SoObjects/Mailer/SOGoDraftObject.h>
29 #import <NGImap4/NGImap4EnvelopeAddress.h>
30 #import <NGImap4/NGImap4Envelope.h>
33 @implementation UIxMailReplyAction
35 - (BOOL)hasReplyPrefix:(NSString *)_subject {
36 static NSString *replyPrefixes[] = {
38 @"RE:", // Outlook v11 (English?)
39 @"AW:", // German Outlook v11
40 @"Re[", // numbered Re, eg "Re[2]:"
44 for (i = 0; replyPrefixes[i] != nil; i++) {
45 if ([_subject hasPrefix:replyPrefixes[i]])
51 - (NSString *)replySubject:(NSString *)_subject {
52 if (![_subject isNotNull] || [_subject length] == 0)
55 if ([self hasReplyPrefix:_subject]) {
56 /* do not do: "Re: Re: Re: My Mail" - a single Re is sufficient ;-) */
60 return [@"Re: " stringByAppendingString:_subject];
63 - (void)addEMailsOfAddresses:(NSArray *)_addrs toArray:(NSMutableArray *)_ma {
66 for (i = 0, count = [_addrs count]; i < count; i++)
67 [_ma addObject:[(NGImap4EnvelopeAddress *)[_addrs objectAtIndex:i] email]];
70 - (void)fillInReplyAddresses:(NSMutableDictionary *)_info
71 replyToAll:(BOOL)_replyToAll
72 envelope:(NGImap4Envelope *)_envelope
75 The rules as implemented by Thunderbird:
76 - if there is a 'reply-to' header, only include that (as TO)
77 - if we reply to all, all non-from addresses are added as CC
78 - the from is always the lone TO (except for reply-to)
80 Note: we cannot check reply-to, because Cyrus even sets a reply-to in the
81 envelope if none is contained in the message itself! (bug or
84 TODO: what about sender (RFC 822 3.6.2)
89 to = [NSMutableArray arrayWithCapacity:2];
91 /* first check for "reply-to" */
93 addrs = [_envelope replyTo];
94 if ([addrs count] == 0) {
95 /* no "reply-to", try "from" */
96 addrs = [_envelope from];
98 [self addEMailsOfAddresses:addrs toArray:to];
99 [_info setObject:to forKey:@"to"];
101 /* CC processing if we reply-to-all: add all 'to' and 'cc' */
104 to = [NSMutableArray arrayWithCapacity:8];
106 [self addEMailsOfAddresses:[_envelope to] toArray:to];
107 [self addEMailsOfAddresses:[_envelope cc] toArray:to];
109 [_info setObject:to forKey:@"cc"];
113 - (NSString *) contentForReplyOnParts: (NSDictionary *) _prts
116 static NSString *textPartSeparator = @"\n---\n";
120 ms = [NSMutableString stringWithCapacity:16000];
121 for (i = 0, count = [_k count]; i < count; i++) {
124 k = [_k objectAtIndex:i];
126 // TODO: this is DUP code to SOGoMailObject
127 if ([k isEqualToString:@"body[text]"])
129 else if ([k hasPrefix:@"body["]) {
130 k = [k substringFromIndex:5];
131 if ([k length] > 0) k = [k substringToIndex:([k length] - 1)];
134 v = [_prts objectForKey:k];
135 if (![v isKindOfClass:[NSString class]]) {
136 [self logWithFormat:@"Note: cannot show part %@", k];
142 if (i != 0) [ms appendString:textPartSeparator];
143 [ms appendString:[v stringByApplyingMailQuoting]];
148 - (NSString *)contentForReply {
149 NSArray *keys, *partInfos;
150 NSDictionary *parts, *infos;
152 keys = [[self clientObject] plainTextContentFetchKeys];
153 // SOGoMailObject *co;
155 // co = [self clientObject];
156 // keys = [co plainTextContentFetchKeys];
157 // infos = [co fetchCoreInfos];
158 // partInfos = [infos objectForKey: keys];
159 // NSLog (@"infos: '%@'", infos);
161 if ([keys count] == 0)
164 if ([keys count] > 1) {
165 /* filter keys, only include top-level, or if none, the first */
166 NSMutableArray *topLevelKeys = nil;
169 for (i = 0; i < [keys count]; i++) {
172 r = [[keys objectAtIndex:i] rangeOfString:@"."];
176 if (topLevelKeys == nil)
177 topLevelKeys = [NSMutableArray arrayWithCapacity:4];
178 [topLevelKeys addObject:[keys objectAtIndex:i]];
181 if ([topLevelKeys count] > 0) {
182 /* use top-level keys if we have some */
186 /* just take the first part */
187 keys = [NSArray arrayWithObject:[keys objectAtIndex:0]];
191 parts = [[self clientObject] fetchPlainTextStrings:keys];
192 return [self contentForReplyOnParts:parts keys:keys];
195 - (id)replyToAll:(BOOL)_replyToAll {
196 NSMutableDictionary *info;
201 /* ensure mail exists and is filled */
203 // TODO: we could transport the body structure in a hidden field of the mail
204 // viewer to avoid refetching the core-info?
205 tmp = [[self clientObject] fetchCoreInfos];
206 if ([tmp isKindOfClass:[NSException class]])
208 if (![tmp isNotNull])
209 return [self didNotFindMailError];
213 if ((error = [self _setupNewDraft]) != nil)
216 /* fill draft info */
218 info = [NSMutableDictionary dictionaryWithCapacity:16];
220 [info setObject:[self replySubject:[[self clientObject] subject]]
222 [self fillInReplyAddresses:info replyToAll:_replyToAll
223 envelope:[[self clientObject] envelope]];
225 /* fill in text content */
227 if ((tmp = [self contentForReply]) != nil)
228 [info setObject:tmp forKey:@"text"];
230 /* save draft info */
232 if ((error = [self->newDraft storeInfo:info]) != nil)
235 // TODO: we might want to pass the original URL to the editor for a final
236 // redirect back to the message?
237 result = [self redirectToEditNewDraft];
243 return [self replyToAll:NO];
245 - (id)replyallAction {
246 return [self replyToAll:YES];
249 @end /* UIxMailReplyAction */