#include "common.h"
#include <GDLContentStore/GCSFolder.h>
+@interface SOGoContentObject(ETag)
+- (NSArray *)parseETagList:(NSString *)_c;
+@end
+
@implementation SOGoContentObject
static BOOL kontactGroupDAV = YES;
return self->content;
}
-- (NSException *)saveContentString:(NSString *)_str {
+- (NSException *)saveContentString:(NSString *)_str
+ baseVersion:(unsigned int)_baseVersion
+{
/* Note: "iCal multifolder saves" are implemented in the apt subclass! */
GCSFolder *folder;
NSException *ex;
[self errorWithFormat:@"Did not find folder of content object."];
return nil;
}
- if ((ex = [folder writeContent:_str toName:[self nameInContainer]])) {
+
+ ex = [folder writeContent:_str toName:[self nameInContainer]
+ baseVersion:_baseVersion];
+ if (ex != nil) {
[self errorWithFormat:@"write failed: %@", ex];
return ex;
}
return nil;
}
+- (NSException *)saveContentString:(NSString *)_str {
+ return [self saveContentString:_str baseVersion:0 /* don't check */];
+}
- (NSException *)delete {
/* Note: "iCal multifolder saves" are implemented in the apt subclass! */
/* actions */
- (id)PUTAction:(WOContext *)_ctx {
- WORequest *rq;
- NSException *error;
- id etag;
+ WORequest *rq;
+ NSException *error;
+ unsigned int baseVersion;
+ id etag;
if ((error = [self matchesRequestConditionInContext:_ctx]) != nil)
return error;
rq = [_ctx request];
- if ((error = [self saveContentString:[rq contentAsString]]) != nil)
+
+ /* determine base version from etag in if-match header */
+ /*
+ Note: The -matchesRequestConditionInContext: already checks whether the
+ etag matches and returns an HTTP exception in case it doesn't.
+ We retrieve the etag again here to _ensure_ a transactionally save
+ commit.
+ (between the check and the update a change could have been done)
+ */
+ etag = [rq headerForKey:@"if-match"];
+ etag = [self parseETagList:etag];
+ if ([etag count] > 0) {
+ if ([etag count] > 1) {
+ /*
+ Note: we would have to attempt a save for _each_ of the etags being
+ passed in! In practice most WebDAV clients submit exactly one
+ etag.
+ */
+ [self warnWithFormat:
+ @"Got multiple if-match etags from client, only attempting to "
+ @"save with the first: %@", etag];
+ }
+
+ etag = [etag objectAtIndex:0];
+ }
+ baseVersion = ([etag length] > 0)
+ ? [etag unsignedIntValue]
+ : 0 /* 0 means 'do not check' */;
+
+ /* attempt a save */
+
+ if ((error = [self saveContentString:[rq contentAsString]
+ baseVersion:baseVersion]) != nil)
return error;
// TODO: this should be automatic if we return nil?