From e5bc46710c715a9dc2e1801bba236fa7e7848b3d Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 10 Jan 2014 14:12:53 -0500 Subject: [PATCH] Initial Active Sync Support! --- ActiveSync/ActiveSyncProduct.m | 30 + ActiveSync/GNUmakefile | 37 + ActiveSync/GNUmakefile.preamble | 1 + ActiveSync/LICENSE | 12 + ActiveSync/NGDOMElement+ActiveSync.h | 34 + ActiveSync/NGDOMElement+ActiveSync.m | 73 + ActiveSync/NGMimeMessage+ActiveSync.h | 33 + ActiveSync/NGMimeMessage+ActiveSync.m | 52 + ActiveSync/NGVCard+ActiveSync.h | 35 + ActiveSync/NGVCard+ActiveSync.m | 92 ++ ActiveSync/NSData+ActiveSync.h | 32 + ActiveSync/NSData+ActiveSync.m | 111 ++ ActiveSync/NSDate+ActiveSync.h | 33 + ActiveSync/NSDate+ActiveSync.m | 32 + ActiveSync/NSString+ActiveSync.h | 38 + ActiveSync/NSString+ActiveSync.m | 113 ++ ActiveSync/README | 6 + ActiveSync/SOGoActiveSyncConstants.h | 32 + ActiveSync/SOGoActiveSyncDispatcher+Sync.h | 37 + ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 910 ++++++++++++ ActiveSync/SOGoActiveSyncDispatcher.h | 33 + ActiveSync/SOGoActiveSyncDispatcher.m | 1219 +++++++++++++++++ ActiveSync/SOGoMailObject+ActiveSync.h | 34 + ActiveSync/SOGoMailObject+ActiveSync.m | 296 ++++ .../SoObjectWebDAVDispatcher+ActiveSync.m | 91 ++ ActiveSync/common.make | 36 + ActiveSync/iCalEvent+ActiveSync.h | 35 + ActiveSync/iCalEvent+ActiveSync.m | 113 ++ ActiveSync/iCalTimeZone+ActiveSync.h | 33 + ActiveSync/iCalTimeZone+ActiveSync.m | 153 +++ ActiveSync/iCalToDo+ActiveSync.h | 34 + ActiveSync/iCalToDo+ActiveSync.m | 85 ++ ActiveSync/product.plist | 3 + 33 files changed, 3908 insertions(+) create mode 100644 ActiveSync/ActiveSyncProduct.m create mode 100644 ActiveSync/GNUmakefile create mode 100644 ActiveSync/GNUmakefile.preamble create mode 100644 ActiveSync/LICENSE create mode 100644 ActiveSync/NGDOMElement+ActiveSync.h create mode 100644 ActiveSync/NGDOMElement+ActiveSync.m create mode 100644 ActiveSync/NGMimeMessage+ActiveSync.h create mode 100644 ActiveSync/NGMimeMessage+ActiveSync.m create mode 100644 ActiveSync/NGVCard+ActiveSync.h create mode 100644 ActiveSync/NGVCard+ActiveSync.m create mode 100644 ActiveSync/NSData+ActiveSync.h create mode 100644 ActiveSync/NSData+ActiveSync.m create mode 100644 ActiveSync/NSDate+ActiveSync.h create mode 100644 ActiveSync/NSDate+ActiveSync.m create mode 100644 ActiveSync/NSString+ActiveSync.h create mode 100644 ActiveSync/NSString+ActiveSync.m create mode 100644 ActiveSync/README create mode 100644 ActiveSync/SOGoActiveSyncConstants.h create mode 100644 ActiveSync/SOGoActiveSyncDispatcher+Sync.h create mode 100644 ActiveSync/SOGoActiveSyncDispatcher+Sync.m create mode 100644 ActiveSync/SOGoActiveSyncDispatcher.h create mode 100644 ActiveSync/SOGoActiveSyncDispatcher.m create mode 100644 ActiveSync/SOGoMailObject+ActiveSync.h create mode 100644 ActiveSync/SOGoMailObject+ActiveSync.m create mode 100644 ActiveSync/SoObjectWebDAVDispatcher+ActiveSync.m create mode 100644 ActiveSync/common.make create mode 100644 ActiveSync/iCalEvent+ActiveSync.h create mode 100644 ActiveSync/iCalEvent+ActiveSync.m create mode 100644 ActiveSync/iCalTimeZone+ActiveSync.h create mode 100644 ActiveSync/iCalTimeZone+ActiveSync.m create mode 100644 ActiveSync/iCalToDo+ActiveSync.h create mode 100644 ActiveSync/iCalToDo+ActiveSync.m create mode 100644 ActiveSync/product.plist diff --git a/ActiveSync/ActiveSyncProduct.m b/ActiveSync/ActiveSyncProduct.m new file mode 100644 index 000000000..9eda82a0b --- /dev/null +++ b/ActiveSync/ActiveSyncProduct.m @@ -0,0 +1,30 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@interface ActiveSyncProduct : NSObject +{ +} + +@end + +@implementation ActiveSyncProduct +@end /* ActiveSyncProduct */ diff --git a/ActiveSync/GNUmakefile b/ActiveSync/GNUmakefile new file mode 100644 index 000000000..e6d2d2ed7 --- /dev/null +++ b/ActiveSync/GNUmakefile @@ -0,0 +1,37 @@ + +# GNUstep makefile + +include common.make + +BUNDLE_NAME = ActiveSync + +ActiveSync_PRINCIPAL_CLASS = ActiveSyncProduct + +ActiveSync_OBJC_FILES = \ + ActiveSyncProduct.m \ + iCalEvent+ActiveSync.m \ + iCalTimeZone+ActiveSync.m \ + iCalToDo+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/local/include/libwbxml-1.0/wbxml/ +ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -lwbxml2 + +-include GNUmakefile.preamble +include $(GNUSTEP_MAKEFILES)/bundle.make +-include GNUmakefile.postamble diff --git a/ActiveSync/GNUmakefile.preamble b/ActiveSync/GNUmakefile.preamble new file mode 100644 index 000000000..c302ad8be --- /dev/null +++ b/ActiveSync/GNUmakefile.preamble @@ -0,0 +1 @@ +# compilation settings diff --git a/ActiveSync/LICENSE b/ActiveSync/LICENSE new file mode 100644 index 000000000..2f5458144 --- /dev/null +++ b/ActiveSync/LICENSE @@ -0,0 +1,12 @@ +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. diff --git a/ActiveSync/NGDOMElement+ActiveSync.h b/ActiveSync/NGDOMElement+ActiveSync.h new file mode 100644 index 000000000..f4f46e6f7 --- /dev/null +++ b/ActiveSync/NGDOMElement+ActiveSync.h @@ -0,0 +1,34 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +@class NSDictionary; + +@interface NGDOMElement (ActiveSync) + +- (NSDictionary *) applicationData; + +@end + +#endif // NGDOMELEMENTACTIVESYNC diff --git a/ActiveSync/NGDOMElement+ActiveSync.m b/ActiveSync/NGDOMElement+ActiveSync.m new file mode 100644 index 000000000..bc0e4c4fb --- /dev/null +++ b/ActiveSync/NGDOMElement+ActiveSync.m @@ -0,0 +1,73 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +@implementation NGDOMElement (ActiveSync) + +// +// We must handle "inner data" like this: +// +// +// +// 2 +// Flag for follow up +// +// +// +- (NSDictionary *) applicationData +{ + NSMutableDictionary *data; + id children; + id element; + int i; + + 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]; + + // Handle inner data + if ([(NSArray *)[element childNodes] count] > 2) + value = [(NGDOMElement *)element applicationData]; + else + value = [[element firstChild] nodeValue]; + + if (value && tag) + [data setObject: value forKey: tag]; + } + } + + return data; +} + +@end diff --git a/ActiveSync/NGMimeMessage+ActiveSync.h b/ActiveSync/NGMimeMessage+ActiveSync.h new file mode 100644 index 000000000..57d172862 --- /dev/null +++ b/ActiveSync/NGMimeMessage+ActiveSync.h @@ -0,0 +1,33 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSArray; + +@interface NGMimeMessage (ActiveSync) + +- (NSArray *) allRecipients; + +@end + +#endif diff --git a/ActiveSync/NGMimeMessage+ActiveSync.m b/ActiveSync/NGMimeMessage+ActiveSync.m new file mode 100644 index 000000000..a5c57e33f --- /dev/null +++ b/ActiveSync/NGMimeMessage+ActiveSync.m @@ -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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import +#import + +#import + +@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 diff --git a/ActiveSync/NGVCard+ActiveSync.h b/ActiveSync/NGVCard+ActiveSync.h new file mode 100644 index 000000000..fb442ae8d --- /dev/null +++ b/ActiveSync/NGVCard+ActiveSync.h @@ -0,0 +1,35 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSDictionary; +@class NSString; + +@interface NGVCard (ActiveSync) + +- (NSString *) activeSyncRepresentation; +- (void) takeActiveSyncValues: (NSDictionary *) theValues; + +@end + +#endif diff --git a/ActiveSync/NGVCard+ActiveSync.m b/ActiveSync/NGVCard+ActiveSync.m new file mode 100644 index 000000000..4d805a10a --- /dev/null +++ b/ActiveSync/NGVCard+ActiveSync.m @@ -0,0 +1,92 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import +#import + +#import + +@implementation NGVCard (ActiveSync) + +- (NSString *) activeSyncRepresentation +{ + NSString *firstName, *lastName; + NSMutableString *s; + CardElement *n; + + s = [NSMutableString string]; + n = [self n]; + + lastName = [n flattenedValueAtIndex: 0 forKey: @""]; + [s appendFormat: @"%@", lastName]; + + + firstName = [n flattenedValueAtIndex: 1 forKey: @""]; + [s appendFormat: @"%@", firstName]; + + return s; +} + +- (void) takeActiveSyncValues: (NSDictionary *) theValues +{ + id o; + + if ((o = [theValues objectForKey: @"CompanyName"])) + { + [self setOrg: o units: nil]; + } + + 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]; + } + + [self setNWithFamily: [theValues objectForKey: @"LastName"] + given: [theValues objectForKey: @"FirstName"] + additional: nil prefixes: nil suffixes: nil]; + + if ((o = [theValues objectForKey: @"MobilePhoneNumber"])) + { + } + + if ((o = [theValues objectForKey: @"Title"])) + { + [self setTitle: o]; + } + + if ((o = [theValues objectForKey: @"WebPage"])) + { + } + +} + +@end diff --git a/ActiveSync/NSData+ActiveSync.h b/ActiveSync/NSData+ActiveSync.h new file mode 100644 index 000000000..fd6ceb1a6 --- /dev/null +++ b/ActiveSync/NSData+ActiveSync.h @@ -0,0 +1,32 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@interface NSData (ActiveSync) + +- (NSData *) wbxml2xml; +- (NSData *) xml2wbxml; + +@end + +#endif diff --git a/ActiveSync/NSData+ActiveSync.m b/ActiveSync/NSData+ActiveSync.m new file mode 100644 index 000000000..f8e89b135 --- /dev/null +++ b/ActiveSync/NSData+ActiveSync.m @@ -0,0 +1,111 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +#include +#include +#include + +@implementation NSData (ActiveSync) + +- (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)); + return nil; + } + + data = [[NSData alloc] initWithBytes: xml length: xml_len]; + [data writeToFile: @"/tmp/protocol.decoded" atomically: YES]; + + 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)); + 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)); + free(wbxml); + wbxml_conv_xml2wbxml_destroy(conv); + return nil; + } + + data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len]; + [data writeToFile: @"/tmp/protocol.encoded" atomically: YES]; + + free(wbxml); + wbxml_conv_xml2wbxml_destroy(conv); + + return AUTORELEASE(data); +} +@end diff --git a/ActiveSync/NSDate+ActiveSync.h b/ActiveSync/NSDate+ActiveSync.h new file mode 100644 index 000000000..89f107105 --- /dev/null +++ b/ActiveSync/NSDate+ActiveSync.h @@ -0,0 +1,33 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSString; + +@interface NSDate (ActiveSync) + +- (NSString *) activeSyncRepresentation; + +@end + +#endif diff --git a/ActiveSync/NSDate+ActiveSync.m b/ActiveSync/NSDate+ActiveSync.m new file mode 100644 index 000000000..d45c8e5b5 --- /dev/null +++ b/ActiveSync/NSDate+ActiveSync.m @@ -0,0 +1,32 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +@implementation NSDate (ActiveSync) + +- (NSString *) activeSyncRepresentation +{ + return [self descriptionWithCalendarFormat: @"%Y%m%dT%H%M%SZ" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil]; +} + +@end diff --git a/ActiveSync/NSString+ActiveSync.h b/ActiveSync/NSString+ActiveSync.h new file mode 100644 index 000000000..dfdfdde03 --- /dev/null +++ b/ActiveSync/NSString+ActiveSync.h @@ -0,0 +1,38 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +#include "SOGoActiveSyncConstants.h" + +@class NSCalendarDate; + +@interface NSString (ActiveSync) + +- (int) activeSyncFolderType; +- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType; +- (NSCalendarDate *) calendarDate; +- (NSString *) deviceId; + +@end + +#endif diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m new file mode 100644 index 000000000..363cb2e29 --- /dev/null +++ b/ActiveSync/NSString+ActiveSync.m @@ -0,0 +1,113 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#include +#include + +@implementation NSString (ActiveSync) + +- (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; + + *folderType = ActiveSyncGenericFolder; + + if ([self hasPrefix: @"vevent/"]) + { + realCollectionId = [self substringFromIndex: 7]; + *folderType = ActiveSyncEventFolder; + } + else if ([self hasPrefix: @"vtodo/"]) + { + realCollectionId = [self substringFromIndex: 6]; + *folderType = ActiveSyncTaskFolder; + } + else if ([self hasPrefix: @"vcard/"]) + { + realCollectionId = [self substringFromIndex: 6]; + *folderType = ActiveSyncContactFolder; + } + else + { + // mail/ + realCollectionId = [self substringFromIndex: 5]; + *folderType = ActiveSyncMailFolder; + } + + 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; +} + +// +// This method extracts the "DeviceId" from a URI: +// +// /SOGo/Microsoft-Server-ActiveSync?Cmd=FolderSync&User=sogo10&DeviceId=SEC17CD1A3E9E3F2&DeviceType=SAMSUNGSGHI317M +// +- (NSString *) deviceId +{ + 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: @"DEVICEID="]) + return [s substringFromIndex: 9]; + } + + return @"Unknown"; +} +@end diff --git a/ActiveSync/README b/ActiveSync/README new file mode 100644 index 000000000..ad8634882 --- /dev/null +++ b/ActiveSync/README @@ -0,0 +1,6 @@ +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. + +Inverse inc. provides this software for free, but is not responsible +for anything related to its usage. diff --git a/ActiveSync/SOGoActiveSyncConstants.h b/ActiveSync/SOGoActiveSyncConstants.h new file mode 100644 index 000000000..cfcb128ba --- /dev/null +++ b/ActiveSync/SOGoActiveSyncConstants.h @@ -0,0 +1,32 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.h b/ActiveSync/SOGoActiveSyncDispatcher+Sync.h new file mode 100644 index 000000000..3de8f6a84 --- /dev/null +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.h @@ -0,0 +1,37 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +@class WOResponse; + +@interface SOGoActiveSyncDispatcher (Sync) + +- (void) processSync: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse; + +@end + +#endif // SOGOACTIVESYNCDISPATCHERSYNC diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m new file mode 100644 index 000000000..1d8fe35e6 --- /dev/null +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -0,0 +1,910 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 "SOGoActiveSyncDispatcher+Sync.h" + + +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +#import +#import +#import +#import + + +#import +#import + +#import + +#import +#import +#import +#import +#import + +#import +#import + +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import + +#import +#import +#import +#import + +#import +#import +#import + +#import +#import + +#include "iCalEvent+ActiveSync.h" +#include "iCalToDo+ActiveSync.h" +#include "NGDOMElement+ActiveSync.h" +#include "NGVCard+ActiveSync.h" +#include "NSData+ActiveSync.h" +#include "NSString+ActiveSync.h" +#include "SOGoActiveSyncConstants.h" +#include "SOGoMailObject+ActiveSync.h" + +@implementation SOGoActiveSyncDispatcher (Sync) + +- (id) collectionFromId: (NSString *) theCollectionId + type: (SOGoMicrosoftActiveSyncFolderType) theFolderType +{ + id collection; + + collection = nil; + + switch (theFolderType) + { + case ActiveSyncContactFolder: + { + collection = [[context activeUser] personalContactsFolderInContext: context]; + } + break; + case ActiveSyncEventFolder: + case ActiveSyncTaskFolder: + { + collection = [[context activeUser] personalCalendarFolderInContext: context]; + } + break; + case ActiveSyncMailFolder: + default: + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *currentFolder; + SOGoUserFolder *userFolder; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId] + inContext: context + acquire: NO]; + } + } + + return collection; +} + +// +// +// +// +// +// +// 1388757902 +// vcard/personal +// +// 25 +// +// +// 1 +// 32768 +// +// +// +// +// 16 +// +// +// 1 +// +// +// Goo Inc. +// annie@broccoli.com +// Broccoli, Annie +// Annie +// Broccoli +// +// +// +// +// +// +// +// +- (void) processSyncAddCommand: (id ) theDocumentElement + inCollection: (id) theCollection + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer +{ + NSArray *additions; + NSString *clientId, *serverId; + NSDictionary *allValues; + + id anAddition, sogoObject, o; + int i; + + additions = (id)[theDocumentElement getElementsByTagName: @"Add"]; + + if ([additions count]) + { + for (i = 0; i < [additions count]; i++) + { + anAddition = [additions objectAtIndex: i]; + + clientId = [[(id)[anAddition getElementsByTagName: @"CientId"] lastObject] textValue]; + allValues = [[(id)[anAddition getElementsByTagName: @"ApplicationData"] lastObject] applicationData]; + + switch (theFolderType) + { + case ActiveSyncContactFolder: + { + serverId = [NSString stringWithFormat: @"%@.vcf", [theCollection globallyUniqueObjectId]]; + sogoObject = [[SOGoContactGCSEntry alloc] initWithName: serverId + inContainer: theCollection]; + o = [sogoObject vCard]; + } + break; + case ActiveSyncEventFolder: + { + serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]]; + sogoObject = [[SOGoAppointmentObject alloc] initWithName: serverId + inContainer: theCollection]; + o = [sogoObject component: YES secure: NO]; + } + break; + case ActiveSyncTaskFolder: + { + serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]]; + sogoObject = [[SOGoTaskObject alloc] initWithName: serverId + inContainer: theCollection]; + o = [sogoObject component: YES secure: NO]; + } + break; + case ActiveSyncMailFolder: + default: + { + // FIXME + continue; + } + } + + [o takeActiveSyncValues: allValues]; + [sogoObject saveComponent: o]; + + // Everything is fine, lets generate our response + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", clientId]; + [theBuffer appendFormat: @"%@", serverId]; + [theBuffer appendFormat: @"%d", 1]; + [theBuffer appendString: @""]; + } + } +} + +// +// +// +// +// +// +// 1387546048 +// vtodo/personal +// +// 25 +// +// +// 1 +// 32768 +// +// +// +// +// 36C5-52B36280-1-27B38F40.ics +// +// +// 1 +// +// +// foobar1 +// 1 +// 0 +// 0 +// 0 +// +// +// +// +// +// +// +- (void) processSyncChangeCommand: (id ) theDocumentElement + inCollection: (id) theCollection + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer +{ + NSDictionary *allChanges; + NSString *serverId; + NSArray *changes; + id aChange, o, sogoObject; + + int i; + + changes = (id)[theDocumentElement getElementsByTagName: @"Change"]; + + if ([changes count]) + { + for (i = 0; i < [changes count]; i++) + { + aChange = [changes objectAtIndex: i]; + + serverId = [[(id)[aChange getElementsByTagName: @"ServerId"] lastObject] textValue]; + allChanges = [[(id)[aChange getElementsByTagName: @"ApplicationData"] lastObject] applicationData]; + + // Fetch the object and apply the changes + sogoObject = [theCollection lookupName: serverId + inContext: context + acquire: NO]; + + switch (theFolderType) + { + case ActiveSyncContactFolder: + { + o = [sogoObject vCard]; + [o takeActiveSyncValues: allChanges]; + [sogoObject saveComponent: o]; + } + break; + case ActiveSyncEventFolder: + case ActiveSyncTaskFolder: + { + o = [sogoObject component: NO secure: NO]; + [o takeActiveSyncValues: allChanges]; + [sogoObject saveComponent: o]; + } + break; + case ActiveSyncMailFolder: + default: + { + [sogoObject takeActiveSyncValues: allChanges]; + } + } + + } + } +} + +// +// +// +// +// +// +// 1388764784 +// vtodo/personal +// +// 25 +// +// +// 1 +// 32768 +// +// +// +// +// 2CB5-52B36080-1-1C1D0240.ics +// +// +// +// +// +// +- (void) processSyncDeleteCommand: (id ) theDocumentElement + inCollection: (id) theCollection + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer +{ + NSArray *deletions; + NSString *serverId; + + id aDelete, sogoObject; + int i; + + deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"]; + + if ([deletions count]) + { + for (i = 0; i < [deletions count]; i++) + { + aDelete = [deletions objectAtIndex: i]; + + serverId = [[(id)[aDelete getElementsByTagName: @"ServerId"] lastObject] textValue]; + + sogoObject = [theCollection lookupName: serverId + inContext: context + acquire: NO]; + + [sogoObject delete]; + } + } +} + + +// +// +// 91 +// +// +- (void) processSyncFetchCommand: (id ) theDocumentElement + inCollection: (id) theCollection + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer +{ + NSString *serverId; + id o; + + serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue]; + + o = [theCollection lookupName: serverId + inContext: context + acquire: NO]; + + // FIXME - error handling + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", serverId]; + [theBuffer appendFormat: @"%d", 1]; + [theBuffer appendString: @""]; + [theBuffer appendString: [o activeSyncRepresentation]]; + [theBuffer appendString: @""]; + [theBuffer appendString: @""]; +} + + +// +// The method handles +// +- (void) processSyncGetChanges: (id ) theDocumentElement + inCollection: (id) theCollection + withSyncKey: (NSString *) theSyncKey + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer +{ + int i; + + // + // No changes in the collection - 2.2.2.19.1.1 Empty Sync Request. + // We check this and we don't generate any commands if we don't have to. + // + if ([theSyncKey isEqualToString: [theCollection davCollectionTag]]) + return; + + [theBuffer appendString: @""]; + + switch (theFolderType) + { + case ActiveSyncContactFolder: + { + NSArray *allContacts; + NGVCard *card; + id contact; + + allContacts = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey]; + + for (i = 0; i < [allContacts count]; i++) + { + contact = [theCollection lookupName: [[allContacts objectAtIndex: i] objectForKey: @"c_name"] + inContext: context + acquire: NO]; + + if (![[[allContacts objectAtIndex: i] objectForKey: @"c_component"] isEqualToString: @"vcard"]) + continue; + + // FIXME: we skip list right now + if ([contact respondsToSelector: @selector (vCard)]) + { + card = [contact vCard]; + + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", [contact nameInContainer]]; + [theBuffer appendString: @""]; + + [theBuffer appendString: [card activeSyncRepresentation]]; + + [theBuffer appendString: @""]; + [theBuffer appendString: @""]; + } + } + } + break; + case ActiveSyncEventFolder: + { + NSArray *allEvents; + NSDictionary *d; + id eventObject; + + allEvents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey]; + + for (i = 0; i < [allEvents count]; i++) + { + NSString *serverId; + iCalEvent *event; + + d = [allEvents objectAtIndex: i]; + + if (![[d objectForKey: @"c_component"] isEqualToString: @"vevent"]) + continue; + + serverId = [d objectForKey: @"c_name"]; + + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", serverId]; + [theBuffer appendString: @""]; + + eventObject = [theCollection lookupName: serverId inContext: self->context acquire: 0]; + + event = [eventObject component: NO secure: NO]; + + [theBuffer appendString: [event activeSyncRepresentation]]; + + [theBuffer appendString: @""]; + [theBuffer appendString: @""]; + + } // for (i = 0; i < [allEvents count]; i++) + } + break; + case ActiveSyncTaskFolder: + { + NSArray *allTasks; + NSDictionary *task; + id taskObject; + + allTasks = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey]; + + for (i = 0; i < [allTasks count]; i++) + + { + int deleted; + + task = [allTasks objectAtIndex: i]; + deleted = [[task objectForKey: @"c_deleted"] intValue]; + + if (!deleted && ![[task objectForKey: @"c_component"] isEqualToString: @"vtodo"]) + continue; + + NSString *uid; + uid = [task objectForKey: @"c_name"]; + + if (deleted) + { + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", uid]; + [theBuffer appendString: @""]; + } + else + { + iCalToDo *todo; + BOOL updated; + + updated = YES; + + if ([[task objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue]) + updated = NO; + + if (updated) + [theBuffer appendString: @""]; + else + [theBuffer appendString: @""]; + + [theBuffer appendFormat: @"%@", uid]; + [theBuffer appendString: @""]; + + taskObject = [theCollection lookupName: uid inContext: self->context acquire: 0]; + + todo = [taskObject component: NO secure: NO]; + + [theBuffer appendString: [todo activeSyncRepresentation]]; + + [theBuffer appendString: @""]; + + if (updated) + [theBuffer appendString: @""]; + else + [theBuffer appendString: @""]; + } + } // for ... + } + break; + case ActiveSyncMailFolder: + default: + { + NSDictionary *aMessage; + NSArray *allMessages; + NSString *uid, *command; + SOGoMailObject *mailObject; + + allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey]; + + for (i = 0; i < [allMessages count]; i++) + { + aMessage = [allMessages objectAtIndex: i]; + + uid = [[[aMessage allKeys] lastObject] stringValue]; + command = [[aMessage allValues] lastObject]; + + if ([command isEqualToString: @"deleted"]) + { + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", uid]; + [theBuffer appendString: @""]; + } + else + { + if ([command isEqualToString: @"added"]) + [theBuffer appendString: @""]; + else + [theBuffer appendString: @""]; + + mailObject = [theCollection lookupName: uid + inContext: context + acquire: 0]; + + [theBuffer appendFormat: @"%@", uid]; + [theBuffer appendString: @""]; + [theBuffer appendString: [mailObject activeSyncRepresentation]]; + [theBuffer appendString: @""]; + + if ([command isEqualToString: @"added"]) + [theBuffer appendString: @""]; + else + [theBuffer appendString: @""]; + + } + } + } + break; + } // switch (folderType) ... + + [theBuffer appendString: @""]; +} + +// +// We have something like this: +// +// +// +// 91 +// +// +// +- (void) processSyncCommands: (id ) theDocumentElement + inCollection: (id) theCollection + withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType + inBuffer: (NSMutableString *) theBuffer + processed: (BOOL *) processed +{ + id aCommandDetails; + id aCommand, element; + NSArray *allCommands; + int i, j; + + allCommands = (id)[theDocumentElement getElementsByTagName: @"Commands"]; + + for (i = 0; i < [allCommands count]; i++) + { + aCommand = [allCommands objectAtIndex: i]; + aCommandDetails = [aCommand childNodes]; + + for (j = 0; j < [(id)aCommandDetails count]; j++) + { + element = [aCommandDetails objectAtIndex: j]; + + if ([element nodeType] == DOM_ELEMENT_NODE) + { + if ([[element tagName] isEqualToString: @"Add"]) + { + // Add + [self processSyncAddCommand: aCommand + inCollection: theCollection + withType: theFolderType + inBuffer: theBuffer]; + *processed = YES; + } + else if ([[element tagName] isEqualToString: @"Change"]) + { + // Change + [self processSyncChangeCommand: aCommand + inCollection: theCollection + withType: theFolderType + inBuffer: theBuffer]; + *processed = YES; + } + else if ([[element tagName] isEqualToString: @"Delete"]) + { + // Delete + [self processSyncDeleteCommand: aCommand + inCollection: theCollection + withType: theFolderType + inBuffer: theBuffer]; + } + else if ([[element tagName] isEqualToString: @"Fetch"]) + { + // Fetch + [self processSyncFetchCommand: aCommand + inCollection: theCollection + withType: theFolderType + inBuffer: theBuffer]; + } + } + } + } +} + +// +// +// +- (void) processSyncCollection: (id ) theDocumentElement + inBuffer: (NSMutableString *) theBuffer +{ + NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType; + SOGoMicrosoftActiveSyncFolderType folderType; + id collection; + + BOOL getChanges, first_sync; + + collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; + realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + collection = [self collectionFromId: realCollectionId type: folderType]; + + syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; + davCollectionTag = [collection davCollectionTag]; + + getChanges = ([(id)[theDocumentElement getElementsByTagName: @"GetChanges"] count] ? YES : NO); + first_sync = NO; + + if ([syncKey isEqualToString: @"0"]) + { + davCollectionTag = @"-1"; + first_sync = YES; + } + + // We check our sync preferences and we stash them + bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; + + if (!bodyPreferenceType) + bodyPreferenceType = @"1"; + + [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; + + + [theBuffer appendString: @""]; + [theBuffer appendFormat: @"%@", davCollectionTag]; + [theBuffer appendFormat: @"%@", collectionId]; + [theBuffer appendFormat: @"%d", 1]; + + // We generate the commands, if any, for the response. We might also have + // generated some in processSyncCommand:inResponse: as we could have + // received a Fetch command + if (getChanges && !first_sync) + { + [self processSyncGetChanges: theDocumentElement + inCollection: collection + withSyncKey: syncKey + withType: folderType + inBuffer: theBuffer]; + } + + // + // We process the commands from the request + // + if (!first_sync) + { + NSMutableString *s; + BOOL processed; + + s = [NSMutableString string]; + processed = NO; + + [self processSyncCommands: theDocumentElement + inCollection: collection + withType: folderType + inBuffer: s + processed: &processed]; + + if (processed) + [theBuffer appendFormat: @"%@", s]; + else + [theBuffer appendString: s]; + } + + [theBuffer appendString: @""]; +} + +// +// Initial folder sync: +// +// +// +// +// +// +// 0 +// folderINBOX +// +// +// +// +// +// Following this will be a GetItemEstimate call. Following our response to the GetItemEstimate, we'll +// have a new Sync call like this: +// +// +// +// +// +// +// 1 +// folderINBOX +// 1 +// +// 50 +// +// 5 -- http://msdn.microsoft.com/en-us/library/gg709713(v=exchg.80).aspx +// -- http://msdn.microsoft.com/en-us/library/ee218197(v=exchg.80).aspx +// 2 -- +// 51200 +// +// +// 4 +// +// +// +// +// +// +// +// +// When adding a new task, we might have something like this: +// +// +// +// +// +// +// 1 +// personal +// +// -- http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx +// 5 -- http://msdn.microsoft.com/en-us/library/gg650865(v=exchg.80).aspx +// +// -- http://msdn.microsoft.com/en-us/library/ee218197(v=exchg.80).aspx +// 1 +// 400000 +// +// +// +// +// new_task_1386614771261 +// +// +// 1 +// 6 +// tomate +// +// test 1 +// 1 +// 2013-12-09T19:00:00.000Z +// 0 +// 0 +// 2013-12-09T19:00:00.000Z +// +// +// +// +// +// +// +// The algorithm here is pretty simple: +// +// 1. extract the list of collections +// 2. for each collection +// 2.1. extract the metadata (id, synckey, etc.) +// 2.2. extract the list of commands +// 2.3. for each command +// 2.3.1 process the command (add/change/delete/fetch) +// 2.3.2 build a response during the processsing +// +// +- (void) processSync: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + id aCollection; + NSArray *allCollections; + NSMutableString *s; + NSData *d; + + int i; + + // We initialize our output buffer + s = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + + allCollections = (id)[theDocumentElement getElementsByTagName: @"Collections"]; + + for (i = 0; i < [allCollections count]; i++) + { + aCollection = [allCollections objectAtIndex: i]; + + [self processSyncCollection: aCollection inBuffer: s]; + } + + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +@end diff --git a/ActiveSync/SOGoActiveSyncDispatcher.h b/ActiveSync/SOGoActiveSyncDispatcher.h new file mode 100644 index 000000000..66baf757e --- /dev/null +++ b/ActiveSync/SOGoActiveSyncDispatcher.h @@ -0,0 +1,33 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSException; + +@interface SOGoActiveSyncDispatcher : NSObject +{ + id context; +} + +- (NSException *) dispatchRequest: (id) theRequest + inResponse: (id) theResponse + context: (id) theContext; + +@end diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m new file mode 100644 index 000000000..54c299d0f --- /dev/null +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -0,0 +1,1219 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 "SOGoActiveSyncDispatcher.h" + + +// NICE INFO +// +// http://blogs.technet.com/b/exchange/archive/2013/10/04/exchange-activesync-mailbox-log-analysis.aspx +// +/* + TODO + X parse correctly the device id + X tester 2 add ou 2 change ou 2 deletes en meme temps (ie., avant de faire un sync) + X ajouter le last UID au sync key dans le cas du IMAP - genre UID-MODSEQ + X au lieu d'avoir 50 if dans le process, construire le selecteur a partir de la string et l'invoker + - implementer MeetingResponse + X implementer ResolveRecipients + X combiner smart reply et smart forward + X implementer MoveItems + X implemneter SmartForward et SmartReply + X implementer Search + - implementer GetAttachment + X supporter les multipart/mixed pour afficher correctement les mails. etc + X fixer la hierarchie dans les folders imap + - finir implementer Ping + X implemneter add/update/delete partout + - implementer le Sync en fonction d'un timestamp initial pour la syncrho genre 1 mois derriere + X tester le sync de plusieurs collections en meme temps - pas juste voir aucun changement dans une collection sur X et dire qu'il n'y a rien de nouveau + - implementer le OOF - voir 4.18.2 dans ASCMD + X implementer le fetch lors du Sync - les iOS font ca + - cleanup the include mess + - FIXER le MoveItem - le DstMsgId - grokker le UID lors du copy + - tester l'ajout/suppression/rename de dossier IMAP + - fixer prob de timezone dans resolve recipients + - fixer prob de timezone dans les events + - cleanup warning mess + - cleanup des fixmes + X fix crash apple reception de mial + fuckup attacments + fuckups html + */ + + +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#import + +#import +#import +#import +#import +#import + +#import +#import +#import + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#import +#import + +#include "iCalEvent+ActiveSync.h" +#include "iCalToDo+ActiveSync.h" +#include "NGMimeMessage+ActiveSync.h" +#include "NGVCard+ActiveSync.h" +#include "NSData+ActiveSync.h" +#include "NSString+ActiveSync.h" +#include "SOGoActiveSyncConstants.h" +#include "SOGoMailObject+ActiveSync.h" + +@implementation SOGoActiveSyncDispatcher + +// +// +// +- (void) processFolderCreate: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *parentId, *displayName, *nameInContainer, *syncKey; + SOGoUserFolder *userFolder; + NSMutableString *s; + NSData *d; + + int type; + + parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue]; + displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue]; + type = [[[(id)[theDocumentElement getElementsByTagName: @"Type"] lastObject] textValue] intValue]; + userFolder = [[context activeUser] homeFolderInContext: context]; + + // See 2.2.3.170.2 Type (FolderCreate) - http://msdn.microsoft.com/en-us/library/gg675445(v=exchg.80).aspx + // We support the following types: + // + // 12 User-created mail folder + // 13 User-created Calendar folder + // 14 User-created Contacts folder + // 15 User-created Tasks folder + // + switch (type == 12) + { + case 12: + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *newFolder; + id currentFolder; + + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]] + inContext: context + acquire: NO]; + + // FIXME + // handle exists (status == 2) + // handle right synckey + if ([newFolder create]) + { + nameInContainer = [newFolder nameInContainer]; + syncKey = @"-1"; + } + else + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"Unable to create folder."]; + return; + } + } + break; + case 13: + case 15: + { + SOGoAppointmentFolders *appointmentFolders; + + appointmentFolders = [userFolder privateCalendars: @"Calendar" inContext: context]; + [appointmentFolders newFolderWithName: displayName + nameInContainer: &nameInContainer]; + syncKey = @"-1"; + } + break; + case 14: + { + SOGoContactFolders *contactFolders; + + contactFolders = [userFolder privateContacts: @"Contacts" inContext: context]; + [contactFolders newFolderWithName: displayName + nameInContainer: &nameInContainer]; + syncKey = @"-1"; + } + break; + default: + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"Unsupported folder type during creation."]; + return; + } + } // switch (type) ... + + + // + // All good, we send our response. The format is documented here: + // 6.7 FolderCreate Response Schema - http://msdn.microsoft.com/en-us/library/dn338950(v=exchg.80).aspx + // + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendFormat: @"%@", syncKey]; + [s appendFormat: @"%@", nameInContainer]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +// +// +// +- (void) processFolderUpdate: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *serverId, *parentId, *displayName; + SOGoMailAccounts *accountsFolder; + SOGoUserFolder *userFolder; + SOGoMailFolder *folderToUpdate; + id currentFolder; + NSException *error; + + serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue]; + parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue]; + displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue]; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + folderToUpdate = [currentFolder lookupName: serverId + inContext: context + acquire: NO]; + + error = [folderToUpdate renameTo: displayName]; + + // Handle new name exist + if (!error) + { + NSMutableString *s; + NSData *d; + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; + } + else + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"Unable to update folder."]; + } +} + + +// +// +// +// +// 0 +// +// +- (void) processFolderSync: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSMutableDictionary *metadata; + NSMutableString *s; + NSString *syncKey; + NSData *d; + + BOOL first_sync; + int status; + + metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: @"SEC17CD1A3E9E3F2"]; + syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; + s = [NSMutableString string]; + + first_sync = NO; + status = 1; + + if ([syncKey isEqualToString: @"0"]) + { + first_sync = YES; + syncKey = @"1"; + } + else if (![syncKey isEqualToString: [[metadata objectForKey: @"FolderSync"] objectForKey: @"SyncKey"]]) + { + // Synchronization key mismatch or invalid synchronization key + status = 9; + } + + [metadata setObject: [NSDictionary dictionaryWithObject: syncKey forKey: @"SyncKey"] forKey: @"FolderSync"]; + [[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata forDevice: [context objectForKey: @"DeviceId"]]; + [[[context activeUser] userSettings] synchronize]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d%@", status, syncKey]; + + // Initial sync, let's return the complete folder list + if (first_sync) + { + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + SOGoUserFolder *userFolder; + id currentFolder; + + NSDictionary *folderMetadata; + NSArray *allFoldersMetadata; + NSString *name, *serverId, *parentId; + + int i, type; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + allFoldersMetadata = [accountFolder allFoldersMetadata]; + + // See 2.2.3.170.3 Type (FolderSync) - http://msdn.microsoft.com/en-us/library/gg650877(v=exchg.80).aspx + [s appendFormat: @"%d", [allFoldersMetadata count]+3]; + + for (i = 0; i < [allFoldersMetadata count]; i++) + { + folderMetadata = [allFoldersMetadata objectAtIndex: i]; + serverId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"path"]]; + name = [folderMetadata objectForKey: @"displayName"]; + + if ([name hasPrefix: @"/"]) + name = [name substringFromIndex: 1]; + + if ([name hasSuffix: @"/"]) + name = [name substringToIndex: [name length]-2]; + + type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType]; + + parentId = @"0"; + + if ([folderMetadata objectForKey: @"parent"]) + { + parentId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"parent"]]; + name = [[name pathComponents] lastObject]; + } + + [s appendFormat: @"%@%@%d%@", + serverId, + parentId, + type, + name]; + } + + // We add the personal calendar - events + // FIXME: add all calendars + currentFolder = [[context activeUser] personalCalendarFolderInContext: context]; + name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]]; + [s appendFormat: @"%@%@%d%@", name, @"0", 8, [currentFolder displayName]]; + + // We add the personal calendar - tasks + // FIXME: add all calendars + currentFolder = [[context activeUser] personalCalendarFolderInContext: context]; + name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]]; + [s appendFormat: @"%@%@%d%@", name, @"0", 7, [currentFolder displayName]]; + + // We add the personal address book + // FIXME: add all address books + currentFolder = [[context activeUser] personalContactsFolderInContext: context]; + name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]]; + [s appendFormat: @"%@%@%d%@", name, @"0", 9, [currentFolder displayName]]; + } + + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +// +// From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx : +// +// <2> Section 2.2.2.6: The GetAttachment command is not supported when the MS-ASProtocolVersion header is set to 14.0 or 14.1 +// in the GetAttachment command request. Use the Fetch element of the ItemOperations command instead. For more information about +// the MS-ASProtocolVersion header, see [MS-ASHTTP] section 2.2.1.1.2.4. +// +- (void) processGetAttachment: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + +} + +// +// +// +// +// +// +// 1 +// folderINBOX +// +// 3 +// +// +// +// +// +- (void) processGetItemEstimate: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + SOGoMailAccounts *accountsFolder; + SOGoUserFolder *userFolder; + id currentFolder, currentCollection; + + NSString *collectionId, *realCollectionId; + NSMutableString *s; + NSData *d; + + SOGoMicrosoftActiveSyncFolderType folderType; + int status; + + s = [NSMutableString string]; + status = 1; + + collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; + realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", realCollectionId] + inContext: context + acquire: NO]; + + // FIXME: we ignore FilterType for now + NSArray *uids = [currentCollection fetchUIDsMatchingQualifier: [EOQualifier qualifierWithQualifierFormat: + @"(not (flags = %@))", + @"deleted"] + sortOrdering: @"REVERSE ARRIVAL" + threaded: NO]; + + + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", status]; + + [s appendString: @"Email"]; + [s appendFormat: @"%@", collectionId]; + [s appendFormat: @"%d", [uids count]]; + + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; + +} + +// +// +// +// +// +// Mailbox -- http://msdn.microsoft.com/en-us/library/gg663522(v=exchg.80).aspx +// 2 -- +// +// +// +// +- (void) processItemOperations: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *fileReference, *realCollectionId; + NSMutableString *s; + + SOGoMicrosoftActiveSyncFolderType folderType; + + fileReference = [[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue]; + + realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; + + if (folderType == ActiveSyncMailFolder) + { + id currentFolder, currentCollection, currentBodyPart; + NSString *folderName, *messageName, *pathToPart; + SOGoMailAccounts *accountsFolder; + SOGoUserFolder *userFolder; + SOGoMailObject *mailObject; + NSData *d; + + NSRange r1, r2; + + r1 = [realCollectionId rangeOfString: @"/"]; + r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; + + folderName = [realCollectionId substringToIndex: r1.location]; + messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; + pathToPart = [realCollectionId substringFromIndex: r2.location+1]; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] + inContext: context + acquire: NO]; + + mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; + currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; + + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendString: @""]; + + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%@", fileReference]; + [s appendString: @""]; + + [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; + [s appendFormat: @"%@", [[[currentBodyPart fetchBLOB] stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""]]; + + [s appendString: @""]; + [s appendString: @""]; + + + [s appendString: @""]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + [theResponse setContent: d]; + } + else + { + [theResponse setStatus: 500]; + } +} + + +// +// +// +// +// +// 85 +// mail/INBOX +// mail/toto +// +// +// +- (void) processMoveItems: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *srcMessageId, *srcFolderId, *dstFolderId; + SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType; + + srcMessageId = [[(id)[theDocumentElement getElementsByTagName: @"SrcMsgId"] lastObject] textValue]; + srcFolderId = [[[(id)[theDocumentElement getElementsByTagName: @"SrcFldId"] lastObject] textValue] realCollectionIdWithFolderType: &srcFolderType]; + dstFolderId = [[[(id)[theDocumentElement getElementsByTagName: @"DstFldId"] lastObject] textValue] realCollectionIdWithFolderType: &dstFolderType]; + + // FIXME + if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder) + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *currentFolder; + SOGoUserFolder *userFolder; + id currentCollection; + NSException *ex; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", srcFolderId] + inContext: context + acquire: NO]; + + // FIXME: improve destination folder + ex = (id)[currentCollection moveUIDs: [NSArray arrayWithObject: srcMessageId] + toFolder: [NSString stringWithFormat: @"/0/folder%@", dstFolderId] + inContext: context]; + if (ex) + { + [theResponse setStatus: 500]; + [theResponse appendContentString: [ex reason]]; + } + else + { + NSMutableString *s; + NSData *d; + + // Everything is alright, lets return the proper response + s = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%d", 1]; + [s appendString: @""]; + + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; + } + } + else + { + [theResponse setStatus: 500]; + } +} + +// +// We ignore everything for now +// +- (void) processPing: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSMutableString *s; + NSData *d; + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"1"]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +// +// +// +// +// sogo1@example.com +// sogo10@sogoludo.inverse +// +// 19 +// +// 2014-01-16T05:00:00.000Z +// 2014-01-17T04:59:00.000Z +// +// +// +// +- (void) processResolveRecipients: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSArray *allRecipients; + int i, j, k; + + allRecipients = (id)[theDocumentElement getElementsByTagName: @"To"]; + + if ([allRecipients count] && [(id)[theDocumentElement getElementsByTagName: @"Availability"] count]) + { + NSCalendarDate *startDate, *endDate; + SOGoAppointmentFolder *folder; + NSString *aRecipient, *login; + NSMutableString *s; + NSArray *freebusy; + SOGoUser *user; + NSData *d; + + unsigned int startdate, enddate, increments; + char c; + + startDate = [[[(id)[theDocumentElement getElementsByTagName: @"StartTime"] lastObject] textValue] calendarDate]; + startdate = [startDate timeIntervalSince1970]; + + endDate = [[[(id)[theDocumentElement getElementsByTagName: @"EndTime"] lastObject] textValue] calendarDate]; + enddate = [endDate timeIntervalSince1970]; + + // Number of 30 mins increments between our two dates + increments = ceil((float)((enddate - startdate)/60/30)) + 1; + + s = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + + for (i = 0; i < [allRecipients count]; i++) + { + aRecipient = [[allRecipients objectAtIndex: i] textValue]; + + login = [[SOGoUserManager sharedUserManager] getUIDForEmail: aRecipient]; + + if (login) + { + user = [SOGoUser userWithLogin: login]; + + [s appendString: @""]; + [s appendFormat: @"%@", aRecipient]; + [s appendFormat: @"%d", 1]; + [s appendFormat: @"%d", 1]; + + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendFormat: @"%@", [user cn]]; + [s appendFormat: @"%@", [user systemEmail]]; + + // Freebusy structure: http://msdn.microsoft.com/en-us/library/gg663493(v=exchg.80).aspx + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendString: @""]; + + folder = [user personalCalendarFolderInContext: context]; + freebusy = [folder fetchFreeBusyInfosFrom: startDate to: endDate]; + + + NGCalendarDateRange *r1, *r2; + + for (j = 1; j <= increments; j++) + { + c = '0'; + + r1 = [NGCalendarDateRange calendarDateRangeWithStartDate: [NSDate dateWithTimeIntervalSince1970: (startdate+j*30*60)] + endDate: [NSDate dateWithTimeIntervalSince1970: (startdate+j*30*60 + 30)]]; + + + for (k = 0; k < [freebusy count]; k++) + { + + r2 = [NGCalendarDateRange calendarDateRangeWithStartDate: [[freebusy objectAtIndex: k] objectForKey: @"startDate"] + endDate: [[freebusy objectAtIndex: k] objectForKey: @"endDate"]]; + + if ([r2 doesIntersectWithDateRange: r1]) + { + c = '2'; + break; + } + } + + + [s appendFormat: @"%c", c]; + } + + + [s appendString: @""]; + [s appendString: @""]; + + + [s appendString: @""]; + [s appendString: @""]; + } + } + + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; + } +} + +// +// +// +// +// +// GAL +// so +// +// 0-19 +// +// +// +// +- (void) processSearch: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + SOGoContactSourceFolder *currentFolder; + NSDictionary *systemSources, *contact; + SOGoContactFolders *contactFolders; + NSArray *allKeys, *allContacts; + SOGoUserFolder *userFolder; + NSString *name, *query; + NSMutableString *s; + NSData *d; + + int i, j, total; + + name = [[(id)[theDocumentElement getElementsByTagName: @"Name"] lastObject] textValue]; + query = [[(id)[theDocumentElement getElementsByTagName: @"Query"] lastObject] textValue]; + + // FIXME: for now, we only search in the GAL + if (![name isEqualToString: @"GAL"]) + { + [theResponse setStatus: 500]; + return; + } + + + userFolder = [[context activeUser] homeFolderInContext: context]; + contactFolders = [userFolder privateContacts: @"Contacts" inContext: context]; + systemSources = [contactFolders systemSources]; + allKeys = [systemSources allKeys]; + + s = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"1"]; + [s appendFormat: @""]; + [s appendFormat: @""]; + [s appendFormat: @"1"]; + + total = 0; + + for (i = 0; i < [allKeys count]; i++) + { + currentFolder = [systemSources objectForKey: [allKeys objectAtIndex: i]]; + allContacts = [currentFolder lookupContactsWithFilter: query + onCriteria: @"name_or_address" + sortBy: @"c_cn" + ordering: NSOrderedAscending + inDomain: [[context activeUser] domain]]; + + for (j = 0; j < [allContacts count]; j++) + { + contact = [allContacts objectAtIndex: j]; + + // We skip lists for now + if ([[contact objectForKey: @"c_component"] isEqualToString: @"vlist"]) + continue; + + // We get the LDIF entry of our record, for easier processing + contact = [[currentFolder lookupName: [contact objectForKey: @"c_name"] inContext: context acquire: NO] ldifRecord]; + + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%@", [contact objectForKey: @"displayname"]]; + [s appendFormat: @"%@", [contact objectForKey: @"givenname"]]; + [s appendFormat: @"%@", [contact objectForKey: @"sn"]]; + [s appendFormat: @"%@", [contact objectForKey: @"mail"]]; + [s appendFormat: @"%@", [contact objectForKey: @"telephonenumber"]]; + [s appendFormat: @"%@", [contact objectForKey: @"o"]]; + [s appendString: @""]; + [s appendString: @""]; + total++; + } + } + + [s appendFormat: @"0-%d", total-1]; + [s appendFormat: @"%d", total]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + +// +// +// +- (NSException *) _sendMail: (NSData *) theMail + recipients: (NSArray *) theRecipients + saveInSentItems: (BOOL) saveInSentItems +{ + id authenticator; + SOGoDomainDefaults *dd; + NSException *error; + NSString *from; + + authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator]; + dd = [[context activeUser] domainDefaults]; + + // We generate the Sender + from = [[[context activeUser] allEmails] objectAtIndex: 0]; + + error = [[SOGoMailer mailerWithDomainDefaults: dd] + sendMailData: theMail + toRecipients: theRecipients + sender: from + withAuthenticator: authenticator + inContext: context]; + + if (error) + { + return error; + } + + if (saveInSentItems) + { + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + SOGoUserFolder *userFolder; + SOGoSentFolder *sentFolder; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + sentFolder = [accountFolder sentFolderInContext: context]; + + [sentFolder postData: theMail flags: @"seen"]; + } + + return nil; +} + +// +// +// +- (void) processSendMail: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NGMimeMessageParser *parser; + NGMimeMessage *message; + NSException *error; + NSData *data; + + // We get the mail's data + data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]; + + // We extract the recipients + parser = [[NGMimeMessageParser alloc] init]; + message = [parser parsePartFromData: data]; + RELEASE(parser); + + error = [self _sendMail: data + recipients: [message allRecipients] + saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)]; + + if (error) + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"FATAL ERROR occured during SendMail"]; + } +} + + + +// +// +// Examples: +// +// +// +// +// +// +// text +// +// +// +// +// +// +// "POST /SOGo/Microsoft-Server-ActiveSync?Cmd=Settings&User=sogo10&DeviceId=SEC17CD1A3E9E3F2&DeviceType=SAMSUNGSGHI317M HTTP/1.1" +// +// +// +// +// +// +// SGH-I317M +// 354422050248226 +// t0ltevl +// Android +// English +// 15147553630 +// SAMSUNG-SGH-I317M/100.40102 +// 0 +// Koodo +// +// +// +// +// We ignore everything for now +// +- (void) processSettings: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + + NSMutableString *s; + NSData *d; + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @" 1"]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + + +// +// +// +// +// C9FF94FE-EA40-473A-B3E2-AAEE94F753A4 +// +// +// +// mail/INBOX +// 82 +// +// ... the data ... +// +// +- (void) processSmartForward: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSString *folderId, *itemId, *realCollectionId; + SOGoMicrosoftActiveSyncFolderType folderType; + + folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue]; + itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue]; + realCollectionId = [folderId realCollectionIdWithFolderType: &folderType]; + + if (folderType == ActiveSyncMailFolder) + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *currentFolder; + SOGoUserFolder *userFolder; + SOGoMailObject *mailObject; + id currentCollection; + + NGMimeMessage *messageFromSmartForward, *messageToSend; + NGMimeMessageParser *parser; + NSData *data; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", realCollectionId] + inContext: context + acquire: NO]; + + mailObject = [currentCollection lookupName: itemId inContext: context acquire: NO]; + + + parser = [[NGMimeMessageParser alloc] init]; + data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]; + messageFromSmartForward = [parser parsePartFromData: data]; + + RELEASE(parser); + + + // We create a new MIME multipart/mixed message. The first part will be the text part + // of our "smart forward" and the second part will be the message/rfc822 part of the + // "smart forwarded" message. + NGMimeBodyPart *bodyPart; + NGMutableHashMap *map; + id body; + + map = [NGHashMap hashMapWithDictionary: [messageFromSmartForward headers]]; + [map setObject: @"multipart/mixed" forKey: @"content-type"]; + + messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease]; + body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease]; + + // First part + map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; + [map setObject: @"text/plain" forKey: @"content-type"]; + bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease]; + [bodyPart setBody: [messageFromSmartForward body]]; + [body addBodyPart: bodyPart]; + + // Second part + // FIXME - SOPE (read "POS") generates garbage if we only have the content-type header + map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; + [map setObject: @"message/rfc822" forKey: @"content-type"]; + bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease]; + [bodyPart setBody: [mailObject content]]; + [body addBodyPart: bodyPart]; + [messageToSend setBody: body]; + + NGMimeMessageGenerator *generator; + generator = [[[NGMimeMessageGenerator alloc] init] autorelease]; + data = [generator generateMimeFromPart: messageToSend]; + + NSException *error = [self _sendMail: data + recipients: [messageFromSmartForward allRecipients] + saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)]; + + if (error) + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"FATAL ERROR occured during SmartForward"]; + } + } + else + { + // FIXME + [theResponse setStatus: 500]; + [theResponse appendContentString: @"SmartForward not-implemented on non-mail folders."]; + } +} + +// +// +// +// +// DD40B5DC-4BDF-4A6A-9D8B-4B02BE5342CD +// +// -- http://msdn.microsoft.com/en-us/library/gg663506(v=exchg.80).aspx +// +// mail/INBOX +// 82 +// +// ... the data ... +// +// +- (void) processSmartReply: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + [self processSmartForward: theDocumentElement inResponse: theResponse]; +} + + + +// +// +// +- (NSException *) dispatchRequest: (id) theRequest + inResponse: (id) theResponse + context: (id) theContext +{ + id documentElement; + id builder, dom; + SEL aSelector; + + NSString *cmdName, *deviceId; + NSData *d; + + ASSIGN(context, theContext); + + // Get the device ID and "stash" it + deviceId = [[theRequest uri] deviceId]; + [context setObject: deviceId forKey: @"DeviceId"]; + + d = [[theRequest content] wbxml2xml]; + + if (!d) + { + return [NSException exceptionWithHTTPStatus: 500]; + } + + builder = [[[NSClassFromString(@"DOMSaxBuilder") alloc] init] autorelease]; + dom = [builder buildFromData: d]; + documentElement = [dom documentElement]; + + // See 2.2.2 Commands - http://msdn.microsoft.com/en-us/library/ee202197(v=exchg.80).aspx + // for all potential commands + cmdName = [NSString stringWithFormat: @"process%@:inResponse:", [documentElement tagName]]; + aSelector = NSSelectorFromString(cmdName); + + [self performSelector: aSelector withObject: documentElement withObject: theResponse]; + + [theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"]; + [theResponse setHeader: @"14.0" forKey: @"MS-Server-ActiveSync"]; + [theResponse 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"]; + [theResponse setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0" forKey: @"MS-ASProtocolVersions"]; + + RELEASE(context); + + return nil; +} + +@end diff --git a/ActiveSync/SOGoMailObject+ActiveSync.h b/ActiveSync/SOGoMailObject+ActiveSync.h new file mode 100644 index 000000000..358c7f73f --- /dev/null +++ b/ActiveSync/SOGoMailObject+ActiveSync.h @@ -0,0 +1,34 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSDictionary; + +@interface SOGoMailObject (ActiveSync) + +- (NSString *) activeSyncRepresentation; +- (void) takeActiveSyncValues: (NSDictionary *) theValues; + +@end + +#endif diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m new file mode 100644 index 000000000..802f7d025 --- /dev/null +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -0,0 +1,296 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import +#import + +#import +#import +#import +#import +#import + +#include "NSDate+ActiveSync.h" + +#include "../SoObjects/Mailer/NSString+Mail.h" + +@implementation SOGoMailObject (ActiveSync) + +- (NSString *) _baseEmailAddressesFrom: (NSArray *) enveloppeAddresses +{ + NSMutableArray *addresses; + NSString *rc; + NGImap4EnvelopeAddress *address; + NSString *email; + 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 baseEMail]]; + + [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 +{ + NSString *type, *subtype; + NSData *d; + + type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString]; + subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString]; + + d = nil; + + if (theType == 1 || theType == 2) + { + if ([type isEqualToString: @"text"]) + { + d = [[self fetchPlainTextParts] objectForKey: @""]; + + // 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; +} + +// +// +// +- (NSString *) activeSyncRepresentation +{ + NSMutableString *s; + NSData *d; + id value; + + int preferredBodyType; + + s = [NSMutableString string]; + + // From + value = [self _baseEmailAddressesFrom: [[self envelope] from]]; + if (value) + [s appendFormat: @"%@", value]; + + // 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 _baseEmailAddressesFrom: [[self envelope] to]]; + if (value) + [s appendFormat: @"%@", value]; + + // Cc - same syntax as the To field + value = [self _baseEmailAddressesFrom: [[self envelope] cc]]; + if (value) + [s appendFormat: @"%@", value]; + + // Subject + value = [self decodedSubject]; + if (value) + [s appendFormat: @"%@", value]; + + // DateReceived + value = [self date]; + if (value) + [s appendFormat: @"%@", [value activeSyncRepresentation]];; + + // DisplayTo + //[s appendFormat: @"\"%@\"", [[context activeUser] login]]; + + // Importance - FIXME + [s appendFormat: @"%@", @"1"]; + + // Read + [s appendFormat: @"%d", ([self read] ? 1 : 0)]; + + // MesssageClass + [s appendFormat: @"%@", @"IPM.Note"]; + + // Reply-To - FIXME + //NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo]; + //if ([replyTo count]) + // [s appendFormat: @"%@", [addressFormatter stringForArray: replyTo]]; + + // InternetCPID - FIXME + [s appendFormat: @"%@", @"65001"]; + + // Body - namespace 17 + preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue]; + + d = [self _preferredBodyDataUsingType: preferredBodyType]; + + if (d) + { + NSString *content; + int len; + + content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding]; + AUTORELEASE(content); + + content = [content stringByEscapingHTMLString]; + len = [content length]; + + [s appendString: @""]; + [s appendFormat: @"%d", preferredBodyType]; + [s appendFormat: @"%d", len]; + [s appendFormat: @"%d", 0]; + [s appendFormat: @"%@", content]; + [s appendString: @""]; + } + + // Attachments -namespace 16 + NSArray *attachmentKeys = [self fetchFileAttachmentKeys]; + if ([attachmentKeys count]) + { + int i; + + [s appendString: @""]; + + for (i = 0; i < [attachmentKeys count]; i++) + { + value = [attachmentKeys objectAtIndex: i]; + + [s appendString: @""]; + [s appendFormat: @"%@", [value objectForKey: @"filename"]]; + + // FileReference must be a unique identifier across the whole store. We use the following structure: + // mail// + // mail/INBOX/2 + [s appendFormat: @"mail/%@/%@/%@", [[self container] relativeImap4Name], [self nameInContainer], [value objectForKey: @"path"]]; + + [s appendFormat: @"%d", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx + [s appendFormat: @"%d", [[value objectForKey: @"size"] intValue]]; + //[s appendFormat: @"%d", 1]; + [s appendString: @""]; + } + + [s appendString: @""]; + } + + // ContentClass + [s appendFormat: @"%@", @"urn:content-classes:message"]; + + // Flags + [s appendString: @""]; + [s appendFormat: @"%d", 0]; + [s appendString: @""]; + + // 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 + [s appendFormat: @"%d", preferredBodyType]; + + return s; +} + +// +// +// +- (void) takeActiveSyncValues: (NSDictionary *) theValues +{ + id o; + + if ((o = [theValues objectForKey: @"Flag"])) + { + o = [o objectForKey: @"FlagStatus"]; + + if ([o intValue]) + [self addFlags: @"\\Flagged"]; + else + [self removeFlags: @"\\Flagged"]; + + } +} + +@end diff --git a/ActiveSync/SoObjectWebDAVDispatcher+ActiveSync.m b/ActiveSync/SoObjectWebDAVDispatcher+ActiveSync.m new file mode 100644 index 000000000..c38742d15 --- /dev/null +++ b/ActiveSync/SoObjectWebDAVDispatcher+ActiveSync.m @@ -0,0 +1,91 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +#include +#include +#include +#include +#include + +#import + +@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.00.0536.000" forKey: @"MS-Server-ActiveSync"]; + [response setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0" 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 diff --git a/ActiveSync/common.make b/ActiveSync/common.make new file mode 100644 index 000000000..78ad58bd1 --- /dev/null +++ b/ActiveSync/common.make @@ -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) diff --git a/ActiveSync/iCalEvent+ActiveSync.h b/ActiveSync/iCalEvent+ActiveSync.h new file mode 100644 index 000000000..6627b1f81 --- /dev/null +++ b/ActiveSync/iCalEvent+ActiveSync.h @@ -0,0 +1,35 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSString; + +@interface iCalEvent (ActiveSync) + +- (NSString *) activeSyncRepresentation; +- (void) takeActiveSyncValues: (NSDictionary *) theValues; + +@end + +#endif diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m new file mode 100644 index 000000000..f54c94628 --- /dev/null +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -0,0 +1,113 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +#import + +#include "iCalTimeZone+ActiveSync.h" +#include "NSDate+ActiveSync.h" +#include "NSString+ActiveSync.h" + +@implementation iCalEvent (ActiveSync) + +- (NSString *) activeSyncRepresentation +{ + NSMutableString *s; + iCalTimeZone *tz; + + s = [NSMutableString string]; + + // DTStamp -- http://msdn.microsoft.com/en-us/library/ee219470(v=exchg.80).aspx + if ([self timeStampAsDate]) + [s appendFormat: @"%@", [[self timeStampAsDate] activeSyncRepresentation]]; + else if ([self created]) + [s appendFormat: @"%@", [[self created] activeSyncRepresentation]]; + + // StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx + if ([self startDate]) + [s appendFormat: @"%@", [[self startDate] activeSyncRepresentation]]; + + // EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx + if ([self endDate]) + [s appendFormat: @"%@", [[self endDate] activeSyncRepresentation]]; + + // Timezone + tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone]; + + if (!tz) + tz = [iCalTimeZone timeZoneForName: @"Europe/London"]; + + [s appendFormat: @"%@", [[tz activeSyncRepresentation] stringByReplacingString: @"\n" withString: @""]];; + + + // Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx + if ([[self summary] length]) + [s appendFormat: @"%@", [self summary]]; + + // UID -- http://msdn.microsoft.com/en-us/library/ee159919(v=exchg.80).aspx + if ([[self uid] length]) + [s appendFormat: @"%@", [self uid]]; + + // Sensitivity - FIXME + [s appendFormat: @"%d", 0]; + + // BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx + [s appendFormat: @"%d", 0]; + + // Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx + + return s; +} + +// +// +// +- (void) takeActiveSyncValues: (NSDictionary *) theValues +{ + id o; + + if ((o = [theValues objectForKey: @"UID"])) + [self setUid: o]; + + if ((o = [theValues objectForKey: @"Subject"])) + [self setSummary: o]; + + if ([[theValues objectForKey: @"AllDayEvent"] intValue]) + { + + } + + if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"])) + [self setComment: o]; + + if ((o = [theValues objectForKey: @"Location"])) + [self setLocation: o]; + + if ((o = [theValues objectForKey: @"StartTime"])) + [self setStartDate: [o calendarDate]]; + + if ((o = [theValues objectForKey: @"EndTime"])) + [self setEndDate: [o calendarDate]]; +} + +@end diff --git a/ActiveSync/iCalTimeZone+ActiveSync.h b/ActiveSync/iCalTimeZone+ActiveSync.h new file mode 100644 index 000000000..3cfadf451 --- /dev/null +++ b/ActiveSync/iCalTimeZone+ActiveSync.h @@ -0,0 +1,33 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSString; + +@interface iCalTimeZone (ActiveSync) + +- (NSString *) activeSyncRepresentation; + +@end + +#endif diff --git a/ActiveSync/iCalTimeZone+ActiveSync.m b/ActiveSync/iCalTimeZone+ActiveSync.m new file mode 100644 index 000000000..5eae381da --- /dev/null +++ b/ActiveSync/iCalTimeZone+ActiveSync.m @@ -0,0 +1,153 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#include +#include +#include +#include + +#import +#import +#import + +#import + +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 *) activeSyncRepresentation +{ + 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 stringByEncodingBase64]; +} + +@end diff --git a/ActiveSync/iCalToDo+ActiveSync.h b/ActiveSync/iCalToDo+ActiveSync.h new file mode 100644 index 000000000..92ef9ef58 --- /dev/null +++ b/ActiveSync/iCalToDo+ActiveSync.h @@ -0,0 +1,34 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 + +@class NSString; + +@interface iCalToDo (ActiveSync) + +- (NSString *) activeSyncRepresentation; +- (void) takeActiveSyncValues: (NSDictionary *) theValues; + +@end + +#endif diff --git a/ActiveSync/iCalToDo+ActiveSync.m b/ActiveSync/iCalToDo+ActiveSync.m new file mode 100644 index 000000000..0e0f97c9a --- /dev/null +++ b/ActiveSync/iCalToDo+ActiveSync.m @@ -0,0 +1,85 @@ +/* + +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: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#import + +#include "NSDate+ActiveSync.h" + +@implementation iCalToDo (ActiveSync) + +- (NSString *) activeSyncRepresentation +{ + NSMutableString *s; + int v; + + s = [NSMutableString string]; + + // Complete + NSCalendarDate *completed; + completed = [self completed]; + [s appendFormat: @"%d", (completed ? 1 : 0)]; + + // DateCompleted + [s appendFormat: @"%@", [completed activeSyncRepresentation]]; + + // Due date + NSCalendarDate *due; + due = [self due]; + if (due) + [s appendFormat: @"%@", [due activeSyncRepresentation]]; + + // Importance + NSString *priority; + priority = [self priority]; + if ([priority isEqualToString: @"9"]) + v = 0; + else if ([priority isEqualToString: @"1"]) + v = 2; + else + v = 1; + [s appendFormat: @"%d", v]; + + // Reminder - FIXME + [s appendFormat: @"%d", 0]; + + // Sensitivity - FIXME + [s appendFormat: @"%d", 0]; + + // UTCStartDate - FIXME + if ([self startDate]) + [s appendFormat: @"%@", [[self startDate] activeSyncRepresentation]]; + + // Subject + [s appendFormat: @"%@", [self summary]]; + + return s; +} + +- (void) takeActiveSyncValues: (NSDictionary *) theValues +{ + id o; + + if ((o = [theValues objectForKey: @"Subject"])) + [self setSummary: o]; +} + +@end diff --git a/ActiveSync/product.plist b/ActiveSync/product.plist new file mode 100644 index 000000000..5e5eadabc --- /dev/null +++ b/ActiveSync/product.plist @@ -0,0 +1,3 @@ +{ + requires = ( MAIN, Appointments, Contacts, Mailer ); +} \ No newline at end of file