mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-27 12:54:17 +00:00
Merge to 2.2.0
This commit is contained in:
@@ -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 */
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
# compilation settings
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ¶ms);
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
requires = ( MAIN, Appointments, Contacts, Mailer );
|
||||
}
|
||||
+9
-7
@@ -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]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+16
-10
@@ -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/>
|
||||
|
||||
+9
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,9 +31,12 @@
|
||||
NSString *field;
|
||||
NSString *currentValue;
|
||||
BOOL htmlComposition;
|
||||
NSString *signaturePlacement;
|
||||
}
|
||||
|
||||
- (void) setSourceMail: (SOGoMailObject *) newSourceMail;
|
||||
- (void) setSignaturePlacement: (NSString *) newPlacement;
|
||||
- (BOOL) signaturePlacementOnTop;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user