diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 83314be2f..b107b94f0 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -2862,6 +2862,7 @@ static BOOL debugOn = NO; NSAutoreleasePool *pool; id builder, dom; SEL aSelector; + id activeUser; NSString *cmdName, *deviceId; NSData *d; @@ -2870,6 +2871,14 @@ static BOOL debugOn = NO; ASSIGN(context, theContext); + activeUser = [context activeUser]; + if (![activeUser canAccessModule: @"ActiveSync"]) + { + [theResponse setStatus: 403]; + [self logWithFormat: @"EAS - Forbidden access for user %@", [activeUser loginInDomain]]; + return nil; + } + // Get the device ID, device type and "stash" them deviceId = [[theRequest uri] deviceId]; [context setObject: deviceId forKey: @"DeviceId"]; diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index 5d5e03dac..b842b2d6c 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -178,56 +178,101 @@ rtf2html (NSData *compressedRTF) andColumns: (struct SPropTagArray *) columns { NSMutableDictionary *recipientProperties; + enum MAPITAGS prop_tag; + char *displayName = NULL; + char *email = NULL; + enum MAPI_OBJTYPE object_type = 0; + char *smtpAddress = NULL; SOGoUser *recipientUser; NSUInteger count; + NSString *recipientEmail = nil; + NSString *recipientFullName = nil; id value; recipientProperties = [NSMutableDictionary dictionaryWithCapacity: columns->cValues + 2]; + for (count = 0; count < columns->cValues; count++) + { + prop_tag = columns->aulPropTag[count]; + switch(prop_tag) { + case PidTagDisplayName: + displayName = recipient->data[count]; + break; + case PidTagEmailAddress: + email = recipient->data[count]; + break; + case PidTagObjectType: + if (recipient->data[count]) + object_type = *((uint8_t*) recipient->data[count]); + break; + case PidTagSmtpAddress: + smtpAddress = recipient->data[count]; + break; + default: + break; + } + + if (recipient->data[count]) + { + value = NSObjectFromValuePointer (prop_tag, + recipient->data[count]); + if (value) + [recipientProperties setObject: value + forKey: MAPIPropertyKey (prop_tag)]; + } + } + if (recipient->username) { value = [NSString stringWithUTF8String: recipient->username]; [recipientProperties setObject: value forKey: @"x500dn"]; + } + if (object_type == MAPI_MAILUSER && recipient->username) + { + /* values from user object have priority over data sent by the client */ recipientUser = [SOGoUser userWithLogin: [value lowercaseString]]; if (recipientUser) { value = [recipientUser cn]; if ([value length] > 0) - [recipientProperties setObject: value forKey: @"fullName"]; + recipientFullName = value; + value = [[recipientUser allEmails] objectAtIndex: 0]; if ([value length] > 0) - [recipientProperties setObject: value forKey: @"email"]; - } - } - else - { - if (recipient->data[0]) - { - value = [NSString stringWithUTF8String: recipient->data[0]]; - if ([value length] > 0) - [recipientProperties setObject: value forKey: @"fullName"]; - } - if (recipient->data[1]) - { - value = [NSString stringWithUTF8String: recipient->data[1]]; - if ([value length] > 0) - [recipientProperties setObject: value forKey: @"email"]; - } + recipientEmail = value; + } } - for (count = 0; count < columns->cValues; count++) + /* If we do not have values from the user object we try to get them from the parameters */ + if (!recipientFullName && displayName) { - if (recipient->data[count]) - { - value = NSObjectFromValuePointer (columns->aulPropTag[count], - recipient->data[count]); - if (value) - [recipientProperties setObject: value - forKey: MAPIPropertyKey (columns->aulPropTag[count])]; - } + value = [NSString stringWithUTF8String: displayName]; + if ([value length] > 0) + recipientFullName = value; } + if (!recipientEmail && email) + { + value = [NSString stringWithUTF8String: email]; + if ([value length] > 0) + recipientEmail = value; + } + + if (!recipientEmail && smtpAddress) + { + value = [NSString stringWithUTF8String: smtpAddress]; + if ([value length] > 0) + recipientEmail = value; + } + + /* Now we can set the properties if we have them */ + if (recipientFullName) + [recipientProperties setObject: recipientFullName forKey: @"fullName"]; + + if (recipientEmail) + [recipientProperties setObject: recipientEmail forKey: @"email"]; + return recipientProperties; } diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 505c305e2..7c3d702ce 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -2132,32 +2132,23 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // if ([[newEvent attendees] count] || [[oldEvent attendees] count]) { - NSString *uid; - + BOOL userIsOrganizer; + // newEvent might be nil here, if we're deleting a RECURRENCE-ID with attendees - // If that's the case, we use the oldEvent for now just to obtain the organizer - // and we'll swap it back to nil once we're done. - if (!newEvent) - newEvent = oldEvent; - - // We fetch the organizer's uid. Sometimes, the recurrence-id will - // have it, sometimes not. If it doesn't, we fetch it from the master event. - uid = [[newEvent organizer] uid]; - - if (!uid && !recurrenceId) - uid = [[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]; - + // If that's the case, we use the oldEvent to obtain the organizer + if (newEvent) + userIsOrganizer = [newEvent userIsOrganizer: ownerUser]; + else + userIsOrganizer = [oldEvent userIsOrganizer: ownerUser]; + // With Thunderbird 10, if you create a recurring event with an exception // occurence, and invite someone, the PUT will have the organizer in the // recurrence-id and not in the master event. We must fix this, otherwise // SOGo will break. if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]) [[[[newEvent parent] events] objectAtIndex: 0] setOrganizer: [newEvent organizer]]; - - if (newEvent == oldEvent) - newEvent = nil; - - if (uid && [uid caseInsensitiveCompare: owner] == NSOrderedSame) + + if (userIsOrganizer) { // A RECCURENCE-ID was removed if (!newEvent && oldEvent) diff --git a/SoObjects/SOGo/BSONCodec.m b/SoObjects/SOGo/BSONCodec.m index 99eee87c4..ce4c01e24 100644 --- a/SoObjects/SOGo/BSONCodec.m +++ b/SoObjects/SOGo/BSONCodec.m @@ -1,10 +1,12 @@ -// -// BSONCodec.m -// BSON Codec for Objective-C. -// -// Created by Martin Kou on 8/17/10. -// MIT License, see LICENSE file for details. -// +/* + * BSONCodec.m + * BSON Codec for Objective-C. + * + * Created by Martin Kou on 8/17/10. + * MIT License, see LICENSE file for details. + * + * Adapted by Ludovic Marcotte and Enrique J. Hernández +*/ #import "BSONCodec.h" #import @@ -570,7 +572,7 @@ static NSDictionary *BSONTypes() { NSString *v; - v = [self descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %Z" + v = [self descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z" locale: nil]; return [v BSONEncode]; @@ -599,7 +601,22 @@ static NSDictionary *BSONTypes() key = [NSString stringWithFormat: @"%s", timezone]; - if (!(tz = [timezoneCache objectForKey: key])) + /* We may have the zone using the UTC offset + or abbreviation (deprecated) */ + if (timezone && strlen(timezone) > 0 && (timezone[0] == '+' || timezone[0] == '-')) + { + NSCalendarDate *tzDate; + + tzDate = [[NSCalendarDate alloc] initWithString: key + calendarFormat: @"%z" + locale: nil]; + [tzDate autorelease]; + if (tzDate) + tz = [tzDate timeZone]; + else + tz = nil; + } + else if (!(tz = [timezoneCache objectForKey: key])) { tz = [NSTimeZone timeZoneWithAbbreviation: key]; diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 1d2d55f5f..d537c6aec 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -737,7 +737,8 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses } NS_HANDLER { - if ([[localException name] isEqual: @"LDAPException"] && ([[[localException userInfo] objectForKey: @"error_code"] intValue] == LDAP_CONSTRAINT_VIOLATION)) + if ([[localException name] isEqual: @"LDAPException"] && + ([[[localException userInfo] objectForKey: @"error_code"] intValue] == LDAP_CONSTRAINT_VIOLATION)) { *perr = PolicyInsufficientPasswordQuality; } @@ -1166,6 +1167,8 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses intoLDIFRecord: (NSMutableDictionary *) ldifRecord]; [self _fillConstraints: ldapEntry forModule: @"Mail" intoLDIFRecord: (NSMutableDictionary *) ldifRecord]; + [self _fillConstraints: ldapEntry forModule: @"ActiveSync" + intoLDIFRecord: (NSMutableDictionary *) ldifRecord]; if (contactMapping) [self _applyContactMappingToResult: ldifRecord]; diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index 936877336..9af654b90 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -157,7 +157,7 @@ // that much about this for now. // + (NSString *) securedValue: (NSString *) theValue - usingKey: (NSString *) theKey + usingKey: (NSString *) theKey { NSData *data; NSString *s; @@ -171,13 +171,12 @@ klen = [data length]; // Get the key - padding it with 0 with key length - pass = (char *)malloc(klen); - memset(pass, 0, klen); + pass = (char *) calloc(klen, sizeof(char)); [theValue getCString: pass maxLength: klen encoding: NSUTF8StringEncoding]; // Target buffer buf = (char *)malloc(klen); - + for (i = 0; i < klen; i++) { buf[i] = key[i] ^ pass[i]; @@ -186,36 +185,38 @@ free(pass); data = [NSData dataWithBytesNoCopy: buf length: klen freeWhenDone: YES]; - + s = [[NSString alloc] initWithData: [data dataByEncodingBase64WithLineLength: 1024] - encoding: NSASCIIStringEncoding]; + encoding: NSASCIIStringEncoding]; return [s autorelease]; } + (NSString *) valueFromSecuredValue: (NSString *) theValue - usingKey: (NSString *) theKey + usingKey: (NSString *) theKey { - NSData *data; + NSData *dataKey, *dataValue; NSString *s; - - char *buf, *key, *pass; - int i, klen; + + char *buf, *key, *value; + size_t i, klen, vlen; // Get the key length and its bytes - data = [theKey dataByDecodingBase64]; - key = (char *)[data bytes]; - klen = [data length]; + dataKey = [theKey dataByDecodingBase64]; + key = (char *)[dataKey bytes]; + klen = [dataKey length]; + + // Get the secured value length and its bytes + dataValue = [theValue dataByDecodingBase64]; + value = (char *)[dataValue bytes]; + vlen = [dataValue length]; - // Get the secured password - pass = (char *)[[theValue dataByDecodingBase64] bytes]; - // Target buffer - buf = (char *)malloc(klen); + buf = (char *) calloc(klen, sizeof(char)); - for (i = 0; i < klen; i++) + for (i = 0; i < klen && i < vlen; i++) { - buf[i] = key[i] ^ pass[i]; + buf[i] = key[i] ^ value[i]; } // buf is now our C string in UTF8 diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 5dcd007df..f93cef7e6 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -438,14 +438,16 @@ static Class NSNullK; NSEnumerator *authIDs; NSString *currentID; BOOL checkOK; + SOGoSystemDefaults *sd; + NSRange r; checkOK = NO; - + authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; - + checkOK = [sogoSource checkLogin: login password: password perr: perr @@ -649,7 +651,7 @@ static Class NSNullK; [currentSource setBindPassword: _pwd]; } } - + return checkOK; } @@ -742,6 +744,12 @@ static Class NSNullK; NSNumber *isGroup; NSArray *c_emails; BOOL access; + NSEnumerator *enumerator; + NSString *access_type; + NSArray *access_types_list = [NSArray arrayWithObjects: @"CalendarAccess", + @"MailAccess", + @"ActiveSyncAccess", + nil]; emails = [NSMutableArray array]; cn = nil; @@ -751,10 +759,10 @@ static Class NSNullK; c_imaplogin = nil; c_sievehostname = nil; - [theCurrentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"CalendarAccess"]; - [theCurrentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"MailAccess"]; + enumerator = [access_types_list objectEnumerator]; + while ((access_type = [enumerator nextObject]) != nil) + [theCurrentUser setObject: [NSNumber numberWithBool: YES] + forKey: access_type]; if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue]) { @@ -763,7 +771,6 @@ static Class NSNullK; r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]]; theUID = [theUID substringToIndex: r.location]; } - sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator]; userEntry = nil; @@ -791,14 +798,15 @@ static Class NSNullK; c_imaplogin = [userEntry objectForKey: @"c_imaplogin"]; if (!c_sievehostname) c_sievehostname = [userEntry objectForKey: @"c_sievehostname"]; - access = [[userEntry objectForKey: @"CalendarAccess"] boolValue]; - if (!access) - [theCurrentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"CalendarAccess"]; - access = [[userEntry objectForKey: @"MailAccess"] boolValue]; - if (!access) - [theCurrentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"MailAccess"]; + + enumerator = [access_types_list objectEnumerator]; + while ((access_type = [enumerator nextObject]) != nil) + { + access = [[userEntry objectForKey: access_type] boolValue]; + if (!access) + [theCurrentUser setObject: [NSNumber numberWithBool: NO] + forKey: access_type]; + } // We check if it's a group isGroup = [userEntry objectForKey: @"isGroup"]; diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 869fb52c6..32f9119e4 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -22,6 +22,7 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRemoveDoubles.m \ SOGoToolRenameUser.m \ SOGoToolRestore.m \ + SOGoToolCreateFolder.m \ SOGoToolUserPreferences.m \ SOGoToolManageEAS.m TOOL_NAME += $(SOGO_TOOL) diff --git a/Tools/SOGoToolCreateFolder.m b/Tools/SOGoToolCreateFolder.m new file mode 100644 index 000000000..423ec7266 --- /dev/null +++ b/Tools/SOGoToolCreateFolder.m @@ -0,0 +1,175 @@ +/* SOGoToolCreateFolder.m - this file is part of SOGo + * Implementation of create-folder command for sogo-tool + * + * Copyright (C) 2015 Javier Amor Garcia + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import + +#import +#import + +#import +#import + +#import "SOGoToolRestore.h" + +@interface SOGoToolCreateFolder : SOGoToolRestore +{ + NSMutableArray *foldersContents; + NSString *folderType; +} + +@end + +@implementation SOGoToolCreateFolder + ++ (NSString *) command +{ + return @"create-folder"; +} + ++ (NSString *) description +{ + return @"create folders for a user"; +} + +- (id) init +{ + if ((self = [super init])) + { + foldersContents = nil; + folderType = nil; + } + + return self; +} + +- (void) dealloc +{ + [foldersContents release]; + [folderType release]; + [super dealloc]; +} + +- (void) usage +{ + fprintf (stderr, "create-folder user type [displayname ...]\n\n" + " user the user on which the folder(s) will be created\n" + " type type of directory. Calendar or Contacts\n" + " displayname display name(s) for the created folder(s)\n\n" + "Examples: sogo-tool create-folder user1 Calendar cal1 cal2\n" + " sogo-tool create-folder user1 Contacts agenda\n"); +} + +- (BOOL) createNewFolderOfType: (NSString *) type + withContent: (NSDictionary *) content +{ + NSString *folder; + NSString *guid; + GCSFolderManager *fm; + GCSFolder *gcsFolder; + BOOL rc; + + guid = [SOGoObject globallyUniqueObjectId]; + folder= [NSString stringWithFormat: @"/Users/%@/%@/%@", + userID, type, guid]; + fm = [GCSFolderManager defaultFolderManager]; + + rc = [self createFolder: folder withFM: fm]; + if (!rc) + { + NSLog (@"Create directory failed at path %s", folder); + return NO; + } + + gcsFolder = [fm folderAtPath: folder]; + if (!gcsFolder) + { + NSLog (@"folder '%@' could not be created", folder); + return NO; + } + + rc = [self restoreDisplayName: [content objectForKey: @"displayname"] + ofFolder: gcsFolder + withFM: fm]; + return rc; +} + +- (BOOL) proceed +{ + BOOL rc; + NSUInteger count, i; + NSDictionary * content; + + rc = YES; + count = [foldersContents count]; + for (i = 0; i < count; i++) + { + content = [foldersContents objectAtIndex: i]; + if (![self createNewFolderOfType: folderType withContent: content]) + { + rc = NO; + } + } + + return rc; +} + +- (BOOL) parseArguments +{ + NSString *identifier; + NSUInteger count, i; + NSDictionary *content; + + count = [arguments count]; + if (count < 3) + { + [self usage]; + return NO; + } + + identifier = [arguments objectAtIndex: 0]; + if (![self fetchUserID: identifier]) + { + fprintf (stderr, "Invalid user:%s\n", [identifier cString]); + return NO; + } + + folderType = [arguments objectAtIndex: 1]; + if (!([folderType isEqualToString: @"Contacts"] || [folderType isEqualToString: @"Calendar"])) + { + fprintf (stderr, "Invalid folder type:%s\n", [folderType cString]); + return NO; + } + + foldersContents = [[NSMutableArray alloc] init]; + for (i = 2; i < count; i++) + { + content = [NSDictionary dictionaryWithObject: [arguments objectAtIndex: i] + forKey: @"displayname"]; + [foldersContents addObject: content]; + } + + return YES; +} + +@end diff --git a/Tools/SOGoToolRestore.h b/Tools/SOGoToolRestore.h new file mode 100644 index 000000000..61438ac18 --- /dev/null +++ b/Tools/SOGoToolRestore.h @@ -0,0 +1,49 @@ +/* SOGoToolRestore.h - this file is part of SOGo + * Header to allow subclassing of SOGoToolRestore class + * + * Copyright (C) 2015 Javier Amor Garcia + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#import "SOGoTool.h" + +typedef enum SOGoToolRestoreMode { + SOGoToolRestoreFolderMode, + SOGoToolRestoreFolderDestructiveMode, + SOGoToolRestoreListFoldersMode, + SOGoToolRestorePreferencesMode +} SOGoToolRestoreMode; + +@interface SOGoToolRestore : SOGoTool +{ + NSString *directory; + NSString *userID; + NSString *filename; + NSString *restoreFolder; + BOOL destructive; /* destructive mode not handled */ + SOGoToolRestoreMode restoreMode; +} + +- (BOOL) fetchUserID: (NSString *) identifier; +- (BOOL) createFolder: (NSString *) folder + withFM: (GCSFolderManager *) fm; +- (BOOL) restoreDisplayName: (NSString *) newDisplayName + ofFolder: (GCSFolder *) gcsFolder + withFM: (GCSFolderManager *) fm; + +@end diff --git a/Tools/SOGoToolRestore.m b/Tools/SOGoToolRestore.m index 5e43b769e..712c680ba 100644 --- a/Tools/SOGoToolRestore.m +++ b/Tools/SOGoToolRestore.m @@ -46,33 +46,14 @@ #import #import -#import "SOGoTool.h" +#import "SOGoToolRestore.h" /* TODO: - respond to "--help restore" - handle database connectivity errors - handle the case where the restored folder has been deleted - write methods in GDLContentStore to get/update displayname - and storing roles */ - -typedef enum SOGoToolRestoreMode { - SOGoToolRestoreFolderMode, - SOGoToolRestoreFolderDestructiveMode, - SOGoToolRestoreListFoldersMode, - SOGoToolRestorePreferencesMode -} SOGoToolRestoreMode; - -@interface SOGoToolRestore : SOGoTool -{ - NSString *directory; - NSString *userID; - NSString *filename; - NSString *restoreFolder; - BOOL destructive; /* destructive mode not handled */ - SOGoToolRestoreMode restoreMode; -} - -@end + and storing roles */ @implementation SOGoToolRestore @@ -176,8 +157,8 @@ typedef enum SOGoToolRestoreMode { SOGoSystemDefaults *sd; SOGoUserManager *lm; NSDictionary *infos; - NSString *uid; - + NSString *uid = nil; + BOOL rc; lm = [SOGoUserManager sharedUserManager]; @@ -188,7 +169,7 @@ typedef enum SOGoToolRestoreMode { { sd = [SOGoSystemDefaults sharedSystemDefaults]; uid = [infos objectForKey: @"c_uid"]; - + if ([sd enableDomainBasedUID] && [uid rangeOfString: @"@"].location == NSNotFound) uid = [NSString stringWithFormat: @"%@@%@", [infos objectForKey: @"c_uid"],