1 /* SOGoAppointmentFolderObject.m - this file is part of SOGo
2 *
3 * Copyright (C) 2010-2012 Inverse inc.
4 *
5 * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
6 *
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This file is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING.  If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23#import <Foundation/NSArray.h>
24#import <Foundation/NSAutoreleasePool.h>
25#import <Foundation/NSCalendarDate.h>
26#import <Foundation/NSDictionary.h>
27#import <Foundation/NSString.h>
28
29#import <NGObjWeb/WOContext+SoObjects.h>
30#import <NGObjWeb/WOResponse.h>
31#import <NGExtensions/NSObject+Logs.h>
32
33#import <NGCards/iCalCalendar.h>
34#import <NGCards/iCalTimeZone.h>
35
36#import <SOGo/SOGoBuild.h>
37#import <SOGo/SOGoDomainDefaults.h>
38#import <SOGo/SOGoUser.h>
39
40#import "SOGoAppointmentFolder.h"
41#import "SOGoCalendarComponent.h"
42
43#import "SOGoAppointmentFolderObject.h"
44
45static NSArray *contentFields = nil;
46
47@implementation SOGoAppointmentFolderObject
48
49+ (void) initialize
50{
51  if (!contentFields)
52    contentFields = [[NSArray alloc] initWithObjects: @"c_name", @"c_version",
53                                     @"c_lastmodified", @"c_creationdate",
54                                     @"c_component", @"c_content", nil];
55}
56
57- (id) init
58{
59  int davCalendarStartTimeLimit;
60  SOGoUser *user;
61
62  if ((self = [super init]))
63    {
64      folder = nil;
65
66      user = [context activeUser];
67      davCalendarStartTimeLimit
68        = [[user domainDefaults] davCalendarStartTimeLimit];
69      /* 43200 = 60 * 60 * 24 / 2 */
70      davTimeHalfLimitSeconds = davCalendarStartTimeLimit * 43200;
71    }
72
73  return self;
74}
75
76- (void) dealloc
77{
78  [folder release];
79  [super dealloc];
80}
81
82- (BOOL) isFolderish
83{
84  return NO;
85}
86
87- (SOGoAppointmentFolder *) _folder
88{
89  NSString *folderName;
90  int length;
91
92  if (!folder)
93    {
94      length = [nameInContainer length];
95      if (length > 3)
96        {
97          folderName = [nameInContainer substringToIndex: length - 4];
98          folder = [container lookupName: folderName
99                               inContext: context
100                                 acquire: NO];
101          [folder retain];
102        }
103    }
104
105  return folder;
106}
107
108- (NSArray *) aclsForUser: (NSString *) login
109{
110  return [[self _folder] aclsForUser: login];
111}
112
113- (void) _extractTimeZones: (NSArray *) timeZones
114            intoDictionary: (NSMutableDictionary *) tzDict
115{
116  int count, max;
117  iCalTimeZone *timeZone;
118  NSString *tzId;
119
120  max = [timeZones count];
121  for (count = 0; count < max; count++)
122    {
123      timeZone = [timeZones objectAtIndex: count];
124      tzId = [timeZone tzId];
125      if (![tzDict objectForKey: tzId])
126        [tzDict setObject: timeZone forKey: tzId];
127    }
128}
129
130- (void) _extractCalendarsData: (NSArray *) calendars
131                 intoTimeZones: (NSMutableDictionary *) timeZones
132                 andComponents: (NSMutableArray *) components
133{
134  int count, max;
135  iCalCalendar *calendar;
136
137  max = [calendars count];
138  for (count = 0; count < max; count++)
139    {
140      calendar = [calendars objectAtIndex: count];
141      [self _extractTimeZones: [calendar timezones]
142               intoDictionary: timeZones];
143      [components addObjectsFromArray: [calendar allObjects]];
144    }
145}
146
147- (NSArray *) _fetchFolderRecords
148{
149  NSCalendarDate *start, *end;
150
151  if (davTimeHalfLimitSeconds)
152    {
153      start = [[NSCalendarDate date] addYear: 0
154                                       month: 0
155                                         day: 0
156                                        hour: 0
157                                      minute: 0
158                                      second: -davTimeHalfLimitSeconds];
159      end = [start addYear: 0
160                     month: 0
161                       day: 0
162                      hour: 0
163                    minute: 0
164                    second: (2 * davTimeHalfLimitSeconds)];
165    }
166  else
167    {
168      start = nil;
169      end = nil;
170    }
171
172  return [[self _folder] bareFetchFields: contentFields
173                                    from: start
174                                      to: end
175                                   title: nil
176                               component: nil
177                       additionalFilters: nil];
178}
179
180- (NSArray *) _folderCalendars
181{
182  NSArray *records;
183  NSMutableArray *calendars;
184  SOGoCalendarComponent *component;
185  NSAutoreleasePool *pool;
186  int count, max;
187  iCalCalendar *calendar;
188  NSString *name;
189
190  records = [self _fetchFolderRecords];
191  max = [records count];
192  calendars = [NSMutableArray arrayWithCapacity: max];
193  pool = nil;
194
195  // This can consume a significant amount of memory so
196  // we use a local autorelease pool to avoid running
197  // out of memory.
198  for (count = 0; count < max; count++)
199    {
200      if (count % 100 == 0)
201	{
202	  RELEASE(pool);
203	  pool = [[NSAutoreleasePool alloc] init];
204	}
205
206      name = [[records objectAtIndex: count] objectForKey: @"c_name"];
207      component = [folder lookupName: name
208                           inContext: context
209                             acquire: NO];
210      calendar = [component calendar: NO secure: !activeUserIsOwner];
211      if (calendar)
212        [calendars addObject: calendar];
213      else
214        [self errorWithFormat: @"record with c_name '%@' should obviously not"
215              @" be listed here", name];
216    }
217
218  RELEASE(pool);
219
220  return calendars;
221}
222
223- (iCalCalendar *) contentCalendar
224{
225  NSArray *calendars;
226  iCalCalendar *calendar;
227  NSMutableDictionary *timeZones;
228  NSMutableArray *components;
229
230  calendars = [self _folderCalendars];
231  timeZones = [NSMutableDictionary dictionaryWithCapacity: 16];
232  components = [NSMutableArray arrayWithCapacity: [calendars count] * 2];
233  [self _extractCalendarsData: calendars
234                intoTimeZones: timeZones andComponents: components];
235
236  calendar = [iCalCalendar groupWithTag: @"vcalendar"];
237  [calendar setMethod: @"PUBLISH"];
238  [calendar setVersion: @"2.0"];
239  [calendar setProdID: [NSString stringWithFormat:
240                                   @"-//Inverse inc./SOGo %@//EN",
241                                 SOGoVersion]];
242  [calendar addChildren: [timeZones allValues]];
243  [calendar addChildren: components];
244
245  return calendar;
246}
247
248- (id) davEntityTag
249{
250  return [[self _folder] davCollectionTag];
251}
252
253- (NSString *) contentAsString
254{
255  [self subclassResponsibility: _cmd];
256
257  return nil;
258}
259
260- (NSString *) davContentType
261{
262  [self subclassResponsibility: _cmd];
263
264  return nil;
265}
266
267- (NSString *) displayName
268{
269  return [[self _folder] displayName];
270}
271
272- (NSString *) davDisplayName
273{
274  return [self displayName];
275}
276
277@end
278