12e952eb55
By expanding roles from the given ACL to have these values as flags inside the OpenChange library. This only applies to Calendar and Tasks folders which stored four different access rights to three different types of events/tasks. As the events and tasks are stored in the same table, I have added two new classes to manage permissions in the same way and this avoids the code duplication called MAPIStoreCalTask(Folder|Message).
670 lines
21 KiB
Objective-C
670 lines
21 KiB
Objective-C
/* MAPIStoreCalendarMessage.m - this file is part of SOGo
|
|
*
|
|
* Copyright (C) 2011-2012 Inverse inc
|
|
*
|
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* 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.
|
|
*/
|
|
|
|
/* TODO:
|
|
- merge common code with tasks
|
|
- take the tz definitions from Outlook */
|
|
|
|
#include <talloc.h>
|
|
#include <util/attr.h>
|
|
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSCalendarDate.h>
|
|
#import <Foundation/NSData.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSTimeZone.h>
|
|
#import <EOControl/EOQualifier.h>
|
|
#import <EOControl/EOFetchSpecification.h>
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
|
#import <NGExtensions/NSObject+Logs.h>
|
|
#import <GDLContentStore/GCSFolder.h>
|
|
#import <NGCards/iCalAlarm.h>
|
|
#import <NGCards/iCalCalendar.h>
|
|
#import <NGCards/iCalEvent.h>
|
|
#import <NGCards/iCalDateTime.h>
|
|
#import <NGCards/iCalPerson.h>
|
|
#import <NGCards/iCalTimeZone.h>
|
|
#import <NGCards/iCalTrigger.h>
|
|
#import <SOGo/SOGoPermissions.h>
|
|
#import <SOGo/SOGoUser.h>
|
|
#import <SOGo/SOGoUserManager.h>
|
|
#import <Appointments/SOGoAppointmentFolder.h>
|
|
#import <Appointments/SOGoAppointmentObject.h>
|
|
#import <Appointments/iCalEntityObject+SOGo.h>
|
|
#import <Mailer/NSString+Mail.h>
|
|
|
|
#import "iCalEvent+MAPIStore.h"
|
|
#import "MAPIStoreAppointmentWrapper.h"
|
|
#import "MAPIStoreCalendarAttachment.h"
|
|
#import "MAPIStoreCalendarFolder.h"
|
|
#import "MAPIStoreContext.h"
|
|
#import "MAPIStoreMapping.h"
|
|
#import "MAPIStoreRecurrenceUtils.h"
|
|
#import "MAPIStoreTypes.h"
|
|
#import "MAPIStoreUserContext.h"
|
|
#import "NSDate+MAPIStore.h"
|
|
#import "NSData+MAPIStore.h"
|
|
#import "NSObject+MAPIStore.h"
|
|
#import "NSString+MAPIStore.h"
|
|
#import "NSValue+MAPIStore.h"
|
|
|
|
#import "MAPIStoreCalendarMessage.h"
|
|
|
|
#undef DEBUG
|
|
#include <stdbool.h>
|
|
#include <gen_ndr/exchange.h>
|
|
#include <gen_ndr/property.h>
|
|
#include <libmapi/libmapi.h>
|
|
#include <mapistore/mapistore.h>
|
|
#include <mapistore/mapistore_errors.h>
|
|
#include <mapistore/mapistore_nameid.h>
|
|
|
|
// extern void ndr_print_AppointmentRecurrencePattern(struct ndr_print *ndr, const char *name, const struct AppointmentRecurrencePattern *r);
|
|
|
|
static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
|
|
|
@implementation SOGoAppointmentObject (MAPIStoreExtension)
|
|
|
|
- (Class) mapistoreMessageClass
|
|
{
|
|
return [MAPIStoreCalendarMessage class];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation MAPIStoreCalendarMessage
|
|
|
|
+ (void) initialize
|
|
{
|
|
NSArrayK = [NSArray class];
|
|
MAPIStoreAppointmentWrapperK = [MAPIStoreAppointmentWrapper class];
|
|
}
|
|
|
|
+ (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
BOOL listedProperties[65536];
|
|
NSUInteger count;
|
|
uint16_t propId;
|
|
|
|
memset (listedProperties, NO, 65536 * sizeof (BOOL));
|
|
[super getAvailableProperties: propertiesP inMemCtx: memCtx];
|
|
for (count = 0; count < (*propertiesP)->cValues; count++)
|
|
{
|
|
propId = ((*propertiesP)->aulPropTag[count] >> 16) & 0xffff;
|
|
listedProperties[propId] = YES;
|
|
}
|
|
[MAPIStoreAppointmentWrapper fillAvailableProperties: *propertiesP
|
|
withExclusions: listedProperties];
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
calendar = nil;
|
|
masterEvent = nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) _setupAttachmentParts
|
|
{
|
|
NSUInteger count, max;
|
|
NSArray *events;
|
|
NSString *newKey;
|
|
MAPIStoreCalendarAttachment *attachment;
|
|
NSUInteger aid;
|
|
iCalEvent *event;
|
|
|
|
events = [calendar events];
|
|
max = [events count];
|
|
for (count = 1; count < max; count++)
|
|
{
|
|
attachment = [MAPIStoreCalendarAttachment
|
|
mapiStoreObjectInContainer: self];
|
|
/* we now that there are no attachments yet, so we can assume that the
|
|
right AID is 0 from the start */
|
|
aid = count - 1;
|
|
[attachment setAID: aid];
|
|
event = [events objectAtIndex: count];
|
|
[attachment setEvent: event];
|
|
newKey = [[event uniqueChildWithTag: @"recurrence-id"]
|
|
flattenedValuesForKey: @""];
|
|
[attachmentParts setObject: attachment forKey: newKey];
|
|
}
|
|
}
|
|
|
|
- (id) initWithSOGoObject: (id) newSOGoObject
|
|
inContainer: (MAPIStoreObject *) newFolder
|
|
{
|
|
MAPIStoreContext *context;
|
|
MAPIStoreUserContext *userContext;
|
|
iCalCalendar *origCalendar;
|
|
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
|
|
|
if ((self = [super initWithSOGoObject: newSOGoObject
|
|
inContainer: newFolder]))
|
|
{
|
|
if ([newSOGoObject isNew])
|
|
{
|
|
ASSIGN (calendar, [iCalCalendar groupWithTag: @"vcalendar"]);
|
|
[calendar setVersion: @"2.0"];
|
|
[calendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"];
|
|
masterEvent = [iCalEvent groupWithTag: @"vevent"];
|
|
[calendar addChild: masterEvent];
|
|
[masterEvent setCreated: [NSCalendarDate date]];
|
|
}
|
|
else
|
|
{
|
|
origCalendar = [sogoObject calendar: YES secure: YES];
|
|
if (!origCalendar)
|
|
{
|
|
[self errorWithFormat: @"Incorrect calendar event %@. Empty message is created",
|
|
[self url]];
|
|
return self;
|
|
}
|
|
calendar = [origCalendar mutableCopy];
|
|
masterEvent = [[calendar events] objectAtIndex: 0];
|
|
[self _setupAttachmentParts];
|
|
}
|
|
context = [self context];
|
|
userContext = [self userContext];
|
|
appointmentWrapper
|
|
= [MAPIStoreAppointmentWrapper wrapperWithICalEvent: masterEvent
|
|
andUser: [userContext sogoUser]
|
|
andSenderEmail: nil
|
|
withConnectionInfo: [context connectionInfo]];
|
|
[self addProxy: appointmentWrapper];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
//NSLog(@"MAPIStoreCalendarMessage: -dealloc (%p)", self);
|
|
[calendar release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (MAPIStoreAppointmentWrapper *) _appointmentWrapper
|
|
{
|
|
NSUInteger i, max;
|
|
id proxy;
|
|
max = [proxies count];
|
|
for (i = 0; i < max; i++) {
|
|
proxy = [proxies objectAtIndex: i];
|
|
if ([proxy isKindOfClass: MAPIStoreAppointmentWrapperK])
|
|
{
|
|
return proxy;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
/* getters */
|
|
- (enum mapistore_error) getPidTagMessageClass: (void **) data
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
SOGoUser *owner;
|
|
|
|
owner = [[self userContext] sogoUser];
|
|
if ([masterEvent userAsAttendee: owner])
|
|
*data = talloc_strdup (memCtx, "IPM.Schedule.Meeting.Request");
|
|
else
|
|
*data = talloc_strdup (memCtx, "IPM.Appointment");
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
}
|
|
|
|
- (enum mapistore_error) getPidLidSideEffects: (void **) data // TODO
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
*data = MAPILongValue (memCtx,
|
|
seOpenToDelete | seOpenToCopy | seOpenToMove
|
|
| seCoerceToInbox | seOpenForCtxMenu);
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
}
|
|
|
|
- (enum mapistore_error) getPidTagProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
return [self getYes: data inMemCtx: memCtx];
|
|
}
|
|
|
|
- (void) getMessageData: (struct mapistore_message **) dataPtr
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
static NSString *recTypes[] = {@"orig", @"to", @"cc", @"bcc"};
|
|
static NSInteger recTypesValue[] = {MAPI_ORIG, MAPI_TO, MAPI_CC, MAPI_BCC};
|
|
struct mapistore_message *msgData;
|
|
NSDictionary *recipients, *contactInfos;
|
|
NSString *email;
|
|
NSArray *attendees;
|
|
NSDictionary *attendee;
|
|
SOGoUserManager *mgr;
|
|
struct mapistore_message_recipient *recipient;
|
|
NSUInteger nRecType, maxRecTypes, count, max, propCount;
|
|
NSObject *currentValue;
|
|
|
|
[super getMessageData: &msgData inMemCtx: memCtx];
|
|
|
|
/* The presence of the "recipients" meta-property means that our most recent
|
|
list of recipients has not been saved yet and that we must return that
|
|
one instead of the one stored in the event. */
|
|
recipients = [properties objectForKey: @"recipients"];
|
|
if (recipients)
|
|
{
|
|
mgr = [SOGoUserManager sharedUserManager];
|
|
|
|
/* TODO: this code might need to be moved into MAPIStoreMessage */
|
|
msgData->columns = set_SPropTagArray (msgData, 9,
|
|
PR_OBJECT_TYPE,
|
|
PR_DISPLAY_TYPE,
|
|
PR_7BIT_DISPLAY_NAME_UNICODE,
|
|
PR_SMTP_ADDRESS_UNICODE,
|
|
PR_SEND_INTERNET_ENCODING,
|
|
PR_RECIPIENT_DISPLAY_NAME_UNICODE,
|
|
PR_RECIPIENT_FLAGS,
|
|
PR_RECIPIENT_ENTRYID,
|
|
PR_RECIPIENT_TRACKSTATUS);
|
|
|
|
maxRecTypes = sizeof(recTypes) / sizeof(recTypes[0]);
|
|
for (nRecType = 0; nRecType < maxRecTypes; nRecType++)
|
|
{
|
|
attendees = [recipients objectForKey: recTypes[nRecType]];
|
|
max = [attendees count];
|
|
if (max > 0)
|
|
{
|
|
msgData->recipients_count += max;
|
|
msgData->recipients = talloc_realloc(msgData,
|
|
msgData->recipients, struct
|
|
mapistore_message_recipient, msgData->recipients_count);
|
|
recipient = msgData->recipients;
|
|
for (count = 0; count < max; count++)
|
|
{
|
|
attendee = [attendees objectAtIndex: count];
|
|
recipient->type = recTypesValue[nRecType];
|
|
email = [attendee objectForKey: @"email"];
|
|
if (email)
|
|
{
|
|
contactInfos
|
|
= [mgr contactInfosForUserWithUIDorEmail: email];
|
|
recipient->username = [[contactInfos
|
|
objectForKey: @"c_uid"]
|
|
asUnicodeInMemCtx: msgData];
|
|
}
|
|
else
|
|
recipient->username = NULL;
|
|
|
|
recipient->data = talloc_array(msgData, void *,
|
|
msgData->columns->cValues);
|
|
for (propCount = 0;
|
|
propCount < msgData->columns->cValues;
|
|
propCount++)
|
|
{
|
|
currentValue = [attendee objectForKey: MAPIPropertyKey (msgData->columns->aulPropTag[propCount])];
|
|
[currentValue getValue: recipient->data + propCount
|
|
forTag: msgData->columns->aulPropTag[propCount]
|
|
inMemCtx: msgData];
|
|
}
|
|
recipient++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[[self _appointmentWrapper] fillMessageData: msgData
|
|
inMemCtx: memCtx];
|
|
}
|
|
|
|
*dataPtr = msgData;
|
|
}
|
|
|
|
- (enum mapistore_error) getPidTagResponseRequested: (void **) data
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
return [self getYes: data inMemCtx: memCtx];
|
|
}
|
|
|
|
/* This three methods: getPidTagNormalizedSubject,
|
|
getPidTagSensitivity and getPidTagImportance are implemented in
|
|
MAPIStoreMessage base class, then the proxy method is not reached
|
|
(see MAPIStoreObject).
|
|
*/
|
|
- (enum mapistore_error) getPidTagNormalizedSubject: (void **) data
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
|
|
|
appointmentWrapper = [self _appointmentWrapper];
|
|
if (appointmentWrapper)
|
|
return [appointmentWrapper getPidTagNormalizedSubject: data inMemCtx: memCtx];
|
|
|
|
return MAPISTORE_ERR_NOT_FOUND;
|
|
}
|
|
|
|
- (enum mapistore_error) getPidTagSensitivity: (void **) data
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
|
|
|
appointmentWrapper = [self _appointmentWrapper];
|
|
if (appointmentWrapper)
|
|
return [appointmentWrapper getPidTagSensitivity: data inMemCtx: memCtx];
|
|
|
|
return [self getLongZero: data inMemCtx: memCtx];
|
|
}
|
|
|
|
- (enum mapistore_error) getPidTagImportance: (void **) data
|
|
inMemCtx: (TALLOC_CTX *) memCtx
|
|
{
|
|
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
|
|
|
appointmentWrapper = [self _appointmentWrapper];
|
|
if (appointmentWrapper)
|
|
return [appointmentWrapper getPidTagImportance: data inMemCtx: memCtx];
|
|
|
|
*data = MAPILongValue (memCtx, 1);
|
|
|
|
return MAPISTORE_SUCCESS;
|
|
}
|
|
|
|
|
|
- (NSString *) _uidFromGlobalObjectId: (TALLOC_CTX *) memCtx
|
|
{
|
|
NSData *objectId;
|
|
NSString *uid = nil;
|
|
char *bytesDup, *uidStart;
|
|
NSUInteger length;
|
|
|
|
/* NOTE: we only handle the generic case at the moment, see
|
|
MAPIStoreAppointmentWrapper */
|
|
objectId = [properties
|
|
objectForKey: MAPIPropertyKey (PidLidGlobalObjectId)];
|
|
if (objectId)
|
|
{
|
|
length = [objectId length];
|
|
bytesDup = talloc_array (memCtx, char, length + 1);
|
|
memcpy (bytesDup, [objectId bytes], length);
|
|
bytesDup[length] = 0;
|
|
uidStart = bytesDup + length - 1;
|
|
while (uidStart != bytesDup && *(uidStart - 1))
|
|
uidStart--;
|
|
if (uidStart > bytesDup && *uidStart)
|
|
uid = [NSString stringWithUTF8String: uidStart];
|
|
talloc_free (bytesDup);
|
|
}
|
|
|
|
return uid;
|
|
}
|
|
|
|
- (SOGoAppointmentObject *) _resurrectRecord: (NSString *) cname
|
|
fromFolder: (SOGoAppointmentFolder *) folder
|
|
{
|
|
NSArray *records;
|
|
EOQualifier *qualifier;
|
|
EOFetchSpecification *fs;
|
|
GCSFolder *ocsFolder;
|
|
SOGoAppointmentObject *newObject;
|
|
NSMutableDictionary *newRecord;
|
|
static NSArray *childRecordFields = nil;
|
|
|
|
if (!childRecordFields)
|
|
{
|
|
childRecordFields = [NSArray arrayWithObjects: @"c_name",
|
|
@"c_creationdate", @"c_lastmodified",
|
|
@"c_content", nil];
|
|
[childRecordFields retain];
|
|
}
|
|
|
|
ocsFolder = [folder ocsFolder];
|
|
|
|
qualifier
|
|
= [EOQualifier qualifierWithQualifierFormat:
|
|
[NSString stringWithFormat: @"c_name='%@'", cname]];
|
|
fs = [EOFetchSpecification fetchSpecificationWithEntityName: [ocsFolder folderName]
|
|
qualifier: qualifier
|
|
sortOrderings: nil];
|
|
records = [ocsFolder fetchFields: childRecordFields
|
|
fetchSpecification: fs
|
|
ignoreDeleted: NO];
|
|
if ([records isKindOfClass: NSArrayK] && [records count])
|
|
{
|
|
newRecord = [[records objectAtIndex: 0] mutableCopy];
|
|
[newRecord setObject: [NSNumber numberWithInt: 0]
|
|
forKey: @"c_version"];
|
|
newObject = [SOGoAppointmentObject objectWithRecord: newRecord
|
|
inContainer: folder];
|
|
[newRecord autorelease];
|
|
[newObject setIsNew: NO];
|
|
}
|
|
else
|
|
newObject = nil;
|
|
|
|
|
|
return newObject;
|
|
}
|
|
|
|
- (void) _fixupAppointmentObjectWithUID: (NSString *) uid
|
|
{
|
|
NSString *cname, *url;
|
|
MAPIStoreMapping *mapping;
|
|
uint64_t objectId;
|
|
WOContext *woContext;
|
|
SOGoAppointmentFolder *folder;
|
|
SOGoAppointmentObject *newObject;
|
|
|
|
cname = [[container sogoObject] resourceNameForEventUID: uid];
|
|
if (cname)
|
|
isNew = NO;
|
|
else
|
|
cname = [NSString stringWithFormat: @"%@.ics", uid];
|
|
|
|
mapping = [self mapping];
|
|
|
|
url = [NSString stringWithFormat: @"%@%@", [container url], cname];
|
|
folder = [sogoObject container];
|
|
|
|
/* reinstantiate the old sogo object and attach it to self */
|
|
[[self userContext] activate];
|
|
woContext = [[self userContext] woContext];
|
|
if (isNew)
|
|
{
|
|
/* event could have been deleted, let's try to resurrect it */
|
|
newObject = [self _resurrectRecord: cname fromFolder: folder];
|
|
if (!newObject)
|
|
{
|
|
newObject = [SOGoAppointmentObject objectWithName: cname
|
|
inContainer: folder];
|
|
[newObject setIsNew: YES];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* dissociate the object url from the old object's id */
|
|
objectId = [mapping idFromURL: url];
|
|
[mapping unregisterURLWithID: objectId];
|
|
newObject = [folder lookupName: cname
|
|
inContext: woContext
|
|
acquire: NO];
|
|
}
|
|
|
|
/* dissociate the object url associated with this object, as we want to
|
|
discard it */
|
|
objectId = [self objectId];
|
|
[mapping unregisterURLWithID: objectId];
|
|
|
|
/* associate the new object url with this object id */
|
|
[mapping registerURL: url withID: objectId];
|
|
|
|
[newObject setContext: woContext];
|
|
ASSIGN (sogoObject, newObject);
|
|
}
|
|
|
|
- (NSUInteger) sensitivity
|
|
{
|
|
return [[self _appointmentWrapper] sensitivity];
|
|
}
|
|
|
|
- (NSString *) creator
|
|
{
|
|
return [[self _appointmentWrapper] creator];
|
|
}
|
|
|
|
- (NSString *) owner
|
|
{
|
|
return [[self _appointmentWrapper] owner];
|
|
}
|
|
|
|
- (void) _updateAttachedEvents
|
|
{
|
|
NSArray *allAttachments;
|
|
NSUInteger count, max;
|
|
NSString *uid, *summary;
|
|
iCalEvent *event;
|
|
MAPIStoreCalendarAttachment *attachment;
|
|
|
|
/* ensure that all exception events have the same UID as the master */
|
|
uid = [masterEvent uid];
|
|
summary = [masterEvent summary];
|
|
|
|
allAttachments = [attachmentParts allValues];
|
|
max = [allAttachments count];
|
|
for (count = 0; count < max; count++)
|
|
{
|
|
attachment = [allAttachments objectAtIndex: count];
|
|
event = [attachment event];
|
|
if ([[event summary] length] == 0)
|
|
[event setSummary: summary];
|
|
[event setUid: uid];
|
|
}
|
|
}
|
|
|
|
- (void) save: (TALLOC_CTX *) memCtx
|
|
{
|
|
// iCalCalendar *vCalendar;
|
|
// NSCalendarDate *now;
|
|
NSString *uid, *nameInContainer;
|
|
// iCalEvent *newEvent;
|
|
// iCalPerson *userPerson;
|
|
SOGoUser *activeUser;
|
|
NSRange rangeOfDot;
|
|
|
|
if (isNew)
|
|
{
|
|
uid = [self _uidFromGlobalObjectId: memCtx];
|
|
if (uid)
|
|
{
|
|
/* Hack required because of what's explained in oxocal 3.1.4.7.1:
|
|
basically, Outlook creates a copy of the event and then removes
|
|
the old instance. We perform a trickery to avoid performing those
|
|
operations in the backend, in a way that enables us to recover
|
|
the initial instance and act solely on it. */
|
|
[self _fixupAppointmentObjectWithUID: uid];
|
|
}
|
|
else
|
|
{
|
|
/* We create a UID from the nameInContainer, or the reverse if the
|
|
latter is already set... */
|
|
nameInContainer = [sogoObject nameInContainer];
|
|
if (nameInContainer)
|
|
{
|
|
rangeOfDot = [nameInContainer rangeOfString: @"."
|
|
options: NSBackwardsSearch];
|
|
if (rangeOfDot.location == NSNotFound)
|
|
uid = nameInContainer;
|
|
else
|
|
uid = [nameInContainer substringToIndex: rangeOfDot.location];
|
|
}
|
|
else
|
|
{
|
|
uid = [SOGoObject globallyUniqueObjectId];
|
|
nameInContainer = [NSString stringWithFormat: @"%@.ics", uid];
|
|
[sogoObject setNameInContainer: nameInContainer];
|
|
}
|
|
}
|
|
[masterEvent setUid: uid];
|
|
}
|
|
|
|
// [self logWithFormat: @"-save, event props:"];
|
|
// MAPIStoreDumpMessageProperties (newProperties);
|
|
|
|
// now = [NSCalendarDate date];
|
|
|
|
activeUser = [[self context] activeUser];
|
|
[masterEvent updateFromMAPIProperties: properties
|
|
inUserContext: [self userContext]
|
|
withActiveUser: activeUser
|
|
isNew: isNew
|
|
inMemCtx: memCtx];
|
|
[self _updateAttachedEvents];
|
|
[[self userContext] activate];
|
|
[sogoObject updateContentWithCalendar: calendar
|
|
fromRequest: nil];
|
|
[self updateVersions];
|
|
}
|
|
|
|
- (id) lookupAttachment: (NSString *) childKey
|
|
{
|
|
return [attachmentParts objectForKey: childKey];
|
|
}
|
|
|
|
- (MAPIStoreAttachment *) createAttachment
|
|
{
|
|
MAPIStoreCalendarAttachment *newAttachment;
|
|
uint32_t newAid;
|
|
NSString *newKey;
|
|
iCalEvent *newEvent;
|
|
|
|
newAid = [[self attachmentKeys] count];
|
|
|
|
newAttachment = [MAPIStoreCalendarAttachment
|
|
mapiStoreObjectInContainer: self];
|
|
[newAttachment setAID: newAid];
|
|
newEvent = [iCalEvent groupWithTag: @"vevent"];
|
|
[newAttachment setEvent: newEvent];
|
|
[calendar addToEvents: newEvent];
|
|
|
|
newKey = [NSString stringWithFormat: @"%ul", newAid];
|
|
[attachmentParts setObject: newAttachment
|
|
forKey: newKey];
|
|
[attachmentKeys release];
|
|
attachmentKeys = nil;
|
|
|
|
return newAttachment;
|
|
}
|
|
|
|
- (enum mapistore_error) setReadFlag: (uint8_t) flag
|
|
{
|
|
return MAPISTORE_SUCCESS;
|
|
}
|
|
|
|
@end
|