Compare commits
410
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
289100beff | ||
|
|
7da6b0b822 | ||
|
|
6cf2fd1e52 | ||
|
|
c95568373f | ||
|
|
bd12a817a6 | ||
|
|
2fd0beeaca | ||
|
|
db4cd9da6e | ||
|
|
d1551bc7bb | ||
|
|
bea9b26907 | ||
|
|
84dfba7978 | ||
|
|
ce3d55d2cc | ||
|
|
b43c3f6d98 | ||
|
|
abcd48a0cd | ||
|
|
d07df554f3 | ||
|
|
772c3b3115 | ||
|
|
ed42d1e26b | ||
|
|
e0c1d5e47b | ||
|
|
55d91973bd | ||
|
|
4f303f8aa2 | ||
|
|
c8995087d8 | ||
|
|
2ae5f342da | ||
|
|
d6ab7582e5 | ||
|
|
1e0473f1e5 | ||
|
|
7990e23aaa | ||
|
|
04ce8d1066 | ||
|
|
37461e650d | ||
|
|
9922ec56d9 | ||
|
|
7893349244 | ||
|
|
e40be92204 | ||
|
|
30103532ae | ||
|
|
b433bac432 | ||
|
|
79bfc7aedf | ||
|
|
54ab0bf822 | ||
|
|
7e8c79c646 | ||
|
|
416711ea43 | ||
|
|
5a8f310bc8 | ||
|
|
ae72c2f1d5 | ||
|
|
ca500df0af | ||
|
|
e2ef0103c4 | ||
|
|
e6d1a00928 | ||
|
|
143ee59f5b | ||
|
|
037a3a136a | ||
|
|
c98a000515 | ||
|
|
cc1e9f2fff | ||
|
|
9dc0a08bba | ||
|
|
5412599832 | ||
|
|
fbf5c4f6c2 | ||
|
|
d97813451e | ||
|
|
4de814c3b2 | ||
|
|
fb121974ec | ||
|
|
39aa9a97db | ||
|
|
dc48e05dd3 | ||
|
|
0ea0a77666 | ||
|
|
f8b7ebdaf8 | ||
|
|
5cf500d560 | ||
|
|
c1ce32ac94 | ||
|
|
e71a1c9974 | ||
|
|
96b2e0e1f5 | ||
|
|
a145fdbb53 | ||
|
|
619e9fe7dd | ||
|
|
64da65d827 | ||
|
|
364a07bd05 | ||
|
|
2c78d60e4a | ||
|
|
ee8caa5a99 | ||
|
|
f666b436dd | ||
|
|
4826f01555 | ||
|
|
0a285eedec | ||
|
|
582c24afc5 | ||
|
|
92ab267db5 | ||
|
|
fbb779a71d | ||
|
|
54b405a83d | ||
|
|
15f95c4956 | ||
|
|
2d65b75b56 | ||
|
|
ebe2a466e7 | ||
|
|
7e89c43919 | ||
|
|
a37f438663 | ||
|
|
dc5237e92e | ||
|
|
50896eb526 | ||
|
|
7ff12c7a64 | ||
|
|
f2b5f5917b | ||
|
|
ee74b78bf2 | ||
|
|
dfb960eac4 | ||
|
|
a1406d01e1 | ||
|
|
07ac3f43fb | ||
|
|
1db2b9793a | ||
|
|
393d8e5a73 | ||
|
|
6f741168a9 | ||
|
|
84c199b95a | ||
|
|
769045bbf6 | ||
|
|
08501dacaa | ||
|
|
b7907d654d | ||
|
|
a94591a365 | ||
|
|
78f5f60b16 | ||
|
|
255b3d2d00 | ||
|
|
1d9e085658 | ||
|
|
9901788db6 | ||
|
|
8e39f03f83 | ||
|
|
f6370d10cb | ||
|
|
d0c1737b1b | ||
|
|
836095e984 | ||
|
|
a86b164228 | ||
|
|
5847e086ec | ||
|
|
3872af35d1 | ||
|
|
beb666bc5f | ||
|
|
8fce211172 | ||
|
|
030d343432 | ||
|
|
a7306a77cb | ||
|
|
58449076a3 | ||
|
|
539060d252 | ||
|
|
336c4fb9d3 | ||
|
|
d3bf3dcd8f | ||
|
|
d4930a7960 | ||
|
|
49ee0850e1 | ||
|
|
0f432b654f | ||
|
|
cdb6085510 | ||
|
|
6b3454376e | ||
|
|
a8e8ec535a | ||
|
|
3f3ae3acd7 | ||
|
|
8706443685 | ||
|
|
ca7c4c026e | ||
|
|
f0a3fdae5b | ||
|
|
210053ea20 | ||
|
|
35f81318d6 | ||
|
|
1d6f1a83af | ||
|
|
914e7b7418 | ||
|
|
bddd67fb93 | ||
|
|
fa23b574eb | ||
|
|
321e16c382 | ||
|
|
479bbce7d0 | ||
|
|
69b06d3c7a | ||
|
|
65d1fc2134 | ||
|
|
cdc74e4984 | ||
|
|
1e4596ca3a | ||
|
|
755efa789b | ||
|
|
eae327ca19 | ||
|
|
01857a9a47 | ||
|
|
e2c3259b16 | ||
|
|
77917780c8 | ||
|
|
9b320ec2a1 | ||
|
|
b2d7eb40c2 | ||
|
|
ab121e6c15 | ||
|
|
94d546dc7a | ||
|
|
2082acc1aa | ||
|
|
06ef3fe856 | ||
|
|
c9ec636a53 | ||
|
|
36a70b161c | ||
|
|
784f0100e9 | ||
|
|
db9536adcc | ||
|
|
c428ac2553 | ||
|
|
f5607b3e85 | ||
|
|
491d7aaffc | ||
|
|
95a8a9316a | ||
|
|
1ee47b1ee2 | ||
|
|
dfde498b7c | ||
|
|
06476abdf7 | ||
|
|
bb0221c763 | ||
|
|
dc869ce106 | ||
|
|
ddcae9f132 | ||
|
|
f11bcd3373 | ||
|
|
a47f1de607 | ||
|
|
09aed26f10 | ||
|
|
0e5de2df42 | ||
|
|
82fb03b67b | ||
|
|
f9c8661fe6 | ||
|
|
661b6694af | ||
|
|
514b1c03be | ||
|
|
a663fdd260 | ||
|
|
0db16aedb1 | ||
|
|
6f70066986 | ||
|
|
b933f53fc2 | ||
|
|
097d5c2333 | ||
|
|
d75fb0722a | ||
|
|
6204aebf2c | ||
|
|
26d876e67a | ||
|
|
cc3652b59b | ||
|
|
9d7bbbc312 | ||
|
|
d2915add6d | ||
|
|
67139ce3e3 | ||
|
|
9eb138b867 | ||
|
|
baf3d1f986 | ||
|
|
ecd336fa5c | ||
|
|
afd73289d1 | ||
|
|
bff21ab1ff | ||
|
|
6d99226b13 | ||
|
|
69974807b5 | ||
|
|
8ee2303e78 | ||
|
|
7bc8cd58dd | ||
|
|
2668bc313c | ||
|
|
418ac15de6 | ||
|
|
637ac1e98a | ||
|
|
b0ba6f4a22 | ||
|
|
cdb766e2ef | ||
|
|
fb4c3a4730 | ||
|
|
6f44ec42c1 | ||
|
|
e25a7e3d8d | ||
|
|
3b0b43f28f | ||
|
|
afa9061f2f | ||
|
|
9f3f2aebe4 | ||
|
|
1d5ae27c99 | ||
|
|
0be36df211 | ||
|
|
beb57707e8 | ||
|
|
1bdd5dfc90 | ||
|
|
43debc1d70 | ||
|
|
37fbd75d55 | ||
|
|
1c1a75d2c1 | ||
|
|
f048860729 | ||
|
|
6d6053865f | ||
|
|
6e00274988 | ||
|
|
307fe90854 | ||
|
|
8031f066dc | ||
|
|
f1fda8bd06 | ||
|
|
517f4b0f49 | ||
|
|
2b54897d29 | ||
|
|
ae7ac1be29 | ||
|
|
6af8b486df | ||
|
|
316ade13f8 | ||
|
|
cbba0719bb | ||
|
|
c4566c4773 | ||
|
|
3b2cbb945c | ||
|
|
8e0849029e | ||
|
|
6192322513 | ||
|
|
44dfd35521 | ||
|
|
575600cabc | ||
|
|
acc2bd0369 | ||
|
|
cd47c926c9 | ||
|
|
9b038db0b6 | ||
|
|
bcc1aa8049 | ||
|
|
ef6aab63c9 | ||
|
|
df59b73a22 | ||
|
|
a5672fda52 | ||
|
|
b593ddc643 | ||
|
|
38d61f720f | ||
|
|
733e334726 | ||
|
|
22888ba5ac | ||
|
|
33c485d2f6 | ||
|
|
9160e34134 | ||
|
|
5ceddc213e | ||
|
|
85d9440ccd | ||
|
|
b97002565e | ||
|
|
03b6a8a028 | ||
|
|
22196e0054 | ||
|
|
8d21bc3e36 | ||
|
|
4c3c741d9f | ||
|
|
731934f061 | ||
|
|
b645b4187d | ||
|
|
9b847a276c | ||
|
|
68216a7456 | ||
|
|
624967c60b | ||
|
|
e6ba15bbae | ||
|
|
2a1b363fee | ||
|
|
eaec3c0610 | ||
|
|
d3a4b497c6 | ||
|
|
b26ea30b1b | ||
|
|
72b210cd2e | ||
|
|
a80d75d09c | ||
|
|
aae76dc4e6 | ||
|
|
c20ca0e33d | ||
|
|
601a9ca559 | ||
|
|
d7c283f751 | ||
|
|
f3325b8b0d | ||
|
|
b4ea4d7e9f | ||
|
|
9447437d6f | ||
|
|
322f72626a | ||
|
|
55ae4cb8c0 | ||
|
|
e5d01428ad | ||
|
|
7406fc3772 | ||
|
|
82178fd6ce | ||
|
|
efe2070227 | ||
|
|
68ebc9e0ff | ||
|
|
6cf1ec998e | ||
|
|
6c9b1a494f | ||
|
|
98e4c8ca55 | ||
|
|
1178bf2d60 | ||
|
|
96986812f4 | ||
|
|
142deeb5f0 | ||
|
|
dc8012e3d3 | ||
|
|
3731819e15 | ||
|
|
eca22caefc | ||
|
|
923bcf8ae2 | ||
|
|
0b44fa81f4 | ||
|
|
776b17ff20 | ||
|
|
12b159a6c0 | ||
|
|
2aa528dacc | ||
|
|
d1d3980919 | ||
|
|
cf35eec1f5 | ||
|
|
58a0b0c173 | ||
|
|
723a9d4e08 | ||
|
|
4cc158043e | ||
|
|
7fd1564a86 | ||
|
|
981c718d22 | ||
|
|
6cfa6b9952 | ||
|
|
0a198ccb05 | ||
|
|
5258ee2e76 | ||
|
|
d5ec25ee62 | ||
|
|
57d591c427 | ||
|
|
b01de763da | ||
|
|
2dcb8fa732 | ||
|
|
6ce4cc1919 | ||
|
|
0e8bf0d239 | ||
|
|
086361b79a | ||
|
|
ab12c84887 | ||
|
|
312084243b | ||
|
|
47be392d4f | ||
|
|
34efcfee4e | ||
|
|
8e7f6d1aec | ||
|
|
e2bee23021 | ||
|
|
1a10599369 | ||
|
|
d907d5d268 | ||
|
|
7014d94937 | ||
|
|
99a36be034 | ||
|
|
8e41e5d665 | ||
|
|
b1dfe47f01 | ||
|
|
014632fddb | ||
|
|
8b44c08a32 | ||
|
|
c893963da5 | ||
|
|
c6b976a830 | ||
|
|
d5d97db8e8 | ||
|
|
d98ff69fbe | ||
|
|
87a91fbdf2 | ||
|
|
daa7ab87d7 | ||
|
|
00cef845b7 | ||
|
|
f9550b9181 | ||
|
|
3e59b1ad6e | ||
|
|
e45451a9f9 | ||
|
|
b35fee8efc | ||
|
|
e9e3dd5646 | ||
|
|
16c863d89d | ||
|
|
7e7d302165 | ||
|
|
d052356724 | ||
|
|
d87056ebfa | ||
|
|
135b463e8f | ||
|
|
f4c4f5af62 | ||
|
|
70e45f7843 | ||
|
|
28e449b050 | ||
|
|
6b52e9c945 | ||
|
|
1cfbea69c1 | ||
|
|
cb9118d869 | ||
|
|
dd6dd7251c | ||
|
|
24c6c8c91d | ||
|
|
b1ac7a0cca | ||
|
|
59e6d6df4a | ||
|
|
71ad5de910 | ||
|
|
b5369530f0 | ||
|
|
ead665de85 | ||
|
|
838ae8995f | ||
|
|
a0c1ce8f3b | ||
|
|
3ef28b390d | ||
|
|
0cd6fe6135 | ||
|
|
204a62aa6a | ||
|
|
31cffdffd3 | ||
|
|
501ee79766 | ||
|
|
255bcbe92f | ||
|
|
72732879fa | ||
|
|
a56fc93a9a | ||
|
|
24a934275f | ||
|
|
58f634bffe | ||
|
|
ca4a754f2c | ||
|
|
8015688df3 | ||
|
|
b07913d66d | ||
|
|
6b49a8a341 | ||
|
|
df8a0d8715 | ||
|
|
e6cc56dca1 | ||
|
|
de3d38262b | ||
|
|
0e56527e05 | ||
|
|
12cdc60af7 | ||
|
|
dba17fbcbc | ||
|
|
ba79234ceb | ||
|
|
067809ef1c | ||
|
|
bdf12e5c01 | ||
|
|
97201c56a4 | ||
|
|
4a6daf45e0 | ||
|
|
7691d616a9 | ||
|
|
2c6d833c11 | ||
|
|
f4e15e2db8 | ||
|
|
a8b716fbaa | ||
|
|
ef7b919175 | ||
|
|
973ab78761 | ||
|
|
c4b0ac209b | ||
|
|
41320a4813 | ||
|
|
c410a9fc3f | ||
|
|
24e35103ff | ||
|
|
616f2abbb2 | ||
|
|
3910769ae8 | ||
|
|
b67e1deda5 | ||
|
|
35ca313c37 | ||
|
|
c631689975 | ||
|
|
cd64ca199a | ||
|
|
be60fdebcc | ||
|
|
d076e04ad4 | ||
|
|
7e0cddc06b | ||
|
|
258444bb93 | ||
|
|
75ce59d010 | ||
|
|
cbfcd54858 | ||
|
|
415fb6034e | ||
|
|
445a1fb2d6 | ||
|
|
c866bce8e8 | ||
|
|
ed4ca3bd1b | ||
|
|
bb9764bc6b | ||
|
|
05578bfcb6 | ||
|
|
a7c2054cce | ||
|
|
52ad0e0e5b | ||
|
|
dc8b73b12b | ||
|
|
18acdf20a6 | ||
|
|
0a23f18fc9 | ||
|
|
b8366d796d | ||
|
|
5ee8cb3a3f | ||
|
|
4e3a242f2f | ||
|
|
68c38e2b5c | ||
|
|
f1d14fdeca | ||
|
|
a5550cff18 |
No files matched your search
+11
@@ -12,6 +12,7 @@ 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
|
||||
@@ -38,6 +39,7 @@ 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
|
||||
@@ -64,6 +66,7 @@ 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
|
||||
@@ -90,6 +93,7 @@ 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
|
||||
@@ -116,6 +120,7 @@ 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
|
||||
@@ -142,6 +147,7 @@ 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
|
||||
@@ -168,6 +174,7 @@ 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
|
||||
@@ -194,6 +201,7 @@ 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
|
||||
@@ -220,6 +228,7 @@ 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
|
||||
@@ -246,6 +255,7 @@ 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
|
||||
@@ -272,6 +282,7 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
Copyright (c) 2015, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -40,35 +40,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@implementation NGMimeMessage (ActiveSync)
|
||||
|
||||
- (NSArray *) allRecipients
|
||||
- (void) _addRecipients: (NSEnumerator *) enumerator
|
||||
toArray: (NSMutableArray *) recipients
|
||||
{
|
||||
NSEnumerator *enumerator, *addressList;
|
||||
NSMutableArray *recipients;
|
||||
NGMailAddressParser *parser;
|
||||
NSEnumerator *addressList;
|
||||
NGMailAddress *address;
|
||||
NSString *s;
|
||||
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
||||
addressList = [[parser parseAddressList] objectEnumerator];
|
||||
|
||||
while ((address = [addressList nextObject]))
|
||||
[recipients addObject: [address address]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) allRecipients
|
||||
{
|
||||
NSMutableArray *recipients;
|
||||
|
||||
recipients = [NSMutableArray array];
|
||||
|
||||
enumerator = [[self headersForKey: @"to"] objectEnumerator];
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
||||
addressList = [[parser parseAddressList] objectEnumerator];
|
||||
|
||||
while ((address = [addressList nextObject]))
|
||||
[recipients addObject: [address address]];
|
||||
}
|
||||
[self _addRecipients: [[self headersForKey: @"to"] 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]];
|
||||
}
|
||||
[self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ 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>
|
||||
|
||||
@@ -193,11 +194,12 @@ 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>"];
|
||||
}
|
||||
@@ -229,7 +231,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -32,7 +32,10 @@ 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>
|
||||
@@ -44,11 +47,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
- (void) _dumpToFile
|
||||
{
|
||||
NSString *path;
|
||||
BOOL debugOn;
|
||||
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
NSLog(@"Original data written to: %@", 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];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -81,7 +91,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self errorWithFormat: @"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
@@ -114,7 +124,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self logWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
@@ -129,7 +139,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self errorWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
Copyright (c) 2014-2015, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -49,6 +49,9 @@ 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,6 +33,7 @@ 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>
|
||||
@@ -127,13 +128,17 @@ static NSArray *easCommandParameters = nil;
|
||||
//
|
||||
- (NSCalendarDate *) calendarDate
|
||||
{
|
||||
NSString *s;
|
||||
id o;
|
||||
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y%m%dT%H%M%SZ"];
|
||||
// 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"];
|
||||
|
||||
if (!o)
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ"];
|
||||
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ %Z"];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -293,6 +298,43 @@ 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/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
|
||||
and send an email to iplicreq@microsoft.com
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
@@ -130,6 +131,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
|
||||
[[o properties] addEntriesFromDictionary: values];
|
||||
@@ -287,9 +289,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
// FIXME
|
||||
//continue;
|
||||
NSLog(@"BLARG!");
|
||||
// FIXME - what to do?
|
||||
[self errorWithFormat: @"Fatal error occured - tried to call -processSyncAddCommand: ... on a mail folder. We abort."];
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -531,6 +532,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processSyncGetChanges: (id <DOMElement>) theDocumentElement
|
||||
inCollection: (id) theCollection
|
||||
withWindowSize: (unsigned int) theWindowSize
|
||||
withMaxSyncResponseSize: (unsigned int) theMaxSyncResponseSize
|
||||
withSyncKey: (NSString *) theSyncKey
|
||||
withFolderType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
withFilterType: (NSCalendarDate *) theFilterType
|
||||
@@ -539,6 +541,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||
NSString *davCollectionTagToStore;
|
||||
NSAutoreleasePool *pool;
|
||||
NSMutableString *s;
|
||||
|
||||
@@ -562,7 +565,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
|
||||
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
|
||||
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize
|
||||
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
|
||||
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
|
||||
theFilterType)
|
||||
{
|
||||
@@ -590,7 +593,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
softdelete_count++;
|
||||
}
|
||||
|
||||
if (softdelete_count >= theWindowSize)
|
||||
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
@@ -617,6 +620,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
more_available = NO;
|
||||
|
||||
davCollectionTagToStore = [theCollection davCollectionTag];
|
||||
|
||||
switch (theFolderType)
|
||||
{
|
||||
// Handle all the GCS components
|
||||
@@ -653,7 +658,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// Check for the WindowSize and slice accordingly
|
||||
if (return_count >= theWindowSize)
|
||||
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
more_available = YES;
|
||||
|
||||
@@ -668,7 +673,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
deleted = [[component objectForKey: @"c_deleted"] intValue];
|
||||
|
||||
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
||||
|
||||
@@ -692,10 +700,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if (![syncCache objectForKey: uid])
|
||||
updated = NO;
|
||||
else if ([[component objectForKey: @"c_lastmodified"] intValue] == [[syncCache objectForKey: uid] intValue])
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
return_count++;
|
||||
|
||||
|
||||
sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType]
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
@@ -705,7 +716,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
componentObject = [sogoObject component: NO secure: NO];
|
||||
|
||||
|
||||
//
|
||||
// We do NOT synchronize NEW events that are in fact, invitations
|
||||
// to events. This is due to the fact that Outlook 2013 creates
|
||||
@@ -727,22 +737,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
partstat = [attendee participationStatus];
|
||||
|
||||
if (partstat == iCalPersonPartStatNeedsAction)
|
||||
continue;
|
||||
{
|
||||
DESTROY(pool);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[syncCache setObject: [component objectForKey: @"c_lastmodified"] forKey: uid];
|
||||
|
||||
// No need to set dateCache for Contacts
|
||||
if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder))
|
||||
{
|
||||
NSCalendarDate *d;
|
||||
|
||||
if ([[component objectForKey: @"c_cycleenddate"] intValue])
|
||||
d = [NSCalendarDate dateWithTimeIntervalSince1970: [[component objectForKey: @"c_cycleenddate"] intValue]];
|
||||
else if ([[component objectForKey: @"c_enddate"] intValue])
|
||||
d = [NSCalendarDate dateWithTimeIntervalSince1970: [[component objectForKey: @"c_enddate"] intValue]];
|
||||
else
|
||||
d = [NSCalendarDate distantFuture];
|
||||
|
||||
[dateCache setObject: d forKey: uid];
|
||||
}
|
||||
|
||||
if (updated)
|
||||
[s appendString: @"<Change xmlns=\"AirSync:\">"];
|
||||
else
|
||||
{
|
||||
// no need to set dateCache for Contacts
|
||||
if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder))
|
||||
[dateCache setObject: [componentObject startDate] ? [componentObject startDate] : [NSCalendarDate date] forKey: uid]; // FIXME: need to set proper date for recurring events - softDelete
|
||||
|
||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
@@ -757,10 +779,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendString: @"</Add>"];
|
||||
|
||||
return_count++;
|
||||
|
||||
DESTROY(pool);
|
||||
}
|
||||
} // for ...
|
||||
|
||||
DESTROY(pool);
|
||||
} // for (i = 0; i < max; i++) ...
|
||||
|
||||
if (more_available)
|
||||
{
|
||||
@@ -770,7 +792,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
{
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata
|
||||
@@ -791,7 +813,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
int j, k, return_count;
|
||||
BOOL found_in_cache;
|
||||
|
||||
|
||||
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
|
||||
max = [allMessages count];
|
||||
|
||||
@@ -829,7 +850,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
found_in_cache = NO;
|
||||
|
||||
|
||||
if (found_in_cache)
|
||||
k = j+1;
|
||||
else
|
||||
@@ -847,7 +867,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// Check for the WindowSize and slice accordingly
|
||||
if (return_count >= theWindowSize)
|
||||
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
|
||||
{
|
||||
NSString *lastSequence;
|
||||
more_available = YES;
|
||||
@@ -947,7 +967,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
{
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
|
||||
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
|
||||
}
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
@@ -1003,7 +1023,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([[element tagName] isEqualToString: @"Add"])
|
||||
{
|
||||
// Add
|
||||
[self processSyncAddCommand: aCommand
|
||||
[self processSyncAddCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1012,7 +1032,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Change"])
|
||||
{
|
||||
// Change
|
||||
[self processSyncChangeCommand: aCommand
|
||||
[self processSyncChangeCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1021,7 +1041,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Delete"])
|
||||
{
|
||||
// Delete
|
||||
[self processSyncDeleteCommand: aCommand
|
||||
[self processSyncDeleteCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1030,7 +1050,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else if ([[element tagName] isEqualToString: @"Fetch"])
|
||||
{
|
||||
// Fetch
|
||||
[self processSyncFetchCommand: aCommand
|
||||
[self processSyncFetchCommand: element
|
||||
inCollection: theCollection
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
@@ -1047,14 +1067,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processSyncCollection: (id <DOMElement>) theDocumentElement
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
changeDetected: (BOOL *) changeDetected
|
||||
maxSyncResponseSize: (int) theMaxSyncResponseSize
|
||||
{
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey;
|
||||
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
id collection, value;
|
||||
|
||||
NSMutableString *changeBuffer, *commandsBuffer;
|
||||
BOOL getChanges, first_sync;
|
||||
unsigned int windowSize, v, status;
|
||||
NSMutableDictionary *folderMetadata, *folderOptions;
|
||||
|
||||
changeBuffer = [NSMutableString string];
|
||||
commandsBuffer = [NSMutableString string];
|
||||
@@ -1076,7 +1098,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
|
||||
//[theBuffer appendFormat: @"<Status>%d</Status>", 8];
|
||||
//[theBuffer appendString: @"</Collection>"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1104,20 +1125,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
first_sync = NO;
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
|
||||
if ([syncKey isEqualToString: @"0"])
|
||||
{
|
||||
davCollectionTag = @"-1";
|
||||
first_sync = YES;
|
||||
*changeDetected = YES;
|
||||
}
|
||||
else if ((![syncKey isEqualToString: @"-1"]) && !([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncCache"]))
|
||||
else if ((![syncKey isEqualToString: @"-1"]) && !([folderMetadata objectForKey: @"SyncCache"]))
|
||||
{
|
||||
//NSLog(@"Reset folder: %@", [collection nameInContainer]);
|
||||
davCollectionTag = @"0";
|
||||
first_sync = YES;
|
||||
*changeDetected = YES;
|
||||
|
||||
if (!([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"displayName"]))
|
||||
if (!([folderMetadata objectForKey: @"displayName"]))
|
||||
status = 12; // need folderSync
|
||||
else
|
||||
status = 3; // do a complete resync
|
||||
@@ -1127,7 +1150,40 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
||||
|
||||
if (!bodyPreferenceType)
|
||||
bodyPreferenceType = @"1";
|
||||
{
|
||||
bodyPreferenceType = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"BodyPreferenceType"];
|
||||
|
||||
// By default, send MIME mails. See #3146 for details.
|
||||
if (!bodyPreferenceType)
|
||||
bodyPreferenceType = @"4";
|
||||
}
|
||||
else
|
||||
{
|
||||
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
|
||||
|
||||
if (!mimeSupport)
|
||||
mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
|
||||
|
||||
if (!mimeSupport)
|
||||
mimeSupport = @"0";
|
||||
|
||||
if ([mimeSupport isEqualToString: @"1"] && [bodyPreferenceType isEqualToString: @"4"])
|
||||
bodyPreferenceType = @"2";
|
||||
else if ([mimeSupport isEqualToString: @"2"] && [bodyPreferenceType isEqualToString: @"4"])
|
||||
bodyPreferenceType = @"4";
|
||||
else if ([mimeSupport isEqualToString: @"0"] && [bodyPreferenceType isEqualToString: @"4"])
|
||||
bodyPreferenceType = @"2";
|
||||
|
||||
|
||||
// Avoid writing to cache if there is nothing to change.
|
||||
if (![[[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"BodyPreferenceType"] isEqualToString: bodyPreferenceType] ||
|
||||
![[[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"] isEqualToString: mimeSupport])
|
||||
{
|
||||
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
|
||||
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
|
||||
}
|
||||
}
|
||||
|
||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
||||
|
||||
@@ -1139,6 +1195,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self processSyncGetChanges: theDocumentElement
|
||||
inCollection: collection
|
||||
withWindowSize: windowSize
|
||||
withMaxSyncResponseSize: theMaxSyncResponseSize
|
||||
withSyncKey: syncKey
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
@@ -1169,6 +1226,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
|
||||
}
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
|
||||
|
||||
// If we got any changes or if we have applied any commands
|
||||
// let's regenerate our SyncKey based on the collection tag.
|
||||
if ([changeBuffer length] || [commandsBuffer length])
|
||||
@@ -1179,7 +1238,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
// Use the SyncKey saved by processSyncGetChanges - if processSyncGetChanges is not called (because of getChanges=false)
|
||||
// SyncKey has the value of the previous sync operation.
|
||||
davCollectionTag = [[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncKey"];
|
||||
davCollectionTag = [folderMetadata objectForKey: @"SyncKey"];
|
||||
|
||||
if (!davCollectionTag)
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
@@ -1189,8 +1248,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (folderType == ActiveSyncMailFolder && [syncKey isEqualToString: @"-1"])
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
|
||||
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
|
||||
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
|
||||
{
|
||||
davCollectionTag = syncKeyInCache;
|
||||
*changeDetected = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the response buffer
|
||||
@@ -1211,7 +1275,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// MoreAvailable breaks Windows Mobile devices if not between <Status> and <Commands>
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/040b254e-f47e-4cc1-a397-6d8393cdb819/airsyncmoreavailable-breaks-windows-mobile-devices-what-am-i-doing-wrong?forum=os_exchangeprotocols
|
||||
if ([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"MoreAvailable"])
|
||||
if ([folderMetadata objectForKey: @"MoreAvailable"])
|
||||
[theBuffer appendString: @"<MoreAvailable/>"];
|
||||
|
||||
[theBuffer appendString: commandsBuffer];
|
||||
@@ -1324,21 +1388,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSArray *allCollections;
|
||||
NSData *d;
|
||||
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval;
|
||||
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
|
||||
BOOL changeDetected;
|
||||
|
||||
changeDetected = NO;
|
||||
|
||||
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
|
||||
|
||||
// We initialize our output buffer
|
||||
output = [[NSMutableString alloc] init];
|
||||
|
||||
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
|
||||
|
||||
//
|
||||
// We don't support yet empty Sync requests. See: http://msdn.microsoft.com/en-us/library/ee203280(v=exchg.80).aspx
|
||||
// We return '13' - see http://msdn.microsoft.com/en-us/library/gg675457(v=exchg.80).aspx
|
||||
//
|
||||
if (!theDocumentElement || [[(id)[theDocumentElement getElementsByTagName: @"Partial"] lastObject] textValue])
|
||||
{
|
||||
[output appendString: @"<Status>13</Status>"];
|
||||
[output appendString: @"</Sync>"];
|
||||
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
return;
|
||||
}
|
||||
|
||||
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
|
||||
defaultInterval = [defaults maximumSyncInterval];
|
||||
internalInterval = [defaults internalSyncInterval];
|
||||
|
||||
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
|
||||
if (heartbeatInterval == 0)
|
||||
heartbeatInterval = internalInterval = 1;
|
||||
|
||||
// We check to see if our heartbeat interval falls into the supported ranges.
|
||||
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
|
||||
{
|
||||
@@ -1355,7 +1440,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
|
||||
|
||||
// We enter our loop detection change
|
||||
for (i = 0; i < (defaultInterval/internalInterval); i++)
|
||||
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
|
||||
{
|
||||
s = [NSMutableString string];
|
||||
|
||||
@@ -1363,36 +1448,50 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
aCollection = [allCollections objectAtIndex: j];
|
||||
|
||||
[self processSyncCollection: aCollection inBuffer: s changeDetected: &changeDetected];
|
||||
[self processSyncCollection: aCollection
|
||||
inBuffer: s
|
||||
changeDetected: &changeDetected
|
||||
maxSyncResponseSize: maxSyncResponseSize];
|
||||
|
||||
if (maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize)
|
||||
break;
|
||||
}
|
||||
|
||||
if (changeDetected)
|
||||
{
|
||||
NSLog(@"Change detected, we push the content.");
|
||||
[self logWithFormat: @"Change detected, we push the content."];
|
||||
break;
|
||||
}
|
||||
else if (heartbeatInterval > 1)
|
||||
{
|
||||
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
|
||||
sleep(internalInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval);
|
||||
sleep(internalInterval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We always return the last generated response.
|
||||
// If we only return <Sync><Collections/></Sync>,
|
||||
// iOS powered devices will simply crash.
|
||||
[output appendString: s];
|
||||
|
||||
[output appendString: @"</Collections></Sync>"];
|
||||
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
|
||||
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
|
||||
{
|
||||
// We always return the last generated response.
|
||||
// If we only return <Sync><Collections/></Sync>,
|
||||
// iOS powered devices will simply crash.
|
||||
[output appendString: s];
|
||||
|
||||
[output appendString: @"</Collections></Sync>"];
|
||||
|
||||
d = [output dataUsingEncoding: NSUTF8StringEncoding];
|
||||
d = [d xml2wbxml];
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
// Avoid overloading the autorelease pool here, as Sync command can
|
||||
// generate fairly large responses.
|
||||
d = [output dataUsingEncoding: NSUTF8StringEncoding];
|
||||
RELEASE(output);
|
||||
|
||||
d = [d xml2wbxml];
|
||||
|
||||
[theResponse setContent: d];
|
||||
}
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large.
Load diff
@@ -47,7 +47,9 @@ 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>
|
||||
@@ -57,6 +59,10 @@ 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"
|
||||
@@ -69,6 +75,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <SOGo/SOGoUser.h>
|
||||
#include <SOGo/NSString+Utilities.h>
|
||||
|
||||
#import <Appointments/SOGoAptMailNotification.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwLowDateTime;
|
||||
uint32_t dwHighDateTime;
|
||||
@@ -204,12 +212,15 @@ 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;
|
||||
@@ -218,10 +229,35 @@ 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"])
|
||||
@@ -229,11 +265,22 @@ struct GlobalObjectId {
|
||||
}
|
||||
|
||||
key = nil;
|
||||
*theNativeTypeFound = 1;
|
||||
|
||||
if (theType == 2)
|
||||
key = htmlKey;
|
||||
else if (theType == 1)
|
||||
if (theType == 2 && htmlKey)
|
||||
{
|
||||
key = htmlKey;
|
||||
*theNativeTypeFound = 2;
|
||||
}
|
||||
else if (theType == 1 && plainKey)
|
||||
key = plainKey;
|
||||
else if (theType == 2 && plainKey)
|
||||
key = plainKey;
|
||||
else if (theType == 1 && htmlKey)
|
||||
{
|
||||
key = htmlKey;
|
||||
*theNativeTypeFound = 2;
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
@@ -251,9 +298,17 @@ struct GlobalObjectId {
|
||||
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"us-ascii";
|
||||
charset = @"utf-8";
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -298,7 +353,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;
|
||||
|
||||
@@ -309,9 +364,13 @@ struct GlobalObjectId {
|
||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"us-ascii";
|
||||
charset = @"utf-8";
|
||||
|
||||
s = [NSString stringWithData: body usingEncodingNamed: charset];
|
||||
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];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -407,14 +466,14 @@ struct GlobalObjectId {
|
||||
// We get the right part based on the preference
|
||||
if (theType == 1 || theType == 2)
|
||||
{
|
||||
if ([type isEqualToString: @"text"])
|
||||
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
|
||||
{
|
||||
NSString *s, *charset;
|
||||
|
||||
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"us-ascii";
|
||||
charset = @"utf-8";
|
||||
|
||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||
|
||||
@@ -428,25 +487,30 @@ 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];
|
||||
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
|
||||
}
|
||||
}
|
||||
else if (theType == 4)
|
||||
{
|
||||
// We sanitize the content *ONLY* for Outlook clients. Outlook has strange issues
|
||||
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
|
||||
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
|
||||
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
|
||||
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
|
||||
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
|
||||
d = [self _sanitizedMIMEMessage];
|
||||
else
|
||||
d = [self content];
|
||||
@@ -461,9 +525,37 @@ struct GlobalObjectId {
|
||||
- (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"];
|
||||
|
||||
@@ -512,6 +604,10 @@ struct GlobalObjectId {
|
||||
NSData *d, *globalObjId;
|
||||
NSArray *attachmentKeys;
|
||||
NSMutableString *s;
|
||||
|
||||
uint32_t v;
|
||||
NSString *p;
|
||||
|
||||
id value;
|
||||
|
||||
iCalCalendar *calendar;
|
||||
@@ -552,8 +648,34 @@ struct GlobalObjectId {
|
||||
if (value)
|
||||
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Importance - FIXME
|
||||
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
|
||||
// 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];
|
||||
|
||||
// Read
|
||||
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
||||
@@ -617,18 +739,18 @@ struct GlobalObjectId {
|
||||
|
||||
[s appendFormat: @"<AllDayEvent xmlns=\"Email:\">%d</AllDayEvent>", ([event isAllDay] ? 1 : 0)];
|
||||
|
||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||
// StartTime -- https://msdn.microsoft.com/en-us/library/ee203365%28v=exchg.80%29.aspx
|
||||
if ([event startDate])
|
||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ([event timeStampAsDate])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationInContext: context]];
|
||||
else if ([event created])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
||||
// EndTime -- https://msdn.microsoft.com/en-us/library/ee158628(v=exchg.80).aspx
|
||||
if ([event endDate])
|
||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// FIXME: Single appointment - others are not supported right now
|
||||
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
|
||||
@@ -676,7 +798,9 @@ 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
|
||||
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
|
||||
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
|
||||
|
||||
[s appendString: @"</MeetingRequest>"];
|
||||
|
||||
// ContentClass
|
||||
@@ -703,6 +827,42 @@ struct GlobalObjectId {
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -728,17 +888,31 @@ struct GlobalObjectId {
|
||||
|
||||
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 (!truncated)
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Data>%@</Data>", content];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", len];
|
||||
[s appendFormat: @"<Body xmlns=\"Email:\">%@</Body>", content];
|
||||
[s appendFormat: @"<BodyTruncated xmlns=\"Email:\">%d</BodyTruncated>", truncated];
|
||||
}
|
||||
[s appendString: @"</Body>"];
|
||||
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>"];
|
||||
}
|
||||
}
|
||||
|
||||
// Attachments -namespace 16
|
||||
@@ -746,9 +920,12 @@ struct GlobalObjectId {
|
||||
if ([attachmentKeys count])
|
||||
{
|
||||
int i;
|
||||
|
||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
|
||||
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
[s appendString: @"<Attachments xmlns=\"Email:\">"];
|
||||
else
|
||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
for (i = 0; i < [attachmentKeys count]; i++)
|
||||
{
|
||||
value = [attachmentKeys objectAtIndex: i];
|
||||
@@ -759,11 +936,22 @@ 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
|
||||
[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: @"<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: @"<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];
|
||||
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 appendString: @"</Attachment>"];
|
||||
}
|
||||
|
||||
@@ -774,10 +962,40 @@ 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"])
|
||||
{
|
||||
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.
|
||||
@@ -836,6 +1054,65 @@ 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;
|
||||
return [o autorelease];
|
||||
}
|
||||
|
||||
- (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.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"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"];
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ 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>
|
||||
@@ -274,11 +275,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// 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: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
|
||||
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 appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
|
||||
@@ -332,7 +342,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
BOOL isAllDay;
|
||||
|
||||
if ((o = [theValues objectForKey: @"UID"]))
|
||||
@@ -384,15 +393,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Some Windows devices don't send during event updates.
|
||||
//if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
// {
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
//}
|
||||
// }
|
||||
//else
|
||||
{
|
||||
// We haven't received a timezone, let's use the user's timezone
|
||||
// specified in SOGo for now.
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
}
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
if ([[[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: @"Location"]))
|
||||
[self setLocation: o];
|
||||
|
||||
@@ -402,21 +424,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[start setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
if (isAllDay)
|
||||
{
|
||||
[start setDate: o];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDateTime: o];
|
||||
}
|
||||
}
|
||||
@@ -429,19 +443,11 @@ 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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ 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>
|
||||
@@ -49,6 +50,7 @@ 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
|
||||
@@ -62,11 +64,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
s = [NSMutableString string];
|
||||
|
||||
// Complete
|
||||
o = [self completed];
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
|
||||
|
||||
// DateCompleted
|
||||
if (o)
|
||||
if ((o = [self completed]))
|
||||
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Start date
|
||||
@@ -97,6 +98,7 @@ 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"])
|
||||
@@ -104,6 +106,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", v];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
@@ -126,11 +130,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// 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: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
|
||||
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>"];
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -143,8 +156,6 @@ 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];
|
||||
@@ -154,9 +165,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[self setSummary: o];
|
||||
|
||||
// FIXME: merge with iCalEvent
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
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 ([[theValues objectForKey: @"Complete"] intValue] &&
|
||||
((o = [theValues objectForKey: @"DateCompleted"])) )
|
||||
@@ -165,27 +183,32 @@ 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];
|
||||
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"StartDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[due setTimeZone: tz];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
|
||||
@@ -167,9 +167,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 and 6
|
||||
* Community ENTerprise Operating System (CentOS) 5 and 6
|
||||
* Debian GNU/Linux 5.0 (Lenny) to 7.0 (Wheezy)
|
||||
* Red Hat Enterprise Linux (RHEL) Server 5, 6 and 7
|
||||
* Community ENTerprise Operating System (CentOS) 5, 6 and 7
|
||||
* Debian GNU/Linux 6.0 (Squeeze) to 8.0 (Jessie)
|
||||
* Ubuntu 10.04 (Lucid) to 14.04 (Trusty)
|
||||
|
||||
Make sure the required components are started automatically at boot time
|
||||
@@ -194,7 +194,8 @@ 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 distribution.
|
||||
installation for a Red Hat or CentOS 6 distribution. Most of these steps
|
||||
should apply to all supported operating systems.
|
||||
|
||||
Software Downloads
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@@ -566,17 +567,29 @@ 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`
|
||||
* `Spanish`
|
||||
* `Slovak`
|
||||
* `SpanishSpain`
|
||||
* `SpanishArgentina`
|
||||
* `Swedish`
|
||||
* `Ukrainian`
|
||||
* `Welsh`
|
||||
|
||||
|D |SOGoNotifyOnPersonalModifications
|
||||
@@ -649,7 +662,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: `( "Czech", "Welsh", "English", "Spanish", "French", "German", "Italian", "Hungarian", "Dutch", "BrazilianPortuguese", "Polish", "Russian", Ukrainian", "Swedish" )`
|
||||
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" )`
|
||||
|
||||
|D |SOGoHideSystemEMail
|
||||
|Parameter used to control if SOGo should hide or not the system email
|
||||
@@ -843,7 +856,7 @@ source:
|
||||
|
||||
[cols="^3,>47,50a"]
|
||||
|=======================================================================
|
||||
.33+|D <|SOGoUserSources
|
||||
.34+|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
|
||||
@@ -911,7 +924,8 @@ 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.
|
||||
limit. If set to `-1`, no limit is imposed but the resource will
|
||||
be marked as busy the first time it is booked.
|
||||
|
||||
|filter (optional)
|
||||
|The filter to use for LDAP queries, it should be defined as an
|
||||
@@ -998,7 +1012,17 @@ 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.
|
||||
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.
|
||||
|
||||
|isAddressBook
|
||||
|If set to `YES`, this LDAP source is used as a shared address book
|
||||
@@ -1037,6 +1061,11 @@ 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.
|
||||
@@ -1326,27 +1355,27 @@ The following table describes the parameters that were set:
|
||||
|
||||
[cols="3,47,50a"]
|
||||
|=======================================================================
|
||||
|D |SOGoProfileURL
|
||||
|S |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`.
|
||||
|
||||
|D |OCSFolderInfoURL
|
||||
|S |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`.
|
||||
|
||||
|D |OCSSessionsFolderURL
|
||||
|S |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`.
|
||||
|
||||
|D |OCSEMailAlarmsFolderURL
|
||||
|S |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
|
||||
@@ -1476,7 +1505,10 @@ 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.
|
||||
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.
|
||||
|
||||
|DomainFieldName (optional)
|
||||
|If set, SOGo will use the value of that field as the domain associated
|
||||
@@ -1675,6 +1707,13 @@ 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.
|
||||
@@ -1778,6 +1817,12 @@ 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
|
||||
@@ -2091,7 +2136,8 @@ Multi-domains Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want your installation to isolate two groups of users, you must
|
||||
define a distinct authentication source for each _domain_. Following is
|
||||
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
|
||||
the same configuration that now includes two domains (acme.com and
|
||||
coyote.com):
|
||||
|
||||
@@ -2099,7 +2145,7 @@ coyote.com):
|
||||
{
|
||||
...
|
||||
domains = {
|
||||
acme = {
|
||||
acme.com = {
|
||||
SOGoMailDomain = acme.com;
|
||||
SOGoDraftsFolderName = Drafts;
|
||||
SOGoUserSources = (
|
||||
@@ -2120,7 +2166,7 @@ coyote.com):
|
||||
}
|
||||
);
|
||||
};
|
||||
coyote = {
|
||||
coyote.com = {
|
||||
SOGoMailDomain = coyote.com;
|
||||
SOGoIMAPServer = imap.coyote.com;
|
||||
SOGoUserSources = (
|
||||
@@ -2151,7 +2197,7 @@ domains.
|
||||
[cols="3,47,50a"]
|
||||
|=======================================================================
|
||||
|S |SOGoEnableDomainBasedUID
|
||||
|Parameter used to activate user identification by domain. Users will be
|
||||
|Parameter used to enable 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
|
||||
@@ -2340,8 +2386,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 ActiveSync
|
||||
--------------------
|
||||
Microsoft Enterprise ActiveSync
|
||||
-------------------------------
|
||||
|
||||
SOGo supports the Microsoft ActiveSync protocol.
|
||||
|
||||
@@ -2390,6 +2436,15 @@ _SOGoMaximumPingInterval_.
|
||||
|
||||
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.
|
||||
@@ -2398,28 +2453,31 @@ 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. 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.
|
||||
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.
|
||||
* 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. This limitation will eventually be overcome in
|
||||
SOGo.
|
||||
to handle many clients. Make sure you tune your SOGo server when having
|
||||
lots of ActiveSync clients.
|
||||
* Repetitive events with occurrences exceptions are currently not
|
||||
supported.
|
||||
* Outlook 2013 Autodiscovery is currently not supported.
|
||||
@@ -2441,13 +2499,90 @@ user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/
|
||||
|
||||
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
|
||||
----------
|
||||
|
||||
@@ -2577,6 +2712,16 @@ current version of SOGo from the previous release.
|
||||
|
||||
[cols="100a"]
|
||||
|=======================================================================
|
||||
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:
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ Red Hat Enterprise Linux v6 x86_64
|
||||
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_nightly.html.
|
||||
http://www.sogo.nu/english/downloads/backend.html.
|
||||
|
||||
In order to satisfy certain dependencies, you should also add the EPEL
|
||||
source corresponding to your distribution and architecture. More
|
||||
@@ -266,32 +266,30 @@ from this guide.
|
||||
|
||||
[NOTE]
|
||||
Samba4/OpenChange are not available for now on CentOS 5 i386/x86_64,
|
||||
CentOS 6 i386 and CentOS 7.
|
||||
and CentOS 7. On Debian-based systems, packages are available only on
|
||||
the x86_64 platform.
|
||||
|
||||
Debian 7.0 (Wheezy) and Ubuntu 12.04 (Precise Pangolin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Debian 7 (Wheezy) and Ubuntu 12.04 (Precise Pangolin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SOGo, OpenChange 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
|
||||
apt sources.
|
||||
|
||||
For Samba 4, you need to use the _wheezy-backports_ repository. To do so, create
|
||||
`/etc/apt/sources.list.d/backports.list`:
|
||||
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:
|
||||
|
||||
deb http://http.debian.net/debian wheezy-backports main
|
||||
|
||||
On Ubuntu 12.04, you will also have to add the Wheezy sources:
|
||||
|
||||
deb http://ftp.us.debian.org/debian wheezy main
|
||||
deb http://security.debian.org/ wheezy/updates main
|
||||
|
||||
Then install Samba 4 on top of an existing SOGo
|
||||
installation:
|
||||
|
||||
----
|
||||
apt-get update
|
||||
apt-get -t wheezy-backports install samba samba-dev
|
||||
apt-get install samba samba-dev
|
||||
----
|
||||
|
||||
Once completed, install the packages related to OpenChange and the SOGo
|
||||
@@ -303,7 +301,12 @@ apt-get install openchangeserver \
|
||||
openchangeproxy \
|
||||
python-ocsmanager \
|
||||
mysql-server \
|
||||
python-mysqldb
|
||||
python-mysqldb \
|
||||
openchange-ocsmanager \
|
||||
openchange-rpcproxy \
|
||||
python-sievelib \
|
||||
python-spyne \
|
||||
python-rpclib
|
||||
----
|
||||
|
||||
Once the packages are installed, refer to the _Configuration_ chapter
|
||||
@@ -315,10 +318,8 @@ disable the upstart check. For more details, refer to:
|
||||
https://wiki.samba.org/index.php/Samba4/InitScript
|
||||
|
||||
|
||||
Ubuntu 14.04 (Trusty Tahr)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For Ubuntu 14.04, you must not use the Debian Wheezy backports.
|
||||
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
|
||||
@@ -341,12 +342,26 @@ apt-get install openchangeserver \
|
||||
openchangeproxy \
|
||||
python-ocsmanager \
|
||||
mysql-server \
|
||||
python-mysqldb
|
||||
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
|
||||
-------------
|
||||
@@ -390,6 +405,7 @@ Add the following parameters to the `[global]` section of the
|
||||
|
||||
----
|
||||
### Configuration required by OpenChange server ###
|
||||
dsdb:schema update allowed = true
|
||||
dcerpc endpoint servers = epmapper, mapiproxy, dnsserver
|
||||
dcerpc_mapiproxy:server = true
|
||||
dcerpc_mapiproxy:interfaces = exchange_emsmdb, exchange_nsp, exchange_ds_rfr
|
||||
@@ -407,6 +423,7 @@ 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
|
||||
@@ -533,7 +550,7 @@ 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/`.
|
||||
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
|
||||
|
||||
The configuration requires three Apache modules: _mod_proxy_,
|
||||
_mod_proxy_http_ and _mod_wsgi_. These are usually already installed but
|
||||
@@ -541,13 +558,17 @@ 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` (or
|
||||
`python26-mod_wsgi.conf` on RHELv5).
|
||||
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
|
||||
|
||||
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
|
||||
@@ -567,9 +588,102 @@ On Debian-based distributions, do:
|
||||
|
||||
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
|
||||
|
||||
[NOTE]
|
||||
Debian-based distributions are not supported anymore for
|
||||
OCSManager/rpcproxy. Support will soon resume.
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- TODO have the build system take care of this -->
|
||||
<releaseinfo>Version 2.2.12 - December 2014</releaseinfo>
|
||||
<subtitle>for version 2.2.12</subtitle>
|
||||
<date>2014-12-18</date>
|
||||
<releaseinfo>Version 2.3.0 - June 2015</releaseinfo>
|
||||
<subtitle>for version 2.3.0</subtitle>
|
||||
<date>2015-06-01</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.
|
After Width: | Height: | Size: 6.3 KiB |
@@ -6,13 +6,13 @@
|
||||
Authors:
|
||||
- Inverse inc. <info@inverse.ca>
|
||||
|
||||
Copyright (C) 2008-2014 Inverse inc.
|
||||
Copyright (C) 2008-2015 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.2.12
|
||||
:release_version: 2.3.0
|
||||
|
||||
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
|
||||
+2
-2
@@ -245,7 +245,7 @@ static BOOL debugLeaks;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@"No value specified for '%@'", *urlString);
|
||||
[self errorWithFormat: @"No value specified for '%@'", *urlString];
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ static BOOL debugLeaks;
|
||||
if (debugLeaks)
|
||||
{
|
||||
if (debugOn)
|
||||
NSLog (@"allocated classes:\n%s", GSDebugAllocationList (YES));
|
||||
[self logWithFormat: @"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-2009 Inverse inc.
|
||||
Copyright (C) 2006-2015 Inverse inc.
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
@@ -1,3 +1,172 @@
|
||||
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 Enhancements
|
||||
- sharing request among different Outlook versions (Zentyal)
|
||||
- improve sync speed from Outlook by non-reprocessing already downloaded unread mails (Zentyal)
|
||||
- give support to calendar sharing 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 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)
|
||||
-------------------
|
||||
|
||||
@@ -12,12 +181,12 @@ Enhancements
|
||||
- updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations
|
||||
|
||||
Bug fixes
|
||||
- fix for privacy and categories for EAS (#3022)
|
||||
- fixed for privacy and categories for EAS (#3022)
|
||||
- correctly set MeetingStatus for EAS on iOS devices
|
||||
- Ubuntu Lucid fixes for EAS
|
||||
- fix calendar reminders for future events (#3008)
|
||||
- fixed calendar reminders for future events (#3008)
|
||||
- make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003)
|
||||
- fix task description truncation affecting WP8 phones over EAS (#3028)
|
||||
- fixed task description truncation affecting WP8 phones over EAS (#3028)
|
||||
|
||||
2.2.11a (2014-12-10)
|
||||
--------------------
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* 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
|
||||
@@ -0,0 +1,214 @@
|
||||
/* 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
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
all::
|
||||
@echo " Python executable: ${PYTHON}"
|
||||
|
||||
SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py "private dir")
|
||||
SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py 'private dir' || echo /var/lib/samba/private)
|
||||
|
||||
$(SOGOBACKEND)_PRINCIPAL_CLASS = MAPIApplication
|
||||
|
||||
@@ -100,6 +100,7 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
MAPIStoreMailAttachment.m \
|
||||
MAPIStoreMailContext.m \
|
||||
MAPIStoreMailFolder.m \
|
||||
MAPIStoreMailFolderTable.m \
|
||||
MAPIStoreMailMessage.m \
|
||||
MAPIStoreMailVolatileMessage.m \
|
||||
MAPIStoreMailMessageTable.m \
|
||||
@@ -110,6 +111,8 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
\
|
||||
MAPIStoreFallbackContext.m \
|
||||
\
|
||||
MAPIStoreSharingMessage.m \
|
||||
\
|
||||
NSArray+MAPIStore.m \
|
||||
NSData+MAPIStore.m \
|
||||
NSDate+MAPIStore.m \
|
||||
@@ -120,7 +123,9 @@ $(SOGOBACKEND)_OBJC_FILES += \
|
||||
iCalEvent+MAPIStore.m \
|
||||
iCalTimeZone+MAPIStore.m \
|
||||
\
|
||||
RTFHandler.m
|
||||
RTFHandler.m \
|
||||
\
|
||||
Codepages.m
|
||||
|
||||
|
||||
$(SOGOBACKEND)_RESOURCE_FILES += \
|
||||
@@ -171,7 +176,7 @@ $(SOGOBACKEND)_LIB_DIRS += \
|
||||
$(LIBMAPISTORE_LIBS)
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += \
|
||||
-Werror -Wall \
|
||||
-Wall \
|
||||
-DSAMBA_PRIVATE_DIR=@"\"$(SAMBA_PRIVATE_DIR)\"" \
|
||||
$(LIBMAPI_CFLAGS) \
|
||||
$(LIBMAPISTORE_CFLAGS) \
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#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"
|
||||
@@ -263,7 +264,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
@@ -272,7 +273,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
recipient->username = NULL;
|
||||
entryId = MAPIStoreExternalEntryId (cn, email);
|
||||
}
|
||||
recipient->type = MAPI_TO;
|
||||
recipient->type = [[person role] isEqualToString: @"OPT-PARTICIPANT"] ? MAPI_CC : MAPI_TO;
|
||||
|
||||
/* properties */
|
||||
p = 0;
|
||||
@@ -282,7 +283,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++;
|
||||
@@ -364,7 +365,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
@@ -382,7 +383,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++;
|
||||
@@ -791,11 +792,20 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
enum mapistore_error rc;
|
||||
NSCalendarDate *dateValue;
|
||||
NSCalendarDate *dateValue, *start;
|
||||
|
||||
if ([event isRecurrent])
|
||||
{
|
||||
dateValue = [[event startDate] hour: 0 minute: 0 second: 0];
|
||||
/* [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];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -894,7 +904,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
}
|
||||
else
|
||||
dateValue = [NSCalendarDate dateWithYear: 4500 month: 8 day: 31
|
||||
hour: 23 minute: 59 second: 59
|
||||
hour: 23 minute: 59 second: 00
|
||||
timeZone: utcTZ];
|
||||
*data = [dateValue asFileTimeInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
@@ -921,7 +931,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username);
|
||||
}
|
||||
else
|
||||
@@ -1191,11 +1201,31 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
return [self getYes: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagSensitivity: (void **) data // not implemented, depends on CLASS
|
||||
- (int) getPidTagSensitivity: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
// normal = 0, personal?? = 1, private = 2, confidential = 3
|
||||
return [self getLongZero: data inMemCtx: 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;
|
||||
}
|
||||
|
||||
- (int) getPidTagImportance: (void **) data
|
||||
@@ -1214,6 +1244,22 @@ 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
|
||||
{
|
||||
@@ -1231,7 +1277,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
/* Avoiding those trail weird characters at event description */
|
||||
range = [stringValue rangeOfString: trimingString
|
||||
options: NSBackwardsSearch];
|
||||
if (range.location > 0)
|
||||
if (range.location != NSNotFound)
|
||||
{
|
||||
stringValue = [stringValue substringToIndex: (NSMaxRange(range) -1)];
|
||||
}
|
||||
@@ -1415,8 +1461,6 @@ static NSCharacterSet *hexCharacterSet = nil;
|
||||
firstStartDate = [event firstRecurrenceStartDate];
|
||||
if (firstStartDate)
|
||||
{
|
||||
[firstStartDate setTimeZone: timeZone];
|
||||
|
||||
arp = talloc_zero (NULL, struct AppointmentRecurrencePattern);
|
||||
[rule fillRecurrencePattern: &arp->RecurrencePattern
|
||||
withEvent: event
|
||||
@@ -1426,6 +1470,12 @@ 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;
|
||||
@@ -1826,16 +1876,25 @@ ReservedBlockEE2Size: 00 00 00 00
|
||||
NSArray *alarms;
|
||||
NSUInteger count, max;
|
||||
iCalAlarm *currentAlarm;
|
||||
NSString *action;
|
||||
NSString *action, *webstatus;
|
||||
|
||||
alarms = [event alarms];
|
||||
max = [alarms count];
|
||||
for (count = 0; !alarm && count < max; count++)
|
||||
{
|
||||
currentAlarm = [alarms objectAtIndex: count];
|
||||
action = [[currentAlarm action] lowercaseString];
|
||||
if (!action || [action isEqualToString: @"display"])
|
||||
ASSIGN (alarm, currentAlarm);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
alarmSet = YES;
|
||||
@@ -2014,17 +2073,23 @@ ReservedBlockEE2Size: 00 00 00 00
|
||||
enum mapistore_error rc;
|
||||
iCalTimeZone *icalTZ;
|
||||
|
||||
if ([event isRecurrent])
|
||||
/* [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)
|
||||
{
|
||||
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;
|
||||
/* [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;
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
@@ -100,6 +100,14 @@
|
||||
[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];
|
||||
|
||||
@@ -121,12 +129,21 @@
|
||||
else if ([roles containsObject: SOGoCalendarRole_PublicViewer]
|
||||
&& [roles containsObject: SOGoCalendarRole_PrivateViewer]
|
||||
&& [roles containsObject: SOGoCalendarRole_ConfidentialViewer])
|
||||
rights |= RightsReadItems | 0x1800;
|
||||
// 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;
|
||||
|
||||
if (rights != 0)
|
||||
rights |= RoleNone; /* actually "folder visible" */
|
||||
|
||||
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
|
||||
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
@@ -169,6 +186,14 @@
|
||||
[(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;
|
||||
static Class NSArrayK, MAPIStoreAppointmentWrapperK;
|
||||
|
||||
@implementation SOGoAppointmentObject (MAPIStoreExtension)
|
||||
|
||||
@@ -97,6 +97,7 @@ static Class NSArrayK;
|
||||
+ (void) initialize
|
||||
{
|
||||
NSArrayK = [NSArray class];
|
||||
MAPIStoreAppointmentWrapperK = [MAPIStoreAppointmentWrapper class];
|
||||
}
|
||||
|
||||
+ (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
|
||||
@@ -180,6 +181,12 @@ static Class NSArrayK;
|
||||
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];
|
||||
@@ -205,13 +212,23 @@ static Class NSArrayK;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* getters */
|
||||
- (int) getPidLidFInvited: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
- (MAPIStoreAppointmentWrapper *) _appointmentWrapper
|
||||
{
|
||||
return [self getYes: data inMemCtx: 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;
|
||||
}
|
||||
|
||||
|
||||
/* getters */
|
||||
- (int) getPidTagMessageClass: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
@@ -324,100 +341,63 @@ static Class NSArrayK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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];
|
||||
[[self _appointmentWrapper] 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;
|
||||
|
||||
@@ -182,6 +182,9 @@ 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)];
|
||||
|
||||
@@ -170,6 +170,21 @@
|
||||
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
|
||||
{
|
||||
@@ -723,6 +738,107 @@
|
||||
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
|
||||
//
|
||||
@@ -1133,12 +1249,18 @@ fromProperties: (NSDictionary *) attachmentProps
|
||||
|
||||
|
||||
//
|
||||
// job title, nickname, company name, deparment, work url, im address/screen name and birthday
|
||||
// job title, profession, 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];
|
||||
@@ -1173,6 +1295,52 @@ 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,6 +220,9 @@ 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)];
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#import "MAPIStoreContext.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <dlinklist.h>
|
||||
#include <stdbool.h>
|
||||
#include <gen_ndr/exchange.h>
|
||||
#include <util/attr.h>
|
||||
@@ -276,6 +277,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
{
|
||||
NSString *username;
|
||||
|
||||
if (newConnInfo == NULL)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [self init]))
|
||||
{
|
||||
ASSIGN (contextUrl, newUrl);
|
||||
@@ -367,8 +373,9 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSString *objectURL, *url;
|
||||
// TDB_DATA key, dbuf;
|
||||
|
||||
url = [[contextUrl absoluteString]
|
||||
stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
url = [contextUrl absoluteString];
|
||||
// FIXME transform percent escapes but not for user part of the url
|
||||
//stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
objectURL = [[userContext mapping] urlFromID: fmid];
|
||||
if (objectURL)
|
||||
{
|
||||
@@ -412,7 +419,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
withFID: (uint64_t) newFid
|
||||
{
|
||||
enum mapistore_error rc;
|
||||
MAPIStoreMapping *mapping;
|
||||
MAPIStoreFolder *baseFolder;
|
||||
SOGoFolder *currentFolder;
|
||||
WOContext *woContext;
|
||||
@@ -420,10 +426,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
NSArray *pathComponents;
|
||||
NSUInteger count, max;
|
||||
|
||||
mapping = [userContext mapping];
|
||||
if (![mapping urlFromID: newFid])
|
||||
[mapping registerURL: [contextUrl absoluteString]
|
||||
withID: newFid];
|
||||
[userContext activateWithUser: activeUser];
|
||||
woContext = [userContext woContext];
|
||||
|
||||
@@ -549,7 +551,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
|
||||
{
|
||||
uint64_t newVersionNumber;
|
||||
|
||||
if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, &newVersionNumber)
|
||||
if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, connInfo->username, &newVersionNumber)
|
||||
!= MAPI_E_SUCCESS)
|
||||
abort ();
|
||||
|
||||
@@ -566,9 +568,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, max, &numbers)
|
||||
memCtx, connInfo->username, max, &numbers)
|
||||
!= MAPI_E_SUCCESS || numbers->cValues != max)
|
||||
abort ();
|
||||
for (count = 0; count < max; count++)
|
||||
|
||||
@@ -138,7 +138,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
MAPIStoreMapping *mapping;
|
||||
NSRange slashRange;
|
||||
|
||||
if (isMove && [targetFolder isKindOfClass: MAPIStoreDBFolderK])
|
||||
pathComponent = nil;
|
||||
|
||||
if (isMove && ([targetFolder isKindOfClass: MAPIStoreDBFolderK] || !targetFolder))
|
||||
{
|
||||
path = [sogoObject path];
|
||||
slashRange = [path rangeOfString: @"/" options: NSBackwardsSearch];
|
||||
@@ -147,14 +149,28 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
|
||||
format: @"db folder path must start with a '/'"];
|
||||
else
|
||||
pathComponent = [path substringFromIndex: slashRange.location + 1];
|
||||
targetPath = [[targetFolder sogoObject] path];
|
||||
newPath = [NSString stringWithFormat: @"%@/%@",
|
||||
targetPath, pathComponent];
|
||||
[dbFolder changePathTo: newPath];
|
||||
|
||||
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];
|
||||
|
||||
mapping = [self mapping];
|
||||
newURL = [NSString stringWithFormat: @"%@%@/",
|
||||
[targetFolder url], pathComponent];
|
||||
|
||||
if (targetFolder)
|
||||
newURL = [NSString stringWithFormat: @"%@%@/",
|
||||
[targetFolder url], pathComponent];
|
||||
else
|
||||
newURL = [NSString stringWithFormat: @"sogo://%@@fallback/%@/",
|
||||
[[self userContext] username], pathComponent];
|
||||
|
||||
[mapping updateID: [self objectId]
|
||||
withURL: newURL];
|
||||
|
||||
|
||||
@@ -50,19 +50,22 @@
|
||||
NSUInteger count;
|
||||
enum MAPITAGS faiProperties[] = { 0x68350102, 0x683c0102, 0x683e0102,
|
||||
0x683f0102, 0x68410003, 0x68420102,
|
||||
0x68450102, 0x68460003 };
|
||||
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);
|
||||
|
||||
properties = talloc_zero (memCtx, struct SPropTagArray);
|
||||
properties->cValues = MAPIStoreSupportedPropertiesCount + 8;
|
||||
properties->cValues = MAPIStoreSupportedPropertiesCount + faiSize;
|
||||
properties->aulPropTag = talloc_array (properties, enum MAPITAGS,
|
||||
MAPIStoreSupportedPropertiesCount + 8);
|
||||
MAPIStoreSupportedPropertiesCount + faiSize);
|
||||
|
||||
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 < 8; count++)
|
||||
for (count = 0; count < faiSize; count++)
|
||||
properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count]
|
||||
= faiProperties[count];
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#undef DEBUG
|
||||
#include <inttypes.h>
|
||||
#include <dlinklist.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
|
||||
@implementation MAPIStoreFallbackContext
|
||||
@@ -55,7 +56,10 @@
|
||||
NSString *baseURL, *url, *name;
|
||||
MAPIStoreUserContext *userContext;
|
||||
|
||||
baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName];
|
||||
baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"]];
|
||||
|
||||
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
context->url = [baseURL asUnicodeInMemCtx: context];
|
||||
@@ -99,7 +103,10 @@
|
||||
forUser: (NSString *) userName
|
||||
{
|
||||
return [NSString stringWithFormat: @"sogo://%@@fallback/0x%.16"PRIx64"/",
|
||||
userName, (unsigned long long) fid];
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
(unsigned long long) fid];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -150,6 +150,7 @@
|
||||
|
||||
/* helpers */
|
||||
- (uint64_t) idForObjectWithKey: (NSString *) childKey;
|
||||
- (MAPIStoreFolder *) rootContainer;
|
||||
|
||||
/* subclasses */
|
||||
- (MAPIStoreMessage *) createMessage;
|
||||
@@ -184,12 +185,6 @@
|
||||
/* 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
|
||||
|
||||
|
||||
+76
-184
@@ -29,6 +29,7 @@
|
||||
#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>
|
||||
@@ -52,6 +53,7 @@
|
||||
#import <SOGo/SOGoCacheGCSFolder.h>
|
||||
#import "SOGoMAPIDBMessage.h"
|
||||
#import "SOGoCacheGCSObject+MAPIStore.h"
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
|
||||
#include <gen_ndr/exchange.h>
|
||||
|
||||
@@ -100,6 +102,8 @@ 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];
|
||||
@@ -231,9 +235,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
SOGoFolder *sogoFolder;
|
||||
WOContext *woContext;
|
||||
|
||||
childFolder = nil;
|
||||
if ([[self folderKeys] containsObject: folderKey])
|
||||
{
|
||||
woContext = [[self userContext] woContext];
|
||||
/* We activate the user for the context using the root folder
|
||||
context as there are times where the active user is not
|
||||
matching with the one stored in the application context
|
||||
and SOGo object is storing cached data with the wrong user */
|
||||
[[self userContext] activateWithUser: [woContext activeUser]];
|
||||
sogoFolder = [sogoObject lookupName: folderKey inContext: woContext
|
||||
acquire: NO];
|
||||
if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK])
|
||||
@@ -278,6 +288,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
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]];
|
||||
@@ -386,7 +402,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
withRow: (struct SRow *) aRow
|
||||
andFID: (uint64_t) fid
|
||||
{
|
||||
int rc = MAPISTORE_SUCCESS;
|
||||
BOOL mapped;
|
||||
enum mapistore_error rc = MAPISTORE_SUCCESS;
|
||||
MAPIStoreMapping *mapping;
|
||||
NSString *baseURL, *childURL, *folderKey;
|
||||
MAPIStoreFolder *childFolder;
|
||||
@@ -414,7 +431,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
childURL = [NSString stringWithFormat: @"%@%@/",
|
||||
baseURL,
|
||||
[folderKey stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
[mapping registerURL: childURL withID: fid];
|
||||
|
||||
mapped = [mapping registerURL: childURL withID: fid];
|
||||
if (!mapped)
|
||||
/* Enforce the creation if the backend does know the fid */
|
||||
[mapping updateURL: childURL withID: fid];
|
||||
|
||||
childFolder = [self lookupFolder: folderKey];
|
||||
if (childFolder)
|
||||
{
|
||||
@@ -433,16 +455,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (void) deleteFolderImpl
|
||||
- (int) deleteFolder
|
||||
{
|
||||
// TODO: raise exception in case underlying delete fails?
|
||||
// [propsMessage delete];
|
||||
[dbFolder delete];
|
||||
}
|
||||
|
||||
- (int) deleteFolder
|
||||
{
|
||||
[self deleteFolderImpl];
|
||||
|
||||
[self cleanupCaches];
|
||||
|
||||
@@ -503,6 +520,12 @@ 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;
|
||||
@@ -564,12 +587,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)
|
||||
@@ -602,51 +625,9 @@ 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];
|
||||
[mapping unregisterURLWithID: mid];
|
||||
/* Ensure we are respecting flags parameter */
|
||||
[mapping unregisterURLWithID: mid andFlags: flags];
|
||||
[self cleanupCaches];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -676,6 +657,7 @@ 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];
|
||||
|
||||
@@ -706,7 +688,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
[destMsg save: memCtx];
|
||||
if (!wantCopy)
|
||||
rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0];
|
||||
/* 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];
|
||||
|
||||
end:
|
||||
//talloc_free (memCtx);
|
||||
@@ -767,13 +751,6 @@ 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];
|
||||
@@ -845,7 +822,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [message url]];
|
||||
[self deleteMessageWithMID: fmid andFlags: 0];
|
||||
[self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE];
|
||||
[mapping registerURL: [targetMessage url]
|
||||
withID: fmid];
|
||||
}
|
||||
@@ -866,7 +843,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [message url]];
|
||||
[self deleteMessageWithMID: fmid andFlags: 0];
|
||||
[self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE];
|
||||
[mapping registerURL: [targetMessage url]
|
||||
withID: fmid];
|
||||
}
|
||||
@@ -893,16 +870,23 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
}
|
||||
|
||||
if (isMove)
|
||||
{
|
||||
fmid = [mapping idFromURL: [self url]];
|
||||
[mapping unregisterURLWithID: fmid];
|
||||
[self deleteFolderImpl];
|
||||
[mapping registerURL: [newFolder url]
|
||||
withID: fmid];
|
||||
}
|
||||
[self deleteFolder];
|
||||
|
||||
[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;
|
||||
@@ -946,6 +930,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
NSString *baseURL, *URL, *key;
|
||||
NSArray *newIDs;
|
||||
uint64_t idNbr;
|
||||
bool softDeleted;
|
||||
|
||||
baseURL = [self url];
|
||||
|
||||
@@ -956,8 +941,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
key = [keys objectAtIndex: count];
|
||||
URL = [NSString stringWithFormat: @"%@%@", baseURL, key];
|
||||
idNbr = [mapping idFromURL: URL];
|
||||
if (idNbr == NSNotFound)
|
||||
idNbr = [mapping idFromURL: URL isSoftDeleted: &softDeleted];
|
||||
if (idNbr == NSNotFound && !softDeleted)
|
||||
[missingURLs addObject: URL];
|
||||
}
|
||||
|
||||
@@ -967,113 +952,6 @@ 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
|
||||
@@ -1088,6 +966,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
MAPIStoreMapping *mapping;
|
||||
struct UI8Array_r *fmids;
|
||||
uint64_t fmid;
|
||||
bool softDeleted;
|
||||
|
||||
keys = [self getDeletedKeysFromChangeNumber: changeNum andCN: &cnNbr
|
||||
inTableType: tableType];
|
||||
@@ -1114,10 +993,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
{
|
||||
url = [NSString stringWithFormat: format,
|
||||
baseURL, [keys objectAtIndex: count]];
|
||||
fmid = [mapping idFromURL: url];
|
||||
fmid = [mapping idFromURL: url isSoftDeleted: &softDeleted];
|
||||
if (fmid != NSNotFound) /* if no fmid is returned, then the object
|
||||
"never existed" in the OpenChange
|
||||
databases */
|
||||
databases. Soft-deleted messages are returned back */
|
||||
{
|
||||
fmids->lpui8[fmids->cValues] = fmid;
|
||||
fmids->cValues++;
|
||||
@@ -1713,10 +1592,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
|
||||
|
||||
aclFolder = [self aclFolder];
|
||||
|
||||
users = [aclFolder aclUsers];
|
||||
users = [[aclFolder aclUsers] copy];
|
||||
max = [users count];
|
||||
for (count = 0; count < max; count++)
|
||||
[aclFolder removeUserFromAcls: [users objectAtIndex: count]];
|
||||
|
||||
[users release];
|
||||
}
|
||||
|
||||
- (int) modifyPermissions: (struct PermissionData *) permissions
|
||||
@@ -1864,6 +1745,17 @@ 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];
|
||||
|
||||
@@ -82,7 +82,9 @@
|
||||
andTDBIndexing: indexing];
|
||||
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
|
||||
baseUrl = [NSString stringWithFormat: @"sogo://%@@%@/",
|
||||
userName, moduleName];
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
moduleName];
|
||||
|
||||
subfolders = [parentFolder subFolders];
|
||||
max = [subfolders count];
|
||||
@@ -127,7 +129,9 @@
|
||||
if (![parentFolder newFolderWithName: folderName
|
||||
nameInContainer: &nameInContainer])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/",
|
||||
userName, moduleName, nameInContainer];
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
moduleName, nameInContainer];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
@@ -163,8 +163,15 @@
|
||||
- (int) getPidTagDisplayName: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [[bodyInfo objectForKey: @"description"]
|
||||
asUnicodeInMemCtx: 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];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -181,11 +188,17 @@
|
||||
- (int) getPidTagAttachMimeTag: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSString *mimeTag;
|
||||
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"]];
|
||||
|
||||
mimeTag = [NSString stringWithFormat: @"%@/%@",
|
||||
[bodyInfo objectForKey: @"type"],
|
||||
[bodyInfo objectForKey: @"subtype"]];
|
||||
*data = [[mimeTag lowercaseString] asUnicodeInMemCtx: memCtx];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
|
||||
@@ -87,7 +87,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct mapistore_contexts_list *firstContext = NULL, *context;
|
||||
NSString *urlBase, *stringData, *currentName, *inboxName, *draftsName, *sentName, *trashName;
|
||||
NSString *urlBase, *stringData, *currentName, *realName, *inboxName, *draftsName, *sentName, *trashName;
|
||||
NSArray *unprefixedFolders;
|
||||
NSMutableArray *secondaryFolders;
|
||||
enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE,
|
||||
@@ -126,7 +126,11 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
trashName = [NSString stringWithFormat: @"folder%@",
|
||||
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
||||
|
||||
urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", userName, userName];
|
||||
urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"]];
|
||||
for (count = 0; count < 3; count++)
|
||||
{
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
@@ -142,6 +146,9 @@ 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];
|
||||
@@ -153,12 +160,23 @@ 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];
|
||||
stringData = [NSString stringWithFormat: @"%@%@",
|
||||
urlBase, [currentName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
context->url = [stringData asUnicodeInMemCtx: context];
|
||||
stringData = [[[currentName substringFromIndex: 6] fromCSSIdentifier] stringByDecodingImap4FolderName];
|
||||
context->name = [stringData asUnicodeInMemCtx: context];
|
||||
// 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];
|
||||
context->main_folder = false;
|
||||
context->role = MAPISTORE_MAIL_ROLE;
|
||||
context->tag = "tag";
|
||||
@@ -188,7 +206,10 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
inContainer: accountFolder];
|
||||
if ([newFolder create])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/",
|
||||
userName, userName,
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
@@ -264,8 +285,12 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
componentsSeparatedByString: @"/"];
|
||||
folderName = [NSString stringWithFormat: @"folder%@",
|
||||
[unprefixedFolders componentsJoinedByString: @"/folder"]];
|
||||
url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@", userName,
|
||||
userName, folderName];
|
||||
url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@",
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
folderName];
|
||||
|
||||
context = talloc_zero (memCtx, struct mapistore_contexts_list);
|
||||
context->url = [url asUnicodeInMemCtx: context];
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
|
||||
/* 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;
|
||||
@@ -52,6 +55,11 @@
|
||||
- (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,6 +52,7 @@
|
||||
#import "MAPIStoreFAIMessage.h"
|
||||
#import "MAPIStoreMailContext.h"
|
||||
#import "MAPIStoreMailMessage.h"
|
||||
#import "MAPIStoreMailFolderTable.h"
|
||||
#import "MAPIStoreMailMessageTable.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
@@ -70,6 +71,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
#undef DEBUG
|
||||
#include <util/attr.h>
|
||||
#include <libmapi/libmapi.h>
|
||||
#include <libmapiproxy.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
@@ -153,6 +155,11 @@ 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
|
||||
@@ -163,6 +170,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
int i;
|
||||
|
||||
nameInContainer = nil;
|
||||
rc = MAPISTORE_ERROR;
|
||||
|
||||
folderName = nil;
|
||||
for (i = 0; !folderName && i < aRow->cValues; i++)
|
||||
@@ -177,6 +185,12 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
{
|
||||
nameInContainer = [NSString stringWithFormat: @"folder%@",
|
||||
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
||||
|
||||
/* it may be the operation is interleaved with operations
|
||||
from other users having cached information in the thread
|
||||
with the other user, so it'd better activate the user again here... */
|
||||
[[self userContext] activateWithUser: [[[self userContext] woContext] activeUser]];
|
||||
|
||||
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
|
||||
inContainer: sogoObject];
|
||||
if ([newFolder create])
|
||||
@@ -351,19 +365,54 @@ 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 errorWithFormat: @"qualifier is not used for folders"];
|
||||
[self warnWithFormat: @"qualifier is only used for folders with name"];
|
||||
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;
|
||||
@@ -511,7 +560,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
*nextModseq;
|
||||
NSString *changeNumber, *uid, *messageKey;
|
||||
uint64_t lastModseqNbr;
|
||||
EOQualifier *kvQualifier, *searchQualifier;
|
||||
EOQualifier *searchQualifier;
|
||||
NSArray *uids, *changeNumbers;
|
||||
NSUInteger count, max;
|
||||
NSArray *fetchResults;
|
||||
@@ -552,14 +601,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
{
|
||||
lastModseqNbr = [lastModseq unsignedLongLongValue];
|
||||
nextModseq = [NSNumber numberWithUnsignedLongLong: lastModseqNbr + 1];
|
||||
kvQualifier = [[EOKeyValueQualifier alloc]
|
||||
searchQualifier = [[EOKeyValueQualifier alloc]
|
||||
initWithKey: @"modseq"
|
||||
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
|
||||
value: nextModseq];
|
||||
searchQualifier = [[EOAndQualifier alloc]
|
||||
initWithQualifiers:
|
||||
kvQualifier, [self nonDeletedQualifier], nil];
|
||||
[kvQualifier release];
|
||||
|
||||
[searchQualifier autorelease];
|
||||
}
|
||||
else
|
||||
@@ -587,7 +633,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
|
||||
fetchResults
|
||||
= [(NSDictionary *) [sogoObject fetchUIDs: uids
|
||||
parts: [NSArray arrayWithObject: @"modseq"]]
|
||||
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
||||
objectForKey: @"fetch"];
|
||||
|
||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
||||
@@ -623,58 +669,230 @@ _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 deleted UIDs */
|
||||
/* 2. we synchronise expunged 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];
|
||||
uid = [[fetchResults objectAtIndex: count] stringValue];
|
||||
if ([messages objectForKey: uid])
|
||||
{
|
||||
newChangeNum = [[changeNumbers objectAtIndex: count]
|
||||
unsignedLongLongValue];
|
||||
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
||||
if (!changeNumber)
|
||||
{
|
||||
newChangeNum = [[self context] getNewChangeNumber];
|
||||
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
|
||||
}
|
||||
[messages removeObjectForKey: uid];
|
||||
[self logWithFormat: @"removed message entry for uid %@", uid];
|
||||
[self logWithFormat: @"Removed message entry for UID %@", uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat:@"Message entry not found 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;
|
||||
}
|
||||
@@ -688,7 +906,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
if (dotRange.location != NSNotFound)
|
||||
messageUid = [messageKey substringToIndex: dotRange.location];
|
||||
else
|
||||
messageUid = nil;
|
||||
{
|
||||
messageUid = nil;
|
||||
[self errorWithFormat:@"%s: Unexpected messageKey value [%@]",
|
||||
__PRETTY_FUNCTION__, messageKey];
|
||||
}
|
||||
|
||||
return messageUid;
|
||||
}
|
||||
@@ -710,12 +932,24 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
{
|
||||
NSMutableDictionary *messages, *messageEntry;
|
||||
NSString *messageUid;
|
||||
BOOL synced;
|
||||
|
||||
messageUid = [self messageUIDFromMessageKey: messageKey];
|
||||
messages = [[versionsMessage properties] objectForKey: @"Messages"];
|
||||
messageEntry = [messages objectForKey: messageUid];
|
||||
if (!messageEntry)
|
||||
abort ();
|
||||
{
|
||||
[self warnWithFormat: @"attempting to synchronise to set the change key 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 ();
|
||||
}
|
||||
}
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
|
||||
|
||||
[versionsMessage save];
|
||||
@@ -784,6 +1018,37 @@ _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
|
||||
@@ -792,6 +1057,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
||||
NSString *changeNumber;
|
||||
uint64_t modseq;
|
||||
NSDictionary *versionProperties;
|
||||
EOQualifier *deletedQualifier, *kvQualifier, *searchQualifier;
|
||||
|
||||
if (tableType == MAPISTORE_MESSAGE_TABLE)
|
||||
{
|
||||
@@ -800,8 +1066,33 @@ _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)
|
||||
{
|
||||
@@ -857,6 +1148,8 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP)
|
||||
uint32_t currentUid, rangeMin;
|
||||
BOOL done = NO, inRange = NO;
|
||||
|
||||
rangeMin = 0;
|
||||
currentUid = 0;
|
||||
UIDs = [NSMutableArray array];
|
||||
while (!done)
|
||||
{
|
||||
@@ -981,13 +1274,20 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
if (![[result objectForKey: @"result"] boolValue])
|
||||
return MAPISTORE_ERROR;
|
||||
|
||||
/* "Move" treatment: Store \Deleted and unregister urls */
|
||||
/* "Move" treatment: Store \Deleted and unregister urls as soft-deleted */
|
||||
if (!wantCopy)
|
||||
{
|
||||
[client storeFlags: [NSArray arrayWithObject: @"Deleted"] forUIDs: uids
|
||||
addOrRemove: YES];
|
||||
for (count = 0; count < midCount; count++)
|
||||
[mapping unregisterURLWithID: srcMids[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];
|
||||
}
|
||||
}
|
||||
|
||||
/* Registration of target messages */
|
||||
@@ -1036,13 +1336,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
}
|
||||
}
|
||||
|
||||
[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];
|
||||
@@ -1061,6 +1354,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
struct SRow folderRow;
|
||||
struct SPropValue nameProperty;
|
||||
MAPIStoreMailFolder *newFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoMailFolder *targetSOGoFolder;
|
||||
NSMutableArray *uids;
|
||||
NSArray *childKeys;
|
||||
@@ -1068,12 +1362,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
NGImap4Connection *connection;
|
||||
NGImap4Client *client;
|
||||
NSString *newURL, *parentDBFolderPath, *childKey, *folderIMAPName,
|
||||
*urlNamePart, *newFolderIMAPName;
|
||||
*urlNamePart, *newFolderIMAPName, *newFolderDBName;
|
||||
NSException *error;
|
||||
MAPIStoreMapping *mapping;
|
||||
NSDictionary *result;
|
||||
|
||||
if ([targetFolder isKindOfClass: MAPIStoreMailFolderK])
|
||||
if ([targetFolder isKindOfClass: MAPIStoreMailFolderK] || (!targetFolder && isMove))
|
||||
{
|
||||
folderURL = [sogoObject imap4URL];
|
||||
if (!newFolderName)
|
||||
@@ -1081,9 +1375,23 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
targetSOGoFolder = [targetFolder sogoObject];
|
||||
if (isMove)
|
||||
{
|
||||
urlNamePart = [newFolderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
|
||||
newFolderURL = [NSURL URLWithString: urlNamePart
|
||||
relativeToURL: [targetSOGoFolder imap4URL]];
|
||||
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];
|
||||
}
|
||||
error = [[sogoObject imap4Connection]
|
||||
moveMailboxAtURL: folderURL
|
||||
toURL: newFolderURL];
|
||||
@@ -1093,16 +1401,29 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
|
||||
{
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
mapping = [self mapping];
|
||||
newURL = [NSString stringWithFormat: @"%@folder%@/",
|
||||
[targetFolder url], urlNamePart];
|
||||
if (targetFolder)
|
||||
newURL = [NSString stringWithFormat: @"%@folder%@/",
|
||||
[targetFolder url], newFolderDBName];
|
||||
else
|
||||
newURL = [NSString stringWithFormat: @"sogo://%@:%@@mail/folder%@/",
|
||||
[[self userContext] username], [[self userContext] username],
|
||||
newFolderDBName];
|
||||
[mapping updateID: [self objectId] withURL: newURL];
|
||||
parentDBFolderPath = [[targetFolder dbFolder] path];
|
||||
if (!parentDBFolderPath)
|
||||
parentDBFolderPath = @"";
|
||||
[dbFolder changePathTo: [NSString stringWithFormat:
|
||||
@"%@/folder%@",
|
||||
parentDBFolderPath,
|
||||
newFolderName]];
|
||||
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];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1290,6 +1611,15 @@ _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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* 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 */
|
||||
@@ -0,0 +1,42 @@
|
||||
/* 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,6 +35,8 @@
|
||||
{
|
||||
BOOL headerSetup;
|
||||
BOOL mailIsEvent;
|
||||
BOOL mailIsMeetingRequest;
|
||||
BOOL mailIsSharingObject;
|
||||
NSString *mimeKey;
|
||||
NSString *headerCharset;
|
||||
NSString *headerEncoding;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#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,6 +41,7 @@
|
||||
#import <Mailer/SOGoMailBodyPart.h>
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
|
||||
#import "Codepages.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
@@ -50,6 +52,7 @@
|
||||
#import "MAPIStoreMailFolder.h"
|
||||
#import "MAPIStoreMapping.h"
|
||||
#import "MAPIStoreSamDBUtils.h"
|
||||
#import "MAPIStoreSharingMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "MAPIStoreUserContext.h"
|
||||
|
||||
@@ -63,7 +66,7 @@
|
||||
|
||||
@class iCalCalendar, iCalEvent;
|
||||
|
||||
static Class NSExceptionK;
|
||||
static Class NSExceptionK, MAPIStoreSharingMessageK;
|
||||
|
||||
@interface NSString (MAPIStoreMIME)
|
||||
|
||||
@@ -106,6 +109,7 @@ static Class NSExceptionK;
|
||||
+ (void) initialize
|
||||
{
|
||||
NSExceptionK = [NSException class];
|
||||
MAPIStoreSharingMessageK = [MAPIStoreSharingMessage class];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
@@ -114,6 +118,8 @@ static Class NSExceptionK;
|
||||
{
|
||||
mimeKey = nil;
|
||||
mailIsEvent = NO;
|
||||
mailIsMeetingRequest = NO;
|
||||
mailIsSharingObject = NO;
|
||||
headerCharset = nil;
|
||||
headerEncoding = nil;
|
||||
headerMimeType = nil;
|
||||
@@ -132,6 +138,7 @@ static Class NSExceptionK;
|
||||
[bodyContent release];
|
||||
[headerMimeType release];
|
||||
[headerCharset release];
|
||||
[headerEncoding release];
|
||||
[appointmentWrapper release];
|
||||
[super dealloc];
|
||||
}
|
||||
@@ -151,6 +158,30 @@ static Class NSExceptionK;
|
||||
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)
|
||||
{
|
||||
@@ -200,9 +231,11 @@ _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",
|
||||
@@ -226,7 +259,27 @@ _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;
|
||||
@@ -368,6 +421,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
{
|
||||
uint64_t version = ULLONG_MAX;
|
||||
NSString *uid, *changeNumber;
|
||||
BOOL synced;
|
||||
|
||||
uid = [(MAPIStoreMailFolder *)
|
||||
container messageUIDFromMessageKey: [self nameInContainer]];
|
||||
@@ -386,8 +440,19 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
[self logWithFormat: @"got one"];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"still nothing. We crash!"];
|
||||
abort();
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
version = [changeNumber unsignedLongLongValue] >> 16;
|
||||
@@ -504,36 +569,31 @@ _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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
- (int) getPidTagReplyRequested: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
return (mailIsEvent
|
||||
? [self getNo: data inMemCtx: memCtx]
|
||||
return (mailIsMeetingRequest
|
||||
? [self getYes: 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
|
||||
{
|
||||
@@ -741,7 +801,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
samCtx = [[self context] connectionInfo]->sam_ctx;
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -923,15 +983,17 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
- (int) getPidTagInternetCodepage: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
/* 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);
|
||||
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]);
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
@@ -1333,6 +1395,41 @@ _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
|
||||
{
|
||||
@@ -1402,10 +1499,10 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if ([cn length] == 0)
|
||||
cn = email;
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
|
||||
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -1566,11 +1663,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
if (!headerSetup)
|
||||
[self _fetchHeaderData];
|
||||
|
||||
if ([mimeKey hasPrefix: @"body.peek"])
|
||||
bodyPartKey = [NSString stringWithFormat: @"body[%@]",
|
||||
[mimeKey _strippedBodyKey]];
|
||||
else
|
||||
bodyPartKey = mimeKey;
|
||||
bodyPartKey = mimeKey;
|
||||
|
||||
return bodyPartKey;
|
||||
}
|
||||
@@ -1584,6 +1677,21 @@ _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
|
||||
{
|
||||
NSNumber *value;
|
||||
@@ -1597,6 +1705,11 @@ _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
|
||||
@@ -28,9 +28,10 @@
|
||||
#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/NSString+Encoding.h>
|
||||
@@ -43,6 +44,7 @@
|
||||
#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>
|
||||
@@ -50,6 +52,7 @@
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
|
||||
#import "Codepages.h"
|
||||
#import "MAPIStoreAttachment.h"
|
||||
#import "MAPIStoreAttachmentTable.h"
|
||||
#import "MAPIStoreContext.h"
|
||||
@@ -62,13 +65,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;
|
||||
|
||||
@@ -383,7 +386,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
|
||||
contactInfos = [mgr contactInfosForUserWithUIDorEmail: email];
|
||||
if (contactInfos)
|
||||
{
|
||||
username = [contactInfos objectForKey: @"c_uid"];
|
||||
username = [contactInfos objectForKey: @"sAMAccountName"];
|
||||
recipient->username = [username asUnicodeInMemCtx: msgData];
|
||||
entryId = MAPIStoreInternalEntryId (samCtx, username);
|
||||
}
|
||||
@@ -531,13 +534,112 @@ 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;
|
||||
NSString *from, *recId, *messageId, *subjectData, *recipientsStr, *msgClass;
|
||||
NSArray *list;
|
||||
NSCalendarDate *date;
|
||||
NSDictionary *recipients;
|
||||
@@ -571,10 +673,96 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
|
||||
list = MakeRecipientsList ([recipients objectForKey: @"orig"]);
|
||||
if ([list count])
|
||||
[headers setObjects: list forKey: @"from"];
|
||||
{
|
||||
[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"];
|
||||
}
|
||||
}
|
||||
else
|
||||
NSLog (@"message without recipients");
|
||||
|
||||
subject = [NSMutableString stringWithCapacity: 128];
|
||||
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)];
|
||||
@@ -592,8 +780,6 @@ 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"];
|
||||
@@ -612,6 +798,13 @@ 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 *
|
||||
@@ -672,21 +865,9 @@ MakeTextHtmlBody (NSDictionary *mailProperties, NSDictionary *attachmentParts,
|
||||
{
|
||||
/* charset */
|
||||
codePage = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_CPID)];
|
||||
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";
|
||||
}
|
||||
charset = [Codepages getNameFromCodepage: codePage];
|
||||
if (!charset)
|
||||
charset = @"utf-8";
|
||||
htmlContentType = [NSString stringWithFormat: @"text/html; charset=%@",
|
||||
charset];
|
||||
|
||||
@@ -834,7 +1015,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
messageBody = MakeMessageBody (properties, attachmentParts, &contentType);
|
||||
if (messageBody)
|
||||
{
|
||||
[headers setObject: contentType forKey: @"content-type"];
|
||||
[message setHeader: contentType forKey: @"content-type"];
|
||||
[message setBody: messageBody];
|
||||
}
|
||||
|
||||
@@ -873,12 +1054,12 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
|
||||
id <SOGoAuthenticator> authenticator;
|
||||
|
||||
msgClass = [properties objectForKey: MAPIPropertyKey (PidTagMessageClass)];
|
||||
if ([msgClass isEqualToString: @"IPM.Note"]) /* we skip invitation replies */
|
||||
if ([msgClass isEqualToString: @"IPM.Note"] || [msgClass isEqualToString: @"IPM.Sharing"]) /* 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++)
|
||||
|
||||
@@ -49,13 +49,19 @@
|
||||
- (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
|
||||
|
||||
|
||||
@@ -114,6 +114,9 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -157,6 +160,21 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
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;
|
||||
|
||||
return idNbr;
|
||||
}
|
||||
|
||||
- (void) _updateFolderWithURL: (NSString *) oldURL
|
||||
withURL: (NSString *) urlString
|
||||
{
|
||||
@@ -209,12 +227,32 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
}
|
||||
}
|
||||
|
||||
- (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 rc;
|
||||
bool softDeleted;
|
||||
|
||||
oldURL = [self urlFromID: idNbr];
|
||||
if (oldURL != NULL)
|
||||
@@ -225,18 +263,20 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
return NO;
|
||||
}
|
||||
|
||||
oldIdNbr = [self idFromURL: urlString];
|
||||
oldIdNbr = [self idFromURL: urlString isSoftDeleted: &softDeleted];
|
||||
if (oldIdNbr != NSNotFound)
|
||||
{
|
||||
[self errorWithFormat:
|
||||
@"attempt to double register an entry with idNbr ('%@', %lld,"
|
||||
@" 0x%.16"PRIx64", oldid=0x%.16"PRIx64")",
|
||||
urlString, idNbr, idNbr, oldIdNbr];
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = YES;
|
||||
// [self logWithFormat: @"registered url '%@' with id %lld (0x%.16"PRIx64")",
|
||||
// urlString, idNbr, idNbr];
|
||||
|
||||
@@ -245,7 +285,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
idNbr, [urlString UTF8String]);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) registerURLs: (NSArray *) urlStrings
|
||||
@@ -275,4 +315,13 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr)
|
||||
idNbr, MAPISTORE_PERMANENT_DELETE);
|
||||
}
|
||||
|
||||
- (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);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -465,16 +465,15 @@ 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];
|
||||
@@ -496,47 +495,6 @@ 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];
|
||||
@@ -782,7 +740,7 @@ rtf2html (NSData *compressedRTF)
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
}
|
||||
|
||||
- (int) getPidTagSensitivity: (void **) data // TODO -> subclass in calendar
|
||||
- (int) getPidTagSensitivity: (void **) data
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
return [self getLongZero: data inMemCtx: memCtx];
|
||||
@@ -795,7 +753,7 @@ rtf2html (NSData *compressedRTF)
|
||||
TALLOC_CTX *localMemCtx;
|
||||
char *prefix, *normalizedSubject;
|
||||
|
||||
localMemCtx = talloc_zero (NULL, TALLOC_CTX);
|
||||
localMemCtx = talloc_zero (memCtx, TALLOC_CTX);
|
||||
if ([self getProperty: (void **) &prefix
|
||||
withTag: PidTagSubjectPrefix
|
||||
inMemCtx: localMemCtx]
|
||||
@@ -807,6 +765,8 @@ rtf2html (NSData *compressedRTF)
|
||||
if (rc == MAPISTORE_SUCCESS)
|
||||
*data = talloc_asprintf (memCtx, "%s%s", prefix, normalizedSubject);
|
||||
|
||||
talloc_free(localMemCtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,58 +87,7 @@
|
||||
|
||||
- (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
|
||||
@@ -45,7 +45,8 @@
|
||||
|
||||
context = talloc_zero(memCtx, struct mapistore_contexts_list);
|
||||
context->url = talloc_asprintf (context, "sogo://%s@notes/",
|
||||
[userName UTF8String]);
|
||||
[[userName stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"] UTF8String]);
|
||||
// context->name = "Notes personnelles";
|
||||
context->main_folder = true;
|
||||
context->role = MAPISTORE_NOTES_ROLE;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#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>
|
||||
@@ -245,17 +244,16 @@ 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:
|
||||
@@ -264,16 +262,6 @@ 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,6 +25,8 @@
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
@@ -41,7 +43,10 @@
|
||||
@interface iCalCalendar (MAPIStoreRecurrence)
|
||||
|
||||
- (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity
|
||||
fromRecurrencePattern: (struct RecurrencePattern *) rp;
|
||||
fromRecurrencePattern: (struct RecurrencePattern *) rp
|
||||
withExceptions: (struct ExceptionInfo *) exInfos
|
||||
andExceptionCount: (uint16_t) exInfoCount
|
||||
inTimeZone: (NSTimeZone *) tz;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#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>
|
||||
@@ -48,6 +49,10 @@
|
||||
|
||||
- (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;
|
||||
@@ -214,34 +219,42 @@
|
||||
|
||||
/* exception dates:
|
||||
- take all deleted instances
|
||||
- remove all modified instances from the above set
|
||||
- remove all modified instances available in ExceptionInfo 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];
|
||||
}
|
||||
|
||||
realExDates = [[exceptionDates allObjects]
|
||||
sortedArrayUsingSelector: @selector (compare:)];
|
||||
max = [realExDates count];
|
||||
for (count = 0; count < max; count++)
|
||||
[entity addToExceptionDates: [realExDates objectAtIndex: count]];
|
||||
/* 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]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -312,15 +325,25 @@
|
||||
rp->RecurFrequency = RecurFrequency_Weekly;
|
||||
rp->PatternType = PatternType_Week;
|
||||
rp->Period = repeatInterval;
|
||||
|
||||
dayOfWeek = [startDate dayOfWeek];
|
||||
|
||||
mask = 0;
|
||||
byDayMask = [self byDayMask];
|
||||
for (count = 0; count < 7; count++)
|
||||
if ([byDayMask occursOnDay: count])
|
||||
mask |= 1 << count;
|
||||
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;
|
||||
}
|
||||
rp->PatternTypeSpecific.WeekRecurrencePattern = mask;
|
||||
|
||||
/* FirstDateTime */
|
||||
dayOfWeek = [startDate dayOfWeek];
|
||||
if (dayOfWeek)
|
||||
beginOfWeek = [startDate dateByAddingYears: 0 months: 0
|
||||
days: -dayOfWeek
|
||||
|
||||
+66
-46
@@ -47,6 +47,7 @@
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
||||
#include <libmapi/libmapi.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
#include <execinfo.h>
|
||||
@@ -58,8 +59,8 @@ static BOOL initialization_done = NO;
|
||||
#define NS_CURRENT_THREAD_REGISTER() \
|
||||
BOOL __nsrct_thread_registered = GSRegisterCurrentThread(); \
|
||||
if (!initialization_done) { \
|
||||
DEBUG(5, ("[SOGo: %s:%d] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \
|
||||
__FUNCTION__, __LINE__, GSCurrentThread(), getpid())); \
|
||||
OC_DEBUG(5, "[SOGo] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \
|
||||
GSCurrentThread(), getpid()); \
|
||||
}
|
||||
#define NS_CURRENT_THREAD_TRY_UNREGISTER() \
|
||||
if (__nsrct_thread_registered) { \
|
||||
@@ -144,7 +145,7 @@ sogo_backend_init (void)
|
||||
GSRegisterCurrentThread();
|
||||
|
||||
if (initialization_done) {
|
||||
DEBUG(0, ("SOGo backend already initialized.\n"));
|
||||
OC_DEBUG(5, "[SOGo] SOGo backend already initialized.\n");
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -189,7 +190,7 @@ sogo_backend_init (void)
|
||||
|
||||
[pool release];
|
||||
|
||||
DEBUG(0, ("[SOGo: %s:%d] backend init SUCCESS. Current thread: %p, pid: %d\n", __FUNCTION__, __LINE__, GSCurrentThread(), getpid()));
|
||||
OC_DEBUG(5, "[SOGo] backend init SUCCESS. Current thread: %p, pid: %d\n", GSCurrentThread(), getpid());
|
||||
initialization_done = YES;
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
@@ -213,7 +214,7 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreContext *context;
|
||||
int rc;
|
||||
|
||||
DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
@@ -250,7 +251,7 @@ sogo_backend_create_root_folder (const char *username,
|
||||
NSString *mapistoreUri;
|
||||
int rc;
|
||||
|
||||
DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
@@ -287,7 +288,7 @@ sogo_backend_list_contexts(const char *username, struct indexing_context *indexi
|
||||
NSString *userName;
|
||||
int rc;
|
||||
|
||||
DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
NS_CURRENT_THREAD_REGISTER();
|
||||
pool = [NSAutoreleasePool new];
|
||||
@@ -335,7 +336,7 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreContext *context;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (backend_object)
|
||||
{
|
||||
@@ -369,7 +370,7 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (backend_object)
|
||||
{
|
||||
@@ -412,7 +413,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid,
|
||||
MAPIStoreFolder *folder, *childFolder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -456,7 +457,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder, *childFolder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -499,7 +500,7 @@ sogo_folder_delete(void *folder_object)
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -531,7 +532,7 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -567,7 +568,7 @@ sogo_folder_open_message(void *folder_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -609,7 +610,7 @@ sogo_folder_create_message(void *folder_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -645,7 +646,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags)
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -683,7 +684,7 @@ sogo_folder_move_copy_messages(void *folder_object,
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -727,7 +728,7 @@ sogo_folder_move_folder(void *folder_object, void *target_folder_object,
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -777,7 +778,7 @@ sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_
|
||||
struct MAPIStoreTallocWrapper *wrapper;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -821,7 +822,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -860,7 +861,7 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -899,7 +900,7 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags,
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -933,7 +934,7 @@ sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_typ
|
||||
MAPIStoreFolder *folder;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (folder_object)
|
||||
{
|
||||
@@ -968,7 +969,7 @@ sogo_message_get_message_data(void *message_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1003,7 +1004,7 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void
|
||||
MAPIStoreAttachment *attachment;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1040,7 +1041,7 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreAttachment *attachment;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1076,7 +1077,7 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo
|
||||
MAPIStoreAttachmentTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1115,7 +1116,8 @@ sogo_message_modify_recipients (void *message_object,
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1150,7 +1152,8 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1183,7 +1186,8 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
|
||||
MAPIStoreMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1216,7 +1220,8 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
|
||||
MAPIStoreMailVolatileMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (message_object)
|
||||
{
|
||||
@@ -1254,7 +1259,8 @@ sogo_message_attachment_open_embedded_message (void *attachment_object,
|
||||
MAPIStoreEmbeddedMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (attachment_object)
|
||||
{
|
||||
@@ -1295,7 +1301,8 @@ sogo_message_attachment_create_embedded_message (void *attachment_object,
|
||||
MAPIStoreEmbeddedMessage *message;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (attachment_object)
|
||||
{
|
||||
@@ -1331,7 +1338,8 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1363,7 +1371,8 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1396,7 +1405,8 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1431,7 +1441,8 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1468,7 +1479,8 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1503,7 +1515,8 @@ sogo_table_get_row_count (void *table_object,
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1536,7 +1549,8 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id)
|
||||
MAPIStoreTable *table;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (table_object)
|
||||
{
|
||||
@@ -1570,7 +1584,8 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
@@ -1605,7 +1620,8 @@ sogo_properties_get_properties (void *object,
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
@@ -1639,7 +1655,8 @@ sogo_properties_set_properties (void *object, struct SRow *aRow)
|
||||
MAPIStoreObject *propObject;
|
||||
int rc;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
|
||||
if (object)
|
||||
{
|
||||
@@ -1674,7 +1691,7 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
|
||||
NSAutoreleasePool *pool;
|
||||
NSString *partialURLString, *username, *directory;
|
||||
|
||||
DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__));
|
||||
OC_DEBUG(5, "[SOGo]");
|
||||
|
||||
if (uri == NULL) return MAPISTORE_ERR_INVALID_PARAMETER;
|
||||
|
||||
@@ -1693,7 +1710,10 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
|
||||
username = [NSString stringWithUTF8String: (user ? user : "*")];
|
||||
/* Do proper directory lookup here */
|
||||
directory = [NSString stringWithUTF8String: (folder ? folder : "*")];
|
||||
partialURLString = [NSString stringWithFormat: @"sogo://%@:*@%@", username, directory];
|
||||
partialURLString = [NSString stringWithFormat: @"sogo://%@:*@%@",
|
||||
[username stringByReplacingOccurrencesOfString: @"@"
|
||||
withString: @"%40"],
|
||||
directory];
|
||||
}
|
||||
if (![partialURLString hasSuffix: @"/"])
|
||||
partialURLString = [partialURLString stringByAppendingString: @"/"];
|
||||
@@ -1776,7 +1796,7 @@ int mapistore_init_backend(void)
|
||||
/* Register ourselves with the MAPISTORE subsystem */
|
||||
ret = mapistore_backend_register (&backend);
|
||||
if (ret != MAPISTORE_SUCCESS)
|
||||
DEBUG(0, ("Failed to register the '%s' mapistore backend!\n", backend.backend.name));
|
||||
OC_DEBUG(0, "[SOGo] Failed to register the '%s' mapistore backend!\n", backend.backend.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -96,8 +96,11 @@ MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username)
|
||||
[entryId appendUInt8: 0]; // end of string
|
||||
}
|
||||
else
|
||||
entryId = nil;
|
||||
|
||||
{
|
||||
NSLog (@"Error trying to generate EntryId for `%@`", username);
|
||||
entryId = nil;
|
||||
}
|
||||
|
||||
return entryId;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/* 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 */
|
||||
@@ -0,0 +1,463 @@
|
||||
/* 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
|
||||
@@ -367,13 +367,15 @@
|
||||
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"];
|
||||
|
||||
tzName = [[[self userContext] timeZone] name];
|
||||
userTZ = [[self userContext] timeZone];
|
||||
tzName = [userTZ name];
|
||||
tz = [iCalTimeZone timeZoneForName: tzName];
|
||||
[vCalendar addTimeZone: tz];
|
||||
|
||||
@@ -428,6 +430,13 @@
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -437,6 +446,13 @@
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -445,7 +461,9 @@
|
||||
if (value)
|
||||
{
|
||||
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"];
|
||||
tzOffset = [[value timeZone] secondsFromGMTForDate: value];
|
||||
/* 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];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -tzOffset];
|
||||
|
||||
@@ -157,6 +157,9 @@ 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,6 +119,7 @@ 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:
|
||||
@@ -154,8 +155,7 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value)
|
||||
// #define PT_I8 0x14
|
||||
// #define PT_SRESTRICT 0xFD
|
||||
// #define PT_ACTIONS 0xFE
|
||||
result = [NSNull null];
|
||||
abort();
|
||||
result = nil;
|
||||
NSLog (@"%s: object type not handled: %d (0x%.4x)",
|
||||
__PRETTY_FUNCTION__, valueType, valueType);
|
||||
}
|
||||
@@ -250,8 +250,7 @@ NSObjectFromSPropValue (const struct SPropValue *value)
|
||||
// #define PT_I8 0x14
|
||||
// #define PT_SRESTRICT 0xFD
|
||||
// #define PT_ACTIONS 0xFE
|
||||
result = [NSNull null];
|
||||
abort();
|
||||
result = nil;
|
||||
NSLog (@"%s: object type not handled: %d (0x%.4x)",
|
||||
__PRETTY_FUNCTION__, valueType, valueType);
|
||||
}
|
||||
|
||||
@@ -311,9 +311,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 loginInDomain] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[[[user login] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
|
||||
|
||||
+118
-39
@@ -26,6 +26,7 @@
|
||||
// Useful macros
|
||||
//
|
||||
#define ADVANCE _bytes++; _current_pos++;
|
||||
#define ADVANCE_N(N) _bytes += (N); _current_pos += (N);
|
||||
#define REWIND _bytes--; _current_pos--;
|
||||
|
||||
|
||||
@@ -412,16 +413,46 @@ const unsigned short ansicpg874[256] = {
|
||||
- (const char *) parseControlWord: (unsigned int *) len
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
|
||||
start = ADVANCE;
|
||||
|
||||
while (isalnum(*_bytes) || *_bytes == '-')
|
||||
|
||||
/*
|
||||
A control word is defined by:
|
||||
|
||||
\<ASCII Letter Sequence><Delimiter>
|
||||
*/
|
||||
while (isalpha(*_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;
|
||||
}
|
||||
|
||||
@@ -508,6 +539,7 @@ const unsigned short ansicpg874[256] = {
|
||||
|
||||
fontTable = [[[RTFFontTable alloc] init] autorelease];
|
||||
fontName = nil;
|
||||
fontInfo = nil;
|
||||
count = 0;
|
||||
|
||||
do
|
||||
@@ -543,7 +575,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
|
||||
@@ -558,12 +590,18 @@ 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
|
||||
@@ -622,13 +660,44 @@ 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
|
||||
{
|
||||
// Do the same as -parseStyleSheet for now, that is, ignore everything.
|
||||
[self parseStyleSheet];
|
||||
[self parseIgnoringEverything];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -648,6 +717,7 @@ 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];
|
||||
@@ -694,19 +764,7 @@ const unsigned short ansicpg874[256] = {
|
||||
}
|
||||
else if (*(_bytes+1) == '*')
|
||||
{
|
||||
int cc = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (*_bytes == '{')
|
||||
cc++;
|
||||
if (*_bytes == '}')
|
||||
cc--;
|
||||
|
||||
ADVANCE;
|
||||
}
|
||||
while (cc != 0);
|
||||
|
||||
[self parseIgnoringEverything];
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -726,16 +784,16 @@ const unsigned short ansicpg874[256] = {
|
||||
{
|
||||
// We rewind our buffer so we start at the beginning of {\fonttbl...
|
||||
_bytes = cw-2;
|
||||
_current_pos -= 10;
|
||||
_current_pos -= 9; // Length: {\fonttbl
|
||||
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 -= 13;
|
||||
_current_pos -= 12; // Length: {\stylesheet
|
||||
[self parseStyleSheet];
|
||||
REWIND;
|
||||
}
|
||||
@@ -746,7 +804,7 @@ const unsigned short ansicpg874[256] = {
|
||||
else if (strncmp(cw, "pict", 4) == 0)
|
||||
{
|
||||
_bytes = cw-2;
|
||||
_current_pos -= 7;
|
||||
_current_pos -= 6; // Length: {\pict
|
||||
[self parsePicture];
|
||||
REWIND;
|
||||
}
|
||||
@@ -766,19 +824,19 @@ const unsigned short ansicpg874[256] = {
|
||||
int color_index;
|
||||
char *v;
|
||||
|
||||
if (!formattingOptions) continue;
|
||||
|
||||
color_index = [[s substringFromIndex: 2] intValue];
|
||||
|
||||
if (!formattingOptions)
|
||||
continue;
|
||||
|
||||
if (formattingOptions->color_index >= 0) // && color_index != formattingOptions->color_index)
|
||||
colorDef = [colorTable colorDefAtIndex: color_index];
|
||||
if (!colorDef) continue;
|
||||
|
||||
if (formattingOptions->color_index >= 0)
|
||||
{
|
||||
[_html appendBytes: "</font>" length: 7];
|
||||
}
|
||||
|
||||
formattingOptions->color_index = color_index;
|
||||
colorDef = [colorTable colorDefAtIndex: color_index];
|
||||
|
||||
|
||||
formattingOptions->color_index = color_index;
|
||||
|
||||
v = malloc(23*sizeof(char));
|
||||
memset(v, 0, 23);
|
||||
sprintf(v, "<font color=\"#%02x%02x%02x\">", colorDef->red, colorDef->green, colorDef->blue);
|
||||
@@ -814,20 +872,41 @@ 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 = malloc(128*sizeof(char));
|
||||
memset(v, 0, 128);
|
||||
sprintf(v, "<font face=\"%s\">", [fontInfo->name UTF8String]);
|
||||
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>");
|
||||
}
|
||||
[_html appendBytes: v length: strlen(v)];
|
||||
free(v);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#import <SOGo/BSONCodec.h>
|
||||
#import "NSObject+PropertyList.h"
|
||||
|
||||
Class MAPIStoreUserContextK, SOGoMAPIDBObjectK;
|
||||
Class MAPIStoreUserContextK, SOGoCacheGCSObjectK;
|
||||
|
||||
static void
|
||||
DumpBSONData(NSData *data)
|
||||
@@ -60,7 +60,7 @@ DbDumpObject (NSString *username, NSString *path)
|
||||
|
||||
ctx = [MAPIStoreUserContextK userContextWithUsername: username
|
||||
andTDBIndexing: NULL];
|
||||
dbobject = [SOGoMAPIDBObjectK new];
|
||||
dbobject = [SOGoCacheGCSObjectK new];
|
||||
[dbobject setTableUrl: [ctx folderTableURL]];
|
||||
record = [dbobject lookupRecord: path newerThanVersion: -1];
|
||||
if (record)
|
||||
@@ -107,7 +107,7 @@ int main (int argc, char *argv[], char *envp[])
|
||||
[loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]];
|
||||
|
||||
MAPIStoreUserContextK = NSClassFromString (@"MAPIStoreUserContext");
|
||||
SOGoMAPIDBObjectK = NSClassFromString (@"SOGoMAPIDBObject");
|
||||
SOGoCacheGCSObjectK = NSClassFromString (@"SOGoCacheGCSObject");
|
||||
|
||||
arguments = [[NSProcessInfo processInfo] arguments];
|
||||
if ([arguments count] > 2) {
|
||||
|
||||
+162
-137
@@ -74,6 +74,7 @@
|
||||
@implementation iCalEvent (MAPIStoreProperties)
|
||||
|
||||
- (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData
|
||||
inTimeZone: (NSTimeZone *) tz
|
||||
inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
struct Binary_r *blob;
|
||||
@@ -81,9 +82,18 @@
|
||||
|
||||
blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx];
|
||||
pattern = get_AppointmentRecurrencePattern (memCtx, blob);
|
||||
[(iCalCalendar *) parent
|
||||
setupRecurrenceWithMasterEntity: self
|
||||
fromRecurrencePattern: &pattern->RecurrencePattern];
|
||||
|
||||
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
|
||||
];
|
||||
//talloc_free (blob);
|
||||
}
|
||||
|
||||
@@ -131,6 +141,107 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (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
|
||||
@@ -140,7 +251,7 @@
|
||||
iCalDateTime *start, *end;
|
||||
iCalTimeZone *tz;
|
||||
NSTimeZone *userTimeZone;
|
||||
NSString *priority;
|
||||
NSString *priority, *class = nil;
|
||||
NSUInteger responseStatus = 0;
|
||||
NSInteger tzOffset;
|
||||
SOGoUser *ownerUser;
|
||||
@@ -207,16 +318,7 @@
|
||||
value
|
||||
= [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)];
|
||||
if (value)
|
||||
{
|
||||
if (!isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: value];
|
||||
value = [value dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
}
|
||||
[self setRecurrenceId: value];
|
||||
}
|
||||
[self setRecurrenceId: value];
|
||||
|
||||
// start
|
||||
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)];
|
||||
@@ -241,13 +343,7 @@
|
||||
[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 */
|
||||
@@ -273,13 +369,7 @@
|
||||
[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 */
|
||||
@@ -302,6 +392,36 @@
|
||||
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
|
||||
@@ -347,7 +467,7 @@
|
||||
value = [properties
|
||||
objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)];
|
||||
if (value)
|
||||
[self _setupEventRecurrence: value inMemCtx: memCtx];
|
||||
[self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx];
|
||||
|
||||
/* alarm */
|
||||
[self _setupEventAlarmFromProperties: properties];
|
||||
@@ -356,106 +476,28 @@
|
||||
value = [properties objectForKey: @"recipients"];
|
||||
if (value)
|
||||
{
|
||||
NSArray *recipients;
|
||||
NSDictionary *dict;
|
||||
NSString *orgEmail, *sentBy, *attEmail;
|
||||
NSString *orgEmail, *sentBy;
|
||||
iCalPerson *person;
|
||||
iCalPersonPartStat newPartStat;
|
||||
NSNumber *flags, *trackStatus;
|
||||
int i, effective;
|
||||
int effective;
|
||||
BOOL organizerIsSet = NO;
|
||||
|
||||
[self setOrganizer: nil];
|
||||
[self removeAllAttendees];
|
||||
|
||||
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;
|
||||
}
|
||||
/* 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];
|
||||
|
||||
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 */
|
||||
if (effective == 0) /* See work-around inside _updateFromAttendeeMAPIProperties */
|
||||
[self setOrganizer: nil];
|
||||
else
|
||||
{
|
||||
@@ -481,7 +523,7 @@
|
||||
= [properties objectForKey: MAPIPropertyKey (PidLidResponseStatus)];
|
||||
if (value)
|
||||
responseStatus = [value unsignedLongValue];
|
||||
|
||||
|
||||
/* FIXME: we should provide a data converter between OL partstats and
|
||||
SOGo */
|
||||
switch (responseStatus)
|
||||
@@ -503,23 +545,6 @@
|
||||
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
|
||||
@@ -534,7 +559,7 @@
|
||||
[person setCn: [dict objectForKey: @"fullName"]];
|
||||
orgEmail = [dict objectForKey: @"email"];
|
||||
[person setEmail: orgEmail];
|
||||
|
||||
|
||||
if (![activeUser isEqual: ownerUser])
|
||||
{
|
||||
dict = [activeUser primaryIdentity];
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
NSArray *byMonth;
|
||||
iCalByDayMask *mask;
|
||||
NSCalendarDate *dateValue;
|
||||
int16_t wDay;
|
||||
|
||||
rrule = [self recurrenceRule];
|
||||
byMonth = [rrule byMonth];
|
||||
@@ -59,7 +60,14 @@
|
||||
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
|
||||
mask = [rrule byDayMask];
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
tzData->wDay = [mask firstOccurrence];
|
||||
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;
|
||||
|
||||
dateValue = [self startDate];
|
||||
tzData->wHour = [dateValue hourOfDay];
|
||||
|
||||
@@ -4,5 +4,6 @@ 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], [handle->url absoluteString]];
|
||||
[_url absoluteString], [currentHandle->url absoluteString]];
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSKeyValueCoding.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@@ -147,7 +148,7 @@
|
||||
if ([keys count] == 0)
|
||||
return folderQualifier;
|
||||
|
||||
bindings = [_folder valuesForKeys:keys];
|
||||
bindings = [_folder dictionaryWithValuesForKeys:keys];
|
||||
return [folderQualifier qualifierWithBindings:bindings
|
||||
requiresAllVariables:NO];
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_partstates;
|
||||
sqlType = "VARCHAR2(255)";
|
||||
sqlType = "CLOB";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
@@ -154,5 +154,10 @@
|
||||
sqlType = "INTEGER";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
columnName = c_description;
|
||||
sqlType = "CLOB";
|
||||
allowsNull = YES;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_partstates;
|
||||
sqlType = "VARCHAR(255)";
|
||||
sqlType = "TEXT";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
@@ -153,6 +153,11 @@
|
||||
columnName = c_nextalarm;
|
||||
sqlType = "INT";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
columnName = c_description;
|
||||
sqlType = "TEXT";
|
||||
allowsNull = YES;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -59,8 +59,10 @@ static NGCardsSaxHandler *sax = nil;
|
||||
[parser setErrorHandler:sax];
|
||||
}
|
||||
else
|
||||
NSLog(@"ERROR(%s): did not find a parser for text/x-vcard!",
|
||||
__PRETTY_FUNCTION__);
|
||||
{
|
||||
//NSLog(@"ERROR(%s): did not find a parser for text/x-vcard!",
|
||||
// __PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
return parser;
|
||||
@@ -170,8 +172,8 @@ static NGCardsSaxHandler *sax = nil;
|
||||
{
|
||||
if (![aChild isKindOfClass: mappedClass])
|
||||
{
|
||||
NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
|
||||
tag, childTag, NSStringFromClass(mappedClass));
|
||||
//NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
|
||||
// tag, childTag, NSStringFromClass(mappedClass));
|
||||
newChild = [aChild elementWithClass: mappedClass];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
- (BOOL)appendLine:(NSString *)_line {
|
||||
if (self->isFinished) {
|
||||
NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
|
||||
//NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
|
||||
return NO;
|
||||
}
|
||||
// limit length to 75 chars
|
||||
|
||||
@@ -55,8 +55,6 @@ static NSArray *privilegedTagNames = nil;
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (content)
|
||||
free (content);
|
||||
[self reset];
|
||||
[cards release];
|
||||
[currentGroup release];
|
||||
|
||||
@@ -77,8 +77,8 @@ static NSString *gmtcalfmt = @"%Y%m%dT%H%M%SZ";
|
||||
return [self icalStringInGMT];
|
||||
else {
|
||||
/* not in GMT */
|
||||
NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
|
||||
__PRETTY_FUNCTION__, _tz);
|
||||
//NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
|
||||
// __PRETTY_FUNCTION__, _tz);
|
||||
return [self icalStringInGMT];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,9 @@
|
||||
}
|
||||
}
|
||||
else
|
||||
NSLog(@"Cannot parse iCal duration value: '%@'", self);
|
||||
{
|
||||
//NSLog(@"Cannot parse iCal duration value: '%@'", self);
|
||||
}
|
||||
|
||||
if (isNegative)
|
||||
ti = -ti;
|
||||
|
||||
@@ -59,8 +59,8 @@ static NSTimeZone *defTZ = nil;
|
||||
[self takeValue:_value forXKey:_key];
|
||||
}
|
||||
else {
|
||||
NSLog(@"0x%08x[%@]: ignoring attempt to set unbound key '%@'",
|
||||
self, NSStringFromClass([self class]), _key);
|
||||
//NSLog(@"0x%08x[%@]: ignoring attempt to set unbound key '%@'",
|
||||
// self, NSStringFromClass([self class]), _key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,13 +156,15 @@
|
||||
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||
tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra]
|
||||
month: [[[rrule byMonth] objectAtIndex: 0] intValue]
|
||||
day: 1 hour: [tzStart hourOfDay]
|
||||
day: 1
|
||||
hour: [tzStart hourOfDay]
|
||||
minute: [tzStart minuteOfHour] second: 0
|
||||
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||
|
||||
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
|
||||
day: 0 hour: 0 minute: 0
|
||||
second: 0];
|
||||
|
||||
|
||||
/* If the day of the time change is "-XSU", we need to determine whether the
|
||||
first day of next month is in the same week. In practice, as most time
|
||||
changes occurs on sundays, it will be false only when that first day is a
|
||||
@@ -171,10 +173,42 @@
|
||||
if (dateDayOfWeek > dayOfWeek && pos < 0)
|
||||
pos++;
|
||||
|
||||
/* We check if the days of the week are identical. This is important because if they
|
||||
are, "pos" actually includes the first day of tmpDate which means we must decrement
|
||||
pos by 1. This happens for example in the eastern timezone (America/Montreal)
|
||||
in 2015. We have:
|
||||
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/Montreal
|
||||
X-LIC-LOCATION:America/Montreal
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0500
|
||||
TZOFFSETTO:-0400
|
||||
TZNAME:EDT
|
||||
DTSTART:19700308T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0400
|
||||
TZOFFSETTO:-0500
|
||||
TZNAME:EST
|
||||
DTSTART:19701101T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
|
||||
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
|
||||
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
|
||||
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
|
||||
effectively shift the time change by a whole week.
|
||||
*/
|
||||
if (dayOfWeek == dateDayOfWeek)
|
||||
pos--;
|
||||
|
||||
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
|
||||
tmpDate = [tmpDate addYear: 0 month: 0 day: offset
|
||||
hour: 0 minute: 0 second: 0];
|
||||
|
||||
|
||||
return tmpDate;
|
||||
}
|
||||
|
||||
|
||||
@@ -1075,6 +1075,12 @@ static NSCharacterSet *whitespaceCharSet = nil;
|
||||
// FIXME: Data is not always utf-8.....
|
||||
source = [[[NSString alloc] initWithData: _data encoding: encoding]
|
||||
autorelease];
|
||||
|
||||
// We fallback to ISO-8859-1 string encoding
|
||||
if (!source)
|
||||
source = [[[NSString alloc] initWithData: _data encoding: NSISOLatin1StringEncoding]
|
||||
autorelease];
|
||||
|
||||
if (!source)
|
||||
{
|
||||
e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=SOGo is a groupware server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Environment="PREFORK=3"
|
||||
EnvironmentFile=-/etc/sysconfig/sogo
|
||||
Type=forking
|
||||
ExecStart=/usr/sbin/sogod -WOWorkersCount ${PREFORK} -WOPidFile /var/run/sogo/sogo.pid -WOLogFile /var/log/sogo/sogo.log
|
||||
PIDFile=/var/run/sogo/sogo.pid
|
||||
User=sogo
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# This script only works with MySQL
|
||||
# updates c_partstates to mediumtext
|
||||
# adds c_description to Calendar quick tables
|
||||
# http://www.sogo.nu/bugs/view.php?id=3175
|
||||
# the field length was actually changed in v2.2.18
|
||||
|
||||
defaultusername=$USER
|
||||
defaulthostname=127.0.0.1
|
||||
defaultdatabase=$USER
|
||||
indextable=$(sogo-tool dump-defaults -f /etc/sogo/sogo.conf | awk -F\" '/ OCSFolderInfoURL =/ {print $2}' | awk -F/ '{print $NF}')
|
||||
if [ -z "$indextable" ]; then
|
||||
echo "Couldn't fetch OCSFolderInfoURL value, aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Username ($defaultusername): " username
|
||||
read -p "Hostname ($defaulthostname): " hostname
|
||||
read -p "Database ($defaultdatabase): " database
|
||||
|
||||
if [ -z "$username" ]
|
||||
then
|
||||
username=$defaultusername
|
||||
fi
|
||||
|
||||
if [ -z "$hostname" ]
|
||||
then
|
||||
hostname=$defaulthostname
|
||||
fi
|
||||
|
||||
if [ -z "$database" ]
|
||||
then
|
||||
database=$defaultdatabase
|
||||
fi
|
||||
|
||||
sqlscript=""
|
||||
|
||||
function adjustSchema() {
|
||||
oldIFS="$IFS"
|
||||
IFS=" "
|
||||
part1="`echo -e \"ALTER TABLE $table MODIFY c_partstates mediumtext;\\n\"`";
|
||||
part2="`echo -e \"ALTER TABLE $table ADD COLUMN c_description mediumtext;\\n\"`";
|
||||
sqlscript="$sqlscript$part1$part2"
|
||||
IFS="$oldIFS"
|
||||
}
|
||||
|
||||
echo "This script will ask for the sql password twice" >&2
|
||||
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
||||
tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"`
|
||||
|
||||
for table in $tables;
|
||||
do
|
||||
adjustSchema
|
||||
done
|
||||
|
||||
echo "$sqlscript" | mysql -p -s -u $username -h $hostname $database
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
# This script only works with PostgreSQL
|
||||
# updates c_partstates to text
|
||||
# adds c_description to Calendar quick tables
|
||||
# http://www.sogo.nu/bugs/view.php?id=3175
|
||||
# the field length was actually changed in v2.2.18
|
||||
|
||||
defaultusername=$USER
|
||||
defaulthostname=localhost
|
||||
defaultdatabase=$USER
|
||||
#indextable=sogo_folder_info
|
||||
indextable=$(sogo-tool dump-defaults -f /etc/sogo/sogo.conf | awk -F\" '/ OCSFolderInfoURL =/ {print $2}' | awk -F/ '{print $NF}')
|
||||
if [ -z "$indextable" ]; then
|
||||
echo "Couldn't fetch OCSFolderInfoURL value, aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Username ($defaultusername): " username
|
||||
read -p "Hostname ($defaulthostname): " hostname
|
||||
read -p "Database ($defaultdatabase): " database
|
||||
|
||||
if [ -z "$username" ]
|
||||
then
|
||||
username=$defaultusername
|
||||
fi
|
||||
if [ -z "$hostname" ]
|
||||
then
|
||||
hostname=$defaulthostname
|
||||
fi
|
||||
if [ -z "$database" ]
|
||||
then
|
||||
database=$defaultdatabase
|
||||
fi
|
||||
|
||||
sqlscript=""
|
||||
|
||||
function adjustSchema() {
|
||||
oldIFS="$IFS"
|
||||
IFS=" "
|
||||
part1="`echo -e \"ALTER TABLE $table ALTER COLUMN c_partstates TYPE TEXT;\\n\"`";
|
||||
part2="`echo -e \"ALTER TABLE $table ADD COLUMN c_description TEXT;\\n\"`";
|
||||
sqlscript="$sqlscript$part1$part2"
|
||||
IFS="$oldIFS"
|
||||
}
|
||||
|
||||
echo "Converting c_cycleinfo from VARCHAR(255) to TEXT in calendar quick tables" >&2
|
||||
tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"`
|
||||
|
||||
for table in $tables;
|
||||
do
|
||||
adjustSchema
|
||||
done
|
||||
|
||||
echo "$sqlscript" | psql -q -e -U $username -h $hostname $database
|
||||
@@ -0,0 +1,67 @@
|
||||
"Inviting the following persons is prohibited:" = "Ondorengo pertsonak gonbidatzea debekatuta dago:";
|
||||
"Personal Calendar" = "Egutegi pertsonala";
|
||||
vevent_class0 = "(Ekitaldi publikoa)";
|
||||
vevent_class1 = "(Ekitaldi pribatua)";
|
||||
vevent_class2 = "(Isilpeko ekitaldia)";
|
||||
|
||||
vtodo_class0 = "(Zeregin publikoa)";
|
||||
vtodo_class1 = "(Zeregin pribatua)";
|
||||
vtodo_class2 = "(Isilpeko egitekoa)";
|
||||
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "\"%{Summary}\" ekitaldia sortu da";
|
||||
"The event \"%{Summary}\" was deleted" = "\"%{Summary}\" ekitaldia ezabatu da";
|
||||
"The event \"%{Summary}\" was updated" = "\"%{Summary}\" ekitaldia eguneratu da";
|
||||
"The following attendees(s) were notified:" = "Ondorengo partaidea(k) jakinarazi d(ir)a:";
|
||||
"The following attendees(s) were added:" = "Ondorengo partaidea(k) gehitu d(ir)a:";
|
||||
"The following attendees(s) were removed:" = "Ondorengo partaidea(k) ezabatu d(ir)a:";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Egutegia";
|
||||
"startDate_label" = "Hasi:";
|
||||
"endDate_label" = "Amaitu:";
|
||||
"due_label" = "Epemuga:";
|
||||
"location_label" = "Kokapena:";
|
||||
"summary_label" = "Laburpena:";
|
||||
"comment_label" = "Iruzkina:";
|
||||
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Ekitaldirako gonbidapena: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(%{SentBy}-k bidalia) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}-k gonbidatu zaitu %{Summary}.-ra\n\nHasiera: %{StartDate}\nAmaiera: %{EndDate}\nDeskribapena: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}-k gonbidatu zaitu %{Summary}-ra \n\nHasiera: %{StartDate}-an, %{StartTime}-tan\nEnd: %{EndDate}-an, %{EndTime}-tan\nDeskribapena: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Ekitaldia bertan behera utzi da: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} ekitaldi hau bertan behera utzi du: %{Summary}.\n\nHasiera: %{StartDate}\nAmaiera: %{EndDate}\nDEskribapena: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}-k honako ekitaldia bertan behera utzi du: %{Summary}.\n\nHasiera: %{StartDate}-an, %{StartTime}-tan\nAmaiera: %{EndDate}-an %{EndTime}-tan\nDeskribapena: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "%{OldStartDate} eguneko \"%{Summary}\" hitzordua aldatu da";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "%{OldStartDate} eguneko eta %{OldStartTime} orduko \"%{Summary}\" hitzordua aldatu da";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= " \"%{Summary}\" bilerako ondorengo parametroak aldatu dira:";
|
||||
"Please accept or decline those changes."
|
||||
= "Mesedez, onartu edo ezetsi ondorengo aldaketak.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Gonbidapena onartua: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Ezetzitako gonbidapena: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Delegatutako gonbidapena: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "\"%{Summary}\" gonbidapenari buruz erabaki gabe";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText} -k zure gonbidapena onartu du.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText}-k zure gonbidapenari uko egin dio.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText}-k zure gonbidapena %{Delegate}-ri delegatu dio.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText}-k ez du oraindik zure gonbidapenari buruz erabakirik hartu.";
|
||||
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Ezin da honako baliabidea atzitu: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "\"%{Cn} %{SystemEmail}\" baliabidearentzako gehienezko erreserba kopurura (%{NumberOfSimultaneousBookings}) iritsi gara. Gatazka sortu duen ekitaldia \"%{EventTitle}\" da, eta %{StartDate}-n hasten da.";
|
||||
@@ -56,7 +56,7 @@ vtodo_class2 = "(Tarefa Confidencial)";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText}foi aceitado seu convite ao evento.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "%{Attendee} %{SentByText}foi declinado seu convite ao evento.";
|
||||
= "%{Attendee} %{SentByText}recusou seu convite para o evento.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText} delegou o convite para %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
|
||||
@@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \
|
||||
MSExchangeFreeBusySOAPResponseMap.plist \
|
||||
MSExchangeFreeBusySOAPRequest.wo
|
||||
|
||||
Appointments_LANGUAGES = Arabic BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
@@ -70,11 +70,27 @@
|
||||
to: (NSCalendarDate *) newEndDate
|
||||
{
|
||||
ASSIGN(address, newAddress);
|
||||
ASSIGN(startDate, newStartDate);
|
||||
ASSIGN(endDate, newEndDate);
|
||||
|
||||
startDate = [NSCalendarDate dateWithYear: [newStartDate yearOfCommonEra]
|
||||
month: [newStartDate monthOfYear]
|
||||
day: [newStartDate dayOfMonth]
|
||||
hour: [newStartDate hourOfDay]
|
||||
minute: [newStartDate minuteOfHour]
|
||||
second: [newStartDate secondOfMinute]
|
||||
timeZone: [newStartDate timeZone]];
|
||||
endDate = [NSCalendarDate dateWithYear: [newEndDate yearOfCommonEra]
|
||||
month: [newEndDate monthOfYear]
|
||||
day: [newEndDate dayOfMonth]
|
||||
hour: [newEndDate hourOfDay]
|
||||
minute: [newEndDate minuteOfHour]
|
||||
second: [newEndDate secondOfMinute]
|
||||
timeZone: [newEndDate timeZone]];
|
||||
|
||||
[startDate setTimeZone: timeZone];
|
||||
[endDate setTimeZone: timeZone];
|
||||
|
||||
[startDate retain];
|
||||
[endDate retain];
|
||||
}
|
||||
|
||||
- (NSString *) serverVersion
|
||||
|
||||
@@ -492,6 +492,9 @@ static Class iCalEventK = nil;
|
||||
//
|
||||
// If the user is NOT the owner of the calendar, by default we exclude the freebusy information.
|
||||
//
|
||||
// We must include the freebusy information of other users if we are actually looking at their freebusy information
|
||||
// but we aren't necessarily subscribed to their calendars.
|
||||
//
|
||||
- (BOOL) includeInFreeBusy
|
||||
{
|
||||
NSNumber *excludeFromFreeBusy;
|
||||
@@ -499,17 +502,32 @@ static Class iCalEventK = nil;
|
||||
BOOL is_owner;
|
||||
|
||||
userLogin = [[context activeUser] login];
|
||||
is_owner = [userLogin isEqualToString: [self ownerInContext: context]];
|
||||
|
||||
is_owner = [userLogin isEqualToString: self->owner];
|
||||
|
||||
// Check if the owner (not the active user) has excluded the calendar from her/his free busy data.
|
||||
excludeFromFreeBusy
|
||||
= [self folderPropertyValueInCategory: @"FreeBusyExclusions"
|
||||
forUser: [SOGoUser userWithLogin: userLogin]];
|
||||
forUser: [context activeUser]];
|
||||
|
||||
if (!excludeFromFreeBusy && !is_owner)
|
||||
return NO;
|
||||
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
if ([self isSubscription])
|
||||
{
|
||||
// If the user has not yet set an include/not include fb information let's EXCLUDE it.
|
||||
if (!excludeFromFreeBusy)
|
||||
return NO;
|
||||
else
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
}
|
||||
else if (is_owner)
|
||||
{
|
||||
// We are the owner but we haven't included/excluded freebusy info, let's INCLUDE it.
|
||||
if (!excludeFromFreeBusy)
|
||||
return YES;
|
||||
else
|
||||
return ![excludeFromFreeBusy boolValue];
|
||||
}
|
||||
|
||||
// It's not a subscribtion and we aren't the owner. Let's INCLUDE the freebusy info.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) setIncludeInFreeBusy: (BOOL) newInclude
|
||||
@@ -782,7 +800,7 @@ static Class iCalEventK = nil;
|
||||
|
||||
/**
|
||||
* Set the timezone of the event start and end dates to the user's timezone.
|
||||
* @param theRecord a dictionnary with the attributes of the event.
|
||||
* @param theRecord a dictionary with the attributes of the event.
|
||||
* @return a copy of theRecord with adjusted dates.
|
||||
*/
|
||||
- (NSMutableDictionary *) _fixupRecord: (NSDictionary *) theRecord
|
||||
@@ -908,11 +926,6 @@ static Class iCalEventK = nil;
|
||||
dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]];
|
||||
[record setObject: dateSecs forKey: @"c_enddate"];
|
||||
|
||||
// The first instance date is added to the dictionary so it can
|
||||
// be used by UIxCalListingActions to compute the DST offset.
|
||||
date = [theFirstCycle startDate];
|
||||
[record setObject: date forKey: @"cycleStartDate"];
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@@ -1085,9 +1098,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
|
||||
[newRecord setObject: dateSecs forKey: @"c_recurrence_id"];
|
||||
[newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"c_iscycle"];
|
||||
// The first instance date is added to the dictionary so it can
|
||||
// be used by UIxCalListingActions to compute the DST offset.
|
||||
[newRecord setObject: [fir startDate] forKey: @"cycleStartDate"];
|
||||
|
||||
// We identified the record as an exception.
|
||||
[newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"isException"];
|
||||
|
||||
@@ -2803,7 +2814,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
@"c_category", @"c_classification", @"c_isallday",
|
||||
@"c_isopaque", @"c_participants", @"c_partmails",
|
||||
@"c_partstates", @"c_sequence", @"c_priority",
|
||||
@"c_cycleinfo", @"c_iscycle", @"c_nextalarm", nil];
|
||||
@"c_cycleinfo", @"c_iscycle", @"c_nextalarm", @"c_description", nil];
|
||||
|
||||
return [self fetchFields: infos from: _startDate to: _endDate title: title
|
||||
component: _component
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/* SOGoAppointmentFolders.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -400,9 +400,9 @@
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
if (currentUID)
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
owner: owner];
|
||||
[self _addOrUpdateEvent: newEvent
|
||||
forUID: currentUID
|
||||
owner: owner];
|
||||
}
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Update"
|
||||
@@ -421,11 +421,14 @@
|
||||
SOGoUserSettings *us;
|
||||
NSMutableArray *unavailableAttendees;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID, *ownerUID, *whiteListString;
|
||||
NSString *currentUID, *ownerUID;
|
||||
NSMutableString *reason;
|
||||
NSDictionary *values;
|
||||
NSMutableDictionary *value, *moduleSettings, *whiteList;
|
||||
NSMutableDictionary *value, *moduleSettings;
|
||||
id whiteList;
|
||||
|
||||
int i, count;
|
||||
|
||||
i = count = 0;
|
||||
|
||||
// Build list of the attendees uids without ressources
|
||||
@@ -443,12 +446,15 @@
|
||||
us = [user userSettings];
|
||||
moduleSettings = [us objectForKey:@"Calendar"];
|
||||
|
||||
// Check if the user prevented his account from beeing invited to events
|
||||
// Check if the user prevented their account from beeing invited to events
|
||||
if (![user isResource] && [[moduleSettings objectForKey:@"PreventInvitations"] boolValue])
|
||||
{
|
||||
// Check if the user have a whiteList
|
||||
whiteListString = [moduleSettings objectForKey:@"PreventInvitationsWhitelist"];
|
||||
whiteList = [whiteListString objectFromJSONString];
|
||||
whiteList = [moduleSettings objectForKey:@"PreventInvitationsWhitelist"];
|
||||
|
||||
// For backward <= 2.2.17 compatibility
|
||||
if ([whiteList isKindOfClass: [NSString class]])
|
||||
whiteList = [whiteList objectFromJSONString];
|
||||
|
||||
// If the filter have a hit, do not add the currentUID to the unavailableAttendees array
|
||||
if (![whiteList objectForKey:ownerUID])
|
||||
@@ -637,10 +643,10 @@
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
if ([user numberOfSimultaneousBookings] <= 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
{
|
||||
if (currentAttendee)
|
||||
@@ -734,7 +740,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// the modification was actually NOT made on the master event
|
||||
if ([theEvent recurrenceId])
|
||||
return;
|
||||
|
||||
|
||||
events = [[theEvent parent] events];
|
||||
|
||||
for (i = 0; i < [events count]; i++)
|
||||
@@ -743,12 +749,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if ([e recurrenceId])
|
||||
for (j = 0; j < [theAttendees count]; j++)
|
||||
if (shouldAdd)
|
||||
[e addToAttendees: [theAttendees objectAtIndex: j]];
|
||||
else
|
||||
[e removeFromAttendees: [theAttendees objectAtIndex: j]];
|
||||
[e addToAttendees: [theAttendees objectAtIndex: j]];
|
||||
else
|
||||
[e removeFromAttendees: [theAttendees objectAtIndex: j]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -803,14 +809,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// We insert the attendees in all exception occurences, if
|
||||
// the attendees were added to the master event.
|
||||
[self _addOrDeleteAttendees: addedAttendees
|
||||
inRecurrenceExceptionsForEvent: newEvent
|
||||
add: YES];
|
||||
inRecurrenceExceptionsForEvent: newEvent
|
||||
add: YES];
|
||||
|
||||
if ([changes sequenceShouldBeIncreased])
|
||||
{
|
||||
[newEvent increaseSequence];
|
||||
|
||||
// Update attendees calendars and send them an update
|
||||
// notification by email
|
||||
// notification by email. We ignore the newly added
|
||||
// attendees as we don't want to send them invitation
|
||||
// update emails
|
||||
[self _handleSequenceUpdateInEvent: newEvent
|
||||
ignoringAttendees: addedAttendees
|
||||
fromOldEvent: oldEvent];
|
||||
@@ -1093,7 +1102,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
else
|
||||
{
|
||||
// We must REMOVE any SENT-BY here. This is important since if A accepted
|
||||
// the event for B and then, B changes by himself his participation status,
|
||||
// the event for B and then, B changes by theirself their participation status,
|
||||
// we don't want to keep the previous SENT-BY attribute there.
|
||||
[(NSMutableDictionary *)[otherAttendee attributes] removeObjectForKey: @"SENT-BY"];
|
||||
}
|
||||
@@ -1191,7 +1200,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
else
|
||||
{
|
||||
// We must REMOVE any SENT-BY here. This is important since if A accepted
|
||||
// the event for B and then, B changes by himself his participation status,
|
||||
// the event for B and then, B changes by theirself their participation status,
|
||||
// we don't want to keep the previous SENT-BY attribute there.
|
||||
[(NSMutableDictionary *)[attendee attributes] removeObjectForKey: @"SENT-BY"];
|
||||
}
|
||||
@@ -1208,7 +1217,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
delegatedUID = [otherDelegate uid];
|
||||
if (delegatedUID)
|
||||
// Delegate attendee is a local user; remove event from his calendar
|
||||
// Delegate attendee is a local user; remove event from their calendar
|
||||
[self _removeEventFromUID: delegatedUID
|
||||
owner: [theOwnerUser login]
|
||||
withRecurrenceId: [event recurrenceId]];
|
||||
@@ -1240,7 +1249,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
[event addToAttendees: delegate];
|
||||
|
||||
if (delegatedUID)
|
||||
// Delegate attendee is a local user; add event to his calendar
|
||||
// Delegate attendee is a local user; add event to their calendar
|
||||
[self _addOrUpdateEvent: event
|
||||
forUID: delegatedUID
|
||||
owner: [theOwnerUser login]];
|
||||
|
||||
@@ -644,8 +644,8 @@
|
||||
// As much as we can, we try to use c_name == c_uid in order
|
||||
// to avoid tricky scenarios with some CalDAV clients. For example,
|
||||
// if Alice invites Bob (both use SOGo) and Bob accepts the invitation
|
||||
// using Lightning before having refreshed his calendar, he'll end up
|
||||
// with a duplicate of the event in his database tables.
|
||||
// using Lightning before having refreshed their calendar, they'll end up
|
||||
// with a duplicate of the event in their database tables.
|
||||
if (isNew)
|
||||
{
|
||||
newUid = nameInContainer;
|
||||
|
||||
@@ -62,17 +62,14 @@
|
||||
SOGoUserManager *um;
|
||||
NSString *domain;
|
||||
NSDictionary *contactInfos;
|
||||
NSArray *contacts;
|
||||
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
contactInfos = [um contactInfosForUserWithUIDorEmail: uid];
|
||||
if (contactInfos == nil)
|
||||
{
|
||||
// Search among global addressbooks
|
||||
domain = [[context activeUser] domain];
|
||||
[um fetchContactsMatching: uid inDomain: domain];
|
||||
contacts = [um fetchContactsMatching: uid inDomain: domain];
|
||||
if ([contacts count] == 1)
|
||||
contactInfos = [contacts lastObject];
|
||||
contactInfos = [um fetchContactWithUID: uid inDomain: domain];
|
||||
}
|
||||
|
||||
/* iCal.app compatibility:
|
||||
@@ -279,7 +276,6 @@
|
||||
if ([uid length])
|
||||
{
|
||||
SOGoUserManager *um;
|
||||
NSArray *contacts;
|
||||
NSString *domain, *email;
|
||||
NSDictionary *contact;
|
||||
MSExchangeFreeBusy *exchangeFreeBusy;
|
||||
@@ -287,10 +283,9 @@
|
||||
|
||||
um = [SOGoUserManager sharedUserManager];
|
||||
domain = [[context activeUser] domain];
|
||||
contacts = [um fetchContactsMatching: uid inDomain: domain];
|
||||
if ([contacts count] == 1)
|
||||
contact = [um fetchContactWithUID: uid inDomain: domain];
|
||||
if (contact)
|
||||
{
|
||||
contact = [contacts lastObject];
|
||||
email = [contact valueForKey: @"c_email"];
|
||||
source = [contact objectForKey: @"source"];
|
||||
if ([email length]
|
||||
@@ -386,8 +381,7 @@
|
||||
([currentStartDate compare: startDate] == NSOrderedAscending)? startDate : currentStartDate, @"startDate",
|
||||
currentEndDate, @"endDate", nil]];
|
||||
|
||||
if (!firstRange
|
||||
&& currentEndDate != endDate
|
||||
if (currentEndDate != endDate
|
||||
&& ([currentEndDate dayOfWeek] == 6 || [currentEndDate dayOfWeek] == 0))
|
||||
{
|
||||
// Fill weekend days
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoWebAppointmentFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2014 Inverse inc.
|
||||
* Copyright (C) 2009-2015 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -137,6 +137,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
|
||||
curl_easy_setopt (curl, CURLOPT_URL, [location UTF8String]);
|
||||
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt (curl, CURLOPT_TIMEOUT, 60L);
|
||||
|
||||
authInfos = [self _loadAuthData];
|
||||
if (authInfos)
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
element = [elements objectAtIndex: 0];
|
||||
else
|
||||
{
|
||||
NSLog(@"ERROR: given calendar contains no elements: %@", self);
|
||||
//NSLog(@"ERROR: given calendar contains no elements: %@", self);
|
||||
element = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +248,12 @@
|
||||
else
|
||||
[row setObject: [NSNull null] forKey: @"c_category"];
|
||||
|
||||
/* handle description */
|
||||
if ([self comment])
|
||||
[row setObject: [self comment] forKey: @"c_description"];
|
||||
else
|
||||
[row setObject: [NSNull null] forKey: @"c_description"];
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,12 @@
|
||||
[row setObject: [categories componentsJoinedByString: @","]
|
||||
forKey: @"c_category"];
|
||||
|
||||
/* handle description */
|
||||
if ([self comment])
|
||||
[row setObject: [self comment] forKey: @"c_description"];
|
||||
else
|
||||
[row setObject: [NSNull null] forKey: @"c_description"];
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "Helbide liburu pertsonala";
|
||||
"Collected Address Book" = "Bildutako helbide liburua";
|
||||
@@ -1,2 +1,2 @@
|
||||
"Personal Address Book" = "Livro de Endereços Pessoais";
|
||||
"Personal Address Book" = "Livro de Endereço Pessoal";
|
||||
"Collected Address Book" = "Catálogos Coletados";
|
||||
@@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
|
||||
Contacts_RESOURCE_FILES += \
|
||||
product.plist \
|
||||
|
||||
Contacts_LANGUAGES = Arabic BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
|
||||
|
||||
Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings
|
||||
|
||||
|
||||
Loaded 100 of 294 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user