Merge to 2.0.3

This commit is contained in:
Francis Lachapelle 2012-12-06 10:40:33 -05:00
commit 4dc7790015
225 changed files with 8432 additions and 2691 deletions

View file

@ -21,6 +21,7 @@ trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings
trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings
trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings
trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings
@ -44,6 +45,7 @@ trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings
trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings
trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings
trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
@ -67,6 +69,7 @@ trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings
trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings
trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings
trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings
@ -90,6 +93,7 @@ trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Contacts/Polish.lproj/Localizable.strings trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Contacts/Russian.lproj/Localizable.strings trans.ru = UI/Contacts/Russian.lproj/Localizable.strings
trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings
trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings
trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings
@ -113,6 +117,7 @@ trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MainUI/Polish.lproj/Localizable.strings trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MainUI/Russian.lproj/Localizable.strings trans.ru = UI/MainUI/Russian.lproj/Localizable.strings
trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings
trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings
trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings
@ -136,6 +141,7 @@ trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Common/Polish.lproj/Localizable.strings trans.pl = UI/Common/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Common/Russian.lproj/Localizable.strings trans.ru = UI/Common/Russian.lproj/Localizable.strings
trans.sk = UI/Common/Slovak.lproj/Localizable.strings
trans.sv = UI/Common/Swedish.lproj/Localizable.strings trans.sv = UI/Common/Swedish.lproj/Localizable.strings
trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings
@ -159,6 +165,7 @@ trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings
trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings
trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings
trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings
@ -182,6 +189,7 @@ trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings
trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings
trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings
trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
@ -205,6 +213,7 @@ trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings
trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
@ -228,5 +237,6 @@ trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings
trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings
trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings
trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings

1091
ChangeLog

File diff suppressed because it is too large Load diff

51
NEWS
View file

@ -1,3 +1,54 @@
2.0.3 (2012-12-06)
------------------
New features
- support for SAML2 for single sign-on, with the help of the lasso library
- added support for the "AUTHENTICATE" command and SASL mechanisms
- added domain default SieveHostFieldName
- added a search field for tasks
Enhancements
- search the contacts for the organization attribute
- in HTML mode, optionally place answer after the quoted text
- improved memory usage of "sogo-tool restore"
- fixed invitations status in OSX iCal.app/Calendar.app (cleanup RSVP attribute)
- now uses "imap4flags" instead of the deprecated "imapflags"
- added Slovak translation - thanks to Martin Pastor
- updated translations
Bug fixes
- fixed LDIF import with categories
- imported events now keep their UID when possible
- fixed importation of multiple calendars
- fixed modification date when drag'n'droping events
- fixed missing 'from' header in Outlook
- fixed invitations in Outlook
- fixed JavaScript regexp for Firefox
- fixed JavaScript syntax for IE7
- fixed all-day event display in day/week view
- fixed parsing of alarm
- fixed Sieve server URL fallback
- fixed Debian cronjob (spool directory cleanup)
2.0.2a (2012-11-15)
-------------------
Enhancements
- improved user rights editor in calendar module
- disable alarms for newly subsribed calendars
Bug fixes
- fixed typos in Spanish (Spain) translation
- fixed display of raw source for tasks
- fixed title display of cards with a photo
- fixed null address in reply-to header of messages
- fixed scrolling for calendar/addressbooks lists
- fixed display of invitations on BlackBerry devices
- fixed sogo-tool rename-user for MySQL database
- fixed corrupted attachments in Webmail
- fixed parsing of URLs that can throw an exception
- fixed password encoding in user sources
2.0.2 (2012-10-24) 2.0.2 (2012-10-24)
------------------ ------------------

View file

@ -7,8 +7,7 @@ libOGoContentStore_LIBRARIES_DEPEND_UPON += \
-lNGExtensions \ -lNGExtensions \
-lEOControl \ -lEOControl \
-lSaxObjC \ -lSaxObjC \
-lgnustep-base \ $(BASE_LIBS)
-lobjc
ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE -I../SoObjects ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE -I../SoObjects
@ -29,4 +28,5 @@ test_quick_extract_TOOL_LIBS += \
-lNGCards \ -lNGCards \
-lGDLContentStore \ -lGDLContentStore \
-lGDLAccess \ -lGDLAccess \
-lOGoContentStore -lOGoContentStore \
$(BASE_LIBS)

View file

@ -48,7 +48,7 @@
- (NSData *) mimeAttachTag; - (NSData *) mimeAttachTag;
/* move & copy operations */ /* move & copy operations */
- (void) copyToAttachment: (MAPIStoreAttachment *) newAttachment; - (void) copyToAttachment: (MAPIStoreAttachment *) newAttachment inMemCtx: (TALLOC_CTX *) memCtx;
/* subclasses */ /* subclasses */
- (MAPIStoreEmbeddedMessage *) openEmbeddedMessage; - (MAPIStoreEmbeddedMessage *) openEmbeddedMessage;

View file

@ -152,13 +152,13 @@
return ULLONG_MAX; return ULLONG_MAX;
} }
- (void) copyToAttachment: (MAPIStoreAttachment *) newAttachment - (void) copyToAttachment: (MAPIStoreAttachment *) newAttachment inMemCtx: (TALLOC_CTX *) memCtx
{ {
void *attachMethod; void *attachMethod;
enum mapistore_error error; enum mapistore_error error;
MAPIStoreEmbeddedMessage *embeddedMessage, *newEmbeddedMessage; MAPIStoreEmbeddedMessage *embeddedMessage, *newEmbeddedMessage;
[self copyPropertiesToObject: newAttachment]; [self copyPropertiesToObject: newAttachment inMemCtx: memCtx];
attachMethod = NULL; attachMethod = NULL;
error = [self getProperty: &attachMethod error = [self getProperty: &attachMethod
@ -170,7 +170,7 @@
{ {
embeddedMessage = [self openEmbeddedMessage]; embeddedMessage = [self openEmbeddedMessage];
newEmbeddedMessage = [newAttachment createEmbeddedMessage]; newEmbeddedMessage = [newAttachment createEmbeddedMessage];
[embeddedMessage copyToMessage: newEmbeddedMessage]; [embeddedMessage copyToMessage: newEmbeddedMessage inMemCtx: memCtx];
} }
talloc_free (attachMethod); talloc_free (attachMethod);
} }

View file

@ -63,6 +63,8 @@
SOGoAppointmentObject *newEntry; SOGoAppointmentObject *newEntry;
NSString *name; NSString *name;
[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
name = [NSString stringWithFormat: @"%@.ics", name = [NSString stringWithFormat: @"%@.ics",
[SOGoObject globallyUniqueObjectId]]; [SOGoObject globallyUniqueObjectId]];
newEntry = [SOGoAppointmentObject objectWithName: name newEntry = [SOGoAppointmentObject objectWithName: name

View file

@ -337,7 +337,18 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
- (BOOL) subscriberCanReadMessages - (BOOL) subscriberCanReadMessages
{ {
return [self _testRoleForActiveUser: MAPIStoreRightReadItems]; NSString *displayName;
/* when this folder is the "Freebusy Data" folder, we need to allow
subscribed to read an open contained messages in order to enable them to
find the "LocalFreebusy" message */
[sogoObject reloadIfNeeded];
displayName = [[sogoObject properties]
objectForKey: MAPIPropertyKey (PidTagDisplayName)];
return ([displayName isEqualToString: @"Freebusy Data"]
|| [self _testRoleForActiveUser: MAPIStoreRightReadItems]);
} }
- (BOOL) subscriberCanDeleteMessages - (BOOL) subscriberCanDeleteMessages

View file

@ -123,7 +123,8 @@
fromFolder: (MAPIStoreFolder *) sourceFolder fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys andChangeKeys: (struct Binary_r **) targetChangeKeys
wantCopy: (uint8_t) want_copy; wantCopy: (uint8_t) want_copy
inMemCtx: (TALLOC_CTX *) memCtx;
- (enum mapistore_error) moveCopyToFolder: (MAPIStoreFolder *) targetFolder - (enum mapistore_error) moveCopyToFolder: (MAPIStoreFolder *) targetFolder
withNewName: (NSString *) newFolderName withNewName: (NSString *) newFolderName

View file

@ -180,6 +180,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (void) dealloc - (void) dealloc
{ {
[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
// [messageKeys release]; // [messageKeys release];
// [faiMessageKeys release]; // [faiMessageKeys release];
// [folderKeys release]; // [folderKeys release];
@ -656,19 +658,23 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
return rc; return rc;
} }
- (int) moveCopyMessageWithMID: (uint64_t) srcMid // private method
- (int) _moveCopyMessageWithMID: (uint64_t) srcMid
fromFolder: (MAPIStoreFolder *) sourceFolder fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey andChangeKey: (struct Binary_r *) targetChangeKey
wantCopy: (uint8_t) wantCopy wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{ {
int rc; int rc;
MAPIStoreMessage *sourceMsg, *destMsg; MAPIStoreMessage *sourceMsg, *destMsg;
TALLOC_CTX *memCtx; //TALLOC_CTX *memCtx;
struct SRow aRow; struct SRow aRow;
struct SPropValue property; struct SPropValue property;
memCtx = talloc_zero (NULL, TALLOC_CTX); [self logWithFormat: @"-moveCopyMessageWithMID: 0x%.16llx .. withMID: 0x%.16llx .. wantCopy: %d", srcMid, targetMid, wantCopy];
//memCtx = talloc_zero (NULL, TALLOC_CTX);
rc = [sourceFolder openMessage: &sourceMsg rc = [sourceFolder openMessage: &sourceMsg
withMID: srcMid withMID: srcMid
forWriting: NO forWriting: NO
@ -681,7 +687,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (rc != MAPISTORE_SUCCESS) if (rc != MAPISTORE_SUCCESS)
goto end; goto end;
[sourceMsg copyToMessage: destMsg]; [sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetChangeKey) if (targetChangeKey)
{ {
@ -698,7 +704,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0]; rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0];
end: end:
talloc_free (memCtx); //talloc_free (memCtx);
return rc; return rc;
} }
@ -709,6 +715,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
withMIDs: (uint64_t *) targetMids withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys andChangeKeys: (struct Binary_r **) targetChangeKeys
wantCopy: (uint8_t) wantCopy wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{ {
int rc = MAPISTORE_SUCCESS; int rc = MAPISTORE_SUCCESS;
NSUInteger count; NSUInteger count;
@ -717,6 +724,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
MAPIStoreMapping *mapping; MAPIStoreMapping *mapping;
SOGoUser *ownerUser; SOGoUser *ownerUser;
struct Binary_r *targetChangeKey; struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
ownerUser = [[self userContext] sogoUser]; ownerUser = [[self userContext] sogoUser];
@ -738,11 +748,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
targetChangeKey = targetChangeKeys[count]; targetChangeKey = targetChangeKeys[count];
else else
targetChangeKey = NULL; targetChangeKey = NULL;
rc = [self moveCopyMessageWithMID: srcMids[count] rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder fromFolder: sourceFolder
withMID: targetMids[count] withMID: targetMids[count]
andChangeKey: targetChangeKey andChangeKey: targetChangeKey
wantCopy: wantCopy]; wantCopy: wantCopy
inMemCtx: memCtx];
} }
else else
rc = MAPISTORE_ERR_NOT_FOUND; rc = MAPISTORE_ERR_NOT_FOUND;
@ -766,6 +777,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
else else
rc = MAPISTORE_ERR_DENIED; rc = MAPISTORE_ERR_DENIED;
//talloc_free (memCtx);
return rc; return rc;
} }
@ -785,6 +798,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSUInteger count, max; NSUInteger count, max;
NSString *childKey; NSString *childKey;
uint64_t fmid; uint64_t fmid;
TALLOC_CTX *memCtx;
memCtx = talloc_zero (NULL, TALLOC_CTX);
/* TODO: one possible issue with this algorithm is that moved messages will /* TODO: one possible issue with this algorithm is that moved messages will
lack a version number and will all be assigned a new one, even though lack a version number and will all be assigned a new one, even though
@ -807,7 +823,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (rc == MAPISTORE_SUCCESS) if (rc == MAPISTORE_SUCCESS)
{ {
newFolder = [targetFolder lookupFolder: childKey]; newFolder = [targetFolder lookupFolder: childKey];
[self copyPropertiesToObject: newFolder]; [self copyPropertiesToObject: newFolder inMemCtx: memCtx];
pool = [NSAutoreleasePool new]; pool = [NSAutoreleasePool new];
children = [self messageKeys]; children = [self messageKeys];
@ -818,7 +834,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
message = [self lookupMessage: childKey]; message = [self lookupMessage: childKey];
targetMessage = [newFolder createMessage: NO]; targetMessage = [newFolder createMessage: NO];
[targetMessage setIsNew: YES]; [targetMessage setIsNew: YES];
[message copyToMessage: targetMessage]; [message copyToMessage: targetMessage inMemCtx: memCtx];
if (isMove) if (isMove)
{ {
fmid = [mapping idFromURL: [message url]]; fmid = [mapping idFromURL: [message url]];
@ -839,7 +855,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
message = [self lookupFAIMessage: childKey]; message = [self lookupFAIMessage: childKey];
targetMessage = [newFolder createMessage: YES]; targetMessage = [newFolder createMessage: YES];
[targetMessage setIsNew: YES]; [targetMessage setIsNew: YES];
[message copyToMessage: targetMessage]; [message copyToMessage: targetMessage inMemCtx: memCtx];
if (isMove) if (isMove)
{ {
fmid = [mapping idFromURL: [message url]]; fmid = [mapping idFromURL: [message url]];
@ -882,6 +898,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
else else
rc = MAPISTORE_ERR_DENIED; rc = MAPISTORE_ERR_DENIED;
talloc_free (memCtx);
return rc; return rc;
} }

View file

@ -929,6 +929,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
withMIDs: (uint64_t *) targetMids withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys andChangeKeys: (struct Binary_r **) targetChangeKeys
wantCopy: (uint8_t) wantCopy wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{ {
NGImap4Connection *connection; NGImap4Connection *connection;
NGImap4Client *client; NGImap4Client *client;
@ -946,7 +948,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys andChangeKeys: targetChangeKeys
wantCopy: wantCopy]; wantCopy: wantCopy
inMemCtx: memCtx];
/* Conversion of mids to IMAP uids */ /* Conversion of mids to IMAP uids */
mapping = [self mapping]; mapping = [self mapping];

View file

@ -569,7 +569,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
} }
list = MakeRecipientsList ([recipients objectForKey: @"orig"]); list = MakeRecipientsList ([recipients objectForKey: @"orig"]);
if (list) if ([list count])
[headers setObjects: list forKey: @"from"]; [headers setObjects: list forKey: @"from"];
} }
else else

View file

@ -71,7 +71,7 @@
- (NSArray *) activeUserRoles; - (NSArray *) activeUserRoles;
/* move & copy internal ops */ /* move & copy internal ops */
- (void) copyToMessage: (MAPIStoreMessage *) newMessage; - (void) copyToMessage: (MAPIStoreMessage *) newMessage inMemCtx: (TALLOC_CTX *) memCtx;
/* subclasses */ /* subclasses */
- (void) save; - (void) save;

View file

@ -139,6 +139,8 @@ rtf2html (NSData *compressedRTF)
- (id) init - (id) init
{ {
[self logWithFormat: @"METHOD '%s' (%d) (%d)", __FUNCTION__, __LINE__, self];
if ((self = [super init])) if ((self = [super init]))
{ {
attachmentParts = [NSMutableDictionary new]; attachmentParts = [NSMutableDictionary new];
@ -151,6 +153,7 @@ rtf2html (NSData *compressedRTF)
- (void) dealloc - (void) dealloc
{ {
[self logWithFormat: @"METHOD '%s' (%d) (%d)", __FUNCTION__, __LINE__, self];
[activeUserRoles release]; [activeUserRoles release];
[attachmentKeys release]; [attachmentKeys release];
[attachmentParts release]; [attachmentParts release];
@ -437,17 +440,19 @@ rtf2html (NSData *compressedRTF)
andType: MAPISTORE_MESSAGE_TABLE]; andType: MAPISTORE_MESSAGE_TABLE];
} }
- (void) copyToMessage: (MAPIStoreMessage *) newMessage - (void) copyToMessage: (MAPIStoreMessage *) newMessage inMemCtx: (TALLOC_CTX *) memCtx;
{ {
TALLOC_CTX *memCtx; //TALLOC_CTX *memCtx;
struct mapistore_message *messageData; struct mapistore_message *messageData;
NSArray *keys; NSArray *keys;
NSUInteger count, max; NSUInteger count, max;
NSString *key; NSString *key;
MAPIStoreAttachment *attachment, *newAttachment; MAPIStoreAttachment *attachment, *newAttachment;
memCtx = talloc_zero (NULL, TALLOC_CTX); [self logWithFormat: @"METHOD '%s' (%d) (%d)", __FUNCTION__, __LINE__, self];
//memCtx = talloc_zero (NULL, TALLOC_CTX);
/* message headers and recipients */ /* message headers and recipients */
[self getMessageData: &messageData inMemCtx: memCtx]; [self getMessageData: &messageData inMemCtx: memCtx];
@ -456,7 +461,7 @@ rtf2html (NSData *compressedRTF)
andColumns: messageData->columns]; andColumns: messageData->columns];
/* properties */ /* properties */
[self copyPropertiesToObject: newMessage]; [self copyPropertiesToObject: newMessage inMemCtx: memCtx];
/* attachments */ /* attachments */
keys = [self attachmentKeys]; keys = [self attachmentKeys];
@ -466,10 +471,10 @@ rtf2html (NSData *compressedRTF)
key = [keys objectAtIndex: count]; key = [keys objectAtIndex: count];
attachment = [self lookupAttachment: key]; attachment = [self lookupAttachment: key];
newAttachment = [newMessage createAttachment]; newAttachment = [newMessage createAttachment];
[attachment copyToAttachment: newAttachment]; [attachment copyToAttachment: newAttachment inMemCtx: memCtx];
} }
talloc_free (memCtx); //talloc_free (memCtx);
} }
- (enum mapistore_error) saveMessage - (enum mapistore_error) saveMessage
@ -485,6 +490,8 @@ rtf2html (NSData *compressedRTF)
BOOL userIsOwner; BOOL userIsOwner;
MAPIStoreMessage *mainMessage; MAPIStoreMessage *mainMessage;
[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
context = [self context]; context = [self context];
ownerUser = [[self userContext] sogoUser]; ownerUser = [[self userContext] sogoUser];
userIsOwner = [[context activeUser] isEqual: ownerUser]; userIsOwner = [[context activeUser] isEqual: ownerUser];

View file

@ -91,7 +91,7 @@
inMemCtx: (TALLOC_CTX *) memCtx; inMemCtx: (TALLOC_CTX *) memCtx;
/* move and copy operations */ /* move and copy operations */
- (void) copyPropertiesToObject: (MAPIStoreObject *) newObject; - (void) copyPropertiesToObject: (MAPIStoreObject *) newObject inMemCtx: (TALLOC_CTX *) memCtx;
/* subclasses */ /* subclasses */
- (NSString *) nameInContainer; - (NSString *) nameInContainer;

View file

@ -317,9 +317,9 @@ static Class NSExceptionK, MAPIStoreFolderK;
} }
/* move and copy operations */ /* move and copy operations */
- (void) copyPropertiesToObject: (MAPIStoreObject *) newObject - (void) copyPropertiesToObject: (MAPIStoreObject *) newObject inMemCtx: (TALLOC_CTX *) memCtx
{ {
TALLOC_CTX *memCtx; //TALLOC_CTX *memCtx;
struct SPropTagArray *availableProps; struct SPropTagArray *availableProps;
struct SRow row; struct SRow row;
enum MAPITAGS propTag; enum MAPITAGS propTag;
@ -328,7 +328,7 @@ static Class NSExceptionK, MAPIStoreFolderK;
enum mapistore_error error; enum mapistore_error error;
void *data; void *data;
memCtx = talloc_zero (NULL, TALLOC_CTX); //memCtx = talloc_zero (NULL, TALLOC_CTX);
[self getAvailableProperties: &availableProps inMemCtx: memCtx]; [self getAvailableProperties: &availableProps inMemCtx: memCtx];
@ -369,8 +369,7 @@ static Class NSExceptionK, MAPIStoreFolderK;
} }
[newObject addPropertiesFromRow: &row]; [newObject addPropertiesFromRow: &row];
talloc_free (memCtx); //talloc_free (memCtx);
} }
/* subclasses */ /* subclasses */

View file

@ -95,6 +95,7 @@ sogo_backend_init (void)
defaults using the system encoding rather than honouring defaults using the system encoding rather than honouring
the encoding specified in the file. */ the encoding specified in the file. */
putenv ("GNUSTEP_STRING_ENCODING=NSUTF8StringEncoding"); putenv ("GNUSTEP_STRING_ENCODING=NSUTF8StringEncoding");
//putenv ("NSZombieEnabled=YES");
[NSProcessInfo initializeWithArguments: argv [NSProcessInfo initializeWithArguments: argv
count: 1 count: 1
@ -567,6 +568,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags)
static enum mapistore_error static enum mapistore_error
sogo_folder_move_copy_messages(void *folder_object, sogo_folder_move_copy_messages(void *folder_object,
void *source_folder_object, void *source_folder_object,
TALLOC_CTX *mem_ctx,
uint32_t mid_count, uint32_t mid_count,
uint64_t *src_mids, uint64_t *t_mids, uint64_t *src_mids, uint64_t *t_mids,
struct Binary_r **target_change_keys, struct Binary_r **target_change_keys,
@ -594,7 +596,8 @@ sogo_folder_move_copy_messages(void *folder_object,
fromFolder: sourceFolder fromFolder: sourceFolder
withMIDs: t_mids withMIDs: t_mids
andChangeKeys: target_change_keys andChangeKeys: target_change_keys
wantCopy: want_copy]; wantCopy: want_copy
inMemCtx: mem_ctx];
[pool release]; [pool release];
GSUnregisterCurrentThread (); GSUnregisterCurrentThread ();
} }

View file

@ -51,6 +51,7 @@ MAPIStoreTallocWrapperDestroy (void *data)
pool = [NSAutoreleasePool new]; pool = [NSAutoreleasePool new];
wrapper = data; wrapper = data;
//NSLog (@"destroying wrapped object (wrapper: %p; object: %p)...\n", wrapper, wrapper->MAPIStoreSOGoObject); //NSLog (@"destroying wrapped object (wrapper: %p; object: %p)...\n", wrapper, wrapper->MAPIStoreSOGoObject);
NSLog (@"destroying wrapped object (wrapper: %p)", wrapper);
[wrapper->instance release]; [wrapper->instance release];
[pool release]; [pool release];
GSUnregisterCurrentThread (); GSUnregisterCurrentThread ();
@ -66,7 +67,7 @@ MAPIStoreTallocWrapperDestroy (void *data)
talloc_set_destructor ((void *) wrapper, MAPIStoreTallocWrapperDestroy); talloc_set_destructor ((void *) wrapper, MAPIStoreTallocWrapperDestroy);
wrapper->instance = self; wrapper->instance = self;
[self retain]; [self retain];
// NSLog (@"returning wrapper: %p; object: %p", wrapper, self); NSLog (@"returning wrapper: %p; object: %p", wrapper, self);
return wrapper; return wrapper;
} }

View file

@ -351,15 +351,12 @@ static GCSStringFormatter *stringFormatter = nil;
- (NSString *) _selectedFields: (NSArray *) fields - (NSString *) _selectedFields: (NSArray *) fields
requirement: (GCSTableRequirement) requirement requirement: (GCSTableRequirement) requirement
{ {
NSMutableString *selectedFields; NSString *selectedFields;
selectedFields = [NSMutableString string]; if (requirement == bothTableRequired && [fields containsObject: @"c_name"])
selectedFields = [self _dottedFields: fields];
if (requirement == bothTableRequired
&& [fields containsObject: @"c_name"])
[selectedFields appendString: [self _dottedFields: fields]];
else else
[selectedFields appendString: [fields componentsJoinedByString: @", "]]; selectedFields = [fields componentsJoinedByString: @", "];
return selectedFields; return selectedFields;
} }

View file

@ -110,6 +110,14 @@
return nil; return nil;
} }
- (NSString *) updateCPathInFolderInfo: (NSString *) tableName
withCPath2: (NSString *) c_path2
{
[self subclassResponsibility: _cmd];
return nil;
}
@end @end
@ -205,6 +213,17 @@
return types; return types;
} }
- (NSString *) updateCPathInFolderInfo: (NSString *) tableName
withCPath2: (NSString *) c_path2
{
static NSString *sqlFolderFormat
= (@"UPDATE %@"
@" SET c_path = '/'||c_path1||'/'||c_path2||'/'||c_path3||'/'||c_path4"
@" WHERE c_path2 = '%@'");
return [NSString stringWithFormat: sqlFolderFormat, tableName, c_path2];
}
@end @end
// //
@ -299,6 +318,17 @@
return types; return types;
} }
- (NSString *) updateCPathInFolderInfo: (NSString *) tableName
withCPath2: (NSString *) c_path2
{
static NSString *sqlFolderFormat
= (@"UPDATE %@"
@" SET c_path = CONCAT('/', c_path1, '/', c_path2, '/', c_path3, '/', c_path4)"
@" WHERE c_path2 = '%@'");
return [NSString stringWithFormat: sqlFolderFormat, tableName, c_path2];
}
@end @end
// //
@ -392,4 +422,15 @@
return types; return types;
} }
- (NSString *) updateCPathInFolderInfo: (NSString *) tableName
withCPath2: (NSString *) c_path2
{
static NSString *sqlFolderFormat
= (@"UPDATE %@"
@" SET c_path = '/'||c_path1||'/'||c_path2||'/'||c_path3||'/'||c_path4"
@" WHERE c_path2 = '%@'");
return [NSString stringWithFormat: sqlFolderFormat, tableName, c_path2];
}
@end @end

View file

@ -8,11 +8,12 @@ ADDITIONAL_INCLUDE_DIRS += -I. -I..
# dependencies # dependencies
BASE_LIBS := $(shell gnustep-config --base-libs)
libGDLContentStore_LIBRARIES_DEPEND_UPON += \ libGDLContentStore_LIBRARIES_DEPEND_UPON += \
-lGDLAccess \ -lGDLAccess \
-lNGExtensions -lEOControl \ -lNGExtensions -lEOControl \
-lgnustep-base -lobjc $(BASE_LIBS)
GDLContentStore_LIBRARIES_DEPEND_UPON += \ GDLContentStore_LIBRARIES_DEPEND_UPON += \
-framework GDLAccess \ -framework GDLAccess \
@ -25,7 +26,7 @@ GCS_TOOL_LIBS += \
-lGDLContentStore -lGDLAccess \ -lGDLContentStore -lGDLAccess \
-lNGExtensions -lEOControl \ -lNGExtensions -lEOControl \
-lDOM -lSaxObjC \ -lDOM -lSaxObjC \
-lgnustep-base $(BASE_LIBS)
else else
GCS_TOOL_LIBS += \ GCS_TOOL_LIBS += \
-framework GDLContentStore -framework GDLAccess \ -framework GDLContentStore -framework GDLAccess \

View file

@ -1,6 +1,6 @@
/* CardElement.h - this file is part of SOPE /* CardElement.h - this file is part of SOPE
* *
* Copyright (C) 2006 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *

View file

@ -1,6 +1,6 @@
/* CardElement.m - this file is part of SOPE /* CardElement.m - this file is part of SOPE
* *
* Copyright (C) 2006 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -424,16 +424,19 @@
} }
static inline BOOL static inline BOOL
_subValuesAreVoid (NSArray *subValues) _subValuesAreVoid (id subValues)
{ {
BOOL result = YES; BOOL result = YES;
NSUInteger count, max; NSUInteger count, max;
result = YES; result = YES;
if ([subValues isKindOfClass: [NSArray class]])
{
max = [subValues count]; max = [subValues count];
for (count = 0; result && count < max; count++) for (count = 0; result && count < max; count++)
result = ([[subValues objectAtIndex: count] length] == 0); result = ([[subValues objectAtIndex: count] length] == 0);
}
return result; return result;
} }

View file

@ -75,6 +75,8 @@
- (void) replaceThisElement: (CardElement *) oldElement - (void) replaceThisElement: (CardElement *) oldElement
withThisOne: (CardElement *) newElement; withThisOne: (CardElement *) newElement;
- (NSArray *) orderOfElements;
@end @end
#endif /* CARDGROUP_H */ #endif /* CARDGROUP_H */

View file

@ -1,6 +1,6 @@
/* CardGroup.m - this file is part of SOPE /* CardGroup.m - this file is part of SOPE
* *
* Copyright (C) 2006 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -454,4 +454,9 @@ static NGCardsSaxHandler *sax = nil;
return new; return new;
} }
- (NSArray *) orderOfElements
{
return nil;
}
@end @end

View file

@ -1,6 +1,6 @@
/* CardVersitRenderer.m - this file is part of SOPE /* CardVersitRenderer.m - this file is part of SOPE
* *
* Copyright (C) 2006 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -107,6 +107,7 @@
CardElement *currentChild; CardElement *currentChild;
NSMutableString *rendering; NSMutableString *rendering;
NSString *groupTag; NSString *groupTag;
NSArray *order;
rendering = [NSMutableString string]; rendering = [NSMutableString string];
@ -120,7 +121,36 @@
groupTag = [groupTag uppercaseString]; groupTag = [groupTag uppercaseString];
[rendering appendFormat: @"BEGIN:%@\r\n", groupTag]; [rendering appendFormat: @"BEGIN:%@\r\n", groupTag];
// We reorder the group elemments, if necessary
order = [aGroup orderOfElements];
if (order)
{
NSMutableArray *orderedElements, *originalElements;
NSArray *currentChildren;
int i, c;
originalElements = [NSMutableArray arrayWithArray: [aGroup children]];
orderedElements = [NSMutableArray array];
c = [order count];
for (i = 0; i < c; i++)
{
currentChildren = [aGroup childrenWithTag: [order objectAtIndex: i]];
[orderedElements addObjectsFromArray: currentChildren];
[originalElements removeObjectsInArray: currentChildren];
}
// We add the remaining, unordered elements
[orderedElements addObjectsFromArray: originalElements];
children = [orderedElements objectEnumerator];
}
else
{
children = [[aGroup children] objectEnumerator]; children = [[aGroup children] objectEnumerator];
}
while ((currentChild = [children nextObject])) while ((currentChild = [children nextObject]))
[rendering appendString: [self render: currentChild]]; [rendering appendString: [self render: currentChild]];
[rendering appendFormat: @"END:%@\r\n", groupTag]; [rendering appendFormat: @"END:%@\r\n", groupTag];

View file

@ -17,11 +17,13 @@ ADDITIONAL_INCLUDE_DIRS += \
# dependencies # dependencies
BASE_LIBS := $(shell gnustep-config --base-libs)
libNGCards_LIBRARIES_DEPEND_UPON += \ libNGCards_LIBRARIES_DEPEND_UPON += \
-lNGExtensions \ -lNGExtensions \
-lEOControl \ -lEOControl \
-lSaxObjC \ -lSaxObjC \
-lgnustep-base -lobjc -lm $(BASE_LIBS)
NGCards_LIBRARIES_DEPEND_UPON += \ NGCards_LIBRARIES_DEPEND_UPON += \
-framework NGExtensions -framework EOControl \ -framework NGExtensions -framework EOControl \

View file

@ -38,18 +38,6 @@
@class iCalTimeZone; @class iCalTimeZone;
@interface iCalCalendar : CardGroup @interface iCalCalendar : CardGroup
// {
// NSString *version;
// NSString *calscale;
// NSString *prodId;
// NSString *method;
// NSMutableArray *todos;
// NSMutableArray *events;
// NSMutableArray *journals;
// NSMutableArray *freeBusys;
// NSMutableDictionary *timezones;
// }
/* accessors */ /* accessors */

View file

@ -210,6 +210,13 @@
return [super versitString]; return [super versitString];
} }
- (NSArray *) orderOfElements
{
return [NSArray arrayWithObjects: @"prodid", @"version", @"method", @"calscale",
@"vtimezone", @"vevent", @"vtodo", @"vjournal", @"vfreebusy", nil];
}
/* ical typing */ /* ical typing */
- (NSString *) entityName - (NSString *) entityName

View file

@ -30,6 +30,9 @@
@class iCalRecurrenceRule; @class iCalRecurrenceRule;
@interface iCalTimeZonePeriod : CardGroup @interface iCalTimeZonePeriod : CardGroup
{
NSCalendarDate *startDate;
}
- (NSCalendarDate *) startDate; - (NSCalendarDate *) startDate;
- (iCalRecurrenceRule *) recurrenceRule; - (iCalRecurrenceRule *) recurrenceRule;

View file

@ -103,10 +103,21 @@
// return dayOfWeek; // return dayOfWeek;
// } // }
- (void) dealloc
{
[startDate release];
[super dealloc];
}
- (NSCalendarDate *) startDate - (NSCalendarDate *) startDate
{ {
return [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] if (!startDate)
{
startDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"]
dateTime]; dateTime];
[startDate retain];
}
return startDate;
} }
- (iCalRecurrenceRule *) recurrenceRule - (iCalRecurrenceRule *) recurrenceRule

View file

@ -59,6 +59,7 @@
NSTimeInterval anInterval; NSTimeInterval anInterval;
id grandParent; id grandParent;
nextAlarmDate = nil;
triggerValue = [[self valueType] uppercaseString]; triggerValue = [[self valueType] uppercaseString];
if ([triggerValue length] == 0) if ([triggerValue length] == 0)
triggerValue = @"DURATION"; triggerValue = @"DURATION";
@ -88,8 +89,6 @@
} }
else if ([triggerValue isEqualToString: @"DATE-TIME"]) else if ([triggerValue isEqualToString: @"DATE-TIME"])
nextAlarmDate = [[self flattenedValuesForKey: @""] asCalendarDate]; nextAlarmDate = [[self flattenedValuesForKey: @""] asCalendarDate];
else
nextAlarmDate = nil;
return nextAlarmDate; return nextAlarmDate;
} }

View file

@ -12,9 +12,11 @@ import sys
imaphost = '127.0.0.1' imaphost = '127.0.0.1'
imapport = 143 imapport = 143
sambaprivate = '/var/lib/samba/private' sambaprivate = '/var/lib/samba/private'
mapistorefolder = "%s/mapistore" % (sambaprivate) mapistorefolder = "%s/mapistore" % (sambaprivate)
sogoDefaultsFile = "/home/sogo/GNUstep/Defaults/.GNUstepDefaults" sogoSysDefaultsFile = "/etc/sogo/sogo.conf"
sogoUserDefaultsFile = "/home/sogo/GNUstep/Defaults/.GNUstepDefaults"
# - takes a username and optionally its password # - takes a username and optionally its password
# - removes the entry in samba's ldap tree via ldbedit (NOTYET) # - removes the entry in samba's ldap tree via ldbedit (NOTYET)
@ -85,7 +87,6 @@ def main():
sqlCleanup(username) sqlCleanup(username)
def extractmb(si): def extractmb(si):
inparen = False inparen = False
inquote = False inquote = False
@ -134,7 +135,6 @@ def cleanupmb(mb, client):
print "mailbox '%s' coult NOT be deleted (code = '%s')" % (mb, code) print "mailbox '%s' coult NOT be deleted (code = '%s')" % (mb, code)
def imapCleanup(imaphost, imapport, username, userpass): def imapCleanup(imaphost, imapport, username, userpass):
client = imaplib.IMAP4(imaphost, imapport) client = imaplib.IMAP4(imaphost, imapport)
(code, data) = client.login(username, userpass) (code, data) = client.login(username, userpass)
if code != "OK": if code != "OK":
@ -142,19 +142,16 @@ def imapCleanup(imaphost, imapport, username, userpass) :
print "Logged in as '%s'" % username print "Logged in as '%s'" % username
for foldername in [ "Sync Issues", "Junk E-mail" ]: for foldername in ("Sync Issues", "Junk E-mail"):
(code, data) = client.list(foldername, "%") (code, data) = client.list(foldername, "%")
if code == "OK": if code == "OK":
for si in data: for si in data:
if si is not None: if si is not None:
print si
continue
mb = extractmb(si) mb = extractmb(si)
cleanupmb(mb, client) cleanupmb(mb, client)
client.logout() client.logout()
def mapistoreCleanup(mapistorefolder, username): def mapistoreCleanup(mapistorefolder, username):
# delete the user's folder under the mapistore and under mapistore/SOGo # delete the user's folder under the mapistore and under mapistore/SOGo
shutil.rmtree("%s/%s" % (mapistorefolder, username), ignore_errors=True) shutil.rmtree("%s/%s" % (mapistorefolder, username), ignore_errors=True)
shutil.rmtree("%s/SOGo/%s" % (mapistorefolder, username), ignore_errors=True) shutil.rmtree("%s/SOGo/%s" % (mapistorefolder, username), ignore_errors=True)
@ -187,18 +184,50 @@ def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
print "table '%s' emptied" % tablename print "table '%s' emptied" % tablename
def getOCSFolderInfoURL(): def getOCSFolderInfoURL():
global sogoDefaultsFile global sogoSysDefaultsFile, sogoUserDefaultsFile
sogoDefaults = plistlib.readPlist(sogoDefaultsFile)
try:
OCSFolderInfoURL = sogoDefaults['sogod']['OCSFolderInfoURL']
except KeyError:
OCSFolderInfoURL = "" OCSFolderInfoURL = ""
# read defaults from /etc/sogo/sogo.conf
if os.path.exists(sogoSysDefaultsFile):
sogoDefaults = plistlib.readPlist(sogoSysDefaultsFile)
if "OCSFolderInfoURL" in sogoDefaults:
OCSFolderInfoURL = sogoDefaults["OCSFolderInfoURL"]
# defaults from user directory must have precedence
if os.path.exists(sogoUserDefaultsFile):
sogoDefaults = plistlib.readPlist(sogoUserDefaultsFile)
if "sogod" in sogoDefaults and "OCSFolderInfoURL" in sogoDefaults["sogod"]:
OCSFolderInfoURL = sogoDefaults['sogod']['OCSFolderInfoURL']
return OCSFolderInfoURL return OCSFolderInfoURL
def asCSSIdentifier(inputString):
cssEscapingCharMap = {"_" : "_U_",
"." : "_D_",
"#" : "_H_",
"@" : "_A_",
"*" : "_S_",
":" : "_C_",
"," : "_CO_",
" " : "_SP_",
"'" : "_SQ_",
"&" : "_AM_",
"+" : "_P_"}
newChars = []
for c in inputString:
if c in cssEscapingCharMap:
newChars.append(cssEscapingCharMap[c])
else:
newChars.append(c)
return "".join(newChars)
def sqlCleanup(username): def sqlCleanup(username):
OCSFolderInfoURL = getOCSFolderInfoURL() OCSFolderInfoURL = getOCSFolderInfoURL()
if (OCSFolderInfoURL is None): if OCSFolderInfoURL is None:
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username)) raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username))
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info # postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
@ -212,13 +241,14 @@ def sqlCleanup(username):
dbname = m.group(6) dbname = m.group(6)
# 7 is folderinfo table # 7 is folderinfo table
encodedUserName = asCSSIdentifier(username)
if (proto == "postgresql"): if (proto == "postgresql"):
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username) postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
elif (proto == "mysql"): elif (proto == "mysql"):
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username) mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
else: else:
raise Exception("Unknown sql proto: " % (proto)) raise Exception("Unknown sql proto: " % (proto))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -28,22 +28,22 @@ $plugins
= array( = array(
"sogo-connector@inverse.ca" "sogo-connector@inverse.ca"
=> array( "application" => "thunderbird", => array( "application" => "thunderbird",
"version" => "10.0.3", "version" => "17.0.2",
"filename" => "sogo-connector-10.0.3.xpi" ), "filename" => "sogo-connector-17.0.2.xpi" ),
"sogo-integrator@inverse.ca" "sogo-integrator@inverse.ca"
=> array( "application" => "thunderbird", => array( "application" => "thunderbird",
"version" => "10.0.3", "version" => "17.0.2",
"filename" => "sogo-integrator-10.0.3.xpi" ), "filename" => "sogo-integrator-17.0.2.xpi" ),
"{e2fda1a4-762b-4020-b5ad-a41df1933103}" "{e2fda1a4-762b-4020-b5ad-a41df1933103}"
=> array( "application" => "thunderbird", => array( "application" => "thunderbird",
"version" => "1.2.3", "version" => "1.9",
"filename" => "lightning.xpi" ) "filename" => "lightning-1.9.xpi" )
); );
$applications $applications
= array( "thunderbird" => "<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id> = array( "thunderbird" => "<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
<em:minVersion>10.0</em:minVersion> <em:minVersion>17.0</em:minVersion>
<em:maxVersion>10.*</em:maxVersion>" ); <em:maxVersion>17.*</em:maxVersion>" );
$pluginname = $_GET["plugin"]; $pluginname = $_GET["plugin"];
$plugin =& $plugins[$pluginname]; $plugin =& $plugins[$pluginname];

View file

@ -8,23 +8,15 @@ vtodo_class1 = "(Tarefa Privada)";
vtodo_class2 = "(Tarefa Confidencial)"; vtodo_class2 = "(Tarefa Confidencial)";
/* Receipts */ /* Receipts */
"Title:" = "Título:"; "The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
"Start:" = "Início:"; "The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
"End:" = "Fim:"; "The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado";
"The following attendees(s) were notified:" = "Estes participantes foram notificados:";
"Receipt: users invited to a meeting" = "Recepção: usuários convidados para uma reunião"; "The following attendees(s) were added:" = "Estes participantes foram adicionados:";
"You have invited the following attendees(s):" = "Você convidou o(s) seguinte(s) participante(s):"; "The following attendees(s) were removed:" = "Estes participantes foram removidos:";
"... to attend the following event:" = ".. para participar do seguinte evento:";
"Receipt: invitation updated" = "Recepção: Convite atualizado";
"The following attendees(s):" = "O(s) seguinte(s) participante(s):";
"... have been notified of the changes to the following event:" = "... foi notificado das alterações do seguinte evento:";
"Receipt: attendees removed from an event" = "Recepção: participantes removidos de um evento";
"You have removed the following attendees(s):" = "Você removeu o(s) seguinte(s) participante(s): ";
"... from the following event:" = "... do seguinte evento:";
/* IMIP messages */ /* IMIP messages */
"calendar_label" = "Calendário:";
"startDate_label" = "Início:"; "startDate_label" = "Início:";
"endDate_label" = "Fim:"; "endDate_label" = "Fim:";
"due_label" = "Data de Vencimento:"; "due_label" = "Data de Vencimento:";
@ -35,14 +27,19 @@ vtodo_class2 = "(Tarefa Confidencial)";
/* Invitation */ /* Invitation */
"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\""; "Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(enviado por %{SentBy}) "; "(sent by %{SentBy}) " = "(enviado por %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}convidou você para %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou você para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}"; "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou você para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Deletion */ /* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\""; "Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}cancelou este evento: %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}"; = "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Update */ /* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "O compromisso \"%{Summary}\" de %{OldStartDate} mudou";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed" "The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou"; = "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou";
"The following parameters have changed in the \"%{Summary}\" meeting:" "The following parameters have changed in the \"%{Summary}\" meeting:"
@ -51,6 +48,10 @@ vtodo_class2 = "(Tarefa Confidencial)";
= "Por favor, aceitar ou recusar as alterações."; = "Por favor, aceitar ou recusar as alterações.";
/* Reply */ /* Reply */
"Accepted invitation: \"%{Summary}\"" = "Convite aceito: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Convite recusado: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "Convite delegado: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "Convite ainda não decidido: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation." "%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}foi aceitado seu convite ao evento."; = "%{Attendee} %{SentByText}foi aceitado seu convite ao evento.";
"%{Attendee} %{SentByText}has declined your event invitation." "%{Attendee} %{SentByText}has declined your event invitation."
@ -61,4 +62,5 @@ vtodo_class2 = "(Tarefa Confidencial)";
= "%{Attendee} %{SentByText}foi ainda não decidiu seu convite ao evento."; = "%{Attendee} %{SentByText}foi ainda não decidiu seu convite ao evento.";
/* Resources */ /* Resources */
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Número máximo de reservas simultâneas (%{NumberOfSimultaneousBookings}) alcançadas para o recurso \"%{Cn} %{SystemEmail}\"."; "Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Não foi possível acessar o recurso: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "O número máximo de reservas simultaneas (%{NumberOfSimultaneousBookings}) acabou para o recurso \"%{Cn} %{SystemEmail}\". O evento conflitante é \"%{EventTitle}\", e inicia em %{StartDate}.";

View file

@ -53,7 +53,7 @@ Appointments_RESOURCE_FILES += \
\ \
MSExchangeFreeBusySOAPRequest.wo MSExchangeFreeBusySOAPRequest.wo
Appointments_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Appointments_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings

View file

@ -28,14 +28,14 @@ vtodo_class2 = "(Zadanie poufne)";
"Event Invitation: \"%{Summary}\"" = "Zaproszenie na wydarzenie: \"%{Summary}\""; "Event Invitation: \"%{Summary}\"" = "Zaproszenie na wydarzenie: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(wysłane przez %{SentBy}) "; "(sent by %{SentBy}) " = "(wysłane przez %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił Cię na %{Summary}.⏎ ⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}"; "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił Cię na %{Summary}.⏎ ⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił cię na %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"; "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił cię na %{Summary}.\n\nPoczątek: %{StartDate} o %{StartTime}\nKoniec: %{EndDate} o %{EndTime}\nOpis: %{Description}";
/* Deletion */ /* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Wydarzenie anulowane: \"%{Summary}\""; "Event Cancelled: \"%{Summary}\"" = "Wydarzenie anulowane: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} anulował(a) to wydarzenie: %{Summary}.⏎⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}"; = "%{Organizer} %{SentByText} anulował(a) to wydarzenie: %{Summary}.⏎⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}anulował(a) to wydarzenie: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"; = "%{Organizer} %{SentByText}anulował(a) to wydarzenie: %{Summary}.\n\nPoczątek: %{StartDate} o %{StartTime}\nKoniec: %{EndDate} o %{EndTime}\nOpis: %{Description}";
/* Update */ /* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed" "The appointment \"%{Summary}\" for the %{OldStartDate} has changed"

View file

@ -2885,11 +2885,26 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
timezone: (iCalTimeZone *) timezone timezone: (iCalTimeZone *) timezone
{ {
SOGoAppointmentObject *object; SOGoAppointmentObject *object;
NSString *uid;
NSMutableString *content; NSMutableString *content;
NSString *uid;
uid = [self globallyUniqueObjectId]; uid = [self globallyUniqueObjectId];
[event setUid: uid]; [event setUid: uid];
// We first look if there's an event with the same UID in our calendar. If not,
// let's reuse what is in the event, otherwise generate a new GUID and use it.
uid = [event uid];
object = [self lookupName: uid
inContext: context
acquire: NO];
if (object && ![object isKindOfClass: [NSException class]])
{
uid = [self globallyUniqueObjectId];
[event setUid: uid];
}
object = [SOGoAppointmentObject objectWithName: uid object = [SOGoAppointmentObject objectWithName: uid
inContainer: self]; inContainer: self];
[object setIsNew: YES]; [object setIsNew: YES];
@ -3024,7 +3039,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
acquire: NO]; acquire: NO];
if (master) if (master)
{ {
// Associate the occurrence to the master event // Associate the occurrence to the master event and skip the actual import process
masterCalendar = [master calendar: NO secure: NO]; masterCalendar = [master calendar: NO secure: NO];
[masterCalendar addToEvents: event]; [masterCalendar addToEvents: event];
if (timezone) if (timezone)

View file

@ -1,8 +1,6 @@
/* SOGoAppointmentFolderICS.m - this file is part of SOGo /* SOGoAppointmentFolderICS.m - this file is part of SOGo
* *
* Copyright (C) 2010 Inverse inc. * Copyright (C) 2010-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -22,6 +20,7 @@
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <NGCards/CardElement.h>
#import <NGCards/iCalCalendar.h> #import <NGCards/iCalCalendar.h>
#import "SOGoAppointmentFolderICS.h" #import "SOGoAppointmentFolderICS.h"
@ -30,7 +29,19 @@
- (NSString *) contentAsString - (NSString *) contentAsString
{ {
return [[self contentCalendar] versitString]; iCalCalendar *cal;
CardElement *e;
NSString *s;
cal = [self contentCalendar];
e = [CardElement simpleElementWithTag: @"x-wr-calname"
value: [self displayName]];
[cal addChild: e];
s = [cal versitString];
[cal removeChild: e];
return s;
} }
- (NSString *) davContentType - (NSString *) davContentType

View file

@ -410,14 +410,6 @@
previousObject: oldEvent previousObject: oldEvent
toAttendees: updateAttendees toAttendees: updateAttendees
withType: @"calendar:invitation-update"]; withType: @"calendar:invitation-update"];
#if 0
// DELETE CODE
[self sendReceiptEmailForObject: newEvent
addedAttendees: nil
deletedAttendees: nil
updatedAttendees: updateAttendees];
#endif
} }
// //
@ -968,6 +960,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[otherAttendee setDelegatedTo: [attendee delegatedTo]]; [otherAttendee setDelegatedTo: [attendee delegatedTo]];
[otherAttendee setDelegatedFrom: [attendee delegatedFrom]]; [otherAttendee setDelegatedFrom: [attendee delegatedFrom]];
// Remove the RSVP attribute, as an action from the attendee
// was actually performed, and this confuses iCal (bug #1850)
[[otherAttendee attributes] removeObjectForKey: @"RSVP"];
// If one has accepted / declined an invitation on behalf of // If one has accepted / declined an invitation on behalf of
// the attendee, we add the user to the SENT-BY attribute. // the attendee, we add the user to the SENT-BY attribute.
if (b && ![[currentUser login] isEqualToString: [theOwnerUser login]]) if (b && ![[currentUser login] isEqualToString: [theOwnerUser login]])
@ -999,7 +995,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// //
// This method is invoked from the SOGo Web interface. // This method is invoked from the SOGo Web interface or from the DAV interface.
// //
// - theOwnerUser is owner of the calendar where the attendee // - theOwnerUser is owner of the calendar where the attendee
// participation state has changed. // participation state has changed.
@ -1063,6 +1059,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|| [currentStatus caseInsensitiveCompare: newStatus] || [currentStatus caseInsensitiveCompare: newStatus]
!= NSOrderedSame) != NSOrderedSame)
{ {
NSMutableArray *delegates;
NSString *delegatedUID;
delegatedUID = nil;
[attendee setPartStat: newStatus]; [attendee setPartStat: newStatus];
// If one has accepted / declined an invitation on behalf of // If one has accepted / declined an invitation on behalf of
@ -1087,9 +1087,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[attendee setDelegatedTo: [delegate email]]; [attendee setDelegatedTo: [delegate email]];
NSString *delegatedUID = nil;
NSMutableArray *delegates;
if (removeDelegate) if (removeDelegate)
{ {
delegates = [NSMutableArray array]; delegates = [NSMutableArray array];
@ -1123,13 +1120,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
previousObject: nil previousObject: nil
toAttendees: delegates toAttendees: delegates
withType: @"calendar:cancellation"]; withType: @"calendar:cancellation"];
#if 0 } // if (removeDelegate)
// DELETE CODE
[self sendReceiptEmailUsingTemplateNamed: @"Deletion"
forObject: event
to: delegates];
#endif
}
if (addDelegate) if (addDelegate)
{ {
@ -1148,12 +1139,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
previousObject: nil previousObject: nil
toAttendees: delegates toAttendees: delegates
withType: @"calendar:invitation"]; withType: @"calendar:invitation"];
#if 0 } // if (addDelegate)
// DELETE CODE
[self sendReceiptEmailUsingTemplateNamed: @"Invitation"
forObject: event to: delegates];
#endif
}
// If the current user isn't the organizer of the event // If the current user isn't the organizer of the event
// that has just been updated, we update the event and // that has just been updated, we update the event and
@ -1161,8 +1147,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
ownerUser = [SOGoUser userWithLogin: owner]; ownerUser = [SOGoUser userWithLogin: owner];
if (!(ex || [event userIsOrganizer: ownerUser])) if (!(ex || [event userIsOrganizer: ownerUser]))
{ {
if ([[attendee rsvp] isEqualToString: @"true"] if ([event isStillRelevant])
&& [event isStillRelevant])
[self sendResponseToOrganizer: event [self sendResponseToOrganizer: event
from: ownerUser]; from: ownerUser];
@ -1372,22 +1357,22 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
reason: @"delegate is a group"]; reason: @"delegate is a group"];
} }
if (ex == nil) if (ex == nil)
{
// Remove the RSVP attribute, as an action from the attendee
// was actually performed, and this confuses iCal (bug #1850)
[[attendee attributes] removeObjectForKey: @"RSVP"];
ex = [self _handleAttendee: attendee ex = [self _handleAttendee: attendee
withDelegate: delegate withDelegate: delegate
ownerUser: ownerUser ownerUser: ownerUser
statusChange: _status statusChange: _status
inEvent: event]; inEvent: event];
}
if (ex == nil) if (ex == nil)
{ {
// We generate the updated iCalendar file and we save it in // We generate the updated iCalendar file and we save it in
// the database. We do this ONLY when using SOGo from the // the database. We do this ONLY when using SOGo from the
// Web interface. Over DAV, it'll be handled directly in // Web interface. Over DAV, it'll be handled directly in
// PUTAction: // PUTAction:
/* FIXME: this is a hack caused by the fact that
updateContentWithCalendar:fromRequest: invokes
changeParticipationStatusblabla, while the latter was
actually designed to be invoked by web methods only */
if (![context request] || [[context request] handledByDefaultHandler]) if (![context request] || [[context request] handledByDefaultHandler])
ex = [self saveContentString: [calendar versitString]]; ex = [self saveContentString: [calendar versitString]];
} }
@ -1944,6 +1929,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
return [NSException exceptionWithHTTPStatus:403 return [NSException exceptionWithHTTPStatus:403
reason: @"sequences don't match"]; reason: @"sequences don't match"];
// Remove the RSVP attribute, as an action from the attendee
// was actually performed, and this confuses iCal (bug #1850)
[[attendee attributes] removeObjectForKey: @"RSVP"];
delegate = nil; delegate = nil;
delegateEmail = [attendee delegatedTo]; delegateEmail = [attendee delegatedTo];

View file

@ -725,7 +725,8 @@
header = [NSString stringWithFormat: @"text/calendar; method=%@;" header = [NSString stringWithFormat: @"text/calendar; method=%@;"
@" charset=\"%@\"", @" charset=\"%@\"",
[(iCalCalendar *) [object parent] method], charset]; [(iCalCalendar *) [object parent] method], charset];
headerMap = [NGMutableHashMap hashMapWithCapacity: 1]; headerMap = [NGMutableHashMap hashMapWithCapacity: 3];
[headerMap setObject: @"urn:content-classes:calendarmessage" forKey: @"Content-Class"];
[headerMap setObject: header forKey: @"content-type"]; [headerMap setObject: header forKey: @"content-type"];
[headerMap setObject: @"quoted-printable" [headerMap setObject: @"quoted-printable"
forKey: @"content-transfer-encoding"]; forKey: @"content-transfer-encoding"];

View file

@ -32,6 +32,7 @@
- (NSArray *) davCalendarUserAddressSet; - (NSArray *) davCalendarUserAddressSet;
- (NSArray *) davCalendarHomeSet; - (NSArray *) davCalendarHomeSet;
- (NSArray *) davCalendarScheduleInboxURL;
- (NSArray *) davCalendarScheduleOutboxURL; - (NSArray *) davCalendarScheduleOutboxURL;
@end @end

View file

@ -0,0 +1,66 @@
"Personal Calendar" = "Osobný kalendár";
vevent_class0 = "(Verejná udalosť)";
vevent_class1 = "(Súkromná udalosť)";
vevent_class2 = "(Dôverná udalosť)";
vtodo_class0 = "(Verejná úloha)";
vtodo_class1 = "(Súkromná úloha)";
vtodo_class2 = "(Dôverná úloha)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Udalosť \"%{Summary}\" bola vytvorená";
"The event \"%{Summary}\" was deleted" = "Udalosť \"%{Summary}\" bola vymazaná";
"The event \"%{Summary}\" was updated" = "Udalosť \"%{Summary}\" bola aktualizovaná";
"The following attendees(s) were notified:" = "Nasledujúci účastník(ci) bol upozornený:";
"The following attendees(s) were added:" = "Nasledujúci účastník(ci) bol pridaný:";
"The following attendees(s) were removed:" = "Nasledujúci účastník(ci) bol odstránený:";
/* IMIP messages */
"calendar_label" = "Kalendár:";
"startDate_label" = "Začiatok:";
"endDate_label" = "Koniec:";
"due_label" = "Platnosť:";
"location_label" = "Miesto:";
"summary_label" = "Zhrnutie:";
"comment_label" = "Komentár:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Pozvánka na udalosť: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(odoslané %{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}Vás pozval na %{Summary}.⏎ ⏎ Začiatok: %{StartDate}⏎ Koniec: %{EndDate}⏎ Podrobnosti: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.⏎ ⏎ Start: %{StartDate} at %{StartTime}⏎ End: %{EndDate} at %{EndTime}⏎ Description: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Udalosť zrušená: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil túto udalosť: %{Summary}.⏎ ⏎ Začiatok: %{StartDate}⏎ Koniec: %{EndDate}⏎ Podrobnosti: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil túto udalosť: %{Summary}.⏎ ⏎\nZačiatok: %{StartDate} o %{StartTime}⏎ Koniec: %{EndDate} o %{EndTime}⏎ \nPopis: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Stretnutie \"%{Summary}\" z %{OldStartDate} bolo zmenené";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "Stretnutie \"%{Summary}\" z %{OldStartDate} o %{OldStartTime} bola zmenená";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "Nasledujúce parametre boli zmenené v \"%{Summary}\" stretnutie:";
"Please accept or decline those changes."
= "Prosím prijmite alebo odmietnite tieto zmeny.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Prijatá pozvánka: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Zamietnutá pozvánka: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "Delegovaná pozvánka: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "Doteraz nerozhodnutá pozvánka: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}prijal Vašu pozvánku.";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText}odmietol Vašu pozvánku.";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText}delegoval pozvánku na %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}zatiaľ nerozhodol o Vašej pozvánke.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Zdroj nedostupný: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximálny počet súčasných rezervácií (%{NumberOfSimultaneousBookings}) prekročil zdroje \"%{Cn} %{SystemEmail}\". Konfliktná udalosť je \"%{EventTitle}\", začínajúca %{StartDate}.";

View file

@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
Contacts_RESOURCE_FILES += \ Contacts_RESOURCE_FILES += \
product.plist \ product.plist \
Contacts_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Contacts_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings

View file

@ -216,11 +216,12 @@ convention:
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord - (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord
{ {
CardElement *element;
NSArray *units;
NSInteger year, yearOfToday, month, day; NSInteger year, yearOfToday, month, day;
CardElement *element;
NSCalendarDate *now; NSCalendarDate *now;
NSArray *units;
NSString *ou; NSString *ou;
id o;
[self setNWithFamily: [ldifRecord objectForKey: @"sn"] [self setNWithFamily: [ldifRecord objectForKey: @"sn"]
given: [ldifRecord objectForKey: @"givenname"] given: [ldifRecord objectForKey: @"givenname"]
@ -303,7 +304,15 @@ convention:
forKey: @""]; forKey: @""];
[self setNote: [ldifRecord objectForKey: @"description"]]; [self setNote: [ldifRecord objectForKey: @"description"]];
[self setCategories: [ldifRecord objectForKey: @"vcardcategories"]];
o = [ldifRecord objectForKey: @"vcardcategories"];
// We can either have an array (from SOGo's web gui) or a
// string (from a LDIF import) as the value here.
if ([o isKindOfClass: [NSArray class]])
[self setCategories: o];
else
[self setCategories: [o componentsSeparatedByString: @","]];
[self cleanupEmptyChildren]; [self cleanupEmptyChildren];
} }

View file

@ -1,6 +1,6 @@
/* NSDictionary+LDIF.m - this file is part of SOGo /* NSDictionary+LDIF.m - this file is part of SOGo
* *
* Copyright (C) 2011 Inverse inc * Copyright (C) 2011-2012 Inverse inc
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *

View file

@ -191,6 +191,10 @@ static NSArray *folderListingFields = nil;
qs = [NSString stringWithFormat: qs = [NSString stringWithFormat:
@"(c_categories isCaseInsensitiveLike: '%%%@%%')", @"(c_categories isCaseInsensitiveLike: '%%%@%%')",
filter]; filter];
else if ([criteria isEqualToString: @"organization"])
qs = [NSString stringWithFormat:
@"(c_o isCaseInsensitiveLike: '%%%@%%')",
filter];
else else
qs = @"(1 == 0)"; qs = @"(1 == 0)";

View file

@ -1,6 +1,6 @@
/* SOGoContactLDIFEntry.m - this file is part of SOGo /* SOGoContactLDIFEntry.m - this file is part of SOGo
* *
* Copyright (C) 2006-2011 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca> * Ludovic Marcotte <lmarcotte@inverse.ca>

View file

@ -0,0 +1 @@
"Personal Address Book" = "Osobné kontakty";

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2007-2010 Inverse inc. Copyright (C) 2007-2012 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo. This file is part of SOGo.
@ -314,8 +314,8 @@ static NSString *userAgent = nil;
if ([replyTo length] > 0) if ([replyTo length] > 0)
{ {
[headers setObject: replyTo forKey: @"reply-to"]; [headers setObject: replyTo forKey: @"reply-to"];
[headers removeObjectForKey: @"replyTo"];
} }
[headers removeObjectForKey: @"replyTo"];
if ([[newHeaders objectForKey: @"receipt"] isEqualToString: @"true"]) if ([[newHeaders objectForKey: @"receipt"] isEqualToString: @"true"])
{ {
@ -804,8 +804,8 @@ static NSString *userAgent = nil;
- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail - (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
{ {
NSDictionary *info, *attachment; NSDictionary *info, *attachment;
NSString *signature, *nl;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
NSString *signature;
[sourceMail fetchCoreInfos]; [sourceMail fetchCoreInfos];
@ -835,7 +835,10 @@ static NSString *userAgent = nil;
// error = [newDraft saveAttachment:content withName:@"forward.eml"]; // error = [newDraft saveAttachment:content withName:@"forward.eml"];
signature = [[self mailAccountFolder] signature]; signature = [[self mailAccountFolder] signature];
if ([signature length]) if ([signature length])
[self setText: [NSString stringWithFormat: @"\n-- \n%@", signature]]; {
nl = (isHTML ? @"<br/>" : @"\n");
[self setText: [NSString stringWithFormat: @"%@-- %@%@", nl, nl, signature]];
}
attachment = [NSDictionary dictionaryWithObjectsAndKeys: attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[sourceMail filenameForForward], @"filename", [sourceMail filenameForForward], @"filename",
@"message/rfc822", @"mimetype", @"message/rfc822", @"mimetype",

View file

@ -1,6 +1,6 @@
/* SOGoMailForward.m - this file is part of SOGo /* SOGoMailForward.m - this file is part of SOGo
* *
* Copyright (C) 2007 Inverse inc. * Copyright (C) 2007-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -226,11 +226,15 @@
- (NSString *) signature - (NSString *) signature
{ {
NSString *signature, *mailSignature; NSString *signature, *mailSignature, *nl;
signature = [[sourceMail mailAccountFolder] signature]; signature = [[sourceMail mailAccountFolder] signature];
if ([signature length]) if ([signature length])
mailSignature = [NSString stringWithFormat: @"-- \n%@", signature]; {
nl = (htmlComposition ? @"<br/>" : @"\n");
mailSignature = [NSString stringWithFormat: @"-- %@%@", nl, signature];
}
else else
mailSignature = @""; mailSignature = @"";

View file

@ -0,0 +1,10 @@
-------- Pôvodná správa --------
Predmet: <#subject/>
Dátum: <#date/>
Od: <#from/>
<#hasReplyTo>Odpoveď na: <#replyTo/></#hasReplyTo><#hasOrganization>Organizácia: <#organization/></#hasOrganization>Komu: <#to/>
<#hasCc>Kópia: <#cc/></#hasCc><#hasNewsGroups>Diskusné skupiny: <#newsgroups/></#hasNewsGroups><#hasReferences>Odkazy: <#references/></#hasReferences>
<#messageBody/>
<#signature/>

View file

@ -0,0 +1,79 @@
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}

View file

@ -0,0 +1,17 @@
<#signaturePlacementOnTop>
<#signature/>
</#signaturePlacementOnTop>
<#outlookMode>-------- Pôvodná správa --------
Predmet: <#subject/>
Dátum: <#date/>
Od: <#from/>
<#hasReplyTo>Odpoveď pre: <#replyTo/></#hasReplyTo><#hasOrganization>Organizácia: <#organization/></#hasOrganization>Komu: <#to/>
<#hasCc>Kópia: <#cc/></#hasCc><#hasNewsGroups>Diskusné skupiny: <#newsgroups/></#hasNewsGroups><#hasReferences>Odkazy: <#references/></#hasReferences></#outlookMode>
<#standardMode> <#date/>, <#from/> napísal:</#standardMode>
<#messageBody/>
<#signaturePlacementOnBottom><#signature/></#signaturePlacementOnBottom>

View file

@ -0,0 +1,106 @@
outlookMode: WOConditional {
condition = outlookMode;
}
standardMode: WOConditional {
condition = outlookMode;
negate = YES;
}
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
replyPlacementOnTop: WOConditional {
condition = replyPlacementOnTop;
}
replyPlacementOnBottom: WOConditional {
condition = replyPlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}

View file

@ -142,6 +142,16 @@ SOGo_RESOURCE_FILES = \
SOGoDefaults.plist \ SOGoDefaults.plist \
DAVReportMap.plist DAVReportMap.plist
ifeq ($(saml2_config), yes)
SOGo_HEADER_FILES += SOGoSAML2Session.h SOGoSAML2Exceptions.h
SOGo_OBJC_FILES += SOGoSAML2Session.m SOGoSAML2Exceptions.m
SOGo_RESOURCE_FILES += SOGoSAML2Metadata.xml
SOGoSAML2Exceptions.h SOGoSAML2Exceptions.m: gen-saml2-exceptions.py
$(ECHO_CREATING) ./gen-saml2-exceptions.py $(LASSO_CFLAGS) $(END_ECHO)
endif
ifeq ($(ldap_config),yes) ifeq ($(ldap_config),yes)
LIBRARY_NAME += \ LIBRARY_NAME += \

View file

@ -24,7 +24,8 @@ SOGo_LIBRARIES_DEPEND_UPON += \
-lNGStreams -lNGExtensions -lEOControl \ -lNGStreams -lNGExtensions -lEOControl \
-lDOM -lSaxObjC \ -lDOM -lSaxObjC \
-lNGLdap -lSBJson \ -lNGLdap -lSBJson \
-lGDLContentStore -lgnustep-base -lobjc -ldl -lGDLContentStore \
$(BASE_LIBS)
ifeq ($(HAS_LIBRARY_gnutls),yes) ifeq ($(HAS_LIBRARY_gnutls),yes)
ADDITIONAL_CPPFLAGS += -DHAVE_GNUTLS=1 ADDITIONAL_CPPFLAGS += -DHAVE_GNUTLS=1
@ -36,6 +37,11 @@ SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
endif endif
endif endif
ifeq ($(HAS_LIBRARY_lasso), yes)
ADDITIONAL_CPPFLAGS += $(LASSO_CFLAGS)
SOGo_LIBRARIES_DEPEND_UPON += $(LASSO_LIBS)
endif
ifeq ($(findstring openbsd, $(GNUSTEP_HOST_OS)), openbsd) ifeq ($(findstring openbsd, $(GNUSTEP_HOST_OS)), openbsd)
SOGo_LIBRARIES_DEPEND_UPON += -lcrypto SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
else else

View file

@ -62,8 +62,11 @@
NSString *IDField; // the first part of a user DN NSString *IDField; // the first part of a user DN
NSString *CNField; NSString *CNField;
NSString *UIDField; NSString *UIDField;
NSArray *mailFields, *searchFields; NSArray *mailFields;
NSString *IMAPHostField, *IMAPLoginField; NSArray *searchFields;
NSString *IMAPHostField;
NSString *IMAPLoginField;
NSString *SieveHostField;
NSArray *bindFields; NSArray *bindFields;
BOOL listRequiresDot; BOOL listRequiresDot;
@ -110,6 +113,7 @@
searchFields: (NSArray *) newSearchFields searchFields: (NSArray *) newSearchFields
IMAPHostField: (NSString *) newIMAPHostField IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
bindFields: (id) newBindFields bindFields: (id) newBindFields
kindField: (NSString *) newKindField kindField: (NSString *) newKindField
andMultipleBookingsField: (NSString *) newMultipleBookingsField; andMultipleBookingsField: (NSString *) newMultipleBookingsField;

View file

@ -107,6 +107,7 @@ static Class NSStringK;
[searchFields retain]; [searchFields retain];
IMAPHostField = nil; IMAPHostField = nil;
IMAPLoginField = nil; IMAPLoginField = nil;
SieveHostField = nil;
bindFields = nil; bindFields = nil;
_scope = @"sub"; _scope = @"sub";
_filter = nil; _filter = nil;
@ -149,6 +150,7 @@ static Class NSStringK;
[searchFields release]; [searchFields release];
[IMAPHostField release]; [IMAPHostField release];
[IMAPLoginField release]; [IMAPLoginField release];
[SieveHostField release];
[bindFields release]; [bindFields release];
[_filter release]; [_filter release];
[_userPasswordAlgorithm release]; [_userPasswordAlgorithm release];
@ -194,6 +196,7 @@ static Class NSStringK;
searchFields: [udSource objectForKey: @"SearchFieldNames"] searchFields: [udSource objectForKey: @"SearchFieldNames"]
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"] IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"] IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
SieveHostField: [udSource objectForKey: @"SieveHostFieldName"]
bindFields: [udSource objectForKey: @"bindFields"] bindFields: [udSource objectForKey: @"bindFields"]
kindField: [udSource objectForKey: @"KindFieldName"] kindField: [udSource objectForKey: @"KindFieldName"]
andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]]; andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]];
@ -311,6 +314,7 @@ static Class NSStringK;
searchFields: (NSArray *) newSearchFields searchFields: (NSArray *) newSearchFields
IMAPHostField: (NSString *) newIMAPHostField IMAPHostField: (NSString *) newIMAPHostField
IMAPLoginField: (NSString *) newIMAPLoginField IMAPLoginField: (NSString *) newIMAPLoginField
SieveHostField: (NSString *) newSieveHostField
bindFields: (id) newBindFields bindFields: (id) newBindFields
kindField: (NSString *) newKindField kindField: (NSString *) newKindField
andMultipleBookingsField: (NSString *) newMultipleBookingsField andMultipleBookingsField: (NSString *) newMultipleBookingsField
@ -326,6 +330,8 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
ASSIGN(IMAPHostField, [newIMAPHostField lowercaseString]); ASSIGN(IMAPHostField, [newIMAPHostField lowercaseString]);
if (newIMAPLoginField) if (newIMAPLoginField)
ASSIGN(IMAPLoginField, [newIMAPLoginField lowercaseString]); ASSIGN(IMAPLoginField, [newIMAPLoginField lowercaseString]);
if (newSieveHostField)
ASSIGN(SieveHostField, [newSieveHostField lowercaseString]);
if (newMailFields) if (newMailFields)
ASSIGN(mailFields, newMailFields); ASSIGN(mailFields, newMailFields);
if (newSearchFields) if (newSearchFields)
@ -586,7 +592,10 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm]; pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
if (pass == nil) if (pass == nil)
{
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm]; [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
return nil;
}
return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass]; return [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
} }
@ -629,13 +638,20 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
NGLdapModification *mod; NGLdapModification *mod;
NGLdapAttribute *attr; NGLdapAttribute *attr;
NSArray *changes; NSArray *changes;
NSString* encryptedPass;
attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"]; attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"];
if ([_userPasswordAlgorithm isEqualToString: @"none"]) if ([_userPasswordAlgorithm isEqualToString: @"none"])
[attr addStringValue: newPassword]; {
encryptedPass = newPassword;
}
else else
[attr addStringValue: [self _encryptPassword: newPassword]]; {
encryptedPass = [self _encryptPassword: newPassword];
}
if(encryptedPass != nil)
{
[attr addStringValue: encryptedPass];
mod = [NGLdapModification replaceModification: attr]; mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod]; changes = [NSArray arrayWithObject: mod];
*perr = PolicyNoError; *perr = PolicyNoError;
@ -643,11 +659,14 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
if ([bindConnection bindWithMethod: @"simple" if ([bindConnection bindWithMethod: @"simple"
binddn: userDN binddn: userDN
credentials: oldPassword]) credentials: oldPassword])
{
didChange = [bindConnection modifyEntryWithDN: userDN didChange = [bindConnection modifyEntryWithDN: userDN
changes: changes]; changes: changes];
}
else else
didChange = NO; didChange = NO;
} }
}
else else
didChange = [bindConnection changePasswordAtDn: userDN didChange = [bindConnection changePasswordAtDn: userDN
oldPassword: oldPassword oldPassword: oldPassword
@ -835,6 +854,13 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField
if ([ldapValue length] > 0) if ([ldapValue length] > 0)
[ldifRecord setObject: ldapValue forKey: @"c_imaplogin"]; [ldifRecord setObject: ldapValue forKey: @"c_imaplogin"];
} }
if (SieveHostField)
{
ldapValue = [[ldapEntry attributeWithName: SieveHostField] stringValueAtIndex: 0];
if ([ldapValue length] > 0)
[ldifRecord setObject: ldapValue forKey: @"c_sievehostname"];
}
} }
- (void) _fillConstraints: (NGLdapEntry *) ldapEntry - (void) _fillConstraints: (NGLdapEntry *) ldapEntry
@ -1596,6 +1622,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
searchFields: nil searchFields: nil
IMAPHostField: nil IMAPHostField: nil
IMAPLoginField: nil IMAPLoginField: nil
SieveHostField: nil
bindFields: nil bindFields: nil
kindField: nil kindField: nil
andMultipleBookingsField: nil]; andMultipleBookingsField: nil];

View file

@ -56,7 +56,7 @@ typedef enum {
- (NSString *) asSHA1String; - (NSString *) asSHA1String;
- (NSString *) asMD5String; - (NSString *) asMD5String;
+ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme; + (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme;
@end @end

View file

@ -71,8 +71,7 @@
{ {
NSString *scheme; NSString *scheme;
NSString *pass; NSString *pass;
NSArray *schemeComps; NSArray *encodingAndScheme;
keyEncoding encoding;
NSRange range; NSRange range;
int selflen, len; int selflen, len;
@ -88,32 +87,11 @@
if (len == 0) if (len == 0)
scheme = defaultScheme; scheme = defaultScheme;
encoding = [NSString getDefaultEncodingForScheme: scheme]; encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
// get the encoding which may be part of the scheme
// e.g. ssha.hex forces a hex encoded ssha scheme
// possible is "b64" or "hex"
schemeComps = [scheme componentsSeparatedByString: @"."];
if ([schemeComps count] == 2)
{
NSString *stringEncoding;
// scheme without encoding string is the first item
scheme = [schemeComps objectAtIndex: 0];
// encoding string is second item
stringEncoding = [schemeComps objectAtIndex: 1];
if ([stringEncoding caseInsensitiveCompare: @"hex"] == NSOrderedSame)
{
encoding = encHex;
}
else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame ||
[stringEncoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
{
encoding = encBase64;
}
}
pass = [self substringWithRange: range]; pass = [self substringWithRange: range];
return [NSArray arrayWithObjects: scheme, pass, [NSNumber numberWithInt: encoding], nil]; // return array with [scheme, password, encoding]
return [NSArray arrayWithObjects: [encodingAndScheme objectAtIndex: 1], pass, [encodingAndScheme objectAtIndex: 0], nil];
} }
/** /**
@ -208,8 +186,10 @@
* *
* @param passwordScheme The scheme to use * @param passwordScheme The scheme to use
* @param theSalt The binary data of the salt * @param theSalt The binary data of the salt
* @param userEncoding The encoding (plain, hex, base64) to be used * @param userEncoding The encoding (plain, hex, base64) to be used. If set to
* @return If successful, the encrypted and encoded NSString of the format {scheme}pass, or nil if the scheme did not exists or an error occured * encDefault, the encoding will be detected from scheme name.
* @return If successful, the encrypted and encoded NSString of the format {scheme}pass,
* or nil if the scheme did not exists or an error occured.
*/ */
- (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme - (NSString *) asCryptedPassUsingScheme: (NSString *) passwordScheme
withSalt: (NSData *) theSalt withSalt: (NSData *) theSalt
@ -217,6 +197,22 @@
{ {
keyEncoding dataEncoding; keyEncoding dataEncoding;
NSData* cryptedData; NSData* cryptedData;
// use default encoding scheme, when set to default
if (userEncoding == encDefault)
{
// the encoding needs to be detected before crypting,
// to get the plain scheme (without encoding identifier)
NSArray* encodingAndScheme;
encodingAndScheme = [NSString getDefaultEncodingForScheme: passwordScheme];
dataEncoding = [[encodingAndScheme objectAtIndex: 0] intValue];
passwordScheme = [encodingAndScheme objectAtIndex: 1];
}
else
{
dataEncoding = userEncoding;
}
// convert NSString to NSData and apply encryption scheme // convert NSString to NSData and apply encryption scheme
cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding]; cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt]; cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
@ -224,12 +220,6 @@
if (cryptedData == nil) if (cryptedData == nil)
return nil; return nil;
// use default encoding scheme, when set to default
if (userEncoding == encDefault)
dataEncoding = [NSString getDefaultEncodingForScheme: passwordScheme];
else
dataEncoding = userEncoding;
if (dataEncoding == encHex) if (dataEncoding == encHex)
{ {
// hex encoding // hex encoding
@ -250,19 +240,49 @@
/** /**
* Returns the encoding for a specified scheme * Returns the encoding for a specified scheme
* *
* @param passwordScheme The scheme for which to get the encoding. * @param passwordScheme The scheme for which to get the encoding. Can be "scheme.encoding" in which case the encoding is returned
* @see keyEncoding * @see keyEncoding
* @return returns the encoding, if unknown returns encPlain * @return returns NSArray with elements {NSNumber encoding, NSString* scheme} where scheme is the 'real' scheme without the ".encoding" part.
* 'encoding' is stored as NSNumber in the array. If the encoding was not detected, encPlain is used for encoding.
*/ */
+ (keyEncoding) getDefaultEncodingForScheme: (NSString *) passwordScheme + (NSArray *) getDefaultEncodingForScheme: (NSString *) passwordScheme
{ {
NSArray *schemeComps;
NSString *trueScheme;
keyEncoding encoding = encPlain;
// get the encoding which may be part of the scheme
// e.g. ssha.hex forces a hex encoded ssha scheme
// possible is "b64" or "hex"
schemeComps = [passwordScheme componentsSeparatedByString: @"."];
if ([schemeComps count] == 2)
{
trueScheme = [schemeComps objectAtIndex: 0];
NSString *stringEncoding;
// encoding string is second item
stringEncoding = [schemeComps objectAtIndex: 1];
if ([stringEncoding caseInsensitiveCompare: @"hex"] == NSOrderedSame)
{
encoding = encHex;
}
else if ([stringEncoding caseInsensitiveCompare: @"b64"] == NSOrderedSame ||
[stringEncoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
{
encoding = encBase64;
}
}
else
{
trueScheme = passwordScheme;
}
// in order to keep backwards-compatibility, hex encoding is used for sha1 here // in order to keep backwards-compatibility, hex encoding is used for sha1 here
if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame || if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame || [passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame || [passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame) [passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
{ {
return encHex; encoding = encHex;
} }
else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame || else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame || [passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame ||
@ -272,9 +292,9 @@
[passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame || [passwordScheme caseInsensitiveCompare: @"sha512"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame) [passwordScheme caseInsensitiveCompare: @"ssha512"] == NSOrderedSame)
{ {
return encBase64; encoding = encBase64;
} }
return encPlain; return [NSArray arrayWithObjects: [NSNumber numberWithInt: encoding], trueScheme, nil];
} }
/** /**

View file

@ -134,15 +134,16 @@ static int cssEscapingCount;
start--; start--;
start++; start++;
length = [self length];
// In [UIxMailPartTextViewer flatContentAsString], we first escape HTML entities and then // In [UIxMailPartTextViewer flatContentAsString], we first escape HTML entities and then
// add URLs. Therefore, the brackets (inequality signs <>) have been encoded at this point. // add URLs. Therefore, the brackets (inequality signs <>) have been encoded at this point.
if ([[self substringWithRange: NSMakeRange (start, 4)] compare: @"&lt;"] == NSOrderedSame) if (length > (start + 4)
&& [[self substringWithRange: NSMakeRange (start, 4)] compare: @"&lt;"] == NSOrderedSame)
start += 4; start += 4;
length = [self length] - start; length -= start;
workRange = NSMakeRange (start, length);
workRange = [self rangeOfCharacterFromSet: urlAfterEndingChars workRange = [self rangeOfCharacterFromSet: urlAfterEndingChars
options: NSLiteralSearch range: workRange]; options: NSLiteralSearch range: NSMakeRange (start, length)];
if (workRange.location != NSNotFound) if (workRange.location != NSNotFound)
length = workRange.location - start; length = workRange.location - start;
while while

View file

@ -111,6 +111,11 @@
- (void) setCASPGTId: (NSString *) pgtId - (void) setCASPGTId: (NSString *) pgtId
forPGTIOU: (NSString *) pgtIou; forPGTIOU: (NSString *) pgtIou;
// SAML2 support
- (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier;
- (void) setSaml2LoginDumps: (NSDictionary *) dump
forIdentifier: (NSString *) identifier;
// //
// ACL caching support // ACL caching support
// //

View file

@ -520,6 +520,27 @@ static memcached_st *handle = NULL;
forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]]; forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]];
} }
// SAML2 support
- (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier
{
NSString *key, *jsonString;
key = [NSString stringWithFormat: @"saml2-login:%@", identifier];
jsonString = [self valueForKey: key];
return [jsonString objectFromJSONString];
}
- (void) setSaml2LoginDumps: (NSDictionary *) dump
forIdentifier: (NSString *) identifier
{
NSString *key;
key = [NSString stringWithFormat: @"saml2-login:%@", identifier];
[self setValue: [dump jsonRepresentation] forKey: key];
}
// //
// ACL caching code // ACL caching code
// //

View file

@ -36,7 +36,7 @@
SOGoSupportedLanguages = ( "Catalan", "Czech", "Dutch", "Danish", "Welsh", "English", SOGoSupportedLanguages = ( "Catalan", "Czech", "Dutch", "Danish", "Welsh", "English",
"SpanishSpain", "SpanishArgentina", "French", "German", "SpanishSpain", "SpanishArgentina", "French", "German",
"Icelandic", "Italian", "Hungarian", "BrazilianPortuguese", "Icelandic", "Italian", "Hungarian", "BrazilianPortuguese",
"NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak",
"Ukrainian", "Swedish" ); "Ukrainian", "Swedish" );
SOGoTimeZone = "UTC"; SOGoTimeZone = "UTC";

View file

@ -205,7 +205,7 @@
mailingMechanism = nil; mailingMechanism = nil;
} }
return mailingMechanism; return [mailingMechanism lowercaseString];
} }
- (NSArray *) mailPollingIntervals - (NSArray *) mailPollingIntervals
@ -220,7 +220,7 @@
- (NSString *) smtpAuthenticationType - (NSString *) smtpAuthenticationType
{ {
return [self stringForKey: @"SOGoSMTPAuthenticationType"]; return [[self stringForKey: @"SOGoSMTPAuthenticationType"] lowercaseString];
} }
- (NSString *) mailSpoolPath - (NSString *) mailSpoolPath

View file

@ -1,7 +1,7 @@
/* SOGoGCSFolder.m - this file is part of SOGo /* SOGoGCSFolder.m - this file is part of SOGo
* *
* Copyright (C) 2004-2005 SKYRIX Software AG * Copyright (C) 2004-2005 SKYRIX Software AG
* Copyright (C) 2006-2010 Inverse inc. * Copyright (C) 2006-2012 Inverse inc.
* *
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca> * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
@ -835,7 +835,7 @@ static NSArray *childRecordFields = nil;
- (BOOL) subscribeUserOrGroup: (NSString *) theIdentifier - (BOOL) subscribeUserOrGroup: (NSString *) theIdentifier
reallyDo: (BOOL) reallyDo reallyDo: (BOOL) reallyDo
{ {
NSMutableDictionary *moduleSettings; NSMutableDictionary *moduleSettings, *folderShowAlarms;
NSMutableArray *folderSubscription; NSMutableArray *folderSubscription;
NSString *subscriptionPointer; NSString *subscriptionPointer;
NSMutableArray *allUsers; NSMutableArray *allUsers;
@ -887,6 +887,8 @@ static NSArray *childRecordFields = nil;
= [moduleSettings objectForKey: @"SubscribedFolders"]; = [moduleSettings objectForKey: @"SubscribedFolders"];
subscriptionPointer = [self folderReference]; subscriptionPointer = [self folderReference];
folderShowAlarms = [moduleSettings objectForKey: @"FolderShowAlarms"];
if (reallyDo) if (reallyDo)
{ {
if (!(folderSubscription if (!(folderSubscription
@ -897,14 +899,26 @@ static NSArray *childRecordFields = nil;
forKey: @"SubscribedFolders"]; forKey: @"SubscribedFolders"];
} }
if (!(folderShowAlarms
&& [folderShowAlarms isKindOfClass: [NSMutableDictionary class]]))
{
folderShowAlarms = [NSMutableDictionary dictionary];
[moduleSettings setObject: folderShowAlarms
forKey: @"FolderShowAlarms"];
}
[folderSubscription addObjectUniquely: subscriptionPointer]; [folderSubscription addObjectUniquely: subscriptionPointer];
// By default, we disable alarms on subscribed calendars
[folderShowAlarms setObject: [NSNumber numberWithBool: NO]
forKey: subscriptionPointer];
} }
else else
{ {
[self removeFolderSettings: moduleSettings [self removeFolderSettings: moduleSettings
withReference: subscriptionPointer]; withReference: subscriptionPointer];
[folderSubscription removeObject: subscriptionPointer]; [folderSubscription removeObject: subscriptionPointer];
[folderShowAlarms removeObjectForKey: subscriptionPointer];
} }
[us synchronize]; [us synchronize];

View file

@ -69,6 +69,11 @@
#import <NGLdap/NGLdapAttribute.h> #import <NGLdap/NGLdapAttribute.h>
#import <NGLdap/NGLdapEntry.h> #import <NGLdap/NGLdapEntry.h>
#define CHECK_CLASS(o) ({ \
if ([o isKindOfClass: [NSString class]]) \
o = [NSArray arrayWithObject: o]; \
})
@implementation SOGoGroup @implementation SOGoGroup
- (id) initWithIdentifier: (NSString *) theID - (id) initWithIdentifier: (NSString *) theID
@ -232,14 +237,17 @@
// Fetch "members" - we get DNs // Fetch "members" - we get DNs
d = [_entry asDictionary]; d = [_entry asDictionary];
o = [d objectForKey: @"member"]; o = [d objectForKey: @"member"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o]; if (o) [dns addObjectsFromArray: o];
// Fetch "uniqueMembers" - we get DNs // Fetch "uniqueMembers" - we get DNs
o = [d objectForKey: @"uniquemember"]; o = [d objectForKey: @"uniquemember"];
CHECK_CLASS(o);
if (o) [dns addObjectsFromArray: o]; if (o) [dns addObjectsFromArray: o];
// Fetch "memberUid" - we get UID (like login names) // Fetch "memberUid" - we get UID (like login names)
o = [d objectForKey: @"memberuid"]; o = [d objectForKey: @"memberuid"];
CHECK_CLASS(o);
if (o) [uids addObjectsFromArray: o]; if (o) [uids addObjectsFromArray: o];
c = [dns count] + [uids count]; c = [dns count] + [uids count];

View file

@ -1236,7 +1236,7 @@
- (SOGoWebDAVValue *) davCurrentUserPrincipal - (SOGoWebDAVValue *) davCurrentUserPrincipal
{ {
NSDictionary *userHREF; NSDictionary *userHREF;
NSString *login; NSString *login, *s;
SOGoUser *activeUser; SOGoUser *activeUser;
SOGoWebDAVValue *davCurrentUserPrincipal; SOGoWebDAVValue *davCurrentUserPrincipal;
@ -1246,7 +1246,8 @@
davCurrentUserPrincipal = nil; davCurrentUserPrincipal = nil;
else else
{ {
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, [self davURLAsString]); s = [NSString stringWithFormat: @"/SOGo/dav/%@", login];
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, s);
davCurrentUserPrincipal davCurrentUserPrincipal
= [davElementWithContent (@"current-user-principal", = [davElementWithContent (@"current-user-principal",
XMLNS_WEBDAV, XMLNS_WEBDAV,

View file

@ -0,0 +1,60 @@
/* SOGoSAML2Session.h - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@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 SOGOSAML2SESSION_H
#define SOGOSAML2SESSION_H
/* implementation of the SAML2 protocol as required for a client:
https://www.oasis-open.org/standards#samlv2.0 */
#import <Foundation/NSObject.h>
#include <lasso/lasso.h>
@class NSString;
@interface SOGoSAML2Session : NSObject
{
LassoLogin *lassoLogin;
NSString *login;
NSString *identifier;
NSString *assertion;
}
+ (NSString *) metadataInContext: (WOContext *) context;
+ (NSString *) authenticationURLInContext: (WOContext *) context;
+ (SOGoSAML2Session *) SAML2SessionInContext: (WOContext *) context;
+ (SOGoSAML2Session *) SAML2SessionWithIdentifier: (NSString *) newIdentifier
inContext: (WOContext *) context;
- (void) processAuthnResponse: (NSString *) authnResponse;
- (NSString *) login;
- (NSString *) identifier;
- (NSString *) assertion;
@end
#endif /* SOGOSAML2SESSION_H */

View file

@ -0,0 +1,424 @@
/* SOGoSAML2Session.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@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.
*/
#include <lasso/lasso.h>
#include <lasso/xml/misc_text_node.h>
#include <lasso/xml/saml-2.0/saml2_attribute.h>
#include <lasso/xml/saml-2.0/saml2_attribute_statement.h>
#include <lasso/xml/saml-2.0/saml2_attribute_value.h>
#include <lasso/xml/saml-2.0/samlp2_authn_request.h>
#include <lasso/xml/saml-2.0/samlp2_response.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSMapTable.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOResponse.h>
#import "SOGoCache.h"
#import "SOGoSAML2Exceptions.h"
#import "SOGoSystemDefaults.h"
#import "SOGoSAML2Session.h"
@interface WOContext (SOGoSAML2Extension)
- (NSString *) SAML2ServerURLString;
@end
@implementation WOContext (SOGoSAML2Extension)
- (NSString *) SAML2ServerURLString
{
NSString *appName;
NSURL *serverURL;
appName = [[WOApplication application] name];
serverURL = [NSURL URLWithString: [NSString stringWithFormat: @"/%@",
appName]
relativeToURL: [self serverURL]];
return [serverURL absoluteString];
}
@end
@implementation SOGoSAML2Session
static NSMapTable *serverTable = nil;
+ (void) initialize
{
if (!serverTable)
{
serverTable = [NSMapTable mapTableWithStrongToWeakObjects];
[serverTable retain];
}
lasso_init ();
}
static LassoServer *
LassoServerInContext (WOContext *context)
{
NSString *urlString, *metadata, *filename, *keyContent, *certContent,
*idpKeyFilename, *idpCertFilename;
LassoServer *server;
SOGoSystemDefaults *sd;
urlString = [context SAML2ServerURLString];
server = NSMapGet (serverTable, urlString);
if (!server)
{
sd = [SOGoSystemDefaults sharedSystemDefaults];
filename = [sd SAML2PrivateKeyLocation];
if (!filename)
[NSException raise: NSInvalidArgumentException
format: @"'SAML2PrivateKeyLocation' not set"];
keyContent = [NSString stringWithContentsOfFile: filename];
if (!keyContent)
[NSException raise: NSGenericException
format: @"private key file '%@' could not be read",
filename];
filename = [sd SAML2CertificateLocation];
if (!filename)
[NSException raise: NSInvalidArgumentException
format: @"'SAML2CertificateLocation' not set"];
certContent = [NSString stringWithContentsOfFile: filename];
if (!certContent)
[NSException raise: NSGenericException
format: @"certificate file '%@' could not be read",
filename];
metadata = [SOGoSAML2Session metadataInContext: context];
/* FIXME: enable key password in config ? */
server = lasso_server_new_from_buffers ([metadata UTF8String],
[keyContent UTF8String],
NULL,
[certContent UTF8String]);
filename = [sd SAML2IdpMetadataLocation];
idpKeyFilename = [sd SAML2IdpPublicKeyLocation];
idpCertFilename = [sd SAML2IdpCertificateLocation];
lasso_server_add_provider (server, LASSO_PROVIDER_ROLE_IDP,
[filename UTF8String],
[idpKeyFilename UTF8String],
[idpCertFilename UTF8String]);
NSMapInsert (serverTable, urlString, server);
}
return server;
}
+ (NSString *) authenticationURLInContext: (WOContext *) context
{
lasso_error_t rc;
LassoServer *server;
LassoLogin *tempLogin;
LassoSamlp2AuthnRequest *request;
NSString *url;
GList *providers;
server = LassoServerInContext (context);
tempLogin = lasso_login_new (server);
providers = g_hash_table_get_keys (server->providers);
rc = lasso_login_init_authn_request (tempLogin, providers->data, LASSO_HTTP_METHOD_REDIRECT);
if (rc)
[NSException raiseSAML2Exception: rc];
request = LASSO_SAMLP2_AUTHN_REQUEST (LASSO_PROFILE (tempLogin)->request);
if (request->NameIDPolicy->Format) {
g_free (request->NameIDPolicy->Format);
}
request->NameIDPolicy->Format = g_strdup(LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT);
request->NameIDPolicy->AllowCreate = 1;
request->ForceAuthn = TRUE;
request->IsPassive = FALSE;
if (request->ProtocolBinding) {
g_free (request->ProtocolBinding);
}
// request->NameIDPolicy = strdup (LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED);
// request->consent = strdup (LASSO_LIB_CONSENT_OBTAINED);
rc = lasso_login_build_authn_request_msg (tempLogin);
if (rc)
[NSException raiseSAML2Exception: rc];
url = [NSString stringWithUTF8String: LASSO_PROFILE (tempLogin)->msg_url];
g_object_unref (tempLogin);
return url;
}
+ (NSString *) metadataInContext: (WOContext *) context
{
NSString *metadata, *serverURLString, *filename;
NSBundle *bundle;
bundle = [NSBundle bundleForClass: self];
filename = [bundle pathForResource: @"SOGoSAML2Metadata" ofType: @"xml"];
if (filename)
{
serverURLString = [context SAML2ServerURLString];
metadata = [[NSString stringWithContentsOfFile: filename]
stringByReplacingString: @"%{base_url}"
withString: serverURLString];
}
else
metadata = nil;
return metadata;
}
- (id) init
{
if ((self = [super init]))
{
lassoLogin = NULL;
login = nil;
identifier = nil;
assertion = nil;
}
return self;
}
- (void) _updateDataFromLogin
{
// LassoSamlp2Response *response;
LassoSaml2Assertion *saml2Assertion;
GList *statementList, *attributeList;
LassoSaml2AttributeStatement *statement;
LassoSaml2Attribute *attribute;
LassoSaml2AttributeValue *value;
LassoMiscTextNode *textNode;
LassoSaml2NameID *nameIdentifier;
gchar *dump;
saml2Assertion
= LASSO_SAML2_ASSERTION (lasso_login_get_assertion (lassoLogin));
if (saml2Assertion)
{
/* deduce user login */
[login release];
login = nil;
statementList = saml2Assertion->AttributeStatement;
while (!login && statementList)
{
statement = LASSO_SAML2_ATTRIBUTE_STATEMENT (statementList->data);
attributeList = statement->Attribute;
while (!login && attributeList)
{
attribute = LASSO_SAML2_ATTRIBUTE (attributeList->data);
if (strcmp (attribute->Name, "uid") == 0)
{
value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data);
textNode = value->any->data;
login = [NSString stringWithUTF8String: textNode->content];
[login retain];
}
else
attributeList = attributeList->next;
}
statementList = statementList->next;
}
/* serialize assertion */
[assertion release];
dump = lasso_node_export_to_xml (LASSO_NODE (saml2Assertion));
if (dump)
{
assertion = [NSString stringWithUTF8String: dump];
[assertion retain];
g_free (dump);
}
else
assertion = nil;
}
nameIdentifier
= LASSO_SAML2_NAME_ID (LASSO_PROFILE (lassoLogin)->nameIdentifier);
if (nameIdentifier)
{
/* deduce session id */
[identifier release];
identifier = [NSString stringWithUTF8String: nameIdentifier->content];
[identifier retain];
}
}
- (id) _initWithDump: (NSDictionary *) saml2Dump
inContext: (WOContext *) context
{
// lasso_error_t rc;
LassoServer *server;
LassoProfile *profile;
const gchar *dump;
if ((self = [self init]))
{
server = LassoServerInContext (context);
lassoLogin = lasso_login_new (server);
if (saml2Dump)
{
profile = LASSO_PROFILE (lassoLogin);
ASSIGN (login, [saml2Dump objectForKey: @"login"]);
ASSIGN (identifier, [saml2Dump objectForKey: @"identifier"]);
ASSIGN (assertion, [saml2Dump objectForKey: @"assertion"]);
dump = [[saml2Dump objectForKey: @"identity"] UTF8String];
if (dump)
lasso_profile_set_identity_from_dump (profile, dump);
dump = [[saml2Dump objectForKey: @"session"] UTF8String];
if (dump)
lasso_profile_set_session_from_dump (profile, dump);
lasso_login_accept_sso (lassoLogin);
// if (rc)
// [NSException raiseSAML2Exception: rc];
[self _updateDataFromLogin];
}
}
return self;
}
- (void) dealloc
{
if (lassoLogin)
g_object_unref (lassoLogin);
[login release];
[identifier release];
[assertion release];
[super dealloc];
}
+ (SOGoSAML2Session *) _SAML2SessionWithDump: (NSDictionary *) saml2Dump
inContext: (WOContext *) context
{
SOGoSAML2Session *newSession;
newSession = [[self alloc] _initWithDump: saml2Dump inContext: context];
[newSession autorelease];
return newSession;
}
+ (SOGoSAML2Session *) SAML2SessionInContext: (WOContext *) context
{
return [self _SAML2SessionWithDump: nil inContext: context];
}
+ (SOGoSAML2Session *) SAML2SessionWithIdentifier: (NSString *) identifier
inContext: (WOContext *) context
{
SOGoSAML2Session *session = nil;
NSDictionary *saml2Dump;
if (identifier)
{
saml2Dump = [[SOGoCache sharedCache]
saml2LoginDumpsForIdentifier: identifier];
if (saml2Dump)
session = [self _SAML2SessionWithDump: saml2Dump
inContext: context];
}
return session;
}
- (NSString *) login
{
return login;
}
- (NSString *) identifier
{
return identifier;
}
- (NSString *) assertion
{
return assertion;
}
- (void) processAuthnResponse: (NSString *) authnResponse
{
lasso_error_t rc;
gchar *responseData, *dump;
LassoProfile *profile;
LassoIdentity *identity;
LassoSession *session;
NSString *nsDump;
NSMutableDictionary *saml2Dump;
responseData = strdup ([authnResponse UTF8String]);
rc = lasso_login_process_authn_response_msg (lassoLogin, responseData);
if (rc)
[NSException raiseSAML2Exception: rc];
rc = lasso_login_accept_sso (lassoLogin);
if (rc)
[NSException raiseSAML2Exception: rc];
[self _updateDataFromLogin];
saml2Dump = [NSMutableDictionary dictionary];
[saml2Dump setObject: login forKey: @"login"];
[saml2Dump setObject: identifier forKey: @"identifier"];
[saml2Dump setObject: assertion forKey: @"assertion"];
profile = LASSO_PROFILE (lassoLogin);
session = lasso_profile_get_session (profile);
if (session)
{
dump = lasso_session_dump (session);
nsDump = [NSString stringWithUTF8String: dump];
[saml2Dump setObject: nsDump forKey: @"session"];
lasso_session_destroy (session);
}
identity = lasso_profile_get_identity (profile);
if (identity)
{
dump = lasso_identity_dump (identity);
nsDump = [NSString stringWithUTF8String: dump];
[saml2Dump setObject: nsDump forKey: @"identity"];
lasso_identity_destroy (identity);
}
[[SOGoCache sharedCache] setSaml2LoginDumps: saml2Dump
forIdentifier: identifier];
free (responseData);
}
@end

View file

@ -43,6 +43,7 @@
login: (NSString **) theLogin login: (NSString **) theLogin
domain: (NSString **) theDomain domain: (NSString **) theDomain
password: (NSString **) thePassword; password: (NSString **) thePassword;
@end @end
#endif #endif

View file

@ -26,6 +26,8 @@
#include "SOGoCache.h" #include "SOGoCache.h"
#import <NGObjWeb/WOCookie.h>
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h> #import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h> #import <Foundation/NSData.h>

View file

@ -51,7 +51,7 @@ static NSMutableDictionary *fieldTypes = nil;
static NSDictionary *sieveFields = nil; static NSDictionary *sieveFields = nil;
static NSDictionary *sieveFlags = nil; static NSDictionary *sieveFlags = nil;
static NSDictionary *operatorRequirements = nil; static NSDictionary *operatorRequirements = nil;
static NSDictionary *methodRequirements = nil; static NSMutableDictionary *methodRequirements = nil;
static NSString *sieveScriptName = @"sogo"; static NSString *sieveScriptName = @"sogo";
@ -188,7 +188,7 @@ static NSString *sieveScriptName = @"sogo";
if (!methodRequirements) if (!methodRequirements)
{ {
methodRequirements methodRequirements
= [NSDictionary dictionaryWithObjectsAndKeys: = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"imapflags", @"addflag", @"imapflags", @"addflag",
@"imapflags", @"removeflag", @"imapflags", @"removeflag",
@"imapflags", @"flag", @"imapflags", @"flag",
@ -615,8 +615,8 @@ static NSString *sieveScriptName = @"sogo";
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
SOGoDomainDefaults *dd; SOGoDomainDefaults *dd;
NGSieveClient *client; NGSieveClient *client;
NSString *filterScript, *v, *sieveServer; NSString *filterScript, *v, *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
NSURL *url; NSURL *url, *cUrl;
int sievePort; int sievePort;
BOOL b, connected; BOOL b, connected;
@ -631,6 +631,115 @@ static NSString *sieveScriptName = @"sogo";
connected = YES; connected = YES;
b = NO; b = NO;
// We connect to our Sieve server and check capabilities, in order
// to generate the right script, based on capabilities
//
// sieveServer might have the following format:
//
// sieve://localhost
// sieve://localhost:2000
// sieve://localhost:2000/?tls=YES
//
// Values such as "localhost" or "localhost:2000" are NOT supported.
//
// We first try to get the user's preferred Sieve server
sieveServer = [[[user mailAccounts] objectAtIndex: 0] objectForKey: @"sieveServerName"];
imapServer = [[[user mailAccounts] objectAtIndex: 0] objectForKey: @"serverName"];
cUrl = [NSURL URLWithString: (sieveServer ? sieveServer : @"")];
if ([dd sieveServer] && [[dd sieveServer] length] > 0)
url = [NSURL URLWithString: [dd sieveServer]];
else
url = [NSURL URLWithString: @"localhost"];
if ([cUrl host])
sieveServer = [cUrl host];
if (!sieveServer && [url host])
sieveServer = [url host];
if (!sieveServer && [dd sieveServer])
sieveServer = [dd sieveServer];
if (!sieveServer && imapServer)
sieveServer = [[NSURL URLWithString: imapServer] host];
if (!sieveServer)
sieveServer = @"localhost";
sieveScheme = [cUrl scheme] ? [cUrl scheme] : [url scheme];
if (!sieveScheme)
sieveScheme = @"sieve";
if ([cUrl port])
sievePort = [[cUrl port] intValue];
else
if ([url port])
sievePort = [[url port] intValue];
else
sievePort = 2000;
sieveQuery = [cUrl query] ? [cUrl query] : [url query];
if (sieveQuery)
sieveQuery = [NSString stringWithFormat: @"/?%@", sieveQuery];
else
sieveQuery = @"";
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@://%@:%d%@",
sieveScheme, sieveServer, sievePort, sieveQuery]];
client = [[NGSieveClient alloc] initWithURL: url];
if (!client) {
NSLog(@"Sieve connection failed on %@", [url description]);
return NO;
}
if (!thePassword) {
[client closeConnection];
return NO;
}
NS_DURING
{
result = [client login: theLogin authname: theAuthName password: thePassword];
}
NS_HANDLER
{
connected = NO;
}
NS_ENDHANDLER
if (!connected)
{
NSLog(@"Sieve connection failed on %@", [url description]);
return NO;
}
if (![[result valueForKey:@"result"] boolValue]) {
NSLog(@"failure. Attempting with a renewed password (no authname supported)");
thePassword = [theAccount imap4PasswordRenewed: YES];
result = [client login: theLogin password: thePassword];
}
if (![[result valueForKey:@"result"] boolValue]) {
NSLog(@"Could not login '%@' on Sieve server: %@: %@",
theLogin, client, result);
[client closeConnection];
return NO;
}
// We adjust the "methodRequirements" based on the server's
// capabilities. Cyrus exposes "imapflags" while Dovecot (and
// potentially others) expose "imap4flags" as specified in RFC5332
if ([client hasCapability: @"imap4flags"])
{
[methodRequirements setObject: @"imap4flags" forKey: @"addflag"];
[methodRequirements setObject: @"imap4flags" forKey: @"removeflag"];
[methodRequirements setObject: @"imap4flags" forKey: @"flag"];
}
//
// Now let's generate the script
//
script = [NSMutableString string]; script = [NSMutableString string];
// We first handle filters // We first handle filters
@ -646,6 +755,7 @@ static NSString *sieveScriptName = @"sogo";
else else
{ {
NSLog(@"Sieve generation failure: %@", [self lastScriptError]); NSLog(@"Sieve generation failure: %@", [self lastScriptError]);
[client closeConnection];
return NO; return NO;
} }
@ -726,87 +836,6 @@ static NSString *sieveScriptName = @"sogo";
[script insertString: header atIndex: 0]; [script insertString: header atIndex: 0];
} }
// We connect to our Sieve server and upload the script.
//
// sieveServer might have the following format:
//
// sieve://localhost
// sieve://localhost:2000
// sieve://localhost:2000/?tls=YES
//
// Values such as "localhost" or "localhost:2000" are NOT supported.
//
sieveServer = [dd sieveServer];
sievePort = 2000;
url = nil;
if (!sieveServer)
{
NSString *s;
s = [dd imapServer];
if (s)
{
NSURL *url;
url = [NSURL URLWithString: s];
if ([url host])
sieveServer = [url host];
else
sieveServer = s;
}
else
sieveServer = @"localhost";
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@:%d", sieveServer, sievePort]];
}
else
{
url = [NSURL URLWithString: sieveServer];
}
client = [[NGSieveClient alloc] initWithURL: url];
if (!client) {
NSLog(@"Sieve connection failed on %@", [url description]);
return NO;
}
if (!thePassword) {
[client closeConnection];
return NO;
}
NS_DURING
{
result = [client login: theLogin authname: theAuthName password: thePassword];
}
NS_HANDLER
{
connected = NO;
}
NS_ENDHANDLER
if (!connected)
{
NSLog(@"Sieve connection failed on %@", [url description]);
return NO;
}
if (![[result valueForKey:@"result"] boolValue]) {
NSLog(@"failure. Attempting with a renewed password (no authname supported)");
thePassword = [theAccount imap4PasswordRenewed: YES];
result = [client login: theLogin password: thePassword];
}
if (![[result valueForKey:@"result"] boolValue]) {
NSLog(@"Could not login '%@' on Sieve server: %@: %@",
theLogin, client, result);
[client closeConnection];
return NO;
}
/* We ensure to deactive the current active script since it could prevent /* We ensure to deactive the current active script since it could prevent
its deletion from the server. */ its deletion from the server. */

View file

@ -76,6 +76,13 @@
- (NSString *) CASServiceURL; - (NSString *) CASServiceURL;
- (BOOL) CASLogoutEnabled; - (BOOL) CASLogoutEnabled;
- (NSString *) SAML2PrivateKeyLocation;
- (NSString *) SAML2CertificateLocation;
- (NSString *) SAML2IdpMetadataLocation;
- (NSString *) SAML2IdpPublicKeyLocation;
- (NSString *) SAML2IdpCertificateLocation;
- (BOOL) SAML2LogoutEnabled;
- (BOOL) enablePublicAccess; - (BOOL) enablePublicAccess;
@end @end

View file

@ -379,7 +379,15 @@ _injectConfigurationFromFile (NSUserDefaults *ud,
- (NSArray *) supportedLanguages - (NSArray *) supportedLanguages
{ {
return [self stringArrayForKey: @"SOGoSupportedLanguages"]; static NSArray *supportedLanguages = nil;
if (!supportedLanguages)
{
supportedLanguages = [self stringArrayForKey: @"SOGoSupportedLanguages"];
[supportedLanguages retain];
}
return supportedLanguages;
} }
- (BOOL) userCanChangePassword - (BOOL) userCanChangePassword
@ -417,6 +425,37 @@ _injectConfigurationFromFile (NSUserDefaults *ud,
return [self boolForKey: @"SOGoCASLogoutEnabled"]; return [self boolForKey: @"SOGoCASLogoutEnabled"];
} }
/* SAML2 support */
- (NSString *) SAML2PrivateKeyLocation
{
return [self stringForKey: @"SOGoSAML2PrivateKeyLocation"];
}
- (NSString *) SAML2CertificateLocation;
{
return [self stringForKey: @"SOGoSAML2CertificateLocation"];
}
- (NSString *) SAML2IdpMetadataLocation
{
return [self stringForKey: @"SOGoSAML2IdpMetadataLocation"];
}
- (NSString *) SAML2IdpPublicKeyLocation
{
return [self stringForKey: @"SOGoSAML2IdpPublicKeyLocation"];
}
- (NSString *) SAML2IdpCertificateLocation
{
return [self stringForKey: @"SOGoSAML2IdpCertificateLocation"];
}
- (BOOL) SAML2LogoutEnabled
{
return [self boolForKey: @"SOGoSAML2LogoutEnabled"];
}
- (BOOL) enablePublicAccess - (BOOL) enablePublicAccess
{ {
return [self boolForKey: @"SOGoEnablePublicAccess"]; return [self boolForKey: @"SOGoEnablePublicAccess"];

View file

@ -576,13 +576,13 @@
- (void) _appendSystemMailAccount - (void) _appendSystemMailAccount
{ {
NSString *fullName, *replyTo, *imapLogin, *imapServer, *signature, NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
*encryption, *scheme, *action, *query, *customEmail; *encryption, *scheme, *action, *query, *customEmail, *sieveServer;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts; NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts;
NSNumber *port; NSNumber *port;
NSMutableArray *identities; NSMutableArray *identities;
NSArray *mails; NSArray *mails;
NSURL *url; NSURL *url, *cUrl;
unsigned int count, max; unsigned int count, max;
NSInteger defaultPort; NSInteger defaultPort;
@ -606,16 +606,22 @@
// imaps://localhost:143/?tls=YES // imaps://localhost:143/?tls=YES
// imaps://localhost/?tls=YES // imaps://localhost/?tls=YES
imapServer = [self _fetchFieldForUser: @"c_imaphostname"]; cImapServer = [self _fetchFieldForUser: @"c_imaphostname"];
if (!imapServer)
imapServer = [[self domainDefaults] imapServer]; imapServer = [[self domainDefaults] imapServer];
cUrl = [NSURL URLWithString: (cImapServer ? cImapServer : @"")];
url = [NSURL URLWithString: imapServer]; url = [NSURL URLWithString: imapServer];
if([cUrl host])
imapServer = [cUrl host];
else
if(cImapServer)
imapServer = cImapServer;
else
if([url host]) if([url host])
imapServer = [url host]; imapServer = [url host];
[mailAccount setObject: imapServer forKey: @"serverName"]; [mailAccount setObject: imapServer forKey: @"serverName"];
// 3. port & encryption // 3. port & encryption
scheme = [url scheme]; scheme = [cUrl scheme] ? [cUrl scheme] : [url scheme];
if (scheme if (scheme
&& [scheme caseInsensitiveCompare: @"imaps"] == NSOrderedSame) && [scheme caseInsensitiveCompare: @"imaps"] == NSOrderedSame)
{ {
@ -624,19 +630,28 @@
} }
else else
{ {
query = [url query]; query = [cUrl query] ? [cUrl query] : [url query];
if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame) if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame)
encryption = @"tls"; encryption = @"tls";
else else
encryption = @"none"; encryption = @"none";
defaultPort = 143; defaultPort = 143;
} }
port = [url port]; port = [cUrl port] ? [cUrl port] : [url port];
if ([port intValue] == 0) /* port is nil or intValue == 0 */ if ([port intValue] == 0) /* port is nil or intValue == 0 */
port = [NSNumber numberWithInt: defaultPort]; port = [NSNumber numberWithInt: defaultPort];
[mailAccount setObject: port forKey: @"port"]; [mailAccount setObject: port forKey: @"port"];
[mailAccount setObject: encryption forKey: @"encryption"]; [mailAccount setObject: encryption forKey: @"encryption"];
// Sieve server
sieveServer = [self _fetchFieldForUser: @"c_sievehostname"];
if (sieveServer)
{
[mailAccount setObject: sieveServer forKey: @"sieveServerName"];
}
/* identities */ /* identities */
identities = [NSMutableArray new]; identities = [NSMutableArray new];
mails = [self allEmails]; mails = [self allEmails];

View file

@ -35,6 +35,9 @@ extern NSString *SOGoWeekStartFirst4DayWeek;
extern NSString *SOGoWeekStartFirstFullWeek; extern NSString *SOGoWeekStartFirstFullWeek;
@interface SOGoUserDefaults : SOGoDefaultsSource @interface SOGoUserDefaults : SOGoDefaultsSource
{
NSString *userLanguage;
}
+ (SOGoUserDefaults *) defaultsForUser: (NSString *) userId + (SOGoUserDefaults *) defaultsForUser: (NSString *) userId
inDomain: (NSString *) domainId; inDomain: (NSString *) domainId;
@ -115,6 +118,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setMailComposeMessageType: (NSString *) newValue; - (void) setMailComposeMessageType: (NSString *) newValue;
- (NSString *) mailComposeMessageType; - (NSString *) mailComposeMessageType;
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
- (NSString *) mailDisplayRemoteInlineImages;
- (void) setMailMessageForwarding: (NSString *) newValue; - (void) setMailMessageForwarding: (NSString *) newValue;
- (NSString *) mailMessageForwarding; - (NSString *) mailMessageForwarding;

View file

@ -22,6 +22,7 @@
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h> #import <Foundation/NSTimeZone.h>
@ -91,6 +92,22 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return ud; return ud;
} }
- (id) init
{
if ((self = [super init]))
{
userLanguage = nil;
}
return self;
}
- (void) dealloc
{
[userLanguage release];
[super dealloc];
}
- (BOOL) _migrateLastModule - (BOOL) _migrateLastModule
{ {
BOOL rc; BOOL rc;
@ -355,21 +372,25 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
- (NSString *) language - (NSString *) language
{ {
NSString *language;
NSArray *supportedLanguages; NSArray *supportedLanguages;
if (!userLanguage)
{
/* see SOGoDomainDefaults for the meaning of this */ /* see SOGoDomainDefaults for the meaning of this */
language = [source objectForKey: @"SOGoLanguage"]; userLanguage = [source objectForKey: @"SOGoLanguage"];
if (!(language && [language isKindOfClass: [NSString class]])) if (!(userLanguage && [userLanguage isKindOfClass: [NSString class]]))
language = [(SOGoDomainDefaults *) parentSource language]; userLanguage = [(SOGoDomainDefaults *) parentSource language];
/* make sure the language is part of the supported languages */
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults] supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
supportedLanguages]; supportedLanguages];
if (![supportedLanguages containsObject: language])
language = [parentSource stringForKey: @"SOGoLanguage"];
return language; /* make sure the language is part of the supported languages */
if (![supportedLanguages containsObject: userLanguage])
userLanguage = [parentSource stringForKey: @"SOGoLanguage"];
[userLanguage retain];
}
return userLanguage;
} }
- (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue - (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue
@ -472,6 +493,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self stringForKey: @"SOGoMailComposeMessageType"]; return [self stringForKey: @"SOGoMailComposeMessageType"];
} }
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
{
[self setObject: newValue forKey: @"SOGoMailDisplayRemoteInlineImages"];
}
- (NSString *) mailDisplayRemoteInlineImages;
{
return [self stringForKey: @"SOGoMailDisplayRemoteInlineImages"];
}
- (void) setMailMessageForwarding: (NSString *) newValue - (void) setMailMessageForwarding: (NSString *) newValue
{ {
[self setObject: newValue forKey: @"SOGoMailMessageForwarding"]; [self setObject: newValue forKey: @"SOGoMailMessageForwarding"];

View file

@ -595,7 +595,7 @@ static Class NSNullK;
withUIDorEmail: (NSString *) uid withUIDorEmail: (NSString *) uid
inDomain: (NSString *) domain inDomain: (NSString *) domain
{ {
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin; NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname;
NSObject <SOGoSource> *currentSource; NSObject <SOGoSource> *currentSource;
NSEnumerator *sogoSources; NSEnumerator *sogoSources;
NSDictionary *userEntry; NSDictionary *userEntry;
@ -610,6 +610,7 @@ static Class NSNullK;
c_domain = nil; c_domain = nil;
c_imaphostname = nil; c_imaphostname = nil;
c_imaplogin = nil; c_imaplogin = nil;
c_sievehostname = nil;
[currentUser setObject: [NSNumber numberWithBool: YES] [currentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"CalendarAccess"]; forKey: @"CalendarAccess"];
@ -640,6 +641,8 @@ static Class NSNullK;
c_imaphostname = [userEntry objectForKey: @"c_imaphostname"]; c_imaphostname = [userEntry objectForKey: @"c_imaphostname"];
if (!c_imaplogin) if (!c_imaplogin)
c_imaplogin = [userEntry objectForKey: @"c_imaplogin"]; c_imaplogin = [userEntry objectForKey: @"c_imaplogin"];
if (!c_sievehostname)
c_sievehostname = [userEntry objectForKey: @"c_sievehostname"];
access = [[userEntry objectForKey: @"CalendarAccess"] boolValue]; access = [[userEntry objectForKey: @"CalendarAccess"] boolValue];
if (!access) if (!access)
[currentUser setObject: [NSNumber numberWithBool: NO] [currentUser setObject: [NSNumber numberWithBool: NO]
@ -675,6 +678,8 @@ static Class NSNullK;
[currentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; [currentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
if (c_imaplogin) if (c_imaplogin)
[currentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; [currentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
if (c_sievehostname)
[currentUser setObject: c_sievehostname forKey: @"c_sievehostname"];
[currentUser setObject: emails forKey: @"emails"]; [currentUser setObject: emails forKey: @"emails"];
[currentUser setObject: cn forKey: @"cn"]; [currentUser setObject: cn forKey: @"cn"];

View file

@ -32,6 +32,9 @@
@class NSString; @class NSString;
@class WOContext;
@class WOCookie;
@class SOGoUser; @class SOGoUser;
@interface SOGoWebAuthenticator : SoCookieAuthenticator <SOGoAuthenticator> @interface SOGoWebAuthenticator : SoCookieAuthenticator <SOGoAuthenticator>
@ -45,6 +48,10 @@
expire: (int *) _expire expire: (int *) _expire
grace: (int *) _grace; grace: (int *) _grace;
- (WOCookie *) cookieWithUsername: (NSString *) username
andPassword: (NSString *) password
inContext: (WOContext *) context;
@end @end
#endif /* _SOGOWEBAUTHENTICATOR_H_ */ #endif /* _SOGOWEBAUTHENTICATOR_H_ */

View file

@ -32,9 +32,12 @@
#import <NGObjWeb/WOCookie.h> #import <NGObjWeb/WOCookie.h>
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h> #import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NSCalendarDate+misc.h> #import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSData+gzip.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSString+Ext.h>
#import <NGLdap/NGLdapConnection.h> #import <NGLdap/NGLdapConnection.h>
#import <MainUI/SOGoRootPage.h> #import <MainUI/SOGoRootPage.h>
@ -47,7 +50,9 @@
#import "SOGoSystemDefaults.h" #import "SOGoSystemDefaults.h"
#import "SOGoUser.h" #import "SOGoUser.h"
#import "SOGoUserManager.h" #import "SOGoUserManager.h"
#if defined(SAML2_CONFIG)
#import "SOGoSAML2Session.h"
#endif
#import "SOGoWebAuthenticator.h" #import "SOGoWebAuthenticator.h"
@implementation SOGoWebAuthenticator @implementation SOGoWebAuthenticator
@ -107,11 +112,13 @@
{ {
SOGoCASSession *session; SOGoCASSession *session;
SOGoSystemDefaults *sd; SOGoSystemDefaults *sd;
NSString *authenticationType;
BOOL rc; BOOL rc;
sd = [SOGoSystemDefaults sharedSystemDefaults]; sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([[sd authenticationType] isEqualToString: @"cas"]) authenticationType = [sd authenticationType];
if ([authenticationType isEqualToString: @"cas"])
{ {
session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO]; session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
if (session) if (session)
@ -119,6 +126,18 @@
else else
rc = NO; rc = NO;
} }
#if defined(SAML2_CONFIG)
else if ([authenticationType isEqualToString: @"saml2"])
{
SOGoSAML2Session *saml2Session;
WOContext *context;
context = [[WOApplication application] context];
saml2Session = [SOGoSAML2Session SAML2SessionWithIdentifier: _pwd
inContext: context];
rc = [[saml2Session login] isEqualToString: _login];
}
#endif /* SAML2_CONFIG */
else else
rc = [[SOGoUserManager sharedUserManager] checkLogin: _login rc = [[SOGoUserManager sharedUserManager] checkLogin: _login
password: _pwd password: _pwd
@ -225,16 +244,18 @@
forURL: (NSURL *) server forURL: (NSURL *) server
forceRenew: (BOOL) renew forceRenew: (BOOL) renew
{ {
NSString *password, *service, *scheme; NSString *authType, *password;
SOGoCASSession *session;
SOGoSystemDefaults *sd;
password = [self passwordInContext: context]; password = [self passwordInContext: context];
if ([password length]) if ([password length])
{ {
sd = [SOGoSystemDefaults sharedSystemDefaults]; authType = [[SOGoSystemDefaults sharedSystemDefaults]
if ([[sd authenticationType] isEqualToString: @"cas"]) authenticationType];
if ([authType isEqualToString: @"cas"])
{ {
SOGoCASSession *session;
NSString *service, *scheme;
session = [SOGoCASSession CASSessionWithIdentifier: password session = [SOGoCASSession CASSessionWithIdentifier: password
fromProxy: NO]; fromProxy: NO];
@ -252,6 +273,23 @@
if ([password length] || renew) if ([password length] || renew)
[session updateCache]; [session updateCache];
} }
#if defined(SAML2_CONFIG)
else if ([authType isEqualToString: @"saml2"])
{
SOGoSAML2Session *session;
WOContext *context;
NSData *assertion;
context = [[WOApplication application] context];
session = [SOGoSAML2Session SAML2SessionWithIdentifier: password
inContext: context];
assertion = [[session assertion]
dataUsingEncoding: NSUTF8StringEncoding];
password = [[[assertion compress] stringByEncodingBase64]
stringByReplacingString: @"\n"
withString: @""];
}
#endif
} }
return password; return password;
@ -314,4 +352,44 @@
[response addCookie: authCookie]; [response addCookie: authCookie];
} }
- (WOCookie *) cookieWithUsername: (NSString *) username
andPassword: (NSString *) password
inContext: (WOContext *) context
{
WOCookie *authCookie;
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: @"%@:%@",
userKey, sessionKey];
cookieValue = [NSString stringWithFormat: @"basic %@",
[cookieString stringByEncodingBase64]];
authCookie = [WOCookie cookieWithName: [self cookieNameInContext: context]
value: cookieValue];
appName = [[context request] applicationName];
[authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
return authCookie;
}
@end /* SOGoWebAuthenticator */ @end /* SOGoWebAuthenticator */

View file

@ -43,6 +43,7 @@
NSArray *_mailFields; NSArray *_mailFields;
NSString *_imapLoginField; NSString *_imapLoginField;
NSString *_imapHostField; NSString *_imapHostField;
NSString *_sieveHostField;
NSString *_userPasswordAlgorithm; NSString *_userPasswordAlgorithm;
NSURL *_viewURL; NSURL *_viewURL;
BOOL _prependPasswordScheme; BOOL _prependPasswordScheme;

View file

@ -98,6 +98,7 @@
_kindField = nil; _kindField = nil;
_multipleBookingsField = nil; _multipleBookingsField = nil;
_imapHostField = nil; _imapHostField = nil;
_sieveHostField = nil;
} }
return self; return self;
@ -115,6 +116,7 @@
[_multipleBookingsField release]; [_multipleBookingsField release];
[_domainField release]; [_domainField release];
[_imapHostField release]; [_imapHostField release];
[_sieveHostField release];
[super dealloc]; [super dealloc];
} }
@ -131,6 +133,7 @@
ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]); ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]);
ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]); ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]);
ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]); ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]);
ASSIGN(_sieveHostField, [udSource objectForKey: @"SieveHostFieldName"]);
ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]); ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]);
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]); ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]); ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
@ -187,7 +190,10 @@
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm]; pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
if (pass == nil) if (pass == nil)
{
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm]; [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
return nil;
}
if (_prependPasswordScheme) if (_prependPasswordScheme)
result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass]; result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
@ -319,6 +325,8 @@
{ {
// Encrypt new password // Encrypt new password
NSString *encryptedPassword = [self _encryptPassword: newPassword]; NSString *encryptedPassword = [self _encryptPassword: newPassword];
if(encryptedPassword == nil)
return NO;
// Save new password // Save new password
login = [login stringByReplacingString: @"'" withString: @"''"]; login = [login stringByReplacingString: @"'" withString: @"''"];
@ -519,6 +527,13 @@
[response setObject: value forKey: @"c_imaphostname"]; [response setObject: value forKey: @"c_imaphostname"];
} }
if (_sieveHostField)
{
value = [response objectForKey: _sieveHostField];
if ([value isNotNull])
[response setObject: value forKey: @"c_sievehostname"];
}
// We check if the user can authenticate // We check if the user can authenticate
if (_authenticationFilter) if (_authenticationFilter)
{ {

View file

@ -0,0 +1,158 @@
#!/usr/bin/python
include_dirs = []
output = "-"
import os
import sys
m_template = """/* %(module)s.m (auto-generated) */
#include <objc/objc.h>
#include <stdint.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import "%(module)s.h"
%(exception_init)s
static NSMutableDictionary *exceptionTable = nil;
@implementation NSException (SOGoSAML2Extension)
static void
InitExceptionTable ()
{
exceptionTable = [NSMutableDictionary new];
%(exception_table_init)s
}
+ (void) raiseSAML2Exception: (lasso_error_t) lassoError
{
NSString *exceptionName, *reason;
if (!exceptionTable)
InitExceptionTable ();
exceptionName = [exceptionTable objectForKey: [NSNumber numberWithInt: lassoError]];
if (!exceptionName)
exceptionName = NSGenericException;
reason = [NSString stringWithUTF8String: lasso_strerror (lassoError)];
if (!reason)
reason = @"unspecified";
[self raise: exceptionName format: @"%%@", reason];
}
@end
"""
h_template = """/* %(module)s.h (auto-generated) */
#ifndef %(h_exclusion)s
#define %(h_exclusion)s 1
#include <lasso/errors.h>
#import <Foundation/NSException.h>
@class NSString;
%(exception_decls)s
@interface NSException (SOGoSAML2Extension)
+ (void) raiseSAML2Exception: (lasso_error_t) lassoError;
@end
#endif /* %(h_exclusion)s */
"""
def ParseErrorsHLine(line):
result = None
if line.startswith("#define LASSO_"):
next_space = line.find(" ", 19)
result = line[8:next_space]
return result
def ParseIncludeDirs(args):
dirs = ["/usr/include", "/usr/local/include"]
ignore = True
for arg in args:
if ignore:
if arg == "-I":
ignore = False
elif arg.startswith("-I"):
dirs.append(arg[2:])
else:
dirs.append(arg)
ignore = True
return dirs
def FindHFile(args, filename):
found = None
include_dirs = ParseIncludeDirs(args)
for dirname in include_dirs:
full_filename = "%s/%s" % (dirname, filename)
if os.path.exists(full_filename):
found = full_filename
if found is None:
raise Exception, "'%s' not found in include dirs" % filename
return found
def ErrorCodeToName(name):
parts = name.split("_")
cap_parts = [part.capitalize() for part in parts]
return "".join(cap_parts)
if __name__ == "__main__":
errors_filename = FindHFile(sys.argv, os.path.join("lasso", "errors.h"))
inf = open(errors_filename)
error_codes = {}
line = inf.readline()
while line != "":
error_code = ParseErrorsHLine(line)
if error_code:
error_codes[error_code] = ErrorCodeToName(error_code)
line = inf.readline()
inf.close()
exception_decls = []
exception_init = []
exception_table_init = []
exc_table_format \
= (" [exceptionTable setObject: %s\n"
" forKey: [NSNumber numberWithInt: %s]];")
for error_code in error_codes:
name = error_codes[error_code];
exception_init.append("NSString * const %s = @\"%s\";" % (name, name))
exception_decls.append("extern NSString * const %s;" % name)
exception_table_init.append(exc_table_format % (name, error_code))
module = "SOGoSAML2Exceptions"
outvars = {"module": module,
"h_exclusion": "%s_H" % module.upper(),
"exception_decls": "\n".join(exception_decls),
"exception_init": "\n".join(exception_init),
"exception_table_init": "\n".join(exception_table_init)}
outf = open("%s.m" % module, "w+")
outf.write(m_template % outvars)
outf.close()
outf = open("%s.h" % module, "w+")
outf.write(h_template % outvars)
outf.close()

View file

@ -13,7 +13,7 @@ import sogoLogin
SOGoSupportedLanguages = [ "Catalan", "Czech", "Dutch", "Danish", "Welsh", "English", SOGoSupportedLanguages = [ "Catalan", "Czech", "Dutch", "Danish", "Welsh", "English",
"SpanishSpain", "SpanishArgentina", "French", "German", "SpanishSpain", "SpanishArgentina", "French", "German",
"Icelandic", "Italian", "Hungarian", "BrazilianPortuguese", "Icelandic", "Italian", "Hungarian", "BrazilianPortuguese",
"NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak",
"Ukrainian", "Swedish" ]; "Ukrainian", "Swedish" ];
daysBetweenResponseList=[1,2,3,5,7,14,21,30] daysBetweenResponseList=[1,2,3,5,7,14,21,30]

View file

@ -208,8 +208,8 @@ class DAVAclTest(unittest.TestCase):
return versitStruct return versitStruct
event_template = """BEGIN:VCALENDAR event_template = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Inverse//Event Generator//EN PRODID:-//Inverse//Event Generator//EN
VERSION:2.0
BEGIN:VEVENT BEGIN:VEVENT
SEQUENCE:0 SEQUENCE:0
TRANSP:OPAQUE TRANSP:OPAQUE

View file

@ -38,10 +38,9 @@ ADDITIONAL_LIB_DIRS += \
-Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj -Wl,-rpath,../../OGoContentStore/obj -Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj -Wl,-rpath,../../OGoContentStore/obj
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed ADDITIONAL_LDFLAGS += -Wl,--no-as-needed
check ::
./obj/sogo-tests
-include GNUmakefile.preamble -include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/test-tool.make include $(GNUSTEP_MAKEFILES)/test-tool.make
-include GNUmakefile.postamble -include GNUmakefile.postamble
check ::
./obj/sogo-tests

View file

@ -35,6 +35,7 @@
#import <GDLContentStore/GCSChannelManager.h> #import <GDLContentStore/GCSChannelManager.h>
#import <GDLContentStore/GCSFolder.h> #import <GDLContentStore/GCSFolder.h>
#import <GDLContentStore/GCSFolderManager.h> #import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/GCSSpecialQueries.h>
#import <GDLContentStore/NSURL+GCS.h> #import <GDLContentStore/NSURL+GCS.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
@ -113,6 +114,7 @@
BOOL rc = NO; BOOL rc = NO;
GCSFolderManager *fm; GCSFolderManager *fm;
GCSChannelManager *cm; GCSChannelManager *cm;
GCSSpecialQueries *specialQueries;
NSURL *folderLocation; NSURL *folderLocation;
EOAdaptorContext *ac; EOAdaptorContext *ac;
EOAdaptorChannel *fc; EOAdaptorChannel *fc;
@ -125,6 +127,7 @@
folderLocation = [fm folderInfoLocation]; folderLocation = [fm folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation]; fc = [cm acquireOpenChannelForURL: folderLocation];
ac = [fc adaptorContext]; ac = [fc adaptorContext];
specialQueries = [fc specialQueries];
sqlFromUserID = [fromUserID asSafeSQLString]; sqlFromUserID = [fromUserID asSafeSQLString];
sqlToUserID = [toUserID asSafeSQLString]; sqlToUserID = [toUserID asSafeSQLString];
@ -137,15 +140,16 @@
if (!sqlError) if (!sqlError)
{ {
sql sql
= [NSString stringWithFormat: @"UPDATE %@" = [specialQueries updateCPathInFolderInfo: [folderLocation gcsTableName]
@" SET c_path = '/'||c_path1||'/'||c_path2||'/'||c_path3||'/'||c_path4" withCPath2: sqlToUserID];
@" WHERE c_path2 = '%@'",
[folderLocation gcsTableName], sqlToUserID];
sqlError = [fc evaluateExpressionX: sql]; sqlError = [fc evaluateExpressionX: sql];
} }
if (sqlError) if (sqlError)
{
[ac rollbackTransaction]; [ac rollbackTransaction];
NSLog([sqlError reason]);
}
else else
rc = [ac commitTransaction]; rc = [ac commitTransaction];
@ -195,7 +199,10 @@
[profileLocation gcsTableName], sqlToUserID, sqlFromUserID]; [profileLocation gcsTableName], sqlToUserID, sqlFromUserID];
sqlError = [fc evaluateExpressionX: sql]; sqlError = [fc evaluateExpressionX: sql];
if (sqlError) if (sqlError)
{
[ac rollbackTransaction]; [ac rollbackTransaction];
NSLog([sqlError reason]);
}
else else
rc = [ac commitTransaction]; rc = [ac commitTransaction];

View file

@ -21,6 +21,7 @@
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSError.h> #import <Foundation/NSError.h>
#import <Foundation/NSFileManager.h> #import <Foundation/NSFileManager.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
@ -346,6 +347,7 @@ typedef enum SOGoToolRestoreMode {
- (BOOL) restoreRecords: (NSArray *) records - (BOOL) restoreRecords: (NSArray *) records
ofFolder: (GCSFolder *) gcsFolder ofFolder: (GCSFolder *) gcsFolder
{ {
NSAutoreleasePool *pool;
NSDictionary *existingRecords, *currentRecord; NSDictionary *existingRecords, *currentRecord;
NSString *cName, *cContent; NSString *cName, *cContent;
int count, max; int count, max;
@ -354,12 +356,18 @@ typedef enum SOGoToolRestoreMode {
if (records) if (records)
{ {
existingRecords = [self fetchExistingRecordsFromFolder: gcsFolder];
pool = [[NSAutoreleasePool alloc] init];
version = 0; version = 0;
rc = YES; rc = YES;
existingRecords = [self fetchExistingRecordsFromFolder: gcsFolder];
max = [records count]; max = [records count];
for (count = 0; count < max; count++) for (count = 0; count < max; count++)
{ {
if (count > 0 && count%100 == 0)
{
DESTROY(pool);
pool = [[NSAutoreleasePool alloc] init];
}
currentRecord = [records objectAtIndex: count]; currentRecord = [records objectAtIndex: count];
cName = [currentRecord objectForKey: @"c_name"]; cName = [currentRecord objectForKey: @"c_name"];
if (![existingRecords objectForKey: cName]) if (![existingRecords objectForKey: cName])
@ -370,6 +378,7 @@ typedef enum SOGoToolRestoreMode {
baseVersion: &version]; baseVersion: &version];
} }
} }
DESTROY(pool);
} }
else else
{ {

View file

@ -6,7 +6,7 @@ BUNDLE_NAME = AdministrationUI
AdministrationUI_PRINCIPAL_CLASS = AdministrationUIProduct AdministrationUI_PRINCIPAL_CLASS = AdministrationUIProduct
AdministrationUI_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Italian NorwegianBokmal NorwegianNynorsk Polish Russian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh AdministrationUI_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
AdministrationUI_OBJC_FILES = \ AdministrationUI_OBJC_FILES = \
AdministrationUIProduct.m \ AdministrationUIProduct.m \

View file

@ -0,0 +1,15 @@
/* this file is in UTF-8 format! */
"Help" = "Pomoc";
"Close" = "Zavrieť";
"Modules" = "Moduly";
/* Modules short names */
"ACLs" = "ACL";
/* Modules titles */
"ACLs_title" = "Spravovanie ACL zložiek uzívateľov";
/* Modules descriptions */
"ACLs_description" = "<p>Administrácia kontroly prístupových práv (ACL) dovoluje spravovať ACLs kalendárov a adresárov pre všetkých užívateľov.</p><p>Pre úpravu ACL zložky užívateľa, napíšte meno užívateľa v kolonke hľadaj hore v okne a dvojklikom potvrďte výber zložky.</p>";

View file

@ -6,7 +6,7 @@ BUNDLE_NAME = CommonUI
CommonUI_PRINCIPAL_CLASS = CommonUIProduct CommonUI_PRINCIPAL_CLASS = CommonUIProduct
CommonUI_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh CommonUI_LANGUAGES = BrazilianPortuguese Catalan Czech Danish Dutch English French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
CommonUI_OBJC_FILES += \ CommonUI_OBJC_FILES += \
CommonUIProduct.m \ CommonUIProduct.m \

View file

@ -25,7 +25,7 @@
"Owner:" = "Właściciel:"; "Owner:" = "Właściciel:";
"Publish the Free/Busy information" = "Opublikuj informację wolny/zajęty"; "Publish the Free/Busy information" = "Opublikuj informację wolny/zajęty";
"Add..." = "Dodaj..."; "Add..." = "Dodaj";
"Remove" = "Usuń"; "Remove" = "Usuń";
"Subscribe User" = "Subskrybuj użytkownika"; "Subscribe User" = "Subskrybuj użytkownika";
@ -52,14 +52,14 @@
/* generic.js */ /* generic.js */
"Unable to subscribe to that folder!" "Unable to subscribe to that folder!"
= "Nie można subskrybować tego foldera!"; = "Nie można subskrybować tego folderu!";
"You cannot subscribe to a folder that you own!" "You cannot subscribe to a folder that you own!"
= "Nie możesz subskrybować foldera, który jest twoją własnością!"; = "Nie możesz subskrybować folderu, który jest twoją własnością!";
"Unable to unsubscribe from that folder!" "Unable to unsubscribe from that folder!"
= "Nie można wyłączyć subskrypcji tego foldera!"; = "Nie można wyłączyć subskrypcji tego folderu!";
"You cannot unsubscribe from a folder that you own!" "You cannot unsubscribe from a folder that you own!"
= "Nie możesz wyłączyć subskrypcji foldera, który jest twoją własnością!"; = "Nie możesz wyłączyć subskrypcji folderu, który jest twoją własnością!";
"Unable to rename that folder!" = "Nie można zmienić nazwy tego foldera!"; "Unable to rename that folder!" = "Nie można zmienić nazwy tego folderu!";
"You have already subscribed to that folder!" "You have already subscribed to that folder!"
= "Już subskrybujesz ten folder!"; = "Już subskrybujesz ten folder!";
"The user rights cannot be edited for this object!" "The user rights cannot be edited for this object!"

View file

@ -0,0 +1,109 @@
/* this file is in UTF-8 format! */
/* toolbars */
"Save" = "Uložiť";
"Close" = "Zavrieť";
"Edit User Rights" = "Upraviť uživateľské práva";
"Home" = "Domov";
"Calendar" = "Kalendár";
"Address Book" = "Adresár";
"Mail" = "Pošta";
"Preferences" = "Predvoľby";
"Administration" = "Správa";
"Disconnect" = "Odpojiť";
"Right Administration" = "Administrácia práv";
"Log Console (dev.)" = "Log konzola (vývoj)";
"User" = "Užívateľ";
"Help" = "Pomoc";
"noJavascriptError" = "Sogo vyžaduje pre spustenie JavaScript. Uistite sa, že táto možnosť je k dispozícii a aktivovaná v prehliadači.";
"noJavascriptRetry" = "Opakovať";
"Owner:" = "Vlastník:";
"Publish the Free/Busy information" = "Publikovanie Free / Busy informácií";
"Add..." = "Pridať...";
"Remove" = "Odstrániť";
"Subscribe User" = "Prihlásiť užívateľa k odberu";
"Any Authenticated User" = "Všetkým prihláseným užívateľom";
"Public Access" = "Verejný prístup";
"Any user not listed above" = "Každý užívateľ, ktorý nie je uvedený vyššie";
"Anybody accessing this resource from the public area" = "Každý, kto prístupuje k tomuto zdroju z verejného priestoru";
"Sorry, the user rights can not be configured for that object." = "Je nám ľúto, užívateľské práva nie je možné konfigurovať pre daný objekt.";
"Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?"
= "Ktorýkoľvek užívateľ tohoto systému bude môcť vidieť Vašu emailovú shránku \"%{0}\". Ste si istý že veríte všetkým užívateľom?";
"Any user with an account on this system will be able to access your calendar \"%{0}\". Are you certain you trust them all?"
= "Ktorýkoľvek užívateľ tohoto systému bude môcť vidieť Váš kalendár \"%{0}\". Ste si istý že veríte všetkým užívateľom?";
"Potentially anyone on the Internet will be able to access your calendar \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?"
= "Potencionálne ktokoľvek na internete bude môcť vidieť Váš kalendár \"%{0}\", napriek tomu že nemá účet v tomto systéme. Sú tieto informácie vhodné pre verejnosť?";
"Any user with an account on this system will be able to access your address book \"%{0}\". Are you certain you trust them all?"
= "Ktorýkoľvek užívateľ tohoto systému bude môcť vidieť Váš adresár \"%{0}\". Ste si istý že veríte všetkým užívateľom?";
"Potentially anyone on the Internet will be able to access your address book \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?"
= "Potencionálne ktokoľvek na internete bude môcť vidieť Váš adresár \"%{0}\", napriek tomu že nemá účet v tomto systéme. Sú tieto informácie vhodné pre verejnosť?";
"Give Access" = "Daj prístup";
"Keep Private" = "Nechaj súkromné";
/* generic.js */
"Unable to subscribe to that folder!"
= "Nedá sa prihlásiť k odberu tejto zložky!";
"You cannot subscribe to a folder that you own!"
= "Nemôžete sa prihlásiť k odberu zložky, ktorú vlastnite!";
"Unable to unsubscribe from that folder!"
= "Nedá sa odhlásiť z tejto zložky!";
"You cannot unsubscribe from a folder that you own!"
= "Nemôžete sa odhlásiť zo zložky, ktorú vlastnite!";
"Unable to rename that folder!" = "Nemožno premenovať túto zložku!";
"You have already subscribed to that folder!"
= "Už ste prihlásení k odberu tejto zložky!";
"The user rights cannot be edited for this object!"
= "Užívateľské práva nemožno upraviť pre tento objekt!";
"A folder by that name already exists." = "Zložka s týmto názvom už existuje.";
"You cannot create a list in a shared address book."
= "Nemôžete vytvoriť zoznam v zdieľanom adresári.";
"Warning" = "Varovanie";
"You are not allowed to access this module or this system. Please contact your system administrator."
= "Nemate dovolený prístup k tomuto modulu alebo tento systém. Obráťte sa na správcu systému.";
"You don't have the required privileges to perform the operation."
= "Nemáte potrebné oprávnenia na vykonanie operácie.";
"noEmailForDelegation" = "Musíte zadať adresu, na ktorú chcete delegovať vaše pozvanie.";
"delegate is organizer" = "Delegát je organizátorom. Zadajte prosím iného delegáta.";
"delegate is a participant" = "Delegát je už účastníkom.";
"delegate is a group" = "Zadaná adresa zodpovedá skupine. Môžete ju delegovať len na jedinečnú osobu.";
"Snooze for " = "Odložiť";
"5 minutes" = "5 minút";
"10 minutes" = "10 minút";
"15 minutes" = "15 minút";
"30 minutes" = "30 minút";
"45 minutes" = "45 minút";
"1 hour" = "1 hodinu";
/* common buttons */
"OK" = "OK";
"Cancel" = "Zrušiť";
"Yes" = "Áno";
"No" = "Nie";
/* alarms */
"Reminder:" = "Pripomienka:";
"Start:" = "Štart:";
"Due Date:" = "Splatnosť:";
"Location:" = "Umiestnenie:";
"a2_Sunday" = "Ne";
"a2_Monday" = "Po";
"a2_Tuesday" = "Ut";
"a2_Wednesday" = "St";
"a2_Thursday" = "Št";
"a2_Friday" = "Pi";
"a2_Saturday" = "So";

View file

@ -485,13 +485,17 @@
BOOL canLogoff; BOOL canLogoff;
id auth; id auth;
SOGoSystemDefaults *sd; SOGoSystemDefaults *sd;
NSString *authType;
auth = [[self clientObject] authenticatorInContext: context]; auth = [[self clientObject] authenticatorInContext: context];
if ([auth respondsToSelector: @selector (cookieNameInContext:)]) if ([auth respondsToSelector: @selector (cookieNameInContext:)])
{ {
sd = [SOGoSystemDefaults sharedSystemDefaults]; sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([[sd authenticationType] isEqualToString: @"cas"]) authType = [sd authenticationType];
if ([authType isEqualToString: @"cas"])
canLogoff = [sd CASLogoutEnabled]; canLogoff = [sd CASLogoutEnabled];
else if ([authType isEqualToString: @"saml2"])
canLogoff = [sd SAML2LogoutEnabled];
else else
canLogoff = [[auth cookieNameInContext: context] length] > 0; canLogoff = [[auth cookieNameInContext: context] length] > 0;
} }

Some files were not shown because too many files have changed in this diff Show more