Compare commits

..
Author SHA1 Message Date
Luc Charland 1dd4d870e0 Changed the keyname to EventsSortingState 2015-08-06 12:38:26 -04:00
303 changed files with 5801 additions and 111404 deletions

No files matched your search

+5 -3
View File
@@ -9,7 +9,6 @@
*/*/obj/
*/obj/
.scss-lint-config.yml_
ActiveSync/ActiveSync.SOGo
Documentation/*.docbook
Documentation/*.pdf
SoObjects/SOGo/SOGo.framework/
@@ -21,8 +20,11 @@ UI/WebServerResources/bower_components/
UI/WebServerResources/css/
UI/WebServerResources/css/styles.css
UI/WebServerResources/css/styles.css.map
UI/WebServerResources/js/*.js
UI/WebServerResources/js/*.js.map
UI/WebServerResources/js/Common.js*
UI/WebServerResources/js/Contacts.js*
UI/WebServerResources/js/Mailer.js*
UI/WebServerResources/js/Preferences.js*
UI/WebServerResources/js/Scheduler.js*
UI/WebServerResources/js/vendor/
UI/WebServerResources/node_modules/
UI/WebServerResources/scss/.sass-cache/
+76 -365
View File
@@ -112,30 +112,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher (Sync)
- (void) _setOrUnsetSyncInProgress: (BOOL) set
invalidate: (BOOL) invalidate
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
if (set)
if (invalidate)
[[o properties] setObject: [NSCalendarDate date] forKey: @"InvalidateSyncInProgress"];
else
[[o properties] setObject: [NSCalendarDate date] forKey: @"SyncInProgress"];
else
{
[[o properties] removeObjectForKey: @"SyncInProgress"];
[[o properties] removeObjectForKey: @"InvalidateSyncInProgress"];
}
[o save];
}
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
forKey: (NSString *) theFolderKey
{
@@ -157,7 +133,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[[o properties] addEntriesFromDictionary: values];
[o save];
@@ -184,7 +159,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -195,7 +170,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
nameInCache = [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
}
return nameInCache;
@@ -429,6 +404,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncEventFolder:
@@ -439,13 +415,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncMailFolder:
default:
{
NSDictionary *result;
NSNumber *modseq;
NSString *modseq;
[sogoObject takeActiveSyncValues: allChanges inContext: context];
@@ -453,11 +430,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
if (modseq)
[syncCache setObject: [modseq stringValue] forKey: serverId];
[syncCache setObject: modseq forKey: serverId];
}
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[theBuffer appendString: @"<Change>"];
@@ -600,43 +577,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFilterType: (NSCalendarDate *) theFilterType
inBuffer: (NSMutableString *) theBuffer
lastServerKey: (NSString **) theLastServerKey
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
BOOL cleanup_needed, more_available;
BOOL more_available;
int i, max;
s = [NSMutableString string];
cleanup_needed = more_available = NO;
more_available = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
// If this is a new sync operation, DateCache and SyncCache need to be deleted
// If this is a new sync operation, DateCache and SyncCache needs to be deleted
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
{
// The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response.
// We need to cleanup this mess.
[self logWithFormat: @"Cache cleanup needed for device %@ - user: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login]];
cleanup_needed = YES;
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
(cleanup_needed ||
( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([folderMetadata objectForKey: @"InitialLoadSequence"]))) &&
theFilterType
)
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
theFilterType)
{
NSArray *allKeys;
NSString *key;
@@ -652,36 +622,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
{
if ([syncCache objectForKey:key])
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete %@", key];
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
[syncCache removeObjectForKey: key];
//[dateCache removeObjectForKey: key];
[syncCache removeObjectForKey: key];
[dateCache removeObjectForKey: key];
softdelete_count++;
}
else if (cleanup_needed)
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete cleanup %@", key];
// With this we make sure that a SoftDelete is set again on next sync.
[syncCache setObject: @"0" forKey: key];
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete final delete %@", key];
// Now we are save to remove the dateCache entry.
[dateCache removeObjectForKey: key];
}
softdelete_count++;
}
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
@@ -708,6 +656,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
return;
more_available = NO;
davCollectionTagToStore = [theCollection davCollectionTag];
@@ -723,7 +673,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
BOOL updated, initialLoadInProgress;
BOOL updated;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -733,24 +683,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
initialLoadInProgress = NO;
if ([theSyncKey isEqualToString: @"-1"])
[folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if ([theSyncKey intValue] < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allComponents = [theCollection syncTokenFieldsWithProperties: nil
matchingSyncToken: theSyncKey
fromDate: theFilterType
initialLoad: initialLoadInProgress];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObject: [[[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending: YES] autorelease]]];
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
// Check for the WindowSize
@@ -913,39 +847,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSArray *allMessages, *a;
NSString *lastSequence, *firstUidAdded;
NSArray *allMessages;
int j, k, return_count, highestmodseq;
BOOL found_in_cache, initialLoadInProgress;
int j, k, return_count;
BOOL found_in_cache;
initialLoadInProgress = NO;
found_in_cache = NO;
lastSequence = nil;
firstUidAdded = nil;
if ([theSyncKey isEqualToString: @"-1"])
{
highestmodseq = 0;
a = [[theCollection davCollectionTag] componentsSeparatedByString: @"-"];
[folderMetadata setObject: [a objectAtIndex: 1] forKey: @"InitialLoadSequence"];
}
else
{
a = [theSyncKey componentsSeparatedByString: @"-"];
highestmodseq = [[a objectAtIndex: 1] intValue];
}
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if (highestmodseq < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType initialLoad: initialLoadInProgress];
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -953,7 +860,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (i = 0; i < max; i++)
{
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
}
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
@@ -962,82 +869,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
if (debugOn)
{
[self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]];
[self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]];
}
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
lastCacheObject = [sortedBySequence lastObject];
//
// Cleanup the mess
//
if (cleanup_needed)
{
NSMutableArray *sortedByUID;
int uidnextFromCache;
sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache];
[sortedByUID sortUsingSelector: @selector(compareUID:)];
// Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request.
a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"];
uidnextFromCache = [[a objectAtIndex: 0] intValue];
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]];
// Remove all entries from cache beginning with the first uid added by the last sync request.
for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++)
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: ADD %d", j];
[syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
[dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
}
RELEASE(sortedByUID);
for (j = 0; j < [allCacheObjects count]; j++)
{
// Update the modseq in cache, sence othersie, it would be identical to the modseq from server
//and we would skip the cache when generating the response.
if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]];
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]];
// For deletes we have to recreate a cache entry to have the <Delete> included in the response.
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
}
}
if (!cleanup_needed &&
[folderMetadata objectForKey: @"MoreAvailable"] &&
lastCacheObject &&
!([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup.
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
{
for (j = 0; j < [allCacheObjects count]; j++)
{
if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) ||
([[allCacheObjects objectAtIndex: j] sequence] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]))
{
// We need to continue with adds or deletes from here.
found_in_cache = YES;
j--;
break;
}
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
{
// Found out where we're at, let's continue from there...
@@ -1052,10 +892,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (found_in_cache)
k = j+1;
else
j = k = 0;
if (debugOn)
[self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k];
{
k = 0;
j = 0;
}
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
return_count = 0;
@@ -1066,38 +908,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check for the WindowSize and slice accordingly
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
{
NSString *lastSequence;
more_available = YES;
if (!firstUidAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
firstUidAdded = [a objectAtIndex: 0];
RETAIN(firstUidAdded);
}
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
//RETAIN(lastSequence);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, lastSequence];
if (debugOn)
[self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey];
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
DESTROY(pool);
break;
}
aCacheObject = [allCacheObjects objectAtIndex: k];
if (debugOn)
[self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject];
// If found in cache, it's either a Change or a Delete operation.
// If found in cache, it's either a Change or a Delete
if ([syncCache objectForKey: [aCacheObject uid]])
{
if ([[aCacheObject sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - DELETE!"];
// Deleted
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
@@ -1115,12 +942,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
{
if (debugOn)
[self logWithFormat: @"EAS - CHANGE!"];
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
@@ -1136,9 +960,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - ADD!"];
// Added
if (![[aCacheObject sequence] isEqual: [NSNull null]])
{
@@ -1171,22 +992,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
// Save the frist UID we add. We will use it for the synckey late.
if (!firstUidAdded)
{
firstUidAdded = [aCacheObject uid];
RETAIN(firstUidAdded);
if (debugOn)
[self logWithFormat: @"EAS - first uid added %@", firstUidAdded];
}
return_count++;
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]];
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
}
}
@@ -1195,27 +1005,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (more_available)
{
//[folderMetadata setObject: lastSequence forKey: @"MoreAvailable"];
[folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
//RELEASE(lastSequence);
}
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
if (firstUidAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
[folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"];
}
else
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
RELEASE(firstUidAdded);
RELEASE(*theLastServerKey);
} // default:
@@ -1314,15 +1113,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
changeDetected: (BOOL *) changeDetected
maxSyncResponseSize: (int) theMaxSyncResponseSize
{
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey;
NSMutableDictionary *folderMetadata, *folderOptions;
NSMutableString *changeBuffer, *commandsBuffer;
id collection, value;
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
SOGoMicrosoftActiveSyncFolderType folderType;
unsigned int windowSize, v, status;
id collection, value;
NSMutableString *changeBuffer, *commandsBuffer;
BOOL getChanges, first_sync;
unsigned int windowSize, v, status;
NSMutableDictionary *folderMetadata, *folderOptions;
changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string];
@@ -1345,15 +1144,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[theBuffer appendString: @"</Collection>"];
return;
}
//
// First check if we have any concurrent Sync requests going on for this device.
// If we do and we are still within our maximumSyncInterval, we let our EAS
// device know to retry.
//
folderKey = [self _getNameInCache: collection withType: folderType];
folderMetadata = [self _folderMetadataForKey: folderKey];
// We check for a window size, default to 100 if not specfied or out of bounds
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
@@ -1378,6 +1169,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
if ([syncKey isEqualToString: @"0"])
{
davCollectionTag = @"-1";
@@ -1432,7 +1225,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
[self _setFolderMetadata: folderMetadata forKey: folderKey];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
}
}
@@ -1478,7 +1271,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
lastServerKey: &lastServerKey];
}
folderMetadata = [self _folderMetadataForKey: folderKey];
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
// If we got any changes or if we have applied any commands
// let's regenerate our SyncKey based on the collection tag.
@@ -1640,15 +1433,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSArray *allCollections;
NSData *d;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
BOOL changeDetected;
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
// We initialize our output buffer
output = [[NSMutableString alloc] init];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
defaultInterval = [defaults maximumSyncInterval];
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
@@ -1663,53 +1457,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[output appendString: @"</Sync>"];
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
RELEASE(output);
return;
}
if ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"])
{
NSCalendarDate *syncStartDate;
// An other sync is going on right now. Lets break it and wait
// until the other process clears it up.
[self _setOrUnsetSyncInProgress: YES invalidate: YES];
total_sleep = 0;
syncStartDate = [[self globalMetadataForDevice] objectForKey: @"SyncInProgress"];
while ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"])
{
// Don't go into a heartbeat loop.
heartbeatInterval = 0;
// We waited too long. Return a fatal error to the client.
if (abs([syncStartDate timeIntervalSinceNow]) > defaultInterval)
{
if (debugOn)
[self logWithFormat: @"EAS - We waited too long. syncStartDate: %@ %@", syncStartDate, [self globalMetadataForDevice]];
[theResponse setStatus: 503];
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
RELEASE(output);
return;
}
if (debugOn)
[self logWithFormat: @"EAS - globalMetadataForDevice %@", [self globalMetadataForDevice]];
[self logWithFormat: @"Sync in progress for device %@ (login: %@). Lock will expire in %d seconds", [context objectForKey: @"DeviceId"], [[context activeUser] login], defaultInterval-total_sleep];
sleep(5);
total_sleep += 5;
}
}
// No lock or broke the existing one, lets grab it and proceed
[self _setOrUnsetSyncInProgress: YES invalidate: NO];
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
defaultInterval = [defaults maximumSyncInterval];
internalInterval = [defaults internalSyncInterval];
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
@@ -1719,16 +1472,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// We check to see if our heartbeat interval falls into the supported ranges.
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
{
int limit;
// Interval is too long, inform the client.
heartbeatInterval = defaultInterval;
// When Status = 14, the Wait interval is specified in minutes while
// defaultInterval is specifed in seconds. Adjust accordinlgy.
limit = defaultInterval/60;
if (limit < 1) limit = 1;
if (limit > 59) limit = 59;
//[output appendFormat: @"<Limit>%d</Limit>", limit];
// Outlook doesn't like this...
//[output appendFormat: @"<Limit>%d</Limit>", defaultInterval];
//[output appendFormat: @"<Status>%d</Status>", 14];
}
@@ -1744,7 +1492,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (j = 0; j < [allCollections count]; j++)
{
aCollection = [allCollections objectAtIndex: j];
[self processSyncCollection: aCollection
inBuffer: s
changeDetected: &changeDetected
@@ -1756,46 +1504,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (changeDetected)
{
// Don't return a response if an other Sync is waiting and this is a heartbeat request.
if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"] && heartbeatInterval > 1)
{
if (debugOn)
[self logWithFormat: @"EAS - Heartbeat stopped - discard response %@", [self globalMetadataForDevice]];
[theResponse setStatus: 503];
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
RELEASE(output);
return;
}
[self logWithFormat: @"Change detected during Sync, we push the content."];
[self logWithFormat: @"Change detected, we push the content."];
break;
}
else if (heartbeatInterval > 1)
{
total_sleep = 0;
while (total_sleep < internalInterval)
{
// We check if we must break the current synchronization since an other Sync
// has just arrived.
if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"])
{
if (debugOn)
[self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]];
// Make sure we end the heardbeat-loop.
heartbeatInterval = internalInterval = 1;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep];
sleep(5);
total_sleep += 5;
}
}
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
}
else
{
@@ -1803,10 +1518,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
//
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0,
// otherwise send an empty response.
//
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
@@ -1824,8 +1537,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Avoid overloading the autorelease pool here, as Sync command can
// generate fairly large responses.
RELEASE(output);
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
}
@end
+1 -6
View File
@@ -1,6 +1,6 @@
/*
Copyright (c) 2014-2015, Inverse inc.
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -32,19 +32,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
@class NSException;
@class NSMutableDictionary;
@class NSURL;
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
NSDictionary *imapFolderGUIDS;
id context;
BOOL debugOn;
}
- (NSMutableDictionary *) globalMetadataForDevice;
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
+22 -37
View File
@@ -32,7 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSLocale.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
@@ -144,20 +143,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher
static BOOL debugOn = NO;
- (id) init
{
[super init];
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
imapFolderGUIDS = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
RELEASE(imapFolderGUIDS);
[super dealloc];
}
@@ -170,16 +169,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties] setObject: theSyncKey
forKey: @"FolderSyncKey"];
[[o properties] removeAllObjects];
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
[o save];
}
- (NSMutableDictionary *) globalMetadataForDevice
- (NSMutableDictionary *) _globalMetadataForDevice
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -203,7 +202,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (theFilter)
{
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -230,27 +229,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
if (!imapFolderGUIDS)
{
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapFolderGUIDS = [accountFolder imapFolderGUIDs];
[imapFolderGUIDS retain];
}
return [[[imapFolderGUIDS allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapGUIDs = [accountFolder imapFolderGUIDs];
//return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
return [[[imapGUIDs allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
}
return theIdToTranslate;
}
//
//
//
@@ -715,7 +710,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
BOOL first_sync;
sm = [SoSecurityManager sharedSecurityManager];
metadata = [self globalMetadataForDevice];
metadata = [self _globalMetadataForDevice];
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
s = [NSMutableString string];
@@ -943,7 +938,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -1029,7 +1023,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1053,7 +1046,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1212,7 +1204,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter initialLoad: NO];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
count = [allMessages count];
@@ -2032,13 +2024,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([foldersWithChanges count])
{
[self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."];
[self logWithFormat: @"Change detected, we push the content."];
status = 2;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval];
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
}
}
@@ -2627,11 +2619,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
// If there is no References: header, initialize it with In-Reply-To.
if ([mailObject inReplyTo] && ![references count])
references = [NSMutableArray arrayWithObject: [mailObject inReplyTo]];
if ([references count] > 0)
{
// If there are more than ten identifiers listed, we eliminate the second one.
@@ -2948,9 +2935,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
NSString *value;
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
locale: [[[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] autorelease]];
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]];
}
else
+2 -8
View File
@@ -541,7 +541,7 @@ struct GlobalObjectId {
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
if (encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@@ -1071,15 +1071,9 @@ struct GlobalObjectId {
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
{
id value;
NSString *reference;
value = [[self mailHeaders] objectForKey: @"references"];
if ([value isKindOfClass: [NSArray class]])
reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0];
else
reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0];
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
if ([reference length] > 0)
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
-1
View File
@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoSyncCacheObject : NSObject
{
@public
id _uid;
id _sequence;
}
+13 -22
View File
@@ -32,17 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
static Class NSNullK;
@implementation SOGoSyncCacheObject
+ (void) initialize
{
NSNullK = [NSNull class];
}
- (id) init
{
if ((self = [super init]))
@@ -54,14 +46,14 @@ static Class NSNullK;
return self;
}
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
{
id o;
o = [[self alloc] init];
[o setUID: [NSNumber numberWithInt: [theUID intValue]]];
[o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
[o setUID: theUID];
[o setSequence: theSequence];
return [o autorelease];
}
@@ -75,7 +67,7 @@ static Class NSNullK;
- (id) uid
{
return [_uid description];
return _uid;
}
- (void) setUID: (id) theUID
@@ -85,7 +77,7 @@ static Class NSNullK;
- (id) sequence
{
return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
return _sequence;
}
- (void) setSequence: (id) theSequence
@@ -96,7 +88,7 @@ static Class NSNullK;
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
{
return [self->_uid compare: theSyncCacheObject->_uid];
return [[self uid] compare: [theSyncCacheObject uid]];
}
//
@@ -105,21 +97,21 @@ static Class NSNullK;
//
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
{
if ([self->_sequence isEqual: [NSNull null]] &&
[theSyncCacheObject->_sequence isEqual: [NSNull null]])
if ([[self sequence] isEqual: [NSNull null]] &&
[[theSyncCacheObject sequence] isEqual: [NSNull null]])
return [self compareUID: theSyncCacheObject];
if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedDescending;
if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedAscending;
// Must check this here, to avoid comparing NSNull objects
if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
return [self compareUID: theSyncCacheObject];
return [self->_sequence compare: theSyncCacheObject->_sequence];
return [[self sequence] compare: [theSyncCacheObject sequence]];
}
- (NSString *) description
@@ -128,4 +120,3 @@ static Class NSNullK;
}
@end
+393 -13095
View File
File diff suppressed because it is too large. Load diff
@@ -233,7 +233,6 @@ Installation
This section will guide you through the installation of the native
Microsoft Outlook compatibility layer SOGo offers.
////
Red Hat Enterprise Linux v6 x86_64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -317,7 +316,7 @@ from this guide.
On Ubuntu 12.04, the Samba init scripts need to be modified to
disable the upstart check. For more details, refer to:
https://wiki.samba.org/index.php/Samba4/InitScript
////
Debian 8 (Jessie) and Ubuntu 14.04 (Trusty Tahr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -396,7 +395,7 @@ samba-tool user setexpiry administrator --noexpiry
----
You might consider changing the realm and domain used, to suit your
environment.
enviroment.
You might also have to
remove `/etc/samba/smb.conf` prior running this command.
@@ -442,7 +441,7 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
@@ -513,11 +512,9 @@ mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/op
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
----
////
On RHEL, make sure SELinux is disabled:
setenforce 0
////
Next, you can start Samba using the usual command:
@@ -548,11 +545,9 @@ This service runs as a WSGI application under apache (mod_wsgi).
While HTTPS is not required to access this service, it is strongly
recommended.
////
On RHEL-based distributions, the apache configuration required by these
services can be found in `/etc/httpd/conf.d/ocsmanager.conf` and
`/etc/httpd/conf.d/rpcproxy.conf`.
////
For Debian-based distributions, these files can be found
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
@@ -568,10 +563,8 @@ The OCS Manager and RPC Proxy configuration module can be enabled using:
a2enconf ocsmanager
a2enconf rpcproxy
////
On RHEL-based distributions, make sure the `LoadModule` directive is
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
////
The _reqtimeout_ apache module is known to cause problems when using the
default configuration shipped with Debian-based systems. On such
@@ -587,12 +580,10 @@ To avoid this problem, use a much higher timeout or disable the module:
a2dismod reqtimeout
You should now restart the Apache service and make sure it will start on
boot.
////
On RHEL-based distributions, do:
boot. On RHEL-based distributions, do:
chkconfig httpd on && /etc/init.d/httpd restart
////
On Debian-based distributions, do:
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
@@ -822,7 +813,7 @@ the `openchange_user_cleanup` script distributed with SOGo. The script
can be found in `/usr/share/doc/sogo/` (`/usr/share/sogo-VERSION/` on
RHEL).
To reset a user, run the script as root:
`openchange_user_cleanup username`. See the usage output for additional options.
`python openchange_user_cleanup username`. See the usage output for additional options.
* The "Out of Office Assistant" will not currently work. This feature
has not been implemented.
* Creating folders below INBOX (when not normally permitted by the IMAP
+5 -33
View File
@@ -1,39 +1,12 @@
2.3.3 (2015-mm-dd)
2.3.x (2015-MM-DD)
------------------
New features
Enhancements
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
Bug fixes
- numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849)
- correctly handle the References header over EAS (#3365)
- make sure English is always used when generating Date headers using EAS (#3356)
2.3.2 (2015-09-16)
------------------
Enhancements
- improved EAS speed and memory usage, avoiding many IMAP LIST commands (#3294)
- improved EAS speed during initial syncing of large mailboxes (#3293)
- updated CKEditor to version 4.5.3
Bug fixes
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
- non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- fixed several sync issues on environments with multiple users (Zentyal)
- folders from other users will no longer appear on your Outlook (Zentyal)
- use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
- session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
- less sync issues when setting read flag (Zentyal)
- attachments with non-latin filenames sent by Outlook are now received (Zentyal)
- support attachments from more mail clients (Zentyal)
- avoid conflicting message on saving a draft mail (Zentyal)
- less conflicting messages in Outlook while moving messages between folders (Zentyal)
- start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344)
- fixed localization of calendar categories with empty profile (#3295)
- fixed options availability in contextual menu of Contacts module (#3342)
- Non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- Fixed several sync issues on environments with multiple users (Zentyal)
- Folders from other users will no longer appear on your Outlook (Zentyal)
- Use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
2.3.1 (2015-07-23)
------------------
@@ -46,7 +19,6 @@ Enhancements
- added create-folder subcommand to sogo-tool to create contact and calendar folders
- group mail addresses can be used as recipient in Outlook
- added 'ActiveSync' module constraints
- updated CKEditor to version 4.5.1
- added Slovenian translation - thanks to Jens Riecken
- added Chinese (Taiwan) translation
+1 -1
View File
@@ -671,7 +671,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
return newAttachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
return MAPISTORE_SUCCESS;
}
-136
View File
@@ -22,7 +22,6 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -35,7 +34,6 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreTypes.h"
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -106,105 +104,6 @@
return objectVersion;
}
- (void) _updatePredecessorChangeList
{
BOOL updated;
enum mapistore_error rc;
NSData *currentChangeList, *changeKey;
NSMutableArray *changeKeys;
NSMutableData *newChangeList;
NSUInteger count, len;
struct SizedXid *changes;
struct SPropValue property;
struct SRow aRow;
struct XID *currentChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t nChanges;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
changeKey = [self getReplicaKeyFromGlobCnt: [self objectVersion]];
currentChangeList = [properties objectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if (!currentChangeList)
{
/* Create a new PredecessorChangeList */
len = [changeKey length];
newChangeList = [NSMutableData dataWithCapacity: len + 1];
[newChangeList appendUInt8: len];
[newChangeList appendData: changeKey];
}
else
{
/* Update current predecessor change list with new change key */
changes = [currentChangeList asSizedXidArrayInMemCtx: localMemCtx
with: &nChanges];
updated = NO;
currentChangeKey = [changeKey asXIDInMemCtx: localMemCtx];
for (count = 0; count < nChanges && !updated; count++)
{
if (GUID_equal(&changes[count].XID.NameSpaceGuid, &currentChangeKey->NameSpaceGuid))
{
NSData *globCnt, *oldGlobCnt;
oldGlobCnt = [NSData dataWithBytes: changes[count].XID.LocalId.data length: changes[count].XID.LocalId.length];
globCnt = [NSData dataWithBytes: currentChangeKey->LocalId.data length: currentChangeKey->LocalId.length];
if ([globCnt compare: oldGlobCnt] == NSOrderedDescending)
{
if ([globCnt length] != [oldGlobCnt length])
{
[self errorWithFormat: @"Cannot compare globcnt with different length: %@ and %@", globCnt, oldGlobCnt];
abort();
}
memcpy (changes[count].XID.LocalId.data, currentChangeKey->LocalId.data, currentChangeKey->LocalId.length);
updated = YES;
}
}
}
/* Serialise it */
changeKeys = [NSMutableArray array];
if (!updated)
[changeKeys addObject: changeKey];
for (count = 0; count < nChanges; count++)
{
changeKey = [NSData dataWithXID: &changes[count].XID];
[changeKeys addObject: changeKey];
}
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare context: localMemCtx];
newChangeList = [NSMutableData data];
len = [changeKeys count];
for (count = 0; count < len; count++)
{
changeKey = [changeKeys objectAtIndex: count];
[newChangeList appendUInt8: [changeKey length]];
[newChangeList appendData: changeKey];
}
}
if ([newChangeList length] > 0)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *[newChangeList asBinaryInMemCtx: localMemCtx];
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [self addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
[self errorWithFormat: @"Impossible to add a new predecessor change list: %d", rc];
}
talloc_free (localMemCtx);
}
//
// FIXME: how this can happen?
//
@@ -267,9 +166,6 @@
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
forKey: @"version"];
/* Update PredecessorChangeList accordingly */
[self _updatePredecessorChangeList];
[self logWithFormat: @"%d props in dict", [properties count]];
[sogoObject save];
@@ -313,36 +209,4 @@
return [sogoObject lastModified];
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
/* Modify PidTagMessageFlags from SetMessageReadFlag and
SyncImportReadStateChanges ROPs */
NSNumber *flags;
uint32_t newFlag;
flags = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
if (flags)
{
newFlag = [flags unsignedLongValue];
if (flag & SUPPRESS_RECEIPT)
newFlag |= MSGFLAG_READ;
if (flag & CLEAR_RN_PENDING)
newFlag &= ~MSGFLAG_RN_PENDING;
if (flag & CLEAR_READ_FLAG)
newFlag &= ~MSGFLAG_READ;
if (flag & CLEAR_NRN_PENDING)
newFlag &= ~MSGFLAG_NRN_PENDING;
}
else
{
newFlag = MSGFLAG_READ;
if (flag & CLEAR_READ_FLAG)
newFlag = 0x0;
}
[properties setObject: [NSNumber numberWithUnsignedLong: newFlag]
forKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
return MAPISTORE_SUCCESS;
}
@end
-1
View File
@@ -121,7 +121,6 @@
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) want_copy
inMemCtx: (TALLOC_CTX *) memCtx;
+8 -20
View File
@@ -642,7 +642,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
andPredecessorChangeList: (struct Binary_r *) targetPredecessorChangeList
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -670,18 +669,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetPredecessorChangeList)
if (targetChangeKey)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *targetPredecessorChangeList;
property.ulPropTag = PidTagChangeKey;
property.value.bin = *targetChangeKey;
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [destMsg addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
{
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
goto end;
}
goto end;
}
[destMsg save: memCtx];
if (!wantCopy)
@@ -700,7 +696,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -710,7 +705,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSString *oldMessageURL;
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
@@ -731,21 +726,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (oldMessageURL)
{
[oldMessageURLs addObject: oldMessageURL];
if (targetChangeKeys && targetPredecessorChangeList)
{
targetChangeKey = targetChangeKeys[count];
targetPredecessorChangeList = targetPredecessorChangeLists[count];
}
if (targetChangeKeys)
targetChangeKey = targetChangeKeys[count];
else
{
targetChangeKey = NULL;
targetPredecessorChangeList = NULL;
}
targetChangeKey = NULL;
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
andPredecessorChangeList: targetPredecessorChangeList
wantCopy: wantCopy
inMemCtx: memCtx];
}
+1 -2
View File
@@ -42,8 +42,7 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;
withChangeKey: (NSData *) newChangeKey;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
+18 -85
View File
@@ -75,7 +75,6 @@ static Class NSNumberK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (void) dealloc
@@ -262,6 +261,7 @@ static Class NSNumberK;
*/
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
inChangeListOnly: (BOOL) inChangeListOnly
{
struct XID *xid;
NSString *guid;
@@ -270,15 +270,19 @@ static Class NSNumberK;
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
if (!inChangeListOnly)
{
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
}
/* 2. append/update predecessor change list */
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
@@ -292,77 +296,6 @@ static Class NSNumberK;
[changeList setObject: globCnt forKey: guid];
}
- (void) _updatePredecessorChangeList: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
withOldChangeKey: (NSData *) oldChangeKey
{
NSData *globCnt, *oldGlobCnt;
NSDictionary *changeKeyDict;
NSString *guid;
NSMutableDictionary *changeList;
struct SizedXid *sizedXIDList;
struct XID xid, *givenChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t i, length;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
if (predecessorChangeList)
{
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: localMemCtx with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
}
}
if (oldChangeKey)
{
givenChangeKey = [oldChangeKey asXIDInMemCtx: localMemCtx];
if (givenChangeKey) {
guid = [NSString stringWithGUID: &givenChangeKey->NameSpaceGuid];
globCnt = [NSData dataWithBytes: givenChangeKey->LocalId.data length: givenChangeKey->LocalId.length];
changeKeyDict = [messageEntry objectForKey: @"ChangeKey"];
if (!changeKeyDict ||
([guid isEqualToString: [changeKeyDict objectForKey: @"GUID"]]
&& ([globCnt compare: [changeKeyDict objectForKey: @"LocalId"]] == NSOrderedDescending)))
{
/* The given change key is greater than current one stored in
metadata or it does not exist */
[messageEntry setObject: [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil]
forKey: @"ChangeKey"];
}
}
}
talloc_free (localMemCtx);
}
- (EOQualifier *) componentQualifier
{
if (!componentQualifier)
@@ -532,7 +465,8 @@ static Class NSNumberK;
// A GLOBCNT structure is a 6-byte global namespace counter,
// we strip the first 2 bytes. The first two bytes is the ReplicaId
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[self _setChangeKey: changeKey forMessageEntry: messageEntry
inChangeListOnly: NO];
}
now = [NSCalendarDate date];
@@ -549,13 +483,12 @@ static Class NSNumberK;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl
withChangeKey: (NSData *) newChangeKey
{
NSMutableDictionary *messages, *messageEntry;
[self synchroniseCache];
if (oldChangeKey || pcl)
if (newChangeKey)
{
messages = [[versionsMessage properties] objectForKey: @"Messages"];
messageEntry = [messages objectForKey: messageKey];
@@ -563,8 +496,8 @@ static Class NSNumberK;
[NSException raise: @"MAPIStoreIOException"
format: @"no version record found for message '%@'",
messageKey];
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
withOldChangeKey: oldChangeKey];
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
inChangeListOnly: YES];
[versionsMessage save];
}
}
+3 -6
View File
@@ -209,16 +209,13 @@
- (void) updateVersions
{
/* Update ChangeKey and PredecessorChangeList on message's save */
NSData *newChangeKey, *predecessorChangeList;
NSData *newChangeKey;
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
[(MAPIStoreGCSFolder *) container
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey
andPredecessorChangeList: predecessorChangeList];
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey];
}
@end
+15 -4
View File
@@ -30,7 +30,6 @@
#import <SOGo/NSArray+Utilities.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreMailMessage.h"
@@ -109,7 +108,7 @@
static char recordBytes[] = {0xd9, 0xd8, 0x11, 0xa3, 0xe2, 0x90, 0x18, 0x41,
0x9e, 0x04, 0x58, 0x46, 0x9d, 0x6d, 0x1b,
0x68};
*data = [[NSData dataWithBytes: recordBytes length: 16]
asBinaryInMemCtx: memCtx];
@@ -118,7 +117,19 @@
- (NSString *) _fileName
{
return [bodyInfo filename];
NSString *fileName;
NSDictionary *parameters;
fileName = [[bodyInfo objectForKey: @"parameterList"]
objectForKey: @"name"];
if (!fileName)
{
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
fileName = [parameters objectForKey: @"filename"];
}
return fileName;
}
- (int) getPidTagAttachLongFilename: (void **) data
@@ -167,7 +178,7 @@
- (int) getPidTagAttachContentId: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
{
*data = [[bodyInfo objectForKey: @"bodyId"]
asUnicodeInMemCtx: memCtx];
-2
View File
@@ -52,8 +52,6 @@
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid;
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey;
+10 -98
View File
@@ -68,8 +68,6 @@
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
#include <gen_ndr/exchange.h>
#undef DEBUG
#include <util/attr.h>
#include <libmapi/libmapi.h>
@@ -111,7 +109,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (BOOL) ensureFolderExists
@@ -519,44 +516,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return [modseq1 compare: modseq2];
}
- (void) _updatePredecessorChangeListWith: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
{
NSData *globCnt, *oldGlobCnt;
NSMutableDictionary *changeList;
NSString *guid;
struct SizedXid *sizedXIDList;
struct XID xid;
uint32_t i, length;
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: NULL with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
talloc_free (sizedXIDList);
}
[versionsMessage save];
}
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
{
@@ -567,8 +526,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
@@ -965,7 +924,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return changeNumber;
}
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
NSMutableDictionary *messages, *messageEntry;
NSString *messageUid;
@@ -976,7 +936,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
messageEntry = [messages objectForKey: messageUid];
if (!messageEntry)
{
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
[self warnWithFormat: @"attempting to synchronise to set the change key for "
@"this message %@", messageKey];
synced = [self synchroniseCacheForUID: messageUid];
if (synced)
@@ -987,57 +947,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
abort ();
}
}
return messageEntry;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
[self _setChangeKey: changeKey
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[versionsMessage save];
}
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
/* Update predecessor change list property given the change key. It
returns if the change key has been added to the list or not */
BOOL added = NO;
NSData *globCnt, *oldGlobCnt;
NSDictionary *messageEntry;
NSMutableDictionary *changeList;
NSString *guid;
struct XID *xid;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
messageEntry = [self _messageEntryFromMessageKey: messageKey];
if (messageEntry)
{
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (changeList)
{
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
{
[changeList setObject: globCnt forKey: guid];
[versionsMessage save];
added = YES;
}
}
else
[self errorWithFormat: @"Missing predecessor change list to update"];
}
return added;
}
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
{
NSDictionary *messages, *changeKeyDict;
@@ -1303,7 +1217,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
@@ -1318,13 +1231,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
NSDictionary *result;
NSUInteger count;
NSArray *a;
NSData *changeList;
NSData *changeKey;
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys
andPredecessorChangeLists: targetPredecessorChangeLists
wantCopy: wantCopy
inMemCtx: memCtx];
@@ -1413,11 +1325,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[self synchroniseCache];
for (count = 0; count < midCount; count++)
{
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
messageKey = [NSString stringWithFormat: @"%@.eml",
[destUIDs objectAtIndex: count]];
[self _updatePredecessorChangeListWith: changeList
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+26 -38
View File
@@ -40,7 +40,6 @@
#import <Mailer/NSData+Mail.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "Codepages.h"
#import "NSData+MAPIStore.h"
@@ -197,7 +196,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
count1 = [keys indexOfObject: data1];
data2 = [entry2 objectForKey: @"mimeType"];
count2 = [keys indexOfObject: data2];
if (count1 == count2)
{
data1 = [entry1 objectForKey: @"key"];
@@ -530,7 +529,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
else
stringValue = @"";
*data = [stringValue asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -625,7 +624,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v = 0;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
@@ -637,7 +636,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([[self attachmentKeys]
count] > 0)
v |= MSGFLAG_HASATTACH;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -657,7 +656,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
v = 2;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -669,15 +668,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
if ([flags containsObject: @"flagged"])
v = 6;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -756,7 +755,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([ngAddress isKindOfClass: [NGMailAddress class]])
{
cn = [ngAddress displayName];
// If we don't have a displayName, we use the email address instead. This
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
// nor in the recipient field if we reply to the email.
@@ -810,7 +809,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
entryId = MAPIStoreExternalEntryId (cn, email);
*data = [entryId asBinaryInMemCtx: memCtx];
rc = MAPISTORE_SUCCESS;
}
else
@@ -967,15 +966,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
{
uint32_t v;
NSString *s;
s = [[sogoObject mailHeaders] objectForKey: @"x-priority"];
v = 0x1;
if ([s hasPrefix: @"1"]) v = 0x2;
else if ([s hasPrefix: @"2"]) v = 0x2;
else if ([s hasPrefix: @"4"]) v = 0x0;
else if ([s hasPrefix: @"5"]) v = 0x0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -1164,14 +1163,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if (!headerSetup)
[self _fetchHeaderData];
if ([headerMimeType isEqualToString: @"text/plain"])
format = EDITOR_FORMAT_PLAINTEXT;
else if ([headerMimeType isEqualToString: @"text/html"])
format = EDITOR_FORMAT_HTML;
else
format = 0; /* EDITOR_FORMAT_DONTKNOW */
*data = MAPILongValue (memCtx, format);
return MAPISTORE_SUCCESS;
@@ -1518,7 +1517,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
p = 0;
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
p++;
@@ -1526,7 +1525,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
recipient->data[p] = MAPILongValue (msgData, 0);
p++;
// PR_7BIT_DISPLAY_NAME_UNICODE
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
p++;
@@ -1534,7 +1533,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_SMTP_ADDRESS_UNICODE
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
p++;
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
p++;
@@ -1567,9 +1566,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
withPrefix: (NSString *) keyPrefix
{
NSArray *parts;
NSDictionary *parameters;
NSUInteger count, max;
if ([[bodyInfo filename] length] > 0)
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
if ([[parameters objectForKey: @"filename"] length] > 0)
{
if ([keyPrefix length] == 0)
keyPrefix = @"0";
@@ -1643,29 +1645,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return attachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
BOOL modified = NO;
BOOL alreadyRead = NO;
NSString *imapFlag = @"\\Seen";
alreadyRead = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"]
containsObject: @"seen"];
/* TODO: notifications should probably be emitted from here */
if (flag & CLEAR_READ_FLAG)
{
[sogoObject removeFlags: imapFlag];
modified = alreadyRead;
}
[sogoObject removeFlags: imapFlag];
else
{
[sogoObject addFlags: imapFlag];
modified = !alreadyRead;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
[sogoObject addFlags: imapFlag];
return MAPISTORE_SUCCESS;
}
@@ -1706,7 +1694,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return nil;
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
NSNumber *value;
+7 -22
View File
@@ -161,30 +161,15 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
{
if (res->relop == RELOP_GT)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
}
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
if (res->relop == RELOP_GT || res->relop == RELOP_GE)
{
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
else
{
/* Ignore other operations as IMAP only support MODSEQ >= X */
[self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=",
[self operatorFromRestrictionOperator: res->relop]];
rc = MAPIRestrictionStateAlwaysTrue;
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
break;
+11 -25
View File
@@ -34,7 +34,6 @@
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSObject+Values.h>
#import <NGExtensions/NSString+Encoding.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
@@ -286,7 +285,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
version = [properties objectForKey: @"version"];
return (version
? [version unsignedLongLongValue]
? exchange_globcnt ([version unsignedLongLongValue])
: ULLONG_MAX);
}
@@ -1111,8 +1110,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL updatedMetadata;
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
NSString *folderName, *flag, *newIdString, *messageKey;
NSData *changeKey, *messageData;
NGImap4Connection *connection;
NGImap4Client *client;
@@ -1148,33 +1146,21 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
[sogoObject setNameInContainer: messageKey];
[mapping registerURL: [self url] withID: mid];
/* synchronise the cache and update the predecessor change list
with the change key provided by the client. Before doing
this, lets issue a unselect/select combo because of timing
issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if
we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)"
command (where XYZ is the HIGHESTMODSEQ+1) immediately after
IMAP APPEND */
/* synchronise the cache and update the change key with the one provided
by the client. Before doing this, lets issue a unselect/select combo
because of timing issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if we do
a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where
XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */
[client unselect];
[client select: folderName];
[(MAPIStoreMailFolder *) container synchroniseCache];
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
if (changeKey)
{
updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey
forMessageWithKey: messageKey];
if (!updatedMetadata)
[self warnWithFormat: @"Predecessor change list not updated with client data"];
}
/* Update version property (PR_CHANGE_KEY indeed) as it is
requested once it is saved */
changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString];
if (changeNumber)
[properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16]
forKey: @"version"];
[(MAPIStoreMailFolder *) container
setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
withAID: (uint32_t) aid;
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
andRowCount: (uint32_t *) countPtr;
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
- (int) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
- (NSArray *) activeContainerMessageTables;
+2 -3
View File
@@ -549,12 +549,11 @@ rtf2html (NSData *compressedRTF)
}
[self save: memCtx];
/* We make sure that any change-related properties are removed from the
/* We make sure that any change-related properties are removes from the
properties dictionary, to make sure that related methods will be
invoked the next time they are requested. */
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if ([container isKindOfClass: MAPIStoreFolderK])
{
@@ -919,7 +918,7 @@ rtf2html (NSData *compressedRTF)
return [self getNo: data inMemCtx: memCtx];;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
// [self subclassResponsibility: _cmd];
+1 -3
View File
@@ -674,7 +674,6 @@ sogo_folder_move_copy_messages(void *folder_object,
uint32_t mid_count,
uint64_t *src_mids, uint64_t *t_mids,
struct Binary_r **target_change_keys,
struct Binary_r **target_predecessor_change_lists,
uint8_t want_copy)
{
MAPIStoreFolder *sourceFolder, *targetFolder;
@@ -699,7 +698,6 @@ sogo_folder_move_copy_messages(void *folder_object,
fromFolder: sourceFolder
withMIDs: t_mids
andChangeKeys: target_change_keys
andPredecessorChangeLists: target_predecessor_change_lists
wantCopy: want_copy
inMemCtx: mem_ctx];
TRYCATCH_END(pool)
@@ -1120,7 +1118,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool;
MAPIStoreMessage *message;
enum mapistore_error rc;
int rc;
if (message_object)
{
+1 -1
View File
@@ -531,7 +531,7 @@
}
[vToDo setTimeStampAsDate: now];
[sogoObject saveCalendar: vCalendar];
[sogoObject saveComponent: vCalendar];
[self updateVersions];
}
-2
View File
@@ -41,8 +41,6 @@
+ (id) dataWithXID: (const struct XID *) xid;
- (struct XID *) asXIDInMemCtx: (void *) memCtx;
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length;
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
+6 -40
View File
@@ -22,7 +22,6 @@
#import <NGExtensions/NSObject+Logs.h>
#import "MAPIStoreTypes.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -30,7 +29,6 @@
#undef DEBUG
#include <stdbool.h>
#include <libmapi/libmapi.h>
#include <talloc.h>
#include <util/time.h>
#include <gen_ndr/exchange.h>
@@ -138,11 +136,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
NSMutableData *xidData;
struct FlatUID_r flatUID;
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
[xidData appendBytes: flatUID.ab length: 16];
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
[xidData appendBytes: xid->Data length: xid->Size];
return xidData;
}
@@ -158,12 +156,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
{
xid = talloc_zero (memCtx, struct XID);
[self _extractGUID: &xid->NameSpaceGuid];
[self _extractGUID: &xid->GUID];
xid->LocalId.length = max - 16;
xid->Size = max - 16;
bytes = (uint8_t *) [self bytes];
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
}
else
{
@@ -174,38 +172,6 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
return xid;
}
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length
{
struct Binary_r bin;
struct SizedXid *sizedXIDArray;
bin.cb = [self length];
bin.lpb = (uint8_t *)[self bytes];
sizedXIDArray = get_SizedXidArray(memCtx, &bin, length);
if (!sizedXIDArray)
{
NSLog (@"Impossible to parse SizedXID array");
return NULL;
}
return sizedXIDArray;
}
- (NSComparisonResult) compare: (NSData *) otherGlobCnt
{
uint64_t globCnt = 0, oGlobCnt = 0;
if ([self length] > 0)
globCnt = *(uint64_t *) [self bytes];
if ([otherGlobCnt length] > 0)
oGlobCnt = *(uint64_t *) [otherGlobCnt bytes];
return MAPICNCompare (globCnt, oGlobCnt, NULL);
}
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
{
+4 -36
View File
@@ -161,11 +161,8 @@
minute: [tzStart minuteOfHour] second: 0
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
tmpDate = [tmpDate addYear: 0
month: ((pos > 0) ? 0 : 1)
day: 0
hour: 0
minute: 0
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
day: 0 hour: 0 minute: 0
second: 0];
/* If the day of the time change is "-XSU", we need to determine whether the
@@ -200,41 +197,12 @@
END:STANDARD
END:VTIMEZONE
The time changes occur on a Sunday, but in March, the 1st is a Sunday itself and in November
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
effectively shift the time change by a whole week.
In Europe/Berlin, we have a different use-case for November. In 2015, November 1st is a Sunday.
The time change in November must occur on October 25th but since tmpDate will be November 1st,
so a Sunday, dateDayOfWeek will be 0 and dayOfWeek will also be 0 we would decrement tmpDate by 14 days,
which is incorrect because it would shift the timezone change one week earlier. We take care about this
one with check if pos is greater or equal than 0 and if so, we don't decrement it.
BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
*/
if (dayOfWeek == dateDayOfWeek && pos >= 0)
if (dayOfWeek == dateDayOfWeek)
pos--;
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
@@ -2988,10 +2988,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
theUser = [SOGoUser userWithLogin: theUID];
aParent = [theUser calendarsFolderInContext: context];
if ([aParent isKindOfClass: [NSException class]])
return nil;
aFolders = [aParent subFolders];
e = [aFolders objectEnumerator];
while ((aFolder = [e nextObject]))
@@ -1493,9 +1493,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// the database. We do this ONLY when using SOGo from the
// Web interface or over ActiveSync.
// Over DAV, it'll be handled directly in PUTAction:
if (![context request] ||
[[context request] handledByDefaultHandler] ||
[[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
if (![context request] || [[context request] handledByDefaultHandler]
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
{
// If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an
// event invitation and also sets an alarm along the way. This would happen ONLY from the web interface.
@@ -84,30 +84,17 @@
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *allEvents, *rules;
NSArray *rules;
NSCalendarDate *untilDate;
NSMutableDictionary *data, *repeat;
NSString *frequency;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
iCalEvent *masterComponent;
iCalRecurrenceRule *rule;
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
if ([self recurrenceId])
{
// If the component is an occurrence of a recurrent component,
// consider the recurrence rules of the master component.
allEvents = [[self parent] events];
masterComponent = [allEvents objectAtIndex: 0];
rules = [masterComponent recurrenceRules];
}
else
{
rules = [self recurrenceRules];
}
rules = [self recurrenceRules];
if ([rules count] > 0)
{
rule = [rules objectAtIndex: 0];
+1 -5
View File
@@ -1,6 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2009-2015 Inverse inc.
* Copyright (C) 2009-2014 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
@@ -31,10 +31,6 @@
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type;
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value;
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
+2 -2
View File
@@ -188,8 +188,8 @@ convention:
}
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value
ofType: (NSString *) type
withValue: (id) value
{
NSArray *allValues;
NSEnumerator *list;
+3 -6
View File
@@ -1,5 +1,6 @@
/*
Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -31,13 +32,9 @@
@class NSString;
@interface SOGoContactGCSFolder : SOGoGCSFolder <SOGoContactFolder>
{
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
- (NSString *) cardDavURL;
- (NSString *) publicCardDavURL;
@end
+2 -63
View File
@@ -1,5 +1,6 @@
/*
Copyright (C) 2006-2013-2015 Inverse inc.
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -66,24 +67,6 @@ static NSArray *folderListingFields = nil;
@"c_component", nil];
}
- (id) init
{
if ((self = [super init]))
{
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (Class) objectClassForContent: (NSString *) content
{
CardGroup *cardEntry;
@@ -453,48 +436,4 @@ static NSArray *folderListingFields = nil;
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[self realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[self publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
@end /* SOGoContactGCSFolder */
+4 -4
View File
@@ -97,10 +97,10 @@
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate;
/* flags */
- (NSException *) addFlagsToAllMessages: (id) _f;
+2 -6
View File
@@ -2106,7 +2106,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
// * 5 FETCH (UID 559 MODSEQ (17))
// * 6 FETCH (UID 560 MODSEQ (18))
// * 7 FETCH (UID 561 MODSEQ (19))
//
//
// fetchUIDsOfVanishedItems ..
//
@@ -2119,7 +2119,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
EOQualifier *searchQualifier;
NSMutableArray *allTokens;
@@ -2196,9 +2195,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [fetchResults count]; i++)
{
if ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"] && initialLoadInProgress)
continue;
d = [NSDictionary dictionaryWithObject: ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"]) ? [NSNull null] : [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
@@ -2209,7 +2205,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
if (highestmodseq == 0)
highestmodseq = 1;
if (highestmodseq > 0 && !initialLoadInProgress)
if (highestmodseq > 0)
{
id uid;
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSArray+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006 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
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSArray+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2011 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
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSObject+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007 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
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSObject+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007 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
+2 -8
View File
@@ -373,7 +373,6 @@ static int cssEscapingCount;
- (NSString *) asCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *cssIdentifier;
unichar currentChar;
int count, max, idx;
@@ -382,12 +381,10 @@ static int cssEscapingCount;
[self _setupCSSEscaping];
cssIdentifier = [NSMutableString string];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
max = [self length];
if (max > 0)
{
if ([numericSet characterIsMember: [self characterAtIndex: 0]])
if (isdigit([self characterAtIndex: 0]))
// A CSS identifier can't start with a digit; we add an underscore
[cssIdentifier appendString: @"_"];
for (count = 0; count < max; count++)
@@ -418,7 +415,6 @@ static int cssEscapingCount;
- (NSString *) fromCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *newString;
NSString *currentString;
int count, length, max, idx;
@@ -427,14 +423,12 @@ static int cssEscapingCount;
if (!cssEscapingStrings)
[self _setupCSSEscaping];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
newString = [NSMutableString string];
max = [self length];
count = 0;
if (max > 0
&& [self characterAtIndex: 0] == '_'
&& [numericSet characterIsMember: [self characterAtIndex: 1]])
&& isdigit([self characterAtIndex: 1]))
{
/* If the identifier starts with an underscore followed by a digit,
we remove the underscore */
-2
View File
@@ -56,8 +56,6 @@ typedef enum {
- (void) reloadIfNeeded;
- (void) save;
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache;
/* accessors */
- (NSMutableString *) path; /* full filename */
-11
View File
@@ -103,22 +103,11 @@ static EOAttribute *textColumn = nil;
}
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer
{
return [self objectWithName: key
inContainer: theContainer
useCache: YES];
}
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache
{
SOGoCache *cache;
id o;
cache = [SOGoCache sharedCache];
if (!useCache)
[cache unregisterObjectWithName: key inContainer: theContainer];
o = [cache objectNamed: key inContainer: theContainer];
if (!o)
+2 -2
View File
@@ -128,8 +128,8 @@
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
fromDate: (NSCalendarDate *) theStartDate;
/* multiget helper */
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
+8 -15
View File
@@ -1151,7 +1151,6 @@ static NSArray *childRecordFields = nil;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
/* TODO:
- validation:
@@ -1201,17 +1200,13 @@ static NSArray *childRecordFields = nil;
mRecords = [NSMutableArray arrayWithArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: YES]];
if (!initialLoadInProgress)
{
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_lastmodified", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
}
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
records = mRecords;
}
else
@@ -1504,9 +1499,7 @@ static NSArray *childRecordFields = nil;
properties = [self parseDAVRequestedProperties: propElement];
records = [self syncTokenFieldsWithProperties: properties
matchingSyncToken: syncToken
fromDate: nil
initialLoad: NO];
fromDate: nil];
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
+1 -4
View File
@@ -778,10 +778,7 @@ static Class NSNullK;
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
// We check if the range is ok here since we could be using DomainLessLogin
if (r.location != NSNotFound)
theUID = [theUID substringToIndex: r.location];
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
+1 -5
View File
@@ -70,11 +70,7 @@ class Carddav:
if not self.cards:
url = "/SOGo/so/%s/Contacts/personal/view" % (self.login)
content = self._get(url)
#print "\nCONTENT:", content
if 'cards' in content:
self.cards = content['cards']
else:
self.cards = []
self.cards = content['cards']
return self.cards
def get_cards(self, pattern):
+4 -4
View File
@@ -89,9 +89,9 @@ class preferences:
get = HTTPPreferencesGET(url)
get.cookie = self.cookie
self.client.execute(get)
if DEBUG: print "DEBUG (url):", url
if DEBUG: print "DEBUG (status):", get.response["status"]
if DEBUG: print "DEBUG (body):", get.response['body']
if DEBUG: print "LUC (url):", url
if DEBUG: print "LUC (status):", get.response["status"]
if DEBUG: print "LUC (body):", get.response['body']
content = simplejson.loads(get.response['body'])
result = None
try:
@@ -117,7 +117,7 @@ class preferences:
# Get the right sub-dict and change the key/value
subdict = self.find_key(self.preferences, preference)
if not subdict:
raise AttributeError("ERROR(nosubdict): looking for %s in: %s" %(preference, str(self.preferences)))
print "LUC(nosubdict):", preference, self.preferences
subdict[preference] = value
def set(self, preference, value=None):
-24
View File
@@ -85,13 +85,6 @@ class JsonDavPhoneTests(unittest.TestCase):
self.allphones = list(self.newphone)
self.allphones.extend(self.newphones_difftype)
self.allphones.extend(self.newphones_sametype)
#- In case there are no cards for this user
try:
self._get_card()
except IndexError:
path = 'Contacts/personal'
(card, path, gid) = self._create_new_card(path)
self._save_card(card)
def tearDown(self):
self._connect_as_user()
@@ -109,23 +102,6 @@ class JsonDavPhoneTests(unittest.TestCase):
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
def _create_new_card(self, path):
gid = self.dv.newguid(path)
card = {'c_categories': None,
'c_cn': 'John Doe',
'c_component': 'vcard',
'c_givenname': 'John Doe',
'c_mail': 'johndoe@nothere.com',
'c_name': gid,
'c_o': '',
'c_screenname': '',
'c_sn': '',
'c_telephonenumber': '123.456.7890',
'emails': [{'type': 'pref', 'value': 'johndoe@nothere.com'}],
'phones': [{'type': 'home', 'value': '111.222.3333'}],
'id': gid}
return (card, path, gid)
def _get_card(self, name="John Doe"):
tmp_card = self.dv.get_cards(name)[0]
self.card = self.dv.get_card(tmp_card['c_name'])
@@ -28,14 +28,12 @@ class preventInvitationsTest(unittest.TestCase):
self.caldav = CalDAVSchedulingTest(self)
cal = self.prefs.get("Calendar")
if "PreventInvitationsWhitelist" not in cal:
#print "LUC (cal):", cal
cal["PreventInvitationsWhitelist"] = None
self.prefs.set("PreventInvitationsWhitelist", None)
if "PreventInvitations" not in cal:
cal["PreventInvitations"] = 0
self.prefs.set("PreventInvitations", 0)
def tearDown(self):
#self.prefs.set("autoReplyText", "")
self.prefs.set("autoReplyText", "")
self.prefs.set('PreventInvitations', 0)
self.prefs.set("PreventInvitationsWhitelist", None)
#- Manual Cleanup, not called because classs is not derived from unittest
+6 -4
View File
@@ -21,8 +21,8 @@ class UIPostsTests(unittest.TestCase):
self.cookie = sogoLogin.getAuthCookie(hostname, port, username, password)
def _urlPostData(self, client, url, data, exp_status=200):
post = webdavlib.HTTPPOST(url, simplejson.dumps(data))
post.content_type = "application/json"
post = webdavlib.HTTPPOST(url, data)
post.content_type = "application/x-www-form-urlencoded"
post.cookie = self.cookie
client.execute(post)
@@ -43,14 +43,16 @@ class UIPostsTests(unittest.TestCase):
""" Add Web Calendar """
ret=True
data = {"url":"%s" % webCalendarURL}
data = "url=%s" % webCalendarURL
calendarBaseURL="/SOGo/so/%s/Calendar" % username
addWebCalendarURL = "%s/addWebCalendar" % calendarBaseURL
response = self._urlPostData(self.client, addWebCalendarURL, data)
respJSON = simplejson.loads(response['body'])
calID = respJSON['id']
folderID = respJSON['folderID']
#sogo1:Calendar/C07-5006F300-1-370E2480
(_, calID) = folderID.split('/', 1)
self.assertNotEqual(calID, None)
# reload the cal
-2
View File
@@ -22,8 +22,6 @@ $(TEST_TOOL)_OBJC_FILES += \
TestSBJsonParser.m \
\
TestNGMimeAddressHeaderFieldGenerator.m \
TestNGMimeMessageGenerator.m \
\
TestNSData+Crypto.m \
TestNSString+Crypto.m \
TestNSString+URLEscaping.m \
-4
View File
@@ -25,8 +25,6 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSValue.h>
@class NSArray;
@@ -54,8 +52,6 @@
- (BOOL) run;
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2;
@end
#define test(c) { \
-75
View File
@@ -185,79 +185,4 @@ static NSString *SOGoTestAssertException = @"SOGoTestAssertException";
return YES;
}
/* Helper function for diffForString:andString */
NSString *_stringForCharacterAtIndex(NSUInteger index, NSString *str, NSUInteger length)
{
NSString *chrStr;
unichar chr;
if (index < length)
{
chr = [str characterAtIndex: index];
if (isprint(chr))
{
chrStr = [NSString stringWithFormat: @"%c", chr];
}
else
{
if (chr == 10)
chrStr = @"[NL]";
else if (chr == 0)
chrStr = @"[\0]";
else
chrStr = [NSString stringWithFormat: @"[NP: %u]", chr];
}
}
else
{
chrStr = @"[none]";
}
return chrStr;
}
/*
Returns a string with a very verbose diff of the two strings.
In case the strings are equal it returns an empty string.
Example output for the strings 'flower' and 'flotera':
<begin of example>
0 |f|
1 |l|
2 |o|
3 |w|t|<--
4 |e|
5 |r|
6 |[none]|a|<--
<end of example>
*/
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2
{
BOOL differencesFound = NO;
NSString *finalSTR = @"";
NSUInteger i, length1, length2;
NSString *sc1, *sc2;
length1 = [str1 length];
length2 = [str2 length];
for (i = 0; i < length1 || i < length2; i++)
{
sc1 = _stringForCharacterAtIndex(i, str1, length1);
sc2 = _stringForCharacterAtIndex(i, str2, length2);
if ([sc1 isEqualToString: sc2])
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|\n", i, sc1];
else
{
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|%@|<--\n", i, sc1, sc2];
differencesFound = YES;
}
}
if (!differencesFound)
return @"";
return finalSTR;
}
@end
-134
View File
@@ -1,134 +0,0 @@
/* TestNGMimeMessageGenerator.m - this file is part of SOGo
*
* 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 "SOGoTest.h"
#import <NGMail/NGMimeMessageGenerator.h>
@interface TestNGMimeMessageGenerator : SOGoTest
@end
@implementation TestNGMimeMessageGenerator
- (void) test_generateDataForHeaderField_value
{
NGMimeMessageGenerator *generator;
NSArray *cases = [NSArray arrayWithObjects:
[NSArray arrayWithObjects: @"Message-ID", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", nil],
[NSArray arrayWithObjects: @"Content-Type",
@"text/plain; charset=utf-8; format=flowed",
@"text/plain; charset=utf-8; format=flowed",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneHebrewOneLatin",
@"עs",
@"=?utf-8?q?=D7=A2s?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneLatineOneHebrew",
@"sע",
@"=?utf-8?q?s=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterHebrew",
@"ע",
@"=?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterRussian",
@"Б",
@"=?utf-8?q?=D0=91?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderParameter",
@"parameter=ע",
@"parameter==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderParameters",
@"plain; parameter=ע; parameter-plain; parameter2=ea",
@"plain;\n parameter==?utf-8?q?=D7=A2?=;\n parameter-plain; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndNoParameter",
@"plain; parameter=ע; parameter-plain; ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n =?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndTwoParameter",
@"plain; parameter=ע; parameter-plain; z=ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n z==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderExtrablanks",
@"plain; parameter=ע; parameter 2spaces; parameter2=ea",
@"plain;\n \\ parameter==?utf-8?q?=D7=A2?=;\n parameter 2spaces; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-Encoded-Unbalanced-Paramter-Quote",
@"text/plain; name=\"ע",
@"text/plain;\n name==?utf-8?q?=22=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"content-type",
@"text/plain; name=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"text/plain;\n name=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-disposition",
@"attachment; filename=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"attachment;\n filename=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-length", @"2912", @"2912", nil],
[NSArray arrayWithObjects: @"content-transfer-encoding", @"quoted-printable", @"quoted-printable", nil],
nil
];
NSEnumerator *enumerator;
NSArray *testCase;
[NGMimeMessageGenerator initialize];
generator = [[NGMimeMessageGenerator alloc] init];
[generator autorelease];
enumerator = [cases objectEnumerator];
while ((testCase = [enumerator nextObject]) != nil)
{
NSData *result;
NSMutableData *resultWithNulByte;
NSString *header = [testCase objectAtIndex: 0];
NSData *headerData = [testCase objectAtIndex: 1];
NSString *expected = [testCase objectAtIndex: 2];
result = [generator generateDataForHeaderField: header
value: headerData];
if (result == nil)
result = [@"[nil]" dataUsingEncoding: NSUTF8StringEncoding];
resultWithNulByte = [result mutableCopy];
[resultWithNulByte appendBytes: "\0" length: 1];
NSString *resultString = [NSString stringWithCString:[resultWithNulByte bytes]];
BOOL testResult = [resultString isEqualToString: expected];
NSString *diff = [self stringFromDiffBetween: [NSString stringWithString: resultString]
and: [NSString stringWithString: expected]];
NSString *testErrorMsg = [NSString
stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %u lengthExpected: %u",
header,
resultString,
expected,
headerData,
diff,
[resultString length],
[expected length]
];
testWithMessage(testResult, testErrorMsg);
}
}
@end
+1 -1
View File
@@ -35,7 +35,7 @@
{
const char *inStrings[] = { "éléphant", "2š", "sogo+test", NULL };
const char **inString;
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo%2Btest" };
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo+test" };
NSString **outString;
NSString *result, *error;
+1 -9
View File
@@ -116,19 +116,13 @@ static NSURL *tableURL = nil;
- (void) _removeUserFolders: (NSString *) userId
{
NSArray *folderPaths;
NSString *path;
int count, max;
folderPaths = [self _userFolderPaths: userId];
max = [folderPaths count];
if (max > 0)
for (count = 0; count < max; count++)
{
path = [folderPaths objectAtIndex: count];
[fm deleteFolderAtPath: path];
if (verbose)
NSLog (@"Deleting %@", path);
}
[fm deleteFolderAtPath: [folderPaths objectAtIndex: count]];
else
NSLog (@"No folder returned for user '%@'", userId);
}
@@ -150,8 +144,6 @@ static NSURL *tableURL = nil;
userId];
if ([fc evaluateExpressionX: sql])
NSLog (@"Unable to delete the preference record for '%@'", userId);
else if (verbose)
NSLog (@"Removed preference record for '%@'", userId);
[cm releaseChannel: fc];
}
}
-1
View File
@@ -12,7 +12,6 @@ AdministrationUI_OBJC_FILES = \
AdministrationUIProduct.m \
\
UIxAdministration.m \
UIxAdministrationAclEditor.m \
UIxAdministrationFilterPanel.m
AdministrationUI_RESOURCE_FILES += \
-5
View File
@@ -46,11 +46,6 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Administration";
}
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext*) context
{
@@ -1,48 +0,0 @@
/* UIxAdministrationAclEditor.h - this file is part of SOGo
*
* Copyright (C) 2006-2014 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
* 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 UIXACLEDITOR_H
#define UIXACLEDITOR_H
#import <SOGoUI/UIxComponent.h>
@interface UIxAdministrationAclEditor : UIxComponent
{
BOOL prepared;
BOOL publishInFreeBusy;
NSArray *aclUsers;
NSArray *savedUIDs;
NSMutableDictionary *users;
NSString *currentUser;
NSString *defaultUserID;
}
- (NSArray *) aclsForObject;
- (void) setCurrentUser: (NSString *) newCurrentUser;
- (NSString *) currentUser;
- (NSString *) currentUserClass;
- (NSDictionary *) currentUserInfos;
- (NSString *) currentUserDisplayName;
- (BOOL) currentUserIsSubscribed;
- (BOOL) isPublicAccessEnabled;
@end
#endif /* UIXACLEDITOR_H */
@@ -1,279 +0,0 @@
/* UIxAdministrationAclEditor.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 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
* 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 <Foundation/NSArray.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSKeyValueCoding.h>
#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/SoUser.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/SOGoContentObject.h>
#import <SOGo/SOGoGCSFolder.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import "UIxAdministrationAclEditor.h"
@implementation UIxAdministrationAclEditor
- (id) init
{
if ((self = [super init]))
{
aclUsers = nil;
prepared = NO;
publishInFreeBusy = NO;
users = [NSMutableDictionary new];
currentUser = nil;
defaultUserID = nil;
savedUIDs = nil;
}
return self;
}
- (void) dealloc
{
[savedUIDs release];
[users release];
[currentUser release];
[defaultUserID release];
[super dealloc];
}
- (NSArray *) aclsForObject
{
if (!aclUsers)
aclUsers = [[self clientObject] aclUsers];
return aclUsers;
}
- (NSString *) defaultUserID
{
if (!defaultUserID)
ASSIGN (defaultUserID, [[self clientObject] defaultUserID]);
return defaultUserID;
}
- (BOOL) canSubscribeUsers
{
return [[self clientObject]
respondsToSelector: @selector (subscribeUserOrGroup:reallyDo:response:)];
}
/**
* @api {get} /so/:username/:folderPath/acls List users with rights
* @apiVersion 1.0.0
* @apiName GetAcls
* @apiGroup Common
* @apiExample {curl} Example usage:
* curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/acls
*
* @apiSuccess (Success 200) {Object[]} users List of users with ACL for the folder
* @apiSuccess (Success 200) {String} users.uid User ID
* @apiSuccess (Success 200) {String} users.userClass Either 'normal-user', 'normal-group' or 'public-access'
* @apiSuccess (Success 200) {Number} users.isSubscribed 1 if the user is subscribed to the folder
* @apiSuccess (Success 200) {String} [users.cn] User fullname
* @apiSuccess (Success 200) {String} [users.c_email] User main email address
*/
- (id <WOActionResults>) aclsAction
{
NSString *currentUID, *ownerLogin, *info;
NSDictionary *currentUserInfos;
NSMutableDictionary *userData;
id <WOActionResults> result;
NSEnumerator *aclsEnum;
if (!prepared)
{
ownerLogin = [[self clientObject] ownerInContext: context];
if (!defaultUserID)
ASSIGN (defaultUserID, [[self clientObject] defaultUserID]);
aclsEnum = [[self aclsForObject] objectEnumerator];
while ((currentUID = [aclsEnum nextObject]))
{
if (!([currentUID isEqualToString: ownerLogin]
|| [currentUID isEqualToString: defaultUserID]
|| [currentUID isEqualToString: @"anonymous"]))
{
// Set the current user in order to get information associated with it
[self setCurrentUser: currentUID];
// Build the object associated to the current UID
currentUserInfos = [self currentUserInfos];
userData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
currentUser, @"uid",
[self currentUserClass], @"userClass",
[NSNumber numberWithBool: [self currentUserIsSubscribed]], @"isSubscribed",
nil];
if ((info = [currentUserInfos objectForKey: @"cn"]) && [info length])
[userData setObject: info forKey: @"cn"];
if ((info = [currentUserInfos objectForKey: @"c_email"]) && [info length])
[userData setObject: info forKey: @"c_email"];
[users setObject: userData forKey: currentUID];
}
}
// Add the 'Any authenticated' user
if ([self canSubscribeUsers])
{
userData = [NSDictionary dictionaryWithObjectsAndKeys:
@"<default>", @"uid",
[self labelForKey: @"Any Authenticated User"], @"cn",
@"public-user", @"userClass",
nil];
[users setObject: userData forKey: @"<default>"];
}
if ([self canSubscribeUsers] && [self isPublicAccessEnabled])
{
// Add the 'public access' user
userData = [NSDictionary dictionaryWithObjectsAndKeys:
@"anonymous", @"uid",
[self labelForKey: @"Public Access"], @"cn",
@"public-user", @"userClass",
nil];
[users setObject: userData forKey: @"anonymous"];
}
prepared = YES;
}
result = [self responseWithStatus: 200
andJSONRepresentation: [NSDictionary dictionaryWithObject: users forKey: @"users"]];
return result;
}
- (void) setCurrentUser: (NSString *) newCurrentUser
{
ASSIGN (currentUser, newCurrentUser);
}
- (NSString *) currentUser
{
return ([currentUser hasPrefix: @"@"]
? [currentUser substringFromIndex: 1]
: currentUser);
}
- (NSString *) currentUserClass
{
return ([currentUser hasPrefix: @"@"]
? @"normal-group"
: @"normal-user");
}
- (NSString *) currentUserDisplayName
{
NSDictionary *infos;
NSString *uid;
SOGoUserManager *um;
um = [SOGoUserManager sharedUserManager];
uid = [self currentUser];
infos = [um contactInfosForUserWithUIDorEmail: uid inDomain: [[context activeUser] domain]];
if (infos)
{
return [NSString stringWithFormat: @"%@ <%@>",
[infos objectForKey: @"cn"],
[infos objectForKey: @"c_email"]];
}
else
return uid;
}
- (NSDictionary *) currentUserInfos
{
SOGoUserManager *um;
um = [SOGoUserManager sharedUserManager];
return [um contactInfosForUserWithUIDorEmail: [self currentUser]];
}
- (BOOL) currentUserIsSubscribed
{
SOGoGCSFolder *folder;
folder = [self clientObject];
return ([folder respondsToSelector: @selector (userIsSubscriber:)]
&& [folder userIsSubscriber: currentUser]);
}
- (void) setUserUIDS: (NSString *) retainedUsers
{
if ([retainedUsers length] > 0)
{
savedUIDs = [retainedUsers componentsSeparatedByString: @","];
[savedUIDs retain];
}
else
savedUIDs = [NSArray new];
}
- (NSString *) folderID
{
return [[self clientObject] nameInContainer];
}
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext *) context
{
return ([[request method] isEqualToString: @"POST"]);
}
- (id <WOActionResults>) saveAclsAction
{
NSEnumerator *aclsEnum;
SOGoObject *clientObject;
NSString *currentUID, *ownerLogin;
clientObject = [self clientObject];
ownerLogin = [clientObject ownerInContext: context];
aclsEnum = [[self aclsForObject] objectEnumerator];
while ((currentUID = [[aclsEnum nextObject] objectForKey: @"c_uid"]))
if ([currentUID isEqualToString: ownerLogin]
|| [savedUIDs containsObject: currentUID])
[users removeObjectForKey: currentUID];
[clientObject removeAclsForUsers: [users allKeys]];
return [self jsCloseWithRefreshMethod: nil];
}
- (BOOL) isPublicAccessEnabled
{
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
-4
View File
@@ -15,10 +15,6 @@
protectedBy = "View";
pageName = "UIxAdministration";
};
UIxAdministrationAclEditor = {
protectedBy = "View";
pageName = "UIxAdministrationAclEditor";
};
};
};
};
+8 -10
View File
@@ -408,17 +408,16 @@
- (id <WOActionResults>) copyAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSDictionary *data;
NSException *ex;
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
request = [context request];
if (destinationFolderId && [contactsId count])
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: YES];
@@ -437,17 +436,16 @@
- (id <WOActionResults>) moveAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSDictionary *data;
NSArray *contactsId;
NSException *ex;
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
request = [context request];
if (destinationFolderId && [contactsId count])
if ((destinationFolderId = [request formValueForKey: @"folder"])
&& (contactsId = [request formValuesForKey: @"uid"]))
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: NO];
+42 -7
View File
@@ -249,7 +249,14 @@
- (NSString *) commonLocalizableStrings
{
return [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
NSString *rc;
if (isPopup)
rc = @"";
else
rc = [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
return rc;
}
- (NSString *) productLocalizableStrings
@@ -433,6 +440,40 @@
return ([[self productCSSURL] length] > 0);
}
- (BOOL) _moduleIs: (NSString *) moduleName
{
NSString *frameworkName;
frameworkName = [[context page] frameworkName];
return [frameworkName isEqualToString: moduleName];
}
- (BOOL) isCalendar
{
return [self _moduleIs: @"SchedulerUI"];
}
- (BOOL) isContacts
{
return [self _moduleIs: @"ContactsUI"];
}
- (BOOL) isMail
{
return [self _moduleIs: @"MailerUI"];
}
- (BOOL) isPreferences
{
return [self _moduleIs: @"PreferencesUI"];
}
- (BOOL) isAdministration
{
return [self _moduleIs: @"AdministrationUI"];
}
- (void) setToolbar: (NSString *) newToolbar
{
ASSIGN (toolbar, newToolbar);
@@ -468,12 +509,6 @@
return [[context activeUser] cn];
}
- (NSString *) userEmail
{
return [[context activeUser] systemEmail];
}
- (BOOL) canLogoff
{
BOOL canLogoff;
@@ -43,27 +43,6 @@
"Copy To" = "Copy To";
"Add to:" = "Add to:";
/* Subheader of empty addressbook */
"No contact" = "No contact";
/* Subheader of system addressbook */
"Start a search to browse this address book" = "Start a search to browse this address book";
/* Number of contacts in addressbook; string is prefixed by number */
"contacts" = "contacts";
/* No contact matching search criteria */
"No matching contact" = "No matching contact";
/* Number of contacts matching search criteria; string is prefixed by number */
"matching contacts" = "matching contacts";
/* Number of selected contacts in list */
"selected" = "selected";
/* Empty right pane */
"No contact selected" = "No contact selected";
/* Tooltips */
"Create a new address book card" = "Create a new address book card";
+6 -11
View File
@@ -367,8 +367,6 @@ static Class SOGoContactGCSEntryK = Nil;
day = [date dayOfMonth];
[card setBday: [NSString stringWithFormat: @"%.4d%.2d%.2d", year, month, day]];
}
else
[card setBday: nil];
if ([[attributes objectForKey: @"addresses"] isKindOfClass: [NSArray class]])
{
@@ -432,9 +430,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"tel"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
element = [card elementWithTag: @"tel" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
}
}
}
@@ -451,9 +448,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([o isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"email"
ofType: [o objectForKey: @"type"]
withValue: [o objectForKey: @"value"]];
element = [card elementWithTag: @"email" ofType: [o objectForKey: @"type"]];
[element setSingleValue: [o objectForKey: @"value"] forKey: @""];
}
}
}
@@ -470,9 +466,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"url"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
element = [card elementWithTag: @"url" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
}
}
}
+13 -7
View File
@@ -112,11 +112,15 @@
request = [context request];
rc = [NSMutableDictionary dictionary];
data = [[[[[request httpRequest] body] parts] lastObject] body];
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
data = [request formValueForKey: @"contactsFile"];
if ([data respondsToSelector: @selector(isEqualToString:)])
fileContent = (NSString *) data;
else
{
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
}
if (fileContent && [fileContent length])
{
@@ -128,10 +132,12 @@
imported = 0;
}
[rc setObject: [NSNumber numberWithInt: imported] forKey: @"imported"];
[rc setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
response = [self responseWithStatus: 200];
[response setHeader: @"text/html" forKey: @"content-type"];
[response setHeader: @"text/html"
forKey: @"content-type"];
[(WOResponse*)response appendContentString: [rc jsonRepresentation]];
return response;
@@ -20,6 +20,14 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoContactGCSFolder;
@interface UIxContactFolderLinksTemplate : UIxComponent
{
SOGoContactGCSFolder *addressBook;
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
@end
@@ -24,6 +24,69 @@
@implementation UIxContactFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
addressBook = [self clientObject];
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[addressBook realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[addressBook publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
+11 -48
View File
@@ -71,11 +71,6 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return self;
}
- (NSString *) modulePath
{
return @"Contacts";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -280,70 +275,32 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [[self queryParameterForKey: @"popup"] boolValue];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
- (NSString *) contactFolders
{
SOGoContactFolders *folderContainer;
NSMutableDictionary *urls, *acls;
NSArray *folders;
NSMutableArray *foldersAttrs;
NSString *userLogin, *owner;
NSArray *folders, *allACLs;
NSDictionary *folderAttrs;
id currentFolder;
BOOL objectCreator, objectEditor, objectEraser;
int max, i;
userLogin = [[context activeUser] login];
folderContainer = [self clientObject];
folders = [folderContainer subFolders];
// return [folderContainer subFolders];
folders = [folderContainer subFolders];
max = [folders count];
foldersAttrs = [NSMutableArray arrayWithCapacity: max];
urls = nil;
for (i = 0; i < max; i++)
{
currentFolder = [folders objectAtIndex: i];
owner = [currentFolder ownerInContext: context];
// We extract URLs for this address book
if ([currentFolder respondsToSelector: @selector(cardDavURL)])
{
urls = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[currentFolder cardDavURL], @"cardDavURL",
nil];
if ([self isPublicAccessEnabled])
{
[urls setObject: [currentFolder publicCardDavURL] forKey: @"publicCardDavURL"];
}
}
// We extract ACLs for this address book
allACLs = ([owner isEqualToString: userLogin] ? nil : [currentFolder aclsForUser: userLogin]);
objectCreator = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectCreator]);
objectEditor = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEditor]);
objectEraser = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEraser]);
acls = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: objectCreator], @"objectCreator",
[NSNumber numberWithBool: objectEditor], @"objectEditor",
[NSNumber numberWithBool: objectEraser], @"objectEraser", nil];
// NOTE: keep urls as the last key/value here, to avoid chopping the dictionary
// if it is not a GCS folder
folderAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%@", [currentFolder nameInContainer]], @"id",
[currentFolder displayName], @"name",
owner, @"owner",
[currentFolder ownerInContext: context], @"owner",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoGCSFolderK]], @"isEditable",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK]
&& ![currentFolder isPersonalSource]], @"isRemote",
acls, @"acls",
urls, @"urls",
nil];
[foldersAttrs addObject: folderAttrs];
}
@@ -462,6 +419,12 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [super defaultAction];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
@interface UIxContactViewTemplate : UIxComponent
+4 -3
View File
@@ -838,10 +838,11 @@
o = [card birthday];
if (o)
{
[data setObject: [NSNumber numberWithInt: [o timeIntervalSince1970]]
forKey: @"birthday"];
NSNumber *time = [NSNumber numberWithInt: [o timeIntervalSince1970]];
[data setObject: time forKey: @"birthday"];
}
// o = [card source];
// if (o) [data setObject: o forKey: @"source"];
o = [card tz];
if (o) [data setObject: o forKey: @"tz"];
+1 -31
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2006-2014 Inverse inc.
This file is part of SOGo.
@@ -39,9 +39,6 @@
#import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <NGCards/NGVCard.h>
#import <NGCards/NGVList.h>
#import <SoObjects/Contacts/SOGoContactGCSEntry.h>
@@ -88,31 +85,6 @@
return s;
}
- (void) saveSortValue
{
NSMutableDictionary *contactSettings;
NSString *ascending, *sort;
SOGoUserSettings *us;
sort = [[context request] formValueForKey: @"sort"];
ascending = [[context request] formValueForKey: @"asc"];
if ([sort length])
{
us = [[context activeUser] userSettings];
contactSettings = [us objectForKey: @"Contact"];
// Must create if it doesn't exist
if (!contactSettings)
{
contactSettings = [NSMutableDictionary dictionary];
[us setObject: contactSettings forKey: @"Contact"];
}
[contactSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: @"SortingState"];
[us synchronize];
}
}
- (NSArray *) contactInfos
{
id <SOGoContactFolder> folder;
@@ -125,8 +97,6 @@
WORequest *rq;
unsigned int i;
[self saveSortValue];
if (!contactInfos)
{
folder = [self clientObject];
@@ -87,7 +87,6 @@
id info, viewer;
NSArray *parts;
NSMutableArray *renderedParts;
NSString *contentType;
NSUInteger i, max;
parts = [[self bodyInfo] objectForKey: @"parts"];
@@ -103,13 +102,9 @@
[viewer setPartPath: [self childPartPath]];
[renderedParts addObject: [viewer renderedPart]];
}
contentType = [NSString stringWithFormat: @"%@/%@",
[[self bodyInfo] objectForKey: @"type"],
[[self bodyInfo] objectForKey: @"subtype"]];
return [NSDictionary dictionaryWithObjectsAndKeys:
[self className], @"type",
contentType, @"contentType",
renderedParts, @"content",
nil];
}
+3 -1
View File
@@ -1,6 +1,8 @@
/* UIxMailPartTextViewer.h - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006 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
+20 -19
View File
@@ -1,22 +1,23 @@
/* UIxMailPartTextViewer.m - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 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
* 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.
*/
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/*
UIxMailPartTextViewer
@@ -65,9 +65,6 @@
"Account: " = "Account: ";
"Shared Account: " = "Shared Account: ";
/* Empty right pane */
"No message selected" = "No message selected";
/* acls */
"Access rights to" = "Access rights to";
"For user" = "For user";
@@ -231,9 +228,6 @@
"Delete Message" = "Delete Message";
"Delete Selected Messages" = "Delete Selected Messages";
/* Number of selected messages in list */
"selected" = "selected";
"This Folder" = "This Folder";
/* Label popup menu */
+8 -15
View File
@@ -92,7 +92,7 @@
}
else
{
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created." inContext: context];
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created."];
jsonResponse = [NSDictionary dictionaryWithObject: [NSString stringWithFormat: errorFormat, folderName]
forKey: @"error"];
response = [self responseWithStatus: 500
@@ -101,7 +101,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter."]
forKey: @"error"];
response = [self responseWithStatus: 500
andJSONRepresentation: jsonResponse];
@@ -149,7 +149,7 @@
if (!newFolderName || [newFolderName length] == 0)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter" inContext: context]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter"]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -160,7 +160,7 @@
error = [co renameTo: newFolderName];
if (error)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder." inContext: context]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -251,7 +251,7 @@
error = [connection moveMailboxAtURL: srcURL toURL: destURL];
if (error)
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -283,7 +283,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -374,7 +374,7 @@
{
uids = [value componentsSeparatedByString: @","];
response = [co archiveUIDs: uids
inArchiveNamed: [self labelForKey: @"Saved Messages.zip" inContext: context]
inArchiveNamed: [self labelForKey: @"Saved Messages.zip"]
inContext: context];
if (!response)
response = [self responseWith204];
@@ -739,7 +739,6 @@
NSDictionary *content, *result;
BOOL addOrRemove;
NGImap4Client *client;
id flag;
int i;
@@ -752,13 +751,7 @@
// We unescape our flags
for (i = [flags count]-1; i >= 0; i--)
{
flag = [flags objectAtIndex: i];
if ([flag isKindOfClass: [NSString class]])
[flags replaceObjectAtIndex: i withObject: [flag fromCSSIdentifier]];
else
[flags removeObjectAtIndex: i];
}
[flags replaceObjectAtIndex: i withObject: [[flags objectAtIndex: i] fromCSSIdentifier]];
co = [self clientObject];
client = [[co imap4Connection] client];
+17 -45
View File
@@ -55,7 +55,6 @@
#import <Mailer/SOGoSentFolder.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h>
@@ -132,7 +131,7 @@
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday" inContext: context];
return [self labelForKey: @"Yesterday"];
}
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] < 7)
{
@@ -164,15 +163,13 @@
//
// Sometimes, the MUAs don't send over the string in () so we ignore it.
//
- (NSDictionary *) messagePriority
- (NSString *) messagePriority
{
NSUInteger priority;
NSString *description;
NSString *result;
NSData *data;
data = [message objectForKey: @"header"];
priority = 3;
description = [self labelForKey: @"normal" inContext: context];
result = @"";
if (data)
{
@@ -193,34 +190,15 @@
s = [[s substringFromIndex: r.location+1]
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([s hasPrefix: @"1"])
{
priority = 1;
description = [self labelForKey: @"highest" inContext: context];
}
else if ([s hasPrefix: @"2"])
{
priority = 2;
description = [self labelForKey: @"high" inContext: context];
}
else if ([s hasPrefix: @"4"])
{
priority = 4;
description = [self labelForKey: @"low" inContext: context];
}
else if ([s hasPrefix: @"5"])
{
priority = 5;
description = [self labelForKey: @"lowest" inContext: context];
}
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"];
}
}
}
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: priority], @"level",
description, @"name",
nil];
return result;
}
- (NSString *) messageSubject
@@ -264,7 +242,7 @@
{
NSString *s;
s = [self labelForKey:@"View Mail Folder" inContext: context];
s = [self labelForKey:@"View Mail Folder"];
s = [s stringByAppendingString:@": "];
s = [s stringByAppendingString:[self objectTitle]];
return s;
@@ -644,12 +622,11 @@
- (NSDictionary *) getUIDsInFolder: (SOGoMailFolder *) folder
withHeaders: (BOOL) includeHeaders
{
NSArray *uids, *threadedUids, *headers;
NSMutableDictionary *data;
NSArray *uids, *threadedUids, *headers;
NSRange r;
SOGoMailAccount *account;
id quota;
NSRange r;
int count;
data = [NSMutableDictionary dictionary];
@@ -667,14 +644,10 @@
if (includeHeaders)
{
// Also retrieve the first headers, up to 'headersPrefetchMaxSize'
NSArray *a;
a = [uids flattenedArray];
count = [a count];
if (count > headersPrefetchMaxSize)
count = headersPrefetchMaxSize;
count = [[uids flattenedArray] count];
if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize;
r = NSMakeRange(0, count);
headers = [self getHeadersForUIDs: [a subarrayWithRange: r]
headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r]
inFolder: folder];
[data setObject: headers forKey: @"headers"];
@@ -743,8 +716,6 @@
* @apiSuccess (Success 200) {String} headers.From.email Sender's email address
* @apiSuccess (Success 200) {Number} headers.isRead 1 if message is read
* @apiSuccess (Success 200) {String} headers.Priority Priority
* @apiSuccess (Success 200) {String} headers.Priority.level Priority number
* @apiSuccess (Success 200) {String} headers.Priority.name Priority description
* @apiSuccess (Success 200) {String} headers.RelativeDate Message date relative to now
* @apiSuccess (Success 200) {String} headers.Size Formatted message size
* @apiSuccess (Success 200) {String[]} headers.Flags Flags, such as "answered" and "seen"
@@ -773,6 +744,7 @@
noHeaders = [[[requestContent objectForKey: @"sortingAttributes"] objectForKey:@"noHeaders"] boolValue];
data = [self getUIDsInFolder: folder
withHeaders: !noHeaders];
[response appendContentString: [data jsonRepresentation]];
return response;
@@ -906,7 +878,7 @@
data = [NSDictionary dictionaryWithObjectsAndKeys:
@"No UID specified", @"error", nil];
return [self responseWithStatus: 404 /* Not Found */
andString: [data jsonRepresentation]];
andString: [data jsonRepresentation]];
}
uids = [data objectForKey: @"uids"];
+3 -20
View File
@@ -87,11 +87,6 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Mail";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -123,18 +118,6 @@
return [accounts jsonRepresentation];
}
- (WOResponse *) mailAccountsAction
{
WOResponse *response;
NSString *s;
s = [self mailAccounts];
response = [self responseWithStatus: 200
andString: s];
return response;
}
- (NSString *) userNames
{
NSArray *accounts, *userNames;
@@ -391,14 +374,14 @@
- (WOResponse *) saveFoldersStateAction
{
WORequest *request;
NSArray *expandedFolders;
NSString *expandedFolders;
[self _setupContext];
request = [context request];
expandedFolders = [[request contentAsString] objectFromJSONString];
expandedFolders = [request formValueForKey: @"expandedFolders"];
[moduleSettings setObject: expandedFolders
forKey: @"ExpandedFolders"];
forKey: @"ExpandedFolders"];
[us synchronize];
-9
View File
@@ -342,19 +342,10 @@
actionClass = "UIxMailMainFrame";
actionName = "unseenCount";
};
mailAccounts = {
protectedBy = "View";
actionClass = "UIxMailMainFrame";
actionName = "mailAccounts";
};
// viewerTemplate = {
// protectedBy = "View";
// pageName = "UIxMailViewTemplate";
// };
UIxMailPopupView = {
protectedBy = "View";
pageName = "UIxMailPopupView";
};
UIxMailEditor = {
protectedBy = "View";
pageName = "UIxMailEditor";
@@ -9,7 +9,6 @@
"Connect" = "Connect";
"Authentication Failed" = "Authentication Failed";
"Wrong username or password." = "Wrong username or password.";
"cookiesNotEnabled" = "You cannot login because your browser's cookies are disabled. Please enable cookies in your browser's settings and try again.";
+6 -7
View File
@@ -183,6 +183,7 @@
SOGoWebAuthenticator *auth;
SOGoAppointmentFolders *calendars;
SOGoUserDefaults *ud;
SOGoSystemDefaults *sd;
SOGoUser *loggedInUser;
NSDictionary *params;
NSString *username, *password, *language, *domain, *remoteHost;
@@ -225,15 +226,13 @@
response = [self responseWithStatus: 200
andJSONRepresentation: json];
// We get the proper username for cookie creation. If we are using a multidomain
// environment with SOGoEnableDomainBasedUID, we could have to append the domain
// to the username. Also when SOGoEnableDomainBasedUID is enabled, we could be in
// the DomainLessLogin situation, so we would NOT add the domain. -getUIDForEmail
// has all the logic for this, so lets use it.
if ([domain isNotNull])
{
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] &&
[username rangeOfString: @"@"].location == NSNotFound)
username = [NSString stringWithFormat: @"%@@%@", username, domain];
}
authCookie = [auth cookieWithUsername: username
+9 -13
View File
@@ -636,7 +636,7 @@
* @apiExample {curl} Example usage:
* curl -i http://localhost/SOGo/so/sogo1/foldersSearch?type=contact
*
* @apiParam {String} type Either 'calendar' or 'contact'. If nothing is specifed, its both.
* @apiParam {String} type Either 'calendar' or 'contact'
*
* @apiSuccess (Success 200) {Object[]} folders List of matching folders
* @apiSuccess (Success 200) {String} folders.name Path of folder
@@ -648,31 +648,27 @@
- (id <WOActionResults>) foldersSearchAction
{
NSString *folderType;
NSMutableArray *folders;
NSArray *folders;
NSDictionary *message;
id <WOActionResults> result;
SOGoUserFolder *userFolder;
folderType = [self queryParameterForKey: @"type"];
folders = [NSMutableArray array];
userFolder = [self clientObject];
if ([folderType length])
{
[folders addObjectsFromArray: [userFolder foldersOfType: folderType
forUID: [userFolder ownerInContext: context]]];
userFolder = [self clientObject];
folders = [userFolder foldersOfType: folderType
forUID: [userFolder ownerInContext: context]];
result = [self _foldersResponseForResults: folders];
}
else
{
[folders addObjectsFromArray: [userFolder foldersOfType: @"calendar"
forUID: [userFolder ownerInContext: context]]];
[folders addObjectsFromArray: [userFolder foldersOfType: @"contact"
forUID: [userFolder ownerInContext: context]]];
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing type parameter"]
forKey: @"error"];
result = [self responseWithStatus: 400 andJSONRepresentation: message];
}
result = [self _foldersResponseForResults: folders];
return result;
}
@@ -1,7 +1,6 @@
/* toolbar */
"Save and Close" = "Save and Close";
"Close" = "Close";
"Preferences saved" = "Preferences saved";
/* tabs */
"General" = "General";
@@ -318,9 +317,6 @@
"matches regex" = "matches regex";
"does not match regex" = "does not match regex";
/* Placeholder for the value field of a condition */
"Value" = "Value";
"Seen" = "Seen";
"Deleted" = "Deleted";
"Answered" = "Answered";
+20 -35
View File
@@ -61,8 +61,7 @@ static SoProduct *preferencesProduct = nil;
SOGoUserDefaults *defaults;
NSMutableArray *accounts;
NSArray *categoryLabels;
NSDictionary *locale;
if (!preferencesProduct)
{
preferencesProduct = [[SoProduct alloc] initWithBundle:
@@ -79,7 +78,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoTimeZone"])
[[defaults source] setObject: [defaults timeZoneName] forKey: @"SOGoTimeZone"];
if (![[defaults source] objectForKey: @"SOGoShortDateFormat"])
[[defaults source] setObject: [defaults shortDateFormat] forKey: @"SOGoShortDateFormat"];
@@ -91,7 +90,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoLoginModule"])
[[defaults source] setObject: [defaults loginModule] forKey: @"SOGoLoginModule"];
if (![[defaults source] objectForKey: @"SOGoRefreshViewCheck"])
[[defaults source] setObject: [defaults refreshViewCheck] forKey: @"SOGoRefreshViewCheck"];
@@ -100,13 +99,13 @@ static SoProduct *preferencesProduct = nil;
//
if (![[defaults source] objectForKey: @"SOGoFirstDayOfWeek"])
[[defaults source] setObject: [NSNumber numberWithInt: [defaults firstDayOfWeek]] forKey: @"SOGoFirstDayOfWeek"];
if (![[defaults source] objectForKey: @"SOGoDayStartTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayStartHour]] forKey: @"SOGoDayStartTime"];
if (![[defaults source] objectForKey: @"SOGoDayEndTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayEndHour]] forKey: @"SOGoDayEndTime"];
if (![[defaults source] objectForKey: @"SOGoFirstWeekOfYear"])
[[defaults source] setObject: [defaults firstWeekOfYear] forKey: @"SOGoFirstWeekOfYear"];
@@ -115,20 +114,20 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoCalendarEventsDefaultClassification"])
[[defaults source] setObject: [defaults calendarEventsDefaultClassification] forKey: @"SOGoCalendarEventsDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarTasksDefaultClassification"])
[[defaults source] setObject: [defaults calendarTasksDefaultClassification] forKey: @"SOGoCalendarTasksDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarDefaultReminder"])
[[defaults source] setObject: [defaults calendarDefaultReminder] forKey: @"SOGoCalendarDefaultReminder"];
// Populate default calendar categories, based on the user's preferred language
if (![defaults calendarCategories])
{
categoryLabels = [[[self labelForKey: @"calendar_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
[defaults setCalendarCategories: categoryLabels];
}
if (![defaults calendarCategoriesColors])
@@ -144,7 +143,7 @@ static SoProduct *preferencesProduct = nil;
[defaults setCalendarCategoriesColors: colors];
}
//
// Default Contacts preferences
//
@@ -155,10 +154,10 @@ static SoProduct *preferencesProduct = nil;
categoryLabels = [[[self labelForKey: @"contacts_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
if (!categoryLabels)
categoryLabels = [NSArray array];
[defaults setContactsCategories: categoryLabels];
}
@@ -173,27 +172,27 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoMailReplyPlacement"])
[[defaults source] setObject: [defaults mailReplyPlacement] forKey: @"SOGoMailReplyPlacement"];
if (![[defaults source] objectForKey: @"SOGoMailSignaturePlacement"])
[[defaults source] setObject: [defaults mailSignaturePlacement] forKey: @"SOGoMailSignaturePlacement"];
if (![[defaults source] objectForKey: @"SOGoMailComposeMessageType"])
[[defaults source] setObject: [defaults mailComposeMessageType] forKey: @"SOGoMailComposeMessageType"];
if (![[defaults source] objectForKey: @"SOGoMailDisplayRemoteInlineImages"])
[[defaults source] setObject: [defaults mailDisplayRemoteInlineImages] forKey: @"SOGoMailDisplayRemoteInlineImages"];
// Populate default mail lablels, based on the user's preferred language
if (![[defaults source] objectForKey: @"SOGoMailLabelsColors"])
{
SOGoMailLabel *label;
NSArray *labels;
id v;
int i;
v = [defaults mailLabelsColors];
labels = [SOGoMailLabel labelsFromDefaults: v component: self];
v = [NSMutableDictionary dictionary];
for (i = 0; i < [labels count]; i++)
@@ -202,7 +201,7 @@ static SoProduct *preferencesProduct = nil;
[v setObject: [NSArray arrayWithObjects: [label label], [label color], nil]
forKey: [label name]];
}
[defaults setMailLabelsColors: v];
}
@@ -215,10 +214,6 @@ static SoProduct *preferencesProduct = nil;
//
values = [[[[defaults source] values] mutableCopy] autorelease];
// Add locale code (used by CK Editor)
locale = [[preferencesProduct resourceManager] localeForLanguageNamed: [defaults language]];
[values setObject: [locale objectForKey: @"NSLocaleCode"] forKey: @"LocaleCode"];
accounts = [NSMutableArray arrayWithArray: [values objectForKey: @"AuxiliaryMailAccounts"]];
account = [[[context activeUser] mailAccounts] objectAtIndex: 0];
if (![account objectForKey: @"receipts"])
@@ -255,16 +250,6 @@ static SoProduct *preferencesProduct = nil;
forKey: @"PreventInvitationsWhitelist"];
}
// Initialize some default values
if (![settings objectForKey: @"Calendar"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Calendar"];
if (![settings objectForKey: @"Contact"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Contact"];
if (![settings objectForKey: @"Mail"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Mail"];
return [self _makeResponse: [[settings source] values]];
}
+1 -10
View File
@@ -169,11 +169,6 @@ static NSArray *reminderValues = nil;
[super dealloc];
}
- (NSString *) modulePath
{
return @"Preferences";
}
// - (void) setHasChanged: (BOOL) newHasChanged
// {
// hasChanged = newHasChanged;
@@ -1069,7 +1064,7 @@ static NSArray *reminderValues = nil;
if (!capabilities)
{
if ([self isSieveScriptsEnabled] && [self _sieveClient])
if ([self _sieveClient])
capabilities = [[self _sieveClient] capabilities];
else
capabilities = [NSArray array];
@@ -2091,10 +2086,6 @@ static NSArray *reminderValues = nil;
// We convert our object into a mutable one
v = [[v mutableCopy] autorelease];
if ([[v objectForKey: @"SOGoLoginModule"] isEqualToString: @"Last"])
[v setObject: [NSNumber numberWithBool: YES] forKey: @"SOGoRememberLastModule"];
else
[v setObject: [NSNumber numberWithBool: NO] forKey: @"SOGoRememberLastModule"];
// We remove short/long date formats if they are default ones
if ([[v objectForKey: @"SOGoShortDateFormat"] isEqualToString: @"default"])
+30 -9
View File
@@ -360,19 +360,40 @@ static SoProduct *commonProduct = nil;
- (NSString *) modulePath
{
if ([[self parent] respondsToSelector: @selector(modulePath)])
SOGoObject *currentClient, *parent;
BOOL found;
NSString *hostLessURL;
Class objectClass, userFolderClass;
// , groupFolderClass
currentClient = [self clientObject];
if (currentClient
&& [currentClient isKindOfClass: [SOGoObject class]])
{
NSString *baseURL;
// groupFolderClass = [SOGoCustomGroupFolder class];
userFolderClass = [SOGoUserFolder class];
baseURL = [[self clientObject] baseURLInContext: context];
if ([baseURL hasSuffix: [NSString stringWithFormat: @"%@/", [[self parent] modulePath]]])
return baseURL;
return [NSString stringWithFormat: @"%@%@", baseURL, [[self parent] modulePath]];
objectClass = [currentClient class];
// found = (objectClass == groupFolderClass || objectClass == userFolderClass);
found = (objectClass == userFolderClass);
while (!found && currentClient)
{
parent = [currentClient container];
objectClass = [parent class];
if (// objectClass == groupFolderClass
// ||
objectClass == userFolderClass)
found = YES;
else
currentClient = parent;
}
}
else
currentClient = [WOApplication application];
hostLessURL = [[currentClient baseURLInContext: context] hostlessURL];
return @"SOGo";
return [hostLessURL substringToIndex: [hostLessURL length] - 1];
}
- (NSString *) ownPath
@@ -439,9 +439,6 @@ validate_endbeforestart = "The end date that you entered occurs before the st
"Month View" = "Month View";
"Reload" = "Reload";
/* Number of selected components in events or tasks list */
"selected" = "selected";
"eventPartStatModificationError" = "Your participation status could not be modified.";
/* menu */
+3 -3
View File
@@ -595,12 +595,12 @@
* @apiSuccess (Success 200) {Boolean} alarm.attendees Alert attendees by email if true and action is email
* @apiSuccess (Success 200) {Boolean} alarm.organizer Alert organizer by email if true and action is email
*
* @apiSuccess {_} ... _From [iCalEvent+SOGo attributesInContext:]_
* @apiSuccess {_} ... _From [iCalEvent+SOGo attributes]_
*
* @apiSuccess (Success 200) {Number} isAllDay 1 if event is all-day
* @apiSuccess (Success 200) {Number} isTransparent 1 if the event is not opaque
*
* @apiSuccess {_} .... _From [iCalEntityObject+SOGo attributesInContext:]_
* @apiSuccess {_} .... _From [iCalEntityObject+SOGo attributes]_
*
* @apiSuccess (Success 200) {Number} sendAppointmentNotifications 1 if notifications must be sent
* @apiSuccess (Success 200) {String} component "vevent"
@@ -626,7 +626,7 @@
* @apiSuccess (Success 200) {String} [attendees.delegatedTo] User that the original request was delegated to
* @apiSuccess (Success 200) {String} [attendees.delegatedFrom] User the request was delegated from
*
* @apiSuccess {_} ..... _From [iCalRepeatableEntityObject+SOGo attributesInContext:]_
* @apiSuccess {_} ..... _From [iCalRepeatableEntityObject+SOGo attributes]_
*
* @apiSuccess (Success 200) {Object} [repeat] Recurrence rule definition
* @apiSuccess (Success 200) {String} repeat.frequency Either daily, (every weekday), weekly, (bi-weekly), monthly, or yearly
+1 -1
View File
@@ -39,7 +39,7 @@
NSArray *weekDays;
NSString *currentView, *timeFormat, *currentTableHour;
NSCalendarDate *startDate, *currentTableDay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
unsigned int numberOfDays;
}
+18 -59
View File
@@ -37,9 +37,6 @@
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
#import "UIxCalDayTable.h"
@class SOGoAppointment;
@@ -187,6 +184,7 @@
{
if (!calendarsToDisplay)
{
int max=0, i;
NSArray *folders;
SOGoAppointmentFolders *co;
SOGoAppointmentFolder *folder;
@@ -294,39 +292,6 @@
return [weekDays objectAtIndex: [currentTableDay dayOfWeek]];
}
- (NSString *) labelForMonth
{
NSCalendarDate *nextDay;
NSString *calendarFormat;
BOOL isLastOrFirstDay;
isLastOrFirstDay = NO;
calendarFormat = @"%b";
if ([currentView hasSuffix: @"dayview"])
{
isLastOrFirstDay = YES;
calendarFormat = @"%B";
}
else
{
if ([currentTableDay dayOfMonth] == 1)
{
isLastOrFirstDay = YES;
}
else if ([currentTableDay dayOfMonth] > 27)
{
nextDay = [currentTableDay dateByAddingYears: 0
months: 0
days: 1];
if ([nextDay dayOfMonth] == 1)
isLastOrFirstDay = YES;
}
}
return isLastOrFirstDay? [currentTableDay descriptionWithCalendarFormat: calendarFormat locale: locale] : nil;
}
- (NSString *) labelForDate
{
return [dateFormatter shortFormattedDate: currentTableDay];
@@ -432,23 +397,18 @@
numberOfDays];
}
// - (NSString *) daysViewClasses
// {
// NSString *daysView;
- (NSString *) daysViewClasses
{
NSString *daysView;
// if ([currentView isEqualToString:@"multicolumndayview"])
// daysView = @"daysView daysViewForMultipleDays";
if ([currentView isEqualToString:@"multicolumndayview"])
daysView = @"daysView daysViewForMultipleDays";
// else
// daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
else
daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
// return daysView;
//}
// - (NSString *) daysViewHeaderClasses
// {
// return [NSString stringWithFormat: @"%@ daysHeader", [self daysViewClasses]];
// }
return daysView;
}
- (NSString *) dayClasses
{
@@ -456,18 +416,22 @@
unsigned int currentDayNbr, realDayOfWeek;
classes = [NSMutableString string];
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
realDayOfWeek = [currentTableDay dayOfWeek];
if ([currentView isEqualToString:@"multicolumndayview"])
[classes appendFormat:@"day dayColumn"];
else {
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
realDayOfWeek = [currentTableDay dayOfWeek];
[classes appendFormat: @"day day%d", currentDayNbr];
[classes appendFormat: @"day day%d", currentDayNbr];
if (numberOfDays > 1)
if (numberOfDays > 1)
{
if (realDayOfWeek == 0 || realDayOfWeek == 6)
[classes appendString: @" weekEndDay"];
if ([currentTableDay isToday])
[classes appendString: @" dayOfToday"];
}
}
return classes;
}
@@ -504,9 +468,4 @@
return NO;
}
- (BOOL) isWeekView
{
return [currentView isEqualToString:@"weekview"];
}
@end
+35 -81
View File
@@ -94,7 +94,7 @@ static NSArray *tasksFields = nil;
@"c_classification", @"c_category",
@"c_partmails", @"c_partstates", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"viewable", @"editable",
@"c_recurrence_id", @"isException", @"editable",
@"erasable", @"ownerIsOrganizer", @"c_description", nil];
[eventsFields retain];
}
@@ -104,7 +104,7 @@ static NSArray *tasksFields = nil;
@"calendarName",
@"c_status", @"c_title", @"c_enddate",
@"c_classification", @"c_location", @"c_category",
@"viewable", @"editable", @"erasable",
@"editable", @"erasable",
@"c_priority", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"c_description", nil];
@@ -258,7 +258,7 @@ static NSArray *tasksFields = nil;
labelKey = [NSString stringWithFormat: @"%@_class%@",
type, [component objectForKey: @"c_classification"]];
[component setObject: [self labelForKey: labelKey inContext: context]
[component setObject: [self labelForKey: labelKey]
forKey: @"c_title"];
}
@@ -328,24 +328,16 @@ static NSArray *tasksFields = nil;
iCalPersonPartStat stat;
NSUInteger count, max;
states = nil;
if ([[theRecord objectForKey: @"c_partmails"] length] > 0)
if ([[theRecord objectForKey: @"c_partmails"] length] > 0 &&
[[theRecord objectForKey: @"c_partstates"] length] > 0)
{
mails = [[theRecord objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"];
// We add some robustness here, in case the quick table had c_partmails defined but
// no corresponding c_partstates entries. This can happen if folks toy around the database
// or if Funambol was used in the past, with a broken connector.
if ([[theRecord objectForKey: @"c_partstates"] length] > 0)
states = [[theRecord objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
states = [[theRecord objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
max = [mails count];
statesDescription = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
stat = (states ? [[states objectAtIndex: count] intValue] : 0);
stat = [[states objectAtIndex: count] intValue];
[statesDescription addObject: [[iCalPerson descriptionForParticipationStatus: stat] lowercaseString]];
}
@@ -449,35 +441,21 @@ static NSArray *tasksFields = nil;
}
else
{
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
id foo;
foo = [currentFolder fetchCoreInfosFrom: startDate
to: endDate
title: value
component: component]
objectEnumerator];
component: component];
currentInfos = [foo objectEnumerator];
}
owner = [currentFolder ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: owner];
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
while ((newInfo = [currentInfos nextObject]))
{
if ([fields containsObject: @"viewable"])
{
if ([owner isEqualToString: userLogin])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
{
role = [currentFolder roleForComponentsWithAccessClass: [[newInfo objectForKey: @"c_classification"] intValue]
forUser : userLogin];
if ([role isEqualToString: @"ComponentViewer"])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
[newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"viewable"];
}
}
if ([fields containsObject: @"editable"])
{
if (folderIsRemote)
@@ -570,19 +548,19 @@ static NSArray *tasksFields = nil;
{
// Same day
if (forAllDay)
return [self labelForKey: @"Today" inContext: context];
return [self labelForKey: @"Today"];
else
return [dateFormatter formattedTime: date];
}
else if ([now dayOfCommonEra] - [date dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday" inContext: context];
return [self labelForKey: @"Yesterday"];
}
else if ([date dayOfCommonEra] - [now dayOfCommonEra] == 1)
{
// Tomorrow
return [self labelForKey: @"Tomorrow" inContext: context];
return [self labelForKey: @"Tomorrow"];
}
else if (abs(delta = [date dayOfCommonEra] - [now dayOfCommonEra]) < 7)
{
@@ -595,7 +573,7 @@ static NSArray *tasksFields = nil;
return dayOfWeek;
else
// With the past 7 days
return [NSString stringWithFormat: [self labelForKey: @"last %@" inContext: context], dayOfWeek];
return [NSString stringWithFormat: [self labelForKey: @"last %@"], dayOfWeek];
}
else
{
@@ -674,11 +652,10 @@ static NSArray *tasksFields = nil;
return [self _responseWithData: data];
}
- (void) saveFilterValue: (NSString *) submodule
- (void) saveFilterValue
{
NSString *filter;
SOGoUserSettings *us;
NSMutableDictionary *calendarSettings;
filter = [[context request] formValueForKey: @"filterpopup"];
if ([filter length]
@@ -686,20 +663,12 @@ static NSArray *tasksFields = nil;
&& ![filter isEqualToString: @"view_future"])
{
us = [[context activeUser] userSettings];
calendarSettings = [us objectForKey: @"Calendar"];
// Must create if it doesn't exist
if (!calendarSettings)
{
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
//[us setObject: filter forKey: submodule];
[calendarSettings setObject: filter forKey: submodule];
[us setObject: filter forKey: @"CalendarDefaultFilter"];
[us synchronize];
}
}
- (void) saveSortValue: (NSString *) submodule
- (void) saveSortValue
{
NSString *sort, *ascending;
SOGoUserSettings *us;
@@ -717,8 +686,7 @@ static NSArray *tasksFields = nil;
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: submodule];
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", (ascending?1:0)], nil] forKey: @"EventsSortingState"];
[us synchronize];
}
}
@@ -754,11 +722,10 @@ static NSArray *tasksFields = nil;
* @apiSuccess (Success 200) {String[]} events.c_partmails Participants email addresses
* @apiSuccess (Success 200) {String[]} events.c_partstates Participants states
* @apiSuccess (Success 200) {String} events.c_owner Event's owner
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic/recurring
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -776,8 +743,8 @@ static NSArray *tasksFields = nil;
NSString *sort, *ascending;
[self _setupContext];
[self saveFilterValue: @"EventsFilterState"];
[self saveSortValue: @"EventsSortingState"];
[self saveFilterValue];
[self saveSortValue];
newEvents = [NSMutableArray array];
events = [[self _fetchFields: eventsFields
@@ -900,10 +867,10 @@ static inline iCalPersonPartStat _userStateInEvent (NSArray *event)
states = [event objectAtIndex: eventPartStatesIndex];
count = 0;
max = [participants count];
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
roles: nil];
while (state == iCalPersonPartStatOther && count < max)
{
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
roles: nil];
if ([user hasEmail: [participants objectAtIndex: count]])
state = [[states objectAtIndex: count] intValue];
else
@@ -1225,7 +1192,7 @@ _computeBlocksPosition (NSArray *blocks)
SOGoAppointmentFolder *folder;
NSMutableArray *selectedCalendars;
NSArray *folders;
NSString *fUID, *fName;
NSString *fUID;
NSNumber *isActive;
unsigned int count, foldersCount;
@@ -1239,11 +1206,7 @@ _computeBlocksPosition (NSArray *blocks)
isActive = [NSNumber numberWithBool: [folder isActive]];
if ([isActive intValue] != 0) {
fUID = [folder nameInContainer];
fName = [folder displayName];
[selectedCalendars addObject: [NSDictionary dictionaryWithObjectsAndKeys:
fUID, @"id",
fName, @"name",
nil]];
[selectedCalendars addObject: fUID];
}
}
return selectedCalendars;
@@ -1281,7 +1244,6 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -1301,10 +1263,9 @@ _computeBlocksPosition (NSArray *blocks)
{
int count, max;
NSArray *events, *event, *calendars;
NSDictionary *eventsBlocks, *calendar;
NSMutableArray *allDayBlocks, *blocks, *currentDay, *eventsForCalendar, *eventsByCalendars;
NSDictionary *eventsBlocks;
NSMutableArray *allDayBlocks, *blocks, *currentDay, *eventsByCalendars, *eventsForCalendar;
NSNumber *eventNbr;
NSString *calendarName, *calendarId;
BOOL isAllDay;
int i, j;
@@ -1318,21 +1279,16 @@ _computeBlocksPosition (NSArray *blocks)
eventsByCalendars = [NSMutableArray arrayWithCapacity:[calendars count]];
for (i = 0; i < [calendars count]; i++) // For each calendar
{
calendar = [calendars objectAtIndex:i];
calendarName =[calendar objectForKey: @"name"];
calendarId = [calendar objectForKey: @"id"];
eventsForCalendar = [NSMutableArray array];
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
for (j = 0; j < [events count]; j++) {
if ([[[events objectAtIndex:j] objectAtIndex:eventFolderIndex] isEqualToString: calendarId]) {
if ([[[events objectAtIndex:j] objectAtIndex:eventFolderIndex] isEqualToString:[calendars objectAtIndex:i]]) {
// Event is in current calendar
[eventsForCalendar addObject: [events objectAtIndex:j]];
}
}
eventsBlocks = [NSDictionary dictionaryWithObjectsAndKeys:
calendarId , @"id",
calendarName, @"calendarName",
eventsFields, @"eventsFields",
eventsFields, @"eventsFields",
eventsForCalendar, @"events",
allDayBlocks, @"allDayBlocks",
blocks, @"blocks", nil];
@@ -1354,7 +1310,8 @@ _computeBlocksPosition (NSArray *blocks)
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
}
[eventsByCalendars addObject: eventsBlocks];
[eventsByCalendars insertObject:eventsBlocks atIndex:i];
}
return [self _responseWithData: eventsByCalendars];
}
@@ -1390,7 +1347,7 @@ _computeBlocksPosition (NSArray *blocks)
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
}
return [self _responseWithData: [NSArray arrayWithObject: eventsBlocks]];
return [self _responseWithData: eventsBlocks];
}
}
@@ -1451,7 +1408,6 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} tasks.c_classification 0: Public, 1: Private, 2: Confidential
* @apiSuccess (Success 200) {String} tasks.c_location Location
* @apiSuccess (Success 200) {String} tasks.c_category Category
* @apiSuccess (Success 200) {Number} tasks.viewable 1 if task is viewable by the active user
* @apiSuccess (Success 200) {Number} tasks.editable 1 if task is editable by the active user
* @apiSuccess (Success 200) {Number} tasks.erasable 1 if task is erasable by the active user
* @apiSuccess (Success 200) {String} tasks.c_priority Priority (0-9)
@@ -1480,8 +1436,6 @@ _computeBlocksPosition (NSArray *blocks)
filteredTasks = [NSMutableArray array];
[self _setupContext];
[self saveFilterValue: @"TasksFilterState"];
[self saveSortValue: @"TasksSortingState"];
startSecs = (unsigned int) [startDate timeIntervalSince1970];
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
+1 -9
View File
@@ -33,7 +33,6 @@
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -46,11 +45,6 @@
@implementation UIxCalMainView
- (NSString *) modulePath
{
return @"Calendar";
}
- (void) checkDefaultModulePreference
{
SOGoUserDefaults *ud;
@@ -267,14 +261,12 @@
- (WOResponse *) saveSelectedListAction
{
WORequest *request;
NSDictionary *params;
NSString *selectedList;
[self _setupContext];
request = [context request];
params = [[request contentAsString] objectFromJSONString];
selectedList = [params objectForKey: @"list"];
selectedList = [request formValueForKey: @"list"];
[moduleSettings setObject: selectedList
forKey: @"SelectedList"];
[us synchronize];
+4 -1
View File
@@ -1,6 +1,8 @@
/* UIxCalMonthView.h - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2009 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
@@ -67,6 +69,7 @@
- (void) setCurrentTableDay: (NSCalendarDate *) newCurrentTableDay;
- (NSCalendarDate *) currentTableDay;
- (NSString *) labelForCurrentDayCell;
@end
+33 -5
View File
@@ -1,6 +1,8 @@
/* UIxCalMonthView.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006, 2007 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
@@ -233,6 +235,27 @@
return currentWeek;
}
- (NSString *) labelForCurrentDayCell
{
NSCalendarDate *lastDayOfMonth;
NSString *label, *monthOfYear;
int dayOfMonth;
dayOfMonth = [currentTableDay dayOfMonth];
lastDayOfMonth = [currentTableDay lastDayOfMonth];
if (dayOfMonth == 1
|| [currentTableDay isDateOnSameDay: lastDayOfMonth])
{
monthOfYear
= [monthNames objectAtIndex: [currentTableDay monthOfYear]];
label = [NSString stringWithFormat: @"%d %@", dayOfMonth, monthOfYear];
}
else
label = [NSString stringWithFormat: @"%d", dayOfMonth];
return label;
}
- (NSString *) headerDayCellClasses
{
unsigned int dayOfWeek;
@@ -242,19 +265,24 @@
return [NSString stringWithFormat: @"headerDay day%d", dayOfWeek];
}
- (NSString *) dayHeaderMonth
- (NSString *) dayHeaderNumber
{
NSString *nameOfMonth = nil;
NSString *nameOfMonth, *dayHeaderNumber;
unsigned int dayOfMonth;
dayOfMonth = [currentTableDay dayOfMonth];
if (dayOfMonth == 1
|| [currentTableDay isDateOnSameDay: [currentTableDay lastDayOfMonth]])
{
nameOfMonth = [self localizedNameForMonthOfYear: [currentTableDay monthOfYear]];
nameOfMonth
= [self localizedNameForMonthOfYear: [currentTableDay monthOfYear]];
dayHeaderNumber = [NSString stringWithFormat: @"%d %@", dayOfMonth,
nameOfMonth];
}
else
dayHeaderNumber = [NSString stringWithFormat: @"%d", dayOfMonth];
return nameOfMonth;
return dayHeaderNumber;
}
- (NSString *) dayCellClasses
@@ -20,6 +20,14 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoAppointmentFolder;
@interface UIxCalendarFolderLinksTemplate : UIxComponent
{
SOGoAppointmentFolder *calendar;
NSString *baseCalDAVURL, *basePublicCalDAVURL;
}
@end
@@ -28,6 +28,89 @@
@implementation UIxCalendarFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
calendar = [self clientObject];
baseCalDAVURL = nil;
basePublicCalDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCalDAVURL release];
[basePublicCalDAVURL release];
[super dealloc];
}
- (NSString *) _baseCalDAVURL
{
NSString *davURL;
if (!baseCalDAVURL)
{
davURL = [[calendar realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCalDAVURL = davURL;
[baseCalDAVURL retain];
}
return baseCalDAVURL;
}
- (NSString *) calDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
}
- (NSString *) webDavICSURL
{
return [calendar webDavICSURL];
}
- (NSString *) webDavXMLURL
{
return [calendar webDavXMLURL];
}
- (NSString *) _basePublicCalDAVURL
{
NSString *davURL;
if (!basePublicCalDAVURL)
{
davURL = [[calendar publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCalDAVURL = davURL;
[basePublicCalDAVURL retain];
}
return basePublicCalDAVURL;
}
- (NSString *) publicCalDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavICSURL
{
return [calendar publicWebDavICSURL];
}
- (NSString *) publicWebDavXMLURL
{
return [calendar publicWebDavXMLURL];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
@@ -35,4 +118,14 @@
enablePublicAccess];
}
- (BOOL) isWebCalendar
{
return ([calendar isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) webCalendarURL
{
return [calendar folderPropertyValueInCategory: @"WebCalendars"];
}
@end
Loaded 100 of 303 files, more files were not shown because too many files have changed in this diff. Show more