]> err.no Git - sope/commitdiff
started to work on a Cookie based authenticator
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Sun, 12 Mar 2006 23:41:40 +0000 (23:41 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Sun, 12 Mar 2006 23:41:40 +0000 (23:41 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@1227 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

13 files changed:
sope-appserver/NGObjWeb/ChangeLog
sope-appserver/NGObjWeb/SoObjects/GNUmakefile
sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h [new file with mode: 0644]
sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m [new file with mode: 0644]
sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m
sope-appserver/NGObjWeb/Version
sope-appserver/samples/SoCookieAuth/Application.m [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/ChangeLog [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/GNUmakefile [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/Main.m [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/README [new file with mode: 0644]
sope-appserver/samples/SoCookieAuth/common.h [new file with mode: 0644]

index d9d73f1eb1c50943af73b2517d7287ef498b5214..b6400efc7aabbc4f0d54a09e30a5ec7be3cc4dda 100644 (file)
@@ -1,5 +1,11 @@
 2006-03-12  Helge Hess  <helge.hess@opengroupware.org>
 
+       * v4.5.223
+
+       * SoObjects: started SoCookieAuthenticator
+
+       * SoObjects/SoHTTPAuthenticator.m: code cleanups
+
        * SoObjects/SoProductLoader.m: quickfix to API (v4.5.222)
 
        * SoObjects: added new class SoProductLoader which can be used to
index 2ee8983fd8a324da0ae211ae90f347b1063b8aeb..886a3ed5d2b249d3cebc8fc65e57652ca80fabc2 100644 (file)
@@ -41,6 +41,7 @@ SoObjects_HEADER_FILES = \
        SoSecurityException.h           \
        SoComponent.h                   \
        SoProductLoader.h               \
+       SoCookieAuthenticator.h         \
 
 SoObjects_OBJC_FILES = \
        NSException+HTTP.m              \
@@ -79,6 +80,7 @@ SoObjects_OBJC_FILES = \
        SoSecurityException.m           \
        SoComponent.m                   \
        SoProductLoader.m               \
+       SoCookieAuthenticator.m         \
 
 -include GNUmakefile.preamble
 include $(GNUSTEP_MAKEFILES)/subproject.make
diff --git a/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h
new file mode 100644 (file)
index 0000000..2f47456
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2006 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __SoObjects_SoCookieAuthenticator_H__
+#define __SoObjects_SoCookieAuthenticator_H__
+
+#import <Foundation/NSObject.h>
+
+/*
+  SoCookieAuthenticator
+
+  SoCookieAuthenticator is an abstract base class for Cookie based
+  authentication. That is, it uses a Cookie to store the credentials
+  or a token of a session.
+  In the simplest case you only need to override -checkLogin:password:
+  to ensure login/password combinations.
+  
+  NOTE: work in progress.
+  
+  TODO: we also need a WOSessionAuthenticator which assumes that the
+        existance of a session implies a successful authentication?
+*/
+
+@class NSString, NSException, NSArray;
+@class WOContext, WOResponse, WOCookie;
+@class SoUser;
+
+@interface SoCookieAuthenticator : NSObject
+{
+}
+
+/* password checker (override in subclasses !) */
+
+- (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd;
+
+/* Cookie authentication */
+
+- (NSString *)cookieNameInContext:(WOContext *)_ctx;
+- (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx;
+
+- (NSString *)checkCredentialsInContext:(WOContext *)_ctx;
+- (NSArray *)parseCredentials:(NSString *)_creds;
+
+/* user management */
+
+- (SoUser *)userInContext:(WOContext *)_ctx;
+- (NSArray *)rolesForLogin:(NSString *)_login;
+
+/* render auth exceptions of SoSecurityManager */
+
+- (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx;
+
+@end
+
+#endif /* __SoObjects_SoCookieAuthenticator_H__ */
diff --git a/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m
new file mode 100644 (file)
index 0000000..4feebd0
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+  Copyright (C) 2006 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "SoCookieAuthenticator.h"
+#include "SoHTTPAuthenticator.h"
+#include "SoUser.h"
+#include "SoPermissions.h"
+#include "NSException+HTTP.h"
+#include <NGObjWeb/WOApplication.h>
+#include <NGObjWeb/WORequest.h>
+#include <NGObjWeb/WOResponse.h>
+#include <NGObjWeb/WOContext.h>
+#include <NGObjWeb/WOCookie.h>
+#include <NGExtensions/NSString+Ext.h>
+#include "common.h"
+
+#if APPLE_RUNTIME || NeXT_RUNTIME
+@interface NSObject(Miss)
+- (void)subclassResponsibility:(SEL)cmd;
+@end
+#endif
+
+// TODO: do we have 'anonymous' in such a scenario???
+// TODO: we want to redirect to a login panel and include the root URL
+//       using a query path
+
+@implementation SoCookieAuthenticator
+
+static NSString *prefix = @"0xHIGHFLYx";
+
++ (int)version {
+  return 1;
+}
+
+/* HTTP basic authentication */
+
+- (NSString *)cookieNameInContext:(WOContext *)_ctx {
+  return [prefix stringByAppendingString:[[_ctx application] name]];
+}
+
+/* check for roles */
+
+- (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd {
+  [self subclassResponsibility:_cmd];
+  return NO;
+}
+
+- (NSArray *)parseCredentials:(NSString *)_creds {
+  if (![_creds isNotEmpty])
+    return nil;
+  
+  /* per default we just reuse HTTP basic credentials! */
+  return [SoHTTPAuthenticator parseCredentials:_creds];
+}
+
+- (NSString *)checkCredentials:(NSString *)_creds {
+  /* checks credentials, returnes login if successful */
+  NSString *login, *pwd;
+  NSArray  *creds;
+  
+  if (![(creds = [self parseCredentials:_creds]) isNotEmpty])
+    return nil;
+  
+  login = [creds objectAtIndex:0];
+  if ([login isEqualToString:@"anonymous"])
+    return @"anonymous";
+  
+  pwd = [creds objectAtIndex:1];
+  if (![self checkLogin:login password:pwd])
+    return nil;
+  
+  return login;
+}
+
+- (NSString *)checkCredentialsInContext:(WOContext *)_ctx {
+  WORequest *rq;
+  NSString  *auth;
+  
+  rq = [_ctx request];
+  
+  auth = [rq cookieValueForKey:[self cookieNameInContext:_ctx]];
+  if (![auth isNotEmpty]) {
+    /* no auth supplied */
+    return @"anonymous";
+  }
+  return [self checkCredentials:auth];
+}
+
+- (NSArray *)rolesForLogin:(NSString *)_login {
+  // TODO: I suppose this should be overridden?
+  NSArray *uroles;
+  
+  // could add manager of login=root
+  
+  uroles = [NSArray arrayWithObjects:
+                     SoRole_Authenticated,
+                     SoRole_Anonymous,
+                     nil];
+  return uroles;
+}
+
+- (SoUser *)userWithLogin:(NSString *)_login andRoles:(NSArray *)_roles
+  inContext:(WOContext *)_ctx
+{
+  /* the actual factory method */
+  return [[[SoUser alloc] initWithLogin:_login roles:_roles] autorelease];
+}
+
+- (SoUser *)userInContext:(WOContext *)_ctx {
+  static SoUser *anonymous = nil;
+  NSString  *login;
+  NSArray   *uroles;
+  
+  if (anonymous == nil) {
+    NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous];
+    anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar];
+  }
+  
+  if ((login = [self checkCredentialsInContext:_ctx]) == nil)
+    /* some error (otherwise result would have been anonymous */
+    return nil;
+  
+  if ([login isEqualToString:@"anonymous"])
+    return anonymous;
+  
+  uroles = [self rolesForLogin:login];
+  return [self userWithLogin:login andRoles:uroles inContext:_ctx];
+}
+
+/* auth fail handling */
+
+- (void)setupAuthFailResponse:(WOResponse *)_response
+  withReason:(NSString *)_reason inContext:(WOContext *)_ctx
+{
+  [_response appendContentString:@"TODO: render login page ..."];
+}
+
+- (WOResponse *)unauthorized:(NSString *)_reason inContext:(WOContext *)_ctx {
+  WOResponse *r;
+  
+  if (![_reason isNotEmpty]) _reason = @"Unauthorized";
+  
+  r = [_ctx response];
+  [self setupAuthFailResponse:r withReason:_reason inContext:_ctx];
+  return r;
+}
+
+- (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx {
+  /* 
+     TODO: this can be called for content which is not accessible to the
+           user? (but the user is otherwise perfectly ok?)
+          Should not: in this case we should get a 404?
+  */
+  if ([_e httpStatus] != 401)
+    return NO;
+  
+  [self setupAuthFailResponse:[_ctx response]
+       withReason:[_e reason] inContext:_ctx];
+  return YES;
+}
+
+/* request preprocessing */
+
+- (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx {
+  /*
+    This is called by SoObjectRequestHandler prior doing any significant
+    processing to allow the authenticator to reject invalid requests.
+  */
+  WOResponse *r;
+  NSString *auth;
+  NSString *k;
+  NSString *user, *pwd;
+  NSRange rng;
+  
+  auth = [[_ctx request] cookieValueForKey:[self cookieNameInContext:_ctx]];
+  if (![auth isNotEmpty]) {
+    /* no authentication provided */
+    static NSArray *anon = nil;
+    if (anon == nil)
+      anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil];
+    
+    [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"];
+    return nil;
+  }
+  
+  /* authentication provided, check whether it's valid */
+  
+  r = [_ctx response];
+  if ([auth length] < 6) {
+    [self logWithFormat:@"tried unknown authentication method: %@ (A)", auth];
+    return [self unauthorized:@"unsupported authentication method"
+                 inContext:_ctx];
+  }
+  k = [[auth substringToIndex:5] lowercaseString];
+  if (![k hasPrefix:@"basic"]) {
+    [self logWithFormat:@"tried unknown authentication method: %@ (B)", auth];
+    return [self unauthorized:@"unsupported authentication method"
+                 inContext:_ctx];
+  }
+  
+  k = [auth substringFromIndex:6];
+  if ((k = [k stringByDecodingBase64]) == nil) {
+    [self logWithFormat:@"tried unknown authentication method: %@ (C)", auth];
+    return [self unauthorized:@"unsupported authentication method"
+                 inContext:_ctx];
+  }
+
+  rng = [k rangeOfString:@":"];
+  if (rng.length <= 0) {
+    [self logWithFormat:@"got malformed basic credentials (missing colon)!"];
+    return [self unauthorized:@"malformed basic credentials!" inContext:_ctx];
+  }
+  
+  user = [k substringToIndex:rng.location];
+  pwd  = [k substringFromIndex:(rng.location + rng.length)];
+  
+  rng = [user rangeOfString:@"\\"];
+  if (rng.length > 0) {
+    [self debugWithFormat:@"splitting of domain in user: '%@'", user];
+    user = [user substringFromIndex:(rng.location + rng.length)];
+  }
+  
+  if (![user isNotEmpty]) {
+    [self logWithFormat:@"got malformed basic credentials!"];
+    return [self unauthorized:@"empty login in credentials?" inContext:_ctx];
+  }
+  if (![pwd isNotEmpty]) {
+    [self logWithFormat:@"got empty password for user '%@'!", user];
+    return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx];
+  }
+  
+  /* authenticate valid credentials */
+  
+  if (![self checkLogin:user password:pwd]) {
+    [self logWithFormat:@"tried wrong password for user '%@'!", user];
+    return [self unauthorized:nil inContext:_ctx];
+  }
+  
+  //[self debugWithFormat:@"authenticated user '%@'", user];
+  
+  /* authentication succeeded */
+  {
+    static NSArray *auth = nil;
+    if (auth == nil) {
+      auth = [[NSArray alloc] initWithObjects:
+                               SoRole_Authenticated, SoRole_Anonymous, nil];
+    }
+    [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"];
+  }
+  return nil;
+}
+
+@end /* SoCookieAuthenticator */
index c7c6de3a2f46d54ebbaebf2ae94d54b41f32e22d..d8f8b15dbf5927baef1a9429d4b02edf08508658 100644 (file)
@@ -1,5 +1,7 @@
 /*
-  Copyright (C) 2002-2005 SKYRIX Software AG
+  Copyright (C)
+  2002-2006 SKYRIX Software AG
+  2006      Helge Hess
 
   This file is part of SOPE.
 
@@ -67,7 +69,7 @@
   NSString *login, *pwd;
   NSString *k;
   
-  if ([_creds length] == 0) {
+  if (![_creds isNotEmpty]) {
     static NSArray *anon = nil;
     if (anon == nil)
       anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil];
   NSString *login, *pwd;
   NSArray  *creds;
   
-  if ((creds = [self parseCredentials:_creds]) == nil)
+  if (![(creds = [self parseCredentials:_creds]) isNotEmpty])
     return nil;
-
+  
   login = [creds objectAtIndex:0];
   if ([login isEqualToString:@"anonymous"])
     return @"anonymous";
   uroles = [NSArray arrayWithObjects:
                      SoRole_Authenticated,
                      SoRole_Anonymous,
-                     nil];
+                   nil];
   return uroles;
 }
 
   WOResponse *r;
   NSString *auth;
 
-  if ([_reason length] == 0) _reason = @"Unauthorized";
+  if (![_reason isNotEmpty]) _reason = @"Unauthorized";
   
   auth = [NSString stringWithFormat:@"basic realm=\"%@\"", 
                   [self authRealmInContext:_ctx]];
     user = [user substringFromIndex:(rng.location + rng.length)];
   }
   
-  if ([user length] == 0) {
+  if (![user isNotEmpty]) {
     [self logWithFormat:@"got malformed basic credentials!"];
     return [self unauthorized:@"empty login in credentials?" inContext:_ctx];
   }
-  if ([pwd length] == 0) {
+  if (![pwd isNotEmpty]) {
     [self logWithFormat:@"got empty password for user '%@'!", user];
     return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx];
   }
index 9b6f14d6611e72f8ffb6041d8e33686a76d6d359..3f44974553ed87cea24e88db9cab71311acfa907 100644 (file)
@@ -1,6 +1,6 @@
 # version file
 
-SUBMINOR_VERSION:=222
+SUBMINOR_VERSION:=223
 
 # v4.5.214 requires libNGExtensions v4.5.179
 # v4.5.122 requires libNGExtensions v4.5.153
diff --git a/sope-appserver/samples/SoCookieAuth/Application.m b/sope-appserver/samples/SoCookieAuth/Application.m
new file mode 100644 (file)
index 0000000..d5fdba5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+  Copyright (C) 2006 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include <NGObjWeb/SoApplication.h>
+
+@interface SoCookieAuth : SoApplication
+{
+}
+
+@end
+
+#include <NGObjWeb/SoCookieAuthenticator.h>
+#include "common.h"
+
+@implementation SoCookieAuth
+
+- (id)authenticatorInContext:(id)_ctx {
+  return [[[SoCookieAuthenticator alloc] init] autorelease];
+}
+
+@end /* SoCookieAuth */
+
+
+
+int main(int argc, char **argv, char **env) {
+  NSAutoreleasePool *pool;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  WOApplicationMain(@"SoCookieAuth", argc, (void*)argv);
+
+  [pool release];
+  return 0;
+}
diff --git a/sope-appserver/samples/SoCookieAuth/ChangeLog b/sope-appserver/samples/SoCookieAuth/ChangeLog
new file mode 100644 (file)
index 0000000..a6b5dd0
--- /dev/null
@@ -0,0 +1,4 @@
+2005-07-10  Helge Hess  <helge.hess@opengroupware.org>
+
+       * created ChangeLog / project
+
diff --git a/sope-appserver/samples/SoCookieAuth/GNUmakefile b/sope-appserver/samples/SoCookieAuth/GNUmakefile
new file mode 100644 (file)
index 0000000..3ae9ece
--- /dev/null
@@ -0,0 +1,13 @@
+# GNUstep makefile
+
+-include ../../../config.make
+include $(GNUSTEP_MAKEFILES)/common.make
+
+WOAPP_NAME = SoCookieAuth
+
+SoCookieAuth_OBJC_FILES += Application.m Main.m
+SoCookieAuth_COMPONENTS += Main.wo
+
+-include GNUmakefile.preamble
+include $(GNUSTEP_MAKEFILES)/woapp.make
+-include GNUmakefile.postamble
diff --git a/sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble b/sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble
new file mode 100644 (file)
index 0000000..5e85b30
--- /dev/null
@@ -0,0 +1,14 @@
+# compile settings
+
+APPSERVER="../.."
+
+ADDITIONAL_LIB_DIRS += \
+       -L$(APPSERVER)/NGObjWeb/$(GNUSTEP_OBJ_DIR)
+
+# static references required for Mach linker
+
+ADDITIONAL_TOOL_LIBS += \
+       -lEOControl     \
+       -lDOM -lXmlRpc -lSaxObjC
+
+ADDITIONAL_LIB_DIRS += -L/usr/local/lib -L/usr/lib
diff --git a/sope-appserver/samples/SoCookieAuth/Main.m b/sope-appserver/samples/SoCookieAuth/Main.m
new file mode 100644 (file)
index 0000000..cdb743d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2006 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include <NGObjWeb/WOComponent.h>
+
+@class NSArray;
+
+@interface Main : WOComponent
+{
+}
+
+@end
+
+#include "common.h"
+
+@implementation Main
+
+- (id)initWithContext:(WOContext *)_ctx {
+  if ((self = [super initWithContext:_ctx]) != nil) {
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [super dealloc];
+}
+
+/* accessors */
+
+#if 0
+- (void)setItem:(id)_item {
+  ASSIGN(self->item, _item);
+}
+- (id)item {
+  return self->item;
+}
+#endif
+
+@end /* Main */
diff --git a/sope-appserver/samples/SoCookieAuth/README b/sope-appserver/samples/SoCookieAuth/README
new file mode 100644 (file)
index 0000000..399ced2
--- /dev/null
@@ -0,0 +1,3 @@
+WORK IN PROGRESS
+
+App to test SOPE cookie authentication (SoCookieAuthenticator)
diff --git a/sope-appserver/samples/SoCookieAuth/common.h b/sope-appserver/samples/SoCookieAuth/common.h
new file mode 100644 (file)
index 0000000..ac5c945
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+  Copyright (C) 2006 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+#include <NGObjWeb/NGObjWeb.h>