See ChangeLog
Monotone-Parent: f5359c59c0bb008203154487db17e1ecdd274c0d Monotone-Revision: 7c78ba28d583536196a1acf34df5c96f40db238e Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2010-12-28T17:42:50 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
7ffe6ee26a
commit
f46ebda105
|
@ -21,3 +21,4 @@ SOPE/NGCards/samples/CardGroup.m
|
|||
SOPE/NGCards/samples/CardVersitRenderer.m
|
||||
SOPE/NGCards/samples/NGCardsSaxHandler.m
|
||||
Tests/Integration/config.py
|
||||
Tests/Integration/teststrings
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
2010-12-28 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Implemented secured sessions. We no longer store in the
|
||||
browser's cookie the base64 encoded version of username:password
|
||||
You MUST now set OCSSessionsFolderURL to a value like:
|
||||
postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_sessions_folder
|
||||
and the database table will be created automatically upon
|
||||
next SOGo's startup.
|
||||
|
||||
2010-12-23 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Added the /SOGo/so/<user>/Calendar/reloadWebCalendarsAndRedirect
|
||||
|
|
Binary file not shown.
10
Main/SOGo.m
10
Main/SOGo.m
|
@ -232,11 +232,19 @@ static BOOL debugLeaks;
|
|||
}
|
||||
}
|
||||
|
||||
if (ok && [defaults enableEMailAlarms])
|
||||
if (ok)
|
||||
{
|
||||
fm = [GCSFolderManager defaultFolderManager];
|
||||
|
||||
// Create the sessions table
|
||||
[[fm sessionsFolder] createFolderIfNotExists];
|
||||
|
||||
// Create the email alarms table, if required
|
||||
if ([defaults enableEMailAlarms])
|
||||
{
|
||||
[[fm alarmsFolder] createFolderIfNotExists];
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
@class NSString, NSArray, NSURL, NSDictionary, NSException;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSFolder, GCSFolderType;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSFolder, GCSFolderType, GCSSessionsFolder;
|
||||
|
||||
@interface GCSFolderManager : NSObject
|
||||
{
|
||||
|
@ -73,6 +73,9 @@
|
|||
/* alarms */
|
||||
- (GCSAlarmsFolder *)alarmsFolder;
|
||||
|
||||
/* sessions */
|
||||
- (GCSSessionsFolder *)sessionsFolder;
|
||||
|
||||
/* folder types */
|
||||
|
||||
- (GCSFolderType *)folderTypeWithName:(NSString *)_name;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2006-2011 Inverse inc.
|
||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo 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.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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.
|
||||
|
@ -42,6 +43,7 @@
|
|||
#import "GCSAlarmsFolder.h"
|
||||
#import "GCSFolder.h"
|
||||
#import "GCSFolderType.h"
|
||||
#import "GCSSessionsFolder.h"
|
||||
#import "GCSSpecialQueries.h"
|
||||
#import "NSURL+GCS.h"
|
||||
|
||||
|
@ -330,7 +332,12 @@ static NSCharacterSet *asciiAlphaNumericCS = nil;
|
|||
return [GCSAlarmsFolder alarmsFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
/* path SQL */
|
||||
|
||||
/* sessions */
|
||||
- (GCSSessionsFolder *)sessionsFolder
|
||||
{
|
||||
return [GCSSessionsFolder sessionsFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
|
||||
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* GCSSessionsFolder.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef GCSSESSIONSFOLDER_H
|
||||
#define GCSSESSIONSFOLDER_H
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSCalendarDate;
|
||||
@class NSException;
|
||||
@class NSNumber;
|
||||
@class NSString;
|
||||
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface GCSSessionsFolder : NSObject
|
||||
{
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (id) sessionsFolderWithFolderManager: (GCSFolderManager *) newFolderManager;
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (void) createFolderIfNotExists;
|
||||
- (BOOL) canConnectStore;
|
||||
- (NSDictionary *) recordForEntryWithID: (NSString *) theID;
|
||||
- (void) deleteRecordForEntryWithID: (NSString *) theID;
|
||||
- (void) writeRecordForEntryWithID: (NSString *) theID
|
||||
value: (NSString *) theValue
|
||||
creationDate: (NSCalendarDate *) theCreationDate
|
||||
lastSeenDate: (NSCalendarDate *) theLastSeenDate;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* GCSSESSIONSFOLDER_H */
|
|
@ -0,0 +1,357 @@
|
|||
/* GCSSessionsFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
#import <GDLAccess/EOEntity.h>
|
||||
#import <GDLAccess/EOSQLQualifier.h>
|
||||
|
||||
#import "EOQualifier+GCS.h"
|
||||
#import "GCSChannelManager.h"
|
||||
#import "GCSFolderManager.h"
|
||||
#import "GCSSpecialQueries.h"
|
||||
#import "GCSStringFormatter.h"
|
||||
#import "NSURL+GCS.h"
|
||||
|
||||
#import "GCSSessionsFolder.h"
|
||||
|
||||
static NSString *sessionsFolderURLString = nil;
|
||||
|
||||
#warning GCSSessionsFolder should share a common ancestor with GCSFolder
|
||||
|
||||
@implementation GCSSessionsFolder
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
|
||||
if (!sessionsFolderURLString)
|
||||
{
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
ASSIGN(sessionsFolderURLString, [ud stringForKey: @"OCSSessionsFolderURL"]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (id) sessionsFolderWithFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
GCSAlarmsFolder *newFolder;
|
||||
|
||||
if (sessionsFolderURLString)
|
||||
{
|
||||
newFolder = [self new];
|
||||
[newFolder autorelease];
|
||||
[newFolder setFolderManager: newFolderManager];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"'OCSSessionsFolderURL' is not set"];
|
||||
newFolder = nil;
|
||||
}
|
||||
|
||||
return newFolder;
|
||||
}
|
||||
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
ASSIGN (folderManager, newFolderManager);
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *) _location
|
||||
{
|
||||
NSURL *location;
|
||||
|
||||
if (sessionsFolderURLString)
|
||||
location = [NSURL URLWithString: sessionsFolderURLString];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"'OCSSessionsFolderURL' is not set"];
|
||||
location = nil;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
- (GCSChannelManager *) _channelManager
|
||||
{
|
||||
return [folderManager channelManager];
|
||||
}
|
||||
|
||||
- (NSString *) _storeTableName
|
||||
{
|
||||
return [[self _location] gcsTableName];
|
||||
}
|
||||
|
||||
- (EOEntity *) _storeTableEntityForChannel: (EOAdaptorChannel *) tc
|
||||
{
|
||||
static EOEntity *entity = nil;
|
||||
EOAttribute *attribute;
|
||||
NSString *tableName;
|
||||
NSString *columns[] = { @"c_id", @"c_value", @"c_creationdate",
|
||||
@"c_lastseen", nil };
|
||||
NSString **column;
|
||||
NSMutableArray *keys;
|
||||
NSDictionary *types;
|
||||
|
||||
if (!entity)
|
||||
{
|
||||
entity = [EOEntity new];
|
||||
tableName = [self _storeTableName];
|
||||
[entity setName: tableName];
|
||||
[entity setExternalName: tableName];
|
||||
|
||||
types = [[tc specialQueries] sessionsAttributeTypes];
|
||||
|
||||
column = columns;
|
||||
while (*column)
|
||||
{
|
||||
attribute = [EOAttribute new];
|
||||
[attribute setName: *column];
|
||||
[attribute setColumnName: *column];
|
||||
[attribute setExternalType: [types objectForKey: *column]];
|
||||
[entity addAttribute: attribute];
|
||||
[attribute release];
|
||||
column++;
|
||||
}
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 1];
|
||||
[keys addObject: [entity attributeNamed: @"c_id"]];
|
||||
[entity setPrimaryKeyAttributes: keys];
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 3];
|
||||
[keys addObject: [entity attributeNamed: @"c_value"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_creationdate"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_lastseen"]];
|
||||
[entity setClassProperties: keys];
|
||||
|
||||
[entity setAttributesUsedForLocking: [NSArray array]];
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/* connection */
|
||||
|
||||
- (EOAdaptorChannel *) _acquireStoreChannel
|
||||
{
|
||||
return [[self _channelManager] acquireOpenChannelForURL: [self _location]];
|
||||
}
|
||||
|
||||
- (void) _releaseChannel: (EOAdaptorChannel *) _channel
|
||||
{
|
||||
[[self _channelManager] releaseChannel:_channel];
|
||||
}
|
||||
|
||||
- (BOOL) canConnectStore
|
||||
{
|
||||
return [[self _channelManager] canConnect:[self _location]];
|
||||
}
|
||||
|
||||
- (void) createFolderIfNotExists
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
NSString *sql, *tableName;
|
||||
GCSSpecialQueries *queries;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
tableName = [self _storeTableName];
|
||||
|
||||
queries = [tc specialQueries];
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@",
|
||||
[self _storeTableName]];
|
||||
if ([tc evaluateExpressionX: sql])
|
||||
{
|
||||
sql = [queries createSessionsFolderWithName: tableName];
|
||||
if (![tc evaluateExpressionX: sql])
|
||||
[self logWithFormat:
|
||||
@"sessions folder table '%@' successfully created!",
|
||||
tableName];
|
||||
}
|
||||
else
|
||||
[tc cancelFetch];
|
||||
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
/* operations */
|
||||
|
||||
- (NSDictionary *) recordForEntryWithID: (NSString *) theID
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
NSString *tableName;
|
||||
NSException *error;
|
||||
NSArray *attrs;
|
||||
NSDictionary *record;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
tableName = [self _storeTableName];
|
||||
|
||||
record = nil;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat:
|
||||
@"c_id='%@'",
|
||||
theID];
|
||||
[qualifier autorelease];
|
||||
|
||||
[context beginTransaction];
|
||||
error = [tc selectAttributesX: [entity attributesUsedForFetch]
|
||||
describedByQualifier: qualifier
|
||||
fetchOrder: nil
|
||||
lock: NO];
|
||||
if (error)
|
||||
[self errorWithFormat:@"%s: cannot execute fetch: %@",
|
||||
__PRETTY_FUNCTION__, error];
|
||||
else
|
||||
{
|
||||
attrs = [tc describeResults: NO];
|
||||
record = [tc fetchAttributes: attrs withZone: NULL];
|
||||
[tc cancelFetch];
|
||||
}
|
||||
[context rollbackTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (NSDictionary *) _newRecordWithID: (NSString *) theID
|
||||
value: (NSString *) theValue
|
||||
creationDate: (NSCalendarDate *) theCreationDate
|
||||
lastSeenDate: (NSCalendarDate *) theLastSeenDate
|
||||
{
|
||||
NSNumber *cd, *ls;
|
||||
|
||||
// We check if recId and alarmDate are nil prior calling -timeIntervalSince1970
|
||||
// Weird gcc optimizations can cause issue here.
|
||||
cd = [NSNumber numberWithInt: (theCreationDate ? (int)[theCreationDate timeIntervalSince1970] : 0)];
|
||||
ls = [NSNumber numberWithInt: (theLastSeenDate ? (int)[theLastSeenDate timeIntervalSince1970] : 0)];
|
||||
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys: theID, @"c_id",
|
||||
theValue, @"c_value",
|
||||
cd, @"c_creationdate",
|
||||
ls, @"c_lastseen",
|
||||
nil];
|
||||
}
|
||||
|
||||
- (void) writeRecordForEntryWithID: (NSString *) theID
|
||||
value: (NSString *) theValue
|
||||
creationDate: (NSCalendarDate *) theCreationDate
|
||||
lastSeenDate: (NSCalendarDate *) theLastSeenDate
|
||||
{
|
||||
NSDictionary *record, *newRecord;
|
||||
NSException *error;
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
newRecord = [self _newRecordWithID: theID
|
||||
value: theValue
|
||||
creationDate: theCreationDate
|
||||
lastSeenDate: theLastSeenDate];
|
||||
record = [self recordForEntryWithID: theID];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
[context beginTransaction];
|
||||
if (record)
|
||||
{
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat:
|
||||
@"c_id='%@'",
|
||||
theID];
|
||||
[qualifier autorelease];
|
||||
error = [tc updateRowX: newRecord describedByQualifier: qualifier];
|
||||
}
|
||||
else
|
||||
error = [tc insertRowX: newRecord forEntity: entity];
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot write record: %@",
|
||||
__PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) deleteRecordForEntryWithID: (NSString *) theID
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
NSException *error;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat:
|
||||
@"c_id='%@'",
|
||||
theID];
|
||||
[qualifier autorelease];
|
||||
[context beginTransaction];
|
||||
error = [tc deleteRowsDescribedByQualifierX: qualifier];
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot delete record: %@",
|
||||
__PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
|
@ -37,6 +37,9 @@
|
|||
- (NSString *) createFolderTableWithName: (NSString *) tableName;
|
||||
- (NSString *) createFolderACLTableWithName: (NSString *) tableName;
|
||||
|
||||
- (NSString *) createSessionsFolderWithName: (NSString *) tableName;
|
||||
- (NSDictionary *) sessionsAttributeTypes;
|
||||
|
||||
@end
|
||||
|
||||
@interface EOAdaptorChannel (GCSSpecialQueries)
|
||||
|
|
|
@ -105,8 +105,26 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *) sessionsAttributeTypes
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
// PostgreSQL database
|
||||
//
|
||||
@implementation GCSPostgreSQLSpecialQueries
|
||||
|
||||
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
|
||||
|
@ -168,8 +186,39 @@
|
|||
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_id VARCHAR(255) NOT NULL,"
|
||||
@" c_value VARCHAR(255) NOT NULL,"
|
||||
@" c_creationdate INT4 NOT NULL,"
|
||||
@" c_lastseen INT4 NOT NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) sessionsAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_id"];
|
||||
[types setObject: @"varchar" forKey: @"c_value"];
|
||||
[types setObject: @"int" forKey: @"c_creationdate"];
|
||||
[types setObject: @"int" forKey: @"c_lastseen"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
// MySQL database
|
||||
//
|
||||
@implementation GCSMySQLSpecialQueries
|
||||
|
||||
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
|
||||
|
@ -231,8 +280,39 @@
|
|||
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_id VARCHAR(255) NOT NULL,"
|
||||
@" c_value VARCHAR(255) NOT NULL,"
|
||||
@" c_creationdate INT NOT NULL,"
|
||||
@" c_lastseen INT NOT NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) sessionsAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_id"];
|
||||
[types setObject: @"varchar" forKey: @"c_value"];
|
||||
[types setObject: @"int" forKey: @"c_creationdate"];
|
||||
[types setObject: @"int" forKey: @"c_lastseen"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
// Oracle database
|
||||
//
|
||||
@implementation GCSOracleSpecialQueries
|
||||
|
||||
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
|
||||
|
@ -293,4 +373,32 @@
|
|||
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_id VARCHAR2(255) NOT NULL,"
|
||||
@" c_value VARCHAR2(255) NOT NULL,"
|
||||
@" c_creationdate INTEGER NOT NULL,"
|
||||
@" c_lastseen INTEGER NOT NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) sessionsAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar2" forKey: @"c_id"];
|
||||
[types setObject: @"varchar2" forKey: @"c_value"];
|
||||
[types setObject: @"integer" forKey: @"c_creationdate"];
|
||||
[types setObject: @"integer" forKey: @"c_lastseen"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -32,6 +32,7 @@ libGDLContentStore_HEADER_FILES += \
|
|||
GCSFolderType.h \
|
||||
GCSChannelManager.h \
|
||||
GCSFieldExtractor.h \
|
||||
GCSSessionsFolder.h \
|
||||
GCSSpecialQueries.h \
|
||||
GCSStringFormatter.h \
|
||||
|
||||
|
@ -48,6 +49,7 @@ libGDLContentStore_OBJC_FILES += \
|
|||
GCSFolderType.m \
|
||||
GCSChannelManager.m \
|
||||
GCSFieldExtractor.m \
|
||||
GCSSessionsFolder.m \
|
||||
GCSSpecialQueries.m \
|
||||
GCSStringFormatter.m \
|
||||
|
||||
|
|
|
@ -629,14 +629,16 @@ static NSString *sieveScriptName = @"sogo";
|
|||
return [NSMutableString string];
|
||||
}
|
||||
|
||||
//
|
||||
// Extract password from basic authentication.
|
||||
//
|
||||
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
|
||||
{
|
||||
/*
|
||||
Extract password from basic authentication.
|
||||
*/
|
||||
NSURL *imapURL;
|
||||
NSString *password;
|
||||
NSURL *imapURL;
|
||||
|
||||
// Default account - ie., the account that is provided with a default
|
||||
// SOGo installation. User-added IMAP accounts will have name >= 1.
|
||||
if ([nameInContainer isEqualToString: @"0"])
|
||||
{
|
||||
imapURL = [self imap4URL];
|
||||
|
|
|
@ -46,6 +46,7 @@ SOGo_HEADER_FILES = \
|
|||
NSURL+DAV.h \
|
||||
\
|
||||
SOGoAuthenticator.h \
|
||||
SOGoSession.h \
|
||||
SOGoCASSession.h \
|
||||
SOGoDAVAuthenticator.h \
|
||||
SOGoProxyAuthenticator.h \
|
||||
|
@ -108,6 +109,7 @@ SOGo_OBJC_FILES = \
|
|||
NSString+Utilities.m \
|
||||
NSURL+DAV.m \
|
||||
\
|
||||
SOGoSession.m \
|
||||
SOGoCASSession.m \
|
||||
SOGoDAVAuthenticator.m \
|
||||
SOGoProxyAuthenticator.m \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoCache.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008-2010 Inverse inc.
|
||||
* Copyright (C) 2008-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
@ -92,7 +92,9 @@
|
|||
forLogin: (NSString *) login;
|
||||
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
|
||||
|
||||
/* CAS support */
|
||||
//
|
||||
// CAS support
|
||||
//
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier;
|
||||
- (NSString *) CASSessionWithTicket: (NSString *) ticket;
|
||||
- (void) setCASSession: (NSString *) casSession
|
||||
|
@ -103,9 +105,11 @@
|
|||
- (void) setCASPGTId: (NSString *) pgtId
|
||||
forPGTIOU: (NSString *) pgtIou;
|
||||
|
||||
//
|
||||
// ACL caching support
|
||||
//
|
||||
- (void) setACLs: (NSDictionary *) theACLs
|
||||
forPath: (NSString *) thePath;
|
||||
|
||||
- (NSMutableDictionary *) aclsForPath: (NSString *) thePath;
|
||||
|
||||
@end
|
||||
|
|
|
@ -36,8 +36,13 @@
|
|||
* <uid>+attributes value = NSMutableDictionary instance > user's LDAP attributes
|
||||
* <object path>+acl value = NSDictionary instance > ACLs on an object at specified path
|
||||
* <groupname>+<domain> value = NSString instance (array components separated by ",") or group member logins for a specific group in domain
|
||||
* cas-id:< > value =
|
||||
* cas-ticket:< > value =
|
||||
* cas-pgtiou:< > value =
|
||||
* session:< > value =
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
@ -437,7 +442,9 @@ static memcached_st *handle = NULL;
|
|||
return [self _valuesOfType: @"settings" forKey: theLogin];
|
||||
}
|
||||
|
||||
/* CAS session support */
|
||||
//
|
||||
// CAS session support
|
||||
//
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"cas-id:%@",
|
||||
|
@ -481,7 +488,7 @@ static memcached_st *handle = NULL;
|
|||
}
|
||||
|
||||
//
|
||||
//
|
||||
// ACL caching code
|
||||
//
|
||||
- (void) setACLs: (NSDictionary *) theACLs
|
||||
forPath: (NSString *) thePath
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo 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.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoProxyAuthenticator.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
* Copyright (C) 2009-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -402,7 +402,6 @@
|
|||
grace: grace];
|
||||
}
|
||||
|
||||
|
||||
return checkOK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoWebAuthenticator.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2010 Inverse inc.
|
||||
* Copyright (C) 2007-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
@ -32,13 +32,16 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGLdap/NGLdapConnection.h>
|
||||
|
||||
#import <MainUI/SOGoRootPage.h>
|
||||
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoCASSession.h"
|
||||
#import "SOGoConstants.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoSession.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "SOGoUserManager.h"
|
||||
|
@ -60,11 +63,31 @@
|
|||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
{
|
||||
NSString *username, *password, *value;
|
||||
SOGoPasswordPolicyError perr;
|
||||
int expire, grace;
|
||||
|
||||
return [self checkLogin: _login
|
||||
password: _pwd
|
||||
|
||||
// We check for the existence of the session in the database/memcache
|
||||
// and we extract the real password from it. Here,
|
||||
//
|
||||
// _login == userKey
|
||||
// _pwd == sessionKey
|
||||
//
|
||||
// If the session isn't present in the database, we fail the login process.
|
||||
//
|
||||
value = [SOGoSession valueForSessionKey: _pwd];
|
||||
|
||||
if (!value)
|
||||
return NO;
|
||||
|
||||
[SOGoSession decodeValue: value
|
||||
usingKey: _login
|
||||
login: &username
|
||||
password: &password];
|
||||
|
||||
return [self checkLogin: username
|
||||
password: password
|
||||
perr: &perr
|
||||
expire: &expire
|
||||
grace: &grace];
|
||||
|
@ -104,6 +127,9 @@
|
|||
return rc;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
||||
{
|
||||
static SOGoUser *anonymous = nil;
|
||||
|
@ -124,20 +150,64 @@
|
|||
|
||||
- (NSString *) passwordInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *creds;
|
||||
NSString *auth, *password;
|
||||
NSArray *creds;
|
||||
|
||||
auth = [[context request]
|
||||
cookieValueForKey: [self cookieNameInContext: context]];
|
||||
creds = [self parseCredentials: auth];
|
||||
if ([creds count] > 1)
|
||||
password = [creds objectAtIndex: 1];
|
||||
{
|
||||
NSString *login;
|
||||
|
||||
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
|
||||
usingKey: [creds objectAtIndex: 0]
|
||||
login: &login
|
||||
password: &password];
|
||||
}
|
||||
else
|
||||
password = nil;
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
//
|
||||
// We overwrite SOPE's method in order to proper retrieve
|
||||
// the username from the cookie.
|
||||
//
|
||||
- (NSString *) checkCredentials: (NSString *)_creds
|
||||
{
|
||||
NSString *login, *pwd, *userKey, *sessionKey;
|
||||
NSArray *creds;
|
||||
|
||||
SOGoPasswordPolicyError perr;
|
||||
int expire, grace;
|
||||
|
||||
if (![(creds = [self parseCredentials:_creds]) isNotEmpty])
|
||||
return nil;
|
||||
|
||||
userKey = [creds objectAtIndex:0];
|
||||
if ([userKey isEqualToString:@"anonymous"])
|
||||
return @"anonymous";
|
||||
|
||||
sessionKey = [creds objectAtIndex:1];
|
||||
|
||||
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: sessionKey]
|
||||
usingKey: userKey
|
||||
login: &login
|
||||
password: &pwd];
|
||||
|
||||
if (![self checkLogin: login
|
||||
password: pwd
|
||||
perr: &perr
|
||||
expire: &expire
|
||||
grace: &grace])
|
||||
return nil;
|
||||
|
||||
return login;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) imapPasswordInContext: (WOContext *) context
|
||||
forServer: (NSString *) imapServer
|
||||
forceRenew: (BOOL) renew
|
||||
|
@ -176,12 +246,12 @@
|
|||
return [SOGoUser userWithLogin: login roles: roles];
|
||||
}
|
||||
|
||||
//
|
||||
// This is called by SoObjectRequestHandler prior doing any significant
|
||||
// processing to allow the authenticator to reject invalid requests.
|
||||
//
|
||||
- (WOResponse *) preprocessCredentialsInContext: (WOContext *) context
|
||||
{
|
||||
/*
|
||||
This is called by SoObjectRequestHandler prior doing any significant
|
||||
processing to allow the authenticator to reject invalid requests.
|
||||
*/
|
||||
WOResponse *response;
|
||||
NSString *auth;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/SOGoWebAuthenticator.h>
|
||||
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoConstants.h>
|
||||
|
||||
#import "SOGoRootPage.h"
|
||||
|
@ -70,18 +70,37 @@
|
|||
forAuthenticator: (SOGoWebAuthenticator *) auth
|
||||
{
|
||||
WOCookie *authCookie;
|
||||
NSString *cookieValue, *cookieString, *appName;
|
||||
NSString *cookieValue, *cookieString, *appName, *sessionKey, *userKey, *securedPassword;
|
||||
|
||||
//
|
||||
// We create a new cookie - thus we create a new session
|
||||
// associated to the user. For security, we generate:
|
||||
//
|
||||
// A- a session key
|
||||
// B- a user key
|
||||
//
|
||||
// In memcached, the session key will be associated to the user's password
|
||||
// which will be XOR'ed with the user key.
|
||||
//
|
||||
sessionKey = [SOGoSession generateKeyForLength: 16];
|
||||
userKey = [SOGoSession generateKeyForLength: 64];
|
||||
|
||||
NSString *value = [NSString stringWithFormat: @"%@:%@", username, password];
|
||||
securedPassword = [SOGoSession securedValue: value usingKey: userKey];
|
||||
|
||||
|
||||
[SOGoSession setValue: securedPassword forSessionKey: sessionKey];
|
||||
|
||||
//cookieString = [NSString stringWithFormat: @"%@:%@",
|
||||
// username, password];
|
||||
cookieString = [NSString stringWithFormat: @"%@:%@",
|
||||
username, password];
|
||||
userKey, sessionKey];
|
||||
cookieValue = [NSString stringWithFormat: @"basic %@",
|
||||
[cookieString stringByEncodingBase64]];
|
||||
authCookie = [WOCookie cookieWithName: [auth cookieNameInContext: context]
|
||||
value: cookieValue];
|
||||
appName = [[context request] applicationName];
|
||||
[authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
/* enable this when we have code to determine whether request is HTTPS:
|
||||
[authCookie setIsSecure: YES]; */
|
||||
|
||||
return authCookie;
|
||||
}
|
||||
|
@ -107,7 +126,9 @@
|
|||
return locationCookie;
|
||||
}
|
||||
|
||||
/* actions */
|
||||
//
|
||||
//
|
||||
//
|
||||
- (WOResponse *) _responseWithLDAPPolicyError: (int) error
|
||||
{
|
||||
NSDictionary *jsonError;
|
||||
|
@ -164,7 +185,8 @@
|
|||
response = [self responseWithStatus: 200
|
||||
andJSONRepresentation: json];
|
||||
|
||||
authCookie = [self _cookieWithUsername: username andPassword: password
|
||||
authCookie = [self _cookieWithUsername: username
|
||||
andPassword: password
|
||||
forAuthenticator: auth];
|
||||
[response addCookie: authCookie];
|
||||
|
||||
|
@ -369,11 +391,6 @@
|
|||
return [[SOGoSystemDefaults sharedSystemDefaults] supportedLanguages];
|
||||
}
|
||||
|
||||
// - (NSString *) language
|
||||
// {
|
||||
// return [SOGoUser language];
|
||||
// }
|
||||
|
||||
- (NSString *) languageText
|
||||
{
|
||||
NSString *text;
|
||||
|
@ -397,7 +414,7 @@
|
|||
|
||||
- (WOResponse *) changePasswordAction
|
||||
{
|
||||
NSString *username, *password, *newPassword;
|
||||
NSString *username, *password, *newPassword, *value;
|
||||
SOGoUserManager *um;
|
||||
SOGoPasswordPolicyError error;
|
||||
WOResponse *response;
|
||||
|
@ -405,11 +422,22 @@
|
|||
NSDictionary *message;
|
||||
SOGoWebAuthenticator *auth;
|
||||
WOCookie *authCookie;
|
||||
NSArray *creds;
|
||||
|
||||
request = [context request];
|
||||
message = [[request contentAsString] objectFromJSONString];
|
||||
username = [message objectForKey: @"userName"];
|
||||
password = [message objectForKey: @"password"];
|
||||
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
value = [[context request]
|
||||
cookieValueForKey: [auth cookieNameInContext: context]];
|
||||
creds = [auth parseCredentials: value];
|
||||
|
||||
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
|
||||
usingKey: [creds objectAtIndex: 0]
|
||||
login: &username
|
||||
password: &password];
|
||||
|
||||
newPassword = [message objectForKey: @"newPassword"];
|
||||
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
|
@ -420,9 +448,10 @@
|
|||
newPassword: newPassword
|
||||
perr: &error])
|
||||
{
|
||||
// We delete the previous session
|
||||
[SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
|
||||
|
||||
response = [self responseWith204];
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
authCookie = [self _cookieWithUsername: username
|
||||
andPassword: newPassword
|
||||
forAuthenticator: auth];
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
|
@ -275,10 +276,12 @@
|
|||
|
||||
- (id <WOActionResults>) logoffAction
|
||||
{
|
||||
SOGoWebAuthenticator *auth;
|
||||
NSString *userName, *value;
|
||||
WOResponse *response;
|
||||
WOCookie *cookie;
|
||||
NSCalendarDate *date;
|
||||
NSString *userName;
|
||||
WOCookie *cookie;
|
||||
NSArray *creds;
|
||||
|
||||
userName = [[context activeUser] login];
|
||||
[self logWithFormat: @"user '%@' logged off", userName];
|
||||
|
@ -288,6 +291,18 @@
|
|||
date = [NSCalendarDate calendarDate];
|
||||
[date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]];
|
||||
|
||||
// We cleanup the memecached/database session cache. We do this before
|
||||
// invoking _logoutCookieWithDate: in order to obtain its value.
|
||||
auth = [[self clientObject] authenticatorInContext: context];
|
||||
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
||||
{
|
||||
value = [[context request] cookieValueForKey: [auth cookieNameInContext: context]];
|
||||
creds = [auth parseCredentials: value];
|
||||
|
||||
if ([creds count] > 1)
|
||||
[SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
|
||||
}
|
||||
|
||||
cookie = [self _logoutCookieWithDate: date];
|
||||
if (cookie)
|
||||
[response addCookie: cookie];
|
||||
|
@ -298,6 +313,7 @@
|
|||
forKey: @"Cache-Control"];
|
||||
[response setHeader: @"no-cache" forKey: @"Pragma"];
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ function onLoginCallback(http) {
|
|||
if (http.status == 200) {
|
||||
// Make sure browser's cookies are enabled
|
||||
var loginCookie = readLoginCookie();
|
||||
|
||||
if (!loginCookie) {
|
||||
SetLogMessage("errorMessage", _("cookiesNotEnabled"));
|
||||
submitBtn.disabled = false;
|
||||
|
|
Loading…
Reference in New Issue