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 <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 {
158 NSCalendarDate *date;
160 date = [[self authorativeEvent] startDate];
161 [date setTimeZone:[[self clientObject] userTimeZone]];
165 - (NSCalendarDate *)endTime {
166 NSCalendarDate *date;
168 date = [[self authorativeEvent] endDate];
169 [date setTimeZone:[[self clientObject] userTimeZone]];
173 - (BOOL)isEndDateOnSameDay {
174 return [[self startTime] isDateOnSameDay:[self endTime]];
176 - (NSTimeInterval)duration {
177 return [[self endTime] timeIntervalSinceDate:[self startTime]];
180 /* calendar folder support */
182 - (id)calendarFolder {
183 /* return scheduling calendar of currently logged-in user */
184 return [[[self context] activeUser] schedulingCalendarInContext:
188 - (id)storedEventObject {
189 /* lookup object in the users Calendar */
192 if (self->storedEventObject != nil)
193 return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
195 calendar = [self calendarFolder];
196 if ([calendar isKindOfClass:[NSException class]]) {
197 [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
202 filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
203 if (filename != nil) {
204 // TODO: When we get an exception, this might be an auth issue meaning
205 // that the UID indeed exists but that the user has no access to
207 // Of course this is quite unusual for the private calendar though.
210 tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
211 if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
212 self->storedEventObject = [tmp retain];
216 if (self->storedEventObject == nil)
217 self->storedEventObject = [[NSNull null] retain];
219 return self->storedEventObject;
222 - (BOOL)isEventStoredInCalendar {
223 return [[self storedEventObject] isNotNull];
226 - (iCalEvent *)storedEvent {
227 return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
230 /* organizer tracking */
232 - (NSString *)loggedInUserEMail {
233 return [[[self context] activeUser] email];
236 - (iCalEvent *)authorativeEvent {
237 /* DB is considered master, when in DB, ignore mail organizer */
238 return [self isEventStoredInCalendar]
243 - (BOOL)isLoggedInUserTheOrganizer {
244 NSString *loginEMail;
246 if ((loginEMail = [self loggedInUserEMail]) == nil) {
247 [self warnWithFormat:@"Could not determine email of logged in user?"];
251 return [[self authorativeEvent] isOrganizer:loginEMail];
254 - (BOOL)isLoggedInUserAnAttendee {
255 NSString *loginEMail;
257 if ((loginEMail = [self loggedInUserEMail]) == nil) {
258 [self warnWithFormat:@"Could not determine email of logged in user?"];
262 return [[self authorativeEvent] isParticipant:loginEMail];
267 - (NSString *) organizerDisplayName
269 iCalPerson *organizer;
272 organizer = [[self authorativeEvent] organizer];
274 value = [self _personForDisplay: organizer];
276 value = @"[todo: no organizer set, use 'from']";
283 - (NGImap4EnvelopeAddress *)replySenderAddress {
285 The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
286 the actual attendees. BUT the attendee field contains the reply-status!
290 tmp = [[self clientObject] fromEnvelopeAddresses];
291 if ([tmp count] == 0) return nil;
292 return [tmp objectAtIndex:0];
295 - (NSString *)replySenderEMail {
296 return [[self replySenderAddress] email];
298 - (NSString *)replySenderBaseEMail {
299 return [[self replySenderAddress] baseEMail];
302 - (iCalPerson *)inReplyAttendee {
305 attendees = [[self inEvent] attendees];
306 if ([attendees count] == 0)
308 if ([attendees count] > 1)
309 [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
311 return [attendees objectAtIndex:0];
313 - (iCalPerson *)storedReplyAttendee {
315 TODO: since an attendee can have multiple email addresses, maybe we
316 should translate the email to an internal uid and then retrieve
317 all emails addresses for matching the participant.
319 Note: -findParticipantWithEmail: does not parse the email!
324 if ((e = [self storedEvent]) == nil)
326 if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
328 if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
332 - (BOOL)isReplySenderAnAttendee {
333 return [[self storedReplyAttendee] isNotNull];
339 return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
342 return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
344 - (id)tentativeLink {
345 return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
348 @end /* UIxMailPartICalViewer */