mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-25 05:15:25 +00:00
+22
@@ -18,10 +18,12 @@ trans.fr = UI/MailerUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MailerUI/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/MailerUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MailerUI/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MailerUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MailerUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MailerUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings
|
||||
@@ -47,10 +49,12 @@ trans.fr = UI/PreferencesUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/PreferencesUI/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/PreferencesUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/PreferencesUI/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/PreferencesUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/PreferencesUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/PreferencesUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings
|
||||
@@ -76,10 +80,12 @@ trans.fr = UI/Scheduler/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Scheduler/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/Scheduler/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Scheduler/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Scheduler/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Scheduler/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Scheduler/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings
|
||||
@@ -105,10 +111,12 @@ trans.fr = UI/Contacts/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Contacts/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/Contacts/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Contacts/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Contacts/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Contacts/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Contacts/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Contacts/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings
|
||||
@@ -134,10 +142,12 @@ trans.fr = UI/MainUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MainUI/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/MainUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MainUI/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MainUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MainUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MainUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MainUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MainUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings
|
||||
@@ -163,10 +173,12 @@ trans.fr = UI/Common/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Common/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/Common/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Common/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Common/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Common/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Common/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Common/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Common/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/Common/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Common/Slovak.lproj/Localizable.strings
|
||||
@@ -192,10 +204,12 @@ trans.fr = UI/AdministrationUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/AdministrationUI/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/AdministrationUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/AdministrationUI/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/AdministrationUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/AdministrationUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/AdministrationUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/AdministrationUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings
|
||||
@@ -221,10 +235,12 @@ trans.fr = SoObjects/Appointments/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Appointments/Hungarian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Appointments/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Appointments/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Appointments/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Appointments/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Appointments/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings
|
||||
@@ -250,10 +266,12 @@ trans.fr = SoObjects/Contacts/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Contacts/Hungarian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Contacts/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Contacts/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Contacts/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Contacts/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Contacts/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
|
||||
@@ -279,10 +297,12 @@ trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Mailer/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Mailer/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Mailer/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Mailer/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings
|
||||
@@ -308,10 +328,12 @@ trans.fr = UI/MailPartViewers/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MailPartViewers/Hungarian.lproj/Localizable.strings
|
||||
trans.is = UI/MailPartViewers/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MailPartViewers/Italian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MailPartViewers/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MailPartViewers/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MailPartViewers/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
# compilation settings
|
||||
ifeq ($(HAS_LIBRARY_ssl),yes)
|
||||
ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
|
||||
BUNDLE_LIBS += -lcrypto
|
||||
endif
|
||||
|
||||
+181
-49
@@ -48,11 +48,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@implementation NGVCard (ActiveSync)
|
||||
|
||||
//
|
||||
// This function is called for each elements which can be ghosted according to specs.
|
||||
// https://msdn.microsoft.com/en-us/library/gg650908%28v=exchg.80%29.aspx
|
||||
//
|
||||
- (BOOL) _isGhosted: (NSString *) element
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSArray *supportedElements;
|
||||
|
||||
supportedElements = [context objectForKey: @"SupportedElements"];
|
||||
|
||||
// If the client does not include a Supported element in the initial Sync command request for
|
||||
// a folder, then all of the elements that can be ghosted are considered not ghosted.
|
||||
if (!supportedElements)
|
||||
return NO;
|
||||
|
||||
// If the client includes an empty Supported element in the initial Sync command request for
|
||||
// a folder, then all elements that can be ghosted are considered ghosted.
|
||||
if (![supportedElements count])
|
||||
return YES;
|
||||
|
||||
// If the client includes a Supported element that contains child elements in the initial
|
||||
// Sync command request for a folder, then each child element of that Supported element is
|
||||
// considered not ghosted. All elements that can be ghosted that are not included as child
|
||||
// elements of the Supported element are considered ghosted.
|
||||
if (!([supportedElements indexOfObject: element] == NSNotFound))
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *emails, *addresses, *categories, *elements;
|
||||
CardElement *n, *homeAdr, *workAdr;
|
||||
NSMutableString *s;
|
||||
NSMutableString *s, *a;
|
||||
NSString *url;
|
||||
id o;
|
||||
|
||||
@@ -63,7 +94,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 0 forKey: @""]))
|
||||
[s appendFormat: @"<LastName xmlns=\"Contacts:\">%@</LastName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
|
||||
[s appendFormat: @"<FirstName xmlns=\"Contacts:\">%@</FirstName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
@@ -146,16 +177,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([addresses count])
|
||||
{
|
||||
homeAdr = [addresses objectAtIndex: 0];
|
||||
a = [NSMutableString string];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [o activeSyncRepresentationInContext: context]];
|
||||
[a appendString: o];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
|
||||
[a appendFormat: @"\n%@", o];
|
||||
|
||||
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [a activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<HomeCity xmlns=\"Contacts:\">%@</HomeCity>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""]))
|
||||
[s appendFormat: @"<HomeState xmlns=\"Contacts:\">%@</HomeState>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""]))
|
||||
[s appendFormat: @"<HomePostalCode xmlns=\"Contacts:\">%@</HomePostalCode>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
@@ -171,9 +208,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([addresses count])
|
||||
{
|
||||
workAdr = [addresses objectAtIndex: 0];
|
||||
a = [NSMutableString string];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [o activeSyncRepresentationInContext: context]];
|
||||
[a appendString: o];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
|
||||
[a appendFormat: @"\n%@", o];
|
||||
|
||||
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [a activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessCity xmlns=\"Contacts:\">%@</BusinessCity>", [o activeSyncRepresentationInContext: context]];
|
||||
@@ -217,6 +260,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
CardElement *element;
|
||||
NSMutableArray *addressLines;
|
||||
id o;
|
||||
|
||||
// Contact's note
|
||||
@@ -226,6 +270,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
else
|
||||
[[self children] removeObjectsInArray: [self childrenWithTag: @"Categories"]];
|
||||
|
||||
// Birthday
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
@@ -233,6 +279,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
|
||||
}
|
||||
else if (![self _isGhosted: @"Birthday" inContext: context])
|
||||
{
|
||||
[self setBday: @""];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Business address information
|
||||
@@ -244,18 +295,48 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// BusinessCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"work"];
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessStreet"]
|
||||
atIndex: 2 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessStreet"]) || ![self _isGhosted: @"BusinessStreet" inContext: context])
|
||||
{
|
||||
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
|
||||
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
|
||||
atIndex: 2 forKey: @""];
|
||||
|
||||
// Extended address line. If there are more than 2 address lines we add them to the extended address line.
|
||||
if ([addressLines count] > 1)
|
||||
{
|
||||
[addressLines removeObjectAtIndex: 0];
|
||||
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
|
||||
atIndex: 1 forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessCity"]) || ![self _isGhosted: @"BusinessCity" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessState"]) || ![self _isGhosted: @"BusinessState" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessPostalCode"]) || ![self _isGhosted: @"BusinessPostalCode" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessCountry"]) || ![self _isGhosted: @"BusinessCountry" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
}
|
||||
|
||||
//
|
||||
// Home address information
|
||||
@@ -267,35 +348,69 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// HomeCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"home"];
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeStreet"]
|
||||
atIndex: 2 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeStreet"]) || ![self _isGhosted: @"HomeStreet" inContext: context])
|
||||
{
|
||||
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
|
||||
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
|
||||
atIndex: 2 forKey: @""];
|
||||
|
||||
// Extended address line. If there are more then 2 address lines we add them to the extended address line.
|
||||
if ([addressLines count] > 1)
|
||||
{
|
||||
[addressLines removeObjectAtIndex: 0];
|
||||
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
|
||||
atIndex: 1 forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeCity"]) || ![self _isGhosted: @"HomeCity" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeState"]) || ![self _isGhosted: @"HomeState" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomePostalCode"]) || ![self _isGhosted: @"HomePostalCode" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeCountry"]) || ![self _isGhosted: @"HomeCountry" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
}
|
||||
|
||||
// Company's name
|
||||
if ((o = [theValues objectForKey: @"CompanyName"]))
|
||||
[self setOrg: o units: nil];
|
||||
else if (![self _isGhosted: @"CompanyName" inContext: context])
|
||||
[self setOrg: @"" units: nil];
|
||||
|
||||
// Department
|
||||
if ((o = [theValues objectForKey: @"Department"]))
|
||||
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
|
||||
else if (![self _isGhosted: @"Department" inContext: context])
|
||||
[self setOrg: nil units: [NSArray arrayWithObjects:@"",nil]];
|
||||
|
||||
// Email addresses
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]))
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]) || ![self _isGhosted: @"Email1Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]))
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]) || ![self _isGhosted: @"Email2Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
@@ -303,7 +418,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
|
||||
// FIXME: revise this when the GUI revamp is done in SOGo
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]))
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]) || ![self _isGhosted: @"Email3Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"three"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
@@ -313,45 +428,62 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// MiddleName
|
||||
// Suffix (II)
|
||||
// Title (Mr.)
|
||||
[self setFn: [theValues objectForKey: @"FileAs"]];
|
||||
if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context])
|
||||
[self setFn: [theValues objectForKey: @"FileAs"]];
|
||||
|
||||
[self setNWithFamily: [theValues objectForKey: @"LastName"]
|
||||
given: [theValues objectForKey: @"FirstName"]
|
||||
additional: nil prefixes: nil suffixes: nil];
|
||||
|
||||
// IM information
|
||||
[[self uniqueChildWithTag: @"x-aim"]
|
||||
setSingleValue: [theValues objectForKey: @"IMAddress"]
|
||||
forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context])
|
||||
[[self uniqueChildWithTag: @"x-aim"]
|
||||
setSingleValue: [theValues objectForKey: @"IMAddress"]
|
||||
forKey: @""];
|
||||
|
||||
//
|
||||
// Phone numbrrs
|
||||
//
|
||||
element = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"BusinessPhoneNumber"]) || ![self _isGhosted: @"BusinessPhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"HomePhoneNumber"]) || ![self _isGhosted: @"HomePhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"MobilePhoneNumber"]) || ![self _isGhosted: @"MobilePhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"BusinessFaxNumber"]) || ![self _isGhosted: @"BusinessFaxNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
|
||||
if ((o = [theValues objectForKey: @"PagerNumber"]) || ![self _isGhosted: @"PagerNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
// Job's title
|
||||
if ((o = [theValues objectForKey: @"JobTitle"]))
|
||||
if ((o = [theValues objectForKey: @"JobTitle"]) || ![self _isGhosted: @"JobTitle" inContext: context])
|
||||
[self setTitle: o];
|
||||
|
||||
// WebPage (work)
|
||||
if ((o = [theValues objectForKey: @"WebPage"]))
|
||||
if ((o = [theValues objectForKey: @"WebPage"]) || ![self _isGhosted: @"WebPage" inContext: context])
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
setSingleValue: o forKey: @""];
|
||||
|
||||
if ((o = [theValues objectForKey: @"NickName"]))
|
||||
if ((o = [theValues objectForKey: @"NickName"]) || ![self _isGhosted: @"NickName" inContext: context])
|
||||
[self setNickname: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Picture"]))
|
||||
|
||||
@@ -112,13 +112,73 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@implementation SOGoActiveSyncDispatcher (Sync)
|
||||
|
||||
- (void) _setOrUnsetSyncRequest: (BOOL) set
|
||||
collections: (NSArray *) collections
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
NSNumber *processIdentifier;
|
||||
NSString *key;
|
||||
int i;
|
||||
|
||||
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
if (set)
|
||||
{
|
||||
RELEASE(syncRequest);
|
||||
syncRequest = [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]];
|
||||
RETAIN(syncRequest);
|
||||
|
||||
[[o properties] setObject: syncRequest forKey: @"SyncRequest"];
|
||||
|
||||
for (i = 0; i < [collections count]; i++)
|
||||
{
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
|
||||
[[o properties] setObject: processIdentifier forKey: key];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[[o properties] removeObjectForKey: @"SyncRequest"];
|
||||
for (i = 0; i < [collections count]; i++)
|
||||
{
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
|
||||
[[o properties] removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
[o save];
|
||||
}
|
||||
|
||||
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
|
||||
forKey: (NSString *) theFolderKey
|
||||
{
|
||||
NSNumber *processIdentifier, *processIdentifierInCache;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSDictionary *values;
|
||||
NSString *key;
|
||||
|
||||
if ([theFolderKey hasPrefix: @"folder"])
|
||||
key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]];
|
||||
else
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", theFolderKey];
|
||||
|
||||
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
|
||||
processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key];
|
||||
|
||||
// Don't update the cache if another request is processing the same collection.
|
||||
if (!([processIdentifierInCache isEqual: processIdentifier]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
|
||||
values = [theFolderMetadata copy];
|
||||
|
||||
@@ -132,6 +192,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
|
||||
@@ -404,8 +465,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
}
|
||||
break;
|
||||
case ActiveSyncEventFolder:
|
||||
@@ -415,27 +476,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o takeActiveSyncValues: allChanges inContext: context];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
|
||||
if ([syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
}
|
||||
break;
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
NSDictionary *result;
|
||||
NSString *modseq;
|
||||
NSNumber *modseq;
|
||||
|
||||
[sogoObject takeActiveSyncValues: allChanges inContext: context];
|
||||
|
||||
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
|
||||
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
|
||||
|
||||
if (modseq)
|
||||
[syncCache setObject: modseq forKey: serverId];
|
||||
if (modseq && [syncCache objectForKey: serverId])
|
||||
[syncCache setObject: [modseq stringValue] forKey: serverId];
|
||||
}
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
@@ -578,36 +639,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
withFilterType: (NSCalendarDate *) theFilterType
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
lastServerKey: (NSString **) theLastServerKey
|
||||
|
||||
defaultInterval: (unsigned int) theDefaultInterval
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||
NSString *davCollectionTagToStore;
|
||||
NSAutoreleasePool *pool;
|
||||
NSMutableString *s;
|
||||
|
||||
BOOL more_available;
|
||||
BOOL cleanup_needed, more_available;
|
||||
int i, max;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
more_available = NO;
|
||||
cleanup_needed = more_available = NO;
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
// If this is a new sync operation, DateCache and SyncCache needs to be deleted
|
||||
// If this is a new sync operation, DateCache and SyncCache need to be deleted
|
||||
if ([theSyncKey isEqualToString: @"-1"])
|
||||
{
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
||||
}
|
||||
else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
|
||||
{
|
||||
// The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response.
|
||||
// We need to cleanup this mess.
|
||||
[self logWithFormat: @"Cache cleanup needed for device %@ - user: %@ syncKey: %@ cache: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login], theSyncKey, [folderMetadata objectForKey: @"SyncKey"]];
|
||||
cleanup_needed = YES;
|
||||
}
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
|
||||
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
|
||||
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
|
||||
theFilterType)
|
||||
(cleanup_needed ||
|
||||
( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
|
||||
!([folderMetadata objectForKey: @"InitialLoadSequence"]))) &&
|
||||
theFilterType
|
||||
)
|
||||
{
|
||||
NSArray *allKeys;
|
||||
NSString *key;
|
||||
@@ -623,14 +692,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
|
||||
{
|
||||
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
|
||||
[s appendString: @"</SoftDelete>"];
|
||||
if ([syncCache objectForKey:key])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - SoftDelete %@", key];
|
||||
|
||||
[syncCache removeObjectForKey: key];
|
||||
[dateCache removeObjectForKey: key];
|
||||
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
|
||||
[s appendString: @"</SoftDelete>"];
|
||||
|
||||
[syncCache removeObjectForKey: key];
|
||||
//[dateCache removeObjectForKey: key];
|
||||
|
||||
softdelete_count++;
|
||||
softdelete_count++;
|
||||
}
|
||||
else if (cleanup_needed)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - SoftDelete cleanup %@", key];
|
||||
|
||||
// With this we make sure that a SoftDelete is set again on next sync.
|
||||
[syncCache setObject: @"0" forKey: key];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - SoftDelete final delete %@", key];
|
||||
|
||||
// Now we are save to remove the dateCache entry.
|
||||
[dateCache removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
@@ -657,8 +748,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
|
||||
return;
|
||||
|
||||
more_available = NO;
|
||||
|
||||
davCollectionTagToStore = [theCollection davCollectionTag];
|
||||
|
||||
@@ -687,7 +776,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
initialLoadInProgress = NO;
|
||||
|
||||
if ([theSyncKey isEqualToString: @"-1"])
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
|
||||
|
||||
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
|
||||
{
|
||||
@@ -707,6 +796,53 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Check for the WindowSize
|
||||
max = [allComponents count];
|
||||
|
||||
//
|
||||
// Cleanup the mess
|
||||
//
|
||||
if (cleanup_needed)
|
||||
{
|
||||
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
component = [allComponents objectAtIndex: i];
|
||||
deleted = [[component objectForKey: @"c_deleted"] intValue];
|
||||
|
||||
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
|
||||
continue;
|
||||
|
||||
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", uid];
|
||||
|
||||
// For deletes we have to recreate a cache entry to make sure the delete is sent again.
|
||||
[syncCache setObject: @"0" forKey: uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([syncCache objectForKey: uid] && [[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: ADD %@", uid];
|
||||
|
||||
// Cleanup the cache to make sure the add is sent again.
|
||||
[syncCache removeObjectForKey: uid];
|
||||
[dateCache removeObjectForKey: uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid];
|
||||
|
||||
// Update cache entry to make sure the change is sent again.
|
||||
[syncCache setObject: @"0" forKey: uid];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return_count = 0;
|
||||
|
||||
for (i = 0; i < max; i++)
|
||||
@@ -865,11 +1001,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
SOGoMailObject *mailObject;
|
||||
NSArray *allMessages, *a;
|
||||
NSString *firstUIDAdded;
|
||||
|
||||
int j, k, return_count, highestmodseq;
|
||||
BOOL found_in_cache, initialLoadInProgress;
|
||||
|
||||
initialLoadInProgress = NO;
|
||||
initialLoadInProgress = NO;
|
||||
found_in_cache = NO;
|
||||
firstUIDAdded = nil;
|
||||
|
||||
if ([theSyncKey isEqualToString: @"-1"])
|
||||
{
|
||||
@@ -900,7 +1039,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
|
||||
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
|
||||
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
|
||||
}
|
||||
|
||||
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
|
||||
@@ -909,15 +1048,82 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
|
||||
|
||||
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
|
||||
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
|
||||
if (debugOn)
|
||||
{
|
||||
[self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]];
|
||||
[self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]];
|
||||
}
|
||||
|
||||
lastCacheObject = [sortedBySequence lastObject];
|
||||
|
||||
//
|
||||
// Cleanup the mess
|
||||
//
|
||||
if (cleanup_needed)
|
||||
{
|
||||
NSMutableArray *sortedByUID;
|
||||
int uidnextFromCache;
|
||||
|
||||
sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache];
|
||||
[sortedByUID sortUsingSelector: @selector(compareUID:)];
|
||||
|
||||
// Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request.
|
||||
a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"];
|
||||
uidnextFromCache = [[a objectAtIndex: 0] intValue];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]];
|
||||
|
||||
// Remove all entries from cache beginning with the first uid added by the last sync request.
|
||||
for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: ADD %d", j];
|
||||
|
||||
[syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
|
||||
[dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
|
||||
}
|
||||
|
||||
RELEASE(sortedByUID);
|
||||
|
||||
for (j = 0; j < [allCacheObjects count]; j++)
|
||||
{
|
||||
// Update the modseq in cache, sence othersie, it would be identical to the modseq from server
|
||||
//and we would skip the cache when generating the response.
|
||||
if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]];
|
||||
|
||||
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
|
||||
}
|
||||
else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]];
|
||||
|
||||
// For deletes we have to recreate a cache entry to have the <Delete> included in the response.
|
||||
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
|
||||
if (!cleanup_needed &&
|
||||
[folderMetadata objectForKey: @"MoreAvailable"] &&
|
||||
lastCacheObject &&
|
||||
!([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup.
|
||||
{
|
||||
for (j = 0; j < [allCacheObjects count]; j++)
|
||||
{
|
||||
if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) ||
|
||||
(![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]))
|
||||
{
|
||||
// We need to continue with adds or deletes from here.
|
||||
found_in_cache = YES;
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
|
||||
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
|
||||
{
|
||||
// Found out where we're at, let's continue from there...
|
||||
@@ -932,12 +1138,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if (found_in_cache)
|
||||
k = j+1;
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
j = 0;
|
||||
}
|
||||
|
||||
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
|
||||
j = k = 0;
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k];
|
||||
|
||||
return_count = 0;
|
||||
|
||||
@@ -951,20 +1155,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSString *lastSequence;
|
||||
more_available = YES;
|
||||
|
||||
if (!firstUIDAdded)
|
||||
{
|
||||
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
|
||||
firstUIDAdded = [a objectAtIndex: 0];
|
||||
RETAIN(firstUIDAdded);
|
||||
}
|
||||
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
|
||||
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
|
||||
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
|
||||
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, lastSequence];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey];
|
||||
|
||||
DESTROY(pool);
|
||||
break;
|
||||
}
|
||||
|
||||
aCacheObject = [allCacheObjects objectAtIndex: k];
|
||||
|
||||
// If found in cache, it's either a Change or a Delete
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject];
|
||||
|
||||
// If found in cache, it's either a Change or a Delete operation.
|
||||
if ([syncCache objectForKey: [aCacheObject uid]])
|
||||
{
|
||||
if ([[aCacheObject sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - DELETE!"];
|
||||
|
||||
// Deleted
|
||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
@@ -982,9 +1201,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
mailObject = [theCollection lookupName: [aCacheObject uid]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
|
||||
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - CHANGE!"];
|
||||
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
@@ -1000,6 +1222,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - ADD!"];
|
||||
|
||||
// Added
|
||||
if (![[aCacheObject sequence] isEqual: [NSNull null]])
|
||||
{
|
||||
@@ -1032,11 +1257,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
|
||||
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
|
||||
|
||||
// Save the frist UID we add. We will use it for the synckey late.
|
||||
if (!firstUIDAdded)
|
||||
{
|
||||
firstUIDAdded = [aCacheObject uid];
|
||||
RETAIN(firstUIDAdded);
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - first uid added %@", firstUIDAdded];
|
||||
}
|
||||
|
||||
return_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,16 +1281,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (more_available)
|
||||
{
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||
|
||||
if (firstUIDAdded)
|
||||
{
|
||||
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
|
||||
[folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"];
|
||||
RELEASE(firstUIDAdded);
|
||||
}
|
||||
else
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
RELEASE(*theLastServerKey);
|
||||
|
||||
} // default:
|
||||
@@ -1153,15 +1398,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
changeDetected: (BOOL *) changeDetected
|
||||
maxSyncResponseSize: (int) theMaxSyncResponseSize
|
||||
{
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
id collection, value;
|
||||
|
||||
NSMutableString *changeBuffer, *commandsBuffer;
|
||||
BOOL getChanges, first_sync;
|
||||
unsigned int windowSize, v, status;
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey;
|
||||
NSMutableDictionary *folderMetadata, *folderOptions;
|
||||
|
||||
NSMutableArray *supportedElements, *supportedElementNames;
|
||||
NSMutableString *changeBuffer, *commandsBuffer;
|
||||
id collection, value;
|
||||
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
unsigned int windowSize, v, status, i;
|
||||
BOOL getChanges, first_sync;
|
||||
|
||||
changeBuffer = [NSMutableString string];
|
||||
commandsBuffer = [NSMutableString string];
|
||||
|
||||
@@ -1184,7 +1430,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//[theBuffer appendString: @"</Collection>"];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// First check if we have any concurrent Sync requests going on for this device.
|
||||
// If we do and we are still within our maximumSyncInterval, we let our EAS
|
||||
// device know to retry.
|
||||
//
|
||||
folderKey = [self _getNameInCache: collection withType: folderType];
|
||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
||||
|
||||
// We check for a window size, default to 100 if not specfied or out of bounds
|
||||
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
|
||||
|
||||
@@ -1209,13 +1463,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
first_sync = NO;
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
|
||||
if ([syncKey isEqualToString: @"0"])
|
||||
{
|
||||
davCollectionTag = @"-1";
|
||||
first_sync = YES;
|
||||
*changeDetected = YES;
|
||||
|
||||
supportedElementNames = [[[NSMutableArray alloc] init] autorelease];
|
||||
value = [theDocumentElement getElementsByTagName: @"Supported"];
|
||||
|
||||
if ([value count])
|
||||
{
|
||||
supportedElements = (id)[[value lastObject] childNodes];
|
||||
|
||||
if ([supportedElements count])
|
||||
{
|
||||
for (i = 0; i < [supportedElements count]; i++)
|
||||
{
|
||||
if ([[supportedElements objectAtIndex: i] nodeType] == DOM_ELEMENT_NODE)
|
||||
[supportedElementNames addObject: [[supportedElements objectAtIndex: i] tagName]];
|
||||
}
|
||||
}
|
||||
|
||||
[folderMetadata setObject: supportedElementNames forKey: @"SupportedElements"];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - %d %@: supportedElements saved: %@", [supportedElements count], [collection nameInContainer], supportedElementNames];
|
||||
}
|
||||
}
|
||||
else if ((![syncKey isEqualToString: @"-1"]) && !([folderMetadata objectForKey: @"SyncCache"]))
|
||||
{
|
||||
@@ -1240,6 +1516,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// By default, send MIME mails. See #3146 for details.
|
||||
if (!bodyPreferenceType)
|
||||
bodyPreferenceType = @"4";
|
||||
|
||||
mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
|
||||
|
||||
if (!mimeSupport)
|
||||
mimeSupport = @"1";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1265,11 +1546,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
|
||||
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
}
|
||||
}
|
||||
|
||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||
[context setObject: mimeSupport forKey: @"MIMESupport"];
|
||||
[context setObject: [folderMetadata objectForKey: @"SupportedElements"] forKey: @"SupportedElements"];
|
||||
|
||||
//
|
||||
// We process the commands from the request
|
||||
@@ -1308,10 +1591,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
inBuffer: changeBuffer
|
||||
lastServerKey: &lastServerKey];
|
||||
lastServerKey: &lastServerKey
|
||||
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]];
|
||||
}
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
||||
|
||||
// If we got any changes or if we have applied any commands
|
||||
// let's regenerate our SyncKey based on the collection tag.
|
||||
@@ -1470,19 +1754,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
SOGoSystemDefaults *defaults;
|
||||
id <DOMElement> aCollection;
|
||||
NSMutableString *output, *s;
|
||||
NSMutableDictionary *globalMetadata;
|
||||
NSNumber *syncRequestInCache, *processIdentifier;
|
||||
NSString *key;
|
||||
NSArray *allCollections;
|
||||
NSData *d;
|
||||
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep;
|
||||
BOOL changeDetected;
|
||||
|
||||
changeDetected = NO;
|
||||
|
||||
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
|
||||
|
||||
// We initialize our output buffer
|
||||
output = [[NSMutableString alloc] init];
|
||||
|
||||
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
defaultInterval = [defaults maximumSyncInterval];
|
||||
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
|
||||
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
|
||||
|
||||
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
|
||||
@@ -1497,12 +1786,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[output appendString: @"</Sync>"];
|
||||
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
RELEASE(output);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let other requests know about the collections we are dealing with.
|
||||
[self _setOrUnsetSyncRequest: YES collections: allCollections];
|
||||
|
||||
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
changeDetected = NO;
|
||||
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
|
||||
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
|
||||
defaultInterval = [defaults maximumSyncInterval];
|
||||
internalInterval = [defaults internalSyncInterval];
|
||||
|
||||
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
|
||||
@@ -1512,17 +1805,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// We check to see if our heartbeat interval falls into the supported ranges.
|
||||
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
|
||||
{
|
||||
int limit;
|
||||
// Interval is too long, inform the client.
|
||||
heartbeatInterval = defaultInterval;
|
||||
|
||||
// Outlook doesn't like this...
|
||||
//[output appendFormat: @"<Limit>%d</Limit>", defaultInterval];
|
||||
// When Status = 14, the Wait interval is specified in minutes while
|
||||
// defaultInterval is specifed in seconds. Adjust accordinlgy.
|
||||
limit = defaultInterval/60;
|
||||
if (limit < 1) limit = 1;
|
||||
if (limit > 59) limit = 59;
|
||||
//[output appendFormat: @"<Limit>%d</Limit>", limit];
|
||||
//[output appendFormat: @"<Status>%d</Status>", 14];
|
||||
}
|
||||
|
||||
[output appendString: @"<Collections>"];
|
||||
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
|
||||
|
||||
// We enter our loop detection change
|
||||
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
|
||||
@@ -1532,25 +1828,62 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
for (j = 0; j < [allCollections count]; j++)
|
||||
{
|
||||
aCollection = [allCollections objectAtIndex: j];
|
||||
|
||||
|
||||
[self processSyncCollection: aCollection
|
||||
inBuffer: s
|
||||
changeDetected: &changeDetected
|
||||
maxSyncResponseSize: maxSyncResponseSize];
|
||||
|
||||
if (maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize)
|
||||
// Don't return a response if another Sync is waiting.
|
||||
globalMetadata = [self globalMetadataForDevice];
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[aCollection getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
|
||||
|
||||
if (!([[globalMetadata objectForKey: key] isEqual: processIdentifier]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Discard response %@", [self globalMetadataForDevice]];
|
||||
|
||||
[theResponse setStatus: 503];
|
||||
|
||||
RELEASE(output);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize))
|
||||
break;
|
||||
}
|
||||
|
||||
if (changeDetected)
|
||||
{
|
||||
[self logWithFormat: @"Change detected, we push the content."];
|
||||
[self logWithFormat: @"Change detected during Sync, we push the content."];
|
||||
break;
|
||||
}
|
||||
else if (heartbeatInterval > 1)
|
||||
{
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
|
||||
sleep(internalInterval);
|
||||
total_sleep = 0;
|
||||
|
||||
while (total_sleep < internalInterval)
|
||||
{
|
||||
// We check if we must break the current synchronization since an other Sync
|
||||
// has just arrived.
|
||||
syncRequestInCache = [[self globalMetadataForDevice] objectForKey: @"SyncRequest"];
|
||||
if (!([syncRequest isEqualToNumber: syncRequestInCache]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]];
|
||||
|
||||
// Make sure we end the heardbeat-loop.
|
||||
heartbeatInterval = internalInterval = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep];
|
||||
sleep(5);
|
||||
total_sleep += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1558,8 +1891,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
|
||||
//
|
||||
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0,
|
||||
// otherwise send an empty response.
|
||||
//
|
||||
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
|
||||
{
|
||||
// We always return the last generated response.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
Copyright (c) 2014-2015, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -31,16 +31,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
|
||||
@class NSCalendarDate;
|
||||
@class NSException;
|
||||
@class NSMutableDictionary;
|
||||
@class NSURL;
|
||||
@class NSNumber;
|
||||
|
||||
@interface SOGoActiveSyncDispatcher : NSObject
|
||||
{
|
||||
NSURL *folderTableURL;
|
||||
NSDictionary *imapFolderGUIDS;
|
||||
id context;
|
||||
|
||||
NSNumber *syncRequest;
|
||||
|
||||
BOOL debugOn;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) globalMetadataForDevice;
|
||||
|
||||
- (id) collectionFromId: (NSString *) theCollectionId
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
|
||||
@@ -30,8 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "SOGoActiveSyncDispatcher.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#if GNUSTEP_BASE_MINOR_VERSION >= 21
|
||||
#import <Foundation/NSLocale.h>
|
||||
#endif
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
@@ -134,6 +138,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
@interface SOGoActiveSyncDispatcher (Sync)
|
||||
|
||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
||||
@@ -143,8 +153,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@implementation SOGoActiveSyncDispatcher
|
||||
|
||||
static BOOL debugOn = NO;
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[super init];
|
||||
@@ -152,6 +160,7 @@ static BOOL debugOn = NO;
|
||||
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
|
||||
folderTableURL = nil;
|
||||
imapFolderGUIDS = nil;
|
||||
syncRequest = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -159,6 +168,7 @@ static BOOL debugOn = NO;
|
||||
{
|
||||
RELEASE(folderTableURL);
|
||||
RELEASE(imapFolderGUIDS);
|
||||
RELEASE(syncRequest);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -171,16 +181,16 @@ static BOOL debugOn = NO;
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
[[o properties] removeAllObjects];
|
||||
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
|
||||
[[o properties] setObject: theSyncKey
|
||||
forKey: @"FolderSyncKey"];
|
||||
[o save];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) _globalMetadataForDevice
|
||||
- (NSMutableDictionary *) globalMetadataForDevice
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
|
||||
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
@@ -204,7 +214,7 @@ static BOOL debugOn = NO;
|
||||
if (theFilter)
|
||||
{
|
||||
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
@@ -697,26 +707,25 @@ static BOOL debugOn = NO;
|
||||
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType;
|
||||
NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType, *operation;
|
||||
NSMutableDictionary *cachedGUIDs, *metadata;
|
||||
NSMutableArray *folders, *processedFolders;
|
||||
NSDictionary *folderMetadata, *imapGUIDs;
|
||||
NSArray *allFoldersMetadata, *allKeys;
|
||||
NSMutableDictionary *cachedGUIDs, *metadata;
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
NSMutableString *s, *commands;
|
||||
SOGoUserFolder *userFolder;
|
||||
NSMutableArray *folders, *processedFolders;
|
||||
SoSecurityManager *sm;
|
||||
SOGoCacheGCSObject *o;
|
||||
id currentFolder;
|
||||
NSData *d;
|
||||
|
||||
int status, command_count, i, type, fi, count;
|
||||
|
||||
BOOL first_sync;
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
metadata = [self _globalMetadataForDevice];
|
||||
metadata = [self globalMetadataForDevice];
|
||||
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||
s = [NSMutableString string];
|
||||
|
||||
@@ -830,32 +839,38 @@ static BOOL debugOn = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
|
||||
[cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
folderType = @"Calendar";
|
||||
else
|
||||
folderType = @"Contacts";
|
||||
if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
|
||||
[cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
folderType = @"Calendar";
|
||||
else
|
||||
folderType = @"Contacts";
|
||||
|
||||
if ([ cKey rangeOfString: @"/"].location != NSNotFound)
|
||||
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
|
||||
if ([ cKey rangeOfString: @"/"].location != NSNotFound)
|
||||
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
|
||||
lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO];
|
||||
|
||||
// remove the folder from device if it doesn't exists or it has not the proper permissions
|
||||
if (!currentFolder ||
|
||||
[sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: currentFolder
|
||||
inContext: context] ||
|
||||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: currentFolder
|
||||
inContext: context])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
[o destroy];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// We skip personal GCS folders - we always want to synchronize these
|
||||
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
|
||||
[[currentFolder nameInContainer] isEqualToString: @"personal"])
|
||||
continue;
|
||||
|
||||
// Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions
|
||||
if (!currentFolder ||
|
||||
![currentFolder synchronize] ||
|
||||
[sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: currentFolder
|
||||
inContext: context] ||
|
||||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: currentFolder
|
||||
inContext: context])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
[o destroy];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle addition and changes
|
||||
for (i = 0; i < [allFoldersMetadata count]; i++)
|
||||
@@ -943,6 +958,7 @@ static BOOL debugOn = NO;
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[o save];
|
||||
@@ -953,17 +969,29 @@ static BOOL debugOn = NO;
|
||||
|
||||
personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer];
|
||||
folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy];
|
||||
[folders autorelease];
|
||||
|
||||
[folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]];
|
||||
|
||||
// Inside this loop we remove all the folder without write/delete permissions
|
||||
// We remove all the folders that aren't GCS-ones, that we don't want to synchronize and
|
||||
// the ones without write/delete permissions.
|
||||
count = [folders count]-1;
|
||||
for (; count >= 0; count--)
|
||||
{
|
||||
if ([sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: [folders objectAtIndex: count]
|
||||
currentFolder = [folders objectAtIndex: count];
|
||||
|
||||
// We skip personal GCS folders - we always want to synchronize these
|
||||
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
|
||||
[[currentFolder nameInContainer] isEqualToString: @"personal"])
|
||||
continue;
|
||||
|
||||
if (![currentFolder isKindOfClass: [SOGoGCSFolder class]] ||
|
||||
![currentFolder synchronize] ||
|
||||
[sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: currentFolder
|
||||
inContext: context] ||
|
||||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: [folders objectAtIndex: count]
|
||||
onObject: currentFolder
|
||||
inContext: context])
|
||||
{
|
||||
[folders removeObjectAtIndex: count];
|
||||
@@ -971,7 +999,6 @@ static BOOL debugOn = NO;
|
||||
}
|
||||
|
||||
count = [folders count]-1;
|
||||
NSString *operation;
|
||||
|
||||
for (fi = 0; fi <= count ; fi++)
|
||||
{
|
||||
@@ -1029,6 +1056,7 @@ static BOOL debugOn = NO;
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
}
|
||||
@@ -1053,6 +1081,7 @@ static BOOL debugOn = NO;
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
}
|
||||
@@ -1263,7 +1292,7 @@ static BOOL debugOn = NO;
|
||||
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
|
||||
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *mimeSupport, *collectionId;
|
||||
NSMutableString *s;
|
||||
NSArray *fetchRequests;
|
||||
id aFetch;
|
||||
@@ -1376,6 +1405,8 @@ static BOOL debugOn = NO;
|
||||
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
|
||||
[context setObject: mimeSupport forKey: @"MIMESupport"];
|
||||
|
||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
||||
@@ -2033,13 +2064,13 @@ static BOOL debugOn = NO;
|
||||
|
||||
if ([foldersWithChanges count])
|
||||
{
|
||||
[self logWithFormat: @"Change detected, we push the content."];
|
||||
[self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."];
|
||||
status = 2;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval];
|
||||
sleep(internalInterval);
|
||||
}
|
||||
}
|
||||
@@ -2103,6 +2134,123 @@ static BOOL debugOn = NO;
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
#ifdef HAVE_OPENSSL
|
||||
- (unsigned int) validateCert: (NSString *) theCert
|
||||
{
|
||||
NSData *d;
|
||||
|
||||
const unsigned char *data;
|
||||
X509_STORE_CTX *ctx;
|
||||
X509_LOOKUP *lookup;
|
||||
X509_STORE *store;
|
||||
X509 *cert;
|
||||
|
||||
BOOL success;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
success = NO;
|
||||
|
||||
d = [theCert dataByDecodingBase64];
|
||||
data = (unsigned char *)[d bytes];
|
||||
len = [d length];
|
||||
|
||||
cert = d2i_X509(NULL, &data, len);
|
||||
if (!cert)
|
||||
{
|
||||
[self logWithFormat: @"EAS - validateCert failed for device %@: d2i_X509 failed", [context objectForKey: @"DeviceId"]];
|
||||
return 17;
|
||||
}
|
||||
|
||||
store = X509_STORE_new();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
if (store)
|
||||
{
|
||||
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
|
||||
if (lookup)
|
||||
{
|
||||
X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
|
||||
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
|
||||
if (lookup)
|
||||
{
|
||||
X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
|
||||
ERR_clear_error();
|
||||
success = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (store)
|
||||
{
|
||||
X509_STORE_free(store);
|
||||
store = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ctx = X509_STORE_CTX_new();
|
||||
if (!ctx)
|
||||
{
|
||||
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_new failed", [context objectForKey: @"DeviceId"]];
|
||||
return 17;
|
||||
}
|
||||
|
||||
if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1)
|
||||
{
|
||||
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_init failed", [context objectForKey: @"DeviceId"]];
|
||||
X509_STORE_CTX_free(ctx);
|
||||
return 17;
|
||||
}
|
||||
|
||||
rc = X509_verify_cert(ctx);
|
||||
X509_STORE_CTX_free(ctx);
|
||||
X509_free(cert);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"EAS - validateCert failed for device %@: err=%d", [context objectForKey: @"DeviceId"], X509_STORE_CTX_get_error(ctx)];
|
||||
return 17;
|
||||
}
|
||||
}
|
||||
#else
|
||||
- (unsigned int) validateCert: (NSString *) theCert
|
||||
{
|
||||
return 17;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void) processValidateCert: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSString *cert;
|
||||
NSData *d;
|
||||
|
||||
cert = [[(id)[theDocumentElement getElementsByTagName: @"Certificate"] lastObject] textValue];
|
||||
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<ValidateCert xmlns=\"ValidateCert:\">"];
|
||||
[s appendString: @"<Status>1</Status><Certificate>"];
|
||||
[s appendFormat: @"<Status>%d</Status>", [self validateCert: cert]];
|
||||
[s appendString: @"</Certificate></ValidateCert>"];
|
||||
|
||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// <?xml version="1.0"?>
|
||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||
@@ -2419,39 +2567,85 @@ static BOOL debugOn = NO;
|
||||
NGMimeMessageParser *parser;
|
||||
NGMimeMessage *message;
|
||||
NSException *error;
|
||||
NSData *data;
|
||||
NGMutableHashMap *map;
|
||||
NGMimeMessage *messageToSend;
|
||||
NGMimeMessageGenerator *generator;
|
||||
NSMutableData *data;
|
||||
NSData *new_from_header;
|
||||
NSDictionary *identity;
|
||||
NSString *fullName, *email;
|
||||
|
||||
const char *bytes;
|
||||
int i, len;
|
||||
BOOL found_header;
|
||||
|
||||
// We get the mail's data
|
||||
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
data = [NSMutableData dataWithData: [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
// We extract the recipients
|
||||
parser = [[NGMimeMessageParser alloc] init];
|
||||
message = [parser parsePartFromData: data];
|
||||
RELEASE(parser);
|
||||
|
||||
map = [NGHashMap hashMapWithDictionary: [message headers]];
|
||||
|
||||
identity = [[context activeUser] primaryIdentity];
|
||||
|
||||
fullName = [identity objectForKey: @"fullName"];
|
||||
email = [identity objectForKey: @"email"];
|
||||
|
||||
if ([fullName length])
|
||||
[map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
|
||||
new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", [fullName asQPSubjectString: @"utf-8"], email] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
else
|
||||
[map setObject: email forKey: @"from"];
|
||||
new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||
bytes = [data bytes];
|
||||
len = [data length];
|
||||
i = 0;
|
||||
found_header = NO;
|
||||
|
||||
[messageToSend setBody: [message body]];
|
||||
// Search for the from-header
|
||||
while (i < len)
|
||||
{
|
||||
if (i == 0 &&
|
||||
(*bytes == 'f' || *bytes == 'F') &&
|
||||
(*(bytes+1) == 'r' || *(bytes+1) == 'R') &&
|
||||
(*(bytes+2) == 'o' || *(bytes+2) == 'O') &&
|
||||
(*(bytes+3) == 'm' || *(bytes+3) == 'M') &&
|
||||
(*(bytes+4) == ':'))
|
||||
{
|
||||
found_header = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (((*bytes == '\r') && (*(bytes+1) == '\n')) &&
|
||||
(*(bytes+2) == 'f' || *(bytes+2) == 'F') &&
|
||||
(*(bytes+3) == 'r' || *(bytes+3) == 'R') &&
|
||||
(*(bytes+4) == 'o' || *(bytes+4) == 'O') &&
|
||||
(*(bytes+5) == 'm' || *(bytes+5) == 'M') &&
|
||||
(*(bytes+6) == ':'))
|
||||
{
|
||||
found_header = YES;
|
||||
i = i + 2; // \r\n
|
||||
break;
|
||||
}
|
||||
|
||||
bytes++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Update/Add the From header in the MIMEBody of the SendMail request.
|
||||
// Any other way to modify the mail body would break s/mime emails.
|
||||
if (found_header)
|
||||
{
|
||||
// Change the From header
|
||||
[data replaceBytesInRange: NSMakeRange(i, [[message headerForKey: @"from"] length]+8) // start of the From header found - length of the parsed from-header-value + 8 (From:+\r\n+1)
|
||||
withBytes: [new_from_header bytes]
|
||||
length: [new_from_header length]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add a From header
|
||||
[data replaceBytesInRange: NSMakeRange(0, 0)
|
||||
withBytes: [new_from_header bytes]
|
||||
length: [new_from_header length]];
|
||||
}
|
||||
|
||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
||||
data = [generator generateMimeFromPart: messageToSend];
|
||||
|
||||
error = [self _sendMail: data
|
||||
recipients: [message allRecipients]
|
||||
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
|
||||
@@ -2948,8 +3142,24 @@ static BOOL debugOn = NO;
|
||||
options: NSCaseInsensitiveSearch].location == NSNotFound)
|
||||
{
|
||||
NSString *value;
|
||||
|
||||
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
|
||||
#if GNUSTEP_BASE_MINOR_VERSION < 21
|
||||
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
|
||||
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
|
||||
locale: nil];
|
||||
#else
|
||||
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
|
||||
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
|
||||
locale: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSArray arrayWithObjects: @"Jan", @"Feb", @"Mar", @"Apr",
|
||||
@"May", @"Jun", @"Jul", @"Aug",
|
||||
@"Sep", @"Oct", @"Nov", @"Dec", nil],
|
||||
@"NSShortMonthNameArray",
|
||||
[NSArray arrayWithObjects: @"Sun", @"Mon", @"Tue", @"Wed", @"Thu",
|
||||
@"Fri", @"Sat", nil],
|
||||
@"NSShortWeekDayNameArray",
|
||||
nil]];
|
||||
|
||||
#endif
|
||||
s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]];
|
||||
}
|
||||
else
|
||||
@@ -3040,8 +3250,7 @@ static BOOL debugOn = NO;
|
||||
return nil;
|
||||
|
||||
urlString = [[user domainDefaults] folderInfoURL];
|
||||
parts = [[urlString componentsSeparatedByString: @"/"]
|
||||
mutableCopy];
|
||||
parts = [[urlString componentsSeparatedByString: @"/"] mutableCopy];
|
||||
[parts autorelease];
|
||||
if ([parts count] == 5)
|
||||
{
|
||||
|
||||
@@ -476,14 +476,16 @@ struct GlobalObjectId {
|
||||
//
|
||||
//
|
||||
- (NSData *) _preferredBodyDataUsingType: (int) theType
|
||||
mimeSupport: (int) theMimeSupport
|
||||
nativeType: (int *) theNativeType
|
||||
{
|
||||
NSString *type, *subtype, *encoding;
|
||||
NSData *d;
|
||||
BOOL isSMIME;
|
||||
|
||||
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
|
||||
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||
|
||||
isSMIME = NO;
|
||||
d = nil;
|
||||
|
||||
// We determine the native type
|
||||
@@ -494,8 +496,14 @@ struct GlobalObjectId {
|
||||
else if ([type isEqualToString: @"multipart"])
|
||||
*theNativeType = 4;
|
||||
|
||||
if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && theMimeSupport > 0)
|
||||
{
|
||||
*theNativeType = 4;
|
||||
isSMIME = YES;
|
||||
}
|
||||
|
||||
// We get the right part based on the preference
|
||||
if (theType == 1 || theType == 2)
|
||||
if ((theType == 1 || theType == 2) && !isSMIME)
|
||||
{
|
||||
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
|
||||
{
|
||||
@@ -536,12 +544,12 @@ struct GlobalObjectId {
|
||||
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
||||
}
|
||||
}
|
||||
else if (theType == 4)
|
||||
else if (theType == 4 || isSMIME)
|
||||
{
|
||||
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
|
||||
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
|
||||
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
|
||||
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
|
||||
if ((encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) && !isSMIME)
|
||||
d = [self _sanitizedMIMEMessage];
|
||||
else
|
||||
d = [self content];
|
||||
@@ -656,16 +664,18 @@ struct GlobalObjectId {
|
||||
{
|
||||
NSData *d, *globalObjId;
|
||||
NSArray *attachmentKeys;
|
||||
NSMutableString *s;
|
||||
|
||||
uint32_t v;
|
||||
NSString *p;
|
||||
|
||||
id value;
|
||||
|
||||
iCalCalendar *calendar;
|
||||
NSString *p, *subtype;
|
||||
NSMutableString *s;
|
||||
id value;
|
||||
|
||||
int preferredBodyType, nativeBodyType;
|
||||
int preferredBodyType, mimeSupport, nativeBodyType;
|
||||
uint32_t v;
|
||||
|
||||
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||
|
||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||
mimeSupport = [[context objectForKey: @"MIMESupport"] intValue];
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
@@ -862,7 +872,12 @@ struct GlobalObjectId {
|
||||
else
|
||||
{
|
||||
// MesssageClass and ContentClass
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
||||
if ([subtype isEqualToString: @"signed"])
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME.MultipartSigned"];
|
||||
else if ([subtype isEqualToString: @"pkcs7-mime"])
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME"];
|
||||
else
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
||||
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
||||
}
|
||||
|
||||
@@ -876,10 +891,8 @@ struct GlobalObjectId {
|
||||
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
|
||||
|
||||
// Body - namespace 17
|
||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||
|
||||
nativeBodyType = 1;
|
||||
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
|
||||
d = [self _preferredBodyDataUsingType: preferredBodyType mimeSupport: mimeSupport nativeType: &nativeBodyType];
|
||||
|
||||
if (calendar && !d)
|
||||
{
|
||||
@@ -981,9 +994,12 @@ struct GlobalObjectId {
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
// Set the correct type if client requested text/html but we got text/plain
|
||||
// Set the correct type if client requested text/html but we got text/plain.
|
||||
// For s/mime mails type is always 4 if mimeSupport is 1 or 2.
|
||||
if (preferredBodyType == 2 && nativeBodyType == 1)
|
||||
[s appendString: @"<Type>1</Type>"];
|
||||
else if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && mimeSupport > 0)
|
||||
[s appendString: @"<Type>4</Type>"];
|
||||
else
|
||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||
|
||||
@@ -1001,7 +1017,8 @@ struct GlobalObjectId {
|
||||
|
||||
// Attachments -namespace 16
|
||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
if ([attachmentKeys count])
|
||||
|
||||
if ([attachmentKeys count] && !([subtype isEqualToString: @"signed"]))
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -1071,9 +1088,15 @@ struct GlobalObjectId {
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
|
||||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
|
||||
{
|
||||
id value;
|
||||
NSString *reference;
|
||||
|
||||
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
|
||||
value = [[self mailHeaders] objectForKey: @"references"];
|
||||
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0];
|
||||
else
|
||||
reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0];
|
||||
|
||||
if ([reference length] > 0)
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
|
||||
@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@interface SOGoSyncCacheObject : NSObject
|
||||
{
|
||||
@public
|
||||
id _uid;
|
||||
id _sequence;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
static Class NSNullK;
|
||||
|
||||
@implementation SOGoSyncCacheObject
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSNullK = [NSNull class];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
@@ -46,14 +54,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [[self alloc] init];
|
||||
|
||||
[o setUID: theUID];
|
||||
[o setSequence: theSequence];
|
||||
|
||||
[o setUID: [NSNumber numberWithInt: [theUID intValue]]];
|
||||
[o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
|
||||
|
||||
return [o autorelease];
|
||||
}
|
||||
@@ -67,7 +75,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (id) uid
|
||||
{
|
||||
return _uid;
|
||||
return [_uid description];
|
||||
}
|
||||
|
||||
- (void) setUID: (id) theUID
|
||||
@@ -77,7 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (id) sequence
|
||||
{
|
||||
return _sequence;
|
||||
return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
|
||||
}
|
||||
|
||||
- (void) setSequence: (id) theSequence
|
||||
@@ -88,7 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
return [[self uid] compare: [theSyncCacheObject uid]];
|
||||
return [self->_uid compare: theSyncCacheObject->_uid];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -97,21 +105,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
if ([[self sequence] isEqual: [NSNull null]] &&
|
||||
[[theSyncCacheObject sequence] isEqual: [NSNull null]])
|
||||
if ([self->_sequence isEqual: [NSNull null]] &&
|
||||
[theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return [self compareUID: theSyncCacheObject];
|
||||
|
||||
if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
|
||||
if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return NSOrderedDescending;
|
||||
|
||||
if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
|
||||
if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return NSOrderedAscending;
|
||||
|
||||
// Must check this here, to avoid comparing NSNull objects
|
||||
if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
|
||||
if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
|
||||
return [self compareUID: theSyncCacheObject];
|
||||
|
||||
return [[self sequence] compare: [theSyncCacheObject sequence]];
|
||||
return [self->_sequence compare: theSyncCacheObject->_sequence];
|
||||
}
|
||||
|
||||
- (NSString *) description
|
||||
@@ -120,3 +128,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// don't send negative reminder - not supported
|
||||
if (delta > 0)
|
||||
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", delta];
|
||||
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", (int)delta];
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
@@ -285,7 +285,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Simple reccurrence rule of type "Monthly"
|
||||
type = 2;
|
||||
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"Calendar:\">%d</Recurrence_DayOfMonth>",
|
||||
[[[self parent] startDate] dayOfMonth]];
|
||||
(int)[[[self parent] startDate] dayOfMonth]];
|
||||
}
|
||||
}
|
||||
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
|
||||
|
||||
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ supported by SOGo:
|
||||
* Red Hat Enterprise Linux (RHEL) Server 5, 6 and 7
|
||||
* Community ENTerprise Operating System (CentOS) 5, 6 and 7
|
||||
* Debian GNU/Linux 6.0 (Squeeze) to 8.0 (Jessie)
|
||||
* Ubuntu 10.04 (Lucid) to 14.04 (Trusty)
|
||||
* Ubuntu 12.04 (Precise) to 14.04 (Trusty)
|
||||
|
||||
Make sure the required components are started automatically at boot time
|
||||
and that they are running before proceeding with the SOGo configuration.
|
||||
|
||||
@@ -388,7 +388,7 @@ Run the following commands as root:
|
||||
|
||||
----
|
||||
samba-tool domain provision --realm=example.com \
|
||||
--domain=OPENCHANGE \
|
||||
--domain=EXAMPLE \
|
||||
--adminpass='%1OpenChange' \
|
||||
--server-role='domain controller'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- TODO have the build system take care of this -->
|
||||
<releaseinfo>Version 2.3.2 - September 2015</releaseinfo>
|
||||
<subtitle>for version 2.3.2</subtitle>
|
||||
<date>2015-09-16</date>
|
||||
<releaseinfo>Version 2.3.3 - November 2015</releaseinfo>
|
||||
<subtitle>for version 2.3.3</subtitle>
|
||||
<date>2015-11-11</date>
|
||||
|
||||
<legalnotice>
|
||||
<para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para>
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
|
||||
// TODO have the build system take care of this
|
||||
|
||||
:release_version: 2.3.2
|
||||
:release_version: 2.3.3
|
||||
|
||||
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||
|
||||
+1
-1
@@ -267,7 +267,7 @@
|
||||
NSObject <DOMNodeList> *list;
|
||||
NSObject <DOMNode> *valueNode;
|
||||
NSArray *elements;
|
||||
NSString *property, *match;
|
||||
NSString *property=nil, *match=nil;
|
||||
|
||||
list = [searchElement getElementsByTagName: @"prop"];
|
||||
if ([list length])
|
||||
|
||||
@@ -1,11 +1,60 @@
|
||||
2.3.3 (2015-mm-dd)
|
||||
2.3.4 (YYYY-MM-DD)
|
||||
------------------
|
||||
|
||||
New features
|
||||
-
|
||||
|
||||
Enhancements
|
||||
- limit the maximum width of toolbar buttons
|
||||
|
||||
|
||||
Bug fixes
|
||||
- JavaScript exception when printing events from calendars with no assigned color (#3203)
|
||||
|
||||
2.3.3a (2015-11-18)
|
||||
-------------------
|
||||
|
||||
Bug fixes
|
||||
- expanded mail folders list is not saved (#3386)
|
||||
- cleanup translations
|
||||
|
||||
2.3.3 (2015-11-11)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- initial S/MIME support for EAS (#3327)
|
||||
- now possible to choose which folders to sync over EAS
|
||||
|
||||
Enhancements
|
||||
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
|
||||
- support for ghosted elements on contacts over EAS
|
||||
- added Macedonian (mk_MK) translation - thanks to Miroslav Jovanovic
|
||||
- added Portuguese (pt) translation - thanks to Eduardo Crispim
|
||||
|
||||
Bug fixes
|
||||
- numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849)
|
||||
- correctly handle the References header over EAS (#3365)
|
||||
- make sure English is always used when generating Date headers using EAS (#3356)
|
||||
- don't escape quoted strings during versit generation
|
||||
- we now return all cards when we receive an empty addressbook-query REPORT
|
||||
- avoid crash when replying to a mail with no recipients (#3359)
|
||||
- inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271)
|
||||
- prevent postal address showing on single line over EAS (#2614)
|
||||
- display missing events when printing working hours only
|
||||
- fix corner case making server crash when syncing hard deleted messages when clear offline items was set up (Zentyal)
|
||||
- avoid infinite Outlook client loops trying to set read flag when it is already set (Zentyal)
|
||||
- avoid crashing when calendar metadata is missing in the cache (Zentyal)
|
||||
- fix recurrence pattern event corner case created by Mozilla Thunderbird which made server crash (Zentyal)
|
||||
- fix corner case that removes attachments on sending messages from Outlook (Zentyal)
|
||||
- freebusy on web interface works again in multidomain environments (Zentyal)
|
||||
- fix double creation of folders in Outlook when the folder name starts with a digit (Zentyal)
|
||||
- avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal)
|
||||
- handle emails having an attachment as their content
|
||||
- fixed JavaScript syntax error in attendees editor
|
||||
- fixed wrong comparison of meta vs. META tag in HTML mails
|
||||
- fixed popup menu position when moved to the left (#3381)
|
||||
- fixed dialog position when at the bottom of the window (#2646, #3378)
|
||||
- fixed addressbrook-only source entires having a c_uid set
|
||||
|
||||
2.3.2 (2015-09-16)
|
||||
------------------
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
forURL: (NSURL *) server
|
||||
forceRenew: (BOOL) renew;
|
||||
|
||||
- (NSString *) passwordInContext: (WOContext *) context;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MAPISTOREAUTHENTICATOR_H */
|
||||
|
||||
@@ -72,4 +72,8 @@
|
||||
return imapPassword;
|
||||
}
|
||||
|
||||
- (NSString *) passwordInContext: (WOContext *) context
|
||||
{
|
||||
return password;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -500,7 +500,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
ms = [NSMutableString stringWithCapacity: 256];
|
||||
[ms appendFormat: @"<0x%p[%@]: ", self, NSStringFromClass ([self class])];
|
||||
|
||||
[ms appendFormat: @" #adaptors=%d", [urlToAdaptor count]];
|
||||
[ms appendFormat: @" #adaptors=%d", (int)[urlToAdaptor count]];
|
||||
|
||||
[ms appendString: @">"];
|
||||
return ms;
|
||||
|
||||
@@ -414,7 +414,7 @@
|
||||
if (group)
|
||||
[str appendFormat: @"%@ (group: %@)\n", tag, group];
|
||||
else
|
||||
[str appendFormat: @"%@\n", tag, group];
|
||||
[str appendFormat: @"%@\n", tag];
|
||||
|
||||
[str appendString: [self versitString]];
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ static NGCardsSaxHandler *sax = nil;
|
||||
max = [children count];
|
||||
if (max > 0)
|
||||
{
|
||||
[str appendFormat: @"\n %d children: {\n", [children count]];
|
||||
[str appendFormat: @"\n %d children: {\n", (int)[children count]];
|
||||
for (count = 0; count < max; count++)
|
||||
[str appendFormat: @" %@\n",
|
||||
[[children objectAtIndex: count] description]];
|
||||
|
||||
@@ -32,16 +32,16 @@
|
||||
- (NSString *) iCalFormattedDateTimeString
|
||||
{
|
||||
return [NSString stringWithFormat: @"%.4d%.2d%.2dT%.2d%.2d%.2d",
|
||||
[self yearOfCommonEra], [self monthOfYear],
|
||||
[self dayOfMonth], [self hourOfDay],
|
||||
[self minuteOfHour], [self secondOfMinute]];
|
||||
(int)[self yearOfCommonEra], (int)[self monthOfYear],
|
||||
(int)[self dayOfMonth], (int)[self hourOfDay],
|
||||
(int)[self minuteOfHour], (int)[self secondOfMinute]];
|
||||
}
|
||||
|
||||
- (NSString *) iCalFormattedDateString
|
||||
{
|
||||
return [NSString stringWithFormat: @"%.4d%.2d%.2d",
|
||||
[self yearOfCommonEra], [self monthOfYear],
|
||||
[self dayOfMonth]];
|
||||
(int)[self yearOfCommonEra], (int)[self monthOfYear],
|
||||
(int)[self dayOfMonth]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -158,16 +158,31 @@
|
||||
NSMutableString *string;
|
||||
unsigned int len, i;
|
||||
unichar c;
|
||||
BOOL isQuoted;
|
||||
|
||||
len = [self length];
|
||||
string = [NSMutableString stringWithCapacity: len * 1.5];
|
||||
isQuoted = NO;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
c = [self characterAtIndex: i];
|
||||
|
||||
if (isQuoted)
|
||||
{
|
||||
if (c == '"')
|
||||
isQuoted = NO;
|
||||
|
||||
[string appendFormat: @"%C", c];
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
isQuoted = YES;
|
||||
[string appendFormat: @"%C", c];
|
||||
break;
|
||||
case '\\':
|
||||
[string appendString: @"\\\\"];
|
||||
break;
|
||||
|
||||
@@ -348,7 +348,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", currentPos]])
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay+1] = YES;
|
||||
currentPos++;
|
||||
}
|
||||
@@ -362,7 +362,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||
if ([byDayMask occursOnDay: currentWeekDay])
|
||||
{
|
||||
if ([bySetPos containsObject:
|
||||
[NSString stringWithFormat: @"%d", currentPos]])
|
||||
[NSString stringWithFormat: @"%d", (int)currentPos]])
|
||||
monthDays[monthDay] = YES;
|
||||
currentPos--;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@
|
||||
- (BOOL)isEqual:(id)_other {
|
||||
if(_other == nil)
|
||||
return NO;
|
||||
if([_other class] != self->isa)
|
||||
if([_other class] != object_getClass(self))
|
||||
return NO;
|
||||
if([_other hash] != [self hash])
|
||||
return NO;
|
||||
|
||||
@@ -309,10 +309,10 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR",
|
||||
else if ([frequency isEqualToString:@"SECONDLY"])
|
||||
freq = iCalRecurrenceFrequenceSecondly;
|
||||
else
|
||||
freq = NSNotFound;
|
||||
freq = (iCalRecurrenceFrequency)NSNotFound;
|
||||
}
|
||||
else
|
||||
freq = NSNotFound;
|
||||
freq = (iCalRecurrenceFrequency)NSNotFound;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1045,7 @@ static NSCharacterSet *whitespaceCharSet = nil;
|
||||
if (debugOn)
|
||||
{
|
||||
NSLog(@"%s: trying to decode data (0x%p,len=%d) ...",
|
||||
__PRETTY_FUNCTION__, _data, [_data length]);
|
||||
__PRETTY_FUNCTION__, _data, (int)[_data length]);
|
||||
}
|
||||
|
||||
if ((len = [_data length]) == 0)
|
||||
@@ -1164,7 +1164,7 @@ static NSCharacterSet *whitespaceCharSet = nil;
|
||||
if (debugOn)
|
||||
{
|
||||
NSLog(@"%s: trying to parse string (0x%p,len=%d) ...",
|
||||
__PRETTY_FUNCTION__, _source, [_source length]);
|
||||
__PRETTY_FUNCTION__, _source, (int)[_source length]);
|
||||
}
|
||||
if (!_sysId) _sysId = @"<string>";
|
||||
[self _parseString: _source];
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
@@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \
|
||||
MSExchangeFreeBusySOAPResponseMap.plist \
|
||||
MSExchangeFreeBusySOAPRequest.wo
|
||||
|
||||
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ size_t curl_body_function_freebusy(void *ptr, size_t size, size_t nmemb, void *i
|
||||
NSMutableString *s;
|
||||
|
||||
s = [NSMutableString stringWithCapacity: 64];
|
||||
[s appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
|
||||
[s appendFormat:@"<0x%08X[%@]:", (unsigned int)self, NSStringFromClass([self class])];
|
||||
if (freeBusyViewType)
|
||||
[s appendFormat:@" freeBusyViewType='%@'", freeBusyViewType];
|
||||
if (mergedFreeBusy)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"Inviting the following persons is prohibited:" = "Канењето на следните лица е забрането:";
|
||||
"Personal Calendar" = "Личен календар";
|
||||
vevent_class0 = "(Јавен настан)";
|
||||
vevent_class1 = "(Приватен настан)";
|
||||
vevent_class2 = "(Доверлив настан)";
|
||||
|
||||
vtodo_class0 = "(Јавна задача)";
|
||||
vtodo_class1 = "(Приватна задача)";
|
||||
vtodo_class2 = "(Доверлива задача)";
|
||||
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "Настанот \"%{Summary}\" беше креиран";
|
||||
"The event \"%{Summary}\" was deleted" = "Настанот \"%{Summary}\" беше избришан";
|
||||
"The event \"%{Summary}\" was updated" = "Настанот \"%{Summary}\" беше освежен";
|
||||
"The following attendees(s) were notified:" = "Следниот учесник(ци) се известени:";
|
||||
"The following attendees(s) were added:" = "Следните учесници беа додадени:";
|
||||
"The following attendees(s) were removed:" = "Следните учесници беа отстранети:";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Календар:";
|
||||
"startDate_label" = "Почеток:";
|
||||
"endDate_label" = "Крај:";
|
||||
"due_label" = "Краен датум:";
|
||||
"location_label" = "Локација:";
|
||||
"summary_label" = "Резиме:";
|
||||
"comment_label" = "Коментар:";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Покана за настанот: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(испратено од %{SentBy}) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ве покани на %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ве покани на %{Summary}.\n\nПочеток: %{StartDate} во %{StartTime}\nКрај: %{EndDate} во %{EndTime}\nОпис: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Настанот е откажан: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}ја откажа поканата: %{Summary}.\n\nПочеток: %{StartDate}\nКрај: %{EndDate}\nОпис: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}го откажа настанот: %{Summary}.\n\nПочеток: %{StartDate} во %{StartTime}\nКрај: %{EndDate} во %{EndTime}\nОпис: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "Состанокот \"%{Summary}\" за %{OldStartDate} е променет";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "Состанокот \"%{Summary}\" за %{OldStartDate} на %{OldStartTime} се промени";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Следните параметри се променија во \"%{Summary}\" состанок:";
|
||||
"Please accept or decline those changes."
|
||||
= "Прифатете ги ли отфрлете ги измените.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Прифатена покана: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Одбиена покана: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Делегирана покана: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "Уште не одлучиле по поканата: \"%{Summary}\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText}ја прифати вашата покана.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText}ја одби вашаа покана.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText}ја делегираше поканата на %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}уште нема одлучено по вашата покана.";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Не можам да ги пристапам ресурсите: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Максималниот број на едновремени закажувања (%{NumberOfSimultaneousBookings}) е постигнат за ресурсот \"%{Cn} %{SystemEmail}\". Конфликтниот настан е \"%{EventTitle}\", и почнува на %{StartDate}.";
|
||||
@@ -0,0 +1,67 @@
|
||||
"Inviting the following persons is prohibited:" = "Convidando as seguintes pessoas é proibido:";
|
||||
"Personal Calendar" = "Calendário Pessoal";
|
||||
vevent_class0 = "(Evento Público)";
|
||||
vevent_class1 = "(Evento Privado)";
|
||||
vevent_class2 = "(Evento Confidencial)";
|
||||
|
||||
vtodo_class0 = "(Tarefa Pública)";
|
||||
vtodo_class1 = "(Tarefa Privada)";
|
||||
vtodo_class2 = "(Tarefa Confidencial)";
|
||||
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
|
||||
"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
|
||||
"The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado";
|
||||
"The following attendees(s) were notified:" = "Os seguintes participantes foram notificados:";
|
||||
"The following attendees(s) were added:" = "Os seguintes participantes foram adicionados:";
|
||||
"The following attendees(s) were removed:" = "Os seguintes participantes foram removidos:";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Calendário:";
|
||||
"startDate_label" = "Início:";
|
||||
"endDate_label" = "Fim:";
|
||||
"due_label" = "Data de Vencimento:";
|
||||
"location_label" = "Local:";
|
||||
"summary_label" = "Resumo:";
|
||||
"comment_label" = "Comentário:";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(enviado por %{SentBy}) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}convidou-o para %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou-o para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}cancelou este evento: %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} às %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "O compromisso \"%{Summary}\" de %{OldStartDate} mudou";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Os seguintes parâmetros mudaram na reunião \"%{Summary}\" :\n\n";
|
||||
"Please accept or decline those changes."
|
||||
= "Por favor, aceitar ou recusar as alterações.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Convite aceite: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Convite recusado: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Convite delegado: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "Convite ainda não decidido: \"%{Summary}\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText} aceitou seu convite ao evento.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText} rejeitou seu convite ao evento.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText} delegou o convite para %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText} ainda não decidiu seu convite ao evento.";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Não foi possível aceder ao recurso: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "O número máximo de reservas simultâneas (%{NumberOfSimultaneousBookings}) acabou para o recurso \"%{Cn} %{SystemEmail}\". O evento em conflito é \"%{EventTitle}\", e inicia em %{StartDate}.";
|
||||
@@ -157,9 +157,6 @@ typedef enum {
|
||||
- (BOOL) showCalendarTasks;
|
||||
- (void) setShowCalendarTasks: (BOOL) new;
|
||||
|
||||
- (NSString *) syncTag;
|
||||
- (void) setSyncTag: (NSString *) newSyncTag;
|
||||
|
||||
- (BOOL) includeInFreeBusy;
|
||||
- (void) setIncludeInFreeBusy: (BOOL) newInclude;
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
#import "SOGoFreeBusyObject.h"
|
||||
#import "SOGoTaskObject.h"
|
||||
#import "SOGoWebAppointmentFolder.h"
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
@@ -2345,7 +2346,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
request = [context request];
|
||||
if (!([request isIPhone] || [request isICal4]))
|
||||
{
|
||||
gdRT = [self groupDavResourceType];
|
||||
gdRT = (NSArray *)[self groupDavResourceType];
|
||||
gdVEventCol = [NSArray arrayWithObjects: [gdRT objectAtIndex: 0],
|
||||
XMLNS_GROUPDAV, nil];
|
||||
[colType addObject: gdVEventCol];
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
#import <NGCards/iCalToDo.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
|
||||
#import <SOGo/SOGoConstants.h>
|
||||
@@ -747,11 +748,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
e = [events objectAtIndex: i];
|
||||
if ([e recurrenceId])
|
||||
for (j = 0; j < [theAttendees count]; j++)
|
||||
if (shouldAdd)
|
||||
for (j = 0; j < [theAttendees count]; j++) {
|
||||
if (shouldAdd) {
|
||||
[e addToAttendees: [theAttendees objectAtIndex: j]];
|
||||
else
|
||||
}
|
||||
else {
|
||||
[e removeFromAttendees: [theAttendees objectAtIndex: j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,7 +1041,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([delegateEmail length])
|
||||
otherDelegate = [event findAttendeeWithEmail: delegateEmail];
|
||||
else
|
||||
otherDelegate = NO;
|
||||
otherDelegate = nil;
|
||||
|
||||
/* we handle the addition/deletion of delegate users */
|
||||
addDelegate = NO;
|
||||
@@ -1075,7 +1079,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([delegateEmail length])
|
||||
otherDelegate = [event findAttendeeWithEmail: delegateEmail];
|
||||
else
|
||||
otherDelegate = NO;
|
||||
otherDelegate = nil;
|
||||
}
|
||||
}
|
||||
if (addDelegate)
|
||||
@@ -1232,7 +1236,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([delegateEmail length])
|
||||
otherDelegate = [event findAttendeeWithEmail: delegateEmail];
|
||||
else
|
||||
otherDelegate = NO;
|
||||
otherDelegate = nil;
|
||||
}
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
@@ -1990,7 +1994,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([container resourceNameForEventUID: eventUID])
|
||||
{
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]];
|
||||
reason: [NSString stringWithFormat: @"Event UID already in use. (%@)", eventUID]];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -140,6 +140,14 @@
|
||||
return aclManager;
|
||||
}
|
||||
|
||||
- (NSException *) changeParticipationStatus: (NSString *) newPartStat
|
||||
withDelegate: (iCalPerson *) delegate
|
||||
alarm: (iCalAlarm *) alarm
|
||||
{
|
||||
// Required for protocol <SOGoComponentOccurence>
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
@@ -171,7 +179,7 @@
|
||||
|
||||
- (Class *) parsingClass
|
||||
{
|
||||
return [iCalCalendar class];
|
||||
return (Class *)[iCalCalendar class];
|
||||
}
|
||||
|
||||
- (NSString *) davContentType
|
||||
|
||||
@@ -102,9 +102,9 @@
|
||||
}
|
||||
|
||||
if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame)
|
||||
aValue = [NSString stringWithString: @"-P"];
|
||||
aValue = (NSString *) @"-P";
|
||||
else
|
||||
aValue = [NSString stringWithString: @"P"];
|
||||
aValue = (NSString *) @"P";
|
||||
|
||||
if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame ||
|
||||
[reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@interface iCalEvent (SOGoExtensions)
|
||||
|
||||
- (BOOL) isStillRelevant;
|
||||
- (unsigned int) occurenceInterval;
|
||||
- (NSTimeInterval) occurenceInterval;
|
||||
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
|
||||
- (NSTimeInterval) occurenceInterval
|
||||
{
|
||||
return [[self endDate] timeIntervalSinceDate: [self startDate]];
|
||||
return (NSTimeInterval)[[self endDate] timeIntervalSinceDate: [self startDate]];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
|
||||
#import "iCalRepeatableEntityObject+SOGo.h"
|
||||
#import "iCalCalendar+SOGo.h"
|
||||
|
||||
@implementation iCalRepeatableEntityObject (SOGoExtensions)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
|
||||
Contacts_RESOURCE_FILES += \
|
||||
product.plist \
|
||||
|
||||
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "Лична адресна книга";
|
||||
"Collected Address Book" = "Собрана адресна книга";
|
||||
@@ -297,7 +297,7 @@ convention:
|
||||
|
||||
if (year && month && day)
|
||||
[self setBday: [NSString stringWithFormat: @"%.4d-%.2d-%.2d",
|
||||
year, month, day]];
|
||||
(int)year, (int)month, (int)day]];
|
||||
else
|
||||
[self setBday: @""];
|
||||
|
||||
@@ -614,11 +614,11 @@ convention:
|
||||
birthDay = [[self bday] asCalendarDate];
|
||||
if (birthDay)
|
||||
{
|
||||
stringValue = [NSString stringWithFormat: @"%.4d", [birthDay yearOfCommonEra]];
|
||||
stringValue = [NSString stringWithFormat: @"%.4d", (int)[birthDay yearOfCommonEra]];
|
||||
[self _setValue: @"birthyear" to: stringValue inLDIFRecord: ldifRecord];
|
||||
stringValue = [NSString stringWithFormat: @"%.2d", [birthDay monthOfYear]];
|
||||
stringValue = [NSString stringWithFormat: @"%.2d", (int)[birthDay monthOfYear]];
|
||||
[self _setValue: @"birthmonth" to: stringValue inLDIFRecord: ldifRecord];
|
||||
stringValue = [NSString stringWithFormat: @"%.2d", [birthDay dayOfMonth]];
|
||||
stringValue = [NSString stringWithFormat: @"%.2d", (int)[birthDay dayOfMonth]];
|
||||
[self _setValue: @"birthday" to: stringValue inLDIFRecord: ldifRecord];
|
||||
}
|
||||
[self _setValue: @"description" to: [self note] inLDIFRecord: ldifRecord];
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "Livro de Endereços Pessoais";
|
||||
"Collected Address Book" = "Catálogos Coleccionados";
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
[response setHeader: [self davContentType] forKey: @"content-type"];
|
||||
[response setHeader: [NSString stringWithFormat:@" %d",
|
||||
[data length]]
|
||||
(int)[data length]]
|
||||
forKey: @"content-length"];
|
||||
[response setContent: data];
|
||||
}
|
||||
|
||||
@@ -268,9 +268,9 @@ Class SOGoContactSourceFolderK;
|
||||
SOGoUser *currentUser;
|
||||
id <SOGoSource> source;
|
||||
|
||||
if ([sourceID isEqualToString: @"personal"])
|
||||
result = [NSException exceptionWithHTTPStatus: 403
|
||||
reason: (@"folder '%@' cannot be deleted", sourceID)];
|
||||
if ([sourceID isEqualToString: @"personal"]){
|
||||
result = [NSException exceptionWithHTTPStatus: 403 reason: [NSString stringWithFormat: (@"folder '%@' cannot be deleted"), sourceID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
result = nil;
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
- (Class *) parsingClass
|
||||
{
|
||||
return [NGVCard class];
|
||||
return (Class *)[NGVCard class];
|
||||
}
|
||||
|
||||
/* content */
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
- (Class *) parsingClass
|
||||
{
|
||||
return [NGVList class];
|
||||
return (Class *)[NGVList class];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#import <SOGo/NSObject+DAV.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
#import <SOGo/SOGoSource.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
@@ -95,7 +96,7 @@
|
||||
{
|
||||
if (![newDisplayName length])
|
||||
newDisplayName = newName;
|
||||
ASSIGN (displayName, newDisplayName);
|
||||
ASSIGN (displayName, (NSMutableString *)newDisplayName);
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -225,8 +226,16 @@
|
||||
NSObject <SOGoSource> *recordSource;
|
||||
|
||||
newRecord = [NSMutableDictionary dictionaryWithCapacity: 8];
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_uid"]
|
||||
forKey: @"c_uid"];
|
||||
|
||||
// We set the c_uid only for authentication sources. SOGoUserSources set
|
||||
// with canAuthenticate = NO and isAddressBook = YES have absolutely *NO REASON*
|
||||
// to have entries with a c_uid. These can collide with real uids.
|
||||
if ([[[[SOGoUserManager sharedUserManager] metadataForSourceID: [source sourceID]] objectForKey: @"canAuthenticate"] boolValue])
|
||||
{
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_uid"]
|
||||
forKey: @"c_uid"];
|
||||
}
|
||||
|
||||
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
|
||||
forKey: @"c_name"];
|
||||
|
||||
@@ -665,7 +674,7 @@
|
||||
BOOL otherIsPersonal;
|
||||
|
||||
otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]]
|
||||
|| ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource]));
|
||||
|| ([otherFolder isKindOfClass: object_getClass(self)] && [otherFolder isPersonalSource]));
|
||||
|
||||
if (isPersonalSource)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* NSObject+CardDAV.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <ludovic@inverse.ca>
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* NSObject+CardDAV.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2011 Inverse inc.
|
||||
*
|
||||
* Author: Ludovic Marcotte <ludovic@inverse.ca>
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -187,6 +185,10 @@
|
||||
[filters addObject: filter];
|
||||
}
|
||||
|
||||
// If no filters are provided, we return everything.
|
||||
if (![filters count])
|
||||
[filters addObject: [NSDictionary dictionaryWithObject: @"." forKey: @"mail"]];
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,12 +72,16 @@ Mailer_RESOURCE_FILES += \
|
||||
SOGoMailIcelandicReply.wo \
|
||||
SOGoMailItalianForward.wo \
|
||||
SOGoMailItalianReply.wo \
|
||||
SOGoMailMacedonianForward.wo \
|
||||
SOGoMailMacedonianReply.wo \
|
||||
SOGoMailNorwegianBokmalForward.wo \
|
||||
SOGoMailNorwegianBokmalReply.wo \
|
||||
SOGoMailNorwegianNynorskForward.wo \
|
||||
SOGoMailNorwegianNynorskReply.wo \
|
||||
SOGoMailPolishForward.wo \
|
||||
SOGoMailPolishReply.wo \
|
||||
SOGoMailPortugueseForward.wo \
|
||||
SOGoMailPortugueseReply.wo \
|
||||
SOGoMailRussianForward.wo \
|
||||
SOGoMailRussianReply.wo \
|
||||
SOGoMailSlovakForward.wo \
|
||||
@@ -95,7 +99,7 @@ Mailer_RESOURCE_FILES += \
|
||||
SOGoMailWelshForward.wo \
|
||||
SOGoMailWelshReply.wo
|
||||
|
||||
Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"OtherUsersFolderName" = "Други корисници";
|
||||
"SharedFoldersName" = "Делени папки";
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import "NSDictionary+Mail.h"
|
||||
|
||||
|
||||
@@ -533,7 +533,7 @@
|
||||
messageID = [NSMutableString string];
|
||||
[messageID appendFormat: @"<%@", [SOGoObject globallyUniqueObjectId]];
|
||||
pGUID = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||
[messageID appendFormat: @"@%u>", [pGUID hash]];
|
||||
[messageID appendFormat: @"@%u>", (unsigned int)[pGUID hash]];
|
||||
|
||||
return [messageID lowercaseString];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"OtherUsersFolderName" = "Outros Utilizadores";
|
||||
"SharedFoldersName" = "Pastas Partilhadas";
|
||||
@@ -179,6 +179,7 @@ static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc",
|
||||
|
||||
static NGMimeType *MultiMixedType = nil;
|
||||
static NGMimeType *MultiAlternativeType = nil;
|
||||
static NGMimeType *MultiRelatedType = nil;
|
||||
static NSString *userAgent = nil;
|
||||
|
||||
+ (void) initialize
|
||||
@@ -189,6 +190,9 @@ static NSString *userAgent = nil;
|
||||
MultiAlternativeType = [NGMimeType mimeType: @"multipart" subType: @"alternative"];
|
||||
[MultiAlternativeType retain];
|
||||
|
||||
MultiRelatedType = [NGMimeType mimeType: @"multipart" subType: @"related"];
|
||||
[MultiRelatedType retain];
|
||||
|
||||
userAgent = [NSString stringWithFormat: @"SOGoMail %@",
|
||||
SOGoVersion];
|
||||
[userAgent retain];
|
||||
@@ -699,7 +703,8 @@ static NSString *userAgent = nil;
|
||||
for (count = max - 1; count >= 0; count--)
|
||||
{
|
||||
currentAddress = [addresses objectAtIndex: count];
|
||||
if ([currentRecipient
|
||||
if (![currentAddress baseEMail] ||
|
||||
[currentRecipient
|
||||
caseInsensitiveCompare: [currentAddress baseEMail]]
|
||||
== NSOrderedSame)
|
||||
{
|
||||
@@ -1678,16 +1683,20 @@ static NSString *userAgent = nil;
|
||||
NGMimeMessage *message;
|
||||
NGMutableHashMap *map;
|
||||
NSString *newText;
|
||||
BOOL has_inline_images;
|
||||
|
||||
message = nil;
|
||||
|
||||
has_inline_images = NO;
|
||||
bodyParts = [NSMutableArray array];
|
||||
|
||||
if (_extractImages)
|
||||
{
|
||||
newText = [text htmlByExtractingImages: bodyParts];
|
||||
if ([bodyParts count])
|
||||
[self setText: newText];
|
||||
{
|
||||
[self setText: newText];
|
||||
has_inline_images = YES;
|
||||
}
|
||||
}
|
||||
|
||||
map = [self mimeHeaderMapWithHeaders: _headers
|
||||
@@ -1704,10 +1713,20 @@ static NSString *userAgent = nil;
|
||||
/* no attachments */
|
||||
message = [self mimeMessageForContentWithHeaderMap: map];
|
||||
else
|
||||
/* attachments, create multipart/mixed */
|
||||
message = [self mimeMultiPartMessageWithHeaderMap: map
|
||||
andBodyParts: bodyParts];
|
||||
//[self debugWithFormat: @"message: %@", message];
|
||||
{
|
||||
// attachments, create multipart/mixed or multipart/related if
|
||||
// we have inline image to avoid Thunderbird bug #61815 (https://bugzilla.mozilla.org/show_bug.cgi?id=61815)
|
||||
if (has_inline_images)
|
||||
{
|
||||
[map removeAllObjectsForKey: @"content-type"];
|
||||
[map addObject: MultiRelatedType forKey: @"content-type"];
|
||||
}
|
||||
|
||||
message = [self mimeMultiPartMessageWithHeaderMap: map
|
||||
andBodyParts: bodyParts];
|
||||
|
||||
//[self debugWithFormat: @"message: %@", message];
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSURL+misc.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
|
||||
#import "SOGoMailAccount.h"
|
||||
#import "SOGoMailManager.h"
|
||||
|
||||
@@ -396,7 +396,7 @@ static BOOL debugOn = NO;
|
||||
mimeType = @"application/octet-stream";
|
||||
|
||||
[response setHeader: mimeType forKey: @"content-type"];
|
||||
[response setHeader: [NSString stringWithFormat:@"%d", [data length]]
|
||||
[response setHeader: [NSString stringWithFormat:@"%d", (int)[data length]]
|
||||
forKey: @"content-length"];
|
||||
|
||||
if (asAttachment)
|
||||
|
||||
@@ -1645,7 +1645,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
|
||||
sortOrderings = [NSMutableArray array];
|
||||
|
||||
if ([self _sortElementIsAscending: sortElement])
|
||||
if ([self _sortElementIsAscending: (NGDOMNodeWithChildren <DOMElement> *)sortElement])
|
||||
sortOrderingOrder = EOCompareAscending;
|
||||
else
|
||||
sortOrderingOrder = EOCompareDescending;
|
||||
@@ -2077,7 +2077,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
// * 5 FETCH (UID 559 MODSEQ (17))
|
||||
// * 6 FETCH (UID 560 MODSEQ (18))
|
||||
// * 7 FETCH (UID 561 MODSEQ (19))
|
||||
|
||||
//
|
||||
//
|
||||
// fetchUIDsOfVanishedItems ..
|
||||
//
|
||||
|
||||
@@ -82,6 +82,9 @@
|
||||
@interface SOGoMailItalianForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailMacedonianForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailNorwegianBokmalForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@@ -100,6 +103,9 @@
|
||||
@interface SOGoMailPolishForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailPortugueseForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailRussianForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
|
||||
@@ -73,10 +73,10 @@
|
||||
|
||||
- (NSString *) newLine
|
||||
{
|
||||
NSString *rc = [NSString stringWithString: @" "];
|
||||
NSString *rc = @" ";
|
||||
|
||||
if (htmlComposition)
|
||||
rc = [NSString stringWithString: @"<br/>"];
|
||||
rc = @"<br/>";
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -293,6 +293,9 @@
|
||||
@implementation SOGoMailItalianForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailMacedonianForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailNorwegianBokmalForward
|
||||
@end
|
||||
|
||||
@@ -311,6 +314,9 @@
|
||||
@implementation SOGoMailPolishForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailPortugueseForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailRussianForward
|
||||
@end
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
int i;
|
||||
|
||||
allLabels = [NSMutableArray array];
|
||||
allKeys = [[theDefaults allKeys] sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)];
|
||||
allKeys = (NSMutableArray *)[[theDefaults allKeys] sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)];
|
||||
|
||||
for (i = 0; i < [allKeys count]; i++)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<#newLine/>
|
||||
<#newLine/>
|
||||
<#signaturePlacementOnTop><#newLine/>
|
||||
<#signature/><#newLine/></#signaturePlacementOnTop>
|
||||
-------- Оригинална порака --------<#newLine/>
|
||||
Тема: <#subject/><#newLine/>
|
||||
Датум: <#date/><#newLine/>
|
||||
Од: <#from/><#newLine/>
|
||||
<#hasReplyTo>Reply-To: <#replyTo/></#hasReplyTo><#hasOrganization>Organization: <#organization/></#hasOrganization>To: <#to/><#newLine/>
|
||||
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences><#newLine/>
|
||||
<#newLine/>
|
||||
<#messageBody/><#newLine/>
|
||||
<#signaturePlacementOnBottom><#newLine/>
|
||||
<#newLine/>
|
||||
<#signature/></#signaturePlacementOnBottom>
|
||||
<#newLine/>
|
||||
@@ -0,0 +1,88 @@
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<#replyPlacementOnTop><#newLine/>
|
||||
<#newLine/>
|
||||
</#replyPlacementOnTop><#signaturePlacementOnTop><#newLine/>
|
||||
<#signature/><#newLine/>
|
||||
</#signaturePlacementOnTop><#outlookMode>-------- Оригинална порака --------<#newLine/>
|
||||
Тема: <#subject/><#newLine/>
|
||||
Датум: <#date/><#newLine/>
|
||||
Од: <#from/><#newLine/>
|
||||
<#hasReplyTo>Reply-To: <#replyTo/></#hasReplyTo><#hasOrganization>Organization: <#organization/></#hasOrganization>To: <#to/><#newLine/>
|
||||
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>References: <#references/></#hasReferences></#outlookMode><#newLine/>
|
||||
<#standardMode>On <#date/>, <#from/> wrote:</#standardMode><#newLine/>
|
||||
<#newLine/>
|
||||
<#messageBody/><#newLine/>
|
||||
<#replyPlacementOnBottom><#newLine/>
|
||||
<#newLine/>
|
||||
</#replyPlacementOnBottom><#signaturePlacementOnBottom><#signature/></#signaturePlacementOnBottom><#newLine/>
|
||||
@@ -0,0 +1,106 @@
|
||||
outlookMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
}
|
||||
|
||||
standardMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
replyPlacementOnTop: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
}
|
||||
|
||||
replyPlacementOnBottom: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -389,6 +389,13 @@ static BOOL debugSoParts = NO;
|
||||
[[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"calendar"])
|
||||
return info;
|
||||
|
||||
// deal with mails that contain only an attachment, for example:
|
||||
// application/pkcs7-mime
|
||||
// application/pdf
|
||||
// etc.
|
||||
if ([[[info valueForKey: @"type"] lowercaseString] isEqualToString: @"application"])
|
||||
return info;
|
||||
|
||||
/*
|
||||
For each path component, eg 1,1,3
|
||||
|
||||
@@ -766,6 +773,7 @@ static BOOL debugSoParts = NO;
|
||||
[part objectForKey: @"subtype"]];
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
// We might end up here because of MUA that actually strips the
|
||||
// Content-Disposition (and thus, the filename) when mails containing
|
||||
// attachments have been forwarded. Thunderbird (2.x) does just that
|
||||
@@ -774,9 +782,15 @@ static BOOL debugSoParts = NO;
|
||||
[mimeType hasPrefix: @"audio/"] ||
|
||||
[mimeType hasPrefix: @"image/"] ||
|
||||
[mimeType hasPrefix: @"video/"])
|
||||
{
|
||||
filename = [NSString stringWithFormat: @"unknown_%@", path];
|
||||
else if ([mimeType isEqualToString: @"message/rfc822"])
|
||||
filename = [NSString stringWithFormat: @"email_%@.eml", path];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([mimeType isEqualToString: @"message/rfc822"])
|
||||
filename = [NSString stringWithFormat: @"email_%@.eml", path];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (filename)
|
||||
@@ -809,7 +823,7 @@ static BOOL debugSoParts = NO;
|
||||
NSMutableDictionary *currentPart;
|
||||
NSString *newPath;
|
||||
NSArray *subparts;
|
||||
NSString *type;
|
||||
NSString *type, *subtype;
|
||||
NSUInteger i;
|
||||
|
||||
type = [[part objectForKey: @"type"] lowercaseString];
|
||||
@@ -820,19 +834,27 @@ static BOOL debugSoParts = NO;
|
||||
{
|
||||
currentPart = [subparts objectAtIndex: i-1];
|
||||
if (path)
|
||||
newPath = [NSString stringWithFormat: @"%@.%d", path, i];
|
||||
newPath = [NSString stringWithFormat: @"%@.%d", path, (int)i];
|
||||
else
|
||||
newPath = [NSString stringWithFormat: @"%d", i];
|
||||
newPath = [NSString stringWithFormat: @"%d", (int)i];
|
||||
[self _fetchFileAttachmentKeysInPart: currentPart
|
||||
intoArray: keys
|
||||
withPath: newPath
|
||||
andPrefix: [NSString stringWithFormat: @"%@/%i", prefix, i]];
|
||||
andPrefix: [NSString stringWithFormat: @"%@/%i", prefix, (int)i]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path)
|
||||
path = @"1";
|
||||
{
|
||||
path = @"1";
|
||||
|
||||
// We set the path to 0 in case of a smime mail if not provided.
|
||||
subtype = [[part objectForKey: @"subtype"] lowercaseString];
|
||||
if ([subtype isEqualToString: @"pkcs7-mime"])
|
||||
path = @"0";
|
||||
}
|
||||
|
||||
[self _fetchFileAttachmentKey: part
|
||||
intoArray: keys
|
||||
withPath: path
|
||||
@@ -1026,6 +1048,14 @@ static BOOL debugSoParts = NO;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
// Handles cases where the email is itself an attachment, so its Content-Type
|
||||
// is application/*, image/* etc.
|
||||
else if ([_key isEqualToString: @"asAttachment"] &&
|
||||
(obj = [self lookupImap4BodyPartKey: @"0" inContext:_ctx]) != nil)
|
||||
{
|
||||
[obj setAsAttachment];
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* return 404 to stop acquisition */
|
||||
return [NSException exceptionWithHTTPStatus:404 /* Not Found */
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<#newLine/>
|
||||
<#newLine/>
|
||||
<#signaturePlacementOnTop><#newLine/>
|
||||
<#signature/><#newLine/></#signaturePlacementOnTop>
|
||||
-------- Mensagem Original --------<#newLine/>
|
||||
Assunto: <#subject/><#newLine/>
|
||||
Data: <#date/><#newLine/>
|
||||
De: <#from/><#newLine/>
|
||||
<#hasReplyTo>Responder-Para: <#replyTo/></#hasReplyTo><#hasOrganization>Organização: <#organization/></#hasOrganization>Para: <#to/><#newLine/>
|
||||
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>Referências: <#references/></#hasReferences><#newLine/>
|
||||
<#newLine/>
|
||||
<#messageBody/><#newLine/>
|
||||
<#signaturePlacementOnBottom><#newLine/>
|
||||
<#newLine/>
|
||||
<#signature/></#signaturePlacementOnBottom>
|
||||
<#newLine/>
|
||||
@@ -0,0 +1,88 @@
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<#signaturePlacementOnTop>
|
||||
|
||||
|
||||
<#signature/>
|
||||
</#signaturePlacementOnTop>
|
||||
<#outlookMode>-------- Mensagem Original --------
|
||||
Assunto: <#subject/>
|
||||
Data: <#date/>
|
||||
De: <#from/>
|
||||
<#hasReplyTo>Responder-Para: <#replyTo/></#hasReplyTo><#hasOrganization>Organização: <#organization/></#hasOrganization>Para: <#to/>
|
||||
<#hasCc>CC: <#cc/></#hasCc><#hasNewsGroups>Newsgroups: <#newsgroups/></#hasNewsGroups><#hasReferences>Referências: <#references/></#hasReferences></#outlookMode>
|
||||
<#standardMode>Em <#date/>, <#from/> escreveu:</#standardMode>
|
||||
|
||||
<#messageBody/>
|
||||
|
||||
|
||||
<#signaturePlacementOnBottom><#signature/></#signaturePlacementOnBottom>
|
||||
@@ -0,0 +1,106 @@
|
||||
outlookMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
}
|
||||
|
||||
standardMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
replyPlacementOnTop: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
}
|
||||
|
||||
replyPlacementOnBottom: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -81,6 +81,9 @@
|
||||
@interface SOGoMailItalianReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailMacedonianReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailNorwegianBokmalReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@@ -99,6 +102,9 @@
|
||||
@interface SOGoMailPolishReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailPortugueseReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailRussianReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@
|
||||
@implementation SOGoMailItalianReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailMacedonianReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailNorwegianBokmalReply
|
||||
@end
|
||||
|
||||
@@ -149,6 +152,9 @@
|
||||
@implementation SOGoMailPolishReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailPortugueseReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailRussianReply
|
||||
@end
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@ static NSDictionary *BSONTypes()
|
||||
|
||||
case 'q': return 0x12;
|
||||
default:
|
||||
[NSException raise: NSInvalidArgumentException format: @"%@::%s - invalid encoding type '%c'", [self class], _cmd, encoding];
|
||||
[NSException raise: NSInvalidArgumentException format: @"%@::%@ - invalid encoding type '%c'", [self class], NSStringFromSelector(_cmd), encoding];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -385,7 +385,7 @@ static NSDictionary *BSONTypes()
|
||||
}
|
||||
|
||||
|
||||
[NSException raise: NSInvalidArgumentException format: @"%@::%s - invalid encoding type '%c'", [self class], _cmd, encoding];
|
||||
[NSException raise: NSInvalidArgumentException format: @"%@::%@ - invalid encoding type '%c'", [self class], NSStringFromSelector(_cmd), encoding];
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -1703,7 +1703,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
|
||||
hostname: hostname
|
||||
port: [NSString stringWithFormat: @"%d", port]
|
||||
encryption: encryption
|
||||
bindAsCurrentUser: NO];
|
||||
bindAsCurrentUser: [NSString stringWithFormat: @"%d", NO]];
|
||||
[ab setBaseDN: [entry dn]
|
||||
IDField: @"cn"
|
||||
CNField: @"displayName"
|
||||
|
||||
@@ -88,9 +88,9 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
|
||||
NSString *str;
|
||||
|
||||
str = [NSString stringWithFormat: @"%.4d%.2d%.2d",
|
||||
[self yearOfCommonEra],
|
||||
[self monthOfYear],
|
||||
[self dayOfMonth]];
|
||||
(int)[self yearOfCommonEra],
|
||||
(int)[self monthOfYear],
|
||||
(int)[self dayOfMonth]];
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -107,9 +107,9 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
|
||||
|
||||
return
|
||||
[NSString stringWithFormat: @"%@, %.2d %@ %d %.2d:%.2d:%.2d %+.4d",
|
||||
rfc822Days[[self dayOfWeek]], [self dayOfMonth],
|
||||
rfc822Months[[self monthOfYear]], [self yearOfCommonEra],
|
||||
[self hourOfDay], [self minuteOfHour], [self secondOfMinute],
|
||||
rfc822Days[[self dayOfWeek]], (int)[self dayOfMonth],
|
||||
rfc822Months[[self monthOfYear]], (int)[self yearOfCommonEra],
|
||||
(int)[self hourOfDay], (int)[self minuteOfHour], (int)[self secondOfMinute],
|
||||
timeZoneShift];
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OpenBSD__
|
||||
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
|
||||
#include <crypt.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
{
|
||||
NSString *newTag;
|
||||
|
||||
newTag = [NSString stringWithFormat: @"n%d", [namespaces count]];
|
||||
newTag = [NSString stringWithFormat: @"n%d", (int)[namespaces count]];
|
||||
[namespaces setObject: newTag forKey: newNS];
|
||||
|
||||
return newTag;
|
||||
|
||||
@@ -309,7 +309,7 @@ static int cssEscapingCount;
|
||||
c == 0xD ||
|
||||
(c >= 0x20 && c <= 0xD7FF) ||
|
||||
(c >= 0xE000 && c <= 0xFFFD) ||
|
||||
(c >= 0x10000 && c <= 0x10FFFF))
|
||||
(c >= (unichar)0x10000 && c <= (unichar)0x10FFFF))
|
||||
{
|
||||
*(start+j) = c;
|
||||
j++;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef SOGOCACHE_H
|
||||
#define SOGOCACHE_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#include <libmemcached/memcached.h>
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
- (NSMutableString *) pathForChild: (NSString *) childName;
|
||||
|
||||
- (void) addUserInAcls: (NSString *) user;
|
||||
|
||||
- (NSArray *) toOneRelationshipKeys;
|
||||
- (NSArray *) toManyRelationshipKeys;
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@ Class SOGoCacheGCSObjectK = Nil;
|
||||
if (record)
|
||||
{
|
||||
if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject)
|
||||
objectClass = isa;
|
||||
objectClass = object_getClass(self);
|
||||
else
|
||||
objectClass = SOGoCacheGCSObjectK;
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ typedef enum {
|
||||
|
||||
- (void) reloadIfNeeded;
|
||||
- (void) save;
|
||||
- (NSException *) destroy;
|
||||
|
||||
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache;
|
||||
|
||||
/* accessors */
|
||||
- (NSMutableString *) path; /* full filename */
|
||||
|
||||
@@ -87,7 +87,7 @@ static EOAttribute *textColumn = nil;
|
||||
{
|
||||
tableUrl = nil;
|
||||
initialized = NO;
|
||||
objectType = -1;
|
||||
objectType = (SOGoCacheObjectType) -1;
|
||||
deleted = NO;
|
||||
version = 0;
|
||||
}
|
||||
@@ -103,11 +103,22 @@ static EOAttribute *textColumn = nil;
|
||||
}
|
||||
|
||||
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer
|
||||
{
|
||||
return [self objectWithName: key
|
||||
inContainer: theContainer
|
||||
useCache: YES];
|
||||
}
|
||||
|
||||
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache
|
||||
{
|
||||
SOGoCache *cache;
|
||||
id o;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
|
||||
if (!useCache)
|
||||
[cache unregisterObjectWithName: key inContainer: theContainer];
|
||||
|
||||
o = [cache objectNamed: key inContainer: theContainer];
|
||||
|
||||
if (!o)
|
||||
@@ -383,7 +394,7 @@ static EOAttribute *textColumn = nil;
|
||||
@"SELECT * FROM %@ WHERE c_path = %@",
|
||||
tableName, pathValue];
|
||||
if (startVersion > -1)
|
||||
[sql appendFormat: @" AND c_version > %d", startVersion];
|
||||
[sql appendFormat: @" AND c_version > %d", (int)startVersion];
|
||||
|
||||
/* execution */
|
||||
records = [self performSQLQuery: sql];
|
||||
@@ -411,15 +422,13 @@ static EOAttribute *textColumn = nil;
|
||||
|
||||
tableName = [self tableName];
|
||||
adaptor = [self tableChannelAdaptor];
|
||||
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@", deviceId]
|
||||
forAttribute: textColumn];
|
||||
|
||||
/* query */
|
||||
sql = [NSMutableString stringWithFormat:
|
||||
@"SELECT * FROM %@ WHERE c_type = %d AND c_deleted <> 1", tableName, objectType];
|
||||
|
||||
if (startVersion > -1)
|
||||
[sql appendFormat: @" AND c_version > %d", startVersion];
|
||||
[sql appendFormat: @" AND c_version > %d", (int)startVersion];
|
||||
|
||||
if (deviceId) {
|
||||
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@%", deviceId]
|
||||
@@ -546,7 +555,7 @@ static EOAttribute *textColumn = nil;
|
||||
|
||||
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
|
||||
|
||||
if (objectType == -1)
|
||||
if (objectType == (SOGoCacheObjectType) -1)
|
||||
[NSException raise: @"SOGoCacheIOException"
|
||||
format: @"object type has not been set for object '%@'",
|
||||
self];
|
||||
@@ -576,7 +585,7 @@ static EOAttribute *textColumn = nil;
|
||||
@")"),
|
||||
tableName,
|
||||
pathValue, parentPathValue, objectType,
|
||||
creationDateValue, lastModifiedValue,
|
||||
(int)creationDateValue, (int)lastModifiedValue,
|
||||
propsValue];
|
||||
isNew = NO;
|
||||
}
|
||||
@@ -590,7 +599,7 @@ static EOAttribute *textColumn = nil;
|
||||
@" c_version = %d, c_content = %@"
|
||||
@" WHERE c_path = %@"),
|
||||
tableName,
|
||||
lastModifiedValue, deletedValue, version, propsValue,
|
||||
(int)lastModifiedValue, (int)deletedValue, (int)version, propsValue,
|
||||
pathValue];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@
|
||||
- (NSCalendarDate *) creationDate;
|
||||
- (NSCalendarDate *) lastModified;
|
||||
|
||||
- (NSException *) destroy;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCACHEOBJECT_H */
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
return (Class *) nil;
|
||||
}
|
||||
|
||||
- (void) _setRecord: (NSDictionary *) objectRecord
|
||||
@@ -254,6 +254,9 @@
|
||||
- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
// TODO: Add exception handling code and return it
|
||||
return (NSException *) nil;
|
||||
}
|
||||
|
||||
- (NSException *) delete
|
||||
@@ -345,7 +348,7 @@
|
||||
|
||||
/* attempt a save */
|
||||
|
||||
error = [self saveComponent: [[self parsingClass] parseSingleFromSource: [rq contentAsString]]
|
||||
error = [self saveComponent: [(id)[self parsingClass] parseSingleFromSource: [rq contentAsString]]
|
||||
baseVersion: baseVersion];
|
||||
if (error)
|
||||
response = (WOResponse *) error;
|
||||
@@ -410,7 +413,7 @@
|
||||
else
|
||||
length = 0;
|
||||
|
||||
return [NSString stringWithFormat: @"%u", length];
|
||||
return [NSString stringWithFormat: @"%u", (unsigned int)length];
|
||||
}
|
||||
|
||||
- (NSException *) davMoveToTargetObject: (id) _target
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
SOGoLanguage = "English";
|
||||
SOGoSupportedLanguages = ( "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czech", "Dutch", "Danish", "Welsh", "English",
|
||||
"SpanishSpain", "SpanishArgentina", "Finnish", "French", "German",
|
||||
"Icelandic", "Italian", "Hungarian", "BrazilianPortuguese",
|
||||
"Icelandic", "Italian", "Macedonian", "Hungarian", "Portuguese", "BrazilianPortuguese",
|
||||
"NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak",
|
||||
"Slovenian", "Ukrainian", "Swedish" );
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
- (void) setDisplayName: (NSString *) newDisplayName
|
||||
{
|
||||
ASSIGN (displayName, newDisplayName);
|
||||
ASSIGN (displayName, (NSMutableString *)newDisplayName);
|
||||
}
|
||||
|
||||
- (NSString *) displayName
|
||||
|
||||
@@ -399,12 +399,12 @@ static NSArray *childRecordFields = nil;
|
||||
if (!displayName)
|
||||
{
|
||||
if (activeUserIsOwner)
|
||||
displayName = [self _displayNameFromOwner];
|
||||
displayName = (NSMutableString *)[self _displayNameFromOwner];
|
||||
else
|
||||
{
|
||||
displayName = [self _displayNameFromSubscriber];
|
||||
displayName = (NSMutableString *)[self _displayNameFromSubscriber];
|
||||
if (!displayName)
|
||||
displayName = [self _displayNameFromOwner];
|
||||
displayName = (NSMutableString *)[self _displayNameFromOwner];
|
||||
}
|
||||
[displayName retain];
|
||||
}
|
||||
@@ -852,7 +852,7 @@ static NSArray *childRecordFields = nil;
|
||||
{
|
||||
currentID = [ids objectAtIndex: count];
|
||||
names = [[currentID componentsSeparatedByString: @"/"] objectEnumerator];
|
||||
deleteObject = self;
|
||||
deleteObject = (SOGoContentObject *)self;
|
||||
while ((currentName = [names nextObject]))
|
||||
{
|
||||
deleteObject = [deleteObject lookupName: currentName
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
{
|
||||
NSArray *allSources;
|
||||
NGLdapEntry *entry;
|
||||
NSObject <SOGoSource> *source;
|
||||
NSObject <SOGoSource, SOGoDNSource> *source;
|
||||
id o;
|
||||
NSEnumerator *gclasses;
|
||||
NSString *gclass;
|
||||
@@ -154,7 +154,7 @@
|
||||
|
||||
for (i = 0; i < [allSources count]; i++)
|
||||
{
|
||||
source = [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]];
|
||||
source = (NSObject <SOGoSource, SOGoDNSource> *) [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]];
|
||||
|
||||
// Our different sources might not all implements groups support
|
||||
if ([source respondsToSelector: theSelector])
|
||||
|
||||
@@ -1069,7 +1069,7 @@
|
||||
[_ms appendFormat:@" name=%@", nameInContainer];
|
||||
if (container)
|
||||
[_ms appendFormat:@" container=0x%08X/%@",
|
||||
container, [container valueForKey:@"nameInContainer"]];
|
||||
(unsigned int)container, [container valueForKey:@"nameInContainer"]];
|
||||
}
|
||||
|
||||
- (NSString *) description
|
||||
@@ -1077,7 +1077,7 @@
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:64];
|
||||
[ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
|
||||
[ms appendFormat:@"<0x%08X[%@]:", (unsigned int) self, NSStringFromClass([self class])];
|
||||
[self appendAttributesToDescription:ms];
|
||||
[ms appendString:@">"];
|
||||
|
||||
@@ -1087,7 +1087,7 @@
|
||||
- (NSString *) loggingPrefix
|
||||
{
|
||||
return [NSString stringWithFormat:@"<0x%08X[%@]:%@>",
|
||||
self, NSStringFromClass([self class]),
|
||||
(unsigned int) self, NSStringFromClass([self class]),
|
||||
[self nameInContainer]];
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user