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
25 Show plain/calendar mail parts.
28 #import <NGExtensions/NSCalendarDate+misc.h>
29 #import <NGExtensions/NSNull+misc.h>
30 #import <NGExtensions/NSObject+Logs.h>
32 #import <NGImap4/NGImap4EnvelopeAddress.h>
34 #import <NGCards/iCalCalendar.h>
35 #import <NGCards/iCalEvent.h>
36 #import <NGCards/iCalPerson.h>
37 #import <NGCards/iCalDateTime.h>
39 #import <SoObjects/SOGo/SOGoDateFormatter.h>
40 #import <SoObjects/SOGo/SOGoUser.h>
41 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
42 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
43 #import <SoObjects/Mailer/SOGoMailObject.h>
45 #import "UIxMailPartICalViewer.h"
47 @implementation UIxMailPartICalViewer
50 [self->storedEventObject release];
51 [self->storedEvent release];
52 [self->attendee release];
54 [self->inCalendar release];
55 [self->inEvent release];
56 [self->dateFormatter release];
62 - (void)resetPathCaches {
63 [super resetPathCaches];
64 [self->inEvent release]; self->inEvent = nil;
65 [self->inCalendar release]; self->inCalendar = nil;
66 [self->storedEventObject release]; self->storedEventObject = nil;
67 [self->storedEvent release]; self->storedEvent = nil;
69 /* not strictly path-related, but useless without it anyway: */
70 [self->attendee release]; self->attendee = nil;
71 [self->item release]; self->item = nil;
74 /* raw content handling */
76 - (NSStringEncoding)fallbackStringEncoding {
78 iCalendar invitations sent by Outlook 2002 have the annoying bug that the
79 mail states an UTF-8 content encoding but the actual iCalendar content is
80 encoding in Latin-1 (or Windows Western?).
82 As a result the content decoding will fail (TODO: always?). In this case we
83 try to decode with Latin-1.
85 Note: we could check for the Outlook x-mailer, but it was considered better
86 to try Latin-1 as a fallback in any case (be tolerant).
88 return NSISOLatin1StringEncoding;
93 - (iCalCalendar *) inCalendar
98 = [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
105 - (BOOL)couldParseCalendar {
106 return [[self inCalendar] isNotNull];
109 - (iCalEvent *)inEvent {
112 if (self->inEvent != nil)
113 return [self->inEvent isNotNull] ? self->inEvent : nil;
115 events = [[self inCalendar] events];
116 if ([events count] > 0) {
117 self->inEvent = [[events objectAtIndex:0] retain];
118 return self->inEvent;
121 self->inEvent = [[NSNull null] retain];
128 - (SOGoDateFormatter *)dateFormatter {
129 if (self->dateFormatter == nil) {
130 dateFormatter = [[context activeUser] dateFormatterInContext: context];
131 [dateFormatter retain];
134 return self->dateFormatter;
137 /* below is copied from UIxAppointmentView, can we avoid that? */
139 - (void)setAttendee:(id)_attendee {
140 ASSIGN(self->attendee, _attendee);
143 return self->attendee;
146 - (NSString *) _personForDisplay: (iCalPerson *) person
148 return [NSString stringWithFormat: @"%@ <%@>",
149 [person cnWithoutQuotes],
150 [person rfc822Email]];
153 - (NSString *) attendeeForDisplay
155 return [self _personForDisplay: attendee];
158 - (void)setItem:(id)_item {
159 ASSIGN(self->item, _item);
165 - (NSCalendarDate *) startTime
167 NSCalendarDate *date;
168 NSTimeZone *timeZone;
170 date = [[self authorativeEvent] startDate];
171 timeZone = [[context activeUser] timeZone];
172 [date setTimeZone: timeZone];
177 - (NSCalendarDate *) endTime
179 NSCalendarDate *date;
180 NSTimeZone *timeZone;
182 date = [[self authorativeEvent] endDate];
183 timeZone = [[context activeUser] timeZone];
184 [date setTimeZone: timeZone];
189 - (BOOL)isEndDateOnSameDay {
190 return [[self startTime] isDateOnSameDay:[self endTime]];
192 - (NSTimeInterval)duration {
193 return [[self endTime] timeIntervalSinceDate:[self startTime]];
196 /* calendar folder support */
198 - (id)calendarFolder {
199 /* return scheduling calendar of currently logged-in user */
203 user = [context activeUser];
204 folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
211 - (id)storedEventObject {
212 /* lookup object in the users Calendar */
215 if (self->storedEventObject != nil)
216 return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
218 calendar = [self calendarFolder];
219 if ([calendar isKindOfClass:[NSException class]]) {
220 [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
225 filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
226 if (filename != nil) {
227 // TODO: When we get an exception, this might be an auth issue meaning
228 // that the UID indeed exists but that the user has no access to
230 // Of course this is quite unusual for the private calendar though.
233 tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
234 if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
235 self->storedEventObject = [tmp retain];
239 if (self->storedEventObject == nil)
240 self->storedEventObject = [[NSNull null] retain];
242 return self->storedEventObject;
245 - (BOOL)isEventStoredInCalendar {
246 return [[self storedEventObject] isNotNull];
249 - (iCalEvent *)storedEvent {
250 return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
253 /* organizer tracking */
255 - (NSString *) loggedInUserEMail
257 NSDictionary *identity;
259 identity = [[context activeUser] primaryIdentity];
261 return [identity objectForKey: @"email"];
264 - (iCalEvent *)authorativeEvent {
265 /* DB is considered master, when in DB, ignore mail organizer */
266 return [self isEventStoredInCalendar]
271 - (BOOL)isLoggedInUserTheOrganizer {
272 NSString *loginEMail;
274 if ((loginEMail = [self loggedInUserEMail]) == nil) {
275 [self warnWithFormat:@"Could not determine email of logged in user?"];
279 return [[self authorativeEvent] isOrganizer:loginEMail];
282 - (BOOL)isLoggedInUserAnAttendee {
283 NSString *loginEMail;
285 if ((loginEMail = [self loggedInUserEMail]) == nil) {
286 [self warnWithFormat:@"Could not determine email of logged in user?"];
290 return [[self authorativeEvent] isParticipant:loginEMail];
295 - (NSString *) organizerDisplayName
297 iCalPerson *organizer;
300 organizer = [[self authorativeEvent] organizer];
302 value = [self _personForDisplay: organizer];
304 value = @"[todo: no organizer set, use 'from']";
311 - (NGImap4EnvelopeAddress *)replySenderAddress {
313 The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
314 the actual attendees. BUT the attendee field contains the reply-status!
318 tmp = [[self clientObject] fromEnvelopeAddresses];
319 if ([tmp count] == 0) return nil;
320 return [tmp objectAtIndex:0];
323 - (NSString *)replySenderEMail {
324 return [[self replySenderAddress] email];
326 - (NSString *)replySenderBaseEMail {
327 return [[self replySenderAddress] baseEMail];
330 - (iCalPerson *)inReplyAttendee {
333 attendees = [[self inEvent] attendees];
334 if ([attendees count] == 0)
336 if ([attendees count] > 1)
337 [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
339 return [attendees objectAtIndex:0];
341 - (iCalPerson *)storedReplyAttendee {
343 TODO: since an attendee can have multiple email addresses, maybe we
344 should translate the email to an internal uid and then retrieve
345 all emails addresses for matching the participant.
347 Note: -findParticipantWithEmail: does not parse the email!
352 if ((e = [self storedEvent]) == nil)
354 if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
356 if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
360 - (BOOL)isReplySenderAnAttendee {
361 return [[self storedReplyAttendee] isNotNull];
367 return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
370 return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
372 - (id)tentativeLink {
373 return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
376 @end /* UIxMailPartICalViewer */