]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartICalViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1263 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailPartViewers / UIxMailPartICalViewer.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3   
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 /*
23   UIxMailPartICalViewer
24  
25   Show plain/calendar mail parts.
26 */
27
28 #import <NGObjWeb/WOResponse.h>
29
30 #import <NGExtensions/NSCalendarDate+misc.h>
31 #import <NGExtensions/NSNull+misc.h>
32 #import <NGExtensions/NSObject+Logs.h>
33
34 #import <NGImap4/NGImap4EnvelopeAddress.h>
35
36 #import <NGCards/iCalCalendar.h>
37 #import <NGCards/iCalEvent.h>
38 #import <NGCards/iCalPerson.h>
39 #import <NGCards/iCalDateTime.h>
40
41 #import <SoObjects/SOGo/SOGoDateFormatter.h>
42 #import <SoObjects/SOGo/SOGoUser.h>
43 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
44 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
45 #import <SoObjects/Mailer/SOGoMailObject.h>
46
47 #import "UIxMailPartICalViewer.h"
48
49 @implementation UIxMailPartICalViewer
50
51 - (void) dealloc
52 {
53   [storedEventObject release];
54   [storedEvent release];
55   [attendee release];
56   [item release];
57   [inCalendar release];
58   [inEvent release];
59   [dateFormatter release];
60   [super dealloc];
61 }
62
63 /* maintain caches */
64
65 - (void) resetPathCaches
66 {
67   [super resetPathCaches];
68   [inEvent release]; inEvent = nil;
69   [inCalendar release]; inCalendar = nil;
70   [storedEventObject release]; storedEventObject = nil;
71   [storedEvent release]; storedEvent = nil;
72  
73   /* not strictly path-related, but useless without it anyway: */
74   [attendee release]; attendee = nil;
75   [item release]; item = nil;
76 }
77
78 /* raw content handling */
79
80 - (NSStringEncoding) fallbackStringEncoding
81 {
82   /*
83     iCalendar invitations sent by Outlook 2002 have the annoying bug that the
84     mail states an UTF-8 content encoding but the actual iCalendar content is
85     encoding in Latin-1 (or Windows Western?).
86  
87     As a result the content decoding will fail (TODO: always?). In this case we
88     try to decode with Latin-1.
89  
90     Note: we could check for the Outlook x-mailer, but it was considered better
91     to try Latin-1 as a fallback in any case (be tolerant).
92   */
93   return NSISOLatin1StringEncoding;
94 }
95
96 /* accessors */
97
98 - (iCalCalendar *) inCalendar
99 {
100   if (!inCalendar)
101     {
102       inCalendar
103         = [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
104       [inCalendar retain];
105     }
106
107   return inCalendar;
108 }
109
110 - (BOOL) couldParseCalendar
111 {
112   return [[self inCalendar] isNotNull];
113 }
114
115 - (iCalEvent *) inEvent
116 {
117   NSArray *events;
118  
119   if (inEvent)
120     return [inEvent isNotNull] ? inEvent : nil;
121  
122   events = [[self inCalendar] events];
123   if ([events count] > 0) {
124     inEvent = [[events objectAtIndex:0] retain];
125     return inEvent;
126   }
127   else {
128     inEvent = [[NSNull null] retain];
129     return nil;
130   }
131 }
132
133 /* formatters */
134
135 - (SOGoDateFormatter *) dateFormatter
136 {
137   if (dateFormatter == nil) {
138     dateFormatter = [[context activeUser] dateFormatterInContext: context];
139     [dateFormatter retain];
140   }
141
142   return dateFormatter;
143 }
144
145 /* below is copied from UIxAppointmentView, can we avoid that? */
146
147 - (void) setAttendee: (id) _attendee
148 {
149   ASSIGN(attendee, _attendee);
150 }
151
152 - (id) attendee
153 {
154   return attendee;
155 }
156
157 - (NSString *) _personForDisplay: (iCalPerson *) person
158 {
159   NSString *fn, *email, *result;
160
161   fn = [person cnWithoutQuotes];
162   email = [person rfc822Email];
163   if ([fn length])
164     result = [NSString stringWithFormat: @"%@ <%@>",
165                        fn, email];
166   else
167     result = email;
168
169   return result;
170 }
171
172 - (NSString *) attendeeForDisplay
173 {
174   return [self _personForDisplay: attendee];
175 }
176
177 - (void) setItem: (id) _item
178 {
179   ASSIGN(item, _item);
180 }
181
182 - (id) item
183 {
184   return item;
185 }
186
187 - (NSCalendarDate *) startTime
188 {
189   NSCalendarDate *date;
190   NSTimeZone *timeZone;
191  
192   date = [[self authorativeEvent] startDate];
193   timeZone = [[context activeUser] timeZone];
194   [date setTimeZone: timeZone];
195
196   return date;
197 }
198
199 - (NSCalendarDate *) endTime
200 {
201   NSCalendarDate *date;
202   NSTimeZone *timeZone;
203  
204   date = [[self authorativeEvent] endDate];
205   timeZone = [[context activeUser] timeZone];
206   [date setTimeZone: timeZone];
207
208   return date;
209 }
210
211 - (BOOL) isEndDateOnSameDay
212 {
213   return [[self startTime] isDateOnSameDay:[self endTime]];
214 }
215
216 - (NSTimeInterval) duration
217 {
218   return [[self endTime] timeIntervalSinceDate:[self startTime]];
219 }
220
221 /* calendar folder support */
222
223 - (id) calendarFolder
224 {
225   /* return scheduling calendar of currently logged-in user */
226   SOGoUser *user;
227   id folder;
228
229   user = [context activeUser];
230   folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
231                                                 inContext: context
232                                                 acquire: NO];
233
234   return [folder lookupName: @"personal" inContext: context acquire: NO];
235 }
236
237 - (id) storedEventObject
238 {
239   /* lookup object in the users Calendar */
240   id calendar;
241  
242   if (storedEventObject)
243     return [storedEventObject isNotNull] ? storedEventObject : nil;
244  
245   calendar = [self calendarFolder];
246   if ([calendar isKindOfClass:[NSException class]]) {
247     [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
248   }
249   else {
250     NSString *filename;
251  
252     filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
253     if (filename) {
254       // TODO: When we get an exception, this might be an auth issue meaning
255       // that the UID indeed exists but that the user has no access to
256       // the object.
257       // Of course this is quite unusual for the private calendar though.
258       id tmp;
259  
260       tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
261       if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
262         storedEventObject = [tmp retain];
263     }
264   }
265  
266   if (storedEventObject == nil)
267     storedEventObject = [[NSNull null] retain];
268  
269   return storedEventObject;
270 }
271
272 - (BOOL) isEventStoredInCalendar
273 {
274   return [[self storedEventObject] isNotNull];
275 }
276
277 - (iCalEvent *) storedEvent
278 {
279   return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
280 }
281
282 /* organizer tracking */
283
284 - (NSString *) loggedInUserEMail
285 {
286   NSDictionary *identity;
287
288   identity = [[context activeUser] primaryIdentity];
289
290   return [identity objectForKey: @"email"];
291 }
292
293 - (iCalEvent *) authorativeEvent
294 {
295   iCalEvent *authorativeEvent;
296
297   if ([[self storedEvent] compare: [self inEvent]]
298       == NSOrderedAscending)
299     authorativeEvent = inEvent;
300   else
301     authorativeEvent = storedEventObject;
302
303   return authorativeEvent;
304 }
305
306 - (BOOL) isLoggedInUserTheOrganizer
307 {
308   iCalPerson *organizer;
309  
310   organizer = [[self authorativeEvent] organizer];
311
312   return [[context activeUser] hasEmail: [organizer rfc822Email]];
313 }
314
315 - (BOOL) isLoggedInUserAnAttendee
316 {
317   NSString *loginEMail;
318  
319   if ((loginEMail = [self loggedInUserEMail]) == nil) {
320     [self warnWithFormat:@"Could not determine email of logged in user?"];
321     return NO;
322   }
323
324   return [[self authorativeEvent] isParticipant:loginEMail];
325 }
326
327 /* derived fields */
328
329 - (NSString *) organizerDisplayName
330 {
331   iCalPerson *organizer;
332   NSString *value;
333
334   organizer = [[self authorativeEvent] organizer];
335   if (organizer)
336     value = [self _personForDisplay: organizer];
337   else
338     value = @"[todo: no organizer set, use 'from']";
339
340   return value;
341 }
342
343 /* replies */
344
345 - (NGImap4EnvelopeAddress *) replySenderAddress
346 {
347   /* 
348      The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
349      the actual attendees. BUT the attendee field contains the reply-status!
350   */
351   id tmp;
352  
353   tmp = [[self clientObject] fromEnvelopeAddresses];
354   if ([tmp count] == 0) return nil;
355   return [tmp objectAtIndex:0];
356 }
357
358 - (NSString *) replySenderEMail
359 {
360   return [[self replySenderAddress] email];
361 }
362
363 - (NSString *) replySenderBaseEMail
364 {
365   return [[self replySenderAddress] baseEMail];
366 }
367
368 - (iCalPerson *) inReplyAttendee
369 {
370   NSArray *attendees;
371  
372   attendees = [[self inEvent] attendees];
373   if ([attendees count] == 0)
374     return nil;
375   if ([attendees count] > 1)
376     [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
377  
378   return [attendees objectAtIndex:0];
379 }
380
381 - (iCalPerson *) storedReplyAttendee
382 {
383   /*
384     TODO: since an attendee can have multiple email addresses, maybe we
385     should translate the email to an internal uid and then retrieve
386     all emails addresses for matching the participant.
387  
388     Note: -findParticipantWithEmail: does not parse the email!
389   */
390   iCalEvent *e;
391   iCalPerson *p;
392
393   p = nil;
394  
395   e = [self storedEvent];
396   if (e)
397     {
398       p = [e findParticipantWithEmail: [self replySenderBaseEMail]];
399       if (!p)
400         p = [e findParticipantWithEmail:[self replySenderEMail]];
401     }
402
403   return p;
404 }
405
406 - (BOOL) isReplySenderAnAttendee
407 {
408   return [[self storedReplyAttendee] isNotNull];
409 }
410
411 @end /* UIxMailPartICalViewer */