merge of '53dfb1b04415248644ed626184df1362cd4ee2f1'
and 'e0d3e7be794ca11b0110ce99b943aa8a46a9bb90' Monotone-Parent: 53dfb1b04415248644ed626184df1362cd4ee2f1 Monotone-Parent: e0d3e7be794ca11b0110ce99b943aa8a46a9bb90 Monotone-Revision: 1464c6c39cc56b3546f7388e70945dfe0e03b3af Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2011-05-03T13:39:48 Monotone-Branch: ca.inverse.sogomaint-2.0.2
commit
7d7bd78947
233
ChangeLog
233
ChangeLog
|
@ -1,3 +1,236 @@
|
|||
2011-04-28 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/SQLSource.{h,m}: Added resources support,
|
||||
just like for LDAP-based sources.
|
||||
* SoObjects/SOGo/SOGoParentFolder.m (_createPersonalFolder):
|
||||
We now also autocreate database tables if the "user"
|
||||
is a resource.
|
||||
|
||||
2011-04-25 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/SchedulerUI.js (onFolderUnsubscribeCB):
|
||||
select the personal calendar to avoid trying creating an event or
|
||||
a task in the calendar to which the user has just unsubscribed.
|
||||
|
||||
2011-04-25 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Added the concept of "resources" in SOGo in order
|
||||
to avoid double-bookings (if not more) and also, handle
|
||||
auto-accepts. This works for the web interface and
|
||||
over DAV - generating 403 errors in case of a conflict.
|
||||
|
||||
2011-04-21 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Added the possibility of translating IMAP namespaces
|
||||
and we now extract the full name the mailbox owner under
|
||||
the "Other Users" namespace.
|
||||
|
||||
2011-04-21 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/SchedulerUI.js (deleteEvent): we now show
|
||||
in the confirmation dialog box which events will be deleted.
|
||||
|
||||
* UI/WebServerResources/generic.js (createDialog): the legend
|
||||
argument can now be a string or an element.
|
||||
(isWebKit): was isSafari; now consider all WebKit-based browsers together.
|
||||
|
||||
* UI/WebServerResources/UIxMailToSelection.js: various
|
||||
improvements to behave like in Thunderbird.
|
||||
|
||||
* UI/WebServerResources/SOGoAutoCompletion.js: idem.
|
||||
|
||||
* UI/WebServerResources/UIxMailEditor.js (initAddresses): was
|
||||
initTabIndex. We were not handling the tab indexes properly which
|
||||
was causing incorrect behavior in IE.
|
||||
|
||||
2011-04-19 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* OpenChange/MAPIStoreMailAttachment.m (-): binary properties are
|
||||
represented by a struct Binary_r.
|
||||
|
||||
* OpenChange/NSObject+MAPIStore.m
|
||||
(-getMAPIValue:forTag:inMemCtx:): binary properties are
|
||||
represented by a struct Binary_r.
|
||||
|
||||
* OpenChange/MAPIStoreTypes.m (NSObjectFromMAPISPropValue)
|
||||
(NSObjectFromSPropValue): we now support PT_SVREID and handle it
|
||||
exactly as PT_BINARY.
|
||||
|
||||
* OpenChange/MAPIStoreMessage.m (-attachmentTable): as all the
|
||||
subclasses were using the same MAPIStoreAttachmentTable
|
||||
implementation, this method now performs the same operation and
|
||||
thereby no longer requires subclasssing.
|
||||
|
||||
* OpenChange/MAPIStoreObject.m (-getProperty:withTag:): compute
|
||||
the method selector and pass it as argument to the IMP of the
|
||||
property getter, as would be with any regular method invocation.
|
||||
|
||||
* OpenChange/MAPIStoreMailMessage.m (-getPrBody:): no longer check
|
||||
the size of the returned content, as streams are now handled
|
||||
internally by OpenChange.
|
||||
(-getPrHtml): same as above.
|
||||
|
||||
* OpenChange/MAPIStoreTypes.m (NSObjectFromStreamData): removed
|
||||
helper function as it's no longer used.
|
||||
|
||||
* OpenChange/MAPIStoreSOGo.m (sogo_op_set_property_from_fd)
|
||||
(sogo_op_get_property_into_fd): those backend methods are no
|
||||
longer used, as OpenChange handles the property streams internally
|
||||
now.
|
||||
|
||||
* OpenChange/gen-property-selectors.py: added code to generate
|
||||
MAPIStoreSupportedProperties as the array of properties that are
|
||||
handled by OpenChange and MAPIStoreSupportedPropertiesCount as the
|
||||
count of those properties.
|
||||
|
||||
* OpenChange/MAPIStoreObject.m (+getAvailableProperties): ObjC
|
||||
corollary of the "sogo_pocop_get_available_properties" backend
|
||||
method.
|
||||
(-getAvailableProperties): proxy to the class method of the same
|
||||
name.
|
||||
|
||||
* OpenChange/MAPIStoreTable.m (+childObjectClass): new mandatory
|
||||
method that returns the Class of the objects represented in the
|
||||
rows of the table.
|
||||
(-getAvailableProperties:): ObjC corollary of the
|
||||
"sogo_pocop_get_available_table_properties" backend method.
|
||||
|
||||
* OpenChange/MAPIStoreSOGo.m
|
||||
(sogo_pocop_get_available_table_properties): new backend getter
|
||||
that returns the list of properties that can be queried on the
|
||||
table rows.
|
||||
(sogo_pocop_get_available_properties): new backend getter
|
||||
that returns the list of properties that can be queried on the
|
||||
discrete object passed as parameter.
|
||||
|
||||
* OpenChange/code-MAPIStorePropertySelectors.m
|
||||
(MAPIStorePropertyGettersForClass): use the
|
||||
"MAPIStoreProeprtyGetter" type rather than IMP as result type.
|
||||
|
||||
* OpenChange/MAPIStoreFolderTable.m (-restrictedChildKeys): no
|
||||
longer a mandatory method, returns [self childKeys] when not
|
||||
overriden and issue an error message.
|
||||
|
||||
* OpenChange/MAPIStoreFolder.m (-createMessage): no longer a
|
||||
mandatory method. Returns nil when not overriden.
|
||||
|
||||
2011-04-19 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
(-_flattenCycleRecord:forRange:intoArray:): fixed the
|
||||
verification of the event's timezone.
|
||||
|
||||
2011-04-15 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/iCalEvent+SOGo.m (-firstOccurenceRange):
|
||||
we now compute the end date by looking at the occurrence
|
||||
interval. This way, we support events with an end date or a duration.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
(-_flattenCycleRecord:forRange:intoArray:): also compute the end
|
||||
date by looking at the occurrence interval.
|
||||
(-importCalendar:): added a duration to events with no end date nor duration.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
(-_adjustEventsInRequest:): new method to verify the vCalendar for
|
||||
any inconsistency or missing attributes. Currently, it only adds a
|
||||
duration if no end date nor duration is found.
|
||||
|
||||
* UI/WebServerResources/ckeditor/config.js: Changed the default
|
||||
enter mode to use BR instead of P. This matches the behavior of Thunderbird.
|
||||
|
||||
2011-04-14 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/UIxContactEditor.js
|
||||
(validateContactEditor): now uses the regexp defined in generic.js
|
||||
(emailRE) to validate the email addresses. This fixes an issue
|
||||
with valid addresses starting with an underscore (bug #1246).
|
||||
|
||||
* UI/MailerUI/UIxMailAccountActions.m (-composeAction): properly decode
|
||||
submitted recipients and subjects (URL-encoded).
|
||||
|
||||
* SoObjects/SOGo/SOGoUserManager.m (-getImapLoginForUID:): we now
|
||||
consider the source parameter IMAPLoginFieldName. We continue to support
|
||||
SOGoForceIMAPLoginWithEmail at the domain/system level.
|
||||
|
||||
* SoObjects/SOGo/LDAPSource.m: we can now define a new parameter
|
||||
named IMAPLoginFieldName to specify which field to use for IMAP
|
||||
authentication. By default, we use the value of the UIDFieldName.
|
||||
|
||||
* SoObjects/SOGo/SQLSource.m
|
||||
(-checkLogin:password:perr:expire:grace:): if the defaults parameter
|
||||
authenticationFilter is defined, we add it to the SQL where clause.
|
||||
(_lookupContactEntry:considerEmail:): we add a new key
|
||||
(canAuthenticate) to the returned dictionary that specifies if the
|
||||
user can authenticate or not. We also add c_imaplogin when the
|
||||
user must authenticate with a different username to the IMAP server.
|
||||
|
||||
* SoObjects/SOGo/SOGoUser.m (-canAuthenticate): new method that
|
||||
returns true if the user can authenticate, based on the
|
||||
authentication filter associated to the source.
|
||||
|
||||
* SoObjects/SOGo/SOGoGCSFolder.m (-ocsFolder): don't automatically
|
||||
create the folder if the user can't authenticate.
|
||||
|
||||
* UI/Contacts/UIxContactsListActions.m: was
|
||||
UIxContactsListView.m. The new method contactsListAction now
|
||||
returns a JSON representation of the addressbook.
|
||||
|
||||
* UI/Contacts/UIxContactFoldersView.m (-personalContactInfos): new
|
||||
method used to populate the wox template with the personal
|
||||
addressbook. The addressbook module is now pre-loaded with it,
|
||||
eliminating an Ajax query.
|
||||
|
||||
* UI/WebServerResources/ContactsUI.js (initContacts): delegate
|
||||
all events to the table instead of each row.
|
||||
(contactsListCallback): we now receive a JSON representation of
|
||||
the contacts and reuse the existing table rows instead of
|
||||
overwriting the table.
|
||||
|
||||
2011-04-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* OpenChange/MAPIStoreObject.m: make use of the IMP cache methods
|
||||
available in MAPIStorePropertySelectors to query object
|
||||
properties.
|
||||
* OpenChange/*{Message,Folder}.m: replaced the implementations of
|
||||
-getProperty:withTag: with equivalent property getter methods.
|
||||
|
||||
* OpenChange/MAPIStoreContext.m (-url): new method that returns
|
||||
the context url.
|
||||
|
||||
* OpenChange/gen-property-selectors.py: new generator script that
|
||||
outputs MAPIStorePropertySelectors.[hm], which is a helper
|
||||
module that provides methods for handling of cached
|
||||
property method IMPs.
|
||||
|
||||
2011-04-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Mailer/SOGoDraftObject.m
|
||||
(-bodyPartForAttachmentWithName:): don't choose "quoted-printable"
|
||||
if the mimetype is anything else than "text/plain" or "text/html",
|
||||
to avoid issues with filenames attached with a wrong mime type.
|
||||
|
||||
2011-04-12 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
(-importCalendar:): we now associate the proper vtimezone to each
|
||||
event found in the calendar.
|
||||
|
||||
2011-04-11 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/UIxMailToSelection.js
|
||||
(addressFieldLostFocus): split content separated by commas
|
||||
or semi-colons.
|
||||
|
||||
2011-04-08 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* Tests/Unit/TestNSString+URLEscaping.m: new test module.
|
||||
(-test_stringByEscapingURL): new test method for -[NSString
|
||||
stringByEscapingURL].
|
||||
|
||||
* Tests/Unit/sogo-tests.m (main): added volatile user defaults for
|
||||
utf8 url encoding.
|
||||
|
||||
2011-04-07 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/MailerUI.js (onMenuRenameFolderConfirm):
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
35
NEWS
35
NEWS
|
@ -1,7 +1,24 @@
|
|||
1.3-2011MMDD (1.3.6)
|
||||
1.3-201004XX (1.3.7)
|
||||
---------------------
|
||||
New Features
|
||||
- IMAP namespaces are now translated and the full name of the
|
||||
mailbox owner is extracted under "Other Users"
|
||||
- added the "authenticationFilter" parameter for SQL-based
|
||||
sources to limit who can authenticate to a local SOGo instance
|
||||
- added support for resources like projectors, conference rooms
|
||||
and more which allows SOGo to avoid double-booking of them
|
||||
and also allows SOGo to automatically accept invitations for them
|
||||
|
||||
Enhancements
|
||||
|
||||
Bug Fixes
|
||||
|
||||
|
||||
1.3-20110408 (1.3.6)
|
||||
--------------------
|
||||
New Features
|
||||
- added Norwegian translation - thanks to Altibox
|
||||
|
||||
Enhancements
|
||||
- updated Italian translation
|
||||
- updated Ukranian translation
|
||||
|
@ -14,12 +31,14 @@ Enhancements
|
|||
- alarms are no longer exported to DAV clients for calendars where the alarms
|
||||
are configured to be disabled
|
||||
- IMAP connection pooling is disabled by default to avoid flooding the IMAP
|
||||
servers in multi-process environments
|
||||
servers in multi-process environments (NGImap4DisableIMAP4Pooling now set
|
||||
to "YES" by default)
|
||||
- sogo-tool: the remove-doubles command now makes use of the card complete
|
||||
names
|
||||
names
|
||||
- sope-appserver: added the ability to configure the minutes timeout per
|
||||
request after which child processes are killed, via
|
||||
WOWatchDogRequestTimeout (default: 10)
|
||||
request after which child processes are killed, via
|
||||
WOWatchDogRequestTimeout (default: 10)
|
||||
|
||||
Bug Fixes
|
||||
- restored the automatic expunge of IMAP folders
|
||||
- various mutli-domain fixes
|
||||
|
@ -29,12 +48,12 @@ Bug Fixes
|
|||
- sogo-tool: now works in multi-domain environments
|
||||
- sogo-tool: now retrieves list of users from the folder info table
|
||||
- sogo-tool: the remove-doubles command is now compatible with the
|
||||
synchronization mechanisms
|
||||
synchronization mechanisms
|
||||
- sope-mime: fixed some parsing problems occurring with dbmail
|
||||
- sope-mime: fixed the fetching of mail body parts when other untagged
|
||||
responses are received
|
||||
responses are received
|
||||
- sope-appserver: fixed a bug leaving child processes performing the watchdog
|
||||
safety belt cleanup
|
||||
safety belt cleanup
|
||||
|
||||
1.3-20110125 (1.3.5)
|
||||
--------------------
|
||||
|
|
|
@ -30,6 +30,7 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
|||
MAPIStoreAuthenticator.m \
|
||||
MAPIStoreMapping.m \
|
||||
MAPIStoreTypes.m \
|
||||
MAPIStorePropertySelectors.m \
|
||||
\
|
||||
SOGoMAPIFSFolder.m \
|
||||
SOGoMAPIFSMessage.m \
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
all:: MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h
|
||||
|
||||
MAPIStorePropertySelectors.m MAPIStorePropertySelectors.h: gen-property-selectors.py code-MAPIStorePropertySelectors.m code-MAPIStorePropertySelectors.h
|
||||
@echo " Auto-generating MAPIStorePropertySelectors.[hm]..."
|
||||
@./gen-property-selectors.py -o MAPIStorePropertySelectors $(LIBMAPISTORE_CFLAGS)
|
|
@ -25,7 +25,13 @@
|
|||
|
||||
#import "MAPIStoreGCSMessage.h"
|
||||
|
||||
@class iCalEvent;
|
||||
|
||||
@interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage
|
||||
{
|
||||
iCalEvent *event;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MAPISTORECALENDARMESSAGE_H */
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <Appointments/SOGoAppointmentObject.h>
|
||||
|
||||
#import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreCalendarAttachment.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreRecurrenceUtils.h"
|
||||
|
@ -73,17 +72,36 @@ static NSTimeZone *utcTZ;
|
|||
[utcTZ retain];
|
||||
}
|
||||
|
||||
- (id) initWithSOGoObject: (id) newSOGoObject
|
||||
inContainer: (MAPIStoreObject *) newContainer
|
||||
{
|
||||
if ((self = [super initWithSOGoObject: newSOGoObject
|
||||
inContainer: newContainer]))
|
||||
{
|
||||
ASSIGN (event, [newSOGoObject component: NO secure: NO]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
attachmentKeys = [NSMutableArray new];
|
||||
attachmentParts = [NSMutableDictionary new];
|
||||
event = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[event release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSTimeZone *) ownerTimeZone
|
||||
{
|
||||
NSString *owner;
|
||||
|
@ -100,7 +118,6 @@ static NSTimeZone *utcTZ;
|
|||
}
|
||||
|
||||
- (void) _setupRecurrenceInCalendar: (iCalCalendar *) calendar
|
||||
withMasterEvent: (iCalEvent *) vEvent
|
||||
fromData: (NSData *) mapiRecurrenceData
|
||||
{
|
||||
struct Binary_r *blob;
|
||||
|
@ -109,13 +126,13 @@ static NSTimeZone *utcTZ;
|
|||
|
||||
/* cleanup */
|
||||
otherEvents = [[calendar events] mutableCopy];
|
||||
[otherEvents removeObject: vEvent];
|
||||
[otherEvents removeObject: event];
|
||||
[calendar removeChildren: otherEvents];
|
||||
[otherEvents release];
|
||||
|
||||
blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx];
|
||||
pattern = get_AppointmentRecurrencePattern (memCtx, blob);
|
||||
[calendar setupRecurrenceWithMasterEntity: vEvent
|
||||
[calendar setupRecurrenceWithMasterEntity: event
|
||||
fromRecurrencePattern: &pattern->RecurrencePattern];
|
||||
talloc_free (pattern);
|
||||
talloc_free (blob);
|
||||
|
@ -150,21 +167,19 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
struct Binary_r *bin;
|
||||
struct SBinary_short *sBin;
|
||||
NSCalendarDate *firstStartDate;
|
||||
iCalEvent *vEvent;
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
vEvent = [sogoObject component: NO secure: NO];
|
||||
rule = [[vEvent recurrenceRules] objectAtIndex: 0];
|
||||
rule = [[event recurrenceRules] objectAtIndex: 0];
|
||||
|
||||
firstStartDate = [vEvent firstRecurrenceStartDate];
|
||||
firstStartDate = [event firstRecurrenceStartDate];
|
||||
if (firstStartDate)
|
||||
{
|
||||
[firstStartDate setTimeZone: [self ownerTimeZone]];
|
||||
|
||||
arp = talloc_zero (memCtx, struct AppointmentRecurrencePattern);
|
||||
_fillAppointmentRecurrencePattern (arp, firstStartDate,
|
||||
[vEvent durationAsTimeInterval],
|
||||
[vEvent lastPossibleRecurrenceStartDate],
|
||||
[event durationAsTimeInterval],
|
||||
[event lastPossibleRecurrenceStartDate],
|
||||
rule);
|
||||
sBin = talloc_zero (memCtx, struct SBinary_short);
|
||||
bin = set_AppointmentRecurrencePattern (sBin, arp);
|
||||
|
@ -184,146 +199,182 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
return sBin;
|
||||
}
|
||||
|
||||
- (enum MAPISTATUS) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
/* getters */
|
||||
- (int) getPrIconIndex: (void **) data // TODO
|
||||
{
|
||||
uint32_t longValue;
|
||||
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// *longValue = 0x00000401 for recurring event
|
||||
// *longValue = 0x00000402 for meeting
|
||||
// *longValue = 0x00000403 for recurring meeting
|
||||
// *longValue = 0x00000404 for invitation
|
||||
|
||||
longValue = 0x0400;
|
||||
if ([event isRecurrent])
|
||||
longValue |= 0x0001;
|
||||
if ([[event attendees] count] > 0)
|
||||
longValue |= 0x0002;
|
||||
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data
|
||||
{
|
||||
*data = talloc_strdup(memCtx, "IPM.Appointment");
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrStartDate: (void **) data
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
|
||||
if ([event isRecurrent])
|
||||
dateValue = [event firstRecurrenceStartDate];
|
||||
else
|
||||
dateValue = [event startDate];
|
||||
[dateValue setTimeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidAppointmentStartWhole: (void **) data
|
||||
{
|
||||
return [self getPrStartDate: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidCommonStart: (void **) data
|
||||
{
|
||||
return [self getPrStartDate: data];
|
||||
}
|
||||
|
||||
- (int) getPrEndDate: (void **) data
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
|
||||
if ([event isRecurrent])
|
||||
dateValue = [event firstRecurrenceStartDate];
|
||||
else
|
||||
dateValue = [event startDate];
|
||||
dateValue
|
||||
= [dateValue dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: (NSInteger) [event
|
||||
durationAsTimeInterval]];
|
||||
[dateValue setTimeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidAppointmentEndWhole: (void **) data
|
||||
{
|
||||
return [self getPrEndDate: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidCommonEnd: (void **) data
|
||||
{
|
||||
return [self getPrEndDate: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidAppointmentDuration: (void **) data
|
||||
{
|
||||
NSTimeInterval timeValue;
|
||||
id event;
|
||||
uint32_t longValue;
|
||||
NSCalendarDate *dateValue;
|
||||
int rc;
|
||||
|
||||
rc = MAPI_E_SUCCESS;
|
||||
switch ((uint32_t) propTag)
|
||||
{
|
||||
case PR_ICON_INDEX: // TODO
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// *longValue = 0x00000401 for recurring event
|
||||
// *longValue = 0x00000402 for meeting
|
||||
// *longValue = 0x00000403 for recurring meeting
|
||||
// *longValue = 0x00000404 for invitation
|
||||
timeValue = [[event endDate] timeIntervalSinceDate: [event startDate]];
|
||||
*data = MAPILongValue (memCtx, (uint32_t) (timeValue / 60));
|
||||
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
longValue = 0x0400;
|
||||
if ([event isRecurrent])
|
||||
longValue |= 0x0001;
|
||||
if ([[event attendees] count] > 0)
|
||||
longValue |= 0x0002;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
break;
|
||||
case PR_MESSAGE_CLASS_UNICODE:
|
||||
*data = talloc_strdup(memCtx, "IPM.Appointment");
|
||||
break;
|
||||
case PR_START_DATE:
|
||||
case PidLidAppointmentStartWhole:
|
||||
case PidLidCommonStart:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
if ([event isRecurrent])
|
||||
dateValue = [event firstRecurrenceStartDate];
|
||||
else
|
||||
dateValue = [event startDate];
|
||||
[dateValue setTimeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_END_DATE:
|
||||
case PidLidAppointmentEndWhole:
|
||||
case PidLidCommonEnd:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
if ([event isRecurrent])
|
||||
dateValue = [event firstRecurrenceStartDate];
|
||||
else
|
||||
dateValue = [event startDate];
|
||||
dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds:
|
||||
(NSInteger) [event durationAsTimeInterval]];
|
||||
[dateValue setTimeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidAppointmentDuration:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
timeValue = [[event endDate] timeIntervalSinceDate: [event startDate]];
|
||||
*data = MAPILongValue (memCtx, (uint32_t) (timeValue / 60));
|
||||
break;
|
||||
case PidLidAppointmentSubType:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
*data = MAPIBoolValue (memCtx, [event isAllDay]);
|
||||
break;
|
||||
case PidLidBusyStatus: // TODO
|
||||
*data = MAPILongValue (memCtx, 0x02);
|
||||
break;
|
||||
- (int) getPidLidAppointmentSubType: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, [event isAllDay]);
|
||||
|
||||
// case 0x82410003: // TODO
|
||||
// *data = MAPILongValue (memCtx, 0);
|
||||
// break;
|
||||
case PR_SUBJECT_UNICODE: // SUMMARY
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
*data = [[event summary] asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidLocation: // LOCATION
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
*data = [[event location] asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidPrivate: // private (bool), should depend on CLASS and permissions
|
||||
*data = MAPIBoolValue (memCtx, NO);
|
||||
break;
|
||||
case PR_SENSITIVITY: // not implemented, depends on CLASS
|
||||
// normal = 0, personal?? = 1, private = 2, confidential = 3
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_CREATION_TIME:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
*data = [[event created] asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
case PR_IMPORTANCE:
|
||||
{
|
||||
unsigned int v;
|
||||
- (int) getPidLidBusyStatus: (void **) data // TODO
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x02);
|
||||
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
if ([[event priority] isEqualToString: @"9"])
|
||||
v = 0x0;
|
||||
else if ([[event priority] isEqualToString: @"1"])
|
||||
v = 0x2;
|
||||
else
|
||||
v = 0x1;
|
||||
- (int) getPrSubject: (void **) data // SUMMARY
|
||||
{
|
||||
*data = [[event summary] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
}
|
||||
break;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidLocation: (void **) data // LOCATION
|
||||
{
|
||||
*data = [[event location] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
/* Recurrence */
|
||||
case PidLidIsRecurring:
|
||||
case PidLidRecurring:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
*data = MAPIBoolValue (memCtx, [event isRecurrent]);
|
||||
break;
|
||||
case PidLidAppointmentRecur:
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
if ([event isRecurrent])
|
||||
*data = [self _computeAppointmentRecur];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
break;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
// case PidLidTimeZoneStruct:
|
||||
// case PR_VD_NAME_UNICODE:
|
||||
// *data = talloc_strdup(memCtx, "PR_VD_NAME_UNICODE");
|
||||
// break;
|
||||
// case PR_EMS_AB_DXA_REMOTE_CLIENT_UNICODE: "Home:" ???
|
||||
// *data = talloc_strdup(memCtx, "PR_EMS...");
|
||||
// break;
|
||||
default:
|
||||
rc = [super getProperty: data
|
||||
withTag: propTag];
|
||||
}
|
||||
- (int) getPidLidPrivate: (void **) data // private (bool), should depend on CLASS and permissions
|
||||
{
|
||||
return [self getNo: data];
|
||||
}
|
||||
|
||||
// #define PR_REPLY_TIME PROP_TAG(PT_SYSTIME , 0x0030) /* 0x00300040 */
|
||||
// #define PR_INTERNET_MESSAGE_ID_UNICODE PROP_TAG(PT_UNICODE , 0x1035) /* 0x1035001f */
|
||||
// #define PR_FLAG_STATUS PROP_TAG(PT_LONG , 0x1090) /* 0x10900003 */
|
||||
- (int) getPrSensitivity: (void **) data // not implemented, depends on CLASS
|
||||
{
|
||||
// normal = 0, personal?? = 1, private = 2, confidential = 3
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrCreationTime: (void **) data
|
||||
{
|
||||
*data = [[event created] asFileTimeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrImportance: (void **) data
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if ([[event priority] isEqualToString: @"9"])
|
||||
v = 0x0;
|
||||
else if ([[event priority] isEqualToString: @"1"])
|
||||
v = 0x2;
|
||||
else
|
||||
v = 0x1;
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidIsRecurring: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, [event isRecurrent]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidRecurring: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, [event isRecurrent]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidAppointmentRecur: (void **) data
|
||||
{
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
if ([event isRecurrent])
|
||||
*data = [self _computeAppointmentRecur];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -333,12 +384,10 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
NSString *name, *email;
|
||||
NSArray *attendees;
|
||||
iCalPerson *person;
|
||||
id event;
|
||||
struct SRowSet *recipients;
|
||||
int count, max;
|
||||
|
||||
[super openMessage: msg];
|
||||
event = [sogoObject component: NO secure: NO];
|
||||
attendees = [event attendees];
|
||||
max = [attendees count];
|
||||
|
||||
|
@ -382,11 +431,11 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
{
|
||||
WOContext *woContext;
|
||||
iCalCalendar *vCalendar;
|
||||
iCalEvent *vEvent;
|
||||
iCalDateTime *start, *end;
|
||||
iCalTimeZone *tz;
|
||||
NSCalendarDate *now;
|
||||
NSString *content, *tzName;
|
||||
iCalEvent *newEvent;
|
||||
id value;
|
||||
|
||||
[self logWithFormat: @"-save, event props:"];
|
||||
|
@ -395,25 +444,29 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
content = [sogoObject contentAsString];
|
||||
if (![content length])
|
||||
{
|
||||
vEvent = [sogoObject component: YES secure: NO];
|
||||
vCalendar = [vEvent parent];
|
||||
newEvent = [sogoObject component: YES secure: NO];
|
||||
if (newEvent != event)
|
||||
ASSIGN (event, newEvent);
|
||||
vCalendar = [event parent];
|
||||
[vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"];
|
||||
content = [vCalendar versitString];
|
||||
}
|
||||
|
||||
vCalendar = [iCalCalendar parseSingleFromSource: content];
|
||||
vEvent = [[vCalendar events] objectAtIndex: 0];
|
||||
newEvent = [[vCalendar events] objectAtIndex: 0];
|
||||
if (newEvent != event)
|
||||
ASSIGN (event, newEvent);
|
||||
|
||||
// summary
|
||||
value = [newProperties
|
||||
objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (value)
|
||||
[vEvent setSummary: value];
|
||||
[event setSummary: value];
|
||||
|
||||
// Location
|
||||
value = [newProperties objectForKey: MAPIPropertyKey (PidLidLocation)];
|
||||
if (value)
|
||||
[vEvent setLocation: value];
|
||||
[event setLocation: value];
|
||||
|
||||
tzName = [[self ownerTimeZone] name];
|
||||
tz = [iCalTimeZone timeZoneForName: tzName];
|
||||
|
@ -425,7 +478,7 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
value = [newProperties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)];
|
||||
if (value)
|
||||
{
|
||||
start = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtstart"];
|
||||
start = (iCalDateTime *) [event uniqueChildWithTag: @"dtstart"];
|
||||
[start setTimeZone: tz];
|
||||
[start setDateTime: value];
|
||||
}
|
||||
|
@ -436,7 +489,7 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
value = [newProperties objectForKey: MAPIPropertyKey (PidLidAppointmentEndWhole)];
|
||||
if (value)
|
||||
{
|
||||
end = (iCalDateTime *) [vEvent uniqueChildWithTag: @"dtend"];
|
||||
end = (iCalDateTime *) [event uniqueChildWithTag: @"dtend"];
|
||||
[end setTimeZone: tz];
|
||||
[end setDateTime: value];
|
||||
}
|
||||
|
@ -444,9 +497,9 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
now = [NSCalendarDate date];
|
||||
if ([sogoObject isNew])
|
||||
{
|
||||
[vEvent setCreated: now];
|
||||
[event setCreated: now];
|
||||
}
|
||||
[vEvent setTimeStampAsDate: now];
|
||||
[event setTimeStampAsDate: now];
|
||||
|
||||
// Organizer and attendees
|
||||
value = [newProperties objectForKey: @"recipients"];
|
||||
|
@ -463,7 +516,7 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
person = [iCalPerson new];
|
||||
[person setCn: [dict objectForKey: @"fullName"]];
|
||||
[person setEmail: [dict objectForKey: @"email"]];
|
||||
[vEvent setOrganizer: person];
|
||||
[event setOrganizer: person];
|
||||
[person release];
|
||||
|
||||
recipients = [value objectForKey: @"to"];
|
||||
|
@ -480,8 +533,8 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
[person setRole: @"REQ-PARTICIPANT"];
|
||||
|
||||
// FIXME: We must NOT always rely on this
|
||||
if (![vEvent isAttendee: [person rfc822Email]])
|
||||
[vEvent addToAttendees: person];
|
||||
if (![event isAttendee: [person rfc822Email]])
|
||||
[event addToAttendees: person];
|
||||
|
||||
[person release];
|
||||
}
|
||||
|
@ -492,11 +545,10 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)];
|
||||
if (value)
|
||||
[self _setupRecurrenceInCalendar: vCalendar
|
||||
withMasterEvent: vEvent
|
||||
fromData: value];
|
||||
|
||||
// [sogoObject saveContentString: [vCalendar versitString]];
|
||||
[sogoObject saveComponent: vEvent];
|
||||
[sogoObject saveComponent: event];
|
||||
}
|
||||
|
||||
/* TODO: those are stubs meant to prevent OpenChange from crashing when a
|
||||
|
@ -513,11 +565,6 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
|
|||
return [attachmentParts objectForKey: childKey];
|
||||
}
|
||||
|
||||
- (MAPIStoreAttachmentTable *) attachmentTable
|
||||
{
|
||||
return [MAPIStoreAttachmentTable tableForContainer: self];
|
||||
}
|
||||
|
||||
- (MAPIStoreAttachment *) createAttachment
|
||||
{
|
||||
MAPIStoreCalendarAttachment *newAttachment;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <Contacts/SOGoContactGCSEntry.h>
|
||||
|
||||
#import "MAPIStorePropertySelectors.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSArray+MAPIStore.h"
|
||||
#import "NSCalendarDate+MAPIStore.h"
|
||||
|
@ -46,461 +47,466 @@
|
|||
|
||||
@implementation MAPIStoreContactsMessage
|
||||
|
||||
- (CardElement *) _element: (NSString *) elementTag
|
||||
ofType: (NSString *) aType
|
||||
excluding: (NSString *) aTypeToExclude
|
||||
inCard: (NGVCard *) card
|
||||
- (int) getPrIconIndex: (void **) data // TODO
|
||||
{
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
*data = MAPILongValue (memCtx, 0x00000200);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data
|
||||
{
|
||||
*data = talloc_strdup (memCtx, "IPM.Contact");
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrOabName: (void **) data
|
||||
{
|
||||
*data = talloc_strdup (memCtx, "PR_OAB_NAME_UNICODE");
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrOabLangid: (void **) data
|
||||
{
|
||||
/* see http://msdn.microsoft.com/en-us/goglobal/bb895996.asxp */
|
||||
/* English US */
|
||||
*data = MAPILongValue (memCtx, 0x0409);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrTitle: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] title];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrCompanyName: (void **) data
|
||||
{
|
||||
NSArray *values;
|
||||
NSString *stringValue;
|
||||
|
||||
values = [[sogoObject vCard] org];
|
||||
stringValue = nil;
|
||||
|
||||
if ([values count] > 0)
|
||||
stringValue = [values objectAtIndex: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrDepartmentName: (void **) data
|
||||
{
|
||||
NSArray *values;
|
||||
NSString *stringValue;
|
||||
|
||||
values = [[sogoObject vCard] org];
|
||||
stringValue = nil;
|
||||
|
||||
if ([values count] > 1)
|
||||
stringValue = [values objectAtIndex: 1];
|
||||
else
|
||||
stringValue = @"";
|
||||
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSendInternetEncoding: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x00065001);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSubject: (void **) data
|
||||
{
|
||||
return [self getPrDisplayName: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidFileUnder: (void **) data
|
||||
{
|
||||
return [self getPrDisplayName: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidFileUnderId: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0xffffffff);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail1DisplayName: (void **) data
|
||||
{
|
||||
NGVCard *vCard;
|
||||
NSString *fn, *email;
|
||||
|
||||
vCard = [sogoObject vCard];
|
||||
fn = [vCard fn];
|
||||
email = [vCard preferredEMail];
|
||||
*data = [[NSString stringWithFormat: @"%@ <%@>", fn, email]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail1OriginalDisplayName: (void **) data
|
||||
{
|
||||
return [self getPidLidEmail1DisplayName: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail1EmailAddress: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrAccount: (void **) data
|
||||
{
|
||||
return [self getPidLidEmail1EmailAddress: data];
|
||||
}
|
||||
|
||||
- (int) getPrContactEmailAddresses: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
*data = [[NSArray arrayWithObject: stringValue]
|
||||
asArrayOfUnicodeStringsInCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrEmsAbTargetAddress: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [[NSString stringWithFormat: @"SMTP:%@", stringValue]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSearchKey: (void **) data // TODO
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMailPermission: (void **) data
|
||||
{
|
||||
return [self getYes: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail2EmailAddress: (void **) data
|
||||
{
|
||||
NSMutableArray *emails;
|
||||
NSString *email, *stringValue;
|
||||
NGVCard *card;
|
||||
NSUInteger count, max;
|
||||
|
||||
emails = [NSMutableArray array];
|
||||
stringValue = nil;
|
||||
|
||||
card = [sogoObject vCard];
|
||||
[emails addObjectsFromArray: [card childrenWithTag: @"email"]];
|
||||
[emails removeObjectsInArray: [card childrenWithTag: @"email"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"pref"]];
|
||||
|
||||
max = [emails count];
|
||||
for (count = 0; !stringValue && count < max; count++)
|
||||
{
|
||||
email = [[emails objectAtIndex: count] value: 0];
|
||||
|
||||
if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame)
|
||||
stringValue = email;
|
||||
}
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail2OriginalDisplayName: (void **) data // Other email
|
||||
{
|
||||
return [self getPidLidEmail2EmailAddress: data];
|
||||
}
|
||||
|
||||
- (int) getPrBody: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] note];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) _getElement: (NSString *) elementTag
|
||||
ofType: (NSString *) aType
|
||||
excluding: (NSString *) aTypeToExclude
|
||||
atPos: (NSUInteger) pos
|
||||
inData: (void **) data
|
||||
{
|
||||
NSArray *elements;
|
||||
CardElement *ce, *found;
|
||||
CardElement *ce;
|
||||
NSUInteger count, max;
|
||||
NGVCard *vCard;
|
||||
NSString *stringValue;
|
||||
|
||||
found = nil;
|
||||
stringValue = nil;
|
||||
|
||||
elements = [[card childrenWithTag: elementTag]
|
||||
vCard = [sogoObject vCard];
|
||||
elements = [[vCard childrenWithTag: elementTag]
|
||||
cardElementsWithAttribute: @"type"
|
||||
havingValue: aType];
|
||||
max = [elements count];
|
||||
for (count = 0; !found && count < max; count++)
|
||||
for (count = 0; !stringValue && count < max; count++)
|
||||
{
|
||||
ce = [elements objectAtIndex: count];
|
||||
if (!aTypeToExclude
|
||||
|| ![ce hasAttribute: @"type" havingValue: aTypeToExclude])
|
||||
found = ce;
|
||||
stringValue = [ce value: pos];
|
||||
}
|
||||
|
||||
return found;
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (enum MAPISTATUS) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) proptag
|
||||
- (int) getPrOfficeTelephoneNumber: (void **) data
|
||||
{
|
||||
NSString *stringValue, *stringValue2;
|
||||
return [self _getElement: @"tel" ofType: @"work" excluding: @"fax"
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrHomeTelephoneNumber: (void **) data
|
||||
{
|
||||
return [self _getElement: @"tel" ofType: @"home" excluding: @"fax"
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrMobileTelephoneNumber: (void **) data
|
||||
{
|
||||
return [self _getElement: @"tel" ofType: @"cell" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrPrimaryTelephoneNumber: (void **) data
|
||||
{
|
||||
return [self _getElement: @"tel" ofType: @"pref" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrBusinessHomePage: (void **) data
|
||||
{
|
||||
return [self _getElement: @"url" ofType: @"work" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrPersonalHomePage: (void **) data
|
||||
{
|
||||
return [self _getElement: @"url" ofType: @"home" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail1AddressType: (void **) data
|
||||
{
|
||||
return [self getSMTPAddrType: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail2AddressType: (void **) data
|
||||
{
|
||||
return [self getSMTPAddrType: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidEmail3AddressType: (void **) data
|
||||
{
|
||||
return [self getSMTPAddrType: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidInstantMessagingAddress: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"]
|
||||
value: 0];
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidPostalAddressId: (void **) data
|
||||
{
|
||||
NSArray *elements;
|
||||
CardElement *element;
|
||||
uint32_t longValue;
|
||||
uint32_t longValue = 0;
|
||||
NGVCard *vCard;
|
||||
enum MAPISTATUS rc;
|
||||
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
switch ((uint32_t) proptag)
|
||||
vCard = [sogoObject vCard];
|
||||
elements = [[vCard childrenWithTag: @"adr"]
|
||||
cardElementsWithAttribute: @"type"
|
||||
havingValue: @"pref"];
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
case PR_ICON_INDEX: // TODO
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
*data = MAPILongValue (memCtx, 0x00000200);
|
||||
break;
|
||||
case PR_MESSAGE_CLASS_UNICODE:
|
||||
*data = talloc_strdup (memCtx, "IPM.Contact");
|
||||
break;
|
||||
// case PR_VD_NAME_UNICODE:
|
||||
// *data = talloc_strdup (memCtx, "PR_VD_NAME_UNICODE");
|
||||
// break;
|
||||
// case PR_EMS_AB_DXA_REMOTE_CLIENT_UNICODE: "Home:" ???
|
||||
// *data = talloc_strdup (memCtx, "PR_EMS...");
|
||||
// break;
|
||||
case PR_OAB_NAME_UNICODE:
|
||||
*data = talloc_strdup (memCtx, "PR_OAB_NAME_UNICODE");
|
||||
break;
|
||||
case PR_OAB_LANGID:
|
||||
/* see http://msdn.microsoft.com/en-us/goglobal/bb895996.asxp */
|
||||
/* English US */
|
||||
*data = MAPILongValue (memCtx, 0x0409);
|
||||
break;
|
||||
|
||||
case PR_TITLE_UNICODE:
|
||||
stringValue = [[sogoObject vCard] title];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_COMPANY_NAME_UNICODE:
|
||||
case PR_DEPARTMENT_NAME_UNICODE:
|
||||
{
|
||||
NSArray *values;
|
||||
|
||||
values = [[sogoObject vCard] org];
|
||||
stringValue = nil;
|
||||
|
||||
if (proptag == PR_COMPANY_NAME_UNICODE && [values count] > 0)
|
||||
stringValue = [values objectAtIndex: 0];
|
||||
else if (proptag == PR_DEPARTMENT_NAME_UNICODE && [values count] > 1)
|
||||
stringValue = [values objectAtIndex: 1];
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
}
|
||||
break;
|
||||
|
||||
case PR_SEND_INTERNET_ENCODING:
|
||||
*data = MAPILongValue (memCtx, 0x00065001);
|
||||
break;
|
||||
|
||||
case PR_SUBJECT_UNICODE:
|
||||
case PR_DISPLAY_NAME_UNICODE: // Full Name
|
||||
case PidLidFileUnder: // contact block title name
|
||||
rc = [super getProperty: data
|
||||
withTag: PR_DISPLAY_NAME_UNICODE];
|
||||
break;
|
||||
case PidLidFileUnderId:
|
||||
*data = MAPILongValue (memCtx, 0xffffffff);
|
||||
break;
|
||||
|
||||
case PidLidEmail1OriginalDisplayName:
|
||||
case PidLidEmail1DisplayName:
|
||||
vCard = [sogoObject vCard];
|
||||
stringValue = [vCard fn];
|
||||
stringValue2 = [vCard preferredEMail];
|
||||
*data = [[NSString stringWithFormat: @"%@ <%@>",
|
||||
stringValue, stringValue2]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PidLidEmail1EmailAddress:
|
||||
case PR_ACCOUNT_UNICODE:
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_CONTACT_EMAIL_ADDRESSES_UNICODE:
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
*data = [[NSArray arrayWithObject: stringValue]
|
||||
asArrayOfUnicodeStringsInCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_EMS_AB_TARGET_ADDRESS_UNICODE:
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [[NSString stringWithFormat: @"SMTP:%@", stringValue]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_SEARCH_KEY: // TODO
|
||||
stringValue = [[sogoObject vCard] preferredEMail];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_MAIL_PERMISSION:
|
||||
*data = MAPIBoolValue (memCtx, YES);
|
||||
break;
|
||||
|
||||
//
|
||||
// TODO - same logic as -secondaryEmail in UI/Contacts/UIxContactView.m
|
||||
// We should eventually merge that in order to not duplicate the code.
|
||||
// We should also eventually handle PidLidEmail3OriginalDisplayName in
|
||||
// SOGo, Thunderbird, etc.
|
||||
//
|
||||
case PidLidEmail2EmailAddress:
|
||||
case PidLidEmail2OriginalDisplayName: // Other email
|
||||
{
|
||||
NSMutableArray *emails;
|
||||
NSString *email;
|
||||
NGVCard *card;
|
||||
|
||||
emails = [NSMutableArray array];
|
||||
stringValue = nil;
|
||||
|
||||
card = [sogoObject vCard];
|
||||
[emails addObjectsFromArray: [card childrenWithTag: @"email"]];
|
||||
[emails removeObjectsInArray: [card childrenWithTag: @"email"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"pref"]];
|
||||
|
||||
if ([emails count] > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [emails count]; i++)
|
||||
{
|
||||
email = [[emails objectAtIndex: i] value: 0];
|
||||
|
||||
if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame)
|
||||
{
|
||||
stringValue = email;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
}
|
||||
break;
|
||||
|
||||
// FIXME: this property does NOT work
|
||||
case PR_BODY_UNICODE:
|
||||
stringValue = [[sogoObject vCard] note];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_OFFICE_TELEPHONE_NUMBER_UNICODE:
|
||||
element = [self _element: @"tel" ofType: @"work"
|
||||
excluding: @"fax"
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_HOME_TELEPHONE_NUMBER_UNICODE:
|
||||
element = [self _element: @"tel" ofType: @"home"
|
||||
excluding: @"fax"
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_MOBILE_TELEPHONE_NUMBER_UNICODE:
|
||||
element = [self _element: @"tel" ofType: @"cell"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_PRIMARY_TELEPHONE_NUMBER_UNICODE:
|
||||
element = [self _element: @"tel" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_BUSINESS_HOME_PAGE_UNICODE:
|
||||
case PR_PERSONAL_HOME_PAGE_UNICODE:
|
||||
{
|
||||
NSString *type;
|
||||
|
||||
type = (proptag == PR_BUSINESS_HOME_PAGE_UNICODE ? @"work" : @"home");
|
||||
element = [self _element: @"url" ofType: type
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
}
|
||||
break;
|
||||
|
||||
case PidLidEmail1AddressType:
|
||||
case PidLidEmail2AddressType:
|
||||
case PidLidEmail3AddressType:
|
||||
*data = [@"SMTP" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PidLidInstantMessagingAddress:
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] value: 0];
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
//
|
||||
// We don't handle 0x00000003 - The Other Address is the mailing address.
|
||||
// See: http://msdn.microsoft.com/en-us/library/cc815430.aspx
|
||||
//
|
||||
case PidLidPostalAddressId:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
element = [elements objectAtIndex: 0];
|
||||
if ([element hasAttribute: @"type"
|
||||
havingValue: @"home"])
|
||||
longValue = 1; // The Home Address is the mailing address.
|
||||
havingValue: @"home"])
|
||||
longValue = 1; // The Home Address is the mailing address.
|
||||
else if ([element hasAttribute: @"type"
|
||||
havingValue: @"work"])
|
||||
longValue = 2; // The Work Address is the mailing address.
|
||||
else
|
||||
longValue = 0; // No address is selected as the mailing address.
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
break;
|
||||
|
||||
/* preferred address */
|
||||
case PR_POSTAL_ADDRESS_UNICODE:
|
||||
element = [self _element: @"label" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_POST_OFFICE_BOX_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_STREET_ADDRESS_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 2];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_LOCALITY_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 3];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_STATE_OR_PROVINCE_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 4];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_POSTAL_CODE_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 5];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_COUNTRY_UNICODE:
|
||||
element = [self _element: @"adr" ofType: @"pref"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 6];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
// case PidLidAddressCountryCode:
|
||||
|
||||
case PidLidWorkAddress:
|
||||
element = [self _element: @"label" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PidLidWorkAddressPostOfficeBox:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 0];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidWorkAddressStreet:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 2];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidWorkAddressCity:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 3];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidWorkAddressState:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 4];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidWorkAddressPostalCode:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 5];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidWorkAddressCountry:
|
||||
element = [self _element: @"adr" ofType: @"work"
|
||||
excluding: nil
|
||||
inCard: [sogoObject vCard]];
|
||||
if (element)
|
||||
stringValue = [element value: 6];
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
// PidTagNickname
|
||||
case PR_NICKNAME_UNICODE:
|
||||
stringValue = [[sogoObject vCard] nickname];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_BIRTHDAY:
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] bday];
|
||||
|
||||
if (stringValue)
|
||||
{
|
||||
dateValue = [NSCalendarDate dateWithString: stringValue
|
||||
calendarFormat: @"%Y-%m-%d"];
|
||||
// FIXME: We add a day, otherwise Outlook 2003 will display at day earlier
|
||||
dateValue = [dateValue addYear: 0 month: 0 day: 1 hour: 0 minute: 0 second: 0];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = [super getProperty: data withTag: proptag];
|
||||
havingValue: @"work"])
|
||||
longValue = 2; // The Work Address is the mailing address.
|
||||
}
|
||||
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrPostalAddress: (void **) data
|
||||
{
|
||||
return [self _getElement: @"label" ofType: @"pref" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrPostOfficeBox: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrStreetAddress: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 2 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrLocality: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 3 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrStateOrProvince: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 4 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrPostalCode: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 5 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrCountry: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"pref" excluding: nil
|
||||
atPos: 6 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddress: (void **) data
|
||||
{
|
||||
return [self _getElement: @"label" ofType: @"work" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressPostOfficeBox: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 0 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressStreet: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 2 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressCity: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 3 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressState: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 4 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressPostalCode: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 5 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidWorkAddressCountry: (void **) data
|
||||
{
|
||||
return [self _getElement: @"adr" ofType: @"work" excluding: nil
|
||||
atPos: 6 inData: data];
|
||||
}
|
||||
|
||||
- (int) getPrNickname: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [[sogoObject vCard] nickname];
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrBirthday: (void **) data
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[sogoObject vCard] bday];
|
||||
if (stringValue)
|
||||
{
|
||||
dateValue = [NSCalendarDate dateWithString: stringValue
|
||||
calendarFormat: @"%Y-%m-%d"];
|
||||
// FIXME: We add a day, otherwise Outlook 2003 will display at day earlier
|
||||
dateValue = [dateValue addYear: 0 month: 0 day: 1 hour: 0 minute: 0 second: 0];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@
|
|||
- (void) setAuthenticator: (MAPIStoreAuthenticator *) newAuthenticator;
|
||||
- (MAPIStoreAuthenticator *) authenticator;
|
||||
|
||||
- (NSURL *) url;
|
||||
|
||||
- (WOContext *) woContext;
|
||||
|
||||
- (void) setupRequest;
|
||||
|
@ -131,14 +133,6 @@
|
|||
- (int) setPropertiesWithFMID: (uint64_t) fmid
|
||||
ofTableType: (uint8_t) tableType
|
||||
inRow: (struct SRow *) aRow;
|
||||
- (int) setProperty: (enum MAPITAGS) property
|
||||
withFMID: (uint64_t) fmid
|
||||
ofTableType: (uint8_t) tableType
|
||||
fromFile: (NSFileHandle *) aFile;
|
||||
- (int) getProperty: (enum MAPITAGS) property
|
||||
withFMID: (uint64_t) fmid
|
||||
ofTableType: (uint8_t) tableType
|
||||
intoFile: (NSFileHandle *) aFile;
|
||||
- (int) modifyRecipientsWithMID: (uint64_t) mid
|
||||
inRows: (struct ModifyRecipientRow *) rows
|
||||
withCount: (NSUInteger) max;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSFileHandle.h>
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSThread.h>
|
||||
|
@ -258,6 +257,11 @@ _prepareContextClass (struct mapistore_context *newMemCtx,
|
|||
return authenticator;
|
||||
}
|
||||
|
||||
- (NSURL *) url
|
||||
{
|
||||
return contextUrl;
|
||||
}
|
||||
|
||||
- (void) setupRequest
|
||||
{
|
||||
NSMutableDictionary *info;
|
||||
|
@ -843,14 +847,14 @@ _prepareContextClass (struct mapistore_context *newMemCtx,
|
|||
{
|
||||
if (propRc == MAPISTORE_ERR_NOT_FOUND)
|
||||
propRc = MAPI_E_NOT_FOUND;
|
||||
else if (propRc == MAPISTORE_ERR_NO_MEMORY)
|
||||
propRc = MAPI_E_NOT_ENOUGH_MEMORY;
|
||||
// else if (propRc == MAPISTORE_ERR_NO_MEMORY)
|
||||
// propRc = MAPI_E_NOT_ENOUGH_MEMORY;
|
||||
if (propValue)
|
||||
talloc_free (propValue);
|
||||
propValue = MAPILongValue (memCtx, propRc);
|
||||
tag = (tag & 0xffff0000) | 0x000a;
|
||||
}
|
||||
set_SPropValue_proptag (aRow->lpProps + count, tag, propValue);
|
||||
set_SPropValue_proptag (aRow->lpProps + count, tag, propValue);
|
||||
}
|
||||
}
|
||||
talloc_free (data);
|
||||
|
@ -977,120 +981,6 @@ _prepareContextClass (struct mapistore_context *newMemCtx,
|
|||
return rc;
|
||||
}
|
||||
|
||||
- (int) setProperty: (enum MAPITAGS) property
|
||||
withFMID: (uint64_t) fmid
|
||||
ofTableType: (uint8_t) tableType
|
||||
fromFile: (NSFileHandle *) aFile
|
||||
{
|
||||
MAPIStoreMessage *message;
|
||||
NSNumber *midKey;
|
||||
NSData *fileData;
|
||||
const char *propName;
|
||||
int rc;
|
||||
|
||||
propName = get_proptag_name (property);
|
||||
if (!propName)
|
||||
propName = "<unknown>";
|
||||
[self logWithFormat: @"METHOD '%s' -- property: %s(%.8x), fmid: 0x%.16x, tableType: %d",
|
||||
__FUNCTION__, propName, property, fmid, tableType];
|
||||
|
||||
fileData = [aFile readDataToEndOfFile];
|
||||
switch (tableType)
|
||||
{
|
||||
case MAPISTORE_MESSAGE:
|
||||
midKey = [NSNumber numberWithUnsignedLongLong: fmid];
|
||||
message = [messages objectForKey: midKey];
|
||||
if (message)
|
||||
{
|
||||
[message addNewProperties:
|
||||
[NSDictionary
|
||||
dictionaryWithObject: NSObjectFromStreamData (property,
|
||||
fileData)
|
||||
forKey: MAPIPropertyKey (property)]];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
break;
|
||||
case MAPISTORE_FOLDER:
|
||||
default:
|
||||
[self errorWithFormat: @"%s: value of tableType not handled: %d",
|
||||
__FUNCTION__, tableType];
|
||||
rc = MAPISTORE_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getProperty: (enum MAPITAGS) property
|
||||
withFMID: (uint64_t) fmid
|
||||
ofTableType: (uint8_t) tableType
|
||||
intoFile: (NSFileHandle *) aFile
|
||||
{
|
||||
MAPIStoreMessage *message;
|
||||
NSNumber *midKey;
|
||||
NSData *fileData;
|
||||
const char *propName;
|
||||
enum MAPISTATUS rc;
|
||||
|
||||
propName = get_proptag_name (property);
|
||||
if (!propName)
|
||||
propName = "<unknown>";
|
||||
[self logWithFormat: @"METHOD '%s' -- property: %s(%.8x), fmid: 0x%.16x, tableType: %d",
|
||||
__FUNCTION__, propName, property, fmid, tableType];
|
||||
|
||||
switch (tableType)
|
||||
{
|
||||
case MAPISTORE_MESSAGE:
|
||||
midKey = [NSNumber numberWithUnsignedLongLong: fmid];
|
||||
message = [messages objectForKey: midKey];
|
||||
if (message)
|
||||
{
|
||||
fileData = [[message newProperties] objectForKey: MAPIPropertyKey (property)];
|
||||
if ([fileData isKindOfClass: NSStringK])
|
||||
fileData = [fileData dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
|
||||
if (fileData)
|
||||
{
|
||||
if (![fileData isKindOfClass: NSDataK])
|
||||
[self
|
||||
errorWithFormat: @"data class not handled for streams: %@",
|
||||
NSStringFromClass ([fileData class])];
|
||||
[aFile writeData: fileData];
|
||||
rc = MAPI_E_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"no data for property %s(%.8x)"
|
||||
@" in mid %.16x", propName, property, fmid];
|
||||
rc = MAPI_E_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"no message found with mid %.16x", fmid];
|
||||
rc = MAPI_E_INVALID_OBJECT;
|
||||
}
|
||||
break;
|
||||
|
||||
// [message setObject: NSObjectFromStreamData (property, fileData)
|
||||
// forKey: MAPIPropertyNumber (property)];
|
||||
// rc = MAPISTORE_SUCCESS;
|
||||
// }
|
||||
// else
|
||||
case MAPISTORE_FOLDER:
|
||||
[self errorWithFormat: @"%s: folder properties not handled yet",
|
||||
__FUNCTION__];
|
||||
rc = MAPI_E_NOT_FOUND;
|
||||
break;
|
||||
default:
|
||||
[self errorWithFormat: @"%s: value of tableType not handled: %d",
|
||||
__FUNCTION__, tableType];
|
||||
rc = MAPI_E_INVALID_OBJECT;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (NSDictionary *) _convertRecipientFromRow: (struct RecipientRow *) row
|
||||
{
|
||||
NSMutableDictionary *recipient;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#import <Mailer/SOGoDraftObject.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreDraftsAttachment.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
|
@ -165,11 +164,6 @@ e)
|
|||
[self _saveAttachment: [attachmentKeys objectAtIndex: count]];
|
||||
}
|
||||
|
||||
- (MAPIStoreAttachmentTable *) attachmentTable
|
||||
{
|
||||
return [MAPIStoreAttachmentTable tableForContainer: self];
|
||||
}
|
||||
|
||||
- (MAPIStoreAttachment *) createAttachment
|
||||
{
|
||||
MAPIStoreDraftsAttachment *newAttachment;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
{
|
||||
id value;
|
||||
enum MAPISTATUS rc;
|
||||
|
||||
|
||||
value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)];
|
||||
if (value)
|
||||
rc = [value getMAPIValue: data forTag: propTag inMemCtx: memCtx];
|
||||
|
|
|
@ -395,8 +395,7 @@ Class NSExceptionK, MAPIStoreMessageTableK, MAPIStoreFAIMessageTableK, MAPIStore
|
|||
|
||||
- (MAPIStoreMessage *) createMessage
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
[self logWithFormat: @"ignored method: %s", __PRETTY_FUNCTION__];
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ static MAPIStoreMapping *mapping;
|
|||
|
||||
- (NSArray *) restrictedChildKeys
|
||||
{
|
||||
[self errorWithFormat: @"restrictions are ignored on folder tables"];
|
||||
/* FIXME: restrictions are ignored on folder tables */
|
||||
return [self childKeys];
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
break;
|
||||
case PR_ATTACH_TAG:
|
||||
*data = [[self mimeAttachTag]
|
||||
asShortBinaryInMemCtx: memCtx];
|
||||
asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_ATTACH_SIZE:
|
||||
longValue = [[bodyInfo objectForKey: @"size"] longValue];
|
||||
|
@ -95,7 +95,7 @@
|
|||
|
||||
case PR_RECORD_KEY:
|
||||
*data = [[NSData dataWithBytes: recordBytes length: 16]
|
||||
asShortBinaryInMemCtx: memCtx];
|
||||
asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
// PR_RECORD_KEY (0xFF90102) D9 D8 11 A3 E2 90 18 41 9E 04 58 46 9D 6D 1B 68
|
||||
|
@ -152,7 +152,7 @@
|
|||
break;
|
||||
|
||||
case PR_ATTACH_DATA_BIN:
|
||||
*data = [[sogoObject fetchBLOB] asShortBinaryInMemCtx: memCtx];
|
||||
*data = [[sogoObject fetchBLOB] asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -48,6 +48,9 @@
|
|||
|
||||
- (void) openMessage: (struct mapistore_message *) msg;
|
||||
|
||||
/* helper getters */
|
||||
- (int) getSMTPAddrType: (void **) data;
|
||||
|
||||
/* subclasses */
|
||||
- (void) submit;
|
||||
- (void) save;
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
|
||||
#import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
@ -127,146 +130,274 @@
|
|||
msg->properties = properties;
|
||||
}
|
||||
|
||||
- (int) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
/* helper getters */
|
||||
- (int) getSMTPAddrType: (void **) data
|
||||
{
|
||||
int rc;
|
||||
*data = [@"SMTP" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
/* getters */
|
||||
- (int) getPrInstId: (void **) data // TODO: DOUBT
|
||||
{
|
||||
/* we return a unique id based on the key */
|
||||
*data = MAPILongLongValue (memCtx, [[sogoObject nameInContainer] hash]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrInstanceNum: (void **) data // TODO: DOUBT
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrRowType: (void **) data // TODO: DOUBT
|
||||
{
|
||||
*data = MAPILongValue (memCtx, TBL_LEAF_ROW);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrDepth: (void **) data // TODO: DOUBT
|
||||
{
|
||||
*data = MAPILongLongValue (memCtx, 0);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrAccess: (void **) data // TODO
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x03);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrAccessLevel: (void **) data // TODO
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x01);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrViewStyle: (void **) data
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrViewMajorversion: (void **) data
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidSideEffects: (void **) data // TODO
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidCurrentVersion: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 115608); // Outlook 11.5608
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidCurrentVersionName: (void **) data
|
||||
{
|
||||
*data = [@"11.0" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidAutoProcessState: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x00000000);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidNameContentClass: (void **) data
|
||||
{
|
||||
*data = [@"Sharing" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrFid: (void **) data
|
||||
{
|
||||
*data = MAPILongLongValue (memCtx, [container objectId]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMid: (void **) data
|
||||
{
|
||||
*data = MAPILongLongValue (memCtx, [self objectId]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageLocaleId: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x0409);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageFlags: (void **) data // TODO
|
||||
{
|
||||
*data = MAPILongValue (memCtx, MSGFLAG_FROMME | MSGFLAG_READ | MSGFLAG_UNMODIFIED);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageSize: (void **) data // TODO
|
||||
{
|
||||
/* TODO: choose another name in SOGo for that method */
|
||||
*data = MAPILongValue (memCtx, [[sogoObject davContentLength] intValue]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMsgStatus: (void **) data // TODO
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrImportance: (void **) data // TODO -> subclass?
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 1);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrPriority: (void **) data // TODO -> subclass?
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrSensitivity: (void **) data // TODO -> subclass in calendar
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
// TODO: PR_CHANGE_KEY is a GID based on the ReplGUID
|
||||
- (int) getPrChangeKey: (void **) data
|
||||
{
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
NSString *stringValue;
|
||||
NSUInteger length;
|
||||
|
||||
rc = MAPI_E_SUCCESS;
|
||||
switch ((uint32_t) propTag)
|
||||
stringValue = [sogoObject davEntityTag];
|
||||
if (stringValue)
|
||||
{
|
||||
case PR_INST_ID: // TODO: DOUBT
|
||||
/* we return a unique id based on the key */
|
||||
*data = MAPILongLongValue (memCtx, [[sogoObject nameInContainer] hash]);
|
||||
break;
|
||||
case PR_INSTANCE_NUM: // TODO: DOUBT
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_ROW_TYPE: // TODO: DOUBT
|
||||
*data = MAPILongValue (memCtx, TBL_LEAF_ROW);
|
||||
break;
|
||||
case PR_DEPTH: // TODO: DOUBT
|
||||
*data = MAPILongLongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_ACCESS: // TODO
|
||||
*data = MAPILongValue (memCtx, 0x03);
|
||||
break;
|
||||
case PR_ACCESS_LEVEL: // TODO
|
||||
*data = MAPILongValue (memCtx, 0x01);
|
||||
break;
|
||||
case PR_VIEW_STYLE:
|
||||
case PR_VIEW_MAJORVERSION:
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PidLidSideEffects: // TODO
|
||||
*data = MAPILongValue (memCtx, 0x00000000);
|
||||
break;
|
||||
case PidLidCurrentVersion:
|
||||
*data = MAPILongValue (memCtx, 115608); // Outlook 11.5608
|
||||
break;
|
||||
case PidLidCurrentVersionName:
|
||||
*data = [@"11.0" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidAutoProcessState:
|
||||
*data = MAPILongValue (memCtx, 0x00000000);
|
||||
break;
|
||||
case PidNameContentClass:
|
||||
*data = [@"Sharing" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_FID:
|
||||
*data = MAPILongLongValue (memCtx, [container objectId]);
|
||||
break;
|
||||
case PR_MID:
|
||||
*data = MAPILongLongValue (memCtx, [self objectId]);
|
||||
break;
|
||||
case PR_MESSAGE_LOCALE_ID:
|
||||
*data = MAPILongValue (memCtx, 0x0409);
|
||||
break;
|
||||
case PR_MESSAGE_FLAGS: // TODO
|
||||
*data = MAPILongValue (memCtx, MSGFLAG_FROMME | MSGFLAG_READ | MSGFLAG_UNMODIFIED);
|
||||
break;
|
||||
case PR_MESSAGE_SIZE: // TODO
|
||||
/* TODO: choose another name in SOGo for that method */
|
||||
*data = MAPILongValue (memCtx, [[sogoObject davContentLength] intValue]);
|
||||
break;
|
||||
case PR_MSG_STATUS: // TODO
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_IMPORTANCE: // TODO -> subclass?
|
||||
*data = MAPILongValue (memCtx, 1);
|
||||
break;
|
||||
case PR_PRIORITY: // TODO -> subclass?
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_SENSITIVITY: // TODO -> subclass in calendar
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_CHANGE_KEY:
|
||||
stringValue = [sogoObject davEntityTag];
|
||||
if (stringValue)
|
||||
{
|
||||
stringValue = @"-1";
|
||||
length = [stringValue length];
|
||||
if (length < 6) /* guid = 16 bytes */
|
||||
{
|
||||
length += 6;
|
||||
stringValue = [NSString stringWithFormat: @"000000%@",
|
||||
stringValue];
|
||||
}
|
||||
if (length > 6)
|
||||
stringValue = [stringValue substringFromIndex: length - 6];
|
||||
stringValue = [NSString stringWithFormat: @"SOGo%@%@%@",
|
||||
stringValue, stringValue, stringValue];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asShortBinaryInMemCtx: memCtx];
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
break;
|
||||
|
||||
case PR_ORIGINAL_SUBJECT_UNICODE:
|
||||
case PR_CONVERSATION_TOPIC_UNICODE:
|
||||
rc = [self getProperty: data withTag: PR_NORMALIZED_SUBJECT_UNICODE];
|
||||
break;
|
||||
|
||||
case PR_SUBJECT_PREFIX_UNICODE:
|
||||
*data = [@"" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_NORMALIZED_SUBJECT_UNICODE:
|
||||
rc = [self getProperty: data withTag: PR_SUBJECT_UNICODE];
|
||||
break;
|
||||
|
||||
case PR_DISPLAY_TO_UNICODE:
|
||||
case PR_DISPLAY_CC_UNICODE:
|
||||
case PR_DISPLAY_BCC_UNICODE:
|
||||
case PR_ORIGINAL_DISPLAY_TO_UNICODE:
|
||||
case PR_ORIGINAL_DISPLAY_CC_UNICODE:
|
||||
case PR_ORIGINAL_DISPLAY_BCC_UNICODE:
|
||||
*data = [@"" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_LAST_MODIFIER_NAME_UNICODE:
|
||||
*data = [@"openchange" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
case PR_ORIG_MESSAGE_CLASS_UNICODE:
|
||||
rc = [self getProperty: data withTag: PR_MESSAGE_CLASS_UNICODE];
|
||||
break;
|
||||
|
||||
case PR_HASATTACH:
|
||||
*data = MAPIBoolValue (memCtx,
|
||||
[[self childKeysMatchingQualifier: nil
|
||||
andSortOrderings: nil] count] > 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = [super getProperty: data withTag: propTag];
|
||||
stringValue = @"-1";
|
||||
length = [stringValue length];
|
||||
if (length < 6) /* guid = 16 bytes */
|
||||
length += 6;
|
||||
stringValue = [NSString stringWithFormat: @"000000%@",
|
||||
stringValue];
|
||||
if (length > 6)
|
||||
stringValue = [stringValue substringFromIndex: length - 6];
|
||||
stringValue = [NSString stringWithFormat: @"SOGo%@%@%@",
|
||||
stringValue, stringValue, stringValue];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPrSubject: (void **) data
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrNormalizedSubject: (void **) data
|
||||
{
|
||||
return [self getPrSubject: data];
|
||||
}
|
||||
|
||||
- (int) getPrOriginalSubject: (void **) data
|
||||
{
|
||||
return [self getPrNormalizedSubject: data];
|
||||
}
|
||||
|
||||
- (int) getPrConversationTopic: (void **) data
|
||||
{
|
||||
return [self getPrNormalizedSubject: data];
|
||||
}
|
||||
|
||||
- (int) getPrSubjectPrefix: (void **) data
|
||||
{
|
||||
return [self getEmptyString: data];
|
||||
}
|
||||
|
||||
- (int) getPrDisplayTo: (void **) data
|
||||
{
|
||||
return [self getEmptyString: data];
|
||||
}
|
||||
|
||||
- (int) getPrDisplayCc: (void **) data
|
||||
{
|
||||
return [self getEmptyString: data];
|
||||
}
|
||||
|
||||
- (int) getPrDisplayBcc: (void **) data
|
||||
{
|
||||
return [self getEmptyString: data];
|
||||
}
|
||||
|
||||
- (int) getPrOriginalDisplayTo: (void **) data
|
||||
{
|
||||
return [self getPrDisplayTo: data];
|
||||
}
|
||||
|
||||
- (int) getPrOriginalDisplayCc: (void **) data
|
||||
{
|
||||
return [self getPrDisplayCc: data];
|
||||
}
|
||||
|
||||
- (int) getPrOriginalDisplayBcc: (void **) data
|
||||
{
|
||||
return [self getPrDisplayBcc: data];
|
||||
}
|
||||
|
||||
- (int) getPrLastModifierName: (void **) data
|
||||
{
|
||||
NSURL *contextUrl;
|
||||
|
||||
contextUrl = [[self context] url];
|
||||
*data = [[contextUrl user] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPrOrigMessageClass: (void **) data
|
||||
{
|
||||
return [self getPrMessageClass: data];
|
||||
}
|
||||
|
||||
- (int) getPrHasattach: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx,
|
||||
[[self childKeysMatchingQualifier: nil
|
||||
andSortOrderings: nil] count] > 0);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
@ -286,9 +417,7 @@
|
|||
|
||||
- (MAPIStoreAttachmentTable *) attachmentTable
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
return [MAPIStoreAttachmentTable tableForContainer: self];
|
||||
}
|
||||
|
||||
- (void) addActiveTable: (MAPIStoreTable *) activeTable
|
||||
|
|
|
@ -24,35 +24,27 @@
|
|||
|
||||
#import "MAPIStoreNotesMessage.h"
|
||||
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
@implementation MAPIStoreNotesMessage
|
||||
|
||||
- (enum MAPISTATUS) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
- (int) getPrIconIndex: (void **) data // TODO
|
||||
{
|
||||
int rc;
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// *longValue = 0x00000300 for blue
|
||||
// *longValue = 0x00000301 for green
|
||||
// *longValue = 0x00000302 for pink
|
||||
// *longValue = 0x00000303 for yellow
|
||||
// *longValue = 0x00000304 for white
|
||||
*data = MAPILongValue (memCtx, 0x00000303);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
rc = MAPI_E_SUCCESS;
|
||||
switch (propTag)
|
||||
{
|
||||
case PR_ICON_INDEX: // TODO
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// *longValue = 0x00000300 for blue
|
||||
// *longValue = 0x00000301 for green
|
||||
// *longValue = 0x00000302 for pink
|
||||
// *longValue = 0x00000303 for yellow
|
||||
// *longValue = 0x00000304 for white
|
||||
*data = MAPILongValue (memCtx, 0x00000303);
|
||||
break;
|
||||
|
||||
case PR_SUBJECT_UNICODE:
|
||||
rc = [super getProperty: data
|
||||
withTag: PR_NORMALIZED_SUBJECT_UNICODE];
|
||||
break;
|
||||
default:
|
||||
rc = [super getProperty: data withTag: propTag];
|
||||
}
|
||||
|
||||
return rc;
|
||||
- (int) getPrSubject: (void **) data
|
||||
{
|
||||
return [self getProperty: data
|
||||
withTag: PR_NORMALIZED_SUBJECT_UNICODE];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
|
||||
@interface MAPIStoreObject : NSObject
|
||||
{
|
||||
const IMP *classGetters;
|
||||
|
||||
NSMutableArray *parentContainersBag;
|
||||
MAPIStoreObject *container;
|
||||
id sogoObject;
|
||||
|
@ -46,6 +48,8 @@
|
|||
|
||||
+ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject
|
||||
inContainer: (MAPIStoreObject *) newContainer;
|
||||
+ (int) getAvailableProperties: (struct SPropTagArray *) properties;
|
||||
|
||||
- (id) initWithSOGoObject: (id) newSOGoObject
|
||||
inContainer: (MAPIStoreObject *) newFolder;
|
||||
|
||||
|
@ -74,6 +78,7 @@
|
|||
- (void) resetNewProperties;
|
||||
|
||||
/* ops */
|
||||
- (int) getAvailableProperties: (struct SPropTagArray *) properties;
|
||||
- (int) getProperties: (struct mapistore_property_data *) data
|
||||
withTags: (enum MAPITAGS *) tags
|
||||
andCount: (uint16_t) columnCount;
|
||||
|
@ -83,6 +88,12 @@
|
|||
- (int) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag;
|
||||
|
||||
/* helper getters */
|
||||
- (int) getEmptyString: (void **) data;
|
||||
- (int) getLongZero: (void **) data;
|
||||
- (int) getYes: (void **) data;
|
||||
- (int) getNo: (void **) data;
|
||||
|
||||
/* subclasses */
|
||||
- (id) lookupChild: (NSString *) childKey;
|
||||
- (NSArray *) childKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#import <SOGo/SOGoObject.h>
|
||||
|
||||
#import "MAPIStoreFolder.h"
|
||||
#import "MAPIStorePropertySelectors.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
@ -60,10 +61,35 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
|||
return newObject;
|
||||
}
|
||||
|
||||
+ (int) getAvailableProperties: (struct SPropTagArray *) properties
|
||||
{
|
||||
const MAPIStorePropertyGetter *classGetters;
|
||||
NSUInteger count;
|
||||
enum MAPITAGS propTag;
|
||||
uint16_t propValue;
|
||||
|
||||
properties->aulPropTag = talloc_array (properties, enum MAPITAGS,
|
||||
MAPIStoreSupportedPropertiesCount);
|
||||
classGetters = MAPIStorePropertyGettersForClass (self);
|
||||
for (count = 0; count < MAPIStoreSupportedPropertiesCount; count++)
|
||||
{
|
||||
propTag = MAPIStoreSupportedProperties[count];
|
||||
propValue = (propTag & 0xffff0000) >> 16;
|
||||
if (classGetters[propValue])
|
||||
{
|
||||
properties->aulPropTag[properties->cValues] = propTag;
|
||||
properties->cValues++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
classGetters = (IMP *) MAPIStorePropertyGettersForClass (isa);
|
||||
parentContainersBag = [NSMutableArray new];
|
||||
container = nil;
|
||||
sogoObject = nil;
|
||||
|
@ -190,53 +216,96 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
|||
- (int) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc;
|
||||
MAPIStorePropertyGetter method = NULL;
|
||||
uint16_t propValue;
|
||||
SEL methodSel;
|
||||
const char *propName;
|
||||
int rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
/* TODO: handle unsaved properties */
|
||||
propValue = (propTag & 0xffff0000) >> 16;
|
||||
methodSel = MAPIStoreSelectorForPropertyGetter (propValue);
|
||||
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
switch (propTag)
|
||||
method = (MAPIStorePropertyGetter) classGetters[propValue];
|
||||
if (method)
|
||||
rc = method (self, methodSel, data);
|
||||
else
|
||||
{
|
||||
case PR_DISPLAY_NAME_UNICODE:
|
||||
*data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_SEARCH_KEY: // TODO
|
||||
stringValue = [sogoObject nameInContainer];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_GENERATE_EXCHANGE_VIEWS: // TODO
|
||||
*data = MAPIBoolValue (memCtx, NO);
|
||||
break;
|
||||
|
||||
default:
|
||||
propName = get_proptag_name (propTag);
|
||||
if (!propName)
|
||||
propName = "<unknown>";
|
||||
[self warnWithFormat:
|
||||
@"unhandled or NULL value: %s (0x%.8x), childKey: %@",
|
||||
propName, propTag, [sogoObject nameInContainer]];
|
||||
// if ((propTag & 0x001F) == 0x001F)
|
||||
// {
|
||||
// stringValue = [NSString stringWithFormat: @"fake %s (0x.8x) value",
|
||||
// propName, propTag];
|
||||
// *data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
// rc = MAPISTORE_SUCCESS;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
*data = NULL;
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
// }
|
||||
break;
|
||||
*data = NULL;
|
||||
|
||||
if (methodSel)
|
||||
{
|
||||
propName = get_proptag_name (propTag);
|
||||
if (!propName)
|
||||
propName = "<unknown>";
|
||||
[self warnWithFormat:
|
||||
@"unimplemented selector (%@) for %s (0x%.8x)",
|
||||
NSStringFromSelector (methodSel), propName, propTag];
|
||||
}
|
||||
else
|
||||
[self warnWithFormat: @"unsupported property tag: 0x%.8x", propTag];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* MAPIStoreProperty protocol */
|
||||
/* helper getters */
|
||||
- (int) getEmptyString: (void **) data
|
||||
{
|
||||
*data = [@"" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getLongZero: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getYes: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, YES);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getNo: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, NO);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
/* getters */
|
||||
- (int) getPrDisplayName: (void **) data
|
||||
{
|
||||
*data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSearchKey: (void **) data
|
||||
{
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [sogoObject nameInContainer];
|
||||
*data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrGenerateExchangeViews: (void **) data
|
||||
{
|
||||
return [self getNo: data];
|
||||
}
|
||||
|
||||
- (int) getAvailableProperties: (struct SPropTagArray *) properties
|
||||
{
|
||||
return [isa getAvailableProperties: properties];
|
||||
}
|
||||
|
||||
- (int) getProperties: (struct mapistore_property_data *) data
|
||||
withTags: (enum MAPITAGS *) tags
|
||||
andCount: (uint16_t) columnCount
|
||||
|
@ -277,8 +346,6 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
|||
- (NSArray *) childKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
|
|
@ -632,68 +632,6 @@ sogo_op_setprops(void *private_data,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sogo_op_set_property_from_fd(void *private_data,
|
||||
uint64_t fmid, uint8_t type,
|
||||
uint32_t property, int fd)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
sogo_context *cContext;
|
||||
MAPIStoreContext *context;
|
||||
NSFileHandle *fileHandle;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
cContext = private_data;
|
||||
context = cContext->objcContext;
|
||||
[context setupRequest];
|
||||
|
||||
fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: fd
|
||||
closeOnDealloc: NO];
|
||||
rc = [context setProperty: property withFMID: fmid ofTableType: type
|
||||
fromFile: fileHandle];
|
||||
[fileHandle release];
|
||||
|
||||
[context tearDownRequest];
|
||||
[pool release];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sogo_op_get_property_into_fd(void *private_data,
|
||||
uint64_t fmid, uint8_t type,
|
||||
uint32_t property, int fd)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
sogo_context *cContext;
|
||||
MAPIStoreContext *context;
|
||||
NSFileHandle *fileHandle;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
cContext = private_data;
|
||||
context = cContext->objcContext;
|
||||
[context setupRequest];
|
||||
|
||||
fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: fd
|
||||
closeOnDealloc: NO];
|
||||
rc = [context getProperty: property withFMID: fmid ofTableType: type
|
||||
intoFile: fileHandle];
|
||||
[fileHandle release];
|
||||
|
||||
[context tearDownRequest];
|
||||
[pool release];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sogo_op_modifyrecipients(void *private_data,
|
||||
uint64_t mid,
|
||||
|
@ -1006,6 +944,30 @@ sogo_pocop_open_embedded_message (void *attachment_object,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sogo_pocop_get_available_table_properties(void *table_object, struct SPropTagArray *properties)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
|
||||
table = table_object;
|
||||
if (table)
|
||||
{
|
||||
pool = [NSAutoreleasePool new];
|
||||
rc = [table getAvailableProperties: properties];
|
||||
[pool release];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@" UNEXPECTED WEIRDNESS: RECEIVED NO DATA");
|
||||
rc = MAPI_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sogo_pocop_set_table_columns (void *table_object, uint16_t count, enum MAPITAGS *properties)
|
||||
{
|
||||
|
@ -1113,6 +1075,30 @@ sogo_pocop_get_table_row (void *table_object, enum table_query_type query_type,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sogo_pocop_get_available_properties(void *object, struct SPropTagArray *properties)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
|
||||
propObject = object;
|
||||
if (propObject)
|
||||
{
|
||||
pool = [NSAutoreleasePool new];
|
||||
rc = [propObject getAvailableProperties: properties];
|
||||
[pool release];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@" UNEXPECTED WEIRDNESS: RECEIVED NO DATA");
|
||||
rc = MAPI_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sogo_pocop_get_properties (void *object,
|
||||
uint16_t count, enum MAPITAGS *properties,
|
||||
|
@ -1243,8 +1229,6 @@ int mapistore_init_backend(void)
|
|||
|
||||
backend.op_setprops = sogo_op_setprops;
|
||||
backend.op_getprops = sogo_op_getprops;
|
||||
backend.op_set_property_from_fd = sogo_op_set_property_from_fd;
|
||||
backend.op_get_property_into_fd = sogo_op_get_property_into_fd;
|
||||
|
||||
/* proof of concept */
|
||||
backend.folder.open_table = sogo_pocop_open_table;
|
||||
|
@ -1252,10 +1236,12 @@ int mapistore_init_backend(void)
|
|||
backend.message.get_attachment = sogo_pocop_get_attachment;
|
||||
backend.message.create_attachment = sogo_pocop_create_attachment;
|
||||
backend.message.open_embedded_message = sogo_pocop_open_embedded_message;
|
||||
backend.table.get_available_properties = sogo_pocop_get_available_table_properties;
|
||||
backend.table.set_restrictions = sogo_pocop_set_table_restrictions;
|
||||
backend.table.set_sort_order = sogo_pocop_set_table_sort_order;
|
||||
backend.table.set_columns = sogo_pocop_set_table_columns;
|
||||
backend.table.get_row = sogo_pocop_get_table_row;
|
||||
backend.properties.get_available_properties = sogo_pocop_get_available_properties;
|
||||
backend.properties.get_properties = sogo_pocop_get_properties;
|
||||
backend.properties.set_properties = sogo_pocop_set_properties;
|
||||
backend.store.release = sogo_pocop_release;
|
||||
|
|
|
@ -68,6 +68,7 @@ typedef enum {
|
|||
}
|
||||
|
||||
+ (id) tableForContainer: (MAPIStoreObject *) newContainer;
|
||||
+ (Class) childObjectClass;
|
||||
|
||||
- (id) initForContainer: (MAPIStoreObject *) newContainer;
|
||||
|
||||
|
@ -81,6 +82,7 @@ typedef enum {
|
|||
|
||||
- (void) cleanupCaches;
|
||||
|
||||
- (int) getAvailableProperties: (struct SPropTagArray *) properties;
|
||||
- (void) setRestrictions: (const struct mapi_SRestriction *) res;
|
||||
- (int) setColumns: (enum MAPITAGS *) newColumns
|
||||
withCount: (uint16_t) newColumCount;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
#import "EOBitmaskQualifier.h"
|
||||
#import "MAPIStoreObject.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
@ -259,6 +260,13 @@ static Class NSDataK, NSStringK;
|
|||
return newTable;
|
||||
}
|
||||
|
||||
+ (Class) childObjectClass
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return Nil;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
|
@ -358,6 +366,11 @@ static Class NSDataK, NSStringK;
|
|||
currentRow = (uint32_t) -1;
|
||||
}
|
||||
|
||||
- (int) getAvailableProperties: (struct SPropTagArray *) properties
|
||||
{
|
||||
return [[isa childObjectClass] getAvailableProperties: properties];
|
||||
}
|
||||
|
||||
- (void) setRestrictions: (const struct mapi_SRestriction *) res
|
||||
{
|
||||
EOQualifier *oldRestriction;
|
||||
|
|
|
@ -25,7 +25,12 @@
|
|||
|
||||
#import "MAPIStoreGCSMessage.h"
|
||||
|
||||
@class iCalToDo;
|
||||
|
||||
@interface MAPIStoreTasksMessage : MAPIStoreGCSMessage
|
||||
{
|
||||
iCalToDo *task;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -49,221 +49,212 @@
|
|||
|
||||
@implementation MAPIStoreTasksMessage
|
||||
|
||||
- (enum MAPISTATUS) getProperty: (void **) data
|
||||
withTag: (enum MAPITAGS) propTag
|
||||
- (id) initWithSOGoObject: (id) newSOGoObject
|
||||
inContainer: (MAPIStoreObject *) newContainer
|
||||
{
|
||||
NSString *status;
|
||||
iCalToDo *task;
|
||||
int32_t statusValue;
|
||||
NSCalendarDate *dateValue;
|
||||
int rc;
|
||||
double doubleValue;
|
||||
|
||||
rc = MAPI_E_SUCCESS;
|
||||
switch ((uint32_t) propTag)
|
||||
if ((self = [super initWithSOGoObject: newSOGoObject
|
||||
inContainer: newContainer]))
|
||||
{
|
||||
case PR_ICON_INDEX: // TODO
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// Unassigned recurring task 0x00000501
|
||||
// Assignee's task 0x00000502
|
||||
// Assigner's task 0x00000503
|
||||
// Task request 0x00000504
|
||||
// Task acceptance 0x00000505
|
||||
// Task rejection 0x00000506
|
||||
*data = MAPILongValue (memCtx, 0x00000500);
|
||||
break;
|
||||
case PR_MESSAGE_CLASS_UNICODE:
|
||||
*data = talloc_strdup(memCtx, "IPM.Task");
|
||||
break;
|
||||
case PR_SUBJECT_UNICODE: // SUMMARY
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
*data = [[task summary] asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
//
|
||||
// Not to be confused with PR_PRIORITY
|
||||
//
|
||||
// IMPORTANCE_LOW = 0x0, IMPORTANCE_NORMAL = 0x1 and IMPORTANCE_HIGH = 0x2
|
||||
//
|
||||
case PR_IMPORTANCE:
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
|
||||
if ([[task priority] isEqualToString: @"9"])
|
||||
v = 0x0;
|
||||
else if ([[task priority] isEqualToString: @"1"])
|
||||
v = 0x2;
|
||||
else
|
||||
v = 0x1;
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
}
|
||||
break;
|
||||
case PidLidTaskComplete:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
*data = MAPIBoolValue (memCtx,
|
||||
[[task status] isEqualToString: @"COMPLETED"]);
|
||||
break;
|
||||
case PidLidPercentComplete:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
doubleValue = ((double) [[task percentComplete] intValue] / 100);
|
||||
*data = MAPIDoubleValue (memCtx, doubleValue);
|
||||
break;
|
||||
case PidLidTaskDateCompleted:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
dateValue = [task completed];
|
||||
if (dateValue)
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
break;
|
||||
// http://msdn.microsoft.com/en-us/library/cc765590.aspx
|
||||
// It's important to have a proper value for PidLidTaskState
|
||||
// as it'll affect the UI options of Outlook - making the
|
||||
// task non-editable in some cases.
|
||||
case PidLidTaskState:
|
||||
*data = MAPILongValue (memCtx, 0x1); // not assigned
|
||||
break;
|
||||
case PidLidTaskMode: // TODO
|
||||
*data = MAPILongValue (memCtx, 0x0);
|
||||
break;
|
||||
case PidLidTaskFRecurring:
|
||||
case PidLidTaskAccepted: // TODO
|
||||
*data = MAPIBoolValue (memCtx, NO);
|
||||
break;
|
||||
case PidLidTaskActualEffort: // TODO
|
||||
case PidLidTaskEstimatedEffort: // TODO
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
case PR_HASATTACH:
|
||||
*data = MAPIBoolValue (memCtx, NO);
|
||||
break;
|
||||
|
||||
case PidLidTaskDueDate:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
dateValue = [task due];
|
||||
if (dateValue)
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
break;
|
||||
|
||||
case PR_CREATION_TIME:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
*data = [[task created] asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
case PR_MESSAGE_DELIVERY_TIME:
|
||||
case PR_CLIENT_SUBMIT_TIME:
|
||||
case PR_LOCAL_COMMIT_TIME:
|
||||
case PR_LAST_MODIFICATION_TIME:
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
*data = [[task lastModified] asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
case PidLidTaskStatus: // status
|
||||
task = [sogoObject component: NO secure: NO];
|
||||
status = [task status];
|
||||
if (![status length]
|
||||
|| [status isEqualToString: @"NEEDS-ACTION"])
|
||||
statusValue = 0;
|
||||
else if ([status isEqualToString: @"IN-PROCESS"])
|
||||
statusValue = 1;
|
||||
else if ([status isEqualToString: @"COMPLETED"])
|
||||
statusValue = 2;
|
||||
else
|
||||
statusValue = 0xff;
|
||||
*data = MAPILongValue (memCtx, statusValue);
|
||||
break;
|
||||
/* Completed */
|
||||
// - 0x81380003 = -2000
|
||||
// + 0x81380003 = -4000
|
||||
|
||||
// 68330048
|
||||
// 68420102
|
||||
case 0x68340003:
|
||||
case 0x683a0003:
|
||||
case 0x68410003:
|
||||
*data = MAPILongValue (memCtx, 0);
|
||||
break;
|
||||
|
||||
// FIXME - use the current user
|
||||
case PidLidTaskOwner:
|
||||
*data = [@"openchange@example.com" asUnicodeInMemCtx: memCtx];
|
||||
break;
|
||||
|
||||
// See http://msdn.microsoft.com/en-us/library/cc842113.aspx
|
||||
case PidLidTaskOwnership:
|
||||
*data = MAPILongValue (memCtx, 0x0); // not assigned
|
||||
break;
|
||||
|
||||
// #define PidLidFlagRequest 0x9027001f
|
||||
// #define PidNameContentType 0x905a001f
|
||||
// #define PidLidBillingInformation 0x908b001f
|
||||
// #define PidLidTaskStartDate 0x911e0040
|
||||
// #define PidLidTaskOwner 0x9122001f
|
||||
// #define PidLidTaskDeadOccurrence 0x9127000b
|
||||
// #define PidLidTaskMultipleRecipients 0x912a0003
|
||||
// #define PidLidTaskHistory 0x912b0003
|
||||
// #define PidLidAcceptanceState 0x912c0003
|
||||
// #define PidLidTaskLastUser 0x912d001f
|
||||
// #define PidLidTaskLastUpdate 0x912e0040
|
||||
// #define PidLidTaskOwnership 0x912f0003
|
||||
// #define PidLidTaskNoCompute 0x9130000b
|
||||
// #define PidLidTaskFFixOffline 0x9131000b
|
||||
// #define PidLidTaskRole 0x9132001f
|
||||
// #define PidLidTaskVersion 0x91330003
|
||||
// #define PidLidTaskAssigner 0x9134001f
|
||||
// #define PidLidTeamTask 0x9135000b
|
||||
// #define PidLidTaskRecurrence 0x91360102
|
||||
// #define PidLidTaskResetReminder 0x9137000b
|
||||
// #define PidLidTaskOrdinal 0x91380003
|
||||
// #define PidLidMileage 0x914a001f
|
||||
// #define PidLidAgingDontAgeMe 0x9185000b
|
||||
// #define PidLidCommonEnd 0x91980040
|
||||
// #define PidLidCommonStart 0x91990040
|
||||
// #define PidLidNonSendableBcc 0x91d7001f
|
||||
// #define PidLidNonSendableCc 0x91d8001f
|
||||
// #define PidLidNonSendtableTo 0x91d9001f
|
||||
// #define PidLidNonSendBccTrackStatus 0x91da1003
|
||||
// #define PidLidNonSendCcTrackStatus 0x91db1003
|
||||
// #define PidLidNonSendToTrackStatus 0x91dc1003
|
||||
// #define PidLidReminderDelta 0x91e90003
|
||||
// #define PidLidReminderFileParameter 0x91ea001f
|
||||
// #define PidLidReminderSignalTime 0x91eb0040
|
||||
// #define PidLidReminderOverride 0x91ec000b
|
||||
// #define PidLidReminderPlaySound 0x91ed000b
|
||||
// #define PidLidReminderSet 0x91ee000b
|
||||
// #define PidLidReminderTime 0x91ef0040
|
||||
// #define PidLidReminderType 0x91f20003
|
||||
// #define PidLidRemoteStatus 0x91f30003
|
||||
// #define PidLidSmartNoAttach 0x91fa000b
|
||||
// #define PidLidTaskGlobalId 0x91fe0102
|
||||
// #define PidLidVerbResponse 0x9203001f
|
||||
// #define PidLidVerbStream 0x92040102
|
||||
// #define PidLidPrivate 0x9224000b
|
||||
// #define PidLidInternetAccountName 0x9225001f
|
||||
// #define PidLidInternetAccountStamp 0x9226001f
|
||||
// #define PidLidUseTnef 0x9227000b
|
||||
// #define PidLidContactLinkName 0x9229001f
|
||||
// #define PidLidContactLinkEntry 0x922a0102
|
||||
// #define PidLidContactLinkSearchKey 0x922b0102
|
||||
// #define PidLidSpamOriginalFolder 0x92370102
|
||||
// #define PidLidTaskUpdates 0x9345000b
|
||||
// #define PidLidTaskStatusOnComplete 0x9346000b
|
||||
// #define PidLidTaskLastDelegate 0x9347001f
|
||||
// #define PidLidTaskAssigners 0x934a0102
|
||||
// #define PidLidTaskFCreator 0x934c000b
|
||||
// #define PidLidImapDeleted 0x94e50003
|
||||
// #define PidLidHeaderItem 0x94e60003
|
||||
|
||||
default:
|
||||
rc = [super getProperty: data withTag: propTag];
|
||||
ASSIGN (task, [newSOGoObject component: NO secure: NO]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[task release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (int) getPrIconIndex: (void **) data // TODO
|
||||
{
|
||||
/* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */
|
||||
// Unassigned recurring task 0x00000501
|
||||
// Assignee's task 0x00000502
|
||||
// Assigner's task 0x00000503
|
||||
// Task request 0x00000504
|
||||
// Task acceptance 0x00000505
|
||||
// Task rejection 0x00000506
|
||||
*data = MAPILongValue (memCtx, 0x00000500);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageClass: (void **) data
|
||||
{
|
||||
*data = talloc_strdup(memCtx, "IPM.Task");
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrSubject: (void **) data // SUMMARY
|
||||
{
|
||||
*data = [[task summary] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrImportance: (void **) data
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if ([[task priority] isEqualToString: @"9"])
|
||||
v = 0x0;
|
||||
else if ([[task priority] isEqualToString: @"1"])
|
||||
v = 0x2;
|
||||
else
|
||||
v = 0x1;
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskComplete: (void **) data
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx,
|
||||
[[task status] isEqualToString: @"COMPLETED"]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidPercentComplete: (void **) data
|
||||
{
|
||||
double doubleValue;
|
||||
|
||||
doubleValue = ((double) [[task percentComplete] intValue] / 100);
|
||||
*data = MAPIDoubleValue (memCtx, doubleValue);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskDateCompleted: (void **) data
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
dateValue = [task completed];
|
||||
if (dateValue)
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskState: (void **) data
|
||||
{
|
||||
*data = MAPILongValue (memCtx, 0x1); // not assigned
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskMode: (void **) data // TODO
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskFRecurring: (void **) data
|
||||
{
|
||||
return [self getNo: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskAccepted: (void **) data // TODO
|
||||
{
|
||||
return [self getNo: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskActualEffort: (void **) data // TODO
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskEstimatedEffort: (void **) data // TODO
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (int) getPrHasattach: (void **) data
|
||||
{
|
||||
return [self getNo: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskDueDate: (void **) data
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
dateValue = [task due];
|
||||
if (dateValue)
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrCreationTime: (void **) data
|
||||
{
|
||||
*data = [[task created] asFileTimeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPrMessageDeliveryTime: (void **) data
|
||||
{
|
||||
*data = [[task lastModified] asFileTimeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getClientSubmitTime: (void **) data
|
||||
{
|
||||
return [self getPrMessageDeliveryTime: data];
|
||||
}
|
||||
|
||||
- (int) getLocalCommitTime: (void **) data
|
||||
{
|
||||
return [self getPrMessageDeliveryTime: data];
|
||||
}
|
||||
|
||||
- (int) getLastModificationTime: (void **) data
|
||||
{
|
||||
return [self getPrMessageDeliveryTime: data];
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskStatus: (void **) data // status
|
||||
{
|
||||
NSString *status;
|
||||
uint32_t longValue;
|
||||
|
||||
status = [task status];
|
||||
if (![status length]
|
||||
|| [status isEqualToString: @"NEEDS-ACTION"])
|
||||
longValue = 0;
|
||||
else if ([status isEqualToString: @"IN-PROCESS"])
|
||||
longValue = 1;
|
||||
else if ([status isEqualToString: @"COMPLETED"])
|
||||
longValue = 2;
|
||||
else
|
||||
longValue = 0xff;
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskOwner: (void **) data
|
||||
{
|
||||
*data = [@"openchange@example.com" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidTaskOwnership: (void **) data
|
||||
{
|
||||
return [self getLongZero: data];
|
||||
}
|
||||
|
||||
- (void) save
|
||||
{
|
||||
iCalCalendar *vCalendar;
|
||||
|
|
|
@ -38,7 +38,6 @@ double *MAPIDoubleValue (void *memCtx, double value);
|
|||
|
||||
id NSObjectFromSPropValue (const struct SPropValue *);
|
||||
id NSObjectFromMAPISPropValue (const struct mapi_SPropValue *);
|
||||
id NSObjectFromStreamData (enum MAPITAGS property, NSData *streamData);
|
||||
|
||||
static inline NSNumber *
|
||||
MAPIPropertyKey (enum MAPITAGS propTag)
|
||||
|
|
|
@ -116,6 +116,7 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value)
|
|||
result = [NSCalendarDate dateFromFileTime: &(value->value.ft)];
|
||||
break;
|
||||
case PT_BINARY:
|
||||
case PT_SVREID:
|
||||
result = [NSData dataWithShortBinary: &value->value.bin];
|
||||
break;
|
||||
case PT_CLSID:
|
||||
|
@ -175,6 +176,7 @@ NSObjectFromSPropValue (const struct SPropValue *value)
|
|||
result = [NSCalendarDate dateFromFileTime: &(value->value.ft)];
|
||||
break;
|
||||
case PT_BINARY:
|
||||
case PT_SVREID:
|
||||
// lpProps->value.bin = *((const struct Binary_r *)data);
|
||||
|
||||
result
|
||||
|
@ -204,36 +206,6 @@ NSObjectFromSPropValue (const struct SPropValue *value)
|
|||
return result;
|
||||
}
|
||||
|
||||
id NSObjectFromStreamData (enum MAPITAGS property, NSData* streamData)
|
||||
{
|
||||
short int valueType;
|
||||
id result;
|
||||
|
||||
valueType = (property & 0xffff);
|
||||
switch (valueType)
|
||||
{
|
||||
case PT_UNICODE:
|
||||
case PT_STRING8:
|
||||
result = [NSString stringWithCharacters: (unichar *) [streamData bytes]
|
||||
length: [streamData length] / 2];
|
||||
break;
|
||||
case PT_BINARY:
|
||||
result = streamData;
|
||||
break;
|
||||
case PT_OBJECT:
|
||||
result = [NSNull null];
|
||||
NSLog (@"%s: object type not handled: %d (0x%.4x)",
|
||||
__PRETTY_FUNCTION__, valueType, valueType);
|
||||
break;
|
||||
default:
|
||||
[NSException raise: @"MAPIStoreStreamTypeException"
|
||||
format: @"invalid data type"];
|
||||
result = nil;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
MAPIStoreDumpMessageProperties (NSDictionary *properties)
|
||||
{
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
*data = [(NSCalendarDate * ) self asFileTimeInMemCtx: memCtx];
|
||||
break;
|
||||
case PT_BINARY:
|
||||
*data = [(NSData *) self asShortBinaryInMemCtx: memCtx];
|
||||
*data = [(NSData *) self asBinaryInMemCtx: memCtx];
|
||||
break;
|
||||
case PT_CLSID:
|
||||
*data = [(NSData *) self asGUIDInMemCtx: memCtx];
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* code-MAPIStorePropertySelectors.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
|
||||
typedef int (*MAPIStorePropertyGetter) (id inst, SEL _cmd, void **data);
|
||||
|
||||
const MAPIStorePropertyGetter *MAPIStorePropertyGettersForClass (Class klass);
|
||||
SEL MAPIStoreSelectorForPropertyGetter (uint16_t propertyId);
|
|
@ -0,0 +1,69 @@
|
|||
/* code-MAPIStorePropertySelectors.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 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 2, 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/NSMapTable.h>
|
||||
#import <NGExtensions/NGLogger.h>
|
||||
|
||||
const MAPIStorePropertyGetter *
|
||||
MAPIStorePropertyGettersForClass (Class klass)
|
||||
{
|
||||
static NSMapTable *classesTable = nil;
|
||||
MAPIStorePropertyGetter *getters;
|
||||
MAPIStorePropertyGetter getter;
|
||||
uint16_t count, idx;
|
||||
SEL currentSel;
|
||||
|
||||
if (!classesTable)
|
||||
classesTable = NSCreateMapTable (NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSNonOwnedPointerMapValueCallBacks,
|
||||
0);
|
||||
|
||||
getters = NSMapGet (classesTable, klass);
|
||||
if (!getters)
|
||||
{
|
||||
getters = NSZoneCalloc (NULL, 65536, sizeof (MAPIStorePropertyGetter));
|
||||
for (count = 0; count < 65535; count++)
|
||||
{
|
||||
idx = MAPIStorePropertyGettersIdx[count];
|
||||
if (idx != 0xffff && !getters[count])
|
||||
{
|
||||
currentSel = MAPIStorePropertyGetterSelectors[idx];
|
||||
if ([klass instancesRespondToSelector: currentSel])
|
||||
{
|
||||
getter = (MAPIStorePropertyGetter)
|
||||
[klass instanceMethodForSelector: currentSel];
|
||||
if (getter)
|
||||
getters[count] = getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getters;
|
||||
}
|
||||
|
||||
SEL MAPIStoreSelectorForPropertyGetter (uint16_t propertyId)
|
||||
{
|
||||
return ((MAPIStorePropertyGettersIdx[propertyId] != 0xffff)
|
||||
? MAPIStorePropertyGetterSelectors[MAPIStorePropertyGettersIdx[propertyId]]
|
||||
: NULL);
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
include_dirs = [ "/usr/include" ]
|
||||
|
||||
output = "-"
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
m_template = """/* %(filename)s (auto-generated) - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
|
||||
#include <objc/objc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#import "%(h_filename)s"
|
||||
|
||||
const NSUInteger MAPIStorePropertyGettersCount = %(nbr_getters)d;
|
||||
const NSUInteger MAPIStoreLastPropertyIdx = %(last_property)d;
|
||||
const NSUInteger MAPIStoreSupportedPropertiesCount = %(nbr_supported_properties)d;
|
||||
|
||||
const enum MAPITAGS MAPIStoreSupportedProperties[] = {
|
||||
%(supported_properties)s
|
||||
};
|
||||
|
||||
static const uint16_t MAPIStorePropertyGettersIdx[] = {
|
||||
%(getters_idx)s
|
||||
};
|
||||
|
||||
static const SEL MAPIStorePropertyGetterSelectors[] = {
|
||||
%(getters)s
|
||||
};
|
||||
|
||||
#include "code-%(filename)s"
|
||||
"""
|
||||
|
||||
h_template = """/* %(filename)s (auto-generated) - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
|
||||
#ifndef %(h_exclusion)s
|
||||
#define %(h_exclusion)s 1
|
||||
|
||||
#import <Foundation/NSObjCRuntime.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <gen_ndr/exchange.h>
|
||||
|
||||
extern const NSUInteger MAPIStorePropertyGettersCount;
|
||||
extern const NSUInteger MAPIStoreLastPropertyIdx;
|
||||
|
||||
extern const NSUInteger MAPIStoreSupportedPropertiesCount;
|
||||
extern const enum MAPITAGS MAPIStoreSupportedProperties[];
|
||||
|
||||
#import "MAPIStoreObject.h"
|
||||
|
||||
@interface MAPIStoreObject (MAPIStorePropertySelectors)
|
||||
|
||||
%(prototypes)s
|
||||
|
||||
@end
|
||||
|
||||
#include "code-%(filename)s"
|
||||
|
||||
#endif /* %(h_exclusion)s */
|
||||
"""
|
||||
|
||||
def ParseExchangeH(names, lines):
|
||||
state = 0
|
||||
maxlines = len(lines)
|
||||
x = 0
|
||||
while x < maxlines and state != 3:
|
||||
stripped = lines[x].strip()
|
||||
if state == 0:
|
||||
if stripped == "enum MAPITAGS":
|
||||
state = 1
|
||||
elif state == 1:
|
||||
if stripped == "{":
|
||||
state = 2
|
||||
elif state == 2:
|
||||
if stripped == "}":
|
||||
state = 3
|
||||
else:
|
||||
ParseExchangeHDefinition(names, stripped)
|
||||
x = x + 1
|
||||
|
||||
def GenExchangeHName(startname):
|
||||
parts = startname.split("_")
|
||||
newParts = []
|
||||
for part in parts:
|
||||
newParts.append("%s%s" % (part[0].upper(), part[1:].lower()))
|
||||
|
||||
return "".join(newParts)
|
||||
|
||||
def ParseExchangeHDefinition(names, line):
|
||||
stripped = line.strip()
|
||||
eqIdx = stripped.find("=")
|
||||
if eqIdx == -1:
|
||||
raise Exception, "line does not contain a '='"
|
||||
propName = GenExchangeHName(stripped[0:eqIdx])
|
||||
if not propName.endswith("_ERROR") and not propName.endswith("_UNICODE"):
|
||||
intIdx = stripped.find("(int", eqIdx)
|
||||
valueIdx = stripped.find("0x", intIdx + 1)
|
||||
endIdx = stripped.find(")", valueIdx)
|
||||
value = int(stripped[valueIdx:endIdx], 16)
|
||||
|
||||
names[propName] = value
|
||||
|
||||
def ParseMapistoreNameIDH(names, lines):
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("#define Pid"):
|
||||
ParseMapistoreNameIDHDefinition(names, stripped)
|
||||
|
||||
def ParseMapistoreNameIDHDefinition(names, line):
|
||||
stripped = line.strip()
|
||||
pidIdx = stripped.find("Pid")
|
||||
if pidIdx == -1:
|
||||
raise Exception, "line does not contain a 'Pid'"
|
||||
valueIdx = stripped.find("0x")
|
||||
propName = stripped[pidIdx:valueIdx].strip()
|
||||
value = int(stripped[valueIdx:], 16)
|
||||
|
||||
names[propName] = value
|
||||
|
||||
def FindHFile(filename):
|
||||
found = None
|
||||
|
||||
for dirname in include_dirs:
|
||||
full_filename = "%s/%s" % (dirname, filename)
|
||||
if os.path.exists(full_filename):
|
||||
found = full_filename
|
||||
|
||||
if found is None:
|
||||
raise Exception, "'%s' not found in include dirs" % filename
|
||||
|
||||
return found
|
||||
|
||||
def ProcessHeaders(names, hdict):
|
||||
for filename in hdict.keys():
|
||||
header_filename = FindHFile(filename)
|
||||
header_file = open(header_filename, "r")
|
||||
lines = header_file.readlines()
|
||||
header_file.close()
|
||||
hdict[filename](names, lines)
|
||||
|
||||
if __name__ == "__main__":
|
||||
arg_count = len(sys.argv)
|
||||
x = 0
|
||||
while x < arg_count:
|
||||
arg = sys.argv[x]
|
||||
argname = None
|
||||
if arg.startswith("-"):
|
||||
argname = arg[1]
|
||||
if len(arg) == 2:
|
||||
argvalue = sys.argv[x + 1]
|
||||
x = x + 1
|
||||
else:
|
||||
argvalue = arg[2:]
|
||||
x = x + 1
|
||||
|
||||
if argname == "o":
|
||||
output = argvalue
|
||||
elif argname == "I":
|
||||
include_dirs.append(argvalue)
|
||||
|
||||
names = {}
|
||||
ProcessHeaders(names,
|
||||
{ "gen_ndr/exchange.h": ParseExchangeH,
|
||||
"mapistore/mapistore_nameid.h": ParseMapistoreNameIDH })
|
||||
|
||||
getters = []
|
||||
getters_idx = []
|
||||
# setters = []
|
||||
# preferred_types = []
|
||||
prototypes = []
|
||||
|
||||
for x in xrange(0x10000):
|
||||
getters_idx.append(" 0xffff")
|
||||
# setters.append(" NULL")
|
||||
|
||||
all_keys = names.keys()
|
||||
for name in all_keys:
|
||||
prop_tag = names[name]
|
||||
if (prop_tag & 0x001e):
|
||||
prop_tag = (prop_tag & 0xffff0000) | 0x001f
|
||||
names[name] = prop_tag
|
||||
if (name.endswith("Error")
|
||||
or name.endswith("Unicode")):
|
||||
del names[name]
|
||||
|
||||
supported_properties = []
|
||||
all_keys = names.keys()
|
||||
current_getter_idx = 0
|
||||
highest_prop_idx = 0
|
||||
for name in all_keys:
|
||||
prop_tag = names[name]
|
||||
supported_properties.append(" 0x%.8x" % prop_tag);
|
||||
prop_idx = (prop_tag & 0xffff0000) >> 16
|
||||
getters_idx[prop_idx] = " %d" % current_getter_idx
|
||||
if prop_idx > highest_prop_idx:
|
||||
highest_prop_idx = prop_idx
|
||||
getters.append(" @selector (get%s:)" % name)
|
||||
# preferred_types.append(" 0x%.4x" % (prop_tag & 0xffff))
|
||||
prototypes.append("- (int) get%s: (void **) data;" % name)
|
||||
current_getter_idx = current_getter_idx + 1
|
||||
# setters[prop_idx] = " @selector (set%s:)" % name
|
||||
# prototypes.append("- (int) set%s: (void **) data;" % name)
|
||||
# prototypes.append("")
|
||||
|
||||
filename = "%s.m" % output
|
||||
h_filename = "%s.h" % output
|
||||
outf = open(filename, "wb+")
|
||||
outf.write(m_template % { "getters_idx": ",\n".join(getters_idx),
|
||||
"getters": ",\n".join(getters),
|
||||
"nbr_getters": len(getters),
|
||||
"last_property": highest_prop_idx,
|
||||
"nbr_supported_properties": len(supported_properties),
|
||||
"supported_properties": ",\n".join(supported_properties),
|
||||
"filename": filename,
|
||||
"h_filename": h_filename })
|
||||
outf.close()
|
||||
|
||||
outf = open(h_filename, "wb+")
|
||||
exclusion = ""
|
||||
for x in h_filename.upper():
|
||||
if ord(x) < 65 or ord(x) > 90:
|
||||
x = "_"
|
||||
exclusion = exclusion + x
|
||||
outf.write(h_template % { "prototypes": "\n".join(prototypes),
|
||||
"h_exclusion": exclusion,
|
||||
"filename": h_filename })
|
||||
outf.close()
|
|
@ -51,6 +51,7 @@
|
|||
- (BOOL) hasEndDate;
|
||||
|
||||
- (NSString *) duration;
|
||||
- (void) setDuration: (NSString *) _value;
|
||||
- (BOOL) hasDuration;
|
||||
- (NSTimeInterval) durationAsTimeInterval;
|
||||
|
||||
|
|
|
@ -60,3 +60,5 @@ vtodo_class2 = "(Tasca confidencial)";
|
|||
= "%{Attendee} %{SentByText}ha delegat la invitació en %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}encara no s'ha decidit sobre la invitació a l'esdeveniment.";
|
||||
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Nombre màxim de reserves simultànies (%{NumberOfSimultaneousBookings}) assolit per al recurs \"%{Cn}{%SystemEmail}\".";
|
||||
|
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Confidential task)";
|
|||
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\".";
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Tâche confidentielle)";
|
|||
= "%{Attendee} %{SentByText}a délégué votre invitation à %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}choisit de reporter sa décision par rapport à votre invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Le nombre maximum (%{NumberOfSimultaneousBookings}) de réservation(s) simultanée(s) a été atteint pour la ressource \"%{Cn} %{SystemEmail}\".";
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
|||
= "%{Attendee} %{SentByText}hat die Einladung delegiert an %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}hat noch nicht über Ihre Termineinladung entschieden.";
|
||||
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maximale Zahl simultaner Buchungen (%{NumberOfSimultaneousBookings}) erreicht für Ressource \"%{Cn} %{SystemEmail}\".";
|
||||
|
||||
|
|
|
@ -60,3 +60,5 @@ vtodo_class2 = "(Bizalmas feladat)";
|
|||
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}még meggondolja a meghívását.";
|
||||
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Az egyidejű foglalások száma (%{NumberOfSimultaneousBookings}) elérte a maximumot \"%{Cn} %{SystemEmail}\".";
|
||||
|
|
|
@ -60,3 +60,5 @@ vtodo_class2 = "(Confidential task)";
|
|||
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}не определился с желанием участвовать в запланированном мероприятии.";
|
||||
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Уже подано максимальное количество заявок (%{NumberOfSimultaneousBookings}) на ресурс \"%{Cn} %{SystemEmail}\".";
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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 Lesser General Public
|
||||
License for more details.
|
||||
|
@ -73,6 +73,7 @@
|
|||
#import <SOGo/WOResponse+SOGo.h>
|
||||
|
||||
#import "iCalRepeatableEntityObject+SOGo.h"
|
||||
#import "iCalEvent+SOGo.h"
|
||||
#import "iCalPerson+SOGo.h"
|
||||
#import "SOGoAppointmentObject.h"
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
|
@ -622,6 +623,9 @@ static NSNumber *sharedYes = nil;
|
|||
return record;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSArray *) fixupRecords: (NSArray *) theRecords
|
||||
{
|
||||
// TODO: is the result supposed to be sorted by date?
|
||||
|
@ -801,6 +805,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
[self _fixExceptionRecord: newRecord fromRow: row];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _appendCycleExceptionsFromRow: (NSDictionary *) row
|
||||
firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
forRange: (NGCalendarDateRange *) dateRange
|
||||
|
@ -849,9 +856,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
NSArray *rules, *exRules, *exDates, *ranges;
|
||||
NSArray *elements, *components;
|
||||
NSString *content;
|
||||
iCalRepeatableEntityObject *component;
|
||||
id firstStartDate, firstEndDate;
|
||||
NSCalendarDate *checkStartDate, *checkEndDate;
|
||||
iCalDateTime *dtstart;
|
||||
NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, *firstEndDate;
|
||||
iCalEvent *component;
|
||||
iCalTimeZone *eventTimeZone;
|
||||
unsigned count, max, offset;
|
||||
|
||||
|
@ -889,53 +896,55 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
{
|
||||
// Retrieve the range of the first/master event
|
||||
component = [components objectAtIndex: 0];
|
||||
firstStartDate = [component uniqueChildWithTag: @"dtstart"];
|
||||
firstEndDate = [component uniqueChildWithTag: @"dtend"];
|
||||
eventTimeZone = [(iCalDateTime*)firstStartDate timeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate]
|
||||
endDate: [[[firstEndDate values] lastObject] asCalendarDate]];
|
||||
dtstart = (iCalDateTime *)[component uniqueChildWithTag: @"dtstart"];
|
||||
firstStartDate = [[[dtstart values] lastObject] asCalendarDate];
|
||||
firstEndDate = [firstStartDate addTimeInterval: [component occurenceInterval]];
|
||||
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate
|
||||
endDate: firstEndDate];
|
||||
|
||||
if (eventTimeZone)
|
||||
{
|
||||
// Adjust the range to check with respect to the event timezone (extracted from the start date)
|
||||
checkStartDate = [eventTimeZone computedDateForDate: [theRange startDate]];
|
||||
checkEndDate = [eventTimeZone computedDateForDate: [theRange endDate]];
|
||||
recurrenceRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate
|
||||
endDate: checkEndDate];
|
||||
|
||||
// Adjust the exception dates
|
||||
exDates = [eventTimeZone computedDatesForStrings: exDates];
|
||||
|
||||
// Adjust the recurrence rules "until" dates
|
||||
rules = [component recurrenceRulesWithTimeZone: eventTimeZone];
|
||||
exRules = [component exceptionRulesWithTimeZone: eventTimeZone];
|
||||
}
|
||||
else
|
||||
{
|
||||
recurrenceRange = theRange;
|
||||
if ([[theRecord objectForKey: @"c_isallday"] boolValue])
|
||||
{
|
||||
// The event lasts all-day and has no timezone (floating); we convert the range of the first event
|
||||
// to the user's timezone
|
||||
offset = [timeZone secondsFromGMTForDate: [firstRange startDate]];
|
||||
firstStartDate = (NSCalendarDate*)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
firstEndDate = (NSCalendarDate*)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
[firstStartDate setTimeZone: timeZone];
|
||||
[firstEndDate setTimeZone: timeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate
|
||||
endDate: firstEndDate];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the occurrences for the given range
|
||||
eventTimeZone = [dtstart timeZone];
|
||||
if (eventTimeZone)
|
||||
{
|
||||
// Adjust the range to check with respect to the event timezone (extracted from the start date)
|
||||
checkStartDate = [eventTimeZone computedDateForDate: [theRange startDate]];
|
||||
checkEndDate = [eventTimeZone computedDateForDate: [theRange endDate]];
|
||||
recurrenceRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate
|
||||
endDate: checkEndDate];
|
||||
|
||||
// Adjust the exception dates
|
||||
exDates = [eventTimeZone computedDatesForStrings: exDates];
|
||||
|
||||
// Adjust the recurrence rules "until" dates
|
||||
rules = [component recurrenceRulesWithTimeZone: eventTimeZone];
|
||||
exRules = [component exceptionRulesWithTimeZone: eventTimeZone];
|
||||
}
|
||||
else
|
||||
{
|
||||
recurrenceRange = theRange;
|
||||
if ([[theRecord objectForKey: @"c_isallday"] boolValue])
|
||||
{
|
||||
// The event lasts all-day and has no timezone (floating); we convert the range of the first event
|
||||
// to the user's timezone
|
||||
offset = [timeZone secondsFromGMTForDate: [firstRange startDate]];
|
||||
firstStartDate = [[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
firstEndDate = [[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
[firstStartDate setTimeZone: timeZone];
|
||||
[firstEndDate setTimeZone: timeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate
|
||||
endDate: firstEndDate];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the occurrences for the given range
|
||||
records = [NSMutableArray array];
|
||||
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
recurrenceRules: rules
|
||||
exceptionRules: exRules
|
||||
exceptionDates: exDates];
|
||||
exceptionRules: exRules
|
||||
exceptionDates: exDates];
|
||||
max = [ranges count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
|
@ -947,15 +956,15 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
if (fixedRow)
|
||||
[records addObject: fixedRow];
|
||||
}
|
||||
|
||||
|
||||
[self _appendCycleExceptionsFromRow: row
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
forRange: theRange
|
||||
toArray: records];
|
||||
|
||||
|
||||
[theRecords addObjectsFromArray: records];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat:@"cyclic record doesn't have content -> %@", theRecord];
|
||||
|
@ -1504,7 +1513,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
|
||||
order = [[container subFolders] indexOfObject: self];
|
||||
|
||||
return [NSString stringWithFormat: @"%d", order];
|
||||
return [NSString stringWithFormat: @"%d", order+1];
|
||||
}
|
||||
|
||||
- (NSException *) setDavCalendarOrder: (NSString *) newColor
|
||||
|
@ -2607,10 +2616,21 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
return ([object saveContentString: content] == nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all components of a vCalendar.
|
||||
* @param calendar the calendar to import
|
||||
* @return the number of components imported
|
||||
*/
|
||||
- (int) importCalendar: (iCalCalendar *) calendar
|
||||
{
|
||||
NSArray *vtimezones;
|
||||
NSMutableArray *components;
|
||||
NSMutableDictionary *timezones;
|
||||
NSString *tz;
|
||||
iCalEntityObject *element;
|
||||
iCalDateTime *startDate;
|
||||
iCalTimeZone *timezone;
|
||||
iCalEvent *event;
|
||||
|
||||
int imported, count, i;
|
||||
|
||||
|
@ -2618,25 +2638,55 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
|||
|
||||
if (calendar)
|
||||
{
|
||||
// Build a hash with the timezones includes in the calendar
|
||||
vtimezones = [calendar timezones];
|
||||
count = [vtimezones count];
|
||||
timezones = [NSMutableDictionary dictionaryWithCapacity: count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
timezone = (iCalTimeZone *)[vtimezones objectAtIndex: i];
|
||||
[timezones setValue: [NSString stringWithFormat: @"%@\n", [timezone versitString]]
|
||||
forKey: [timezone tzId]];
|
||||
}
|
||||
|
||||
// Parse events/todos/journals and import them
|
||||
components = [[calendar events] mutableCopy];
|
||||
[components autorelease];
|
||||
[components addObjectsFromArray: [calendar todos]];
|
||||
[components addObjectsFromArray: [calendar journals]];
|
||||
[components addObjectsFromArray: [calendar freeBusys]];
|
||||
|
||||
#warning FIXME we might want to eventually support multiple timezone definitions and match the one used by the event
|
||||
if ([[calendar timezones] count])
|
||||
tz = [NSString stringWithFormat: @"%@\n",
|
||||
[[[calendar timezones] lastObject] versitString]];
|
||||
else
|
||||
tz = @"";
|
||||
|
||||
count = [components count];
|
||||
for (i = 0; i < count; i++)
|
||||
if ([self importComponent: [components objectAtIndex: i] timezone: tz])
|
||||
imported++;
|
||||
{
|
||||
tz = nil;
|
||||
element = [components objectAtIndex: i];
|
||||
// Use the timezone of the start date.
|
||||
startDate = (iCalDateTime *)[element uniqueChildWithTag: @"dtstart"];
|
||||
if (startDate)
|
||||
{
|
||||
timezone = [startDate timeZone];
|
||||
tz = [timezones valueForKey: [timezone tzId]];
|
||||
if ([element isKindOfClass: [iCalEvent class]])
|
||||
{
|
||||
event = (iCalEvent *)element;
|
||||
if (![event hasEndDate] && ![event hasDuration])
|
||||
{
|
||||
// No end date, no duration
|
||||
if ([event isAllDay])
|
||||
[event setDuration: @"P1D"];
|
||||
else
|
||||
[event setDuration: @"PT1H"];
|
||||
|
||||
[self errorWithFormat: @"Importing event with no end date; setting duration to %@", [event duration]];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([self importComponent: element
|
||||
timezone: (tz == nil? @"" : tz)])
|
||||
imported++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2007-2010 Inverse inc.
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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 Lesser General Public
|
||||
License for more details.
|
||||
|
@ -25,17 +24,6 @@
|
|||
|
||||
#import <SOGo/SOGoContentObject.h>
|
||||
|
||||
/*
|
||||
SOGoAppointmentObject
|
||||
|
||||
Represents a single appointment. This SOPE controller object manages all the
|
||||
attendee storages (that is, it might store into multiple folders for meeting
|
||||
appointments!).
|
||||
|
||||
Note: SOGoAppointmentObject do not need to exist yet. They can also be "new"
|
||||
appointments with an externally generated unique key.
|
||||
*/
|
||||
|
||||
@class NSArray;
|
||||
@class NSException;
|
||||
@class NSString;
|
||||
|
@ -61,13 +49,6 @@
|
|||
- (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients from: (NSString *) originator;
|
||||
- (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients from: (NSString *) originator;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
// - (NSException *) saveContentString: (NSString *) _iCal
|
||||
// baseSequence: (int) _v;
|
||||
// - (NSException *) deleteWithBaseSequence: (int) _v;
|
||||
// - (NSException *) saveContentString: (NSString *) _iCalString;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __Appointments_SOGoAppointmentObject_H__ */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo
|
||||
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
|
@ -42,6 +42,7 @@
|
|||
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
|
@ -198,7 +199,6 @@
|
|||
// We check if the attendee that was added to a single occurence is
|
||||
// present in the master component. If not, we add it with a participation
|
||||
// status set to "DECLINED".
|
||||
|
||||
user = [SOGoUser userWithLogin: theUID];
|
||||
person = [iCalPerson elementWithTag: @"attendee"];
|
||||
[person setCn: [user cn]];
|
||||
|
@ -425,14 +425,117 @@
|
|||
}
|
||||
|
||||
//
|
||||
// This methods scans the list of attendees. If they are
|
||||
// considered as resource, it checks for conflicting
|
||||
// dates for the event.
|
||||
//
|
||||
// We check for between startDate + 1 second and
|
||||
// endDate - 1 second
|
||||
//
|
||||
//
|
||||
- (void) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
// It also CHANGES the participation status of resources
|
||||
// depending on constraints defined on them.
|
||||
//
|
||||
// Note that it doesn't matter if it changes the participation
|
||||
// status since in case of an error, nothing will get saved.
|
||||
//
|
||||
- (NSException *) _handleResourcesConflicts: (NSArray *) theAttendees
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID;
|
||||
SOGoUser *user;
|
||||
|
||||
enumerator = [theAttendees objectEnumerator];
|
||||
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
|
||||
if (currentUID)
|
||||
{
|
||||
user = [SOGoUser userWithLogin: currentUID];
|
||||
|
||||
if ([user isResource])
|
||||
{
|
||||
SOGoAppointmentFolder *folder;
|
||||
NSCalendarDate *start, *end;
|
||||
NSMutableArray *fbInfo;
|
||||
int i;
|
||||
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [[SOGoUser userWithLogin: currentUID]
|
||||
personalCalendarFolderInContext: context];
|
||||
|
||||
|
||||
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
|
||||
to: end]];
|
||||
|
||||
// We first remove any occurences in the freebusy that corresponds to the
|
||||
// current event. We do this to avoid raising a conflict if we move a 1 hour
|
||||
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
|
||||
for (i = [fbInfo count]-1; i >= 0; i--)
|
||||
{
|
||||
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
else
|
||||
{
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
nil];
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"."]];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus:403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSException *) _handleAddedUsers: (NSArray *) attendees
|
||||
fromEvent: (iCalEvent *) newEvent
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID;
|
||||
NSException *e;
|
||||
|
||||
// We check for conflicts
|
||||
if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent]))
|
||||
return e;
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
|
@ -443,25 +546,31 @@
|
|||
forUID: currentUID
|
||||
owner: owner];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
- (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
|
||||
fromOldEvent: (iCalEvent *) oldEvent
|
||||
{
|
||||
NSArray *attendees;
|
||||
iCalEventChanges *changes;
|
||||
NSArray *attendees;
|
||||
NSException *ex;
|
||||
|
||||
changes = [newEvent getChangesRelativeToEvent: oldEvent];
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
// Set new attendees status to "needs action" and recompute changes when
|
||||
// the list of attendees has changed.
|
||||
// the list of attendees has changed. The list might have changed since
|
||||
// by changing a major property of the event, we remove all the delegation
|
||||
// chains to "other" attendees
|
||||
if ([self _requireResponseFromAttendees: newEvent])
|
||||
changes = [newEvent getChangesRelativeToEvent: oldEvent];
|
||||
}
|
||||
|
||||
attendees = [changes deletedAttendees];
|
||||
if ([attendees count])
|
||||
{
|
||||
|
@ -475,6 +584,10 @@
|
|||
forObject: newEvent to: attendees];
|
||||
}
|
||||
|
||||
if ((ex = [self _handleResourcesConflicts: [newEvent attendees]
|
||||
forEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
attendees = [changes insertedAttendees];
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
|
@ -516,7 +629,9 @@
|
|||
if ([attendees count])
|
||||
{
|
||||
// Send an invitation to new attendees
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
previousObject: oldEvent
|
||||
|
@ -524,6 +639,8 @@
|
|||
[self sendReceiptEmailUsingTemplateNamed: @"Invitation"
|
||||
forObject: newEvent to: attendees];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -551,14 +668,15 @@
|
|||
// _updateAttendee:withDelegate:ownerUser:forEventUID:withRecurrenceId:withSequence:forUID:shouldAddSentBy:
|
||||
//
|
||||
//
|
||||
- (void) saveComponent: (iCalEvent *) newEvent
|
||||
- (NSException *) saveComponent: (iCalEvent *) newEvent
|
||||
{
|
||||
iCalEvent *oldEvent, *oldMasterEvent;
|
||||
NSArray *attendees;
|
||||
NSCalendarDate *recurrenceId;
|
||||
NSString *recurrenceTime;
|
||||
SOGoUser *ownerUser;
|
||||
|
||||
NSArray *attendees;
|
||||
NSException *ex;
|
||||
|
||||
[[newEvent parent] setMethod: @""];
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
|
||||
|
@ -574,7 +692,11 @@
|
|||
attendees = [newEvent attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleAddedUsers: attendees fromEvent: newEvent];
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
previousObject: nil
|
||||
|
@ -586,6 +708,7 @@
|
|||
else
|
||||
{
|
||||
BOOL hasOrganizer;
|
||||
|
||||
// Event is modified -- sent update status to all attendees
|
||||
// and modify their calendars.
|
||||
recurrenceId = [newEvent recurrenceId];
|
||||
|
@ -606,8 +729,10 @@
|
|||
hasOrganizer = [[[oldMasterEvent organizer] email] length];
|
||||
|
||||
if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser])
|
||||
// The owner is the organizer of the event; handle the modifications
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
// The owner is the organizer of the event; handle the modifications. We aslo
|
||||
// catch conflicts just like when the events are created
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
return ex;
|
||||
}
|
||||
|
||||
[super saveComponent: newEvent];
|
||||
|
@ -618,6 +743,8 @@
|
|||
safeCalendar = nil;
|
||||
[originalCalendar release];
|
||||
originalCalendar = nil;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1336,6 +1463,44 @@
|
|||
[rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify vCalendar for any inconsistency or missing attributes.
|
||||
* Currently only check if the events have an end date or a duration.
|
||||
* @param rq the HTTP PUT request
|
||||
*/
|
||||
- (void) _adjustEventsInRequest: (WORequest *) rq
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSArray *allEvents;
|
||||
iCalEvent *event;
|
||||
NSUInteger i;
|
||||
BOOL modified;
|
||||
|
||||
calendar = [iCalCalendar parseSingleFromSource: [rq contentAsString]];
|
||||
allEvents = [calendar events];
|
||||
modified = NO;
|
||||
|
||||
for (i = 0; i < [allEvents count]; i++)
|
||||
{
|
||||
event = [allEvents objectAtIndex: i];
|
||||
|
||||
if (![event hasEndDate] && ![event hasDuration])
|
||||
{
|
||||
// No end date, no duration
|
||||
if ([event isAllDay])
|
||||
[event setDuration: @"P1D"];
|
||||
else
|
||||
[event setDuration: @"PT1H"];
|
||||
|
||||
modified = YES;
|
||||
[self errorWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
|
||||
}
|
||||
}
|
||||
|
||||
if (modified)
|
||||
[rq setContent: [[calendar versitString] dataUsingEncoding: [rq contentEncoding]]];
|
||||
}
|
||||
|
||||
- (void) _decomposeGroupsInRequest: (WORequest *) rq
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
|
@ -1441,6 +1606,7 @@
|
|||
//
|
||||
- (id) PUTAction: (WOContext *) _ctx
|
||||
{
|
||||
NSException *ex;
|
||||
NSArray *roles;
|
||||
WORequest *rq;
|
||||
id response;
|
||||
|
@ -1471,6 +1637,8 @@
|
|||
{
|
||||
[self _adjustTransparencyInRequest: rq];
|
||||
}
|
||||
|
||||
[self _adjustEventsInRequest: rq];
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1500,7 +1668,9 @@
|
|||
attendees = [event attendeesWithoutUser: ownerUser];
|
||||
if ([attendees count])
|
||||
{
|
||||
[self _handleAddedUsers: attendees fromEvent: event];
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: event]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [event itipEntryWithMethod: @"request"]
|
||||
previousObject: nil
|
||||
|
@ -1640,8 +1810,9 @@
|
|||
|
||||
if ([uid caseInsensitiveCompare: owner] == NSOrderedSame)
|
||||
{
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
|
||||
if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent]))
|
||||
return ex;
|
||||
|
||||
// A RECURRENCE-ID was removed so there has to be a change in the master event
|
||||
// We could also have an EXDATE added in the master component of the attendees
|
||||
// so we always compare the MASTER event.
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
toFolder: (SOGoGCSFolder *) newFolder;
|
||||
|
||||
- (void) updateComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject;
|
||||
|
||||
/* mail notifications */
|
||||
- (void) sendEMailUsingTemplateNamed: (NSString *) pageName
|
||||
|
|
|
@ -655,13 +655,15 @@ static inline BOOL _occurenceHasID (iCalRepeatableEntityObject *occurence,
|
|||
}
|
||||
}
|
||||
|
||||
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||
- (NSException *) saveComponent: (iCalRepeatableEntityObject *) newObject
|
||||
{
|
||||
NSString *newiCalString;
|
||||
|
||||
newiCalString = [[newObject parent] versitString];
|
||||
|
||||
[self saveContentString: newiCalString];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* raw saving */
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2000-2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OGo
|
||||
This file is part of SOGo
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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 Lesser General Public
|
||||
License for more details.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2000-2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OGo
|
||||
This file is part of SOGo
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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 Lesser General Public
|
||||
License for more details.
|
||||
|
|
|
@ -60,3 +60,6 @@ vtodo_class2 = "(Tarea confidencial)";
|
|||
= "%{Attendee} %{SentByText}has delegado la invitación para %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}no ha tomado tadavia una decision sobre esta cita.";
|
||||
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Número máximo de reservas simultáneas (%{NumberOfSimultaneousBookings}) alcanzado para recurso \"%{Cn} %{SystemEmail}\".";
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
@interface iCalEvent (SOGoExtensions)
|
||||
|
||||
- (BOOL) isStillRelevant;
|
||||
- (unsigned int) occurenceInterval;
|
||||
- (NSMutableDictionary *) quickRecord;
|
||||
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
|
||||
|
||||
|
|
|
@ -272,14 +272,22 @@
|
|||
*/
|
||||
- (NGCalendarDateRange *) firstOccurenceRange
|
||||
{
|
||||
iCalDateTime *firstStartDate, *firstEndDate;
|
||||
iCalDateTime *firstStartDate;
|
||||
NSCalendarDate *start, *end;
|
||||
NGCalendarDateRange *firstRange;
|
||||
|
||||
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
firstEndDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtend"];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate]
|
||||
endDate: [[[firstEndDate values] lastObject] asCalendarDate]];
|
||||
firstRange = nil;
|
||||
|
||||
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
if ([[firstStartDate values] count] > 0)
|
||||
{
|
||||
start = [[[firstStartDate values] lastObject] asCalendarDate];
|
||||
end = [start addTimeInterval: [self occurenceInterval]];
|
||||
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end];
|
||||
}
|
||||
|
||||
return firstRange;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,8 +131,8 @@
|
|||
firstRange = [self firstOccurenceRange];
|
||||
|
||||
// Set the range to check with respect to the event timezone (extracted from the start date)
|
||||
firstStartDate = (iCalDateTime*) [self uniqueChildWithTag: @"dtstart"];
|
||||
eventTimeZone = [(iCalDateTime*) firstStartDate timeZone];
|
||||
firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
|
||||
eventTimeZone = [(iCalDateTime *)firstStartDate timeZone];
|
||||
if (eventTimeZone)
|
||||
startDate = [eventTimeZone computedDateForDate: theOccurenceDate];
|
||||
else
|
||||
|
@ -144,12 +144,12 @@
|
|||
// to the occurence's timezone.
|
||||
timeZone = [theOccurenceDate timeZone];
|
||||
offset = [timeZone secondsFromGMTForDate: [firstRange startDate]];
|
||||
firstStartDate = (NSCalendarDate*) [[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
firstStartDate = (NSCalendarDate *)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
firstEndDate = (NSCalendarDate*) [[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
firstEndDate = (NSCalendarDate *)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
[(NSCalendarDate*) firstStartDate setTimeZone: timeZone];
|
||||
[(NSCalendarDate*) firstEndDate setTimeZone: timeZone];
|
||||
[(NSCalendarDate *)firstStartDate setTimeZone: timeZone];
|
||||
[(NSCalendarDate *)firstEndDate setTimeZone: timeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate
|
||||
endDate: firstEndDate];
|
||||
}
|
||||
|
|
|
@ -1141,7 +1141,7 @@ static NSString *userAgent = nil;
|
|||
|
||||
if ((s = [self contentTypeForAttachmentWithName:_name]) != nil) {
|
||||
[map setObject:s forKey: @"content-type"];
|
||||
if ([s hasPrefix: @"text/"])
|
||||
if ([s hasPrefix: @"text/plain"] || [s hasPrefix: @"text/html"])
|
||||
attachAsString = YES;
|
||||
else if ([s hasPrefix: @"message/rfc822"])
|
||||
is7bit = YES;
|
||||
|
|
|
@ -57,6 +57,8 @@ typedef enum {
|
|||
SOGoTrashFolder *trashFolder;
|
||||
SOGoIMAPAclStyle imapAclStyle;
|
||||
NSMutableArray *identities;
|
||||
NSString *otherUsersFolderName;
|
||||
NSString *sharedFoldersName;
|
||||
}
|
||||
|
||||
- (SOGoIMAPAclStyle) imapAclStyle;
|
||||
|
@ -81,6 +83,8 @@ typedef enum {
|
|||
- (NSString *) draftsFolderNameInContext: (id)_ctx;
|
||||
- (NSString *) sentFolderNameInContext: (id)_ctx;
|
||||
- (NSString *) trashFolderNameInContext: (id)_ctx;
|
||||
- (NSString *) otherUsersFolderNameInContext: (id)_ctx;
|
||||
- (NSString *) sharedFoldersNameInContext: (id)_ctx;
|
||||
|
||||
- (SOGoMailFolder *) inboxFolderInContext: (id)_ctx;
|
||||
- (SOGoDraftsFolder *) draftsFolderInContext: (id)_ctx;
|
||||
|
|
|
@ -72,6 +72,8 @@ static NSString *sieveScriptName = @"sogo";
|
|||
trashFolder = nil;
|
||||
imapAclStyle = undefined;
|
||||
identities = nil;
|
||||
otherUsersFolderName = nil;
|
||||
sharedFoldersName = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -84,6 +86,8 @@ static NSString *sieveScriptName = @"sogo";
|
|||
[sentFolder release];
|
||||
[trashFolder release];
|
||||
[identities release];
|
||||
[otherUsersFolderName release];
|
||||
[sharedFoldersName release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -120,17 +124,33 @@ static NSString *sieveScriptName = @"sogo";
|
|||
NSArray *namespace;
|
||||
NGImap4Client *client;
|
||||
|
||||
SOGoUser *user;
|
||||
NSArray *accounts;
|
||||
NSDictionary *account;
|
||||
|
||||
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
|
||||
accounts = [user mailAccounts];
|
||||
account = [accounts objectAtIndex: [nameInContainer intValue]];
|
||||
client = [[self imap4Connection] client];
|
||||
namespaceDict = [client namespace];
|
||||
|
||||
namespace = [namespaceDict objectForKey: @"personal"];
|
||||
if (namespace)
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
|
||||
namespace = [namespaceDict objectForKey: @"other users"];
|
||||
if (namespace)
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
{
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
ASSIGN(otherUsersFolderName, [folders lastObject]);
|
||||
}
|
||||
|
||||
namespace = [namespaceDict objectForKey: @"shared"];
|
||||
if (namespace)
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
{
|
||||
[self _appendNamespace: namespace toFolders: folders];
|
||||
ASSIGN(sharedFoldersName, [folders lastObject]);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) _namespacesWithKey: (NSString *) nsKey
|
||||
|
@ -788,6 +808,16 @@ static NSString *sieveScriptName = @"sogo";
|
|||
return [self _userFolderNameWithPurpose: @"Trash"];
|
||||
}
|
||||
|
||||
- (NSString *) otherUsersFolderNameInContext: (id)_ctx
|
||||
{
|
||||
return otherUsersFolderName;
|
||||
}
|
||||
|
||||
- (NSString *) sharedFoldersNameInContext: (id)_ctx
|
||||
{
|
||||
return sharedFoldersName;
|
||||
}
|
||||
|
||||
- (id) folderWithTraversal: (NSString *) traversal
|
||||
andClassName: (NSString *) className
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@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
|
||||
|
@ -53,7 +54,7 @@
|
|||
NSString *CNField;
|
||||
NSString *UIDField;
|
||||
NSArray *mailFields, *searchFields;
|
||||
NSString *IMAPHostField;
|
||||
NSString *IMAPHostField, *IMAPLoginField;
|
||||
NSArray *bindFields;
|
||||
BOOL _bindAsCurrentUser;
|
||||
|
||||
|
@ -67,6 +68,10 @@
|
|||
BOOL passwordPolicy;
|
||||
|
||||
NSMutableDictionary *_dnCache;
|
||||
|
||||
/* resources handling */
|
||||
NSString *kindField;
|
||||
NSString *multipleBookingsField;
|
||||
}
|
||||
|
||||
- (void) setBindDN: (NSString *) newBindDN
|
||||
|
@ -83,7 +88,10 @@
|
|||
mailFields: (NSArray *) newMailFields
|
||||
searchFields: (NSArray *) newSearchFields
|
||||
IMAPHostField: (NSString *) newIMAPHostField
|
||||
andBindFields: (id) newBindFields;
|
||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||
bindFields: (id) newBindFields
|
||||
kindField: (NSString *) newKindField
|
||||
andMultipleBookingsField: (NSString *) newMultipleBookingsField;
|
||||
|
||||
- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID;
|
||||
- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@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
|
||||
|
@ -161,6 +162,7 @@ static NSArray *commonSearchFields;
|
|||
searchFields = [NSArray arrayWithObjects: @"sn", @"displayname", @"telephonenumber", nil];
|
||||
[searchFields retain];
|
||||
IMAPHostField = nil;
|
||||
IMAPLoginField = nil;
|
||||
bindFields = nil;
|
||||
_scope = @"sub";
|
||||
_filter = nil;
|
||||
|
@ -168,6 +170,9 @@ static NSArray *commonSearchFields;
|
|||
searchAttributes = nil;
|
||||
passwordPolicy = NO;
|
||||
|
||||
kindField = nil;
|
||||
multipleBookingsField = nil;
|
||||
|
||||
_dnCache = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
|
@ -176,7 +181,6 @@ static NSArray *commonSearchFields;
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
NSLog(@"LDAPSource: -dealloc");
|
||||
[bindDN release];
|
||||
[hostname release];
|
||||
[encryption release];
|
||||
|
@ -188,6 +192,7 @@ static NSArray *commonSearchFields;
|
|||
[mailFields release];
|
||||
[searchFields release];
|
||||
[IMAPHostField release];
|
||||
[IMAPLoginField release];
|
||||
[bindFields release];
|
||||
[_filter release];
|
||||
[sourceID release];
|
||||
|
@ -196,6 +201,8 @@ static NSArray *commonSearchFields;
|
|||
[searchAttributes release];
|
||||
[domain release];
|
||||
[_dnCache release];
|
||||
[kindField release];
|
||||
[multipleBookingsField release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -223,7 +230,10 @@ static NSArray *commonSearchFields;
|
|||
mailFields: [udSource objectForKey: @"MailFieldNames"]
|
||||
searchFields: [udSource objectForKey: @"SearchFieldNames"]
|
||||
IMAPHostField: [udSource objectForKey: @"IMAPHostFieldName"]
|
||||
andBindFields: [udSource objectForKey: @"bindFields"]];
|
||||
IMAPLoginField: [udSource objectForKey: @"IMAPLoginFieldName"]
|
||||
bindFields: [udSource objectForKey: @"bindFields"]
|
||||
kindField: [udSource objectForKey: @"KindFieldName"]
|
||||
andMultipleBookingsField: [udSource objectForKey: @"MultipleBookingsFieldName"]];
|
||||
|
||||
if ([sourceDomain length])
|
||||
{
|
||||
|
@ -305,7 +315,10 @@ static NSArray *commonSearchFields;
|
|||
mailFields: (NSArray *) newMailFields
|
||||
searchFields: (NSArray *) newSearchFields
|
||||
IMAPHostField: (NSString *) newIMAPHostField
|
||||
andBindFields: (id) newBindFields
|
||||
IMAPLoginField: (NSString *) newIMAPLoginField
|
||||
bindFields: (id) newBindFields
|
||||
kindField: (NSString *) newKindField
|
||||
andMultipleBookingsField: (NSString *) newMultipleBookingsField
|
||||
{
|
||||
ASSIGN (baseDN, [newBaseDN lowercaseString]);
|
||||
if (newIDField)
|
||||
|
@ -316,6 +329,8 @@ static NSArray *commonSearchFields;
|
|||
ASSIGN (UIDField, newUIDField);
|
||||
if (newIMAPHostField)
|
||||
ASSIGN (IMAPHostField, newIMAPHostField);
|
||||
if (newIMAPLoginField)
|
||||
ASSIGN (IMAPLoginField, newIMAPLoginField);
|
||||
if (newMailFields)
|
||||
ASSIGN (mailFields, newMailFields);
|
||||
if (newSearchFields)
|
||||
|
@ -343,6 +358,10 @@ static NSArray *commonSearchFields;
|
|||
ASSIGN(bindFields, [newBindFields componentsSeparatedByString: @","]);
|
||||
}
|
||||
}
|
||||
if (newKindField)
|
||||
ASSIGN(kindField, newKindField);
|
||||
if (newMultipleBookingsField)
|
||||
ASSIGN(multipleBookingsField, newMultipleBookingsField);
|
||||
}
|
||||
|
||||
- (BOOL) _setupEncryption: (NGLdapConnection *) encryptedConn
|
||||
|
@ -694,6 +713,17 @@ static NSArray *commonSearchFields;
|
|||
// Add IMAP hostname from user defaults
|
||||
if ([IMAPHostField length])
|
||||
[searchAttributes addObjectUniquely: IMAPHostField];
|
||||
|
||||
// Add IMAP login from user defaults
|
||||
if ([IMAPLoginField length])
|
||||
[searchAttributes addObjectUniquely: IMAPLoginField];
|
||||
|
||||
// Add the resources handling attributes
|
||||
if ([kindField length])
|
||||
[searchAttributes addObjectUniquely: kindField];
|
||||
|
||||
if ([multipleBookingsField length])
|
||||
[searchAttributes addObjectUniquely: multipleBookingsField];
|
||||
}
|
||||
|
||||
return searchAttributes;
|
||||
|
@ -761,6 +791,13 @@ static NSArray *commonSearchFields;
|
|||
if ([ldapValue length] > 0)
|
||||
[contactEntry setObject: ldapValue forKey: @"c_imaphostname"];
|
||||
}
|
||||
|
||||
if (IMAPLoginField)
|
||||
{
|
||||
ldapValue = [[ldapEntry attributeWithName: IMAPLoginField] stringValueAtIndex: 0];
|
||||
if ([ldapValue length] > 0)
|
||||
[contactEntry setObject: ldapValue forKey: @"c_imaplogin"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _fillConstraints: (NGLdapEntry *) ldapEntry
|
||||
|
@ -838,6 +875,14 @@ static NSArray *commonSearchFields;
|
|||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isGroup"];
|
||||
}
|
||||
// We check if our entry is a resource. We also support
|
||||
// determining resources based on the KindFieldName attribute
|
||||
// value - see below.
|
||||
else if ([classes containsObject: @"calendarresource"])
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isResource"];
|
||||
}
|
||||
}
|
||||
|
||||
while ((currentAttribute = [attributes nextObject]))
|
||||
|
@ -847,7 +892,37 @@ static NSArray *commonSearchFields;
|
|||
|
||||
// It's important here to set our attributes' key in lowercase.
|
||||
if (value)
|
||||
[contactEntry setObject: value forKey: [currentAttribute lowercaseString]];
|
||||
{
|
||||
currentAttribute = [currentAttribute lowercaseString];
|
||||
[contactEntry setObject: value forKey: currentAttribute];
|
||||
|
||||
// We check if that entry corresponds to a resource. For this,
|
||||
// kindField must be defined and it must hold one of those values
|
||||
//
|
||||
// location
|
||||
// thing
|
||||
// group
|
||||
//
|
||||
if (kindField &&
|
||||
[kindField caseInsensitiveCompare: currentAttribute] == NSOrderedSame)
|
||||
{
|
||||
if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"thing"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"group"] == NSOrderedSame)
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isResource"];
|
||||
}
|
||||
}
|
||||
// We check for the number of simultanous bookings that is allowed.
|
||||
// A value of 0 means that there's no limit.
|
||||
if (multipleBookingsField &&
|
||||
[multipleBookingsField caseInsensitiveCompare: currentAttribute] == NSOrderedSame)
|
||||
{
|
||||
[contactEntry setObject: [NSNumber numberWithInt: [value intValue]]
|
||||
forKey: @"numberOfSimultaneousBookings"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = [[ldapEntry attributeWithName: IDField] stringValueAtIndex: 0];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2009 Inverse inc.
|
||||
* Copyright (C) 2007-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -480,14 +480,17 @@ static NSArray *childRecordFields = nil;
|
|||
- (GCSFolder *) ocsFolder
|
||||
{
|
||||
GCSFolder *folder;
|
||||
SOGoUser *user;
|
||||
NSString *userLogin;
|
||||
|
||||
if (!ocsFolder)
|
||||
{
|
||||
ocsFolder = [self ocsFolderForPath: [self ocsPath]];
|
||||
userLogin = [[context activeUser] login];
|
||||
user = [context activeUser];
|
||||
userLogin = [user login];
|
||||
if (!ocsFolder
|
||||
&& [userLogin isEqualToString: [self ownerInContext: context]]
|
||||
&& [user canAuthenticate]
|
||||
&& [self folderIsMandatory]
|
||||
&& [self create])
|
||||
ocsFolder = [self ocsFolderForPath: [self ocsPath]];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoGroup.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2010 Inverse inc.
|
||||
* Copyright (C) 2009-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -159,7 +159,12 @@ static SoSecurityManager *sm = nil;
|
|||
SOGoGCSFolder *folder;
|
||||
|
||||
roles = [[context activeUser] rolesForObject: self inContext: context];
|
||||
if ([roles containsObject: SoRole_Owner])
|
||||
|
||||
// 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.
|
||||
if ([roles containsObject: SoRole_Owner] ||
|
||||
[[context activeUser] isResource])
|
||||
{
|
||||
folder = [subFolderClass objectWithName: @"personal" inContainer: self];
|
||||
[folder setDisplayName: [self defaultFolderName]];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
Copyright (C) 2006-2011 Inverse inc.
|
||||
Copyright (C) 2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo.
|
||||
|
@ -112,17 +112,19 @@
|
|||
- (NSMutableDictionary *) defaultIdentity;
|
||||
|
||||
- (BOOL) isSuperUser;
|
||||
- (BOOL) canAuthenticate;
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource;
|
||||
- (int) numberOfSimultaneousBookings;
|
||||
|
||||
/* module access */
|
||||
- (BOOL) canAccessModule: (NSString *) module;
|
||||
|
||||
/* folders */
|
||||
|
||||
- (SOGoUserFolder *) homeFolderInContext: (id) context;
|
||||
|
||||
- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context;
|
||||
- (SOGoAppointmentFolder *)
|
||||
personalCalendarFolderInContext: (WOContext *) context;
|
||||
- (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -757,6 +757,37 @@
|
|||
return [[_domainDefaults superUsernames] containsObject: login];
|
||||
}
|
||||
|
||||
- (BOOL) canAuthenticate
|
||||
{
|
||||
id authValue;
|
||||
|
||||
authValue = [self _fetchFieldForUser: @"canAuthenticate"];
|
||||
|
||||
return [authValue boolValue];
|
||||
}
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource
|
||||
{
|
||||
NSNumber *v;
|
||||
|
||||
v = [self _fetchFieldForUser: @"isResource"];
|
||||
|
||||
return (v && [v intValue]);
|
||||
}
|
||||
|
||||
- (int) numberOfSimultaneousBookings
|
||||
{
|
||||
NSNumber *v;
|
||||
|
||||
v = [self _fetchFieldForUser: @"numberOfSimultaneousBookings"];
|
||||
|
||||
if (v)
|
||||
return [v intValue];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* module access */
|
||||
- (BOOL) canAccessModule: (NSString *) module
|
||||
{
|
||||
|
|
|
@ -337,17 +337,23 @@
|
|||
- (NSString *) getImapLoginForUID: (NSString *) uid
|
||||
{
|
||||
NSDictionary *contactInfos;
|
||||
NSString *domain;
|
||||
NSString *domain, *login;
|
||||
SOGoDomainDefaults *dd;
|
||||
|
||||
contactInfos = [self contactInfosForUserWithUIDorEmail: uid];
|
||||
login = [contactInfos objectForKey: @"c_imaplogin"];
|
||||
domain = [contactInfos objectForKey: @"c_domain"];
|
||||
if ([domain length])
|
||||
dd = [SOGoDomainDefaults defaultsForDomain: domain];
|
||||
else
|
||||
dd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return ([dd forceIMAPLoginWithEmail] ? [self getEmailForUID: uid] : uid);
|
||||
if (login == nil)
|
||||
{
|
||||
if ([domain length])
|
||||
dd = [SOGoDomainDefaults defaultsForDomain: domain];
|
||||
else
|
||||
dd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
login = [dd forceIMAPLoginWithEmail] ? [self getEmailForUID: uid] : uid;
|
||||
}
|
||||
|
||||
return login;
|
||||
}
|
||||
|
||||
- (NSString *) getUIDForEmail: (NSString *) email
|
||||
|
@ -542,6 +548,9 @@
|
|||
[contact setObject: [emails objectAtIndex: 0] forKey: @"c_email"];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser
|
||||
withUIDorEmail: (NSString *) uid
|
||||
{
|
||||
|
@ -549,7 +558,7 @@
|
|||
NSDictionary *userEntry;
|
||||
NSEnumerator *sogoSources;
|
||||
NSObject <SOGoDNSource> *currentSource;
|
||||
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname;
|
||||
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin;
|
||||
NSArray *c_emails;
|
||||
BOOL access;
|
||||
|
||||
|
@ -558,6 +567,7 @@
|
|||
c_uid = nil;
|
||||
c_domain = nil;
|
||||
c_imaphostname = nil;
|
||||
c_imaplogin = nil;
|
||||
|
||||
[currentUser setObject: [NSNumber numberWithBool: YES]
|
||||
forKey: @"CalendarAccess"];
|
||||
|
@ -583,6 +593,8 @@
|
|||
[emails addObjectsFromArray: c_emails];
|
||||
if (!c_imaphostname)
|
||||
c_imaphostname = [userEntry objectForKey: @"c_imaphostname"];
|
||||
if (!c_imaplogin)
|
||||
c_imaplogin = [userEntry objectForKey: @"c_imaplogin"];
|
||||
access = [[userEntry objectForKey: @"CalendarAccess"] boolValue];
|
||||
if (!access)
|
||||
[currentUser setObject: [NSNumber numberWithBool: NO]
|
||||
|
@ -591,6 +603,15 @@
|
|||
if (!access)
|
||||
[currentUser setObject: [NSNumber numberWithBool: NO]
|
||||
forKey: @"MailAccess"];
|
||||
|
||||
// We also fill the resource attributes, if any
|
||||
if ([userEntry objectForKey: @"isResource"])
|
||||
[currentUser setObject: [userEntry objectForKey: @"isResource"]
|
||||
forKey: @"isResource"];
|
||||
if ([userEntry objectForKey: @"numberOfSimultaneousBookings"])
|
||||
[currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
|
||||
forKey: @"numberOfSimultaneousBookings"];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,6 +624,9 @@
|
|||
|
||||
if (c_imaphostname)
|
||||
[currentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
|
||||
if (c_imaplogin)
|
||||
[currentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
|
||||
|
||||
[currentUser setObject: emails forKey: @"emails"];
|
||||
[currentUser setObject: cn forKey: @"cn"];
|
||||
[currentUser setObject: c_uid forKey: @"c_uid"];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoUserSettings.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
* Copyright (C) 2009-2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
* Copyright (C) 2009-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Authors: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@invers.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
|
||||
|
@ -36,9 +37,15 @@
|
|||
{
|
||||
NSString *_sourceID;
|
||||
NSString *_domain;
|
||||
NSString *_authenticationFilter;
|
||||
NSArray *_mailFields;
|
||||
NSString *_imapLoginField;
|
||||
NSString *_userPasswordAlgorithm;
|
||||
NSURL *_viewURL;
|
||||
|
||||
/* resources handling */
|
||||
NSString *_kindField;
|
||||
NSString *_multipleBookingsField;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
* Copyright (C) 2009-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Authors: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@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
|
||||
|
@ -31,9 +32,8 @@
|
|||
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
#import <GDLContentStore/EOQualifier+GCS.h>
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
|
||||
#import "SOGoConstants.h"
|
||||
#import "NSString+Utilities.h"
|
||||
|
@ -79,9 +79,12 @@
|
|||
if ((self = [super init]))
|
||||
{
|
||||
_sourceID = nil;
|
||||
_authenticationFilter = nil;
|
||||
_mailFields = nil;
|
||||
_userPasswordAlgorithm = nil;
|
||||
_viewURL = nil;
|
||||
_kindField = nil;
|
||||
_multipleBookingsField = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -90,9 +93,12 @@
|
|||
- (void) dealloc
|
||||
{
|
||||
[_sourceID release];
|
||||
[_authenticationFilter release];
|
||||
[_mailFields release];
|
||||
[_userPasswordAlgorithm release];
|
||||
[_viewURL release];
|
||||
[_kindField release];
|
||||
[_multipleBookingsField release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
@ -103,9 +109,13 @@
|
|||
self = [self init];
|
||||
|
||||
ASSIGN(_sourceID, [udSource objectForKey: @"id"]);
|
||||
ASSIGN(_authenticationFilter, [udSource objectForKey: @"authenticationFilter"]);
|
||||
ASSIGN(_mailFields, [udSource objectForKey: @"MailFieldNames"]);
|
||||
ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]);
|
||||
|
||||
ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]);
|
||||
ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]);
|
||||
ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]);
|
||||
|
||||
if (!_userPasswordAlgorithm)
|
||||
_userPasswordAlgorithm = @"none";
|
||||
|
||||
|
@ -203,9 +213,10 @@
|
|||
grace: (int *) _grace
|
||||
{
|
||||
EOAdaptorChannel *channel;
|
||||
EOQualifier *qualifier;
|
||||
GCSChannelManager *cm;
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
NSMutableString *sql;
|
||||
BOOL rc;
|
||||
|
||||
rc = NO;
|
||||
|
@ -214,12 +225,25 @@
|
|||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: _viewURL];
|
||||
if (channel)
|
||||
{
|
||||
sql = [NSString stringWithFormat: (@"SELECT c_password"
|
||||
@" FROM %@"
|
||||
@" WHERE c_uid = '%@'"),
|
||||
[_viewURL gcsTableName], _login];
|
||||
|
||||
{
|
||||
qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid"
|
||||
operatorSelector: EOQualifierOperatorEqual
|
||||
value: _login];
|
||||
[qualifier autorelease];
|
||||
sql = [NSMutableString stringWithFormat: @"SELECT c_password"
|
||||
@" FROM %@"
|
||||
@" WHERE ",
|
||||
[_viewURL gcsTableName]];
|
||||
if (_authenticationFilter)
|
||||
{
|
||||
qualifier = [[EOAndQualifier alloc] initWithQualifiers:
|
||||
qualifier,
|
||||
[EOQualifier qualifierWithQualifierFormat: _authenticationFilter],
|
||||
nil];
|
||||
[qualifier autorelease];
|
||||
}
|
||||
[qualifier _gcsAppendToString: sql];
|
||||
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (!ex)
|
||||
{
|
||||
|
@ -235,7 +259,8 @@
|
|||
[channel cancelFetch];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
|
||||
[self errorWithFormat: @"could not run SQL '%@': %@", qualifier, ex];
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
else
|
||||
|
@ -328,8 +353,10 @@
|
|||
{
|
||||
NSMutableDictionary *response;
|
||||
EOAdaptorChannel *channel;
|
||||
EOQualifier *qualifier;
|
||||
GCSChannelManager *cm;
|
||||
NSString *sql, *value;
|
||||
NSMutableString *sql;
|
||||
NSString *value;
|
||||
NSException *ex;
|
||||
|
||||
response = nil;
|
||||
|
@ -340,21 +367,21 @@
|
|||
if (channel)
|
||||
{
|
||||
if (!b)
|
||||
sql = [NSString stringWithFormat: (@"SELECT *"
|
||||
@" FROM %@"
|
||||
@" WHERE c_uid = '%@'"),
|
||||
[_viewURL gcsTableName], theID];
|
||||
sql = [NSMutableString stringWithFormat: (@"SELECT *"
|
||||
@" FROM %@"
|
||||
@" WHERE c_uid = '%@'"),
|
||||
[_viewURL gcsTableName], theID];
|
||||
else
|
||||
{
|
||||
sql = [NSString stringWithFormat: (@"SELECT *"
|
||||
@" FROM %@"
|
||||
@" WHERE c_uid = '%@' OR"
|
||||
@" LOWER(mail) = '%@'"),
|
||||
[_viewURL gcsTableName], theID, [theID lowercaseString]];
|
||||
|
||||
sql = [NSMutableString stringWithFormat: (@"SELECT *"
|
||||
@" FROM %@"
|
||||
@" WHERE c_uid = '%@' OR"
|
||||
@" LOWER(mail) = '%@'"),
|
||||
[_viewURL gcsTableName], theID, [theID lowercaseString]];
|
||||
|
||||
if (_mailFields && [_mailFields count] > 0)
|
||||
{
|
||||
sql = [sql stringByAppendingString: [self _whereClauseFromArray: _mailFields value: [theID lowercaseString] exact: YES]];
|
||||
[sql appendString: [self _whereClauseFromArray: _mailFields value: [theID lowercaseString] exact: YES]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,6 +424,73 @@
|
|||
}
|
||||
|
||||
[response setObject: emails forKey: @"c_emails"];
|
||||
|
||||
// We check if the user can authenticate
|
||||
if (_authenticationFilter)
|
||||
{
|
||||
EOQualifier *q_uid, *q_auth;
|
||||
|
||||
sql = [NSMutableString stringWithFormat: @"SELECT c_uid"
|
||||
@" FROM %@"
|
||||
@" WHERE ",
|
||||
[_viewURL gcsTableName]];
|
||||
|
||||
q_auth = [EOQualifier qualifierWithQualifierFormat: _authenticationFilter];
|
||||
|
||||
q_uid = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid"
|
||||
operatorSelector: EOQualifierOperatorEqual
|
||||
value: theID];
|
||||
[q_uid autorelease];
|
||||
|
||||
qualifier = [[EOAndQualifier alloc] initWithQualifiers: q_uid, q_auth, nil];
|
||||
[qualifier autorelease];
|
||||
[qualifier _gcsAppendToString: sql];
|
||||
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (!ex)
|
||||
{
|
||||
NSDictionary *authResponse;
|
||||
|
||||
authResponse = [channel fetchAttributes: [channel describeResults: NO] withZone: NULL];
|
||||
[response setObject: [NSNumber numberWithBool: [authResponse count] > 0] forKey: @"canAuthenticate"];
|
||||
[channel cancelFetch];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
|
||||
}
|
||||
else
|
||||
[response setObject: [NSNumber numberWithBool: YES] forKey: @"canAuthenticate"];
|
||||
|
||||
// We check if we should use a different login for IMAP
|
||||
if (_imapLoginField)
|
||||
{
|
||||
if ([response objectForKey: _imapLoginField])
|
||||
[response setObject: [response objectForKey: _imapLoginField] forKey: @"c_imaplogin"];
|
||||
}
|
||||
|
||||
// We check if it's a resource of not
|
||||
if (_kindField)
|
||||
{
|
||||
if ((value = [response objectForKey: _kindField]))
|
||||
{
|
||||
if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"thing"] == NSOrderedSame ||
|
||||
[value caseInsensitiveCompare: @"group"] == NSOrderedSame)
|
||||
{
|
||||
[response setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"isResource"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_multipleBookingsField)
|
||||
{
|
||||
if ((value = [response objectForKey: _multipleBookingsField]))
|
||||
{
|
||||
[response setObject: [NSNumber numberWithInt: [value intValue]]
|
||||
forKey: @"numberOfSimultaneousBookings"];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
|
||||
|
|
|
@ -19,7 +19,8 @@ $(TEST_TOOL)_OBJC_FILES += \
|
|||
\
|
||||
TestSBJsonParser.m \
|
||||
\
|
||||
TestNGMimeAddressHeaderFieldGenerator.m
|
||||
TestNGMimeAddressHeaderFieldGenerator.m \
|
||||
TestNSString+URLEscaping.m
|
||||
|
||||
TOOL_NAME = $(TEST_TOOL)
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* TestNSString+URLEscaping.m - this file is part of $PROJECT_NAME_HERE$
|
||||
*
|
||||
* Copyright (C) 2011 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 2, 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.
|
||||
*/
|
||||
|
||||
/* This file is encoded in utf-8. */
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import "SOGoTest.h"
|
||||
|
||||
@interface TestNSString_plus_URLEscaping : SOGoTest
|
||||
@end
|
||||
|
||||
@implementation TestNSString_plus_URLEscaping
|
||||
|
||||
- (void) test_stringByEscapingURL
|
||||
{
|
||||
const char *inStrings[] = { "éléphant", "2š", NULL };
|
||||
const char **inString;
|
||||
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1" };
|
||||
NSString **outString;
|
||||
NSString *result, *error;
|
||||
|
||||
inString = inStrings;
|
||||
outString = outStrings;
|
||||
while (*inString)
|
||||
{
|
||||
result = [[NSString stringWithUTF8String: *inString] stringByEscapingURL];
|
||||
error = [NSString stringWithFormat:
|
||||
@"string '%s' badly escaped: '%@' (expected '%@')",
|
||||
*inString, result, *outString];
|
||||
testWithMessage([result isEqualToString: *outString], error);
|
||||
inString++;
|
||||
outString++;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -28,8 +28,20 @@ int main()
|
|||
{
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
NSDictionary *defaults;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
defaults = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool: YES],
|
||||
@"NGUseUTF8AsURLEncoding",
|
||||
nil];
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setVolatileDomain: defaults
|
||||
forName: @"sogo-tests-volatile"];
|
||||
[ud addSuiteNamed: @"sogo-tests-volatile"];
|
||||
|
||||
rc = [[SOGoTestRunner testRunner] run];
|
||||
[pool release];
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ ContactsUI_PRINCIPAL_CLASS = ContactsUIProduct
|
|||
ContactsUI_LANGUAGES = BrazilianPortuguese Catalan Czech Dutch English French German Hungarian Italian Norwegian Polish Russian Spanish Swedish Ukrainian Welsh
|
||||
|
||||
ContactsUI_OBJC_FILES = \
|
||||
UIxContactsUserFolders.m \
|
||||
UIxContactsUserFolders.m \
|
||||
UIxContactsMailerSelection.m \
|
||||
UIxContactsUserRightsEditor.m \
|
||||
\
|
||||
|
@ -18,10 +18,10 @@ ContactsUI_OBJC_FILES = \
|
|||
UIxContactActions.m \
|
||||
UIxContactView.m \
|
||||
UIxContactEditor.m \
|
||||
UIxListView.m \
|
||||
UIxListEditor.m \
|
||||
UIxContactsListView.m \
|
||||
UIxContactFoldersView.m \
|
||||
UIxListView.m \
|
||||
UIxListEditor.m \
|
||||
UIxContactsListActions.m \
|
||||
UIxContactFoldersView.m \
|
||||
UIxContactFolderActions.m
|
||||
|
||||
ContactsUI_RESOURCE_FILES += \
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
@interface UIxContactFoldersView : UIxComponent
|
||||
{
|
||||
SOGoUserSettings *us;
|
||||
NSDictionary *currentContact;
|
||||
NSString *selectorComponentClass;
|
||||
NSMutableDictionary *moduleSettings;
|
||||
id currentFolder;
|
||||
|
@ -37,6 +38,8 @@
|
|||
|
||||
- (NSArray *) contactFolders;
|
||||
|
||||
- (NSArray *) personalContactInfos;
|
||||
|
||||
- (NSString *) currentContactFolderId;
|
||||
- (NSString *) currentContactFolderOwner;
|
||||
- (NSString *) currentContactFolderName;
|
||||
|
|
|
@ -87,6 +87,38 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void) setCurrentContact: (NSDictionary *) _contact
|
||||
{
|
||||
currentContact = _contact;
|
||||
}
|
||||
|
||||
- (NSDictionary *) currentContact
|
||||
{
|
||||
return currentContact;
|
||||
}
|
||||
|
||||
- (NSString *) currentContactClasses
|
||||
{
|
||||
return [[currentContact objectForKey: @"c_component"] lowercaseString];
|
||||
}
|
||||
|
||||
- (NSArray *) personalContactInfos
|
||||
{
|
||||
SOGoContactFolders *folders;
|
||||
id <SOGoContactFolder> folder;
|
||||
NSArray *contactInfos;
|
||||
|
||||
folders = [self clientObject];
|
||||
folder = [folders lookupPersonalFolder: @"personal" ignoringRights: YES];
|
||||
|
||||
contactInfos = [folder lookupContactsWithFilter: nil
|
||||
onCriteria: nil
|
||||
sortBy: @"c_cn"
|
||||
ordering: NSOrderedAscending];
|
||||
|
||||
return contactInfos;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) mailerContactsAction
|
||||
{
|
||||
selectorComponentClass = @"UIxContactsMailerSelection";
|
||||
|
@ -316,8 +348,23 @@
|
|||
|
||||
- (id) defaultAction
|
||||
{
|
||||
// NSString *check;
|
||||
// WOResponse *response;
|
||||
// static NSString *etag = @"\"contacts-ui\"";
|
||||
|
||||
[self checkDefaultModulePreference];
|
||||
|
||||
// check = [[context request] headerForKey: @"if-none-match"];
|
||||
// if ([check length] > 0 && [check rangeOfString: etag].location != NSNotFound) /* not perfectly correct */
|
||||
// response = [self responseWithStatus: 304];
|
||||
// else
|
||||
// {
|
||||
// response = [context response];
|
||||
// [response setHeader: etag forKey: @"etag"];
|
||||
// response = (WOResponse *) [super defaultAction];
|
||||
// }
|
||||
|
||||
// return response;
|
||||
return [super defaultAction];
|
||||
}
|
||||
|
||||
|
|
|
@ -19,17 +19,17 @@
|
|||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UIxContactsListView_H__
|
||||
#define __UIxContactsListView_H__
|
||||
#ifndef __UIxContactsListActions_H__
|
||||
#define __UIxContactsListActions_H__
|
||||
|
||||
#import <SOGoUI/UIxComponent.h>
|
||||
#import <NGObjWeb/WODirectAction.h>
|
||||
|
||||
@class NSDictionary;
|
||||
@class NSString;
|
||||
|
||||
@protocol SOGoContactObject;
|
||||
|
||||
@interface UIxContactsListView : UIxComponent
|
||||
@interface UIxContactsListActions : WODirectAction
|
||||
{
|
||||
NSDictionary *currentContact;
|
||||
NSArray *contactInfos;
|
||||
|
@ -37,4 +37,4 @@
|
|||
|
||||
@end
|
||||
|
||||
#endif /* __UIxContactsListView_H__ */
|
||||
#endif /* __UIxContactsListActions_H__ */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
Copyright (C) 2006-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
|
@ -32,6 +32,8 @@
|
|||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <Common/WODirectAction+SOGo.h>
|
||||
|
||||
#import <Contacts/SOGoContactObject.h>
|
||||
#import <Contacts/SOGoContactFolder.h>
|
||||
#import <Contacts/SOGoContactFolders.h>
|
||||
|
@ -44,9 +46,9 @@
|
|||
#import <SoObjects/Contacts/SOGoContactGCSFolder.h>
|
||||
#import <GDLContentStore/GCSFolder.h>
|
||||
|
||||
#import "UIxContactsListView.h"
|
||||
#import "UIxContactsListActions.h"
|
||||
|
||||
@implementation UIxContactsListView
|
||||
@implementation UIxContactsListActions
|
||||
|
||||
- (id) init
|
||||
{
|
||||
|
@ -64,16 +66,6 @@
|
|||
|
||||
/* accessors */
|
||||
|
||||
- (void) setCurrentContact: (NSDictionary *) _contact
|
||||
{
|
||||
currentContact = _contact;
|
||||
}
|
||||
|
||||
- (NSDictionary *) currentContact
|
||||
{
|
||||
return currentContact;
|
||||
}
|
||||
|
||||
- (NSString *) defaultSortKey
|
||||
{
|
||||
return @"c_cn";
|
||||
|
@ -82,8 +74,10 @@
|
|||
- (NSString *) sortKey
|
||||
{
|
||||
NSString *s;
|
||||
WORequest *rq;
|
||||
|
||||
s = [self queryParameterForKey: @"sort"];
|
||||
rq = [context request];
|
||||
s = [rq formValueForKey: @"sort"];
|
||||
if (![s length])
|
||||
s = [self defaultSortKey];
|
||||
|
||||
|
@ -95,18 +89,20 @@
|
|||
id <SOGoContactFolder> folder;
|
||||
NSString *ascending, *searchText, *valueText;
|
||||
NSComparisonResult ordering;
|
||||
WORequest *rq;
|
||||
|
||||
if (!contactInfos)
|
||||
{
|
||||
folder = [self clientObject];
|
||||
rq = [context request];
|
||||
|
||||
ascending = [self queryParameterForKey: @"asc"];
|
||||
ascending = [rq formValueForKey: @"asc"];
|
||||
ordering = ((![ascending length] || [ascending boolValue])
|
||||
? NSOrderedAscending : NSOrderedDescending);
|
||||
|
||||
searchText = [self queryParameterForKey: @"search"];
|
||||
searchText = [rq formValueForKey: @"search"];
|
||||
if ([searchText length] > 0)
|
||||
valueText = [self queryParameterForKey: @"value"];
|
||||
valueText = [rq formValueForKey: @"value"];
|
||||
else
|
||||
valueText = nil;
|
||||
|
||||
|
@ -121,9 +117,21 @@
|
|||
return contactInfos;
|
||||
}
|
||||
|
||||
- (NSString *) currentContactClasses
|
||||
/**
|
||||
* Retrieve the addressbook contacts with respect to the sort and
|
||||
* search criteria.
|
||||
* @return a JSON array of dictionaries representing the contacts.
|
||||
*/
|
||||
- (id <WOActionResults>) contactsListAction
|
||||
{
|
||||
return [[currentContact objectForKey: @"c_component"] lowercaseString];
|
||||
id <WOActionResults> result;
|
||||
NSArray *contactsList;
|
||||
|
||||
contactsList = [self contactInfos];
|
||||
result = [self responseWithStatus: 200
|
||||
andString: [contactsList jsonRepresentation]];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) contactSearchAction
|
||||
|
@ -136,11 +144,12 @@
|
|||
NSMutableDictionary *uniqueContacts;
|
||||
unsigned int i;
|
||||
NSSortDescriptor *commonNameDescriptor;
|
||||
WORequest *rq;
|
||||
|
||||
searchText = [self queryParameterForKey: @"search"];
|
||||
rq = [context request];
|
||||
searchText = [rq formValueForKey: @"search"];
|
||||
if ([searchText length] > 0)
|
||||
{
|
||||
folder = nil;
|
||||
NS_DURING
|
||||
folder = [self clientObject];
|
||||
NS_HANDLER
|
||||
|
@ -176,9 +185,8 @@
|
|||
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys: searchText, @"searchText",
|
||||
sortedContacts, @"contacts", nil];
|
||||
result = [self responseWithStatus: 200];
|
||||
|
||||
[(WOResponse*)result appendContentString: [data jsonRepresentation]];
|
||||
result = [self responseWithStatus: 200
|
||||
andString: [data jsonRepresentation]];
|
||||
}
|
||||
else
|
||||
result = [NSException exceptionWithHTTPStatus: 400
|
||||
|
@ -187,13 +195,4 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* actions */
|
||||
|
||||
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq
|
||||
inContext: (WOContext*) _c
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end /* UIxContactsListView */
|
||||
@end /* UIxContactsListActions */
|
|
@ -71,11 +71,12 @@
|
|||
methods = {
|
||||
view = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxContactsListView";
|
||||
actionClass = "UIxContactsListActions";
|
||||
actionName = "contactsList";
|
||||
};
|
||||
contactSearch = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "UIxContactsListView";
|
||||
actionClass = "UIxContactsListActions";
|
||||
actionName = "contactSearch";
|
||||
};
|
||||
newcontact = {
|
||||
|
@ -90,7 +91,7 @@
|
|||
};
|
||||
mailer-contacts = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxContactsListView";
|
||||
pageName = "UIxContactFoldersView";
|
||||
actionName = "mailerContacts";
|
||||
};
|
||||
export = {
|
||||
|
@ -130,7 +131,8 @@
|
|||
methods = {
|
||||
view = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "UIxContactsListView";
|
||||
actionClass = "UIxContactsListActions";
|
||||
actionName = "contactsList";
|
||||
};
|
||||
newcontact = {
|
||||
protectedBy = "<public>";
|
||||
|
@ -139,7 +141,7 @@
|
|||
};
|
||||
mailer-contacts = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "UIxContactsListView";
|
||||
pageName = "UIxContactFoldersView";
|
||||
actionName = "mailerContacts";
|
||||
};
|
||||
canAccessContent = {
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Entrada";
|
||||
"DraftsFolderName" = "Rascunhos";
|
||||
"SieveFolderName" = "Filtros";
|
||||
"OtherUsersFolderName" = "Outros Usuários";
|
||||
"SharedFoldersName" = "Pastas Compartilhadas";
|
||||
"Folders" = "Pastas"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Safata d'entrada";
|
||||
"DraftsFolderName" = "Esborranys";
|
||||
"SieveFolderName" = "Filtres";
|
||||
"OtherUsersFolderName" = "Altres usuaris";
|
||||
"SharedFoldersName" = "Carpetes compartides";
|
||||
"Folders" = "Carpetes"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Doručená pošta";
|
||||
"DraftsFolderName" = "Koncepty";
|
||||
"SieveFolderName" = "Filtry";
|
||||
"OtherUsersFolderName" = "Ostatní uživatelé";
|
||||
"SharedFoldersName" = "Sdílené složky";
|
||||
"Folders" = "Složky"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Postvak IN";
|
||||
"DraftsFolderName" = "Concepten";
|
||||
"SieveFolderName" = "Berichtregel";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Mappen"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Inbox";
|
||||
"DraftsFolderName" = "Drafts";
|
||||
"SieveFolderName" = "Filters";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Folders"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Courrier entrant";
|
||||
"DraftsFolderName" = "Brouillons";
|
||||
"SieveFolderName" = "Filtres";
|
||||
"OtherUsersFolderName" = "Autres utilisateurs";
|
||||
"SharedFoldersName" = "Dossiers partagés";
|
||||
"Folders" = "Dossiers"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Posteingang";
|
||||
"DraftsFolderName" = "Entwürfe";
|
||||
"SieveFolderName" = "Filter";
|
||||
"Other Users" = "Andere Benutzer";
|
||||
"Shared Folders" = "Gemeinsame Ordner";
|
||||
"Folders" = "Ordner"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Beérkezett üzenetek";
|
||||
"DraftsFolderName" = "Piszkozatok";
|
||||
"SieveFolderName" = "Szűrők";
|
||||
"OtherUsersFolderName" = "Egyéb felhasználók";
|
||||
"SharedFoldersName" = "Megosztott mappák";
|
||||
"Folders" = "Mappák"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Posta in arrivo";
|
||||
"DraftsFolderName" = "Bozze";
|
||||
"SieveFolderName" = "Filtri";
|
||||
"OtherUsersFolderName" = "Altri Utenti";
|
||||
"SharedFoldersName" = "Cartelle Condivise";
|
||||
"Folders" = "Cartelle"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Innboks";
|
||||
"DraftsFolderName" = "Kladder";
|
||||
"SieveFolderName" = "Filter";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Mapper"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Odebrane";
|
||||
"DraftsFolderName" = "Szkice";
|
||||
"SieveFolderName" = "Filtry";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Foldery"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Входящие";
|
||||
"DraftsFolderName" = "Черновики";
|
||||
"SieveFolderName" = "Папки";
|
||||
"OtherUsersFolderName" = "Другие пользователи";
|
||||
"SharedFoldersName" = "Общие папки";
|
||||
"Folders" = "Папки"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Bandeja de entrada";
|
||||
"DraftsFolderName" = "Borradores";
|
||||
"SieveFolderName" = "Filtros";
|
||||
"OtherUsersFolderName" = "Otros usuarios";
|
||||
"SharedFoldersName" = "Carpetas compartidas";
|
||||
"Folders" = "Carpetas"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "No se puede enviar el mensaje: (smtp) todos los destinatarios han sido descartados";
|
||||
"cannot send message (smtp) - recipients discarded:" = "No se puede enviar el mensaje: (smtp) destinatarios descartados:";
|
||||
"cannot send message: (smtp) error when connecting" = "No se puede enviar el mensaje: (smtp) error de conexión";
|
||||
"cannot send message: (smtp) error when connecting" = "No se puede enviar el mensaje: (smtp) error de conexión";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Inkorgen";
|
||||
"DraftsFolderName" = "Utkast";
|
||||
"SieveFolderName" = "Filter";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Mappar"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxMailAccountActions.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse inc.
|
||||
* Copyright (C) 2007-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
@ -33,6 +33,8 @@
|
|||
NSString *draftsFolderName;
|
||||
NSString *sentFolderName;
|
||||
NSString *trashFolderName;
|
||||
NSString *otherUsersFolderName;
|
||||
NSString *sharedFoldersName;
|
||||
}
|
||||
|
||||
- (WOResponse *) listMailboxesAction;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxMailAccountActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007, 2011 Inverse inc.
|
||||
* Copyright (C) 2007-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
@ -30,6 +30,7 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoDraftObject.h>
|
||||
|
@ -39,6 +40,7 @@
|
|||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
|
||||
#import "../Common/WODirectAction+SOGo.h"
|
||||
|
||||
|
@ -54,6 +56,8 @@
|
|||
draftsFolderName = nil;
|
||||
sentFolderName = nil;
|
||||
trashFolderName = nil;
|
||||
otherUsersFolderName = nil;
|
||||
sharedFoldersName = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -65,6 +69,8 @@
|
|||
[draftsFolderName release];
|
||||
[sentFolderName release];
|
||||
[trashFolderName release];
|
||||
[otherUsersFolderName release];
|
||||
[sharedFoldersName release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -82,11 +88,17 @@
|
|||
[co draftsFolderNameInContext: context],
|
||||
[co sentFolderNameInContext: context],
|
||||
[co trashFolderNameInContext: context],
|
||||
[co otherUsersFolderNameInContext: context],
|
||||
[co sharedFoldersNameInContext: context],
|
||||
nil] stringsWithFormat: @"/%@"];
|
||||
ASSIGN (inboxFolderName, [specialFolders objectAtIndex: 0]);
|
||||
ASSIGN (draftsFolderName, [specialFolders objectAtIndex: 1]);
|
||||
ASSIGN (sentFolderName, [specialFolders objectAtIndex: 2]);
|
||||
ASSIGN (trashFolderName, [specialFolders objectAtIndex: 3]);
|
||||
ASSIGN(inboxFolderName, [specialFolders objectAtIndex: 0]);
|
||||
ASSIGN(draftsFolderName, [specialFolders objectAtIndex: 1]);
|
||||
ASSIGN(sentFolderName, [specialFolders objectAtIndex: 2]);
|
||||
ASSIGN(trashFolderName, [specialFolders objectAtIndex: 3]);
|
||||
if ([specialFolders count] > 4)
|
||||
ASSIGN(otherUsersFolderName, [specialFolders objectAtIndex: 4]);
|
||||
if ([specialFolders count] > 5)
|
||||
ASSIGN(sharedFoldersName, [specialFolders objectAtIndex: 5]);
|
||||
}
|
||||
|
||||
if ([folderName isEqualToString: inboxFolderName])
|
||||
|
@ -105,17 +117,46 @@
|
|||
|
||||
- (NSArray *) _jsonFolders: (NSEnumerator *) rawFolders
|
||||
{
|
||||
NSMutableArray *folders;
|
||||
NSString *currentFolder;
|
||||
NSString *currentFolder, *currentDisplayName, *currentFolderType, *login, *fullName;
|
||||
NSMutableArray *pathComponents;
|
||||
SOGoUserManager *userManager;
|
||||
NSDictionary *folderData;
|
||||
NSMutableArray *folders;
|
||||
|
||||
folders = [NSMutableArray array];
|
||||
while ((currentFolder = [rawFolders nextObject]))
|
||||
{
|
||||
currentFolderType = [self _folderType: currentFolder];
|
||||
|
||||
// We translate the "Other Users" and "Shared Folders" namespaces.
|
||||
// While we're at it, we also translate the user's mailbox names
|
||||
// to the full name of the person.
|
||||
if (otherUsersFolderName && [currentFolder hasPrefix: otherUsersFolderName])
|
||||
{
|
||||
// We have a string like /Other Users/lmarcotte/...
|
||||
pathComponents = [NSMutableArray arrayWithArray: [currentFolder pathComponents]];
|
||||
login = [pathComponents objectAtIndex: 2];
|
||||
userManager = [SOGoUserManager sharedUserManager];
|
||||
fullName = [userManager getCNForUID: login];
|
||||
[pathComponents removeObjectsInRange: NSMakeRange(0,3)];
|
||||
|
||||
currentDisplayName = [NSString stringWithFormat: @"/%@/%@/%@",
|
||||
[self labelForKey: @"OtherUsersFolderName"],
|
||||
(fullName != nil ? fullName : login),
|
||||
[pathComponents componentsJoinedByString: @"/"]];
|
||||
|
||||
}
|
||||
else if (sharedFoldersName && [currentFolder hasPrefix: sharedFoldersName])
|
||||
currentDisplayName = [NSString stringWithFormat: @"/%@%@", [self labelForKey: @"SharedFoldersName"],
|
||||
[currentFolder substringFromIndex: [sharedFoldersName length]]];
|
||||
else
|
||||
currentDisplayName = currentFolder;
|
||||
|
||||
folderData = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
currentFolder, @"path",
|
||||
[self _folderType: currentFolder], @"type",
|
||||
nil];
|
||||
currentFolderType, @"type",
|
||||
currentDisplayName, @"displayName",
|
||||
nil];
|
||||
[folders addObject: folderData];
|
||||
}
|
||||
|
||||
|
@ -170,7 +211,7 @@
|
|||
value = [[self request] formValueForKey: @"mailto"];
|
||||
if ([value length] > 0)
|
||||
{
|
||||
mailTo = [value componentsSeparatedByString: @","];
|
||||
mailTo = [[value stringByUnescapingURL] componentsSeparatedByString: @","];
|
||||
[headers setObject: mailTo forKey: @"to"];
|
||||
save = YES;
|
||||
}
|
||||
|
@ -178,7 +219,7 @@
|
|||
value = [[self request] formValueForKey: @"subject"];
|
||||
if ([value length] > 0)
|
||||
{
|
||||
[headers setObject: value forKey: @"subject"];
|
||||
[headers setObject: [value stringByUnescapingURL] forKey: @"subject"];
|
||||
save = YES;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2007-2010 Inverse inc.
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo.
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Вхідні";
|
||||
"DraftsFolderName" = "Чернетки";
|
||||
"SieveFolderName" = "Теки";
|
||||
"OtherUsersFolderName" = "Інші користувачі";
|
||||
"SharedFoldersName" = "Спільні теки";
|
||||
"Folders" = "Всі теки"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Помилка під час відправлення повідомлення: некоректні адреси всіх отримувачів.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Помила під час відправлення повідомлення: такі адреси некоректні:";
|
||||
"cannot send message: (smtp) error when connecting" = "Помилка під час відправлення повідомлення: SMTP-сервер не відповідає.";
|
||||
"cannot send message: (smtp) error when connecting" = "Помилка під час відправлення повідомлення: SMTP-сервер не відповідає.";
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
"InboxFolderName" = "Newydd";
|
||||
"DraftsFolderName" = "Draffts";
|
||||
"SieveFolderName" = "Ffilteri";
|
||||
"OtherUsersFolderName" = "Other Users";
|
||||
"SharedFoldersName" = "Shared Folders";
|
||||
"Folders" = "Ffolderi"; /* title line */
|
||||
|
||||
/* MailMoveToPopUp */
|
||||
|
@ -286,4 +288,4 @@
|
|||
/* Message sending */
|
||||
"cannot send message: (smtp) all recipients discarded" = "Cannot send message: all recipients are invalid.";
|
||||
"cannot send message (smtp) - recipients discarded:" = "Cannot send message. The following addresses are invalid:";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
"cannot send message: (smtp) error when connecting" = "Cannot send message: error when connecting to the SMTP server.";
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Copyright (C) 2007-2011 Inverse inc.
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo 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 Lesser General Public
|
||||
License for more details.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxJSClose.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse inc.
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxJSClose.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse inc.
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -459,7 +459,7 @@ validate_endbeforestart = "A data que você informou ocorre antes da data ini
|
|||
"Workweek days only" = "Somente semanas úteis";
|
||||
"Tasks in View" = "Tarefas na vista";
|
||||
|
||||
"eventDeleteConfirmation" = "Apagar permanentemente este evento.\nVocê gostaria de continuar?";
|
||||
"eventDeleteConfirmation" = "O(s) seguinte(s) evento(s) será(ão) apagado(s): \n%{0}\nGostaria de continuar?";
|
||||
"taskDeleteConfirmation" = "Apagar permanentemente esta tarefa.\nVocê gostaria de continuar?";
|
||||
|
||||
"You cannot remove nor unsubscribe from your personal calendar."
|
||||
|
|
|
@ -459,7 +459,7 @@ validate_endbeforestart = "La data/hora de començament és posterior a la d'
|
|||
"Workweek days only" = "Només dies laborables";
|
||||
"Tasks in View" = "Mostrar tasques";
|
||||
|
||||
"eventDeleteConfirmation" = "Aquest esdeveniment s'esborrarà definitivament. Voleu continuar?";
|
||||
"eventDeleteConfirmation" = "El següent esdeveniment (s) s'esborrarà: \n%{0}\nVoleu continuar?";
|
||||
"taskDeleteConfirmation" = "Aquesta tasca s'esborrarà definitivament. Voleu continuar?";
|
||||
|
||||
"You cannot remove nor unsubscribe from your personal calendar."
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue