Monotone-Parent: e785838ad834b8a805eeb85700cd115f2501c0f7

Monotone-Revision: db5e568c7dc79c31416d0756d5275c3a7271ea61

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-06-19T14:49:47
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2009-06-19 14:49:47 +00:00
parent 853cc1312e
commit 1a67a3454b
2 changed files with 244 additions and 0 deletions

View File

@ -1,5 +1,9 @@
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
method that returns a DAV dictionary from a flat DAV element (self)
string and a content.

View File

@ -58,6 +58,7 @@
#import "NSArray+Utilities.h"
#import "NSObject+DAV.h"
#import "NSString+Utilities.h"
#import "NSString+DAV.h"
#import "DOMNode+SOGo.h"
#import "SOGoContentObject.h"
@ -842,6 +843,245 @@ static NSArray *childRecordFields = nil;
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 */
- (void) initializeQuickTablesAclsInContext: (WOContext *) localContext
{