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.sogo
maint-2.0.2
Ludovic Marcotte 2011-05-03 13:39:48 +00:00
commit 7d7bd78947
212 changed files with 4156 additions and 2412 deletions

233
ChangeLog
View File

@ -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):

35
NEWS
View File

@ -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)
--------------------

View File

@ -30,6 +30,7 @@ $(SOGOBACKEND)_OBJC_FILES += \
MAPIStoreAuthenticator.m \
MAPIStoreMapping.m \
MAPIStoreTypes.m \
MAPIStorePropertySelectors.m \
\
SOGoMAPIFSFolder.m \
SOGoMAPIFSMessage.m \

View File

@ -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)

View File

@ -25,7 +25,13 @@
#import "MAPIStoreGCSMessage.h"
@class iCalEvent;
@interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage
{
iCalEvent *event;
}
@end
#endif /* MAPISTORECALENDARMESSAGE_H */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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];

View File

@ -395,8 +395,7 @@ Class NSExceptionK, MAPIStoreMessageTableK, MAPIStoreFAIMessageTableK, MAPIStore
- (MAPIStoreMessage *) createMessage
{
[self subclassResponsibility: _cmd];
[self logWithFormat: @"ignored method: %s", __PRETTY_FUNCTION__];
return nil;
}

View File

@ -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];
}

View File

@ -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

View File

@ -48,6 +48,9 @@
- (void) openMessage: (struct mapistore_message *) msg;
/* helper getters */
- (int) getSMTPAddrType: (void **) data;
/* subclasses */
- (void) submit;
- (void) save;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -25,7 +25,12 @@
#import "MAPIStoreGCSMessage.h"
@class iCalToDo;
@interface MAPIStoreTasksMessage : MAPIStoreGCSMessage
{
iCalToDo *task;
}
@end

View File

@ -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;

View File

@ -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)

View File

@ -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)
{

View File

@ -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];

View File

@ -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);

View File

@ -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);
}

View File

@ -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()

View File

@ -51,6 +51,7 @@
- (BOOL) hasEndDate;
- (NSString *) duration;
- (void) setDuration: (NSString *) _value;
- (BOOL) hasDuration;
- (NSTimeInterval) durationAsTimeInterval;

View File

@ -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}\".";

View File

@ -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}\".";

View File

@ -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}\".";

View File

@ -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}\".";

View File

@ -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}\".";

View File

@ -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}\".";

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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.

View File

@ -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

View File

@ -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 */

View File

@ -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.

View File

@ -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.

View File

@ -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}\".";

View File

@ -31,6 +31,7 @@
@interface iCalEvent (SOGoExtensions)
- (BOOL) isStillRelevant;
- (unsigned int) occurenceInterval;
- (NSMutableDictionary *) quickRecord;
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;

View File

@ -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;
}

View File

@ -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];
}

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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;

View File

@ -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];

View File

@ -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>
*

View File

@ -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]];

View File

@ -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>
*

View File

@ -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]];

View File

@ -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

View File

@ -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
{

View File

@ -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"];

View File

@ -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>
*

View File

@ -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

View File

@ -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];

View File

@ -19,7 +19,8 @@ $(TEST_TOOL)_OBJC_FILES += \
\
TestSBJsonParser.m \
\
TestNGMimeAddressHeaderFieldGenerator.m
TestNGMimeAddressHeaderFieldGenerator.m \
TestNSString+URLEscaping.m
TOOL_NAME = $(TEST_TOOL)

View File

@ -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

View File

@ -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];

View File

@ -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 += \

View File

@ -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;

View File

@ -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];
}

View File

@ -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__ */

View File

@ -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 */

View File

@ -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 = {

View File

@ -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.";

View File

@ -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.";

View File

@ -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.";

View File

@ -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.";

View File

@ -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.";

View File

@ -165,6 +165,8 @@
"InboxFolderName" = "Courrier entrant";
"DraftsFolderName" = "Brouillons";
"SieveFolderName" = "Filtres";
"OtherUsersFolderName" = "Autres utilisateurs";
"SharedFoldersName" = "Dossiers partagés";
"Folders" = "Dossiers"; /* title line */
/* MailMoveToPopUp */

View File

@ -165,6 +165,8 @@
"InboxFolderName" = "Posteingang";
"DraftsFolderName" = "Entwürfe";
"SieveFolderName" = "Filter";
"Other Users" = "Andere Benutzer";
"Shared Folders" = "Gemeinsame Ordner";
"Folders" = "Ordner"; /* title line */
/* MailMoveToPopUp */

View File

@ -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.";

View File

@ -165,6 +165,8 @@
"InboxFolderName" = "Posta in arrivo";
"DraftsFolderName" = "Bozze";
"SieveFolderName" = "Filtri";
"OtherUsersFolderName" = "Altri Utenti";
"SharedFoldersName" = "Cartelle Condivise";
"Folders" = "Cartelle"; /* title line */
/* MailMoveToPopUp */

View File

@ -165,6 +165,8 @@
"InboxFolderName" = "Innboks";
"DraftsFolderName" = "Kladder";
"SieveFolderName" = "Filter";
"OtherUsersFolderName" = "Other Users";
"SharedFoldersName" = "Shared Folders";
"Folders" = "Mapper"; /* title line */
/* MailMoveToPopUp */

View File

@ -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.";

View File

@ -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.";

View File

@ -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";

View File

@ -165,6 +165,8 @@
"InboxFolderName" = "Inkorgen";
"DraftsFolderName" = "Utkast";
"SieveFolderName" = "Filter";
"OtherUsersFolderName" = "Other Users";
"SharedFoldersName" = "Shared Folders";
"Folders" = "Mappar"; /* title line */
/* MailMoveToPopUp */

View File

@ -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;

View File

@ -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;
}

View File

@ -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.

View File

@ -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-сервер не відповідає.";

View File

@ -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.";

View File

@ -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.

View File

@ -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>
*

View File

@ -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>
*

View File

@ -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."

View File

@ -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