Merge to 2.2.0

This commit is contained in:
Francis Lachapelle
2014-02-24 16:54:11 -05:00
399 changed files with 16495 additions and 3793 deletions
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/NSObject.h>
@interface ActiveSyncProduct : NSObject
{
}
@end
@implementation ActiveSyncProduct
@end /* ActiveSyncProduct */
+39
View File
@@ -0,0 +1,39 @@
# GNUstep makefile
include common.make
BUNDLE_NAME = ActiveSync
ActiveSync_PRINCIPAL_CLASS = ActiveSyncProduct
ActiveSync_OBJC_FILES = \
ActiveSyncProduct.m \
iCalEvent+ActiveSync.m \
iCalRecurrenceRule+ActiveSync.m \
iCalTimeZone+ActiveSync.m \
iCalToDo+ActiveSync.m \
NSCalendarDate+ActiveSync.m \
NSData+ActiveSync.m \
NSDate+ActiveSync.m \
NGDOMElement+ActiveSync.m \
NGMimeMessage+ActiveSync.m \
NGVCard+ActiveSync.m \
NSString+ActiveSync.m \
SOGoActiveSyncDispatcher.m \
SOGoActiveSyncDispatcher+Sync.m \
SOGoMailObject+ActiveSync.m \
SoObjectWebDAVDispatcher+ActiveSync.m
ActiveSync_RESOURCE_FILES += \
product.plist
ADDITIONAL_OBJCFLAGS += -Wno-deprecated-declarations
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/ -I../SoObjects/
ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/
ADDITIONAL_INCLUDE_DIRS += -I/usr/include/libwbxml-1.0/
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -lwbxml2
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/bundle.make
-include GNUmakefile.postamble
+1
View File
@@ -0,0 +1 @@
# compilation settings
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+44
View File
@@ -0,0 +1,44 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NGDOMELEMENTACTIVESYNC_H__
#define __NGDOMELEMENTACTIVESYNC_H__
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
@class NSDictionary;
@interface NGDOMElement (ActiveSync)
- (NSDictionary *) applicationData;
@end
#endif // NGDOMELEMENTACTIVESYNC
+158
View File
@@ -0,0 +1,158 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "NGDOMElement+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
static NSArray *asElementArray = nil;
@implementation NGDOMElement (ActiveSync)
//
// We must handle "inner data" like this:
//
// <ApplicationData>
// <Flag xmlns="Email:">
// <FlagStatus>2</FlagStatus>
// <FlagType>Flag for follow up</FlagType>
// </Flag>
// </ApplicationData>
//
// and stuff like that:
//
// <Attendees xmlns="Calendar:">
// <Attendee>
// <Attendee_Email>sogo1@example.com</Attendee_Email>
// <Attendee_Name>John Doe</Attendee_Name>
// <Attendee_Status>5</Attendee_Status>
// <Attendee_Type>1</Attendee_Type>
// </Attendee>
// <Attendee>
// <Attendee_Email>sogo2@example.com</Attendee_Email>
// <Attendee_Name>Balthazar César</Attendee_Name>
// <Attendee_Status>5</Attendee_Status>
// <Attendee_Type>1</Attendee_Type>
// </Attendee>
// <Attendee>
// <Attendee_Email>sogo3@example.com</Attendee_Email>
// <Attendee_Name>Wolfgang Fritz</Attendee_Name>
// <Attendee_Status>5</Attendee_Status>
// <Attendee_Type>1</Attendee_Type>
// </Attendee>
// </Attendees>
//
- (NSDictionary *) applicationData
{
NSMutableDictionary *data;
id <DOMNodeList> children;
id <DOMElement> element;
int i, count;
if (!asElementArray)
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil];
data = [NSMutableDictionary dictionary];
children = [self childNodes];
for (i = 0; i < [children length]; i++)
{
element = [children objectAtIndex: i];
if ([element nodeType] == DOM_ELEMENT_NODE)
{
NSString *tag;
id value;
tag = [element tagName];
count = [(NSArray *)[element childNodes] count];
// Handle inner data - see above for samples
if (count > 2)
{
NSMutableArray *innerElements;
id <DOMElement> innerElement;
NSArray *childNodes;
NSString *innerTag;
BOOL same;
int j;
childNodes = (NSArray *)[element childNodes];
innerElements = [NSMutableArray array];
innerTag = nil;
same = YES;
for (j = 1; j < count; j++)
{
innerElement = [childNodes objectAtIndex: j];
if ([innerElement nodeType] == DOM_ELEMENT_NODE)
{
if (!innerTag)
innerTag = [innerElement tagName];
if ([innerTag isEqualToString: [innerElement tagName]])
{
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
}
else
{
same = NO;
break;
}
}
}
if (same && [asElementArray containsObject: innerTag])
value = innerElements;
else
{
value = [(NGDOMElement *)element applicationData];
// Don't set empty values like Foo = {}
if (![value count])
value = nil;
}
}
else
value = [[element firstChild] textValue];
if (value && tag)
[data setObject: value forKey: tag];
}
}
return data;
}
@end
+43
View File
@@ -0,0 +1,43 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NGMIMEMESSAGEACTIVESYNC_H__
#define __NGMIMEMESSAGEACTIVESYNC_H__
#import <NGMail/NGMimeMessage.h>
@class NSArray;
@interface NGMimeMessage (ActiveSync)
- (NSArray *) allRecipients;
@end
#endif
+62
View File
@@ -0,0 +1,62 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NGMimeMessage+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <SOGo/NSString+Utilities.h>
@implementation NGMimeMessage (ActiveSync)
- (NSArray *) allRecipients
{
NSMutableArray *recipients;
NSEnumerator *enumerator;
NSString *s;
recipients = [NSMutableArray array];
enumerator = [[self headersForKey: @"to"] objectEnumerator];
while ((s = [enumerator nextObject]))
{
[recipients addObject: [s pureEMailAddress]];
}
enumerator = [[self headersForKey: @"cc"] objectEnumerator];
while ((s = [enumerator nextObject]))
{
[recipients addObject: [s pureEMailAddress]];
}
return recipients;
}
@end
+47
View File
@@ -0,0 +1,47 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NGVCARDACTIVESYNC_H__
#define __NGVCARDACTIVESYNC_H__
#import <NGCards/NGVCard.h>
@class NSDictionary;
@class NSString;
@class WOContext;
@interface NGVCard (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context;
@end
#endif
+308
View File
@@ -0,0 +1,308 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NGVCard+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <NGExtensions/NSString+misc.h>
#import <NGCards/CardElement.h>
#import <Contacts/NGVCard+SOGo.h>
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
@implementation NGVCard (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
CardElement *n, *homeAdr, *workAdr;
NSArray *emails, *addresses;
NSMutableString *s;
id o;
int i;
s = [NSMutableString string];
n = [self n];
if ((o = [n flattenedValueAtIndex: 0 forKey: @""]))
[s appendFormat: @"<LastName xmlns=\"Contacts:\">%@</LastName>", [o activeSyncRepresentationInContext: context]];
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
[s appendFormat: @"<FirstName xmlns=\"Contacts:\">%@</FirstName>", [o activeSyncRepresentationInContext: context]];
if ((o = [self workCompany]))
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
if ((o = [self title]))
[s appendFormat: @"<JobTitle xmlns=\"Contacts:\">%@</JobTitle>", [o activeSyncRepresentationInContext: context]];
if ((o = [self preferredEMail]))
[s appendFormat: @"<Email1Address xmlns=\"Contacts:\">%@</Email1Address>", o];
// Secondary email addresses (2 and 3)
emails = [self secondaryEmails];
for (i = 0; i < [emails count]; i++)
{
o = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
[s appendFormat: @"<Email%dAddress xmlns=\"Contacts:\">%@</Email%dAddress>", i+2, o, i+2];
if (i == 1)
break;
}
// Telephone numbers
if ((o = [self workPhone]) && [o length])
[s appendFormat: @"<BusinessPhoneNumber xmlns=\"Contacts:\">%@</BusinessPhoneNumber>", [o activeSyncRepresentationInContext: context]];
if ((o = [self homePhone]) && [o length])
[s appendFormat: @"<HomePhoneNumber xmlns=\"Contacts:\">%@</HomePhoneNumber>", [o activeSyncRepresentationInContext: context]];
if ((o = [self fax]) && [o length])
[s appendFormat: @"<BusinessFaxNumber xmlns=\"Contacts:\">%@</BusinessFaxNumber>", [o activeSyncRepresentationInContext: context]];
if ((o = [self mobile]) && [o length])
[s appendFormat: @"<MobilePhoneNumber xmlns=\"Contacts:\">%@</MobilePhoneNumber>", [o activeSyncRepresentationInContext: context]];
if ((o = [self pager]) && [o length])
[s appendFormat: @"<PagerNumber xmlns=\"Contacts:\">%@</PagerNumber>", [o activeSyncRepresentationInContext: context]];
// Home Address
addresses = [self childrenWithTag: @"adr"
andAttribute: @"type"
havingValue: @"home"];
if ([addresses count])
{
homeAdr = [addresses objectAtIndex: 0];
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"<HomeCity xmlns=\"Contacts:\">%@</HomeCity>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""]))
[s appendFormat: @"<HomeState xmlns=\"Contacts:\">%@</HomeState>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""]))
[s appendFormat: @"<HomePostalCode xmlns=\"Contacts:\">%@</HomePostalCode>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 6 forKey: @""]))
[s appendFormat: @"<HomeCountry xmlns=\"Contacts:\">%@</HomeCountry>", [o activeSyncRepresentationInContext: context]];
}
// Work Address
addresses = [self childrenWithTag: @"adr"
andAttribute: @"type"
havingValue: @"work"];
if ([addresses count])
{
workAdr = [addresses objectAtIndex: 0];
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [o activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"<BusinessCity xmlns=\"Contacts:\">%@</BusinessCity>", [o activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 4 forKey: @""]))
[s appendFormat: @"<BusinessState xmlns=\"Contacts:\">%@</BusinessState>", [o activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 5 forKey: @""]))
[s appendFormat: @"<BusinessPostalCode xmlns=\"Contacts:\">%@</BusinessPostalCode>", [o activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 6 forKey: @""]))
[s appendFormat: @"<BusinessCountry xmlns=\"Contacts:\">%@</BusinessCountry>", [o activeSyncRepresentationInContext: context]];
}
// Other, less important fields
if ((o = [self birthday]))
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationWithoutSeparatorsInContext: context]];
if ((o = [self note]))
{
o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
return s;
}
//
//
//
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
CardElement *element;
id o;
// Contact's note
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setNote: o];
// Birthday
if ((o = [theValues objectForKey: @"Birthday"]))
{
o = [o calendarDate];
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]];
}
//
// Business address information
//
// BusinessStreet
// BusinessCity
// BusinessPostalCode
// BusinessState
// BusinessCountry
//
element = [self elementWithTag: @"adr" ofType: @"work"];
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessStreet"]
atIndex: 2 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
atIndex: 6 forKey: @""];
//
// Home address information
//
// HomeStreet
// HomeCity
// HomePostalCode
// HomeState
// HomeCountry
//
element = [self elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeStreet"]
atIndex: 2 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
atIndex: 6 forKey: @""];
// Company's name
if ((o = [theValues objectForKey: @"CompanyName"]))
{
[self setOrg: o units: nil];
}
// Email addresses
if ((o = [theValues objectForKey: @"Email1Address"]))
{
[self addEmail: o types: [NSArray arrayWithObject: @"pref"]];
}
if ((o = [theValues objectForKey: @"Email2Address"]))
{
[self addEmail: o types: nil];
}
if ((o = [theValues objectForKey: @"Email3Address"]))
{
[self addEmail: o types: nil];
}
// Formatted name
// MiddleName
// Suffix (II)
// Title (Mr.)
[self setFn: [theValues objectForKey: @"FileAs"]];
[self setNWithFamily: [theValues objectForKey: @"LastName"]
given: [theValues objectForKey: @"FirstName"]
additional: nil prefixes: nil suffixes: nil];
// IM information
[[self uniqueChildWithTag: @"x-aim"]
setSingleValue: [theValues objectForKey: @"IMAddress"]
forKey: @""];
//
// Phone numbrrs
//
element = [self elementWithTag: @"tel" ofType: @"work"];
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
element = [self elementWithTag: @"tel" ofType: @"home"];
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
element = [self elementWithTag: @"tel" ofType: @"cell"];
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
element = [self elementWithTag: @"tel" ofType: @"fax"];
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
element = [self elementWithTag: @"tel" ofType: @"pager"];
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
// Job's title
if ((o = [theValues objectForKey: @"JobTitle"]))
{
[self setTitle: o];
}
// WebPage (work)
if ((o = [theValues objectForKey: @"WebPage"]))
{
[[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: o forKey: @""];
}
}
@end
+43
View File
@@ -0,0 +1,43 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NSCALENDARDATEACTIVESYNC_H__
#define __NSCALENDARDATEACTIVESYNC_H__
#import <Foundation/NSCalendarDate.h>
@class NSString;
@interface NSCalendarDate (ActiveSync)
+ (NSCalendarDate *) dateFromFilterType: (NSString *) theFilterType;
@end
#endif
+87
View File
@@ -0,0 +1,87 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NSCalendarDate+ActiveSync.h"
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#define ONE_DAY 86400
@implementation NSCalendarDate (ActiveSync)
//
// See http://msdn.microsoft.com/en-us/library/gg709713(v=exchg.80).aspx for available types
//
+ (NSCalendarDate *) dateFromFilterType: (NSString *) theFilterType
{
NSCalendarDate *d;
d = [NSCalendarDate calendarDate];
if (d)
{
int value;
switch ([theFilterType intValue])
{
case 1:
value = ONE_DAY;
break;
case 2:
value = 3 * ONE_DAY;
break;
case 3:
value = 7 * ONE_DAY;
break;
case 4:
value = 14 * ONE_DAY;
break;
case 5:
value = 30 * ONE_DAY;
break;
case 6:
value = 90 * ONE_DAY;
break;
case 7:
value = 180 * ONE_DAY;
break;
case 0:
case 8:
default:
return nil;
}
return [d initWithTimeIntervalSinceNow: -value];
}
return d;
}
@end
+46
View File
@@ -0,0 +1,46 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NSDATAACTIVESYNC_H__
#define __NSDATAACTIVESYNC_H__
#import <Foundation/NSData.h>
@class NSString;
@class WOContext;
@interface NSData (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (NSData *) wbxml2xml;
- (NSData *) xml2wbxml;
@end
#endif
+152
View File
@@ -0,0 +1,152 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NSData+ActiveSync.h"
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSString.h>
#import <NGExtensions/NGBase64Coding.h>
#include <wbxml/wbxml.h>
#include <wbxml/wbxml_conv.h>
#include <wbxml/wbxml_errors.h>
#define WBXMLDEBUG 0
@implementation NSData (ActiveSync)
- (void) _dumpToFile
{
NSString *path;
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
[self writeToFile: path atomically: YES];
NSLog(@"Original data written to: %@", path);
}
//
// Encodes the data in base64 and strip newline characters
//
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""];
}
- (NSData *) wbxml2xml
{
WBXMLGenXMLParams params;
NSData *data;
unsigned int wbxml_len, xml_len, ret;
unsigned char *wbxml, *xml;
wbxml = (unsigned char*)[self bytes];
wbxml_len = [self length];
xml = NULL;
xml_len = 0;
params.lang = WBXML_LANG_ACTIVESYNC;
params.gen_type = WBXML_GEN_XML_INDENT;
params.indent = 1;
params.keep_ignorable_ws = FALSE;
ret = wbxml_conv_wbxml2xml_withlen(wbxml, wbxml_len, &xml, &xml_len, &params);
if (ret != WBXML_OK)
{
NSLog(@"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret));
[self _dumpToFile];
return nil;
}
data = [[NSData alloc] initWithBytes: xml length: xml_len];
#if WBXMLDEBUG
[data writeToFile: @"/tmp/protocol.decoded" atomically: YES];
#endif
free(xml);
return AUTORELEASE(data);
}
- (NSData *) xml2wbxml
{
WBXMLConvXML2WBXML *conv;
NSData *data;
unsigned int wbxml_len, xml_len, ret;
unsigned char *wbxml, *xml;
xml = (unsigned char*)[self bytes];
xml_len = [self length];
wbxml = NULL;
wbxml_len = 0;
conv = NULL;
ret = wbxml_conv_xml2wbxml_create(&conv);
if (ret != WBXML_OK)
{
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
[self _dumpToFile];
return nil;
}
wbxml_conv_xml2wbxml_enable_preserve_whitespaces(conv);
// From libwbxml's changelog in v0.11.0: "The public ID is set to unknown and the DTD is not included. This is required for Microsoft ActiveSync."
wbxml_conv_xml2wbxml_disable_public_id(conv);
wbxml_conv_xml2wbxml_disable_string_table(conv);
ret = wbxml_conv_xml2wbxml_run(conv, xml, xml_len, &wbxml, &wbxml_len);
if (ret != WBXML_OK)
{
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
[self _dumpToFile];
free(wbxml);
wbxml_conv_xml2wbxml_destroy(conv);
return nil;
}
data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len];
#if WBXMLDEBUG
[data writeToFile: @"/tmp/protocol.encoded" atomically: YES];
#endif
free(wbxml);
wbxml_conv_xml2wbxml_destroy(conv);
return AUTORELEASE(data);
}
@end
+45
View File
@@ -0,0 +1,45 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NSDATEACTIVESYNC_H__
#define __NSDATEACTIVESYNC_H__
#import <Foundation/NSDate.h>
@class NSString;
@class WOContext;
@interface NSDate (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (NSString *) activeSyncRepresentationWithoutSeparatorsInContext: (WOContext *) context;
@end
#endif
+52
View File
@@ -0,0 +1,52 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "NSDate+ActiveSync.h"
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
@implementation NSDate (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
return [self descriptionWithCalendarFormat: @"%Y-%m-%d-T%H:%M:%S.%FZ" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
}
//
// From [MS-ASDTYPE].pdf - section 2.3 "Dates and times in calendar items MUST NOT include punctuation separators."
//
- (NSString *) activeSyncRepresentationWithoutSeparatorsInContext: (WOContext *) context
{
return [self descriptionWithCalendarFormat: @"%Y%m%dT%H%M%SZ" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
}
@end
+54
View File
@@ -0,0 +1,54 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NSSTRINGACTIVESYNC_H__
#define __NSSTRINGACTIVESYNC_H__
#import <Foundation/NSString.h>
#include "SOGoActiveSyncConstants.h"
@class NSCalendarDate;
@class NSData;
@class WOContext;
@interface NSString (ActiveSync)
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType;
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (int) activeSyncFolderType;
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
- (NSCalendarDate *) calendarDate;
- (NSString *) deviceId;
- (NSString *) command;
- (NSData *) convertHexStringToBytes;
@end
#endif
+283
View File
@@ -0,0 +1,283 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "NSString+ActiveSync.h"
#include <Foundation/NSArray.h>
#include <Foundation/NSCalendarDate.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <SOGo/NSString+Utilities.h>
#include <NGExtensions/NSString+misc.h>
@implementation NSString (ActiveSync)
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
{
if (folderType == ActiveSyncEventFolder)
{
int len;
len = [self length];
if (len > 4 && [self hasSuffix: @".ics"])
return [self substringToIndex: len-4];
else
return [NSString stringWithFormat: @"%@.ics", self];
}
return self;
}
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSString *s;
s = [self stringByEscapingHTMLString];
return [[s componentsSeparatedByCharactersInSet: [self safeCharacterSet]]
componentsJoinedByString: @""];
}
- (int) activeSyncFolderType
{
if ([self isEqualToString: @"inbox"])
return 2;
else if ([self isEqualToString: @"draft"])
return 3;
else if ([self isEqualToString: @"sent"])
return 5;
else if ([self isEqualToString: @"trash"])
return 4;
return 12;
}
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
{
NSString *realCollectionId, *v;
*folderType = ActiveSyncGenericFolder;
v = [self stringByUnescapingURL];
if ([v hasPrefix: @"vevent/"])
{
realCollectionId = [v substringFromIndex: 7];
*folderType = ActiveSyncEventFolder;
}
else if ([v hasPrefix: @"vtodo/"])
{
realCollectionId = [v substringFromIndex: 6];
*folderType = ActiveSyncTaskFolder;
}
else if ([v hasPrefix: @"vcard/"])
{
realCollectionId = [v substringFromIndex: 6];
*folderType = ActiveSyncContactFolder;
}
else if ([v hasPrefix: @"mail/"])
{
realCollectionId = [[v stringByUnescapingURL] substringFromIndex: 5];
*folderType = ActiveSyncMailFolder;
}
else
{
realCollectionId = nil;
}
return realCollectionId;
}
//
// 2014-01-16T05:00:00.000Z
//
// See http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSCalendarDate.html#method$NSCalendarDate-initWithString$calendarFormat$ for the format details.
//
- (NSCalendarDate *) calendarDate
{
id o;
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y%m%dT%H%M%SZ"];
if (!o)
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ"];
return o;
}
- (NSString *) _valueForParameter: (NSString *) theParameter
{
NSArray *components;
NSString *s;
int i;
components = [[[self componentsSeparatedByString: @"/"] lastObject] componentsSeparatedByString: @"&"];
for (i = 0; i < [components count]; i++)
{
s = [components objectAtIndex: i];
if ([[s uppercaseString] hasPrefix: theParameter])
return [s substringFromIndex: [theParameter length]];
}
return nil;
}
//
// This method extracts the "DeviceId" from a URI:
//
// /SOGo/Microsoft-Server-ActiveSync?Cmd=FolderSync&User=sogo10&DeviceId=SEC17CD1A3E9E3F2&DeviceType=SAMSUNGSGHI317M
//
- (NSString *) deviceId
{
NSString *s;
s = [self _valueForParameter: @"DEVICEID="];
if (!s)
s = @"Unknown";
return s;
}
//
//
//
- (NSString *) command
{
NSString *s;
s = [self _valueForParameter: @"CMD="];
if (!s)
s = @"Unknown";
return s;
}
//
// FIXME: combine with our OpenChange code.
//
- (char) _decodeHexByte: (char) byteChar
{
char newByte;
if (byteChar >= 48 && byteChar <= 57)
newByte = (uint8_t) byteChar - 48;
else if (byteChar >= 65 && byteChar <= 70)
newByte = (uint8_t) byteChar - 55;
else if (byteChar >= 97 && byteChar <= 102)
newByte = (uint8_t) byteChar - 87;
else
newByte = -1;
return newByte;
}
//
// FIXME: combine with our OpenChange code.
//
- (BOOL) _decodeHexByte: (uint8_t *) byte
atPos: (NSUInteger) pos
{
BOOL error = NO;
char newByte;
unichar byteChar;
byteChar = [self characterAtIndex: pos];
if (byteChar < 256)
{
newByte = [self _decodeHexByte: (char) byteChar];
if (newByte == -1)
error = YES;
else
*byte = newByte;
}
else
error = YES;
return error;
}
//
// FIXME: combine with our OpenChange code.
//
- (BOOL) _decodeHexPair: (uint8_t *) byte
atPos: (NSUInteger) pos
{
BOOL error;
uint8_t lowValue, highValue;
error = [self _decodeHexByte: &highValue atPos: pos];
if (!error)
{
error = [self _decodeHexByte: &lowValue atPos: pos + 1];
if (!error)
*byte = highValue << 4 | lowValue;
}
return error;
}
//
// FIXME: combine with our OpenChange code.
//
- (NSData *) convertHexStringToBytes
{
NSUInteger count, strLen, bytesLen;
uint8_t *bytes, *currentByte;
NSData *decoded = nil;
BOOL error = NO;
strLen = [self length];
if ((strLen % 2) == 0)
{
bytesLen = strLen / 2;
bytes = NSZoneCalloc (NULL, bytesLen, sizeof (uint8_t));
currentByte = bytes;
for (count = 0; !error && count < strLen; count += 2)
{
error = [self _decodeHexPair: currentByte atPos: count];
currentByte++;
}
if (error)
NSZoneFree (NULL, bytes);
else
decoded = [NSData dataWithBytesNoCopy: bytes
length: bytesLen
freeWhenDone: YES];
}
return decoded;
}
@end
+12
View File
@@ -0,0 +1,12 @@
In order to use this software in production environments, you need to
get a proper usage license from Microsoft. Please contact them directly
to negotiate the fees associated to your user base.
To contact Microsoft, please visit:
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
and send an email to iplicreq@microsoft.com
Inverse inc. provides this software for free, but is not responsible
for anything related to its usage.
+42
View File
@@ -0,0 +1,42 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SOGOACTIVESYNCCONSTANTS_H__
#define __SOGOACTIVESYNCCONSTANTS_H__
typedef enum
{
ActiveSyncGenericFolder = 0,
ActiveSyncMailFolder = 1,
ActiveSyncContactFolder = 2,
ActiveSyncEventFolder = 3,
ActiveSyncTaskFolder = 4,
} SOGoMicrosoftActiveSyncFolderType;
#endif
@@ -0,0 +1,47 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SOGOACTIVESYNCDISPATCHERSYNC_H__
#define __SOGOACTIVESYNCDISPATCHERSYNC_H__
#import "SOGoActiveSyncDispatcher.h"
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
@class WOResponse;
@interface SOGoActiveSyncDispatcher (Sync)
- (void) processSync: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse;
@end
#endif // SOGOACTIVESYNCDISPATCHERSYNC
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/NSObject.h>
#include "SOGoActiveSyncConstants.h"
@class NSException;
@interface SOGoActiveSyncDispatcher : NSObject
{
id context;
}
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
- (NSException *) dispatchRequest: (id) theRequest
inResponse: (id) theResponse
context: (id) theContext;
@end
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SOGOMAILOBJECTACTIVESYNC_H__
#define __SOGOMAILOBJECTACTIVESYNC_H__
#import <Mailer/SOGoMailObject.h>
@class iCalCalendar;
@class NSDictionary;
@class WOContext;
@interface SOGoMailObject (ActiveSync)
- (iCalCalendar *) calendarFromIMIPMessage;
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context;
@end
#endif
+638
View File
@@ -0,0 +1,638 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "SOGoMailObject+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTimeZone.h>
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSString+Encoding.h>
#import <NGImap4/NGImap4Envelope.h>
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#include "iCalTimeZone+ActiveSync.h"
#include "NSData+ActiveSync.h"
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
#include <Appointments/iCalEntityObject+SOGo.h>
#include <Appointments/iCalPerson+SOGo.h>
#include <Mailer/NSString+Mail.h>
#include <Mailer/SOGoMailBodyPart.h>
#include <SOGo/SOGoUser.h>
typedef struct {
uint32_t dwLowDateTime;
uint32_t dwHighDateTime;
} FILETIME;
struct GlobalObjectId {
uint8_t ByteArrayID[16];
uint8_t YH;
uint8_t YL;
uint8_t Month;
uint8_t D;
FILETIME CreationTime;
uint8_t X[8];
uint32_t Size;
uint8_t* Data;
};
@implementation SOGoMailObject (ActiveSync)
//
//
//
- (void) _setInstanceDate: (struct GlobalObjectId *) newGlobalId
fromDate: (NSCalendarDate *) instanceDate
{
uint16_t year;
if (instanceDate)
{
//[instanceDate setTimeZone: timeZone];
year = [instanceDate yearOfCommonEra];
newGlobalId->YH = year >> 8;
newGlobalId->YL = year & 0xff;
newGlobalId->Month = [instanceDate monthOfYear];
newGlobalId->D = [instanceDate dayOfMonth];
}
}
//
// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx
//
- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event
{
NSData *binPrefix, *globalObjectId, *uidAsASCII;
NSString *prefix, *uid;
struct GlobalObjectId newGlobalId;
const char *bytes;
prefix = @"040000008200e00074c5b7101a82e008";
// dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00"
uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 };
uid = [event uid];
binPrefix = [prefix convertHexStringToBytes];
[binPrefix getBytes: &newGlobalId.ByteArrayID];
[self _setInstanceDate: &newGlobalId
fromDate: [event recurrenceId]];
uidAsASCII = [uid dataUsingEncoding: NSASCIIStringEncoding];
bytes = [uidAsASCII bytes];
// 0x0c is the size of our dataPrefix
newGlobalId.Size = 0x0c + [uidAsASCII length];
newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t));
memcpy(newGlobalId.Data, dataPrefix, 0x0c);
memcpy(newGlobalId.Data + 0x0c, bytes, newGlobalId.Size - 0x0c);
globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)];
free(newGlobalId.Data);
return [globalObjectId autorelease];
}
//
// For debugging purposes...
//
- (NSString *) _uidFromGlobalObjectId: (NSData *) objectId
{
NSString *uid;
struct GlobalObjectId *newGlobalId;
NSUInteger length;
uint8_t *bytes;
length = [objectId length];
uid = nil;
bytes = malloc(length*sizeof(uint8_t));
[objectId getBytes: bytes length: length];
newGlobalId = (struct GlobalObjectId *)bytes;
// We must take the offset (dataPrefix) into account
uid = [[NSString alloc] initWithBytes: newGlobalId->Data+12 length: newGlobalId->Size-12 encoding: NSASCIIStringEncoding];
free(bytes);
return AUTORELEASE(uid);
}
//
//
//
- (NSString *) _emailAddressesFrom: (NSArray *) enveloppeAddresses
{
NGImap4EnvelopeAddress *address;
NSMutableArray *addresses;
NSString *email, *rc;
int i, max;
rc = nil;
max = [enveloppeAddresses count];
if (max > 0)
{
addresses = [NSMutableArray array];
for (i = 0; i < max; i++)
{
address = [enveloppeAddresses objectAtIndex: i];
email = [NSString stringWithFormat: @"\"%@\" <%@>", [address personalName], [address baseEMail]];
if (email)
[addresses addObject: email];
}
rc = [addresses componentsJoinedByString: @", "];
}
return rc;
}
//
//
//
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
{
NSString *key, *plainKey, *htmlKey, *type, *subtype;
NSDictionary *textParts, *part;
NSEnumerator *e;
NSData *d;
textParts = [self fetchPlainTextParts];
e = [textParts keyEnumerator];
plainKey = nil;
htmlKey = nil;
d = nil;
while ((key = [e nextObject]))
{
part = [self lookupInfoForBodyPart: key];
type = [part valueForKey: @"type"];
subtype = [part valueForKey: @"subtype"];
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
htmlKey = key;
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
plainKey = key;
}
if (theType == 2)
{
d = [[self fetchPlainTextParts] objectForKey: htmlKey];
}
else if (theType == 1)
{
d = [[self fetchPlainTextParts] objectForKey: plainKey];
}
return d;
}
//
//
//
- (NSData *) _preferredBodyDataUsingType: (int) theType
nativeType: (int *) theNativeType
{
NSString *type, *subtype, *encoding;
NSData *d;
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
d = nil;
// We determine the native type
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
*theNativeType = 1;
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
*theNativeType = 2;
else if ([type isEqualToString: @"multipart"])
*theNativeType = 4;
// We get the right part based on the preference
if (theType == 1 || theType == 2)
{
if ([type isEqualToString: @"text"])
{
d = [[self fetchPlainTextParts] objectForKey: @""];
// We check if we have base64 encoded parts. If so, we just
// un-encode them before using them
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
if ([encoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
d = [d dataByDecodingBase64];
// Check if we must convert html->plain
if (theType == 1 && [subtype isEqualToString: @"html"])
{
NSString *s;
s = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
AUTORELEASE(s);
s = [s htmlToText];
d = [s dataUsingEncoding: NSUTF8StringEncoding];
}
}
else if ([type isEqualToString: @"multipart"])
{
d = [self _preferredBodyDataInMultipartUsingType: theType];
}
}
else if (theType == 4)
{
d = [self content];
}
return d;
}
//
//
//
- (iCalCalendar *) calendarFromIMIPMessage
{
NSDictionary *part;
NSArray *parts;
int i;
// We check if we have at least 2 parts and if one of them is a text/calendar
parts = [[self bodyStructure] objectForKey: @"parts"];
if ([parts count] > 1)
{
for (i = 0; i < [parts count]; i++)
{
part = [parts objectAtIndex: i];
if ([[part objectForKey: @"type"] isEqualToString: @"text"] &&
[[part objectForKey: @"subtype"] isEqualToString: @"calendar"])
{
id bodyPart;
bodyPart = [self lookupImap4BodyPartKey: [NSString stringWithFormat: @"%d", i+1]
inContext: self->context];
if (bodyPart)
{
iCalCalendar *calendar;
NSData *calendarData;
calendarData = [bodyPart fetchBLOB];
calendar = nil;
NS_DURING
calendar = [iCalCalendar parseSingleFromSource: calendarData];
NS_HANDLER
calendar = nil;
NS_ENDHANDLER
return calendar;
}
}
}
}
return nil;
}
//
//
//
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
{
NSData *d, *globalObjId;
NSMutableString *s;
id value;
iCalCalendar *calendar;
int preferredBodyType, nativeBodyType;
s = [NSMutableString string];
// To - "The value of this element contains one or more e-mail addresses.
// If there are multiple e-mail addresses, they are separated by commas."
value = [self _emailAddressesFrom: [[self envelope] to]];
if (value)
[s appendFormat: @"<To xmlns=\"Email:\">%@</To>", [value activeSyncRepresentationInContext: context]];
// From
value = [self _emailAddressesFrom: [[self envelope] from]];
if (value)
[s appendFormat: @"<From xmlns=\"Email:\">%@</From>", [value activeSyncRepresentationInContext: context]];
// Subject
value = [self decodedSubject];
if (value)
{
[s appendFormat: @"<Subject xmlns=\"Email:\">%@</Subject>", [value activeSyncRepresentationInContext: context]];
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [value activeSyncRepresentationInContext: context]];
}
// DateReceived
value = [self date];
if (value)
[s appendFormat: @"<DateReceived xmlns=\"Email:\">%@</DateReceived>", [value activeSyncRepresentationWithoutSeparatorsInContext: context]];
// DisplayTo
[s appendFormat: @"<DisplayTo xmlns=\"Email:\">%@</DisplayTo>", [[context activeUser] login]];
// Cc - same syntax as the To field
value = [self _emailAddressesFrom: [[self envelope] cc]];
if (value)
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
// Importance - FIXME
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
// Read
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
// We handle MeetingRequest
calendar = [self calendarFromIMIPMessage];
if (calendar)
{
NSString *method, *className;
iCalPerson *attendee;
iCalTimeZone *tz;
iCalEvent *event;
iCalPersonPartStat partstat;
int v;
event = [[calendar events] lastObject];
method = [[event parent] method];
// If we are the organizer, let's pick the attendee based on the From address
if ([event userIsOrganizer: [context activeUser]])
attendee = [event findAttendeeWithEmail: [[[[self envelope] from] lastObject] baseEMail]];
else
attendee = [event findAttendeeWithEmail: [[[context activeUser] allEmails] objectAtIndex: 0]];
partstat = [attendee participationStatus];
// We generate the correct MessageClass
if ([method isEqualToString: @"REQUEST"])
className = @"IPM.Schedule.Meeting.Request";
else if ([method isEqualToString: @"REPLY"])
{
switch (partstat)
{
case iCalPersonPartStatAccepted:
className = @"IPM.Schedule.Meeting.Resp.Pos";
break;
case iCalPersonPartStatDeclined:
className = @"IPM.Schedule.Meeting.Resp.Neg";
break;
case iCalPersonPartStatTentative:
case iCalPersonPartStatNeedsAction:
className = @"IPM.Schedule.Meeting.Resp.Tent";
break;
default:
className = @"IPM.Appointment";
NSLog(@"unhandled part stat");
}
}
else if ([method isEqualToString: @"COUNTER"])
className = @"IPM.Schedule.Meeting.Resp.Tent";
else if ([method isEqualToString: @"CANCEL"])
className = @"IPM.Schedule.Meeting.Cancelled";
else
className = @"IPM.Appointment";
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", className];
[s appendString: @"<MeetingRequest xmlns=\"Email:\">"];
[s appendFormat: @"<AllDayEvent xmlns=\"Email:\">%d</AllDayEvent>", ([event isAllDay] ? 1 : 0)];
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
if ([event startDate])
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
if ([event timeStampAsDate])
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
else if ([event created])
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
if ([event endDate])
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// FIXME: Single appointment - others are not supported right now
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
// Location
if ([[event location] length])
[s appendFormat: @"<Location xmlns=\"Email:\">%@</Location>", [[event location] activeSyncRepresentationInContext: context]];
[s appendFormat: @"<Organizer xmlns=\"Email:\">%@</Organizer>", [[[event organizer] mailAddress] activeSyncRepresentationInContext: context]];
// This will trigger the SendMail command. We set it to no for email invitations as
// SOGo will send emails when MeetingResponse is called.
[s appendFormat: @"<ResponseRequested xmlns=\"Email:\">%d</ResponseRequested>", 0];
// Sensitivity
if ([[event accessClass] isEqualToString: @"PRIVATE"])
v = 2;
if ([[event accessClass] isEqualToString: @"CONFIDENTIAL"])
v = 3;
else
v = 0;
[s appendFormat: @"<Sensitivity xmlns=\"Email:\">%d</Sensitivity>", v];
[s appendFormat: @"<BusyStatus xmlns=\"Email:\">%d</BusyStatus>", 2];
// Timezone
tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone];
if (!tz)
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
[s appendFormat: @"<TimeZone xmlns=\"Email:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
// We disallow new time proposals
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Email:\">%d</DisallowNewTimeProposal>", 1];
// From http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx:
//
// "Clients that need to determine whether the GlobalObjId element for a meeting request corresponds to an existing Calendar
// object in the Calendar folder have to convert the GlobalObjId element value to a UID element value to make the comparison."
//
globalObjId = [self _computeGlobalObjectIdFromEvent: event];
[s appendFormat: @"<GlobalObjId xmlns=\"Email:\">%@</GlobalObjId>", [globalObjId activeSyncRepresentationInContext: context]];
// We set the right message type - we must set AS version to 14.1 for this
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
[s appendString: @"</MeetingRequest>"];
// ContentClass
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:calendarmessage"];
}
else
{
// MesssageClass and ContentClass
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
}
// Reply-To - FIXME
//NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo];
//if ([replyTo count])
// [s appendFormat: @"<Reply-To xmlns=\"Email:\">%@</Reply-To>", [addressFormatter stringForArray: replyTo]];
// InternetCPID - 65001 == UTF-8, we use this all the time for now.
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
// Body - namespace 17
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
nativeBodyType = 1;
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
if (d)
{
NSString *content;
int len, truncated;
content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
// FIXME: This is a hack. We should normally avoid doing this as we might get
// broken encodings. We should rather tell that the data was truncated and expect
// a ItemOperations call to download the whole base64 encoding multipart.
if (!content)
content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding];
AUTORELEASE(content);
content = [content activeSyncRepresentationInContext: context];
truncated = 0;
len = [content length];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
if (!truncated)
[s appendFormat: @"<Data>%@</Data>", content];
[s appendString: @"</Body>"];
}
// Attachments -namespace 16
NSArray *attachmentKeys = [self fetchFileAttachmentKeys];
if ([attachmentKeys count])
{
int i;
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
for (i = 0; i < [attachmentKeys count]; i++)
{
value = [attachmentKeys objectAtIndex: i];
[s appendString: @"<Attachment>"];
[s appendFormat: @"<DisplayName>%@</DisplayName>", [[value objectForKey: @"filename"] activeSyncRepresentationInContext: context]];
// FileReference must be a unique identifier across the whole store. We use the following structure:
// mail/<foldername>/<message UID/<pathofpart>
// mail/INBOX/2
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
[s appendString: @"</Attachment>"];
}
[s appendString: @"</Attachments>"];
}
// Flags
[s appendString: @"<Flag xmlns=\"Email:\">"];
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", 0];
[s appendString: @"</Flag>"];
// FIXME - support these in the future
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
//[s appendString: @"<ConversationIndex xmlns=\"Email2:\">zot=</ConversationIndex>"];
// NativeBodyType -- http://msdn.microsoft.com/en-us/library/ee218276(v=exchg.80).aspx
// This is a required child element.
// 1 -> plain/text, 2 -> HTML and 3 -> RTF
if (nativeBodyType == 4)
nativeBodyType = 1;
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", nativeBodyType];
return s;
}
//
//
//
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) _context
{
id o;
if ((o = [theValues objectForKey: @"Flag"]))
{
o = [o objectForKey: @"FlagStatus"];
if ([o intValue])
[self addFlags: @"\\Flagged"];
else
[self removeFlags: @"\\Flagged"];
}
}
@end
@@ -0,0 +1,101 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <NGObjWeb/SoObjectWebDAVDispatcher.h>
#include <NGObjWeb/SoObject+SoDAV.h>
#include <NGObjWeb/WEClientCapabilities.h>
#include <NGObjWeb/WOContext.h>
#include <NGObjWeb/WORequest.h>
#include <NGObjWeb/WOResponse.h>
#import <Foundation/NSArray.h>
@interface SoObjectWebDAVDispatcher (ActiveSync)
- (id)_callObjectMethod:(NSString *)_method inContext:(WOContext *)_ctx;
- (id) doOPTIONS:(WOContext *)_ctx;
@end
@implementation SoObjectWebDAVDispatcher (ActiveSync)
- (id) doOPTIONS:(WOContext *)_ctx
{
WOResponse *response;
/*
See example: http://msdn.microsoft.com/en-us/library/ee204257(v=exchg.80).aspx
*/
if ([[[_ctx request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
{
response = [_ctx response];
[response setStatus: 200];
[response setHeader: @"private" forKey: @"Cache-Control"];
[response setHeader: @"OPTIONS, POST" forKey: @"Allow"];
[response setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"];
[response setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
[response setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"];
[response setHeader: @"OPTIONS, POST" forKey: @"Public"];
}
else
{
NSArray *tmp;
id result;
/* this checks whether the object provides a specific OPTIONS method */
if ((result = [self _callObjectMethod:@"OPTIONS" inContext:_ctx]) != nil)
return result;
response = [_ctx response];
[response setStatus:200 /* OK */];
if ((tmp = [self->object davAllowedMethodsInContext:_ctx]) != nil)
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"allow"];
if ([[[_ctx request] clientCapabilities] isWebFolder]) {
/*
As described over here:
http://teyc.editthispage.com/2005/06/02
This page also says that: "MS-Auth-Via header is not required to work
with Web Folders".
*/
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"public"];
}
if ((tmp = [self->object davComplianceClassesInContext:_ctx]) != nil)
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"dav"];
}
return response;
}
@end
+36
View File
@@ -0,0 +1,36 @@
# common make file for SoObject bundles
include ../config.make
include $(GNUSTEP_MAKEFILES)/common.make
include ../Version
NEEDS_GUI=no
BUNDLE_EXTENSION = .SOGo
BUNDLE_INSTALL_DIR = $(SOGO_LIBDIR)
WOBUNDLE_EXTENSION = $(BUNDLE_EXTENSION)
WOBUNDLE_INSTALL_DIR = $(BUNDLE_INSTALL_DIR)
# SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
ADDITIONAL_INCLUDE_DIRS += \
-I.. \
-I../.. \
-I../../SOPE
ADDITIONAL_LIB_DIRS += \
-L../SOGo/SOGo.framework/ \
-L../../SOGo/$(GNUSTEP_OBJ_DIR)/ \
-L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \
-L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
-L/usr/local/lib
BUNDLE_LIBS += \
-lSOGo \
-lGDLContentStore \
-lGDLAccess \
-lNGObjWeb \
-lNGCards -lNGMime -lNGLdap \
-lNGStreams -lNGExtensions -lEOControl \
-lDOM -lSaxObjC -lSBJson
ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
+47
View File
@@ -0,0 +1,47 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ICALEVENTACTIVESYNC_H__
#define __ICALEVENTACTIVESYNC_H__
#import <NGCards/iCalEvent.h>
@class NSString;
@class WOContext;
@interface iCalEvent (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context;
@end
#endif
+471
View File
@@ -0,0 +1,471 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "iCalEvent+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGExtensions/NSString+misc.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#include "iCalRecurrenceRule+ActiveSync.h"
#include "iCalTimeZone+ActiveSync.h"
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
@implementation iCalEvent (ActiveSync)
- (int) _attendeeStatus: (iCalPerson *) attendee
{
int attendee_status;
attendee_status = 5;
if ([[attendee partStat] caseInsensitiveCompare: @"ACCEPTED"] == NSOrderedSame)
attendee_status = 3;
else if ([[attendee partStat] caseInsensitiveCompare: @"DECLINED"] == NSOrderedSame)
attendee_status = 4;
else if ([[attendee partStat] caseInsensitiveCompare: @"TENTATIVE"] == NSOrderedSame)
attendee_status = 2;
return attendee_status;
}
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSMutableString *s;
NSArray *attendees;
iCalPerson *organizer, *attendee;
iCalTimeZone *tz;
id o;
int v;
s = [NSMutableString string];
[s appendFormat: @"<AllDayEvent xmlns=\"Calendar:\">%d</AllDayEvent>", ([self isAllDay] ? 1 : 0)];
// DTStamp -- http://msdn.microsoft.com/en-us/library/ee219470(v=exchg.80).aspx
if ([self timeStampAsDate])
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[self timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
else if ([self created])
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[self created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
if ([self startDate])
[s appendFormat: @"<StartTime xmlns=\"Calendar:\">%@</StartTime>", [[self startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
if ([self endDate])
[s appendFormat: @"<EndTime xmlns=\"Calendar:\">%@</EndTime>", [[self endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// Timezone
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
if (!tz)
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
[s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
// Organizer and other invitations related properties
if ((organizer = [self organizer]))
{
o = [organizer rfc822Email];
if ([o length])
{
[s appendFormat: @"<Organizer_Email xmlns=\"Calendar:\">%@</Organizer_Email>", o];
o = [organizer cn];
if ([o length])
[s appendFormat: @"<Organizer_Name xmlns=\"Calendar:\">%@</Organizer_Name>", o];
}
}
// Attendees
attendees = [self attendees];
if ([attendees count])
{
int i, attendee_status, attendee_type;
[s appendString: @"<Attendees xmlns=\"Calendar:\">"];
for (i = 0; i < [attendees count]; i++)
{
[s appendString: @"<Attendee xmlns=\"Calendar:\">"];
attendee = [attendees objectAtIndex: i];
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]];
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [attendee cn]];
attendee_status = [self _attendeeStatus: attendee];
[s appendFormat: @"<Attendee_Status xmlns=\"Calendar:\">%d</Attendee_Status>", attendee_status];
// FIXME: handle resource
if ([[attendee role] caseInsensitiveCompare: @"REQ-PARTICIPANT"] == NSOrderedSame)
attendee_type = 1;
else
attendee_type = 2;
[s appendFormat: @"<Attendee_Type xmlns=\"Calendar:\">%d</Attendee_Type>", attendee_type];
[s appendString: @"</Attendee>"];
}
[s appendString: @"</Attendees>"];
}
// This depends on the 'NEEDS-ACTION' parameter.
// This will trigger the SendMail command
if ([self userIsAttendee: [context activeUser]])
{
iCalPerson *attendee;
int attendee_status;
attendee = [self userAsAttendee: [context activeUser]];
attendee_status = [self _attendeeStatus: attendee];
[s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1];
[s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status];
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", 3];
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Calendar:\">%d</DisallowNewTimeProposal>", 1];
// BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
[s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", 2];
}
// Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx
if ([[self summary] length])
[s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
// Location
if ([[self location] length])
[s appendFormat: @"<Location xmlns=\"Calendar:\">%@</Location>", [[self location] activeSyncRepresentationInContext: context]];
// Importance - NOT SUPPORTED - DO NOT ENABLE
//o = [self priority];
//if ([o isEqualToString: @"9"])
// v = 0;
//else if ([o isEqualToString: @"1"])
// v = 2;
//else
// v = 1;
//[s appendFormat: @"<Importance xmlns=\"Calendar:\">%d</Importance>", v];
// UID -- http://msdn.microsoft.com/en-us/library/ee159919(v=exchg.80).aspx
if ([[self uid] length])
[s appendFormat: @"<UID xmlns=\"Calendar:\">%@</UID>", [self uid]];
// Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2;
if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
v = 3;
else
v = 0;
[s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v];
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
// TODO
// Recurrence rules
if ([self isRecurrent])
{
[s appendString: [[[self recurrenceRules] lastObject] activeSyncRepresentationInContext: context]];
}
// Comment
o = [self comment];
if ([o length])
{
o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
return s;
}
//
// To understand meeting requests/responses, see:
//
// http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx
// http://blogs.msdn.com/b/exchangedev/archive/2011/07/29/working-with-meeting-responses-in-exchange-activesync.aspx
//
//
// Here is an example of a Sync call when sogo10 accepts an invitation from sogo3:
//
// <Change>
// <ServerId>2978-52EA9D00-1-A253E70.ics</ServerId>
// <ApplicationData>
// <TimeZone xmlns="Calendar:">LAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w==</TimeZone>
// <AllDayEvent xmlns="Calendar:">0</AllDayEvent>
// <StartTime xmlns="Calendar:">20140207T130000Z</StartTime>
// <EndTime xmlns="Calendar:">20140207T140000Z</EndTime>
// <DTStamp xmlns="Calendar:">20140130T185245Z</DTStamp>
// <Subject xmlns="Calendar:">test 8</Subject>
// <Sensitivity xmlns="Calendar:">0</Sensitivity>
// <Body xmlns="AirSyncBase:">
// <Type>1</Type>
// <Data/>
// </Body>
// <Organizer_Email xmlns="Calendar:">sogo3@example.com</Organizer_Email>
// <UID xmlns="Calendar:">2978-52EA9D00-1-A253E70</UID>
// <Attendees xmlns="Calendar:">
// <Attendee>
// <Attendee_Name>sogo10</Attendee_Name>
// <Attendee_Email>sogo10@example.com</Attendee_Email>
// <Attendee_Type>1</Attendee_Type>
// </Attendee>
// </Attendees>
// <BusyStatus xmlns="Calendar:">2</BusyStatus>
// <MeetingStatus xmlns="Calendar:">3</MeetingStatus>
// <Organizer_Name xmlns="Calendar:">Wolfgang Fritz</Organizer_Name>
// </ApplicationData>
// </Change>
//
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
iCalDateTime *start, *end;
NSTimeZone *userTimeZone;
iCalTimeZone *tz;
id o;
NSInteger tzOffset;
BOOL isAllDay;
if ((o = [theValues objectForKey: @"UID"]))
[self setUid: o];
// FIXME: merge with iCalToDo
if ((o = [theValues objectForKey: @"Subject"]))
[self setSummary: o];
isAllDay = NO;
if ([[theValues objectForKey: @"AllDayEvent"] intValue])
{
isAllDay = YES;
}
//
// 0- free, 1- tentative, 2- busy and 3- out of office
//
if ((o = [theValues objectForKey: @"BusyStatus"]))
{
[o intValue];
}
//
//
//
if ((o = [theValues objectForKey: @"MeetingStatus"]))
{
[o intValue];
}
//
// 0- normal, 1- personal, 2- private and 3-confidential
//
if ((o = [theValues objectForKey: @"Sensitivy"]))
{
switch ([o intValue])
{
case 2:
[self setAccessClass: @"PRIVATE"];
break;
case 3:
[self setAccessClass: @"CONFIDENTIAL"];
break;
case 0:
case 1:
default:
[self setAccessClass: @"PUBLIC"];
}
}
if ((o = [theValues objectForKey: @"TimeZone"]))
{
// Ugh, we ignore it for now.
userTimeZone = [[[context activeUser] userDefaults] timeZone];
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[(iCalCalendar *) parent addTimeZone: tz];
}
// FIXME: merge with iCalToDo
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
if ((o = [theValues objectForKey: @"Location"]))
[self setLocation: o];
if ((o = [theValues objectForKey: @"StartTime"]))
{
o = [o calendarDate];
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
[start setTimeZone: tz];
if (isAllDay)
{
[start setDate: o];
[start setTimeZone: nil];
}
else
{
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[start setDateTime: o];
}
}
if ((o = [theValues objectForKey: @"EndTime"]))
{
o = [o calendarDate];
end = (iCalDateTime *) [self uniqueChildWithTag: @"dtend"];
[end setTimeZone: tz];
if (isAllDay)
{
[end setDate: o];
[end setTimeZone: nil];
}
else
{
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[end setDateTime: o];
}
}
// Recurrence
if ((o = [theValues objectForKey: @"Recurrence"]))
{
iCalRecurrenceRule *rule;
rule = [[iCalRecurrenceRule alloc] init];
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
RELEASE(rule);
[rule takeActiveSyncValues: o inContext: context];
}
// Organizer - we don't touch the value unless we're the organizer
if ((o = [theValues objectForKey: @"Organizer_Email"]) &&
([self userIsOrganizer: [context activeUser]] || [[context activeUser] hasEmail: o]))
{
iCalPerson *person;
person = [iCalPerson elementWithTag: @"organizer"];
[person setEmail: o];
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
[person setPartStat: @"ACCEPTED"];
[self setOrganizer: person];
}
// Attendees - we don't touch the values if we're an attendee. This is gonna
// be done automatically by the ActiveSync client when invoking MeetingResponse.
if (![self userIsAttendee: [context activeUser]])
{
if ((o = [theValues objectForKey: @"Attendees"]))
{
NSMutableArray *attendees;
NSDictionary *attendee;
iCalPerson *person;
int status, i;
attendees = [NSMutableArray array];
for (i = 0; i < [o count]; i++)
{
// Each attendee has is a dictionary similar to this:
// { "Attendee_Email" = "sogo3@example.com"; "Attendee_Name" = "Wolfgang Fritz"; "Attendee_Status" = 5; "Attendee_Type" = 1; }
attendee = [o objectAtIndex: i];
person = [iCalPerson elementWithTag: @"attendee"];
[person setCn: [attendee objectForKey: @"Attendee_Name"]];
[person setEmail: [attendee objectForKey: @"Attendee_Email"]];
status = [[attendee objectForKey: @"Attendee_Status"] intValue];
switch (status)
{
case 2:
[person setPartStat: @"TENTATIVE"];
break;
case 3:
[person setPartStat: @"ACCEPTED"];
break;
case 4:
[person setPartStat: @"DECLINED"];
break;
case 0:
case 5:
default:
[person setPartStat: @"NEEDS-ACTION"];
break;
}
// FIXME: handle Attendee_Type
[attendees addObject: person];
}
[self setAttendees: attendees];
}
}
}
@end
@@ -0,0 +1,48 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ICALRECURRENCERULEACTIVESYNC_H__
#define __ICALRECURRENCERULEACTIVESYNC_H__
#import <NGCards/iCalRecurrenceRule.h>
@class NSDictionary;
@class NSString;
@class WOContext;
@interface iCalRecurrenceRule (ActiveSync)
- (NSString *) activeSyncRepresentationInContext:(WOContext *) context;
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context;
@end
#endif
+355
View File
@@ -0,0 +1,355 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "iCalRecurrenceRule+ActiveSync.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <NGCards/iCalByDayMask.h>
#import "NSCalendarDate+ActiveSync.h"
#import "NSDate+ActiveSync.h"
@implementation iCalRecurrenceRule (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSMutableString *s;
int type;
s = [NSMutableString string];
[s appendString: @"<Recurrence xmlns=\"Calendar:\">"];
// 0 -> daily, 1 -> weekly, 2 -> montly, 5 -> yearly
type = 0;
if ([self frequency] == iCalRecurrenceFrequenceDaily)
{
type = 0;
// 1 -> sunday, 2 -> monday, 4 -> tuesday, 8 -> wednesday, 16 -> thursday, 32 -> friday, 64 -> saturday, 127 -> last day of month (montly or yearl recurrences only)
if ([[self byDayMask] isWeekDays])
{
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (2|4|8|16|32)];
}
else
{
[s appendFormat: @"<Recurrence_Interval xmlns=\"Calendar:\">%d</Recurrence_Interval>", [self repeatInterval]];
}
}
else if ([self frequency] == iCalRecurrenceFrequenceWeekly)
{
iCalWeekOccurrences *occurrences;
int i, v;
type = 1;
occurrences = [[self byDayMask] weekDayOccurrences];
v = 0;
for (i = 0; i < 7; i++)
{
if (occurrences[i])
v += (1 << i);
}
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", v];
[s appendFormat: @"<Recurrence_Interval xmlns=\"Calendar:\">%d</Recurrence_Interval>", [self repeatInterval]];
}
else if ([self frequency] == iCalRecurrenceFrequenceMonthly)
{
if ([[self byDay] length])
{
int firstOccurrence;
iCalByDayMask *dm;
type = 3; // recurs monthly on the nth day
dm = [self byDayMask];
firstOccurrence = [dayMask firstOccurrence];
// Handle the case for "Last day of the month"
if (firstOccurrence < 0)
firstOccurrence = 5;
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", firstOccurrence];
}
else if ([[self byMonthDay] count])
{
NSArray *days;
type = 2; // recurs monthly
days = [self byMonthDay];
if ([days count] > 0 && [[days objectAtIndex: 0] intValue] < 0)
{
// Last day of the month
iCalByDayMask *dm;
dm = [self byDayMask];
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", 5];
}
else
{
// Unsupported rule in ActiveSync/Outlook. Rule that says "Repeat on the 7th and 8th of each month".
// FIXME
}
}
}
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
{
type = 6; // Yearly on the nth day
if ([[self flattenedValuesForKey: @"bymonth"] length])
{
if ([[self byDay] length])
{
int firstOccurrence;
iCalByDayMask *dm;
dm = [self byDayMask];
firstOccurrence = [dm firstOccurrence];
if (firstOccurrence < 0)
firstOccurrence = 5;
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", firstOccurrence];
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"Calendar:\">%@</Recurrence_MonthOfYear>",
[self flattenedValuesForKey: @"bymonth"]];
}
else
{
type = 5; // Yearly
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"Calendar:\">%@</Recurrence_DayOfMonth>",
[self flattenedValuesForKey: @"bymonthday"]];
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"Calendar:\">%@</Recurrence_MonthOfYear>",
[self flattenedValuesForKey: @"bymonth"]];
}
}
else
type = 5;
}
[s appendFormat: @"<Recurrence_Type xmlns=\"Calendar:\">%d</Recurrence_Type>", type];
// Occurrences / Until
//[s appendFormat: @"<Recurrence_Occurrences xmlns=\"Calendar:\">%d</Recurrence_Occurrences>", 5];
if ([self repeatCount])
{
[s appendFormat: @"<Recurrence_Occurrences xmlns=\"Calendar:\">%@</Recurrence_Occurrences>",
[self flattenedValuesForKey: @"count"]];
}
else if ([self untilDate])
{
NSCalendarDate *date;
date = [self untilDate];
//ud = [[context activeUser] userDefaults];
//[date setTimeZone: [ud timeZone]];
[s appendFormat: @"<Recurrence_Until xmlns=\"Calendar:\">%@</Recurrence_Until>",
[date activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
[s appendString: @"</Recurrence>"];
return s;
}
//
//
//
- (void) _setByDayFromValues: (NSDictionary *) theValues
{
NSString *day;
id o;
unsigned int day_of_week;
int i, week_of_month;
o = [theValues objectForKey: @"Recurrence_DayOfWeek"];
// The documentation says WeekOfMonth must be between 1 and 5. The value
// 5 means "last week of month"
week_of_month = [[theValues objectForKey: @"Recurrence_WeekOfMonth"] intValue];
if (week_of_month > 4)
week_of_month = -1;
// We find the correct day of the week
day_of_week = [o intValue];
for (i = 0; i < 7; i++)
{
if ((1<<i) == day_of_week)
{
day_of_week = i;
break;
}
}
day = [self iCalRepresentationForWeekDay: i];
[self setSingleValue: [NSString stringWithFormat: @"%d%@",
week_of_month, day]
forKey: @"byday"];
}
- (void) _setByMonthFromValues: (NSDictionary *) theValues
{
unsigned int month_of_year;
month_of_year = [[theValues objectForKey: @"Recurrence_MonthOfYear"] intValue];
[self setSingleValue: [NSString stringWithFormat: @"%d", month_of_year]
forKey: @"bymonth"];
}
//
//
//
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
id o;
int recurrenceType;
recurrenceType = [[theValues objectForKey: @"Recurrence_Type"] intValue];
if ((o = [theValues objectForKey: @"Recurrence_Interval"]))
{
[self setRepeatInterval: [o intValue]];
}
else
[self setRepeatInterval: 1];
switch (recurrenceType)
{
//
// Daily
//
case 0:
[self setFrequency: iCalRecurrenceFrequenceDaily];
// Every weekday
if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
{
[self setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
}
break;
//
// Weekly
//
case 1:
[self setFrequency: iCalRecurrenceFrequenceWeekly];
// 42 == Every Monday, Wednesday and Friday, for example
if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
{
iCalWeekOccurrences days;
unsigned int i, v;
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
v = [o intValue];
for (i = 0; i < 7; i++)
{
if (v & (1<<i))
days[i] = iCalWeekOccurrenceAll;
}
[self setByDayMask: [iCalByDayMask byDayMaskWithDays: days]];
}
break;
//
// Montly
//
case 2:
case 3:
[self setFrequency: iCalRecurrenceFrequenceMonthly];
// The 5th of every X month(s)
if ((o = [theValues objectForKey: @"Recurrence_DayOfMonth"]))
{
[self setValues: [NSArray arrayWithObject: o]
atIndex: 0 forKey: @"bymonthday"];
}
// The 3rd Thursay every X month(s)
else if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
{
[self _setByDayFromValues: theValues];
}
break;
//
// Yearly
//
case 5:
case 6:
default:
[self setFrequency: iCalRecurrenceFrequenceYearly];
// On April 19th
if ((o = [theValues objectForKey: @"Recurrence_DayOfMonth"]))
{
[self setValues: [NSArray arrayWithObject: o] atIndex: 0
forKey: @"bymonthday"];
[self _setByMonthFromValues: theValues];
}
else
{
// On the Second Wednesday of April
[self _setByDayFromValues: theValues];
[self _setByMonthFromValues: theValues];
}
break;
}
if ((o = [theValues objectForKey: @"Recurrence_Occurrences"]))
{
[self setRepeatCount: [o intValue]];
}
else if ((o = [theValues objectForKey: @"Recurrence_Until"]))
{
[self setUntilDate: [o calendarDate]];
}
}
@end
+44
View File
@@ -0,0 +1,44 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ICALTIMEZONEACTIVESYNC_H__
#define __ICALTIMEZONEACTIVESYNC_H__
#import <NGCards/iCalTimeZone.h>
@class NSString;
@class WOContext;
@interface iCalTimeZone (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
@end
#endif
+165
View File
@@ -0,0 +1,165 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "iCalTimeZone+ActiveSync.h"
#include <Foundation/NSArray.h>
#include <Foundation/NSCalendarDate.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSString.h>
#import <NGCards/iCalByDayMask.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGExtensions/NGBase64Coding.h>
#include "NSData+ActiveSync.h"
struct SYSTEMTIME {
uint16_t wYear;
uint16_t wMonth;
uint16_t wDayOfWeek;
uint16_t wDay;
uint16_t wHour;
uint16_t wMinute;
uint16_t wSecond;
uint16_t wMilliseconds;
};
@interface iCalTimeZonePeriod (ActiveSync)
- (void) _fillTZDate: (struct SYSTEMTIME *) tzData;
@end
@implementation iCalTimeZonePeriod (ActiveSync)
//
// FIXME - combine with iCalTimeZone+MAPIStore.m
//
- (void) _fillTZDate: (struct SYSTEMTIME *) tzData
{
iCalRecurrenceRule *rrule;
NSArray *byMonth;
iCalByDayMask *mask;
NSCalendarDate *dateValue;
rrule = [self recurrenceRule];
byMonth = [rrule byMonth];
if ([byMonth count] > 0)
{
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
mask = [rrule byDayMask];
tzData->wDayOfWeek = [mask firstDay];
tzData->wDay = [mask firstOccurrence];
dateValue = [self startDate];
tzData->wHour = [dateValue hourOfDay];
tzData->wMinute = [dateValue minuteOfHour];
tzData->wSecond = [dateValue secondOfMinute];
}
}
@end
@implementation iCalTimeZone (ActiveSync)
//
// FIXME - combine with iCalTimeZone+MAPIStore.m
//
- (iCalTimeZonePeriod *) _mostRecentPeriodWithName: (NSString *) periodName
{
NSArray *periods;
iCalTimeZonePeriod *period;
NSUInteger max;
periods = [self childrenWithTag: periodName];
max = [periods count];
if (max > 0)
{
periods = [periods sortedArrayUsingSelector: @selector (compare:)];
period = (iCalTimeZonePeriod *) [periods objectAtIndex: (max - 1)];
}
else
period = nil;
return period;
}
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
iCalTimeZonePeriod *period;
NSMutableData *bytes;
uint32_t lBias;
uint32_t lStandardBias;
uint32_t lDaylightBias;
//uint16_t wStandardYear;
struct SYSTEMTIME stStandardDate;
//uint16_t wDaylightYear;
struct SYSTEMTIME stDaylightDate;
char standardName[64], daylightName[64];
bytes = [NSMutableData data];
memset(standardName, 0, 64);
memset(daylightName, 0, 64);
lStandardBias = 0;
period = [self _mostRecentPeriodWithName: @"STANDARD"];
lBias = -[period secondsOffsetFromGMT] / 60;
[period _fillTZDate: &stStandardDate];
period = [self _mostRecentPeriodWithName: @"DAYLIGHT"];
if (!period)
stStandardDate.wMonth = 0;
lDaylightBias = (uint32_t) -([period secondsOffsetFromGMT] / 60) - lBias;
[period _fillTZDate: &stDaylightDate];
//wStandardYear = stStandardDate.wYear;
//wDaylightYear = stDaylightDate.wYear;
// We build the timezone
[bytes appendBytes: &lBias length: 4];
[bytes appendBytes: standardName length: 64];
[bytes appendBytes: &stStandardDate length: 16];
[bytes appendBytes: &lStandardBias length: 4];
[bytes appendBytes: daylightName length: 64];
[bytes appendBytes: &stDaylightDate length: 16];
[bytes appendBytes: &lDaylightBias length: 4];
return [bytes activeSyncRepresentationInContext: context];
}
@end
+46
View File
@@ -0,0 +1,46 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ICALTODOACTIVESYNC_H__
#define __ICALTODOACTIVESYNC_H__
#import <NGCards/iCalToDo.h>
@class NSString;
@class WOContext;
@interface iCalToDo (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context;
@end
#endif
+192
View File
@@ -0,0 +1,192 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "iCalToDo+ActiveSync.h"
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGExtensions/NSString+misc.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZone.h>
#include "NSDate+ActiveSync.h"
#include "NSString+ActiveSync.h"
@implementation iCalToDo (ActiveSync)
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSMutableString *s;
id o;
int v;
s = [NSMutableString string];
// Complete
o = [self completed];
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
// DateCompleted
if (o)
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
// Start date
if ((o = [self startDate]))
{
[s appendFormat: @"<StartDate xmlns=\"Tasks:\">%@</StartDate>", [o activeSyncRepresentationInContext: context]];
[s appendFormat: @"<UTCStartDate xmlns=\"Tasks:\">%@</UTCStartDate>", [o activeSyncRepresentationInContext: context]];
}
// Due date
if ((o = [self due]))
{
[s appendFormat: @"<DueDate xmlns=\"Tasks:\">%@</DueDate>", [o activeSyncRepresentationInContext: context]];
[s appendFormat: @"<UTCDueDate xmlns=\"Tasks:\">%@</UTCDueDate>", [o activeSyncRepresentationInContext: context]];
}
// Importance
o = [self priority];
if ([o isEqualToString: @"9"])
v = 0;
else if ([o isEqualToString: @"1"])
v = 2;
else
v = 1;
[s appendFormat: @"<Importance xmlns=\"Tasks:\">%d</Importance>", v];
// Reminder - FIXME
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
// Sensitivity - FIXME
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", 0];
// Subject
o = [self summary];
if ([o length])
[s appendFormat: @"<Subject xmlns=\"Tasks:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
if ((o = [self comment]))
{
o = [o activeSyncRepresentationInContext: context];
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
return s;
}
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
NSTimeZone *userTimeZone;
iCalTimeZone *tz;
id o;
NSInteger tzOffset;
userTimeZone = [[[context activeUser] userDefaults] timeZone];
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[(iCalCalendar *) parent addTimeZone: tz];
// FIXME: merge with iCalEvent
if ((o = [theValues objectForKey: @"Subject"]))
[self setSummary: o];
// FIXME: merge with iCalEvent
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
[self setComment: o];
if ([[theValues objectForKey: @"Complete"] intValue] &&
((o = [theValues objectForKey: @"DateCompleted"])) )
{
iCalDateTime *completed;
o = [o calendarDate];
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"];
//tzOffset = [[o timeZone] secondsFromGMTForDate: o];
//o = [o dateByAddingYears: 0 months: 0 days: 0
// hours: 0 minutes: 0
// seconds: -tzOffset];
[completed setDate: o];
[self setStatus: @"COMPLETED"];
}
if ((o = [theValues objectForKey: @"DueDate"]))
{
iCalDateTime *due;
o = [o calendarDate];
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
[due setTimeZone: tz];
tzOffset = [userTimeZone secondsFromGMTForDate: o];
o = [o dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: tzOffset];
[due setDateTime: o];
}
// 2 == high, 1 == normal, 0 == low
if ((o = [theValues objectForKey: @"Importance"]))
{
if ([o intValue] == 2)
[self setPriority: @"1"];
else if ([o intValue] == 1)
[self setPriority: @"5"];
else
[self setPriority: @"9"];
}
if ((o = [theValues objectForKey: @"ReminderTime"]))
{
}
}
@end
+3
View File
@@ -0,0 +1,3 @@
{
requires = ( MAIN, Appointments, Contacts, Mailer );
}
+9 -7
View File
@@ -2,8 +2,6 @@ Alias /SOGo.woa/WebServerResources/ \
/usr/lib/GNUstep/SOGo/WebServerResources/
Alias /SOGo/WebServerResources/ \
/usr/lib/GNUstep/SOGo/WebServerResources/
AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
/usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2
<Directory /usr/lib/GNUstep/SOGo/>
AllowOverride None
@@ -18,10 +16,6 @@ AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
</IfModule>
</Directory>
<LocationMatch "^/SOGo/so/ControlPanel/Products/.*UI/Resources/.*\.(jpg|png|gif|css|js)">
SetHandler default-handler
</LocationMatch>
## Uncomment the following to enable proxy-side authentication, you will then
## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and
## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section
@@ -48,6 +42,14 @@ ProxyPreserveHost On
ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
# Enable to use Microsoft ActiveSync support
# Note that you MUST have many sogod workers to use ActiveSync.
# See the SOGo Installation and Configuration guide for more details.
#
#ProxyPass /Microsoft-Server-ActiveSync \
# http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
# retry=60 connectiontimeout=5 timeout=360
<Proxy http://127.0.0.1:20000/SOGo>
## adjust the following to your configuration
RequestHeader set "x-webobjects-server-port" "443"
@@ -66,7 +68,7 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
Allow from all
</Proxy>
# For apple autoconfiguration
# For Apple autoconfiguration
<IfModule rewrite_module>
RewriteEngine On
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
+2363
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 KiB

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 311 KiB

+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGo+DAV.m - this file is part of SOGo
*
* Copyright (C) 2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-2013 Inverse inc.
*
* 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
+1 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2005-2011 Inverse inc.
Copyright (C) 2005-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo
+71
View File
@@ -1,3 +1,74 @@
2.2.0 (2014-02-24)
------------------
New features
- initial implementation of Microsoft ActiveSync protocol
- it's now possible to set a default reminder for calendar components
using SOGoCalendarDefaultReminder
- select multiple files to attach to a message or drag'n'drop files onto the
mail editor; will also now display progress of uploads
- new popup menu to download all attachments of a mail
- move & copy messages between different accounts
- support for the Sieve 'body' extension (mail filter based on the body content)
Enhancements
- we now automatically convert <img src=data...> into file attachments
using CIDs to prevent Outlook issues
- updated French, Finnish, Polish, German, Russian, and Spanish (Spain) translations
- XMLHttpRequest.js is now loaded conditionaly (< IE9)
- format time in attendees invitation window according to the user's locale
- improved IE11 support
- respect signature placement when forwarding a message
- respect Sieve server capabilities
- encode messages in quoted-printable when content is bigger than 72 bytes
- we now use binary encoding in memcached (#2587)
- warn user when overbooking a resource by creating an event in its calendar (#2541)
- converted JavaScript alerts to inline CSS dialogs in appointment editor
- visually identify users with no freebusy information in autocompletion widget of attendees editor (#2565)
- respect occurences of recurrent events when deleting selected events (#1950)
- improved confirmation dialog box when deleting events and tasks
- moved the DN cache to SOGoCache - avoiding sogod restarts after RDN operations
- don't use the HTML editor with Internet Explorer 7
- add message-id header to appointment notifications (#2535)
- detect URLs in popup of events
- improved display of a contact (#2350)
Bug fixes
- don't load 'background' attribute (#2437)
- fixed validation of subscribed folders (#2583)
- fixed display of folder names in messages filter editor (#2569)
- fixed contextual menu of the current calendar view (#2557)
- fixed handling of the '=' character in cards/events/tasks (#2505)
- simplify searches in the address book (#2187)
- warn user when dnd failed because of a resource conflict (#1613)
- respect the maximum number of bookings when viewing the freebusy information of a resource (#2560)
- encode HTML entities when forwarding an HTML message inline in plain text composition mode (#2411)
- encode HTML entities in JSON data (#2598)
- fixed handling of ACLs on shared calendars with multiple groups (#1854)
- fixed HTML formatting of appointment notifications for Outlook (#2233)
- replace slashes by dashes in filenames of attachments to avoid a 404 return code (#2537)
- avoid over-using LDAP connections when decomposing groups
- fixed display of a contact's birthday when not defined (#2503)
- fixed JavaScript error when switching views in calendar module (#2613)
2.1.1b (2013-12-04)
-------------------
Enhancements
- updated CKEditor to version 4.3.0 and added tab module
Bug fixes
- HTML formatting is now retained when forwarding/replying to a mail using the HTML editor
- put the text part before the HTML part when composing mail to fix a display issue with Thunderbird (#2512)
2.1.1a (2013-11-22)
-------------------
Bug fixes
- fixed Sieve filters editor (#2504)
- moved missing translation to UI/Common (#2499)
- fixed potential crasher in OpenChange
2.1.1 (2013-11-19)
------------------
+3 -3
View File
@@ -76,7 +76,7 @@
},
{
columnName = c_participants;
sqlType = "VARCHAR(1000000)";
sqlType = "TEXT";
allowsNull = YES;
},
{
@@ -91,7 +91,7 @@
},
{
columnName = c_cycleinfo;
sqlType = "VARCHAR(1000000)";
sqlType = "TEXT";
allowsNull = YES;
},
{
@@ -126,7 +126,7 @@
},
{
columnName = c_partmails;
sqlType = "VARCHAR(100000)";
sqlType = "TEXT";
allowsNull = YES;
},
{
+2
View File
@@ -122,6 +122,8 @@
[MAPIApp setUserContext: userContext];
moduleName = [self MAPIModuleName];
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
nameInContainer = nil;
if (![parentFolder newFolderWithName: folderName
nameInContainer: &nameInContainer])
mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/",
+1 -3
View File
@@ -1,8 +1,6 @@
/* MAPIStoreMailFolder.m - this file is part of SOGo
*
* Copyright (C) 2011-2012 Inverse inc
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2011-2013 Inverse inc
*
* 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
+14 -2
View File
@@ -35,6 +35,8 @@
@"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
#endif
static NSArray *privilegedTagNames = nil;
@implementation NGCardsSaxHandler
- (id) init
@@ -42,6 +44,12 @@
if ((self = [super init]))
topGroupClass = nil;
if (!privilegedTagNames)
{
privilegedTagNames = [NSArray arrayWithObjects: @"ADR", @"N", @"RRULE", nil];
RETAIN(privilegedTagNames);
}
return self;
}
@@ -210,8 +218,12 @@
length: contentLength];
free (content);
content = NULL;
// NSLog (@"content: '%@'", s);
contentValues = [s vCardSubvalues];
//NSLog(@"content: '%@'", s);
if ([privilegedTagNames containsObject: [currentElement tag]])
contentValues = [s vCardSubvalues];
else
contentValues = [NSMutableDictionary dictionaryWithObject: [NSMutableArray arrayWithObject: [s asCardAttributeValues]]
forKey: @""];
}
else
contentValues = nil;
+27 -2
View File
@@ -33,8 +33,10 @@
//SOGoMailSpoolPath = /var/spool/sogo;
//NGImap4ConnectionStringSeparator = "/";
/* Notifications */
//SOGoAppointmentSendEMailNotifications = NO;
//SOGoACLsSendEMailNotifications = NO;
//SOGoFoldersSendEMailNotifications = NO;
/* Authentication */
//SOGoPasswordChangeEnabled = YES;
@@ -58,13 +60,33 @@
// }
//);
/* LDAP AD/Samba4 example */
//SOGoUserSources = (
// {
// type = ldap;
// CNFieldName = cn;
// UIDFieldName = sAMAccountName;
// baseDN = "CN=users,dc=domain,dc=tld";
// bindDN = "CN=sogo,CN=users,DC=domain,DC=tld";
// bindFields = (sAMAccountName, mail);
// bindPassword = password;
// canAuthenticate = YES;
// displayName = "Public";
// hostname = ldap://127.0.0.1:389;
// filter = "mail = '*'";
// id = directory;
// isAddressBook = YES;
// }
//);
/* SQL authentication example */
/* These database columns MUST be present in the view/table:
* c_uid - will be used for authentication - it's the username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries
* c_password - password of the user, plain-text, md5 or sha encoded for now
* c_cn - the user's common name - such as "John Doe"
* mail - the user's mail address
* mail - the user's mail address
* See the installation guide for more details
*/
//SOGoUserSources =
@@ -85,6 +107,7 @@
//SOGoForwardEnabled = YES;
//SOGoSieveScriptsEnabled = YES;
//SOGoMailAuxiliaryUserAccountsEnabled = YES;
//SOGoTrustProxyAuthentication = NO;
/* General */
//SOGoLanguage = English;
@@ -94,7 +117,9 @@
// ConfidentialDAndTViewer
//);
//SOGoSuperUsernames = (sogo1, sogo2); // This is an array - keep the parens!
//SxVMemLimit = 384
//SxVMemLimit = 384;
//WOPidFile = "/var/run/sogo/sogo.pid";
//SOGoMemcachedHost = "/var/run/memcached.sock";
/* Debug */
//SOGoDebugRequests = YES;
+1 -1
View File
@@ -15,7 +15,7 @@
#* * * * * sogo /usr/sbin/sogo-ealarms-notify
# Daily backups
# - writes to /home/sogo/backups/ by default
# - writes to ~sogo/backups/ by default
# - will keep 31 days worth of backups by default
# - runs once a day by default, but can run more frequently
# - make sure to set the path to sogo-backup.sh correctly
+8 -8
View File
@@ -28,22 +28,22 @@ $plugins
= array(
"sogo-connector@inverse.ca"
=> array( "application" => "thunderbird",
"version" => "17.0.3",
"filename" => "sogo-connector-17.0.3.xpi" ),
"version" => "24.0.4",
"filename" => "sogo-connector-24.0.4.xpi" ),
"sogo-integrator@inverse.ca"
=> array( "application" => "thunderbird",
"version" => "17.0.3",
"filename" => "sogo-integrator-17.0.3.xpi" ),
"version" => "24.0.4",
"filename" => "sogo-integrator-24.0.4.xpi" ),
"{e2fda1a4-762b-4020-b5ad-a41df1933103}"
=> array( "application" => "thunderbird",
"version" => "1.9",
"filename" => "lightning-1.9.xpi" )
"version" => "2.6.4",
"filename" => "lightning-2.6.4.xpi" )
);
$applications
= array( "thunderbird" => "<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
<em:minVersion>17.0</em:minVersion>
<em:maxVersion>17.*</em:maxVersion>" );
<em:minVersion>24.0</em:minVersion>
<em:maxVersion>24.*</em:maxVersion>" );
$pluginname = $_GET["plugin"];
$plugin =& $plugins[$pluginname];
+1 -3
View File
@@ -1,8 +1,6 @@
/* MSExchangeFreeBusy.m - this file is part of SOGo
*
* Copyright (C) 2012 Inverse inc.
*
* Author: Francis Lachapelle <flachapelle@inverse.ca>
* Copyright (C) 2012-2014 Inverse inc.
*
* 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
+61 -14
View File
@@ -573,17 +573,27 @@ static iCalEvent *iCalEventK = nil;
}
grantedCount = [grantedClasses count];
if (grantedCount == 3)
filter = @"";
{
// User have access to all three classifications
filter = @"";
}
else if (grantedCount == 2)
filter
= [NSString stringWithFormat: @"c_classification != %@",
[deniedClasses objectAtIndex: 0]];
{
// User has access to all but one of the classifications
filter = [NSString stringWithFormat: @"c_classification != %@",
[deniedClasses objectAtIndex: 0]];
}
else if (grantedCount == 1)
filter
= [NSString stringWithFormat: @"c_classification = %@",
[grantedClasses objectAtIndex: 0]];
{
// User has access to only one classification
filter = [NSString stringWithFormat: @"c_classification = %@",
[grantedClasses objectAtIndex: 0]];
}
else
filter = nil;
{
// User has access to no classification
filter = nil;
}
return filter;
}
@@ -676,7 +686,6 @@ static iCalEvent *iCalEventK = nil;
qualifier = nil;
/* fetch non-recurrent apts first */
records = [folder fetchFields: fields matchingQualifier: qualifier];
}
else
@@ -871,7 +880,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
{
NSCalendarDate *recurrenceId;
NSMutableDictionary *newRecord;
NSDictionary *oldRecord;
NGCalendarDateRange *newRecordRange;
NSComparisonResult compare;
int recordIndex, secondsOffsetFromGMT;
@@ -2533,7 +2541,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
unsigned int permStrIndex;
[super initializeQuickTablesAclsInContext: localContext];
/* We assume "userIsOwner" will be set after calling the super method. */
/* We assume "userCanAccessAllObjects" will be set after calling the super method. */
if (!userCanAccessAllObjects)
{
login = [[localContext activeUser] login];
@@ -3123,6 +3131,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
{
NSMutableArray *aclsForUser;
NSArray *superAcls;
static NSArray *rolesClassifications = nil;
superAcls = [super aclsForUser: uid forObjectAtPath: objectPathArray];
if ([uid isEqualToString: [self defaultUserID]])
@@ -3137,14 +3146,52 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
[aclsForUser addObject: SoRole_Authenticated];
}
else
aclsForUser = (NSMutableArray *) superAcls;
{
aclsForUser = [NSMutableArray array];
if (!rolesClassifications)
{
rolesClassifications =
[NSArray arrayWithObjects:
[NSArray arrayWithObjects:
SOGoCalendarRole_PublicModifier,
SOGoCalendarRole_PublicResponder,
SOGoCalendarRole_PublicViewer,
SOGoCalendarRole_PublicDAndTViewer,
nil],
[NSArray arrayWithObjects:
SOGoCalendarRole_ConfidentialModifier,
SOGoCalendarRole_ConfidentialResponder,
SOGoCalendarRole_ConfidentialViewer,
SOGoCalendarRole_ConfidentialDAndTViewer,
nil],
[NSArray arrayWithObjects:
SOGoCalendarRole_PrivateModifier,
SOGoCalendarRole_PrivateResponder,
SOGoCalendarRole_PrivateViewer,
SOGoCalendarRole_PrivateDAndTViewer,
nil],
[NSArray arrayWithObject: SOGoRole_ObjectCreator],
[NSArray arrayWithObject: SOGoRole_ObjectEraser],
nil];
[rolesClassifications retain];
}
// When a user is a member of many groups for which there are access rights, multiple access rights
// can be returned for each classification. In this case, we only keep the highest access right.
int i, count = [rolesClassifications count];
NSString *role;
for (i = 0; i < count; i++)
{
role = [[rolesClassifications objectAtIndex: i] firstObjectCommonWithArray: superAcls];
if (role)
[aclsForUser addObject: role];
}
}
return aclsForUser;
}
/* caldav-proxy */
- (SOGoAppointmentProxyPermission)
proxyPermissionForUserWithLogin: (NSString *) login
- (SOGoAppointmentProxyPermission) proxyPermissionForUserWithLogin: (NSString *) login
{
SOGoAppointmentProxyPermission permission;
NSArray *roles;
+91 -57
View File
@@ -431,18 +431,38 @@
forEvent: (iCalEvent *) theEvent
{
iCalPerson *currentAttendee;
NSMutableArray *attendees;
NSEnumerator *enumerator;
NSString *currentUID;
SOGoUser *user;
SOGoUser *user, *currentUser, *ownerUser;
// Build a list of the attendees uids
attendees = [NSMutableArray arrayWithCapacity: [theAttendees count]];
enumerator = [theAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
if (currentUID)
{
{
[attendees addObject: currentUID];
}
}
// If the active user is not the owner of the calendar, check possible conflict when
// the owner is a resource
currentUser = [context activeUser];
if (!activeUserIsOwner && ![currentUser isSuperUser])
{
ownerUser = [SOGoUser userWithLogin: owner];
if ([ownerUser isResource])
{
[attendees addObject: owner];
}
}
enumerator = [attendees objectEnumerator];
while ((currentUID = [enumerator nextObject]))
{
user = [SOGoUser userWithLogin: currentUID];
if ([user isResource])
@@ -461,8 +481,7 @@
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
folder = [[SOGoUser userWithLogin: currentUID]
personalCalendarFolderInContext: context];
folder = [user personalCalendarFolderInContext: context];
// Deny access to the resource if the ACLs don't allow the user
if (![folder aclSQLListingFilter])
@@ -526,52 +545,64 @@
[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])
// Find the attendee associated to the current UID
for (i = 0; i < [theAttendees count]; i++)
{
currentAttendee = [theAttendees objectAtIndex: i];
if ([[currentAttendee uid] isEqualToString: currentUID])
break;
else
currentAttendee = nil;
}
if (currentAttendee)
{
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 attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
else
{
iCalCalendar *calendar;
NSDictionary *values;
NSString *reason;
iCalEvent *event;
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
event = [[calendar events] lastObject];
values = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
[user cn], @"Cn",
[user systemEmail], @"SystemEmail",
([event summary] ? [event summary] : @""), @"EventTitle",
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
nil];
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
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 attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
else
{
iCalCalendar *calendar;
NSDictionary *values;
NSString *reason;
iCalEvent *event;
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
event = [[calendar events] lastObject];
values = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
[user cn], @"Cn",
[user systemEmail], @"SystemEmail",
([event summary] ? [event summary] : @""), @"EventTitle",
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
nil];
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
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 attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
}
}
}
}
}
return nil;
@@ -787,7 +818,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[self expandGroupsInEvent: newEvent];
// We first update the event. It is important to this initially
// We first update the event. It is important to do this initially
// as the event's UID might get modified.
[super updateComponent: newEvent];
@@ -795,13 +826,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
// New event -- send invitation to all attendees
attendees = [newEvent attendeesWithoutUser: ownerUser];
// We catch conflicts and abort the save process immediately
// in case of one with resources
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
return ex;
if ([attendees count])
{
// 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
@@ -1375,9 +1407,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
// We generate the updated iCalendar file and we save it in
// the database. We do this ONLY when using SOGo from the
// Web interface. Over DAV, it'll be handled directly in
// PUTAction:
if (![context request] || [[context request] handledByDefaultHandler])
// Web interface or over ActiveSync.
// Over DAV, it'll be handled directly in PUTAction:
if (![context request]
|| [[context request] handledByDefaultHandler]
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
ex = [self saveContentString: [[event parent] versitString]];
}
}
@@ -1,9 +1,6 @@
/* SOGoCalendarComponent.h - this file is part of SOGo
*
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* Copyright (C) 2006-2013 Inverse inc.
*
* 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
+49 -35
View File
@@ -1,10 +1,6 @@
/* SOGoCalendarComponent.m - this file is part of SOGo
*
* Copyright (C) 2006-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
* Copyright (C) 2006-2014 Inverse inc.
*
* 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
@@ -22,8 +18,10 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -62,6 +60,7 @@
#import <SOGo/SOGoWebDAVAclManager.h>
#import <SOGo/WORequest+SOGo.h>
#import <Appointments/SOGoAppointmentFolder.h>
#import <Mailer/NSString+Mail.h>
#import "SOGoAptMailICalReply.h"
#import "SOGoAptMailNotification.h"
@@ -219,29 +218,13 @@
- (NSString *) secureContentAsString
{
iCalCalendar *tmpCalendar;
iCalRepeatableEntityObject *tmpComponent;
// NSArray *roles;
iCalCalendar *tmpCalendar;
NSArray *allComponents;
SoSecurityManager *sm;
NSString *iCalString;
// uid = [[context activeUser] login];
// roles = [self aclsForUser: uid];
// if ([roles containsObject: SOGoCalendarRole_Organizer]
// || [roles containsObject: SOGoCalendarRole_Participant]
// || [roles containsObject: SOGoCalendarRole_ComponentViewer])
// calContent = content;
// else if ([roles containsObject: SOGoCalendarRole_ComponentDAndTViewer])
// {
// tmpCalendar = [[self calendar: NO] copy];
// tmpComponent = (iCalRepeatableEntityObject *)
// [tmpCalendar firstChildWithTag: [self componentTag]];
// [self _filterComponent: tmpComponent];
// calContent = [tmpCalendar versitString];
// [tmpCalendar release];
// }
// else
// calContent = nil;
int i;
sm = [SoSecurityManager sharedSecurityManager];
if (activeUserIsOwner
@@ -253,14 +236,21 @@
onObject: self inContext: context])
{
tmpCalendar = [[self calendar: NO secure: NO] mutableCopy];
tmpComponent = (iCalRepeatableEntityObject *)
[tmpCalendar firstChildWithTag: [self componentTag]];
[self _filterComponent: tmpComponent];
// We filter all components, in case we have RECURRENCE-ID
allComponents = [tmpCalendar childrenWithTag: [self componentTag]];
for (i = 0; i < [allComponents count]; i++)
{
tmpComponent = (iCalRepeatableEntityObject *)[allComponents objectAtIndex:i];
[self _filterComponent: tmpComponent];
// We add an additional header here to inform clients (if necessary) that
// we churned the content of the calendar.
[tmpComponent addChild: [CardElement simpleElementWithTag: @"X-SOGo-Secure"
value: @"YES"]];
// We add an additional header here to inform clients (if necessary) that
// we churned the content of the calendar.
[tmpComponent addChild: [CardElement simpleElementWithTag: @"X-SOGo-Secure"
value: @"YES"]];
}
iCalString = [tmpCalendar versitString];
[tmpCalendar release];
}
@@ -499,18 +489,31 @@
NSMutableArray *allAttendees;
iCalPerson *currentAttendee;
NSEnumerator *enumerator;
NSAutoreleasePool *pool;
SOGoGroup *group;
BOOL eventWasModified;
unsigned int i;
unsigned int i, j;
domain = [[context activeUser] domain];
organizerEmail = [[theEvent organizer] rfc822Email];
eventWasModified = NO;
allAttendees = [NSMutableArray arrayWithArray: [theEvent attendees]];
enumerator = [[theEvent attendees] objectEnumerator];
j = 0;
pool = [[NSAutoreleasePool alloc] init];
while ((currentAttendee = [enumerator nextObject]))
{
if (j%5 == 0)
{
RELEASE(pool);
pool = [[NSAutoreleasePool alloc] init];
}
group = [SOGoGroup groupWithEmail: [currentAttendee rfc822Email]
inDomain: domain];
if (group)
@@ -554,11 +557,15 @@
eventWasModified = YES;
}
}
j++;
} // while (currentAttendee ...
if (eventWasModified)
[theEvent setAttendees: allAttendees];
RELEASE(pool);
return eventWasModified;
}
@@ -841,6 +848,7 @@
mailDate = [[NSCalendarDate date] rfc822DateString];
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: subject forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
if ([msgType length] > 0)
[headerMap setObject: msgType forKey: @"x-sogo-message-type"];
msg = [NGMimeMessage messageWithHeader: headerMap];
@@ -870,8 +878,7 @@
sendMimePart: msg
toRecipients: [NSArray arrayWithObject: email]
sender: shortSenderEmail
withAuthenticator: [self
authenticatorInContext: context]
withAuthenticator: [self authenticatorInContext: context]
inContext: context];
}
}
@@ -896,7 +903,7 @@
SOGoDomainDefaults *dd;
dd = [from domainDefaults];
if ([dd appointmentSendEMailNotifications])
if ([dd appointmentSendEMailNotifications] && [event isStillRelevant])
{
/* get WOApplication instance */
app = [WOApplication application];
@@ -925,6 +932,7 @@
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: [[p getSubject] asQPSubjectString: @"UTF-8"]
forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
[headerMap setObject: @"calendar:invitation-reply" forKey: @"x-sogo-message-type"];
@@ -1034,6 +1042,11 @@
// Recipient is fixed, which is the calendar owner
ownerUser = [SOGoUser userWithLogin: self->owner];
recipientIdentity = [ownerUser primaryIdentity];
// Safety net for broken configurations
if (!recipientIdentity)
return;
recipientEmail = [recipientIdentity objectForKey: @"email"];
fullRecipientEmail = [recipientIdentity keysWithFormat: @"%{fullName} <%{email}>"];
@@ -1042,6 +1055,7 @@
mailDate = [[NSCalendarDate date] rfc822DateString];
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: [page getSubject] forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
[headerMap setObject: @"text/html; charset=utf-8"
forKey: @"content-type"];
+5 -3
View File
@@ -1,14 +1,16 @@
/*
Copyright (C) 2006-2013 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.
@@ -1,6 +1,6 @@
/* iCalEntityObject+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2013 Inverse inc.
* Copyright (C) 2007-2014 Inverse inc.
*
* 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
@@ -1,8 +1,6 @@
/* iCalEntityObject+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2014 Inverse inc.
*
* 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
+15 -3
View File
@@ -1,8 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Cyril Robert <crobert@inverse.ca>
* Copyright (C) 2009-2014 Inverse inc.
*
* 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
@@ -30,9 +28,23 @@
@interface NGVCard (SOGoExtensions)
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type;
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
- (NSString *) workCompany;
- (NSString *) fullName;
- (NSArray *) secondaryEmails;
- (NSString *) workPhone;
- (NSString *) homePhone;
- (NSString *) fax;
- (NSString *) mobile;
- (NSString *) pager;
- (NSCalendarDate *) birthday;
@end
#endif /* NGVCARD_SOGO_H */
+165 -16
View File
@@ -1,8 +1,6 @@
/* NGVCard+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Cyril Robert <crobert@inverse.ca>
* Copyright (C) 2009-2014 Inverse inc.
*
* 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
@@ -29,6 +27,8 @@
#import <NGCards/NSArray+NGCards.h>
#import <NGCards/NSString+NGCards.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import "NSDictionary+LDIF.h"
#import "NGVCard+SOGo.h"
@@ -164,8 +164,8 @@ convention:
@implementation NGVCard (SOGoExtensions)
/* LDIF -> VCARD */
- (CardElement *) _elementWithTag: (NSString *) elementTag
ofType: (NSString *) type
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type
{
NSArray *elements;
CardElement *element;
@@ -188,16 +188,16 @@ convention:
{
CardElement *phone;
phone = [self _elementWithTag: @"tel" ofType: @"work"];
phone = [self elementWithTag: @"tel" ofType: @"work"];
[phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"home"];
phone = [self elementWithTag: @"tel" ofType: @"home"];
[phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"cell"];
phone = [self elementWithTag: @"tel" ofType: @"cell"];
[phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"fax"];
phone = [self elementWithTag: @"tel" ofType: @"fax"];
[phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]
forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"pager"];
phone = [self elementWithTag: @"tel" ofType: @"pager"];
[phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""];
}
@@ -205,9 +205,9 @@ convention:
{
CardElement *mail, *homeMail;
mail = [self _elementWithTag: @"email" ofType: @"work"];
mail = [self elementWithTag: @"email" ofType: @"work"];
[mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""];
homeMail = [self _elementWithTag: @"email" ofType: @"home"];
homeMail = [self elementWithTag: @"email" ofType: @"home"];
[homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""];
[[self uniqueChildWithTag: @"x-mozilla-html"]
setSingleValue: [ldifRecord objectForKey: @"mozillausehtmlmail"]
@@ -230,7 +230,7 @@ convention:
[self setFn: [ldifRecord objectForKey: @"displayname"]];
[self setTitle: [ldifRecord objectForKey: @"title"]];
element = [self _elementWithTag: @"adr" ofType: @"home"];
element = [self elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"]
atIndex: 1 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet"]
@@ -244,7 +244,7 @@ convention:
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomecountryname"]
atIndex: 6 forKey: @""];
element = [self _elementWithTag: @"adr" ofType: @"work"];
element = [self elementWithTag: @"adr" ofType: @"work"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillaworkstreet2"]
atIndex: 1 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"street"]
@@ -268,9 +268,9 @@ convention:
[self _setPhoneValues: ldifRecord];
[self _setEmails: ldifRecord];
[[self _elementWithTag: @"url" ofType: @"home"]
[[self elementWithTag: @"url" ofType: @"home"]
setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""];
[[self _elementWithTag: @"url" ofType: @"work"]
[[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""];
[[self uniqueChildWithTag: @"x-aim"]
@@ -639,4 +639,153 @@ convention:
return ldifRecord;
}
- (NSString *) workCompany
{
CardElement *org;
NSString *company;
org = [self org];
company = [org flattenedValueAtIndex: 0 forKey: @""];
if ([company length] == 0)
company = nil;
return company;
}
- (NSString *) fullName
{
CardElement *n;
NSString *fn, *firstName, *lastName, *org;
fn = [self fn];
if ([fn length] == 0)
{
n = [self n];
lastName = [n flattenedValueAtIndex: 0 forKey: @""];
firstName = [n flattenedValueAtIndex: 1 forKey: @""];
if ([firstName length] > 0)
{
if ([lastName length] > 0)
fn = [NSString stringWithFormat: @"%@ %@", firstName, lastName];
else
fn = firstName;
}
else if ([lastName length] > 0)
fn = lastName;
else
{
n = [self org];
org = [n flattenedValueAtIndex: 0 forKey: @""];
fn = org;
}
}
return fn;
}
- (NSArray *) secondaryEmails
{
NSMutableArray *emails;
NSString *email;
int i;
emails = [NSMutableArray array];
[emails addObjectsFromArray: [self childrenWithTag: @"email"]];
[emails removeObjectsInArray: [self childrenWithTag: @"email"
andAttribute: @"type"
havingValue: @"pref"]];
for (i = [emails count]-1; i >= 0; i--)
{
email = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
if ([email caseInsensitiveCompare: [self preferredEMail]] == NSOrderedSame)
[emails removeObjectAtIndex: i];
}
return emails;
}
- (NSString *) _phoneOfType: (NSString *) aType
excluding: (NSString *) aTypeToExclude
{
NSArray *elements, *phones;
NSString *phone;
phones = [self childrenWithTag: @"tel"];
elements = [phones cardElementsWithAttribute: @"type"
havingValue: aType];
phone = nil;
if ([elements count] > 0)
{
CardElement *ce;
int i;
for (i = 0; i < [elements count]; i++)
{
ce = [elements objectAtIndex: i];
phone = [ce flattenedValuesForKey: @""];
if (!aTypeToExclude)
break;
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
break;
phone = nil;
}
}
return phone;
}
- (NSString *) workPhone
{
// We do this (exclude FAX) in order to avoid setting the WORK number as the FAX
// one if we do see the FAX field BEFORE the WORK number.
return [self _phoneOfType: @"work" excluding: @"fax"];
}
- (NSString *) homePhone
{
return [self _phoneOfType: @"home" excluding: @"fax"];
}
- (NSString *) fax
{
return [self _phoneOfType: @"fax" excluding: nil];
}
- (NSString *) mobile
{
return [self _phoneOfType: @"cell" excluding: nil];
}
- (NSString *) pager
{
return [self _phoneOfType: @"pager" excluding: nil];
}
- (NSCalendarDate *) birthday
{
NSString *bday, *value;
NSCalendarDate *date;
bday = [self bday];
date = nil;
if ([bday length] > 0)
{
// Expected format of BDAY is YYYY[-]MM[-]DD
value = [bday stringByReplacingString: @"-" withString: @""];
date = [NSCalendarDate dateFromShortDateString: value
andShortTimeString: nil
inTimeZone: nil];
}
return date;
}
@end /* NGVCard */
+3 -3
View File
@@ -1,8 +1,6 @@
/* SOGoContactFolders.h - this file is part of SOGo
*
* Copyright (C) 2006, 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2013 Inverse inc.
*
* 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,6 +29,8 @@
withDisplayName: (NSString *) newDisplayName;
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID;
- (NSDictionary *) systemSources;
@end
#endif /* SOGOCONTACTFOLDERS_H */
+12 -2
View File
@@ -110,8 +110,9 @@
return result;
}
- (NSException *) appendSystemSources
- (NSDictionary *) systemSources
{
NSMutableDictionary *systemSources;
SOGoUserManager *um;
SOGoSystemDefaults *sd;
NSEnumerator *sourceIDs, *domains;
@@ -119,6 +120,8 @@
SOGoContactSourceFolder *currentFolder;
SOGoUser *currentUser;
systemSources = [NSMutableDictionary dictionary];
if (! ([[context request] isIPhoneAddressBookApp] &&
![[context request] isAndroid]))
{
@@ -143,13 +146,20 @@
andDisplayName: srcDisplayName
inContainer: self];
[currentFolder setSource: [um sourceWithID: currentSourceID]];
[subFolders setObject: currentFolder forKey: currentSourceID];
[systemSources setObject: currentFolder forKey: currentSourceID];
}
domain = [domains nextObject];
}
}
}
return systemSources;
}
- (NSException *) appendSystemSources
{
[subFolders addEntriesFromDictionary: [self systemSources]];
return nil;
}
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoContactGCSEntry.h - this file is part of SOGo
*
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2014 Inverse inc.
*
* 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
+7 -3
View File
@@ -1,8 +1,6 @@
/* SOGoContactGCSEntry.h - this file is part of SOGo
*
* Copyright (C) 2006-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2014 Inverse inc.
*
* 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
@@ -159,6 +157,12 @@
return result;
}
- (NSException *) saveComponent: (NGVCard *) newCard
{
ASSIGN(card, newCard);
return [self save];
}
- (NSException *) saveContentString: (NSString *) newContent
baseVersion: (unsigned int) newVersion
{
+1 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2011 Inverse inc.
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
+5 -3
View File
@@ -1,14 +1,16 @@
/*
Copyright (C) 2006-2014 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.
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoContactSourceFolder.h - this file is part of SOGo
*
* Copyright (C) 2006-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2013 Inverse inc.
*
* 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
+8 -9
View File
@@ -1,8 +1,6 @@
/* SOGoContactSourceFolder.m - this file is part of SOGo
*
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2013 Inverse inc.
*
* 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
@@ -534,14 +532,12 @@
[r appendString: [[object objectForKey: @"c_name"] stringByEscapingURL]];
[r appendString: @"</D:href>"];
// NSLog (@"(appendPropstats...): %@", [NSDate date]);
propstats = [self _propstats: properties count: propertiesCount
ofObject: object];
max = [propstats count];
for (count = 0; count < max; count++)
[self _appendPropstat: [propstats objectAtIndex: count]
toBuffer: r];
// NSLog (@"/(appendPropstats...): %@", [NSDate date]);
toBuffer: r];
[r appendString: @"</D:response>"];
}
@@ -562,8 +558,10 @@
NSString *url, *baseURL, *cname;
NSString **propertiesArray;
NSMutableString *buffer;
unsigned int count, max, propertiesCount;
NSDictionary *object;
unsigned int count, max, propertiesCount;
baseURL = [self davURLAsString];
#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276
if (![baseURL hasSuffix: @"/"])
@@ -580,8 +578,9 @@
element = [refs objectAtIndex: count];
url = [[[element firstChild] nodeValue] stringByUnescapingURL];
cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL];
if (cname)
[self appendObject: [source lookupContactEntry: cname]
object = [source lookupContactEntry: cname];
if (object)
[self appendObject: object
properties: propertiesArray
count: propertiesCount
withBaseURL: baseURL
+4
View File
@@ -92,6 +92,10 @@ Mailer_RESOURCE_FILES += \
SOGoMailWelshReply.wo
Mailer_LANGUAGES = English French
Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/
ADDITIONAL_INCLUDE_DIRS += $(shell xml2-config --cflags)
ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/
+6 -1
View File
@@ -1,6 +1,6 @@
/* NSString+Mail.h - this file is part of SOGo
*
* Copyright (C) 2007-2013 Inverse inc.
* Copyright (C) 2007-2014 Inverse inc.
*
* 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
@@ -21,12 +21,17 @@
#ifndef NSSTRING_MAIL_H
#define NSSTRING_MAIL_H
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
@interface NSString (SOGoExtension)
+ (NSString *) generateMessageID;
- (NSString *) htmlToText;
- (NSString *) htmlByExtractingImages: (NSMutableArray *) theImages;
- (NSString *) stringByConvertingCRLNToHTML;
- (int) indexOf: (unichar) _c
fromIndex: (int) start;
- (int) indexOf: (unichar) _c;
- (NSString *) decodedHeader;
+258 -44
View File
@@ -1,6 +1,6 @@
/* NSString+Mail.m - this file is part of SOGo
*
* Copyright (C) 2008-2013 Inverse inc.
* Copyright (C) 2008-2014 Inverse inc.
*
* 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
@@ -20,21 +20,28 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSException.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSValue.h>
#import <SaxObjC/SaxAttributes.h>
#import <SaxObjC/SaxContentHandler.h>
#import <SaxObjC/SaxLexicalHandler.h>
#import <SaxObjC/SaxXMLReader.h>
#import <SaxObjC/SaxXMLReaderFactory.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NGQuotedPrintableCoding.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeFileData.h>
#include <libxml/encoding.h>
#import "NSString+Mail.h"
#import "NSData+Mail.h"
#import "../SOGo/SOGoObject.h"
#if 0
#define showWhoWeAre() \
@@ -43,11 +50,16 @@
#define showWhoWeAre() {}
#endif
@interface _SOGoHTMLToTextContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
#define paddingBuffer 8192
@interface _SOGoHTMLContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
{
NSMutableArray *images;
NSArray *ignoreContentTags;
NSArray *specialTreatmentTags;
NSArray *voidTags;
BOOL ignoreContent;
BOOL orderedList;
BOOL unorderedList;
@@ -57,32 +69,27 @@
}
+ (id) htmlToTextContentHandler;
+ (id) sanitizerContentHandler;
- (NSString *) result;
- (void) setIgnoreContentTags: (NSArray *) theTags;
- (void) setSpecialTreatmentTags: (NSArray *) theTags;
- (void) setVoidTags: (NSArray *) theTags;
- (void) setImages: (NSMutableArray *) theImages;
@end
@implementation _SOGoHTMLToTextContentHandler
+ (id) htmlToTextContentHandler
{
static id htmlToTextContentHandler;
if (!htmlToTextContentHandler)
htmlToTextContentHandler = [self new];
return htmlToTextContentHandler;
}
@implementation _SOGoHTMLContentHandler
- (id) init
{
if ((self = [super init]))
{
ignoreContentTags = [NSArray arrayWithObjects: @"head", @"script",
@"style", nil];
specialTreatmentTags = [NSArray arrayWithObjects: @"body", @"p", @"ul",
@"li", @"table", @"tr", @"td", @"th",
@"br", @"hr", @"dt", @"dd", nil];
images = nil;
ignoreContentTags = nil;
specialTreatmentTags = nil;
[ignoreContentTags retain];
[specialTreatmentTags retain];
@@ -97,6 +104,37 @@
return self;
}
+ (id) htmlToTextContentHandler
{
static id htmlToTextContentHandler;
if (!htmlToTextContentHandler)
htmlToTextContentHandler = [self new];
[htmlToTextContentHandler setIgnoreContentTags: [NSArray arrayWithObjects: @"head", @"script",
@"style", nil]];
[htmlToTextContentHandler setSpecialTreatmentTags: [NSArray arrayWithObjects: @"p", @"ul",
@"li", @"table", @"tr", @"td", @"th",
@"br", @"hr", @"dt", @"dd", nil]];
return htmlToTextContentHandler;
}
+ (id) sanitizerContentHandler
{
static id sanitizerContentHandler;
if (!sanitizerContentHandler)
sanitizerContentHandler = [self new];
[sanitizerContentHandler setVoidTags: [NSArray arrayWithObjects: @"area", @"base",
@"basefont", @"br", @"col", @"frame", @"hr",
@"img", @"input", @"isindex", @"link",
@"meta", @"param", @"", nil]];
return sanitizerContentHandler;
}
- (xmlCharEncoding) contentEncoding
{
return XML_CHAR_ENCODING_UTF8;
@@ -121,6 +159,30 @@
return newResult;
}
- (void) setIgnoreContentTags: (NSArray *) theTags
{
ASSIGN(ignoreContentTags, theTags);
}
- (void) setSpecialTreatmentTags: (NSArray *) theTags
{
ASSIGN(specialTreatmentTags, theTags);
}
- (void) setVoidTags: (NSArray *) theTags
{
ASSIGN(voidTags, theTags);
}
//
// We MUST NOT retain the array here
//
- (void) setImages: (NSMutableArray *) theImages
{
images = theImages;
}
/* SaxContentHandler */
- (void) startDocument
{
@@ -169,12 +231,12 @@
else if ([tagName isEqualToString: @"li"])
{
if (orderedList)
{
listCount++;
[result appendFormat: @" %d. ", listCount];
}
{
listCount++;
[result appendFormat: @" %d. ", listCount];
}
else
[result appendString: @" * "];
[result appendString: @" * "];
}
else if ([tagName isEqualToString: @"dd"])
[result appendString: @" "];
@@ -197,7 +259,7 @@
[result appendString: @":\n"];
}
else if ([tagName isEqualToString: @"li"]
|| [tagName isEqualToString: @"dd"])
|| [tagName isEqualToString: @"dd"])
[result appendString: @"\n"];
}
@@ -210,13 +272,116 @@
showWhoWeAre();
if (!ignoreContent)
tagName = [rawName lowercaseString];
if (!ignoreContent && ignoreContentTags && specialTreatmentTags)
{
tagName = [rawName lowercaseString];
if ([ignoreContentTags containsObject: tagName])
ignoreContent = YES;
ignoreContent = YES;
else if ([specialTreatmentTags containsObject: tagName])
[self _startSpecialTreatment: tagName];
[self _startSpecialTreatment: tagName];
}
else
{
if ([tagName isEqualToString: @"img"])
{
NSString *value;
value = [attributes valueForRawName: @"src"];
//
// Check for Data URI Scheme
//
// data:[<MIME-type>][;charset=<encoding>][;base64],<data>
//
if ([value length] > 5 && [[value substringToIndex: 5] caseInsensitiveCompare: @"data:"] == NSOrderedSame)
{
NSString *uniqueId, *mimeType, *encoding;
NGMimeBodyPart *bodyPart;
NGMutableHashMap *map;
NSData *data;
id body;
int i, j, k;
i = [value indexOf: ';'];
j = [value indexOf: ';' fromIndex: i+1];
k = [value indexOf: ','];
// We try to get the MIME type
mimeType = nil;
if (i > 5 && i < k)
{
mimeType = [value substringWithRange: NSMakeRange(5, i-5)];
}
else
i = 5;
// We might get a stupid value. We discard anything that doesn't have a / in it
if ([mimeType indexOf: '/'] < 0)
mimeType = @"image/jpeg";
// We check and skip the charset
if (j < i)
j = i;
// We check the encoding and we completely ignore it
encoding = [value substringWithRange: NSMakeRange(j+1, k-j-1)];
if (![encoding length])
encoding = @"base64";
data = [[value substringFromIndex: k+1] dataUsingEncoding: NSASCIIStringEncoding];
uniqueId = [SOGoObject globallyUniqueObjectId];
map = [[[NGMutableHashMap alloc] initWithCapacity:5] autorelease];
[map setObject: encoding forKey: @"content-transfer-encoding"];
[map setObject:[NSNumber numberWithInt:[data length]] forKey: @"content-length"];
[map setObject: [NSString stringWithFormat: @"inline; filename=\"%@\"", uniqueId] forKey: @"content-disposition"];
[map setObject: [NSString stringWithFormat: @"%@; name=\"%@\"", mimeType, uniqueId] forKey: @"content-type"];
[map setObject: [NSString stringWithFormat: @"<%@>", uniqueId] forKey: @"content-id"];
body = [[NGMimeFileData alloc] initWithBytes: [data bytes] length: [data length]];
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
[bodyPart setBody: body];
[body release];
[images addObject: bodyPart];
[result appendFormat: @"<img src=\"cid:%@\" type=\"%@\"/>", uniqueId, mimeType];
}
}
else if (voidTags)
{
NSString *type;
int i;
[result appendString: @"<"];
[result appendString: rawName];
for (i = 0; i < [attributes count]; i++)
{
[result appendString: @" "];
[result appendString: [attributes nameAtIndex: i]];
[result appendString: @"='"];
[result appendString: [attributes valueAtIndex: i]];
[result appendString: @"'"];
type = [attributes typeAtIndex: i];
if (![type isEqualToString: @"CDATA"])
{
[result appendString: @"["];
[result appendString: type];
[result appendString: @"]"];
}
}
if ([voidTags containsObject: tagName])
[result appendString: @"/"];
[result appendString: @">"];
}
}
}
@@ -228,13 +393,22 @@
showWhoWeAre();
if (ignoreContent)
if (ignoreContentTags && specialTreatmentTags)
{
if (ignoreContent)
{
tagName = [rawName lowercaseString];
if ([ignoreContentTags containsObject: tagName])
ignoreContent = NO;
else if ([specialTreatmentTags containsObject: tagName])
[self _endSpecialTreatment: tagName];
}
}
else if (voidTags)
{
tagName = [rawName lowercaseString];
if ([ignoreContentTags containsObject: tagName])
ignoreContent = NO;
else if ([specialTreatmentTags containsObject: tagName])
[self _endSpecialTreatment: tagName];
if (![voidTags containsObject: tagName])
[result appendFormat: @"</%@>", rawName];
}
}
@@ -343,15 +517,28 @@
@implementation NSString (SOGoExtension)
+ (NSString *) generateMessageID
{
NSMutableString *messageID;
NSString *pGUID;
messageID = [NSMutableString string];
[messageID appendFormat: @"<%@", [SOGoObject globallyUniqueObjectId]];
pGUID = [[NSProcessInfo processInfo] globallyUniqueString];
[messageID appendFormat: @"@%u>", [pGUID hash]];
return [messageID lowercaseString];
}
- (NSString *) htmlToText
{
_SOGoHTMLToTextContentHandler *handler;
_SOGoHTMLContentHandler *handler;
id <NSObject, SaxXMLReader> parser;
NSData *d;
parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
createXMLReaderForMimeType: @"text/html"];
handler = [_SOGoHTMLToTextContentHandler htmlToTextContentHandler];
handler = [_SOGoHTMLContentHandler htmlToTextContentHandler];
[parser setContentHandler: handler];
d = [self dataUsingEncoding: NSUTF8StringEncoding];
@@ -360,7 +547,24 @@
return [handler result];
}
#define paddingBuffer 8192
- (NSString *) htmlByExtractingImages: (NSMutableArray *) theImages
{
_SOGoHTMLContentHandler *handler;
id <NSObject, SaxXMLReader> parser;
NSData *d;
parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
createXMLReaderForMimeType: @"text/html"];
handler = [_SOGoHTMLContentHandler sanitizerContentHandler];
[handler setImages: theImages];
[parser setContentHandler: handler];
d = [self dataUsingEncoding: NSUTF8StringEncoding];
[parser parseFromSource: d];
return [handler result];
}
static inline char *
convertChars (const char *oldString, unsigned int oldLength,
@@ -423,8 +627,7 @@ convertChars (const char *oldString, unsigned int oldLength,
unsigned int newLength;
utf8String = [self UTF8String];
newString = convertChars (utf8String, strlen (utf8String),
&newLength);
newString = convertChars (utf8String, strlen (utf8String), &newLength);
convertedString = [[NSString alloc] initWithBytes: newString
length: newLength
encoding: NSUTF8StringEncoding];
@@ -434,18 +637,29 @@ convertChars (const char *oldString, unsigned int oldLength,
return convertedString;
}
- (int) indexOf: (unichar) _c
fromIndex: (int) start
{
int i, len;
len = [self length];
for (i = 0; i < len; i++)
if (start < 0 || start >= len)
start = 0;
for (i = start; i < len; i++)
{
if ([self characterAtIndex: i] == _c) return i;
}
return -1;
}
- (int) indexOf: (unichar) _c
{
return [self indexOf: _c fromIndex: 0];
}
- (NSString *) decodedHeader
@@ -453,7 +667,7 @@ convertChars (const char *oldString, unsigned int oldLength,
NSString *decodedHeader;
decodedHeader = [[self dataUsingEncoding: NSASCIIStringEncoding]
decodedHeader];
decodedHeader];
if (!decodedHeader)
decodedHeader = self;
+6 -3
View File
@@ -40,10 +40,11 @@
@class NSData;
@class NSDictionary;
@class NSException;
@class NGImap4Envelope;
@class NGMimeMessage;
@class NSMutableDictionary;
@class NSString;
@class NGImap4Envelope;
@class NGMimeBodyPart;
@class NGMimeMessage;
@class SOGoMailObject;
@@ -87,8 +88,10 @@
/* attachments */
- (NSArray *) fetchAttachmentNames;
- (NSArray *) fetchAttachmentAttrs;
- (BOOL) isValidAttachmentName: (NSString *) _name;
- (NGMimeBodyPart *) bodyPartForAttachmentWithName: (NSString *) _name;
- (NSString *) pathToAttachmentWithName: (NSString *) _name;
- (NSException *) saveAttachment: (NSData *) _attach
withMetadata: (NSDictionary *) metadata;
- (NSException *) deleteAttachmentWithName: (NSString *) _name;
+168 -78
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2012 Inverse inc.
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -256,19 +256,6 @@ static NSString *userAgent = nil;
/* contents */
- (NSString *) _generateMessageID
{
NSMutableString *messageID;
NSString *pGUID;
messageID = [NSMutableString string];
[messageID appendFormat: @"<%@", [self globallyUniqueObjectId]];
pGUID = [[NSProcessInfo processInfo] globallyUniqueString];
[messageID appendFormat: @"@%u>", [pGUID hash]];
return [messageID lowercaseString];
}
- (void) setHeaders: (NSDictionary *) newHeaders
{
id headerValue;
@@ -288,7 +275,7 @@ static NSString *userAgent = nil;
messageID = [headers objectForKey: @"message-id"];
if (!messageID)
{
messageID = [self _generateMessageID];
messageID = [NSString generateMessageID];
[headers setObject: messageID forKey: @"message-id"];
}
@@ -395,11 +382,17 @@ static NSString *userAgent = nil;
[self setSourceFolder: [paths componentsJoinedByString: @"/"]];
}
//
//
//
- (NSString *) sourceFolder
{
return sourceFolder;
}
//
// Store the message definition in a plist file (.info.plist) in the spool directory
//
- (NSException *) storeInfo
{
NSMutableDictionary *infos;
@@ -446,6 +439,9 @@ static NSString *userAgent = nil;
return error;
}
//
//
//
- (void) _loadInfosFromDictionary: (NSDictionary *) infoDict
{
id value;
@@ -478,11 +474,17 @@ static NSString *userAgent = nil;
[self setInReplyTo: value];
}
//
//
//
- (NSString *) relativeImap4Name
{
return [NSString stringWithFormat: @"%d", IMAP4ID];
}
//
//
//
- (void) fetchInfo
{
NSString *p;
@@ -504,16 +506,25 @@ static NSString *userAgent = nil;
[self debugWithFormat: @"Note: info object does not yet exist: %@", p];
}
//
//
//
- (void) setIMAP4ID: (int) newIMAP4ID
{
IMAP4ID = newIMAP4ID;
}
//
//
//
- (int) IMAP4ID
{
return IMAP4ID;
}
//
//
//
- (NSException *) save
{
NGImap4Client *client;
@@ -535,23 +546,31 @@ static NSString *userAgent = nil;
}
folder = [imap4 imap4FolderNameForURL: [container imap4URL]];
result
= [client append: message toFolder: folder
withFlags: [NSArray arrayWithObjects: @"seen", @"draft", nil]];
result = [client append: message toFolder: folder
withFlags: [NSArray arrayWithObjects: @"seen", @"draft", nil]];
if ([[result objectForKey: @"result"] boolValue])
{
if (IMAP4ID > -1)
error = [imap4 markURLDeleted: [self imap4URL]];
IMAP4ID = [self IMAP4IDFromAppendResult: result];
if (imap4URL)
{
// Invalidate the IMAP message URL since the message ID has changed
[imap4URL release];
imap4URL = nil;
}
[self storeInfo];
}
else
error = [NSException exceptionWithHTTPStatus:500 /* Server Error */
reason: @"Failed to store message"];
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: [result objectForKey: @"reason"]];
return error;
}
//
//
//
- (void) _addEMailsOfAddresses: (NSArray *) _addrs
toArray: (NSMutableArray *) _ma
{
@@ -564,6 +583,9 @@ static NSString *userAgent = nil;
[_ma addObject: [currentAddress email]];
}
//
//
//
- (void) _addRecipients: (NSArray *) recipients
toArray: (NSMutableArray *) array
{
@@ -576,6 +598,9 @@ static NSString *userAgent = nil;
[array addObject: [currentAddress baseEMail]];
}
//
//
//
- (void) _purgeRecipients: (NSArray *) recipients
fromAddresses: (NSMutableArray *) addresses
{
@@ -602,6 +627,9 @@ static NSString *userAgent = nil;
}
}
//
//
//
- (void) _fillInReplyAddresses: (NSMutableDictionary *) _info
replyToAll: (BOOL) _replyToAll
envelope: (NGImap4Envelope *) _envelope
@@ -711,6 +739,9 @@ static NSString *userAgent = nil;
}
}
//
//
//
- (NSArray *) _attachmentBodiesFromPaths: (NSArray *) paths
fromResponseFetch: (NSDictionary *) fetch;
{
@@ -731,15 +762,18 @@ static NSString *userAgent = nil;
return bodies;
}
- (void) _fetchAttachments: (NSArray *) parts
fromMail: (SOGoMailObject *) sourceMail
//
//
//
- (void) _fetchAttachmentsFromMail: (SOGoMailObject *) sourceMail
{
unsigned int count, max;
NSArray *paths, *bodies;
NSArray *parts, *paths, *bodies;
NSData *body;
NSDictionary *currentInfo;
NGHashMap *response;
parts = [sourceMail fetchFileAttachmentKeys];
max = [parts count];
if (max > 0)
{
@@ -758,6 +792,9 @@ static NSString *userAgent = nil;
}
}
//
//
//
- (void) fetchMailForEditing: (SOGoMailObject *) sourceMail
{
NSString *subject, *msgid;
@@ -767,8 +804,7 @@ static NSString *userAgent = nil;
[sourceMail fetchCoreInfos];
[self _fetchAttachments: [sourceMail fetchFileAttachmentKeys]
fromMail: sourceMail];
[self _fetchAttachmentsFromMail: sourceMail];
info = [NSMutableDictionary dictionaryWithCapacity: 16];
subject = [sourceMail subject];
if ([subject length] > 0)
@@ -804,10 +840,13 @@ static NSString *userAgent = nil;
[self storeInfo];
}
//
//
//
- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
toAll: (BOOL) toAll
{
NSString *contentForReply, *msgID;
NSString *msgID;
NSMutableDictionary *info;
NGImap4Envelope *sourceEnvelope;
@@ -822,8 +861,7 @@ static NSString *userAgent = nil;
msgID = [sourceEnvelope messageID];
if ([msgID length] > 0)
[self setInReplyTo: msgID];
contentForReply = [sourceMail contentForReply];
[self setText: contentForReply];
[self setText: [sourceMail contentForReply]];
[self setHeaders: info];
[self setSourceURL: [sourceMail imap4URLString]];
[self setSourceFlag: @"Answered"];
@@ -858,8 +896,7 @@ static NSString *userAgent = nil;
if ([[ud mailMessageForwarding] isEqualToString: @"inline"])
{
[self setText: [sourceMail contentForInlineForward]];
[self _fetchAttachments: [sourceMail fetchFileAttachmentKeys]
fromMail: sourceMail];
[self _fetchAttachmentsFromMail: sourceMail];
}
else
{
@@ -869,7 +906,7 @@ static NSString *userAgent = nil;
if ([signature length])
{
nl = (isHTML ? @"<br/>" : @"\n");
[self setText: [NSString stringWithFormat: @"%@-- %@%@", nl, nl, signature]];
[self setText: [NSString stringWithFormat: @"%@%@-- %@%@", nl, nl, nl, signature]];
}
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[sourceMail filenameForForward], @"filename",
@@ -880,6 +917,10 @@ static NSString *userAgent = nil;
}
[self storeInfo];
// Save the message to the IMAP store so the user can eventually view the attached file(s)
// from the Web interface
[self save];
}
/* accessors */
@@ -898,13 +939,19 @@ static NSString *userAgent = nil;
/* attachments */
- (NSArray *) fetchAttachmentNames
//
// Return the attributes (name, size and mime body part) of the files found in the draft folder
// on the local filesystem
//
- (NSArray *) fetchAttachmentAttrs
{
NSMutableArray *ma;
NSFileManager *fm;
NSArray *files;
unsigned count, max;
NSString *filename;
NSDictionary *fileAttrs;
NGMimeBodyPart *bodyPart;
unsigned count, max;
fm = [NSFileManager defaultManager];
files = [fm directoryContentsAtPath: [self draftFolderPath]];
@@ -915,7 +962,13 @@ static NSString *userAgent = nil;
{
filename = [files objectAtIndex: count];
if (![filename hasPrefix: @"."])
[ma addObject: filename];
{
fileAttrs = [fm fileAttributesAtPath: [self pathToAttachmentWithName: filename] traverseLink: YES];
bodyPart = [self bodyPartForAttachmentWithName: filename];
[ma addObject: [NSDictionary dictionaryWithObjectsAndKeys: filename, @"filename",
[fileAttrs objectForKey: @"NSFileSize"], @"size",
bodyPart, @"part", nil]];
}
}
return ma;
@@ -1081,11 +1134,11 @@ static NSString *userAgent = nil;
body = [[[NGMimeMultipartBody alloc] initWithPart: message] autorelease];
[map addObject: MultiAlternativeType forKey: @"content-type"];
// Get the text part from it and add it
[body addBodyPart: [self plainTextBodyPartForText]];
// Add the HTML part
[body addBodyPart: [self bodyPartForText]];
// Get the text part from it and add it too
[body addBodyPart: [self plainTextBodyPartForText]];
}
[message setBody: body];
@@ -1174,8 +1227,8 @@ static NSString *userAgent = nil;
/* check attachment */
fm = [NSFileManager defaultManager];
p = [self pathToAttachmentWithName:_name];
if (![fm isReadableFileAtPath:p]) {
p = [self pathToAttachmentWithName: _name];
if (![fm isReadableFileAtPath: p]) {
[self errorWithFormat: @"did not find attachment: '%@'", _name];
return nil;
}
@@ -1184,21 +1237,21 @@ static NSString *userAgent = nil;
/* prepare header of body part */
map = [[[NGMutableHashMap alloc] initWithCapacity:4] autorelease];
map = [[[NGMutableHashMap alloc] initWithCapacity: 4] autorelease];
if ((s = [self contentTypeForAttachmentWithName:_name]) != nil) {
[map setObject:s forKey: @"content-type"];
[map setObject: s forKey: @"content-type"];
if ([s hasPrefix: @"text/plain"] || [s hasPrefix: @"text/html"])
attachAsString = YES;
else if ([s hasPrefix: @"message/rfc822"])
attachAsRFC822 = YES;
}
if ((s = [self contentDispositionForAttachmentWithName:_name]))
if ((s = [self contentDispositionForAttachmentWithName: _name]))
{
NGMimeContentDispositionHeaderField *o;
o = [[NGMimeContentDispositionHeaderField alloc] initWithString: s];
[map setObject:o forKey: @"content-disposition"];
[map setObject: o forKey: @"content-disposition"];
[o release];
}
@@ -1233,7 +1286,6 @@ static NSString *userAgent = nil;
if (attachAsRFC822)
{
[map setObject: @"8bit" forKey: @"content-transfer-encoding"];
[map setObject: @"inline" forKey: @"content-disposition"];
}
else
{
@@ -1255,27 +1307,33 @@ static NSString *userAgent = nil;
return bodyPart;
}
//
//
//
- (NSArray *) bodyPartsForAllAttachments
{
/* returns nil on error */
NSArray *names;
NSArray *attrs;
unsigned i, count;
NGMimeBodyPart *bodyPart;
NSMutableArray *bodyParts;
names = [self fetchAttachmentNames];
count = [names count];
attrs = [self fetchAttachmentAttrs];
count = [attrs count];
bodyParts = [NSMutableArray arrayWithCapacity: count];
for (i = 0; i < count; i++)
{
bodyPart = [self bodyPartForAttachmentWithName: [names objectAtIndex: i]];
bodyPart = [self bodyPartForAttachmentWithName: [[attrs objectAtIndex: i] objectForKey: @"filename"]];
[bodyParts addObject: bodyPart];
}
return bodyParts;
}
//
//
//
- (NGMimeBodyPart *) mimeMultipartAlternative
{
NGMimeMultipartBody *textParts;
@@ -1289,11 +1347,11 @@ static NSString *userAgent = nil;
textParts = [[NGMimeMultipartBody alloc] initWithPart: part];
// Get the text part from it and add it
[textParts addBodyPart: [self plainTextBodyPartForText]];
// Add the HTML part
[textParts addBodyPart: [self bodyPartForText]];
// Get the text part from it and add it too
[textParts addBodyPart: [self plainTextBodyPartForText]];
[part setBody: textParts];
RELEASE(textParts);
@@ -1301,6 +1359,9 @@ static NSString *userAgent = nil;
return part;
}
//
//
//
- (NGMimeMessage *) mimeMultiPartMessageWithHeaderMap: (NGMutableHashMap *) map
andBodyParts: (NSArray *) _bodyParts
{
@@ -1340,6 +1401,9 @@ static NSString *userAgent = nil;
return message;
}
//
//
//
- (void) _addHeaders: (NSDictionary *) _h
toHeaderMap: (NGMutableHashMap *) _map
{
@@ -1511,60 +1575,78 @@ static NSString *userAgent = nil;
return map;
}
//
//
//
- (NGMimeMessage *) mimeMessageWithHeaders: (NSDictionary *) _headers
excluding: (NSArray *) _exclude
extractingImages: (BOOL) _extractImages
{
NGMutableHashMap *map;
NSArray *bodyParts;
NGMimeMessage *message;
NSMutableArray *bodyParts;
NGMimeMessage *message;
NGMutableHashMap *map;
NSString *newText;
message = nil;
bodyParts = [NSMutableArray array];
if (_extractImages)
{
newText = [text htmlByExtractingImages: bodyParts];
if ([bodyParts count])
[self setText: newText];
}
map = [self mimeHeaderMapWithHeaders: _headers
excluding: _exclude];
excluding: _exclude];
if (map)
{
//[self debugWithFormat: @"MIME Envelope: %@", map];
bodyParts = [self bodyPartsForAllAttachments];
if (bodyParts)
{
//[self debugWithFormat: @"attachments: %@", bodyParts];
if ([bodyParts count] == 0)
/* no attachments */
message = [self mimeMessageForContentWithHeaderMap: map];
else
/* attachments, create multipart/mixed */
message = [self mimeMultiPartMessageWithHeaderMap: map
andBodyParts: bodyParts];
//[self debugWithFormat: @"message: %@", message];
}
[bodyParts addObjectsFromArray: [self bodyPartsForAllAttachments]];
//[self debugWithFormat: @"attachments: %@", bodyParts];
if ([bodyParts count] == 0)
/* no attachments */
message = [self mimeMessageForContentWithHeaderMap: map];
else
[self errorWithFormat:
@"could not create body parts for attachments!"];
/* attachments, create multipart/mixed */
message = [self mimeMultiPartMessageWithHeaderMap: map
andBodyParts: bodyParts];
//[self debugWithFormat: @"message: %@", message];
}
return message;
}
//
// Return a NGMimeMessage object with inline HTML images (<img src=data>) extracted as attachments (<img src=cid>).
//
- (NGMimeMessage *) mimeMessage
{
return [self mimeMessageWithHeaders: nil excluding: nil];
return [self mimeMessageWithHeaders: nil excluding: nil extractingImages: YES];
}
//
// Return a NSData object of the message with no alteration.
//
- (NSData *) mimeMessageAsData
{
NGMimeMessageGenerator *generator;
NSData *message;
generator = [NGMimeMessageGenerator new];
message = [generator generateMimeFromPart: [self mimeMessage]];
message = [generator generateMimeFromPart: [self mimeMessageWithHeaders: nil excluding: nil extractingImages: NO]];
[generator release];
return message;
}
//
//
//
- (NSArray *) allRecipients
{
NSMutableArray *allRecipients;
@@ -1584,6 +1666,9 @@ static NSString *userAgent = nil;
return allRecipients;
}
//
//
//
- (NSArray *) allBareRecipients
{
NSMutableArray *bareRecipients;
@@ -1599,11 +1684,17 @@ static NSString *userAgent = nil;
return bareRecipients;
}
//
//
//
- (NSException *) sendMail
{
return [self sendMailAndCopyToSent: YES];
}
//
//
//
- (NSException *) sendMailAndCopyToSent: (BOOL) copyToSent
{
NSMutableData *cleaned_message;
@@ -1620,9 +1711,8 @@ static NSString *userAgent = nil;
NGMimeMessageGenerator *generator;
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
message = [generator generateMimeFromPart: [self mimeMessageWithHeaders: nil
excluding: nil]];
message = [generator generateMimeFromPart: [self mimeMessage]];
//
// We now look for the Bcc: header. If it is present, we remove it.
// Some servers, like qmail, do not remove it automatically.
+2 -1
View File
@@ -80,10 +80,11 @@ typedef enum {
- (NSArray *) toManyRelationshipKeysWithNamespaces: (BOOL) withNSs;
- (NSArray *) allFolderPaths;
- (NSArray *) allFoldersMetadata;
- (BOOL) isInDraftsFolder;
/* special folders */
- (NSString *) inboxFolderNameInContext: (id)_ctx;
- (NSString *) draftsFolderNameInContext: (id)_ctx;
- (NSString *) sentFolderNameInContext: (id)_ctx;
+127 -5
View File
@@ -21,6 +21,7 @@
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSString.h>
@@ -47,6 +48,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoSieveManager.h>
#import "SOGoDraftsFolder.h"
@@ -309,10 +311,7 @@ static NSString *inboxFolderName = @"INBOX";
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
return [manager updateFiltersForLogin: [[self imap4URL] user]
authname: [[self imap4URL] user]
password: [self imap4PasswordRenewed: NO]
account: self];
return [manager updateFiltersForAccount: self];
}
@@ -339,6 +338,9 @@ static NSString *inboxFolderName = @"INBOX";
return folders;
}
//
//
//
- (NSArray *) allFolderPaths
{
NSMutableArray *folderPaths, *namespaces;
@@ -382,8 +384,128 @@ static NSString *inboxFolderName = @"INBOX";
return folderPaths;
}
/* IMAP4 */
//
//
//
- (NSString *) _folderType: (NSString *) folderName
{
NSString *folderType;
if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", inboxFolderName]])
folderType = @"inbox";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self draftsFolderNameInContext: context]]])
folderType = @"draft";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self sentFolderNameInContext: context]]])
folderType = @"sent";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self trashFolderNameInContext: context]]])
folderType = @"trash";
else
folderType = @"folder";
return folderType;
}
- (NSString *) _parentForFolder: (NSString *) folderName
foldersList: (NSArray *) theFolders
{
NSArray *pathComponents;
NSString *s;
int i;
pathComponents = [folderName pathComponents];
s = [[[pathComponents subarrayWithRange: NSMakeRange(0,[pathComponents count]-1)] componentsJoinedByString: @"/"] substringFromIndex: 1];
for (i = 0; i < [theFolders count]; i++)
{
if ([s isEqualToString: [theFolders objectAtIndex: i]])
return s;
}
return nil;
}
//
//
//
- (NSArray *) allFoldersMetadata
{
NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName, *parent;
NSMutableArray *pathComponents, *folders;
SOGoUserManager *userManager;
NSEnumerator *rawFolders;
NSDictionary *folderData;
NSAutoreleasePool *pool;
NSArray *allFolderPaths;
allFolderPaths = [self allFolderPaths];
rawFolders = [allFolderPaths objectEnumerator];
folders = [NSMutableArray array];
while ((currentFolder = [rawFolders nextObject]))
{
// Using a local pool to avoid using too many file descriptors. This could
// happen with tons of mailboxes under "Other Users" as LDAP connections
// are never reused and "autoreleased" at the end. This loop would consume
// lots of LDAP connections during its execution.
pool = [[NSAutoreleasePool alloc] init];
currentDecodedFolder = [currentFolder stringByDecodingImap4FolderName];
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 && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", otherUsersFolderName]])
{
// We have a string like /Other Users/lmarcotte/... under Cyrus, but we could
// also have something like /shared under Dovecot. So we swap the username only
// if we have one, of course.
pathComponents = [NSMutableArray arrayWithArray: [currentDecodedFolder pathComponents]];
if ([pathComponents count] > 2)
{
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
{
currentDisplayName = [NSString stringWithFormat: @"/%@%@",
[self labelForKey: @"OtherUsersFolderName"],
[currentDecodedFolder substringFromIndex:
[otherUsersFolderName length]+1]];
}
}
else if (sharedFoldersName && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", sharedFoldersName]])
currentDisplayName = [NSString stringWithFormat: @"/%@%@", [self labelForKey: @"SharedFoldersName"],
[currentDecodedFolder substringFromIndex: [sharedFoldersName length]+1]];
else
currentDisplayName = currentDecodedFolder;
parent = [self _parentForFolder: currentFolder foldersList: allFolderPaths];
folderData = [NSDictionary dictionaryWithObjectsAndKeys:
currentFolder, @"path",
currentFolderType, @"type",
currentDisplayName, @"displayName",
parent, @"parent",
nil];
[folders addObject: folderData];
[pool release];
}
return folders;
}
/* IMAP4 */
- (NSDictionary *) _mailAccount
{
NSDictionary *mailAccount;
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- الرسالة الاصلية --------<#newLine/>
الموضوع: <#subject/><#newLine/>
التاريخ: <#date/><#newLine/>
@@ -9,5 +10,7 @@
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>مجموعات الأخبار: <#newsgroups/></#hasNewsGroups><#hasReferences>المراجع: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,10 +1,16 @@
-------- Original Message --------
Assunto: <#subject/>
Data: <#date/>
De: <#from/>
<#hasReplyTo>Responder-Para: <#replyTo/></#hasReplyTo><#hasOrganization>Organização: <#organization/></#hasOrganization>Para: <#to/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>Referências: <#references/></#hasReferences>
<#messageBody/>
<#signature/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Original Message --------<#newLine/>
Assunto: <#subject/><#newLine/>
Data: <#date/><#newLine/>
De: <#from/><#newLine/>
<#hasReplyTo>Responder-Para: <#replyTo/></#hasReplyTo><#hasOrganization>Organização: <#organization/></#hasOrganization>Para: <#to/><#newLine/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>Referências: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
@@ -72,3 +72,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Original Message --------<#newLine/>
Subject: <#subject/><#newLine/>
Date: <#date/><#newLine/>
@@ -9,5 +10,7 @@ From: <#from/><#newLine/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,10 +1,16 @@
-------- Původní zpráva --------
Předmět: <#subject/>
Datum: <#date/>
Od: <#from/>
<#hasReplyTo>Odpověď na: <#replyTo/></#hasReplyTo><#hasOrganization>Organizace: <#organization/></#hasOrganization>Komu: <#to/>
<#hasCc>Kopie: <#cc/></#hasCc><#hasNewsGroups>Diskuzní skupiny: <#newsgroups/></#hasNewsGroups><#hasReferences>Odkazy: <#references/></#hasReferences>
<#messageBody/>
<#signature/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Původní zpráva --------<#newLine/>
Předmět: <#subject/><#newLine/>
Datum: <#date/><#newLine/>
Od: <#from/><#newLine/>
<#hasReplyTo>Odpověď na: <#replyTo/></#hasReplyTo><#hasOrganization>Organizace: <#organization/></#hasOrganization>Komu: <#to/><#newLine/>
<#hasCc>Kopie: <#cc/></#hasCc><#hasNewsGroups>Diskuzní skupiny: <#newsgroups/></#hasNewsGroups><#hasReferences>Odkazy: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Original Message --------<#newLine/>
Subject: <#subject/><#newLine/>
Date: <#date/><#newLine/>
@@ -9,5 +10,7 @@ From: <#from/><#newLine/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Original Message --------<#newLine/>
Subject: <#subject/><#newLine/>
Date: <#date/><#newLine/>
@@ -9,5 +10,7 @@ From: <#from/><#newLine/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Original Message --------<#newLine/>
Subject: <#subject/><#newLine/>
Date: <#date/><#newLine/>
@@ -9,5 +10,7 @@ From: <#from/><#newLine/>
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,21 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,6 +1,7 @@
<#newLine/>
<#newLine/>
<#newLine/>
<#signaturePlacementOnTop><#newLine/>
<#signature/><#newLine/></#signaturePlacementOnTop>
-------- Alkuperäinen viesti --------<#newLine/>
Aihe: <#subject/><#newLine/>
Pvm: <#date/><#newLine/>
@@ -9,5 +10,7 @@ Keneltä: <#from/><#newLine/>
<#hasCc>Kopio: <#cc/></#hasCc><#hasNewsGroups>Uutisryhmät: <#newsgroups/></#hasNewsGroups><#hasReferences>Viitteet: <#references/></#hasReferences><#newLine/>
<#newLine/>
<#messageBody/><#newLine/>
<#signaturePlacementOnBottom><#newLine/>
<#newLine/>
<#signature/></#signaturePlacementOnBottom>
<#newLine/>
<#signature/><#newLine/>
@@ -77,3 +77,12 @@ signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
+7
View File
@@ -94,6 +94,13 @@
- (NSCalendarDate *) mostRecentMessageDate;
- (NSString *) davCollectionTagFromId: (NSString *) theId;
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate;
/* flags */
- (NSException *) addFlagsToAllMessages: (id) _f;
+261 -18
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2009-2013 Inverse inc.
Copyright (C) 2009-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -24,6 +24,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSTask.h>
#import <NGObjWeb/NSException+HTTP.h>
@@ -614,20 +615,19 @@ static NSString *defaultUserID = @"anyone";
if (max > 1)
{
currentAccountName = [[self mailAccountFolder] nameInContainer];
if ([[folders objectAtIndex: 1] isEqualToString: currentAccountName])
{
for (count = 2; count < max; count++)
{
currentFolderName
= [[folders objectAtIndex: count] substringFromIndex: 6];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
}
client = [[self imap4Connection] client];
[imap4 selectFolder: [self imap4URL]];
client = [[self imap4Connection] client];
if (client)
for (count = 2; count < max; count++)
{
currentFolderName = [[folders objectAtIndex: count] substringFromIndex: 6];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
}
if (client)
{
if ([[folders objectAtIndex: 1] isEqualToString: currentAccountName])
{
[imap4 selectFolder: [self imap4URL]];
// We make sure the destination IMAP folder exist, if not, we create it.
result = [[client status: imapDestinationFolder
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]
@@ -647,13 +647,69 @@ static NSString *defaultUserID = @"anyone";
objectForKey: @"description"]];
}
else
result = [NSException exceptionWithName: @"SOGoMailException"
reason: @"IMAP connection is invalid"
userInfo: nil];
{
// Destination folder is in a different account
SOGoMailAccounts *accounts;
SOGoMailAccount *account;
accounts = [[self container] container];
account = [accounts lookupName: [folders objectAtIndex: 1] inContext: localContext acquire: NO];
if ([account isKindOfClass: [NSException class]])
{
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Cannot copy messages to other account."];
}
else
{
NSEnumerator *messages;
NSDictionary *message;
NSData *content;
NSArray *flags;
// Fetch messages
result = [client fetchUids: uids parts: [NSArray arrayWithObjects: @"RFC822", @"FLAGS", nil]];
if ([[result objectForKey: @"result"] boolValue])
{
result = [result valueForKey: @"fetch"];
if ([result isKindOfClass: [NSArray class]] && [result count] > 0)
{
// Copy each message to the other account
client = [[account imap4Connection] client];
[[account imap4Connection] selectFolder: imapDestinationFolder];
messages = [result objectEnumerator];
result = nil;
while (result == nil && (message = [messages nextObject]))
{
if ((content = [message valueForKey: @"message"]) != nil)
{
flags = [message valueForKey: @"flags"];
result = [client append: content toFolder: imapDestinationFolder withFlags: flags];
if ([[result objectForKey: @"result"] boolValue])
result = nil;
else
[self logWithFormat: @"ERROR: Can't append message: %@", result];
}
}
}
else
{
[self logWithFormat: @"ERROR: unexpected IMAP4 result (missing 'fetch'): %@", result];
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Unexpected IMAP4 result"];
}
}
else
{
[self logWithFormat: @"ERROR: Can't fetch messages: %@", result];
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Can't fetch messages"];
}
}
}
}
else
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Cannot copy messages across different accounts."];
result = [NSException exceptionWithName: @"SOGoMailException"
reason: @"IMAP connection is invalid"
userInfo: nil];
}
else
result = [NSException exceptionWithHTTPStatus: 500
@@ -1869,6 +1925,193 @@ static NSString *defaultUserID = @"anyone";
return date;
}
- (NSString *) davCollectionTagFromId: (NSString *) theId
{
NSString *tag;
tag = @"-1";
if ([self imap4Connection])
{
NSDictionary *result;
unsigned int modseq, uid;
uid = [theId intValue];
result = [[imap4 client] fetchModseqForUid: uid];
modseq = [[[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"] intValue];
tag = [NSString stringWithFormat: @"%d-%d", uid, modseq-1];
}
return tag;
}
- (NSString *) davCollectionTag
{
NSString *tag;
tag = @"-1";
if ([self imap4Connection])
{
NSString *folderName;
NSDictionary *result;
folderName = [imap4 imap4FolderNameForURL: [self imap4URL]];
[[imap4 client] unselect];
result = [[imap4 client] select: folderName];
tag = [NSString stringWithFormat: @"%@-%@", [result objectForKey: @"uidnext"], [result objectForKey: @"highestmodseq"]];
}
return tag;
}
//
// FIXME - see below for code refactoring with MAPIStoreMailFolder.
//
- (EOQualifier *) _nonDeletedQualifier
{
static EOQualifier *nonDeletedQualifier = nil;
EOQualifier *deletedQualifier;
if (!nonDeletedQualifier)
{
deletedQualifier
= [[EOKeyValueQualifier alloc]
initWithKey: @"FLAGS"
operatorSelector: EOQualifierOperatorContains
value: [NSArray arrayWithObject: @"Deleted"]];
nonDeletedQualifier = [[EONotQualifier alloc]
initWithQualifier: deletedQualifier];
[deletedQualifier release];
}
return nonDeletedQualifier;
}
//
// Check updated items
//
//
// . uid fetch 1:* (FLAGS) (changedsince 171)
//
// To get the modseq of a specific message:
//
// . uid fetch 124569:124569 uid (changedsince 1)
//
//
// Deleted: "UID FETCH 1:* (UID) (CHANGEDSINCE 171 VANISHED)"
// fetchUIDsOfVanishedItems ..
//
// . uid fetch 1:* (FLAGS) (changedsince 176 vanished)
// * VANISHED (EARLIER) 36
//
//
// FIXME: refactor MAPIStoreMailFolder.m - synchroniseCache to use this method
//
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate
{
EOQualifier *searchQualifier;
NSMutableArray *allTokens;
NSArray *a, *uids;
NSDictionary *d;
int uidnext, highestmodseq, i;
allTokens = [NSMutableArray array];
if ([theSyncToken isEqualToString: @"-1"])
{
uidnext = highestmodseq = 0;
}
else
{
a = [theSyncToken componentsSeparatedByString: @"-"];
uidnext = [[a objectAtIndex: 0] intValue];
highestmodseq = [[a objectAtIndex: 1] intValue];
}
// We first make sure QRESYNC is enabled
[[self imap4Connection] enableExtensions: [NSArray arrayWithObject: @"QRESYNC"]];
// We fetch new messages and modified messages
if (highestmodseq)
{
EOKeyValueQualifier *kvQualifier;
NSNumber *nextModseq;
nextModseq = [NSNumber numberWithUnsignedLongLong: highestmodseq + 1];
kvQualifier = [[EOKeyValueQualifier alloc]
initWithKey: @"modseq"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: nextModseq];
searchQualifier = [[EOAndQualifier alloc]
initWithQualifiers:
kvQualifier, [self _nonDeletedQualifier], nil];
[kvQualifier release];
[searchQualifier autorelease];
}
else
{
searchQualifier = [self _nonDeletedQualifier];
}
if (theStartDate)
{
EOQualifier *sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
@"(DATE >= %@)", theStartDate];
searchQualifier = [[EOAndQualifier alloc] initWithQualifiers: searchQualifier, sinceDateQualifier,
nil];
[searchQualifier autorelease];
}
// we fetch modified or added uids
uids = [self fetchUIDsMatchingQualifier: searchQualifier
sortOrdering: nil];
for (i = 0; i < [uids count]; i++)
{
// New messages
if ([[uids objectAtIndex: i] intValue] >= uidnext)
{
d = [NSDictionary dictionaryWithObject: @"added" forKey: [uids objectAtIndex: i]];
}
// Changed messages
else
{
d = [NSDictionary dictionaryWithObject: @"changed" forKey: [uids objectAtIndex: i]];
}
[allTokens addObject: d];
}
// We fetch deleted ones
if (highestmodseq > 0)
{
uids = [self fetchUIDsOfVanishedItems: highestmodseq];
for (i = 0; i < [uids count]; i++)
{
d = [NSDictionary dictionaryWithObject: @"deleted" forKey: [uids objectAtIndex: i]];
[allTokens addObject: d];
}
}
return allTokens;
}
@end /* SOGoMailFolder */
@implementation SOGoSpecialMailFolder
+3
View File
@@ -31,9 +31,12 @@
NSString *field;
NSString *currentValue;
BOOL htmlComposition;
NSString *signaturePlacement;
}
- (void) setSourceMail: (SOGoMailObject *) newSourceMail;
- (void) setSignaturePlacement: (NSString *) newPlacement;
- (BOOL) signaturePlacementOnTop;
@end
+10
View File
@@ -56,6 +56,16 @@
[super dealloc];
}
- (void) setSignaturePlacement: (NSString *) newPlacement
{
signaturePlacement = newPlacement;
}
- (BOOL) signaturePlacementOnTop
{
return [signaturePlacement isEqual: @"above"];
}
- (void) setSourceMail: (SOGoMailObject *) newSourceMail
{
ASSIGN (sourceMail, newSourceMail);

Some files were not shown because too many files have changed in this diff Show More