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