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
200 /* return scheduling calendar of currently logged-in user */
204 user = [context activeUser];
205 folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
209 return [folder lookupName: @"personal" inContext: context acquire: NO];
212 - (id)storedEventObject {
213 /* lookup object in the users Calendar */
216 if (self->storedEventObject != nil)
217 return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
219 calendar = [self calendarFolder];
220 if ([calendar isKindOfClass:[NSException class]]) {
221 [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
226 filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
227 if (filename != nil) {
228 // TODO: When we get an exception, this might be an auth issue meaning
229 // that the UID indeed exists but that the user has no access to
231 // Of course this is quite unusual for the private calendar though.
234 tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
235 if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
236 self->storedEventObject = [tmp retain];
240 if (self->storedEventObject == nil)
241 self->storedEventObject = [[NSNull null] retain];
243 return self->storedEventObject;
246 - (BOOL)isEventStoredInCalendar {
247 return [[self storedEventObject] isNotNull];
250 - (iCalEvent *)storedEvent {
251 return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
254 /* organizer tracking */
256 - (NSString *) loggedInUserEMail
258 NSDictionary *identity;
260 identity = [[context activeUser] primaryIdentity];
262 return [identity objectForKey: @"email"];
265 - (iCalEvent *)authorativeEvent {
266 /* DB is considered master, when in DB, ignore mail organizer */
267 return [self isEventStoredInCalendar]
272 - (BOOL)isLoggedInUserTheOrganizer {
273 NSString *loginEMail;
275 if ((loginEMail = [self loggedInUserEMail]) == nil) {
276 [self warnWithFormat:@"Could not determine email of logged in user?"];
280 return [[self authorativeEvent] isOrganizer:loginEMail];
283 - (BOOL)isLoggedInUserAnAttendee {
284 NSString *loginEMail;
286 if ((loginEMail = [self loggedInUserEMail]) == nil) {
287 [self warnWithFormat:@"Could not determine email of logged in user?"];
291 return [[self authorativeEvent] isParticipant:loginEMail];
296 - (NSString *) organizerDisplayName
298 iCalPerson *organizer;
301 organizer = [[self authorativeEvent] organizer];
303 value = [self _personForDisplay: organizer];
305 value = @"[todo: no organizer set, use 'from']";
312 - (NGImap4EnvelopeAddress *)replySenderAddress {
314 The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
315 the actual attendees. BUT the attendee field contains the reply-status!
319 tmp = [[self clientObject] fromEnvelopeAddresses];
320 if ([tmp count] == 0) return nil;
321 return [tmp objectAtIndex:0];
324 - (NSString *)replySenderEMail {
325 return [[self replySenderAddress] email];
327 - (NSString *)replySenderBaseEMail {
328 return [[self replySenderAddress] baseEMail];
331 - (iCalPerson *)inReplyAttendee {
334 attendees = [[self inEvent] attendees];
335 if ([attendees count] == 0)
337 if ([attendees count] > 1)
338 [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
340 return [attendees objectAtIndex:0];
342 - (iCalPerson *)storedReplyAttendee {
344 TODO: since an attendee can have multiple email addresses, maybe we
345 should translate the email to an internal uid and then retrieve
346 all emails addresses for matching the participant.
348 Note: -findParticipantWithEmail: does not parse the email!
353 if ((e = [self storedEvent]) == nil)
355 if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
357 if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
361 - (BOOL)isReplySenderAnAttendee {
362 return [[self storedReplyAttendee] isNotNull];
368 return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
371 return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
373 - (id)tentativeLink {
374 return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
377 @end /* UIxMailPartICalViewer */