]> err.no Git - scalable-opengroupware.org/blob - Main/SOGo.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1147 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / Main / SOGo.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 #import <Foundation/NSDebug.h>
23 #import <Foundation/NSData.h>
24 #import <Foundation/NSProcessInfo.h>
25 #import <Foundation/NSRunLoop.h>
26 #import <Foundation/NSURL.h>
27 #import <Foundation/NSUserDefaults.h>
28
29 #import <GDLAccess/EOAdaptorChannel.h>
30 #import <GDLContentStore/GCSChannelManager.h>
31
32 #import <NGObjWeb/SoApplication.h>
33 #import <NGObjWeb/SoClassSecurityInfo.h>
34 #import <NGObjWeb/WOContext.h>
35 #import <NGObjWeb/WORequest.h>
36
37 #import <NGExtensions/NGBundleManager.h>
38 #import <NGExtensions/NSNull+misc.h>
39 #import <NGExtensions/NSObject+Logs.h>
40 #import <NGExtensions/NSProcessInfo+misc.h>
41
42 #import <WEExtensions/WEResourceManager.h>
43
44 #import <SoObjects/SOGo/SOGoAuthenticator.h>
45 #import <SoObjects/SOGo/SOGoUserFolder.h>
46 #import <SoObjects/SOGo/SOGoUser.h>
47 #import <SoObjects/SOGo/SOGoPermissions.h>
48
49 #import "build.h"
50 #import "SOGoProductLoader.h"
51
52 @interface SOGo : SoApplication
53 {
54   NSMutableDictionary *localeLUT;
55 }
56
57 - (NSDictionary *) currentLocaleConsideringLanguages:(NSArray *)_langs;
58 - (NSDictionary *) localeForLanguageNamed:(NSString *)_name;
59
60 @end
61
62 @implementation SOGo
63
64 static unsigned int vMemSizeLimit = 0;
65 static BOOL doCrashOnSessionCreate = NO;
66 static BOOL hasCheckedTables = NO;
67
68 #ifdef GNUSTEP_BASE_LIBRARY
69 static BOOL debugObjectAllocation = NO;
70 #endif
71
72 + (void) initialize
73 {
74   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
75   SoClassSecurityInfo *sInfo;
76   NSArray *basicRoles;
77   id tmp;
78
79   NSLog (@"starting SOGo (build %@)", SOGoBuildDate);
80   
81   doCrashOnSessionCreate = [ud boolForKey:@"SOGoCrashOnSessionCreate"];
82 #ifdef GNUSTEP_BASE_LIBRARY
83   debugObjectAllocation = [ud boolForKey: @"SOGoDebugObjectAllocation"];
84   if (debugObjectAllocation)
85     {
86       NSLog (@"activating stats on object allocation");
87       GSDebugAllocationActive (YES);
88     }
89 #endif
90
91   /* vMem size check - default is 200MB */
92     
93   tmp = [ud objectForKey: @"SxVMemLimit"];
94   vMemSizeLimit = ((tmp != nil) ? [tmp intValue] : 200);
95   if (vMemSizeLimit > 0)
96     NSLog(@"Note: vmem size check enabled: shutting down app when "
97           @"vmem > %d MB", vMemSizeLimit);
98 #if LIB_FOUNDATION_LIBRARY
99   if ([ud boolForKey:@"SOGoEnableDoubleReleaseCheck"])
100     [NSAutoreleasePool enableDoubleReleaseCheck: YES];
101 #endif
102
103   /* SoClass security declarations */
104   sInfo = [self soClassSecurityInfo];
105   /* require View permission to access the root (bound to authenticated ...) */
106   [sInfo declareObjectProtected: SoPerm_View];
107
108   /* to allow public access to all contained objects (subkeys) */
109   [sInfo setDefaultAccess: @"allow"];
110
111   basicRoles = [NSArray arrayWithObjects: SoRole_Authenticated,
112                         SOGoRole_FreeBusy, nil];
113
114   /* require Authenticated role for View and WebDAV */
115   [sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_View];
116   [sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_WebDAVAccess];
117 }
118
119 - (id) init
120 {
121   if ((self = [super init]))
122     {
123       WOResourceManager *rm;
124
125       /* ensure core SoClass'es are setup */
126       [$(@"SOGoObject") soClass];
127       [$(@"SOGoContentObject") soClass];
128       [$(@"SOGoFolder") soClass];
129
130       /* setup locale cache */
131       localeLUT = [[NSMutableDictionary alloc] initWithCapacity:2];
132     
133       /* load products */
134       [[SOGoProductLoader productLoader] loadProducts];
135     
136       /* setup resource manager */
137       rm = [[WEResourceManager alloc] init];
138       [self setResourceManager:rm];
139     }
140
141   return self;
142 }
143
144 - (void) dealloc
145 {
146   [localeLUT release];
147   [super dealloc];
148 }
149
150 - (void) _checkTableWithCM: (GCSChannelManager *) cm
151                   tableURL: (NSString *) url
152                    andType: (NSString *) tableType
153 {
154   NSString *tableName, *descFile, *tableFile, *fileSuffix;
155   EOAdaptorChannel *tc;
156   NGBundleManager *bm;
157   NSBundle *bundle;
158   unsigned int length;
159   NSURL *channelURL;
160
161   bm = [NGBundleManager defaultBundleManager];
162
163   channelURL = [NSURL URLWithString: url];
164   fileSuffix = [channelURL scheme];
165   tc = [cm acquireOpenChannelForURL: channelURL];
166
167   tableName = [url lastPathComponent];
168   if ([tc evaluateExpressionX:
169             [NSString stringWithFormat: @"SELECT count(*) FROM %@", tableName]])
170     {
171       bundle = [bm bundleWithName: @"MainUI" type: @"SOGo"];
172       length = [tableType length] - 3;
173       tableFile = [tableType substringToIndex: length];
174       descFile
175         = [bundle pathForResource: [NSString stringWithFormat: @"%@-%@",
176                                              tableFile, fileSuffix]
177                          ofType: @"sql"];
178       if (!descFile)
179         descFile = [bundle pathForResource: tableFile ofType: @"sql"];
180       if (![tc evaluateExpressionX:
181                  [NSString stringWithContentsOfFile: descFile]])
182         [self logWithFormat: @"table '%@' successfully created!", tableName];
183     }
184   else
185     [tc cancelFetch];
186
187   [cm releaseChannel: tc];
188 }
189
190 - (BOOL) _checkMandatoryTables
191 {
192   GCSChannelManager *cm;
193   NSString *urlStrings[] = {@"AgenorProfileURL", @"OCSFolderInfoURL", nil};
194   NSString **urlString;
195   NSString *value;
196   NSUserDefaults *ud;
197   BOOL ok;
198
199   ud = [NSUserDefaults standardUserDefaults];
200   ok = YES;
201   cm = [GCSChannelManager defaultChannelManager];
202
203   urlString = urlStrings;
204   while (ok && *urlString)
205     {
206       value = [ud stringForKey: *urlString];
207       if (value)
208         {
209           [self _checkTableWithCM: cm tableURL: value andType: *urlString];
210           urlString++;
211         }
212       else
213         {
214           NSLog (@"No value specified for '%@'", *urlString);
215           ok = NO;
216         }
217     }
218
219   return ok;
220 }
221
222 - (void) run
223 {
224   if (!hasCheckedTables)
225     {
226       hasCheckedTables = YES;
227       [self _checkMandatoryTables];
228     }
229   [super run];
230 }
231
232 /* authenticator */
233
234 - (id) authenticatorInContext: (id) _ctx
235 {
236   return [$(@"SOGoAuthenticator") sharedSOGoAuthenticator];
237 }
238
239 /* name lookup */
240
241 - (BOOL) isUserName: (NSString *) _key
242           inContext: (id) _ctx
243 {
244   if ([_key length] < 1)
245     return NO;
246   
247   if (isdigit([_key characterAtIndex:0]))
248     return NO;
249
250   return YES;
251 }
252
253 - (id) lookupUser: (NSString *) _key
254         inContext: (id)_ctx
255 {
256   SOGoUser *user;
257   id userFolder;
258
259   user = [SOGoUser userWithLogin: _key roles: nil];
260   if (user)
261     userFolder = [$(@"SOGoUserFolder") objectWithName: _key
262                    inContainer: self];
263   else
264     userFolder = nil;
265
266   return userFolder;
267 }
268
269 - (void) _setupLocaleInContext: (WOContext *) _ctx
270 {
271   NSArray      *langs;
272   NSDictionary *locale;
273   
274   if ([[_ctx valueForKey:@"locale"] isNotNull])
275     return;
276
277   langs = [[_ctx request] browserLanguages];
278   locale = [self currentLocaleConsideringLanguages:langs];
279   [_ctx takeValue:locale forKey:@"locale"];
280 }
281
282 - (id) lookupName: (NSString *) _key
283         inContext: (id) _ctx
284           acquire: (BOOL) _flag
285 {
286   id obj;
287
288 #ifdef GNUSTEP_BASE_LIBRARY
289   if (debugObjectAllocation)
290     NSLog(@"objects allocated\n%s", GSDebugAllocationList (YES));
291 #endif
292   /* put locale info into the context in case it's not there */
293   [self _setupLocaleInContext:_ctx];
294   
295   /* first check attributes directly bound to the application */
296   if ((obj = [super lookupName:_key inContext:_ctx acquire:_flag]))
297     return obj;
298   
299   /* 
300      The problem is, that at this point we still get request for resources,
301      eg 'favicon.ico'.
302      
303      Addition: we also get queries for various other methods, like "GET" if
304                no method was provided in the query path.
305   */
306   
307   if ([_key isEqualToString:@"favicon.ico"])
308     return nil;
309
310   if ([self isUserName:_key inContext:_ctx])
311     return [self lookupUser:_key inContext:_ctx];
312
313   return nil;
314 }
315
316 /* WebDAV */
317
318 - (NSString *) davDisplayName
319 {
320   /* this is used in the UI, eg in the navigation */
321   return @"SOGo";
322 }
323
324 /* exception handling */
325
326 - (WOResponse *) handleException: (NSException *) _exc
327                        inContext: (WOContext *) _ctx
328 {
329   printf("EXCEPTION: %s\n", [[_exc description] cString]);
330   abort();
331 }
332
333 /* runtime maintenance */
334
335 - (void) checkIfDaemonHasToBeShutdown
336 {
337   unsigned int vmem;
338
339   if (vMemSizeLimit > 0)
340     {
341       vmem = [[NSProcessInfo processInfo] virtualMemorySize]/1048576;
342
343       if (vmem > vMemSizeLimit)
344         {
345           [self logWithFormat:
346                   @"terminating app, vMem size limit (%d MB) has been reached"
347                 @" (currently %d MB)",
348                 vMemSizeLimit, vmem];
349 //           if (debugObjectAllocation)
350 //             [self _dumpClassAllocation];
351           [self terminate];
352         }
353     }
354 }
355
356 - (WOResponse *) dispatchRequest: (WORequest *) _request
357 {
358   static NSArray *runLoopModes = nil;
359   WOResponse *resp;
360
361   resp = [super dispatchRequest: _request];
362
363   if (![self isTerminating])
364     {
365       if (!runLoopModes)
366         runLoopModes = [[NSArray alloc] initWithObjects: NSDefaultRunLoopMode, nil];
367   
368       // TODO: a bit complicated? (-perform:afterDelay: doesn't work?)
369       [[NSRunLoop currentRunLoop] performSelector:
370                                     @selector (checkIfDaemonHasToBeShutdown)
371                                   target: self argument: nil
372                                   order:1 modes:runLoopModes];
373     }
374
375   return resp;
376 }
377
378 /* session management */
379
380 - (id) createSessionForRequest: (WORequest *) _request
381 {
382   [self warnWithFormat: @"session creation requested!"];
383   if (doCrashOnSessionCreate)
384     abort();
385   return [super createSessionForRequest:_request];
386 }
387
388 /* localization */
389
390 - (NSDictionary *) currentLocaleConsideringLanguages: (NSArray *) langs
391 {
392   NSEnumerator *enumerator;
393   NSString *lname;
394   NSDictionary *locale;
395
396   enumerator = [langs objectEnumerator];
397   lname = nil;
398   locale = nil;
399   lname = [enumerator nextObject];
400   while (lname && !locale)
401     {
402       locale = [self localeForLanguageNamed: lname];
403       lname = [enumerator nextObject];
404     }
405
406   if (!locale)
407     locale = [self localeForLanguageNamed: @"English"];
408
409   /* no appropriate language, fallback to default */
410   return locale;
411 }
412
413 - (NSString *) pathToLocaleForLanguageNamed: (NSString *) _name
414 {
415   static Class MainProduct = Nil;
416   NSString *lpath;
417
418   lpath = [[self resourceManager] pathForResourceNamed: @"Locale"
419                                   inFramework: nil
420                                   languages: [NSArray arrayWithObject:_name]];
421   if (![lpath length])
422     {
423       if (!MainProduct)
424         {
425           MainProduct = $(@"MainUIProduct");
426           if (!MainProduct)
427             [self errorWithFormat: @"did not find MainUIProduct class!"];
428         }
429
430       lpath = [(id) MainProduct pathToLocaleForLanguageNamed: _name];
431       if (![lpath length])
432         lpath = nil;
433     }
434
435   return lpath;
436 }
437
438 - (NSDictionary *) localeForLanguageNamed: (NSString *) _name
439 {
440   NSString     *lpath;
441   id           data;
442   NSDictionary *locale;
443
444   locale = nil;
445   if ([_name length] > 0)
446     {
447       locale = [localeLUT objectForKey: _name];
448       if (!locale)
449         {
450           lpath = [self pathToLocaleForLanguageNamed:_name];
451           if (lpath)
452             {
453               data = [NSData dataWithContentsOfFile: lpath];
454               if (data)
455                 {
456                   data = [[[NSString alloc] initWithData: data
457                                             encoding: NSUTF8StringEncoding] autorelease];
458                   locale = [data propertyList];
459                   if (locale) 
460                     [localeLUT setObject: locale forKey: _name];
461                   else
462                     [self logWithFormat:@"%s couldn't load locale with name:%@",
463                           __PRETTY_FUNCTION__,
464                           _name];
465                 }
466               else
467                 [self logWithFormat:@"%s didn't find locale with name: %@",
468                       __PRETTY_FUNCTION__,
469                       _name];
470             }
471           else
472             [self errorWithFormat:@"did not find Locale for language: %@", _name];
473         }
474     }
475   else
476     [self errorWithFormat:@"%s: name parameter must not be nil!",
477           __PRETTY_FUNCTION__];
478
479   return locale;
480 }
481
482 /* name (used by the WEResourceManager) */
483
484 - (NSString *) name
485 {
486   return @"SOGo-0.9";
487 }
488
489 @end /* SOGo */