mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-27 12:54:17 +00:00
Merge to 2.2.4
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
config.make
|
||||
tags
|
||||
*._*
|
||||
*/obj/
|
||||
*._*
|
||||
*/*/obj/
|
||||
*/*/*/obj/
|
||||
*/*/*.SOGo/
|
||||
@@ -11,3 +13,4 @@ SoObjects/SOGo/SOGo.framework/
|
||||
SoObjects/SOGo/derived_src/
|
||||
Tests/*/config.py
|
||||
*.pyc
|
||||
._*
|
||||
|
||||
@@ -14,16 +14,18 @@ ActiveSync_OBJC_FILES = \
|
||||
iCalRecurrenceRule+ActiveSync.m \
|
||||
iCalTimeZone+ActiveSync.m \
|
||||
iCalToDo+ActiveSync.m \
|
||||
NSCalendarDate+ActiveSync.m \
|
||||
NSCalendarDate+ActiveSync.m \
|
||||
NSData+ActiveSync.m \
|
||||
NSDate+ActiveSync.m \
|
||||
NGDOMElement+ActiveSync.m \
|
||||
NGMimeMessage+ActiveSync.m \
|
||||
NGVCard+ActiveSync.m \
|
||||
NSArray+SyncCache.m \
|
||||
NSString+ActiveSync.m \
|
||||
SOGoActiveSyncDispatcher.m \
|
||||
SOGoActiveSyncDispatcher+Sync.m \
|
||||
SOGoMailObject+ActiveSync.m \
|
||||
SOGoMailObject+ActiveSync.m \
|
||||
SOGoSyncCacheObject.m \
|
||||
SoObjectWebDAVDispatcher+ActiveSync.m
|
||||
|
||||
ActiveSync_RESOURCE_FILES += \
|
||||
|
||||
@@ -37,6 +37,28 @@ static NSArray *asElementArray = nil;
|
||||
|
||||
@implementation NGDOMElement (ActiveSync)
|
||||
|
||||
- (BOOL) isTextNode
|
||||
{
|
||||
id <DOMNodeList> children;
|
||||
id <DOMElement> element;
|
||||
int i;
|
||||
|
||||
if ([self nodeType] == DOM_TEXT_NODE)
|
||||
return YES;
|
||||
|
||||
children = [self childNodes];
|
||||
|
||||
for (i = 0; i < [children length]; i++)
|
||||
{
|
||||
element = [children objectAtIndex: i];
|
||||
|
||||
if ([element nodeType] != DOM_TEXT_NODE)
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//
|
||||
// We must handle "inner data" like this:
|
||||
//
|
||||
@@ -79,7 +101,7 @@ static NSArray *asElementArray = nil;
|
||||
int i, count;
|
||||
|
||||
if (!asElementArray)
|
||||
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil];
|
||||
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", nil];
|
||||
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
@@ -96,9 +118,17 @@ static NSArray *asElementArray = nil;
|
||||
|
||||
tag = [element tagName];
|
||||
count = [(NSArray *)[element childNodes] count];
|
||||
|
||||
|
||||
// We check if the node is a text one or if all its
|
||||
// children are text nodes. This is important to avoid side-effects
|
||||
// in SOPE where "foo & bar" would result into 3 childnodes instead
|
||||
// of just one.
|
||||
if ([(id)element isTextNode])
|
||||
{
|
||||
value = [(id)element textValue];
|
||||
}
|
||||
// Handle inner data - see above for samples
|
||||
if (count > 2)
|
||||
else
|
||||
{
|
||||
NSMutableArray *innerElements;
|
||||
id <DOMElement> innerElement;
|
||||
@@ -123,7 +153,10 @@ static NSArray *asElementArray = nil;
|
||||
|
||||
if ([innerTag isEqualToString: [innerElement tagName]])
|
||||
{
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
|
||||
if ([(id)innerElement isTextNode])
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement textValue]];
|
||||
else
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -144,12 +177,11 @@ static NSArray *asElementArray = nil;
|
||||
value = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
value = [[element firstChild] textValue];
|
||||
|
||||
if (value && tag)
|
||||
[data setObject: value forKey: tag];
|
||||
}
|
||||
|
||||
} // if ([element nodeType] == DOM_ELEMENT_NODE)
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -47,9 +47,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *emails, *addresses, *categories, *elements;
|
||||
CardElement *n, *homeAdr, *workAdr;
|
||||
NSArray *emails, *addresses;
|
||||
NSMutableString *s;
|
||||
NSString *url;
|
||||
id o;
|
||||
|
||||
int i;
|
||||
@@ -65,10 +66,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [self workCompany]))
|
||||
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [[self org] flattenedValueAtIndex: 1 forKey: @""]))
|
||||
[s appendFormat: @"<Department xmlns=\"Contacts:\">%@</Department>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Contacts:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Contacts:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
elements = [self childrenWithTag: @"url"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"work"];
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
url = [[elements objectAtIndex: 0] flattenedValuesForKey: @""];
|
||||
[s appendFormat: @"<WebPage xmlns=\"Contacts:\">%@</WebPage>", [url activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
|
||||
if ((o = [[self uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]))
|
||||
[s appendFormat: @"<IMAddress xmlns=\"Contacts:\">%@</IMAddress>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self nickname]))
|
||||
[s appendFormat: @"<NickName xmlns=\"Contacts:\">%@</NickName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
if ((o = [self title]))
|
||||
[s appendFormat: @"<JobTitle xmlns=\"Contacts:\">%@</JobTitle>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
if ((o = [self preferredEMail]))
|
||||
[s appendFormat: @"<Email1Address xmlns=\"Contacts:\">%@</Email1Address>", o];
|
||||
|
||||
@@ -154,7 +187,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Other, less important fields
|
||||
if ((o = [self birthday]))
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
@@ -183,6 +216,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setNote: o];
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]))
|
||||
[self setCategories: o];
|
||||
|
||||
// Birthday
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
@@ -238,24 +275,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Company's name
|
||||
if ((o = [theValues objectForKey: @"CompanyName"]))
|
||||
{
|
||||
[self setOrg: o units: nil];
|
||||
}
|
||||
[self setOrg: o units: nil];
|
||||
|
||||
// Department
|
||||
if ((o = [theValues objectForKey: @"Department"]))
|
||||
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
|
||||
|
||||
// Email addresses
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]))
|
||||
{
|
||||
[self addEmail: o types: [NSArray arrayWithObject: @"pref"]];
|
||||
element = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]))
|
||||
{
|
||||
[self addEmail: o types: nil];
|
||||
element = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
}
|
||||
|
||||
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
|
||||
// FIXME: revise this when the GUI revamp is done in SOGo
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]))
|
||||
{
|
||||
[self addEmail: o types: nil];
|
||||
element = [self elementWithTag: @"email" ofType: @"three"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
}
|
||||
|
||||
// Formatted name
|
||||
@@ -293,16 +337,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Job's title
|
||||
if ((o = [theValues objectForKey: @"JobTitle"]))
|
||||
{
|
||||
[self setTitle: o];
|
||||
}
|
||||
[self setTitle: o];
|
||||
|
||||
// WebPage (work)
|
||||
if ((o = [theValues objectForKey: @"WebPage"]))
|
||||
{
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
setSingleValue: o forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"NickName"]))
|
||||
[self setNickname: o];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NSARRAYSYNCCACHE_H__
|
||||
#define __NSARRAYSYNCCACHE_H__
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
@class NSDictionary;
|
||||
|
||||
@interface NSMutableArray (SyncCache)
|
||||
|
||||
- (id) initWithDictionary: (NSDictionary *) theDictionary;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSArray (SyncCache)
|
||||
|
||||
- (NSDictionary *) dictionaryValue;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NSArray+SyncCache.h"
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
#include "SOGoSyncCacheObject.h"
|
||||
|
||||
@implementation NSMutableArray (SyncCache)
|
||||
|
||||
- (id) initWithDictionary: (NSDictionary *) theDictionary
|
||||
{
|
||||
SOGoSyncCacheObject *o;
|
||||
NSArray *allKeys;
|
||||
id key;
|
||||
int i;
|
||||
|
||||
self = [self initWithCapacity: [theDictionary count]];
|
||||
|
||||
allKeys = [theDictionary allKeys];
|
||||
|
||||
for (i = 0; i < [allKeys count]; i++)
|
||||
{
|
||||
key = [allKeys objectAtIndex: i];
|
||||
o = [SOGoSyncCacheObject syncCacheObjectWithUID: key
|
||||
sequence: [theDictionary objectForKey: key]];
|
||||
[self addObject: o];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@implementation NSArray (SyncCache)
|
||||
|
||||
- (NSDictionary *) dictionaryValue
|
||||
{
|
||||
NSMutableDictionary *d;
|
||||
SOGoSyncCacheObject *o;
|
||||
int i;
|
||||
|
||||
d = [NSMutableDictionary dictionary];
|
||||
|
||||
for (i = 0; i < [self count]; i++)
|
||||
{
|
||||
o = [self objectAtIndex: i];
|
||||
[d setObject: [o sequence] forKey: [o uid]];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -32,9 +32,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/SoApplication.h>
|
||||
@@ -72,6 +74,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
#import <Appointments/SOGoAppointmentObject.h>
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
@@ -101,11 +104,48 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "NSString+ActiveSync.h"
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
#include "SOGoMailObject+ActiveSync.h"
|
||||
#include "SOGoSyncCacheObject.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@implementation SOGoActiveSyncDispatcher (Sync)
|
||||
|
||||
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
|
||||
forKey: (NSString *) theFolderKey
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
NSString *key;
|
||||
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
|
||||
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
||||
[o save];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
NSString *key;
|
||||
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
return [o properties];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// <?xml version="1.0"?>
|
||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||
@@ -423,7 +463,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[theBuffer appendString: @"</Fetch>"];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// The method handles <GetChanges/>
|
||||
//
|
||||
@@ -450,6 +489,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
return;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
more_available = NO;
|
||||
|
||||
switch (theFolderType)
|
||||
@@ -567,106 +607,189 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
NSMutableArray *addedOrChangedMessages;
|
||||
NSString *uid, *command, *key;
|
||||
NSMutableDictionary *syncCache, *dateCache, *folderMetadata;
|
||||
SOGoSyncCacheObject *lastCacheObject, *aCacheObject;
|
||||
NSMutableArray *allCacheObjects, *sortedBySequence;
|
||||
|
||||
SOGoMailObject *mailObject;
|
||||
NSDictionary *aMessage;
|
||||
NSArray *allMessages;
|
||||
int deleted_count;
|
||||
|
||||
int j, k, return_count;
|
||||
BOOL found_in_cache;
|
||||
|
||||
|
||||
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
|
||||
addedOrChangedMessages = [NSMutableArray array];
|
||||
deleted_count = 0;
|
||||
|
||||
// Check for the WindowSize.
|
||||
// FIXME: we should eventually check for modseq and slice the maximum
|
||||
// amount of messages returned to ensure we don't have the same
|
||||
// modseq accross contiguous boundaries
|
||||
max = [allMessages count];
|
||||
|
||||
// We first check the number of deleted messages we have
|
||||
// We do NOT honor the window size here as it seems to be
|
||||
// impossible to get the modseq of an expunged message so
|
||||
// we can't iterate in the list of deleted messages.
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
aMessage = [allMessages objectAtIndex: i];
|
||||
|
||||
uid = [[[aMessage allKeys] lastObject] stringValue];
|
||||
command = [[aMessage allValues] lastObject];
|
||||
|
||||
if ([command isEqualToString: @"deleted"])
|
||||
{
|
||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
[s appendString: @"</Delete>"];
|
||||
deleted_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
[addedOrChangedMessages addObject: aMessage];
|
||||
}
|
||||
}
|
||||
|
||||
// We then "pad" with our added/changed messages. We ALWAYS
|
||||
// at least return one if available
|
||||
max = [addedOrChangedMessages count];
|
||||
allCacheObjects = [NSMutableArray array];
|
||||
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
aMessage = [addedOrChangedMessages objectAtIndex: i];
|
||||
|
||||
uid = [[[aMessage allKeys] lastObject] stringValue];
|
||||
command = [[aMessage allValues] lastObject];
|
||||
|
||||
// We check for Outlook stupidity to avoid creating duplicates - see the comment
|
||||
// in SOGoActiveSyncDispatcher.m: -processMoveItems:inResponse: for more details.
|
||||
key = [NSString stringWithFormat: @"%@+%@+%@+%@",
|
||||
[[context activeUser] login],
|
||||
[context objectForKey: @"DeviceType"],
|
||||
[theCollection displayName],
|
||||
uid];
|
||||
|
||||
if ([[SOGoCache sharedCache] valueForKey: key])
|
||||
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
|
||||
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
|
||||
}
|
||||
|
||||
// If it's a new Sync operation, DateCache and SyncCache need to be deleted
|
||||
// but GUID stored by folderSync shouldn't be touched
|
||||
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
|
||||
if ([theSyncKey isEqualToString: @"-1"])
|
||||
{
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
||||
}
|
||||
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
|
||||
// with the same name has been created but folderSync has not yet updated the cache
|
||||
if (!([[theCollection nameInContainer] isEqualToString:
|
||||
[NSString stringWithFormat: @"folder%@", [self globallyUniqueIDToIMAPFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]]))
|
||||
{
|
||||
NSLog(@"GUID mismatch don't sync now!");
|
||||
return;
|
||||
}
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
|
||||
[sortedBySequence sortUsingSelector: @selector(compareSequence:)];
|
||||
[sortedBySequence autorelease];
|
||||
|
||||
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
|
||||
|
||||
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
|
||||
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
|
||||
|
||||
lastCacheObject = [sortedBySequence lastObject];
|
||||
|
||||
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
|
||||
{
|
||||
for (j = 0; j < [allCacheObjects count]; j++)
|
||||
{
|
||||
[[SOGoCache sharedCache] removeValueForKey: key];
|
||||
command = @"changed";
|
||||
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
|
||||
{
|
||||
// Found out where we're at, let's continue from there...
|
||||
found_in_cache = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ([command isEqualToString: @"added"])
|
||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
||||
else
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
|
||||
mailObject = [theCollection lookupName: uid
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</ApplicationData>"];
|
||||
|
||||
if ([command isEqualToString: @"added"])
|
||||
[s appendString: @"</Add>"];
|
||||
else
|
||||
[s appendString: @"</Change>"];
|
||||
|
||||
|
||||
// We check if we must stop padding
|
||||
if (i+1+deleted_count > theWindowSize)
|
||||
}
|
||||
else
|
||||
found_in_cache = NO;
|
||||
|
||||
|
||||
if (found_in_cache)
|
||||
k = j+1;
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
j = 0;
|
||||
}
|
||||
|
||||
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
|
||||
|
||||
return_count = 0;
|
||||
|
||||
for (; k < [allCacheObjects count]; k++)
|
||||
{
|
||||
// Check for the WindowSize and slice accordingly
|
||||
if (return_count >= theWindowSize)
|
||||
{
|
||||
NSString *lastSequence;
|
||||
more_available = YES;
|
||||
|
||||
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
|
||||
*theLastServerKey = [NSString stringWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
|
||||
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
|
||||
break;
|
||||
}
|
||||
|
||||
aCacheObject = [allCacheObjects objectAtIndex: k];
|
||||
|
||||
// If found in cache, it's either a Change or a Delete
|
||||
if ([syncCache objectForKey: [aCacheObject uid]])
|
||||
{
|
||||
if ([[aCacheObject sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
// Deleted
|
||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"</Delete>"];
|
||||
|
||||
[syncCache removeObjectForKey: [aCacheObject uid]];
|
||||
[dateCache removeObjectForKey: [aCacheObject uid]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Changed
|
||||
outlook_hack:
|
||||
mailObject = [theCollection lookupName: [aCacheObject uid]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</ApplicationData>"];
|
||||
[s appendString: @"</Change>"];
|
||||
|
||||
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
|
||||
}
|
||||
|
||||
return_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Added
|
||||
if (![[aCacheObject sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
NSString *key;
|
||||
|
||||
// We check for Outlook stupidity to avoid creating duplicates - see the comment
|
||||
// in SOGoActiveSyncDispatcher.m: -processMoveItems:inResponse: for more details.
|
||||
key = [NSString stringWithFormat: @"%@+%@+%@+%@",
|
||||
[[context activeUser] login],
|
||||
[context objectForKey: @"DeviceType"],
|
||||
[theCollection displayName],
|
||||
[aCacheObject uid]];
|
||||
|
||||
if ([[SOGoCache sharedCache] valueForKey: key])
|
||||
{
|
||||
[[SOGoCache sharedCache] removeValueForKey: key];
|
||||
goto outlook_hack;
|
||||
}
|
||||
|
||||
mailObject = [theCollection lookupName: [aCacheObject uid]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</ApplicationData>"];
|
||||
[s appendString: @"</Add>"];
|
||||
|
||||
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
|
||||
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
|
||||
return_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if (more_available)
|
||||
{
|
||||
*theLastServerKey = uid;
|
||||
}
|
||||
}
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
else
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
|
||||
|
||||
[self _setFolderMetadata: folderMetadata
|
||||
forKey: [theCollection nameInContainer]];
|
||||
} // default:
|
||||
break;
|
||||
} // switch (folderType) ...
|
||||
|
||||
@@ -767,13 +890,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
NSMutableString *changeBuffer, *commandsBuffer;
|
||||
BOOL getChanges, first_sync;
|
||||
unsigned int windowSize;
|
||||
unsigned int windowSize, v;
|
||||
|
||||
changeBuffer = [NSMutableString string];
|
||||
commandsBuffer = [NSMutableString string];
|
||||
|
||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
collection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
||||
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||
@@ -783,6 +907,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (windowSize == 0 || windowSize > 512)
|
||||
windowSize = 100;
|
||||
|
||||
// We check if we must overwrite the windowSize with a system preference. This can be useful
|
||||
// if the user population has large mailboxes and slow connectivity
|
||||
if ((v = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncWindowSize]))
|
||||
windowSize = v;
|
||||
|
||||
lastServerKey = nil;
|
||||
|
||||
@@ -855,12 +984,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([changeBuffer length] || [commandsBuffer length])
|
||||
{
|
||||
if (lastServerKey)
|
||||
davCollectionTag = [collection davCollectionTagFromId: lastServerKey];
|
||||
else
|
||||
davCollectionTag = lastServerKey;
|
||||
else if (![[self _folderMetadataForKey: [collection nameInContainer]] objectForKey: @"MoreAvailable"])
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
|
||||
*changeDetected = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (folderType == ActiveSyncMailFolder && [syncKey isEqualToString: @"-1"])
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
}
|
||||
|
||||
|
||||
// Generate the response buffer
|
||||
[theBuffer appendString: @"<Collection>"];
|
||||
|
||||
@@ -32,17 +32,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
|
||||
@class NSException;
|
||||
@class NSURL;
|
||||
|
||||
@interface SOGoActiveSyncDispatcher : NSObject
|
||||
{
|
||||
NSURL *folderTableURL;
|
||||
id context;
|
||||
}
|
||||
|
||||
- (id) collectionFromId: (NSString *) theCollectionId
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
- (NSException *) dispatchRequest: (id) theRequest
|
||||
inResponse: (id) theResponse
|
||||
context: (id) theContext;
|
||||
|
||||
- (NSURL *) folderTableURL;
|
||||
- (void) ensureFolderTableExists;
|
||||
|
||||
@end
|
||||
|
||||
@@ -77,6 +77,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <SOGo/NSArray+DAV.h>
|
||||
#import <SOGo/NSDictionary+DAV.h>
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
#import <SOGo/SOGoDAVAuthenticator.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoMailer.h>
|
||||
@@ -85,6 +86,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
#import <SOGo/GCSSpecialQueries+SOGoCacheObject.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Appointments/SOGoAppointmentFolders.h>
|
||||
@@ -115,24 +118,76 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
#include "SOGoMailObject+ActiveSync.h"
|
||||
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@implementation SOGoActiveSyncDispatcher
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[super init];
|
||||
|
||||
folderTableURL = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(folderTableURL);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) _setFolderSyncKey: (NSString *) theSyncKey
|
||||
{
|
||||
NSMutableDictionary *metadata;
|
||||
|
||||
metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
|
||||
|
||||
[metadata setObject: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"SyncKey"] forKey: @"FolderSync"];
|
||||
SOGoCacheGCSObject *o;
|
||||
|
||||
[[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata
|
||||
forDevice: [context objectForKey: @"DeviceId"]];
|
||||
|
||||
[[[context activeUser] userSettings] synchronize];
|
||||
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
[[o properties] removeAllObjects];
|
||||
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
|
||||
[o save];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) _globalMetadataForDevice
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
return [o properties];
|
||||
}
|
||||
|
||||
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
{
|
||||
if (theFolderType == ActiveSyncMailFolder)
|
||||
{
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
NSDictionary *imapGUIDs;
|
||||
|
||||
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 theIdToTranslate;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -166,7 +221,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
|
||||
collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
@@ -212,20 +267,52 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
// If the parrent is 0 -> ok ; otherwise need to build the foldername based on parentId + displayName
|
||||
if ([parentId isEqualToString: @"0"])
|
||||
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
else
|
||||
{
|
||||
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: ActiveSyncMailFolder];
|
||||
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [parentId stringByEncodingImap4FolderName],
|
||||
[displayName stringByEncodingImap4FolderName]]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// handle exists (status == 2)
|
||||
// handle right synckey
|
||||
if ([newFolder create])
|
||||
{
|
||||
SOGoMailAccount *accountFolder;
|
||||
NSDictionary *imapGUIDs;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSString *key;
|
||||
|
||||
nameInContainer = [newFolder nameInContainer];
|
||||
|
||||
// We strip the "folder" prefix
|
||||
nameInContainer = [nameInContainer substringFromIndex: 6];
|
||||
|
||||
// save new guid into cache
|
||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
// update GUID in cache
|
||||
imapGUIDs = [accountFolder imapFolderGUIDs];
|
||||
|
||||
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], nameInContainer ];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
nameInContainer =[imapGUIDs objectForKey: nameInContainer];
|
||||
|
||||
[[o properties ] setObject: nameInContainer forKey: @"GUID"];
|
||||
[o save];
|
||||
|
||||
nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL];
|
||||
}
|
||||
else
|
||||
@@ -306,9 +393,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSString *serverId;
|
||||
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
|
||||
|
||||
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
@@ -369,6 +456,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
int status;
|
||||
|
||||
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
|
||||
parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
|
||||
displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
|
||||
|
||||
@@ -380,7 +468,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
error = [folderToUpdate renameTo: displayName];
|
||||
// If parent is 0 or displayname is not changed it is either a rename of a folder in 0 or a move to 0
|
||||
if ([parentId isEqualToString: @"0"] ||
|
||||
([serverId hasSuffix: [NSString stringWithFormat: @"/%@", displayName]] && [parentId isEqualToString: @"0"]))
|
||||
{
|
||||
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"/%@", [displayName stringByEncodingImap4FolderName]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: folderType];
|
||||
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName],
|
||||
[displayName stringByEncodingImap4FolderName]]];
|
||||
}
|
||||
|
||||
// Handle new name exist
|
||||
if (!error)
|
||||
@@ -396,10 +495,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx
|
||||
// we return '9' - we force a FolderSync
|
||||
status = 9;
|
||||
status = 1;
|
||||
|
||||
[self _setFolderSyncKey: syncKey];
|
||||
|
||||
// FIXME - TODO: We *MUST* update the path in our cache! See -changePathTo in SOGoCacheGCSObject
|
||||
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
@@ -430,15 +531,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
|
||||
NSMutableDictionary *metadata;
|
||||
NSMutableString *s;
|
||||
NSString *syncKey;
|
||||
NSData *d;
|
||||
|
||||
|
||||
BOOL first_sync;
|
||||
int status;
|
||||
|
||||
metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
|
||||
metadata = [self _globalMetadataForDevice];
|
||||
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||
s = [NSMutableString string];
|
||||
|
||||
@@ -450,7 +552,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
first_sync = YES;
|
||||
syncKey = @"1";
|
||||
}
|
||||
else if (![syncKey isEqualToString: [[metadata objectForKey: @"FolderSync"] objectForKey: @"SyncKey"]])
|
||||
else if (![syncKey isEqualToString: [metadata objectForKey: @"FolderSyncKey"]])
|
||||
{
|
||||
// Synchronization key mismatch or invalid synchronization key
|
||||
status = 9;
|
||||
@@ -461,84 +563,213 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendFormat: @"<FolderSync xmlns=\"FolderHierarchy:\"><Status>%d</Status><SyncKey>%@</SyncKey><Changes>", status, syncKey];
|
||||
|
||||
// Initial sync, let's return the complete folder list
|
||||
if (first_sync)
|
||||
|
||||
if (status == 1)
|
||||
{
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
id currentFolder;
|
||||
|
||||
NSDictionary *folderMetadata;
|
||||
NSArray *allFoldersMetadata;
|
||||
NSString *name, *serverId, *parentId;
|
||||
NSString *key, *cKey, *nkey, *name, *serverId, *parentId;
|
||||
NSDictionary *folderMetadata, *imapGUIDs;
|
||||
NSArray *allFoldersMetadata, *allKeys;
|
||||
NSMutableDictionary *cachedGUIDs;
|
||||
NSMutableString *commands;
|
||||
SOGoCacheGCSObject *o;
|
||||
|
||||
int i, type, command_count;
|
||||
|
||||
int i, type;
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
allFoldersMetadata = [accountFolder allFoldersMetadata];
|
||||
|
||||
// Get GUIDs of folder (IMAP)
|
||||
// e.g. {INBOX = "sogo_73c_192bd57b_d8"
|
||||
imapGUIDs = [accountFolder imapFolderGUIDs];
|
||||
|
||||
// See 2.2.3.170.3 Type (FolderSync) - http://msdn.microsoft.com/en-us/library/gg650877(v=exchg.80).aspx
|
||||
[s appendFormat: @"<Count>%d</Count>", [allFoldersMetadata count]+3];
|
||||
cachedGUIDs = [NSMutableDictionary dictionary];
|
||||
|
||||
// No need to read cached folder infos during first sync. Otherwise, pull it from the database.
|
||||
// e.g. {"sogo_73c_192bd57b_d8" = INBOX} - guid = foldername for easy reverse lookup with imapGUIDs
|
||||
if (!first_sync)
|
||||
{
|
||||
NSArray *foldersInCache;
|
||||
NSString *folderName;
|
||||
|
||||
// get the list of folder stored in cache
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], @"0"];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
foldersInCache = [o folderList: [context objectForKey: @"DeviceId"] newerThanVersion: -1];
|
||||
|
||||
// get guids of folders stored in cache
|
||||
for (i = 0; i < [foldersInCache count]; i++)
|
||||
{
|
||||
folderName = [foldersInCache objectAtIndex: i];
|
||||
key = [folderName substringFromIndex: 1];
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
if ([[o properties ] objectForKey: @"GUID"])
|
||||
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+7]
|
||||
forKey: [[o properties] objectForKey: @"GUID"]];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle folders that have been deleted over IMAP
|
||||
command_count = 0;
|
||||
commands = [NSMutableString string];
|
||||
allKeys = [cachedGUIDs allKeys];
|
||||
|
||||
for (i = 0; i < [allKeys count]; i++)
|
||||
{
|
||||
cKey = [allKeys objectAtIndex: i];
|
||||
|
||||
if (![imapGUIDs allKeysForObject: cKey])
|
||||
{
|
||||
// Delete folders cache content to avoid stale data if a new folder gets created with the same name
|
||||
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: cKey]];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
// Only send a delete command if GUID is found
|
||||
if ([[o properties] objectForKey: @"GUID"])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [[NSString stringWithFormat: @"mail/%@", [[o properties ] objectForKey: @"GUID"]] stringByEscapingURL] ];
|
||||
command_count++;
|
||||
}
|
||||
|
||||
[[o properties] removeAllObjects];
|
||||
[o save];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle addition and changes
|
||||
for (i = 0; i < [allFoldersMetadata count]; i++)
|
||||
{
|
||||
folderMetadata = [allFoldersMetadata objectAtIndex: i];
|
||||
serverId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"path"]];
|
||||
|
||||
// No GUID -> no sync
|
||||
if (!([imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]))
|
||||
continue;
|
||||
|
||||
serverId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]];
|
||||
name = [folderMetadata objectForKey: @"displayName"];
|
||||
|
||||
if ([name hasPrefix: @"/"])
|
||||
name = [name substringFromIndex: 1];
|
||||
|
||||
if ([name hasSuffix: @"/"])
|
||||
name = [name substringToIndex: [name length]-2];
|
||||
|
||||
name = [name substringToIndex: [name length]-1];
|
||||
|
||||
type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType];
|
||||
|
||||
parentId = @"0";
|
||||
|
||||
|
||||
if ([folderMetadata objectForKey: @"parent"])
|
||||
{
|
||||
parentId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"parent"]];
|
||||
parentId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]];
|
||||
name = [[name pathComponents] lastObject];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
|
||||
[serverId stringByEscapingURL],
|
||||
[parentId stringByEscapingURL],
|
||||
type,
|
||||
[name activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Decide between add and change
|
||||
if ([cachedGUIDs objectForKey: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]])
|
||||
{
|
||||
// Search GUID to check name change in cache (diff between IMAP and cache)
|
||||
if ((![[[folderMetadata objectForKey: @"path"] substringFromIndex: 1] isEqualToString: [imapGUIDs objectForKey: [cachedGUIDs objectForKey:
|
||||
[[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]]))
|
||||
{
|
||||
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey:
|
||||
[imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]];
|
||||
nkey = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] ];
|
||||
|
||||
if (![key isEqualToString: nkey])
|
||||
{
|
||||
[commands appendFormat: @"<Update><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Update>",
|
||||
[serverId stringByEscapingURL],
|
||||
[parentId stringByEscapingURL],
|
||||
type,
|
||||
[name activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
// Change path in cache
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
[o changePathTo: [NSString stringWithFormat: @"/%@", nkey]]; // ?? why is '/' prefix needed - problem in changePathTo?
|
||||
|
||||
command_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[commands appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
|
||||
[serverId stringByEscapingURL],
|
||||
[parentId stringByEscapingURL],
|
||||
type,
|
||||
[name activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Store folder's GUID in cache
|
||||
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
[[o properties ] setObject: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]] forKey: @"GUID"];
|
||||
[o save];
|
||||
|
||||
command_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// We add the personal calendar - events
|
||||
// FIXME: add all calendars
|
||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We add the personal calendar - tasks
|
||||
// FIXME: add all calendars
|
||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We add the personal address book
|
||||
// FIXME: add all address books
|
||||
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
if (first_sync)
|
||||
[s appendFormat: @"<Count>%d</Count>", command_count+3];
|
||||
else
|
||||
[s appendFormat: @"<Count>%d</Count>", command_count];
|
||||
|
||||
if (command_count > 0)
|
||||
[s appendFormat: @"%@", commands];
|
||||
|
||||
if (first_sync)
|
||||
{
|
||||
// We add the personal calendar - events
|
||||
// FIXME: add all calendars
|
||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We add the personal calendar - tasks
|
||||
// FIXME: add all calendars
|
||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We add the personal address book
|
||||
// FIXME: add all address books
|
||||
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
|
||||
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
|
||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[s appendString: @"</Changes></FolderSync>"];
|
||||
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
|
||||
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx :
|
||||
@@ -585,6 +816,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
||||
//
|
||||
@@ -659,6 +891,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
@@ -752,6 +985,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
status = 1;
|
||||
|
||||
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
|
||||
requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
||||
appointmentObject = nil;
|
||||
@@ -902,6 +1136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSDictionary *response;
|
||||
NSString *v;
|
||||
|
||||
srcFolderId = [self globallyUniqueIDToIMAPFolderName: srcFolderId type: srcFolderType];
|
||||
dstFolderId = [self globallyUniqueIDToIMAPFolderName: dstFolderId type: dstFolderType];
|
||||
|
||||
currentCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
||||
|
||||
client = [[currentCollection imap4Connection] client];
|
||||
@@ -1037,6 +1274,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
//
|
||||
// We ignore everything for now.
|
||||
//
|
||||
- (void) processProvision: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSData *d;
|
||||
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<Provision xmlns=\"Provision:\">"];
|
||||
[s appendString: @"</Provision>"];
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
//
|
||||
// <?xml version="1.0"?>
|
||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||
@@ -1420,11 +1677,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
NSString *folderId, *itemId, *realCollectionId;
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
id value;
|
||||
|
||||
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
|
||||
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
|
||||
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
|
||||
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
|
||||
|
||||
// ReplaceMime isn't specified so we must NOT use the server copy
|
||||
// but rather take the data as-is from the client.
|
||||
if (![value count])
|
||||
{
|
||||
[self processSendMail: theDocumentElement
|
||||
inResponse: theResponse];
|
||||
return;
|
||||
}
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
@@ -1559,6 +1829,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
cmdName = [[theRequest uri] command];
|
||||
|
||||
// We make sure our cache table exists
|
||||
[self ensureFolderTableExists];
|
||||
|
||||
//
|
||||
// If the MS-ASProtocolVersion header is set to "12.1", the body of the SendMail request is
|
||||
// is a "message/rfc822" payload - otherwise, it's a WBXML blob.
|
||||
@@ -1630,4 +1903,72 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSURL *) folderTableURL
|
||||
{
|
||||
NSString *urlString, *ocFSTableName;
|
||||
NSMutableArray *parts;
|
||||
SOGoUser *user;
|
||||
|
||||
if (!folderTableURL)
|
||||
{
|
||||
user = [context activeUser];
|
||||
|
||||
if (![user loginInDomain])
|
||||
return nil;
|
||||
|
||||
urlString = [[user domainDefaults] folderInfoURL];
|
||||
parts = [[urlString componentsSeparatedByString: @"/"]
|
||||
mutableCopy];
|
||||
[parts autorelease];
|
||||
if ([parts count] == 5)
|
||||
{
|
||||
/* If "OCSFolderInfoURL" is properly configured, we must have 5
|
||||
parts in this url. */
|
||||
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
|
||||
[[user loginInDomain] asCSSIdentifier]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
|
||||
[folderTableURL retain];
|
||||
}
|
||||
else
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"'OCSFolderInfoURL' is not set"];
|
||||
}
|
||||
|
||||
return folderTableURL;
|
||||
}
|
||||
|
||||
- (void) ensureFolderTableExists
|
||||
{
|
||||
GCSChannelManager *cm;
|
||||
EOAdaptorChannel *channel;
|
||||
NSString *tableName, *query;
|
||||
GCSSpecialQueries *queries;
|
||||
|
||||
[self folderTableURL];
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: folderTableURL];
|
||||
|
||||
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
|
||||
tableName = [[folderTableURL path] lastPathComponent];
|
||||
if (tableName &&
|
||||
[channel evaluateExpressionX:
|
||||
[NSString stringWithFormat: @"SELECT count(*) FROM %@",
|
||||
tableName]])
|
||||
{
|
||||
queries = [channel specialQueries];
|
||||
query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
|
||||
if ([channel evaluateExpressionX: query])
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"could not create special table '%@'", tableName];
|
||||
}
|
||||
else
|
||||
[channel cancelFetch];
|
||||
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "SOGoMailObject+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
@@ -479,7 +480,9 @@ struct GlobalObjectId {
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NSData *d, *globalObjId;
|
||||
NSArray *attachmentKeys;
|
||||
NSMutableString *s;
|
||||
id value;
|
||||
|
||||
@@ -670,6 +673,10 @@ struct GlobalObjectId {
|
||||
// Body - namespace 17
|
||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||
|
||||
// Make use of a local pool here as _preferredBodyDataUsingType:nativeType: will consume
|
||||
// a significant amout of RAM and file descriptors
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
nativeBodyType = 1;
|
||||
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
|
||||
|
||||
@@ -710,8 +717,10 @@ struct GlobalObjectId {
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
DESTROY(pool);
|
||||
|
||||
// Attachments -namespace 16
|
||||
NSArray *attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
if ([attachmentKeys count])
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __SOGOSYNCCACHEOBJECT_H__
|
||||
#define __SOGOSYNCCACHEOBJECT_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSNull;
|
||||
|
||||
@interface SOGoSyncCacheObject : NSObject
|
||||
{
|
||||
id _uid;
|
||||
id _sequence;
|
||||
}
|
||||
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
|
||||
- (id) uid;
|
||||
- (void) setUID: (id) theUID;
|
||||
- (id) sequence;
|
||||
- (void) setSequence: (id) theSequence;
|
||||
|
||||
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject;
|
||||
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "SOGoSyncCacheObject.h"
|
||||
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@implementation SOGoSyncCacheObject
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_uid = nil;
|
||||
_sequence = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [[self alloc] init];
|
||||
|
||||
[o setUID: theUID];
|
||||
[o setSequence: theSequence];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(_uid);
|
||||
RELEASE(_sequence);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) uid
|
||||
{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
- (void) setUID: (id) theUID
|
||||
{
|
||||
ASSIGN(_uid, theUID);
|
||||
}
|
||||
|
||||
- (id) sequence
|
||||
{
|
||||
return _sequence;
|
||||
}
|
||||
|
||||
- (void) setSequence: (id) theSequence
|
||||
{
|
||||
ASSIGN(_sequence, theSequence);
|
||||
}
|
||||
|
||||
|
||||
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
return [[self uid] compare: [theSyncCacheObject uid]];
|
||||
}
|
||||
|
||||
//
|
||||
// We might get NSNull values here, so if both are NSNull instances,
|
||||
// we sort by UID. If both sequences are equal, we also sort by UID.
|
||||
//
|
||||
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
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]])
|
||||
return NSOrderedDescending;
|
||||
|
||||
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)
|
||||
return [self compareUID: theSyncCacheObject];
|
||||
|
||||
return [[self sequence] compare: [theSyncCacheObject sequence]];
|
||||
}
|
||||
|
||||
- (NSString *) description
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@-%@", _uid, _sequence];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -160,7 +160,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
attendee = [attendees objectAtIndex: i];
|
||||
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]];
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [attendee cn]];
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
|
||||
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (occurrences[i])
|
||||
if (occurrences[0][i])
|
||||
v += (1 << i);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,48 @@
|
||||
2.2.4 (2014-05-29)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- new print option in Calendar module
|
||||
- now able to save unknown recipient emails to address book on send (#1496)
|
||||
|
||||
Enhancements
|
||||
- Sieve folder encoding is now configurable (#2622)
|
||||
- SOGo version is now displayed in preferences window (#2612)
|
||||
- report Sieve error when saving preferences (#1046)
|
||||
- added the SOGoMaximumSyncWindowSize system default to overwrite the
|
||||
maximum number of items returned during an ActiveSync sync operation
|
||||
- updated datepicker
|
||||
- addressbooks properties are now accessible from a popup window
|
||||
- extended events and tasks searches
|
||||
- updated Czech, French, Hungarian, Polish, Russian, Slovak, Spanish (Argentina), and Spanish (Spain) translations
|
||||
- added more sycned contact properties when using ActiveSync (#2775)
|
||||
- now possible to configure the default subscribed resource name using SOGoSubscriptionFolderFormat
|
||||
- now handle server-side folder updates using ActiveSync (#2688)
|
||||
- updated CKEditor to version 4.4.1
|
||||
|
||||
Bug fixes
|
||||
- fixed saved HTML content of draft when attaching a file
|
||||
- fixed text nodes of HTML content handler by encoding HTML entities
|
||||
- fixed iCal7 delegation issue with the "inbox" folder (#2489)
|
||||
- fixed birth date validity checks (#1636)
|
||||
- fixed URL handling (#2616)
|
||||
- improved folder rename operations using ActiveSync (#2700)
|
||||
- fixed SmartReply/Forward when ReplaceMime was omitted (#2680)
|
||||
- fixed wrong generation of weekly repetitive events with ActiveSync (#2654)
|
||||
- fixed incorrect XML data conversion with ActiveSync (#2695)
|
||||
- fixed display of events having a category with HTML entities (#2703)
|
||||
- fixed display of images in CSS background (#2437)
|
||||
- fixed limitation of Sieve script size (#2745)
|
||||
- fixed sync-token generation when no change was returned (#2492)
|
||||
- fixed the IMAP copy/move operation between subfolders in different accounts
|
||||
- fixed synchronization of seen/unseen status of msgs in Webmail (#2715)
|
||||
- fixed focus of popup windows open through a contextual menu with Firefox on Windows 7
|
||||
- fixed missing characters in shared folder names over ActiveSync (#2709)
|
||||
- fixed reply and forward mail templates for Brazilian Portuguese (#2738)
|
||||
- fixed newline in signature when forwarding a message as attachment in HTML mode (#2787)
|
||||
- fixed restoration of options (priority & return receipt) when editing a draft (#193)
|
||||
- fixed update of participation status via CalDAV (#2786)
|
||||
|
||||
2.2.3 (2014-04-03)
|
||||
------------------
|
||||
|
||||
|
||||
+1
-11
@@ -46,11 +46,8 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
MAPIStoreSamDBUtils.m \
|
||||
MAPIStoreUserContext.m \
|
||||
\
|
||||
SOGoMAPIObject.m \
|
||||
\
|
||||
SOGoMAPIDBObject.m \
|
||||
SOGoMAPIDBMessage.m \
|
||||
SOGoMAPIDBFolder.m \
|
||||
SOGoCacheGCSObject+MAPIStore.m \
|
||||
\
|
||||
MAPIStoreAppointmentWrapper.m \
|
||||
MAPIStoreAttachment.m \
|
||||
@@ -123,13 +120,6 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
iCalEvent+MAPIStore.m \
|
||||
iCalTimeZone+MAPIStore.m \
|
||||
\
|
||||
GCSSpecialQueries+OpenChange.m\
|
||||
\
|
||||
EOQualifier+MAPI.m \
|
||||
\
|
||||
EOBitmaskQualifier.m \
|
||||
\
|
||||
BSONCodec.m \
|
||||
RTFHandler.m
|
||||
|
||||
|
||||
|
||||
@@ -3,3 +3,6 @@ all:: MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h
|
||||
MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h: gen-property-selectors.py code-MAPIStorePropertySelectors.m code-MAPIStorePropertySelectors.h
|
||||
@echo " Auto-generating MAPIStorePropertySelectors.[hm]..."
|
||||
@$(PYTHON) ./gen-property-selectors.py -o MAPIStorePropertySelectors $(LIBMAPISTORE_CFLAGS)
|
||||
|
||||
distclean clean::
|
||||
-rm -f MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MAPIStoreDBBaseContext.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -32,7 +30,7 @@
|
||||
#import "MAPIStoreDBFolder.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
|
||||
#import "MAPIStoreDBBaseContext.h"
|
||||
|
||||
@@ -60,7 +58,7 @@ static Class MAPIStoreDBFolderK;
|
||||
|
||||
- (void) ensureContextFolder
|
||||
{
|
||||
SOGoMAPIDBFolder *currentFolder;
|
||||
SOGoCacheGCSFolder *currentFolder;
|
||||
NSArray *parts;
|
||||
NSMutableArray *folders;
|
||||
NSString *folderName;
|
||||
@@ -78,7 +76,7 @@ static Class MAPIStoreDBFolderK;
|
||||
folderName = [parts objectAtIndex: count];
|
||||
if ([folderName length] > 0)
|
||||
{
|
||||
currentFolder = [SOGoMAPIDBFolder objectWithName: folderName
|
||||
currentFolder = [SOGoCacheGCSFolder objectWithName: folderName
|
||||
inContainer: currentFolder];
|
||||
[folders addObject: currentFolder];
|
||||
}
|
||||
@@ -98,11 +96,11 @@ static Class MAPIStoreDBFolderK;
|
||||
|
||||
- (id) rootSOGoFolder
|
||||
{
|
||||
SOGoMAPIDBFolder *folder;
|
||||
SOGoCacheGCSFolder *folder;
|
||||
|
||||
[userContext ensureFolderTableExists];
|
||||
|
||||
folder = [SOGoMAPIDBFolder objectWithName: [isa MAPIModuleName]
|
||||
folder = [SOGoCacheGCSFolder objectWithName: [isa MAPIModuleName]
|
||||
inContainer: nil];
|
||||
[folder setTableUrl: [userContext folderTableURL]];
|
||||
// [folder reloadIfNeeded];
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#import <EOControl/EOQualifier.h>
|
||||
#import <SOGo/SOGoFolder.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import "EOQualifier+MAPI.h"
|
||||
#import <SOGo/EOQualifier+SOGoCacheObject.h>
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreDBFolderTable.h"
|
||||
#import "MAPIStoreDBMessage.h"
|
||||
@@ -40,7 +40,7 @@
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
|
||||
#import "MAPIStoreDBFolder.h"
|
||||
@@ -49,7 +49,7 @@
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
static Class EOKeyValueQualifierK, SOGoMAPIDBFolderK, MAPIStoreDBFolderK;
|
||||
static Class EOKeyValueQualifierK, SOGoCacheGCSFolderK, MAPIStoreDBFolderK;
|
||||
|
||||
static NSString *MAPIStoreRightReadItems = @"RightsReadItems";
|
||||
static NSString *MAPIStoreRightCreateItems = @"RightsCreateItems";
|
||||
@@ -66,7 +66,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
+ (void) initialize
|
||||
{
|
||||
EOKeyValueQualifierK = [EOKeyValueQualifier class];
|
||||
SOGoMAPIDBFolderK = [SOGoMAPIDBFolder class];
|
||||
SOGoCacheGCSFolderK = [SOGoCacheGCSFolder class];
|
||||
MAPIStoreDBFolderK = [MAPIStoreDBFolder class];
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
{
|
||||
enum mapistore_error rc;
|
||||
NSString *folderName, *nameInContainer;
|
||||
SOGoMAPIDBFolder *newFolder;
|
||||
SOGoCacheGCSFolder *newFolder;
|
||||
struct SPropValue *value;
|
||||
|
||||
value = get_SPropValue_SRow (aRow, PidTagDisplayName);
|
||||
@@ -111,7 +111,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
{
|
||||
nameInContainer = [NSString stringWithFormat: @"0x%.16"PRIx64,
|
||||
(unsigned long long) newFID];
|
||||
newFolder = [SOGoMAPIDBFolderK objectWithName: nameInContainer
|
||||
newFolder = [SOGoCacheGCSFolderK objectWithName: nameInContainer
|
||||
inContainer: sogoObject];
|
||||
[newFolder reloadIfNeeded];
|
||||
[[newFolder properties] setObject: folderName
|
||||
@@ -181,7 +181,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
|
||||
inContainer: sogoObject];
|
||||
[fsObject setObjectType: MAPIDBObjectTypeMessage];
|
||||
[fsObject setObjectType: MAPIMessageCacheObject];
|
||||
[fsObject reloadIfNeeded];
|
||||
newMessage = [MAPIStoreDBMessage mapiStoreObjectWithSOGoObject: fsObject
|
||||
inContainer: self];
|
||||
@@ -198,7 +198,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
if ([[context activeUser] isEqual: ownerUser]
|
||||
|| [self subscriberCanReadMessages])
|
||||
keys = [(SOGoMAPIDBFolder *) sogoObject childKeysOfType: MAPIDBObjectTypeMessage
|
||||
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
@@ -211,7 +211,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
return [dbFolder childKeysOfType: MAPIDBObjectTypeFolder
|
||||
return [dbFolder childKeysOfType: MAPIFolderCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MAPIStoreFallbackContext.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-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
|
||||
@@ -26,7 +24,7 @@
|
||||
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
|
||||
#import "MAPIStoreFallbackContext.h"
|
||||
|
||||
@@ -51,7 +49,7 @@
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *firstContext = NULL, *context;
|
||||
SOGoMAPIDBFolder *root;
|
||||
SOGoCacheGCSFolder *root;
|
||||
NSArray *names;
|
||||
NSUInteger count, max;
|
||||
NSString *baseURL, *url, *name;
|
||||
@@ -70,7 +68,7 @@
|
||||
|
||||
/* Maybe emsmdbp_provisioning should be fixed in order to only take the uri
|
||||
returned above to avoid deleting its entries... */
|
||||
root = [SOGoMAPIDBFolder objectWithName: [self MAPIModuleName]
|
||||
root = [SOGoCacheGCSFolder objectWithName: [self MAPIModuleName]
|
||||
inContainer: nil];
|
||||
[root setOwner: userName];
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MAPIStoreFolder.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-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
|
||||
@@ -38,7 +36,7 @@
|
||||
@class MAPIStoreMessageTable;
|
||||
@class MAPIStorePermissionsTable;
|
||||
@class SOGoFolder;
|
||||
@class SOGoMAPIDBFolder;
|
||||
@class SOGoCacheGCSFolder;
|
||||
@class SOGoMAPIDBMessage;
|
||||
|
||||
#import "MAPIStoreSOGoObject.h"
|
||||
@@ -50,7 +48,7 @@
|
||||
// NSArray *faiMessageKeys;
|
||||
// NSArray *folderKeys;
|
||||
|
||||
SOGoMAPIDBFolder *dbFolder;
|
||||
SOGoCacheGCSFolder *dbFolder;
|
||||
// SOGoMAPIDBFolder *faiFolder;
|
||||
// SOGoMAPIDBFolder *propsFolder;
|
||||
// SOGoMAPIDBMessage *propsMessage;
|
||||
@@ -60,7 +58,7 @@
|
||||
|
||||
- (void) setupAuxiliaryObjects;
|
||||
|
||||
- (SOGoMAPIDBFolder *) dbFolder;
|
||||
- (SOGoCacheGCSFolder *) dbFolder;
|
||||
|
||||
- (NSArray *) activeMessageTables;
|
||||
- (NSArray *) activeFAIMessageTables;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MAPIStoreFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-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
|
||||
@@ -51,8 +49,9 @@
|
||||
#import "NSDate+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
#import "SOGoCacheGCSObject+MAPIStore.h"
|
||||
|
||||
#include <gen_ndr/exchange.h>
|
||||
|
||||
@@ -120,7 +119,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
[userContext ensureFolderTableExists];
|
||||
|
||||
ASSIGN (dbFolder,
|
||||
[SOGoMAPIDBFolder objectWithName: folderName
|
||||
[SOGoCacheGCSFolder objectWithName: folderName
|
||||
inContainer: [container dbFolder]]);
|
||||
[dbFolder setTableUrl: [userContext folderTableURL]];
|
||||
if (!container && [path length] > 0)
|
||||
@@ -138,7 +137,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
// ASSIGN (propsMessage,
|
||||
// [SOGoMAPIDBMessage objectWithName: @"properties.plist"
|
||||
// inContainer: dbFolder]);
|
||||
// [propsMessage setObjectType: MAPIDBObjectTypeInternal];
|
||||
// [propsMessage setObjectType: MAPIInternalCacheObject];
|
||||
// [propsMessage reloadIfNeeded];
|
||||
[properties release];
|
||||
properties = [dbFolder properties];
|
||||
@@ -192,7 +191,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (SOGoMAPIDBFolder *) dbFolder
|
||||
- (SOGoCacheGCSFolder *) dbFolder
|
||||
{
|
||||
return dbFolder;
|
||||
}
|
||||
@@ -1221,7 +1220,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
- (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
return [dbFolder childKeysOfType: MAPIDBObjectTypeFAI
|
||||
return [dbFolder childKeysOfType: MAPIFAICacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
@@ -1531,7 +1530,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
newKey = [NSString stringWithFormat: @"%@.plist",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
dbObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: dbFolder];
|
||||
[dbObject setObjectType: MAPIDBObjectTypeFAI];
|
||||
[dbObject setObjectType: MAPIFAICacheObject];
|
||||
[dbObject setIsNew: YES];
|
||||
newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject
|
||||
inContainer: self];
|
||||
|
||||
@@ -74,7 +74,7 @@ static Class NSNumberK;
|
||||
ASSIGN (versionsMessage,
|
||||
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
|
||||
inContainer: dbFolder]);
|
||||
[versionsMessage setObjectType: MAPIDBObjectTypeInternal];
|
||||
[versionsMessage setObjectType: MAPIInternalCacheObject];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
|
||||
@@ -106,7 +106,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
ASSIGN (versionsMessage,
|
||||
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
|
||||
inContainer: dbFolder]);
|
||||
[versionsMessage setObjectType: MAPIDBObjectTypeInternal];
|
||||
[versionsMessage setObjectType: MAPIInternalCacheObject];
|
||||
}
|
||||
|
||||
- (BOOL) ensureFolderExists
|
||||
@@ -1172,9 +1172,9 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
|
||||
- (MAPIStoreMessage *) createMessage
|
||||
{
|
||||
SOGoMAPIObject *childObject;
|
||||
SOGoCacheObject *childObject;
|
||||
|
||||
childObject = [SOGoMAPIObject objectWithName: [SOGoMAPIObject
|
||||
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
|
||||
globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
return [MAPIStoreMailVolatileMessage
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "SOGoMAPIObject.h"
|
||||
#import <SOGo/SOGoCacheObject.h>
|
||||
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
#import "EOBitmaskQualifier.h"
|
||||
#import <SOGo/EOBitmaskQualifier.h>
|
||||
#import "MAPIStoreActiveTables.h"
|
||||
#import "MAPIStoreObject.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoMailAccounts.h>
|
||||
|
||||
#import "GCSSpecialQueries+OpenChange.h"
|
||||
#import <SOGo/GCSSpecialQueries+SOGoCacheObject.h>
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreAuthenticator.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
@@ -295,7 +295,7 @@ static NSMapTable *contextsTable = nil;
|
||||
{
|
||||
/* If "OCSFolderInfoURL" is properly configured, we must have 5
|
||||
parts in this url. */
|
||||
ocFSTableName = [NSString stringWithFormat: @"socfs_%@",
|
||||
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
|
||||
[username asCSSIdentifier]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
@@ -329,7 +329,7 @@ static NSMapTable *contextsTable = nil;
|
||||
tableName]])
|
||||
{
|
||||
queries = [channel specialQueries];
|
||||
query = [queries createOpenChangeFSTableWithName: tableName];
|
||||
query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
|
||||
if ([channel evaluateExpressionX: query])
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"could not create special table '%@'", tableName];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* dbmsgdump.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-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
|
||||
@@ -33,7 +31,7 @@
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import "BSONCodec.h"
|
||||
#import <SOGo/BSONCodec.h>
|
||||
|
||||
const char *indentationStep = " ";
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SOGoCacheGCSObject+MAPIStore.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 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 3, 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 SOGOCACHEGCSOBJECTMAPISTORE
|
||||
#define SOGOCACHEGCSOBJECTMAPISTORE
|
||||
|
||||
#include <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
@interface SOGoCacheGCSObject (MAPIStore)
|
||||
|
||||
- (Class) mapistoreMessageClass;
|
||||
|
||||
@end
|
||||
|
||||
#endif // SOGOCACHEGCSOBJECTMAPISTORE
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SOGoCacheGCSObject+MAPIStore.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 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 3, 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/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#include "MAPIStoreTypes.h"
|
||||
|
||||
#include "SOGoCacheGCSObject+MAPIStore.h"
|
||||
|
||||
@implementation SOGoCacheGCSObject (MAPIStore)
|
||||
|
||||
- (Class) mapistoreMessageClass
|
||||
{
|
||||
NSString *className, *mapiMsgClass;
|
||||
|
||||
switch (objectType)
|
||||
{
|
||||
case MAPIMessageCacheObject:
|
||||
mapiMsgClass = [properties
|
||||
objectForKey: MAPIPropertyKey (PidTagMessageClass)];
|
||||
if (mapiMsgClass)
|
||||
{
|
||||
if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"])
|
||||
className = @"MAPIStoreNotesMessage";
|
||||
else
|
||||
className = @"MAPIStoreDBMessage";
|
||||
//[self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'",
|
||||
// mapiMsgClass, className];
|
||||
}
|
||||
else
|
||||
{
|
||||
//[self warnWithFormat: @"PidTagMessageClass is not set, falling back"
|
||||
// @" to 'MAPIStoreDBMessage'"];
|
||||
className = @"MAPIStoreDBMessage";
|
||||
}
|
||||
break;
|
||||
case MAPIFAICacheObject:
|
||||
className = @"MAPIStoreFAIMessage";
|
||||
break;
|
||||
default:
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"message class should not be queried for objects"
|
||||
@" of type '%d'", objectType];
|
||||
}
|
||||
|
||||
return NSClassFromString (className);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIDBMessage.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -23,12 +21,12 @@
|
||||
#ifndef SOGOMAPIDBMESSAGE_H
|
||||
#define SOGOMAPIDBMESSAGE_H
|
||||
|
||||
#import "SOGoMAPIDBObject.h"
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
@class NSDate;
|
||||
@class NSString;
|
||||
|
||||
@interface SOGoMAPIDBMessage : SOGoMAPIDBObject
|
||||
@interface SOGoMAPIDBMessage : SOGoCacheGCSObject
|
||||
@end
|
||||
|
||||
#endif /* SOGOMAPIDBMESSAGE_H */
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "SOGoMAPIDBObject.h"
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
#import "NSObject+PropertyList.m"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ sogoUserDefaultsFile = os.path.expanduser("~sogo/GNUstep/Defaults/.GNUstepDefaul
|
||||
# - removes the entry in samba's ldap tree via ldbedit (NOTYET)
|
||||
# - remove the user's directory under mapistore/ and mapistore/SOGo
|
||||
# - cleanup Junk Folders and Sync Issues imap folders
|
||||
# - Delete the socfs_ table for the username.
|
||||
# - Delete the sogo_cache_folder_ table for the username.
|
||||
|
||||
def usage():
|
||||
print """
|
||||
@@ -210,7 +210,7 @@ On RHEL, install it using 'yum install MySQL-python'"""
|
||||
|
||||
conn = MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
|
||||
c=conn.cursor()
|
||||
tablename="socfs_%s" % (username)
|
||||
tablename="sogo_cache_folder_%s" % (username)
|
||||
c.execute("TRUNCATE TABLE %s" % tablename)
|
||||
print "Table %s emptied"
|
||||
|
||||
@@ -225,7 +225,7 @@ On RHEL, install it using 'yum install python-pgsql'"""
|
||||
raise Exception(msg)
|
||||
|
||||
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
|
||||
tablename = "socfs_%s" % username
|
||||
tablename = "sogo_cache_folder_%s" % username
|
||||
conn.query("DELETE FROM %s" % tablename)
|
||||
print "Table '%s' emptied" % tablename
|
||||
|
||||
@@ -278,7 +278,7 @@ def sqlCleanup(username):
|
||||
print "Starting SQL cleanup"
|
||||
OCSFolderInfoURL = getOCSFolderInfoURL()
|
||||
if OCSFolderInfoURL is None:
|
||||
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username))
|
||||
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the sogo_cache_folder_%s table should be truncated manually" % (username))
|
||||
|
||||
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
|
||||
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2007-2013 Inverse inc.
|
||||
Copyright (C) 2007-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo.
|
||||
@@ -1280,13 +1280,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
[baseWhere addObject: privacySQLString];
|
||||
|
||||
if ([title length])
|
||||
[baseWhere
|
||||
addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'",
|
||||
if ([filters length])
|
||||
{
|
||||
if ([filters isEqualToString:@"title_Category_Location"] || [filters isEqualToString:@"entireContent"])
|
||||
{
|
||||
[baseWhere addObject: [NSString stringWithFormat: @"(c_title isCaseInsensitiveLike: '%%%@%%' OR c_category isCaseInsensitiveLike: '%%%@%%' OR c_location isCaseInsensitiveLike: '%%%@%%')",
|
||||
[title stringByReplacingString: @"'" withString: @"\\'\\'"],
|
||||
[title stringByReplacingString: @"'" withString: @"\\'\\'"],
|
||||
[title stringByReplacingString: @"'" withString: @"\\'\\'"]]];
|
||||
}
|
||||
}
|
||||
else
|
||||
[baseWhere addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'",
|
||||
[title stringByReplacingString: @"'" withString: @"\\'\\'"]]];
|
||||
|
||||
if ([filters length])
|
||||
[baseWhere addObject: [NSString stringWithFormat: @"(%@)", filters]];
|
||||
|
||||
/* prepare mandatory fields */
|
||||
|
||||
fields = [NSMutableArray arrayWithArray: _fields];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/* SOGoAppointmentFolders.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
* Copyright (C) 2007-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
|
||||
@@ -56,13 +56,6 @@
|
||||
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
|
||||
@interface SOGoParentFolder (Private)
|
||||
|
||||
- (NSException *) _fetchPersonalFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc;
|
||||
|
||||
@end
|
||||
|
||||
static SoSecurityManager *sm = nil;
|
||||
|
||||
@implementation SOGoAppointmentFolders
|
||||
@@ -596,8 +589,9 @@ static SoSecurityManager *sm = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSException *) _fetchPersonalFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
- (NSException *) fetchSpecialFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
andFolderType: (SOGoFolderType) folderType
|
||||
{
|
||||
BOOL isWebRequest;
|
||||
NSException *error;
|
||||
@@ -607,7 +601,7 @@ static SoSecurityManager *sm = nil;
|
||||
SOGoWebAppointmentFolder *webFolder;
|
||||
NSString *name;
|
||||
|
||||
error = [super _fetchPersonalFolders: sql withChannel: fc];
|
||||
error = [super fetchSpecialFolders: sql withChannel: fc andFolderType: folderType];
|
||||
if (!error)
|
||||
{
|
||||
isWebRequest = [[context request] handledByDefaultHandler];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoAppointmentInboxFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-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
|
||||
|
||||
@@ -148,14 +148,14 @@
|
||||
object = [folder lookupName: nameInContainer
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
if ([object isKindOfClass: [NSException class]])
|
||||
if ([object isKindOfClass: [NSException class]] || [object isNew])
|
||||
{
|
||||
possibleName = [folder resourceNameForEventUID: eventUID];
|
||||
if (possibleName)
|
||||
{
|
||||
object = [folder lookupName: possibleName
|
||||
inContext: context acquire: NO];
|
||||
if ([object isKindOfClass: [NSException class]])
|
||||
if ([object isKindOfClass: [NSException class]] || [object isNew])
|
||||
object = nil;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
SOGoAppointmentInboxFolder = {
|
||||
superclass = "SOGoAppointmentFolder";
|
||||
defaultRoles = {
|
||||
"Access Contents Information" = ( "Owner" );
|
||||
"Access Contents Information" = ( "Owner", "AuthorizedSubscriber" );
|
||||
};
|
||||
};
|
||||
SOGoCalendarComponent = {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Osobní kontakty";
|
||||
"Collected Address Book" = "Sebrané kontakty";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Personal Address Book";
|
||||
"Collected Address Book" = "Collected Address Book";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Carnet d'adresses personnel";
|
||||
"Collected Address Book" = "Adresses collectées";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Személyes címjegyzék";
|
||||
"Collected Address Book" = "Összegyűjtöt címek jegyzéke";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Osobista książka adresowa";
|
||||
"Collected Address Book" = "Zbierana Książka Adresowa";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Личная адресная книга";
|
||||
"Collected Address Book" = "Собранные адреса";
|
||||
|
||||
@@ -25,12 +25,19 @@
|
||||
|
||||
@interface SOGoContactFolders : SOGoParentFolder
|
||||
|
||||
- (NSString *) defaultFolderName;
|
||||
- (NSString *) collectedFolderName;
|
||||
|
||||
- (NSException *) renameLDAPAddressBook: (NSString *) sourceID
|
||||
withDisplayName: (NSString *) newDisplayName;
|
||||
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID;
|
||||
|
||||
- (NSDictionary *) systemSources;
|
||||
|
||||
- (NSArray *) allContactsFromFilter: (NSString *) theFilter
|
||||
excludeGroups: (BOOL) excludeGroups
|
||||
excludeLists: (BOOL) excludeLists;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCONTACTFOLDERS_H */
|
||||
|
||||
@@ -22,12 +22,18 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSSortDescriptor.h>
|
||||
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <DOM/DOMElement.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLContentStore/GCSFolderManager.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
@@ -40,10 +46,17 @@
|
||||
|
||||
#import "SOGoContactFolders.h"
|
||||
|
||||
Class SOGoContactSourceFolderK;
|
||||
|
||||
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
|
||||
|
||||
@implementation SOGoContactFolders
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
SOGoContactSourceFolderK = [SOGoContactSourceFolder class];
|
||||
}
|
||||
|
||||
+ (NSString *) gcsFolderType
|
||||
{
|
||||
return @"Contact";
|
||||
@@ -110,6 +123,38 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSException *) appendCollectedSources
|
||||
{
|
||||
GCSChannelManager *cm;
|
||||
EOAdaptorChannel *fc;
|
||||
NSURL *folderLocation;
|
||||
NSString *sql, *gcsFolderType;
|
||||
NSException *error;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
|
||||
fc = [cm acquireOpenChannelForURL: folderLocation];
|
||||
if ([fc isOpen])
|
||||
{
|
||||
gcsFolderType = [[self class] gcsFolderType];
|
||||
|
||||
sql = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
|
||||
@" WHERE c_path2 = '%@'"
|
||||
@" AND c_folder_type = '%@'"),
|
||||
[folderLocation gcsTableName], owner, gcsFolderType];
|
||||
|
||||
error = [super fetchSpecialFolders: sql withChannel: fc andFolderType: SOGoCollectedFolder];
|
||||
|
||||
[cm releaseChannel: fc];
|
||||
}
|
||||
else
|
||||
error = [NSException exceptionWithName: @"SOGoDBException"
|
||||
reason: @"database connection could not be open"
|
||||
userInfo: nil];
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSDictionary *) systemSources
|
||||
{
|
||||
NSMutableDictionary *systemSources;
|
||||
@@ -225,7 +270,7 @@
|
||||
|
||||
if ([sourceID isEqualToString: @"personal"])
|
||||
result = [NSException exceptionWithHTTPStatus: 403
|
||||
reason: @"folder 'personal' cannot be deleted"];
|
||||
reason: (@"folder '%@' cannot be deleted", sourceID)];
|
||||
else
|
||||
{
|
||||
result = nil;
|
||||
@@ -250,6 +295,11 @@
|
||||
return [self labelForKey: @"Personal Address Book"];
|
||||
}
|
||||
|
||||
- (NSString *) collectedFolderName
|
||||
{
|
||||
return [self labelForKey: @"Collected Address Book"];
|
||||
}
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys
|
||||
{
|
||||
NSMutableArray *keys;
|
||||
@@ -371,4 +421,89 @@
|
||||
asWebDAVValue];
|
||||
}
|
||||
|
||||
- (NSArray *) allContactsFromFilter: (NSString *) theFilter
|
||||
excludeGroups: (BOOL) excludeGroups
|
||||
excludeLists: (BOOL) excludeLists
|
||||
{
|
||||
SOGoFolder <SOGoContactFolder> *folder;
|
||||
NSString *mail, *domain;
|
||||
NSArray *folders, *contacts, *descriptors, *sortedContacts;
|
||||
NSMutableArray *sortedFolders;
|
||||
NSMutableDictionary *contact, *uniqueContacts;
|
||||
unsigned int i, j, max;
|
||||
NSSortDescriptor *commonNameDescriptor;
|
||||
|
||||
// NSLog(@"Search all contacts: %@", searchText);
|
||||
|
||||
domain = [[context activeUser] domain];
|
||||
folders = nil;
|
||||
NS_DURING
|
||||
folders = [self subFolders];
|
||||
NS_HANDLER
|
||||
/* We need to specifically test for @"SOGoDBException", which is
|
||||
raised explicitly in SOGoParentFolder. Any other exception should
|
||||
be re-raised. */
|
||||
if ([[localException name] isEqualToString: @"SOGoDBException"])
|
||||
folders = nil;
|
||||
else
|
||||
[localException raise];
|
||||
NS_ENDHANDLER;
|
||||
max = [folders count];
|
||||
sortedFolders = [NSMutableArray arrayWithCapacity: max];
|
||||
uniqueContacts = [NSMutableDictionary dictionary];
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
folder = [folders objectAtIndex: i];
|
||||
/* We first search in LDAP folders (in case of duplicated entries in GCS folders) */
|
||||
if ([folder isKindOfClass: SOGoContactSourceFolderK])
|
||||
[sortedFolders insertObject: folder atIndex: 0];
|
||||
else
|
||||
[sortedFolders addObject: folder];
|
||||
}
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
folder = [sortedFolders objectAtIndex: i];
|
||||
//NSLog(@" Address book: %@ (%@)", [folder displayName], [folder class]);
|
||||
contacts = [folder lookupContactsWithFilter: theFilter
|
||||
onCriteria: @"name_or_address"
|
||||
sortBy: @"c_cn"
|
||||
ordering: NSOrderedAscending
|
||||
inDomain: domain];
|
||||
for (j = 0; j < [contacts count]; j++)
|
||||
{
|
||||
contact = [contacts objectAtIndex: j];
|
||||
mail = [contact objectForKey: @"c_mail"];
|
||||
//NSLog(@" found %@ (%@) ? %@", [contact objectForKey: @"c_name"], mail,
|
||||
// [contact description]);
|
||||
if (!excludeLists && [[contact objectForKey: @"c_component"]
|
||||
isEqualToString: @"vlist"])
|
||||
{
|
||||
[contact setObject: [folder nameInContainer]
|
||||
forKey: @"container"];
|
||||
[uniqueContacts setObject: contact
|
||||
forKey: [contact objectForKey: @"c_name"]];
|
||||
}
|
||||
else if ([mail length]
|
||||
&& [uniqueContacts objectForKey: mail] == nil
|
||||
&& !(excludeGroups && [contact objectForKey: @"isGroup"]))
|
||||
[uniqueContacts setObject: contact forKey: mail];
|
||||
}
|
||||
}
|
||||
if ([uniqueContacts count] > 0)
|
||||
{
|
||||
// Sort the contacts by display name
|
||||
commonNameDescriptor = [[NSSortDescriptor alloc] initWithKey: @"c_cn"
|
||||
ascending:YES];
|
||||
descriptors = [NSArray arrayWithObjects: commonNameDescriptor, nil];
|
||||
[commonNameDescriptor release];
|
||||
sortedContacts = [[uniqueContacts allValues]
|
||||
sortedArrayUsingDescriptors: descriptors];
|
||||
}
|
||||
else
|
||||
sortedContacts = [NSArray array];
|
||||
|
||||
return sortedContacts;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Osobné kontakty";
|
||||
"Collected Address Book" = "Pozbierané osobné kontakty";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Libreta personal de direcciones";
|
||||
"Collected Address Book" = "Direcciones recopiladas";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
"Personal Address Book" = "Libreta personal de direcciones";
|
||||
"Collected Address Book" = "Libreta de direcciones recogidas";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
"SieveFolderName" = "Filtry";
|
||||
"OtherUsersFolderName" = "Ostatní uživatelé";
|
||||
"SharedFoldersName" = "Sdílené složky";
|
||||
|
||||
@@ -417,7 +417,15 @@
|
||||
{
|
||||
if (!ignoreContent)
|
||||
{
|
||||
[result appendString: [NSString stringWithCharacters: characters length: length]];
|
||||
// Append a text node
|
||||
if (ignoreContentTags)
|
||||
// We are converting a HTML message to plain text (htmlToTextContentHandler):
|
||||
// include the HTML tags in the text
|
||||
[result appendString: [NSString stringWithCharacters: characters length: length]];
|
||||
else
|
||||
// We are sanitizing an HTML message (sanitizerContentHandler):
|
||||
// escape the HTML entitites so they are visible
|
||||
[result appendString: [[NSString stringWithCharacters: characters length: length] stringByEscapingHTMLString]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
"SieveFolderName" = "Filtry";
|
||||
"OtherUsersFolderName" = "Inni użytkownicy";
|
||||
"SharedFoldersName" = "Foldery współdzielone";
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
- (NSData *) mimeMessageAsData;
|
||||
|
||||
/* operations */
|
||||
- (NSArray *) allRecipients;
|
||||
- (NSArray *) allBareRecipients;
|
||||
|
||||
- (NSException *) delete;
|
||||
- (NSException *) sendMail;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGImap4/NGImap4Envelope.h>
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
#import <NGMail/NGMailAddressParser.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
@@ -63,6 +64,11 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
#import <Contacts/SOGoContactFolders.h>
|
||||
#import <Contacts/SOGoContactGCSEntry.h>
|
||||
|
||||
#import "NSData+Mail.h"
|
||||
#import "NSString+Mail.h"
|
||||
#import "SOGoMailAccount.h"
|
||||
@@ -260,7 +266,7 @@ static NSString *userAgent = nil;
|
||||
{
|
||||
id headerValue;
|
||||
unsigned int count;
|
||||
NSString *messageID, *priority, *pureSender, *replyTo;
|
||||
NSString *messageID, *priority, *pureSender, *replyTo, *receipt;
|
||||
|
||||
for (count = 0; count < 8; count++)
|
||||
{
|
||||
@@ -279,26 +285,57 @@ static NSString *userAgent = nil;
|
||||
[headers setObject: messageID forKey: @"message-id"];
|
||||
}
|
||||
|
||||
priority = [newHeaders objectForKey: @"priority"];
|
||||
if (!priority || [priority isEqualToString: @"NORMAL"])
|
||||
priority = [newHeaders objectForKey: @"X-Priority"];
|
||||
if (priority)
|
||||
{
|
||||
[headers removeObjectForKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"HIGHEST"])
|
||||
{
|
||||
[headers setObject: @"1 (Highest)" forKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"HIGH"])
|
||||
{
|
||||
[headers setObject: @"2 (High)" forKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"LOW"])
|
||||
{
|
||||
[headers setObject: @"4 (Low)" forKey: @"X-Priority"];
|
||||
// newHeaders come from MIME message; convert X-Priority to Web representation
|
||||
[headers setObject: priority forKey: @"X-Priority"];
|
||||
[headers removeObjectForKey: @"priority"];
|
||||
if ([priority isEqualToString: @"1 (Highest)"])
|
||||
{
|
||||
[headers setObject: @"HIGHEST" forKey: @"priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"2 (High)"])
|
||||
{
|
||||
[headers setObject: @"HIGH" forKey: @"priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"4 (Low)"])
|
||||
{
|
||||
[headers setObject: @"LOW" forKey: @"priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"5 (Lowest)"])
|
||||
{
|
||||
[headers setObject: @"LOWEST" forKey: @"priority"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[headers setObject: @"5 (Lowest)" forKey: @"X-Priority"];
|
||||
// newHeaders come from Web form; convert priority to MIME header representation
|
||||
priority = [newHeaders objectForKey: @"priority"];
|
||||
if (!priority || [priority isEqualToString: @"NORMAL"])
|
||||
{
|
||||
[headers removeObjectForKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"HIGHEST"])
|
||||
{
|
||||
[headers setObject: @"1 (Highest)" forKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"HIGH"])
|
||||
{
|
||||
[headers setObject: @"2 (High)" forKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority isEqualToString: @"LOW"])
|
||||
{
|
||||
[headers setObject: @"4 (Low)" forKey: @"X-Priority"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[headers setObject: @"5 (Lowest)" forKey: @"X-Priority"];
|
||||
}
|
||||
if (priority)
|
||||
{
|
||||
[headers setObject: priority forKey: @"priority"];
|
||||
}
|
||||
}
|
||||
|
||||
replyTo = [headers objectForKey: @"replyTo"];
|
||||
@@ -308,14 +345,30 @@ static NSString *userAgent = nil;
|
||||
}
|
||||
[headers removeObjectForKey: @"replyTo"];
|
||||
|
||||
if ([[newHeaders objectForKey: @"receipt"] isEqualToString: @"true"])
|
||||
receipt = [newHeaders objectForKey: @"Disposition-Notification-To"];
|
||||
if ([receipt length] > 0)
|
||||
{
|
||||
pureSender = [[newHeaders objectForKey: @"from"] pureEMailAddress];
|
||||
if (pureSender)
|
||||
[headers setObject: pureSender forKey: @"Disposition-Notification-To"];
|
||||
[headers setObject: @"true" forKey: @"receipt"];
|
||||
[headers setObject: receipt forKey: @"Disposition-Notification-To"];
|
||||
}
|
||||
else
|
||||
[headers removeObjectForKey: @"Disposition-Notification-To"];
|
||||
{
|
||||
receipt = [newHeaders objectForKey: @"receipt"];
|
||||
if ([receipt isEqualToString: @"true"])
|
||||
{
|
||||
[headers setObject: receipt forKey: @"receipt"];
|
||||
pureSender = [[newHeaders objectForKey: @"from"] pureEMailAddress];
|
||||
if (pureSender)
|
||||
{
|
||||
[headers setObject: pureSender forKey: @"Disposition-Notification-To"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[headers removeObjectForKey: @"receipt"];
|
||||
[headers removeObjectForKey: @"Disposition-Notification-To"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *) headers
|
||||
@@ -799,8 +852,10 @@ static NSString *userAgent = nil;
|
||||
{
|
||||
NSString *subject, *msgid;
|
||||
NSMutableDictionary *info;
|
||||
NSDictionary *h;
|
||||
NSMutableArray *addresses;
|
||||
NGImap4Envelope *sourceEnvelope;
|
||||
id priority, receipt;
|
||||
|
||||
[sourceMail fetchCoreInfos];
|
||||
|
||||
@@ -830,6 +885,15 @@ static NSString *userAgent = nil;
|
||||
[self _addEMailsOfAddresses: [sourceEnvelope replyTo] toArray: addresses];
|
||||
if ([addresses count] > 0)
|
||||
[info setObject: addresses forKey: @"replyTo"];
|
||||
|
||||
h = [sourceMail mailHeaders];
|
||||
priority = [h objectForKey: @"x-priority"];
|
||||
if ([priority isNotEmpty] && [priority isKindOfClass: [NSString class]])
|
||||
[info setObject: (NSString*)priority forKey: @"X-Priority"];
|
||||
receipt = [h objectForKey: @"disposition-notification-to"];
|
||||
if ([receipt isNotEmpty] && [receipt isKindOfClass: [NSString class]])
|
||||
[info setObject: (NSString*)receipt forKey: @"Disposition-Notification-To"];
|
||||
|
||||
[self setHeaders: info];
|
||||
|
||||
[self setText: [sourceMail contentForEditing]];
|
||||
@@ -1660,7 +1724,7 @@ static NSString *userAgent = nil;
|
||||
{
|
||||
recipients = [headers objectForKey: fieldNames[count]];
|
||||
if ([recipients count] > 0)
|
||||
[allRecipients addObjectsFromArray: recipients];
|
||||
[allRecipients addObjectsFromArray: recipients];
|
||||
}
|
||||
|
||||
return allRecipients;
|
||||
@@ -1689,6 +1753,63 @@ static NSString *userAgent = nil;
|
||||
//
|
||||
- (NSException *) sendMail
|
||||
{
|
||||
SOGoUserDefaults *ud;
|
||||
ud = [[context activeUser] userDefaults];
|
||||
|
||||
if ([ud mailAddOutgoingAddresses])
|
||||
{
|
||||
SOGoContactFolders *contactFolders;
|
||||
NGMailAddressParser *parser;
|
||||
id parsedRecipient;
|
||||
SOGoContactFolder *folder;
|
||||
SOGoContactGCSEntry *newContact;
|
||||
NGVCard *card;
|
||||
Class contactGCSEntry;
|
||||
NSMutableArray *recipients;
|
||||
NSString *recipient, *emailAddress, *addressBook, *uid;
|
||||
NSArray *matchingContacts;
|
||||
int i;
|
||||
|
||||
// Get all the addressbooks
|
||||
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
||||
lookupName: @"Contacts"
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
// Get all the recipients from the current email
|
||||
recipients = [self allRecipients];
|
||||
for (i = 0; i < [recipients count]; i++)
|
||||
{
|
||||
// The address contains a string. ex: "John Doe <sogo1@exemple.com>"
|
||||
recipient = [recipients objectAtIndex: i];
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: recipient];
|
||||
parsedRecipient = [parser parse];
|
||||
emailAddress = [parsedRecipient address];
|
||||
|
||||
matchingContacts = [contactFolders allContactsFromFilter: emailAddress
|
||||
excludeGroups: YES
|
||||
excludeLists: YES];
|
||||
}
|
||||
// If we don't get any results from the autocompletion code, we add it..
|
||||
if ([matchingContacts count] == 0)
|
||||
{
|
||||
// Get the selected addressbook from the user preferences where the new address will be added
|
||||
addressBook = [ud selectedAddressBook];
|
||||
folder = [contactFolders lookupName: addressBook inContext: context acquire: NO];
|
||||
uid = [folder globallyUniqueObjectId];
|
||||
|
||||
if (folder && uid)
|
||||
{
|
||||
card = [NGVCard cardWithUid: uid];
|
||||
[card addEmail: emailAddress types: nil];
|
||||
|
||||
contactGCSEntry = NSClassFromString(@"SOGoContactGCSEntry");
|
||||
newContact = [contactGCSEntry objectWithName: uid
|
||||
inContainer: folder];
|
||||
[newContact setIsNew: YES];
|
||||
[newContact saveContentString: [card versitString]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [self sendMailAndCopyToSent: YES];
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ typedef enum {
|
||||
- (NSArray *) allFolderPaths;
|
||||
- (NSArray *) allFoldersMetadata;
|
||||
|
||||
- (NSDictionary *) imapFolderGUIDs;
|
||||
|
||||
- (BOOL) isInDraftsFolder;
|
||||
|
||||
/* special folders */
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
#import "SOGoUser+Mailer.h"
|
||||
|
||||
#import "SOGoMailAccount.h"
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
|
||||
|
||||
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
|
||||
|
||||
@@ -659,6 +661,62 @@ static NSString *inboxFolderName = @"INBOX";
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
- (NSDictionary *) imapFolderGUIDs
|
||||
{
|
||||
NSDictionary *result, *nresult, *folderData;
|
||||
NSMutableDictionary *folders;
|
||||
NGImap4Client *client;
|
||||
SOGoUserDefaults *ud;
|
||||
NSArray *folderList;
|
||||
NSEnumerator *e;
|
||||
NSString *guid;
|
||||
id object;
|
||||
|
||||
BOOL subscribedOnly;
|
||||
|
||||
ud = [[context activeUser] userDefaults];
|
||||
subscribedOnly = [ud mailShowSubscribedFoldersOnly];
|
||||
|
||||
folderList = [[self imap4Connection] allFoldersForURL: [self imap4URL]
|
||||
onlySubscribedFolders: subscribedOnly];
|
||||
|
||||
folders = [NSMutableDictionary dictionary];
|
||||
|
||||
client = [[self imap4Connection] client];
|
||||
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
|
||||
|
||||
e = [folderList objectEnumerator];
|
||||
|
||||
while (object = [e nextObject])
|
||||
{
|
||||
guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"];
|
||||
|
||||
if (!guid)
|
||||
{
|
||||
guid = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||
nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
|
||||
|
||||
if (![[nresult objectForKey: @"result"] boolValue])
|
||||
{
|
||||
// Need to implement X-GUID query for Dovecot - this requires modification in SOPE to support following command:
|
||||
// 1 list "" "*" return (status (x-guid)) -> this would avoid firing a command per folder to IMAP
|
||||
nresult = [client status: [object substringFromIndex: 1] flags: [NSArray arrayWithObject: @"x-guid"]];
|
||||
guid = [nresult objectForKey: @"x-guid"];
|
||||
if (!guid)
|
||||
{
|
||||
guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[folders setObject: guid forKey: [object substringFromIndex: 1]];
|
||||
}
|
||||
|
||||
return folders;
|
||||
}
|
||||
|
||||
|
||||
/* name lookup */
|
||||
|
||||
- (id) lookupName: (NSString *) _key
|
||||
|
||||
+5
@@ -13,6 +13,11 @@ from: WOString {
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
+5
@@ -22,6 +22,11 @@ from: WOString {
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2009-2011 Inverse inc.
|
||||
Copyright (C) 2009-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo
|
||||
@@ -94,7 +94,6 @@
|
||||
|
||||
- (NSCalendarDate *) mostRecentMessageDate;
|
||||
|
||||
- (NSString *) davCollectionTagFromId: (NSString *) theId;
|
||||
- (NSString *) davCollectionTag;
|
||||
|
||||
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
|
||||
|
||||
@@ -72,6 +72,28 @@
|
||||
|
||||
static NSString *defaultUserID = @"anyone";
|
||||
|
||||
static NSComparisonResult
|
||||
_compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
{
|
||||
static NSNumber *zeroNumber = nil;
|
||||
NSNumber *modseq1, *modseq2;
|
||||
|
||||
if (!zeroNumber)
|
||||
{
|
||||
zeroNumber = [NSNumber numberWithUnsignedLongLong: 0];
|
||||
[zeroNumber retain];
|
||||
}
|
||||
|
||||
modseq1 = [entry1 objectForKey: @"modseq"];
|
||||
if (!modseq1)
|
||||
modseq1 = zeroNumber;
|
||||
modseq2 = [entry2 objectForKey: @"modseq"];
|
||||
if (!modseq2)
|
||||
modseq2 = zeroNumber;
|
||||
|
||||
return [modseq1 compare: modseq2];
|
||||
}
|
||||
|
||||
@interface NGImap4Connection (PrivateMethods)
|
||||
|
||||
- (NSString *) imap4FolderNameForURL: (NSURL *) url;
|
||||
@@ -291,10 +313,18 @@ static NSString *defaultUserID = @"anyone";
|
||||
path = [[imap4URL path] stringByDeletingLastPathComponent];
|
||||
if (![path hasSuffix: @"/"])
|
||||
path = [path stringByAppendingString: @"/"];
|
||||
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
|
||||
host: [imap4URL host]
|
||||
path: [NSString stringWithFormat: @"%@%@",
|
||||
path, [newName stringByEncodingImap4FolderName]]];
|
||||
|
||||
// If new name contains the path - dont't need to add
|
||||
if ([newName rangeOfString: @"/"].location == NSNotFound)
|
||||
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
|
||||
host: [imap4URL host]
|
||||
path: [NSString stringWithFormat: @"%@%@",
|
||||
path, [newName stringByEncodingImap4FolderName]]];
|
||||
else
|
||||
destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
|
||||
host: [imap4URL host]
|
||||
path: [NSString stringWithFormat: @"%@",
|
||||
[newName stringByEncodingImap4FolderName]]];
|
||||
[destURL autorelease];
|
||||
error = [imap4 moveMailboxAtURL: imap4URL
|
||||
toURL: destURL];
|
||||
@@ -652,8 +682,12 @@ static NSString *defaultUserID = @"anyone";
|
||||
// Destination folder is in a different account
|
||||
SOGoMailAccounts *accounts;
|
||||
SOGoMailAccount *account;
|
||||
accounts = [[self container] container];
|
||||
SOGoUserFolder *userFolder;
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accounts = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
account = [accounts lookupName: [folders objectAtIndex: 1] inContext: localContext acquire: NO];
|
||||
|
||||
if ([account isKindOfClass: [NSException class]])
|
||||
{
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
@@ -796,8 +830,9 @@ static NSString *defaultUserID = @"anyone";
|
||||
- (NSArray *) fetchUIDs: (NSArray *) _uids
|
||||
parts: (NSArray *) _parts
|
||||
{
|
||||
return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
|
||||
parts: _parts];
|
||||
return [[self imap4Connection] fetchUIDs: _uids
|
||||
inURL: [self imap4URL]
|
||||
parts: _parts];
|
||||
}
|
||||
|
||||
- (NSArray *) fetchUIDsOfVanishedItems: (uint64_t) modseq
|
||||
@@ -823,7 +858,7 @@ static NSString *defaultUserID = @"anyone";
|
||||
toFolderURL: [self imap4URL]];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 502 /* Bad Gateway */
|
||||
reason: [NSString stringWithFormat: @"%@ is not an IMAP4 folder", [self relativeImap4Name]]];
|
||||
reason: [NSString stringWithFormat: @"%@ is not an IMAP4 folder", [self relativeImap4Name]]];
|
||||
}
|
||||
|
||||
- (NSException *) expunge
|
||||
@@ -2001,16 +2036,45 @@ static NSString *defaultUserID = @"anyone";
|
||||
//
|
||||
// Check updated items
|
||||
//
|
||||
// . UID FETCH 1:* (UID) (CHANGEDSINCE 1)
|
||||
// * 1 FETCH (UID 542 MODSEQ (7))
|
||||
// * 2 FETCH (UID 553 MODSEQ (14))
|
||||
// * 3 FETCH (UID 554 MODSEQ (16))
|
||||
// * 4 FETCH (UID 555 MODSEQ (15))
|
||||
// * 5 FETCH (UID 559 MODSEQ (17))
|
||||
// * 6 FETCH (UID 560 MODSEQ (18))
|
||||
// * 7 FETCH (UID 561 MODSEQ (19))
|
||||
//
|
||||
// . uid fetch 1:* (UID) (changedsince 171)
|
||||
// SORT + MODSEQ: http://www.watersprings.org/pub/id/draft-melnikov-condstore-sort-00.txt
|
||||
// With date, not modseq
|
||||
// . UID SORT (DATE) UTF-8 (NOT DELETED) (SINCE "15-Mar-2014")
|
||||
// * SORT 553 542 555 554 601 559 560 561 565 602 603 605 611 610 612 613 614 615 616 617 618 621 619 620 622 623
|
||||
//
|
||||
// . UID SORT (DATE) UTF-8 ((MODSEQ 64) (NOT DELETED)) (SINCE "15-Mar-2014")
|
||||
// * SORT 623 624 (MODSEQ 65)
|
||||
// . OK Completed (2 msgs in 0.000 secs)
|
||||
//
|
||||
// ".. the server MUST also append (to the end of the untagged SORT response) the highest mod-sequence for all messages being returned."
|
||||
//
|
||||
// To get the modseq of a specific message:
|
||||
//
|
||||
// . uid fetch 124569:124569 uid (changedsince 1)
|
||||
// . UID FETCH 124569:124569 (UID MODSEQ)
|
||||
// * 4900 FETCH (UID 124569 MODSEQ (2))
|
||||
//
|
||||
//
|
||||
// Deleted: "UID FETCH 1:* (UID) (CHANGEDSINCE 171 VANISHED)"
|
||||
// To get deleted messages
|
||||
//
|
||||
// . UID FETCH 1:* (UID) (CHANGEDSINCE 1 VANISHED)
|
||||
// * VANISHED (EARLIER) 1:541,543:552,556:558,562:564,566:600,604,606:609
|
||||
// * 1 FETCH (UID 542 MODSEQ (7))
|
||||
// * 2 FETCH (UID 553 MODSEQ (14))
|
||||
// * 3 FETCH (UID 554 MODSEQ (16))
|
||||
// * 4 FETCH (UID 555 MODSEQ (15))
|
||||
// * 5 FETCH (UID 559 MODSEQ (17))
|
||||
// * 6 FETCH (UID 560 MODSEQ (18))
|
||||
// * 7 FETCH (UID 561 MODSEQ (19))
|
||||
|
||||
//
|
||||
// fetchUIDsOfVanishedItems ..
|
||||
//
|
||||
// . uid fetch 1:* (FLAGS) (changedsince 176 vanished)
|
||||
@@ -2027,6 +2091,7 @@ static NSString *defaultUserID = @"anyone";
|
||||
NSMutableArray *allTokens;
|
||||
NSArray *a, *uids;
|
||||
NSDictionary *d;
|
||||
id fetchResults;
|
||||
|
||||
int uidnext, highestmodseq, i;
|
||||
|
||||
@@ -2053,7 +2118,7 @@ static NSString *defaultUserID = @"anyone";
|
||||
EOKeyValueQualifier *kvQualifier;
|
||||
NSNumber *nextModseq;
|
||||
|
||||
nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq + 1];
|
||||
nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq];
|
||||
kvQualifier = [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"modseq"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
@@ -2071,9 +2136,10 @@ static NSString *defaultUserID = @"anyone";
|
||||
|
||||
if (theStartDate)
|
||||
{
|
||||
EOQualifier *sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
||||
@"(DATE >= %@)", theStartDate];
|
||||
EOQualifier *sinceDateQualifier;
|
||||
|
||||
sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
|
||||
@"(DATE >= %@)", theStartDate];
|
||||
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
|
||||
nil];
|
||||
[searchQualifier autorelease];
|
||||
@@ -2084,24 +2150,28 @@ static NSString *defaultUserID = @"anyone";
|
||||
uids = [self fetchUIDsMatchingQualifier: searchQualifier
|
||||
sortOrdering: nil];
|
||||
|
||||
for (i = 0; i < [uids count]; i++)
|
||||
{
|
||||
// New messages
|
||||
if ([[uids objectAtIndex: i] intValue] >= uidnext)
|
||||
{
|
||||
d = [NSDictionary dictionaryWithObject: @"added" forKey: [uids objectAtIndex: i]];
|
||||
}
|
||||
// Changed messages
|
||||
else
|
||||
{
|
||||
d = [NSDictionary dictionaryWithObject: @"changed" forKey: [uids objectAtIndex: i]];
|
||||
}
|
||||
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
|
||||
parts: [NSArray arrayWithObject: @"modseq"]]
|
||||
objectForKey: @"fetch"];
|
||||
|
||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
||||
entries with a MODSEQ of 0 */
|
||||
fetchResults
|
||||
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
|
||||
context: NULL];
|
||||
|
||||
for (i = 0; i < [fetchResults count]; i++)
|
||||
{
|
||||
d = [NSDictionary dictionaryWithObject: [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
|
||||
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
|
||||
[allTokens addObject: d];
|
||||
}
|
||||
|
||||
|
||||
// We fetch deleted ones
|
||||
if (highestmodseq == 0)
|
||||
highestmodseq = 1;
|
||||
|
||||
if (highestmodseq > 0)
|
||||
{
|
||||
id uid;
|
||||
@@ -2110,8 +2180,8 @@ static NSString *defaultUserID = @"anyone";
|
||||
|
||||
for (i = 0; i < [uids count]; i++)
|
||||
{
|
||||
uid = [uids objectAtIndex: i];
|
||||
d = [NSDictionary dictionaryWithObject: @"deleted" forKey: uid];
|
||||
uid = [[uids objectAtIndex: i] stringValue];
|
||||
d = [NSDictionary dictionaryWithObject: [NSNull null] forKey: uid];
|
||||
[allTokens addObject: d];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "../../UI/SOGoUI/UIxComponent.h";
|
||||
#import "../../UI/SOGoUI/UIxComponent.h"
|
||||
|
||||
@interface SOGoMailLabel : NSObject
|
||||
{
|
||||
|
||||
@@ -770,6 +770,9 @@ static BOOL debugSoParts = NO;
|
||||
[mimeType hasPrefix: @"image/"] ||
|
||||
[mimeType hasPrefix: @"video/"])
|
||||
filename = [NSString stringWithFormat: @"unknown_%@", path];
|
||||
else if ([mimeType isEqualToString: @"message/rfc822"])
|
||||
filename = [NSString stringWithFormat: @"email_%@.eml", path];
|
||||
|
||||
|
||||
if (filename)
|
||||
{
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
"SieveFolderName" = "Filtre";
|
||||
"OtherUsersFolderName" = "Ostatní užívatelia";
|
||||
"SharedFoldersName" = "Zdielané adresáre";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
"SieveFolderName" = "Filtros";
|
||||
"OtherUsersFolderName" = "Otros usuarios";
|
||||
"SharedFoldersName" = "Carpetas compartidas";
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* EOBitmaskQualifier.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-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
|
||||
@@ -1,8 +1,6 @@
|
||||
/* EOBitmaskQualifier.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-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
|
||||
@@ -1,8 +1,6 @@
|
||||
/* EOQualifier+MAPI.h - this file is part of SOGo
|
||||
/* EOQualifier+SOGoCacheObject.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-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
|
||||
@@ -20,17 +18,17 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef EOQUALIFIER_MAPI_H
|
||||
#define EOQUALIFIER_MAPI_H
|
||||
#ifndef EOQUALIFIER_SOGOCACHEOBJECT_H
|
||||
#define EOQUALIFIER_SOGOCACHEOBJECT_H
|
||||
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
@class SOGoMAPIDBObject;
|
||||
@class SOGoCacheGCSObject;
|
||||
|
||||
@interface EOQualifier (MAPIStoreRestrictions)
|
||||
@interface EOQualifier (SOGoCacheObjectRestrictions)
|
||||
|
||||
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object;
|
||||
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoCacheGCSObject *) object;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* EOQUALIFIER_MAPI_H */
|
||||
#endif /* EOQUALIFIER_CACHEOBJECT_H */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* EOQualifier+MAPI.m - this file is part of SOGo
|
||||
/* EOQualifier+SOGoCacheObject.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-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
|
||||
@@ -29,11 +27,11 @@
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "EOBitmaskQualifier.h"
|
||||
#import "SOGoMAPIDBObject.h"
|
||||
#import "SOGoCacheGCSObject.h"
|
||||
|
||||
#import "EOQualifier+MAPI.h"
|
||||
#import "EOQualifier+SOGoCacheObject.h"
|
||||
|
||||
@implementation EOQualifier (MAPIStoreRestrictions)
|
||||
@implementation EOQualifier (SOGoCacheObjectRestrictions)
|
||||
|
||||
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
|
||||
{
|
||||
@@ -41,7 +39,7 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object
|
||||
- (BOOL) evaluateSOGoMAPIDBObject: (SOGoCacheGCSObject *) object
|
||||
{
|
||||
NSDictionary *properties;
|
||||
BOOL rc;
|
||||
@@ -58,7 +56,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation EOAndQualifier (MAPIStoreRestrictionsPrivate)
|
||||
@implementation EOAndQualifier (SOGoCacheRestrictionsPrivate)
|
||||
|
||||
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
|
||||
{
|
||||
@@ -76,7 +74,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation EOOrQualifier (MAPIStoreRestrictionsPrivate)
|
||||
@implementation EOOrQualifier (SOGoCacheObjectRestrictionsPrivate)
|
||||
|
||||
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
|
||||
{
|
||||
@@ -94,7 +92,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation EONotQualifier (MAPIStoreRestrictionsPrivate)
|
||||
@implementation EONotQualifier (SOGoCacheObjectRestrictionsPrivate)
|
||||
|
||||
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
|
||||
{
|
||||
@@ -103,7 +101,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation EOKeyValueQualifier (MAPIStoreRestrictionsPrivate)
|
||||
@implementation EOKeyValueQualifier (SOGoCacheObjectRestrictionsPrivate)
|
||||
|
||||
typedef BOOL (*EOComparator) (id, SEL, id);
|
||||
|
||||
@@ -134,7 +132,7 @@ typedef BOOL (*EOComparator) (id, SEL, id);
|
||||
|
||||
@end
|
||||
|
||||
@implementation EOBitmaskQualifier (MAPIStoreRestrictionsPrivate)
|
||||
@implementation EOBitmaskQualifier (SOGoCacheObjectRestrictionsPrivate)
|
||||
|
||||
- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties
|
||||
{
|
||||
+7
-9
@@ -1,8 +1,6 @@
|
||||
/* GCSSpecialQueries+OpenChange.h - this file is part of SOGo
|
||||
/* GCSSpecialQueries+SOGoCacheObject.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -20,15 +18,15 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef GCSSPECIALQUERIES_OPENCHANGE_H
|
||||
#define GCSSPECIALQUERIES_OPENCHANGE_H
|
||||
#ifndef GCSSPECIALQUERIES_SOGOCACHEOBJECT_H
|
||||
#define GCSSPECIALQUERIES_SOGOCACHEOBJECT_H
|
||||
|
||||
#import <GDLContentStore/GCSSpecialQueries.h>
|
||||
|
||||
@interface GCSSpecialQueries (OpenChangeHelpers)
|
||||
@interface GCSSpecialQueries (SOGoCacheObject)
|
||||
|
||||
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName;
|
||||
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* GCSSPECIALQUERIES_OPENCHANGE_H */
|
||||
#endif /* GCSSPECIALQUERIES_SOGOCACHEOBJECT_H */
|
||||
+14
-16
@@ -1,8 +1,6 @@
|
||||
/* GCSSpecialQueries+OpenChange.m - this file is part of SOGo
|
||||
/* GCSSpecialQueries+SOGoCacheObject.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -22,22 +20,22 @@
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "GCSSpecialQueries+OpenChange.h"
|
||||
#import "GCSSpecialQueries+SOGoCacheObject.h"
|
||||
|
||||
@interface GCSPostgreSQLSpecialQueries (OpenChangeHelpers)
|
||||
@interface GCSPostgreSQLSpecialQueries (SOGoObjectCache)
|
||||
@end
|
||||
|
||||
@interface GCSMySQLSpecialQueries (OpenChangeHelpers)
|
||||
@interface GCSMySQLSpecialQueries (SOGoObjectCache)
|
||||
@end
|
||||
|
||||
@interface GCSOracleSpecialQueries (OpenChangeHelpers)
|
||||
@interface GCSOracleSpecialQueries (SOGoObjectCache)
|
||||
@end
|
||||
|
||||
@implementation GCSSpecialQueries (OpenChangeHelpers)
|
||||
@implementation GCSSpecialQueries (SOGoObjectCache)
|
||||
|
||||
/* FIXME: c_parent_path should be indexed */
|
||||
|
||||
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
|
||||
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
@@ -46,9 +44,9 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCSPostgreSQLSpecialQueries (OpenChangeHelpers)
|
||||
@implementation GCSPostgreSQLSpecialQueries (SOGoObjectCache)
|
||||
|
||||
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
|
||||
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@@ -66,9 +64,9 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCSMySQLSpecialQueries (OpenChangeHelpers)
|
||||
@implementation GCSMySQLSpecialQueries (SOGoObjectCache)
|
||||
|
||||
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
|
||||
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@@ -86,9 +84,9 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCSOracleSpecialQueries (OpenChangeHelpers)
|
||||
@implementation GCSOracleSpecialQueries (SOGoObjectCache)
|
||||
|
||||
- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName
|
||||
- (NSString *) createSOGoCacheGCSFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@@ -15,7 +15,14 @@ SOGo_HEADER_FILES = \
|
||||
SOGoBuild.h \
|
||||
SOGoProductLoader.h \
|
||||
\
|
||||
BSONCodec.h \
|
||||
EOBitmaskQualifier.h \
|
||||
EOQualifier+SOGoCacheObject.h \
|
||||
GCSSpecialQueries+SOGoCacheObject.h \
|
||||
SOGoCache.h \
|
||||
SOGoCacheGCSFolder.h \
|
||||
SOGoCacheGCSObject.h \
|
||||
SOGoCacheObject.h \
|
||||
SOGoConstants.h \
|
||||
SOGoObject.h \
|
||||
SOGoContentObject.h \
|
||||
@@ -80,7 +87,14 @@ SOGo_OBJC_FILES = \
|
||||
SOGoBuild.m \
|
||||
SOGoProductLoader.m \
|
||||
\
|
||||
BSONCodec.m \
|
||||
EOBitmaskQualifier.m \
|
||||
EOQualifier+SOGoCacheObject.m \
|
||||
GCSSpecialQueries+SOGoCacheObject.m \
|
||||
SOGoCache.m \
|
||||
SOGoCacheGCSFolder.m \
|
||||
SOGoCacheGCSObject.m \
|
||||
SOGoCacheObject.m \
|
||||
SOGoConstants.m \
|
||||
SOGoObject.m \
|
||||
SOGoContentObject.m \
|
||||
@@ -158,6 +172,9 @@ ifeq ($(saml2_config), yes)
|
||||
SOGoSAML2Exceptions.h SOGoSAML2Exceptions.m: gen-saml2-exceptions.py
|
||||
$(ECHO_CREATING) ./gen-saml2-exceptions.py $(LASSO_CFLAGS) $(END_ECHO)
|
||||
|
||||
distclean clean::
|
||||
-rm -f SOGoSAML2Exceptions.h SOGoSAML2Exceptions.m
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(ldap_config),yes)
|
||||
|
||||
@@ -46,7 +46,7 @@ static NSString **cssEscapingStrings = NULL;
|
||||
static unichar *cssEscapingCharacters = NULL;
|
||||
static int cssEscapingCount;
|
||||
|
||||
static unichar thisCharCode[29];
|
||||
static unichar thisCharCode[30];
|
||||
static NSString *controlCharString = nil;
|
||||
static NSCharacterSet *controlCharSet = nil;
|
||||
|
||||
@@ -285,8 +285,8 @@ static NSCharacterSet *controlCharSet = nil;
|
||||
int i, j;
|
||||
|
||||
// Create an array of chars for all control characters between 0x00 and 0x1F,
|
||||
// apart from \t, \n, \f and \r (0x08, 0x09, 0x0A, 0x0C and 0x0D)
|
||||
for (i = 0, j = 0x00; j < 0x08; i++, j++) {
|
||||
// apart from \t, \n, \f and \r (0x09, 0x0A, 0x0C and 0x0D)
|
||||
for (i = 0, j = 0x00; j <= 0x08; i++, j++) {
|
||||
thisCharCode[i] = j;
|
||||
}
|
||||
thisCharCode[i++] = 0x0B;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIDBFolder.h - this file is part of SOGo
|
||||
/* SOGoCacheGCSFolder.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -20,10 +18,10 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOMAPIDBFOLDER_H
|
||||
#define SOGOMAPIDBFOLDER_H
|
||||
#ifndef SOGOCACHEGCSFOLDER_H
|
||||
#define SOGOCACHEGCSFOLDER_H
|
||||
|
||||
#import "SOGoMAPIDBObject.h"
|
||||
#import "SOGoCacheGCSObject.h"
|
||||
|
||||
@class NSArray;
|
||||
@class NSMutableString;
|
||||
@@ -32,12 +30,10 @@
|
||||
|
||||
@class EOQualifier;
|
||||
|
||||
@class SOGoMAPIDBMessage;
|
||||
|
||||
@interface SOGoMAPIDBFolder : SOGoMAPIDBObject
|
||||
@interface SOGoCacheGCSFolder : SOGoCacheGCSObject
|
||||
{
|
||||
NSString *pathPrefix; /* for root folders */
|
||||
SOGoMAPIDBObject *aclMessage;
|
||||
SOGoCacheGCSObject *aclMessage;
|
||||
}
|
||||
|
||||
- (void) setPathPrefix: (NSString *) newPathPrefix;
|
||||
@@ -47,7 +43,7 @@
|
||||
- (NSArray *) toOneRelationshipKeys;
|
||||
- (NSArray *) toManyRelationshipKeys;
|
||||
|
||||
- (NSArray *) childKeysOfType: (MAPIDBObjectType) type
|
||||
- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type
|
||||
includeDeleted: (BOOL) includeDeleted
|
||||
matchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings;
|
||||
@@ -56,4 +52,4 @@
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOMAPIDBFOLDER_H */
|
||||
#endif /* SOGOCACHEGCSFOLDER_H */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIDBFolder.m - this file is part of SOGo
|
||||
/* SOGoCacheGCSFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -33,34 +31,32 @@
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
// #import <GDLContentStore/EOQualifier+GCS.m>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import "EOQualifier+MAPI.h"
|
||||
#import "GCSSpecialQueries+OpenChange.h"
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
#import "EOQualifier+SOGoCacheObject.h"
|
||||
#import "GCSSpecialQueries+SOGoCacheObject.h"
|
||||
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import "SOGoCacheGCSFolder.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <stdbool.h>
|
||||
#include <talloc.h>
|
||||
#include <util/time.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
#include <libmapiproxy.h>
|
||||
#include <param.h>
|
||||
//#include <stdbool.h>
|
||||
//#include <talloc.h>
|
||||
//#include <util/time.h>
|
||||
//#include <mapistore/mapistore.h>
|
||||
//#include <mapistore/mapistore_errors.h>
|
||||
//#include <libmapiproxy.h>
|
||||
//#include <param.h>
|
||||
|
||||
Class SOGoMAPIDBObjectK = Nil;
|
||||
Class SOGoCacheGCSObjectK = Nil;
|
||||
|
||||
@implementation SOGoMAPIDBFolder
|
||||
@implementation SOGoCacheGCSFolder
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
SOGoMAPIDBObjectK = [SOGoMAPIDBObject class];
|
||||
SOGoCacheGCSObjectK = [SOGoCacheGCSObject class];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
@@ -77,10 +73,10 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
{
|
||||
if ((self = [super initWithName: name inContainer: newContainer]))
|
||||
{
|
||||
objectType = MAPIDBObjectTypeFolder;
|
||||
aclMessage = [SOGoMAPIDBObject objectWithName: @"permissions"
|
||||
objectType = MAPIFolderCacheObject;
|
||||
aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions"
|
||||
inContainer: self];
|
||||
[aclMessage setObjectType: MAPIDBObjectTypeInternal];
|
||||
[aclMessage setObjectType: MAPIInternalCacheObject];
|
||||
[aclMessage retain];
|
||||
}
|
||||
|
||||
@@ -135,7 +131,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self];
|
||||
// }
|
||||
|
||||
- (NSArray *) childKeysOfType: (MAPIDBObjectType) type
|
||||
- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type
|
||||
includeDeleted: (BOOL) includeDeleted
|
||||
matchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
@@ -148,7 +144,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
NSArray *records;
|
||||
NSDictionary *record;
|
||||
NSUInteger childPathPrefixLen, count, max;
|
||||
SOGoMAPIDBObject *currentChild;
|
||||
SOGoCacheGCSObject *currentChild;
|
||||
|
||||
/* query construction */
|
||||
sql = [NSMutableString stringWithCapacity: 256];
|
||||
@@ -182,7 +178,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
{
|
||||
if (qualifier)
|
||||
{
|
||||
currentChild = [SOGoMAPIDBObject objectWithName: childKey
|
||||
currentChild = [SOGoCacheGCSObject objectWithName: childKey
|
||||
inContainer: self];
|
||||
[currentChild setupFromRecord: record];
|
||||
if ([qualifier evaluateSOGoMAPIDBObject: currentChild])
|
||||
@@ -201,7 +197,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys
|
||||
{
|
||||
return [self childKeysOfType: MAPIDBObjectTypeFolder
|
||||
return [self childKeysOfType: MAPIFolderCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: nil
|
||||
andSortOrderings: nil];
|
||||
@@ -209,7 +205,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
|
||||
- (NSArray *) toOneRelationshipKeys
|
||||
{
|
||||
return [self childKeysOfType: MAPIDBObjectTypeMessage
|
||||
return [self childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: nil
|
||||
andSortOrderings: nil];
|
||||
@@ -359,10 +355,10 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
record = [self lookupRecord: childPath newerThanVersion: -1];
|
||||
if (record)
|
||||
{
|
||||
if ([[record objectForKey: @"c_type"] intValue] == MAPIDBObjectTypeFolder)
|
||||
if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject)
|
||||
objectClass = isa;
|
||||
else
|
||||
objectClass = SOGoMAPIDBObjectK;
|
||||
objectClass = SOGoCacheGCSObjectK;
|
||||
|
||||
object = [objectClass objectWithName: childName
|
||||
inContainer: self];
|
||||
@@ -379,7 +375,7 @@ Class SOGoMAPIDBObjectK = Nil;
|
||||
{
|
||||
id object;
|
||||
|
||||
object = [SOGoMAPIDBFolder objectWithName: folderName
|
||||
object = [SOGoCacheGCSFolder objectWithName: folderName
|
||||
inContainer: self];
|
||||
[object reloadIfNeeded];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIDBObject.h - this file is part of SOGo
|
||||
/* SOGoCacheGCSObject.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -20,10 +18,10 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOMAPIDBOBJECT_H
|
||||
#define SOGOMAPIDBOBJECT_H
|
||||
#ifndef SOGOCACHEGCSOBJECT_H
|
||||
#define SOGOCACHEGCSOBJECT_H
|
||||
|
||||
#import "SOGoMAPIObject.h"
|
||||
#import "SOGoCacheObject.h"
|
||||
|
||||
@class NSArray;
|
||||
@class NSMutableDictionary;
|
||||
@@ -34,18 +32,20 @@
|
||||
@class EOAdaptor;
|
||||
|
||||
typedef enum {
|
||||
MAPIDBObjectTypeFolder = 1,
|
||||
MAPIDBObjectTypeMessage = 2,
|
||||
MAPIDBObjectTypeFAI = 3,
|
||||
MAPIDBObjectTypeInternal = 99 /* object = property list */
|
||||
} MAPIDBObjectType;
|
||||
MAPIFolderCacheObject = 1,
|
||||
MAPIMessageCacheObject = 2,
|
||||
MAPIFAICacheObject = 3,
|
||||
MAPIInternalCacheObject = 99, /* object = property list */
|
||||
ActiveSyncGlobalCacheObject = 200,
|
||||
ActiveSyncFolderCacheObject = 201
|
||||
} SOGoCacheObjectType;
|
||||
|
||||
@interface SOGoMAPIDBObject : SOGoMAPIObject
|
||||
@interface SOGoCacheGCSObject : SOGoCacheObject
|
||||
{
|
||||
NSURL *tableUrl;
|
||||
|
||||
BOOL initialized; /* safe guard */
|
||||
MAPIDBObjectType objectType;
|
||||
SOGoCacheObjectType objectType;
|
||||
NSInteger version;
|
||||
BOOL deleted;
|
||||
}
|
||||
@@ -67,8 +67,11 @@ typedef enum {
|
||||
- (NSDictionary *) lookupRecord: (NSString *) path
|
||||
newerThanVersion: (NSInteger) startVersion;
|
||||
|
||||
- (void) setObjectType: (MAPIDBObjectType) newObjectType;
|
||||
- (MAPIDBObjectType) objectType; /* message, fai, folder */
|
||||
- (NSArray *) folderList: (NSString *) deviceId
|
||||
newerThanVersion: (NSInteger) startVersion;
|
||||
|
||||
- (void) setObjectType: (SOGoCacheObjectType) newObjectType;
|
||||
- (SOGoCacheObjectType) objectType; /* message, fai, folder */
|
||||
|
||||
/* automatically set from actions */
|
||||
- (BOOL) deleted;
|
||||
@@ -82,4 +85,4 @@ typedef enum {
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOMAPIDBOBJECT_H */
|
||||
#endif /* SOGOCACHEGCSOBJECT_H */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIDBObject.m - this file is part of SOGo
|
||||
/* SOGoCacheGCSObject.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -43,38 +41,20 @@
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import "GCSSpecialQueries+OpenChange.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "SOGoMAPIDBFolder.h"
|
||||
#import "GCSSpecialQueries+SOGoCacheObject.h"
|
||||
#import "SOGoCacheGCSFolder.h"
|
||||
#import "BSONCodec.h"
|
||||
|
||||
#import "SOGoMAPIDBObject.h"
|
||||
|
||||
//
|
||||
// This defines the storage for internal properly list, stored in the
|
||||
// database. Possible values are:
|
||||
//
|
||||
// NSPropertyListGNUstepFormat = 1000
|
||||
// NSPropertyListGNUstepBinaryFormat = 1001
|
||||
// NSPropertyListOpenStepFormat = 1
|
||||
// NSPropertyListXMLFormat_v1_0 = 100
|
||||
// NSPropertyListBinaryFormat_v1_0 = 200
|
||||
//
|
||||
static NSPropertyListFormat plistFormat;
|
||||
#import "SOGoCacheGCSObject.h"
|
||||
|
||||
static EOAttribute *textColumn = nil;
|
||||
|
||||
@implementation SOGoMAPIDBObject
|
||||
@implementation SOGoCacheGCSObject
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSDictionary *description;
|
||||
|
||||
plistFormat = [[NSUserDefaults standardUserDefaults] integerForKey: @"SOGoPropertyListFormat"];
|
||||
|
||||
if (!plistFormat)
|
||||
plistFormat = NSPropertyListGNUstepBinaryFormat;
|
||||
|
||||
if (!textColumn)
|
||||
{
|
||||
/* TODO: this is a hack for providing an EOAttribute definition that is
|
||||
@@ -92,19 +72,14 @@ static EOAttribute *textColumn = nil;
|
||||
/*
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_path VARCHAR(255) PRIMARY KEY,"
|
||||
@" c_type VARCHAR(20) NOT NULL,"
|
||||
@" c_parent_path VARCHAR(255),"
|
||||
@" c_type SMALLINT NOT NULL,"
|
||||
@" c_creationdate INT4 NOT NULL,"
|
||||
@" c_lastmodified INT4 NOT NULL,"
|
||||
@" c_version INT4 NOT NULL DEFAULT 0,"
|
||||
@" c_deleted SMALLINT NOT NULL DEFAULT 0,"
|
||||
@" c_content TEXT)");
|
||||
*/
|
||||
|
||||
/* indexes:
|
||||
c_path (primary key)
|
||||
c_counter
|
||||
c_path, c_type
|
||||
c_path, c_creationdate */
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
@@ -137,7 +112,7 @@ static EOAttribute *textColumn = nil;
|
||||
tableUrl = [container tableUrl];
|
||||
[tableUrl retain];
|
||||
if (!tableUrl)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"table url is not set for object '%@'", self];
|
||||
}
|
||||
|
||||
@@ -157,9 +132,8 @@ static EOAttribute *textColumn = nil;
|
||||
- (void) setupFromRecord: (NSDictionary *) record
|
||||
{
|
||||
NSInteger intValue;
|
||||
NSString *propsValue;//, *error;
|
||||
NSString *propsValue;
|
||||
NSDictionary *newValues;
|
||||
//NSPropertyListFormat format;
|
||||
|
||||
objectType = [[record objectForKey: @"c_type"] intValue];
|
||||
intValue = [[record objectForKey: @"c_creationdate"] intValue];
|
||||
@@ -195,7 +169,7 @@ static EOAttribute *textColumn = nil;
|
||||
path = [NSMutableString stringWithFormat: @"/%@", nameInContainer];
|
||||
|
||||
if ([path rangeOfString: @"//"].location != NSNotFound)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"object path has not been properly set for"
|
||||
" folder '%@' (%@)",
|
||||
self, path];
|
||||
@@ -203,12 +177,12 @@ static EOAttribute *textColumn = nil;
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void) setObjectType: (MAPIDBObjectType) newObjectType
|
||||
- (void) setObjectType: (SOGoCacheObjectType) newObjectType
|
||||
{
|
||||
objectType = newObjectType;
|
||||
}
|
||||
|
||||
- (MAPIDBObjectType) objectType /* message, fai, folder */
|
||||
- (SOGoCacheObjectType) objectType /* message, fai, folder */
|
||||
{
|
||||
return objectType;
|
||||
}
|
||||
@@ -216,7 +190,7 @@ static EOAttribute *textColumn = nil;
|
||||
- (NSCalendarDate *) creationDate
|
||||
{
|
||||
if (!initialized)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"record has not been initialized: %@", self];
|
||||
|
||||
return creationDate;
|
||||
@@ -225,7 +199,7 @@ static EOAttribute *textColumn = nil;
|
||||
- (NSCalendarDate *) lastModified
|
||||
{
|
||||
if (!initialized)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"record has not been initialized: %@", self];
|
||||
|
||||
return lastModified;
|
||||
@@ -236,43 +210,6 @@ static EOAttribute *textColumn = nil;
|
||||
return deleted;
|
||||
}
|
||||
|
||||
- (Class) mapistoreMessageClass
|
||||
{
|
||||
NSString *className, *mapiMsgClass;
|
||||
|
||||
switch (objectType)
|
||||
{
|
||||
case MAPIDBObjectTypeMessage:
|
||||
mapiMsgClass = [properties
|
||||
objectForKey: MAPIPropertyKey (PidTagMessageClass)];
|
||||
if (mapiMsgClass)
|
||||
{
|
||||
if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"])
|
||||
className = @"MAPIStoreNotesMessage";
|
||||
else
|
||||
className = @"MAPIStoreDBMessage";
|
||||
//[self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'",
|
||||
// mapiMsgClass, className];
|
||||
}
|
||||
else
|
||||
{
|
||||
//[self warnWithFormat: @"PidTagMessageClass is not set, falling back"
|
||||
// @" to 'MAPIStoreDBMessage'"];
|
||||
className = @"MAPIStoreDBMessage";
|
||||
}
|
||||
break;
|
||||
case MAPIDBObjectTypeFAI:
|
||||
className = @"MAPIStoreFAIMessage";
|
||||
break;
|
||||
default:
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"message class should not be queried for objects"
|
||||
@" of type '%d'", objectType];
|
||||
}
|
||||
|
||||
return NSClassFromString (className);
|
||||
}
|
||||
|
||||
/* actions */
|
||||
- (void) setNameInContainer: (NSString *) newNameInContainer
|
||||
{
|
||||
@@ -413,7 +350,7 @@ static EOAttribute *textColumn = nil;
|
||||
EOAdaptor *adaptor;
|
||||
|
||||
if ([path hasSuffix: @"/"])
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"path ends with a slash: %@", path];
|
||||
|
||||
tableName = [self tableName];
|
||||
@@ -438,6 +375,47 @@ static EOAttribute *textColumn = nil;
|
||||
return record;
|
||||
}
|
||||
|
||||
// get a list of all folders
|
||||
- (NSArray *) folderList: (NSString *) deviceId
|
||||
newerThanVersion: (NSInteger) startVersion
|
||||
{
|
||||
NSMutableArray *recordsOut;
|
||||
NSArray *records;
|
||||
NSString *tableName, *pathValue;
|
||||
NSMutableString *sql;
|
||||
EOAdaptor *adaptor;
|
||||
NSUInteger count, max;
|
||||
|
||||
if ([deviceId hasSuffix: @"/"])
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"path ends with a slash: %@", deviceId];
|
||||
|
||||
tableName = [self tableName];
|
||||
adaptor = [self tableChannelAdaptor];
|
||||
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@+folder%", deviceId]
|
||||
forAttribute: textColumn];
|
||||
|
||||
/* query */
|
||||
sql = [NSMutableString stringWithFormat:
|
||||
@"SELECT * FROM %@ WHERE c_path LIKE %@ AND c_deleted <> 1",
|
||||
tableName, pathValue];
|
||||
if (startVersion > -1)
|
||||
[sql appendFormat: @" AND c_version > %d", startVersion];
|
||||
|
||||
/* execution */
|
||||
records = [self performSQLQuery: sql];
|
||||
|
||||
max = [records count];
|
||||
recordsOut = [[NSMutableArray alloc] init];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
[recordsOut addObject: [[records objectAtIndex: count] objectForKey: @"c_path"]];
|
||||
}
|
||||
|
||||
return recordsOut;
|
||||
}
|
||||
|
||||
|
||||
- (void) reloadIfNeeded
|
||||
{
|
||||
/* if object is uninitialized: reload without condition, otherwise, load if
|
||||
@@ -491,7 +469,7 @@ static EOAttribute *textColumn = nil;
|
||||
NSException *result;
|
||||
|
||||
if (!initialized)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"record has not been initialized: %@", self];
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
@@ -503,12 +481,6 @@ static EOAttribute *textColumn = nil;
|
||||
now = [NSCalendarDate date];
|
||||
ASSIGN (lastModified, now);
|
||||
|
||||
/*
|
||||
- (NSException *)insertRowX:(NSDictionary *)_row forEntity:(EOEntity *)_entity;
|
||||
- (NSException *)updateRowX:(NSDictionary*)aRow
|
||||
describedByQualifier:(EOSQLQualifier*)aQualifier;
|
||||
*/
|
||||
|
||||
adaptor = [[channel adaptorContext] adaptor];
|
||||
pathValue = [adaptor formatValue: [self path]
|
||||
forAttribute: textColumn];
|
||||
@@ -516,7 +488,7 @@ static EOAttribute *textColumn = nil;
|
||||
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
|
||||
|
||||
if (objectType == -1)
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"object type has not been set for object '%@'",
|
||||
self];
|
||||
|
||||
@@ -567,12 +539,6 @@ static EOAttribute *textColumn = nil;
|
||||
if (result)
|
||||
[self errorWithFormat: @"could not insert/update record for record %@"
|
||||
@" in %@: %@", pathValue, tableName, result];
|
||||
// @" c_path VARCHAR(255) PRIMARY KEY,"
|
||||
// @" c_type SMALLINT NOT NULL,"
|
||||
// @" c_creationdate INT4 NOT NULL,"
|
||||
// @" c_lastmodified INT4 NOT NULL,"
|
||||
// @" c_deleted SMALLINT NOT NULL DEFAULT 0,"
|
||||
// @" c_content BLOB");
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIObject.h - this file is part of SOGo
|
||||
/* SOGoCacheObject.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -20,14 +18,14 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOMAPIOBJECT_H
|
||||
#define SOGOMAPIOBJECT_H
|
||||
#ifndef SOGOCACHEOBJECT_H
|
||||
#define SOGOCACHEOBJECT_H
|
||||
|
||||
#import <SOGo/SOGoObject.h>
|
||||
|
||||
@class NSMutableDictionary;
|
||||
|
||||
@interface SOGoMAPIObject : SOGoObject
|
||||
@interface SOGoCacheObject : SOGoObject
|
||||
{
|
||||
BOOL isNew;
|
||||
NSMutableDictionary *properties;
|
||||
@@ -46,4 +44,4 @@
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOMAPIOBJECT_H */
|
||||
#endif /* SOGOCACHEOBJECT_H */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoMAPIObject.m - this file is part of SOGo
|
||||
/* SOGoCacgeObject.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2012-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
|
||||
@@ -20,12 +18,12 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
#import "SOGoMAPIObject.h"
|
||||
#import "SOGoCacheObject.h"
|
||||
|
||||
@implementation SOGoMAPIObject
|
||||
@implementation SOGoCacheObject
|
||||
|
||||
- (id) init
|
||||
{
|
||||
@@ -51,4 +51,10 @@ typedef enum
|
||||
EventUpdated = 2,
|
||||
} SOGoEventOperation;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SOGoPersonalFolder = 0,
|
||||
SOGoCollectedFolder = 1,
|
||||
} SOGoFolderType;
|
||||
|
||||
#endif /* _SOGOCONSTANTS_H_ */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
SOGoZipPath = "/usr/bin/zip";
|
||||
|
||||
SOGoEncryptionKey = "MySOGoEncryptionKey";
|
||||
SOGoSieveFolderEncoding = "UTF-7";
|
||||
|
||||
WOUseRelativeURLs = YES;
|
||||
WOMessageUseUTF8 = YES;
|
||||
@@ -53,6 +54,7 @@
|
||||
|
||||
SOGoIMAPServer = "localhost";
|
||||
SOGoMailDomain = "localhost";
|
||||
SOGoSelectedAddressBook = "collected";
|
||||
SOGoMailMessageCheck = "manually";
|
||||
SOGoMailMessageForwarding = "inline";
|
||||
SOGoMailReplyPlacement = "below";
|
||||
@@ -83,4 +85,6 @@
|
||||
$label4 = ("To Do", "#3333FF");
|
||||
$label5 = ("Later", "#993399");
|
||||
};
|
||||
|
||||
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
- (NSArray *) calendarDefaultRoles;
|
||||
- (NSArray *) contactsDefaultRoles;
|
||||
- (NSArray *) mailPollingIntervals;
|
||||
- (NSString *) subscriptionFolderFormat;
|
||||
|
||||
- (NSString *) calendarDefaultCategoryColor;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoDomainDefaults.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2012 Inverse inc.
|
||||
* Copyright (C) 2009-2014 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
@@ -176,6 +176,11 @@
|
||||
return [self stringArrayForKey: @"SOGoContactsDefaultRoles"];
|
||||
}
|
||||
|
||||
- (NSString *) subscriptionFolderFormat
|
||||
{
|
||||
return [self stringForKey: @"SOGoSubscriptionFolderFormat"];
|
||||
}
|
||||
|
||||
//
|
||||
// In v2.0.4, SOGoForceIMAPLoginWithEmail was renamed to SOGoForceExternalLoginWithEmail
|
||||
// but we keep backward compatbility for now with previous versions.
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
NSString *ocsPath;
|
||||
GCSFolder *ocsFolder;
|
||||
NSMutableDictionary *childRecords;
|
||||
NSMutableDictionary *folderSubscriptionValues;
|
||||
BOOL userCanAccessAllObjects; /* i.e. user obtains 'Access Object' on
|
||||
subobjects */
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/* SOGoGCSFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
* Copyright (C) 2006-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* 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
|
||||
@@ -198,6 +196,7 @@ static NSArray *childRecordFields = nil;
|
||||
ocsPath = nil;
|
||||
ocsFolder = nil;
|
||||
childRecords = [NSMutableDictionary new];
|
||||
folderSubscriptionValues = nil;
|
||||
userCanAccessAllObjects = NO;
|
||||
}
|
||||
|
||||
@@ -209,6 +208,7 @@ static NSArray *childRecordFields = nil;
|
||||
[ocsFolder release];
|
||||
[ocsPath release];
|
||||
[childRecords release];
|
||||
[folderSubscriptionValues release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -277,29 +277,38 @@ static NSArray *childRecordFields = nil;
|
||||
{
|
||||
NSString *primaryDN;
|
||||
NSDictionary *ownerIdentity;
|
||||
|
||||
NSString *subjectFormat;
|
||||
SOGoDomainDefaults *dd;
|
||||
|
||||
primaryDN = [row objectForKey: @"c_foldername"];
|
||||
if ([primaryDN length])
|
||||
{
|
||||
displayName = [NSMutableString new];
|
||||
if ([primaryDN isEqualToString: [container defaultFolderName]])
|
||||
[displayName appendString: [self labelForKey: primaryDN
|
||||
inContext: context]];
|
||||
else
|
||||
[displayName appendString: primaryDN];
|
||||
|
||||
if (!activeUserIsOwner)
|
||||
{
|
||||
displayName = [NSMutableString new];
|
||||
if ([primaryDN isEqualToString: [container defaultFolderName]])
|
||||
[displayName appendString: [self labelForKey: primaryDN
|
||||
inContext: context]];
|
||||
else
|
||||
[displayName appendString: primaryDN];
|
||||
// We MUST NOT use SOGoUser instances here (by calling -primaryIdentity)
|
||||
// as it'll load user defaults and user settings which is _very costly_
|
||||
// since it involves JSON parsing and database requests
|
||||
ownerIdentity = [[SOGoUserManager sharedUserManager]
|
||||
contactInfosForUserWithUIDorEmail: owner];
|
||||
|
||||
if (!activeUserIsOwner)
|
||||
{
|
||||
// We MUST NOT use SOGoUser instances here (by calling -primaryIdentity)
|
||||
// as it'll load user defaults and user settings which is _very costly_
|
||||
// since it involves JSON parsing and database requests
|
||||
ownerIdentity = [[SOGoUserManager sharedUserManager]
|
||||
contactInfosForUserWithUIDorEmail: owner];
|
||||
|
||||
[displayName appendFormat: @" (%@ <%@>)", [ownerIdentity objectForKey: @"cn"],
|
||||
[ownerIdentity objectForKey: @"c_email"]];
|
||||
}
|
||||
folderSubscriptionValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys: displayName, @"FolderName",
|
||||
[ownerIdentity objectForKey: @"cn"], @"UserName",
|
||||
[ownerIdentity objectForKey: @"c_email"], @"Email", nil];
|
||||
|
||||
dd = [[context activeUser] domainDefaults];
|
||||
subjectFormat = [dd subscriptionFolderFormat];
|
||||
|
||||
displayName = [folderSubscriptionValues keysWithFormat: subjectFormat];
|
||||
[displayName retain];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This method fetches the display name defined by the owner, but is also the
|
||||
@@ -1358,11 +1367,15 @@ static NSArray *childRecordFields = nil;
|
||||
}
|
||||
|
||||
NSZoneFree (NULL, selectors);
|
||||
|
||||
|
||||
/* If we haven't gotten any result to return, let's use the previously
|
||||
supplied sync-token */
|
||||
if (max == 0)
|
||||
newToken = syncToken;
|
||||
/* If the most recent c_lastmodified is "now", we need to return "now - 1"
|
||||
in order to make sure during the next sync that every records that might
|
||||
get added at the same moment are not lost. */
|
||||
if (!newToken || newToken == now)
|
||||
else if (!newToken || newToken == now)
|
||||
newToken = now - 1;
|
||||
|
||||
newTokenStr = [NSString stringWithFormat: @"%d", newToken];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoParentFolder.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2013 Inverse inc.
|
||||
* 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
|
||||
@@ -22,7 +22,9 @@
|
||||
#define SOGOPARENTFOLDERS_H
|
||||
|
||||
#import "SOGoFolder.h"
|
||||
#import "SOGoConstants.h"
|
||||
|
||||
@class EOAdaptorChannel;
|
||||
@class NSMutableDictionary;
|
||||
@class NSString;
|
||||
@class WOResponse;
|
||||
@@ -39,6 +41,7 @@
|
||||
+ (Class) subFolderClass;
|
||||
|
||||
- (NSString *) defaultFolderName;
|
||||
- (NSString *) collectedFolderName;
|
||||
|
||||
- (NSException *) appendPersonalSources;
|
||||
- (void) removeSubFolder: (NSString *) subfolderName;
|
||||
@@ -58,6 +61,10 @@
|
||||
- (id) lookupPersonalFolder: (NSString *) name
|
||||
ignoringRights: (BOOL) ignoreRights;
|
||||
|
||||
- (NSException *) fetchSpecialFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
andFolderType: (SOGoFolderType) folderType;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOPARENTFOLDERS_H */
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#import <DOM/DOMElement.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
|
||||
#import "NSObject+DAV.h"
|
||||
@@ -153,9 +153,15 @@ static SoSecurityManager *sm = nil;
|
||||
return @"Personal";
|
||||
}
|
||||
|
||||
- (void) _createPersonalFolder
|
||||
- (NSString *) collectedFolderName
|
||||
{
|
||||
return @"Collected";
|
||||
}
|
||||
|
||||
- (void) createSpecialFolder: (SOGoFolderType) folderType
|
||||
{
|
||||
NSArray *roles;
|
||||
NSString *folderName;
|
||||
SOGoGCSFolder *folder;
|
||||
SOGoUser *folderOwner;
|
||||
|
||||
@@ -165,50 +171,70 @@ static SoSecurityManager *sm = nil;
|
||||
// We autocreate the calendars if the user is the owner, a superuser or
|
||||
// if it's a resource as we won't necessarily want to login as a resource
|
||||
// in order to create its database tables.
|
||||
// FolderType is an enum where 0 = Personal and 1 = collected
|
||||
if ([roles containsObject: SoRole_Owner] ||
|
||||
(folderOwner && [folderOwner isResource]))
|
||||
{
|
||||
folder = [subFolderClass objectWithName: @"personal" inContainer: self];
|
||||
[folder setDisplayName: [self defaultFolderName]];
|
||||
[folder
|
||||
setOCSPath: [NSString stringWithFormat: @"%@/personal", OCSPath]];
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
folderName = @"personal";
|
||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||
[folder setDisplayName: [self defaultFolderName]];
|
||||
}
|
||||
else if (folderType == SOGoCollectedFolder)
|
||||
{
|
||||
folderName = @"collected";
|
||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||
[folder setDisplayName: [self collectedFolderName]];
|
||||
}
|
||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
||||
|
||||
if ([folder create])
|
||||
[subFolders setObject: folder forKey: @"personal"];
|
||||
[subFolders setObject: folder forKey: folderName];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSException *) _fetchPersonalFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
- (NSException *) fetchSpecialFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
andFolderType: (SOGoFolderType) folderType
|
||||
{
|
||||
NSArray *attrs;
|
||||
NSDictionary *row;
|
||||
SOGoGCSFolder *folder;
|
||||
NSString *key;
|
||||
NSException *error;
|
||||
SOGoUserDefaults *ud;
|
||||
ud = [[context activeUser] userDefaults];
|
||||
|
||||
if (!subFolderClass)
|
||||
subFolderClass = [[self class] subFolderClass];
|
||||
|
||||
error = [fc evaluateExpressionX: sql];
|
||||
if (!error)
|
||||
{
|
||||
attrs = [fc describeResults: NO];
|
||||
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
attrs = [fc describeResults: NO];
|
||||
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
key = [row objectForKey: @"c_path4"];
|
||||
if ([key isKindOfClass: [NSString class]])
|
||||
{
|
||||
folder = [subFolderClass objectWithName: key inContainer: self];
|
||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@",
|
||||
OCSPath, key]];
|
||||
[subFolders setObject: folder forKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
if (![subFolders objectForKey: @"personal"])
|
||||
[self _createPersonalFolder];
|
||||
key = [row objectForKey: @"c_path4"];
|
||||
if ([key isKindOfClass: [NSString class]])
|
||||
{
|
||||
folder = [subFolderClass objectWithName: key inContainer: self];
|
||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, key]];
|
||||
[subFolders setObject: folder forKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
if (![subFolders objectForKey: @"personal"])
|
||||
[self createSpecialFolder: SOGoPersonalFolder];
|
||||
}
|
||||
else if (folderType == SOGoCollectedFolder)
|
||||
{
|
||||
if (![subFolders objectForKey: @"collected"])
|
||||
if ([[ud selectedAddressBook] isEqualToString:@"collected"])
|
||||
[self createSpecialFolder: SOGoCollectedFolder];
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -221,25 +247,21 @@ static SoSecurityManager *sm = nil;
|
||||
NSException *error;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
folderLocation
|
||||
= [[GCSFolderManager defaultFolderManager] folderInfoLocation];
|
||||
folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
|
||||
fc = [cm acquireOpenChannelForURL: folderLocation];
|
||||
if ([fc isOpen])
|
||||
{
|
||||
gcsFolderType = [[self class] gcsFolderType];
|
||||
|
||||
sql
|
||||
= [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
|
||||
@" WHERE c_path2 = '%@'"
|
||||
@" AND c_folder_type = '%@'"),
|
||||
[folderLocation gcsTableName],
|
||||
owner,
|
||||
gcsFolderType];
|
||||
error = [self _fetchPersonalFolders: sql withChannel: fc];
|
||||
[cm releaseChannel: fc];
|
||||
// sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'",
|
||||
// uidColumnName, [self uid]];
|
||||
}
|
||||
{
|
||||
gcsFolderType = [[self class] gcsFolderType];
|
||||
|
||||
sql = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
|
||||
@" WHERE c_path2 = '%@'"
|
||||
@" AND c_folder_type = '%@'"),
|
||||
[folderLocation gcsTableName], owner, gcsFolderType];
|
||||
|
||||
error = [self fetchSpecialFolders: sql withChannel: fc andFolderType: SOGoPersonalFolder];
|
||||
|
||||
[cm releaseChannel: fc];
|
||||
}
|
||||
else
|
||||
error = [NSException exceptionWithName: @"SOGoDBException"
|
||||
reason: @"database connection could not be open"
|
||||
@@ -248,6 +270,7 @@ static SoSecurityManager *sm = nil;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
- (NSException *) appendSystemSources
|
||||
{
|
||||
return nil;
|
||||
@@ -388,12 +411,15 @@ static SoSecurityManager *sm = nil;
|
||||
subFolders = [NSMutableDictionary new];
|
||||
error = [self appendPersonalSources];
|
||||
if (!error)
|
||||
error = [self appendSystemSources];
|
||||
if ([self respondsToSelector:@selector(appendCollectedSources)])
|
||||
error = [self appendCollectedSources];
|
||||
if (!error)
|
||||
error = [self appendSystemSources]; // TODO : Not really a testcase, see function
|
||||
if (error)
|
||||
{
|
||||
[subFolders release];
|
||||
subFolders = nil;
|
||||
}
|
||||
{
|
||||
[subFolders release];
|
||||
subFolders = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
error = nil;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoPermissions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* 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
|
||||
|
||||
@@ -584,7 +584,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
int count, max;
|
||||
NSDictionary *currentScript;
|
||||
|
||||
sieveScript = [NSMutableString stringWithCapacity: 8192];
|
||||
sieveScript = [NSMutableString string];
|
||||
|
||||
ASSIGN (requirements, newRequirements);
|
||||
[scriptError release];
|
||||
@@ -755,7 +755,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
return nil;
|
||||
}
|
||||
|
||||
return client;
|
||||
return [client autorelease];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
- (BOOL) trustProxyAuthentication;
|
||||
- (NSString *) encryptionKey;
|
||||
- (BOOL) useRelativeURLs;
|
||||
- (NSString *) sieveFolderEncoding;
|
||||
|
||||
- (BOOL) isWebAccessEnabled;
|
||||
- (BOOL) isCalendarDAVAccessEnabled;
|
||||
@@ -95,6 +96,7 @@
|
||||
- (int) maximumPingInterval;
|
||||
- (int) maximumSyncInterval;
|
||||
- (int) internalSyncInterval;
|
||||
- (int) maximumSyncWindowSize;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -356,6 +356,12 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self boolForKey: @"WOUseRelativeURLs"];
|
||||
}
|
||||
|
||||
- (NSString *) sieveFolderEncoding
|
||||
{
|
||||
return [self stringForKey: @"SOGoSieveFolderEncoding"];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) isWebAccessEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoWebAccessEnabled"];
|
||||
@@ -615,4 +621,9 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return v;
|
||||
}
|
||||
|
||||
- (int) maximumSyncWindowSize
|
||||
{
|
||||
return [self integerForKey: @"SOGoMaximumSyncWindowSize"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -87,6 +87,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
||||
- (NSString *) language;
|
||||
|
||||
/* mail */
|
||||
- (void) setMailAddOutgoingAddresses: (BOOL) newValue;
|
||||
- (BOOL) mailAddOutgoingAddresses;
|
||||
|
||||
- (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue;
|
||||
- (BOOL) mailShowSubscribedFoldersOnly;
|
||||
|
||||
@@ -111,6 +114,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
||||
- (void) setMailListViewColumnsOrder: (NSArray *) newValue;
|
||||
- (NSArray *) mailListViewColumnsOrder;
|
||||
|
||||
- (void) setSelectedAddressBook: (NSString *) newValue;
|
||||
- (NSString *) selectedAddressBook;
|
||||
|
||||
- (void) setMailMessageCheck: (NSString *) newValue;
|
||||
- (NSString *) mailMessageCheck;
|
||||
|
||||
|
||||
@@ -185,8 +185,8 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
{
|
||||
migratedKeys
|
||||
= [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"SOGoLoginModule", @"SOGoUIxDefaultModule",
|
||||
@"SOGoLoginModule", @"SOGoDefaultModule",
|
||||
@"SOGoLoginModule", @"SOGoUIxDefaultModule",
|
||||
@"SOGoLoginModule", @"SOGoDefaultModule",
|
||||
@"SOGoTimeFormat", @"TimeFormat",
|
||||
@"SOGoShortDateFormat", @"ShortDateFormat",
|
||||
@"SOGoLongDateFormat", @"LongDateFormat",
|
||||
@@ -197,6 +197,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
@"SOGoLanguage", @"SOGoDefaultLanguage",
|
||||
@"SOGoLanguage", @"Language",
|
||||
@"SOGoMailComposeMessageType", @"ComposeMessagesType",
|
||||
@"SOGoSelectedAddressBook", @"SelectedAddressBook",
|
||||
@"SOGoMailMessageCheck", @"MessageCheck",
|
||||
@"SOGoMailMessageForwarding", @"MessageForwarding",
|
||||
@"SOGoMailSignature", @"MailSignature",
|
||||
@@ -384,6 +385,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
return userLanguage;
|
||||
}
|
||||
|
||||
- (void) setMailAddOutgoingAddresses: (BOOL) newValue
|
||||
{
|
||||
[self setBool: newValue forKey: @"SOGoMailAddOutgoingAddresses"];
|
||||
}
|
||||
|
||||
- (BOOL) mailAddOutgoingAddresses
|
||||
{
|
||||
return [self boolForKey: @"SOGoMailAddOutgoingAddresses"];
|
||||
}
|
||||
|
||||
- (void) setMailShowSubscribedFoldersOnly: (BOOL) newValue
|
||||
{
|
||||
[self setBool: newValue forKey: @"SOGoMailShowSubscribedFoldersOnly"];
|
||||
@@ -467,6 +478,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
return [self stringArrayForKey: @"SOGoMailListViewColumnsOrder"];
|
||||
}
|
||||
|
||||
- (void) setSelectedAddressBook:(NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoSelectedAddressBook"];
|
||||
}
|
||||
|
||||
- (NSString *) selectedAddressBook
|
||||
{
|
||||
return [self stringForKey: @"SOGoSelectedAddressBook"];
|
||||
}
|
||||
|
||||
- (void) setMailMessageCheck: (NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoMailMessageCheck"];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoUserSettings.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2013 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
|
||||
@@ -34,13 +34,6 @@
|
||||
- (NSArray *) subscribedCalendars;
|
||||
- (NSArray *) subscribedAddressBooks;
|
||||
|
||||
|
||||
/* Microsoft Active Sync support */
|
||||
- (void) setMicrosoftActiveSyncMetadata: (NSDictionary *) theMetadata
|
||||
forDevice: (NSString *) theDeviceID;
|
||||
|
||||
- (NSMutableDictionary *) microsoftActiveSyncMetadataForDevice: (NSString *) theDevice;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOUSERSETTINGS_H */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user