]> err.no Git - scalable-opengroupware.org/blob - Main/SOGo.m
b4d247f8cd0df4bf33f89ce8bfb56ebe7a4decd6
[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;
155   EOAdaptorChannel *tc;
156   NGBundleManager *bm;
157   NSBundle *bundle;
158   unsigned int length;
159
160   bm = [NGBundleManager defaultBundleManager];
161   
162   tc = [cm acquireOpenChannelForURL: [NSURL URLWithString: url]];
163
164   tableName = [url lastPathComponent];
165   if ([tc evaluateExpressionX:
166             [NSString stringWithFormat: @"SELECT count(*) FROM %@", tableName]])
167     {
168       bundle = [bm bundleWithName: @"MainUI" type: @"SOGo"];
169       length = [tableType length] - 3;
170       descFile = [bundle pathForResource: [tableType substringToIndex: length]
171                          ofType: @"sql"];
172       if (![tc evaluateExpressionX:
173                  [NSString stringWithContentsOfFile: descFile]])
174         [self logWithFormat: @"table '%@' successfully created!", tableName];
175     }
176   else
177     [tc cancelFetch];
178
179   [cm releaseChannel: tc];
180 }
181
182 - (BOOL) _checkMandatoryTables
183 {
184   GCSChannelManager *cm;
185   NSString *urlStrings[] = {@"AgenorProfileURL", @"OCSFolderInfoURL", nil};
186   NSString **urlString;
187   NSString *value;
188   NSUserDefaults *ud;
189   BOOL ok;
190
191   ud = [NSUserDefaults standardUserDefaults];
192   ok = YES;
193   cm = [GCSChannelManager defaultChannelManager];
194
195   urlString = urlStrings;
196   while (ok && *urlString)
197     {
198       value = [ud stringForKey: *urlString];
199       if (value)
200         {
201           [self _checkTableWithCM: cm tableURL: value andType: *urlString];
202           urlString++;
203         }
204       else
205         {
206           NSLog (@"No value specified for '%@'", *urlString);
207           ok = NO;
208         }
209     }
210
211   return ok;
212 }
213
214 - (void) run
215 {
216   if (!hasCheckedTables)
217     {
218       hasCheckedTables = YES;
219       [self _checkMandatoryTables];
220     }
221   [super run];
222 }
223
224 /* authenticator */
225
226 - (id) authenticatorInContext: (id) _ctx
227 {
228   return [$(@"SOGoAuthenticator") sharedSOGoAuthenticator];
229 }
230
231 /* name lookup */
232
233 - (BOOL) isUserName: (NSString *) _key
234           inContext: (id) _ctx
235 {
236   if ([_key length] < 1)
237     return NO;
238   
239   if (isdigit([_key characterAtIndex:0]))
240     return NO;
241
242   return YES;
243 }
244
245 - (id) lookupUser: (NSString *) _key
246         inContext: (id)_ctx
247 {
248   SOGoUser *user;
249   id userFolder;
250
251   user = [SOGoUser userWithLogin: _key roles: nil];
252   if (user)
253     userFolder = [$(@"SOGoUserFolder") objectWithName: _key
254                    inContainer: self];
255   else
256     userFolder = nil;
257
258   return userFolder;
259 }
260
261 - (void) _setupLocaleInContext: (WOContext *) _ctx
262 {
263   NSArray      *langs;
264   NSDictionary *locale;
265   
266   if ([[_ctx valueForKey:@"locale"] isNotNull])
267     return;
268
269   langs = [[_ctx request] browserLanguages];
270   locale = [self currentLocaleConsideringLanguages:langs];
271   [_ctx takeValue:locale forKey:@"locale"];
272 }
273
274 - (id) lookupName: (NSString *) _key
275         inContext: (id) _ctx
276           acquire: (BOOL) _flag
277 {
278   id obj;
279
280 #ifdef GNUSTEP_BASE_LIBRARY
281   if (debugObjectAllocation)
282     NSLog(@"objects allocated\n%s", GSDebugAllocationList (YES));
283 #endif
284   /* put locale info into the context in case it's not there */
285   [self _setupLocaleInContext:_ctx];
286   
287   /* first check attributes directly bound to the application */
288   if ((obj = [super lookupName:_key inContext:_ctx acquire:_flag]))
289     return obj;
290   
291   /* 
292      The problem is, that at this point we still get request for resources,
293      eg 'favicon.ico'.
294      
295      Addition: we also get queries for various other methods, like "GET" if
296                no method was provided in the query path.
297   */
298   
299   if ([_key isEqualToString:@"favicon.ico"])
300     return nil;
301
302   if ([self isUserName:_key inContext:_ctx])
303     return [self lookupUser:_key inContext:_ctx];
304
305   return nil;
306 }
307
308 /* WebDAV */
309
310 - (NSString *) davDisplayName
311 {
312   /* this is used in the UI, eg in the navigation */
313   return @"SOGo";
314 }
315
316 /* exception handling */
317
318 - (WOResponse *) handleException: (NSException *) _exc
319                        inContext: (WOContext *) _ctx
320 {
321   printf("EXCEPTION: %s\n", [[_exc description] cString]);
322   abort();
323 }
324
325 /* runtime maintenance */
326
327 - (void) checkIfDaemonHasToBeShutdown
328 {
329   unsigned int vmem;
330
331   if (vMemSizeLimit > 0)
332     {
333       vmem = [[NSProcessInfo processInfo] virtualMemorySize]/1048576;
334
335       if (vmem > vMemSizeLimit)
336         {
337           [self logWithFormat:
338                   @"terminating app, vMem size limit (%d MB) has been reached"
339                 @" (currently %d MB)",
340                 vMemSizeLimit, vmem];
341 //           if (debugObjectAllocation)
342 //             [self _dumpClassAllocation];
343           [self terminate];
344         }
345     }
346 }
347
348 - (WOResponse *) dispatchRequest: (WORequest *) _request
349 {
350   static NSArray *runLoopModes = nil;
351   WOResponse *resp;
352
353   resp = [super dispatchRequest: _request];
354
355   if (![self isTerminating])
356     {
357       if (!runLoopModes)
358         runLoopModes = [[NSArray alloc] initWithObjects: NSDefaultRunLoopMode, nil];
359   
360       // TODO: a bit complicated? (-perform:afterDelay: doesn't work?)
361       [[NSRunLoop currentRunLoop] performSelector:
362                                     @selector (checkIfDaemonHasToBeShutdown)
363                                   target: self argument: nil
364                                   order:1 modes:runLoopModes];
365     }
366
367   return resp;
368 }
369
370 /* session management */
371
372 - (id) createSessionForRequest: (WORequest *) _request
373 {
374   [self warnWithFormat: @"session creation requested!"];
375   if (doCrashOnSessionCreate)
376     abort();
377   return [super createSessionForRequest:_request];
378 }
379
380 /* localization */
381
382 - (NSDictionary *) currentLocaleConsideringLanguages: (NSArray *) langs
383 {
384   NSEnumerator *enumerator;
385   NSString *lname;
386   NSDictionary *locale;
387
388   enumerator = [langs objectEnumerator];
389   lname = nil;
390   locale = nil;
391   lname = [enumerator nextObject];
392   while (lname && !locale)
393     {
394       locale = [self localeForLanguageNamed: lname];
395       lname = [enumerator nextObject];
396     }
397
398   if (!locale)
399     locale = [self localeForLanguageNamed: @"English"];
400
401   /* no appropriate language, fallback to default */
402   return locale;
403 }
404
405 - (NSString *) pathToLocaleForLanguageNamed: (NSString *) _name
406 {
407   static Class MainProduct = Nil;
408   NSString *lpath;
409
410   lpath = [[self resourceManager] pathForResourceNamed: @"Locale"
411                                   inFramework: nil
412                                   languages: [NSArray arrayWithObject:_name]];
413   if (![lpath length])
414     {
415       if (!MainProduct)
416         {
417           MainProduct = $(@"MainUIProduct");
418           if (!MainProduct)
419             [self errorWithFormat: @"did not find MainUIProduct class!"];
420         }
421
422       lpath = [(id) MainProduct pathToLocaleForLanguageNamed: _name];
423       if (![lpath length])
424         lpath = nil;
425     }
426
427   return lpath;
428 }
429
430 - (NSDictionary *) localeForLanguageNamed: (NSString *) _name
431 {
432   NSString     *lpath;
433   id           data;
434   NSDictionary *locale;
435
436   locale = nil;
437   if ([_name length] > 0)
438     {
439       locale = [localeLUT objectForKey: _name];
440       if (!locale)
441         {
442           lpath = [self pathToLocaleForLanguageNamed:_name];
443           if (lpath)
444             {
445               data = [NSData dataWithContentsOfFile: lpath];
446               if (data)
447                 {
448                   data = [[[NSString alloc] initWithData: data
449                                             encoding: NSUTF8StringEncoding] autorelease];
450                   locale = [data propertyList];
451                   if (locale) 
452                     [localeLUT setObject: locale forKey: _name];
453                   else
454                     [self logWithFormat:@"%s couldn't load locale with name:%@",
455                           __PRETTY_FUNCTION__,
456                           _name];
457                 }
458               else
459                 [self logWithFormat:@"%s didn't find locale with name: %@",
460                       __PRETTY_FUNCTION__,
461                       _name];
462             }
463           else
464             [self errorWithFormat:@"did not find Locale for language: %@", _name];
465         }
466     }
467   else
468     [self errorWithFormat:@"%s: name parameter must not be nil!",
469           __PRETTY_FUNCTION__];
470
471   return locale;
472 }
473
474 /* name (used by the WEResourceManager) */
475
476 - (NSString *) name
477 {
478   return @"SOGo-0.9";
479 }
480
481 @end /* SOGo */