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 <SOGoUI/SOGoDateFormatter.h>
29 #import <SoObjects/SOGo/SOGoUser.h>
30 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
31 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
32 #import <SoObjects/Mailer/SOGoMailObject.h>
33 #import <NGCards/NGCards.h>
34 #import <NGImap4/NGImap4EnvelopeAddress.h>
37 #import "UIxMailPartICalViewer.h"
39 @implementation UIxMailPartICalViewer
42 [self->storedEventObject release];
43 [self->storedEvent release];
44 [self->attendee release];
46 [self->inCalendar release];
47 [self->inEvent release];
48 [self->dateFormatter release];
54 - (void)resetPathCaches {
55 [super resetPathCaches];
56 [self->inEvent release]; self->inEvent = nil;
57 [self->inCalendar release]; self->inCalendar = nil;
58 [self->storedEventObject release]; self->storedEventObject = nil;
59 [self->storedEvent release]; self->storedEvent = nil;
61 /* not strictly path-related, but useless without it anyway: */
62 [self->attendee release]; self->attendee = nil;
63 [self->item release]; self->item = nil;
66 /* raw content handling */
68 - (NSStringEncoding)fallbackStringEncoding {
70 iCalendar invitations sent by Outlook 2002 have the annoying bug that the
71 mail states an UTF-8 content encoding but the actual iCalendar content is
72 encoding in Latin-1 (or Windows Western?).
74 As a result the content decoding will fail (TODO: always?). In this case we
75 try to decode with Latin-1.
77 Note: we could check for the Outlook x-mailer, but it was considered better
78 to try Latin-1 as a fallback in any case (be tolerant).
80 return NSISOLatin1StringEncoding;
85 - (iCalCalendar *) inCalendar
90 = [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
97 - (BOOL)couldParseCalendar {
98 return [[self inCalendar] isNotNull];
101 - (iCalEvent *)inEvent {
104 if (self->inEvent != nil)
105 return [self->inEvent isNotNull] ? self->inEvent : nil;
107 events = [[self inCalendar] events];
108 if ([events count] > 0) {
109 self->inEvent = [[events objectAtIndex:0] retain];
110 return self->inEvent;
113 self->inEvent = [[NSNull null] retain];
120 - (SOGoDateFormatter *)dateFormatter {
121 if (self->dateFormatter == nil) {
122 self->dateFormatter =
123 [[SOGoDateFormatter alloc] initWithLocale:[self locale]];
124 [self->dateFormatter setFullWeekdayNameAndDetails];
126 return self->dateFormatter;
129 /* below is copied from UIxAppointmentView, can we avoid that? */
131 - (void)setAttendee:(id)_attendee {
132 ASSIGN(self->attendee, _attendee);
135 return self->attendee;
138 - (NSString *) _personForDisplay: (iCalPerson *) person
140 return [NSString stringWithFormat: @"%@ <%@>",
141 [person cnWithoutQuotes],
142 [person rfc822Email]];
145 - (NSString *) attendeeForDisplay
147 return [self _personForDisplay: attendee];
150 - (void)setItem:(id)_item {
151 ASSIGN(self->item, _item);
157 - (NSCalendarDate *) startTime
159 NSCalendarDate *date;
160 NSTimeZone *timeZone;
162 date = [[self authorativeEvent] startDate];
163 timeZone = [[context activeUser] timeZone];
164 [date setTimeZone: timeZone];
169 - (NSCalendarDate *) endTime
171 NSCalendarDate *date;
172 NSTimeZone *timeZone;
174 date = [[self authorativeEvent] endDate];
175 timeZone = [[context activeUser] timeZone];
176 [date setTimeZone: timeZone];
181 - (BOOL)isEndDateOnSameDay {
182 return [[self startTime] isDateOnSameDay:[self endTime]];
184 - (NSTimeInterval)duration {
185 return [[self endTime] timeIntervalSinceDate:[self startTime]];
188 /* calendar folder support */
190 - (id)calendarFolder {
191 /* return scheduling calendar of currently logged-in user */
195 user = [context activeUser];
196 folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
203 - (id)storedEventObject {
204 /* lookup object in the users Calendar */
207 if (self->storedEventObject != nil)
208 return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
210 calendar = [self calendarFolder];
211 if ([calendar isKindOfClass:[NSException class]]) {
212 [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
217 filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
218 if (filename != nil) {
219 // TODO: When we get an exception, this might be an auth issue meaning
220 // that the UID indeed exists but that the user has no access to
222 // Of course this is quite unusual for the private calendar though.
225 tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
226 if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
227 self->storedEventObject = [tmp retain];
231 if (self->storedEventObject == nil)
232 self->storedEventObject = [[NSNull null] retain];
234 return self->storedEventObject;
237 - (BOOL)isEventStoredInCalendar {
238 return [[self storedEventObject] isNotNull];
241 - (iCalEvent *)storedEvent {
242 return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
245 /* organizer tracking */
247 - (NSString *)loggedInUserEMail {
248 return [[[self context] activeUser] primaryEmail];
251 - (iCalEvent *)authorativeEvent {
252 /* DB is considered master, when in DB, ignore mail organizer */
253 return [self isEventStoredInCalendar]
258 - (BOOL)isLoggedInUserTheOrganizer {
259 NSString *loginEMail;
261 if ((loginEMail = [self loggedInUserEMail]) == nil) {
262 [self warnWithFormat:@"Could not determine email of logged in user?"];
266 return [[self authorativeEvent] isOrganizer:loginEMail];
269 - (BOOL)isLoggedInUserAnAttendee {
270 NSString *loginEMail;
272 if ((loginEMail = [self loggedInUserEMail]) == nil) {
273 [self warnWithFormat:@"Could not determine email of logged in user?"];
277 return [[self authorativeEvent] isParticipant:loginEMail];
282 - (NSString *) organizerDisplayName
284 iCalPerson *organizer;
287 organizer = [[self authorativeEvent] organizer];
289 value = [self _personForDisplay: organizer];
291 value = @"[todo: no organizer set, use 'from']";
298 - (NGImap4EnvelopeAddress *)replySenderAddress {
300 The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
301 the actual attendees. BUT the attendee field contains the reply-status!
305 tmp = [[self clientObject] fromEnvelopeAddresses];
306 if ([tmp count] == 0) return nil;
307 return [tmp objectAtIndex:0];
310 - (NSString *)replySenderEMail {
311 return [[self replySenderAddress] email];
313 - (NSString *)replySenderBaseEMail {
314 return [[self replySenderAddress] baseEMail];
317 - (iCalPerson *)inReplyAttendee {
320 attendees = [[self inEvent] attendees];
321 if ([attendees count] == 0)
323 if ([attendees count] > 1)
324 [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
326 return [attendees objectAtIndex:0];
328 - (iCalPerson *)storedReplyAttendee {
330 TODO: since an attendee can have multiple email addresses, maybe we
331 should translate the email to an internal uid and then retrieve
332 all emails addresses for matching the participant.
334 Note: -findParticipantWithEmail: does not parse the email!
339 if ((e = [self storedEvent]) == nil)
341 if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
343 if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
347 - (BOOL)isReplySenderAnAttendee {
348 return [[self storedReplyAttendee] isNotNull];
354 return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
357 return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
359 - (id)tentativeLink {
360 return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
363 @end /* UIxMailPartICalViewer */