Merge to 2.0.3

maint-2.1.1
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
fromFolder: (MAPIStoreFolder *) sourceFolder - (int) _moveCopyMessageWithMID: (uint64_t) srcMid
withMID: (uint64_t) targetMid fromFolder: (MAPIStoreFolder *) sourceFolder
andChangeKey: (struct Binary_r *) targetChangeKey withMID: (uint64_t) targetMid
wantCopy: (uint8_t) wantCopy andChangeKey: (struct Binary_r *) targetChangeKey
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;
@ -765,7 +776,9 @@ 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

@ -50,7 +50,8 @@ MAPIStoreTallocWrapperDestroy (void *data)
GSRegisterCurrentThread (); GSRegisterCurrentThread ();
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 if (requirement == bothTableRequired && [fields containsObject: @"c_name"])
&& [fields containsObject: @"c_name"]) selectedFields = [self _dottedFields: fields];
[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;
max = [subValues count]; if ([subValues isKindOfClass: [NSArray class]])
for (count = 0; result && count < max; count++) {
result = ([[subValues objectAtIndex: count] length] == 0); max = [subValues count];
for (count = 0; result && count < max; count++)
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];
children = [[aGroup children] objectEnumerator];
// 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];
}
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)
dateTime]; {
startDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"]
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)
@ -23,7 +25,7 @@ sogoDefaultsFile = "/home/sogo/GNUstep/Defaults/.GNUstepDefaults"
# - Delete the socfs_ table for the username. # - Delete the socfs_ table for the username.
def usage(): def usage():
print """ print """
%s [-i imaphost] ] [-p imapport] [-s sambaprivate] username [password] %s [-i imaphost] ] [-p imapport] [-s sambaprivate] username [password]
-i imaphost IMAP host to connect to [%s] -i imaphost IMAP host to connect to [%s]
-p imappost IMAP port to use [%d] -p imappost IMAP port to use [%d]
@ -31,60 +33,59 @@ def usage():
""" % (os.path.basename(sys.argv[0]), imaphost, imapport, sambaprivate) """ % (os.path.basename(sys.argv[0]), imaphost, imapport, sambaprivate)
def main(): def main():
global sambaprivate global sambaprivate
global mapistorefolder global mapistorefolder
global imaphost global imaphost
global imapport global imapport
try: try:
opts, args = getopt.getopt(sys.argv[1:], "i:p:s:") opts, args = getopt.getopt(sys.argv[1:], "i:p:s:")
except getopt.GetoptError, err: except getopt.GetoptError, err:
print str(err) print str(err)
usage() usage()
sys.exit(2) sys.exit(2)
for o, a in opts: for o, a in opts:
if o == "-i": if o == "-i":
imaphost = a imaphost = a
elif o == "-p": elif o == "-p":
imapport = a imapport = a
elif o == "-s": elif o == "-s":
sambaprivate = a sambaprivate = a
mapistorefolder = "%s/mapistore" % (sambaprivate) mapistorefolder = "%s/mapistore" % (sambaprivate)
else: else:
assert False, "unhandled option" assert False, "unhandled option"
argslen = len(args) argslen = len(args)
if (argslen == 2): if (argslen == 2):
username = args[0] username = args[0]
userpass = args[1] userpass = args[1]
elif (argslen == 1): elif (argslen == 1):
username = args[0] username = args[0]
userpass = username userpass = username
print "Using username as password" print "Using username as password"
else: else:
usage() usage()
print "Specify a user (and optionally the password)" print "Specify a user (and optionally the password)"
sys.exit(2) sys.exit(2)
# cleanup starts here # cleanup starts here
try: try:
imapCleanup(imaphost, imapport, username, userpass) imapCleanup(imaphost, imapport, username, userpass)
except Exception as e: except Exception as e:
print "Error during imapCleanup, continuing: %s" % str(e) print "Error during imapCleanup, continuing: %s" % str(e)
try: try:
mapistoreCleanup(mapistorefolder, username) mapistoreCleanup(mapistorefolder, username)
except (shutil.Error, OSError) as e: except (shutil.Error, OSError) as e:
print "Error during mapistoreCleanup, continuing: %s" % str(e) print "Error during mapistoreCleanup, continuing: %s" % str(e)
# try: # try:
# pass # pass
# #ldbCleanup(sambaprivate, username) # #ldbCleanup(sambaprivate, username)
# except ldb.LdbError as e: # except ldb.LdbError as e:
# print "Error during ldbCleanup, continuing: %s" % str(e) # print "Error during ldbCleanup, continuing: %s" % str(e)
sqlCleanup(username)
sqlCleanup(username)
def extractmb(si): def extractmb(si):
inparen = False inparen = False
@ -133,8 +134,7 @@ def cleanupmb(mb, client):
else: else:
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,83 +142,113 @@ 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)
# NOTYET # NOTYET
#def ldbCleanup(sambaprivate, username): #def ldbCleanup(sambaprivate, username):
# conn = ldb.Ldb("%s/openchange.ldb" % (sambaprivate)) # conn = ldb.Ldb("%s/openchange.ldb" % (sambaprivate))
#### entries = conn.search(None, expression="(|(cn=%s)(MAPIStoreURI=sogo://%s:*)(MAPIStoreURI=sogo://%s@*))" % (username,username,username), #### entries = conn.search(None, expression="(|(cn=%s)(MAPIStoreURI=sogo://%s:*)(MAPIStoreURI=sogo://%s@*))" % (username,username,username),
# entries = conn.search(None, expression="cn=%s" % (username), # entries = conn.search(None, expression="cn=%s" % (username),
# scope=ldb.SCOPE_SUBTREE) # scope=ldb.SCOPE_SUBTREE)
# for entry in entries: # for entry in entries:
# print "Deleting %s" % (entry.dn) # print "Deleting %s" % (entry.dn)
# conn.delete(entry.dn) # conn.delete(entry.dn)
def mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username): def mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
import MySQLdb import MySQLdb
conn= MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname) conn= MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
c=conn.cursor() c=conn.cursor()
tablename="socfs_%s" % (username) tablename="socfs_%s" % (username)
c.execute("TRUNCATE TABLE %s" % tablename) c.execute("TRUNCATE TABLE %s" % tablename)
print "Table %s emptied" print "Table %s emptied"
def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username): def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
import pg import pg
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname) conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
tablename = "socfs_%s" % username tablename = "socfs_%s" % username
conn.query("DELETE FROM %s" % tablename) conn.query("DELETE FROM %s" % tablename)
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 = ""
return 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
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
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL) m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
proto = m.group(1) proto = m.group(1)
dbuser = m.group(2) dbuser = m.group(2)
dbpass = m.group(3) dbpass = m.group(3)
dbhost = m.group(4) dbhost = m.group(4)
dbport = m.group(5) dbport = m.group(5)
dbname = m.group(6) dbname = m.group(6)
# 7 is folderinfo table # 7 is folderinfo table
if (proto == "postgresql"): encodedUserName = asCSSIdentifier(username)
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username)
elif (proto == "mysql"):
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username)
else:
raise Exception("Unknown sql proto: " % (proto))
if (proto == "postgresql"):
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
elif (proto == "mysql"):
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
else:
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,10 +1147,9 @@ 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];
organizerUID = [[event organizer] uid]; organizerUID = [[event organizer] uid];
@ -1372,22 +1357,22 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
reason: @"delegate is a group"]; reason: @"delegate is a group"];
} }
if (ex == nil) if (ex == nil)
ex = [self _handleAttendee: attendee {
withDelegate: delegate // Remove the RSVP attribute, as an action from the attendee
ownerUser: ownerUser // was actually performed, and this confuses iCal (bug #1850)
statusChange: _status [[attendee attributes] removeObjectForKey: @"RSVP"];
inEvent: event]; ex = [self _handleAttendee: attendee
withDelegate: delegate
ownerUser: ownerUser
statusChange: _status
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

@ -25,13 +25,13 @@ vtodo_class2 = "(Tarea confidencial)";
"comment_label" = "Comentario:"; "comment_label" = "Comentario:";
/* Invitation */ /* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invitación al evento: \"% {Summary}\""; "Event Invitation: \"%{Summary}\"" = "Invitación al 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}le ha invitado a %{Summary}.\n\nInicio: %{StartDate}\nFin: %{EndDate}\nDescripción: %{Description}"; "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}le ha invitado a %{Summary}.\n\nInicio: %{StartDate}\nFin: %{EndDate}\nDescripción: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}le ha invitado a %{Summary}.\n\nInicio: %{StartDate} a las %{StartTime}\nFin: %{EndDate} a las %{EndTime}\nDescripción: %{Description}"; "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}le ha invitado a %{Summary}.\n\nInicio: %{StartDate} a las %{StartTime}\nFin: %{EndDate} a las %{EndTime}\nDescripción: %{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}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}ha cancelado este evento: %{Summary}.\n\nInicio: %{StartDate}\nFin: %{EndDate}\nDescripción: %{Description}"; = "%{Organizer} %{SentByText}ha cancelado este evento: %{Summary}.\n\nInicio: %{StartDate}\nFin: %{EndDate}\nDescripción: %{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}"

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,18 +216,19 @@ 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"]
additional: nil prefixes: nil suffixes: nil]; additional: nil prefixes: nil suffixes: nil];
[self setNickname: [ldifRecord objectForKey: @"mozillanickname"]]; [self setNickname: [ldifRecord objectForKey: @"mozillanickname"]];
[self setFn: [ldifRecord objectForKey: @"displayname"]]; [self setFn: [ldifRecord objectForKey: @"displayname"]];
[self setTitle: [ldifRecord objectForKey: @"title"]]; [self setTitle: [ldifRecord objectForKey: @"title"]];
element = [self _elementWithTag: @"adr" ofType: @"home"]; element = [self _elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"] [element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"]
@ -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.
@ -269,7 +269,7 @@ static NSString *userAgent = nil;
{ {
id headerValue; id headerValue;
unsigned int count; unsigned int count;
NSString *messageID, *priority, *pureSender,*replyTo; NSString *messageID, *priority, *pureSender, *replyTo;
for (count = 0; count < 8; count++) for (count = 0; count < 8; count++)
{ {
@ -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];
@ -831,11 +831,14 @@ static NSString *userAgent = nil;
} }
else else
{ {
// TODO: use subject for filename? // TODO: use subject for filename?
// 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,10 +107,11 @@ 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;
_userPasswordAlgorithm = nil; _userPasswordAlgorithm = nil;
listRequiresDot = YES; listRequiresDot = YES;
searchAttributes = nil; searchAttributes = 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,24 +638,34 @@ 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]; {
else encryptedPass = newPassword;
[attr addStringValue: [self _encryptPassword: newPassword]]; }
else
mod = [NGLdapModification replaceModification: attr]; {
changes = [NSArray arrayWithObject: mod]; encryptedPass = [self _encryptPassword: newPassword];
*perr = PolicyNoError; }
if(encryptedPass != nil)
{
[attr addStringValue: encryptedPass];
mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod];
*perr = PolicyNoError;
if ([bindConnection bindWithMethod: @"simple" if ([bindConnection bindWithMethod: @"simple"
binddn: userDN binddn: userDN
credentials: oldPassword]) credentials: oldPassword])
didChange = [bindConnection modifyEntryWithDN: userDN {
changes: changes]; didChange = [bindConnection modifyEntryWithDN: userDN
else changes: changes];
didChange = NO; }
else
didChange = NO;
}
} }
else else
didChange = [bindConnection changePasswordAtDn: userDN didChange = [bindConnection changePasswordAtDn: userDN
@ -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];
} }
/** /**
@ -147,7 +125,7 @@
if (encoding == encHex) if (encoding == encHex)
{ {
decodedData = [NSData decodeDataFromHexString: pass]; decodedData = [NSData decodeDataFromHexString: pass];
if(decodedData == nil) if(decodedData == nil)
{ {
decodedData = [NSData data]; decodedData = [NSData data];
@ -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;
@ -886,6 +886,8 @@ static NSArray *childRecordFields = nil;
folderSubscription folderSubscription
= [moduleSettings objectForKey: @"SubscribedFolders"]; = [moduleSettings objectForKey: @"SubscribedFolders"];
subscriptionPointer = [self folderReference]; subscriptionPointer = [self folderReference];
folderShowAlarms = [moduleSettings objectForKey: @"FolderShowAlarms"];
if (reallyDo) if (reallyDo)
{ {
@ -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,16 +188,16 @@ 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",
@"vacation", @"vacation", @"vacation", @"vacation",
@"notify", @"notify", @"notify", @"notify",
@"fileinto", @"fileinto", @"fileinto", @"fileinto",
@"reject", @"reject", @"reject", @"reject",
@"regex", @"regex", @"regex", @"regex",
nil]; nil];
[methodRequirements retain]; [methodRequirements retain];
} }
} }
@ -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 ([url host]) if([cUrl host])
imapServer = [url host]; imapServer = [cUrl host];
else
if(cImapServer)
imapServer = cImapServer;
else
if([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;
/* see SOGoDomainDefaults for the meaning of this */
language = [source objectForKey: @"SOGoLanguage"];
if (!(language && [language isKindOfClass: [NSString class]]))
language = [(SOGoDomainDefaults *) parentSource language];
/* make sure the language is part of the supported languages */
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
supportedLanguages];
if (![supportedLanguages containsObject: language])
language = [parentSource stringForKey: @"SOGoLanguage"];
return language; if (!userLanguage)
{
/* see SOGoDomainDefaults for the meaning of this */
userLanguage = [source objectForKey: @"SOGoLanguage"];
if (!(userLanguage && [userLanguage isKindOfClass: [NSString class]]))
userLanguage = [(SOGoDomainDefaults *) parentSource language];
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
supportedLanguages];
/* 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];
@ -308,18 +314,20 @@
NSString *sqlstr; NSString *sqlstr;
BOOL didChange; BOOL didChange;
BOOL isOldPwdOk; BOOL isOldPwdOk;
isOldPwdOk = NO; isOldPwdOk = NO;
didChange = NO; didChange = NO;
// Verify current password // Verify current password
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0]; isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0];
if (isOldPwdOk) if (isOldPwdOk)
{ {
// 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: @"''"];
cm = [GCSChannelManager defaultChannelManager]; cm = [GCSChannelManager defaultChannelManager];
@ -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";
@ -39,7 +39,7 @@
"Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?" "Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?"
= "Dowolny użytkownik systemu będzie miał dostęp do Twojej skrzynki \"%{0}\". Naprawdę ufasz im wszystkim?"; = "Dowolny użytkownik systemu będzie miał dostęp do Twojej skrzynki \"%{0}\". Naprawdę ufasz im wszystkim?";
"Any user with an account on this system will be able to access your calendar \"%{0}\". Are you certain you trust them all?" "Any user with an account on this system will be able to access your calendar \"%{0}\". Are you certain you trust them all?"
= "Dowolny użytkownik systemu będzie miał dostęp do Twojego kalendarza \"%{0}\". Naprawdę ufasz im wszystkim?"; = "Dowolny użytkownik systemu będzie miał dostęp do Twojego kalendarza \"%{0}\". Naprawdę ufasz im wszystkim?";
"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?" "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?"
= "Twój kalendarz \"%{0}\" będzie publiczne dostępny dla każdego w Internecie. Czy te informacje napewno mają być tak upublicznione?"; = "Twój kalendarz \"%{0}\" będzie publiczne dostępny dla każdego w Internecie. Czy te informacje napewno mają być tak upublicznione?";
@ -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!"
@ -79,7 +79,7 @@
"delegate is a participant" = "Delegat jest już uczestnikiem."; "delegate is a participant" = "Delegat jest już uczestnikiem.";
"delegate is a group" = "Wskazany adres jest grupą. Możesz oddelegować tylko pojedynczną osobę."; "delegate is a group" = "Wskazany adres jest grupą. Możesz oddelegować tylko pojedynczną osobę.";
"Snooze for " = "Drzemka przez"; "Snooze for " = "Drzemka przez ";
"5 minutes" = "5 minut"; "5 minutes" = "5 minut";
"10 minutes" = "10 minut"; "10 minutes" = "10 minut";
"15 minutes" = "15 minut"; "15 minutes" = "15 minut";
@ -106,4 +106,4 @@
"a2_Wednesday" = "Śr"; "a2_Wednesday" = "Śr";
"a2_Thursday" = "Cz"; "a2_Thursday" = "Cz";
"a2_Friday" = "Pi"; "a2_Friday" = "Pi";
"a2_Saturday" = "So"; "a2_Saturday" = "So";

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";

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