Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7862cda661 | ||
|
|
329552030b | ||
|
|
04904d8c61 | ||
|
|
f0b4a05c70 | ||
|
|
95d6911f1a | ||
|
|
48f98ef34e | ||
|
|
ae0c5eae77 | ||
|
|
0c2d727057 | ||
|
|
072f5f5a15 | ||
|
|
1db85743a0 | ||
|
|
2cf66e037d | ||
|
|
449d838dc2 | ||
|
|
c4bc215a47 | ||
|
|
c9936d7ecf | ||
|
|
885e59a2af | ||
|
|
bf398889bf | ||
|
|
e26e3d7307 | ||
|
|
045b275b27 | ||
|
|
7ec1973e0f | ||
|
|
a79a9c4eff | ||
|
|
f049e2430a | ||
|
|
59b3219f2d | ||
|
|
e9eb8bdc86 | ||
|
|
5e0c7ae4f2 | ||
|
|
c1c0c1ab44 | ||
|
|
a59fff785b | ||
|
|
6a70db80e6 | ||
|
|
d610630c8d | ||
|
|
4dc7790015 | ||
|
|
9dd000a2ca | ||
|
|
ce4084e528 |
No files matched your search
-33
@@ -12,7 +12,6 @@ trans.da_DK = UI/MailerUI/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/MailerUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MailerUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MailerUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MailerUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MailerUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MailerUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MailerUI/Hungarian.lproj/Localizable.strings
|
||||
@@ -25,10 +24,8 @@ trans.pl = UI/MailerUI/Polish.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
|
||||
trans.sl_SI = UI/MailerUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-preferencesui]
|
||||
source_file = UI/PreferencesUI/English.lproj/Localizable.strings
|
||||
@@ -41,7 +38,6 @@ trans.da_DK = UI/PreferencesUI/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/PreferencesUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/PreferencesUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/PreferencesUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/PreferencesUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/PreferencesUI/Hungarian.lproj/Localizable.strings
|
||||
@@ -54,10 +50,8 @@ trans.pl = UI/PreferencesUI/Polish.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
|
||||
trans.sl_SI = UI/PreferencesUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-scheduler]
|
||||
source_file = UI/Scheduler/English.lproj/Localizable.strings
|
||||
@@ -70,7 +64,6 @@ trans.da_DK = UI/Scheduler/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/Scheduler/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Scheduler/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Scheduler/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Scheduler/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Scheduler/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Scheduler/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Scheduler/Hungarian.lproj/Localizable.strings
|
||||
@@ -83,10 +76,8 @@ trans.pl = UI/Scheduler/Polish.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
|
||||
trans.sl_SI = UI/Scheduler/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-contacts]
|
||||
source_file = UI/Contacts/English.lproj/Localizable.strings
|
||||
@@ -99,7 +90,6 @@ trans.da_DK = UI/Contacts/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/Contacts/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Contacts/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Contacts/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Contacts/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Contacts/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Contacts/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Contacts/Hungarian.lproj/Localizable.strings
|
||||
@@ -112,10 +102,8 @@ trans.pl = UI/Contacts/Polish.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
|
||||
trans.sl_SI = UI/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Contacts/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mainui]
|
||||
source_file = UI/MainUI/English.lproj/Localizable.strings
|
||||
@@ -128,7 +116,6 @@ trans.da_DK = UI/MainUI/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/MainUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MainUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MainUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MainUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MainUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MainUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MainUI/Hungarian.lproj/Localizable.strings
|
||||
@@ -141,10 +128,8 @@ trans.pl = UI/MainUI/Polish.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
|
||||
trans.sl_SI = UI/MainUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MainUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-common]
|
||||
source_file = UI/Common/English.lproj/Localizable.strings
|
||||
@@ -157,7 +142,6 @@ trans.da_DK = UI/Common/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/Common/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Common/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Common/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Common/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Common/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Common/French.lproj/Localizable.strings
|
||||
trans.hu = UI/Common/Hungarian.lproj/Localizable.strings
|
||||
@@ -170,10 +154,8 @@ trans.pl = UI/Common/Polish.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
|
||||
trans.sl_SI = UI/Common/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/Common/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Common/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-administrationui]
|
||||
source_file = UI/AdministrationUI/English.lproj/Localizable.strings
|
||||
@@ -186,7 +168,6 @@ trans.da_DK = UI/AdministrationUI/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/AdministrationUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/AdministrationUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/AdministrationUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/AdministrationUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/AdministrationUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/AdministrationUI/French.lproj/Localizable.strings
|
||||
trans.hu = UI/AdministrationUI/Hungarian.lproj/Localizable.strings
|
||||
@@ -199,10 +180,8 @@ trans.pl = UI/AdministrationUI/Polish.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
|
||||
trans.sl_SI = UI/AdministrationUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/AdministrationUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-appointments]
|
||||
source_file = SoObjects/Appointments/English.lproj/Localizable.strings
|
||||
@@ -215,7 +194,6 @@ trans.da_DK = SoObjects/Appointments/Danish.lproj/Localizable.strings
|
||||
trans.de = SoObjects/Appointments/German.lproj/Localizable.strings
|
||||
trans.es_AR = SoObjects/Appointments/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = SoObjects/Appointments/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Appointments/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Appointments/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Appointments/Hungarian.lproj/Localizable.strings
|
||||
@@ -228,10 +206,8 @@ trans.pl = SoObjects/Appointments/Polish.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
|
||||
trans.sl_SI = SoObjects/Appointments/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Appointments/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-contacts]
|
||||
source_file = SoObjects/Contacts/English.lproj/Localizable.strings
|
||||
@@ -244,7 +220,6 @@ trans.da_DK = SoObjects/Contacts/Danish.lproj/Localizable.strings
|
||||
trans.de = SoObjects/Contacts/German.lproj/Localizable.strings
|
||||
trans.es_AR = SoObjects/Contacts/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = SoObjects/Contacts/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = SoObjects/Contacts/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Contacts/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Contacts/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Contacts/Hungarian.lproj/Localizable.strings
|
||||
@@ -257,10 +232,8 @@ trans.pl = SoObjects/Contacts/Polish.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
|
||||
trans.sl_SI = SoObjects/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Contacts/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-mailer]
|
||||
source_file = SoObjects/Mailer/English.lproj/Localizable.strings
|
||||
@@ -273,7 +246,6 @@ trans.da_DK = SoObjects/Mailer/Danish.lproj/Localizable.strings
|
||||
trans.de = SoObjects/Mailer/German.lproj/Localizable.strings
|
||||
trans.es_AR = SoObjects/Mailer/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = SoObjects/Mailer/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = SoObjects/Mailer/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Mailer/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings
|
||||
@@ -286,10 +258,8 @@ trans.pl = SoObjects/Mailer/Polish.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
|
||||
trans.sl_SI = SoObjects/Mailer/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Mailer/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Mailer/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Mailer/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mailpartviewers]
|
||||
source_file = UI/MailPartViewers/English.lproj/Localizable.strings
|
||||
@@ -302,7 +272,6 @@ trans.da_DK = UI/MailPartViewers/Danish.lproj/Localizable.strings
|
||||
trans.de = UI/MailPartViewers/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MailPartViewers/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MailPartViewers/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MailPartViewers/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MailPartViewers/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MailPartViewers/French.lproj/Localizable.strings
|
||||
trans.hu = UI/MailPartViewers/Hungarian.lproj/Localizable.strings
|
||||
@@ -315,7 +284,5 @@ trans.pl = UI/MailPartViewers/Polish.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
|
||||
trans.sl_SI = UI/MailPartViewers/Slovenian.lproj/Localizable.strings
|
||||
trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings
|
||||
trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailPartViewers/ChineseTaiwan.lproj/Localizable.strings
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Inverse inc.
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -40,14 +40,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@implementation NGMimeMessage (ActiveSync)
|
||||
|
||||
- (void) _addRecipients: (NSEnumerator *) enumerator
|
||||
toArray: (NSMutableArray *) recipients
|
||||
- (NSArray *) allRecipients
|
||||
{
|
||||
NSEnumerator *enumerator, *addressList;
|
||||
NSMutableArray *recipients;
|
||||
NGMailAddressParser *parser;
|
||||
NSEnumerator *addressList;
|
||||
NGMailAddress *address;
|
||||
NSString *s;
|
||||
|
||||
recipients = [NSMutableArray array];
|
||||
|
||||
enumerator = [[self headersForKey: @"to"] objectEnumerator];
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
||||
@@ -56,22 +59,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
while ((address = [addressList nextObject]))
|
||||
[recipients addObject: [address address]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) allRecipients
|
||||
{
|
||||
NSMutableArray *recipients;
|
||||
|
||||
recipients = [NSMutableArray array];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"to"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
enumerator = [[self headersForKey: @"cc"] objectEnumerator];
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
||||
addressList = [[parser parseAddressList] objectEnumerator];
|
||||
|
||||
while ((address = [addressList nextObject]))
|
||||
[recipients addObject: [address address]];
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
@@ -194,12 +193,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
@@ -224,14 +222,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setNote: o];
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
if ((o = [theValues objectForKey: @"Categories"]))
|
||||
[self setCategories: o];
|
||||
|
||||
// Birthday
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -32,10 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#include <wbxml/wbxml.h>
|
||||
#include <wbxml/wbxml_conv.h>
|
||||
@@ -47,18 +44,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (void) _dumpToFile
|
||||
{
|
||||
BOOL debugOn;
|
||||
NSString *path;
|
||||
|
||||
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
|
||||
|
||||
if (debugOn)
|
||||
{
|
||||
NSString *path;
|
||||
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
[self errorWithFormat: @"Original data written to: %@", path];
|
||||
}
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
NSLog(@"Original data written to: %@", path);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -66,33 +56,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSString *tmp, *s;
|
||||
unichar *buf, *start, c;
|
||||
int len, i, j;
|
||||
|
||||
tmp = [self stringByEncodingBase64] ;
|
||||
|
||||
len = [tmp length];
|
||||
|
||||
start = buf = (unichar *)malloc(len*sizeof(unichar));
|
||||
[tmp getCharacters: buf range: NSMakeRange(0, len)];
|
||||
|
||||
for (i = 0, j = 0; i < len; i++)
|
||||
{
|
||||
c = *buf;
|
||||
|
||||
if (!(c == 0xA))
|
||||
{
|
||||
*(start+j) = c;
|
||||
j++;
|
||||
}
|
||||
|
||||
buf++;
|
||||
}
|
||||
|
||||
s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES];
|
||||
|
||||
return AUTORELEASE(s);
|
||||
return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""];
|
||||
}
|
||||
|
||||
- (NSData *) wbxml2xml
|
||||
@@ -117,18 +81,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
[self errorWithFormat: @"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
NSLog(@"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [NSData dataWithBytesNoCopy: xml length: xml_len freeWhenDone: YES];
|
||||
data = [[NSData alloc] initWithBytes: xml length: xml_len];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.decoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
return data;
|
||||
free(xml);
|
||||
|
||||
return AUTORELEASE(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +116,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
[self logWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
@@ -165,21 +131,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
[self errorWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [NSData dataWithBytesNoCopy: wbxml length: wbxml_len freeWhenDone: YES];
|
||||
data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.encoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
|
||||
return data;
|
||||
|
||||
return AUTORELEASE(data);
|
||||
}
|
||||
@end
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014-2015, Inverse inc.
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -49,9 +49,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (NSString *) deviceType;
|
||||
- (NSString *) attachmentName;
|
||||
- (NSString *) command;
|
||||
- (NSString *) collectionid;
|
||||
- (NSString *) itemid;
|
||||
- (BOOL) acceptsMultiPart;
|
||||
- (NSData *) convertHexStringToBytes;
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,112 +33,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <Foundation/NSCalendarDate.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSDate.h>
|
||||
#include <Foundation/NSTimeZone.h>
|
||||
|
||||
#include <SOGo/NSString+Utilities.h>
|
||||
#include <SOGo/NSData+Crypto.h>
|
||||
|
||||
#include <NGExtensions/NGBase64Coding.h>
|
||||
#include <NGExtensions/NSString+misc.h>
|
||||
|
||||
static NSArray *easCommandCodes = nil;
|
||||
static NSArray *easCommandParameters = nil;
|
||||
|
||||
@implementation NSString (ActiveSync)
|
||||
|
||||
//
|
||||
// This is a copy from NSString+XMLEscaping.m from SOPE.
|
||||
// The difference here is that we use wchar_t instead of unichar.
|
||||
// This is needed to get the rigth numeric character reference.
|
||||
// e.g. SMILING FACE WITH OPEN MOUTH
|
||||
// ok: wchar_t -> 😃 wrong: unichar -> � �
|
||||
//
|
||||
// We avoir naming it like the one in SOPE since if the ActiveSync
|
||||
// bundle is loaded, it'll overwrite the one provided by SOPE.
|
||||
//
|
||||
- (NSString *) _stringByEscapingXMLStringUsingCharacters {
|
||||
register unsigned i, len, j;
|
||||
register wchar_t *buf;
|
||||
const wchar_t *chars;
|
||||
unsigned escapeCount;
|
||||
|
||||
if ([self length] == 0) return @"";
|
||||
|
||||
NSData *data = [self dataUsingEncoding:NSUTF32StringEncoding];
|
||||
chars = [data bytes];
|
||||
len = [data length]/4;
|
||||
|
||||
/* check for characters to escape ... */
|
||||
for (i = 0, escapeCount = 0; i < len; i++) {
|
||||
switch (chars[i]) {
|
||||
case '&': case '"': case '<': case '>': case '\r':
|
||||
escapeCount++;
|
||||
break;
|
||||
default:
|
||||
if (chars[i] > 127)
|
||||
escapeCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (escapeCount == 0 ) {
|
||||
/* nothing to escape ... */
|
||||
return [[self copy] autorelease];
|
||||
}
|
||||
|
||||
buf = calloc((len + 5) + (escapeCount * 16), sizeof(wchar_t));
|
||||
for (i = 0, j = 0; i < len; i++) {
|
||||
switch (chars[i]) {
|
||||
/* escape special chars */
|
||||
case '\r':
|
||||
buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++;
|
||||
buf[j] = '3'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '&':
|
||||
buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++;
|
||||
buf[j] = 'p'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '"':
|
||||
buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++;
|
||||
buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';'; j++;
|
||||
break;
|
||||
case '<':
|
||||
buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++;
|
||||
buf[j] = ';'; j++;
|
||||
break;
|
||||
case '>':
|
||||
buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++;
|
||||
buf[j] = ';'; j++;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* escape big chars */
|
||||
if (chars[i] > 127) {
|
||||
unsigned char nbuf[32];
|
||||
unsigned int k;
|
||||
|
||||
sprintf((char *)nbuf, "&#%i;", (int)chars[i]);
|
||||
for (k = 0; nbuf[k] != '\0'; k++) {
|
||||
buf[j] = nbuf[k];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
else if (chars[i] == 0x9 || chars[i] == 0xA || chars[i] == 0xD || chars[i] >= 0x20) { // ignore any unsupported control character
|
||||
/* nothing to escape */
|
||||
buf[j] = chars[i];
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self = [[NSString alloc] initWithBytesNoCopy: buf
|
||||
length: (j*sizeof(wchar_t))
|
||||
encoding: NSUTF32StringEncoding
|
||||
freeWhenDone: YES];
|
||||
|
||||
return [self autorelease];
|
||||
}
|
||||
|
||||
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
|
||||
{
|
||||
if (folderType == ActiveSyncEventFolder)
|
||||
@@ -158,7 +59,11 @@ static NSArray *easCommandParameters = nil;
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
return [self _stringByEscapingXMLStringUsingCharacters];
|
||||
NSString *s;
|
||||
|
||||
s = [self stringByEscapingHTMLString];
|
||||
|
||||
return [s safeString];
|
||||
}
|
||||
|
||||
- (int) activeSyncFolderType
|
||||
@@ -217,98 +122,23 @@ static NSArray *easCommandParameters = nil;
|
||||
//
|
||||
- (NSCalendarDate *) calendarDate
|
||||
{
|
||||
NSString *s;
|
||||
id o;
|
||||
|
||||
// We force parsing in the GMT timezone. If we don't do that, the date will be parsed
|
||||
// in the default timezone.
|
||||
s = [NSString stringWithFormat: @"%@ GMT", self];
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y%m%dT%H%M%SZ %Z"];
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y%m%dT%H%M%SZ"];
|
||||
|
||||
if (!o)
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ %Z"];
|
||||
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ"];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
- (NSString *) _valueForParameter: (NSString *) theParameter
|
||||
{
|
||||
NSMutableArray *components;
|
||||
NSArray *components;
|
||||
NSString *s;
|
||||
int i;
|
||||
|
||||
components = [NSMutableArray arrayWithArray: [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"]];
|
||||
|
||||
// We handle BASE64 encoded queryStrings. See http://msdn.microsoft.com/en-us/library/ee160227%28v=exchg.80%29.aspx for details.
|
||||
if ([components count] == 1)
|
||||
{
|
||||
NSString *deviceType, *parameterValue;
|
||||
NSData *queryString;
|
||||
|
||||
int cmd_code, deviceid_length, policy_length, devicetype_length, parameter_code, parameter_length, i;
|
||||
const char* qs_bytes;
|
||||
|
||||
queryString = [[components objectAtIndex: 0] dataByDecodingBase64];
|
||||
|
||||
if (![queryString length])
|
||||
return nil;
|
||||
|
||||
qs_bytes = (const char*)[queryString bytes];
|
||||
|
||||
if (!easCommandCodes)
|
||||
{
|
||||
easCommandCodes = [NSArray arrayWithObjects:@"Sync", @"SendMail", @"SmartForward", @"SmartReply", @"GetAttachment", @"na", @"na", @"na", @"na",
|
||||
@"FolderSync", @"FolderCreate", @"FolderDelete", @"FolderUpdate", @"MoveItems", @"GetItemEstimate", @"MeetingResponse",
|
||||
@"Search", @"Settings", @"Ping", @"ItemOperations", @"Provision", @"ResolveRecipients", @"ValidateCert", nil];
|
||||
RETAIN(easCommandCodes);
|
||||
}
|
||||
|
||||
if (!easCommandParameters)
|
||||
{
|
||||
easCommandParameters = [NSArray arrayWithObjects:@"AttachmentName", @"CollectionId", @"na", @"ItemId", @"LongId", @"na", @"Occurrence", @"Options", @"User", nil];
|
||||
RETAIN(easCommandParameters);
|
||||
}
|
||||
|
||||
// Command code, 1 byte, ie.: cmd=
|
||||
cmd_code = qs_bytes[1];
|
||||
[components addObject: [NSString stringWithFormat: @"cmd=%@", [easCommandCodes objectAtIndex: cmd_code]]];
|
||||
|
||||
// Device ID length and Device ID (variable)
|
||||
deviceid_length = qs_bytes[4];
|
||||
[components addObject: [NSString stringWithFormat: @"deviceId=%@", [[NSData encodeDataAsHexString: [queryString subdataWithRange: NSMakeRange(5, deviceid_length)]] uppercaseString]]];
|
||||
|
||||
// Device type length and type (variable)
|
||||
policy_length = qs_bytes[5+deviceid_length];
|
||||
devicetype_length = qs_bytes[5+deviceid_length+1+policy_length];
|
||||
deviceType = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(5+deviceid_length+1+policy_length+1, devicetype_length)]
|
||||
encoding: NSASCIIStringEncoding];
|
||||
AUTORELEASE(deviceType);
|
||||
|
||||
[components addObject: [NSString stringWithFormat: @"deviceType=%@", deviceType]];
|
||||
|
||||
// Command Parameters
|
||||
i = 5+deviceid_length+1+policy_length+1+devicetype_length;
|
||||
|
||||
while (i < [queryString length])
|
||||
{
|
||||
parameter_code = qs_bytes[i];
|
||||
parameter_length = qs_bytes[i+1];
|
||||
parameterValue = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(i+1+1, parameter_length)]
|
||||
encoding: NSASCIIStringEncoding];
|
||||
|
||||
AUTORELEASE(parameterValue);
|
||||
|
||||
// parameter_code 7 == Options
|
||||
// http://msdn.microsoft.com/en-us/library/ee237789(v=exchg.80).aspx
|
||||
if (parameter_code == 7)
|
||||
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code],
|
||||
([parameterValue isEqualToString: @"\001"]) ? @"SaveInSent" : @"AcceptMultiPart"]];
|
||||
else
|
||||
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code], parameterValue]];
|
||||
|
||||
i = i + 1 + 1 + parameter_length;
|
||||
}
|
||||
}
|
||||
components = [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"];
|
||||
|
||||
for (i = 0; i < [components count]; i++)
|
||||
{
|
||||
@@ -387,43 +217,6 @@ static NSArray *easCommandParameters = nil;
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString *) itemid
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"ITEMID="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString *) collectionid
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"COLLECTIONID="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (BOOL) acceptsMultiPart
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"OPTIONS="];
|
||||
|
||||
if (s && [s rangeOfString: @"AcceptMultiPart" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ to negotiate the fees associated to your user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
|
||||
and send an email to iplicreq@microsoft.com
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@interface SOGoActiveSyncDispatcher : NSObject
|
||||
{
|
||||
NSURL *folderTableURL;
|
||||
NSDictionary *imapFolderGUIDS;
|
||||
id context;
|
||||
}
|
||||
|
||||
|
||||
+562
-1402
File diff suppressed because it is too large.
Load diff
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "SOGoMailObject+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
@@ -47,9 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGExtensions/NSString+Encoding.h>
|
||||
#import <NGImap4/NGImap4Envelope.h>
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeFileData.h>
|
||||
@@ -59,10 +58,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
|
||||
#import <Mailer/SOGoMailLabel.h>
|
||||
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#include "iCalTimeZone+ActiveSync.h"
|
||||
#include "NSData+ActiveSync.h"
|
||||
#include "NSDate+ActiveSync.h"
|
||||
@@ -72,45 +67,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <Appointments/iCalPerson+SOGo.h>
|
||||
#include <Mailer/NSString+Mail.h>
|
||||
#include <Mailer/SOGoMailBodyPart.h>
|
||||
|
||||
#include <SOGo/SOGoUser.h>
|
||||
#include <SOGo/NSString+Utilities.h>
|
||||
|
||||
#import <Appointments/SOGoAptMailNotification.h>
|
||||
|
||||
unsigned char strToChar(char a, char b) {
|
||||
char encoder[3] = {'\0','\0','\0'};
|
||||
encoder[0] = a;
|
||||
encoder[1] = b;
|
||||
return (char) strtol(encoder,NULL,16);
|
||||
}
|
||||
|
||||
@interface NSString (NSStringExtensions)
|
||||
- (NSData *) decodeFromHexidecimal;
|
||||
@end
|
||||
|
||||
@implementation NSString (NSStringExtensions)
|
||||
|
||||
- (NSData *) decodeFromHexidecimal;
|
||||
{
|
||||
const char * bytes = [self cStringUsingEncoding: NSUTF8StringEncoding];
|
||||
NSUInteger length = strlen(bytes);
|
||||
unsigned char * r = (unsigned char *) malloc(length / 2 + 1);
|
||||
unsigned char * index = r;
|
||||
|
||||
while ((*bytes) && (*(bytes +1))) {
|
||||
*index = strToChar(*bytes, *(bytes +1));
|
||||
index++;
|
||||
bytes+=2;
|
||||
}
|
||||
*index = '\0';
|
||||
|
||||
NSData * result = [NSData dataWithBytes: r length: length / 2];
|
||||
free(r);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwLowDateTime;
|
||||
@@ -126,7 +84,7 @@ struct GlobalObjectId {
|
||||
FILETIME CreationTime;
|
||||
uint8_t X[8];
|
||||
uint32_t Size;
|
||||
uint8_t Data[0];
|
||||
uint8_t* Data;
|
||||
};
|
||||
|
||||
@implementation SOGoMailObject (ActiveSync)
|
||||
@@ -153,40 +111,39 @@ struct GlobalObjectId {
|
||||
//
|
||||
// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx
|
||||
//
|
||||
|
||||
- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event
|
||||
{
|
||||
NSData *binPrefix, *globalObjectId, *uidAsASCII;
|
||||
NSString *prefix, *uid;
|
||||
struct GlobalObjectId *newGlobalId;
|
||||
|
||||
struct GlobalObjectId newGlobalId;
|
||||
const char *bytes;
|
||||
|
||||
uid = [event uid];
|
||||
uidAsASCII = [uid decodeFromHexidecimal];
|
||||
newGlobalId = (struct GlobalObjectId*)calloc(sizeof(uint8_t), sizeof(struct GlobalObjectId) + 0x0c + [uidAsASCII length]);
|
||||
|
||||
|
||||
prefix = @"040000008200e00074c5b7101a82e008";
|
||||
|
||||
// dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00"
|
||||
uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 };
|
||||
uid = [event uid];
|
||||
|
||||
binPrefix = [prefix convertHexStringToBytes];
|
||||
[binPrefix getBytes: &newGlobalId->ByteArrayID];
|
||||
[self _setInstanceDate: newGlobalId
|
||||
[binPrefix getBytes: &newGlobalId.ByteArrayID];
|
||||
[self _setInstanceDate: &newGlobalId
|
||||
fromDate: [event recurrenceId]];
|
||||
uidAsASCII = [uid dataUsingEncoding: NSASCIIStringEncoding];
|
||||
bytes = [uidAsASCII bytes];
|
||||
|
||||
// 0x0c is the size of our dataPrefix
|
||||
newGlobalId->Size = 0x0c + [uidAsASCII length];
|
||||
memcpy(newGlobalId->Data, dataPrefix, 0x0c);
|
||||
memcpy(newGlobalId->Data + 0x0c, bytes, newGlobalId->Size - 0x0c);
|
||||
newGlobalId.Size = 0x0c + [uidAsASCII length];
|
||||
newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t));
|
||||
memcpy(newGlobalId.Data, dataPrefix, 0x0c);
|
||||
memcpy(newGlobalId.Data + 0x0c, bytes, newGlobalId.Size - 0x0c);
|
||||
|
||||
globalObjectId = [[NSData alloc] initWithBytes: newGlobalId length: 40 + newGlobalId->Size*sizeof(uint8_t)];
|
||||
free(newGlobalId);
|
||||
globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)];
|
||||
free(newGlobalId.Data);
|
||||
|
||||
return [globalObjectId autorelease];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// For debugging purposes...
|
||||
//
|
||||
@@ -248,15 +205,12 @@ struct GlobalObjectId {
|
||||
//
|
||||
//
|
||||
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
|
||||
nativeTypeFound: (int *) theNativeTypeFound
|
||||
{
|
||||
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
|
||||
NSDictionary *textParts, *part;
|
||||
NSEnumerator *e;
|
||||
NSData *d;
|
||||
|
||||
BOOL ignore;
|
||||
|
||||
textParts = [self fetchPlainTextParts];
|
||||
e = [textParts keyEnumerator];
|
||||
plainKey = nil;
|
||||
@@ -265,35 +219,10 @@ struct GlobalObjectId {
|
||||
|
||||
while ((key = [e nextObject]))
|
||||
{
|
||||
// Walk the hierarchy up and check whether parents are of type mulipart - i.e. ignore message/rfc822
|
||||
if ([key countOccurrencesOfString: @"."] > 0)
|
||||
{
|
||||
NSMutableArray *a;
|
||||
|
||||
a = [[[key componentsSeparatedByString: @"."] mutableCopy] autorelease];
|
||||
ignore = NO;
|
||||
|
||||
while ([a count] > 0)
|
||||
{
|
||||
[a removeLastObject];
|
||||
part = [self lookupInfoForBodyPart: [a componentsJoinedByString: @"."]];
|
||||
|
||||
if (![[part valueForKey: @"type"] isEqualToString: @"multipart"])
|
||||
ignore = YES;
|
||||
}
|
||||
|
||||
if (ignore)
|
||||
continue;
|
||||
}
|
||||
|
||||
part = [self lookupInfoForBodyPart: key];
|
||||
type = [part valueForKey: @"type"];
|
||||
subtype = [part valueForKey: @"subtype"];
|
||||
|
||||
// Don't select an attachment as body
|
||||
if ([[[part valueForKey: @"disposition"] valueForKey: @"type"] isEqualToString: @"attachment"])
|
||||
continue;
|
||||
|
||||
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
|
||||
htmlKey = key;
|
||||
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
|
||||
@@ -301,22 +230,11 @@ struct GlobalObjectId {
|
||||
}
|
||||
|
||||
key = nil;
|
||||
*theNativeTypeFound = 1;
|
||||
|
||||
if (theType == 2 && htmlKey)
|
||||
{
|
||||
key = htmlKey;
|
||||
*theNativeTypeFound = 2;
|
||||
}
|
||||
else if (theType == 1 && plainKey)
|
||||
if (theType == 2)
|
||||
key = htmlKey;
|
||||
else if (theType == 1)
|
||||
key = plainKey;
|
||||
else if (theType == 2 && plainKey)
|
||||
key = plainKey;
|
||||
else if (theType == 1 && htmlKey)
|
||||
{
|
||||
key = htmlKey;
|
||||
*theNativeTypeFound = 2;
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
@@ -334,17 +252,9 @@ struct GlobalObjectId {
|
||||
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"utf-8";
|
||||
charset = @"us-ascii";
|
||||
|
||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||
|
||||
// We fallback to ISO-8859-1 string encoding
|
||||
if (!s)
|
||||
s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
|
||||
|
||||
if (theType == 1 && *theNativeTypeFound == 2)
|
||||
s = [s htmlToText];
|
||||
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
@@ -373,7 +283,7 @@ struct GlobalObjectId {
|
||||
performed: b];
|
||||
}
|
||||
}
|
||||
else if ([thePart isKindOfClass: [NGMimeBodyPart class]] || [thePart isKindOfClass: [NGMimeMessage class]])
|
||||
else if ([thePart isKindOfClass: [NGMimeBodyPart class]])
|
||||
{
|
||||
NGMimeFileData *fdata;
|
||||
id body;
|
||||
@@ -389,7 +299,7 @@ struct GlobalObjectId {
|
||||
[[[thePart contentType] type] isEqualToString: @"text"] &&
|
||||
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
|
||||
{
|
||||
// We make sure everything is encoded in UTF-8.
|
||||
// We make sure everything is encoded in UTF-8
|
||||
NGMimeType *mimeType;
|
||||
NSString *s;
|
||||
|
||||
@@ -400,13 +310,9 @@ struct GlobalObjectId {
|
||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"utf-8";
|
||||
charset = @"us-ascii";
|
||||
|
||||
s = [NSString stringWithData: body usingEncodingNamed: charset];
|
||||
|
||||
// We fallback to ISO-8859-1 string encoding. We avoid #3103.
|
||||
if (!s)
|
||||
s = [[[NSString alloc] initWithData: body encoding: NSISOLatin1StringEncoding] autorelease];
|
||||
s = [NSString stringWithData: body usingEncodingNamed: charset];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +362,8 @@ struct GlobalObjectId {
|
||||
|
||||
if (message)
|
||||
{
|
||||
[self _sanitizedMIMEPart: message performed: &b];
|
||||
[self _sanitizedMIMEPart: [message body]
|
||||
performed: &b];
|
||||
|
||||
if (b)
|
||||
{
|
||||
@@ -497,14 +404,14 @@ struct GlobalObjectId {
|
||||
// We get the right part based on the preference
|
||||
if (theType == 1 || theType == 2)
|
||||
{
|
||||
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
|
||||
if ([type isEqualToString: @"text"])
|
||||
{
|
||||
NSString *s, *charset;
|
||||
|
||||
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"utf-8";
|
||||
charset = @"us-ascii";
|
||||
|
||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||
|
||||
@@ -518,30 +425,25 @@ struct GlobalObjectId {
|
||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||
|
||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||
|
||||
// We fallback to ISO-8859-1 string encoding. We avoid #3103.
|
||||
if (!s)
|
||||
s = [[[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding] autorelease];
|
||||
|
||||
// Check if we must convert html->plain
|
||||
if (theType == 1 && [subtype isEqualToString: @"html"])
|
||||
{
|
||||
s = [s htmlToText];
|
||||
}
|
||||
|
||||
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
else if ([type isEqualToString: @"multipart"])
|
||||
{
|
||||
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
||||
d = [self _preferredBodyDataInMultipartUsingType: theType];
|
||||
}
|
||||
}
|
||||
else if (theType == 4)
|
||||
{
|
||||
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
|
||||
// We sanitize the content *ONLY* for Outlook clients. 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 ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
|
||||
d = [self _sanitizedMIMEMessage];
|
||||
else
|
||||
d = [self content];
|
||||
@@ -550,65 +452,15 @@ struct GlobalObjectId {
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) _getNormalizedSubject
|
||||
{
|
||||
NSString *subject;
|
||||
NSUInteger colIdx;
|
||||
NSString *stringValue;
|
||||
|
||||
subject = [[self subject] decodedHeader];
|
||||
|
||||
colIdx = [subject rangeOfString: @":" options:NSBackwardsSearch].location;
|
||||
if (colIdx != NSNotFound && colIdx + 1 < [subject length])
|
||||
stringValue = [[subject substringFromIndex: colIdx + 1] stringByTrimmingLeadSpaces];
|
||||
else
|
||||
stringValue = subject;
|
||||
|
||||
if (!stringValue)
|
||||
stringValue = @"";
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (iCalCalendar *) calendarFromIMIPMessage
|
||||
{
|
||||
NSDictionary *part;
|
||||
NSString *type, *subtype;
|
||||
NSArray *parts;
|
||||
int i;
|
||||
|
||||
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
|
||||
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||
|
||||
// process mail of type text/calendar
|
||||
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"calendar"])
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSString *encoding;
|
||||
NSData *calendarData;
|
||||
|
||||
encoding = [[[self bodyStructure] valueForKey: @"encoding"] lowercaseString];
|
||||
calendarData = [[self fetchPlainTextParts] objectForKey: @""];
|
||||
|
||||
if ([encoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
|
||||
calendarData = [calendarData dataByDecodingBase64];
|
||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||
calendarData = [calendarData dataByDecodingQuotedPrintableTransferEncoding];
|
||||
|
||||
NS_DURING
|
||||
calendar = [iCalCalendar parseSingleFromSource: calendarData];
|
||||
NS_HANDLER
|
||||
calendar = nil;
|
||||
NS_ENDHANDLER
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
// We check if we have at least 2 parts and if one of them is a text/calendar
|
||||
parts = [[self bodyStructure] objectForKey: @"parts"];
|
||||
|
||||
@@ -654,13 +506,10 @@ struct GlobalObjectId {
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NSData *d, *globalObjId;
|
||||
NSArray *attachmentKeys;
|
||||
NSMutableString *s;
|
||||
|
||||
uint32_t v;
|
||||
NSString *p;
|
||||
|
||||
id value;
|
||||
|
||||
iCalCalendar *calendar;
|
||||
@@ -685,7 +534,7 @@ struct GlobalObjectId {
|
||||
if (value)
|
||||
{
|
||||
[s appendFormat: @"<Subject xmlns=\"Email:\">%@</Subject>", [value activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [[self _getNormalizedSubject] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [value activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// DateReceived
|
||||
@@ -701,34 +550,8 @@ struct GlobalObjectId {
|
||||
if (value)
|
||||
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Importance
|
||||
v = 0x1;
|
||||
value = [[self mailHeaders] objectForKey: @"x-priority"];
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
p = [value lastObject];
|
||||
else
|
||||
p = value;
|
||||
|
||||
if (p)
|
||||
{
|
||||
if ([p hasPrefix: @"1"]) v = 0x2;
|
||||
else if ([p hasPrefix: @"2"]) v = 0x2;
|
||||
else if ([p hasPrefix: @"4"]) v = 0x0;
|
||||
else if ([p hasPrefix: @"5"]) v = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = [[self mailHeaders] objectForKey: @"importance"];
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
p = [value lastObject];
|
||||
else
|
||||
p = value;
|
||||
|
||||
if ([p hasPrefix: @"High"]) v = 0x2;
|
||||
else if ([p hasPrefix: @"Low"]) v = 0x0;
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Importance xmlns=\"Email:\">%d</Importance>", v];
|
||||
// Importance - FIXME
|
||||
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
|
||||
|
||||
// Read
|
||||
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
||||
@@ -792,18 +615,18 @@ struct GlobalObjectId {
|
||||
|
||||
[s appendFormat: @"<AllDayEvent xmlns=\"Email:\">%d</AllDayEvent>", ([event isAllDay] ? 1 : 0)];
|
||||
|
||||
// StartTime -- https://msdn.microsoft.com/en-us/library/ee203365%28v=exchg.80%29.aspx
|
||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||
if ([event startDate])
|
||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
if ([event timeStampAsDate])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
else if ([event created])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
// EndTime -- https://msdn.microsoft.com/en-us/library/ee158628(v=exchg.80).aspx
|
||||
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
||||
if ([event endDate])
|
||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
// FIXME: Single appointment - others are not supported right now
|
||||
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
|
||||
@@ -851,9 +674,7 @@ struct GlobalObjectId {
|
||||
[s appendFormat: @"<GlobalObjId xmlns=\"Email:\">%@</GlobalObjId>", [globalObjId activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We set the right message type - we must set AS version to 14.1 for this
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
|
||||
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
|
||||
|
||||
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
|
||||
[s appendString: @"</MeetingRequest>"];
|
||||
|
||||
// ContentClass
|
||||
@@ -878,82 +699,19 @@ struct GlobalObjectId {
|
||||
// Body - namespace 17
|
||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||
|
||||
// Make use of a local pool here as _preferredBodyDataUsingType:nativeType: will consume
|
||||
// a significant amout of RAM and file descriptors
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
nativeBodyType = 1;
|
||||
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
|
||||
|
||||
if (calendar && !d)
|
||||
{
|
||||
WOApplication *app;
|
||||
SOGoAptMailNotification *p;
|
||||
NSString *pageName;
|
||||
|
||||
nativeBodyType = 2;
|
||||
|
||||
/* get WOApplication instance */
|
||||
app = [WOApplication application];
|
||||
|
||||
/* create page name */
|
||||
pageName = [NSString stringWithFormat: @"SOGoAptMail%@", @"Invitation"];
|
||||
/* construct message content */
|
||||
p = [app pageWithName: pageName inContext: context];
|
||||
[p setApt: (iCalEvent *) [[calendar events] lastObject]];
|
||||
|
||||
if ([[ [[calendar events] lastObject] organizer] cn] && [[[ [[calendar events] lastObject] organizer] cn] length])
|
||||
{
|
||||
[p setOrganizerName: [[ [[calendar events] lastObject] organizer] cn]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[p setOrganizerName: [[SOGoUser userWithLogin: owner] cn]];
|
||||
}
|
||||
|
||||
if (preferredBodyType == 1 && nativeBodyType == 2)
|
||||
d = [[[p getBody] htmlToText] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
else
|
||||
{
|
||||
preferredBodyType = 2;
|
||||
d = [[p getBody] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (d)
|
||||
{
|
||||
NSMutableData *sanitizedData;
|
||||
NSString *content;
|
||||
int len, truncated;
|
||||
|
||||
// Outlook fails to decode quoted-printable (see #3082) if lines are not termined by CRLF.
|
||||
const char *bytes;
|
||||
char *mbytes;
|
||||
int mlen;
|
||||
|
||||
len = [d length];
|
||||
mlen = 0;
|
||||
|
||||
sanitizedData = [NSMutableData dataWithLength: len*2];
|
||||
|
||||
bytes = [d bytes];
|
||||
mbytes = [sanitizedData mutableBytes];
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (*bytes == '\n' && *(bytes-1) != '\r' && mlen > 0)
|
||||
{
|
||||
*mbytes = '\r';
|
||||
mbytes++;
|
||||
mlen++;
|
||||
}
|
||||
|
||||
*mbytes = *bytes;
|
||||
mbytes++; bytes++;
|
||||
len--;
|
||||
mlen++;
|
||||
}
|
||||
|
||||
[sanitizedData setLength: mlen];
|
||||
|
||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSUTF8StringEncoding];
|
||||
|
||||
content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||
|
||||
// FIXME: This is a hack. We should normally avoid doing this as we might get
|
||||
// broken encodings. We should rather tell that the data was truncated and expect
|
||||
@@ -963,53 +721,38 @@ struct GlobalObjectId {
|
||||
// for an "interesting" discussion around this.
|
||||
//
|
||||
if (!content)
|
||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSISOLatin1StringEncoding];
|
||||
content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding];
|
||||
|
||||
AUTORELEASE(content);
|
||||
|
||||
content = [content activeSyncRepresentationInContext: context];
|
||||
truncated = 0;
|
||||
|
||||
|
||||
len = [content length];
|
||||
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", truncated];
|
||||
[s appendFormat: @"<Preview></Preview>"];
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
if (!truncated)
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Email:\">%@</Body>", content];
|
||||
[s appendFormat: @"<BodyTruncated xmlns=\"Email:\">%d</BodyTruncated>", truncated];
|
||||
[s appendFormat: @"<Data>%@</Data>", content];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
// Set the correct type if client requested text/html but we got text/plain
|
||||
if (preferredBodyType == 2 && nativeBodyType == 1)
|
||||
[s appendString: @"<Type>1</Type>"];
|
||||
else
|
||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", truncated];
|
||||
[s appendFormat: @"<Preview></Preview>"];
|
||||
|
||||
if (!truncated)
|
||||
{
|
||||
[s appendFormat: @"<Data>%@</Data>", content];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
|
||||
}
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
|
||||
DESTROY(pool);
|
||||
|
||||
// Attachments -namespace 16
|
||||
attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
if ([attachmentKeys count])
|
||||
{
|
||||
int i;
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
[s appendString: @"<Attachments xmlns=\"Email:\">"];
|
||||
else
|
||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
|
||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
for (i = 0; i < [attachmentKeys count]; i++)
|
||||
{
|
||||
value = [attachmentKeys objectAtIndex: i];
|
||||
@@ -1020,22 +763,11 @@ struct GlobalObjectId {
|
||||
// FileReference must be a unique identifier across the whole store. We use the following structure:
|
||||
// mail/<foldername>/<message UID/<pathofpart>
|
||||
// mail/INBOX/2
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
[s appendFormat: @"<AttName>mail/%@/%@/%@</AttName>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||
else
|
||||
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<AttMethod>%d</AttMethod>", 1];
|
||||
[s appendFormat: @"<AttSize>%d</AttSize>", [[value objectForKey: @"size"] intValue]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
|
||||
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
|
||||
}
|
||||
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
|
||||
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
|
||||
[s appendString: @"</Attachment>"];
|
||||
}
|
||||
|
||||
@@ -1046,46 +778,10 @@ struct GlobalObjectId {
|
||||
[s appendString: @"<Flag xmlns=\"Email:\">"];
|
||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", ([self flagged] ? 2 : 0)];
|
||||
[s appendString: @"</Flag>"];
|
||||
|
||||
|
||||
// Categroies/Labels
|
||||
NSEnumerator *categories;
|
||||
categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
|
||||
|
||||
if (categories)
|
||||
{
|
||||
NSString *currentFlag;
|
||||
NSDictionary *v;
|
||||
|
||||
v = [[[context activeUser] userDefaults] mailLabelsColors];
|
||||
|
||||
[s appendFormat: @"<Categories xmlns=\"Email:\">"];
|
||||
while ((currentFlag = [categories nextObject]))
|
||||
{
|
||||
if ([[v objectForKey: currentFlag] objectAtIndex:0])
|
||||
[s appendFormat: @"<Category>%@</Category>", [[[v objectForKey: currentFlag] objectAtIndex:0] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
|
||||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
|
||||
{
|
||||
NSString *reference;
|
||||
|
||||
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
|
||||
|
||||
if ([reference length] > 0)
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
else if ([self inReplyTo])
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self inReplyTo] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
else if ([self messageId])
|
||||
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self messageId] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// FIXME - support these in the future
|
||||
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
|
||||
//[s appendString: @"<ConversationIndex xmlns=\"Email2:\">zot=</ConversationIndex>"];
|
||||
|
||||
|
||||
// NativeBodyType -- http://msdn.microsoft.com/en-us/library/ee218276(v=exchg.80).aspx
|
||||
// This is a required child element.
|
||||
@@ -1144,65 +840,6 @@ struct GlobalObjectId {
|
||||
else
|
||||
[self removeFlags: @"seen"];;
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Categories"]))
|
||||
{
|
||||
NSEnumerator *categories;
|
||||
NSString *currentFlag;
|
||||
NSDictionary *v;
|
||||
|
||||
v = [[[context activeUser] userDefaults] mailLabelsColors];
|
||||
|
||||
// add categories/labels sent from client
|
||||
if ([o isKindOfClass: [NSArray class]])
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
NSMutableArray *labels;
|
||||
NSEnumerator *flags;
|
||||
id key;
|
||||
|
||||
labels = [NSMutableArray array];
|
||||
|
||||
enumerator = [v keyEnumerator];
|
||||
flags = [o objectEnumerator];
|
||||
|
||||
while ((currentFlag = [flags nextObject]))
|
||||
{
|
||||
while ((key = [enumerator nextObject]))
|
||||
{
|
||||
if (([currentFlag isEqualToString:[[v objectForKey:key] objectAtIndex:0]]))
|
||||
{
|
||||
[labels addObject: key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self addFlags: [labels componentsJoinedByString: @" "]];
|
||||
}
|
||||
|
||||
categories = [[[self fetchCoreInfos] objectForKey: @"flags"] objectEnumerator];
|
||||
|
||||
// remove all categories/labels from server which were not it the list sent from client
|
||||
if (categories)
|
||||
{
|
||||
while ((currentFlag = [categories nextObject]))
|
||||
{
|
||||
// only deal with lables and don't touch flags like seen and flagged
|
||||
if (([v objectForKey: currentFlag]))
|
||||
{
|
||||
if (![o isKindOfClass: [NSArray class]])
|
||||
{
|
||||
[self removeFlags: currentFlag];
|
||||
}
|
||||
else if (([o indexOfObject: [[v objectForKey:currentFlag] objectAtIndex:0]] == NSNotFound))
|
||||
{
|
||||
[self removeFlags: currentFlag];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -55,7 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o setUID: theUID];
|
||||
[o setSequence: theSequence];
|
||||
|
||||
return [o autorelease];
|
||||
return o;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
||||
@@ -61,7 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[response setHeader: @"private" forKey: @"Cache-Control"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Allow"];
|
||||
[response setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"];
|
||||
[response setHeader: @"2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Public"];
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@ ADDITIONAL_INCLUDE_DIRS += \
|
||||
-I../../SOPE
|
||||
|
||||
ADDITIONAL_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo \
|
||||
-L../SoObjects/SOGo/SOGo.framework/ \
|
||||
-L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L/usr/local/lib \
|
||||
-Wl,-rpath,$(SOGO_SYSLIBDIR)/sogo
|
||||
-L/usr/local/lib
|
||||
|
||||
BUNDLE_LIBS += \
|
||||
-lSOGo \
|
||||
|
||||
@@ -40,7 +40,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
@@ -77,13 +76,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSArray *attendees, *categories;
|
||||
NSArray *attendees;
|
||||
|
||||
iCalPerson *organizer, *attendee;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
int v, i, meetingStatus;
|
||||
int v;
|
||||
|
||||
NSTimeZone *userTimeZone;
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
@@ -128,14 +127,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
|
||||
|
||||
if (!tz)
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
|
||||
|
||||
[s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Organizer and other invitations related properties
|
||||
if ((organizer = [self organizer]))
|
||||
{
|
||||
meetingStatus = 1; // meeting and the user is the meeting organizer.
|
||||
o = [organizer rfc822Email];
|
||||
if ([o length])
|
||||
{
|
||||
@@ -161,7 +159,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendString: @"<Attendee xmlns=\"Calendar:\">"];
|
||||
|
||||
attendee = [attendees objectAtIndex: i];
|
||||
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [[attendee rfc822Email] activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]];
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
|
||||
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
@@ -179,10 +177,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
[s appendString: @"</Attendees>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
meetingStatus = 0; // appointment
|
||||
}
|
||||
|
||||
// This depends on the 'NEEDS-ACTION' parameter.
|
||||
// This will trigger the SendMail command
|
||||
@@ -192,21 +186,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
int attendee_status;
|
||||
|
||||
meetingStatus = 3; // event is a meeting, and the user is not the meeting organizer
|
||||
|
||||
attendee = [self userAsAttendee: [context activeUser]];
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
|
||||
[s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1];
|
||||
[s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status];
|
||||
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", 3];
|
||||
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Calendar:\">%d</DisallowNewTimeProposal>", 1];
|
||||
|
||||
// BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
|
||||
[s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", 2];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", meetingStatus];
|
||||
|
||||
// Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx
|
||||
if ([[self summary] length])
|
||||
[s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
|
||||
@@ -232,25 +223,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Sensitivity
|
||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Calendar:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Calendar:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
|
||||
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
|
||||
// TODO: improve this to handle more alarm types
|
||||
@@ -272,23 +250,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
o = [self comment];
|
||||
if ([o length])
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Calendar:\">%@</Body>", o];
|
||||
[s appendString: @"<BodyTruncated xmlns=\"Calendar:\">0</BodyTruncated>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
|
||||
@@ -342,6 +310,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
BOOL isAllDay;
|
||||
|
||||
if ((o = [theValues objectForKey: @"UID"]))
|
||||
@@ -365,10 +334,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o intValue];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||
{
|
||||
[o intValue];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- normal, 1- personal, 2- private and 3-confidential
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"Sensitivity"]))
|
||||
if ((o = [theValues objectForKey: @"Sensitivy"]))
|
||||
{
|
||||
switch ([o intValue])
|
||||
{
|
||||
@@ -385,36 +362,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
}
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
|
||||
// We ignore TimeZone sent by mobile devices for now.
|
||||
// Some Windows devices don't send during event updates.
|
||||
//if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
// {
|
||||
// }
|
||||
//else
|
||||
if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
{
|
||||
// We haven't received a timezone, let's use the user's timezone
|
||||
// specified in SOGo for now.
|
||||
// Ugh, we ignore it for now.
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
}
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
if ((o = [theValues objectForKey: @"Body"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Location"]))
|
||||
[self setLocation: o];
|
||||
|
||||
@@ -424,13 +383,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[start setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDate: o];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDateTime: o];
|
||||
}
|
||||
}
|
||||
@@ -443,11 +410,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[end setDate: o];
|
||||
[end setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[end setDateTime: o];
|
||||
}
|
||||
}
|
||||
@@ -506,47 +481,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||
{
|
||||
//
|
||||
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
||||
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
||||
// and there's no organizer, we fake one.
|
||||
//
|
||||
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: [[[context activeUser] primaryIdentity] objectForKey: @"email"]];
|
||||
[person setCn: [[context activeUser] cn]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
//
|
||||
// When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3.
|
||||
// Use the organizer from the request if the event has no organizer.
|
||||
//
|
||||
if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: [theValues objectForKey: @"Organizer_Email"]];
|
||||
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Attendees - we don't touch the values if we're an attendee. This is gonna
|
||||
// be done automatically by the ActiveSync client when invoking MeetingResponse.
|
||||
if (![self userIsAttendee: [context activeUser]])
|
||||
{
|
||||
// Windows phones sens sometimes an empty Attendees tag.
|
||||
// We check it's an array before processing it.
|
||||
if ((o = [theValues objectForKey: @"Attendees"])&& [o isKindOfClass: [NSArray class]])
|
||||
if ((o = [theValues objectForKey: @"Attendees"]))
|
||||
{
|
||||
NSMutableArray *attendees;
|
||||
NSDictionary *attendee;
|
||||
|
||||
@@ -76,17 +76,15 @@ struct SYSTEMTIME {
|
||||
byMonth = [rrule byMonth];
|
||||
if ([byMonth count] > 0)
|
||||
{
|
||||
tzData->wYear = 0;
|
||||
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
|
||||
mask = [rrule byDayMask];
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
tzData->wDay = ([mask firstOccurrence] == -1) ? 5 : [mask firstOccurrence];
|
||||
tzData->wDay = [mask firstOccurrence];
|
||||
|
||||
dateValue = [self startDate];
|
||||
tzData->wHour = [dateValue hourOfDay];
|
||||
tzData->wMinute = [dateValue minuteOfHour];
|
||||
tzData->wSecond = [dateValue secondOfMinute];
|
||||
tzData->wMilliseconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
@@ -50,24 +49,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
|
||||
@implementation iCalToDo (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSArray *categories;
|
||||
id o;
|
||||
|
||||
int v, i;
|
||||
int v;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
// Complete
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
|
||||
o = [self completed];
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
|
||||
|
||||
// DateCompleted
|
||||
if ((o = [self completed]))
|
||||
if (o)
|
||||
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Start date
|
||||
@@ -98,28 +96,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Reminder - FIXME
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||
|
||||
// Sensitivity
|
||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", v];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Tasks:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Tasks:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
// Sensitivity - FIXME
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", 0];
|
||||
|
||||
// Subject
|
||||
o = [self summary];
|
||||
if ([o length])
|
||||
@@ -127,23 +106,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [self comment]))
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Tasks:\">%@</Body>", o];
|
||||
[s appendString: @"<BodyTruncated xmlns=\"Tasks:\">0</BodyTruncated>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -156,6 +125,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
@@ -165,16 +136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setSummary: o];
|
||||
|
||||
// FIXME: merge with iCalEvent
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
if ((o = [theValues objectForKey: @"Body"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
|
||||
if ([[theValues objectForKey: @"Complete"] intValue] &&
|
||||
((o = [theValues objectForKey: @"DateCompleted"])) )
|
||||
@@ -183,32 +147,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
o = [o calendarDate];
|
||||
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"];
|
||||
//tzOffset = [[o timeZone] secondsFromGMTForDate: o];
|
||||
//o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
// hours: 0 minutes: 0
|
||||
// seconds: -tzOffset];
|
||||
[completed setDate: o];
|
||||
[self setStatus: @"COMPLETED"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setStatus: @"IN-PROCESS"];
|
||||
[self setCompleted: nil];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"DueDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
|
||||
[due setTimeZone: tz];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"StartDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[due setTimeZone: tz];
|
||||
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
@@ -223,29 +182,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setPriority: @"9"];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- normal, 1- personal, 2- private and 3-confidential
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"Sensitivity"]))
|
||||
{
|
||||
switch ([o intValue])
|
||||
{
|
||||
case 2:
|
||||
[self setAccessClass: @"PRIVATE"];
|
||||
break;
|
||||
case 3:
|
||||
[self setAccessClass: @"CONFIDENTIAL"];
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
[self setAccessClass: @"PUBLIC"];
|
||||
}
|
||||
}
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"ReminderTime"]))
|
||||
{
|
||||
|
||||
+1
-13
@@ -26,23 +26,12 @@ Alias /SOGo/WebServerResources/ \
|
||||
## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and
|
||||
## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section
|
||||
## below.
|
||||
#
|
||||
## For full proxy-side authentication:
|
||||
#<Location /SOGo>
|
||||
# AuthType XXX
|
||||
# Require valid-user
|
||||
# SetEnv proxy-nokeepalive 1
|
||||
# Allow from all
|
||||
#</Location>
|
||||
#
|
||||
## For proxy-side authentication only for CardDAV and GroupDAV from external
|
||||
## clients:
|
||||
#<Location /SOGo/dav>
|
||||
# AuthType XXX
|
||||
# Require valid-user
|
||||
# SetEnv proxy-nokeepalive 1
|
||||
# Allow from all
|
||||
#</Location>
|
||||
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
@@ -75,8 +64,7 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
||||
|
||||
## When using proxy-side autentication, you need to uncomment and
|
||||
## adjust the following line:
|
||||
RequestHeader unset "x-webobjects-remote-user"
|
||||
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" env=REMOTE_USER
|
||||
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e"
|
||||
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
all: $(patsubst %.asciidoc,%.pdf,$(wildcard *.asciidoc))
|
||||
|
||||
%.pdf : %.asciidoc
|
||||
asciidoc -a docinfo1 -b docbook -d book -o $<.docbook $<
|
||||
asciidoc -a docinfo1 -b docbook -d book -d book -o $<.docbook $<
|
||||
fop -c fonts/fop-config.xml -xsl docbook/xsl/sogo-fo.xsl -xml $<.docbook -pdf $@
|
||||
|
||||
clean:
|
||||
|
||||
@@ -80,12 +80,6 @@ and others)
|
||||
* SMTP server (Postfix, Sendmail and others)
|
||||
* IMAP server (Courier, Cyrus IMAP Server, Dovecot and others)
|
||||
|
||||
If you plan to use ActiveSync, an IMAP server supporting the ACL,
|
||||
UIDPLUS, QRESYNC, ANNOTATE (or X-GUID) IMAP extensions is required,
|
||||
such as Cyrus IMAP version 2.4 or later, or Dovecot version
|
||||
2.1 or later. If your current IMAP server does not support these
|
||||
extensions, you can use Dovecot's proxying capabilities.
|
||||
|
||||
In this guide, we assume that all those components are running on the
|
||||
same server (i.e., `localhost` or `127.0.0.1`) that SOGo will be
|
||||
installed on.
|
||||
@@ -167,9 +161,9 @@ Operating System Requirements
|
||||
The following 32-bit and 64-bit operating systems are currently
|
||||
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)
|
||||
* Red Hat Enterprise Linux (RHEL) Server 5 and 6
|
||||
* Community ENTerprise Operating System (CentOS) 5 and 6
|
||||
* Debian GNU/Linux 5.0 (Lenny) to 7.0 (Wheezy)
|
||||
* Ubuntu 10.04 (Lucid) to 14.04 (Trusty)
|
||||
|
||||
Make sure the required components are started automatically at boot time
|
||||
@@ -194,13 +188,12 @@ Installation
|
||||
|
||||
This section will guide you through the installation of SOGo together
|
||||
with its dependencies. The steps described here apply to an RPM-based
|
||||
installation for a Red Hat or CentOS 6 distribution. Most of these steps
|
||||
should apply to all supported operating systems.
|
||||
installation for a Red Hat or CentOS distribution.
|
||||
|
||||
Software Downloads
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SOGo can be installed using the `yum` utility. To do so, first create
|
||||
SOGo can be installed using the+yum+utility. To do so, first create
|
||||
the `/etc/yum.repos.d/inverse.repo` configuration file with the following
|
||||
content:
|
||||
|
||||
@@ -265,13 +258,13 @@ In SOGo, the user's applications settings are stored
|
||||
in `/etc/sogo/sogo.conf`. You can use your favourite text editor to
|
||||
modify the file.
|
||||
|
||||
The `sogo.conf` file is a serialized _property list_. This simple format
|
||||
The +sogo.conf+ file is a serialized _property list_. This simple format
|
||||
encapsulates four basic data types: arrays, dictionaries (or hashes),
|
||||
strings and numbers. Numbers are represented as-is, except for booleans
|
||||
which can take the unquoted values `YES` and `NO`. Strings are not
|
||||
mandatorily quoted, but doing so will avoid you many problems. A
|
||||
dictionary is a sequence of key and value pairs separated in their
|
||||
middle with a `=` sign. It starts with a `{` and ends with a
|
||||
middle with a `=` sign. It starts with a `\{` and ends with a
|
||||
corresponding `}`. Each value definition in a dictionary ends with a
|
||||
semicolon. An array is a chain of values starting with `(` and ending
|
||||
with `)`, where the values are separated with a `,`. Also, the file
|
||||
@@ -280,11 +273,6 @@ is not required, only recommended. Block comments are delimited by `/*`
|
||||
and `*/` and can span multiple lines while line comments must start with
|
||||
`//`.
|
||||
|
||||
The configuration must be contained in a root dictionary, thus be completely
|
||||
wrapped within curly brackets `{ [configuration] }`. If SOGo refuses to
|
||||
start due to syntax errors in its configuration file, `plparse` is helpful
|
||||
for finding these, as it indicates the line containing the problem.
|
||||
|
||||
Preferences Hierarchy
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -437,42 +425,29 @@ Defaults to `YES` when unset.
|
||||
|The location of the SSL private key file on the filesystem that is used
|
||||
by SOGo to sign and encrypt communications with the SAML2 identity
|
||||
provider. This file must be generated for each running SOGo service
|
||||
(rather than host). Make sure this file is readable by the SOGo user.
|
||||
(rather than host).
|
||||
|
||||
|S |SOGoSAML2CertiticateLocation
|
||||
|The location of the SSL certificate file. This file must be generated
|
||||
for each running SOGo service. Make sure this file is readable by the SOGo user.
|
||||
for each running SOGo service.
|
||||
|
||||
|S |SOGoSAML2IdpMetadataLocation
|
||||
|The location of the metadata file that describes the services available
|
||||
on the SAML2 identify provider. The content of this file is usually generated
|
||||
directly by your SAML 2.0 IdP solution. For example, using SimpleSAMLphp, you
|
||||
can get the metadata directly from https://MYSERVER/simplesaml/saml2/idp/metadata.php
|
||||
Make sure this file is readable by the SOGo user.
|
||||
on the SAML2 identify provider.
|
||||
|
||||
|S |SOGoSAML2IdpPublicKeyLocation
|
||||
|The location of the SSL public key file on the filesystem that is used
|
||||
by SOGo to sign and encrypt communications with the SAML2 identity
|
||||
provider. This file should be part of the setup of your identity
|
||||
provider. Make sure this file is readable by the SOGo user.
|
||||
provider.
|
||||
|
||||
|S |SOGoSAML2IdpCertificateLocation
|
||||
|The location of the SSL certificate file. This file should be part of
|
||||
the setup of your identity provider. Make sure this file is readable by the SOGo user.
|
||||
|
||||
|S |SOGoSAML2LoginAttribute
|
||||
|The attribute provided by the IdP to identify the user in SOGo.
|
||||
the setup of your identity provider.
|
||||
|
||||
|S |SOGoSAML2LogoutEnabled
|
||||
|Boolean value indicated whether the "Logout" link is enabled when using
|
||||
SAML2 as authentication mechanism. When using this feature, SOGo will invoke
|
||||
the IdP to proceed with the logout procedure. When the user clicks on the logout
|
||||
button, a redirection will be made to the IdP to trigger the logout.
|
||||
|
||||
|S |SOGoSAML2LogoutURL
|
||||
|The URL to which redirect the user after the "Logout" link is clicked.
|
||||
SOGoSAML2LogoutEnabled must be set to YES. If unset, the user will be
|
||||
redirected to a blank page.
|
||||
SAML2 as authentication mechanism.
|
||||
|
||||
|D |SOGoTimeZone
|
||||
|Parameter used to set a default time zone for users. The default
|
||||
@@ -567,29 +542,17 @@ usernames like this: `SOGoSuperUsernames = (<username1>[, <username2>, ...]);`
|
||||
for SOGo. Possible values are:
|
||||
|
||||
[options="compact"]
|
||||
* `Arabic`
|
||||
* `Basque`
|
||||
* `BrazilianPortuguese`
|
||||
* `Catalan`
|
||||
* `Czech`
|
||||
* `Danish`
|
||||
* `Dutch`
|
||||
* `English`
|
||||
* `Finnish`
|
||||
* `French`
|
||||
* `German`
|
||||
* `Hungarian`
|
||||
* `Icelandic`
|
||||
* `Italian`
|
||||
* `NorwegianBokmal`
|
||||
* `NorwegianNynorsk`
|
||||
* `Polish`
|
||||
* `Russian`
|
||||
* `Slovak`
|
||||
* `SpanishSpain`
|
||||
* `SpanishArgentina`
|
||||
* `Spanish`
|
||||
* `Swedish`
|
||||
* `Ukrainian`
|
||||
* `Welsh`
|
||||
|
||||
|D |SOGoNotifyOnPersonalModifications
|
||||
@@ -662,7 +625,7 @@ of 1 day by default.
|
||||
|Parameter used to configure which languages are available from SOGo's
|
||||
Web interface. Available languages are specified as an array of string.
|
||||
|
||||
The default value is: `( "Arabic", "Basque", "Catalan", "Czech", "Dutch", "Danish", "Welsh", "English", "SpanishSpain", "SpanishArgentina", "Finnish", "French", "German", "Icelandic", "Italian", "Hungarian", "BrazilianPortuguese", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak", "Ukrainian", "Swedish" )`
|
||||
The default value is: `( "Czech", "Welsh", "English", "Spanish", "French", "German", "Italian", "Hungarian", "Dutch", "BrazilianPortuguese", "Polish", "Russian", Ukrainian", "Swedish" )`
|
||||
|
||||
|D |SOGoHideSystemEMail
|
||||
|Parameter used to control if SOGo should hide or not the system email
|
||||
@@ -856,7 +819,7 @@ source:
|
||||
|
||||
[cols="^3,>47,50a"]
|
||||
|=======================================================================
|
||||
.34+|D <|SOGoUserSources
|
||||
.33+|D <|SOGoUserSources
|
||||
|Parameter used to set the LDAP and/or SQL sources used for
|
||||
authentication and global address books. Multiple sources can be
|
||||
specified as an array of dictionaries. A dictionary that defines an LDAP
|
||||
@@ -924,8 +887,7 @@ resource if the entry has the calendarresource objectClass set.
|
||||
to which a resource can be part of at any point in time.
|
||||
|
||||
If this is set to `0`, or if the attribute is missing, it means no
|
||||
limit. If set to `-1`, no limit is imposed but the resource will
|
||||
be marked as busy the first time it is booked.
|
||||
limit.
|
||||
|
||||
|filter (optional)
|
||||
|The filter to use for LDAP queries, it should be defined as an
|
||||
@@ -1012,17 +974,7 @@ context as Dovecot stores in its database.
|
||||
|passwordPolicy
|
||||
|If set to `YES`, SOGo will use the extended LDAP Password Policies
|
||||
attributes. If you LDAP server does not support those and you activate
|
||||
this feature, every LDAP requests will fail. Note that some LDAP servers
|
||||
require LDAP/SSL for password policies to work. This is the case for
|
||||
example with 389 Directory Server.
|
||||
|
||||
|updateSambaNTLMPasswords
|
||||
|If set to `YES`, SOGo will automatically update the sambaNTPassword
|
||||
and sambaLMPassword attributes when changing passwords. The attributes
|
||||
must be called sambaNTPassword and sambaLMPassword. You must also make
|
||||
sure the correct ACL is set in your LDAP server to allow users to change
|
||||
their own sambaNTPassword and sambaLMPassword password attributes.
|
||||
Defaults to `NO` when unset.
|
||||
this feature, every LDAP requests will fail.
|
||||
|
||||
|isAddressBook
|
||||
|If set to `YES`, this LDAP source is used as a shared address book
|
||||
@@ -1035,8 +987,8 @@ repository
|
||||
|
||||
|ModulesConstraints (optional)
|
||||
|Limits the access of any module through a constraint based on an LDAP
|
||||
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`,
|
||||
and/or `ActiveSync` for example:
|
||||
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`, for
|
||||
example:
|
||||
|
||||
----
|
||||
ModulesConstraints = {
|
||||
@@ -1061,11 +1013,6 @@ of supported attributes.
|
||||
user addressbooks (see _abOU_ below), this list of object classes will
|
||||
be applied to new records as they are created.
|
||||
|
||||
|GroupObjectClasses
|
||||
|A list (array) of names identifying groups within the LDAP source. If not
|
||||
set, SOGo will use `group`, `groupofnames`, `groupofuniquenames`
|
||||
and `posixgroup`.
|
||||
|
||||
|modifiers
|
||||
|A list (array) of usernames that are authorized to perform
|
||||
modifications to the address book defined by this LDAP source.
|
||||
@@ -1220,7 +1167,7 @@ keytool -import -keystore /etc/ssl/certs/java/cacerts \
|
||||
|
||||
*The certificate used by the CAS server must also be trusted by SOGo.*
|
||||
In case of a self-signed certificate, this means exporting tomcat's
|
||||
certificate using the `keytool` utility, converting it to PEM format and
|
||||
certificate using the +keytool+utility, converting it to PEM format and
|
||||
appending it to the `ca-certificates.crt` file (the name and location of
|
||||
that file differs between distributions). Basically:
|
||||
|
||||
@@ -1283,32 +1230,13 @@ documentation of your identity provider and the SAML2 configuration keys
|
||||
that are listed above for proper setup. Once a SOGo instance is
|
||||
configured properly, the metadata for that instance can be retrieved
|
||||
from `http://<hostname>/SOGo/saml2-metadata` for registration with the
|
||||
identity provider. SOGo will dynamically generate the metadata based on
|
||||
the SOGoSAML2CertificateLocation's content and the SOGo server name.
|
||||
|
||||
When using SimpleSAMLphp, make sure the convert OID to names by modifying your
|
||||
`metadata/saml20-idp-hosted.php` to contain something like this:
|
||||
|
||||
----
|
||||
'attributes.NameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
|
||||
'authproc' => array(
|
||||
100 => array('class' => 'core:AttributeMap', 'oid2name'),
|
||||
),
|
||||
----
|
||||
|
||||
If you want to test the IdP-initiated logout using SimpleSAMLphp, you can do so by opening
|
||||
the following URL:
|
||||
|
||||
----
|
||||
https://idp.example.org/simplesaml/saml2/idp/SingleLogoutService.php?ReturnTo=www.sogo.nu
|
||||
----
|
||||
identity provider.
|
||||
|
||||
In order to relay authentication information to your IMAP server and if
|
||||
you make use of the CrudeSAML SASL plugin, you need to make sure that
|
||||
_NGImap4AuthMechanism_ is configured to use the `SAML` mechanism. If you
|
||||
make use of the CrudeSAML PAM plugin, this value may be left empty.
|
||||
|
||||
|
||||
Database Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1355,27 +1283,27 @@ The following table describes the parameters that were set:
|
||||
|
||||
[cols="3,47,50a"]
|
||||
|=======================================================================
|
||||
|S |SOGoProfileURL
|
||||
|D |SOGoProfileURL
|
||||
|Parameter used to set the database URL so that SOGo can retrieve user
|
||||
profiles.
|
||||
|
||||
For MySQL, set the database URL to something like:
|
||||
`mysql://sogo:sogo@localhost:3306/sogo/sogo_user_profile`.
|
||||
|
||||
|S |OCSFolderInfoURL
|
||||
|D |OCSFolderInfoURL
|
||||
|Parameter used to set the database URL so that SOGo can retrieve the
|
||||
location of user folders (address books and calendars).
|
||||
|
||||
For Oracle, set the database URL to something like:
|
||||
`oracle://sogo:sogo@localhost:1526/sogo/sogo_folder_info`.
|
||||
|
||||
|S |OCSSessionsFolderURL
|
||||
|D |OCSSessionsFolderURL
|
||||
|Parameter used to set the database URL so that SOGo can store and
|
||||
retrieve secured user sessions information. For PostgreSQL, the database
|
||||
URL could be set to something like:
|
||||
`postgresql://sogo:sogo@localhost:5432/sogo/sogo_sessions_folder`.
|
||||
|
||||
|S |OCSEMailAlarmsFolderURL
|
||||
|D |OCSEMailAlarmsFolderURL
|
||||
|Parameter used to set the database URL for email-based alarms (that can
|
||||
be set on events and tasks). This parameter is relevant only if
|
||||
_SOGoEnableEMailAlarms_ is set to `YES`. For PostgreSQL, the database
|
||||
@@ -1505,10 +1433,7 @@ case, SOGo will consider the returned entry to be a resource.
|
||||
which a resource can be part of at any point in time.
|
||||
|
||||
If this is set to `0`, or if the attribute is missing, it means no
|
||||
limit and the resource will always be marked as free. If set to `-1`,
|
||||
no limit is imposed but the resource will be marked as busy the first
|
||||
time it is booked. If greater than 0, the resource will get marked as
|
||||
busy once it reaches the value.
|
||||
limit.
|
||||
|
||||
|DomainFieldName (optional)
|
||||
|If set, SOGo will use the value of that field as the domain associated
|
||||
@@ -1656,8 +1581,8 @@ Note that TLS is supported but SSL is not.
|
||||
|
||||
|D |SOGoSieveFolderEncoding
|
||||
|Parameter used to specify which encoding is used for IMAP folder names
|
||||
in Sieve filters. Defaults to `UTF-7`. The other possible value is
|
||||
`UTF-8`.
|
||||
in Sieve filters. Defaults to "UTF-7". The other possible value is
|
||||
"UTF-8".
|
||||
|
||||
|U |SOGoMailShowSubscribedFoldersOnly
|
||||
|Parameter used to specify if the Web interface should only show
|
||||
@@ -1707,19 +1632,6 @@ cronjob `sogo-tmpwatch`.
|
||||
|
||||
Defaults to `/var/spool/sogo`.
|
||||
|
||||
|S |NGMimeBuildMimeTempDirectory
|
||||
|Parameter used to set the path where temporary files will be stored
|
||||
by SOPE when dealing with MIME messages.
|
||||
|
||||
Defaults to `/tmp`.
|
||||
|
||||
|
||||
|S |NGImap4DisableIMAP4Pooling
|
||||
|Disables IMAP pooling when set to `YES`. Enable pooling by setting to
|
||||
`NO` or using a caching proxy like imapproxy.
|
||||
|
||||
The default value is `YES`.
|
||||
|
||||
|S |NGImap4ConnectionStringSeparator
|
||||
|Parameter used to set the IMAP mailbox separator. Setting this will
|
||||
also have an impact on the mailbox separator used by Sieve filters.
|
||||
@@ -1732,7 +1644,7 @@ SASL mechanism. Please note that feature might be limited at this time.
|
||||
|
||||
|D |NGImap4ConnectionGroupIdPrefix
|
||||
|Prefix to prepend to names in IMAP ACL transactions, to indicate the
|
||||
name is a group name, not a user name.
|
||||
name is a group name not a user name.
|
||||
|
||||
RFC4314 gives examples where group names are prefixed with `$`. Dovecot,
|
||||
for one, follows this scheme, and will, for example, apply permissions
|
||||
@@ -1817,12 +1729,6 @@ host.
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|
||||
|D |SOGoForwardConstraints
|
||||
|Parameter used to set constraints on possible addresses used when
|
||||
automatically forwarding mails. When set to `0` (default), no constraint
|
||||
is enforced. When set to `1`, only internal domains can be used. When
|
||||
set to `2`, only external domains can be used.
|
||||
|
||||
|D |SOGoSieveScriptsEnabled
|
||||
|Parameter used to activate the edition from the preferences windows of
|
||||
server-side mail filters. Requires Sieve script support on the IMAP
|
||||
@@ -1920,11 +1826,10 @@ events. This parameter is an array of arbitrary strings.
|
||||
|
||||
Defaults to a list that depends on the language.
|
||||
|
||||
|U |SOGoCalendarCategoriesColors
|
||||
|Parameter used to define the colour of categories. This parameter
|
||||
is a dictionary of category name/color.
|
||||
|U |SOGoCalendarDefaultCategoryColor
|
||||
|Parameter used to define the default colour of categories.
|
||||
|
||||
Defaults to `#F0F0F0` for all categories when unset.
|
||||
Defaults to `#F0F0F0` when unset.
|
||||
|
||||
|U |SOGoCalendarEventsDefaultClassification
|
||||
|Parameter used to defined the default classification for new events.
|
||||
@@ -2088,7 +1993,7 @@ Defaults to `NO` when unset.
|
||||
SOGo Configuration Summary
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The complete SOGo configuration file `/etc/sogo/sogo.conf` should look
|
||||
The complete SOGo configuration file+/etc/sogo/sogo.conf+should look
|
||||
like this:
|
||||
|
||||
----
|
||||
@@ -2137,8 +2042,7 @@ Multi-domains Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want your installation to isolate two groups of users, you must
|
||||
define a distinct authentication source for each _domain_. Your domain keys
|
||||
must have the same value as your email domain you want to add. Following is
|
||||
define a distinct authentication source for each _domain_. Following is
|
||||
the same configuration that now includes two domains (acme.com and
|
||||
coyote.com):
|
||||
|
||||
@@ -2146,7 +2050,7 @@ coyote.com):
|
||||
{
|
||||
...
|
||||
domains = {
|
||||
acme.com = {
|
||||
acme = {
|
||||
SOGoMailDomain = acme.com;
|
||||
SOGoDraftsFolderName = Drafts;
|
||||
SOGoUserSources = (
|
||||
@@ -2167,7 +2071,7 @@ coyote.com):
|
||||
}
|
||||
);
|
||||
};
|
||||
coyote.com = {
|
||||
coyote = {
|
||||
SOGoMailDomain = coyote.com;
|
||||
SOGoIMAPServer = imap.coyote.com;
|
||||
SOGoUserSources = (
|
||||
@@ -2198,7 +2102,7 @@ domains.
|
||||
[cols="3,47,50a"]
|
||||
|=======================================================================
|
||||
|S |SOGoEnableDomainBasedUID
|
||||
|Parameter used to enable user identification by domain. Users will be
|
||||
|Parameter used to activate user identification by domain. Users will be
|
||||
able (without being required) to login using the form `username@domain`,
|
||||
meaning that values of _UIDFieldName_ no longer have to be unique among
|
||||
all domains but only within the same domain. Internally, users will
|
||||
@@ -2387,8 +2291,8 @@ easier. These GUI utilities can make use of templates to create and
|
||||
pre-configure typical user accounts or any standardized LDAP record,
|
||||
along with the correct object classes, fields and default values.
|
||||
|
||||
Microsoft Enterprise ActiveSync
|
||||
-------------------------------
|
||||
Microsoft ActiveSync
|
||||
--------------------
|
||||
|
||||
SOGo supports the Microsoft ActiveSync protocol.
|
||||
|
||||
@@ -2421,7 +2325,7 @@ ActiveSync:
|
||||
|Parameter used to set the maximum amount of time, in seconds, SOGo will
|
||||
wait before replying to a Ping command.
|
||||
|
||||
If not set, it defaults to `10` seconds.
|
||||
If not set, it defaults to `5` seconds.
|
||||
|
||||
|S |SOGoMaximumSyncInterval
|
||||
|Parameter used to set the maximum amount of time, in seconds, SOGo will
|
||||
@@ -2432,20 +2336,10 @@ If not set, it defaults to `30` seconds.
|
||||
|S |SOGoInternalSyncInterval
|
||||
|Parameter used to set the maximum amount of time, in seconds, SOGo will
|
||||
wait before doing an internal check for data changes (add, delete, and
|
||||
update). This parameter must be lower than _SOGoMaximumSyncInterval_ and
|
||||
_SOGoMaximumPingInterval_.
|
||||
update). This parameter must be lower than _SOGoMaximumSyncInterval_.
|
||||
|
||||
If not set, it defaults to `10` seconds.
|
||||
|
||||
|S |SOGoMaximumSyncResponseSize
|
||||
|Parameter used to overwrite the maximum response size during
|
||||
a Sync operation. The value is in kilobytes. Setting this to 512
|
||||
means the response size will be of 524288 bytes or less. Note that
|
||||
if you set the value too low and a mail message (or any other object)
|
||||
surpasses it, it will still be synced but only this item will be.
|
||||
|
||||
Defaults to `0`, which means no overwrite is performed.
|
||||
|
||||
|S |SOGoMaximumSyncWindowSize
|
||||
|Parameter used to overwrite the maximum number of items returned during
|
||||
a Sync operation.
|
||||
@@ -2454,31 +2348,28 @@ Defaults to `0`, which means no overwrite is performed.
|
||||
|
||||
Setting this parameter to a value greater than `512` will
|
||||
have unexpected behaviour with various ActiveSync clients.
|
||||
|S |SOGoEASDebugEnabled
|
||||
|Parameter used to log the complete request and response of every single
|
||||
EAS command.
|
||||
|
||||
Defaults to `NO`, which means no logging is performed.
|
||||
|=======================================================================
|
||||
|
||||
Please be aware of the following limitations:
|
||||
|
||||
* Currently, only the personal calendar and address book are
|
||||
synchronized. Adding support for all folders is planned.
|
||||
* When creating an Outlook 2013 profile, you must actually kill Outlook
|
||||
before the end of the creation process. See http://www.vionblog.com/connect-zimbra-community-with-outlook-2013
|
||||
for a procedure example.
|
||||
* Outlook 2013 does not search the GAL. One possible alternative
|
||||
solution is to configure Outlook to use a LDAP server (over SSL) with
|
||||
authentication. Outlook 2013 also does not seem to support multiple
|
||||
address books over ActiveSync.
|
||||
* To successfully synchronize Outlook email categories, a corresponding
|
||||
mail label (Preferences->Mail Options) has to be created manually in SOGo
|
||||
for each label defined in Outlook. The name in SOGo and in Outlook must be
|
||||
identical.
|
||||
authentication. Alternatively, when supporting more than just the
|
||||
personal address book, we'll also be able to expose the LDAP/SQL based
|
||||
address books in SOGo over ActiveSync.
|
||||
* Make sure you do not use a self-signed certificate. While this will
|
||||
work, Outlook will work intermittently as it will raise popups for
|
||||
certificate validation, sometimes in background, preventing the user to
|
||||
see the warning and thus, preventing any synchronization to happen.
|
||||
* ActiveSync clients keep connections open for a while. Each connection
|
||||
will grab a hold on a sogod process so you will need a lot of processes
|
||||
to handle many clients. Make sure you tune your SOGo server when having
|
||||
lots of ActiveSync clients.
|
||||
to handle many clients. This limitation will eventually be overcome in
|
||||
SOGo.
|
||||
* Repetitive events with occurrences exceptions are currently not
|
||||
supported.
|
||||
* Outlook 2013 Autodiscovery is currently not supported.
|
||||
@@ -2488,10 +2379,6 @@ see http://support.microsoft.com/kb/291621 for configuration
|
||||
instructions. On the SOGo side, _SOGoEnablePublicAccess_ must be set to
|
||||
`YES` and the URL to use must be of the following format:
|
||||
`http://<hostname>/SOGo/dav/public/%NAME%/freebusy.ifb`
|
||||
* If you have very large mail folders (thousands of messages), you will
|
||||
need to adjust the word size of your IMAP server. In Dovecot, the parameter
|
||||
to increase is "imap_max_line_length" while under Cyrus IMAP Server, the
|
||||
parameter is "maxword". We suggest a buffer of 2MB.
|
||||
|
||||
In order to use the SOGo ActiveSync support code in production
|
||||
environments, you need to get a proper usage license from Microsoft.
|
||||
@@ -2500,90 +2387,13 @@ user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
|
||||
and send an email to iplicreq@microsoft.com
|
||||
|
||||
Inverse inc. provides this software for free, but is not responsible for
|
||||
anything related to its usage.
|
||||
|
||||
Microsoft Enterprise ActiveSync Tuning
|
||||
--------------------------------------
|
||||
|
||||
First of all, it is important to know that most EAS devices will keep
|
||||
HTTP connections open to SOGo (and thus, Apache) for a long time. This
|
||||
is required for "push" to work properly. Connections can stay open for
|
||||
up to one hour, or 3600 seconds.
|
||||
|
||||
The first parameter to check is related to Apache's proxying to
|
||||
SOGo:
|
||||
|
||||
----
|
||||
ProxyPass /Microsoft-Server-ActiveSync \
|
||||
http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
|
||||
retry=60 connectiontimeout=5 timeout=360
|
||||
----
|
||||
|
||||
The above line sets a timeout for up to 360 seconds, or 6 minutes. If
|
||||
you want to let EAS clients keep their HTTP connections open for up
|
||||
to an hour, you must change the timeout parameter and set it to 3600.
|
||||
|
||||
If you change this value, the WOWatchDogRequestTimeout parameter must be changed
|
||||
accordingly in SOGo's configuration file (/etc/sogo/sogo.conf). By default,
|
||||
a SOGo child process is allowed to handle a request that can take up
|
||||
to 10 minutes before it gets killed by its parent process. When using
|
||||
EAS "push", the client expects to keep its connection open for up to one
|
||||
hour - so the WOWatchDogRequestTimeout, which is set in minutes,
|
||||
must be adjusted accordingly.
|
||||
|
||||
EAS clients will keep HTTP connections open for a long time
|
||||
during these two EAS commands: Ping and Sync. By default, SOGo will prevent
|
||||
EAS clients from keeping connections for a long time. This is to avoid the
|
||||
situation where all SOGo child processes would be monopolized by EAS clients -
|
||||
rendering the SOGo web interface or DAV interface unavailable. The
|
||||
default SOGo behavior is thus similar to disable EAS push entirely.
|
||||
|
||||
Two SOGo configuration parameters are available to modify this behavior:
|
||||
SOGoMaximumPingInterval (set by default to 10 seconds) and
|
||||
SOGoMaximumSyncInterval (set by default to 30 seconds). If you want
|
||||
connection to stay open for up to one hour, you should set these
|
||||
slightly under 3600 seconds (say 3540 - or 59 minutes). During a
|
||||
long-lived HTTP connection, the SOGo child process will perform
|
||||
internal polling to detect changes and return them to the EAS client
|
||||
if any changes are found. The parameter used to control this
|
||||
is SOGoInternalSyncInterval. By default, polling is done every 10
|
||||
seconds. This might generate too much load on large-scale system.
|
||||
|
||||
The last configuration parameter to adjust is WOWorkersCount - which sets the
|
||||
number of SOGo child process that will be used to handle requests.
|
||||
You should have at least one child per EAS device configured to use
|
||||
"push". You must also have more children than you have EAS devices
|
||||
configured to use "push" - in order to handle normal SOGo requests to
|
||||
its Web or DAV interfaces.
|
||||
|
||||
Here are some usage examples for EAS devices using "push". In all
|
||||
cases, the Apache timeout is set to 3600 and the
|
||||
WOWatchDogRequestTimeout parameter is set to 60.
|
||||
|
||||
Example 1 - 100 users, 10 EAS devices:
|
||||
|
||||
----
|
||||
WOWorkersCount = 15;
|
||||
SOGoMaximumPingInterval = 3540;
|
||||
SOGoMaximumSyncInterval = 3540;
|
||||
SOGoInternalSyncInterval = 30;
|
||||
----
|
||||
|
||||
Example 2 - 1000 users, 100 EAS devices:
|
||||
|
||||
----
|
||||
WOWorkersCount = 120;
|
||||
SOGoMaximumPingInterval = 3540;
|
||||
SOGoMaximumSyncInterval = 3540;
|
||||
SOGoInternalSyncInterval = 60;
|
||||
----
|
||||
|
||||
|
||||
Using SOGo
|
||||
----------
|
||||
|
||||
@@ -2703,7 +2513,7 @@ any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
|
||||
2013 is also supported.
|
||||
|
||||
The Microsoft ActiveSync server URL is generally something
|
||||
like: `http://localhost/Microsoft-Server-ActiveSync`.
|
||||
like: `http://localhost/Microsoft-Active-Sync`.
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
@@ -2713,21 +2523,6 @@ current version of SOGo from the previous release.
|
||||
|
||||
[cols="100a"]
|
||||
|=======================================================================
|
||||
h|2.3.1
|
||||
|The SOGoCalendarDefaultCategoryColor default has been removed. If you
|
||||
want to customize the color of calendar categories, use the
|
||||
SOGoCalendarCategories and SOGoCalendarCategoriesColors defaults.
|
||||
|
||||
h|2.3.0
|
||||
|Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or
|
||||
`sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL).
|
||||
|
||||
This will grow the "participant states" field of calendar quick tables to a larger
|
||||
size and add the the "c_description" column to calendar quick tables.
|
||||
|
||||
Moreover, if you are using a multi-domain configuration, make sure the keys for
|
||||
your domains match the email domains you have defined.
|
||||
|
||||
h|2.2.8
|
||||
|The configuration configuration parameters were renamed:
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ https://addons.mozilla.org/en-US/thunderbird/addon/lightning/
|
||||
When using version 17 ESR of Thunderbird, you can use Mozilla Lightning
|
||||
version 1.9.1. In order to download the extension, please visit:
|
||||
|
||||
https://addons.mozilla.org/en-US/thunderbird/addon/lightning/versions/1.9.1
|
||||
https://addons.mozilla.org/en-US/thunderbird/addon/lightning/versions/?page=1#version-1.9.1
|
||||
|
||||
For Thunderbird 2 and 3.1, you need the _Inverse Edition_of Mozilla
|
||||
Lightning. It provides back ported features, bug fixes and security
|
||||
|
||||
@@ -233,14 +233,14 @@ Installation
|
||||
This section will guide you through the installation of the native
|
||||
Microsoft Outlook compatibility layer SOGo offers.
|
||||
|
||||
////
|
||||
Red Hat Enterprise Linux v6 x86_64
|
||||
Red Hat Enterprise Linux v5 and v6
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are using Red Hat Enterprise Linux version 6 x86_64, packages
|
||||
for Samba 4, OpenChange and SOGo and the SOGo OpenChange backend are
|
||||
available from SOGo's web site. Please follow the instructions from
|
||||
http://www.sogo.nu/english/downloads/backend.html.
|
||||
If you are using Red Hat Enterprise Linux (or CentOS) version 5 or
|
||||
version 6, packages for Samba 4, OpenChange and SOGo and the SOGo
|
||||
OpenChange backend are available from SOGo's web site. Please follow the
|
||||
instructions from
|
||||
http://www.sogo.nu/english/downloads/backend_nightly.html.
|
||||
|
||||
In order to satisfy certain dependencies, you should also add the EPEL
|
||||
source corresponding to your distribution and architecture. More
|
||||
@@ -253,46 +253,53 @@ installation:
|
||||
|
||||
----
|
||||
yum clean all && yum makecache
|
||||
yum install samba \
|
||||
yum install samba4 \
|
||||
openchange \
|
||||
sogo-openchange-backend \
|
||||
openchange-ocsmanager \
|
||||
openchange-rpcproxy \
|
||||
mysql-server \
|
||||
MySQL-python
|
||||
openchange-rpcproxy
|
||||
----
|
||||
|
||||
Once the packages are installed, refer to the _Configuration_ chapter
|
||||
from this guide.
|
||||
|
||||
[NOTE]
|
||||
Samba4/OpenChange are not available for now on CentOS 5 i386/x86_64,
|
||||
and CentOS 7. On Debian-based systems, packages are available only on
|
||||
the x86_64 platform.
|
||||
Debian 6.0 (Squeeze) and Ubuntu 12.04 (Precise Pangolin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Debian 7 (Wheezy) and Ubuntu 12.04 (Precise Pangolin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SOGo, OpenChange and the SOGo OpenChange backend are now
|
||||
Samba 4, OpenChange, SOGo and the SOGo OpenChange backend are now
|
||||
available from SOGo's web site. Please follow the instructions from
|
||||
http://www.sogo.nu/english/downloads/backend.html to setup your
|
||||
http://www.sogo.nu/english/downloads/backend_nightly.html to setup your
|
||||
apt sources.
|
||||
|
||||
On Debian7, for some Samba 4 dependencies, you need to use the
|
||||
_wheezy-backports_ repository. To do so, create
|
||||
`/etc/apt/sources.list.d/backports.list` with the following
|
||||
content:
|
||||
Debian Squeeze ships an older version of some libraries required by
|
||||
Samba 4. In order to workaround this, users of this distribution will
|
||||
have to use the _squeeze-backports_ repository. To do so, create
|
||||
`/etc/apt/sources.list.d/backports.list`:
|
||||
|
||||
deb http://http.debian.net/debian wheezy-backports main
|
||||
deb http://backports.debian.org/debian-backports squeeze-backports main
|
||||
|
||||
Then install Samba 4 on top of an existing SOGo
|
||||
Then install the dependencies on Debian Squeeze, do:
|
||||
|
||||
----
|
||||
apt-get update
|
||||
apt-get install -t squeeze-backports libwbclient-dev samba-common smbclient libsmbclient libsmbclient-dev
|
||||
----
|
||||
|
||||
Once ready, install the `samba4` package on top of an existing SOGo
|
||||
installation:
|
||||
|
||||
----
|
||||
apt-get update
|
||||
apt-get install samba samba-dev
|
||||
apt-get install samba4
|
||||
----
|
||||
|
||||
The current post installation script shipped with the Samba 4 package is
|
||||
far from perfect and might fail even on a fresh install. The following
|
||||
command is needed to let dpkg know that everything is fine about Samba 4
|
||||
if the post install script fails.
|
||||
|
||||
sed --in-place 'N; s/Package: samba4\nStatus: install ok half-configured/Package: samba4\nStatus: install ok installed/;' /var/lib/dpkg/status
|
||||
|
||||
Once completed, install the packages related to OpenChange and the SOGo
|
||||
provider:
|
||||
|
||||
@@ -300,70 +307,13 @@ provider:
|
||||
apt-get install openchangeserver \
|
||||
sogo-openchange \
|
||||
openchangeproxy \
|
||||
python-ocsmanager \
|
||||
mysql-server \
|
||||
python-mysqldb \
|
||||
openchange-ocsmanager \
|
||||
openchange-rpcproxy \
|
||||
python-sievelib \
|
||||
python-spyne \
|
||||
python-rpclib
|
||||
openchange-ocsmanager \
|
||||
openchange-rpcproxy
|
||||
----
|
||||
|
||||
Once the packages are installed, refer to the _Configuration_ chapter
|
||||
from this guide.
|
||||
|
||||
[NOTE]
|
||||
On Ubuntu 12.04, the Samba init scripts need to be modified to
|
||||
disable the upstart check. For more details, refer to:
|
||||
https://wiki.samba.org/index.php/Samba4/InitScript
|
||||
////
|
||||
|
||||
Debian 8 (Jessie) and Ubuntu 14.04 (Trusty Tahr)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please follow the instructions from
|
||||
http://www.sogo.nu/english/downloads/backend.html to setup your
|
||||
apt sources.
|
||||
|
||||
Then install Samba 4 on top of an existing SOGo
|
||||
installation:
|
||||
|
||||
----
|
||||
apt-get update
|
||||
apt-get install samba samba-dev
|
||||
----
|
||||
|
||||
Once completed, install the packages related to OpenChange and the SOGo
|
||||
provider:
|
||||
|
||||
----
|
||||
apt-get install openchangeserver \
|
||||
sogo-openchange \
|
||||
openchangeproxy \
|
||||
python-ocsmanager \
|
||||
mysql-server \
|
||||
python-mysqldb \
|
||||
openchange-ocsmanager \
|
||||
openchange-rpcproxy \
|
||||
python-sievelib \
|
||||
python-spyne \
|
||||
python-rpclib
|
||||
----
|
||||
|
||||
Once the packages are installed, refer to the _Configuration_ chapter
|
||||
from this guide.
|
||||
|
||||
[NOTE]
|
||||
The `ocsmanager.conf` and `rpcproxy.conf` are currently located in
|
||||
`/etc/apache2/conf.d`. These should be moved to `/etc/apache2/conf-available`.
|
||||
This is a packaging error that will soon be fixed.
|
||||
|
||||
[NOTE]
|
||||
You might have to adjust the `rpcproxy.conf` configuration file to add the
|
||||
`Require all granted` permission if you get Apache errors such as
|
||||
`client denied by server configuration`.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
@@ -396,18 +346,19 @@ samba-tool user setexpiry administrator --noexpiry
|
||||
----
|
||||
|
||||
You might consider changing the realm and domain used, to suit your
|
||||
environment.
|
||||
enviroment.
|
||||
|
||||
You might also have to
|
||||
remove `/etc/samba/smb.conf` prior running this command.
|
||||
remove `/etc/samba4/smb.conf` (or `/etc/samba/smb.conf` on Debian-based
|
||||
distributions) prior running this command.
|
||||
|
||||
Add the following parameters to the `[global]` section of the
|
||||
`/etc/samba/smb.conf` configuration file:
|
||||
`/etc/samba4/smb.conf` (`/samba/smb.conf` if you use a Debian-based
|
||||
distribution) configuration file:
|
||||
|
||||
----
|
||||
### Configuration required by OpenChange server ###
|
||||
dsdb:schema update allowed = true
|
||||
dcerpc endpoint servers = epmapper, mapiproxy, dnsserver
|
||||
dcerpc endpoint servers = +epmapper, +mapiproxy
|
||||
dcerpc_mapiproxy:server = true
|
||||
dcerpc_mapiproxy:interfaces = exchange_emsmdb, exchange_nsp, exchange_ds_rfr
|
||||
### Configuration required by OpenChange server ###
|
||||
@@ -424,7 +375,6 @@ Your Samba 4 configuration file should look like this:
|
||||
netbios name = sogo
|
||||
passdb backend = samba4
|
||||
### Configuration required by OpenChange server ###
|
||||
dsdb:schema update allowed = true
|
||||
dcerpc endpoint servers = +epmapper, +mapiproxy
|
||||
dcerpc_mapiproxy:server = true
|
||||
dcerpc_mapiproxy:interfaces = exchange_emsmdb, exchange_nsp, exchange_ds_rfr
|
||||
@@ -442,22 +392,11 @@ Your Samba 4 configuration file should look like this:
|
||||
OpenChange Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
|
||||
|
||||
First, create the OpenChange MySQL user:
|
||||
|
||||
----
|
||||
$ mysql -u root -p
|
||||
mysql> CREATE USER 'openchange-user'@'localhost' IDENTIFIED BY 'openchange$123';
|
||||
mysql> GRANT ALL PRIVILEGES ON `openchange`.* TO 'openchange-user'@'localhost' WITH GRANT OPTION;
|
||||
mysql> FLUSH PRIVILEGES;
|
||||
----
|
||||
|
||||
The Samba AD schema needs to be filled with additional object
|
||||
definitions by running the following commands:
|
||||
|
||||
----
|
||||
openchange_provision --standalone
|
||||
openchange_provision
|
||||
|
||||
NOTE: This operation can take several minutes
|
||||
[+] Step 1: Register Exchange OIDs
|
||||
@@ -471,61 +410,38 @@ NOTE: This operation can take several minutes
|
||||
[+] Step 9: Add Exchange classes to Samba schema
|
||||
[+] Step 10: Add possSuperior attributes to Exchange classes
|
||||
[+] Step 11: Extend existing Samba classes and attributes
|
||||
[+] Step 12: Generic Exchange configuration objects
|
||||
[+] Step 13: Finalize generic Exchange configuration objects
|
||||
[SUCCESS] Done!
|
||||
[+] Step 1: Exchange Samba registration
|
||||
[SUCCESS] Done!
|
||||
[+] Step 1: Register Exchange Samba as the main server
|
||||
[+] Step 12: Exchange Samba with Exchange configuration objects
|
||||
[SUCCESS] Done!
|
||||
----
|
||||
|
||||
Create the OpenChange database:
|
||||
You can safely ignore the "`ERROR: no subClassOf 'serviceAdministrationPoint' for 'rRASAdministrationConnectionPoint'`" message when running the `openchange_provision` command.
|
||||
|
||||
Provision the OpenChange database:
|
||||
|
||||
----
|
||||
openchange_provision --openchangedb --openchangedb-uri 'mysql://openchange-user:openchange$123@localhost/openchange'
|
||||
openchange_provision --openchangedb
|
||||
|
||||
Setting up openchange db
|
||||
[+] Public Folders
|
||||
===================
|
||||
* Public Folder Root : 0x0100000000000001 (72057594037927937)
|
||||
* IPM_SUBTREE : 0x0200000000000001 (144115188075855873)
|
||||
* NON_IPM_SUBTREE : 0x0300000000000001 (216172782113783809)
|
||||
* EFORMS REGISTRY : 0x0400000000000001 (288230376151711745)
|
||||
* OFFLINE ADDRESS BOOK : 0x0500000000000001 (360287970189639681)
|
||||
* /o=First Organization/cn=addrlists/cn=oabs/cn=Default Offline Address Book: 0x0600000000000001 (432345564227567617)
|
||||
* SCHEDULE+ FREE BUSY : 0x0700000000000001 (504403158265495553)
|
||||
* EX:/o=first organization/ou=first administrative group: 0x0800000000000001 (576460752303423489)
|
||||
* Events Root : 0x0900000000000001 (648518346341351425)
|
||||
* Public Folder Root 0x0100000000000001
|
||||
* IPM_SUBTREE 0x0200000000000001
|
||||
* NON_IPM_SUBTREE 0x0300000000000001
|
||||
* EFORMS REGISTRY 0x0400000000000001
|
||||
* OFFLINE ADDRESS BOOK 0x0500000000000001
|
||||
* /o=First Organization/cn=addrlists/cn=oabs/cn=Default Offline Address Book 0x0600000000000001
|
||||
* SCHEDULE+ FREE BUSY 0x0700000000000001
|
||||
* EX:/o=First Organization/ou=Exchange Administrative Group (UBUNTU-OC) 0x0800000000000001
|
||||
* Events Root 0x0900000000000001
|
||||
----
|
||||
|
||||
Finally, modify `/etc/samba/smb.conf` to specify OpenChange connection information
|
||||
for its indexing database. Add the following at the end of the `[global]` section:
|
||||
|
||||
----
|
||||
mapistore:namedproperties = mysql
|
||||
namedproperties:mysql_user = openchange-user
|
||||
namedproperties:mysql_pass = openchange$123
|
||||
namedproperties:mysql_host = localhost
|
||||
namedproperties:mysql_db = openchange
|
||||
|
||||
mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/openchange
|
||||
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
|
||||
----
|
||||
|
||||
////
|
||||
On RHEL, make sure SELinux is disabled:
|
||||
|
||||
setenforce 0
|
||||
////
|
||||
|
||||
Next, you can start Samba using the usual command:
|
||||
Next, you can start Samba using the usual command :
|
||||
|
||||
/etc/init.d/samba start
|
||||
|
||||
On upstart-based distributions, use:
|
||||
|
||||
start samba-ad-dc
|
||||
/etc/init.d/samba4 start
|
||||
|
||||
You can also launch the OpenChange web services:
|
||||
|
||||
@@ -548,14 +464,12 @@ This service runs as a WSGI application under apache (mod_wsgi).
|
||||
While HTTPS is not required to access this service, it is strongly
|
||||
recommended.
|
||||
|
||||
////
|
||||
On RHEL-based distributions, the apache configuration required by these
|
||||
services can be found in `/etc/httpd/conf.d/ocsmanager.conf` and
|
||||
`/etc/httpd/conf.d/rpcproxy.conf`.
|
||||
////
|
||||
|
||||
For Debian-based distributions, these files can be found
|
||||
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
|
||||
in `/etc/apache2/conf.d/`.
|
||||
|
||||
The configuration requires three Apache modules: _mod_proxy_,
|
||||
_mod_proxy_http_ and _mod_wsgi_. These are usually already installed but
|
||||
@@ -563,19 +477,13 @@ might need to be activated on Debian-based installations:
|
||||
|
||||
a2enmod proxy proxy_http wsgi
|
||||
|
||||
The OCS Manager and RPC Proxy configuration module can be enabled using:
|
||||
|
||||
a2enconf ocsmanager
|
||||
a2enconf rpcproxy
|
||||
|
||||
////
|
||||
On RHEL-based distributions, make sure the `LoadModule` directive is
|
||||
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
|
||||
////
|
||||
uncommented in `/etc/httpd/conf.d/wsgi.conf` (or
|
||||
`python26-mod_wsgi.conf` on RHELv5).
|
||||
|
||||
The _reqtimeout_ apache module is known to cause problems when using the
|
||||
default configuration shipped with Debian-based systems. On such
|
||||
distributions, Apache will close (HTTP/1.1 500) any HTTP request for
|
||||
distributions, apache will close (HTTP/1.1 500) any HTTP request for
|
||||
which the HTTP body hasn't arrived in 10 seconds.
|
||||
|
||||
While this is arguably good practice with regular HTTP, it will disrupt
|
||||
@@ -587,113 +495,14 @@ To avoid this problem, use a much higher timeout or disable the module:
|
||||
a2dismod reqtimeout
|
||||
|
||||
You should now restart the Apache service and make sure it will start on
|
||||
boot.
|
||||
////
|
||||
On RHEL-based distributions, do:
|
||||
boot. On RHEL-based distributions, do:
|
||||
|
||||
chkconfig httpd on && /etc/init.d/httpd restart
|
||||
////
|
||||
|
||||
On Debian-based distributions, do:
|
||||
|
||||
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
|
||||
|
||||
Finally, you must adjust the OCS Manager configuration file, which is
|
||||
located in `/etc/ocsmanager/ocsmanager.ini`. You should enable LDAP-based
|
||||
authentication in the `main` section and configure it accordingly. You should
|
||||
also enable rpcproxy. You file should be similar to this one:
|
||||
|
||||
----
|
||||
[DEFAULT]
|
||||
debug = true
|
||||
email_to = you@yourdomain.com
|
||||
smtp_server = localhost
|
||||
error_email_from = paste@localhost
|
||||
|
||||
[main]
|
||||
auth = ldap
|
||||
mapistore_root = /var/lib/samba/private
|
||||
mapistore_data = /var/lib/samba/private/mapistore
|
||||
debug = yes
|
||||
|
||||
[auth:file]
|
||||
|
||||
[auth:ldap]
|
||||
host = ldap://127.0.0.1
|
||||
port = 389
|
||||
bind_dn = cn=administrator,cn=Users,dc=example,dc=com
|
||||
bind_pw = %1OpenChange
|
||||
basedn = cn=Users,dc=example,dc=com
|
||||
|
||||
[auth:single]
|
||||
username = openchange
|
||||
password = {SSHA}I6Hy5Wv0wuxyXvMBFWFQDVVN12_CLaX9
|
||||
|
||||
[server:main]
|
||||
use = egg:Paste#http
|
||||
host = 127.0.0.1
|
||||
port = 5000
|
||||
protocol_version = HTTP/1.1
|
||||
|
||||
[app:main]
|
||||
use = egg:ocsmanager
|
||||
full_stack = true
|
||||
static_files = true
|
||||
cache_dir = %(here)s/data
|
||||
beaker.session.key = ocsmanager
|
||||
beaker.session.secret = SDyKK3dKyDgW0mlpqttTMGU1f
|
||||
app_instance_uuid = {ee533ebc-f266-49d1-ae10-d017ee6aa98c}
|
||||
NTLMAUTHHANDLER_WORKDIR = /var/cache/ntlmauthhandler
|
||||
SAMBA_HOST = 127.0.0.1
|
||||
|
||||
[rpcproxy:ldap]
|
||||
host = localhost
|
||||
port = 389
|
||||
basedn = CN=Users,DC=example,DC=com
|
||||
set debug = true
|
||||
----
|
||||
|
||||
----
|
||||
[autodiscover]
|
||||
|
||||
[autodiscover:rpcproxy]
|
||||
enabled = true
|
||||
|
||||
[outofoffice]
|
||||
|
||||
[outofoffice:file]
|
||||
sieve_script_path = /var/vmail/$domain/$user/sieve-script
|
||||
sieve_script_path_mkdir = false
|
||||
|
||||
[outofoffice:managesieve]
|
||||
secret = secret
|
||||
|
||||
[loggers]
|
||||
keys = root
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = INFO
|
||||
handlers = console
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s
|
||||
----
|
||||
|
||||
Once completed, start the OCS Manager service:
|
||||
|
||||
/etc/init.d/openchange-ocsmanager start
|
||||
|
||||
Name Service Configuration for Web Services
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -731,13 +540,14 @@ samba-tool domain passwordsettings set --complexity=off
|
||||
samba-tool domain passwordsettings set --min-pwd-length=1
|
||||
samba-tool user add <username>
|
||||
samba-tool user setexpiry <username> --noexpiry
|
||||
# create user in openchange
|
||||
openchange_newuser --create <username>
|
||||
# create user in openchange+ +openchange_newuser --create <username>
|
||||
----
|
||||
|
||||
If you don't have a trust between your IMAP server and SOGo, you must at
|
||||
this point set the cleartext password of the newly created user in
|
||||
`/var/lib/samba/private/mapistore/<username/password`.
|
||||
`/var/lib/samba4/private/mapistore/<username/password` (or
|
||||
`/var/lib/samba/private/mapistore/<username/password` on Debian-based
|
||||
distributions).
|
||||
|
||||
This per-user file contains the cleartext password of the user as a
|
||||
UTF-8 string, on a single line. This password will be used to
|
||||
@@ -822,7 +632,7 @@ the `openchange_user_cleanup` script distributed with SOGo. The script
|
||||
can be found in `/usr/share/doc/sogo/` (`/usr/share/sogo-VERSION/` on
|
||||
RHEL).
|
||||
To reset a user, run the script as root:
|
||||
`openchange_user_cleanup username`. See the usage output for additional options.
|
||||
`python openchange_user_cleanup username`. See the usage output for additional options.
|
||||
* The "Out of Office Assistant" will not currently work. This feature
|
||||
has not been implemented.
|
||||
* Creating folders below INBOX (when not normally permitted by the IMAP
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<?xml version='1.0'?>
|
||||
<xsl:stylesheet
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:fo="http://www.w3.org/1999/XSL/Format"
|
||||
version="1.0">
|
||||
|
||||
<!-- ********************************************************************
|
||||
|
||||
SOGo Documentation Docbook FO Parameters
|
||||
|
||||
This file is part of the SOGo project.
|
||||
Authors:
|
||||
- Inverse inc. <info@inverse.ca>
|
||||
|
||||
Copyright (C) 2011-2014 Inverse inc.
|
||||
License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
|
||||
|
||||
******************************************************************** -->
|
||||
|
||||
<!--
|
||||
Global Tasks
|
||||
|
||||
TODO prettier revhistory
|
||||
TODO prettier Table of Contents
|
||||
TODO generate PDF table of contents (like OSX's Preview shows on the right hand side)
|
||||
TODO title 2
|
||||
- align with text?
|
||||
- more above whitespace
|
||||
TODO change the bullet for a prettier one
|
||||
TODO title 3 and 4
|
||||
- align with text?
|
||||
- should be easier to differentiate (check network guide)
|
||||
TODO icon on line wrap in monospace boxes
|
||||
TODO caution, notes, warnings, etc.
|
||||
- box around it
|
||||
- sexy icon
|
||||
TODO -> is converted into an arrow but it's not pretty (is it font or docbook-thingy?)
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
Load default values
|
||||
|
||||
Real upstream schema is at:
|
||||
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
|
||||
|
||||
but we decided to load all sensible local xsd since it only produce a warning on missing imports.
|
||||
-->
|
||||
<!-- CentOS / RHEL -->
|
||||
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl"/>
|
||||
<!-- Debian / Ubuntu -->
|
||||
<xsl:import href="/usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl"/>
|
||||
<!-- OSX through mac ports -->
|
||||
<xsl:import href="/opt/local/share/xsl/docbook-xsl/fo/docbook.xsl"/>
|
||||
|
||||
<!-- title page extra styling -->
|
||||
<xsl:import href="titlepage-fo.xsl"/>
|
||||
|
||||
<!-- header / footer extra styling -->
|
||||
<xsl:import href="headerfooter-fo.xsl"/>
|
||||
|
||||
<!-- attaching an image to the verso legalnotice component -->
|
||||
<xsl:template match="legalnotice" mode="book.titlepage.verso.mode">
|
||||
<xsl:apply-templates mode="titlepage.mode"/>
|
||||
<fo:block text-align="right">
|
||||
<fo:external-graphic src="url('images/inverse-logo.jpg')" width="3in" content-width="scale-to-fit"/>
|
||||
</fo:block>
|
||||
</xsl:template>
|
||||
|
||||
<!-- stylesheet options -->
|
||||
<xsl:param name="title.font.family">Lato-Medium</xsl:param>
|
||||
<xsl:param name="chapter.autolabel" select="0"/>
|
||||
<xsl:attribute-set name="component.title.properties">
|
||||
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
|
||||
<xsl:attribute name="border-bottom">solid 2px</xsl:attribute>
|
||||
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="section.title.level1.properties">
|
||||
<xsl:attribute name="border-bottom">solid 1px</xsl:attribute>
|
||||
<xsl:attribute name="margin-top">1em</xsl:attribute>
|
||||
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="section.title.level2.properties">
|
||||
<xsl:attribute name="margin-top">1em</xsl:attribute>
|
||||
<xsl:attribute name="margin-bottom">1em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="section.title.level3.properties">
|
||||
<xsl:attribute name="margin-top">1em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="section.title.level4.properties">
|
||||
<xsl:attribute name="margin-top">1em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- paragraph spacing -->
|
||||
<xsl:attribute-set name="normal.para.spacing">
|
||||
<xsl:attribute name="space-before.optimum">1.5em</xsl:attribute>
|
||||
<xsl:attribute name="space-before.minimum">1.5em</xsl:attribute>
|
||||
<xsl:attribute name="space-before.maximum">2.2em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- default fonts -->
|
||||
<xsl:param name="body.font.family">Lato-Light</xsl:param>
|
||||
<xsl:param name="body.font.master">10</xsl:param>
|
||||
<xsl:param name="monospace.font.family">Incosolata</xsl:param>
|
||||
|
||||
<!-- revision table layout -->
|
||||
<xsl:attribute-set name="revhistory.title.properties">
|
||||
<xsl:attribute name="font-size">12pt</xsl:attribute>
|
||||
<xsl:attribute name="font-weight">bold</xsl:attribute>
|
||||
<xsl:attribute name="text-align">center</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="revhistory.table.properties">
|
||||
<xsl:attribute name="break-before">page</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
<xsl:attribute-set name="revhistory.table.cell.properties">
|
||||
<xsl:attribute name="border-bottom">1px solid</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- Table Of Contents (TOC) options -->
|
||||
<!-- We only want 2 level of ToC depth -->
|
||||
<xsl:param name="toc.section.depth" select="2"/>
|
||||
|
||||
<!-- titles left margin -->
|
||||
<xsl:attribute-set name="section.title.properties">
|
||||
<xsl:attribute name="start-indent"><xsl:value-of select="$body.start.indent"/></xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<xsl:attribute-set name="list.item.spacing">
|
||||
<xsl:attribute name="space-before.optimum">0em</xsl:attribute>
|
||||
<xsl:attribute name="space-before.minimum">0em</xsl:attribute>
|
||||
<xsl:attribute name="space-before.maximum">0.2em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- lists type -->
|
||||
<xsl:template name="itemizedlist.label.markup">
|
||||
<xsl:param name="itemsymbol" select="'square'"/>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$itemsymbol='square'"><fo:inline font-family="Lato">∏</fo:inline></xsl:when>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
<xsl:template name="next.itemsymbol">
|
||||
<xsl:param name="itemsymbol" select="'default'"/>
|
||||
<xsl:choose>
|
||||
<xsl:otherwise>square</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<!-- admonition -->
|
||||
<xsl:param name="admon.graphics" select="1"></xsl:param>
|
||||
<xsl:param name="admon.graphics.path">images/</xsl:param>
|
||||
<xsl:param name="admon.graphics.extension">.png</xsl:param>
|
||||
<xsl:attribute-set name="graphical.admonition.properties">
|
||||
<xsl:attribute name="border-top">1px solid</xsl:attribute>
|
||||
<xsl:attribute name="border-bottom">1px solid</xsl:attribute>
|
||||
<xsl:attribute name="padding-top">0.5em</xsl:attribute>
|
||||
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
|
||||
<xsl:attribute name="margin-left">2em</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
|
||||
<!-- grey boxes around code (screen, programlisting) -->
|
||||
<xsl:param name="shade.verbatim" select="1"/>
|
||||
<xsl:attribute-set name="shade.verbatim.style">
|
||||
<xsl:attribute name="background-color">#E0E0E0</xsl:attribute>
|
||||
<xsl:attribute name="border">thin #9F9F9F solid</xsl:attribute>
|
||||
<xsl:attribute name="margin">0pt</xsl:attribute>
|
||||
<xsl:attribute name="padding">0.5em</xsl:attribute>
|
||||
<!-- prevent page breaks in screen and programlisting tags -->
|
||||
<xsl:attribute name="keep-together.within-column">always</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- breaking long lines in code (screen, programlisting) -->
|
||||
<xsl:attribute-set name="monospace.verbatim.properties">
|
||||
<xsl:attribute name="wrap-option">wrap</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- don't show raw links in [ .. ] after a link -->
|
||||
<xsl:param name="ulink.show" select="0"/>
|
||||
|
||||
<!-- blue underlined hyperlink -->
|
||||
<xsl:attribute-set name="xref.properties">
|
||||
<xsl:attribute name="color">blue</xsl:attribute>
|
||||
<xsl:attribute name="text-decoration">underline</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- copyright in range instead of seperated years -->
|
||||
<xsl:param name="make.year.ranges" select="1" />
|
||||
|
||||
<!-- variablelist behavior (asciidoc's term:: lists) -->
|
||||
<!-- <xsl:param name="variablelist.term.break.after" select="1" /> -->
|
||||
|
||||
</xsl:stylesheet>
|
||||
<!-- vim: set shiftwidth=2 tabstop=2 expandtab: -->
|
||||
@@ -165,13 +165,6 @@
|
||||
<xsl:attribute name="text-decoration">underline</xsl:attribute>
|
||||
</xsl:attribute-set>
|
||||
|
||||
<!-- strong emphasis in bold -->
|
||||
<xsl:template match="emphasis[@role='strong']">
|
||||
<fo:inline font-family="Lato" font-weight="normal">
|
||||
<xsl:apply-templates/>
|
||||
</fo:inline>
|
||||
</xsl:template>
|
||||
|
||||
<!-- copyright in range instead of seperated years -->
|
||||
<xsl:param name="make.year.ranges" select="1" />
|
||||
|
||||
|
||||
@@ -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.</subtitle>
|
||||
<date>2015-09-16</date>
|
||||
<releaseinfo>Version 2.2.9 - September 2014</releaseinfo>
|
||||
<subtitle>for version 2.2.9</subtitle>
|
||||
<date>2014-09-26</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>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -6,13 +6,13 @@
|
||||
Authors:
|
||||
- Inverse inc. <info@inverse.ca>
|
||||
|
||||
Copyright (C) 2008-2015 Inverse inc.
|
||||
Copyright (C) 2008-2014 Inverse inc.
|
||||
License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html
|
||||
|
||||
////
|
||||
|
||||
// TODO have the build system take care of this
|
||||
|
||||
:release_version: 2.3.2
|
||||
:release_version: 2.2.9
|
||||
|
||||
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||
+2
-1
@@ -6,7 +6,8 @@ include ../Version
|
||||
|
||||
ADDITIONAL_OBJCFLAGS += -fPIE
|
||||
ADDITIONAL_INCLUDE_DIRS +=
|
||||
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo
|
||||
ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj/
|
||||
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie
|
||||
|
||||
SOGOD = sogod
|
||||
TOOL_NAME = $(SOGOD)
|
||||
|
||||
@@ -9,9 +9,10 @@ ADDITIONAL_INCLUDE_DIRS += \
|
||||
-I..
|
||||
|
||||
ADDITIONAL_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/sogo \
|
||||
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/
|
||||
-L../SoObjects/SOGo/SOGo.framework \
|
||||
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/
|
||||
|
||||
SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
|
||||
|
||||
$(SOGOD)_TOOL_LIBS += \
|
||||
-lSOGo \
|
||||
|
||||
+3
-3
@@ -245,7 +245,7 @@ static BOOL debugLeaks;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"No value specified for '%@'", *urlString];
|
||||
NSLog (@"No value specified for '%@'", *urlString);
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ static BOOL debugLeaks;
|
||||
{
|
||||
id authenticator;
|
||||
|
||||
if (trustProxyAuthentication && [[context request] headerForKey: @"x-webobjects-remote-user"])
|
||||
if (trustProxyAuthentication)
|
||||
authenticator = [SOGoProxyAuthenticator sharedSOGoProxyAuthenticator];
|
||||
else
|
||||
{
|
||||
@@ -441,7 +441,7 @@ static BOOL debugLeaks;
|
||||
if (debugLeaks)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"allocated classes:\n%s", GSDebugAllocationList (YES)];
|
||||
NSLog (@"allocated classes:\n%s", GSDebugAllocationList (YES));
|
||||
else
|
||||
{
|
||||
debugOn = YES;
|
||||
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2015 Inverse inc.
|
||||
Copyright (C) 2006-2009 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
This file is part of OpenGroupware.org.
|
||||
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
@@ -1,310 +1,3 @@
|
||||
2.3.2 (2015-09-16)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- improved EAS speed and memory usage, avoiding many IMAP LIST commands (#3294)
|
||||
- improved EAS speed during initial syncing of large mailboxes (#3293)
|
||||
- updated CKEditor to version 4.5.3
|
||||
|
||||
Bug fixes
|
||||
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
|
||||
- non-latin subfolder names are displayed correctly on Outlook (Zentyal)
|
||||
- fixed several sync issues on environments with multiple users (Zentyal)
|
||||
- folders from other users will no longer appear on your Outlook (Zentyal)
|
||||
- use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
|
||||
- session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
|
||||
- less sync issues when setting read flag (Zentyal)
|
||||
- attachments with non-latin filenames sent by Outlook are now received (Zentyal)
|
||||
- support attachments from more mail clients (Zentyal)
|
||||
- avoid conflicting message on saving a draft mail (Zentyal)
|
||||
- less conflicting messages in Outlook while moving messages between folders (Zentyal)
|
||||
- start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344)
|
||||
- fixed localization of calendar categories with empty profile (#3295)
|
||||
- fixed options availability in contextual menu of Contacts module (#3342)
|
||||
|
||||
2.3.1 (2015-07-23)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- improved EAS speed, especially when fetching big attachments
|
||||
- now always enforce the organizer's default identity in appointments
|
||||
- improved the handling of default calendar categories/colors (#3200)
|
||||
- added support for DeletesAsMoves over EAS
|
||||
- added create-folder subcommand to sogo-tool to create contact and calendar folders
|
||||
- group mail addresses can be used as recipient in Outlook
|
||||
- added 'ActiveSync' module constraints
|
||||
- updated CKEditor to version 4.5.1
|
||||
- added Slovenian translation - thanks to Jens Riecken
|
||||
- added Chinese (Taiwan) translation
|
||||
|
||||
Bug fixes
|
||||
- EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders
|
||||
- fixed some rare cornercases in multidomain configurations
|
||||
- properly escape folder after creation using EAS (#3237)
|
||||
- fixed potential organizer highjacking when using EAS (#3131)
|
||||
- properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082)
|
||||
- properly encode id of DOM elements in Address Book module (#3239, #3245)
|
||||
- fixed multi-domain support for sogo-tool backup/restore (#2600)
|
||||
- fixed data ordering in events list of Calendar module (#3261)
|
||||
- fixed data ordering in tasks list of Calendar module (#3267)
|
||||
- Android EAS Lollipop fixes (#3268 and #3269)
|
||||
- improved EAS email flagging handling (#3140)
|
||||
- fixed computation of GlobalObjectId (#3235)
|
||||
- fixed EAS conversation ID issues on BB10 (#3152)
|
||||
- fixed CR/LF printing in event's description (#3228)
|
||||
- optimized Calendar module in multidomain configurations
|
||||
|
||||
2.3.0 (2015-06-01)
|
||||
-------------------
|
||||
|
||||
New features
|
||||
- Internet headers are now shown in Outlook (Zentyal)
|
||||
|
||||
Enhancements
|
||||
- improved multipart handling using EAS
|
||||
- added systemd startup script (PR#76)
|
||||
- added Basque translation - thanks to Gorka Gonzalez
|
||||
- updated Brazilian (Portuguese), Dutch, Norwegian (Bokmal), Polish, Russian, and Spanish (Spain) translations
|
||||
- calendar sharing request support among different Outlook versions (Zentyal)
|
||||
- improved sync speed from Outlook by non-reprocessing already downloaded unread mails (Zentyal)
|
||||
- added support for sharing calendar invitations
|
||||
- missing contact fields are now saved and available when sharing it (Office, Profession, Manager's name, Assistant's name, Spouse/Partner, Anniversary) (Zentyal)
|
||||
- appointment color and importance work now between Outlooks (Zentyal)
|
||||
- synchronize events, contacts and tasks in reverse chronological order (Zentyal)
|
||||
- during login, we now extract the domain from the user to accelerate authentication requests on sources
|
||||
- make sure sure email invitations can always be read by EAS clients
|
||||
- now able to print event/task's description (new components only) in the list view (#2881)
|
||||
- now possible to log EAS commands using the SOGoEASDebugEnabled system defaults
|
||||
- many improvements to EAS SmartReply/SmartForward commands
|
||||
- event invitation response mails from Outlook are now sent
|
||||
- mail subfolders created in WebMail are created when Outlook synchronises
|
||||
- mail root folder created in WebMail (same level INBOX) are created on Outlook logon
|
||||
|
||||
Bug fixes
|
||||
- now keep the BodyPreference for future EAS use and default to MIME if none set (#3146)
|
||||
- EAS reply fix when message/rfc822 parts are included in the original mail (#3153)
|
||||
- fixed yet an other potential crash during freebusy lookups during timezone changes
|
||||
- fixed display of freebusy information in event attendees editor during timezone changes
|
||||
- fixed timezone of MSExchange freebusy information
|
||||
- fixed a potential EAS error with multiple email priority flags
|
||||
- fixed paragraphs margins in HTML messages (#3163)
|
||||
- fixed regression when loading the inbox for the first time
|
||||
- fixed serialization of the PreventInvitationsWhitelist settings
|
||||
- fixed md4 support (for NTLM password changes) with GNU TLS
|
||||
- fixed edition of attachment URL in event/task editor
|
||||
- sent mails are not longer in Drafts folder using Outlook (Zentyal)
|
||||
- deleted mails are properly synced between Outlook profiles from the same account (Zentyal)
|
||||
- does not create a mail folder in other user's mailbox (Zentyal)
|
||||
- fix server-side crash with invalid events (Zentyal)
|
||||
- fix setting permissions for a folder with several users (Zentyal)
|
||||
- fix reception of calendar event invitations on optional attendees (Zentyal)
|
||||
- fix server side crash parsing rtf without color table (Zentyal)
|
||||
- weekly recurring events created in SOGo web interface are now shown in Outlook (Zentyal)
|
||||
- fix exception modifications import in recurrence series (Zentyal)
|
||||
- fix server side crash parsing rtf emails with images (with word97 format) (Zentyal)
|
||||
- fix sender on importing email messages like event invitations (Zentyal)
|
||||
- fix Outlook crashes when modifying the view of a folder (Zentyal)
|
||||
- fix server side crash when reading some recurrence appointments (Zentyal)
|
||||
- Outlook clients can use reply all functionality on multidomain environment (Zentyal)
|
||||
- optional attendes on events are now shown properly (Zentyal)
|
||||
- fixed the EAS maximum response size being per-folder, and not global
|
||||
- now set MeetingMessageType only for EAS 14.1
|
||||
- now correctly handle external invitations using EAS
|
||||
- now correctly handle multiple email addresses in the GAL over EAS (#3102)
|
||||
- now handle very large amount of participants correctly (#3175)
|
||||
- fix message bodies not shown on some EAS devices (#3173)
|
||||
- avoid appending the domain unconditionally when SOGoEnableDomainBasedUID is set to YES
|
||||
- recurrent all day events are now shown properly in Outlook
|
||||
|
||||
2.2.17a (2015-03-15)
|
||||
--------------------
|
||||
|
||||
Bug fixes
|
||||
- avoid calling -stringByReplacingOccurrencesOfString:... for old GNUstep runtime
|
||||
|
||||
2.2.17 (2015-03-24)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- support for mail prority using EAS
|
||||
- immediately delete mails from EAS clients when they are marked as deleted on the IMAP server
|
||||
- now favor login@domain as the default email address if multiple mail: fields are specified
|
||||
- enable by default HTML mails support using EAS on Windows and BB phones
|
||||
- now possible to configure objectClass names for LDAP groups using GroupObjectClasses (#1499)
|
||||
|
||||
Bug fixes
|
||||
- fixed login issue after password change (#2601)
|
||||
- fixed potential encoding issue using EAS and 8-bit mails (#3116)
|
||||
- multiple collections support for GetItemEstimate using EAS
|
||||
- fixed empty sync responses for EAS 2.5 and 12.0 clients
|
||||
- use the correct mail body element for EAS 2.5 clients
|
||||
- fixed tasks disappearing issue with RoadSync
|
||||
- use the correct body element for events for EAS 2.5 clients
|
||||
- SmartReply improvements for missing body attributes
|
||||
- do not use syncKey from cache when davCollectionTag = -1
|
||||
- use correct mail attachment elements for EAS 2.5 clients
|
||||
- fixed contacts lookup by UID in freebusy
|
||||
- reduced telephone number to a single value in JSON response of contacts list
|
||||
- fixed freebusy data when 'busy off hours' is enabled and period starts during the weekend
|
||||
- fixed fetching of freebusy data from the Web interface
|
||||
- fixed EAS handling of Bcc in emails (#3138)
|
||||
- fixed Language-Region tags in Web interface (#3121)
|
||||
- properly fallback over EAS to UTF-8 and then Latin1 for messages w/o charset (#3103)
|
||||
- prevent potential freebusy lookup crashes during timezone changes with repetitive events
|
||||
- improved GetItemEstimate to count all vasnished/deleted mails too
|
||||
- improvements to EAS SyncKey handling to avoid missing mails (#3048, #3058)
|
||||
- fixed EAS replies decoding from Outlook (#3123)
|
||||
|
||||
2.2.16 (2015-02-12)
|
||||
-------------------
|
||||
|
||||
New features
|
||||
- now possible for SOGo to change the sambaNTPassword/sambaLMPassword
|
||||
- now possible to limit automatic forwards to internal/external domains
|
||||
|
||||
Enhancements
|
||||
- added support for email categories using EAS (#2995)
|
||||
- now possible to always send vacation messages (#2332)
|
||||
- added EAS best practices to the documentation
|
||||
- improved fetching of text parts over EAS
|
||||
- updated Czech, Finnish, French, German and Hungarian translations
|
||||
|
||||
Bug fixes
|
||||
- (regression) fixed sending a message when mail module is not active (#3088)
|
||||
- mail labels with blanks are not handled correctly (#3078)
|
||||
- fixed BlackBerry issues sending multiple mails over EAS (#3095)
|
||||
- fixed plain/text mails showing on one line on Android/EAS (#3055)
|
||||
- fixed exception in sogo-tool when parsing arguments of a set operation
|
||||
|
||||
2.2.15 (2015-01-30)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- improved handling of EAS Push when no heartbeat is provided
|
||||
- no longer need to kill Outlook 2013 when creating EAS profiles (#3076)
|
||||
- improved server-side CSS cleaner (#3040)
|
||||
- unified the logging messages in sogo.log file (#2534/#3063)
|
||||
- updated Brazilian (Portuguese) and Hungarian translations
|
||||
|
||||
2.2.14 (2015-01-20)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- MultipleBookingsFieldName can be set to -1 to show busy status when booked at least once
|
||||
- handle multipart objects in EAS/ItemOperations
|
||||
|
||||
Bug fixes
|
||||
- fixed calendar selection in event and task editors (#3049, #3050)
|
||||
- check for resources existence when listing subscribed ones (#3054)
|
||||
- correctly recognize Apple Calendar on Yosemite (#2960)
|
||||
- fixed two potential autorelease pool leaks (#3026 and #3051)
|
||||
- fixed birthday offset in EAS
|
||||
- fixed From's full name over EAS
|
||||
- fixed potential issue when handling multiple Add/Change/Delete/Fetch EAS commands (#3057)
|
||||
- fixed wrong timezone calculation on recurring events
|
||||
|
||||
2.2.13 (2014-12-30)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- initial support for empty sync request/response for EAS
|
||||
- added the SOGoMaximumSyncResponseSize EAS configuration parameter to
|
||||
support memory-limited sync response sizes
|
||||
- we now not only use the creation date for event's cutoff date (EAS)
|
||||
|
||||
Bug fixes
|
||||
- fixed contact description truncation on WP8 phones (#3028)
|
||||
- fixed freebusy information not always returned
|
||||
- fixed tz issue when the user one was different from the system one with EAS
|
||||
|
||||
2.2.12a (2014-12-19)
|
||||
--------------------
|
||||
|
||||
Bug fixes
|
||||
- fixed empty HTML mails being sent (#3034)
|
||||
|
||||
2.2.12 (2014-12-18)
|
||||
-------------------
|
||||
|
||||
New features
|
||||
- allow including or not freebusy info from subscribed calendars
|
||||
- now possible to set an autosave timer for draft messages
|
||||
- now possible to set alarms on event invitations (#76)
|
||||
|
||||
Enhancements
|
||||
- updated CKEditor to version 4.4.6 and added the 'Source Area' plugin
|
||||
- avoid testing for IMAP ANNOTATION when X-GUID is available (#3018)
|
||||
- updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations
|
||||
|
||||
Bug fixes
|
||||
- fixed for privacy and categories for EAS (#3022)
|
||||
- correctly set MeetingStatus for EAS on iOS devices
|
||||
- Ubuntu Lucid fixes for EAS
|
||||
- fixed calendar reminders for future events (#3008)
|
||||
- make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003)
|
||||
- fixed task description truncation affecting WP8 phones over EAS (#3028)
|
||||
|
||||
2.2.11a (2014-12-10)
|
||||
--------------------
|
||||
|
||||
Bug fixes
|
||||
- make sure all address books returned using EAS are GCS ones
|
||||
|
||||
2.2.11 (2014-12-09)
|
||||
-------------------
|
||||
|
||||
New features
|
||||
- sogo-tool can now be used to manage EAS metadata for all devices
|
||||
|
||||
Enhancements
|
||||
- improved the SAML2 documentation
|
||||
- radically reduced AES memory usage
|
||||
|
||||
Bug fixes
|
||||
- now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381)
|
||||
- added support for IdP-initiated SAML2 logout (#2377)
|
||||
- we now generate SAML2 metadata on the fly (#2378)
|
||||
- we now handle correctly the SOGo logout when using SAML (#2376 and #2379)
|
||||
- fixed freebusy lookups going off bounds for resources (#3010)
|
||||
- fixed EAS clients moving mails between folders but disconnecting before receiving server's response (#2982)
|
||||
|
||||
2.2.10 (2014-11-21)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- no longer leaking database passwords in the logs (#2953)
|
||||
- added support for multiple calendars and address books over ActiveSync
|
||||
- updated timezone information (#2968)
|
||||
- updated Brazilian Portuguese, Czech, Dutch, Finnish, French, German, Hungarian, Polish,
|
||||
Russian, Spanish (Argentina), and Spanish (Spain) translations
|
||||
- updated CKEditor to version 4.4.5
|
||||
|
||||
Bug fixes
|
||||
- fixed freebusy lookup with "Show time as busy" (#2930)
|
||||
- don't escape <br>'s in a card's note field
|
||||
- fixed folder's display name when subscribing to a folder
|
||||
- fixed folder's display name when the active user subscribes another user to one of her/his folders
|
||||
- fixed error with new user default sorting value for the mailer module (#2952)
|
||||
- fixed ActiveSync PING command flooding the server (#2940)
|
||||
- fixed many interop issues with Windows Phones over ActiveSync
|
||||
- fixed automatic return receipts crash when not in the recepient list (#2965)
|
||||
- fixed support for Sieve folder encoding parameter (#2622)
|
||||
- fixed rename of subscribed addressbooks
|
||||
- sanitize strings before escaping them when using EAS
|
||||
- fixed handling of event invitations on iOS/EAS with no organizer (#2978)
|
||||
- fixed corrupted png files (#2975)
|
||||
- improved dramatically the BSON decoding speed
|
||||
- added WindowSize support for GCS collections when using EAS
|
||||
- fixed IMAP search with non-ASCII folder names
|
||||
- fixed extraction of email addresses when pasting text with tabs (#2945)
|
||||
- fixed Outlook attachment corruption issues when using AES (#2957)
|
||||
|
||||
2.2.9a (2014-09-29)
|
||||
-------------------
|
||||
|
||||
Bug fixes
|
||||
- correctly skip unallowed characters (#2936)
|
||||
|
||||
2.2.9 (2014-09-26)
|
||||
------------------
|
||||
|
||||
@@ -312,7 +5,7 @@ New features
|
||||
- support for recurrent tasks (#2160)
|
||||
- support for alarms on recurrent events / tasks
|
||||
|
||||
Enhancements
|
||||
Enchancements
|
||||
- alarms can now be snoozed for 1 day
|
||||
- better iOS/Mac OS X Calendar compability regarding alarms (#1920)
|
||||
- force default classification over CalDAV if none is set (#2326)
|
||||
@@ -337,7 +30,7 @@ New features
|
||||
- new user settings for threads collapsing
|
||||
- IMAP global search support (#2670)
|
||||
|
||||
Enhancements
|
||||
Enchancements
|
||||
- major refactoring of the GCS component saving code (dropped OGoContentStore)
|
||||
- printing calendars in colors is now possible in all views; list, daily, weekly and multicolumns
|
||||
- new option to print calendars events and tasks with a background color or with a border color
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* Codepages.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2014 Jesús García Sáez
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
|
||||
@interface Codepages
|
||||
|
||||
+ (NSDictionary *) getCodepagesTable;
|
||||
+ (NSDictionary *) getReverseCodepagesTable;
|
||||
|
||||
+ (NSNumber *) getCodepageFromName: (NSString *) name;
|
||||
+ (NSString *) getNameFromCodepage: (NSNumber *) codepage;
|
||||
|
||||
@end
|
||||
@@ -1,214 +0,0 @@
|
||||
/* Codepages.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2014 Jesús García Sáez
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import "Codepages.h"
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
@implementation Codepages
|
||||
|
||||
+ (NSDictionary *) getCodepagesTable
|
||||
{
|
||||
static NSDictionary *table = nil;
|
||||
|
||||
if (table == nil)
|
||||
{
|
||||
/* http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx */
|
||||
table = [[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt: 37], @"ibm037",
|
||||
[NSNumber numberWithInt: 437], @"ibm437",
|
||||
[NSNumber numberWithInt: 500], @"ibm500",
|
||||
[NSNumber numberWithInt: 708], @"asmo-708",
|
||||
[NSNumber numberWithInt: 720], @"dos-720",
|
||||
[NSNumber numberWithInt: 737], @"ibm737",
|
||||
[NSNumber numberWithInt: 775], @"ibm775",
|
||||
[NSNumber numberWithInt: 850], @"ibm850",
|
||||
[NSNumber numberWithInt: 852], @"ibm852",
|
||||
[NSNumber numberWithInt: 855], @"ibm855",
|
||||
[NSNumber numberWithInt: 857], @"ibm857",
|
||||
[NSNumber numberWithInt: 858], @"ibm00858",
|
||||
[NSNumber numberWithInt: 860], @"ibm860",
|
||||
[NSNumber numberWithInt: 861], @"ibm861",
|
||||
[NSNumber numberWithInt: 862], @"dos-862",
|
||||
[NSNumber numberWithInt: 863], @"ibm863",
|
||||
[NSNumber numberWithInt: 864], @"ibm864",
|
||||
[NSNumber numberWithInt: 865], @"ibm865",
|
||||
[NSNumber numberWithInt: 866], @"cp866",
|
||||
[NSNumber numberWithInt: 869], @"ibm869",
|
||||
[NSNumber numberWithInt: 870], @"ibm870",
|
||||
[NSNumber numberWithInt: 874], @"windows-874",
|
||||
[NSNumber numberWithInt: 875], @"cp875",
|
||||
[NSNumber numberWithInt: 932], @"shift_jis",
|
||||
[NSNumber numberWithInt: 936], @"gb2312",
|
||||
[NSNumber numberWithInt: 949], @"ks_c_5601-1987",
|
||||
[NSNumber numberWithInt: 950], @"big5",
|
||||
[NSNumber numberWithInt: 1026], @"ibm1026",
|
||||
[NSNumber numberWithInt: 1047], @"ibm01047",
|
||||
[NSNumber numberWithInt: 1140], @"ibm01140",
|
||||
[NSNumber numberWithInt: 1141], @"ibm01141",
|
||||
[NSNumber numberWithInt: 1142], @"ibm01142",
|
||||
[NSNumber numberWithInt: 1143], @"ibm01143",
|
||||
[NSNumber numberWithInt: 1144], @"ibm01144",
|
||||
[NSNumber numberWithInt: 1145], @"ibm01145",
|
||||
[NSNumber numberWithInt: 1146], @"ibm01146",
|
||||
[NSNumber numberWithInt: 1147], @"ibm01147",
|
||||
[NSNumber numberWithInt: 1148], @"ibm01148",
|
||||
[NSNumber numberWithInt: 1149], @"ibm01149",
|
||||
[NSNumber numberWithInt: 1200], @"utf-16",
|
||||
[NSNumber numberWithInt: 1201], @"unicodefffe",
|
||||
[NSNumber numberWithInt: 1250], @"windows-1250",
|
||||
[NSNumber numberWithInt: 1251], @"windows-1251",
|
||||
[NSNumber numberWithInt: 1252], @"windows-1252",
|
||||
[NSNumber numberWithInt: 1253], @"windows-1253",
|
||||
[NSNumber numberWithInt: 1254], @"windows-1254",
|
||||
[NSNumber numberWithInt: 1255], @"windows-1255",
|
||||
[NSNumber numberWithInt: 1256], @"windows-1256",
|
||||
[NSNumber numberWithInt: 1257], @"windows-1257",
|
||||
[NSNumber numberWithInt: 1258], @"windows-1258",
|
||||
[NSNumber numberWithInt: 1361], @"johab",
|
||||
[NSNumber numberWithInt: 10000], @"macintosh",
|
||||
[NSNumber numberWithInt: 10001], @"x-mac-japanese",
|
||||
[NSNumber numberWithInt: 10002], @"x-mac-chinesetrad",
|
||||
[NSNumber numberWithInt: 10003], @"x-mac-korean",
|
||||
[NSNumber numberWithInt: 10004], @"x-mac-arabic",
|
||||
[NSNumber numberWithInt: 10005], @"x-mac-hebrew",
|
||||
[NSNumber numberWithInt: 10006], @"x-mac-greek",
|
||||
[NSNumber numberWithInt: 10007], @"x-mac-cyrillic",
|
||||
[NSNumber numberWithInt: 10008], @"x-mac-chinesesimp",
|
||||
[NSNumber numberWithInt: 10010], @"x-mac-romanian",
|
||||
[NSNumber numberWithInt: 10017], @"x-mac-ukrainian",
|
||||
[NSNumber numberWithInt: 10021], @"x-mac-thai",
|
||||
[NSNumber numberWithInt: 10029], @"x-mac-ce",
|
||||
[NSNumber numberWithInt: 10079], @"x-mac-icelandic",
|
||||
[NSNumber numberWithInt: 10081], @"x-mac-turkish",
|
||||
[NSNumber numberWithInt: 10082], @"x-mac-croatian",
|
||||
[NSNumber numberWithInt: 12000], @"utf-32",
|
||||
[NSNumber numberWithInt: 12001], @"utf-32be",
|
||||
[NSNumber numberWithInt: 20000], @"x-chinese-cns",
|
||||
[NSNumber numberWithInt: 20001], @"x-cp20001",
|
||||
[NSNumber numberWithInt: 20002], @"x-chinese-eten",
|
||||
[NSNumber numberWithInt: 20003], @"x-cp20003",
|
||||
[NSNumber numberWithInt: 20004], @"x-cp20004",
|
||||
[NSNumber numberWithInt: 20005], @"x-cp20005",
|
||||
[NSNumber numberWithInt: 20105], @"x-ia5",
|
||||
[NSNumber numberWithInt: 20106], @"x-ia5-german",
|
||||
[NSNumber numberWithInt: 20107], @"x-ia5-swedish",
|
||||
[NSNumber numberWithInt: 20108], @"x-ia5-norwegian",
|
||||
[NSNumber numberWithInt: 20127], @"us-ascii",
|
||||
[NSNumber numberWithInt: 20261], @"x-cp20261",
|
||||
[NSNumber numberWithInt: 20269], @"x-cp20269",
|
||||
[NSNumber numberWithInt: 20273], @"ibm273",
|
||||
[NSNumber numberWithInt: 20277], @"ibm277",
|
||||
[NSNumber numberWithInt: 20278], @"ibm278",
|
||||
[NSNumber numberWithInt: 20280], @"ibm280",
|
||||
[NSNumber numberWithInt: 20284], @"ibm284",
|
||||
[NSNumber numberWithInt: 20285], @"ibm285",
|
||||
[NSNumber numberWithInt: 20290], @"ibm290",
|
||||
[NSNumber numberWithInt: 20297], @"ibm297",
|
||||
[NSNumber numberWithInt: 20420], @"ibm420",
|
||||
[NSNumber numberWithInt: 20423], @"ibm423",
|
||||
[NSNumber numberWithInt: 20424], @"ibm424",
|
||||
[NSNumber numberWithInt: 20833], @"x-ebcdic-koreanextended",
|
||||
[NSNumber numberWithInt: 20838], @"ibm-thai",
|
||||
[NSNumber numberWithInt: 20866], @"koi8-r",
|
||||
[NSNumber numberWithInt: 20871], @"ibm871",
|
||||
[NSNumber numberWithInt: 20880], @"ibm880",
|
||||
[NSNumber numberWithInt: 20905], @"ibm905",
|
||||
[NSNumber numberWithInt: 20924], @"ibm00924",
|
||||
[NSNumber numberWithInt: 20932], @"euc-jp",
|
||||
[NSNumber numberWithInt: 20936], @"x-cp20936",
|
||||
[NSNumber numberWithInt: 20949], @"x-cp20949",
|
||||
[NSNumber numberWithInt: 21025], @"cp1025",
|
||||
[NSNumber numberWithInt: 21866], @"koi8-u",
|
||||
[NSNumber numberWithInt: 28591], @"iso-8859-1",
|
||||
[NSNumber numberWithInt: 28592], @"iso-8859-2",
|
||||
[NSNumber numberWithInt: 28593], @"iso-8859-3",
|
||||
[NSNumber numberWithInt: 28594], @"iso-8859-4",
|
||||
[NSNumber numberWithInt: 28595], @"iso-8859-5",
|
||||
[NSNumber numberWithInt: 28596], @"iso-8859-6",
|
||||
[NSNumber numberWithInt: 28597], @"iso-8859-7",
|
||||
[NSNumber numberWithInt: 28598], @"iso-8859-8",
|
||||
[NSNumber numberWithInt: 28599], @"iso-8859-9",
|
||||
[NSNumber numberWithInt: 28603], @"iso-8859-13",
|
||||
[NSNumber numberWithInt: 28605], @"iso-8859-15",
|
||||
[NSNumber numberWithInt: 29001], @"x-europa",
|
||||
[NSNumber numberWithInt: 38598], @"iso-8859-8-i",
|
||||
[NSNumber numberWithInt: 50220], @"iso-2022-jp",
|
||||
[NSNumber numberWithInt: 50221], @"csiso2022jp",
|
||||
[NSNumber numberWithInt: 50222], @"iso-2022-jp",
|
||||
[NSNumber numberWithInt: 50225], @"iso-2022-kr",
|
||||
[NSNumber numberWithInt: 50227], @"x-cp50227",
|
||||
[NSNumber numberWithInt: 51932], @"euc-jp",
|
||||
[NSNumber numberWithInt: 51936], @"euc-cn",
|
||||
[NSNumber numberWithInt: 51949], @"euc-kr",
|
||||
[NSNumber numberWithInt: 52936], @"hz-gb-2312",
|
||||
[NSNumber numberWithInt: 54936], @"gb18030",
|
||||
[NSNumber numberWithInt: 57002], @"x-iscii-de",
|
||||
[NSNumber numberWithInt: 57003], @"x-iscii-be",
|
||||
[NSNumber numberWithInt: 57004], @"x-iscii-ta",
|
||||
[NSNumber numberWithInt: 57005], @"x-iscii-te",
|
||||
[NSNumber numberWithInt: 57006], @"x-iscii-as",
|
||||
[NSNumber numberWithInt: 57007], @"x-iscii-or",
|
||||
[NSNumber numberWithInt: 57008], @"x-iscii-ka",
|
||||
[NSNumber numberWithInt: 57009], @"x-iscii-ma",
|
||||
[NSNumber numberWithInt: 57010], @"x-iscii-gu",
|
||||
[NSNumber numberWithInt: 57011], @"x-iscii-pa",
|
||||
[NSNumber numberWithInt: 65000], @"utf-7",
|
||||
[NSNumber numberWithInt: 65001], @"utf-8",
|
||||
nil] retain];
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
+ (NSDictionary *) getReverseCodepagesTable
|
||||
{
|
||||
static NSDictionary *table = nil;
|
||||
|
||||
if (table == nil)
|
||||
{
|
||||
NSDictionary *codepages_table;
|
||||
NSEnumerator *enumerator;
|
||||
NSMutableArray *codepages, *names;
|
||||
id key;
|
||||
// Build reverse table: (NSNumber) codepage -> (NSString) encoding name
|
||||
codepages_table = [self getCodepagesTable];
|
||||
codepages = [NSMutableArray arrayWithCapacity: [codepages_table count]];
|
||||
names = [NSMutableArray arrayWithCapacity: [codepages_table count]];
|
||||
enumerator = [codepages_table keyEnumerator];
|
||||
while ((key = [enumerator nextObject]))
|
||||
{
|
||||
[names addObject: key];
|
||||
[codepages addObject: [codepages_table objectForKey: key]];
|
||||
}
|
||||
table = [[NSDictionary dictionaryWithObjects: names forKeys: codepages] retain];
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
+ (NSNumber *) getCodepageFromName: (NSString *) name
|
||||
{
|
||||
return [[self getCodepagesTable] objectForKey: [name lowercaseString]];
|
||||
}
|
||||
|
||||
+ (NSString *) getNameFromCodepage: (NSNumber *) codepage
|
||||
{
|
||||
return [[self getReverseCodepagesTable] objectForKey: codepage];
|
||||
}
|
||||
|
||||
@end
|
||||
+7
-15
@@ -31,7 +31,7 @@ endif
|
||||
all::
|
||||
@echo " Python executable: ${PYTHON}"
|
||||
|
||||
SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py 'private dir' || echo /var/lib/samba/private)
|
||||
SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py "private dir")
|
||||
|
||||
$(SOGOBACKEND)_PRINCIPAL_CLASS = MAPIApplication
|
||||
|
||||
@@ -100,7 +100,6 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
MAPIStoreMailAttachment.m \
|
||||
MAPIStoreMailContext.m \
|
||||
MAPIStoreMailFolder.m \
|
||||
MAPIStoreMailFolderTable.m \
|
||||
MAPIStoreMailMessage.m \
|
||||
MAPIStoreMailVolatileMessage.m \
|
||||
MAPIStoreMailMessageTable.m \
|
||||
@@ -111,8 +110,6 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
\
|
||||
MAPIStoreFallbackContext.m \
|
||||
\
|
||||
MAPIStoreSharingMessage.m \
|
||||
\
|
||||
NSArray+MAPIStore.m \
|
||||
NSData+MAPIStore.m \
|
||||
NSDate+MAPIStore.m \
|
||||
@@ -123,9 +120,7 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
iCalEvent+MAPIStore.m \
|
||||
iCalTimeZone+MAPIStore.m \
|
||||
\
|
||||
RTFHandler.m \
|
||||
\
|
||||
Codepages.m
|
||||
RTFHandler.m
|
||||
|
||||
|
||||
$(SOGOBACKEND)_RESOURCE_FILES += \
|
||||
@@ -138,11 +133,10 @@ $(PLREADER_TOOL)_OBJC_FILES += \
|
||||
|
||||
DBMSGREADER_TOOL = dbmsgreader
|
||||
$(DBMSGREADER_TOOL)_OBJC_FILES += \
|
||||
dbmsgreader.m \
|
||||
NSObject+PropertyList.m
|
||||
dbmsgreader.m
|
||||
|
||||
$(DBMSGREADER_TOOL)_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/sogo -lSOGo \
|
||||
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \
|
||||
-L../SOPE/GDLContentStore/obj/ -lGDLContentStore \
|
||||
-L../SOPE/NGCards/obj/ -lNGCards \
|
||||
-lNGObjWeb
|
||||
@@ -166,17 +160,17 @@ LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy -lWEExte
|
||||
|
||||
$(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends
|
||||
$(MAPISTORESOGO)_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/sogo/ -lSOGo -lgnustep-base -lobjc -lNGObjWeb \
|
||||
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo -lgnustep-base -lobjc -lNGObjWeb \
|
||||
$(LIBMAPI_LIBS) \
|
||||
$(LIBMAPISTORE_LIBS)
|
||||
|
||||
$(SOGOBACKEND)_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/sogo/ -lSOGo \
|
||||
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \
|
||||
$(LIBMAPI_LIBS) \
|
||||
$(LIBMAPISTORE_LIBS)
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += \
|
||||
-Wall \
|
||||
-Werror -Wall \
|
||||
-DSAMBA_PRIVATE_DIR=@"\"$(SAMBA_PRIVATE_DIR)\"" \
|
||||
$(LIBMAPI_CFLAGS) \
|
||||
$(LIBMAPISTORE_CFLAGS) \
|
||||
@@ -184,8 +178,6 @@ ADDITIONAL_INCLUDE_DIRS += \
|
||||
-DBACKEND_BUNDLE_NAME="@\"$(BUNDLE_NAME)$(BUNDLE_EXTENSION)\"" \
|
||||
-DSOGO_BUNDLES_DIR="@\"$(BUNDLE_INSTALL_DIR)\""
|
||||
|
||||
ADDITIONAL_LDFLAGS += -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
|
||||
@@ -33,9 +33,8 @@
|
||||
}
|
||||
|
||||
- (id) authenticatorInContext: (id) context;
|
||||
- (MAPIStoreUserContext *) userContext;
|
||||
|
||||
- (void) setUserContext: (MAPIStoreUserContext *) newContext;
|
||||
- (void) cleanup;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -76,11 +76,6 @@ MAPIApplication *MAPIApp = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (MAPIStoreUserContext *) userContext
|
||||
{
|
||||
return userContext;
|
||||
}
|
||||
|
||||
- (void) setUserContext: (MAPIStoreUserContext *) newContext
|
||||
{
|
||||
/* user contexts must not be retained here ad their holder (mapistore)
|
||||
@@ -93,9 +88,4 @@ MAPIApplication *MAPIApp = nil;
|
||||
return [userContext authenticator];
|
||||
}
|
||||
|
||||
- (void) cleanup
|
||||
{
|
||||
[userContext deactivate];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -45,7 +45,6 @@
|
||||
#import "MAPIStoreRecurrenceUtils.h"
|
||||
#import "MAPIStoreSamDBUtils.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSArray+MAPIStore.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSDate+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
@@ -264,7 +263,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
@@ -273,7 +272,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
recipient->username = NULL;
|
||||
entryId = MAPIStoreExternalEntryId (cn, email);
|
||||
}
|
||||
recipient->type = [[person role] isEqualToString: @"OPT-PARTICIPANT"] ? MAPI_CC : MAPI_TO;
|
||||
recipient->type = MAPI_TO;
|
||||
|
||||
/* properties */
|
||||
p = 0;
|
||||
@@ -283,7 +282,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
|
||||
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
|
||||
p++;
|
||||
|
||||
|
||||
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0);
|
||||
p++;
|
||||
@@ -365,7 +364,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
@@ -383,7 +382,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
|
||||
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
|
||||
p++;
|
||||
|
||||
|
||||
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0);
|
||||
p++;
|
||||
@@ -792,20 +791,11 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc;
|
||||
NSCalendarDate *dateValue, *start;
|
||||
NSCalendarDate *dateValue;
|
||||
|
||||
if ([event isRecurrent])
|
||||
{
|
||||
/* [MS-OXOCAL] For a recurring series, this property specifies
|
||||
midnight in the user's machine time zone, on the date of the
|
||||
first instance, then is persisted in UTC. */
|
||||
start = [event startDate];
|
||||
dateValue = [NSCalendarDate dateWithYear: [start yearOfCommonEra]
|
||||
month: [start monthOfYear]
|
||||
day: [start dayOfMonth]
|
||||
hour: 0 minute: 0 second: 0
|
||||
timeZone: timeZone];
|
||||
[dateValue setTimeZone: utcTZ];
|
||||
dateValue = [[event startDate] hour: 0 minute: 0 second: 0];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -904,7 +894,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
}
|
||||
else
|
||||
dateValue = [NSCalendarDate dateWithYear: 4500 month: 8 day: 31
|
||||
hour: 23 minute: 59 second: 00
|
||||
hour: 23 minute: 59 second: 59
|
||||
timeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
@@ -931,7 +921,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
else
|
||||
@@ -1201,31 +1191,11 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
return [self getYes: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagSensitivity: (void **) data
|
||||
- (int) getPidTagSensitivity: (void **) data // not implemented, depends on CLASS
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* See [MS-OXCICAL] Section 2.1.3.11.20.4 */
|
||||
uint32_t v;
|
||||
NSString *accessClass;
|
||||
|
||||
accessClass = [event accessClass];
|
||||
if (accessClass)
|
||||
{
|
||||
if ([accessClass isEqualToString: @"X-PERSONAL"])
|
||||
v = 0x1;
|
||||
else if ([accessClass isEqualToString: @"PRIVATE"])
|
||||
v = 0x2;
|
||||
else if ([accessClass isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 0x3;
|
||||
else
|
||||
v = 0x0; /* PUBLIC */
|
||||
}
|
||||
else
|
||||
v = 0x0; /* PUBLIC */
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
// normal = 0, personal?? = 1, private = 2, confidential = 3
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagImportance: (void **) data
|
||||
@@ -1244,29 +1214,11 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidNameKeywords: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* See [MS-OXCICAL] Section 2.1.3.1.1.20.3 */
|
||||
NSArray *categories;
|
||||
|
||||
categories = [event categories];
|
||||
if (categories)
|
||||
{
|
||||
*data = [categories asMVUnicodeInMemCtx: memCtx];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPidTagBody: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
int rc;
|
||||
NSRange range;
|
||||
NSString *stringValue;
|
||||
NSString *trimingString = @"\r\n\n";
|
||||
|
||||
/* FIXME: there is a confusion in NGCards around "comment" and "description" */
|
||||
stringValue = [event comment];
|
||||
@@ -1274,14 +1226,6 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
&& ![stringValue isEqualToString: @"\r\n"]
|
||||
&& ![stringValue isEqualToString: @"\n"])
|
||||
{
|
||||
/* Avoiding those trail weird characters at event description */
|
||||
range = [stringValue rangeOfString: trimingString
|
||||
options: NSBackwardsSearch];
|
||||
if (range.location != NSNotFound)
|
||||
{
|
||||
stringValue = [stringValue substringToIndex: (NSMaxRange(range) -1)];
|
||||
}
|
||||
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
}
|
||||
@@ -1461,6 +1405,8 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
firstStartDate = [event firstRecurrenceStartDate];
|
||||
if (firstStartDate)
|
||||
{
|
||||
[firstStartDate setTimeZone: timeZone];
|
||||
|
||||
arp = talloc_zero (NULL, struct AppointmentRecurrencePattern);
|
||||
[rule fillRecurrencePattern: &arp->RecurrencePattern
|
||||
withEvent: event
|
||||
@@ -1470,12 +1416,6 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
arp->WriterVersion2 = 0x00003008; /* 0x3008 for compatibility with
|
||||
ol2003 */
|
||||
|
||||
/* All day events' dates are specified in floating time
|
||||
([MS-OXCICAL] 2.1.3.1.1.20.10). The StartTimeOffset and EndTimeOffset
|
||||
fields are relative to midnight of those days ([MS-OXOCAL] 2.2.1.44.5),
|
||||
so no time zone adjustment is needed */
|
||||
if (![event isAllDay])
|
||||
[firstStartDate setTimeZone: timeZone];
|
||||
startMinutes = ([firstStartDate hourOfDay] * 60
|
||||
+ [firstStartDate minuteOfHour]);
|
||||
arp->StartTimeOffset = startMinutes;
|
||||
@@ -1876,25 +1816,16 @@ ReservedBlockEE2Size: 00 00 00 00
|
||||
NSArray *alarms;
|
||||
NSUInteger count, max;
|
||||
iCalAlarm *currentAlarm;
|
||||
NSString *action, *webstatus;
|
||||
NSString *action;
|
||||
|
||||
alarms = [event alarms];
|
||||
max = [alarms count];
|
||||
for (count = 0; !alarm && count < max; count++)
|
||||
{
|
||||
currentAlarm = [alarms objectAtIndex: count];
|
||||
|
||||
// Only handle 'display' alarms
|
||||
action = [currentAlarm action];
|
||||
if ([action caseInsensitiveCompare: @"display"] != NSOrderedSame)
|
||||
continue;
|
||||
|
||||
// Ignore alarms already triggered
|
||||
webstatus = [[currentAlarm trigger] value: 0 ofAttribute: @"x-webstatus"];
|
||||
if ([webstatus caseInsensitiveCompare: @"triggered"] == NSOrderedSame)
|
||||
continue;
|
||||
|
||||
ASSIGN (alarm, currentAlarm);
|
||||
action = [[currentAlarm action] lowercaseString];
|
||||
if (!action || [action isEqualToString: @"display"])
|
||||
ASSIGN (alarm, currentAlarm);
|
||||
}
|
||||
|
||||
alarmSet = YES;
|
||||
@@ -2073,23 +2004,17 @@ ReservedBlockEE2Size: 00 00 00 00
|
||||
enum mapistore_error rc;
|
||||
iCalTimeZone *icalTZ;
|
||||
|
||||
/* [MS-OXOCAL] 3.1.5.5.1: This property is used in floating (all-day) events,
|
||||
specified in floating time, to convert the start date from UTC to the user's
|
||||
time zone */
|
||||
if ([event isAllDay])
|
||||
icalTZ = [iCalTimeZone timeZoneForName: [timeZone timeZoneName]];
|
||||
else if ([event isRecurrent])
|
||||
icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone];
|
||||
else
|
||||
icalTZ = nil;
|
||||
|
||||
if (icalTZ)
|
||||
if ([event isRecurrent])
|
||||
{
|
||||
/* [MS-OXOCAL] 2.2.1.42: This property can only have the E flag set in the
|
||||
TimeZoneDefinition struct */
|
||||
*data = [icalTZ asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG
|
||||
inMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone];
|
||||
if (icalTZ)
|
||||
{
|
||||
*data = [icalTZ asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG | TZRULE_FLAG_RECUR_CURRENT_TZREG
|
||||
inMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
@@ -63,15 +63,19 @@
|
||||
SOGoAppointmentObject *newEntry;
|
||||
NSString *name;
|
||||
|
||||
[[self userContext] activate];
|
||||
//[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
|
||||
|
||||
name = [NSString stringWithFormat: @"%@.ics",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoAppointmentObject objectWithName: name
|
||||
inContainer: sogoObject];
|
||||
[newEntry setIsNew: YES];
|
||||
/* the WOContext is required here for resolving notification pages */
|
||||
[newEntry setContext: [[self userContext] woContext]];
|
||||
newMessage = [MAPIStoreCalendarMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@@ -96,14 +100,6 @@
|
||||
[roles addObject: SOGoCalendarRole_PrivateViewer];
|
||||
[roles addObject: SOGoCalendarRole_ConfidentialViewer];
|
||||
}
|
||||
if (rights & RightsFreeBusySimple)
|
||||
{
|
||||
[roles addObject: SOGoCalendarRole_PublicDAndTViewer];
|
||||
}
|
||||
if (rights & RightsFreeBusyDetailed)
|
||||
{
|
||||
[roles addObject: SOGoCalendarRole_ConfidentialDAndTViewer];
|
||||
}
|
||||
|
||||
// [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles];
|
||||
|
||||
@@ -125,21 +121,12 @@
|
||||
else if ([roles containsObject: SOGoCalendarRole_PublicViewer]
|
||||
&& [roles containsObject: SOGoCalendarRole_PrivateViewer]
|
||||
&& [roles containsObject: SOGoCalendarRole_ConfidentialViewer])
|
||||
// We have to set by hand other rights as only the highest role is returned
|
||||
// See SOGoAppointmentFolder.m:aclsForUser for details
|
||||
rights |= RightsReadItems | RightsFreeBusySimple | RightsFreeBusyDetailed;
|
||||
|
||||
if ([roles containsObject: SOGoCalendarRole_PublicDAndTViewer])
|
||||
rights |= RightsFreeBusySimple;
|
||||
|
||||
if ([roles containsObject: SOGoCalendarRole_ConfidentialDAndTViewer])
|
||||
rights |= RightsFreeBusyDetailed;
|
||||
|
||||
rights |= RightsReadItems | 0x1800;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -182,14 +169,6 @@
|
||||
[(SOGoAppointmentFolder *) sogoObject aclSQLListingFilter]];
|
||||
}
|
||||
|
||||
- (int) getPidTagContainerClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"IPF.Appointment" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidTagDefaultPostMessageClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
// extern void ndr_print_AppointmentRecurrencePattern(struct ndr_print *ndr, const char *name, const struct AppointmentRecurrencePattern *r);
|
||||
|
||||
static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
static Class NSArrayK;
|
||||
|
||||
@implementation SOGoAppointmentObject (MAPIStoreExtension)
|
||||
|
||||
@@ -97,7 +97,6 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
+ (void) initialize
|
||||
{
|
||||
NSArrayK = [NSArray class];
|
||||
MAPIStoreAppointmentWrapperK = [MAPIStoreAppointmentWrapper class];
|
||||
}
|
||||
|
||||
+ (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
@@ -181,12 +180,6 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
else
|
||||
{
|
||||
origCalendar = [sogoObject calendar: YES secure: YES];
|
||||
if (!origCalendar)
|
||||
{
|
||||
[self errorWithFormat: @"Incorrect calendar event %@. Empty message is created",
|
||||
[self url]];
|
||||
return self;
|
||||
}
|
||||
calendar = [origCalendar mutableCopy];
|
||||
masterEvent = [[calendar events] objectAtIndex: 0];
|
||||
[self _setupAttachmentParts];
|
||||
@@ -212,23 +205,13 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (MAPIStoreAppointmentWrapper *) _appointmentWrapper
|
||||
/* getters */
|
||||
- (int) getPidLidFInvited: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSUInteger i, max;
|
||||
id proxy;
|
||||
max = [proxies count];
|
||||
for (i = 0; i < max; i++) {
|
||||
proxy = [proxies objectAtIndex: i];
|
||||
if ([proxy isKindOfClass: MAPIStoreAppointmentWrapperK])
|
||||
{
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
return [self getYes: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
|
||||
/* getters */
|
||||
- (int) getPidTagMessageClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -341,63 +324,100 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self _appointmentWrapper] fillMessageData: msgData
|
||||
inMemCtx: memCtx];
|
||||
/* HACK: we know the first (and only) proxy is our appointment wrapper
|
||||
instance, but this might not always be true */
|
||||
[[proxies objectAtIndex: 0] fillMessageData: msgData
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
*dataPtr = msgData;
|
||||
}
|
||||
|
||||
/* sender representing */
|
||||
// - (int) getPidTagSentRepresentingEmailAddress: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagSenderEmailAddress: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagSentRepresentingAddressType: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getSMTPAddrType: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagSentRepresentingName: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagSenderName: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagSentRepresentingEntryId: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagSenderEntryId: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
/* attendee */
|
||||
// - (int) getPidTagReceivedByAddressType: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [appointmentWrapper getPidTagReceivedByAddressType: data
|
||||
// inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedByEmailAddress: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [appointmentWrapper getPidTagReceivedByEmailAddress: data
|
||||
// inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedByName: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [appointmentWrapper getPidTagReceivedByName: data
|
||||
// inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedByEntryId: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [appointmentWrapper getPidTagReceivedByEntryId: data
|
||||
// inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// /* attendee representing */
|
||||
// - (int) getPidTagReceivedRepresentingEmailAddress: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagReceivedByEmailAddress: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedRepresentingAddressType: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getSMTPAddrType: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedRepresentingName: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagReceivedByName: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
// - (int) getPidTagReceivedRepresentingEntryId: (void **) data
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// return [self getPidTagReceivedByEntryId: data inMemCtx: memCtx];
|
||||
// }
|
||||
|
||||
- (int) getPidTagResponseRequested: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getYes: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
/* This three methods: getPidTagNormalizedSubject,
|
||||
getPidTagSensitivity and getPidTagImportance are implemented in
|
||||
MAPIStoreMessage base class, then the proxy method is not reached
|
||||
(see MAPIStoreObject).
|
||||
*/
|
||||
- (int) getPidTagNormalizedSubject: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
||||
|
||||
appointmentWrapper = [self _appointmentWrapper];
|
||||
if (appointmentWrapper)
|
||||
return [appointmentWrapper getPidTagNormalizedSubject: data inMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
- (int) getPidTagSensitivity: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
||||
|
||||
appointmentWrapper = [self _appointmentWrapper];
|
||||
if (appointmentWrapper)
|
||||
return [appointmentWrapper getPidTagSensitivity: data inMemCtx: memCtx];
|
||||
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagImportance: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
MAPIStoreAppointmentWrapper *appointmentWrapper;
|
||||
|
||||
appointmentWrapper = [self _appointmentWrapper];
|
||||
if (appointmentWrapper)
|
||||
return [appointmentWrapper getPidTagImportance: data inMemCtx: memCtx];
|
||||
|
||||
*data = MAPILongValue (memCtx, 1);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) _uidFromGlobalObjectId: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSData *objectId;
|
||||
@@ -494,7 +514,6 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
folder = [sogoObject container];
|
||||
|
||||
/* reinstantiate the old sogo object and attach it to self */
|
||||
[[self userContext] activate];
|
||||
woContext = [[self userContext] woContext];
|
||||
if (isNew)
|
||||
{
|
||||
@@ -578,7 +597,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
// iCalCalendar *vCalendar;
|
||||
// NSCalendarDate *now;
|
||||
@@ -635,7 +654,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
withActiveUser: activeUser
|
||||
inMemCtx: memCtx];
|
||||
[self _updateAttachedEvents];
|
||||
[[self userContext] activate];
|
||||
[[self userContext] activateWithUser: activeUser];
|
||||
[sogoObject updateContentWithCalendar: calendar
|
||||
fromRequest: nil];
|
||||
[self updateVersions];
|
||||
@@ -671,7 +690,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
return newAttachment;
|
||||
}
|
||||
|
||||
- (enum mapistore_error) setReadFlag: (uint8_t) flag
|
||||
- (int) setReadFlag: (uint8_t) flag
|
||||
{
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -182,9 +182,6 @@ static Class MAPIStoreCalendarMessageK = Nil;
|
||||
forKey: MAPIPropertyKey (PR_CREATION_TIME)];
|
||||
[knownProperties setObject: @"c_uid"
|
||||
forKey: MAPIPropertyKey (PR_OWNER_APPT_ID)];
|
||||
/* Use by oxcfxics to sort the latest first */
|
||||
[knownProperties setObject: @"c_lastmodified"
|
||||
forKey: MAPIPropertyKey (PidTagMessageDeliveryTime)];
|
||||
}
|
||||
|
||||
return [knownProperties objectForKey: MAPIPropertyKey (property)];
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#import <Contacts/SOGoContactFolders.h>
|
||||
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "MAPIStoreContactsContext.h"
|
||||
#import "MAPIStoreContactsMessage.h"
|
||||
#import "MAPIStoreContactsMessageTable.h"
|
||||
@@ -59,12 +58,10 @@
|
||||
SOGoContactGCSEntry *newEntry;
|
||||
NSString *name;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
name = [NSString stringWithFormat: @"%@.vcf",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoContactGCSEntry objectWithName: name
|
||||
inContainer: sogoObject];
|
||||
inContainer: sogoObject];
|
||||
[newEntry setIsNew: YES];
|
||||
newMessage = [MAPIStoreContactsMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
@@ -103,7 +100,7 @@
|
||||
rights |= RightsReadItems;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,21 +170,6 @@
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidTagProfession: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[sogoObject vCard] role];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagCompanyName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -738,107 +723,6 @@
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagWeddingAnniversary: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSCalendarDate *dateValue;
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-anniversary"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue && ! [stringValue isEqualToString: @""])
|
||||
{
|
||||
dateValue = [NSCalendarDate dateWithString: stringValue
|
||||
calendarFormat: @"%Y-%m-%d"];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagSpouseName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagManagerName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-manager"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagAssistant: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-assistant"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidTagOfficeLocation: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidFreeBusyLocation: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *stringValue;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
|
||||
stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"]
|
||||
flattenedValuesForKey: @""];
|
||||
if (stringValue)
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
//
|
||||
// Decomposed fullname getters
|
||||
//
|
||||
@@ -1249,18 +1133,12 @@ fromProperties: (NSDictionary *) attachmentProps
|
||||
|
||||
|
||||
//
|
||||
// job title, profession, nickname, company name, deparment, work url, im address/screen name and birthday
|
||||
// job title, nickname, company name, deparment, work url, im address/screen name and birthday
|
||||
//
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_TITLE_UNICODE)];
|
||||
if (value)
|
||||
[newCard setTitle: value];
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_PROFESSION_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[newCard setRole: value];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_NICKNAME_UNICODE)];
|
||||
if (value)
|
||||
[newCard setNickname: value];
|
||||
@@ -1295,52 +1173,6 @@ fromProperties: (NSDictionary *) attachmentProps
|
||||
[newCard setBday: [value descriptionWithCalendarFormat: @"%Y-%m-%d"]];
|
||||
}
|
||||
|
||||
//
|
||||
// wedding anniversary, spouse's name, manager's name, assistant's name, office location, freebusy location
|
||||
//
|
||||
value = [properties objectForKey: MAPIPropertyKey(PidTagWeddingAnniversary)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"x-ms-anniversary"]
|
||||
setSingleValue: [value descriptionWithCalendarFormat: @"%Y-%m-%d"]
|
||||
forKey: @""];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_SPOUSE_NAME_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"x-ms-spouse"]
|
||||
setSingleValue: value forKey: @""];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_MANAGER_NAME_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"x-ms-manager"]
|
||||
setSingleValue: value forKey: @""];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_ASSISTANT_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"x-ms-assistant"]
|
||||
setSingleValue: value forKey: @""];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_LOCATION_UNICODE)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"x-ms-office"]
|
||||
setSingleValue: value forKey: @""];
|
||||
}
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey(PidLidFreeBusyLocation)];
|
||||
if (value)
|
||||
{
|
||||
[[newCard uniqueChildWithTag: @"fburl"]
|
||||
setSingleValue: value forKey: @""];
|
||||
}
|
||||
|
||||
/* photo */
|
||||
if ([attachmentParts count] > 0)
|
||||
{
|
||||
|
||||
@@ -220,9 +220,6 @@ static Class MAPIStoreContactsMessageK, NGMailAddressK, NSDataK, NSStringK;
|
||||
forKey: MAPIPropertyKey (PidTagSubject)];
|
||||
[knownProperties setObject: @"c_cn"
|
||||
forKey: MAPIPropertyKey (PidTagNormalizedSubject)];
|
||||
/* Use by oxcfxics to sort the latest first */
|
||||
[knownProperties setObject: @"c_lastmodified"
|
||||
forKey: MAPIPropertyKey (PidTagMessageDeliveryTime)];
|
||||
}
|
||||
|
||||
return [knownProperties objectForKey: MAPIPropertyKey (property)];
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
+ (enum mapistore_error) createRootFolder: (NSString **) mapistoreUriP
|
||||
withFID: (uint64_t ) fid
|
||||
@@ -75,11 +75,11 @@
|
||||
+ (int) openContext: (MAPIStoreContext **) contextPtr
|
||||
withURI: (const char *) newUri
|
||||
connectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
andTDBIndexing: (struct indexing_context *) indexing;
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb;
|
||||
|
||||
- (id) initFromURL: (NSURL *) newUri
|
||||
withConnectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
andTDBIndexing: (struct indexing_context *) indexing;
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb;
|
||||
|
||||
- (NSURL *) url;
|
||||
- (struct mapistore_connection_info *) connectionInfo;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import "MAPIStoreAttachment.h"
|
||||
// #import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreFallbackContext.h"
|
||||
#import "MAPIStoreFolder.h"
|
||||
#import "MAPIStoreFolderTable.h"
|
||||
@@ -50,7 +51,6 @@
|
||||
#import "MAPIStoreContext.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <dlinklist.h>
|
||||
#include <stdbool.h>
|
||||
#include <gen_ndr/exchange.h>
|
||||
#include <util/attr.h>
|
||||
@@ -88,31 +88,32 @@ static NSMutableDictionary *contextClassMapping;
|
||||
currentClass = [classes objectAtIndex: count];
|
||||
moduleName = [currentClass MAPIModuleName];
|
||||
if (moduleName)
|
||||
{
|
||||
[contextClassMapping setObject: currentClass
|
||||
{
|
||||
[contextClassMapping setObject: currentClass
|
||||
forKey: moduleName];
|
||||
NSLog (@" registered class '%@' as handler of '%@' contexts",
|
||||
NSStringFromClass (currentClass), moduleName);
|
||||
}
|
||||
NSLog (@" registered class '%@' as handler of '%@' contexts",
|
||||
NSStringFromClass (currentClass), moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
MAPIStoreFallbackContextK = [MAPIStoreFallbackContext class];
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *list, *current;
|
||||
NSArray *classes;
|
||||
Class currentClass;
|
||||
NSUInteger count, max;
|
||||
MAPIStoreUserContext *userContext;
|
||||
|
||||
list = NULL;
|
||||
|
||||
// User context is activated on initialization
|
||||
[MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexingTdb];
|
||||
[userContext activateWithUser: [userContext sogoUser]];
|
||||
|
||||
classes = GSObjCAllSubclassesOfClass (self);
|
||||
max = [classes count];
|
||||
@@ -120,7 +121,7 @@ static NSMutableDictionary *contextClassMapping;
|
||||
{
|
||||
currentClass = [classes objectAtIndex: count];
|
||||
current = [currentClass listContextsForUser: userName
|
||||
withIndexing: indexing
|
||||
withTDBIndexing: indexingTdb
|
||||
inMemCtx: memCtx];
|
||||
if (current)
|
||||
DLIST_CONCATENATE(list, current, void);
|
||||
@@ -130,7 +131,7 @@ static NSMutableDictionary *contextClassMapping;
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return NULL;
|
||||
@@ -215,7 +216,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
+ (int) openContext: (MAPIStoreContext **) contextPtr
|
||||
withURI: (const char *) newUri
|
||||
connectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
andTDBIndexing: (struct indexing_context *) indexing
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
{
|
||||
MAPIStoreContext *context;
|
||||
Class contextClass;
|
||||
@@ -223,6 +224,8 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSURL *baseURL;
|
||||
int rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
//NSLog (@"METHOD '%s' (%d) -- uri: '%s'", __FUNCTION__, __LINE__, newUri);
|
||||
|
||||
context = nil;
|
||||
|
||||
baseURL = CompleteURLFromMapistoreURI (newUri);
|
||||
@@ -236,7 +239,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
{
|
||||
context = [[contextClass alloc] initFromURL: baseURL
|
||||
withConnectionInfo: newConnInfo
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
if (context)
|
||||
{
|
||||
[context autorelease];
|
||||
@@ -269,15 +272,10 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
- (id) initFromURL: (NSURL *) newUrl
|
||||
withConnectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
andTDBIndexing: (struct indexing_context *) indexing
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
{
|
||||
NSString *username;
|
||||
|
||||
if (newConnInfo == NULL)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [self init]))
|
||||
{
|
||||
ASSIGN (contextUrl, newUrl);
|
||||
@@ -293,7 +291,14 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
ASSIGN (userContext,
|
||||
[MAPIStoreUserContext userContextWithUsername: username
|
||||
andTDBIndexing: indexing]);
|
||||
andTDBIndexing: indexingTdb]);
|
||||
|
||||
#if 0
|
||||
mapistore_mgmt_backend_register_user (newConnInfo,
|
||||
"SOGo",
|
||||
[username UTF8String]);
|
||||
#endif
|
||||
|
||||
connInfo = newConnInfo;
|
||||
username = [NSString stringWithUTF8String: newConnInfo->username];
|
||||
ASSIGN (activeUser, [SOGoUser userWithLogin: username]);
|
||||
@@ -311,6 +316,12 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
#if 0
|
||||
mapistore_mgmt_backend_unregister_user ([self connectionInfo], "SOGo",
|
||||
[[userContext username]
|
||||
UTF8String]);
|
||||
#endif
|
||||
|
||||
[contextUrl release];
|
||||
[userContext release];
|
||||
[containersBag release];
|
||||
@@ -338,29 +349,42 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
return activeUser;
|
||||
}
|
||||
|
||||
// - (void) logRestriction: (struct mapi_SRestriction *) res
|
||||
// withState: (MAPIRestrictionState) state
|
||||
// {
|
||||
// NSString *resStr;
|
||||
|
||||
// resStr = MAPIStringForRestriction (res);
|
||||
|
||||
// [self logWithFormat: @"%@ --> %@", resStr, MAPIStringForRestrictionState (state)];
|
||||
// }
|
||||
|
||||
- (int) getPath: (char **) path
|
||||
ofFMID: (uint64_t) fmid
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
int rc;
|
||||
NSString *objectURL, *url;
|
||||
// TDB_DATA key, dbuf;
|
||||
|
||||
url = [contextUrl absoluteString];
|
||||
// FIXME transform percent escapes but not for user part of the url
|
||||
//[xxxx stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
url = [[contextUrl absoluteString]
|
||||
stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
objectURL = [[userContext mapping] urlFromID: fmid];
|
||||
if (objectURL)
|
||||
{
|
||||
if ([objectURL hasPrefix: url])
|
||||
{
|
||||
*path = [[objectURL substringFromIndex: 7] asUnicodeInMemCtx: memCtx];
|
||||
[self logWithFormat: @"found path '%s' for fmid 0x%.16"PRIx64"", *path, fmid];
|
||||
*path = [[objectURL substringFromIndex: 7]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
[self logWithFormat: @"found path '%s' for fmid %.16x",
|
||||
*path, fmid];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"context (%@, %@) does not contain "
|
||||
@"found fmid: 0x%.16"PRIx64"", objectURL, url, fmid];
|
||||
[self logWithFormat: @"context (%@, %@) does not contain"
|
||||
@" found fmid: 0x%.16x",
|
||||
objectURL, url, fmid];
|
||||
*path = NULL;
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -368,6 +392,14 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"%s: you should *never* get here", __PRETTY_FUNCTION__];
|
||||
// /* attempt to populate our mapping dict with data from indexing.tdb */
|
||||
// key.dptr = (unsigned char *) talloc_asprintf (memCtx, "0x%.16llx",
|
||||
// (long long unsigned int )fmid);
|
||||
// key.dsize = strlen ((const char *) key.dptr);
|
||||
|
||||
// dbuf = tdb_fetch (memCtx->indexing_list->index_ctx->tdb, key);
|
||||
// talloc_free (key.dptr);
|
||||
// uri = talloc_strndup (memCtx, (const char *)dbuf.dptr, dbuf.dsize);
|
||||
*path = NULL;
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -383,6 +415,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
withFID: (uint64_t) newFid
|
||||
{
|
||||
enum mapistore_error rc;
|
||||
MAPIStoreMapping *mapping;
|
||||
MAPIStoreFolder *baseFolder;
|
||||
SOGoFolder *currentFolder;
|
||||
WOContext *woContext;
|
||||
@@ -390,7 +423,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSArray *pathComponents;
|
||||
NSUInteger count, max;
|
||||
|
||||
[userContext activate];
|
||||
mapping = [userContext mapping];
|
||||
if (![mapping urlFromID: newFid])
|
||||
[mapping registerURL: [contextUrl absoluteString]
|
||||
withID: newFid];
|
||||
[userContext activateWithUser: activeUser];
|
||||
woContext = [userContext woContext];
|
||||
|
||||
[self ensureContextFolder];
|
||||
@@ -452,7 +489,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
/* utils */
|
||||
|
||||
- (NSString *) extractChildNameFromURL: (NSString *) objectURL
|
||||
andFolderURLAt: (NSString **) folderURL;
|
||||
andFolderURLAt: (NSString **) folderURL;
|
||||
{
|
||||
NSString *childKey;
|
||||
NSRange lastSlash;
|
||||
@@ -461,15 +498,15 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
if ([objectURL hasSuffix: @"/"])
|
||||
objectURL = [objectURL substringToIndex: [objectURL length] - 2];
|
||||
lastSlash = [objectURL rangeOfString: @"/"
|
||||
options: NSBackwardsSearch];
|
||||
options: NSBackwardsSearch];
|
||||
if (lastSlash.location != NSNotFound)
|
||||
{
|
||||
slashPtr = NSMaxRange (lastSlash);
|
||||
childKey = [objectURL substringFromIndex: slashPtr];
|
||||
if ([childKey length] == 0)
|
||||
childKey = nil;
|
||||
childKey = nil;
|
||||
if (folderURL)
|
||||
*folderURL = [objectURL substringToIndex: slashPtr];
|
||||
*folderURL = [objectURL substringToIndex: slashPtr];
|
||||
}
|
||||
else
|
||||
childKey = nil;
|
||||
@@ -480,10 +517,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
- (uint64_t) idForObjectWithKey: (NSString *) key
|
||||
inFolderURL: (NSString *) folderURL
|
||||
{
|
||||
NSString *childURL;
|
||||
NSString *childURL, *owner;
|
||||
MAPIStoreMapping *mapping;
|
||||
uint64_t mappingId;
|
||||
enum mapistore_error ret;
|
||||
uint32_t contextId;
|
||||
void *rootObject;
|
||||
|
||||
if (key)
|
||||
childURL = [NSString stringWithFormat: @"%@%@", folderURL,
|
||||
@@ -494,13 +532,17 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
mappingId = [mapping idFromURL: childURL];
|
||||
if (mappingId == NSNotFound)
|
||||
{
|
||||
[self logWithFormat: @"No id exist yet for '%@', requesting one", childURL];
|
||||
ret = mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId);
|
||||
if (ret == MAPISTORE_SUCCESS)
|
||||
[mapping registerURL: childURL withID: mappingId];
|
||||
else
|
||||
[self errorWithFormat: @"Error trying to get new folder id (%d): %s",
|
||||
ret, mapistore_errstr (ret)];
|
||||
//[self warnWithFormat: @"no id exist yet for '%@', requesting one...",
|
||||
// childURL];
|
||||
openchangedb_get_new_folderID (connInfo->oc_ctx, &mappingId);
|
||||
[mapping registerURL: childURL withID: mappingId];
|
||||
contextId = 0;
|
||||
|
||||
mapistore_search_context_by_uri (connInfo->mstore_ctx, [folderURL UTF8String],
|
||||
&contextId, &rootObject);
|
||||
owner = [userContext username];
|
||||
mapistore_indexing_record_add_mid (connInfo->mstore_ctx, contextId,
|
||||
[owner UTF8String], mappingId);
|
||||
}
|
||||
|
||||
return mappingId;
|
||||
@@ -510,7 +552,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
{
|
||||
uint64_t newVersionNumber;
|
||||
|
||||
if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, connInfo->username, &newVersionNumber)
|
||||
if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, &newVersionNumber)
|
||||
!= MAPI_E_SUCCESS)
|
||||
abort ();
|
||||
|
||||
@@ -527,9 +569,9 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
memCtx = talloc_zero(NULL, TALLOC_CTX);
|
||||
newChangeNumbers = [NSMutableArray arrayWithCapacity: max];
|
||||
|
||||
|
||||
if (openchangedb_get_new_changeNumbers (connInfo->oc_ctx,
|
||||
memCtx, connInfo->username, max, &numbers)
|
||||
memCtx, max, &numbers)
|
||||
!= MAPI_E_SUCCESS || numbers->cValues != max)
|
||||
abort ();
|
||||
for (count = 0; count < max; count++)
|
||||
@@ -554,14 +596,15 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
|
||||
memCtx = talloc_zero(NULL, TALLOC_CTX);
|
||||
newFMIDs = [NSMutableArray arrayWithCapacity: max];
|
||||
|
||||
if (mapistore_indexing_get_new_folderIDs (connInfo->mstore_ctx,
|
||||
memCtx, max, &numbers)
|
||||
!= MAPISTORE_SUCCESS || numbers->cValues != max)
|
||||
|
||||
if (openchangedb_get_new_folderIDs (connInfo->oc_ctx,
|
||||
memCtx, max, &numbers)
|
||||
!= MAPI_E_SUCCESS || numbers->cValues != max)
|
||||
abort ();
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
newNumber = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
|
||||
newNumber
|
||||
= [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
|
||||
[newFMIDs addObject: newNumber];
|
||||
}
|
||||
|
||||
|
||||
@@ -138,9 +138,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
MAPIStoreMapping *mapping;
|
||||
NSRange slashRange;
|
||||
|
||||
pathComponent = nil;
|
||||
|
||||
if (isMove && ([targetFolder isKindOfClass: MAPIStoreDBFolderK] || !targetFolder))
|
||||
if (isMove && [targetFolder isKindOfClass: MAPIStoreDBFolderK])
|
||||
{
|
||||
path = [sogoObject path];
|
||||
slashRange = [path rangeOfString: @"/" options: NSBackwardsSearch];
|
||||
@@ -149,28 +147,14 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
format: @"db folder path must start with a '/'"];
|
||||
else
|
||||
pathComponent = [path substringFromIndex: slashRange.location + 1];
|
||||
|
||||
if (targetFolder)
|
||||
{
|
||||
targetPath = [[targetFolder sogoObject] path];
|
||||
newPath = [NSString stringWithFormat: @"%@/%@",
|
||||
targetPath, pathComponent];
|
||||
[dbFolder changePathTo: newPath
|
||||
intoNewContainer: [targetFolder dbFolder]];
|
||||
}
|
||||
else
|
||||
[dbFolder changePathTo: [NSString stringWithFormat: @"/fallback/%@", pathComponent]
|
||||
intoNewContainer: nil];
|
||||
|
||||
targetPath = [[targetFolder sogoObject] path];
|
||||
newPath = [NSString stringWithFormat: @"%@/%@",
|
||||
targetPath, pathComponent];
|
||||
[dbFolder changePathTo: newPath];
|
||||
|
||||
mapping = [self mapping];
|
||||
|
||||
if (targetFolder)
|
||||
newURL = [NSString stringWithFormat: @"%@%@/",
|
||||
[targetFolder url], pathComponent];
|
||||
else
|
||||
newURL = [NSString stringWithFormat: @"sogo://%@@fallback/%@/",
|
||||
[[self userContext] username], pathComponent];
|
||||
|
||||
newURL = [NSString stringWithFormat: @"%@%@/",
|
||||
[targetFolder url], pathComponent];
|
||||
[mapping updateID: [self objectId]
|
||||
withURL: newURL];
|
||||
|
||||
@@ -193,8 +177,6 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
SOGoMAPIDBMessage *fsObject;
|
||||
NSString *newKey;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
newKey = [NSString stringWithFormat: @"%@.plist",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
|
||||
@@ -216,13 +198,10 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
if ([[context activeUser] isEqual: ownerUser]
|
||||
|| [self subscriberCanReadMessages])
|
||||
{
|
||||
[[self userContext] activate];
|
||||
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
}
|
||||
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
andSortOrderings: sortOrderings];
|
||||
else
|
||||
keys = [NSArray array];
|
||||
|
||||
@@ -232,7 +211,6 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
[[self userContext] activate];
|
||||
return [dbFolder childKeysOfType: MAPIFolderCacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
@@ -268,7 +246,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
if ([date laterDate: fileDate] == fileDate)
|
||||
{
|
||||
//[self logWithFormat: @"current date: %@", date];
|
||||
|
||||
|
||||
date = fileDate;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +310,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
rights |= RightsFolderContact;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -367,7 +345,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
subscribed to read an open contained messages in order to enable them to
|
||||
find the "LocalFreebusy" message */
|
||||
[sogoObject reloadIfNeeded];
|
||||
|
||||
|
||||
displayName = [[sogoObject properties]
|
||||
objectForKey: MAPIPropertyKey (PidTagDisplayName)];
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
@@ -35,7 +34,6 @@
|
||||
#import "MAPIStoreDBFolder.h"
|
||||
#import "MAPIStoreDBMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
||||
@@ -52,22 +50,19 @@
|
||||
NSUInteger count;
|
||||
enum MAPITAGS faiProperties[] = { 0x68350102, 0x683c0102, 0x683e0102,
|
||||
0x683f0102, 0x68410003, 0x68420102,
|
||||
0x68450102, 0x68460003,
|
||||
// PR_VD_NAME_W, PR_VD_FLAGS, PR_VD_VERSION, PR_VIEW_CLSID
|
||||
0x7006001F, 0x70030003, 0x70070003, 0x68330048 };
|
||||
size_t faiSize = sizeof(faiProperties) / sizeof(enum MAPITAGS);
|
||||
0x68450102, 0x68460003 };
|
||||
|
||||
properties = talloc_zero (memCtx, struct SPropTagArray);
|
||||
properties->cValues = MAPIStoreSupportedPropertiesCount + faiSize;
|
||||
properties->cValues = MAPIStoreSupportedPropertiesCount + 8;
|
||||
properties->aulPropTag = talloc_array (properties, enum MAPITAGS,
|
||||
MAPIStoreSupportedPropertiesCount + faiSize);
|
||||
MAPIStoreSupportedPropertiesCount + 8);
|
||||
|
||||
for (count = 0; count < MAPIStoreSupportedPropertiesCount; count++)
|
||||
properties->aulPropTag[count] = MAPIStoreSupportedProperties[count];
|
||||
|
||||
/* FIXME (hack): append a few undocumented properties that can be added to
|
||||
FAI messages */
|
||||
for (count = 0; count < faiSize; count++)
|
||||
for (count = 0; count < 8; count++)
|
||||
properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count]
|
||||
= faiProperties[count];
|
||||
|
||||
@@ -106,105 +101,6 @@
|
||||
return objectVersion;
|
||||
}
|
||||
|
||||
- (void) _updatePredecessorChangeList
|
||||
{
|
||||
BOOL updated;
|
||||
enum mapistore_error rc;
|
||||
NSData *currentChangeList, *changeKey;
|
||||
NSMutableArray *changeKeys;
|
||||
NSMutableData *newChangeList;
|
||||
NSUInteger count, len;
|
||||
struct SizedXid *changes;
|
||||
struct SPropValue property;
|
||||
struct SRow aRow;
|
||||
struct XID *currentChangeKey;
|
||||
TALLOC_CTX *localMemCtx;
|
||||
uint32_t nChanges;
|
||||
|
||||
localMemCtx = talloc_new (NULL);
|
||||
if (!localMemCtx)
|
||||
{
|
||||
[self errorWithFormat: @"No more memory"];
|
||||
return;
|
||||
}
|
||||
|
||||
changeKey = [self getReplicaKeyFromGlobCnt: [self objectVersion]];
|
||||
|
||||
currentChangeList = [properties objectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
|
||||
if (!currentChangeList)
|
||||
{
|
||||
/* Create a new PredecessorChangeList */
|
||||
len = [changeKey length];
|
||||
newChangeList = [NSMutableData dataWithCapacity: len + 1];
|
||||
[newChangeList appendUInt8: len];
|
||||
[newChangeList appendData: changeKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update current predecessor change list with new change key */
|
||||
changes = [currentChangeList asSizedXidArrayInMemCtx: localMemCtx
|
||||
with: &nChanges];
|
||||
|
||||
updated = NO;
|
||||
currentChangeKey = [changeKey asXIDInMemCtx: localMemCtx];
|
||||
for (count = 0; count < nChanges && !updated; count++)
|
||||
{
|
||||
if (GUID_equal(&changes[count].XID.NameSpaceGuid, ¤tChangeKey->NameSpaceGuid))
|
||||
{
|
||||
NSData *globCnt, *oldGlobCnt;
|
||||
oldGlobCnt = [NSData dataWithBytes: changes[count].XID.LocalId.data length: changes[count].XID.LocalId.length];
|
||||
globCnt = [NSData dataWithBytes: currentChangeKey->LocalId.data length: currentChangeKey->LocalId.length];
|
||||
if ([globCnt compare: oldGlobCnt] == NSOrderedDescending)
|
||||
{
|
||||
if ([globCnt length] != [oldGlobCnt length])
|
||||
{
|
||||
[self errorWithFormat: @"Cannot compare globcnt with different length: %@ and %@", globCnt, oldGlobCnt];
|
||||
abort();
|
||||
}
|
||||
memcpy (changes[count].XID.LocalId.data, currentChangeKey->LocalId.data, currentChangeKey->LocalId.length);
|
||||
updated = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Serialise it */
|
||||
changeKeys = [NSMutableArray array];
|
||||
|
||||
if (!updated)
|
||||
[changeKeys addObject: changeKey];
|
||||
|
||||
for (count = 0; count < nChanges; count++)
|
||||
{
|
||||
changeKey = [NSData dataWithXID: &changes[count].XID];
|
||||
[changeKeys addObject: changeKey];
|
||||
}
|
||||
|
||||
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare context: localMemCtx];
|
||||
|
||||
newChangeList = [NSMutableData data];
|
||||
len = [changeKeys count];
|
||||
for (count = 0; count < len; count++)
|
||||
{
|
||||
changeKey = [changeKeys objectAtIndex: count];
|
||||
[newChangeList appendUInt8: [changeKey length]];
|
||||
[newChangeList appendData: changeKey];
|
||||
}
|
||||
}
|
||||
|
||||
if ([newChangeList length] > 0)
|
||||
{
|
||||
property.ulPropTag = PidTagPredecessorChangeList;
|
||||
property.value.bin = *[newChangeList asBinaryInMemCtx: localMemCtx];
|
||||
aRow.cValues = 1;
|
||||
aRow.lpProps = &property;
|
||||
rc = [self addPropertiesFromRow: &aRow];
|
||||
if (rc != MAPISTORE_SUCCESS)
|
||||
[self errorWithFormat: @"Impossible to add a new predecessor change list: %d", rc];
|
||||
}
|
||||
|
||||
talloc_free (localMemCtx);
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: how this can happen?
|
||||
//
|
||||
@@ -267,9 +163,6 @@
|
||||
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
|
||||
forKey: @"version"];
|
||||
|
||||
/* Update PredecessorChangeList accordingly */
|
||||
[self _updatePredecessorChangeList];
|
||||
|
||||
[self logWithFormat: @"%d props in dict", [properties count]];
|
||||
|
||||
[sogoObject save];
|
||||
@@ -313,36 +206,4 @@
|
||||
return [sogoObject lastModified];
|
||||
}
|
||||
|
||||
- (enum mapistore_error) setReadFlag: (uint8_t) flag
|
||||
{
|
||||
/* Modify PidTagMessageFlags from SetMessageReadFlag and
|
||||
SyncImportReadStateChanges ROPs */
|
||||
NSNumber *flags;
|
||||
uint32_t newFlag;
|
||||
|
||||
flags = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
|
||||
if (flags)
|
||||
{
|
||||
newFlag = [flags unsignedLongValue];
|
||||
if (flag & SUPPRESS_RECEIPT)
|
||||
newFlag |= MSGFLAG_READ;
|
||||
if (flag & CLEAR_RN_PENDING)
|
||||
newFlag &= ~MSGFLAG_RN_PENDING;
|
||||
if (flag & CLEAR_READ_FLAG)
|
||||
newFlag &= ~MSGFLAG_READ;
|
||||
if (flag & CLEAR_NRN_PENDING)
|
||||
newFlag &= ~MSGFLAG_NRN_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
newFlag = MSGFLAG_READ;
|
||||
if (flag & CLEAR_READ_FLAG)
|
||||
newFlag = 0x0;
|
||||
}
|
||||
[properties setObject: [NSNumber numberWithUnsignedLong: newFlag]
|
||||
forKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#undef DEBUG
|
||||
#include <inttypes.h>
|
||||
#include <dlinklist.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
|
||||
@implementation MAPIStoreFallbackContext
|
||||
@@ -46,7 +45,7 @@
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *firstContext = NULL, *context;
|
||||
@@ -56,10 +55,7 @@
|
||||
NSString *baseURL, *url, *name;
|
||||
MAPIStoreUserContext *userContext;
|
||||
|
||||
baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"]];
|
||||
|
||||
baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName];
|
||||
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
context->url = [baseURL asUnicodeInMemCtx: context];
|
||||
@@ -76,7 +72,7 @@
|
||||
inContainer: nil];
|
||||
[root setOwner: userName];
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
[userContext ensureFolderTableExists];
|
||||
[root setTableUrl: [userContext folderTableURL]];
|
||||
names = [root toManyRelationshipKeys];
|
||||
@@ -103,10 +99,7 @@
|
||||
forUser: (NSString *) userName
|
||||
{
|
||||
return [NSString stringWithFormat: @"sogo://%@@fallback/0x%.16"PRIx64"/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
(unsigned long long) fid];
|
||||
|
||||
userName, (unsigned long long) fid];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -121,7 +121,6 @@
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMIDs: (uint64_t *) targetMids
|
||||
andChangeKeys: (struct Binary_r **) targetChangeKeys
|
||||
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
|
||||
wantCopy: (uint8_t) want_copy
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
|
||||
@@ -151,7 +150,6 @@
|
||||
|
||||
/* helpers */
|
||||
- (uint64_t) idForObjectWithKey: (NSString *) childKey;
|
||||
- (MAPIStoreFolder *) rootContainer;
|
||||
|
||||
/* subclasses */
|
||||
- (MAPIStoreMessage *) createMessage;
|
||||
@@ -186,6 +184,12 @@
|
||||
/* subclass helpers */
|
||||
- (void) setupVersionsMessage;
|
||||
- (void) ensureIDsForChildKeys: (NSArray *) keys;
|
||||
- (void) postNotificationsForMoveCopyMessagesWithMIDs: (uint64_t *) srcMids
|
||||
andMessageURLs: (NSArray *) oldMessageURLs
|
||||
andCount: (uint32_t) midCount
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMIDs: (uint64_t *) targetMids
|
||||
wantCopy: (uint8_t) wantCopy;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+208
-121
@@ -29,7 +29,6 @@
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <SOGo/SOGoContentObject.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoFolder.h>
|
||||
@@ -53,7 +52,6 @@
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
#import "SOGoCacheGCSObject+MAPIStore.h"
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
|
||||
#include <gen_ndr/exchange.h>
|
||||
|
||||
@@ -102,8 +100,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSUInteger lastPartIdx;
|
||||
MAPIStoreUserContext *userContext;
|
||||
|
||||
parts = 0;
|
||||
lastPartIdx = 0;
|
||||
folderURL = [NSURL URLWithString: [self url]];
|
||||
/* note: -[NSURL path] returns an unescaped representation */
|
||||
path = [folderURL path];
|
||||
@@ -120,7 +116,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
folderName = [folderURL host];
|
||||
|
||||
userContext = [self userContext];
|
||||
[userContext activate];
|
||||
[userContext ensureFolderTableExists];
|
||||
|
||||
ASSIGN (dbFolder,
|
||||
@@ -232,19 +227,24 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
- (id) lookupFolder: (NSString *) folderKey
|
||||
{
|
||||
MAPIStoreFolder *childFolder = nil;
|
||||
MAPIStoreFolder *childFolder;
|
||||
SOGoFolder *sogoFolder;
|
||||
WOContext *woContext;
|
||||
|
||||
if ([[self folderKeys] containsObject: folderKey])
|
||||
{
|
||||
[[self userContext] activate];
|
||||
sogoFolder = [sogoObject lookupName: folderKey
|
||||
inContext: nil
|
||||
woContext = [[self userContext] woContext];
|
||||
sogoFolder = [sogoObject lookupName: folderKey inContext: woContext
|
||||
acquire: NO];
|
||||
if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK])
|
||||
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
|
||||
inContainer: self];
|
||||
{
|
||||
[sogoFolder setContext: woContext];
|
||||
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
|
||||
inContainer: self];
|
||||
}
|
||||
}
|
||||
else
|
||||
childFolder = nil;
|
||||
|
||||
return childFolder;
|
||||
}
|
||||
@@ -275,16 +275,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
if (messageKey)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
msgObject = [sogoObject lookupName: messageKey
|
||||
inContext: nil
|
||||
acquire: NO];
|
||||
/* If the lookup in the indexing table works, but the IMAP does
|
||||
not have the message, then the message does not exist in this
|
||||
folder */
|
||||
if (msgObject && [msgObject isKindOfClass: [SOGoMailObject class]]
|
||||
&& ! [(SOGoMailObject *)msgObject doesMailExist])
|
||||
return nil;
|
||||
if (msgObject && ![msgObject isKindOfClass: NSExceptionK])
|
||||
{
|
||||
[msgObject setContext: [[self userContext] woContext]];
|
||||
@@ -305,7 +298,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
if (messageKey)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
if ([[self faiMessageKeys] containsObject: messageKey])
|
||||
{
|
||||
msgObject = [dbFolder lookupName: messageKey
|
||||
@@ -394,8 +386,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
withRow: (struct SRow *) aRow
|
||||
andFID: (uint64_t) fid
|
||||
{
|
||||
BOOL mapped;
|
||||
enum mapistore_error rc = MAPISTORE_SUCCESS;
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
MAPIStoreMapping *mapping;
|
||||
NSString *baseURL, *childURL, *folderKey;
|
||||
MAPIStoreFolder *childFolder;
|
||||
@@ -423,12 +414,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
childURL = [NSString stringWithFormat: @"%@%@/",
|
||||
baseURL,
|
||||
[folderKey stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
|
||||
mapped = [mapping registerURL: childURL withID: fid];
|
||||
if (!mapped)
|
||||
/* Enforce the creation if the backend does know the fid */
|
||||
[mapping updateURL: childURL withID: fid];
|
||||
|
||||
[mapping registerURL: childURL withID: fid];
|
||||
childFolder = [self lookupFolder: folderKey];
|
||||
if (childFolder)
|
||||
{
|
||||
@@ -449,7 +435,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
- (int) deleteFolder
|
||||
{
|
||||
// TODO: raise exception in case underlying delete fails?
|
||||
// [propsMessage delete];
|
||||
[dbFolder delete];
|
||||
|
||||
@@ -512,12 +497,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
else
|
||||
rc = MAPISTORE_ERR_DENIED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unregistering from indexing table as the backend says the
|
||||
object was not found */
|
||||
[mapping unregisterURLWithID: mid];
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -538,7 +517,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
context = [self context];
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
[[self userContext] activate];
|
||||
|
||||
if ([[context activeUser] isEqual: ownerUser]
|
||||
|| (!isAssociated && [self subscriberCanCreateMessages]))
|
||||
@@ -580,12 +558,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSUInteger count, max;
|
||||
id msgObject;
|
||||
SOGoUser *ownerUser;
|
||||
struct mapistore_connection_info *connInfo;
|
||||
struct mapistore_object_notification_parameters *notif_parameters;
|
||||
int rc;
|
||||
|
||||
/* flags that control the behaviour of the operation
|
||||
(MAPISTORE_SOFT_DELETE or MAPISTORE_PERMANENT_DELETE) */
|
||||
[self logWithFormat: @"-deleteMessageWithMID: mid: 0x%.16llx flags: %d", mid, flags];
|
||||
|
||||
|
||||
mapping = [self mapping];
|
||||
childURL = [mapping urlFromID: mid];
|
||||
if (childURL)
|
||||
@@ -618,9 +596,51 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
else
|
||||
{
|
||||
if (![message isNew])
|
||||
{
|
||||
/* folder notification */
|
||||
notif_parameters
|
||||
= talloc_zero(NULL,
|
||||
struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = [self objectId];
|
||||
notif_parameters->tag_count = 5;
|
||||
notif_parameters->tags = talloc_array (notif_parameters,
|
||||
enum MAPITAGS, 5);
|
||||
notif_parameters->tags[0] = PR_CONTENT_COUNT;
|
||||
notif_parameters->tags[1] = PR_DELETED_COUNT_TOTAL;
|
||||
notif_parameters->tags[2] = PR_MESSAGE_SIZE;
|
||||
notif_parameters->tags[3] = PR_NORMAL_MESSAGE_SIZE;
|
||||
notif_parameters->tags[4] = PR_DELETED_MSG_COUNT;
|
||||
notif_parameters->new_message_count = true;
|
||||
notif_parameters->message_count = [[self messageKeys]
|
||||
count] - 1;
|
||||
connInfo = [[self context] connectionInfo];
|
||||
mapistore_push_notification (connInfo->mstore_ctx,
|
||||
MAPISTORE_FOLDER,
|
||||
MAPISTORE_OBJECT_MODIFIED,
|
||||
notif_parameters);
|
||||
talloc_free(notif_parameters);
|
||||
|
||||
/* message notification */
|
||||
notif_parameters
|
||||
= talloc_zero(NULL,
|
||||
struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = mid;
|
||||
notif_parameters->folder_id = [self objectId];
|
||||
/* Exchange sends a fnevObjectCreated!! */
|
||||
mapistore_push_notification (connInfo->mstore_ctx,
|
||||
MAPISTORE_MESSAGE,
|
||||
MAPISTORE_OBJECT_CREATED,
|
||||
notif_parameters);
|
||||
talloc_free(notif_parameters);
|
||||
|
||||
/* table notification */
|
||||
for (count = 0; count < max; count++)
|
||||
[[activeTables objectAtIndex: count]
|
||||
notifyChangesForChild: message];
|
||||
}
|
||||
[self logWithFormat: @"successfully deleted object at URL: %@", childURL];
|
||||
/* Ensure we are respecting flags parameter */
|
||||
[mapping unregisterURLWithID: mid andFlags: flags];
|
||||
[mapping unregisterURLWithID: mid];
|
||||
[self cleanupCaches];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -642,7 +662,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMID: (uint64_t) targetMid
|
||||
andChangeKey: (struct Binary_r *) targetChangeKey
|
||||
andPredecessorChangeList: (struct Binary_r *) targetPredecessorChangeList
|
||||
wantCopy: (uint8_t) wantCopy
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -651,7 +670,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
//TALLOC_CTX *memCtx;
|
||||
struct SRow aRow;
|
||||
struct SPropValue property;
|
||||
uint8_t deleteFlags;
|
||||
|
||||
[self logWithFormat: @"-moveCopyMessageWithMID: 0x%.16llx .. withMID: 0x%.16llx .. wantCopy: %d", srcMid, targetMid, wantCopy];
|
||||
|
||||
@@ -670,24 +688,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
|
||||
|
||||
if (targetPredecessorChangeList)
|
||||
if (targetChangeKey)
|
||||
{
|
||||
property.ulPropTag = PidTagPredecessorChangeList;
|
||||
property.value.bin = *targetPredecessorChangeList;
|
||||
property.ulPropTag = PidTagChangeKey;
|
||||
property.value.bin = *targetChangeKey;
|
||||
aRow.cValues = 1;
|
||||
aRow.lpProps = &property;
|
||||
rc = [destMsg addPropertiesFromRow: &aRow];
|
||||
if (rc != MAPISTORE_SUCCESS)
|
||||
{
|
||||
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
|
||||
goto end;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
[destMsg save: memCtx];
|
||||
if (!wantCopy)
|
||||
/* We want to keep mid for restoring/shared data to work if mids are different. */
|
||||
deleteFlags = (srcMid == targetMid) ? MAPISTORE_PERMANENT_DELETE : MAPISTORE_SOFT_DELETE;
|
||||
rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: deleteFlags];
|
||||
rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0];
|
||||
|
||||
end:
|
||||
//talloc_free (memCtx);
|
||||
@@ -700,7 +713,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMIDs: (uint64_t *) targetMids
|
||||
andChangeKeys: (struct Binary_r **) targetChangeKeys
|
||||
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
|
||||
wantCopy: (uint8_t) wantCopy
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -710,7 +722,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSString *oldMessageURL;
|
||||
MAPIStoreMapping *mapping;
|
||||
SOGoUser *ownerUser;
|
||||
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
|
||||
struct Binary_r *targetChangeKey;
|
||||
//TALLOC_CTX *memCtx;
|
||||
|
||||
//memCtx = talloc_zero (NULL, TALLOC_CTX);
|
||||
@@ -731,21 +743,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
if (oldMessageURL)
|
||||
{
|
||||
[oldMessageURLs addObject: oldMessageURL];
|
||||
if (targetChangeKeys && targetPredecessorChangeList)
|
||||
{
|
||||
targetChangeKey = targetChangeKeys[count];
|
||||
targetPredecessorChangeList = targetPredecessorChangeLists[count];
|
||||
}
|
||||
if (targetChangeKeys)
|
||||
targetChangeKey = targetChangeKeys[count];
|
||||
else
|
||||
{
|
||||
targetChangeKey = NULL;
|
||||
targetPredecessorChangeList = NULL;
|
||||
}
|
||||
targetChangeKey = NULL;
|
||||
rc = [self _moveCopyMessageWithMID: srcMids[count]
|
||||
fromFolder: sourceFolder
|
||||
withMID: targetMids[count]
|
||||
andChangeKey: targetChangeKey
|
||||
andPredecessorChangeList: targetPredecessorChangeList
|
||||
wantCopy: wantCopy
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
@@ -756,6 +761,13 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
/* Notifications */
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
{
|
||||
[self postNotificationsForMoveCopyMessagesWithMIDs: srcMids
|
||||
andMessageURLs: oldMessageURLs
|
||||
andCount: midCount
|
||||
fromFolder: sourceFolder
|
||||
withMIDs: targetMids
|
||||
wantCopy: wantCopy];
|
||||
|
||||
// We cleanup cache of our source and destination folders
|
||||
[self cleanupCaches];
|
||||
[sourceFolder cleanupCaches];
|
||||
@@ -763,9 +775,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_DENIED;
|
||||
|
||||
|
||||
//talloc_free (memCtx);
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -827,7 +839,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [message url]];
|
||||
[self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE];
|
||||
[self deleteMessageWithMID: fmid andFlags: 0];
|
||||
[mapping registerURL: [targetMessage url]
|
||||
withID: fmid];
|
||||
}
|
||||
@@ -848,7 +860,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [message url]];
|
||||
[self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE];
|
||||
[self deleteMessageWithMID: fmid andFlags: 0];
|
||||
[mapping registerURL: [targetMessage url]
|
||||
withID: fmid];
|
||||
}
|
||||
@@ -875,23 +887,16 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [self url]];
|
||||
[mapping unregisterURLWithID: fmid];
|
||||
[self deleteFolder];
|
||||
|
||||
[mapping registerURL: [newFolder url]
|
||||
withID: fmid];
|
||||
}
|
||||
[targetFolder cleanupCaches];
|
||||
}
|
||||
[self cleanupCaches];
|
||||
|
||||
/* We perform the mapping operations at the
|
||||
end as objectId is required to be available
|
||||
until the caches are cleaned up */
|
||||
if (isMove && rc == MAPISTORE_SUCCESS)
|
||||
{
|
||||
fmid = [mapping idFromURL: [self url]];
|
||||
[mapping unregisterURLWithID: fmid];
|
||||
[mapping registerURL: [newFolder url]
|
||||
withID: fmid];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_DENIED;
|
||||
@@ -935,8 +940,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSString *baseURL, *URL, *key;
|
||||
NSArray *newIDs;
|
||||
uint64_t idNbr;
|
||||
bool softDeleted;
|
||||
|
||||
|
||||
baseURL = [self url];
|
||||
|
||||
mapping = [self mapping];
|
||||
@@ -946,8 +950,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
key = [keys objectAtIndex: count];
|
||||
URL = [NSString stringWithFormat: @"%@%@", baseURL, key];
|
||||
idNbr = [mapping idFromURL: URL isSoftDeleted: &softDeleted];
|
||||
if (idNbr == NSNotFound && !softDeleted)
|
||||
idNbr = [mapping idFromURL: URL];
|
||||
if (idNbr == NSNotFound)
|
||||
[missingURLs addObject: URL];
|
||||
}
|
||||
|
||||
@@ -957,6 +961,113 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
withIDs: newIDs];
|
||||
}
|
||||
|
||||
- (void) postNotificationsForMoveCopyMessagesWithMIDs: (uint64_t *) srcMids
|
||||
andMessageURLs: (NSArray *) oldMessageURLs
|
||||
andCount: (uint32_t) midCount
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMIDs: (uint64_t *) targetMids
|
||||
wantCopy: (uint8_t) wantCopy
|
||||
{
|
||||
NSArray *activeTables;
|
||||
NSUInteger count, tableCount, max;
|
||||
MAPIStoreMessage *message;
|
||||
NSString *messageURL;
|
||||
MAPIStoreMapping *mapping;
|
||||
struct mapistore_object_notification_parameters *notif_parameters;
|
||||
struct mapistore_connection_info *connInfo;
|
||||
|
||||
connInfo = [[self context] connectionInfo];
|
||||
|
||||
// For the "source folder, we ensure the table caches are loaded so
|
||||
// that old and new state can be compared
|
||||
activeTables = [sourceFolder activeMessageTables];
|
||||
max = [activeTables count];
|
||||
for (count = 0; count < max; count++)
|
||||
[[activeTables objectAtIndex: count] restrictedChildKeys];
|
||||
|
||||
if (!wantCopy)
|
||||
{
|
||||
// We notify the client. We start with the source folder.
|
||||
notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = [sourceFolder objectId];
|
||||
notif_parameters->tag_count = 5;
|
||||
notif_parameters->tags = talloc_array (notif_parameters, enum MAPITAGS, 5);
|
||||
notif_parameters->tags[0] = PR_CONTENT_COUNT;
|
||||
notif_parameters->tags[1] = PR_DELETED_COUNT_TOTAL;
|
||||
notif_parameters->tags[2] = PR_MESSAGE_SIZE;
|
||||
notif_parameters->tags[3] = PR_NORMAL_MESSAGE_SIZE;
|
||||
notif_parameters->tags[4] = PR_RECIPIENT_ON_NORMAL_MSG_COUNT;
|
||||
notif_parameters->new_message_count = true;
|
||||
notif_parameters->message_count = [[sourceFolder messageKeys] count] - midCount;
|
||||
mapistore_push_notification (connInfo->mstore_ctx,
|
||||
MAPISTORE_FOLDER,
|
||||
MAPISTORE_OBJECT_MODIFIED,
|
||||
notif_parameters);
|
||||
talloc_free(notif_parameters);
|
||||
}
|
||||
|
||||
// move/copy notification of the copied/moved message
|
||||
for (count = 0; count < midCount; count++)
|
||||
{
|
||||
notif_parameters = talloc_zero (NULL, struct mapistore_object_notification_parameters);
|
||||
notif_parameters->tag_count = 0;
|
||||
notif_parameters->new_message_count = true;
|
||||
notif_parameters->message_count = 0;
|
||||
notif_parameters->object_id = targetMids[count];
|
||||
notif_parameters->folder_id = [self objectId];
|
||||
notif_parameters->old_object_id = srcMids[count];
|
||||
notif_parameters->old_folder_id = [sourceFolder objectId];
|
||||
|
||||
mapistore_push_notification (connInfo->mstore_ctx,
|
||||
MAPISTORE_MESSAGE,
|
||||
(wantCopy ? MAPISTORE_OBJECT_COPIED : MAPISTORE_OBJECT_MOVED),
|
||||
notif_parameters);
|
||||
talloc_free (notif_parameters);
|
||||
|
||||
message = [sourceFolder lookupMessageByURL: [oldMessageURLs objectAtIndex: count]];
|
||||
// table notification
|
||||
for (tableCount = 0; tableCount < max; tableCount++)
|
||||
[[activeTables objectAtIndex: tableCount]
|
||||
notifyChangesForChild: message];
|
||||
}
|
||||
|
||||
// For the "destination folder, we ensure the table caches are loaded so
|
||||
// that old and new state can be compared
|
||||
activeTables = [self activeMessageTables];
|
||||
max = [activeTables count];
|
||||
for (count = 0; count < max; count++)
|
||||
[[activeTables objectAtIndex: count] restrictedChildKeys];
|
||||
|
||||
notif_parameters = talloc_zero(NULL, struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = [self objectId];
|
||||
notif_parameters->tag_count = 5;
|
||||
notif_parameters->tags = talloc_array (notif_parameters, enum MAPITAGS, 5);
|
||||
notif_parameters->tags[0] = PR_CONTENT_COUNT;
|
||||
notif_parameters->tags[1] = PR_DELETED_COUNT_TOTAL;
|
||||
notif_parameters->tags[2] = PR_MESSAGE_SIZE;
|
||||
notif_parameters->tags[3] = PR_NORMAL_MESSAGE_SIZE;
|
||||
notif_parameters->tags[4] = PR_RECIPIENT_ON_NORMAL_MSG_COUNT;
|
||||
notif_parameters->new_message_count = true;
|
||||
notif_parameters->message_count = [[self messageKeys] count] + midCount;
|
||||
connInfo = [[self context] connectionInfo];
|
||||
mapistore_push_notification (connInfo->mstore_ctx,
|
||||
MAPISTORE_FOLDER,
|
||||
MAPISTORE_OBJECT_MODIFIED,
|
||||
notif_parameters);
|
||||
talloc_free(notif_parameters);
|
||||
|
||||
// table notification
|
||||
mapping = [self mapping];
|
||||
for (count = 0; count < midCount; count++)
|
||||
{
|
||||
messageURL = [mapping urlFromID: targetMids[count]];
|
||||
message = [self lookupMessageByURL: messageURL];
|
||||
for (tableCount = 0; tableCount < max; tableCount++)
|
||||
[[activeTables objectAtIndex: tableCount]
|
||||
notifyChangesForChild: message];
|
||||
}
|
||||
}
|
||||
|
||||
- (int) getDeletedFMIDs: (struct UI8Array_r **) fmidsPtr
|
||||
andCN: (uint64_t *) cnPtr
|
||||
fromChangeNumber: (uint64_t) changeNum
|
||||
@@ -971,7 +1082,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
MAPIStoreMapping *mapping;
|
||||
struct UI8Array_r *fmids;
|
||||
uint64_t fmid;
|
||||
bool softDeleted;
|
||||
|
||||
keys = [self getDeletedKeysFromChangeNumber: changeNum andCN: &cnNbr
|
||||
inTableType: tableType];
|
||||
@@ -998,10 +1108,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
url = [NSString stringWithFormat: format,
|
||||
baseURL, [keys objectAtIndex: count]];
|
||||
fmid = [mapping idFromURL: url isSoftDeleted: &softDeleted];
|
||||
fmid = [mapping idFromURL: url];
|
||||
if (fmid != NSNotFound) /* if no fmid is returned, then the object
|
||||
"never existed" in the OpenChange
|
||||
databases. Soft-deleted messages are returned back */
|
||||
databases */
|
||||
{
|
||||
fmids->lpui8[fmids->cValues] = fmid;
|
||||
fmids->cValues++;
|
||||
@@ -1065,10 +1175,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
static enum MAPITAGS bannedProps[] = { PR_MID, PR_FID, PR_PARENT_FID,
|
||||
PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY,
|
||||
PR_CHANGE_KEY, PidTagChangeNumber, 0x00000000 };
|
||||
PR_CHANGE_KEY, 0x00000000 };
|
||||
enum MAPITAGS *currentProp;
|
||||
NSMutableDictionary *propsCopy;
|
||||
uint64_t cn;
|
||||
|
||||
/* TODO: this should no longer be required once mapistore v2 API is in
|
||||
place, when we can then do this from -dealloc below */
|
||||
@@ -1086,12 +1195,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
|
||||
[properties addEntriesFromDictionary: propsCopy];
|
||||
|
||||
/* Update change number after setting the properties */
|
||||
cn = [[self context] getNewChangeNumber];
|
||||
[properties setObject: [NSNumber numberWithUnsignedLongLong: cn]
|
||||
forKey: MAPIPropertyKey (PidTagChangeNumber)];
|
||||
|
||||
[dbFolder save];
|
||||
}
|
||||
|
||||
@@ -1117,7 +1220,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
- (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
[[self userContext] activate];
|
||||
return [dbFolder childKeysOfType: MAPIFAICacheObject
|
||||
includeDeleted: NO
|
||||
matchingQualifier: qualifier
|
||||
@@ -1226,7 +1328,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
/*
|
||||
Possible values are:
|
||||
|
||||
|
||||
0x00000001 Modify
|
||||
0x00000002 Read
|
||||
0x00000004 Delete
|
||||
@@ -1257,7 +1359,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
access |= 0x10;
|
||||
if (userIsOwner)
|
||||
access |= 0x20;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, access);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -1286,7 +1388,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
rights |= RightsCreateSubfolders;
|
||||
if (userIsOwner)
|
||||
rights |= RightsFolderOwner | RightsFolderContact;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, rights);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -1322,7 +1424,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = MAPIBoolValue (memCtx, [self supportsSubFolders] && [[self folderKeys] count] > 0);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1330,7 +1432,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = MAPILongValue (memCtx, [[self folderKeys] count]);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1432,7 +1534,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
[dbObject setIsNew: YES];
|
||||
newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject
|
||||
inContainer: self];
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@@ -1441,8 +1543,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
MAPIStoreMessage *newMessage;
|
||||
WOContext *woContext;
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
if (isAssociated)
|
||||
newMessage = [self _createAssociatedMessage];
|
||||
else
|
||||
@@ -1607,12 +1707,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
aclFolder = [self aclFolder];
|
||||
|
||||
users = [[aclFolder aclUsers] copy];
|
||||
users = [aclFolder aclUsers];
|
||||
max = [users count];
|
||||
for (count = 0; count < max; count++)
|
||||
[aclFolder removeUserFromAcls: [users objectAtIndex: count]];
|
||||
|
||||
[users release];
|
||||
}
|
||||
|
||||
- (int) modifyPermissions: (struct PermissionData *) permissions
|
||||
@@ -1642,7 +1740,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
permissionUser = nil;
|
||||
permissionRoles = nil;
|
||||
|
||||
|
||||
if (currentPermission->PermissionDataFlags == ROW_ADD)
|
||||
isAdd = YES;
|
||||
else if (currentPermission->PermissionDataFlags == ROW_MODIFY)
|
||||
@@ -1760,17 +1858,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
inFolderURL: [self url]];
|
||||
}
|
||||
|
||||
- (MAPIStoreFolder *) rootContainer
|
||||
{
|
||||
/* Return the oldest ancestor, which does not have
|
||||
container. If there is not container, it returns itself.
|
||||
*/
|
||||
if (container)
|
||||
return [container rootContainer];
|
||||
else
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDate *) creationTime
|
||||
{
|
||||
return [dbFolder creationDate];
|
||||
@@ -1793,7 +1880,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *firstContext = NULL, *context;
|
||||
@@ -79,12 +79,10 @@
|
||||
if (moduleName)
|
||||
{
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
|
||||
baseUrl = [NSString stringWithFormat: @"sogo://%@@%@/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
moduleName];
|
||||
userName, moduleName];
|
||||
|
||||
subfolders = [parentFolder subFolders];
|
||||
max = [subfolders count];
|
||||
@@ -121,6 +119,7 @@
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: NULL];
|
||||
[MAPIApp setUserContext: userContext];
|
||||
moduleName = [self MAPIModuleName];
|
||||
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
|
||||
nameInContainer = nil;
|
||||
@@ -128,11 +127,10 @@
|
||||
if (![parentFolder newFolderWithName: folderName
|
||||
nameInContainer: &nameInContainer])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
moduleName, nameInContainer];
|
||||
userName, moduleName, nameInContainer];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
return mapistoreURI;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
/* synchronisation */
|
||||
- (BOOL) synchroniseCache;
|
||||
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
|
||||
withChangeKey: (NSData *) oldChangeKey
|
||||
andPredecessorChangeList: (NSData *) pcl;
|
||||
withChangeKey: (NSData *) newChangeKey;
|
||||
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
|
||||
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
|
||||
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
|
||||
|
||||
@@ -75,7 +75,6 @@ static Class NSNumberK;
|
||||
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
|
||||
inContainer: dbFolder]);
|
||||
[versionsMessage setObjectType: MAPIInternalCacheObject];
|
||||
[versionsMessage reloadIfNeeded];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
@@ -262,6 +261,7 @@ static Class NSNumberK;
|
||||
*/
|
||||
- (void) _setChangeKey: (NSData *) changeKey
|
||||
forMessageEntry: (NSMutableDictionary *) messageEntry
|
||||
inChangeListOnly: (BOOL) inChangeListOnly
|
||||
{
|
||||
struct XID *xid;
|
||||
NSString *guid;
|
||||
@@ -270,15 +270,19 @@ static Class NSNumberK;
|
||||
NSMutableDictionary *changeList;
|
||||
|
||||
xid = [changeKey asXIDInMemCtx: NULL];
|
||||
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
|
||||
guid = [NSString stringWithGUID: &xid->GUID];
|
||||
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
|
||||
talloc_free (xid);
|
||||
|
||||
/* 1. set change key association */
|
||||
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
|
||||
globCnt, @"LocalId",
|
||||
nil];
|
||||
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
|
||||
if (!inChangeListOnly)
|
||||
{
|
||||
/* 1. set change key association */
|
||||
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
guid, @"GUID",
|
||||
globCnt, @"LocalId",
|
||||
nil];
|
||||
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
|
||||
}
|
||||
|
||||
/* 2. append/update predecessor change list */
|
||||
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
|
||||
@@ -292,77 +296,6 @@ static Class NSNumberK;
|
||||
[changeList setObject: globCnt forKey: guid];
|
||||
}
|
||||
|
||||
- (void) _updatePredecessorChangeList: (NSData *) predecessorChangeList
|
||||
forMessageEntry: (NSMutableDictionary *) messageEntry
|
||||
withOldChangeKey: (NSData *) oldChangeKey
|
||||
{
|
||||
NSData *globCnt, *oldGlobCnt;
|
||||
NSDictionary *changeKeyDict;
|
||||
NSString *guid;
|
||||
NSMutableDictionary *changeList;
|
||||
struct SizedXid *sizedXIDList;
|
||||
struct XID xid, *givenChangeKey;
|
||||
TALLOC_CTX *localMemCtx;
|
||||
uint32_t i, length;
|
||||
|
||||
localMemCtx = talloc_new (NULL);
|
||||
if (!localMemCtx)
|
||||
{
|
||||
[self errorWithFormat: @"No more memory"];
|
||||
return;
|
||||
}
|
||||
|
||||
if (predecessorChangeList)
|
||||
{
|
||||
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: localMemCtx with: &length];
|
||||
|
||||
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
|
||||
if (!changeList)
|
||||
{
|
||||
changeList = [NSMutableDictionary new];
|
||||
[messageEntry setObject: changeList
|
||||
forKey: @"PredecessorChangeList"];
|
||||
[changeList release];
|
||||
}
|
||||
|
||||
if (sizedXIDList) {
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
xid = sizedXIDList[i].XID;
|
||||
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
|
||||
oldGlobCnt = [changeList objectForKey: guid];
|
||||
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
|
||||
[changeList setObject: globCnt forKey: guid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldChangeKey)
|
||||
{
|
||||
givenChangeKey = [oldChangeKey asXIDInMemCtx: localMemCtx];
|
||||
if (givenChangeKey) {
|
||||
guid = [NSString stringWithGUID: &givenChangeKey->NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: givenChangeKey->LocalId.data length: givenChangeKey->LocalId.length];
|
||||
|
||||
changeKeyDict = [messageEntry objectForKey: @"ChangeKey"];
|
||||
if (!changeKeyDict ||
|
||||
([guid isEqualToString: [changeKeyDict objectForKey: @"GUID"]]
|
||||
&& ([globCnt compare: [changeKeyDict objectForKey: @"LocalId"]] == NSOrderedDescending)))
|
||||
{
|
||||
/* The given change key is greater than current one stored in
|
||||
metadata or it does not exist */
|
||||
[messageEntry setObject: [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
|
||||
globCnt, @"LocalId",
|
||||
nil]
|
||||
forKey: @"ChangeKey"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free (localMemCtx);
|
||||
}
|
||||
|
||||
- (EOQualifier *) componentQualifier
|
||||
{
|
||||
if (!componentQualifier)
|
||||
@@ -532,7 +465,8 @@ static Class NSNumberK;
|
||||
// A GLOBCNT structure is a 6-byte global namespace counter,
|
||||
// we strip the first 2 bytes. The first two bytes is the ReplicaId
|
||||
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry
|
||||
inChangeListOnly: NO];
|
||||
}
|
||||
|
||||
now = [NSCalendarDate date];
|
||||
@@ -549,13 +483,12 @@ static Class NSNumberK;
|
||||
}
|
||||
|
||||
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
|
||||
withChangeKey: (NSData *) oldChangeKey
|
||||
andPredecessorChangeList: (NSData *) pcl
|
||||
withChangeKey: (NSData *) newChangeKey
|
||||
{
|
||||
NSMutableDictionary *messages, *messageEntry;
|
||||
|
||||
[self synchroniseCache];
|
||||
if (oldChangeKey || pcl)
|
||||
if (newChangeKey)
|
||||
{
|
||||
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
||||
messageEntry = [messages objectForKey: messageKey];
|
||||
@@ -563,8 +496,8 @@ static Class NSNumberK;
|
||||
[NSException raise: @"MAPIStoreIOException"
|
||||
format: @"no version record found for message '%@'",
|
||||
messageKey];
|
||||
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
|
||||
withOldChangeKey: oldChangeKey];
|
||||
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
|
||||
inChangeListOnly: YES];
|
||||
[versionsMessage save];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,16 +209,13 @@
|
||||
|
||||
- (void) updateVersions
|
||||
{
|
||||
/* Update ChangeKey and PredecessorChangeList on message's save */
|
||||
NSData *newChangeKey, *predecessorChangeList;
|
||||
NSData *newChangeKey;
|
||||
|
||||
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
|
||||
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
|
||||
|
||||
[(MAPIStoreGCSFolder *) container
|
||||
updateVersionsForMessageWithKey: [self nameInContainer]
|
||||
withChangeKey: newChangeKey
|
||||
andPredecessorChangeList: predecessorChangeList];
|
||||
updateVersionsForMessageWithKey: [self nameInContainer]
|
||||
withChangeKey: newChangeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -30,7 +30,6 @@
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <Mailer/SOGoMailBodyPart.h>
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <Mailer/NSDictionary+Mail.h>
|
||||
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "MAPIStoreMailMessage.h"
|
||||
@@ -109,7 +108,7 @@
|
||||
static char recordBytes[] = {0xd9, 0xd8, 0x11, 0xa3, 0xe2, 0x90, 0x18, 0x41,
|
||||
0x9e, 0x04, 0x58, 0x46, 0x9d, 0x6d, 0x1b,
|
||||
0x68};
|
||||
|
||||
|
||||
*data = [[NSData dataWithBytes: recordBytes length: 16]
|
||||
asBinaryInMemCtx: memCtx];
|
||||
|
||||
@@ -118,7 +117,19 @@
|
||||
|
||||
- (NSString *) _fileName
|
||||
{
|
||||
return [bodyInfo filename];
|
||||
NSString *fileName;
|
||||
NSDictionary *parameters;
|
||||
|
||||
fileName = [[bodyInfo objectForKey: @"parameterList"]
|
||||
objectForKey: @"name"];
|
||||
if (!fileName)
|
||||
{
|
||||
parameters = [[bodyInfo objectForKey: @"disposition"]
|
||||
objectForKey: @"parameterList"];
|
||||
fileName = [parameters objectForKey: @"filename"];
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
- (int) getPidTagAttachLongFilename: (void **) data
|
||||
@@ -152,22 +163,15 @@
|
||||
- (int) getPidTagDisplayName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *fileName;
|
||||
|
||||
fileName = [self _fileName];
|
||||
if ([fileName isEqualToString: @"sharing_metadata.xml"])
|
||||
/* Required to disallow user from seeing the attachment by default */
|
||||
*data = [@"sharing_metadata.xml" asUnicodeInMemCtx: memCtx];
|
||||
else
|
||||
*data = [[bodyInfo objectForKey: @"description"]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
*data = [[bodyInfo objectForKey: @"description"]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidTagAttachContentId: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
{
|
||||
*data = [[bodyInfo objectForKey: @"bodyId"]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
|
||||
@@ -177,17 +181,11 @@
|
||||
- (int) getPidTagAttachMimeTag: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *mimeTag, *fileName;
|
||||
|
||||
fileName = [self _fileName];
|
||||
if ([fileName isEqualToString: @"sharing_metadata.xml"])
|
||||
/* Required by [MS-OXWSMSHR] Section 3.1.1 */
|
||||
mimeTag = [NSString stringWithFormat: @"application/x-sharing-metadata-xml"];
|
||||
else
|
||||
mimeTag = [NSString stringWithFormat: @"%@/%@",
|
||||
[bodyInfo objectForKey: @"type"],
|
||||
[bodyInfo objectForKey: @"subtype"]];
|
||||
NSString *mimeTag;
|
||||
|
||||
mimeTag = [NSString stringWithFormat: @"%@/%@",
|
||||
[bodyInfo objectForKey: @"type"],
|
||||
[bodyInfo objectForKey: @"subtype"]];
|
||||
*data = [[mimeTag lowercaseString] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
|
||||
@@ -67,7 +67,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
NSArray *parts;
|
||||
NSString *lastFolder;
|
||||
NSUInteger max;
|
||||
|
||||
|
||||
parts = [folderName componentsSeparatedByString: @"/"];
|
||||
max = [parts count];
|
||||
if (max > 1)
|
||||
@@ -83,11 +83,11 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *firstContext = NULL, *context;
|
||||
NSString *urlBase, *stringData, *currentName, *realName, *inboxName, *draftsName, *sentName, *trashName;
|
||||
NSString *urlBase, *stringData, *currentName, *inboxName, *draftsName, *sentName, *trashName;
|
||||
NSArray *unprefixedFolders;
|
||||
NSMutableArray *secondaryFolders;
|
||||
enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE,
|
||||
@@ -100,7 +100,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
WOContext *woContext;
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
||||
woContext = [userContext woContext];
|
||||
|
||||
@@ -126,11 +126,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
trashName = [NSString stringWithFormat: @"folder%@",
|
||||
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
||||
|
||||
urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"]];
|
||||
urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", userName, userName];
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
@@ -146,9 +142,6 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
DLIST_ADD_END (firstContext, context, void);
|
||||
}
|
||||
|
||||
/* FIXME: Flush any cache before retrieving the hierarchy */
|
||||
[accountFolder flushMailCaches];
|
||||
|
||||
secondaryFolders = [[accountFolder toManyRelationshipKeysWithNamespaces: NO]
|
||||
mutableCopy];
|
||||
[secondaryFolders autorelease];
|
||||
@@ -160,23 +153,12 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
// secondaryFolders has the names (1) Imap4Encoded and (2) asCSSIdentifier
|
||||
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
|
||||
currentName = [secondaryFolders objectAtIndex: count];
|
||||
// To get the real name we have to revert that (applying the decode functions)
|
||||
// in reverse order
|
||||
// e.g.: Problèmes de synchronisation
|
||||
realName = [[currentName fromCSSIdentifier]
|
||||
stringByDecodingImap4FolderName];
|
||||
// And finally to represent that as URI we have to (1) asCSSIdentifier,
|
||||
// (2) Imap4Encode and (3) AddPercentEscapes
|
||||
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
|
||||
// In the example there are no percent escapes added because is already ok
|
||||
stringData = [[[realName asCSSIdentifier]
|
||||
stringByEncodingImap4FolderName]
|
||||
stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
context->url = [[NSString stringWithFormat: @"%@%@", urlBase, stringData] asUnicodeInMemCtx: context];
|
||||
context->name = [[realName substringFromIndex: 6] asUnicodeInMemCtx: context];
|
||||
stringData = [NSString stringWithFormat: @"%@%@",
|
||||
urlBase, [currentName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
context->url = [stringData asUnicodeInMemCtx: context];
|
||||
stringData = [[[currentName substringFromIndex: 6] fromCSSIdentifier] stringByDecodingImap4FolderName];
|
||||
context->name = [stringData asUnicodeInMemCtx: context];
|
||||
context->main_folder = false;
|
||||
context->role = MAPISTORE_MAIL_ROLE;
|
||||
context->tag = "tag";
|
||||
@@ -198,6 +180,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: NULL];
|
||||
[MAPIApp setUserContext: userContext];
|
||||
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
||||
folderName = [NSString stringWithFormat: @"folder%@",
|
||||
[newFolderName asCSSIdentifier]];
|
||||
@@ -205,13 +188,11 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
inContainer: accountFolder];
|
||||
if ([newFolder create])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
userName, userName,
|
||||
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
return mapistoreURI;
|
||||
}
|
||||
@@ -264,7 +245,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *context;
|
||||
@@ -275,7 +256,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
WOContext *woContext;
|
||||
|
||||
userContext = [MAPIStoreUserContext userContextWithUsername: userName
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
|
||||
woContext = [userContext woContext];
|
||||
|
||||
@@ -283,12 +264,8 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
componentsSeparatedByString: @"/"];
|
||||
folderName = [NSString stringWithFormat: @"folder%@",
|
||||
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
||||
url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
folderName];
|
||||
url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@", userName,
|
||||
userName, folderName];
|
||||
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
context->url = [url asUnicodeInMemCtx: context];
|
||||
|
||||
@@ -44,24 +44,14 @@
|
||||
|
||||
/* synchronisation & versioning */
|
||||
- (BOOL) synchroniseCache;
|
||||
- (void) synchronizeUpdatedFolder: (NSNumber *) lastModseq
|
||||
withMapping: (NSMutableDictionary *) mapping;
|
||||
- (BOOL) synchroniseCacheForUID: (NSString *) messageUID;
|
||||
- (NSNumber *) modseqFromMessageChangeNumber: (NSString *) changeNum;
|
||||
- (NSString *) messageUIDFromMessageKey: (NSString *) messageKey;
|
||||
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid;
|
||||
- (void) setChangeKey: (NSData *) changeKey
|
||||
forMessageWithKey: (NSString *) messageKey;
|
||||
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
|
||||
forMessageWithKey: (NSString *) messageKey;
|
||||
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
|
||||
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey;
|
||||
|
||||
/* Extra properties from mail messages that already hit the server */
|
||||
- (void) setExtraProperties: (NSDictionary *) props
|
||||
forMessage: (NSString *) messageKey;
|
||||
- (NSDictionary *) extraPropertiesForMessage: (NSString *) messageKey;
|
||||
|
||||
@end
|
||||
|
||||
/* MAPIStoreOutboxFolder is a special subclass of MAPIStoreMailFolder where
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#import "MAPIStoreFAIMessage.h"
|
||||
#import "MAPIStoreMailContext.h"
|
||||
#import "MAPIStoreMailMessage.h"
|
||||
#import "MAPIStoreMailFolderTable.h"
|
||||
#import "MAPIStoreMailMessageTable.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
@@ -68,12 +67,9 @@
|
||||
|
||||
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
|
||||
#include <gen_ndr/exchange.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <util/attr.h>
|
||||
#include <libmapi/libmapi.h>
|
||||
#include <libmapiproxy.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
@@ -111,7 +107,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
|
||||
inContainer: dbFolder]);
|
||||
[versionsMessage setObjectType: MAPIInternalCacheObject];
|
||||
[versionsMessage reloadIfNeeded];
|
||||
}
|
||||
|
||||
- (BOOL) ensureFolderExists
|
||||
@@ -142,7 +137,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
[[self mapping] updateID: fid withURL: [self url]];
|
||||
[dbFolder setNameInContainer: newNameInContainer];
|
||||
[self cleanupCaches];
|
||||
|
||||
|
||||
propsCopy = [newProperties mutableCopy];
|
||||
[propsCopy removeObjectForKey: key];
|
||||
[propsCopy autorelease];
|
||||
@@ -158,11 +153,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
return [MAPIStoreMailMessageTable tableForContainer: self];
|
||||
}
|
||||
|
||||
- (MAPIStoreFolderTable *) folderTable
|
||||
{
|
||||
return [MAPIStoreMailFolderTable tableForContainer: self];
|
||||
}
|
||||
|
||||
- (enum mapistore_error) createFolder: (struct SRow *) aRow
|
||||
withFID: (uint64_t) newFID
|
||||
andKey: (NSString **) newKeyP
|
||||
@@ -173,7 +163,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
int i;
|
||||
|
||||
nameInContainer = nil;
|
||||
rc = MAPISTORE_ERROR;
|
||||
|
||||
folderName = nil;
|
||||
for (i = 0; !folderName && i < aRow->cValues; i++)
|
||||
@@ -188,9 +177,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
{
|
||||
nameInContainer = [NSString stringWithFormat: @"folder%@",
|
||||
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
||||
|
||||
[[self userContext] activate];
|
||||
|
||||
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
|
||||
inContainer: sogoObject];
|
||||
if ([newFolder create])
|
||||
@@ -245,7 +231,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
sortOrdering: nil]
|
||||
count];
|
||||
*data = MAPILongValue (memCtx, longValue);
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -253,7 +239,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"IPF.Note" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -265,7 +251,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
if (!nonDeletedQualifier)
|
||||
{
|
||||
deletedQualifier
|
||||
= [[EOKeyValueQualifier alloc]
|
||||
= [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"FLAGS"
|
||||
operatorSelector: EOQualifierOperatorContains
|
||||
value: [NSArray arrayWithObject: @"Deleted"]];
|
||||
@@ -365,54 +351,19 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
|
||||
andSortOrderings: (NSArray *) sortOrderings
|
||||
{
|
||||
NSArray *filteredSubfolderKeys;
|
||||
NSMutableArray *subfolderKeys;
|
||||
NSMutableArray *subfolderKeysQualifying;
|
||||
NSString *subfolderKey;
|
||||
NSUInteger count, max;
|
||||
|
||||
if ([self ensureFolderExists])
|
||||
{
|
||||
/* Only folder name can be used as qualifier key */
|
||||
if (qualifier)
|
||||
[self warnWithFormat: @"qualifier is only used for folders with name"];
|
||||
[self errorWithFormat: @"qualifier is not used for folders"];
|
||||
if (sortOrderings)
|
||||
[self errorWithFormat: @"sort orderings are not used for folders"];
|
||||
|
||||
/* FIXME: Flush any cache before retrieving the hierarchy, this
|
||||
slows things down but it is safer */
|
||||
if (!qualifier)
|
||||
[sogoObject flushMailCaches];
|
||||
|
||||
|
||||
subfolderKeys = [[sogoObject toManyRelationshipKeys] mutableCopy];
|
||||
[subfolderKeys autorelease];
|
||||
|
||||
[self _cleanupSubfolderKeys: subfolderKeys];
|
||||
|
||||
if (qualifier)
|
||||
{
|
||||
subfolderKeysQualifying = [NSMutableArray array];
|
||||
max = [subfolderKeys count];
|
||||
for (count = 0; count < max; count++) {
|
||||
subfolderKey = [subfolderKeys objectAtIndex: count];
|
||||
/* Remove "folder" prefix */
|
||||
subfolderKey = [subfolderKey substringFromIndex: 6];
|
||||
subfolderKey = [[subfolderKey fromCSSIdentifier] stringByDecodingImap4FolderName];
|
||||
[subfolderKeysQualifying addObject: [NSDictionary dictionaryWithObject: subfolderKey
|
||||
forKey: @"name"]];
|
||||
}
|
||||
filteredSubfolderKeys = [subfolderKeysQualifying filteredArrayUsingQualifier: qualifier];
|
||||
|
||||
max = [filteredSubfolderKeys count];
|
||||
subfolderKeys = [NSMutableArray arrayWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
subfolderKey = [[filteredSubfolderKeys objectAtIndex: count] valueForKey: @"name"];
|
||||
subfolderKey = [NSString stringWithFormat: @"folder%@", [[subfolderKey stringByEncodingImap4FolderName] asCSSIdentifier]];
|
||||
[subfolderKeys addObject: subfolderKey];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
subfolderKeys = nil;
|
||||
@@ -472,7 +423,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
}
|
||||
else
|
||||
supportsSubFolders = YES;
|
||||
|
||||
|
||||
return supportsSubFolders;
|
||||
}
|
||||
|
||||
@@ -519,44 +470,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
return [modseq1 compare: modseq2];
|
||||
}
|
||||
|
||||
- (void) _updatePredecessorChangeListWith: (NSData *) predecessorChangeList
|
||||
forMessageEntry: (NSMutableDictionary *) messageEntry
|
||||
{
|
||||
NSData *globCnt, *oldGlobCnt;
|
||||
NSMutableDictionary *changeList;
|
||||
NSString *guid;
|
||||
struct SizedXid *sizedXIDList;
|
||||
struct XID xid;
|
||||
uint32_t i, length;
|
||||
|
||||
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: NULL with: &length];
|
||||
|
||||
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
|
||||
if (!changeList)
|
||||
{
|
||||
changeList = [NSMutableDictionary new];
|
||||
[messageEntry setObject: changeList
|
||||
forKey: @"PredecessorChangeList"];
|
||||
[changeList release];
|
||||
}
|
||||
|
||||
if (sizedXIDList) {
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
xid = sizedXIDList[i].XID;
|
||||
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
|
||||
oldGlobCnt = [changeList objectForKey: guid];
|
||||
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
|
||||
[changeList setObject: globCnt forKey: guid];
|
||||
}
|
||||
|
||||
talloc_free (sizedXIDList);
|
||||
}
|
||||
|
||||
[versionsMessage save];
|
||||
}
|
||||
|
||||
- (void) _setChangeKey: (NSData *) changeKey
|
||||
forMessageEntry: (NSMutableDictionary *) messageEntry
|
||||
{
|
||||
@@ -567,8 +480,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
NSMutableDictionary *changeList;
|
||||
|
||||
xid = [changeKey asXIDInMemCtx: NULL];
|
||||
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
|
||||
guid = [NSString stringWithGUID: &xid->GUID];
|
||||
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
|
||||
talloc_free (xid);
|
||||
|
||||
/* 1. set change key association */
|
||||
@@ -598,7 +511,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
*nextModseq;
|
||||
NSString *changeNumber, *uid, *messageKey;
|
||||
uint64_t lastModseqNbr;
|
||||
EOQualifier *searchQualifier;
|
||||
EOQualifier *kvQualifier, *searchQualifier;
|
||||
NSArray *uids, *changeNumbers;
|
||||
NSUInteger count, max;
|
||||
NSArray *fetchResults;
|
||||
@@ -639,11 +552,14 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
{
|
||||
lastModseqNbr = [lastModseq unsignedLongLongValue];
|
||||
nextModseq = [NSNumber numberWithUnsignedLongLong: lastModseqNbr + 1];
|
||||
searchQualifier = [[EOKeyValueQualifier alloc]
|
||||
kvQualifier = [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"modseq"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
value: nextModseq];
|
||||
|
||||
searchQualifier = [[EOAndQualifier alloc]
|
||||
initWithQualifiers:
|
||||
kvQualifier, [self nonDeletedQualifier], nil];
|
||||
[kvQualifier release];
|
||||
[searchQualifier autorelease];
|
||||
}
|
||||
else
|
||||
@@ -671,7 +587,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
|
||||
fetchResults
|
||||
= [(NSDictionary *) [sogoObject fetchUIDs: uids
|
||||
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
||||
parts: [NSArray arrayWithObject: @"modseq"]]
|
||||
objectForKey: @"fetch"];
|
||||
|
||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
||||
@@ -679,7 +595,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
fetchResults
|
||||
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
|
||||
context: NULL];
|
||||
|
||||
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
result = [fetchResults objectAtIndex: count];
|
||||
@@ -707,230 +623,58 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
if (!lastModseq
|
||||
|| ([lastModseq compare: modseq] == NSOrderedAscending))
|
||||
lastModseq = modseq;
|
||||
|
||||
if ([[result objectForKey: @"flags"] containsObject: @"deleted"])
|
||||
[currentProperties setObject: changeNumber
|
||||
forKey: @"SyncLastDeleteChangeNumber"];
|
||||
}
|
||||
|
||||
[currentProperties setObject: lastModseq forKey: @"SyncLastModseq"];
|
||||
foundChange = YES;
|
||||
}
|
||||
|
||||
/* 2. we synchronise expunged UIDs */
|
||||
/* 2. we synchronise deleted UIDs */
|
||||
if (initialLastModseq)
|
||||
{
|
||||
fetchResults = [(SOGoMailFolder *) sogoObject
|
||||
fetchUIDsOfVanishedItems: lastModseqNbr];
|
||||
|
||||
max = [fetchResults count];
|
||||
|
||||
changeNumbers = [[self context] getNewChangeNumbers: max];
|
||||
changeNumber = nil;
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
uid = [[fetchResults objectAtIndex: count] stringValue];
|
||||
uid = [fetchResults objectAtIndex: count];
|
||||
if ([messages objectForKey: uid])
|
||||
{
|
||||
if (!changeNumber)
|
||||
{
|
||||
newChangeNum = [[self context] getNewChangeNumber];
|
||||
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
||||
}
|
||||
newChangeNum = [[changeNumbers objectAtIndex: count]
|
||||
unsignedLongLongValue];
|
||||
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
||||
[messages removeObjectForKey: uid];
|
||||
[self logWithFormat: @"Removed message entry for UID %@", uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat:@"Message entry not found for UID %@", uid];
|
||||
[self logWithFormat: @"removed message entry for uid %@", uid];
|
||||
}
|
||||
}
|
||||
if (changeNumber)
|
||||
{
|
||||
[currentProperties setObject: changeNumber
|
||||
forKey: @"SyncLastDeleteChangeNumber"];
|
||||
[mapping setObject: lastModseq forKey: changeNumber];
|
||||
foundChange = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundChange)
|
||||
{
|
||||
[self synchronizeUpdatedFolder: lastModseq
|
||||
withMapping: mapping];
|
||||
|
||||
ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]];
|
||||
[currentProperties setObject: ti
|
||||
forKey: @"SyncLastSynchronisationDate"];
|
||||
[versionsMessage save];
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (void) synchronizeUpdatedFolder: (NSNumber *) lastModseq
|
||||
withMapping: (NSMutableDictionary *) mapping
|
||||
{
|
||||
/* This method should be called whenever something has changed on the folder.
|
||||
Then we will perform two actions:
|
||||
1 - Update the PidTagChangeNumber property of the root container.
|
||||
2 - Store relationship PidTagChangenumber with lastModseq value on the
|
||||
mapping given as parameter for this folder */
|
||||
uint64_t *current_cn;
|
||||
struct SRow row;
|
||||
struct SPropValue prop;
|
||||
uint64_t fid;
|
||||
const char *username;
|
||||
struct openchangedb_context *oc_ctx;
|
||||
enum MAPISTATUS retval;
|
||||
TALLOC_CTX *local_mem_ctx = NULL;
|
||||
|
||||
row.cValues = 1;
|
||||
prop.ulPropTag = PidTagChangeNumber;
|
||||
prop.value.d = 0; // It doesn't matter, it will be autogenerated
|
||||
row.lpProps = ∝
|
||||
|
||||
/* We are doing a "touch" operation to update change number of the root container.
|
||||
We get the root container as it has the properties in the OpenChange DB */
|
||||
username = [[self context] connectionInfo]->username;
|
||||
oc_ctx = [[self context] connectionInfo]->oc_ctx;
|
||||
fid = [[self rootContainer] objectId];
|
||||
retval = openchangedb_set_folder_properties(oc_ctx, username, fid, &row);
|
||||
if (retval != MAPI_E_SUCCESS)
|
||||
{
|
||||
[self errorWithFormat:@"%s: Error setting change number on %"PRIu64,
|
||||
__PRETTY_FUNCTION__, fid];
|
||||
return;
|
||||
}
|
||||
|
||||
local_mem_ctx = talloc_named(NULL, 0, __PRETTY_FUNCTION__);
|
||||
if (local_mem_ctx == NULL)
|
||||
{
|
||||
[self errorWithFormat:@"%s: Error with talloc_named, out of memory?",
|
||||
__PRETTY_FUNCTION__];
|
||||
return;
|
||||
}
|
||||
retval = openchangedb_get_folder_property(local_mem_ctx, oc_ctx, username,
|
||||
PidTagChangeNumber, fid,
|
||||
(void **) ¤t_cn);
|
||||
if (retval != MAPI_E_SUCCESS)
|
||||
{
|
||||
[self errorWithFormat:@"%s: Error getting change number on %"PRIu64,
|
||||
__PRETTY_FUNCTION__, fid];
|
||||
talloc_free(local_mem_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
[mapping setObject: lastModseq
|
||||
forKey: [NSString stringWithUnsignedLongLong: *current_cn]];
|
||||
talloc_free(local_mem_ctx);
|
||||
}
|
||||
|
||||
- (BOOL) synchroniseCacheForUID: (NSString *) messageUID
|
||||
{
|
||||
/* Try to synchronise old UIDs in versions.plist cache using an
|
||||
specific UID. It returns a boolean indicating if the
|
||||
synchronisation were done.
|
||||
|
||||
It should be used as last resort, keeping synchroniseCache to main
|
||||
sync entry point.
|
||||
*/
|
||||
NSMutableDictionary *currentProperties, *messages, *messageEntry, *mapping;
|
||||
NSArray *fetchResults;
|
||||
uint64_t changeNumber;
|
||||
NSDictionary *result;
|
||||
NSNumber *modseq;
|
||||
NSString *changeNumberStr;
|
||||
NSData *changeKey;
|
||||
|
||||
[versionsMessage reloadIfNeeded];
|
||||
currentProperties = [versionsMessage properties];
|
||||
messages = [currentProperties objectForKey: @"Messages"];
|
||||
messageEntry = [messages objectForKey: messageUID];
|
||||
if (!messageEntry)
|
||||
{
|
||||
fetchResults = [(NSDictionary *) [sogoObject fetchUIDs: [NSArray arrayWithObject: messageUID]
|
||||
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
||||
objectForKey: @"fetch"];
|
||||
if ([fetchResults count] == 1)
|
||||
{
|
||||
result = [fetchResults objectAtIndex: 0];
|
||||
modseq = [result objectForKey: @"modseq"];
|
||||
changeNumber = [[self context] getNewChangeNumber];
|
||||
changeNumberStr = [NSString stringWithUnsignedLongLong: changeNumber];
|
||||
|
||||
/* Create new message entry in Messages dict */
|
||||
messageEntry = [NSMutableDictionary new];
|
||||
[messages setObject: messageEntry forKey: messageUID];
|
||||
[messageEntry release];
|
||||
|
||||
/* Store the modseq and change number */
|
||||
[messageEntry setObject: modseq forKey: @"modseq"];
|
||||
[messageEntry setObject: changeNumberStr forKey: @"version"];
|
||||
|
||||
/* Store the change key */
|
||||
changeKey = [self getReplicaKeyFromGlobCnt: changeNumber >> 16];
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
||||
|
||||
/* Store the changeNumber -> modseq mapping */
|
||||
mapping = [currentProperties objectForKey: @"VersionMapping"];
|
||||
[mapping setObject: modseq forKey: changeNumberStr];
|
||||
|
||||
/* Store the last deleted change number if it is soft-deleted */
|
||||
if ([[result objectForKey: @"flags"] containsObject: @"deleted"])
|
||||
[currentProperties setObject: changeNumberStr
|
||||
forKey: @"SyncLastDeleteChangeNumber"];
|
||||
|
||||
/* Save the message */
|
||||
[versionsMessage save];
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
/* If message entry exists, then synchroniseCache did its job */
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (NSNumber *) modseqFromMessageChangeNumber: (NSString *) changeNum
|
||||
{
|
||||
NSDictionary *mapping;
|
||||
NSNumber *modseq;
|
||||
NSEnumerator *enumerator;
|
||||
id key;
|
||||
uint64_t found, target, current, replica_id, current_cn;
|
||||
NSString *closestChangeNum;
|
||||
|
||||
mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"];
|
||||
modseq = [mapping objectForKey: changeNum];
|
||||
if (modseq) return modseq;
|
||||
|
||||
// Not found from stored change numbers for this folder.
|
||||
// Get the closest modseq for the change number given.
|
||||
// O(n) cost but will be unusual behaviour.
|
||||
target = exchange_globcnt([changeNum unsignedLongLongValue] >> 16);
|
||||
replica_id = [changeNum unsignedLongLongValue] & 0xFFFF;
|
||||
found = 0;
|
||||
enumerator = [mapping keyEnumerator];
|
||||
while ((key = [enumerator nextObject]))
|
||||
{
|
||||
current_cn = [(NSString *)key unsignedLongLongValue];
|
||||
if ((current_cn & 0xFFFF) != replica_id)
|
||||
continue;
|
||||
current = exchange_globcnt(current_cn >> 16);
|
||||
if (current < target && current > found)
|
||||
found = current;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
closestChangeNum = [NSString stringWithUnsignedLongLong:
|
||||
(exchange_globcnt(found) << 16 | replica_id)];
|
||||
modseq = [mapping objectForKey: closestChangeNum];
|
||||
}
|
||||
|
||||
return modseq;
|
||||
}
|
||||
@@ -944,11 +688,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
if (dotRange.location != NSNotFound)
|
||||
messageUid = [messageKey substringToIndex: dotRange.location];
|
||||
else
|
||||
{
|
||||
messageUid = nil;
|
||||
[self errorWithFormat:@"%s: Unexpected messageKey value [%@]",
|
||||
__PRETTY_FUNCTION__, messageKey];
|
||||
}
|
||||
messageUid = nil;
|
||||
|
||||
return messageUid;
|
||||
}
|
||||
@@ -965,79 +705,22 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
return changeNumber;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
|
||||
- (void) setChangeKey: (NSData *) changeKey
|
||||
forMessageWithKey: (NSString *) messageKey
|
||||
{
|
||||
NSMutableDictionary *messages, *messageEntry;
|
||||
NSString *messageUid;
|
||||
BOOL synced;
|
||||
|
||||
messageUid = [self messageUIDFromMessageKey: messageKey];
|
||||
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
||||
messageEntry = [messages objectForKey: messageUid];
|
||||
if (!messageEntry)
|
||||
{
|
||||
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
|
||||
@"this message %@", messageKey];
|
||||
synced = [self synchroniseCacheForUID: messageUid];
|
||||
if (synced)
|
||||
messageEntry = [[[versionsMessage properties] objectForKey: @"Messages"] objectForKey: messageUid];
|
||||
if (!messageEntry)
|
||||
{
|
||||
[self errorWithFormat: @"still nothing. We crash!"];
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
return messageEntry;
|
||||
}
|
||||
|
||||
- (void) setChangeKey: (NSData *) changeKey
|
||||
forMessageWithKey: (NSString *) messageKey
|
||||
{
|
||||
[self _setChangeKey: changeKey
|
||||
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
|
||||
|
||||
abort ();
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
||||
|
||||
[versionsMessage save];
|
||||
}
|
||||
|
||||
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
|
||||
forMessageWithKey: (NSString *) messageKey
|
||||
{
|
||||
/* Update predecessor change list property given the change key. It
|
||||
returns if the change key has been added to the list or not */
|
||||
BOOL added = NO;
|
||||
NSData *globCnt, *oldGlobCnt;
|
||||
NSDictionary *messageEntry;
|
||||
NSMutableDictionary *changeList;
|
||||
NSString *guid;
|
||||
struct XID *xid;
|
||||
|
||||
xid = [changeKey asXIDInMemCtx: NULL];
|
||||
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
|
||||
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
|
||||
talloc_free (xid);
|
||||
|
||||
messageEntry = [self _messageEntryFromMessageKey: messageKey];
|
||||
if (messageEntry)
|
||||
{
|
||||
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
|
||||
if (changeList)
|
||||
{
|
||||
oldGlobCnt = [changeList objectForKey: guid];
|
||||
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
|
||||
{
|
||||
[changeList setObject: globCnt forKey: guid];
|
||||
[versionsMessage save];
|
||||
added = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"Missing predecessor change list to update"];
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
|
||||
{
|
||||
NSDictionary *messages, *changeKeyDict;
|
||||
@@ -1101,37 +784,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Management for extra properties once they already hit the IMAP server */
|
||||
- (void) setExtraProperties: (NSDictionary *) props
|
||||
forMessage: (NSString *) messageKey
|
||||
{
|
||||
NSMutableDictionary *extraProps, *currentProperties;
|
||||
NSString *messageUid;
|
||||
|
||||
messageUid = [self messageUIDFromMessageKey: messageKey];
|
||||
currentProperties = [versionsMessage properties];
|
||||
extraProps = [currentProperties objectForKey: @"ExtraMessagesProperties"];
|
||||
if (!extraProps)
|
||||
{
|
||||
extraProps = [NSMutableDictionary new];
|
||||
[currentProperties setObject: extraProps forKey: @"ExtraMessagesProperties"];
|
||||
[extraProps release];
|
||||
}
|
||||
|
||||
[extraProps setObject: props
|
||||
forKey: messageUid];
|
||||
[versionsMessage save];
|
||||
}
|
||||
|
||||
- (NSDictionary *) extraPropertiesForMessage: (NSString *) messageKey
|
||||
{
|
||||
NSString *messageUid;
|
||||
|
||||
messageUid = [self messageUIDFromMessageKey: messageKey];
|
||||
return [[[versionsMessage properties] objectForKey: @"ExtraMessagesProperties"]
|
||||
objectForKey: messageUid];
|
||||
}
|
||||
|
||||
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
|
||||
andCN: (NSNumber **) cnNbr
|
||||
inTableType: (uint8_t) tableType
|
||||
@@ -1140,7 +792,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
NSString *changeNumber;
|
||||
uint64_t modseq;
|
||||
NSDictionary *versionProperties;
|
||||
EOQualifier *deletedQualifier, *kvQualifier, *searchQualifier;
|
||||
|
||||
if (tableType == MAPISTORE_MESSAGE_TABLE)
|
||||
{
|
||||
@@ -1149,33 +800,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
unsignedLongLongValue];
|
||||
if (modseq > 0)
|
||||
{
|
||||
/* Hard deleted items */
|
||||
deletedUIDs = [(SOGoMailFolder *) sogoObject
|
||||
fetchUIDsOfVanishedItems: modseq];
|
||||
|
||||
/* Soft deleted items */
|
||||
kvQualifier = [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"modseq"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
value: [NSNumber numberWithUnsignedLongLong: modseq]];
|
||||
deletedQualifier
|
||||
= [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"FLAGS"
|
||||
operatorSelector: EOQualifierOperatorContains
|
||||
value: [NSArray arrayWithObject: @"Deleted"]];
|
||||
|
||||
searchQualifier = [[EOAndQualifier alloc]
|
||||
initWithQualifiers:
|
||||
kvQualifier, deletedQualifier, nil];
|
||||
|
||||
deletedUIDs = [deletedUIDs arrayByAddingObjectsFromArray:
|
||||
[sogoObject fetchUIDsMatchingQualifier: searchQualifier
|
||||
sortOrdering: nil]];
|
||||
|
||||
[deletedQualifier release];
|
||||
[kvQualifier release];
|
||||
[searchQualifier release];
|
||||
|
||||
deletedKeys = [deletedUIDs stringsWithFormat: @"%@.eml"];
|
||||
if ([deletedUIDs count] > 0)
|
||||
{
|
||||
@@ -1231,8 +857,6 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP)
|
||||
uint32_t currentUid, rangeMin;
|
||||
BOOL done = NO, inRange = NO;
|
||||
|
||||
rangeMin = 0;
|
||||
currentUid = 0;
|
||||
UIDs = [NSMutableArray array];
|
||||
while (!done)
|
||||
{
|
||||
@@ -1261,7 +885,7 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP)
|
||||
count++;
|
||||
}
|
||||
*UIDsP = UIDs;
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1303,7 +927,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
fromFolder: (MAPIStoreFolder *) sourceFolder
|
||||
withMIDs: (uint64_t *) targetMids
|
||||
andChangeKeys: (struct Binary_r **) targetChangeKeys
|
||||
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
|
||||
wantCopy: (uint8_t) wantCopy
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
|
||||
@@ -1318,13 +941,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
NSDictionary *result;
|
||||
NSUInteger count;
|
||||
NSArray *a;
|
||||
NSData *changeList;
|
||||
NSData *changeKey;
|
||||
|
||||
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
|
||||
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
|
||||
fromFolder: sourceFolder withMIDs: targetMids
|
||||
andChangeKeys: targetChangeKeys
|
||||
andPredecessorChangeLists: targetPredecessorChangeLists
|
||||
wantCopy: wantCopy
|
||||
inMemCtx: memCtx];
|
||||
|
||||
@@ -1359,20 +981,13 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
if (![[result objectForKey: @"result"] boolValue])
|
||||
return MAPISTORE_ERROR;
|
||||
|
||||
/* "Move" treatment: Store \Deleted and unregister urls as soft-deleted */
|
||||
/* "Move" treatment: Store \Deleted and unregister urls */
|
||||
if (!wantCopy)
|
||||
{
|
||||
[client storeFlags: [NSArray arrayWithObject: @"Deleted"] forUIDs: uids
|
||||
addOrRemove: YES];
|
||||
for (count = 0; count < midCount; count++)
|
||||
{
|
||||
/* Using soft-deleted to make deleted fmids to return the
|
||||
srcMids.
|
||||
See [MAPIStoreFolder getDeletedFMIDs:andCN:fromChangeNumber:inTableType:inMemCtx]
|
||||
for details */
|
||||
[mapping unregisterURLWithID: srcMids[count]
|
||||
andFlags: MAPISTORE_SOFT_DELETE];
|
||||
}
|
||||
[mapping unregisterURLWithID: srcMids[count]];
|
||||
}
|
||||
|
||||
/* Registration of target messages */
|
||||
@@ -1413,14 +1028,21 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
[self synchroniseCache];
|
||||
for (count = 0; count < midCount; count++)
|
||||
{
|
||||
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
|
||||
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
|
||||
messageKey = [NSString stringWithFormat: @"%@.eml",
|
||||
[destUIDs objectAtIndex: count]];
|
||||
[self _updatePredecessorChangeListWith: changeList
|
||||
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
|
||||
[self setChangeKey: changeKey
|
||||
forMessageWithKey: messageKey];
|
||||
}
|
||||
}
|
||||
|
||||
[self postNotificationsForMoveCopyMessagesWithMIDs: srcMids
|
||||
andMessageURLs: oldMessageURLs
|
||||
andCount: midCount
|
||||
fromFolder: sourceFolder
|
||||
withMIDs: targetMids
|
||||
wantCopy: wantCopy];
|
||||
|
||||
// We cleanup cache of our source and destination folders
|
||||
[self cleanupCaches];
|
||||
[sourceFolder cleanupCaches];
|
||||
@@ -1439,7 +1061,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
struct SRow folderRow;
|
||||
struct SPropValue nameProperty;
|
||||
MAPIStoreMailFolder *newFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoMailFolder *targetSOGoFolder;
|
||||
NSMutableArray *uids;
|
||||
NSArray *childKeys;
|
||||
@@ -1447,12 +1068,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
NGImap4Connection *connection;
|
||||
NGImap4Client *client;
|
||||
NSString *newURL, *parentDBFolderPath, *childKey, *folderIMAPName,
|
||||
*urlNamePart, *newFolderIMAPName, *newFolderDBName;
|
||||
*urlNamePart, *newFolderIMAPName;
|
||||
NSException *error;
|
||||
MAPIStoreMapping *mapping;
|
||||
NSDictionary *result;
|
||||
|
||||
if ([targetFolder isKindOfClass: MAPIStoreMailFolderK] || (!targetFolder && isMove))
|
||||
if ([targetFolder isKindOfClass: MAPIStoreMailFolderK])
|
||||
{
|
||||
folderURL = [sogoObject imap4URL];
|
||||
if (!newFolderName)
|
||||
@@ -1460,23 +1081,9 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
targetSOGoFolder = [targetFolder sogoObject];
|
||||
if (isMove)
|
||||
{
|
||||
newFolderDBName = [[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier];
|
||||
if (targetSOGoFolder)
|
||||
{
|
||||
/* Mimetise [SOGoMailFolderK imap4URLString] */
|
||||
urlNamePart = [[newFolderName stringByEncodingImap4FolderName] stringByEscapingURL];
|
||||
newFolderURL = [NSURL URLWithString: urlNamePart
|
||||
relativeToURL: [targetSOGoFolder imap4URL]];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Mimetise what createRootSecondaryFolderWithFID does */
|
||||
accountFolder = [[[self userContext] rootFolders] objectForKey: @"mail"];
|
||||
targetSOGoFolder = [SOGoMailFolder objectWithName: [NSString stringWithFormat: @"folder%@",
|
||||
newFolderDBName]
|
||||
inContainer: accountFolder];
|
||||
newFolderURL = [targetSOGoFolder imap4URL];
|
||||
}
|
||||
urlNamePart = [newFolderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
newFolderURL = [NSURL URLWithString: urlNamePart
|
||||
relativeToURL: [targetSOGoFolder imap4URL]];
|
||||
error = [[sogoObject imap4Connection]
|
||||
moveMailboxAtURL: folderURL
|
||||
toURL: newFolderURL];
|
||||
@@ -1486,29 +1093,16 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
{
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
mapping = [self mapping];
|
||||
if (targetFolder)
|
||||
newURL = [NSString stringWithFormat: @"%@folder%@/",
|
||||
[targetFolder url], newFolderDBName];
|
||||
else
|
||||
newURL = [NSString stringWithFormat: @"sogo://%@:%@@mail/folder%@/",
|
||||
[[self userContext] username], [[self userContext] username],
|
||||
newFolderDBName];
|
||||
newURL = [NSString stringWithFormat: @"%@folder%@/",
|
||||
[targetFolder url], urlNamePart];
|
||||
[mapping updateID: [self objectId] withURL: newURL];
|
||||
if (targetFolder)
|
||||
{
|
||||
parentDBFolderPath = [[targetFolder dbFolder] path];
|
||||
if (!parentDBFolderPath)
|
||||
parentDBFolderPath = @"";
|
||||
[dbFolder changePathTo: [NSString stringWithFormat:
|
||||
@"%@/folder%@",
|
||||
parentDBFolderPath,
|
||||
newFolderDBName]
|
||||
intoNewContainer: [targetFolder dbFolder]];
|
||||
}
|
||||
else
|
||||
[dbFolder changePathTo: [NSString stringWithFormat:
|
||||
@"/mail/folder%@", newFolderDBName]
|
||||
intoNewContainer: nil];
|
||||
parentDBFolderPath = [[targetFolder dbFolder] path];
|
||||
if (!parentDBFolderPath)
|
||||
parentDBFolderPath = @"";
|
||||
[dbFolder changePathTo: [NSString stringWithFormat:
|
||||
@"%@/folder%@",
|
||||
parentDBFolderPath,
|
||||
newFolderName]];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1541,7 +1135,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
[uids addObject: [self messageUIDFromMessageKey: childKey]];
|
||||
}
|
||||
|
||||
result = [client copyUids: uids
|
||||
result = [client copyUids: uids
|
||||
toFolder: newFolderIMAPName];
|
||||
if ([[result objectForKey: @"result"] boolValue])
|
||||
{
|
||||
@@ -1580,10 +1174,9 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
{
|
||||
SOGoCacheObject *childObject;
|
||||
|
||||
[[[self context] userContext] activate];
|
||||
|
||||
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
|
||||
globallyUniqueObjectId]
|
||||
inContainer: sogoObject];
|
||||
return [MAPIStoreMailVolatileMessage
|
||||
mapiStoreObjectWithSOGoObject: childObject
|
||||
inContainer: self];
|
||||
@@ -1659,7 +1252,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -1697,20 +1290,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
{
|
||||
[bodyPartKeys addObject: bodyPartKey];
|
||||
messageUid = [self messageUIDFromMessageKey: messageKey];
|
||||
/* If the bodyPartKey include peek, remove it as it is not returned
|
||||
as key in the IMAP server response.
|
||||
|
||||
IMAP conversation example:
|
||||
a4 UID FETCH 1 (UID BODY.PEEK[text])
|
||||
* 1 FETCH (UID 1 BODY[TEXT] {1677}
|
||||
*/
|
||||
bodyPartKey = [bodyPartKey stringByReplacingOccurrencesOfString: @"body.peek"
|
||||
withString: @"body"];
|
||||
[keyAssoc setObject: bodyPartKey forKey: messageUid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
client = [[(SOGoMailFolder *) sogoObject imap4Connection] client];
|
||||
[client select: [sogoObject absoluteImap4Name]];
|
||||
response = [client fetchUids: [keyAssoc allKeys]
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/* MAPIStoreMailFolderTable.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2015 Enrique J. Hernández
|
||||
*
|
||||
* Author: Enrique J. Hernández <ejhernandez@zentyal.com>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef MAPISTOREMAILFOLDERTABLE_H
|
||||
#define MAPISTOREMAILFOLDERTABLE_H
|
||||
|
||||
#import "MAPIStoreFolderTable.h"
|
||||
|
||||
@interface MAPIStoreMailFolderTable : MAPIStoreFolderTable
|
||||
@end
|
||||
|
||||
#endif /* MAPISTOREMAILFOLDERTABLE_H */
|
||||
@@ -1,42 +0,0 @@
|
||||
/* MAPIStoreMailFolderTable.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2015 Enrique J. Hernández
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "MAPIStoreMailFolderTable.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
|
||||
@implementation MAPIStoreMailFolderTable
|
||||
|
||||
- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property
|
||||
{
|
||||
switch(property)
|
||||
{
|
||||
case PR_DISPLAY_NAME:
|
||||
case PR_DISPLAY_NAME_UNICODE:
|
||||
return @"name";
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
{
|
||||
BOOL headerSetup;
|
||||
BOOL mailIsEvent;
|
||||
BOOL mailIsMeetingRequest;
|
||||
BOOL mailIsSharingObject;
|
||||
NSString *mimeKey;
|
||||
NSString *headerCharset;
|
||||
NSString *headerEncoding;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
#import <NGMail/NGMailAddress.h>
|
||||
#import <NGMail/NGMailAddressParser.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
@@ -40,9 +39,7 @@
|
||||
#import <Mailer/NSData+Mail.h>
|
||||
#import <Mailer/SOGoMailBodyPart.h>
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <Mailer/NSDictionary+Mail.h>
|
||||
|
||||
#import "Codepages.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
@@ -53,7 +50,6 @@
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreSamDBUtils.h"
|
||||
#import "MAPIStoreSharingMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
|
||||
@@ -67,7 +63,7 @@
|
||||
|
||||
@class iCalCalendar, iCalEvent;
|
||||
|
||||
static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
static Class NSExceptionK;
|
||||
|
||||
@interface NSString (MAPIStoreMIME)
|
||||
|
||||
@@ -110,7 +106,6 @@ static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
+ (void) initialize
|
||||
{
|
||||
NSExceptionK = [NSException class];
|
||||
MAPIStoreSharingMessageK = [MAPIStoreSharingMessage class];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
@@ -119,8 +114,6 @@ static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
{
|
||||
mimeKey = nil;
|
||||
mailIsEvent = NO;
|
||||
mailIsMeetingRequest = NO;
|
||||
mailIsSharingObject = NO;
|
||||
headerCharset = nil;
|
||||
headerEncoding = nil;
|
||||
headerMimeType = nil;
|
||||
@@ -139,7 +132,6 @@ static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
[bodyContent release];
|
||||
[headerMimeType release];
|
||||
[headerCharset release];
|
||||
[headerEncoding release];
|
||||
[appointmentWrapper release];
|
||||
[super dealloc];
|
||||
}
|
||||
@@ -159,30 +151,6 @@ static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
return [sogoObject date];
|
||||
}
|
||||
|
||||
- (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
BOOL listedProperties[65536];
|
||||
NSUInteger count;
|
||||
uint16_t propId;
|
||||
|
||||
if (mailIsSharingObject)
|
||||
{
|
||||
memset (listedProperties, NO, 65536 * sizeof (BOOL));
|
||||
[super getAvailableProperties: propertiesP inMemCtx: memCtx];
|
||||
for (count = 0; count < (*propertiesP)->cValues; count++)
|
||||
{
|
||||
propId = ((*propertiesP)->aulPropTag[count] >> 16) & 0xffff;
|
||||
listedProperties[propId] = YES;
|
||||
}
|
||||
[MAPIStoreSharingMessage fillAvailableProperties: *propertiesP
|
||||
withExclusions: listedProperties];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
return [super getAvailableProperties: propertiesP inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
static NSComparisonResult
|
||||
_compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
{
|
||||
@@ -197,7 +165,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
count1 = [keys indexOfObject: data1];
|
||||
data2 = [entry2 objectForKey: @"mimeType"];
|
||||
count2 = [keys indexOfObject: data2];
|
||||
|
||||
|
||||
if (count1 == count2)
|
||||
{
|
||||
data1 = [entry1 objectForKey: @"key"];
|
||||
@@ -232,11 +200,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
|
||||
- (void) _fetchHeaderData
|
||||
{
|
||||
MAPIStoreSharingMessage *sharingMessage;
|
||||
NSMutableArray *keys;
|
||||
NSArray *acceptedTypes;
|
||||
NSDictionary *messageData, *partHeaderData, *parameters;
|
||||
NSString *sharingHeader;
|
||||
|
||||
acceptedTypes = [NSArray arrayWithObjects: @"text/calendar",
|
||||
@"application/ics",
|
||||
@@ -260,27 +226,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
ASSIGN (headerCharset, [parameters objectForKey: @"charset"]);
|
||||
if ([headerMimeType isEqualToString: @"text/calendar"]
|
||||
|| [headerMimeType isEqualToString: @"application/ics"])
|
||||
{
|
||||
mailIsEvent = YES;
|
||||
if ([[parameters objectForKey: @"method"] isEqualToString: @"REQUEST"])
|
||||
mailIsMeetingRequest = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
sharingHeader = [[sogoObject mailHeaders] objectForKey: @"x-ms-sharing-localtype"];
|
||||
if (sharingHeader)
|
||||
{
|
||||
mailIsSharingObject = YES;
|
||||
/* It is difficult to subclass this in folder class, that's why
|
||||
a sharing object is a proxy in a mail message */
|
||||
sharingMessage = [[MAPIStoreSharingMessage alloc]
|
||||
initWithMailHeaders: [sogoObject mailHeaders]
|
||||
andConnectionInfo: [[self context] connectionInfo]
|
||||
fromMessage: self];
|
||||
[self addProxy: sharingMessage];
|
||||
[sharingMessage release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headerSetup = YES;
|
||||
@@ -422,7 +368,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
{
|
||||
uint64_t version = ULLONG_MAX;
|
||||
NSString *uid, *changeNumber;
|
||||
BOOL synced;
|
||||
|
||||
uid = [(MAPIStoreMailFolder *)
|
||||
container messageUIDFromMessageKey: [self nameInContainer]];
|
||||
@@ -441,19 +386,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
[self logWithFormat: @"got one"];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"attempting to get change number"
|
||||
@" by synchronising this specific message..."];
|
||||
synced = [(MAPIStoreMailFolder *) container synchroniseCacheForUID: uid];
|
||||
if (synced)
|
||||
{
|
||||
changeNumber = [(MAPIStoreMailFolder *) container
|
||||
changeNumberForMessageUID: uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"still nothing. We crash!"];
|
||||
abort();
|
||||
}
|
||||
[self errorWithFormat: @"still nothing. We crash!"];
|
||||
abort();
|
||||
}
|
||||
}
|
||||
version = [changeNumber unsignedLongLongValue] >> 16;
|
||||
@@ -530,7 +464,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
else
|
||||
stringValue = @"";
|
||||
*data = [stringValue asUnicodeInMemCtx: memCtx];
|
||||
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -570,31 +504,36 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if (mailIsEvent)
|
||||
[[self _appointmentWrapper] getPidTagMessageClass: data
|
||||
inMemCtx: memCtx];
|
||||
else if (mailIsSharingObject)
|
||||
*data = talloc_strdup (memCtx, "IPM.Sharing");
|
||||
else
|
||||
*data = talloc_strdup (memCtx, "IPM.Note");
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidTagReplyRequested: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
/* Note: this applies to regular mails... */
|
||||
// - (int) getPidTagReplyRequested: (void **) data // TODO
|
||||
// inMemCtx: (TALLOC_CTX *) memCtx
|
||||
// {
|
||||
// if (!headerSetup)
|
||||
// [self _fetchHeaderData];
|
||||
|
||||
// return (mailIsEvent
|
||||
// ? [self getYes: data inMemCtx: memCtx]
|
||||
// : [self getNo: data inMemCtx: memCtx]);
|
||||
// }
|
||||
|
||||
/* ... while this applies to invitations. */
|
||||
- (int) getPidTagResponseRequested: (void **) data // TODO
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
return (mailIsMeetingRequest
|
||||
? [self getYes: data inMemCtx: memCtx]
|
||||
return (mailIsEvent
|
||||
? [self getNo: data inMemCtx: memCtx]
|
||||
: MAPISTORE_ERR_NOT_FOUND);
|
||||
}
|
||||
|
||||
- (int) getPidTagResponseRequested: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidTagReplyRequested: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagLatestDeliveryTime: (void **) data // DOUBT
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -625,7 +564,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
NSDictionary *coreInfos;
|
||||
NSArray *flags;
|
||||
unsigned int v = 0;
|
||||
|
||||
|
||||
coreInfos = [sogoObject fetchCoreInfos];
|
||||
flags = [coreInfos objectForKey: @"flags"];
|
||||
|
||||
@@ -637,7 +576,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if ([[self attachmentKeys]
|
||||
count] > 0)
|
||||
v |= MSGFLAG_HASATTACH;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -657,7 +596,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
v = 2;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -669,15 +608,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
NSDictionary *coreInfos;
|
||||
NSArray *flags;
|
||||
unsigned int v;
|
||||
|
||||
|
||||
coreInfos = [sogoObject fetchCoreInfos];
|
||||
|
||||
|
||||
flags = [coreInfos objectForKey: @"flags"];
|
||||
if ([flags containsObject: @"flagged"])
|
||||
v = 6;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -756,7 +695,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if ([ngAddress isKindOfClass: [NGMailAddress class]])
|
||||
{
|
||||
cn = [ngAddress displayName];
|
||||
|
||||
|
||||
// If we don't have a displayName, we use the email address instead. This
|
||||
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
|
||||
// nor in the recipient field if we reply to the email.
|
||||
@@ -802,7 +741,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
samCtx = [[self context] connectionInfo]->sam_ctx;
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -810,7 +749,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
entryId = MAPIStoreExternalEntryId (cn, email);
|
||||
|
||||
*data = [entryId asBinaryInMemCtx: memCtx];
|
||||
|
||||
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
@@ -967,15 +906,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
{
|
||||
uint32_t v;
|
||||
NSString *s;
|
||||
|
||||
|
||||
s = [[sogoObject mailHeaders] objectForKey: @"x-priority"];
|
||||
v = 0x1;
|
||||
|
||||
|
||||
if ([s hasPrefix: @"1"]) v = 0x2;
|
||||
else if ([s hasPrefix: @"2"]) v = 0x2;
|
||||
else if ([s hasPrefix: @"4"]) v = 0x0;
|
||||
else if ([s hasPrefix: @"5"]) v = 0x0;
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, v);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -984,17 +923,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
- (int) getPidTagInternetCodepage: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSNumber *codepage;
|
||||
|
||||
codepage = [Codepages getCodepageFromName: headerCharset];
|
||||
if (!codepage)
|
||||
{
|
||||
[self warnWithFormat: @"Couldn't find codepage from `%@`. "
|
||||
@"Using UTF-8 by default", headerCharset];
|
||||
codepage = [Codepages getCodepageFromName: @"utf-8"];
|
||||
}
|
||||
|
||||
*data = MAPILongValue(memCtx, [codepage intValue]);
|
||||
/* ref:
|
||||
http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx
|
||||
|
||||
minimal list that should be handled:
|
||||
us-ascii: 20127
|
||||
iso-8859-1: 28591
|
||||
iso-8859-15: 28605
|
||||
utf-8: 65001 */
|
||||
*data = MAPILongValue(memCtx, 65001);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -1164,14 +1101,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
|
||||
if ([headerMimeType isEqualToString: @"text/plain"])
|
||||
format = EDITOR_FORMAT_PLAINTEXT;
|
||||
else if ([headerMimeType isEqualToString: @"text/html"])
|
||||
format = EDITOR_FORMAT_HTML;
|
||||
else
|
||||
format = 0; /* EDITOR_FORMAT_DONTKNOW */
|
||||
|
||||
|
||||
*data = MAPILongValue (memCtx, format);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -1396,41 +1333,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
: MAPISTORE_ERR_NOT_FOUND);
|
||||
}
|
||||
|
||||
- (int) getPidTagTransportMessageHeaders: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSDictionary *mailHeaders;
|
||||
NSEnumerator *keyEnumerator;
|
||||
NSMutableArray *headers;
|
||||
NGMimeMessageGenerator *g;
|
||||
NSString *headerKey, *fullHeader, *headerGenerated;
|
||||
id headerValue;
|
||||
NSData *headerData;
|
||||
|
||||
/* Let's encode each mail header and put them on 'headers' array */
|
||||
mailHeaders = [sogoObject mailHeaders];
|
||||
headers = [NSMutableArray arrayWithCapacity: [mailHeaders count]];
|
||||
|
||||
g = [[NGMimeMessageGenerator alloc] init];
|
||||
keyEnumerator = [mailHeaders keyEnumerator];
|
||||
while ((headerKey = [keyEnumerator nextObject]))
|
||||
{
|
||||
headerValue = [mailHeaders objectForKey: headerKey];
|
||||
|
||||
headerData = [g generateDataForHeaderField: headerKey value: headerValue];
|
||||
headerGenerated = [[NSString alloc] initWithData: headerData encoding:NSUTF8StringEncoding];
|
||||
fullHeader = [NSString stringWithFormat:@"%@: %@", headerKey, headerGenerated];
|
||||
[headerGenerated release];
|
||||
|
||||
[headers addObject: fullHeader];
|
||||
}
|
||||
[g release];
|
||||
|
||||
*data = [[headers componentsJoinedByString:@"\n"] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (void) getMessageData: (struct mapistore_message **) dataPtr
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -1500,10 +1402,10 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if ([cn length] == 0)
|
||||
cn = email;
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -1518,7 +1420,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
p = 0;
|
||||
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
|
||||
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
|
||||
|
||||
|
||||
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
|
||||
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
|
||||
p++;
|
||||
@@ -1526,7 +1428,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0);
|
||||
p++;
|
||||
|
||||
|
||||
// PR_7BIT_DISPLAY_NAME_UNICODE
|
||||
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
|
||||
p++;
|
||||
@@ -1534,7 +1436,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
// PR_SMTP_ADDRESS_UNICODE
|
||||
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
|
||||
p++;
|
||||
|
||||
|
||||
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
|
||||
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
|
||||
p++;
|
||||
@@ -1567,9 +1469,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
withPrefix: (NSString *) keyPrefix
|
||||
{
|
||||
NSArray *parts;
|
||||
NSDictionary *parameters;
|
||||
NSUInteger count, max;
|
||||
|
||||
if ([[bodyInfo filename] length] > 0)
|
||||
parameters = [[bodyInfo objectForKey: @"disposition"]
|
||||
objectForKey: @"parameterList"];
|
||||
if ([[parameters objectForKey: @"filename"] length] > 0)
|
||||
{
|
||||
if ([keyPrefix length] == 0)
|
||||
keyPrefix = @"0";
|
||||
@@ -1612,8 +1517,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
max = [keyParts count];
|
||||
if (max > 0)
|
||||
{
|
||||
[[self userContext] activate];
|
||||
|
||||
currentPart = [sogoObject lookupName: [keyParts objectAtIndex: 0]
|
||||
inContext: nil
|
||||
acquire: NO];
|
||||
@@ -1643,29 +1546,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
return attachment;
|
||||
}
|
||||
|
||||
- (enum mapistore_error) setReadFlag: (uint8_t) flag
|
||||
- (int) setReadFlag: (uint8_t) flag
|
||||
{
|
||||
BOOL modified = NO;
|
||||
BOOL alreadyRead = NO;
|
||||
NSString *imapFlag = @"\\Seen";
|
||||
|
||||
alreadyRead = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"]
|
||||
containsObject: @"seen"];
|
||||
|
||||
/* TODO: notifications should probably be emitted from here */
|
||||
if (flag & CLEAR_READ_FLAG)
|
||||
{
|
||||
[sogoObject removeFlags: imapFlag];
|
||||
modified = alreadyRead;
|
||||
}
|
||||
[sogoObject removeFlags: imapFlag];
|
||||
else
|
||||
{
|
||||
[sogoObject addFlags: imapFlag];
|
||||
modified = !alreadyRead;
|
||||
}
|
||||
|
||||
if (modified)
|
||||
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
|
||||
[sogoObject addFlags: imapFlag];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -1677,7 +1566,11 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
bodyPartKey = mimeKey;
|
||||
if ([mimeKey hasPrefix: @"body.peek"])
|
||||
bodyPartKey = [NSString stringWithFormat: @"body[%@]",
|
||||
[mimeKey _strippedBodyKey]];
|
||||
else
|
||||
bodyPartKey = mimeKey;
|
||||
|
||||
return bodyPartKey;
|
||||
}
|
||||
@@ -1691,22 +1584,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
bodySetup = YES;
|
||||
}
|
||||
|
||||
- (MAPIStoreSharingMessage *) _sharingObject
|
||||
{
|
||||
/* Get the sharing object if available */
|
||||
NSUInteger i, max;
|
||||
id proxy;
|
||||
|
||||
max = [proxies count];
|
||||
for (i = 0; i < max; i++) {
|
||||
proxy = [proxies objectAtIndex: i];
|
||||
if ([proxy isKindOfClass: MAPIStoreSharingMessageK])
|
||||
return proxy;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSNumber *value;
|
||||
|
||||
@@ -1719,11 +1597,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
else /* 0: unflagged, 1: follow up complete */
|
||||
[sogoObject removeFlags: @"\\Flagged"];
|
||||
}
|
||||
|
||||
if (mailIsSharingObject)
|
||||
[[self _sharingObject] saveWithMessage: self
|
||||
andSOGoObject: sogoObject];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -161,30 +161,15 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
|
||||
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
|
||||
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
|
||||
if (modseq)
|
||||
{
|
||||
if (res->relop == RELOP_GT)
|
||||
modseq = [NSNumber numberWithUnsignedLongLong:
|
||||
[modseq unsignedLongLongValue] + 1];
|
||||
|
||||
}
|
||||
modseq = [NSNumber numberWithUnsignedLongLong:
|
||||
[modseq unsignedLongLongValue] + 1];
|
||||
else
|
||||
modseq = [NSNumber numberWithUnsignedLongLong: 0];
|
||||
|
||||
if (res->relop == RELOP_GT || res->relop == RELOP_GE)
|
||||
{
|
||||
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
value: modseq];
|
||||
[*qualifier autorelease];
|
||||
rc = MAPIRestrictionStateNeedsEval;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Ignore other operations as IMAP only support MODSEQ >= X */
|
||||
[self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=",
|
||||
[self operatorFromRestrictionOperator: res->relop]];
|
||||
rc = MAPIRestrictionStateAlwaysTrue;
|
||||
}
|
||||
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
value: modseq];
|
||||
[*qualifier autorelease];
|
||||
rc = MAPIRestrictionStateNeedsEval;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -28,13 +28,11 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSScanner.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSObject+Values.h>
|
||||
#import <NGExtensions/NSString+Encoding.h>
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
@@ -45,7 +43,6 @@
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoCacheObject.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoMailer.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
@@ -53,7 +50,6 @@
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
|
||||
#import "Codepages.h"
|
||||
#import "MAPIStoreAttachment.h"
|
||||
#import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
@@ -66,13 +62,13 @@
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import <SOGo/SOGoCacheObject.h>
|
||||
|
||||
#import "MAPIStoreMailVolatileMessage.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
#include <mapistore/mapistore_nameid.h>
|
||||
|
||||
static Class NSNumberK = Nil;
|
||||
|
||||
@@ -286,7 +282,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
|
||||
version = [properties objectForKey: @"version"];
|
||||
|
||||
return (version
|
||||
? [version unsignedLongLongValue]
|
||||
? exchange_globcnt ([version unsignedLongLongValue])
|
||||
: ULLONG_MAX);
|
||||
}
|
||||
|
||||
@@ -371,7 +367,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
|
||||
msgData->recipients_count = max;
|
||||
msgData->recipients = talloc_array (msgData, struct mapistore_message_recipient, max);
|
||||
current = 0;
|
||||
for (type = MAPI_ORIG; type <= MAPI_BCC; type++)
|
||||
for (type = 0; type < 4; type++)
|
||||
{
|
||||
recipients = [allRecipients objectForKey: recTypes[type]];
|
||||
recipientsMax = [recipients count];
|
||||
@@ -387,7 +383,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -535,116 +531,17 @@ QuoteSpecials (NSString *address)
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
FillMessageHeadersFromSharingProperties (NGMutableHashMap *headers, NSDictionary *mailProperties)
|
||||
{
|
||||
/* Store the *important* properties related with a sharing object as
|
||||
MIME eXtension headers. See [MS-OXSHARE] Section 2.2 for details
|
||||
about the properties */
|
||||
|
||||
id value;
|
||||
NSNumber *sharingFlavourNum = nil;
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingCapabilities)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-Capabilities"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingFlavor)];
|
||||
if (value)
|
||||
sharingFlavourNum = (NSNumber *)value;
|
||||
else
|
||||
{
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidNameXSharingFlavor)];
|
||||
if (value)
|
||||
{
|
||||
/* Transform the hex string to unsigned int */
|
||||
NSScanner *scanner;
|
||||
unsigned int sharingFlavour;
|
||||
scanner = [NSScanner scannerWithString:value];
|
||||
if ([scanner scanHexInt:&sharingFlavour])
|
||||
sharingFlavourNum =[NSNumber numberWithUnsignedInt: sharingFlavour];
|
||||
}
|
||||
}
|
||||
if (sharingFlavourNum)
|
||||
{
|
||||
if ([sharingFlavourNum unsignedIntegerValue] == 0x5100)
|
||||
{
|
||||
/* 0x5100 sharing flavour is not in standard but it seems to
|
||||
be a denial of request + invitation message so we store
|
||||
deny sharing flavour */
|
||||
sharingFlavourNum = [NSNumber numberWithUnsignedInt: SHARING_DENY_REQUEST];
|
||||
}
|
||||
[headers setObject: sharingFlavourNum
|
||||
forKey: @"X-MS-Sharing-Flavor"];
|
||||
}
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorEntryId)];
|
||||
if (value)
|
||||
[headers setObject: [[value stringByEncodingBase64] stringByReplacingOccurrencesOfString: @"\n"
|
||||
withString: @""]
|
||||
forKey: @"X-MS-Sharing-InitiatorEntryId"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorName)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-InitiatorName"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorSmtp)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-InitiatorSmtp"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingLocalType)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-LocalType"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingProviderName)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-ProviderName"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteName)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-RemoteName"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteStoreUid)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-RemoteStoreUid"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteUid)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-RemoteUid"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-ResponseTime"];
|
||||
|
||||
value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingResponseType)];
|
||||
if (value)
|
||||
[headers setObject: value
|
||||
forKey: @"X-MS-Sharing-ResponseType"];
|
||||
|
||||
}
|
||||
|
||||
static inline void
|
||||
FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
NSDictionary *mailProperties, BOOL withBcc,
|
||||
struct mapistore_connection_info *connInfo)
|
||||
{
|
||||
BOOL fromResolved = NO;
|
||||
NSData *senderEntryId;
|
||||
NSMutableString *subject;
|
||||
NSString *from, *recId, *messageId, *subjectData, *recipientsStr, *msgClass;
|
||||
NSString *from, *recId, *messageId, *subjectData;
|
||||
NSArray *list;
|
||||
NSCalendarDate *date;
|
||||
NSDictionary *recipients;
|
||||
enum ulRecipClass type, bccLimit;
|
||||
NSUInteger type, bccLimit;
|
||||
SOGoUser *activeUser;
|
||||
NSNumber *priority;
|
||||
|
||||
@@ -664,8 +561,8 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
bccLimit = MAPI_BCC;
|
||||
else
|
||||
bccLimit = MAPI_CC;
|
||||
|
||||
for (type = MAPI_TO; type <= bccLimit; type++)
|
||||
bccLimit++;
|
||||
for (type = MAPI_TO; type < bccLimit; type++)
|
||||
{
|
||||
recId = recTypes[type];
|
||||
list = MakeRecipientsList ([recipients objectForKey: recId]);
|
||||
@@ -674,96 +571,10 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
|
||||
list = MakeRecipientsList ([recipients objectForKey: @"orig"]);
|
||||
if ([list count])
|
||||
{
|
||||
[headers setObjects: list forKey: @"from"];
|
||||
fromResolved = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromResolved)
|
||||
{
|
||||
NSLog (@"Message without an orig from, try to guess it from PidTagSenderEntryId");
|
||||
senderEntryId = [mailProperties objectForKey: MAPIPropertyKey (PR_SENDER_ENTRYID)];
|
||||
if (senderEntryId)
|
||||
{
|
||||
struct Binary_r bin32;
|
||||
struct AddressBookEntryId *addrBookEntryId;
|
||||
NSString *username;
|
||||
NSMutableDictionary *fromRecipient;
|
||||
|
||||
fromRecipient = [NSMutableDictionary dictionaryWithCapacity: 2];
|
||||
|
||||
bin32.cb = [senderEntryId length];
|
||||
bin32.lpb = (uint8_t *) [senderEntryId bytes];
|
||||
addrBookEntryId = get_AddressBookEntryId (connInfo->sam_ctx, &bin32);
|
||||
if (addrBookEntryId && [[NSString stringWithGUID: &addrBookEntryId->ProviderUID]
|
||||
hasSuffix: @"08002b2fe182"])
|
||||
{
|
||||
/* TODO: better way to distinguish local and other ones */
|
||||
username = MAPIStoreSamDBUserAttribute (connInfo->sam_ctx, @"legacyExchangeDN",
|
||||
[NSString stringWithUTF8String: addrBookEntryId->X500DN], @"sAMAccountName");
|
||||
if (username)
|
||||
{
|
||||
SOGoUser *fromUser;
|
||||
|
||||
fromUser = [SOGoUser userWithLogin: [username lowercaseString]];
|
||||
[fromRecipient setObject: [fromUser cn] forKey: @"fullName"];
|
||||
[fromRecipient setObject: [[fromUser allEmails] objectAtIndex: 0]
|
||||
forKey: @"email"];
|
||||
}
|
||||
else
|
||||
[fromRecipient setObject: [NSString stringWithUTF8String: addrBookEntryId->X500DN]
|
||||
forKey: @"email"];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try with One-Off EntryId */
|
||||
struct OneOffEntryId *oneOffEntryId;
|
||||
|
||||
oneOffEntryId = get_OneOffEntryId (connInfo->sam_ctx, &bin32);
|
||||
if (oneOffEntryId && [[NSString stringWithGUID: &oneOffEntryId->ProviderUID]
|
||||
hasSuffix: @"00dd010f5402"])
|
||||
{
|
||||
[fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->DisplayName.lpszW]
|
||||
forKey: @"fullName"];
|
||||
[fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->EmailAddress.lpszW]
|
||||
forKey: @"email"];
|
||||
}
|
||||
talloc_free (oneOffEntryId);
|
||||
}
|
||||
talloc_free (addrBookEntryId);
|
||||
|
||||
if ([[fromRecipient allKeys] count] > 0)
|
||||
{
|
||||
list = MakeRecipientsList ([NSArray arrayWithObjects: fromRecipient, nil]);
|
||||
if ([list count])
|
||||
[headers setObjects: list forKey: @"from"];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!recipients)
|
||||
{
|
||||
NSLog (@"Message without recipients."
|
||||
@"Guessing recipients from PidTagOriginalDisplayTo and PidTagOriginalCc");
|
||||
recipientsStr = [mailProperties objectForKey: MAPIPropertyKey (PidTagOriginalDisplayTo)];
|
||||
if (recipientsStr)
|
||||
{
|
||||
list = [recipientsStr componentsSeparatedByString:@", "];
|
||||
if ([list count])
|
||||
[headers setObjects: list forKey: @"to"];
|
||||
}
|
||||
|
||||
recipientsStr = [mailProperties objectForKey: MAPIPropertyKey (PidTagOriginalDisplayCc)];
|
||||
if (recipientsStr)
|
||||
{
|
||||
list = [recipientsStr componentsSeparatedByString:@", "];
|
||||
if ([list count])
|
||||
[headers setObjects: list forKey: @"cc"];
|
||||
}
|
||||
[headers setObjects: list forKey: @"from"];
|
||||
}
|
||||
else
|
||||
NSLog (@"message without recipients");
|
||||
|
||||
subject = [NSMutableString stringWithCapacity: 128];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)];
|
||||
@@ -772,12 +583,6 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
if ([subject length] == 0)
|
||||
{
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)];
|
||||
if (subjectData)
|
||||
[subject appendString: subjectData];
|
||||
}
|
||||
[headers setObject: [subject asQPSubjectString: @"utf-8"] forKey: @"subject"];
|
||||
|
||||
messageId = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_MESSAGE_ID_UNICODE)];
|
||||
@@ -787,6 +592,8 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
date = [mailProperties objectForKey: MAPIPropertyKey (PR_CLIENT_SUBMIT_TIME)];
|
||||
if (date)
|
||||
{
|
||||
date = [date addYear: 0 month: 0 day: 0
|
||||
hour: 0 minute: 0 second: [[date timeZone] secondsFromGMT]];
|
||||
[headers addObject: [date rfc822DateString] forKey: @"date"];
|
||||
}
|
||||
[headers addObject: @"1.0" forKey: @"MIME-Version"];
|
||||
@@ -805,13 +612,6 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
{
|
||||
[headers addObject: @"5 (Lowest)" forKey: @"X-Priority"];
|
||||
}
|
||||
|
||||
msgClass = [mailProperties objectForKey: MAPIPropertyKey (PidTagMessageClass)];
|
||||
if ([msgClass isEqualToString: @"IPM.Sharing"])
|
||||
{
|
||||
FillMessageHeadersFromSharingProperties (headers, mailProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static NSArray *
|
||||
@@ -872,9 +672,21 @@ MakeTextHtmlBody (NSDictionary *mailProperties, NSDictionary *attachmentParts,
|
||||
{
|
||||
/* charset */
|
||||
codePage = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_CPID)];
|
||||
charset = [Codepages getNameFromCodepage: codePage];
|
||||
if (!charset)
|
||||
charset = @"utf-8";
|
||||
switch ([codePage intValue])
|
||||
{
|
||||
case 20127:
|
||||
charset = @"us-ascii";
|
||||
break;
|
||||
case 28605:
|
||||
charset = @"iso-8859-15";
|
||||
break;
|
||||
case 65001:
|
||||
charset = @"utf-8";
|
||||
break;
|
||||
case 28591:
|
||||
default:
|
||||
charset = @"iso-8859-1";
|
||||
}
|
||||
htmlContentType = [NSString stringWithFormat: @"text/html; charset=%@",
|
||||
charset];
|
||||
|
||||
@@ -1022,7 +834,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
messageBody = MakeMessageBody (properties, attachmentParts, &contentType);
|
||||
if (messageBody)
|
||||
{
|
||||
[message setHeader: contentType forKey: @"content-type"];
|
||||
[headers setObject: contentType forKey: @"content-type"];
|
||||
[message setBody: messageBody];
|
||||
}
|
||||
|
||||
@@ -1053,7 +865,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
NSMutableArray *recipientEmails;
|
||||
NSArray *list;
|
||||
NSString *recId, *from, *msgClass;
|
||||
enum ulRecipClass type;
|
||||
NSUInteger count;
|
||||
SOGoUser *activeUser;
|
||||
SOGoDomainDefaults *dd;
|
||||
NSException *error;
|
||||
@@ -1061,17 +873,17 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
id <SOGoAuthenticator> authenticator;
|
||||
|
||||
msgClass = [properties objectForKey: MAPIPropertyKey (PidTagMessageClass)];
|
||||
if ([msgClass isEqualToString: @"IPM.Note"] || [msgClass isEqualToString: @"IPM.Sharing"]) /* we skip invitation replies */
|
||||
if ([msgClass isEqualToString: @"IPM.Note"]) /* we skip invitation replies */
|
||||
{
|
||||
/* send mail */
|
||||
|
||||
messageData = [self _generateMailDataWithBcc: NO];
|
||||
|
||||
|
||||
recipientEmails = [NSMutableArray arrayWithCapacity: 32];
|
||||
recipients = [properties objectForKey: @"recipients"];
|
||||
for (type = MAPI_ORIG; type <= MAPI_BCC; type++)
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
recId = recTypes[type];
|
||||
recId = recTypes[count];
|
||||
list = [recipients objectForKey: recId];
|
||||
[recipientEmails
|
||||
addObjectsFromArray: [list objectsForKey: @"email"
|
||||
@@ -1084,7 +896,6 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
dd = [activeUser domainDefaults];
|
||||
from = [[activeUser allEmails] objectAtIndex: 0];
|
||||
|
||||
[[self userContext] activate];
|
||||
woContext = [[self userContext] woContext];
|
||||
authenticator = [sogoObject authenticatorInContext: woContext];
|
||||
error = [[SOGoMailer mailerWithDomainDefaults: dd]
|
||||
@@ -1111,8 +922,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
|
||||
- (void) save: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
BOOL updatedMetadata;
|
||||
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
|
||||
NSString *folderName, *flag, *newIdString, *messageKey;
|
||||
NSData *changeKey, *messageData;
|
||||
NGImap4Connection *connection;
|
||||
NGImap4Client *client;
|
||||
@@ -1148,33 +958,21 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
[sogoObject setNameInContainer: messageKey];
|
||||
[mapping registerURL: [self url] withID: mid];
|
||||
|
||||
/* synchronise the cache and update the predecessor change list
|
||||
with the change key provided by the client. Before doing
|
||||
this, lets issue a unselect/select combo because of timing
|
||||
issues with Dovecot in obtaining the latest modseq.
|
||||
Sometimes, Dovecot doesn't return the newly appended UID if
|
||||
we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)"
|
||||
command (where XYZ is the HIGHESTMODSEQ+1) immediately after
|
||||
IMAP APPEND */
|
||||
/* synchronise the cache and update the change key with the one provided
|
||||
by the client. Before doing this, lets issue a unselect/select combo
|
||||
because of timing issues with Dovecot in obtaining the latest modseq.
|
||||
Sometimes, Dovecot doesn't return the newly appended UID if we do
|
||||
a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where
|
||||
XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */
|
||||
[client unselect];
|
||||
[client select: folderName];
|
||||
|
||||
[(MAPIStoreMailFolder *) container synchroniseCache];
|
||||
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
|
||||
if (changeKey)
|
||||
{
|
||||
updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey
|
||||
forMessageWithKey: messageKey];
|
||||
if (!updatedMetadata)
|
||||
[self warnWithFormat: @"Predecessor change list not updated with client data"];
|
||||
}
|
||||
|
||||
/* Update version property (PR_CHANGE_KEY indeed) as it is
|
||||
requested once it is saved */
|
||||
changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString];
|
||||
if (changeNumber)
|
||||
[properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16]
|
||||
forKey: @"version"];
|
||||
[(MAPIStoreMailFolder *) container
|
||||
setChangeKey: changeKey
|
||||
forMessageWithKey: messageKey];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,15 +32,17 @@
|
||||
{
|
||||
void *memCtx;
|
||||
NSString *username;
|
||||
struct indexing_context *indexing;
|
||||
struct tdb_wrap *indexing;
|
||||
NSMutableDictionary *mapping; /* FID/MID -> url */
|
||||
NSMutableDictionary *reverseMapping; /* url -> FID/MID */
|
||||
NSUInteger useCount;
|
||||
}
|
||||
|
||||
+ (id) mappingForUsername: (NSString *) username
|
||||
withIndexing: (struct indexing_context *) indexing;
|
||||
withIndexing: (struct tdb_wrap *) indexing;
|
||||
|
||||
- (id) initForUsername: (NSString *) username
|
||||
withIndexing: (struct indexing_context *) indexing;
|
||||
withIndexing: (struct tdb_wrap *) indexing;
|
||||
|
||||
|
||||
- (void) increaseUseCount;
|
||||
@@ -49,19 +51,13 @@
|
||||
- (NSString *) urlFromID: (uint64_t) idKey;
|
||||
|
||||
- (uint64_t) idFromURL: (NSString *) url;
|
||||
- (uint64_t) idFromURL: (NSString *) url
|
||||
isSoftDeleted: (bool *) softDeleted;
|
||||
- (BOOL) registerURL: (NSString *) urlString
|
||||
withID: (uint64_t) idNbr;
|
||||
- (void) registerURLs: (NSArray *) urlString
|
||||
withIDs: (NSArray *) idNbrs;
|
||||
- (void) unregisterURLWithID: (uint64_t) idNbr;
|
||||
- (void) unregisterURLWithID: (uint64_t) idNbr
|
||||
andFlags: (uint8_t) flags;
|
||||
- (void) updateID: (uint64_t) idNbr
|
||||
withURL: (NSString *) urlString;
|
||||
- (BOOL) updateURL: (NSString *) urlString
|
||||
withID: (uint64_t) idNbr;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+171
-115
@@ -33,15 +33,16 @@
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
#import "MAPIStoreTypes.h"
|
||||
|
||||
#import "MAPIStoreMapping.h"
|
||||
|
||||
#include <talloc.h>
|
||||
#include <tdb.h>
|
||||
|
||||
struct tdb_wrap {
|
||||
struct tdb_context *tdb;
|
||||
};
|
||||
|
||||
static NSMutableDictionary *mappingRegistry = nil;
|
||||
|
||||
@@ -58,9 +59,51 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
return [NSString stringWithUnsignedLongLong: idNbr];
|
||||
}
|
||||
|
||||
static int
|
||||
MAPIStoreMappingTDBTraverse (TDB_CONTEXT *ctx, TDB_DATA data1, TDB_DATA data2,
|
||||
void *data)
|
||||
{
|
||||
NSMutableDictionary *mapping;
|
||||
id idKey;
|
||||
NSString *uri;
|
||||
char *idStr, *uriStr;
|
||||
uint64_t idNbr;
|
||||
|
||||
// get the key
|
||||
// key examples : key(18) = "0x6900000000000001"
|
||||
// key(31) = "SOFT_DELETED:0xb100020000000001"
|
||||
//
|
||||
idStr = (char *) data1.dptr;
|
||||
idKey = nil;
|
||||
|
||||
if (strncmp(idStr, "SOFT_DELETED:", 13) != 0)
|
||||
{
|
||||
// It's very important here to use strtoull and NOT strtoll as
|
||||
// the latter will overflow a long long with typical key values.
|
||||
idNbr = strtoull(idStr, NULL, 0);
|
||||
// idKey = [NSNumber numberWithUnsignedLongLong: idNbr];
|
||||
idKey = MAPIStoreMappingKeyFromId(idNbr);
|
||||
}
|
||||
|
||||
// get the value and null-terminate it
|
||||
uriStr = (char *)malloc(sizeof(char *) * data2.dsize+1);
|
||||
memset(uriStr, 0, data2.dsize+1);
|
||||
memcpy(uriStr, (const char *) data2.dptr, data2.dsize);
|
||||
uri = [NSString stringWithUTF8String: uriStr];
|
||||
free (uriStr);
|
||||
|
||||
mapping = data;
|
||||
|
||||
if (uri && idKey)
|
||||
{
|
||||
[mapping setObject: uri forKey: idKey];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ (id) mappingForUsername: (NSString *) username
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withIndexing: (struct tdb_wrap *) indexing
|
||||
{
|
||||
id mapping;
|
||||
|
||||
@@ -80,6 +123,8 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
if ((self = [super init]))
|
||||
{
|
||||
memCtx = talloc_zero (NULL, TALLOC_CTX);
|
||||
mapping = [NSMutableDictionary new];
|
||||
reverseMapping = [NSMutableDictionary new];
|
||||
indexing = NULL;
|
||||
useCount = 0;
|
||||
}
|
||||
@@ -108,15 +153,29 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
}
|
||||
|
||||
- (id) initForUsername: (NSString *) newUsername
|
||||
withIndexing: (struct indexing_context *) newIndexing
|
||||
withIndexing: (struct tdb_wrap *) newIndexing
|
||||
{
|
||||
NSString *idNbr, *uri;
|
||||
NSArray *keys;
|
||||
NSUInteger count, max;
|
||||
|
||||
if ((self = [self init]))
|
||||
{
|
||||
ASSIGN (username, newUsername);
|
||||
indexing = newIndexing;
|
||||
/* Workaround so all indexing context are valid and won't be freed. */
|
||||
// TODO refactor indexing interface
|
||||
talloc_reference(memCtx, indexing);
|
||||
(void) talloc_reference (memCtx, newIndexing);
|
||||
tdb_traverse_read (indexing->tdb, MAPIStoreMappingTDBTraverse, mapping);
|
||||
keys = [mapping allKeys];
|
||||
max = [keys count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
idNbr = [keys objectAtIndex: count];
|
||||
uri = [mapping objectForKey: idNbr];
|
||||
//[self logWithFormat: @"preregistered id '%@' for url '%@'", idNbr, uri];
|
||||
[reverseMapping setObject: idNbr forKey: uri];
|
||||
}
|
||||
|
||||
//[self logWithFormat: @"Complete mapping: %@ \nComplete reverse mapping: %@", mapping, reverseMapping];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -125,52 +184,27 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
- (void) dealloc
|
||||
{
|
||||
[username release];
|
||||
[mapping release];
|
||||
[reverseMapping release];
|
||||
talloc_free (memCtx);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString *) urlFromID: (uint64_t) idNbr
|
||||
{
|
||||
char* url = NULL;
|
||||
enum mapistore_error ret;
|
||||
bool soft_delete = false;
|
||||
|
||||
ret = indexing->get_uri(indexing, [username UTF8String],
|
||||
memCtx, idNbr, &url, &soft_delete);
|
||||
if (ret != MAPISTORE_SUCCESS)
|
||||
return NULL;
|
||||
NSString *res = [[[NSString alloc] initWithUTF8String:url] autorelease];
|
||||
talloc_free(url);
|
||||
|
||||
return res;
|
||||
return [mapping objectForKey: MAPIStoreMappingKeyFromId (idNbr)];
|
||||
}
|
||||
|
||||
- (uint64_t) idFromURL: (NSString *) url
|
||||
{
|
||||
enum mapistore_error ret;
|
||||
id key;
|
||||
uint64_t idNbr;
|
||||
bool softDeleted;
|
||||
|
||||
ret = indexing->get_fmid(indexing, [username UTF8String], [url UTF8String],
|
||||
false, &idNbr, &softDeleted);
|
||||
|
||||
if (ret == MAPISTORE_SUCCESS && !softDeleted)
|
||||
return idNbr;
|
||||
key = [reverseMapping objectForKey: url];
|
||||
if (key)
|
||||
idNbr = [key unsignedLongLongValue];
|
||||
else
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (uint64_t) idFromURL: (NSString *) url
|
||||
isSoftDeleted: (bool *) softDeleted
|
||||
{
|
||||
enum mapistore_error ret;
|
||||
uint64_t idNbr;
|
||||
|
||||
ret = indexing->get_fmid(indexing, [username UTF8String], [url UTF8String],
|
||||
false, &idNbr, softDeleted);
|
||||
|
||||
if (ret != MAPISTORE_SUCCESS)
|
||||
return NSNotFound;
|
||||
idNbr = NSNotFound;
|
||||
|
||||
return idNbr;
|
||||
}
|
||||
@@ -178,34 +212,55 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
- (void) _updateFolderWithURL: (NSString *) oldURL
|
||||
withURL: (NSString *) urlString
|
||||
{
|
||||
const char *searchURL;
|
||||
uint64_t idNbr;
|
||||
bool softDeleted;
|
||||
NSString *current;
|
||||
NSString *newURL;
|
||||
NSArray *allKeys;
|
||||
NSUInteger count, max;
|
||||
NSString *currentKey, *newKey;
|
||||
id idKey;
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if ([oldURL isEqualToString: urlString]) return;
|
||||
[oldURL retain];
|
||||
|
||||
searchURL = [[oldURL stringByAppendingString:@"*"] UTF8String];
|
||||
allKeys = [reverseMapping allKeys];
|
||||
max = [allKeys count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentKey = [allKeys objectAtIndex: count];
|
||||
if ([currentKey hasPrefix: oldURL])
|
||||
{
|
||||
newKey = [currentKey stringByReplacingPrefix: oldURL
|
||||
withPrefix: urlString];
|
||||
|
||||
while (indexing->get_fmid(indexing, [username UTF8String],
|
||||
searchURL,true, &idNbr, &softDeleted) == MAPISTORE_SUCCESS)
|
||||
{
|
||||
// Ignore deleted
|
||||
if (softDeleted) continue;
|
||||
idKey = [reverseMapping objectForKey: currentKey];
|
||||
[mapping setObject: newKey forKey: idKey];
|
||||
[reverseMapping setObject: idKey forKey: newKey];
|
||||
[reverseMapping removeObjectForKey: currentKey];
|
||||
|
||||
current = [self urlFromID:idNbr];
|
||||
newURL = [current stringByReplacingPrefix: oldURL withPrefix: urlString];
|
||||
indexing->update_fmid(indexing, [username UTF8String], idNbr, [newURL UTF8String]);
|
||||
}
|
||||
/* update the record in the indexing database */
|
||||
key.dptr = (unsigned char *) talloc_asprintf (NULL, "0x%.16"PRIx64,
|
||||
(uint64_t) [idKey unsignedLongLongValue]);
|
||||
key.dsize = strlen ((const char *) key.dptr);
|
||||
|
||||
dbuf.dptr = (unsigned char *) talloc_strdup (NULL,
|
||||
[newKey UTF8String]);
|
||||
dbuf.dsize = strlen ((const char *) dbuf.dptr);
|
||||
tdb_store (indexing->tdb, key, dbuf, TDB_MODIFY);
|
||||
talloc_free (key.dptr);
|
||||
talloc_free (dbuf.dptr);
|
||||
}
|
||||
}
|
||||
|
||||
[oldURL release];
|
||||
}
|
||||
|
||||
- (void) updateID: (uint64_t) idNbr
|
||||
withURL: (NSString *) urlString
|
||||
{
|
||||
NSString *oldURL;
|
||||
id idKey;
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
oldURL = [self urlFromID: idNbr];
|
||||
idKey = MAPIStoreMappingKeyFromId (idNbr);
|
||||
oldURL = [mapping objectForKey: idKey];
|
||||
if (oldURL)
|
||||
{
|
||||
if ([oldURL hasSuffix: @"/"]) /* is container ? */
|
||||
@@ -213,79 +268,69 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
if (![urlString hasSuffix: @"/"])
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"a container url must have an ending '/'"];
|
||||
tdb_transaction_start (indexing->tdb);
|
||||
[self _updateFolderWithURL: oldURL withURL: urlString];
|
||||
tdb_transaction_commit (indexing->tdb);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([urlString hasSuffix: @"/"])
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"a leaf url must not have an ending '/'"];
|
||||
[mapping setObject: urlString forKey: idKey];
|
||||
[reverseMapping setObject: idKey forKey: urlString];
|
||||
[reverseMapping removeObjectForKey: oldURL];
|
||||
|
||||
indexing->update_fmid(indexing, [username UTF8String],
|
||||
idNbr, [urlString UTF8String]);
|
||||
/* update the record in the indexing database */
|
||||
key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr);
|
||||
key.dsize = strlen((const char *) key.dptr);
|
||||
|
||||
dbuf.dptr = (unsigned char *) talloc_strdup (NULL, [urlString UTF8String]);
|
||||
dbuf.dsize = strlen((const char *) dbuf.dptr);
|
||||
tdb_store (indexing->tdb, key, dbuf, TDB_MODIFY);
|
||||
talloc_free (key.dptr);
|
||||
talloc_free (dbuf.dptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) updateURL: (NSString *) urlString
|
||||
withID: (uint64_t) idNbr
|
||||
{
|
||||
BOOL rc = NO;
|
||||
uint64_t oldIDNbr;
|
||||
|
||||
oldIDNbr = [self idFromURL: urlString];
|
||||
if (oldIDNbr)
|
||||
{
|
||||
[self logWithFormat: @"Updating URL: %@ with id %.16"PRIx64" from old id %.16"PRIx64,
|
||||
urlString, idNbr, oldIDNbr];
|
||||
[self unregisterURLWithID: oldIDNbr];
|
||||
[self registerURL: urlString
|
||||
withID: idNbr];
|
||||
rc = YES;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) registerURL: (NSString *) urlString
|
||||
withID: (uint64_t) idNbr
|
||||
{
|
||||
NSString *oldURL;
|
||||
uint64_t oldIdNbr;
|
||||
bool softDeleted;
|
||||
id idKey;
|
||||
BOOL rc;
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
oldURL = [self urlFromID: idNbr];
|
||||
if (oldURL != NULL)
|
||||
idKey = MAPIStoreMappingKeyFromId (idNbr);
|
||||
if ([mapping objectForKey: idKey]
|
||||
|| [reverseMapping objectForKey: urlString])
|
||||
{
|
||||
[self errorWithFormat:
|
||||
@"url with idNbr already registered: (oldUrl='%@', newUrl='%@', id=x%.16"PRIx64")",
|
||||
oldURL, urlString, idNbr];
|
||||
return NO;
|
||||
}
|
||||
|
||||
oldIdNbr = [self idFromURL: urlString isSoftDeleted: &softDeleted];
|
||||
if (oldIdNbr != NSNotFound)
|
||||
{
|
||||
if (softDeleted)
|
||||
[self logWithFormat: @"Attempt to register a soft-deleted %@", urlString];
|
||||
else
|
||||
[self errorWithFormat:
|
||||
@"attempt to double register an entry with idNbr ('%@', %lld,"
|
||||
@" 0x%.16"PRIx64", oldid=0x%.16"PRIx64")",
|
||||
urlString, idNbr, idNbr, oldIdNbr];
|
||||
return NO;
|
||||
@"attempt to double register an entry ('%@', %lld,"
|
||||
@" 0x%.16"PRIx64")",
|
||||
urlString, idNbr, idNbr];
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[mapping setObject: urlString forKey: idKey];
|
||||
[reverseMapping setObject: idKey forKey: urlString];
|
||||
rc = YES;
|
||||
// [self logWithFormat: @"registered url '%@' with id %lld (0x%.16"PRIx64")",
|
||||
// urlString, idNbr, idNbr];
|
||||
|
||||
/* Add the record given its fid and mapistore_uri */
|
||||
indexing->add_fmid(indexing, [username UTF8String],
|
||||
idNbr, [urlString UTF8String]);
|
||||
key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr);
|
||||
key.dsize = strlen((const char *) key.dptr);
|
||||
|
||||
dbuf.dptr = (unsigned char *) talloc_strdup(NULL, [urlString UTF8String]);
|
||||
dbuf.dsize = strlen((const char *) dbuf.dptr);
|
||||
tdb_store (indexing->tdb, key, dbuf, TDB_INSERT);
|
||||
talloc_free (key.dptr);
|
||||
talloc_free (dbuf.dptr);
|
||||
}
|
||||
|
||||
return YES;
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (void) registerURLs: (NSArray *) urlStrings
|
||||
@@ -296,6 +341,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
max = [urlStrings count];
|
||||
if (max == [idNbrs count])
|
||||
{
|
||||
tdb_transaction_start (indexing->tdb);
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
newID = [[idNbrs objectAtIndex: count]
|
||||
@@ -303,6 +349,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
[self registerURL: [urlStrings objectAtIndex: count]
|
||||
withID: newID];
|
||||
}
|
||||
tdb_transaction_commit (indexing->tdb);
|
||||
}
|
||||
else
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
@@ -311,17 +358,26 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
|
||||
- (void) unregisterURLWithID: (uint64_t) idNbr
|
||||
{
|
||||
indexing->del_fmid(indexing, [username UTF8String],
|
||||
idNbr, MAPISTORE_PERMANENT_DELETE);
|
||||
}
|
||||
NSString *urlString;
|
||||
id idKey;
|
||||
TDB_DATA key;
|
||||
|
||||
- (void) unregisterURLWithID: (uint64_t) idNbr
|
||||
andFlags: (uint8_t) flags
|
||||
{
|
||||
indexing->del_fmid(indexing, [username UTF8String],
|
||||
idNbr,
|
||||
(flags == MAPISTORE_SOFT_DELETE) ? MAPISTORE_SOFT_DELETE : MAPISTORE_PERMANENT_DELETE);
|
||||
}
|
||||
idKey = MAPIStoreMappingKeyFromId (idNbr);
|
||||
urlString = [mapping objectForKey: idKey];
|
||||
if (urlString)
|
||||
{
|
||||
// [self logWithFormat: @"unregistering url '%@' with id %lld (0x%.16"PRIx64")",
|
||||
// urlString, idNbr, idNbr];
|
||||
[reverseMapping removeObjectForKey: urlString];
|
||||
[mapping removeObjectForKey: idKey];
|
||||
|
||||
/* We hard-delete the entry from the indexing database */
|
||||
key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr);
|
||||
key.dsize = strlen((const char *) key.dptr);
|
||||
|
||||
tdb_delete(indexing->tdb, key);
|
||||
talloc_free(key.dptr);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -63,7 +63,7 @@
|
||||
withAID: (uint32_t) aid;
|
||||
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
|
||||
andRowCount: (uint32_t *) countPtr;
|
||||
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
|
||||
- (int) setReadFlag: (uint8_t) flag;
|
||||
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
|
||||
|
||||
- (NSArray *) activeContainerMessageTables;
|
||||
|
||||
@@ -178,101 +178,56 @@ rtf2html (NSData *compressedRTF)
|
||||
andColumns: (struct SPropTagArray *) columns
|
||||
{
|
||||
NSMutableDictionary *recipientProperties;
|
||||
enum MAPITAGS prop_tag;
|
||||
char *displayName = NULL;
|
||||
char *email = NULL;
|
||||
enum MAPI_OBJTYPE object_type = 0;
|
||||
char *smtpAddress = NULL;
|
||||
SOGoUser *recipientUser;
|
||||
NSUInteger count;
|
||||
NSString *recipientEmail = nil;
|
||||
NSString *recipientFullName = nil;
|
||||
id value;
|
||||
|
||||
recipientProperties = [NSMutableDictionary dictionaryWithCapacity: columns->cValues + 2];
|
||||
|
||||
for (count = 0; count < columns->cValues; count++)
|
||||
{
|
||||
prop_tag = columns->aulPropTag[count];
|
||||
switch(prop_tag) {
|
||||
case PidTagDisplayName:
|
||||
displayName = recipient->data[count];
|
||||
break;
|
||||
case PidTagEmailAddress:
|
||||
email = recipient->data[count];
|
||||
break;
|
||||
case PidTagObjectType:
|
||||
if (recipient->data[count])
|
||||
object_type = *((uint8_t*) recipient->data[count]);
|
||||
break;
|
||||
case PidTagSmtpAddress:
|
||||
smtpAddress = recipient->data[count];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (recipient->data[count])
|
||||
{
|
||||
value = NSObjectFromValuePointer (prop_tag,
|
||||
recipient->data[count]);
|
||||
if (value)
|
||||
[recipientProperties setObject: value
|
||||
forKey: MAPIPropertyKey (prop_tag)];
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient->username)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->username];
|
||||
[recipientProperties setObject: value forKey: @"x500dn"];
|
||||
}
|
||||
|
||||
if (object_type == MAPI_MAILUSER && recipient->username)
|
||||
{
|
||||
/* values from user object have priority over data sent by the client */
|
||||
recipientUser = [SOGoUser userWithLogin: [value lowercaseString]];
|
||||
if (recipientUser)
|
||||
{
|
||||
value = [recipientUser cn];
|
||||
if ([value length] > 0)
|
||||
recipientFullName = value;
|
||||
|
||||
[recipientProperties setObject: value forKey: @"fullName"];
|
||||
value = [[recipientUser allEmails] objectAtIndex: 0];
|
||||
if ([value length] > 0)
|
||||
recipientEmail = value;
|
||||
}
|
||||
[recipientProperties setObject: value forKey: @"email"];
|
||||
}
|
||||
}
|
||||
|
||||
/* If we do not have values from the user object we try to get them from the parameters */
|
||||
if (!recipientFullName && displayName)
|
||||
else
|
||||
{
|
||||
value = [NSString stringWithUTF8String: displayName];
|
||||
if ([value length] > 0)
|
||||
recipientFullName = value;
|
||||
if (recipient->data[0])
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->data[0]];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"fullName"];
|
||||
}
|
||||
if (recipient->data[1])
|
||||
{
|
||||
value = [NSString stringWithUTF8String: recipient->data[1]];
|
||||
if ([value length] > 0)
|
||||
[recipientProperties setObject: value forKey: @"email"];
|
||||
}
|
||||
}
|
||||
|
||||
if (!recipientEmail && email)
|
||||
for (count = 0; count < columns->cValues; count++)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: email];
|
||||
if ([value length] > 0)
|
||||
recipientEmail = value;
|
||||
if (recipient->data[count])
|
||||
{
|
||||
value = NSObjectFromValuePointer (columns->aulPropTag[count],
|
||||
recipient->data[count]);
|
||||
if (value)
|
||||
[recipientProperties setObject: value
|
||||
forKey: MAPIPropertyKey (columns->aulPropTag[count])];
|
||||
}
|
||||
}
|
||||
|
||||
if (!recipientEmail && smtpAddress)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: smtpAddress];
|
||||
if ([value length] > 0)
|
||||
recipientEmail = value;
|
||||
}
|
||||
|
||||
/* Now we can set the properties if we have them */
|
||||
if (recipientFullName)
|
||||
[recipientProperties setObject: recipientFullName forKey: @"fullName"];
|
||||
|
||||
if (recipientEmail)
|
||||
[recipientProperties setObject: recipientEmail forKey: @"email"];
|
||||
|
||||
return recipientProperties;
|
||||
}
|
||||
|
||||
@@ -510,15 +465,16 @@ rtf2html (NSData *compressedRTF)
|
||||
enum mapistore_error rc;
|
||||
NSArray *containerTables;
|
||||
NSUInteger count, max;
|
||||
struct mapistore_object_notification_parameters *notif_parameters;
|
||||
uint64_t folderId;
|
||||
struct mapistore_context *mstoreCtx;
|
||||
MAPIStoreContext *context;
|
||||
SOGoUser *ownerUser;
|
||||
BOOL userIsOwner;
|
||||
MAPIStoreMessage *mainMessage;
|
||||
|
||||
//[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
|
||||
|
||||
containerTables = nil;
|
||||
max = 0;
|
||||
|
||||
context = [self context];
|
||||
ownerUser = [[self userContext] sogoUser];
|
||||
userIsOwner = [[context activeUser] isEqual: ownerUser];
|
||||
@@ -540,6 +496,47 @@ rtf2html (NSData *compressedRTF)
|
||||
/* notifications */
|
||||
if ([container isKindOfClass: MAPIStoreFolderK])
|
||||
{
|
||||
folderId = [(MAPIStoreFolder *) container objectId];
|
||||
mstoreCtx = [[self context] connectionInfo]->mstore_ctx;
|
||||
|
||||
/* folder modified */
|
||||
notif_parameters
|
||||
= talloc_zero(memCtx, struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = folderId;
|
||||
if (isNew)
|
||||
{
|
||||
notif_parameters->tag_count = 3;
|
||||
notif_parameters->tags = talloc_array (notif_parameters,
|
||||
enum MAPITAGS, 3);
|
||||
notif_parameters->tags[0] = PR_CONTENT_COUNT;
|
||||
notif_parameters->tags[1] = PR_MESSAGE_SIZE;
|
||||
notif_parameters->tags[2] = PR_NORMAL_MESSAGE_SIZE;
|
||||
notif_parameters->new_message_count = true;
|
||||
notif_parameters->message_count
|
||||
= [[(MAPIStoreFolder *) container messageKeys] count] + 1;
|
||||
}
|
||||
mapistore_push_notification (mstoreCtx,
|
||||
MAPISTORE_FOLDER,
|
||||
MAPISTORE_OBJECT_MODIFIED,
|
||||
notif_parameters);
|
||||
talloc_free (notif_parameters);
|
||||
|
||||
/* message created */
|
||||
if (isNew)
|
||||
{
|
||||
notif_parameters
|
||||
= talloc_zero(memCtx,
|
||||
struct mapistore_object_notification_parameters);
|
||||
notif_parameters->object_id = [self objectId];
|
||||
notif_parameters->folder_id = folderId;
|
||||
|
||||
notif_parameters->tag_count = 0xffff;
|
||||
mapistore_push_notification (mstoreCtx,
|
||||
MAPISTORE_MESSAGE, MAPISTORE_OBJECT_CREATED,
|
||||
notif_parameters);
|
||||
talloc_free (notif_parameters);
|
||||
}
|
||||
|
||||
/* we ensure the table caches are loaded so that old and new state
|
||||
can be compared */
|
||||
containerTables = [self activeContainerMessageTables];
|
||||
@@ -549,12 +546,11 @@ rtf2html (NSData *compressedRTF)
|
||||
}
|
||||
|
||||
[self save: memCtx];
|
||||
/* We make sure that any change-related properties are removed from the
|
||||
/* We make sure that any change-related properties are removes from the
|
||||
properties dictionary, to make sure that related methods will be
|
||||
invoked the next time they are requested. */
|
||||
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)];
|
||||
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)];
|
||||
[properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
|
||||
|
||||
if ([container isKindOfClass: MAPIStoreFolderK])
|
||||
{
|
||||
@@ -786,7 +782,7 @@ rtf2html (NSData *compressedRTF)
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagSensitivity: (void **) data
|
||||
- (int) getPidTagSensitivity: (void **) data // TODO -> subclass in calendar
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
@@ -799,7 +795,7 @@ rtf2html (NSData *compressedRTF)
|
||||
TALLOC_CTX *localMemCtx;
|
||||
char *prefix, *normalizedSubject;
|
||||
|
||||
localMemCtx = talloc_zero (memCtx, TALLOC_CTX);
|
||||
localMemCtx = talloc_zero (NULL, TALLOC_CTX);
|
||||
if ([self getProperty: (void **) &prefix
|
||||
withTag: PidTagSubjectPrefix
|
||||
inMemCtx: localMemCtx]
|
||||
@@ -811,8 +807,6 @@ rtf2html (NSData *compressedRTF)
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*data = talloc_asprintf (memCtx, "%s%s", prefix, normalizedSubject);
|
||||
|
||||
talloc_free(localMemCtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -919,7 +913,7 @@ rtf2html (NSData *compressedRTF)
|
||||
return [self getNo: data inMemCtx: memCtx];;
|
||||
}
|
||||
|
||||
- (enum mapistore_error) setReadFlag: (uint8_t) flag
|
||||
- (int) setReadFlag: (uint8_t) flag
|
||||
{
|
||||
// [self subclassResponsibility: _cmd];
|
||||
|
||||
|
||||
@@ -87,7 +87,58 @@
|
||||
|
||||
- (void) notifyChangesForChild: (MAPIStoreMessage *) child
|
||||
{
|
||||
NSUInteger currentChildRow, newChildRow;
|
||||
NSArray *list;
|
||||
NSString *childName;
|
||||
struct mapistore_table_notification_parameters notif_parameters;
|
||||
struct mapistore_context *mstoreCtx;
|
||||
|
||||
mstoreCtx = [[(MAPIStoreFolder *) container context]
|
||||
connectionInfo]->mstore_ctx;
|
||||
|
||||
notif_parameters.table_type = tableType;
|
||||
notif_parameters.handle = handleId;
|
||||
notif_parameters.folder_id = [(MAPIStoreFolder *) container objectId];
|
||||
notif_parameters.object_id = [child objectId];
|
||||
notif_parameters.instance_id = 0; /* TODO: always 0 ? */
|
||||
|
||||
childName = [child nameInContainer];
|
||||
list = [self restrictedChildKeys];
|
||||
currentChildRow = [list indexOfObject: childName];
|
||||
notif_parameters.row_id = currentChildRow;
|
||||
|
||||
[self cleanupCaches];
|
||||
list = [self restrictedChildKeys];
|
||||
newChildRow = [list indexOfObject: childName];
|
||||
|
||||
if (currentChildRow == NSNotFound)
|
||||
{
|
||||
if (newChildRow != NSNotFound)
|
||||
{
|
||||
notif_parameters.row_id = newChildRow;
|
||||
mapistore_push_notification (mstoreCtx,
|
||||
MAPISTORE_TABLE,
|
||||
MAPISTORE_OBJECT_CREATED,
|
||||
¬if_parameters);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newChildRow == NSNotFound)
|
||||
mapistore_push_notification (mstoreCtx,
|
||||
MAPISTORE_TABLE,
|
||||
MAPISTORE_OBJECT_DELETED,
|
||||
¬if_parameters);
|
||||
else
|
||||
{
|
||||
/* the fact that the row order has changed has no impact here */
|
||||
notif_parameters.row_id = newChildRow;
|
||||
mapistore_push_notification (mstoreCtx,
|
||||
MAPISTORE_TABLE,
|
||||
MAPISTORE_OBJECT_MODIFIED,
|
||||
¬if_parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -38,15 +38,14 @@
|
||||
}
|
||||
|
||||
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
|
||||
withIndexing: (struct indexing_context *) indexing
|
||||
withTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *context;
|
||||
|
||||
context = talloc_zero(memCtx, struct mapistore_contexts_list);
|
||||
context->url = talloc_asprintf (context, "sogo://%s@notes/",
|
||||
[[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"] UTF8String]);
|
||||
[userName UTF8String]);
|
||||
// context->name = "Notes personnelles";
|
||||
context->main_folder = true;
|
||||
context->role = MAPISTORE_NOTES_ROLE;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
@@ -244,16 +245,17 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
||||
struct SPropValue *cValue;
|
||||
NSUInteger counter;
|
||||
NSMutableDictionary *newProperties;
|
||||
NSTimeZone *tz;
|
||||
NSInteger tzOffset;
|
||||
id value;
|
||||
|
||||
tz = nil;
|
||||
|
||||
newProperties = [NSMutableDictionary dictionaryWithCapacity: aRow->cValues];
|
||||
for (counter = 0; counter < aRow->cValues; counter++)
|
||||
{
|
||||
cValue = aRow->lpProps + counter;
|
||||
value = NSObjectFromSPropValue (cValue);
|
||||
if (value == nil)
|
||||
continue;
|
||||
|
||||
switch (cValue->ulPropTag & 0xffff)
|
||||
{
|
||||
case PT_STRING8:
|
||||
@@ -262,6 +264,16 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
||||
@"attempting to set string property as PR_STRING8: %.8x",
|
||||
cValue->ulPropTag];
|
||||
break;
|
||||
case PT_SYSTIME:
|
||||
if (!tz)
|
||||
{
|
||||
tz = [[self userContext] timeZone];
|
||||
tzOffset = -[tz secondsFromGMT];
|
||||
}
|
||||
value = [value addYear: 0 month: 0 day: 0
|
||||
hour: 0 minute: 0 second: tzOffset];
|
||||
[value setTimeZone: tz];
|
||||
break;
|
||||
}
|
||||
[newProperties setObject: value
|
||||
forKey: MAPIPropertyKey (cValue->ulPropTag)];
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
@@ -43,10 +41,7 @@
|
||||
@interface iCalCalendar (MAPIStoreRecurrence)
|
||||
|
||||
- (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity
|
||||
fromRecurrencePattern: (struct RecurrencePattern *) rp
|
||||
withExceptions: (struct ExceptionInfo *) exInfos
|
||||
andExceptionCount: (uint16_t) exInfoCount
|
||||
inTimeZone: (NSTimeZone *) tz;
|
||||
fromRecurrencePattern: (struct RecurrencePattern *) rp;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
@@ -49,10 +48,6 @@
|
||||
|
||||
- (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity
|
||||
fromRecurrencePattern: (struct RecurrencePattern *) rp
|
||||
withExceptions: (struct ExceptionInfo *) exInfos
|
||||
andExceptionCount: (uint16_t) exInfoCount
|
||||
inTimeZone: (NSTimeZone *) tz
|
||||
|
||||
{
|
||||
NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate;
|
||||
NSString *monthDay, *month;
|
||||
@@ -219,42 +214,34 @@
|
||||
|
||||
/* exception dates:
|
||||
- take all deleted instances
|
||||
- remove all modified instances available in ExceptionInfo from the above set
|
||||
- remove all modified instances from the above set
|
||||
- add remaining instances, in chronological order
|
||||
*/
|
||||
exceptionDates = [NSMutableSet set];
|
||||
for (count = 0; count < rp->DeletedInstanceCount; count++)
|
||||
{
|
||||
exDate
|
||||
= [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]];
|
||||
exDate = [exDate hour: [startDate hourOfDay]
|
||||
minute: [startDate minuteOfHour]
|
||||
second: [startDate secondOfMinute]];
|
||||
[exceptionDates addObject: exDate];
|
||||
}
|
||||
for (count = 0; count < rp->ModifiedInstanceCount; count++)
|
||||
{
|
||||
exDate
|
||||
= [NSDate dateFromMinutesSince1601: rp->ModifiedInstanceDates[count]];
|
||||
exDate = [exDate hour: [startDate hourOfDay]
|
||||
minute: [startDate minuteOfHour]
|
||||
second: [startDate secondOfMinute]];
|
||||
[exceptionDates removeObject: exDate];
|
||||
}
|
||||
|
||||
/* Heuristic to avoid these loops */
|
||||
if (rp->DeletedInstanceCount != rp->ModifiedInstanceCount) {
|
||||
exceptionDates = [NSMutableSet set];
|
||||
for (count = 0; count < rp->DeletedInstanceCount; count++)
|
||||
{
|
||||
exDate
|
||||
= [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]];
|
||||
exDate = [exDate hour: [startDate hourOfDay]
|
||||
minute: [startDate minuteOfHour]
|
||||
second: [startDate secondOfMinute]];
|
||||
[exceptionDates addObject: exDate];
|
||||
}
|
||||
/* Read the exceptions to remove the instances that are modified and not deleted */
|
||||
if (exInfos && exInfoCount > 0)
|
||||
{
|
||||
for (count = 0; count < exInfoCount; count++)
|
||||
{
|
||||
/* The OriginalStartDate is in local time */
|
||||
exDate = [NSDate dateFromMinutesSince1601: exInfos[count].OriginalStartDate];
|
||||
exDate = [exDate dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: - [tz secondsFromGMT]];
|
||||
[exceptionDates removeObject: exDate];
|
||||
}
|
||||
}
|
||||
|
||||
realExDates = [[exceptionDates allObjects]
|
||||
sortedArrayUsingSelector: @selector (compare:)];
|
||||
max = [realExDates count];
|
||||
for (count = 0; count < max; count++)
|
||||
[entity addToExceptionDates: [realExDates objectAtIndex: count]];
|
||||
}
|
||||
realExDates = [[exceptionDates allObjects]
|
||||
sortedArrayUsingSelector: @selector (compare:)];
|
||||
max = [realExDates count];
|
||||
for (count = 0; count < max; count++)
|
||||
[entity addToExceptionDates: [realExDates objectAtIndex: count]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -325,25 +312,15 @@
|
||||
rp->RecurFrequency = RecurFrequency_Weekly;
|
||||
rp->PatternType = PatternType_Week;
|
||||
rp->Period = repeatInterval;
|
||||
|
||||
dayOfWeek = [startDate dayOfWeek];
|
||||
|
||||
mask = 0;
|
||||
byDayMask = [self byDayMask];
|
||||
if (byDayMask)
|
||||
{
|
||||
for (count = 0; count < 7; count++)
|
||||
if ([byDayMask occursOnDay: count])
|
||||
mask |= 1 << count;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the recurrence pattern using start date */
|
||||
mask |= 1 << dayOfWeek;
|
||||
}
|
||||
for (count = 0; count < 7; count++)
|
||||
if ([byDayMask occursOnDay: count])
|
||||
mask |= 1 << count;
|
||||
rp->PatternTypeSpecific.WeekRecurrencePattern = mask;
|
||||
|
||||
/* FirstDateTime */
|
||||
dayOfWeek = [startDate dayOfWeek];
|
||||
if (dayOfWeek)
|
||||
beginOfWeek = [startDate dateByAddingYears: 0 months: 0
|
||||
days: -dayOfWeek
|
||||
|
||||
+192
-342
File diff suppressed because it is too large.
Load diff
@@ -96,11 +96,8 @@ MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username)
|
||||
[entryId appendUInt8: 0]; // end of string
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@"Error trying to generate EntryId for `%@`", username);
|
||||
entryId = nil;
|
||||
}
|
||||
|
||||
entryId = nil;
|
||||
|
||||
return entryId;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/* MAPIStoreSharingMessage.h - this file is part of SOGo-OpenChange
|
||||
*
|
||||
* Copyright (C) 2015 Enrique J. Hernández
|
||||
*
|
||||
* Author: Enrique J. Hernández <ejhernandez@zentyal.com>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef MAPISTORESHARINGMESSAGE_H
|
||||
#define MAPISTORESHARINGMESSAGE_H
|
||||
|
||||
#import "MAPIStoreMailMessage.h"
|
||||
#import "MAPIStoreObjectProxy.h"
|
||||
|
||||
#define SHARING_SPECIAL_FOLDER 0x40290
|
||||
|
||||
@interface MAPIStoreSharingMessage : MAPIStoreObjectProxy
|
||||
{
|
||||
struct mapistore_connection_info *connInfo;
|
||||
NSMutableDictionary *properties;
|
||||
}
|
||||
|
||||
- (id) initWithMailHeaders: (NSDictionary *) mailHeaders
|
||||
andConnectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
fromMessage: (MAPIStoreMailMessage *) msg;
|
||||
|
||||
/* getters */
|
||||
- (int) getPidLidSharingCapabilities: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingCapabilities: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingConfigurationUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingConfigUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingFlavor: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingFlavor: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingInitiatorEntryId: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingInitiatorName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingInitiatorSmtp: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingLocalType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingLocalType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingProviderGuid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingProviderGuid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingProviderName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingProviderName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingProviderUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingProviderUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingRemoteName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingRemoteName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingRemoteStoreUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameXSharingRemoteStoreUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingRemoteType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidTypeXSharingRemoteType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingResponseTime: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidLidSharingResponseType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
- (int) getPidNameContentClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx;
|
||||
|
||||
/* Save */
|
||||
- (void) saveWithMessage: (MAPIStoreMailMessage *) msg
|
||||
andSOGoObject: (SOGoMailObject *) sogoObject;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MAPISTORECALENDARWRAPPER_H */
|
||||
@@ -1,463 +0,0 @@
|
||||
/* MAPIStoreSharingMessage.m - this file is part of SOGo-OpenChange
|
||||
*
|
||||
* Copyright (C) 2015 Enrique J. Hernández
|
||||
*
|
||||
* Author: Enrique J. Hernández <ejhernandez@zentyal.com>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSDate+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "NSValue+MAPIStore.h"
|
||||
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
#import "MAPIStoreSharingMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
|
||||
#include <gen_ndr/property.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
#include <mapistore/mapistore_nameid.h>
|
||||
|
||||
@implementation MAPIStoreSharingMessage
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
connInfo = NULL;
|
||||
properties = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithMailHeaders: (NSDictionary *) mailHeaders
|
||||
andConnectionInfo: (struct mapistore_connection_info *) newConnInfo
|
||||
fromMessage: (MAPIStoreMailMessage *) msg
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
NSString *key;
|
||||
|
||||
if ((self = [self init]))
|
||||
{
|
||||
connInfo = newConnInfo;
|
||||
enumerator = [mailHeaders keyEnumerator];
|
||||
properties = [NSMutableDictionary new];
|
||||
while ((key = [enumerator nextObject]))
|
||||
{
|
||||
if ([key hasPrefix: @"x-ms-sharing-"])
|
||||
[properties setObject: [mailHeaders objectForKey: key]
|
||||
forKey: key];
|
||||
}
|
||||
|
||||
/* Set request properties from container folder */
|
||||
NSDictionary *requestProps = [(MAPIStoreMailFolder *)[msg container]
|
||||
extraPropertiesForMessage: [msg nameInContainer]];
|
||||
if (requestProps)
|
||||
[properties addEntriesFromDictionary: requestProps];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[properties release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (enum mapistore_error) _getStringProperty: (NSString *) propertyName
|
||||
inData: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: propertyName];
|
||||
if (value)
|
||||
{
|
||||
*data = [value asUnicodeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingCapabilities: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: @"x-ms-sharing-capabilities"];
|
||||
if (value)
|
||||
{
|
||||
*data = MAPILongValue (memCtx, [value intValue]);
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingCapabilities: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: @"x-ms-sharing-capabilities"];
|
||||
if (value)
|
||||
{
|
||||
*data = [[NSString stringWithFormat:@"%X", [value intValue]]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingConfigurationUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
*data = [@"" asUnicodeInMemCtx: memCtx];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingConfigUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingConfigurationUrl: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingFlavor: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* See [MS-OXSHARE] Section 2.2.2.5 for details */
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value, auxValue;
|
||||
|
||||
value = [properties objectForKey: @"x-ms-sharing-flavor"];
|
||||
if (value)
|
||||
{
|
||||
*data = MAPILongValue (memCtx, [value intValue]);
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Guess its value required by old clients based on other properties */
|
||||
value = [properties objectForKey: @"x-ms-sharing-capabilities"];
|
||||
if (value)
|
||||
{
|
||||
if ([value intValue] == SHARING_SPECIAL_FOLDER)
|
||||
{
|
||||
value = [properties objectForKey: @"x-ms-sharing-responsetime"];
|
||||
auxValue = [properties objectForKey: @"x-ms-sharing-remoteuid"];
|
||||
if (value) /* A sharing request */
|
||||
{
|
||||
if (auxValue)
|
||||
*data = MAPILongValue (memCtx, SHARING_INVITATION_REQUEST_FOLDER);
|
||||
else
|
||||
*data = MAPILongValue (memCtx, SHARING_REQUEST_SPECIAL_FOLDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auxValue) /* It SHOULD be an invitation or response */
|
||||
*data = MAPILongValue (memCtx, SHARING_INVITATION_SPECIAL_FOLDER);
|
||||
else /* No remote info, then denial */
|
||||
*data = MAPILongValue (memCtx, SHARING_DENY_REQUEST);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*data = MAPILongValue (memCtx, SHARING_INVITATION_FOLDER);
|
||||
}
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingFlavor: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: @"x-ms-sharing-flavor"];
|
||||
if (value)
|
||||
{
|
||||
*data = [[NSString stringWithFormat:@"%X", [value intValue]]
|
||||
asUnicodeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingInitiatorEntryId: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
NSData *entryId;
|
||||
|
||||
value = [properties objectForKey: @"x-ms-sharing-initiatorentryid"];
|
||||
if (value)
|
||||
{
|
||||
entryId = [value dataByDecodingBase64];
|
||||
if (entryId)
|
||||
{
|
||||
*data = [entryId asBinaryInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingInitiatorName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-initiatorname"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingInitiatorSmtp: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-initiatorsmtp"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingLocalType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-localtype"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingLocalType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingLocalType: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingProviderGuid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
const unsigned char providerGuidBytes[] = {0xAE, 0xF0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
|
||||
NSData *providerGuid = [NSData dataWithBytes: providerGuidBytes length: 16];
|
||||
*data = [providerGuid asBinaryInMemCtx: memCtx];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingProviderGuid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"AEF0060000000000C000000000000046" asUnicodeInMemCtx: memCtx];
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingProviderName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
return [self _getStringProperty: @"x-ms-sharing-providername"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingProviderName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingProviderName: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingProviderUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-providerurl"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingProviderUrl: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingProviderUrl: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingRemoteName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-remotename"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingRemoteName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingRemoteName: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingRemoteStoreUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-remotestoreuid"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidNameXSharingRemoteStoreUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingRemoteStoreUid: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingRemoteType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-remotetype"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTypeXSharingRemoteType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingRemoteType: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingRemoteUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self _getStringProperty: @"x-ms-sharing-remoteuid"
|
||||
inData: data
|
||||
inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidUidXSharingRemoteUid: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getPidLidSharingRemoteUid: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingResponseTime: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)];
|
||||
if (!value)
|
||||
{
|
||||
value = [properties objectForKey: @"x-ms-sharing-responsetime"];
|
||||
if (value)
|
||||
{
|
||||
/* Here the value is a GSCBufferString */
|
||||
NSString * responseTime = [NSString stringWithUTF8String: [value UTF8String]];
|
||||
value = [NSDate dateWithString: responseTime];
|
||||
}
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
*data = [value asFileTimeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidLidSharingResponseType: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
|
||||
enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
id value;
|
||||
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidLidSharingResponseType)];
|
||||
if (!value)
|
||||
value = [properties objectForKey: @"x-ms-sharing-responsetype"];
|
||||
|
||||
if (value)
|
||||
{
|
||||
*data = MAPILongValue (memCtx, [value intValue]);
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (int) getPidNameContentClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = talloc_strdup (memCtx, "Sharing");
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (void) saveWithMessage: (MAPIStoreMailMessage *) msg
|
||||
andSOGoObject: (SOGoMailObject *) sogoObject
|
||||
{
|
||||
/* Store PidLidSharingResponseType and PidLidSharingResponseTime if
|
||||
required in versions message from the container as I don't see
|
||||
other straightforward place to put that information */
|
||||
id response;
|
||||
NSDictionary *propsToStore;
|
||||
|
||||
response = [[msg properties] objectForKey: MAPIPropertyKey (PidLidSharingResponseType)];
|
||||
if (response)
|
||||
{
|
||||
/* FIXME: Is there any better way to increase the modseq? */
|
||||
[sogoObject addFlags: @"\\Draft"];
|
||||
[sogoObject removeFlags: @"\\Draft"];
|
||||
|
||||
/* Store this modification in container folder along the property values */
|
||||
propsToStore = [NSDictionary dictionaryWithObjects:
|
||||
[NSArray arrayWithObjects: response,
|
||||
[[msg properties] objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)],
|
||||
nil]
|
||||
forKeys:
|
||||
[NSArray arrayWithObjects: MAPIPropertyKey (PidLidSharingResponseType),
|
||||
MAPIPropertyKey (PidLidSharingResponseTime), nil]];
|
||||
|
||||
[(MAPIStoreMailFolder *)[msg container] setExtraProperties: propsToStore
|
||||
forMessage: [msg nameInContainer]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -31,7 +31,6 @@
|
||||
#import <Appointments/SOGoTaskObject.h>
|
||||
|
||||
#import "MAPIApplication.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import "MAPIStoreTasksContext.h"
|
||||
#import "MAPIStoreTasksMessage.h"
|
||||
#import "MAPIStoreTasksMessageTable.h"
|
||||
@@ -62,7 +61,6 @@
|
||||
SOGoTaskObject *newEntry;
|
||||
NSString *name;
|
||||
|
||||
[[[self context] userContext] activate];
|
||||
name = [NSString stringWithFormat: @"%@.ics",
|
||||
[SOGoObject globallyUniqueObjectId]];
|
||||
newEntry = [SOGoTaskObject objectWithName: name
|
||||
@@ -71,7 +69,7 @@
|
||||
newMessage = [MAPIStoreTasksMessage mapiStoreObjectWithSOGoObject: newEntry
|
||||
inContainer: self];
|
||||
|
||||
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@@ -118,7 +116,7 @@
|
||||
rights |= RightsReadItems;
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
|
||||
@@ -367,15 +367,13 @@
|
||||
NSString *status, *priority, *tzName;
|
||||
NSCalendarDate *now;
|
||||
NSInteger tzOffset;
|
||||
NSTimeZone *userTZ;
|
||||
double doubleValue;
|
||||
|
||||
vToDo = [sogoObject component: YES secure: NO];
|
||||
vCalendar = [vToDo parent];
|
||||
[vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"];
|
||||
|
||||
userTZ = [[self userContext] timeZone];
|
||||
tzName = [userTZ name];
|
||||
tzName = [[[self userContext] timeZone] name];
|
||||
tz = [iCalTimeZone timeZoneForName: tzName];
|
||||
[vCalendar addTimeZone: tz];
|
||||
|
||||
@@ -430,13 +428,6 @@
|
||||
{
|
||||
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"];
|
||||
[date setTimeZone: tz];
|
||||
/* The property is set to user's local time zone.
|
||||
See: [MS-OXOTASK] 2.2.2.2.4
|
||||
TODO: Ignore when the PT_SYSTIME is 0x5AE980E0*/
|
||||
tzOffset = [userTZ secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -tzOffset];
|
||||
[date setDateTime: value];
|
||||
}
|
||||
|
||||
@@ -446,13 +437,6 @@
|
||||
{
|
||||
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"];
|
||||
[date setTimeZone: tz];
|
||||
/* The property is set to user's local time zone.
|
||||
See: [MS-OXOTASK] 2.2.2.2.5
|
||||
TODO: Ignore when the PT_SYSTIME is 0x5AE980E0*/
|
||||
tzOffset = [userTZ secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -tzOffset];
|
||||
[date setDateTime: value];
|
||||
}
|
||||
|
||||
@@ -461,9 +445,7 @@
|
||||
if (value)
|
||||
{
|
||||
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"];
|
||||
/* The property is set to midnight in local time zone converted to UTC:
|
||||
See: [MS-OXOTASK] 2.2.2.2.9 */
|
||||
tzOffset = [userTZ secondsFromGMTForDate: value];
|
||||
tzOffset = [[value timeZone] secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -tzOffset];
|
||||
@@ -531,7 +513,7 @@
|
||||
}
|
||||
[vToDo setTimeStampAsDate: now];
|
||||
|
||||
[sogoObject saveCalendar: vCalendar];
|
||||
[sogoObject saveComponent: vCalendar];
|
||||
|
||||
[self updateVersions];
|
||||
}
|
||||
|
||||
@@ -157,9 +157,6 @@ static Class MAPIStoreTasksMessageK = Nil;
|
||||
forKey: MAPIPropertyKey (PidLidTaskDueDate)];
|
||||
[knownProperties setObject: @"c_creationdate"
|
||||
forKey: MAPIPropertyKey (PidLidTaskOrdinal)];
|
||||
/* Use by oxcfxics to sort the latest first */
|
||||
[knownProperties setObject: @"c_lastmodified"
|
||||
forKey: MAPIPropertyKey (PidTagMessageDeliveryTime)];
|
||||
}
|
||||
|
||||
return [knownProperties objectForKey: MAPIPropertyKey (property)];
|
||||
|
||||
@@ -119,7 +119,6 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value)
|
||||
break;
|
||||
case PT_SYSTIME:
|
||||
result = [NSCalendarDate dateFromFileTime: &(value->value.ft)];
|
||||
[result setTimeZone: utcTZ];
|
||||
break;
|
||||
case PT_BINARY:
|
||||
case PT_SVREID:
|
||||
@@ -155,7 +154,8 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value)
|
||||
// #define PT_I8 0x14
|
||||
// #define PT_SRESTRICT 0xFD
|
||||
// #define PT_ACTIONS 0xFE
|
||||
result = nil;
|
||||
result = [NSNull null];
|
||||
abort();
|
||||
NSLog (@"%s: object type not handled: %d (0x%.4x)",
|
||||
__PRETTY_FUNCTION__, valueType, valueType);
|
||||
}
|
||||
@@ -250,7 +250,8 @@ NSObjectFromSPropValue (const struct SPropValue *value)
|
||||
// #define PT_I8 0x14
|
||||
// #define PT_SRESTRICT 0xFD
|
||||
// #define PT_ACTIONS 0xFE
|
||||
result = nil;
|
||||
result = [NSNull null];
|
||||
abort();
|
||||
NSLog (@"%s: object type not handled: %d (0x%.4x)",
|
||||
__PRETTY_FUNCTION__, valueType, valueType);
|
||||
}
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
}
|
||||
|
||||
+ (id) userContextWithUsername: (NSString *) username
|
||||
andTDBIndexing: (struct indexing_context *) indexing;
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb;
|
||||
|
||||
- (id) initWithUsername: (NSString *) newUsername
|
||||
andTDBIndexing: (struct indexing_context *) indexing;
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb;
|
||||
|
||||
- (NSString *) username;
|
||||
- (SOGoUser *) sogoUser;
|
||||
@@ -86,8 +86,6 @@
|
||||
|
||||
/* SOGo hacky magic */
|
||||
- (void) activateWithUser: (SOGoUser *) activeUser;
|
||||
- (void) activate;
|
||||
- (void) deactivate;
|
||||
- (MAPIStoreAuthenticator *) authenticator;
|
||||
- (WOContext *) woContext;
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserFolder.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoMailAccounts.h>
|
||||
|
||||
@@ -61,7 +60,7 @@ static NSMapTable *contextsTable = nil;
|
||||
}
|
||||
|
||||
+ (id) userContextWithUsername: (NSString *) username
|
||||
andTDBIndexing: (struct indexing_context *) indexing;
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb;
|
||||
{
|
||||
id userContext;
|
||||
|
||||
@@ -69,7 +68,7 @@ static NSMapTable *contextsTable = nil;
|
||||
if (!userContext)
|
||||
{
|
||||
userContext = [[self alloc] initWithUsername: username
|
||||
andTDBIndexing: indexing];
|
||||
andTDBIndexing: indexingTdb];
|
||||
[userContext autorelease];
|
||||
[contextsTable setObject: userContext forKey: username];
|
||||
}
|
||||
@@ -101,12 +100,16 @@ static NSMapTable *contextsTable = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *) _readPasswordFile: (NSString *) path
|
||||
- (NSString *) _readUserPassword: (NSString *) newUsername
|
||||
{
|
||||
NSString *password;
|
||||
NSString *password, *path;
|
||||
NSData *content;
|
||||
|
||||
|
||||
password = nil;
|
||||
|
||||
path = [NSString stringWithFormat: SAMBA_PRIVATE_DIR
|
||||
@"/mapistore/%@/password", newUsername];
|
||||
|
||||
content = [NSData dataWithContentsOfFile: path];
|
||||
|
||||
if (content)
|
||||
@@ -121,26 +124,8 @@ static NSMapTable *contextsTable = nil;
|
||||
return password;
|
||||
}
|
||||
|
||||
- (NSString *) _readUserPassword: (NSString *) newUsername
|
||||
{
|
||||
NSString *password, *path;
|
||||
|
||||
path = [NSString stringWithFormat: SAMBA_PRIVATE_DIR
|
||||
@"/mapistore/%@/password", newUsername];
|
||||
|
||||
password = [self _readPasswordFile: path];
|
||||
if (password == nil)
|
||||
{
|
||||
// Try to get master password
|
||||
path = [NSString stringWithFormat: SAMBA_PRIVATE_DIR @"/mapistore/master.password"];
|
||||
password = [self _readPasswordFile: path];
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
- (id) initWithUsername: (NSString *) newUsername
|
||||
andTDBIndexing: (struct indexing_context *) indexing
|
||||
andTDBIndexing: (struct tdb_wrap *) indexingTdb
|
||||
{
|
||||
NSString *userPassword;
|
||||
|
||||
@@ -148,9 +133,9 @@ static NSMapTable *contextsTable = nil;
|
||||
{
|
||||
/* "username" will be retained by table */
|
||||
username = newUsername;
|
||||
if (indexing)
|
||||
if (indexingTdb)
|
||||
ASSIGN (mapping, [MAPIStoreMapping mappingForUsername: username
|
||||
withIndexing: indexing]);
|
||||
withIndexing: indexingTdb]);
|
||||
|
||||
authenticator = [MAPIStoreAuthenticator new];
|
||||
[authenticator setUsername: username];
|
||||
@@ -159,8 +144,6 @@ static NSMapTable *contextsTable = nil;
|
||||
if ([userPassword length] == 0)
|
||||
userPassword = username;
|
||||
[authenticator setPassword: userPassword];
|
||||
// Activate the profile on initialization
|
||||
[self activate];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -217,7 +200,7 @@ static NSMapTable *contextsTable = nil;
|
||||
if (!userFolder)
|
||||
{
|
||||
userFolder = [SOGoUserFolder objectWithName: username
|
||||
inContainer: nil];
|
||||
inContainer: MAPIApp];
|
||||
[userFolder retain];
|
||||
}
|
||||
|
||||
@@ -235,8 +218,7 @@ static NSMapTable *contextsTable = nil;
|
||||
if (!rootFolders)
|
||||
{
|
||||
rootFolders = [NSMutableDictionary new];
|
||||
[self activate];
|
||||
[self userFolder]; // force lazy initialization
|
||||
[self userFolder];
|
||||
[woContext setClientObject: userFolder];
|
||||
|
||||
/* Calendar */
|
||||
@@ -261,10 +243,10 @@ static NSMapTable *contextsTable = nil;
|
||||
acquire: NO];
|
||||
[containersBag addObject: accountsFolder];
|
||||
[woContext setClientObject: accountsFolder];
|
||||
|
||||
currentFolder = [accountsFolder lookupName: @"0"
|
||||
inContext: woContext
|
||||
acquire: NO];
|
||||
|
||||
[rootFolders setObject: currentFolder
|
||||
forKey: @"mail"];
|
||||
connection = [currentFolder imap4Connection];
|
||||
@@ -272,7 +254,8 @@ static NSMapTable *contextsTable = nil;
|
||||
|
||||
/* ensure the folder cache is filled */
|
||||
[currentFolder toManyRelationshipKeysWithNamespaces: YES];
|
||||
hierarchy = [connection cachedHierarchyResultsForURL: [currentFolder imap4URL]];
|
||||
hierarchy = [connection
|
||||
cachedHierarchyResultsForURL: [currentFolder imap4URL]];
|
||||
flags = [[hierarchy objectForKey: @"list"] objectForKey: @"/INBOX"];
|
||||
inboxHasNoInferiors = [flags containsObject: @"noinferiors"];
|
||||
}
|
||||
@@ -314,9 +297,9 @@ static NSMapTable *contextsTable = nil;
|
||||
parts in this url. We strip the '-' character in case we have
|
||||
this in the domain part - like foo@bar-zot.com */
|
||||
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
|
||||
[[[user login] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[[[user loginInDomain] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
|
||||
@@ -341,7 +324,7 @@ static NSMapTable *contextsTable = nil;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: folderTableURL];
|
||||
|
||||
|
||||
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
|
||||
tableName = [[folderTableURL path] lastPathComponent];
|
||||
if ([channel evaluateExpressionX:
|
||||
@@ -358,7 +341,7 @@ static NSMapTable *contextsTable = nil;
|
||||
[channel cancelFetch];
|
||||
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
|
||||
/* SOGo context objects */
|
||||
@@ -372,11 +355,6 @@ static NSMapTable *contextsTable = nil;
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
- (void) activate
|
||||
{
|
||||
[self activateWithUser: [self sogoUser]];
|
||||
}
|
||||
|
||||
- (void) activateWithUser: (SOGoUser *) activeUser;
|
||||
{
|
||||
NSMutableDictionary *info;
|
||||
@@ -387,24 +365,4 @@ static NSMapTable *contextsTable = nil;
|
||||
[info setObject: woContext forKey: @"WOContext"];
|
||||
}
|
||||
|
||||
- (void) deactivate
|
||||
{
|
||||
NSMutableDictionary *info;
|
||||
|
||||
if (self == [MAPIApp userContext])
|
||||
[MAPIApp setUserContext: nil];
|
||||
else
|
||||
[self errorWithFormat: @"Error: Tried to deactivate an user context "
|
||||
@"not enabled (%@ vs %@)",
|
||||
[self username], [[MAPIApp userContext] username]];
|
||||
|
||||
info = [[NSThread currentThread] threadDictionary];
|
||||
if (woContext == [info objectForKey: @"WOContext"])
|
||||
[info removeObjectForKey: @"WOContext"];
|
||||
else
|
||||
[self errorWithFormat: @"Error: Tried to deactivate a WOContext "
|
||||
@"not enabled (%@ vs %@)",
|
||||
woContext, [info objectForKey: @"WOContext"]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -41,8 +41,6 @@
|
||||
|
||||
+ (id) dataWithXID: (const struct XID *) xid;
|
||||
- (struct XID *) asXIDInMemCtx: (void *) memCtx;
|
||||
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
|
||||
with: (uint32_t *) length;
|
||||
|
||||
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
|
||||
andCnt: (NSData *) globCnt;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
||||
@@ -30,7 +29,6 @@
|
||||
|
||||
#undef DEBUG
|
||||
#include <stdbool.h>
|
||||
#include <libmapi/libmapi.h>
|
||||
#include <talloc.h>
|
||||
#include <util/time.h>
|
||||
#include <gen_ndr/exchange.h>
|
||||
@@ -138,11 +136,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
|
||||
NSMutableData *xidData;
|
||||
struct FlatUID_r flatUID;
|
||||
|
||||
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
|
||||
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
|
||||
|
||||
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
|
||||
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
|
||||
[xidData appendBytes: flatUID.ab length: 16];
|
||||
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
|
||||
[xidData appendBytes: xid->Data length: xid->Size];
|
||||
|
||||
return xidData;
|
||||
}
|
||||
@@ -158,12 +156,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
|
||||
{
|
||||
xid = talloc_zero (memCtx, struct XID);
|
||||
|
||||
[self _extractGUID: &xid->NameSpaceGuid];
|
||||
[self _extractGUID: &xid->GUID];
|
||||
|
||||
xid->LocalId.length = max - 16;
|
||||
xid->Size = max - 16;
|
||||
|
||||
bytes = (uint8_t *) [self bytes];
|
||||
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
|
||||
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -174,38 +172,6 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
|
||||
return xid;
|
||||
}
|
||||
|
||||
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
|
||||
with: (uint32_t *) length
|
||||
{
|
||||
struct Binary_r bin;
|
||||
struct SizedXid *sizedXIDArray;
|
||||
|
||||
bin.cb = [self length];
|
||||
bin.lpb = (uint8_t *)[self bytes];
|
||||
|
||||
sizedXIDArray = get_SizedXidArray(memCtx, &bin, length);
|
||||
if (!sizedXIDArray)
|
||||
{
|
||||
NSLog (@"Impossible to parse SizedXID array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sizedXIDArray;
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compare: (NSData *) otherGlobCnt
|
||||
{
|
||||
uint64_t globCnt = 0, oGlobCnt = 0;
|
||||
|
||||
if ([self length] > 0)
|
||||
globCnt = *(uint64_t *) [self bytes];
|
||||
|
||||
if ([otherGlobCnt length] > 0)
|
||||
oGlobCnt = *(uint64_t *) [otherGlobCnt bytes];
|
||||
|
||||
return MAPICNCompare (globCnt, oGlobCnt, NULL);
|
||||
}
|
||||
|
||||
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
|
||||
andCnt: (NSData *) globCnt;
|
||||
{
|
||||
|
||||
@@ -47,13 +47,13 @@ MAPIStoreTallocWrapperDestroy (void *data)
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
NSAutoreleasePool *pool;
|
||||
|
||||
// GSRegisterCurrentThread ();
|
||||
GSRegisterCurrentThread ();
|
||||
pool = [NSAutoreleasePool new];
|
||||
wrapper = data;
|
||||
//NSLog (@"destroying wrapped object (wrapper: %p; object: %p (%@))...\n", wrapper, wrapper->instance, NSStringFromClass([wrapper->instance class]));
|
||||
[wrapper->instance release];
|
||||
[pool release];
|
||||
// GSUnregisterCurrentThread ();
|
||||
GSUnregisterCurrentThread ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/* dbmsgdump.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2014 Kamen Mazdrashki <kmazdrashki@zentyal.com>
|
||||
*
|
||||
* Based on implementation done by Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A format-agnostic dump extensions for:
|
||||
* NSArray
|
||||
* NSObject
|
||||
* NSDictionary
|
||||
*/
|
||||
|
||||
#ifndef NSOBJECT_PROPERTYLIST_H
|
||||
#define NSOBJECT_PROPERTYLIST_H
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
|
||||
@interface NSObject (plext)
|
||||
|
||||
- (void) displayWithIndentation: (NSInteger) anInt;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface NSDictionary (plext)
|
||||
|
||||
- (void) displayKey: (NSString *) key
|
||||
withIndentation: (NSInteger) anInt;
|
||||
|
||||
- (void) displayWithIndentation: (NSInteger) anInt;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface NSArray (plext)
|
||||
|
||||
- (void) displayCount: (NSUInteger) count
|
||||
withIndentation: (NSInteger) anInt;
|
||||
|
||||
- (void) displayWithIndentation: (NSInteger) anInt;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NSOBJECT_PROPERTYLIST_H */
|
||||
@@ -32,10 +32,14 @@
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <SOGo/BSONCodec.h>
|
||||
#import "NSObject+PropertyList.h"
|
||||
|
||||
const char *indentationStep = " ";
|
||||
|
||||
@interface NSObject (plext)
|
||||
|
||||
- (void) displayWithIndentation: (NSInteger) anInt;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSObject (plext)
|
||||
|
||||
@@ -135,3 +139,46 @@ const char *indentationStep = " ";
|
||||
|
||||
@end
|
||||
|
||||
static void
|
||||
OCDumpPListData (NSData *content)
|
||||
{
|
||||
//NSDictionary *d;
|
||||
//NSPropertyListFormat format;
|
||||
//NSString *error = nil;
|
||||
//const char *formatName;
|
||||
|
||||
//d = [NSPropertyListSerialization propertyListFromData: content
|
||||
// mutabilityOption: NSPropertyListImmutable
|
||||
// format: &format
|
||||
// errorDescription: &error];
|
||||
//d = [content BSONValue];
|
||||
|
||||
// if (d)
|
||||
// {
|
||||
// switch (format)
|
||||
// {
|
||||
// case NSPropertyListOpenStepFormat:
|
||||
// formatName = "OpenStep";
|
||||
// break;
|
||||
// case NSPropertyListXMLFormat_v1_0:
|
||||
// formatName = "XML";
|
||||
// break;
|
||||
// case NSPropertyListBinaryFormat_v1_0:
|
||||
// formatName = "Binary";
|
||||
// break;
|
||||
// case NSPropertyListGNUstepFormat:
|
||||
// formatName = "GNUstep";
|
||||
// break;
|
||||
// case NSPropertyListGNUstepBinaryFormat:
|
||||
// formatName = "GNUstep binary";
|
||||
// break;
|
||||
// default: formatName = "unknown";
|
||||
// }
|
||||
|
||||
// printf ("File format is: %s\n", formatName);
|
||||
// [d displayWithIndentation: 0];
|
||||
// printf ("\n");
|
||||
// }
|
||||
// else
|
||||
// printf ("an error occurred: %s\n", [error UTF8String]);
|
||||
}
|
||||
+39
-118
@@ -26,7 +26,6 @@
|
||||
// Useful macros
|
||||
//
|
||||
#define ADVANCE _bytes++; _current_pos++;
|
||||
#define ADVANCE_N(N) _bytes += (N); _current_pos += (N);
|
||||
#define REWIND _bytes--; _current_pos--;
|
||||
|
||||
|
||||
@@ -413,46 +412,16 @@ const unsigned short ansicpg874[256] = {
|
||||
- (const char *) parseControlWord: (unsigned int *) len
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
|
||||
start = ADVANCE;
|
||||
|
||||
/*
|
||||
A control word is defined by:
|
||||
|
||||
\<ASCII Letter Sequence><Delimiter>
|
||||
*/
|
||||
while (isalpha(*_bytes))
|
||||
|
||||
while (isalnum(*_bytes) || *_bytes == '-')
|
||||
{
|
||||
ADVANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
The <Delimiter> can be one of the following:
|
||||
|
||||
- A space. This serves only to delimit a control word and is
|
||||
ignored in subsequent processing.
|
||||
|
||||
- A numeric digit or an ASCII minus sign (-), which indicates
|
||||
that a numeric parameter is associated with the control word.
|
||||
Only this case requires to include it in the control word.
|
||||
|
||||
- Any character other than a letter or a digit
|
||||
*/
|
||||
if (*_bytes == '-' || isdigit(*_bytes))
|
||||
{
|
||||
ADVANCE;
|
||||
while (isdigit(*_bytes)) // TODO: Allow up to 10 digits
|
||||
{
|
||||
ADVANCE;
|
||||
}
|
||||
}
|
||||
/* In this case, the delimiting character terminates the control
|
||||
word and is not part of the control word. */
|
||||
|
||||
end = _bytes;
|
||||
|
||||
|
||||
*len = end-start-1;
|
||||
|
||||
return start+1;
|
||||
}
|
||||
|
||||
@@ -539,7 +508,6 @@ const unsigned short ansicpg874[256] = {
|
||||
|
||||
fontTable = [[[RTFFontTable alloc] init] autorelease];
|
||||
fontName = nil;
|
||||
fontInfo = nil;
|
||||
count = 0;
|
||||
|
||||
do
|
||||
@@ -575,7 +543,7 @@ const unsigned short ansicpg874[256] = {
|
||||
// Skip our control word
|
||||
if (strncmp((const char*)cw, "fonttbl", len) == 0)
|
||||
continue;
|
||||
|
||||
|
||||
// We must at least parse <fontnum><fontfamily><fcharset>
|
||||
s = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1
|
||||
length: len-1
|
||||
@@ -590,18 +558,12 @@ const unsigned short ansicpg874[256] = {
|
||||
|
||||
// We now parse <fontfamily><fcharset>
|
||||
cw = [self parseControlWord: &len];
|
||||
if (len == 0) // Possibly parsing a space
|
||||
cw = [self parseControlWord: &len];
|
||||
|
||||
fontInfo->family = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1
|
||||
length: len-1
|
||||
encoding: NSASCIIStringEncoding
|
||||
freeWhenDone: NO];
|
||||
|
||||
cw = [self parseControlWord: &len];
|
||||
if (len == 0) // Possibly parsing a space
|
||||
cw = [self parseControlWord: &len];
|
||||
|
||||
fontInfo->charset = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1
|
||||
length: len-1
|
||||
encoding: NSASCIIStringEncoding
|
||||
@@ -660,44 +622,13 @@ const unsigned short ansicpg874[256] = {
|
||||
} while (count != 0);
|
||||
}
|
||||
|
||||
- (void) parseIgnoringEverything
|
||||
{
|
||||
unsigned int count = 1;
|
||||
// Ignore everything. But we cannot parse it blindly because it could have
|
||||
// binary data with '}' and '{' bytes, so disasters can happen and they will
|
||||
do
|
||||
{
|
||||
if (*_bytes == '\\')
|
||||
{
|
||||
unsigned int binary_size, len = 0, cw_len;
|
||||
const char *cw = [self parseControlWord: &len];
|
||||
cw_len = strlen("bin");
|
||||
if (strncmp(cw, "bin", cw_len) == 0 && len > cw_len)
|
||||
{
|
||||
NSString *s;
|
||||
s = [[NSString alloc] initWithBytesNoCopy: (void *) cw + cw_len
|
||||
length: len - cw_len
|
||||
encoding: NSASCIIStringEncoding
|
||||
freeWhenDone: NO];
|
||||
[s autorelease];
|
||||
binary_size = [s intValue];
|
||||
ADVANCE_N(binary_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (*_bytes == '{') count++;
|
||||
if (*_bytes == '}') count--;
|
||||
ADVANCE;
|
||||
}
|
||||
while (count > 0);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) parsePicture
|
||||
{
|
||||
[self parseIgnoringEverything];
|
||||
// Do the same as -parseStyleSheet for now, that is, ignore everything.
|
||||
[self parseStyleSheet];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -717,7 +648,6 @@ const unsigned short ansicpg874[256] = {
|
||||
fontTable = nil;
|
||||
colorTable = nil;
|
||||
charset = NULL;
|
||||
formattingOptions = nil;
|
||||
|
||||
_html = [[NSMutableData alloc] init];
|
||||
[_html appendBytes: "<html><meta charset='utf-8'><body>" length: 34];
|
||||
@@ -764,7 +694,19 @@ const unsigned short ansicpg874[256] = {
|
||||
}
|
||||
else if (*(_bytes+1) == '*')
|
||||
{
|
||||
[self parseIgnoringEverything];
|
||||
int cc = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (*_bytes == '{')
|
||||
cc++;
|
||||
if (*_bytes == '}')
|
||||
cc--;
|
||||
|
||||
ADVANCE;
|
||||
}
|
||||
while (cc != 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -784,16 +726,16 @@ const unsigned short ansicpg874[256] = {
|
||||
{
|
||||
// We rewind our buffer so we start at the beginning of {\fonttbl...
|
||||
_bytes = cw-2;
|
||||
_current_pos -= 9; // Length: {\fonttbl
|
||||
_current_pos -= 10;
|
||||
fontTable = [self parseFontTable];
|
||||
|
||||
// We go back 1 byte in order to end our section properly ('}' character)
|
||||
// We go back 1 byte in order to end our section properly ('}' character
|
||||
REWIND;
|
||||
}
|
||||
else if (strncmp(cw, "stylesheet", 10) == 0)
|
||||
{
|
||||
_bytes = cw-2;
|
||||
_current_pos -= 12; // Length: {\stylesheet
|
||||
_current_pos -= 13;
|
||||
[self parseStyleSheet];
|
||||
REWIND;
|
||||
}
|
||||
@@ -804,7 +746,7 @@ const unsigned short ansicpg874[256] = {
|
||||
else if (strncmp(cw, "pict", 4) == 0)
|
||||
{
|
||||
_bytes = cw-2;
|
||||
_current_pos -= 6; // Length: {\pict
|
||||
_current_pos -= 7;
|
||||
[self parsePicture];
|
||||
REWIND;
|
||||
}
|
||||
@@ -824,19 +766,19 @@ const unsigned short ansicpg874[256] = {
|
||||
int color_index;
|
||||
char *v;
|
||||
|
||||
if (!formattingOptions) continue;
|
||||
|
||||
color_index = [[s substringFromIndex: 2] intValue];
|
||||
colorDef = [colorTable colorDefAtIndex: color_index];
|
||||
if (!colorDef) continue;
|
||||
|
||||
if (formattingOptions->color_index >= 0)
|
||||
|
||||
if (!formattingOptions)
|
||||
continue;
|
||||
|
||||
if (formattingOptions->color_index >= 0) // && color_index != formattingOptions->color_index)
|
||||
{
|
||||
[_html appendBytes: "</font>" length: 7];
|
||||
}
|
||||
|
||||
formattingOptions->color_index = color_index;
|
||||
|
||||
|
||||
formattingOptions->color_index = color_index;
|
||||
colorDef = [colorTable colorDefAtIndex: color_index];
|
||||
|
||||
v = malloc(23*sizeof(char));
|
||||
memset(v, 0, 23);
|
||||
sprintf(v, "<font color=\"#%02x%02x%02x\">", colorDef->red, colorDef->green, colorDef->blue);
|
||||
@@ -872,41 +814,20 @@ const unsigned short ansicpg874[256] = {
|
||||
|
||||
if (!formattingOptions)
|
||||
continue;
|
||||
|
||||
|
||||
if (formattingOptions->font_index >= 0 &&
|
||||
font_index != formattingOptions->font_index)
|
||||
{
|
||||
[_html appendBytes: "</font>" length: 7];
|
||||
}
|
||||
|
||||
|
||||
formattingOptions->font_index = font_index;
|
||||
|
||||
fontInfo = [fontTable fontInfoAtIndex: font_index];
|
||||
char *v = NULL;
|
||||
if (fontInfo && fontInfo->name)
|
||||
{
|
||||
if ([fontInfo->name length] < 128)
|
||||
{
|
||||
int tag_size = 15 + [fontInfo->name length];
|
||||
v = calloc(tag_size, sizeof(char));
|
||||
snprintf(v, tag_size, "<font face=\"%s\">", [fontInfo->name UTF8String]);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"RTFHandler: Font %u has %d chars length, parse error? "
|
||||
"Ignored", font_index, [fontInfo->name length]);
|
||||
v = calloc(7, sizeof(char));
|
||||
sprintf(v, "<font>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RTF badformed? We don't know about that font (font_index).
|
||||
// Anyhow, we still open the html tag because in the future
|
||||
// we will close it (e.g. when new font is used).
|
||||
v = calloc(7, sizeof(char));
|
||||
sprintf(v, "<font>");
|
||||
}
|
||||
|
||||
char *v = malloc(128*sizeof(char));
|
||||
memset(v, 0, 128);
|
||||
sprintf(v, "<font face=\"%s\">", [fontInfo->name UTF8String]);
|
||||
[_html appendBytes: v length: strlen(v)];
|
||||
free(v);
|
||||
}
|
||||
|
||||
@@ -36,19 +36,9 @@
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
#import <SOGo/BSONCodec.h>
|
||||
#import "NSObject+PropertyList.h"
|
||||
#import "NSObject+PropertyList.m"
|
||||
|
||||
Class MAPIStoreUserContextK, SOGoCacheGCSObjectK;
|
||||
|
||||
static void
|
||||
DumpBSONData(NSData *data)
|
||||
{
|
||||
NSDictionary *dvalue;
|
||||
dvalue = [data BSONValue];
|
||||
[dvalue displayWithIndentation:0];
|
||||
printf("\n");
|
||||
}
|
||||
Class MAPIStoreUserContextK, SOGoMAPIDBObjectK;
|
||||
|
||||
static void
|
||||
DbDumpObject (NSString *username, NSString *path)
|
||||
@@ -60,14 +50,13 @@ DbDumpObject (NSString *username, NSString *path)
|
||||
|
||||
ctx = [MAPIStoreUserContextK userContextWithUsername: username
|
||||
andTDBIndexing: NULL];
|
||||
dbobject = [SOGoCacheGCSObjectK new];
|
||||
dbobject = [SOGoMAPIDBObjectK new];
|
||||
[dbobject setTableUrl: [ctx folderTableURL]];
|
||||
record = [dbobject lookupRecord: path newerThanVersion: -1];
|
||||
if (record)
|
||||
{
|
||||
printf("record found: %p\n", record);
|
||||
content = [[record objectForKey: @"c_content"] dataByDecodingBase64];
|
||||
DumpBSONData(content);
|
||||
OCDumpPListData (content);
|
||||
}
|
||||
else
|
||||
NSLog (@"record not found");
|
||||
@@ -107,15 +96,12 @@ int main (int argc, char *argv[], char *envp[])
|
||||
[loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]];
|
||||
|
||||
MAPIStoreUserContextK = NSClassFromString (@"MAPIStoreUserContext");
|
||||
SOGoCacheGCSObjectK = NSClassFromString (@"SOGoCacheGCSObject");
|
||||
SOGoMAPIDBObjectK = NSClassFromString (@"SOGoMAPIDBObject");
|
||||
|
||||
arguments = [[NSProcessInfo processInfo] arguments];
|
||||
if ([arguments count] > 2) {
|
||||
if ([arguments count] > 2)
|
||||
DbDumpObject ([arguments objectAtIndex: 1],
|
||||
[arguments objectAtIndex: 2]);
|
||||
} else if ([arguments count] > 1) {
|
||||
DumpBSONData([[arguments objectAtIndex:1] dataByDecodingBase64]);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
|
||||
|
||||
+137
-180
@@ -74,7 +74,6 @@
|
||||
@implementation iCalEvent (MAPIStoreProperties)
|
||||
|
||||
- (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData
|
||||
inTimeZone: (NSTimeZone *) tz
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct Binary_r *blob;
|
||||
@@ -82,18 +81,9 @@
|
||||
|
||||
blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx];
|
||||
pattern = get_AppointmentRecurrencePattern (memCtx, blob);
|
||||
|
||||
if (pattern == NULL) {
|
||||
[self logWithFormat: @"Error parsing recurrence pattern. No changes in event recurrence will be done"];
|
||||
return;
|
||||
}
|
||||
|
||||
[(iCalCalendar *) parent setupRecurrenceWithMasterEntity: self
|
||||
fromRecurrencePattern: &pattern->RecurrencePattern
|
||||
withExceptions: pattern->ExceptionInfo
|
||||
andExceptionCount: pattern->ExceptionCount
|
||||
inTimeZone: tz
|
||||
];
|
||||
[(iCalCalendar *) parent
|
||||
setupRecurrenceWithMasterEntity: self
|
||||
fromRecurrencePattern: &pattern->RecurrencePattern];
|
||||
//talloc_free (blob);
|
||||
}
|
||||
|
||||
@@ -141,107 +131,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (int) _updateFromAttendeeMAPIProperties: (NSArray *) recipients
|
||||
withRole: (NSString *) role
|
||||
outParam: (BOOL *) organizerIsSet
|
||||
{
|
||||
NSDictionary *dict;
|
||||
NSString *attEmail;
|
||||
iCalPerson *person;
|
||||
iCalPersonPartStat newPartStat;
|
||||
NSNumber *flags, *trackStatus;
|
||||
int i, effective;
|
||||
|
||||
effective = 0;
|
||||
for (i = 0; i < [recipients count]; i++)
|
||||
{
|
||||
dict = [recipients objectAtIndex: i];
|
||||
person = [iCalPerson new];
|
||||
[person setCn: [dict objectForKey: @"fullName"]];
|
||||
attEmail = [dict objectForKey: @"email"];
|
||||
[person setEmail: attEmail];
|
||||
|
||||
flags = [dict objectForKey: MAPIPropertyKey (PR_RECIPIENT_FLAGS)];
|
||||
if (!flags)
|
||||
{
|
||||
[self logWithFormat:
|
||||
@"no recipient flags specified: skipping recipient"];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (([flags unsignedIntValue] & 0x0002)) /* recipOrganizer */
|
||||
{
|
||||
[self setOrganizer: person];
|
||||
*organizerIsSet = YES;
|
||||
[self logWithFormat: @"organizer set via recipient flags"];
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL isOrganizer = NO;
|
||||
|
||||
// /* Work-around: it happens that Outlook still passes the
|
||||
// organizer as a recipient, maybe because of a feature
|
||||
// documented in a pre-mesozoic PDF still buried in a
|
||||
// cavern... In that case we remove it, and we keep the
|
||||
// number of effective recipients in "effective". If the
|
||||
// total is 0, we remove the "ORGANIZER" too. */
|
||||
// if ([attEmail isEqualToString: orgEmail])
|
||||
// {
|
||||
// [self logWithFormat:
|
||||
// @"avoiding setting organizer as recipient"];
|
||||
// continue;
|
||||
// }
|
||||
|
||||
trackStatus = [dict objectForKey: MAPIPropertyKey (PidTagRecipientTrackStatus)];
|
||||
if (trackStatus)
|
||||
{
|
||||
/* FIXME: we should provide a data converter between OL
|
||||
partstats and SOGo */
|
||||
switch ([trackStatus unsignedIntValue])
|
||||
{
|
||||
case 0x01: /* respOrganized */
|
||||
isOrganizer = YES;
|
||||
break;
|
||||
case 0x02: /* respTentative */
|
||||
newPartStat = iCalPersonPartStatTentative;
|
||||
break;
|
||||
case 0x03: /* respAccepted */
|
||||
newPartStat = iCalPersonPartStatAccepted;
|
||||
break;
|
||||
case 0x04: /* respDeclined */
|
||||
newPartStat = iCalPersonPartStatDeclined;
|
||||
break;
|
||||
default:
|
||||
newPartStat = iCalPersonPartStatNeedsAction;
|
||||
}
|
||||
|
||||
if (isOrganizer)
|
||||
{
|
||||
[self setOrganizer: person];
|
||||
*organizerIsSet = YES;
|
||||
[self logWithFormat: @"organizer set via track status"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[person setParticipationStatus: newPartStat];
|
||||
[person setRsvp: @"TRUE"];
|
||||
[person setRole: role];
|
||||
[self addToAttendees: person];
|
||||
effective++;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"skipped recipient due"
|
||||
@" to missing track status"];
|
||||
}
|
||||
|
||||
[person release];
|
||||
}
|
||||
|
||||
return effective;
|
||||
}
|
||||
|
||||
|
||||
- (void) updateFromMAPIProperties: (NSDictionary *) properties
|
||||
inUserContext: (MAPIStoreUserContext *) userContext
|
||||
withActiveUser: (SOGoUser *) activeUser
|
||||
@@ -251,7 +140,7 @@
|
||||
iCalDateTime *start, *end;
|
||||
iCalTimeZone *tz;
|
||||
NSTimeZone *userTimeZone;
|
||||
NSString *priority, *class = nil;
|
||||
NSString *priority;
|
||||
NSUInteger responseStatus = 0;
|
||||
NSInteger tzOffset;
|
||||
SOGoUser *ownerUser;
|
||||
@@ -318,7 +207,16 @@
|
||||
value
|
||||
= [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)];
|
||||
if (value)
|
||||
[self setRecurrenceId: value];
|
||||
{
|
||||
if (!isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
}
|
||||
[self setRecurrenceId: value];
|
||||
}
|
||||
|
||||
// start
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)];
|
||||
@@ -330,20 +228,17 @@
|
||||
[start setTimeZone: tz];
|
||||
if (isAllDay)
|
||||
{
|
||||
/* when user TZ is positive (East) all-day events were not
|
||||
shown properly in SOGo UI. This day delay fixes it */
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: value];
|
||||
if (tzOffset > 0)
|
||||
{
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 1
|
||||
hours: 0 minutes: 0
|
||||
seconds: 0];
|
||||
}
|
||||
[start setDate: value];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDateTime: value];
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
||||
@@ -356,20 +251,17 @@
|
||||
[end setTimeZone: tz];
|
||||
if (isAllDay)
|
||||
{
|
||||
/* when user TZ is positive (East) all-day events were not
|
||||
shown properly in SOGo UI. This day delay fixes it */
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: value];
|
||||
if (tzOffset > 0)
|
||||
{
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 1
|
||||
hours: 0 minutes: 0
|
||||
seconds: 0];
|
||||
}
|
||||
[end setDate: value];
|
||||
[end setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [[value timeZone] secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[end setDateTime: value];
|
||||
}
|
||||
}
|
||||
|
||||
/* priority */
|
||||
@@ -392,36 +284,6 @@
|
||||
priority = @"0"; // None
|
||||
[self setPriority: priority];
|
||||
|
||||
/* class */
|
||||
/* See [MS-OXCICAL] Section 2.1.3.11.20.4 */
|
||||
value = [properties objectForKey: MAPIPropertyKey(PR_SENSITIVITY)];
|
||||
if (value)
|
||||
{
|
||||
switch ([value intValue])
|
||||
{
|
||||
case 1:
|
||||
class = @"X-PERSONAL";
|
||||
break;
|
||||
case 2:
|
||||
class = @"PRIVATE";
|
||||
break;
|
||||
case 3:
|
||||
class = @"CONFIDENTIAL";
|
||||
break;
|
||||
default: /* 0 as well */
|
||||
class = @"PUBLIC";
|
||||
}
|
||||
}
|
||||
|
||||
if (class)
|
||||
[self setAccessClass: class];
|
||||
|
||||
/* Categories */
|
||||
/* See [MS-OXCICAL] Section 2.1.3.1.1.20.3 */
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidNameKeywords)];
|
||||
if (value)
|
||||
[self setCategories: value];
|
||||
|
||||
/* show time as free/busy/tentative/out of office. Possible values are:
|
||||
0x00000000 - olFree
|
||||
0x00000001 - olTentative
|
||||
@@ -467,7 +329,7 @@
|
||||
value = [properties
|
||||
objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)];
|
||||
if (value)
|
||||
[self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx];
|
||||
[self _setupEventRecurrence: value inMemCtx: memCtx];
|
||||
|
||||
/* alarm */
|
||||
[self _setupEventAlarmFromProperties: properties];
|
||||
@@ -476,28 +338,106 @@
|
||||
value = [properties objectForKey: @"recipients"];
|
||||
if (value)
|
||||
{
|
||||
NSArray *recipients;
|
||||
NSDictionary *dict;
|
||||
NSString *orgEmail, *sentBy;
|
||||
NSString *orgEmail, *sentBy, *attEmail;
|
||||
iCalPerson *person;
|
||||
iCalPersonPartStat newPartStat;
|
||||
int effective;
|
||||
NSNumber *flags, *trackStatus;
|
||||
int i, effective;
|
||||
BOOL organizerIsSet = NO;
|
||||
|
||||
[self setOrganizer: nil];
|
||||
[self removeAllAttendees];
|
||||
|
||||
/* In [MS-OXOCAL] Section 2.2.4.10.7 says the recipient type is 0x01 as Required
|
||||
and 0x02 as Optional and other documents such [MS-OXCMSG] 2.2.3.1.2 indicates
|
||||
that MAPI_TO is 0x01 and MAPI_CC is 0x02, that's why in SOGo is in 'to' and 'cc'
|
||||
respectively. */
|
||||
effective = [self _updateFromAttendeeMAPIProperties: [value objectForKey: @"to"]
|
||||
withRole: @"REQ-PARTICIPANT"
|
||||
outParam: &organizerIsSet];
|
||||
effective += [self _updateFromAttendeeMAPIProperties: [value objectForKey: @"cc"]
|
||||
withRole: @"OPT-PARTICIPANT"
|
||||
outParam: &organizerIsSet];
|
||||
recipients = [value objectForKey: @"to"];
|
||||
effective = 0;
|
||||
for (i = 0; i < [recipients count]; i++)
|
||||
{
|
||||
dict = [recipients objectAtIndex: i];
|
||||
person = [iCalPerson new];
|
||||
[person setCn: [dict objectForKey: @"fullName"]];
|
||||
attEmail = [dict objectForKey: @"email"];
|
||||
[person setEmail: attEmail];
|
||||
|
||||
flags = [dict objectForKey: MAPIPropertyKey (PR_RECIPIENT_FLAGS)];
|
||||
if (!flags)
|
||||
{
|
||||
[self logWithFormat:
|
||||
@"no recipient flags specified: skipping recipient"];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effective == 0) /* See work-around inside _updateFromAttendeeMAPIProperties */
|
||||
if (([flags unsignedIntValue] & 0x0002)) /* recipOrganizer */
|
||||
{
|
||||
[self setOrganizer: person];
|
||||
organizerIsSet = YES;
|
||||
[self logWithFormat: @"organizer set via recipient flags"];
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL isOrganizer = NO;
|
||||
|
||||
// /* Work-around: it happens that Outlook still passes the
|
||||
// organizer as a recipient, maybe because of a feature
|
||||
// documented in a pre-mesozoic PDF still buried in a
|
||||
// cavern... In that case we remove it, and we keep the
|
||||
// number of effective recipients in "effective". If the
|
||||
// total is 0, we remove the "ORGANIZER" too. */
|
||||
// if ([attEmail isEqualToString: orgEmail])
|
||||
// {
|
||||
// [self logWithFormat:
|
||||
// @"avoiding setting organizer as recipient"];
|
||||
// continue;
|
||||
// }
|
||||
|
||||
trackStatus = [dict objectForKey: MAPIPropertyKey (PidTagRecipientTrackStatus)];
|
||||
if (trackStatus)
|
||||
{
|
||||
/* FIXME: we should provide a data converter between OL
|
||||
partstats and SOGo */
|
||||
switch ([trackStatus unsignedIntValue])
|
||||
{
|
||||
case 0x01: /* respOrganized */
|
||||
isOrganizer = YES;
|
||||
break;
|
||||
case 0x02: /* respTentative */
|
||||
newPartStat = iCalPersonPartStatTentative;
|
||||
break;
|
||||
case 0x03: /* respAccepted */
|
||||
newPartStat = iCalPersonPartStatAccepted;
|
||||
break;
|
||||
case 0x04: /* respDeclined */
|
||||
newPartStat = iCalPersonPartStatDeclined;
|
||||
break;
|
||||
default:
|
||||
newPartStat = iCalPersonPartStatNeedsAction;
|
||||
}
|
||||
|
||||
if (isOrganizer)
|
||||
{
|
||||
[self setOrganizer: person];
|
||||
organizerIsSet = YES;
|
||||
[self logWithFormat: @"organizer set via track status"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[person setParticipationStatus: newPartStat];
|
||||
[person setRsvp: @"TRUE"];
|
||||
[person setRole: @"REQ-PARTICIPANT"];
|
||||
[self addToAttendees: person];
|
||||
effective++;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"skipped recipient due"
|
||||
@" to missing track status"];
|
||||
}
|
||||
|
||||
[person release];
|
||||
}
|
||||
|
||||
if (effective == 0) /* See work-around above */
|
||||
[self setOrganizer: nil];
|
||||
else
|
||||
{
|
||||
@@ -523,7 +463,7 @@
|
||||
= [properties objectForKey: MAPIPropertyKey (PidLidResponseStatus)];
|
||||
if (value)
|
||||
responseStatus = [value unsignedLongValue];
|
||||
|
||||
|
||||
/* FIXME: we should provide a data converter between OL partstats and
|
||||
SOGo */
|
||||
switch (responseStatus)
|
||||
@@ -545,6 +485,23 @@
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidLidAttendeeCriticalChange)];
|
||||
if (value && ![value isNever])
|
||||
[self setTimeStampAsDate: value];
|
||||
// if (newPartStat // != iCalPersonPartStatUndefined
|
||||
// )
|
||||
// {
|
||||
// // iCalPerson *participant;
|
||||
|
||||
// // participant = [self userAsAttendee: ownerUser];
|
||||
// // [participant setParticipationStatus: newPartStat];
|
||||
// // [sogoObject saveComponent: self];
|
||||
|
||||
// [sogoObject changeParticipationStatus: newPartStat
|
||||
// withDelegate: nil];
|
||||
// // [[self context] tearDownRequest];
|
||||
// }
|
||||
// // }1005
|
||||
|
||||
// // else
|
||||
// // {
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -559,7 +516,7 @@
|
||||
[person setCn: [dict objectForKey: @"fullName"]];
|
||||
orgEmail = [dict objectForKey: @"email"];
|
||||
[person setEmail: orgEmail];
|
||||
|
||||
|
||||
if (![activeUser isEqual: ownerUser])
|
||||
{
|
||||
dict = [activeUser primaryIdentity];
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
NSArray *byMonth;
|
||||
iCalByDayMask *mask;
|
||||
NSCalendarDate *dateValue;
|
||||
int16_t wDay;
|
||||
|
||||
rrule = [self recurrenceRule];
|
||||
byMonth = [rrule byMonth];
|
||||
@@ -60,14 +59,7 @@
|
||||
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
|
||||
mask = [rrule byDayMask];
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
wDay = [mask firstOccurrence];
|
||||
if (wDay < 0)
|
||||
/* [MS-OXOCAL] the wDay field is set to indicate the
|
||||
occurrence of the day of the week within the month (1 to
|
||||
5, where 5 indicates the final occurrence during the
|
||||
month if that day of the week does not occur 5 times). */
|
||||
wDay += 6;
|
||||
tzData->wDay = (uint16_t) wDay;
|
||||
tzData->wDay = [mask firstOccurrence];
|
||||
|
||||
dateValue = [self startDate];
|
||||
tzData->wHour = [dateValue hourOfDay];
|
||||
|
||||
@@ -29,50 +29,6 @@
|
||||
|
||||
#import "NSObject+PropertyList.m"
|
||||
|
||||
static void
|
||||
OCDumpPListData (NSData *content)
|
||||
{
|
||||
//NSDictionary *d;
|
||||
//NSPropertyListFormat format;
|
||||
//NSString *error = nil;
|
||||
//const char *formatName;
|
||||
|
||||
//d = [NSPropertyListSerialization propertyListFromData: content
|
||||
// mutabilityOption: NSPropertyListImmutable
|
||||
// format: &format
|
||||
// errorDescription: &error];
|
||||
//d = [content BSONValue];
|
||||
|
||||
// if (d)
|
||||
// {
|
||||
// switch (format)
|
||||
// {
|
||||
// case NSPropertyListOpenStepFormat:
|
||||
// formatName = "OpenStep";
|
||||
// break;
|
||||
// case NSPropertyListXMLFormat_v1_0:
|
||||
// formatName = "XML";
|
||||
// break;
|
||||
// case NSPropertyListBinaryFormat_v1_0:
|
||||
// formatName = "Binary";
|
||||
// break;
|
||||
// case NSPropertyListGNUstepFormat:
|
||||
// formatName = "GNUstep";
|
||||
// break;
|
||||
// case NSPropertyListGNUstepBinaryFormat:
|
||||
// formatName = "GNUstep binary";
|
||||
// break;
|
||||
// default: formatName = "unknown";
|
||||
// }
|
||||
|
||||
// printf ("File format is: %s\n", formatName);
|
||||
// [d displayWithIndentation: 0];
|
||||
// printf ("\n");
|
||||
// }
|
||||
// else
|
||||
// printf ("an error occurred: %s\n", [error UTF8String]);
|
||||
}
|
||||
|
||||
static void
|
||||
PLReaderDumpPListFile (NSString *filename)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,5 @@ import sys
|
||||
import samba.param
|
||||
|
||||
a = samba.param.LoadParm()
|
||||
a.set('debug level', '0')
|
||||
a.load_default()
|
||||
print a.get(sys.argv[1])
|
||||
@@ -240,7 +240,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
handle = currentHandle;
|
||||
else if (debugPools)
|
||||
[self logWithFormat: @"DBPOOL: cannot use handle (%@ vs %@) ",
|
||||
[_url absoluteString], [currentHandle->url absoluteString]];
|
||||
[_url absoluteString], [handle->url absoluteString]];
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -276,7 +276,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelHandle *handle;
|
||||
NSCalendarDate *now, *lastFailure;
|
||||
NSString *urlId, *url;
|
||||
NSString *urlId;
|
||||
|
||||
channel = nil;
|
||||
urlId = [_url gcsURLId];
|
||||
@@ -304,10 +304,10 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
url = [NSString stringWithFormat: @"%@://%@%@", [_url scheme], [_url host], [_url path]];
|
||||
if (debugPools)
|
||||
{
|
||||
[self logWithFormat: @"DBPOOL: create new DB channel for %@", url];
|
||||
[self logWithFormat: @"DBPOOL: create new DB channel for URL: %@",
|
||||
[_url absoluteString]];
|
||||
}
|
||||
|
||||
/* create channel */
|
||||
@@ -330,13 +330,15 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
|
||||
if (lastFailure)
|
||||
{
|
||||
[self logWithFormat: @"db for %@ is now back up", url];
|
||||
[self logWithFormat: @"db for %@ is now back up",
|
||||
[_url absoluteString]];
|
||||
[lastFailures removeObjectForKey: urlId];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"could not open channel %@ for %@", channel, url];
|
||||
[self errorWithFormat: @"could not open channel %@ for URL: %@",
|
||||
channel, [_url absoluteString]];
|
||||
channel = nil;
|
||||
[lastFailures setObject: now forKey: urlId];
|
||||
[self warnWithFormat: @" will prevent opening of this"
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSKeyValueCoding.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@@ -148,7 +147,7 @@
|
||||
if ([keys count] == 0)
|
||||
return folderQualifier;
|
||||
|
||||
bindings = [_folder dictionaryWithValuesForKeys:keys];
|
||||
bindings = [_folder valuesForKeys:keys];
|
||||
return [folderQualifier qualifierWithBindings:bindings
|
||||
requiresAllVariables:NO];
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ ifneq ($(frameworks),yes)
|
||||
ifneq ($(FHS_INSTALL_ROOT),)
|
||||
GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include
|
||||
endif
|
||||
GNUSTEP_TARGET_LDIR=sogo
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
else
|
||||
include $(GNUSTEP_MAKEFILES)/framework.make
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_partstates;
|
||||
sqlType = "CLOB";
|
||||
sqlType = "VARCHAR2(255)";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
@@ -154,10 +154,5 @@
|
||||
sqlType = "INTEGER";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
columnName = c_description;
|
||||
sqlType = "CLOB";
|
||||
allowsNull = YES;
|
||||
}
|
||||
);
|
||||
}
|
||||
Loaded 100 of 1067 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user