pull/226/head
parent
961b2226f0
commit
f7c4486370
|
@ -51,6 +51,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
#import <Appointments/SOGoAppointmentObject.h>
|
||||
|
@ -272,7 +273,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache, *allValues;
|
||||
NSString *clientId, *serverId, *easId;
|
||||
NSArray *additions;
|
||||
NSArray *additions, *roles;
|
||||
|
||||
id anAddition, sogoObject, o;
|
||||
BOOL is_new;
|
||||
|
@ -296,6 +297,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
serverId = [NSString stringWithFormat: @"%@.vcf", [theCollection globallyUniqueObjectId]];
|
||||
sogoObject = [[SOGoContactGCSEntry alloc] initWithName: serverId
|
||||
inContainer: theCollection];
|
||||
[sogoObject autorelease];
|
||||
o = [sogoObject vCard];
|
||||
}
|
||||
break;
|
||||
|
@ -319,6 +321,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
sogoObject = [[SOGoAppointmentObject alloc] initWithName: [serverId sanitizedServerIdWithType: theFolderType]
|
||||
inContainer: theCollection];
|
||||
[sogoObject autorelease];
|
||||
o = [sogoObject component: YES secure: NO];
|
||||
}
|
||||
else
|
||||
|
@ -333,6 +336,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]];
|
||||
sogoObject = [[SOGoTaskObject alloc] initWithName: serverId
|
||||
inContainer: theCollection];
|
||||
[sogoObject autorelease];
|
||||
o = [sogoObject component: YES secure: NO];
|
||||
}
|
||||
break;
|
||||
|
@ -344,11 +348,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
[o takeActiveSyncValues: allValues inContext: context];
|
||||
[sogoObject setIsNew: is_new];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
|
||||
roles = [theCollection aclsForUser: [[context activeUser] login]];
|
||||
|
||||
if (![roles containsObject: SOGoRole_ObjectCreator] && ![[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
// This will trigger a delete-command to remove the component without proper permission.
|
||||
// FIXME: ultimately, we need to find a better way to create a phantom object in the user's calendar
|
||||
// and delete it to keep the transactional process in shape. We could also create a deleted one right way
|
||||
// We strip any attendees from the values as we don't want to send bogus invitation emails
|
||||
[allValues removeObjectForKey: @"Attendees"];
|
||||
[o takeActiveSyncValues: allValues inContext: context];
|
||||
[sogoObject setIsNew: YES];
|
||||
[sogoObject saveComponent: o];
|
||||
[sogoObject delete];
|
||||
}
|
||||
else
|
||||
{
|
||||
[o takeActiveSyncValues: allValues inContext: context];
|
||||
[sogoObject setIsNew: is_new];
|
||||
[sogoObject saveComponent: o];
|
||||
}
|
||||
|
||||
// Update syncCache
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
|
@ -388,6 +409,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
|
||||
|
||||
// make sure to pickup the delete immediately if we don't have proper permission to add
|
||||
if (![roles containsObject: SOGoRole_ObjectCreator] && ![[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
// Everything is fine, lets generate our response
|
||||
|
@ -444,7 +469,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
NSDictionary *allChanges;
|
||||
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||
NSArray *changes, *a;
|
||||
NSArray *changes, *a, *roles;
|
||||
id aChange, o, sogoObject;
|
||||
NSMutableDictionary *folderMetadata, *syncCache, *uidCache;
|
||||
|
||||
|
@ -495,7 +520,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Found serverId: %@ for easId: %@", serverId, easId];
|
||||
|
||||
}
|
||||
else
|
||||
serverId = easId;
|
||||
|
@ -519,32 +543,60 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
case ActiveSyncContactFolder:
|
||||
{
|
||||
o = [sogoObject vCard];
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
roles = [sogoObject aclsForUser:[[context activeUser] login]];
|
||||
o = [sogoObject vCard];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
// Don't update the component without proper permission
|
||||
if (([roles containsObject: SOGoRole_ObjectEditor] || [[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]]))
|
||||
{
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat: @"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
}
|
||||
// Trigger a change-command to override client changes since we don't have permissions
|
||||
else
|
||||
[sogoObject touch];
|
||||
}
|
||||
break;
|
||||
case ActiveSyncEventFolder:
|
||||
case ActiveSyncTaskFolder:
|
||||
{
|
||||
roles = [sogoObject aclsForUser:[[context activeUser] login]];
|
||||
|
||||
o = [sogoObject component: NO secure: NO];
|
||||
|
||||
if (theFolderType == ActiveSyncEventFolder &&
|
||||
[(iCalEvent *)o userIsAttendee: [context activeUser]])
|
||||
{
|
||||
[o changeParticipationStatus: allChanges inContext: context component: sogoObject];
|
||||
// Don't update the component without proper permission
|
||||
if ([roles containsObject: SOGoCalendarRole_ComponentResponder] || [[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
[o changeParticipationStatus: allChanges inContext: context component: sogoObject];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat: @"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
}
|
||||
// Trigger a change-command to override client changes since we don't have permissions
|
||||
else
|
||||
[sogoObject touch];
|
||||
}
|
||||
else
|
||||
{
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
}
|
||||
// Don't update the component without proper permission
|
||||
if ([roles containsObject: SOGoCalendarRole_ComponentModifier] || [[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat: @"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
}
|
||||
// Trigger a change-command to override client changes since we don't have permissions
|
||||
else
|
||||
[sogoObject touch];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ActiveSyncMailFolder:
|
||||
|
@ -559,13 +611,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
|
||||
|
||||
if (modseq && [syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [modseq stringValue] forKey: serverId];
|
||||
[syncCache setObject: [modseq stringValue] forKey: serverId];
|
||||
}
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
|
@ -604,10 +655,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
{
|
||||
|
||||
id aDelete, sogoObject, value;
|
||||
NSArray *deletions, *a;
|
||||
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||
NSArray *deletions, *a, *roles;
|
||||
id aDelete, sogoObject, value;
|
||||
|
||||
BOOL deletesAsMoves, useTrash;
|
||||
int i;
|
||||
|
@ -616,11 +666,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
if ([deletions count])
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache;
|
||||
NSMutableDictionary *folderMetadata, *uidCache;
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
// From the documention, if DeletesAsMoves is missing, we must assume it's a YES.
|
||||
|
@ -677,22 +725,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
if (![sogoObject isKindOfClass: [NSException class]])
|
||||
{
|
||||
// FIXME: handle errors here
|
||||
if (deletesAsMoves && theFolderType == ActiveSyncMailFolder)
|
||||
[(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context];
|
||||
else if (theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder)
|
||||
{
|
||||
[sogoObject prepareDelete];
|
||||
[sogoObject delete];
|
||||
}
|
||||
else
|
||||
[sogoObject delete];
|
||||
// Remove the cache entry. If the delete fails due to permission issues we do a fake update to trigger
|
||||
// an add-command.
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
[syncCache removeObjectForKey: serverId];
|
||||
[dateCache removeObjectForKey: serverId];
|
||||
//[uidCache removeObjectForKey: serverId];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
// FIXME: handle errors here
|
||||
if (deletesAsMoves && theFolderType == ActiveSyncMailFolder)
|
||||
{
|
||||
[(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context];
|
||||
}
|
||||
else if (theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder || theFolderType == ActiveSyncContactFolder)
|
||||
{
|
||||
roles = [theCollection aclsForUser:[[context activeUser] login]];
|
||||
|
||||
// We check ACLs on the collection and not on the SOGo object itself, as the add/delete rights are on the collection itself
|
||||
if (![roles containsObject: SOGoRole_ObjectEraser] || ![[sogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
// This will trigger an add-command to re-add the component to the client
|
||||
[sogoObject touch];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(theFolderType == ActiveSyncContactFolder))
|
||||
[sogoObject prepareDelete];
|
||||
[sogoObject delete];
|
||||
}
|
||||
}
|
||||
else
|
||||
[sogoObject delete];
|
||||
|
||||
[theBuffer appendString: @"<Delete>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
|
@ -991,7 +1061,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // if (cleanup_needed) ...
|
||||
|
||||
return_count = 0;
|
||||
component = nil;
|
||||
|
@ -1084,8 +1154,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
continue;
|
||||
}
|
||||
|
||||
return_count++;
|
||||
|
||||
sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
@ -1093,10 +1161,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
if (theFolderType == ActiveSyncContactFolder)
|
||||
componentObject = [sogoObject vCard];
|
||||
else
|
||||
componentObject = [sogoObject component: NO secure: NO];
|
||||
componentObject = [sogoObject component: NO secure: YES];
|
||||
|
||||
[syncCache setObject: [component objectForKey: @"c_lastmodified"] forKey: uid];
|
||||
|
||||
// We have got a null componentObject, i.e. we have no permission to view the calendar entry.
|
||||
// For updated calendar entries we have to remove it from the client and for new entries we just ignore them.
|
||||
if (!componentObject)
|
||||
{
|
||||
if (updated)
|
||||
{
|
||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||
|
||||
if (![[theCollection nameInContainer] isEqualToString: @"personal"] && theMergeFolder)
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@{+}%@</ServerId>", [theCollection nameInContainer], easId];
|
||||
else
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", easId];
|
||||
|
||||
[s appendString: @"</Delete>"];
|
||||
|
||||
[syncCache removeObjectForKey: uid];
|
||||
[dateCache removeObjectForKey: uid];
|
||||
return_count++;
|
||||
}
|
||||
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to set dateCache for Contacts
|
||||
if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder))
|
||||
{
|
||||
|
@ -1703,6 +1795,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
first_sync = NO;
|
||||
|
||||
// We check if the folder permissions have changed since the last sync. If so, we simply clear the cached data
|
||||
// and a "full sync" will be issued by the EAS client as it'll repull all elements from the collection.
|
||||
// We don't need to check for contact folder. Either it has View_object or it will be removed by FolderSync.
|
||||
if ((folderType == ActiveSyncEventFolder || folderType == ActiveSyncTaskFolder) && ![[collection ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
NSArray *folderRoles, *folderRolesInCache;
|
||||
|
||||
folderRoles = [collection aclsForUser: [[context activeUser] login]];
|
||||
folderRolesInCache = [folderMetadata objectForKey: @"FolderPermissions"];
|
||||
if (![folderRoles isEqualToArray: folderRolesInCache])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Folder permission change: %@ (%@ <> %@). Refresh folder", [collection nameInContainer], folderRoles, folderRolesInCache];
|
||||
|
||||
// Cleanup metadata
|
||||
[folderMetadata removeObjectForKey: @"SyncKey"];
|
||||
[folderMetadata removeObjectForKey: @"SyncCache"];
|
||||
[folderMetadata removeObjectForKey: @"DateCache"];
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
|
||||
[folderMetadata setObject: folderRoles forKey: @"FolderPermissions"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
}
|
||||
}
|
||||
|
||||
if ([syncKey isEqualToString: @"0"])
|
||||
{
|
||||
davCollectionTag = @"-1";
|
||||
|
@ -1841,7 +1958,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
// Windows phones don't like empty Responses tags - such as: <Responses></Responses>.
|
||||
// We only generate this tag when there is a response
|
||||
if (processed && [s length])
|
||||
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
||||
{
|
||||
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
||||
getChanges = NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1984,7 +2104,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
if (!(mfCollection && [mfCollection synchronize]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Folder %@ not found. Reset peronal folder to cleanup", folderName];
|
||||
[self logWithFormat: @"EAS - Folder %@ not found. Reset personal folder to cleanup", folderName];
|
||||
|
||||
davCollectionTag = @"0";
|
||||
*changeDetected = YES;
|
||||
|
@ -2002,6 +2122,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
break;
|
||||
}
|
||||
|
||||
// Here we don't need to check for contact folder. Either it has View_object or it will be removed by folersync.
|
||||
if ((folderType == ActiveSyncEventFolder || folderType == ActiveSyncTaskFolder) && ![[mfCollection ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
NSArray *folderRoles, *folderRolesInCache;
|
||||
|
||||
folderRoles = [mfCollection aclsForUser: [[context activeUser] login]];
|
||||
folderRolesInCache = [folderMetadata objectForKey: @"FolderPermissions"];
|
||||
|
||||
if (![folderRoles isEqualToArray: folderRolesInCache])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Folder permission change: %@ (%@ <> %@). Refresh folder", [collection nameInContainer], folderRoles, folderRolesInCache];
|
||||
|
||||
davCollectionTag = @"0";
|
||||
*changeDetected = YES;
|
||||
|
||||
status = 3;
|
||||
// Cleanup metadata
|
||||
[folderMetadata removeObjectForKey: @"SyncKey"];
|
||||
[folderMetadata removeObjectForKey: @"SyncCache"];
|
||||
[folderMetadata removeObjectForKey: @"DateCache"];
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
|
||||
[folderMetadata setObject: folderRoles forKey: @"FolderPermissions"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Merging folder %@ into personal folder", folderName];
|
||||
|
||||
|
@ -2062,7 +2211,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
}
|
||||
} // end if
|
||||
} // if (![changeBuffer length]) ...
|
||||
|
||||
if (*changeDetected || cacheUpdateNeeded)
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
|
|
|
@ -82,6 +82,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#import <SOGo/WORequest+SOGo.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Appointments/SOGoAppointmentFolders.h>
|
||||
|
@ -138,7 +139,7 @@ void handle_eas_terminate(int signum)
|
|||
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey;
|
||||
- (void) _setOrUnsetSyncRequest: (BOOL) set
|
||||
collections: (NSArray *) collections;
|
||||
|
||||
- (NSString *) _getNameInCache: (id) theCollection withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -773,19 +774,18 @@ void handle_eas_terminate(int signum)
|
|||
SOGoMailAccount *accountFolder;
|
||||
NSMutableString *s, *commands;
|
||||
SOGoUserFolder *userFolder;
|
||||
SoSecurityManager *sm;
|
||||
NSArray *allKeys, *roles;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSArray *allKeys;
|
||||
id currentFolder;
|
||||
NSData *d;
|
||||
|
||||
int status, command_count, i, type, fi, count;
|
||||
BOOL first_sync;
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
metadata = [self globalMetadataForDevice];
|
||||
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||
s = [NSMutableString string];
|
||||
personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer];
|
||||
|
||||
first_sync = NO;
|
||||
status = 1;
|
||||
|
@ -936,15 +936,8 @@ void handle_eas_terminate(int signum)
|
|||
[o save];
|
||||
}
|
||||
|
||||
// Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions
|
||||
if (!currentFolder ||
|
||||
![currentFolder synchronize] ||
|
||||
[sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: currentFolder
|
||||
inContext: context] ||
|
||||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: currentFolder
|
||||
inContext: context])
|
||||
// Remove the folder from device if it doesn't exist, or don't want to sync it.
|
||||
if (!currentFolder || !([currentFolder synchronize]))
|
||||
{
|
||||
// Don't send a delete when MergedFoler is set, we have done it above.
|
||||
// Windows Phones don't like when a <Delete>-folder is sent twice.
|
||||
|
@ -956,6 +949,22 @@ void handle_eas_terminate(int signum)
|
|||
[o destroy];
|
||||
}
|
||||
|
||||
// Remove the folder from device if it is a contact folder and we have no SOGoRole_ObjectViewer.
|
||||
if ([currentFolder isKindOfClass: [SOGoContactGCSFolder class]] && ![[currentFolder ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
roles = [currentFolder aclsForUser: [[context activeUser] login]];
|
||||
if (![roles containsObject: SOGoRole_ObjectViewer])
|
||||
{
|
||||
// Don't send a delete when MergedFoler is set, we have done it above.
|
||||
// Windows Phones don't like when a <Delete>-folder is sent twice.
|
||||
if (![[[o properties] objectForKey: @"MergedFolder"] isEqualToString: @"2"])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
}
|
||||
[o destroy];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1063,16 +1072,17 @@ void handle_eas_terminate(int signum)
|
|||
|
||||
command_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer];
|
||||
folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy];
|
||||
[folders autorelease];
|
||||
// We get the list of subscribed calendars
|
||||
folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy];
|
||||
[folders autorelease];
|
||||
|
||||
[folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]];
|
||||
// We get the list of subscribed address books
|
||||
[folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]];
|
||||
|
||||
// We remove all the folders that aren't GCS-ones, that we don't want to synchronize and
|
||||
// the ones without write/delete permissions.
|
||||
// contact folder without SOGoRole_ObjectViewer.
|
||||
count = [folders count]-1;
|
||||
for (; count >= 0; count--)
|
||||
{
|
||||
|
@ -1084,26 +1094,28 @@ void handle_eas_terminate(int signum)
|
|||
continue;
|
||||
|
||||
if (![currentFolder isKindOfClass: [SOGoGCSFolder class]] ||
|
||||
![currentFolder synchronize] ||
|
||||
[sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: currentFolder
|
||||
inContext: context] ||
|
||||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: currentFolder
|
||||
inContext: context])
|
||||
![currentFolder synchronize])
|
||||
{
|
||||
[folders removeObjectAtIndex: count];
|
||||
}
|
||||
|
||||
// Remove the folder from the device if it is a contact folder and we have no SOGoRole_ObjectViewer access right.
|
||||
if ([currentFolder isKindOfClass: [SOGoContactGCSFolder class]] && ![[currentFolder ownerInContext: context] isEqualToString: [[context activeUser] login]])
|
||||
{
|
||||
roles = [currentFolder aclsForUser: [[context activeUser] login]];
|
||||
if (![roles containsObject: SOGoRole_ObjectViewer])
|
||||
[folders removeObjectAtIndex: count];
|
||||
}
|
||||
}
|
||||
|
||||
count = [folders count]-1;
|
||||
|
||||
for (fi = 0; fi <= count ; fi++)
|
||||
{
|
||||
if ([[folders objectAtIndex:fi] isKindOfClass: [SOGoAppointmentFolder class]])
|
||||
name = [NSString stringWithFormat: @"vevent/%@", [[folders objectAtIndex:fi] nameInContainer]];
|
||||
if ([[folders objectAtIndex: fi] isKindOfClass: [SOGoAppointmentFolder class]])
|
||||
name = [NSString stringWithFormat: @"vevent/%@", [[folders objectAtIndex: fi] nameInContainer]];
|
||||
else
|
||||
name = [NSString stringWithFormat: @"vcard/%@", [[folders objectAtIndex:fi] nameInContainer]];
|
||||
name = [NSString stringWithFormat: @"vcard/%@", [[folders objectAtIndex: fi] nameInContainer]];
|
||||
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], name];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
|
@ -1112,9 +1124,9 @@ void handle_eas_terminate(int signum)
|
|||
[o reloadIfNeeded];
|
||||
|
||||
// Decide between add and change
|
||||
if (![[o properties ] objectForKey: @"displayName"] || first_sync)
|
||||
if (![[o properties ] objectForKey: @"displayName"] || first_sync)
|
||||
operation = @"Add";
|
||||
else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]])
|
||||
else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]])
|
||||
operation = @"Update";
|
||||
else
|
||||
operation = nil;
|
||||
|
@ -1129,7 +1141,7 @@ void handle_eas_terminate(int signum)
|
|||
|
||||
command_count++;
|
||||
|
||||
[[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"];
|
||||
[[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"];
|
||||
[o save];
|
||||
|
||||
name = [NSString stringWithFormat: @"vtodo/%@", [[folders objectAtIndex:fi] nameInContainer]];
|
||||
|
@ -1895,7 +1907,6 @@ void handle_eas_terminate(int signum)
|
|||
SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType;
|
||||
id <DOMElement> aMoveOperation;
|
||||
NSArray *moveOperations;
|
||||
SoSecurityManager *sm;
|
||||
NSMutableString *s;
|
||||
NSData *d;
|
||||
int i;
|
||||
|
@ -2051,14 +2062,13 @@ void handle_eas_terminate(int signum)
|
|||
// Save dstMessageId in cache - it will help to recover if the request fails before the response can be sent to client
|
||||
[newSuccessfulMoveItemsOps setObject: dstMessageId forKey: srcMessageId];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
id srcCollection, dstCollection, srcSogoObject, dstSogoObject;
|
||||
NSArray *elements;
|
||||
NSArray *elements, *srcObjectRoles, *dstObjectRoles;
|
||||
NSString *newUID, *origSrcMessageId;
|
||||
NSMutableDictionary *srcUidCache, *dstUidCache, *dstSyncCache;
|
||||
NSMutableDictionary *srcUidCache, *dstUidCache, *srcSyncCache, *srcDateCache, *dstSyncCache;
|
||||
NSException *ex;
|
||||
|
||||
unsigned int count, max;
|
||||
|
@ -2098,81 +2108,92 @@ void handle_eas_terminate(int signum)
|
|||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (![sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: srcCollection
|
||||
inContext: context] &&
|
||||
![srcSogoObject isKindOfClass: [NSException class]])
|
||||
if (![srcSogoObject isKindOfClass: [NSException class]])
|
||||
{
|
||||
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: dstCollection
|
||||
inContext: context])
|
||||
newUID = [srcSogoObject globallyUniqueObjectId];
|
||||
dstSogoObject = [[SOGoAppointmentObject alloc] initWithName: [newUID sanitizedServerIdWithType: srcFolderType]
|
||||
inContainer: dstCollection];
|
||||
|
||||
dstObjectRoles = [dstSogoObject aclsForUser: [[context activeUser] login]];
|
||||
srcObjectRoles = [srcSogoObject aclsForUser: [[context activeUser] login]];
|
||||
|
||||
if (([dstObjectRoles containsObject: SOGoRole_ObjectCreator] || [[dstSogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]]) &&
|
||||
([srcObjectRoles containsObject: SOGoRole_ObjectEraser] || [[srcSogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]]))
|
||||
{
|
||||
newUID = [srcSogoObject globallyUniqueObjectId];
|
||||
dstSogoObject = [[SOGoAppointmentObject alloc] initWithName: [newUID sanitizedServerIdWithType: srcFolderType]
|
||||
inContainer: dstCollection];
|
||||
elements = [[srcSogoObject calendar: NO secure: NO] allObjects];
|
||||
max = [elements count];
|
||||
for (count = 0; count < max; count++)
|
||||
[[elements objectAtIndex: count] setUid: newUID];
|
||||
|
||||
ex = [dstSogoObject saveCalendar: [srcSogoObject calendar: NO secure: NO]];
|
||||
if (!ex)
|
||||
{
|
||||
ex = [srcSogoObject delete];
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
|
||||
[dstSyncCache setObject: [dstFolderMetadata objectForKey: @"SyncKey"] forKey: newUID];
|
||||
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", newUID];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
|
||||
// Save dstMessageId in cache - it will help to recover if the request fails before the response can be sent to client
|
||||
[newSuccessfulMoveItemsOps setObject: newUID forKey: srcMessageId];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId])
|
||||
{
|
||||
// Move failed but we can recover the dstMessageId from previous request
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] ];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
[newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId];
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - MoveItem failed due to missing permissions: srcMessageId: %@ dstMessageId: %@", srcMessageId, newUID];
|
||||
|
||||
// Make sure that the entry gets re-added to the source folder.
|
||||
srcSyncCache = [srcFolderMetadata objectForKey: @"SyncCache"];
|
||||
srcDateCache = [srcFolderMetadata objectForKey: @"DateCache"];
|
||||
[srcSyncCache removeObjectForKey: srcMessageId];
|
||||
[srcDateCache removeObjectForKey: srcMessageId];
|
||||
|
||||
// Make sure that the entry gets removed from the destination folder.
|
||||
[dstSyncCache setObject: [dstFolderMetadata objectForKey: @"SyncKey"] forKey: newUID];
|
||||
ex = [dstSogoObject saveCalendar: [srcSogoObject calendar: NO secure: YES]];
|
||||
ex = [dstSogoObject delete];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!ex && ![srcSogoObject isKindOfClass: [NSException class]])
|
||||
{
|
||||
if (([dstObjectRoles containsObject: SOGoRole_ObjectCreator] || [[dstSogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]]) &&
|
||||
([srcObjectRoles containsObject: SOGoRole_ObjectEraser] || [[srcSogoObject ownerInContext: context] isEqualToString: [[context activeUser] login]]))
|
||||
ex = [srcSogoObject delete];
|
||||
else
|
||||
ex = [srcSogoObject touch]; // make sure to include the object in next sync.
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
|
||||
[dstSyncCache setObject: [dstFolderMetadata objectForKey: @"SyncKey"] forKey: newUID];
|
||||
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", newUID];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
|
||||
// Save dstMessageId in cache - it will help to recover if the request fails before the response can be sent to client
|
||||
[newSuccessfulMoveItemsOps setObject: newUID forKey: srcMessageId];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId])
|
||||
{
|
||||
// Move failed but we can recover the dstMessageId from previous request
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] ];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
[newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId];
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1
NEWS
1
NEWS
|
@ -4,6 +4,7 @@
|
|||
New features
|
||||
- [core] support repetitive email alarms on tasks and events (#1053)
|
||||
- [web] allow to hide center column on large screens
|
||||
- [eas] relaxed permission requirements for subscription synchronizations (#3118 and #3180)
|
||||
|
||||
Enhancements
|
||||
- [core] added sha256-crypt and sha512-crypt password support
|
||||
|
|
Loading…
Reference in New Issue