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.pt_BR = UI/MailerUI/BrazilianPortuguese.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.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.pt_BR = UI/PreferencesUI/BrazilianPortuguese.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.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.pt_BR = UI/Scheduler/BrazilianPortuguese.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.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.pt_BR = UI/Contacts/BrazilianPortuguese.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.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.pt_BR = UI/MainUI/BrazilianPortuguese.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.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.pt_BR = UI/Common/BrazilianPortuguese.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.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.pt_BR = UI/AdministrationUI/BrazilianPortuguese.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.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.pt_BR = SoObjects/Appointments/BrazilianPortuguese.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.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.pt_BR = SoObjects/Contacts/BrazilianPortuguese.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.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.pt_BR = UI/MailPartViewers/BrazilianPortuguese.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.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)
------------------

View File

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

View File

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

View File

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

View File

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

View File

@ -337,7 +337,18 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
- (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

View File

@ -123,7 +123,8 @@
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
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
withNewName: (NSString *) newFolderName

View File

@ -180,6 +180,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (void) dealloc
{
[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
// [messageKeys release];
// [faiMessageKeys release];
// [folderKeys release];
@ -656,19 +658,23 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
return rc;
}
- (int) moveCopyMessageWithMID: (uint64_t) srcMid
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
wantCopy: (uint8_t) wantCopy
// private method
- (int) _moveCopyMessageWithMID: (uint64_t) srcMid
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
int rc;
MAPIStoreMessage *sourceMsg, *destMsg;
TALLOC_CTX *memCtx;
//TALLOC_CTX *memCtx;
struct SRow aRow;
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
withMID: srcMid
forWriting: NO
@ -681,7 +687,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (rc != MAPISTORE_SUCCESS)
goto end;
[sourceMsg copyToMessage: destMsg];
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetChangeKey)
{
@ -698,7 +704,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0];
end:
talloc_free (memCtx);
//talloc_free (memCtx);
return rc;
}
@ -709,6 +715,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
int rc = MAPISTORE_SUCCESS;
NSUInteger count;
@ -717,6 +724,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
ownerUser = [[self userContext] sogoUser];
@ -738,11 +748,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
targetChangeKey = targetChangeKeys[count];
else
targetChangeKey = NULL;
rc = [self moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
wantCopy: wantCopy];
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
wantCopy: wantCopy
inMemCtx: memCtx];
}
else
rc = MAPISTORE_ERR_NOT_FOUND;
@ -765,7 +776,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
}
else
rc = MAPISTORE_ERR_DENIED;
//talloc_free (memCtx);
return rc;
}
@ -785,6 +798,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSUInteger count, max;
NSString *childKey;
uint64_t fmid;
TALLOC_CTX *memCtx;
memCtx = talloc_zero (NULL, TALLOC_CTX);
/* 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
@ -807,7 +823,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (rc == MAPISTORE_SUCCESS)
{
newFolder = [targetFolder lookupFolder: childKey];
[self copyPropertiesToObject: newFolder];
[self copyPropertiesToObject: newFolder inMemCtx: memCtx];
pool = [NSAutoreleasePool new];
children = [self messageKeys];
@ -818,7 +834,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
message = [self lookupMessage: childKey];
targetMessage = [newFolder createMessage: NO];
[targetMessage setIsNew: YES];
[message copyToMessage: targetMessage];
[message copyToMessage: targetMessage inMemCtx: memCtx];
if (isMove)
{
fmid = [mapping idFromURL: [message url]];
@ -839,7 +855,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
message = [self lookupFAIMessage: childKey];
targetMessage = [newFolder createMessage: YES];
[targetMessage setIsNew: YES];
[message copyToMessage: targetMessage];
[message copyToMessage: targetMessage inMemCtx: memCtx];
if (isMove)
{
fmid = [mapping idFromURL: [message url]];
@ -882,6 +898,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
else
rc = MAPISTORE_ERR_DENIED;
talloc_free (memCtx);
return rc;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -317,9 +317,9 @@ static Class NSExceptionK, MAPIStoreFolderK;
}
/* 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 SRow row;
enum MAPITAGS propTag;
@ -328,7 +328,7 @@ static Class NSExceptionK, MAPIStoreFolderK;
enum mapistore_error error;
void *data;
memCtx = talloc_zero (NULL, TALLOC_CTX);
//memCtx = talloc_zero (NULL, TALLOC_CTX);
[self getAvailableProperties: &availableProps inMemCtx: memCtx];
@ -369,8 +369,7 @@ static Class NSExceptionK, MAPIStoreFolderK;
}
[newObject addPropertiesFromRow: &row];
talloc_free (memCtx);
//talloc_free (memCtx);
}
/* subclasses */

View File

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

View File

@ -50,7 +50,8 @@ MAPIStoreTallocWrapperDestroy (void *data)
GSRegisterCurrentThread ();
pool = [NSAutoreleasePool new];
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];
[pool release];
GSUnregisterCurrentThread ();
@ -66,7 +67,7 @@ MAPIStoreTallocWrapperDestroy (void *data)
talloc_set_destructor ((void *) wrapper, MAPIStoreTallocWrapperDestroy);
wrapper->instance = self;
[self retain];
// NSLog (@"returning wrapper: %p; object: %p", wrapper, self);
NSLog (@"returning wrapper: %p; object: %p", wrapper, self);
return wrapper;
}

View File

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

View File

@ -110,6 +110,14 @@
return nil;
}
- (NSString *) updateCPathInFolderInfo: (NSString *) tableName
withCPath2: (NSString *) c_path2
{
[self subclassResponsibility: _cmd];
return nil;
}
@end
@ -205,6 +213,17 @@
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
//
@ -299,6 +318,17 @@
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
//
@ -392,4 +422,15 @@
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

View File

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

View File

@ -1,6 +1,6 @@
/* 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>
*

View File

@ -1,6 +1,6 @@
/* 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>
*
@ -424,16 +424,19 @@
}
static inline BOOL
_subValuesAreVoid (NSArray *subValues)
_subValuesAreVoid (id subValues)
{
BOOL result = YES;
NSUInteger count, max;
result = YES;
max = [subValues count];
for (count = 0; result && count < max; count++)
result = ([[subValues objectAtIndex: count] length] == 0);
if ([subValues isKindOfClass: [NSArray class]])
{
max = [subValues count];
for (count = 0; result && count < max; count++)
result = ([[subValues objectAtIndex: count] length] == 0);
}
return result;
}

View File

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

View File

@ -1,6 +1,6 @@
/* 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>
*
@ -454,4 +454,9 @@ static NGCardsSaxHandler *sax = nil;
return new;
}
- (NSArray *) orderOfElements
{
return nil;
}
@end

View File

@ -1,6 +1,6 @@
/* 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>
*
@ -107,6 +107,7 @@
CardElement *currentChild;
NSMutableString *rendering;
NSString *groupTag;
NSArray *order;
rendering = [NSMutableString string];
@ -120,7 +121,36 @@
groupTag = [groupTag uppercaseString];
[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]))
[rendering appendString: [self render: currentChild]];
[rendering appendFormat: @"END:%@\r\n", groupTag];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,9 +12,11 @@ import sys
imaphost = '127.0.0.1'
imapport = 143
sambaprivate = '/var/lib/samba/private'
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
# - 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.
def usage():
print """
print """
%s [-i imaphost] ] [-p imapport] [-s sambaprivate] username [password]
-i imaphost IMAP host to connect to [%s]
-p imappost IMAP port to use [%d]
@ -31,60 +33,59 @@ def usage():
""" % (os.path.basename(sys.argv[0]), imaphost, imapport, sambaprivate)
def main():
global sambaprivate
global mapistorefolder
global imaphost
global imapport
try:
opts, args = getopt.getopt(sys.argv[1:], "i:p:s:")
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
global sambaprivate
global mapistorefolder
global imaphost
global imapport
try:
opts, args = getopt.getopt(sys.argv[1:], "i:p:s:")
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
for o, a in opts:
if o == "-i":
imaphost = a
elif o == "-p":
imapport = a
elif o == "-s":
sambaprivate = a
mapistorefolder = "%s/mapistore" % (sambaprivate)
else:
assert False, "unhandled option"
for o, a in opts:
if o == "-i":
imaphost = a
elif o == "-p":
imapport = a
elif o == "-s":
sambaprivate = a
mapistorefolder = "%s/mapistore" % (sambaprivate)
else:
assert False, "unhandled option"
argslen = len(args)
if (argslen == 2):
username = args[0]
userpass = args[1]
elif (argslen == 1):
username = args[0]
userpass = username
print "Using username as password"
else:
usage()
print "Specify a user (and optionally the password)"
sys.exit(2)
argslen = len(args)
if (argslen == 2):
username = args[0]
userpass = args[1]
elif (argslen == 1):
username = args[0]
userpass = username
print "Using username as password"
else:
usage()
print "Specify a user (and optionally the password)"
sys.exit(2)
# cleanup starts here
try:
imapCleanup(imaphost, imapport, username, userpass)
except Exception as e:
print "Error during imapCleanup, continuing: %s" % str(e)
# cleanup starts here
try:
imapCleanup(imaphost, imapport, username, userpass)
except Exception as e:
print "Error during imapCleanup, continuing: %s" % str(e)
try:
mapistoreCleanup(mapistorefolder, username)
except (shutil.Error, OSError) as e:
print "Error during mapistoreCleanup, continuing: %s" % str(e)
try:
mapistoreCleanup(mapistorefolder, username)
except (shutil.Error, OSError) as e:
print "Error during mapistoreCleanup, continuing: %s" % str(e)
# try:
# pass
# #ldbCleanup(sambaprivate, username)
# except ldb.LdbError as e:
# print "Error during ldbCleanup, continuing: %s" % str(e)
sqlCleanup(username)
# try:
# pass
# #ldbCleanup(sambaprivate, username)
# except ldb.LdbError as e:
# print "Error during ldbCleanup, continuing: %s" % str(e)
sqlCleanup(username)
def extractmb(si):
inparen = False
@ -133,8 +134,7 @@ def cleanupmb(mb, client):
else:
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)
(code, data) = client.login(username, userpass)
if code != "OK":
@ -142,83 +142,113 @@ def imapCleanup(imaphost, imapport, username, userpass) :
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, "%")
if code == "OK":
for si in data:
if si is not None:
print si
continue
mb = extractmb(si)
cleanupmb(mb, client)
client.logout()
def mapistoreCleanup(mapistorefolder, username):
# delete the user's folder under the mapistore and under mapistore/SOGo
shutil.rmtree("%s/%s" % (mapistorefolder, username), ignore_errors=True)
shutil.rmtree("%s/SOGo/%s" % (mapistorefolder, username), ignore_errors=True)
# NOTYET
#def ldbCleanup(sambaprivate, username):
# 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" % (username),
# scope=ldb.SCOPE_SUBTREE)
# for entry in entries:
# print "Deleting %s" % (entry.dn)
# conn.delete(entry.dn)
# NOTYET
#def ldbCleanup(sambaprivate, username):
# 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" % (username),
# scope=ldb.SCOPE_SUBTREE)
# for entry in entries:
# print "Deleting %s" % (entry.dn)
# conn.delete(entry.dn)
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)
c=conn.cursor()
tablename="socfs_%s" % (username)
c.execute("TRUNCATE TABLE %s" % tablename)
print "Table %s emptied"
conn= MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
c=conn.cursor()
tablename="socfs_%s" % (username)
c.execute("TRUNCATE TABLE %s" % tablename)
print "Table %s emptied"
def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
import pg
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
tablename = "socfs_%s" % username
conn.query("DELETE FROM %s" % tablename)
print "table '%s' emptied" % tablename
import pg
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
tablename = "socfs_%s" % username
conn.query("DELETE FROM %s" % tablename)
print "table '%s' emptied" % tablename
def getOCSFolderInfoURL():
global sogoDefaultsFile
sogoDefaults = plistlib.readPlist(sogoDefaultsFile)
try:
OCSFolderInfoURL = sogoDefaults['sogod']['OCSFolderInfoURL']
except KeyError:
global sogoSysDefaultsFile, sogoUserDefaultsFile
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):
OCSFolderInfoURL = getOCSFolderInfoURL()
if (OCSFolderInfoURL is None):
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username))
OCSFolderInfoURL = getOCSFolderInfoURL()
if OCSFolderInfoURL is None:
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
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
proto = m.group(1)
dbuser = m.group(2)
dbpass = m.group(3)
dbhost = m.group(4)
dbport = m.group(5)
dbname = m.group(6)
# 7 is folderinfo table
proto = m.group(1)
dbuser = m.group(2)
dbpass = m.group(3)
dbhost = m.group(4)
dbport = m.group(5)
dbname = m.group(6)
# 7 is folderinfo table
if (proto == "postgresql"):
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username)
elif (proto == "mysql"):
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username)
else:
raise Exception("Unknown sql proto: " % (proto))
encodedUserName = asCSSIdentifier(username)
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__":
main()
main()

View File

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

View File

@ -8,23 +8,15 @@ vtodo_class1 = "(Tarefa Privada)";
vtodo_class2 = "(Tarefa Confidencial)";
/* Receipts */
"Title:" = "Título:";
"Start:" = "Início:";
"End:" = "Fim:";
"Receipt: users invited to a meeting" = "Recepção: usuários convidados para uma reunião";
"You have invited the following attendees(s):" = "Você convidou o(s) seguinte(s) participante(s):";
"... 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:";
"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
"The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado";
"The following attendees(s) were notified:" = "Estes participantes foram notificados:";
"The following attendees(s) were added:" = "Estes participantes foram adicionados:";
"The following attendees(s) were removed:" = "Estes participantes foram removidos:";
/* IMIP messages */
"calendar_label" = "Calendário:";
"startDate_label" = "Início:";
"endDate_label" = "Fim:";
"due_label" = "Data de Vencimento:";
@ -35,14 +27,19 @@ vtodo_class2 = "(Tarefa Confidencial)";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\"";
"(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}";
/* Deletion */
"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} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* 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"
= "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou";
"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.";
/* 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}foi aceitado seu convite ao evento.";
"%{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.";
/* 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
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

View File

@ -28,14 +28,14 @@ vtodo_class2 = "(Zadanie poufne)";
"Event Invitation: \"%{Summary}\"" = "Zaproszenie na wydarzenie: \"%{Summary}\"";
"(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} 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 */
"Event Cancelled: \"%{Summary}\"" = "Wydarzenie anulowane: \"%{Summary}\"";
"%{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}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 */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"

View File

@ -2885,11 +2885,26 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
timezone: (iCalTimeZone *) timezone
{
SOGoAppointmentObject *object;
NSString *uid;
NSMutableString *content;
NSString *uid;
uid = [self globallyUniqueObjectId];
[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
inContainer: self];
[object setIsNew: YES];
@ -3024,7 +3039,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
acquire: NO];
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 addToEvents: event];
if (timezone)

View File

@ -1,8 +1,6 @@
/* SOGoAppointmentFolderICS.m - this file is part of SOGo
*
* Copyright (C) 2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2012 Inverse inc.
*
* 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
@ -22,6 +20,7 @@
#import <Foundation/NSString.h>
#import <NGCards/CardElement.h>
#import <NGCards/iCalCalendar.h>
#import "SOGoAppointmentFolderICS.h"
@ -30,7 +29,19 @@
- (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

View File

@ -410,14 +410,6 @@
previousObject: oldEvent
toAttendees: updateAttendees
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 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
// the attendee, we add the user to the SENT-BY attribute.
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
// participation state has changed.
@ -1063,6 +1059,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|| [currentStatus caseInsensitiveCompare: newStatus]
!= NSOrderedSame)
{
NSMutableArray *delegates;
NSString *delegatedUID;
delegatedUID = nil;
[attendee setPartStat: newStatus];
// If one has accepted / declined an invitation on behalf of
@ -1087,9 +1087,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[attendee setDelegatedTo: [delegate email]];
NSString *delegatedUID = nil;
NSMutableArray *delegates;
if (removeDelegate)
{
delegates = [NSMutableArray array];
@ -1123,13 +1120,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
previousObject: nil
toAttendees: delegates
withType: @"calendar:cancellation"];
#if 0
// DELETE CODE
[self sendReceiptEmailUsingTemplateNamed: @"Deletion"
forObject: event
to: delegates];
#endif
}
} // if (removeDelegate)
if (addDelegate)
{
@ -1148,12 +1139,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
previousObject: nil
toAttendees: delegates
withType: @"calendar:invitation"];
#if 0
// DELETE CODE
[self sendReceiptEmailUsingTemplateNamed: @"Invitation"
forObject: event to: delegates];
#endif
}
} // if (addDelegate)
// If the current user isn't the organizer of the event
// that has just been updated, we update the event and
@ -1161,10 +1147,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
ownerUser = [SOGoUser userWithLogin: owner];
if (!(ex || [event userIsOrganizer: ownerUser]))
{
if ([[attendee rsvp] isEqualToString: @"true"]
&& [event isStillRelevant])
if ([event isStillRelevant])
[self sendResponseToOrganizer: event
from: ownerUser];
from: ownerUser];
organizerUID = [[event organizer] uid];
@ -1372,22 +1357,22 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
reason: @"delegate is a group"];
}
if (ex == nil)
ex = [self _handleAttendee: attendee
withDelegate: delegate
ownerUser: ownerUser
statusChange: _status
inEvent: event];
{
// Remove the RSVP attribute, as an action from the attendee
// was actually performed, and this confuses iCal (bug #1850)
[[attendee attributes] removeObjectForKey: @"RSVP"];
ex = [self _handleAttendee: attendee
withDelegate: delegate
ownerUser: ownerUser
statusChange: _status
inEvent: event];
}
if (ex == nil)
{
// We generate the updated iCalendar file and we save it in
// the database. We do this ONLY when using SOGo from the
// Web interface. Over DAV, it'll be handled directly in
// 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])
ex = [self saveContentString: [calendar versitString]];
}
@ -1944,6 +1929,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
return [NSException exceptionWithHTTPStatus:403
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;
delegateEmail = [attendee delegatedTo];

View File

@ -725,7 +725,8 @@
header = [NSString stringWithFormat: @"text/calendar; 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: @"quoted-printable"
forKey: @"content-transfer-encoding"];

View File

@ -32,6 +32,7 @@
- (NSArray *) davCalendarUserAddressSet;
- (NSArray *) davCalendarHomeSet;
- (NSArray *) davCalendarScheduleInboxURL;
- (NSArray *) davCalendarScheduleOutboxURL;
@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:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invitación al evento: \"% {Summary}\"";
"Event Invitation: \"%{Summary}\"" = "Invitación al evento: \"%{Summary}\"";
"(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} 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 */
"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}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}"

View File

@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
Contacts_RESOURCE_FILES += \
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

View File

@ -216,18 +216,19 @@ convention:
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord
{
CardElement *element;
NSArray *units;
NSInteger year, yearOfToday, month, day;
CardElement *element;
NSCalendarDate *now;
NSArray *units;
NSString *ou;
id o;
[self setNWithFamily: [ldifRecord objectForKey: @"sn"]
given: [ldifRecord objectForKey: @"givenname"]
additional: nil prefixes: nil suffixes: nil];
[self setNickname: [ldifRecord objectForKey: @"mozillanickname"]];
[self setFn: [ldifRecord objectForKey: @"displayname"]];
[self setTitle: [ldifRecord objectForKey: @"title"]];
[self setTitle: [ldifRecord objectForKey: @"title"]];
element = [self _elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"]
@ -303,7 +304,15 @@ convention:
forKey: @""];
[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];
}

View File

@ -1,6 +1,6 @@
/* 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>
*

View File

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

View File

@ -1,6 +1,6 @@
/* 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>
* 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
This file is part of SOGo.
@ -269,7 +269,7 @@ static NSString *userAgent = nil;
{
id headerValue;
unsigned int count;
NSString *messageID, *priority, *pureSender,*replyTo;
NSString *messageID, *priority, *pureSender, *replyTo;
for (count = 0; count < 8; count++)
{
@ -314,8 +314,8 @@ static NSString *userAgent = nil;
if ([replyTo length] > 0)
{
[headers setObject: replyTo forKey: @"reply-to"];
[headers removeObjectForKey: @"replyTo"];
}
[headers removeObjectForKey: @"replyTo"];
if ([[newHeaders objectForKey: @"receipt"] isEqualToString: @"true"])
{
@ -804,8 +804,8 @@ static NSString *userAgent = nil;
- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
{
NSDictionary *info, *attachment;
NSString *signature, *nl;
SOGoUserDefaults *ud;
NSString *signature;
[sourceMail fetchCoreInfos];
@ -831,11 +831,14 @@ static NSString *userAgent = nil;
}
else
{
// TODO: use subject for filename?
// error = [newDraft saveAttachment:content withName:@"forward.eml"];
// TODO: use subject for filename?
// error = [newDraft saveAttachment:content withName:@"forward.eml"];
signature = [[self mailAccountFolder] signature];
if ([signature length])
[self setText: [NSString stringWithFormat: @"\n-- \n%@", signature]];
{
nl = (isHTML ? @"<br/>" : @"\n");
[self setText: [NSString stringWithFormat: @"%@-- %@%@", nl, nl, signature]];
}
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[sourceMail filenameForForward], @"filename",
@"message/rfc822", @"mimetype",

View File

@ -1,6 +1,6 @@
/* 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>
*
@ -226,11 +226,15 @@
- (NSString *) signature
{
NSString *signature, *mailSignature;
NSString *signature, *mailSignature, *nl;
signature = [[sourceMail mailAccountFolder] signature];
if ([signature length])
mailSignature = [NSString stringWithFormat: @"-- \n%@", signature];
{
nl = (htmlComposition ? @"<br/>" : @"\n");
mailSignature = [NSString stringWithFormat: @"-- %@%@", nl, signature];
}
else
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 \
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)
LIBRARY_NAME += \

View File

@ -24,7 +24,8 @@ SOGo_LIBRARIES_DEPEND_UPON += \
-lNGStreams -lNGExtensions -lEOControl \
-lDOM -lSaxObjC \
-lNGLdap -lSBJson \
-lGDLContentStore -lgnustep-base -lobjc -ldl
-lGDLContentStore \
$(BASE_LIBS)
ifeq ($(HAS_LIBRARY_gnutls),yes)
ADDITIONAL_CPPFLAGS += -DHAVE_GNUTLS=1
@ -36,6 +37,11 @@ SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
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)
SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
else

View File

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

View File

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

View File

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

View File

@ -71,8 +71,7 @@
{
NSString *scheme;
NSString *pass;
NSArray *schemeComps;
keyEncoding encoding;
NSArray *encodingAndScheme;
NSRange range;
int selflen, len;
@ -88,32 +87,11 @@
if (len == 0)
scheme = defaultScheme;
encoding = [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;
}
}
encodingAndScheme = [NSString getDefaultEncodingForScheme: scheme];
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)
{
decodedData = [NSData decodeDataFromHexString: pass];
if(decodedData == nil)
{
decodedData = [NSData data];
@ -208,8 +186,10 @@
*
* @param passwordScheme The scheme to use
* @param theSalt The binary data of the salt
* @param userEncoding The encoding (plain, hex, base64) to be used
* @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
* @param userEncoding The encoding (plain, hex, base64) to be used. If set to
* 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
withSalt: (NSData *) theSalt
@ -217,6 +197,22 @@
{
keyEncoding dataEncoding;
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
cryptedData = [self dataUsingEncoding: NSUTF8StringEncoding];
cryptedData = [cryptedData asCryptedPassUsingScheme: passwordScheme withSalt: theSalt];
@ -224,12 +220,6 @@
if (cryptedData == nil)
return nil;
// use default encoding scheme, when set to default
if (userEncoding == encDefault)
dataEncoding = [NSString getDefaultEncodingForScheme: passwordScheme];
else
dataEncoding = userEncoding;
if (dataEncoding == encHex)
{
// hex encoding
@ -250,19 +240,49 @@
/**
* 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
* @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
if ([passwordScheme caseInsensitiveCompare: @"md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"plain-md5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"sha"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"cram-md5"] == NSOrderedSame)
{
return encHex;
encoding = encHex;
}
else if ([passwordScheme caseInsensitiveCompare: @"smd5"] == NSOrderedSame ||
[passwordScheme caseInsensitiveCompare: @"ldap-md5"] == NSOrderedSame ||
@ -272,9 +292,9 @@
[passwordScheme caseInsensitiveCompare: @"sha512"] == 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++;
length = [self length];
// In [UIxMailPartTextViewer flatContentAsString], we first escape HTML entities and then
// 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;
length = [self length] - start;
workRange = NSMakeRange (start, length);
length -= start;
workRange = [self rangeOfCharacterFromSet: urlAfterEndingChars
options: NSLiteralSearch range: workRange];
options: NSLiteralSearch range: NSMakeRange (start, length)];
if (workRange.location != NSNotFound)
length = workRange.location - start;
while

View File

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

View File

@ -520,6 +520,27 @@ static memcached_st *handle = NULL;
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
//

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* SOGoGCSFolder.m - this file is part of SOGo
*
* 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>
*
@ -835,7 +835,7 @@ static NSArray *childRecordFields = nil;
- (BOOL) subscribeUserOrGroup: (NSString *) theIdentifier
reallyDo: (BOOL) reallyDo
{
NSMutableDictionary *moduleSettings;
NSMutableDictionary *moduleSettings, *folderShowAlarms;
NSMutableArray *folderSubscription;
NSString *subscriptionPointer;
NSMutableArray *allUsers;
@ -886,6 +886,8 @@ static NSArray *childRecordFields = nil;
folderSubscription
= [moduleSettings objectForKey: @"SubscribedFolders"];
subscriptionPointer = [self folderReference];
folderShowAlarms = [moduleSettings objectForKey: @"FolderShowAlarms"];
if (reallyDo)
{
@ -897,14 +899,26 @@ static NSArray *childRecordFields = nil;
forKey: @"SubscribedFolders"];
}
if (!(folderShowAlarms
&& [folderShowAlarms isKindOfClass: [NSMutableDictionary class]]))
{
folderShowAlarms = [NSMutableDictionary dictionary];
[moduleSettings setObject: folderShowAlarms
forKey: @"FolderShowAlarms"];
}
[folderSubscription addObjectUniquely: subscriptionPointer];
// By default, we disable alarms on subscribed calendars
[folderShowAlarms setObject: [NSNumber numberWithBool: NO]
forKey: subscriptionPointer];
}
else
{
[self removeFolderSettings: moduleSettings
withReference: subscriptionPointer];
[folderSubscription removeObject: subscriptionPointer];
[folderShowAlarms removeObjectForKey: subscriptionPointer];
}
[us synchronize];

View File

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

View File

@ -1236,7 +1236,7 @@
- (SOGoWebDAVValue *) davCurrentUserPrincipal
{
NSDictionary *userHREF;
NSString *login;
NSString *login, *s;
SOGoUser *activeUser;
SOGoWebDAVValue *davCurrentUserPrincipal;
@ -1246,7 +1246,8 @@
davCurrentUserPrincipal = nil;
else
{
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, [self davURLAsString]);
s = [NSString stringWithFormat: @"/SOGo/dav/%@", login];
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, s);
davCurrentUserPrincipal
= [davElementWithContent (@"current-user-principal",
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
domain: (NSString **) theDomain
password: (NSString **) thePassword;
@end
#endif

View File

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

View File

@ -51,7 +51,7 @@ static NSMutableDictionary *fieldTypes = nil;
static NSDictionary *sieveFields = nil;
static NSDictionary *sieveFlags = nil;
static NSDictionary *operatorRequirements = nil;
static NSDictionary *methodRequirements = nil;
static NSMutableDictionary *methodRequirements = nil;
static NSString *sieveScriptName = @"sogo";
@ -188,16 +188,16 @@ static NSString *sieveScriptName = @"sogo";
if (!methodRequirements)
{
methodRequirements
= [NSDictionary dictionaryWithObjectsAndKeys:
@"imapflags", @"addflag",
@"imapflags", @"removeflag",
@"imapflags", @"flag",
@"vacation", @"vacation",
@"notify", @"notify",
@"fileinto", @"fileinto",
@"reject", @"reject",
@"regex", @"regex",
nil];
= [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"imapflags", @"addflag",
@"imapflags", @"removeflag",
@"imapflags", @"flag",
@"vacation", @"vacation",
@"notify", @"notify",
@"fileinto", @"fileinto",
@"reject", @"reject",
@"regex", @"regex",
nil];
[methodRequirements retain];
}
}
@ -615,8 +615,8 @@ static NSString *sieveScriptName = @"sogo";
SOGoUserDefaults *ud;
SOGoDomainDefaults *dd;
NGSieveClient *client;
NSString *filterScript, *v, *sieveServer;
NSURL *url;
NSString *filterScript, *v, *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
NSURL *url, *cUrl;
int sievePort;
BOOL b, connected;
@ -631,6 +631,115 @@ static NSString *sieveScriptName = @"sogo";
connected = YES;
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];
// We first handle filters
@ -646,6 +755,7 @@ static NSString *sieveScriptName = @"sogo";
else
{
NSLog(@"Sieve generation failure: %@", [self lastScriptError]);
[client closeConnection];
return NO;
}
@ -726,87 +836,6 @@ static NSString *sieveScriptName = @"sogo";
[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
its deletion from the server. */

View File

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

View File

@ -379,7 +379,15 @@ _injectConfigurationFromFile (NSUserDefaults *ud,
- (NSArray *) supportedLanguages
{
return [self stringArrayForKey: @"SOGoSupportedLanguages"];
static NSArray *supportedLanguages = nil;
if (!supportedLanguages)
{
supportedLanguages = [self stringArrayForKey: @"SOGoSupportedLanguages"];
[supportedLanguages retain];
}
return supportedLanguages;
}
- (BOOL) userCanChangePassword
@ -417,6 +425,37 @@ _injectConfigurationFromFile (NSUserDefaults *ud,
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
{
return [self boolForKey: @"SOGoEnablePublicAccess"];

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
@ -91,6 +92,22 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return ud;
}
- (id) init
{
if ((self = [super init]))
{
userLanguage = nil;
}
return self;
}
- (void) dealloc
{
[userLanguage release];
[super dealloc];
}
- (BOOL) _migrateLastModule
{
BOOL rc;
@ -355,21 +372,25 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
- (NSString *) language
{
NSString *language;
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
@ -472,6 +493,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self stringForKey: @"SOGoMailComposeMessageType"];
}
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
{
[self setObject: newValue forKey: @"SOGoMailDisplayRemoteInlineImages"];
}
- (NSString *) mailDisplayRemoteInlineImages;
{
return [self stringForKey: @"SOGoMailDisplayRemoteInlineImages"];
}
- (void) setMailMessageForwarding: (NSString *) newValue
{
[self setObject: newValue forKey: @"SOGoMailMessageForwarding"];

View File

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

View File

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

View File

@ -32,9 +32,12 @@
#import <NGObjWeb/WOCookie.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSData+gzip.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSString+Ext.h>
#import <NGLdap/NGLdapConnection.h>
#import <MainUI/SOGoRootPage.h>
@ -47,7 +50,9 @@
#import "SOGoSystemDefaults.h"
#import "SOGoUser.h"
#import "SOGoUserManager.h"
#if defined(SAML2_CONFIG)
#import "SOGoSAML2Session.h"
#endif
#import "SOGoWebAuthenticator.h"
@implementation SOGoWebAuthenticator
@ -107,11 +112,13 @@
{
SOGoCASSession *session;
SOGoSystemDefaults *sd;
NSString *authenticationType;
BOOL rc;
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([[sd authenticationType] isEqualToString: @"cas"])
authenticationType = [sd authenticationType];
if ([authenticationType isEqualToString: @"cas"])
{
session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
if (session)
@ -119,6 +126,18 @@
else
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
rc = [[SOGoUserManager sharedUserManager] checkLogin: _login
password: _pwd
@ -225,16 +244,18 @@
forURL: (NSURL *) server
forceRenew: (BOOL) renew
{
NSString *password, *service, *scheme;
SOGoCASSession *session;
SOGoSystemDefaults *sd;
NSString *authType, *password;
password = [self passwordInContext: context];
if ([password length])
{
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([[sd authenticationType] isEqualToString: @"cas"])
authType = [[SOGoSystemDefaults sharedSystemDefaults]
authenticationType];
if ([authType isEqualToString: @"cas"])
{
SOGoCASSession *session;
NSString *service, *scheme;
session = [SOGoCASSession CASSessionWithIdentifier: password
fromProxy: NO];
@ -252,6 +273,23 @@
if ([password length] || renew)
[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;
@ -314,4 +352,44 @@
[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 */

View File

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

View File

@ -98,6 +98,7 @@
_kindField = nil;
_multipleBookingsField = nil;
_imapHostField = nil;
_sieveHostField = nil;
}
return self;
@ -115,6 +116,7 @@
[_multipleBookingsField release];
[_domainField release];
[_imapHostField release];
[_sieveHostField release];
[super dealloc];
}
@ -131,6 +133,7 @@
ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]);
ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]);
ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]);
ASSIGN(_sieveHostField, [udSource objectForKey: @"SieveHostFieldName"]);
ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]);
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]);
@ -187,7 +190,10 @@
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm];
if (pass == nil)
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
{
[self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm];
return nil;
}
if (_prependPasswordScheme)
result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass];
@ -308,18 +314,20 @@
NSString *sqlstr;
BOOL didChange;
BOOL isOldPwdOk;
isOldPwdOk = NO;
didChange = NO;
// Verify current password
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0];
if (isOldPwdOk)
{
// Encrypt new password
NSString *encryptedPassword = [self _encryptPassword: newPassword];
if(encryptedPassword == nil)
return NO;
// Save new password
login = [login stringByReplacingString: @"'" withString: @"''"];
cm = [GCSChannelManager defaultChannelManager];
@ -519,6 +527,13 @@
[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
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",
"SpanishSpain", "SpanishArgentina", "French", "German",
"Icelandic", "Italian", "Hungarian", "BrazilianPortuguese",
"NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian",
"NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak",
"Ukrainian", "Swedish" ];
daysBetweenResponseList=[1,2,3,5,7,14,21,30]

View File

@ -208,8 +208,8 @@ class DAVAclTest(unittest.TestCase):
return versitStruct
event_template = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Inverse//Event Generator//EN
VERSION:2.0
BEGIN:VEVENT
SEQUENCE:0
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
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed
check ::
./obj/sogo-tests
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/test-tool.make
-include GNUmakefile.postamble
check ::
./obj/sogo-tests

View File

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

View File

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

View File

@ -6,7 +6,7 @@ BUNDLE_NAME = AdministrationUI
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 = \
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_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 += \
CommonUIProduct.m \

View File

@ -25,7 +25,7 @@
"Owner:" = "Właściciel:";
"Publish the Free/Busy information" = "Opublikuj informację wolny/zajęty";
"Add..." = "Dodaj...";
"Add..." = "Dodaj";
"Remove" = "Usuń";
"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?"
= "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?";
"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?";
@ -52,14 +52,14 @@
/* generic.js */
"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!"
= "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!"
= "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!"
= "Nie możesz wyłączyć subskrypcji foldera, który jest twoją własnością!";
"Unable to rename that folder!" = "Nie można zmienić nazwy tego foldera!";
= "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 folderu!";
"You have already subscribed to that folder!"
= "Już subskrybujesz ten folder!";
"The user rights cannot be edited for this object!"
@ -79,7 +79,7 @@
"delegate is a participant" = "Delegat jest już uczestnikiem.";
"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";
"10 minutes" = "10 minut";
"15 minutes" = "15 minut";
@ -106,4 +106,4 @@
"a2_Wednesday" = "Śr";
"a2_Thursday" = "Cz";
"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