Monotone-Parent: e785838ad834b8a805eeb85700cd115f2501c0f7
Monotone-Revision: db5e568c7dc79c31416d0756d5275c3a7271ea61 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-06-19T14:49:47 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
parent
853cc1312e
commit
1a67a3454b
|
@ -1,5 +1,9 @@
|
||||||
2009-06-19 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
2009-06-19 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
|
* SoObjects/SOGo/SOGoGCSFolder.m (davSyncCollection:): new method
|
||||||
|
that implements the draft webdav sync specification
|
||||||
|
(http://ietfreport.isoc.org/idref/draft-daboo-webdav-sync/).
|
||||||
|
|
||||||
* SoObjects/SOGo/NSString+DAV.m (asWebDAVTupleWithContent:): new
|
* SoObjects/SOGo/NSString+DAV.m (asWebDAVTupleWithContent:): new
|
||||||
method that returns a DAV dictionary from a flat DAV element (self)
|
method that returns a DAV dictionary from a flat DAV element (self)
|
||||||
string and a content.
|
string and a content.
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
#import "NSArray+Utilities.h"
|
#import "NSArray+Utilities.h"
|
||||||
#import "NSObject+DAV.h"
|
#import "NSObject+DAV.h"
|
||||||
#import "NSString+Utilities.h"
|
#import "NSString+Utilities.h"
|
||||||
|
#import "NSString+DAV.h"
|
||||||
|
|
||||||
#import "DOMNode+SOGo.h"
|
#import "DOMNode+SOGo.h"
|
||||||
#import "SOGoContentObject.h"
|
#import "SOGoContentObject.h"
|
||||||
|
@ -842,6 +843,245 @@ static NSArray *childRecordFields = nil;
|
||||||
return sqlFieldsTable;
|
return sqlFieldsTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) _fetchSyncTokenFields: (NSDictionary *) properties
|
||||||
|
matchingSyncToken: (int) syncToken
|
||||||
|
{
|
||||||
|
/* TODO:
|
||||||
|
- validation:
|
||||||
|
- synctoken and return "DAV:valid-sync-token" as error if needed
|
||||||
|
- properties
|
||||||
|
- database errors */
|
||||||
|
NSMutableArray *fields;
|
||||||
|
EOQualifier *qualifier;
|
||||||
|
GCSFolder *folder;
|
||||||
|
EOFetchSpecification *syncSpec;
|
||||||
|
|
||||||
|
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component",
|
||||||
|
@"c_creationdate", @"c_lastmodified", @"c_deleted",
|
||||||
|
nil];
|
||||||
|
[fields addObjectsFromArray: [properties allValues]];
|
||||||
|
|
||||||
|
if (syncToken)
|
||||||
|
qualifier
|
||||||
|
= [EOQualifier qualifierWithQualifierFormat:
|
||||||
|
[NSString stringWithFormat: @"c_lastmodified > %d",
|
||||||
|
syncToken]];
|
||||||
|
else
|
||||||
|
qualifier = nil;
|
||||||
|
|
||||||
|
folder = [self ocsFolder];
|
||||||
|
syncSpec = [EOFetchSpecification
|
||||||
|
fetchSpecificationWithEntityName: [folder folderName]
|
||||||
|
qualifier: qualifier
|
||||||
|
sortOrderings: nil];
|
||||||
|
return [folder fetchFields: fields fetchSpecification: syncSpec
|
||||||
|
ignoreDeleted: (!syncToken)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These methods are the optimal ones to generate propstats for DAV reports,
|
||||||
|
it should be used in other subclasses. */
|
||||||
|
- (NSDictionary *) _davPropstat: (NSArray *) properties
|
||||||
|
withStatus: (NSString *) status
|
||||||
|
{
|
||||||
|
NSMutableArray *propstat;
|
||||||
|
|
||||||
|
propstat = [NSMutableArray arrayWithCapacity: 2];
|
||||||
|
[propstat addObject: davElementWithContent (@"prop", XMLNS_WEBDAV,
|
||||||
|
properties)];
|
||||||
|
[propstat addObject: davElementWithContent (@"status", XMLNS_WEBDAV,
|
||||||
|
status)];
|
||||||
|
|
||||||
|
return davElementWithContent (@"propstat", XMLNS_WEBDAV, propstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *) _davPropStatsWithProperties: (NSArray *) davProperties
|
||||||
|
andMethodSelectors: (SEL *) selectors
|
||||||
|
fromRecord: (NSDictionary *) record
|
||||||
|
{
|
||||||
|
SOGoContentObject *sogoObject;
|
||||||
|
unsigned int count, max;
|
||||||
|
NSMutableArray *properties200, *properties404, *propstats;
|
||||||
|
NSDictionary *propContent;
|
||||||
|
id result;
|
||||||
|
|
||||||
|
propstats = [NSMutableArray arrayWithCapacity: 2];
|
||||||
|
|
||||||
|
max = [davProperties count];
|
||||||
|
properties200 = [NSMutableArray arrayWithCapacity: max];
|
||||||
|
properties404 = [NSMutableArray arrayWithCapacity: max];
|
||||||
|
|
||||||
|
sogoObject = [self _createChildComponentWithRecord: record];
|
||||||
|
for (count = 0; count < max; count++)
|
||||||
|
{
|
||||||
|
if (selectors[count]
|
||||||
|
&& [sogoObject respondsToSelector: selectors[count]])
|
||||||
|
result = [sogoObject performSelector: selectors[count]];
|
||||||
|
else
|
||||||
|
result = nil;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
propContent = [[davProperties objectAtIndex: count]
|
||||||
|
asWebDAVTupleWithContent: result];
|
||||||
|
[properties200 addObject: propContent];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
propContent = [[davProperties objectAtIndex: count]
|
||||||
|
asWebDAVTuple];
|
||||||
|
[properties404 addObject: propContent];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([properties200 count])
|
||||||
|
[propstats addObject: [self _davPropstat: properties200
|
||||||
|
withStatus: @"HTTP/1.1 200 OK"]];
|
||||||
|
if ([properties404 count])
|
||||||
|
[propstats addObject: [self _davPropstat: properties404
|
||||||
|
withStatus: @"HTTP/1.1 404 Not Found"]];
|
||||||
|
|
||||||
|
return propstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *) _syncResponseWithProperties: (NSArray *) properties
|
||||||
|
andMethodSelectors: (SEL *) selectors
|
||||||
|
fromRecord: (NSDictionary *) record
|
||||||
|
withToken: (int) syncToken
|
||||||
|
andBaseURL: (NSString *) baseURL
|
||||||
|
{
|
||||||
|
static NSString *status[] = { @"HTTP/1.1 404 Not Found",
|
||||||
|
@"HTTP/1.1 201 Created",
|
||||||
|
@"HTTP/1.1 200 OK" };
|
||||||
|
NSMutableArray *children;
|
||||||
|
NSString *href;
|
||||||
|
unsigned int statusIndex;
|
||||||
|
|
||||||
|
children = [NSMutableArray arrayWithCapacity: 3];
|
||||||
|
href = [NSString stringWithFormat: @"%@%@",
|
||||||
|
baseURL, [record objectForKey: @"c_name"]];
|
||||||
|
[children addObject: davElementWithContent (@"href", XMLNS_WEBDAV,
|
||||||
|
href)];
|
||||||
|
if (syncToken)
|
||||||
|
{
|
||||||
|
if ([[record objectForKey: @"c_deleted"] intValue] > 0)
|
||||||
|
statusIndex = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ([[record objectForKey: @"c_creationdate"] intValue]
|
||||||
|
>= syncToken)
|
||||||
|
statusIndex = 1;
|
||||||
|
else
|
||||||
|
statusIndex = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
statusIndex = 1;
|
||||||
|
|
||||||
|
[children addObject: davElementWithContent (@"status", XMLNS_WEBDAV,
|
||||||
|
status[statusIndex])];
|
||||||
|
if (statusIndex)
|
||||||
|
[children
|
||||||
|
addObjectsFromArray: [self _davPropStatsWithProperties: properties
|
||||||
|
andMethodSelectors: selectors
|
||||||
|
fromRecord: record]];
|
||||||
|
|
||||||
|
return davElementWithContent (@"sync-response", XMLNS_WEBDAV, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) _appendComponentProperties: (NSArray *) properties
|
||||||
|
fromRecords: (NSArray *) records
|
||||||
|
matchingSyncToken: (int) syncToken
|
||||||
|
toResponse: (WOResponse *) response
|
||||||
|
{
|
||||||
|
NSMutableArray *syncResponses;
|
||||||
|
NSDictionary *multistatus, *record;
|
||||||
|
unsigned int count, max, now;
|
||||||
|
int newToken, currentLM;
|
||||||
|
NSString *baseURL, *newTokenStr;
|
||||||
|
SEL *selectors;
|
||||||
|
|
||||||
|
max = [properties count];
|
||||||
|
selectors = NSZoneMalloc (NULL, sizeof (max * sizeof (SEL)));
|
||||||
|
for (count = 0; count < max; count++)
|
||||||
|
selectors[count]
|
||||||
|
= SOGoSelectorForPropertyGetter ([properties objectAtIndex: count]);
|
||||||
|
|
||||||
|
now = (unsigned int) [[NSDate date] timeIntervalSince1970];
|
||||||
|
|
||||||
|
newToken = 0;
|
||||||
|
|
||||||
|
baseURL = [[self davURL] absoluteString];
|
||||||
|
|
||||||
|
max = [records count];
|
||||||
|
syncResponses = [NSMutableArray arrayWithCapacity: max + 1];
|
||||||
|
for (count = 0; count < max; count++)
|
||||||
|
{
|
||||||
|
record = [records objectAtIndex: count];
|
||||||
|
currentLM = [[record objectForKey: @"c_lastmodified"] intValue];
|
||||||
|
if (newToken < currentLM)
|
||||||
|
newToken = currentLM;
|
||||||
|
[syncResponses addObject: [self _syncResponseWithProperties: properties
|
||||||
|
andMethodSelectors: selectors
|
||||||
|
fromRecord: record
|
||||||
|
withToken: syncToken
|
||||||
|
andBaseURL: baseURL]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSZoneFree (NULL, selectors);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
newToken = now - 1;
|
||||||
|
|
||||||
|
newTokenStr = [NSString stringWithFormat: @"%d", newToken];
|
||||||
|
[syncResponses addObject: davElementWithContent (@"sync-token",
|
||||||
|
XMLNS_WEBDAV,
|
||||||
|
newTokenStr)];
|
||||||
|
multistatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV,
|
||||||
|
syncResponses);
|
||||||
|
[response
|
||||||
|
appendContentString: [multistatus asWebDavStringWithNamespaces: nil]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (WOResponse *) davSyncCollection: (WOContext *) localContext
|
||||||
|
{
|
||||||
|
WOResponse *r;
|
||||||
|
id <DOMDocument> document;
|
||||||
|
DOMElement *documentElement, *propElement;
|
||||||
|
NSString *syncToken;
|
||||||
|
NSDictionary *properties;
|
||||||
|
NSArray *records;
|
||||||
|
int syncTokenInt;
|
||||||
|
|
||||||
|
r = [context response];
|
||||||
|
[r setStatus: 207];
|
||||||
|
[r setContentEncoding: NSUTF8StringEncoding];
|
||||||
|
[r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
|
||||||
|
[r setHeader: @"no-cache" forKey: @"pragma"];
|
||||||
|
[r setHeader: @"no-cache" forKey: @"cache-control"];
|
||||||
|
[r appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
|
||||||
|
|
||||||
|
document = [[context request] contentAsDOMDocument];
|
||||||
|
documentElement = (DOMElement *) [document documentElement];
|
||||||
|
syncToken = [[documentElement firstElementWithTag: @"sync-token"
|
||||||
|
inNamespace: XMLNS_WEBDAV] textValue];
|
||||||
|
propElement = [documentElement firstElementWithTag: @"prop"
|
||||||
|
inNamespace: XMLNS_WEBDAV];
|
||||||
|
|
||||||
|
syncTokenInt = [syncToken intValue];
|
||||||
|
properties = [self parseDAVRequestedProperties: propElement];
|
||||||
|
records = [self _fetchSyncTokenFields: properties
|
||||||
|
matchingSyncToken: syncTokenInt];
|
||||||
|
[self _appendComponentProperties: [properties allKeys]
|
||||||
|
fromRecords: records
|
||||||
|
matchingSyncToken: syncTokenInt
|
||||||
|
toResponse: r];
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* handling acls from quick tables */
|
/* handling acls from quick tables */
|
||||||
- (void) initializeQuickTablesAclsInContext: (WOContext *) localContext
|
- (void) initializeQuickTablesAclsInContext: (WOContext *) localContext
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue