Compare commits
501
Commits
SOGo-2.0.5
...
SOGo-2.2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f98ef34e | ||
|
|
378f2b5876 | ||
|
|
d091b3d7cd | ||
|
|
370e1e6d26 | ||
|
|
e2b3d5a7ff | ||
|
|
078b88a396 | ||
|
|
ee09b80fc3 | ||
|
|
99d9615c92 | ||
|
|
79c3fa8ad2 | ||
|
|
fd07112113 | ||
|
|
ce5d591d1d | ||
|
|
e603742d71 | ||
|
|
585d224a15 | ||
|
|
6a39b48a37 | ||
|
|
633723a470 | ||
|
|
ba32b95308 | ||
|
|
6e4f776d4b | ||
|
|
e5fb7dc7b8 | ||
|
|
50e7ea7188 | ||
|
|
ae0c5eae77 | ||
|
|
89e61f6d5d | ||
|
|
0c2d727057 | ||
|
|
85f4af56e9 | ||
|
|
04bdb22b00 | ||
|
|
874b1d9eba | ||
|
|
b143688419 | ||
|
|
5f3d2d988e | ||
|
|
93f1540c17 | ||
|
|
877e44c6d2 | ||
|
|
4ca3d351e0 | ||
|
|
19b6704955 | ||
|
|
64787555ff | ||
|
|
27ffcf240f | ||
|
|
893d75bd5f | ||
|
|
b0f26f1dcc | ||
|
|
a196ed210e | ||
|
|
1c28a4b4d2 | ||
|
|
fc56493b78 | ||
|
|
3a33010918 | ||
|
|
83bccda551 | ||
|
|
35d71f6150 | ||
|
|
e92a145325 | ||
|
|
c3062284e3 | ||
|
|
e2f192b793 | ||
|
|
b8f239bf34 | ||
|
|
e968be8024 | ||
|
|
3c3bebeee3 | ||
|
|
26e9defdbd | ||
|
|
8742f28c29 | ||
|
|
bd577fd513 | ||
|
|
c8550c8a4a | ||
|
|
1bdf88fb67 | ||
|
|
167a06e3e9 | ||
|
|
af68f10115 | ||
|
|
cbd22762c3 | ||
|
|
78636f0396 | ||
|
|
037c2d3ddf | ||
|
|
072f5f5a15 | ||
|
|
41fe9195f2 | ||
|
|
23789b48bc | ||
|
|
142296f71e | ||
|
|
3bae2387d4 | ||
|
|
6cecca6c4f | ||
|
|
28b1938bd9 | ||
|
|
31ace947cb | ||
|
|
0cea36df2e | ||
|
|
afd7452323 | ||
|
|
e4a21bcef5 | ||
|
|
0c99323db3 | ||
|
|
da208d5b4a | ||
|
|
158af71152 | ||
|
|
163bce39b5 | ||
|
|
02c72fee6a | ||
|
|
8bef84bded | ||
|
|
8db3b22eb9 | ||
|
|
1aabcf04b5 | ||
|
|
1db85743a0 | ||
|
|
c6b227160c | ||
|
|
5f2920db9b | ||
|
|
761a7e2be1 | ||
|
|
9ed1d57ec2 | ||
|
|
2b6428efd0 | ||
|
|
be36e0ceea | ||
|
|
070d3dd825 | ||
|
|
96f2552b45 | ||
|
|
2948920e0b | ||
|
|
212d7149ed | ||
|
|
668c7e8b49 | ||
|
|
9851c32a1e | ||
|
|
e7f27427c8 | ||
|
|
f0a29d3ca2 | ||
|
|
ec0a5ac4bf | ||
|
|
4ca8b9d0aa | ||
|
|
d560d0e470 | ||
|
|
401320e08b | ||
|
|
8083b41092 | ||
|
|
9218c7f253 | ||
|
|
4d1fdb33f5 | ||
|
|
2356562778 | ||
|
|
c686e3294d | ||
|
|
1ff91f7b59 | ||
|
|
b34c6324cd | ||
|
|
75c8362df8 | ||
|
|
ae07de5f6f | ||
|
|
06d2364ea2 | ||
|
|
29e081ec56 | ||
|
|
113b02fd71 | ||
|
|
35b108e645 | ||
|
|
70266155d3 | ||
|
|
fcfd8be771 | ||
|
|
b91032db8e | ||
|
|
f4874600fe | ||
|
|
b14e99b32a | ||
|
|
ea4b21e991 | ||
|
|
0307b8339a | ||
|
|
b5f1d3a19a | ||
|
|
8225f76279 | ||
|
|
c94595ea7f | ||
|
|
3a5e44e7eb | ||
|
|
80a0940765 | ||
|
|
7118bbe0ab | ||
|
|
eee5beb698 | ||
|
|
3363b253ac | ||
|
|
4070b9222d | ||
|
|
5ea56a6f8a | ||
|
|
379258de59 | ||
|
|
93b3685aa4 | ||
|
|
c4dc4d4eda | ||
|
|
c6d104fac5 | ||
|
|
ef79c09642 | ||
|
|
aeb712083a | ||
|
|
2c678101fc | ||
|
|
b95362f963 | ||
|
|
3175a9169f | ||
|
|
9311f05cc0 | ||
|
|
78cbcfb560 | ||
|
|
b8610b3eca | ||
|
|
1a7fc2a0e9 | ||
|
|
f7a20d0a83 | ||
|
|
a1cf066828 | ||
|
|
359b6a99aa | ||
|
|
826537ed01 | ||
|
|
3950e9d4c3 | ||
|
|
9885211a5b | ||
|
|
ef1be5dc5f | ||
|
|
4e42d5d410 | ||
|
|
31969d162d | ||
|
|
2ff3b5ef5f | ||
|
|
7fe48a1555 | ||
|
|
fcddeb4224 | ||
|
|
f1cb87aa02 | ||
|
|
a6424680cc | ||
|
|
b3dc645282 | ||
|
|
2f0419c18a | ||
|
|
9e9407cf30 | ||
|
|
d709a1216b | ||
|
|
588352be7d | ||
|
|
a40d7ca342 | ||
|
|
e4ce687e29 | ||
|
|
bcb0764b19 | ||
|
|
ddd7be4333 | ||
|
|
d9f4a9935a | ||
|
|
ae75fee512 | ||
|
|
8cdacd065f | ||
|
|
af6a69c9fc | ||
|
|
dc41e29f05 | ||
|
|
afc7519beb | ||
|
|
f2d5a76911 | ||
|
|
342ad84a24 | ||
|
|
9580a8f8ee | ||
|
|
a96380a0dc | ||
|
|
2fed02da59 | ||
|
|
84dc08adb6 | ||
|
|
c160edf20a | ||
|
|
3b7a3c94d4 | ||
|
|
7b97e28cff | ||
|
|
81bf1b41e9 | ||
|
|
ea86f136b7 | ||
|
|
fc0c580fe1 | ||
|
|
42a7bcb9ac | ||
|
|
9669f2bbba | ||
|
|
2563408872 | ||
|
|
dbea244f49 | ||
|
|
0f9d8cdd2c | ||
|
|
5105dbb342 | ||
|
|
b26e767a10 | ||
|
|
940f85fde2 | ||
|
|
adee80529e | ||
|
|
295dfcfdd5 | ||
|
|
aa18abd842 | ||
|
|
feb398d59c | ||
|
|
19b404de45 | ||
|
|
d1a384e539 | ||
|
|
e7f38f940e | ||
|
|
f7ba5d2346 | ||
|
|
eeddf43a78 | ||
|
|
eabe829236 | ||
|
|
762b841249 | ||
|
|
3090448fcb | ||
|
|
a3e1d4813a | ||
|
|
046a645119 | ||
|
|
07d4b2bae2 | ||
|
|
343f2d8bfe | ||
|
|
65276f7dc0 | ||
|
|
24663682d6 | ||
|
|
57cd315f2a | ||
|
|
29fcfda118 | ||
|
|
ada90677e1 | ||
|
|
4c9c07e358 | ||
|
|
b71180b006 | ||
|
|
1ed41e2831 | ||
|
|
948553dce3 | ||
|
|
74adf78446 | ||
|
|
ee0ae8a1c0 | ||
|
|
38c649751c | ||
|
|
645c718dfa | ||
|
|
02992a7018 | ||
|
|
0ffa0649c5 | ||
|
|
7c7cabe432 | ||
|
|
bb9c4cf039 | ||
|
|
c252f5b7a8 | ||
|
|
2e44ac0f9b | ||
|
|
2a9ccf8122 | ||
|
|
f74066c9a6 | ||
|
|
ee49836f2e | ||
|
|
bf798061d3 | ||
|
|
834e05bab1 | ||
|
|
e8438a6235 | ||
|
|
68dafdfdeb | ||
|
|
e5bc46710c | ||
|
|
7355eae1dc | ||
|
|
1d9febb511 | ||
|
|
e21b30d768 | ||
|
|
ae200360ba | ||
|
|
e217ffb6c4 | ||
|
|
a4a3a735b4 | ||
|
|
13721b961b | ||
|
|
689a1e94e0 | ||
|
|
afcd92fb71 | ||
|
|
7260c07628 | ||
|
|
a24d809e40 | ||
|
|
ed97407578 | ||
|
|
ca541d7299 | ||
|
|
2d683ffc77 | ||
|
|
d603a8672e | ||
|
|
d51e1da57f | ||
|
|
80fd439dea | ||
|
|
a0d7f184fe | ||
|
|
fe2826ca76 | ||
|
|
ff9ea3b779 | ||
|
|
e70793e7ae | ||
|
|
0fdea48ce8 | ||
|
|
2b53705d44 | ||
|
|
b716331d8e | ||
|
|
586d66b113 | ||
|
|
5756defbf1 | ||
|
|
7130cec4d1 | ||
|
|
009cfccb2c | ||
|
|
09e14df79b | ||
|
|
ba29e7620d | ||
|
|
32516b162e | ||
|
|
a8e780516b | ||
|
|
bd7ad5be16 | ||
|
|
bed57af015 | ||
|
|
5f369f201d | ||
|
|
dc21c723f6 | ||
|
|
1f7994d1bf | ||
|
|
7369a82bab | ||
|
|
1a900b05d9 | ||
|
|
a8e3418a4c | ||
|
|
59ce12a51b | ||
|
|
a1bec0e995 | ||
|
|
08ab36244c | ||
|
|
9ea880e80b | ||
|
|
7d0a9aa1b7 | ||
|
|
2fa654fadb | ||
|
|
4a98e5b521 | ||
|
|
e5103faed4 | ||
|
|
f9ad9ea815 | ||
|
|
cfbd53e374 | ||
|
|
f2beabec88 | ||
|
|
eaa0fb3cf1 | ||
|
|
db56d1fa6c | ||
|
|
37de8c6141 | ||
|
|
b0eb34f6b7 | ||
|
|
1cf696f57c | ||
|
|
2114a48222 | ||
|
|
384d998c47 | ||
|
|
2211f6d168 | ||
|
|
32ba01315f | ||
|
|
682ed767d9 | ||
|
|
5536f8967e | ||
|
|
df3e9033f3 | ||
|
|
f167475c91 | ||
|
|
e4aedbac08 | ||
|
|
2cf66e037d | ||
|
|
6587f4f193 | ||
|
|
c8a4ea5548 | ||
|
|
148fd4936c | ||
|
|
a7fef39448 | ||
|
|
5a3d0d27e0 | ||
|
|
eb9111b5fa | ||
|
|
eb7d1f08bc | ||
|
|
854fed529c | ||
|
|
3c29e0724d | ||
|
|
cebaed9059 | ||
|
|
aae26602b0 | ||
|
|
05233d9c53 | ||
|
|
e862194b32 | ||
|
|
884fbfdb73 | ||
|
|
c05cf00db7 | ||
|
|
198db855f6 | ||
|
|
1c439c866e | ||
|
|
616ee7c6e4 | ||
|
|
37d3234b60 | ||
|
|
449d838dc2 | ||
|
|
6a9bcfda68 | ||
|
|
d0b09ebe35 | ||
|
|
0361b7dfdb | ||
|
|
38b350382a | ||
|
|
73141a645e | ||
|
|
f07526423f | ||
|
|
ba35a4b003 | ||
|
|
47c4ad5807 | ||
|
|
0f3e31ebf1 | ||
|
|
87261f3280 | ||
|
|
bd2759ecdf | ||
|
|
be1bcf3c9b | ||
|
|
053b090aff | ||
|
|
189f2218a8 | ||
|
|
656869a4a2 | ||
|
|
16f62e2393 | ||
|
|
9289615996 | ||
|
|
fdea04c08c | ||
|
|
edca3638ec | ||
|
|
05990e9a14 | ||
|
|
3cce9b2012 | ||
|
|
7dc65af300 | ||
|
|
84e999229b | ||
|
|
3dbfb0ed2d | ||
|
|
d1f3417c3a | ||
|
|
7c7031333e | ||
|
|
1e48481426 | ||
|
|
8ae1624604 | ||
|
|
7166f5a597 | ||
|
|
55052ea36f | ||
|
|
99d38417cc | ||
|
|
29eed182a7 | ||
|
|
744610dbe1 | ||
|
|
d2b739d261 | ||
|
|
2ba8254ecc | ||
|
|
28e600a4e6 | ||
|
|
ab6e4841ba | ||
|
|
91f1ab6a60 | ||
|
|
32a534919d | ||
|
|
5b77c2b724 | ||
|
|
70086e5ee6 | ||
|
|
7ddc1b9e2d | ||
|
|
6d93db96e4 | ||
|
|
27445d3b85 | ||
|
|
60d6abe542 | ||
|
|
baacf8516d | ||
|
|
46a48faf23 | ||
|
|
193720cfbb | ||
|
|
f40580d632 | ||
|
|
ca4d89fb08 | ||
|
|
906985c1f3 | ||
|
|
de8bf64c70 | ||
|
|
0c38a9e555 | ||
|
|
3c6c90d434 | ||
|
|
72a4b075d6 | ||
|
|
24c0fb1cc4 | ||
|
|
7241e3e30e | ||
|
|
1d705dc00b | ||
|
|
df42ab2a46 | ||
|
|
53f9d0d7f5 | ||
|
|
f59c77a950 | ||
|
|
cd4abe4b5b | ||
|
|
69b0f9fcbf | ||
|
|
dceead3997 | ||
|
|
573c2ec114 | ||
|
|
6a68c255bd | ||
|
|
e9d8b729b6 | ||
|
|
1e7dfc0934 | ||
|
|
d7e6648396 | ||
|
|
65603201f7 | ||
|
|
9fd6f75c4e | ||
|
|
b96f25f99f | ||
|
|
d7851999da | ||
|
|
e6ab0a590a | ||
|
|
be531100c6 | ||
|
|
e946a67ddf | ||
|
|
2fe87f14fd | ||
|
|
b2f012cae2 | ||
|
|
33659aa947 | ||
|
|
df1ab29872 | ||
|
|
61e59b864e | ||
|
|
0a590ba388 | ||
|
|
ab92516dd0 | ||
|
|
7b90b892fc | ||
|
|
b654ffb806 | ||
|
|
6de689bbe2 | ||
|
|
7eb8b9c2c3 | ||
|
|
3877f3001f | ||
|
|
4d2c734781 | ||
|
|
e57d875e0a | ||
|
|
b1df03adc7 | ||
|
|
c4bc215a47 | ||
|
|
bd776152db | ||
|
|
c6374b52ec | ||
|
|
734c164ae0 | ||
|
|
c9936d7ecf | ||
|
|
9f381e768d | ||
|
|
b531b6160c | ||
|
|
885e59a2af | ||
|
|
8a041860a0 | ||
|
|
d5f76ac832 | ||
|
|
12b7fea825 | ||
|
|
1c82af8842 | ||
|
|
6cef5245e1 | ||
|
|
0069a4200b | ||
|
|
e24c20c48a | ||
|
|
256b679798 | ||
|
|
b85ed0db1c | ||
|
|
048bae2fc5 | ||
|
|
19e542396b | ||
|
|
249a1fcf14 | ||
|
|
7dad5ad84e | ||
|
|
16105d37b8 | ||
|
|
e08ebd2390 | ||
|
|
03553980fd | ||
|
|
33fd60efb3 | ||
|
|
b8e2255a07 | ||
|
|
e687dbf155 | ||
|
|
bf398889bf | ||
|
|
f42d52743d | ||
|
|
70cd64a626 | ||
|
|
4359b05da8 | ||
|
|
e26e3d7307 | ||
|
|
25dbd4849d | ||
|
|
045b275b27 | ||
|
|
a2a89e8720 | ||
|
|
7ec1973e0f | ||
|
|
2581f6c568 | ||
|
|
20369631c9 | ||
|
|
1221da2443 | ||
|
|
f27b8b6a54 | ||
|
|
a79a9c4eff | ||
|
|
a6b393c2c1 | ||
|
|
faf228022a | ||
|
|
3e87ba92db | ||
|
|
0a87ff97b6 | ||
|
|
031cd210f7 | ||
|
|
9d9a434fdc | ||
|
|
41ed498d65 | ||
|
|
d28482557f | ||
|
|
4c97d54939 | ||
|
|
c08263d1c3 | ||
|
|
dc96466462 | ||
|
|
1734b387a3 | ||
|
|
871115e349 | ||
|
|
a764286879 | ||
|
|
5304461e4c | ||
|
|
6b25f08943 | ||
|
|
a0022e06f7 | ||
|
|
f24e65b6d8 | ||
|
|
0fb006c19f | ||
|
|
47a3f1a19d | ||
|
|
f25344182d | ||
|
|
f24e3a0a31 | ||
|
|
afb7bc309c | ||
|
|
cfee5aa3f4 | ||
|
|
2a51f6f385 | ||
|
|
4cffea43eb | ||
|
|
cafce5753d | ||
|
|
5d6a5f5d5e | ||
|
|
8e5a26dfb1 | ||
|
|
4cb0facd72 | ||
|
|
854bce28fb | ||
|
|
49e964c529 | ||
|
|
3281cea2a2 | ||
|
|
82253e91d2 | ||
|
|
1d1ed9a031 | ||
|
|
2bd8a2935f | ||
|
|
8a25e38827 | ||
|
|
4ec19d46fa | ||
|
|
d46a39e252 | ||
|
|
23e10d30be | ||
|
|
f049e2430a | ||
|
|
a53c66e8a4 | ||
|
|
4fb3492ea8 | ||
|
|
353f40ac3e | ||
|
|
96c831f726 | ||
|
|
59b3219f2d | ||
|
|
d7fdfc6cb2 | ||
|
|
d02f58b3cd | ||
|
|
62fee93d86 | ||
|
|
139c14d9e1 | ||
|
|
8ee15c6f77 | ||
|
|
5d17414018 | ||
|
|
3cd91846a6 |
No files matched your search
@@ -9,3 +9,5 @@ tags
|
||||
*.swp
|
||||
SoObjects/SOGo/SOGo.framework/
|
||||
SoObjects/SOGo/derived_src/
|
||||
Tests/*/config.py
|
||||
*.pyc
|
||||
+27
-1
@@ -1,5 +1,5 @@
|
||||
[main]
|
||||
host = https://www.transifex.net
|
||||
host = https://www.transifex.com
|
||||
|
||||
[sogo.ui-mailerui]
|
||||
source_file = UI/MailerUI/English.lproj/Localizable.strings
|
||||
@@ -235,6 +235,32 @@ trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-mailer]
|
||||
source_file = SoObjects/Mailer/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = SoObjects/Mailer/Arabic.lproj/Localizable.strings
|
||||
trans.ca = SoObjects/Mailer/Catalan.lproj/Localizable.strings
|
||||
trans.cs = SoObjects/Mailer/Czech.lproj/Localizable.strings
|
||||
trans.cy = SoObjects/Mailer/Welsh.lproj/Localizable.strings
|
||||
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.fi = SoObjects/Mailer/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Mailer/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Mailer/Italian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Mailer/Swedish.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Mailer/Ukrainian.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mailpartviewers]
|
||||
source_file = UI/MailPartViewers/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface ActiveSyncProduct : NSObject
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ActiveSyncProduct
|
||||
@end /* ActiveSyncProduct */
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
# GNUstep makefile
|
||||
|
||||
include common.make
|
||||
|
||||
BUNDLE_NAME = ActiveSync
|
||||
|
||||
ActiveSync_PRINCIPAL_CLASS = ActiveSyncProduct
|
||||
|
||||
ActiveSync_OBJC_FILES = \
|
||||
ActiveSyncProduct.m \
|
||||
iCalAlarm+ActiveSync.m \
|
||||
iCalEvent+ActiveSync.m \
|
||||
iCalRecurrenceRule+ActiveSync.m \
|
||||
iCalTimeZone+ActiveSync.m \
|
||||
iCalToDo+ActiveSync.m \
|
||||
NSCalendarDate+ActiveSync.m \
|
||||
NSData+ActiveSync.m \
|
||||
NSDate+ActiveSync.m \
|
||||
NGDOMElement+ActiveSync.m \
|
||||
NGMimeMessage+ActiveSync.m \
|
||||
NGVCard+ActiveSync.m \
|
||||
NSString+ActiveSync.m \
|
||||
SOGoActiveSyncDispatcher.m \
|
||||
SOGoActiveSyncDispatcher+Sync.m \
|
||||
SOGoMailObject+ActiveSync.m \
|
||||
SoObjectWebDAVDispatcher+ActiveSync.m
|
||||
|
||||
ActiveSync_RESOURCE_FILES += \
|
||||
product.plist
|
||||
|
||||
ADDITIONAL_OBJCFLAGS += -Wno-deprecated-declarations
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../SOPE/ -I../SoObjects/
|
||||
ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj/ -L../SOPE/NGCards/obj/
|
||||
ADDITIONAL_INCLUDE_DIRS += -I/usr/include/libwbxml-1.0/
|
||||
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -lwbxml2
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
||||
-include GNUmakefile.postamble
|
||||
@@ -0,0 +1 @@
|
||||
# compilation settings
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NGDOMELEMENTACTIVESYNC_H__
|
||||
#define __NGDOMELEMENTACTIVESYNC_H__
|
||||
|
||||
#import <DOM/DOMElement.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
|
||||
@class NSDictionary;
|
||||
|
||||
@interface NGDOMElement (ActiveSync)
|
||||
|
||||
- (NSDictionary *) applicationData;
|
||||
|
||||
@end
|
||||
|
||||
#endif // NGDOMELEMENTACTIVESYNC
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#include "NGDOMElement+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
static NSArray *asElementArray = nil;
|
||||
|
||||
@implementation NGDOMElement (ActiveSync)
|
||||
|
||||
//
|
||||
// We must handle "inner data" like this:
|
||||
//
|
||||
// <ApplicationData>
|
||||
// <Flag xmlns="Email:">
|
||||
// <FlagStatus>2</FlagStatus>
|
||||
// <FlagType>Flag for follow up</FlagType>
|
||||
// </Flag>
|
||||
// </ApplicationData>
|
||||
//
|
||||
// and stuff like that:
|
||||
//
|
||||
// <Attendees xmlns="Calendar:">
|
||||
// <Attendee>
|
||||
// <Attendee_Email>sogo1@example.com</Attendee_Email>
|
||||
// <Attendee_Name>John Doe</Attendee_Name>
|
||||
// <Attendee_Status>5</Attendee_Status>
|
||||
// <Attendee_Type>1</Attendee_Type>
|
||||
// </Attendee>
|
||||
// <Attendee>
|
||||
// <Attendee_Email>sogo2@example.com</Attendee_Email>
|
||||
// <Attendee_Name>Balthazar César</Attendee_Name>
|
||||
// <Attendee_Status>5</Attendee_Status>
|
||||
// <Attendee_Type>1</Attendee_Type>
|
||||
// </Attendee>
|
||||
// <Attendee>
|
||||
// <Attendee_Email>sogo3@example.com</Attendee_Email>
|
||||
// <Attendee_Name>Wolfgang Fritz</Attendee_Name>
|
||||
// <Attendee_Status>5</Attendee_Status>
|
||||
// <Attendee_Type>1</Attendee_Type>
|
||||
// </Attendee>
|
||||
// </Attendees>
|
||||
//
|
||||
|
||||
- (NSDictionary *) applicationData
|
||||
{
|
||||
NSMutableDictionary *data;
|
||||
id <DOMNodeList> children;
|
||||
id <DOMElement> element;
|
||||
int i, count;
|
||||
|
||||
if (!asElementArray)
|
||||
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", nil];
|
||||
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
children = [self childNodes];
|
||||
|
||||
for (i = 0; i < [children length]; i++)
|
||||
{
|
||||
element = [children objectAtIndex: i];
|
||||
|
||||
if ([element nodeType] == DOM_ELEMENT_NODE)
|
||||
{
|
||||
NSString *tag;
|
||||
id value;
|
||||
|
||||
tag = [element tagName];
|
||||
count = [(NSArray *)[element childNodes] count];
|
||||
|
||||
// Handle inner data - see above for samples
|
||||
if (count > 2)
|
||||
{
|
||||
NSMutableArray *innerElements;
|
||||
id <DOMElement> innerElement;
|
||||
NSArray *childNodes;
|
||||
NSString *innerTag;
|
||||
BOOL same;
|
||||
int j;
|
||||
|
||||
childNodes = (NSArray *)[element childNodes];
|
||||
innerElements = [NSMutableArray array];
|
||||
innerTag = nil;
|
||||
same = YES;
|
||||
|
||||
for (j = 1; j < count; j++)
|
||||
{
|
||||
innerElement = [childNodes objectAtIndex: j];
|
||||
|
||||
if ([innerElement nodeType] == DOM_ELEMENT_NODE)
|
||||
{
|
||||
if (!innerTag)
|
||||
innerTag = [innerElement tagName];
|
||||
|
||||
if ([innerTag isEqualToString: [innerElement tagName]])
|
||||
{
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement applicationData]];
|
||||
}
|
||||
else
|
||||
{
|
||||
same = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (same && [asElementArray containsObject: innerTag])
|
||||
value = innerElements;
|
||||
else
|
||||
{
|
||||
value = [(NGDOMElement *)element applicationData];
|
||||
|
||||
// Don't set empty values like Foo = {}
|
||||
if (![value count])
|
||||
value = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
value = [[element firstChild] textValue];
|
||||
|
||||
if (value && tag)
|
||||
[data setObject: value forKey: tag];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NGMIMEMESSAGEACTIVESYNC_H__
|
||||
#define __NGMIMEMESSAGEACTIVESYNC_H__
|
||||
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
|
||||
@class NSArray;
|
||||
|
||||
@interface NGMimeMessage (ActiveSync)
|
||||
|
||||
- (NSArray *) allRecipients;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NGMimeMessage+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
@implementation NGMimeMessage (ActiveSync)
|
||||
|
||||
- (NSArray *) allRecipients
|
||||
{
|
||||
NSMutableArray *recipients;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *s;
|
||||
|
||||
recipients = [NSMutableArray array];
|
||||
|
||||
enumerator = [[self headersForKey: @"to"] objectEnumerator];
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
[recipients addObject: [s pureEMailAddress]];
|
||||
}
|
||||
|
||||
enumerator = [[self headersForKey: @"cc"] objectEnumerator];
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
[recipients addObject: [s pureEMailAddress]];
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NGVCARDACTIVESYNC_H__
|
||||
#define __NGVCARDACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
@class NSDictionary;
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface NGVCard (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NGVCard+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <NGCards/CardElement.h>
|
||||
|
||||
#import <Contacts/NGVCard+SOGo.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
@implementation NGVCard (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
CardElement *n, *homeAdr, *workAdr;
|
||||
NSArray *emails, *addresses;
|
||||
NSMutableString *s;
|
||||
id o;
|
||||
|
||||
int i;
|
||||
|
||||
s = [NSMutableString string];
|
||||
n = [self n];
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 0 forKey: @""]))
|
||||
[s appendFormat: @"<LastName xmlns=\"Contacts:\">%@</LastName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
|
||||
[s appendFormat: @"<FirstName xmlns=\"Contacts:\">%@</FirstName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self workCompany]))
|
||||
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self title]))
|
||||
[s appendFormat: @"<JobTitle xmlns=\"Contacts:\">%@</JobTitle>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self preferredEMail]))
|
||||
[s appendFormat: @"<Email1Address xmlns=\"Contacts:\">%@</Email1Address>", o];
|
||||
|
||||
|
||||
// Secondary email addresses (2 and 3)
|
||||
emails = [self secondaryEmails];
|
||||
|
||||
for (i = 0; i < [emails count]; i++)
|
||||
{
|
||||
o = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
|
||||
|
||||
[s appendFormat: @"<Email%dAddress xmlns=\"Contacts:\">%@</Email%dAddress>", i+2, o, i+2];
|
||||
|
||||
if (i == 1)
|
||||
break;
|
||||
}
|
||||
|
||||
// Telephone numbers
|
||||
if ((o = [self workPhone]) && [o length])
|
||||
[s appendFormat: @"<BusinessPhoneNumber xmlns=\"Contacts:\">%@</BusinessPhoneNumber>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self homePhone]) && [o length])
|
||||
[s appendFormat: @"<HomePhoneNumber xmlns=\"Contacts:\">%@</HomePhoneNumber>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self fax]) && [o length])
|
||||
[s appendFormat: @"<BusinessFaxNumber xmlns=\"Contacts:\">%@</BusinessFaxNumber>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self mobile]) && [o length])
|
||||
[s appendFormat: @"<MobilePhoneNumber xmlns=\"Contacts:\">%@</MobilePhoneNumber>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self pager]) && [o length])
|
||||
[s appendFormat: @"<PagerNumber xmlns=\"Contacts:\">%@</PagerNumber>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Home Address
|
||||
addresses = [self childrenWithTag: @"adr"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"home"];
|
||||
|
||||
if ([addresses count])
|
||||
{
|
||||
homeAdr = [addresses objectAtIndex: 0];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<HomeCity xmlns=\"Contacts:\">%@</HomeCity>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""]))
|
||||
[s appendFormat: @"<HomeState xmlns=\"Contacts:\">%@</HomeState>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""]))
|
||||
[s appendFormat: @"<HomePostalCode xmlns=\"Contacts:\">%@</HomePostalCode>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 6 forKey: @""]))
|
||||
[s appendFormat: @"<HomeCountry xmlns=\"Contacts:\">%@</HomeCountry>", [o activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Work Address
|
||||
addresses = [self childrenWithTag: @"adr"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"work"];
|
||||
|
||||
if ([addresses count])
|
||||
{
|
||||
workAdr = [addresses objectAtIndex: 0];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessCity xmlns=\"Contacts:\">%@</BusinessCity>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 4 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessState xmlns=\"Contacts:\">%@</BusinessState>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 5 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessPostalCode xmlns=\"Contacts:\">%@</BusinessPostalCode>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 6 forKey: @""]))
|
||||
[s appendFormat: @"<BusinessCountry xmlns=\"Contacts:\">%@</BusinessCountry>", [o activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Other, less important fields
|
||||
if ((o = [self birthday]))
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>", [o activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
CardElement *element;
|
||||
id o;
|
||||
|
||||
// Contact's note
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setNote: o];
|
||||
|
||||
// Birthday
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]];
|
||||
}
|
||||
|
||||
//
|
||||
// Business address information
|
||||
//
|
||||
// BusinessStreet
|
||||
// BusinessCity
|
||||
// BusinessPostalCode
|
||||
// BusinessState
|
||||
// BusinessCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"work"];
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessStreet"]
|
||||
atIndex: 2 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
|
||||
//
|
||||
// Home address information
|
||||
//
|
||||
// HomeStreet
|
||||
// HomeCity
|
||||
// HomePostalCode
|
||||
// HomeState
|
||||
// HomeCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"home"];
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeStreet"]
|
||||
atIndex: 2 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
|
||||
// Company's name
|
||||
if ((o = [theValues objectForKey: @"CompanyName"]))
|
||||
{
|
||||
[self setOrg: o units: nil];
|
||||
}
|
||||
|
||||
// Email addresses
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]))
|
||||
{
|
||||
[self addEmail: o types: [NSArray arrayWithObject: @"pref"]];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]))
|
||||
{
|
||||
[self addEmail: o types: nil];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]))
|
||||
{
|
||||
[self addEmail: o types: nil];
|
||||
}
|
||||
|
||||
// Formatted name
|
||||
// MiddleName
|
||||
// Suffix (II)
|
||||
// Title (Mr.)
|
||||
[self setFn: [theValues objectForKey: @"FileAs"]];
|
||||
|
||||
[self setNWithFamily: [theValues objectForKey: @"LastName"]
|
||||
given: [theValues objectForKey: @"FirstName"]
|
||||
additional: nil prefixes: nil suffixes: nil];
|
||||
|
||||
// IM information
|
||||
[[self uniqueChildWithTag: @"x-aim"]
|
||||
setSingleValue: [theValues objectForKey: @"IMAddress"]
|
||||
forKey: @""];
|
||||
|
||||
//
|
||||
// Phone numbrrs
|
||||
//
|
||||
element = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
|
||||
|
||||
element = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
|
||||
|
||||
// Job's title
|
||||
if ((o = [theValues objectForKey: @"JobTitle"]))
|
||||
{
|
||||
[self setTitle: o];
|
||||
}
|
||||
|
||||
// WebPage (work)
|
||||
if ((o = [theValues objectForKey: @"WebPage"]))
|
||||
{
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
setSingleValue: o forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NSCALENDARDATEACTIVESYNC_H__
|
||||
#define __NSCALENDARDATEACTIVESYNC_H__
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
|
||||
@class NSString;
|
||||
|
||||
@interface NSCalendarDate (ActiveSync)
|
||||
|
||||
+ (NSCalendarDate *) dateFromFilterType: (NSString *) theFilterType;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NSCalendarDate+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#define ONE_DAY 86400
|
||||
|
||||
@implementation NSCalendarDate (ActiveSync)
|
||||
|
||||
//
|
||||
// See http://msdn.microsoft.com/en-us/library/gg709713(v=exchg.80).aspx for available types
|
||||
//
|
||||
+ (NSCalendarDate *) dateFromFilterType: (NSString *) theFilterType
|
||||
{
|
||||
NSCalendarDate *d;
|
||||
|
||||
d = [NSCalendarDate calendarDate];
|
||||
|
||||
if (d)
|
||||
{
|
||||
int value;
|
||||
|
||||
switch ([theFilterType intValue])
|
||||
{
|
||||
case 1:
|
||||
value = ONE_DAY;
|
||||
break;
|
||||
case 2:
|
||||
value = 3 * ONE_DAY;
|
||||
break;
|
||||
case 3:
|
||||
value = 7 * ONE_DAY;
|
||||
break;
|
||||
case 4:
|
||||
value = 14 * ONE_DAY;
|
||||
break;
|
||||
case 5:
|
||||
value = 30 * ONE_DAY;
|
||||
break;
|
||||
case 6:
|
||||
value = 90 * ONE_DAY;
|
||||
break;
|
||||
case 7:
|
||||
value = 180 * ONE_DAY;
|
||||
break;
|
||||
case 0:
|
||||
case 8:
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [d initWithTimeIntervalSinceNow: -value];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NSDATAACTIVESYNC_H__
|
||||
#define __NSDATAACTIVESYNC_H__
|
||||
|
||||
#import <Foundation/NSData.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface NSData (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (NSData *) wbxml2xml;
|
||||
- (NSData *) xml2wbxml;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NSData+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
|
||||
#include <wbxml/wbxml.h>
|
||||
#include <wbxml/wbxml_conv.h>
|
||||
#include <wbxml/wbxml_errors.h>
|
||||
|
||||
#define WBXMLDEBUG 0
|
||||
|
||||
@implementation NSData (ActiveSync)
|
||||
|
||||
- (void) _dumpToFile
|
||||
{
|
||||
NSString *path;
|
||||
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
NSLog(@"Original data written to: %@", path);
|
||||
}
|
||||
|
||||
//
|
||||
// Encodes the data in base64 and strip newline characters
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""];
|
||||
}
|
||||
|
||||
- (NSData *) wbxml2xml
|
||||
{
|
||||
WBXMLGenXMLParams params;
|
||||
NSData *data;
|
||||
|
||||
unsigned int wbxml_len, xml_len, ret;
|
||||
unsigned char *wbxml, *xml;
|
||||
|
||||
wbxml = (unsigned char*)[self bytes];
|
||||
wbxml_len = [self length];
|
||||
xml = NULL;
|
||||
xml_len = 0;
|
||||
|
||||
params.lang = WBXML_LANG_ACTIVESYNC;
|
||||
params.gen_type = WBXML_GEN_XML_INDENT;
|
||||
params.indent = 1;
|
||||
params.keep_ignorable_ws = FALSE;
|
||||
|
||||
ret = wbxml_conv_wbxml2xml_withlen(wbxml, wbxml_len, &xml, &xml_len, ¶ms);
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [[NSData alloc] initWithBytes: xml length: xml_len];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.decoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
free(xml);
|
||||
|
||||
return AUTORELEASE(data);
|
||||
}
|
||||
|
||||
|
||||
- (NSData *) xml2wbxml
|
||||
{
|
||||
WBXMLConvXML2WBXML *conv;
|
||||
NSData *data;
|
||||
|
||||
unsigned int wbxml_len, xml_len, ret;
|
||||
unsigned char *wbxml, *xml;
|
||||
|
||||
xml = (unsigned char*)[self bytes];
|
||||
xml_len = [self length];
|
||||
wbxml = NULL;
|
||||
wbxml_len = 0;
|
||||
conv = NULL;
|
||||
|
||||
ret = wbxml_conv_xml2wbxml_create(&conv);
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
|
||||
wbxml_conv_xml2wbxml_enable_preserve_whitespaces(conv);
|
||||
|
||||
// From libwbxml's changelog in v0.11.0: "The public ID is set to unknown and the DTD is not included. This is required for Microsoft ActiveSync."
|
||||
wbxml_conv_xml2wbxml_disable_public_id(conv);
|
||||
wbxml_conv_xml2wbxml_disable_string_table(conv);
|
||||
|
||||
ret = wbxml_conv_xml2wbxml_run(conv, xml, xml_len, &wbxml, &wbxml_len);
|
||||
|
||||
if (ret != WBXML_OK)
|
||||
{
|
||||
NSLog(@"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret));
|
||||
[self _dumpToFile];
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.encoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
|
||||
return AUTORELEASE(data);
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NSDATEACTIVESYNC_H__
|
||||
#define __NSDATEACTIVESYNC_H__
|
||||
|
||||
#import <Foundation/NSDate.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface NSDate (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (NSString *) activeSyncRepresentationWithoutSeparatorsInContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "NSDate+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
@implementation NSDate (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
return [self descriptionWithCalendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// From [MS-ASDTYPE].pdf - section 2.3 "Dates and times in calendar items MUST NOT include punctuation separators."
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationWithoutSeparatorsInContext: (WOContext *) context
|
||||
{
|
||||
return [self descriptionWithCalendarFormat: @"%Y%m%dT%H%M%SZ" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __NSSTRINGACTIVESYNC_H__
|
||||
#define __NSSTRINGACTIVESYNC_H__
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
|
||||
@class NSCalendarDate;
|
||||
@class NSData;
|
||||
@class WOContext;
|
||||
|
||||
@interface NSString (ActiveSync)
|
||||
|
||||
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType;
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (int) activeSyncFolderType;
|
||||
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
|
||||
- (NSCalendarDate *) calendarDate;
|
||||
- (NSString *) deviceId;
|
||||
- (NSString *) deviceType;
|
||||
- (NSString *) command;
|
||||
- (NSData *) convertHexStringToBytes;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSCalendarDate.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSDate.h>
|
||||
|
||||
#include <SOGo/NSString+Utilities.h>
|
||||
|
||||
#include <NGExtensions/NSString+misc.h>
|
||||
|
||||
@implementation NSString (ActiveSync)
|
||||
|
||||
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
|
||||
{
|
||||
if (folderType == ActiveSyncEventFolder)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = [self length];
|
||||
|
||||
if (len > 4 && [self hasSuffix: @".ics"])
|
||||
return [self substringToIndex: len-4];
|
||||
else
|
||||
return [NSString stringWithFormat: @"%@.ics", self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self stringByEscapingHTMLString];
|
||||
|
||||
return [[s componentsSeparatedByCharactersInSet: [self safeCharacterSet]]
|
||||
componentsJoinedByString: @""];
|
||||
}
|
||||
|
||||
- (int) activeSyncFolderType
|
||||
{
|
||||
if ([self isEqualToString: @"inbox"])
|
||||
return 2;
|
||||
else if ([self isEqualToString: @"draft"])
|
||||
return 3;
|
||||
else if ([self isEqualToString: @"sent"])
|
||||
return 5;
|
||||
else if ([self isEqualToString: @"trash"])
|
||||
return 4;
|
||||
|
||||
return 12;
|
||||
}
|
||||
|
||||
- (NSString *) realCollectionIdWithFolderType: (SOGoMicrosoftActiveSyncFolderType *) folderType;
|
||||
{
|
||||
NSString *realCollectionId, *v;
|
||||
|
||||
*folderType = ActiveSyncGenericFolder;
|
||||
v = [self stringByUnescapingURL];
|
||||
|
||||
if ([v hasPrefix: @"vevent/"])
|
||||
{
|
||||
realCollectionId = [v substringFromIndex: 7];
|
||||
*folderType = ActiveSyncEventFolder;
|
||||
}
|
||||
else if ([v hasPrefix: @"vtodo/"])
|
||||
{
|
||||
realCollectionId = [v substringFromIndex: 6];
|
||||
*folderType = ActiveSyncTaskFolder;
|
||||
}
|
||||
else if ([v hasPrefix: @"vcard/"])
|
||||
{
|
||||
realCollectionId = [v substringFromIndex: 6];
|
||||
*folderType = ActiveSyncContactFolder;
|
||||
}
|
||||
else if ([v hasPrefix: @"mail/"])
|
||||
{
|
||||
realCollectionId = [[v stringByUnescapingURL] substringFromIndex: 5];
|
||||
*folderType = ActiveSyncMailFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
realCollectionId = nil;
|
||||
}
|
||||
|
||||
return realCollectionId;
|
||||
}
|
||||
|
||||
//
|
||||
// 2014-01-16T05:00:00.000Z
|
||||
//
|
||||
// See http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSCalendarDate.html#method$NSCalendarDate-initWithString$calendarFormat$ for the format details.
|
||||
//
|
||||
- (NSCalendarDate *) calendarDate
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y%m%dT%H%M%SZ"];
|
||||
|
||||
if (!o)
|
||||
o = [NSCalendarDate dateWithString: self calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ"];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
- (NSString *) _valueForParameter: (NSString *) theParameter
|
||||
{
|
||||
NSArray *components;
|
||||
NSString *s;
|
||||
int i;
|
||||
|
||||
components = [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"];
|
||||
|
||||
for (i = 0; i < [components count]; i++)
|
||||
{
|
||||
s = [components objectAtIndex: i];
|
||||
|
||||
if ([[s uppercaseString] hasPrefix: theParameter])
|
||||
return [s substringFromIndex: [theParameter length]];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// This method extracts the "DeviceId" from a URI:
|
||||
//
|
||||
// /SOGo/Microsoft-Server-ActiveSync?Cmd=FolderSync&User=sogo10&DeviceId=SEC17CD1A3E9E3F2&DeviceType=SAMSUNGSGHI317M
|
||||
//
|
||||
- (NSString *) deviceId
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"DEVICEID="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// This method extracts the "DeviceType" from a URI:
|
||||
//
|
||||
// /SOGo/Microsoft-Server-ActiveSync?Cmd=FolderSync&User=sogo10&DeviceId=SEC17CD1A3E9E3F2&DeviceType=SAMSUNGSGHI317M
|
||||
//
|
||||
- (NSString *) deviceType
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"DEVICETYPE="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) command
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"CMD="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
- (char) _decodeHexByte: (char) byteChar
|
||||
{
|
||||
char newByte;
|
||||
|
||||
if (byteChar >= 48 && byteChar <= 57)
|
||||
newByte = (uint8_t) byteChar - 48;
|
||||
else if (byteChar >= 65 && byteChar <= 70)
|
||||
newByte = (uint8_t) byteChar - 55;
|
||||
else if (byteChar >= 97 && byteChar <= 102)
|
||||
newByte = (uint8_t) byteChar - 87;
|
||||
else
|
||||
newByte = -1;
|
||||
|
||||
return newByte;
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
- (BOOL) _decodeHexByte: (uint8_t *) byte
|
||||
atPos: (NSUInteger) pos
|
||||
{
|
||||
BOOL error = NO;
|
||||
char newByte;
|
||||
unichar byteChar;
|
||||
|
||||
byteChar = [self characterAtIndex: pos];
|
||||
if (byteChar < 256)
|
||||
{
|
||||
newByte = [self _decodeHexByte: (char) byteChar];
|
||||
if (newByte == -1)
|
||||
error = YES;
|
||||
else
|
||||
*byte = newByte;
|
||||
}
|
||||
else
|
||||
error = YES;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
- (BOOL) _decodeHexPair: (uint8_t *) byte
|
||||
atPos: (NSUInteger) pos
|
||||
{
|
||||
BOOL error;
|
||||
uint8_t lowValue, highValue;
|
||||
|
||||
error = [self _decodeHexByte: &highValue atPos: pos];
|
||||
if (!error)
|
||||
{
|
||||
error = [self _decodeHexByte: &lowValue atPos: pos + 1];
|
||||
if (!error)
|
||||
*byte = highValue << 4 | lowValue;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
- (NSData *) convertHexStringToBytes
|
||||
{
|
||||
NSUInteger count, strLen, bytesLen;
|
||||
uint8_t *bytes, *currentByte;
|
||||
NSData *decoded = nil;
|
||||
BOOL error = NO;
|
||||
|
||||
strLen = [self length];
|
||||
if ((strLen % 2) == 0)
|
||||
{
|
||||
bytesLen = strLen / 2;
|
||||
bytes = NSZoneCalloc (NULL, bytesLen, sizeof (uint8_t));
|
||||
currentByte = bytes;
|
||||
for (count = 0; !error && count < strLen; count += 2)
|
||||
{
|
||||
error = [self _decodeHexPair: currentByte atPos: count];
|
||||
currentByte++;
|
||||
}
|
||||
if (error)
|
||||
NSZoneFree (NULL, bytes);
|
||||
else
|
||||
decoded = [NSData dataWithBytesNoCopy: bytes
|
||||
length: bytesLen
|
||||
freeWhenDone: YES];
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
In order to use this software in production environments, you need to
|
||||
get a proper usage license from Microsoft. Please contact them directly
|
||||
to negotiate the fees associated to your user base.
|
||||
|
||||
To contact Microsoft, please visit:
|
||||
|
||||
http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx
|
||||
|
||||
and send an email to iplicreq@microsoft.com
|
||||
|
||||
Inverse inc. provides this software for free, but is not responsible
|
||||
for anything related to its usage.
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __SOGOACTIVESYNCCONSTANTS_H__
|
||||
#define __SOGOACTIVESYNCCONSTANTS_H__
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ActiveSyncGenericFolder = 0,
|
||||
ActiveSyncMailFolder = 1,
|
||||
ActiveSyncContactFolder = 2,
|
||||
ActiveSyncEventFolder = 3,
|
||||
ActiveSyncTaskFolder = 4,
|
||||
} SOGoMicrosoftActiveSyncFolderType;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __SOGOACTIVESYNCDISPATCHERSYNC_H__
|
||||
#define __SOGOACTIVESYNCDISPATCHERSYNC_H__
|
||||
|
||||
#import "SOGoActiveSyncDispatcher.h"
|
||||
|
||||
#import <DOM/DOMElement.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
|
||||
@class WOResponse;
|
||||
|
||||
@interface SOGoActiveSyncDispatcher (Sync)
|
||||
|
||||
- (void) processSync: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse;
|
||||
|
||||
@end
|
||||
|
||||
#endif // SOGOACTIVESYNCDISPATCHERSYNC
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#include "SOGoActiveSyncConstants.h"
|
||||
|
||||
@class NSException;
|
||||
|
||||
@interface SOGoActiveSyncDispatcher : NSObject
|
||||
{
|
||||
id context;
|
||||
}
|
||||
|
||||
- (id) collectionFromId: (NSString *) theCollectionId
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
- (NSException *) dispatchRequest: (id) theRequest
|
||||
inResponse: (id) theResponse
|
||||
context: (id) theContext;
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __SOGOMAILOBJECTACTIVESYNC_H__
|
||||
#define __SOGOMAILOBJECTACTIVESYNC_H__
|
||||
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
|
||||
@class iCalCalendar;
|
||||
@class NSDictionary;
|
||||
@class WOContext;
|
||||
|
||||
@interface SOGoMailObject (ActiveSync)
|
||||
|
||||
- (iCalCalendar *) calendarFromIMIPMessage;
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#include "SOGoMailObject+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
#import <NGExtensions/NGQuotedPrintableCoding.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSString+Encoding.h>
|
||||
#import <NGImap4/NGImap4Envelope.h>
|
||||
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeFileData.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
#import <NGMime/NGMimeType.h>
|
||||
#import <NGMail/NGMimeMessageParser.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
|
||||
#include "iCalTimeZone+ActiveSync.h"
|
||||
#include "NSData+ActiveSync.h"
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
#include <Appointments/iCalEntityObject+SOGo.h>
|
||||
#include <Appointments/iCalPerson+SOGo.h>
|
||||
#include <Mailer/NSString+Mail.h>
|
||||
#include <Mailer/SOGoMailBodyPart.h>
|
||||
|
||||
#include <SOGo/SOGoUser.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwLowDateTime;
|
||||
uint32_t dwHighDateTime;
|
||||
} FILETIME;
|
||||
|
||||
struct GlobalObjectId {
|
||||
uint8_t ByteArrayID[16];
|
||||
uint8_t YH;
|
||||
uint8_t YL;
|
||||
uint8_t Month;
|
||||
uint8_t D;
|
||||
FILETIME CreationTime;
|
||||
uint8_t X[8];
|
||||
uint32_t Size;
|
||||
uint8_t* Data;
|
||||
};
|
||||
|
||||
@implementation SOGoMailObject (ActiveSync)
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _setInstanceDate: (struct GlobalObjectId *) newGlobalId
|
||||
fromDate: (NSCalendarDate *) instanceDate
|
||||
{
|
||||
uint16_t year;
|
||||
|
||||
if (instanceDate)
|
||||
{
|
||||
//[instanceDate setTimeZone: timeZone];
|
||||
year = [instanceDate yearOfCommonEra];
|
||||
newGlobalId->YH = year >> 8;
|
||||
newGlobalId->YL = year & 0xff;
|
||||
newGlobalId->Month = [instanceDate monthOfYear];
|
||||
newGlobalId->D = [instanceDate dayOfMonth];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx
|
||||
//
|
||||
- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event
|
||||
{
|
||||
NSData *binPrefix, *globalObjectId, *uidAsASCII;
|
||||
NSString *prefix, *uid;
|
||||
|
||||
struct GlobalObjectId newGlobalId;
|
||||
const char *bytes;
|
||||
|
||||
prefix = @"040000008200e00074c5b7101a82e008";
|
||||
|
||||
// dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00"
|
||||
uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 };
|
||||
uid = [event uid];
|
||||
|
||||
binPrefix = [prefix convertHexStringToBytes];
|
||||
[binPrefix getBytes: &newGlobalId.ByteArrayID];
|
||||
[self _setInstanceDate: &newGlobalId
|
||||
fromDate: [event recurrenceId]];
|
||||
uidAsASCII = [uid dataUsingEncoding: NSASCIIStringEncoding];
|
||||
bytes = [uidAsASCII bytes];
|
||||
|
||||
// 0x0c is the size of our dataPrefix
|
||||
newGlobalId.Size = 0x0c + [uidAsASCII length];
|
||||
newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t));
|
||||
memcpy(newGlobalId.Data, dataPrefix, 0x0c);
|
||||
memcpy(newGlobalId.Data + 0x0c, bytes, newGlobalId.Size - 0x0c);
|
||||
|
||||
globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)];
|
||||
free(newGlobalId.Data);
|
||||
|
||||
return [globalObjectId autorelease];
|
||||
}
|
||||
|
||||
//
|
||||
// For debugging purposes...
|
||||
//
|
||||
- (NSString *) _uidFromGlobalObjectId: (NSData *) objectId
|
||||
{
|
||||
NSString *uid;
|
||||
|
||||
struct GlobalObjectId *newGlobalId;
|
||||
NSUInteger length;
|
||||
uint8_t *bytes;
|
||||
|
||||
length = [objectId length];
|
||||
uid = nil;
|
||||
|
||||
bytes = malloc(length*sizeof(uint8_t));
|
||||
[objectId getBytes: bytes length: length];
|
||||
|
||||
newGlobalId = (struct GlobalObjectId *)bytes;
|
||||
|
||||
// We must take the offset (dataPrefix) into account
|
||||
uid = [[NSString alloc] initWithBytes: newGlobalId->Data+12 length: newGlobalId->Size-12 encoding: NSASCIIStringEncoding];
|
||||
free(bytes);
|
||||
|
||||
return AUTORELEASE(uid);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) _emailAddressesFrom: (NSArray *) enveloppeAddresses
|
||||
{
|
||||
NGImap4EnvelopeAddress *address;
|
||||
NSString *email, *rc, *name;
|
||||
NSMutableArray *addresses;
|
||||
int i, max;
|
||||
|
||||
rc = nil;
|
||||
max = [enveloppeAddresses count];
|
||||
|
||||
if (max > 0)
|
||||
{
|
||||
addresses = [NSMutableArray array];
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
address = [enveloppeAddresses objectAtIndex: i];
|
||||
name = [address personalName];
|
||||
email = [NSString stringWithFormat: @"\"%@\" <%@>", (name ? name : [address baseEMail]), [address baseEMail]];
|
||||
|
||||
if (email)
|
||||
[addresses addObject: email];
|
||||
}
|
||||
rc = [addresses componentsJoinedByString: @", "];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSData *) _preferredBodyDataInMultipartUsingType: (int) theType
|
||||
{
|
||||
NSString *encoding, *key, *plainKey, *htmlKey, *type, *subtype;
|
||||
NSDictionary *textParts, *part;
|
||||
NSEnumerator *e;
|
||||
NSData *d;
|
||||
|
||||
textParts = [self fetchPlainTextParts];
|
||||
e = [textParts keyEnumerator];
|
||||
plainKey = nil;
|
||||
htmlKey = nil;
|
||||
d = nil;
|
||||
|
||||
while ((key = [e nextObject]))
|
||||
{
|
||||
part = [self lookupInfoForBodyPart: key];
|
||||
type = [part valueForKey: @"type"];
|
||||
subtype = [part valueForKey: @"subtype"];
|
||||
|
||||
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
|
||||
htmlKey = key;
|
||||
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
|
||||
plainKey = key;
|
||||
}
|
||||
|
||||
key = nil;
|
||||
|
||||
if (theType == 2)
|
||||
key = htmlKey;
|
||||
else if (theType == 1)
|
||||
key = plainKey;
|
||||
|
||||
if (key)
|
||||
{
|
||||
d = [[self fetchPlainTextParts] objectForKey: key];
|
||||
|
||||
encoding = [[self lookupInfoForBodyPart: key] objectForKey: @"encoding"];
|
||||
|
||||
if ([encoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
|
||||
d = [d dataByDecodingBase64];
|
||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _sanitizedMIMEPart: (id) thePart
|
||||
performed: (BOOL *) b
|
||||
{
|
||||
if ([thePart isKindOfClass: [NGMimeMultipartBody class]])
|
||||
{
|
||||
NGMimeBodyPart *part;
|
||||
NSArray *parts;
|
||||
int i;
|
||||
|
||||
parts = [thePart parts];
|
||||
|
||||
for (i = 0; i < [parts count]; i++)
|
||||
{
|
||||
part = [parts objectAtIndex: i];
|
||||
|
||||
[self _sanitizedMIMEPart: part
|
||||
performed: b];
|
||||
}
|
||||
}
|
||||
else if ([thePart isKindOfClass: [NGMimeBodyPart class]])
|
||||
{
|
||||
NGMimeFileData *fdata;
|
||||
id body;
|
||||
|
||||
body = [thePart body];
|
||||
|
||||
if ([body isKindOfClass: [NGMimeMultipartBody class]])
|
||||
{
|
||||
[self _sanitizedMIMEPart: body
|
||||
performed: b];
|
||||
}
|
||||
else if ([body isKindOfClass: [NSData class]] &&
|
||||
[[[thePart contentType] type] isEqualToString: @"text"] &&
|
||||
([[[thePart contentType] subType] isEqualToString: @"plain"] || [[[thePart contentType] subType] isEqualToString: @"html"]))
|
||||
{
|
||||
// We make sure everything is encoded in UTF-8
|
||||
NSString *charset, *s;
|
||||
NGMimeType *mimeType;
|
||||
int encoding;
|
||||
|
||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||
encoding = [NGMimeType stringEncodingForCharset: charset];
|
||||
|
||||
s = [[NSString alloc] initWithData: body encoding: encoding];
|
||||
AUTORELEASE(s);
|
||||
|
||||
if (s)
|
||||
{
|
||||
body = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
mimeType = [NGMimeType mimeType: [[thePart contentType] type]
|
||||
subType: [[thePart contentType] subType]
|
||||
parameters: [NSDictionary dictionaryWithObject: @"utf-8" forKey: @"charset"]];
|
||||
[thePart setHeader: mimeType forKey: @"content-type"];
|
||||
|
||||
fdata = [[NGMimeFileData alloc] initWithBytes: [body bytes]
|
||||
length: [body length]];
|
||||
|
||||
[thePart setBody: fdata];
|
||||
RELEASE(fdata);
|
||||
*b = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSData *) _sanitizedMIMEMessage
|
||||
{
|
||||
NGMimeMessageParser *parser;
|
||||
NGMimeMessage *message;
|
||||
NSData *d;
|
||||
|
||||
BOOL b;
|
||||
|
||||
d = [self content];
|
||||
|
||||
parser = [[NGMimeMessageParser alloc] init];
|
||||
AUTORELEASE(parser);
|
||||
|
||||
message = [parser parsePartFromData: d];
|
||||
b = NO;
|
||||
|
||||
if (message)
|
||||
{
|
||||
[self _sanitizedMIMEPart: [message body]
|
||||
performed: &b];
|
||||
|
||||
if (b)
|
||||
{
|
||||
NGMimeMessageGenerator *generator;
|
||||
|
||||
generator = [[NGMimeMessageGenerator alloc] init];
|
||||
AUTORELEASE(generator);
|
||||
|
||||
d = [generator generateMimeFromPart: message];
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSData *) _preferredBodyDataUsingType: (int) theType
|
||||
nativeType: (int *) theNativeType
|
||||
{
|
||||
NSString *type, *subtype, *encoding;
|
||||
NSData *d;
|
||||
|
||||
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
|
||||
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
|
||||
|
||||
d = nil;
|
||||
|
||||
// We determine the native type
|
||||
if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"plain"])
|
||||
*theNativeType = 1;
|
||||
else if ([type isEqualToString: @"text"] && [subtype isEqualToString: @"html"])
|
||||
*theNativeType = 2;
|
||||
else if ([type isEqualToString: @"multipart"])
|
||||
*theNativeType = 4;
|
||||
|
||||
// We get the right part based on the preference
|
||||
if (theType == 1 || theType == 2)
|
||||
{
|
||||
if ([type isEqualToString: @"text"])
|
||||
{
|
||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||
|
||||
// We check if we have base64 encoded parts. If so, we just
|
||||
// un-encode them before using them
|
||||
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
|
||||
|
||||
if ([encoding caseInsensitiveCompare: @"base64"] == NSOrderedSame)
|
||||
d = [d dataByDecodingBase64];
|
||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||
|
||||
// Check if we must convert html->plain
|
||||
if (theType == 1 && [subtype isEqualToString: @"html"])
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||
AUTORELEASE(s);
|
||||
|
||||
s = [s htmlToText];
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
else if ([type isEqualToString: @"multipart"])
|
||||
{
|
||||
d = [self _preferredBodyDataInMultipartUsingType: theType];
|
||||
}
|
||||
}
|
||||
else if (theType == 4)
|
||||
{
|
||||
// We sanitize the content *ONLY* for Outlook clients. Outlook has strange issues
|
||||
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
|
||||
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
|
||||
d = [self _sanitizedMIMEMessage];
|
||||
else
|
||||
d = [self content];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (iCalCalendar *) calendarFromIMIPMessage
|
||||
{
|
||||
NSDictionary *part;
|
||||
NSArray *parts;
|
||||
int i;
|
||||
|
||||
// We check if we have at least 2 parts and if one of them is a text/calendar
|
||||
parts = [[self bodyStructure] objectForKey: @"parts"];
|
||||
|
||||
if ([parts count] > 1)
|
||||
{
|
||||
for (i = 0; i < [parts count]; i++)
|
||||
{
|
||||
part = [parts objectAtIndex: i];
|
||||
|
||||
if ([[part objectForKey: @"type"] isEqualToString: @"text"] &&
|
||||
[[part objectForKey: @"subtype"] isEqualToString: @"calendar"])
|
||||
{
|
||||
id bodyPart;
|
||||
|
||||
bodyPart = [self lookupImap4BodyPartKey: [NSString stringWithFormat: @"%d", i+1]
|
||||
inContext: self->context];
|
||||
|
||||
if (bodyPart)
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSData *calendarData;
|
||||
|
||||
calendarData = [bodyPart fetchBLOB];
|
||||
calendar = nil;
|
||||
|
||||
NS_DURING
|
||||
calendar = [iCalCalendar parseSingleFromSource: calendarData];
|
||||
NS_HANDLER
|
||||
calendar = nil;
|
||||
NS_ENDHANDLER
|
||||
|
||||
return calendar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) _context
|
||||
{
|
||||
NSData *d, *globalObjId;
|
||||
NSMutableString *s;
|
||||
id value;
|
||||
|
||||
iCalCalendar *calendar;
|
||||
|
||||
int preferredBodyType, nativeBodyType;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
// To - "The value of this element contains one or more e-mail addresses.
|
||||
// If there are multiple e-mail addresses, they are separated by commas."
|
||||
value = [self _emailAddressesFrom: [[self envelope] to]];
|
||||
if (value)
|
||||
[s appendFormat: @"<To xmlns=\"Email:\">%@</To>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// From
|
||||
value = [self _emailAddressesFrom: [[self envelope] from]];
|
||||
if (value)
|
||||
[s appendFormat: @"<From xmlns=\"Email:\">%@</From>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Subject
|
||||
value = [self decodedSubject];
|
||||
if (value)
|
||||
{
|
||||
[s appendFormat: @"<Subject xmlns=\"Email:\">%@</Subject>", [value activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [value activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// DateReceived
|
||||
value = [self date];
|
||||
if (value)
|
||||
[s appendFormat: @"<DateReceived xmlns=\"Email:\">%@</DateReceived>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// DisplayTo
|
||||
[s appendFormat: @"<DisplayTo xmlns=\"Email:\">%@</DisplayTo>", [[context activeUser] login]];
|
||||
|
||||
// Cc - same syntax as the To field
|
||||
value = [self _emailAddressesFrom: [[self envelope] cc]];
|
||||
if (value)
|
||||
[s appendFormat: @"<Cc xmlns=\"Email:\">%@</Cc>", [value activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Importance - FIXME
|
||||
[s appendFormat: @"<Importance xmlns=\"Email:\">%@</Importance>", @"1"];
|
||||
|
||||
// Read
|
||||
[s appendFormat: @"<Read xmlns=\"Email:\">%d</Read>", ([self read] ? 1 : 0)];
|
||||
|
||||
// We handle MeetingRequest
|
||||
calendar = [self calendarFromIMIPMessage];
|
||||
|
||||
if (calendar)
|
||||
{
|
||||
NSString *method, *className;
|
||||
iCalPerson *attendee;
|
||||
iCalTimeZone *tz;
|
||||
iCalEvent *event;
|
||||
|
||||
iCalPersonPartStat partstat;
|
||||
int v;
|
||||
|
||||
event = [[calendar events] lastObject];
|
||||
method = [[event parent] method];
|
||||
|
||||
// If we are the organizer, let's pick the attendee based on the From address
|
||||
if ([event userIsOrganizer: [context activeUser]])
|
||||
attendee = [event findAttendeeWithEmail: [[[[self envelope] from] lastObject] baseEMail]];
|
||||
else
|
||||
attendee = [event findAttendeeWithEmail: [[[context activeUser] allEmails] objectAtIndex: 0]];
|
||||
|
||||
partstat = [attendee participationStatus];
|
||||
|
||||
// We generate the correct MessageClass
|
||||
if ([method isEqualToString: @"REQUEST"])
|
||||
className = @"IPM.Schedule.Meeting.Request";
|
||||
else if ([method isEqualToString: @"REPLY"])
|
||||
{
|
||||
switch (partstat)
|
||||
{
|
||||
case iCalPersonPartStatAccepted:
|
||||
className = @"IPM.Schedule.Meeting.Resp.Pos";
|
||||
break;
|
||||
case iCalPersonPartStatDeclined:
|
||||
className = @"IPM.Schedule.Meeting.Resp.Neg";
|
||||
break;
|
||||
case iCalPersonPartStatTentative:
|
||||
case iCalPersonPartStatNeedsAction:
|
||||
className = @"IPM.Schedule.Meeting.Resp.Tent";
|
||||
break;
|
||||
default:
|
||||
className = @"IPM.Appointment";
|
||||
NSLog(@"unhandled part stat");
|
||||
}
|
||||
}
|
||||
else if ([method isEqualToString: @"COUNTER"])
|
||||
className = @"IPM.Schedule.Meeting.Resp.Tent";
|
||||
else if ([method isEqualToString: @"CANCEL"])
|
||||
className = @"IPM.Schedule.Meeting.Cancelled";
|
||||
else
|
||||
className = @"IPM.Appointment";
|
||||
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", className];
|
||||
|
||||
[s appendString: @"<MeetingRequest xmlns=\"Email:\">"];
|
||||
|
||||
[s appendFormat: @"<AllDayEvent xmlns=\"Email:\">%d</AllDayEvent>", ([event isAllDay] ? 1 : 0)];
|
||||
|
||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||
if ([event startDate])
|
||||
[s appendFormat: @"<StartTime xmlns=\"Email:\">%@</StartTime>", [[event startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
if ([event timeStampAsDate])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
else if ([event created])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Email:\">%@</DTStamp>", [[event created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
||||
if ([event endDate])
|
||||
[s appendFormat: @"<EndTime xmlns=\"Email:\">%@</EndTime>", [[event endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
// FIXME: Single appointment - others are not supported right now
|
||||
[s appendFormat: @"<InstanceType xmlns=\"Email:\">%d</InstanceType>", 0];
|
||||
|
||||
// Location
|
||||
if ([[event location] length])
|
||||
[s appendFormat: @"<Location xmlns=\"Email:\">%@</Location>", [[event location] activeSyncRepresentationInContext: context]];
|
||||
|
||||
[s appendFormat: @"<Organizer xmlns=\"Email:\">%@</Organizer>", [[[event organizer] mailAddress] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// This will trigger the SendMail command. We set it to no for email invitations as
|
||||
// SOGo will send emails when MeetingResponse is called.
|
||||
[s appendFormat: @"<ResponseRequested xmlns=\"Email:\">%d</ResponseRequested>", 0];
|
||||
|
||||
// Sensitivity
|
||||
if ([[event accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
if ([[event accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Email:\">%d</Sensitivity>", v];
|
||||
|
||||
[s appendFormat: @"<BusyStatus xmlns=\"Email:\">%d</BusyStatus>", 2];
|
||||
|
||||
// Timezone
|
||||
tz = [(iCalDateTime *)[event firstChildWithTag: @"dtstart"] timeZone];
|
||||
|
||||
if (!tz)
|
||||
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
|
||||
|
||||
[s appendFormat: @"<TimeZone xmlns=\"Email:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
// We disallow new time proposals
|
||||
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Email:\">%d</DisallowNewTimeProposal>", 1];
|
||||
|
||||
// From http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx:
|
||||
//
|
||||
// "Clients that need to determine whether the GlobalObjId element for a meeting request corresponds to an existing Calendar
|
||||
// object in the Calendar folder have to convert the GlobalObjId element value to a UID element value to make the comparison."
|
||||
//
|
||||
globalObjId = [self _computeGlobalObjectIdFromEvent: event];
|
||||
[s appendFormat: @"<GlobalObjId xmlns=\"Email:\">%@</GlobalObjId>", [globalObjId activeSyncRepresentationInContext: context]];
|
||||
|
||||
// We set the right message type - we must set AS version to 14.1 for this
|
||||
[s appendFormat: @"<MeetingMessageType xmlns=\"Email2:\">%d</MeetingMessageType>", 1];
|
||||
[s appendString: @"</MeetingRequest>"];
|
||||
|
||||
// ContentClass
|
||||
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:calendarmessage"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// MesssageClass and ContentClass
|
||||
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
|
||||
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
|
||||
}
|
||||
|
||||
// Reply-To - FIXME
|
||||
//NSArray *replyTo = [[message objectForKey: @"envelope"] replyTo];
|
||||
//if ([replyTo count])
|
||||
// [s appendFormat: @"<Reply-To xmlns=\"Email:\">%@</Reply-To>", [addressFormatter stringForArray: replyTo]];
|
||||
|
||||
// InternetCPID - 65001 == UTF-8, we use this all the time for now.
|
||||
// - 20127 == US-ASCII
|
||||
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
|
||||
|
||||
// Body - namespace 17
|
||||
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
|
||||
|
||||
nativeBodyType = 1;
|
||||
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
|
||||
|
||||
if (d)
|
||||
{
|
||||
NSString *content;
|
||||
int len, truncated;
|
||||
|
||||
content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||
|
||||
// FIXME: This is a hack. We should normally avoid doing this as we might get
|
||||
// broken encodings. We should rather tell that the data was truncated and expect
|
||||
// a ItemOperations call to download the whole base64 encoding multipart.
|
||||
//
|
||||
// See http://social.msdn.microsoft.com/Forums/en-US/b9944e49-9bc9-4ab8-ba33-a9fc08557c5b/mime-raw-data-in-eas-sync-response?forum=os_exchangeprotocols
|
||||
// for an "interesting" discussion around this.
|
||||
//
|
||||
if (!content)
|
||||
content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding];
|
||||
|
||||
AUTORELEASE(content);
|
||||
|
||||
content = [content activeSyncRepresentationInContext: context];
|
||||
truncated = 0;
|
||||
|
||||
len = [content length];
|
||||
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
|
||||
[s appendFormat: @"<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
|
||||
NSArray *attachmentKeys = [self fetchFileAttachmentKeys];
|
||||
if ([attachmentKeys count])
|
||||
{
|
||||
int i;
|
||||
|
||||
[s appendString: @"<Attachments xmlns=\"AirSyncBase:\">"];
|
||||
|
||||
for (i = 0; i < [attachmentKeys count]; i++)
|
||||
{
|
||||
value = [attachmentKeys objectAtIndex: i];
|
||||
|
||||
[s appendString: @"<Attachment>"];
|
||||
[s appendFormat: @"<DisplayName>%@</DisplayName>", [[value objectForKey: @"filename"] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// FileReference must be a unique identifier across the whole store. We use the following structure:
|
||||
// mail/<foldername>/<message UID/<pathofpart>
|
||||
// mail/INBOX/2
|
||||
[s appendFormat: @"<FileReference>mail/%@/%@/%@</FileReference>", [[[self container] relativeImap4Name] stringByEscapingURL], [self nameInContainer], [value objectForKey: @"path"]];
|
||||
|
||||
[s appendFormat: @"<Method>%d</Method>", 1]; // See: http://msdn.microsoft.com/en-us/library/ee160322(v=exchg.80).aspx
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [[value objectForKey: @"size"] intValue]];
|
||||
//[s appendFormat: @"<IsInline>%d</IsInline>", 1];
|
||||
[s appendString: @"</Attachment>"];
|
||||
}
|
||||
|
||||
[s appendString: @"</Attachments>"];
|
||||
}
|
||||
|
||||
// Flags
|
||||
[s appendString: @"<Flag xmlns=\"Email:\">"];
|
||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", 0];
|
||||
[s appendString: @"</Flag>"];
|
||||
|
||||
// FIXME - support these in the future
|
||||
//[s appendString: @"<ConversationId xmlns=\"Email2:\">foobar</ConversationId>"];
|
||||
//[s appendString: @"<ConversationIndex xmlns=\"Email2:\">zot=</ConversationIndex>"];
|
||||
|
||||
// NativeBodyType -- http://msdn.microsoft.com/en-us/library/ee218276(v=exchg.80).aspx
|
||||
// This is a required child element.
|
||||
// 1 -> plain/text, 2 -> HTML and 3 -> RTF
|
||||
if (nativeBodyType == 4)
|
||||
nativeBodyType = 1;
|
||||
|
||||
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", nativeBodyType];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// Exemple for a message being marked as read:
|
||||
//
|
||||
// <Change>
|
||||
// <ServerId>607</ServerId>
|
||||
// <ApplicationData>
|
||||
// <Read xmlns="Email:">1</Read>
|
||||
// </ApplicationData>
|
||||
// </Change>
|
||||
// </Commands>
|
||||
//
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) _context
|
||||
{
|
||||
id o;
|
||||
|
||||
if ((o = [theValues objectForKey: @"Flag"]))
|
||||
{
|
||||
o = [o objectForKey: @"FlagStatus"];
|
||||
|
||||
if ([o intValue])
|
||||
[self addFlags: @"\\Flagged"];
|
||||
else
|
||||
[self removeFlags: @"\\Flagged"];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Read"]))
|
||||
{
|
||||
if ([o intValue])
|
||||
[self addFlags: @"seen"];
|
||||
else
|
||||
[self removeFlags: @"seen"];;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import <NGObjWeb/SoObjectWebDAVDispatcher.h>
|
||||
|
||||
#include <NGObjWeb/SoObject+SoDAV.h>
|
||||
#include <NGObjWeb/WEClientCapabilities.h>
|
||||
#include <NGObjWeb/WOContext.h>
|
||||
#include <NGObjWeb/WORequest.h>
|
||||
#include <NGObjWeb/WOResponse.h>
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
@interface SoObjectWebDAVDispatcher (ActiveSync)
|
||||
|
||||
- (id)_callObjectMethod:(NSString *)_method inContext:(WOContext *)_ctx;
|
||||
- (id) doOPTIONS:(WOContext *)_ctx;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SoObjectWebDAVDispatcher (ActiveSync)
|
||||
|
||||
- (id) doOPTIONS:(WOContext *)_ctx
|
||||
{
|
||||
WOResponse *response;
|
||||
|
||||
/*
|
||||
See example: http://msdn.microsoft.com/en-us/library/ee204257(v=exchg.80).aspx
|
||||
*/
|
||||
if ([[[_ctx request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
|
||||
{
|
||||
response = [_ctx response];
|
||||
[response setStatus: 200];
|
||||
|
||||
[response setHeader: @"private" forKey: @"Cache-Control"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Allow"];
|
||||
[response setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"];
|
||||
[response setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"];
|
||||
[response setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"];
|
||||
[response setHeader: @"OPTIONS, POST" forKey: @"Public"];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSArray *tmp;
|
||||
id result;
|
||||
|
||||
/* this checks whether the object provides a specific OPTIONS method */
|
||||
if ((result = [self _callObjectMethod:@"OPTIONS" inContext:_ctx]) != nil)
|
||||
return result;
|
||||
|
||||
response = [_ctx response];
|
||||
[response setStatus:200 /* OK */];
|
||||
|
||||
if ((tmp = [self->object davAllowedMethodsInContext:_ctx]) != nil)
|
||||
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"allow"];
|
||||
|
||||
if ([[[_ctx request] clientCapabilities] isWebFolder]) {
|
||||
/*
|
||||
As described over here:
|
||||
http://teyc.editthispage.com/2005/06/02
|
||||
|
||||
This page also says that: "MS-Auth-Via header is not required to work
|
||||
with Web Folders".
|
||||
*/
|
||||
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"public"];
|
||||
}
|
||||
|
||||
if ((tmp = [self->object davComplianceClassesInContext:_ctx]) != nil)
|
||||
[response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"dav"];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
include ../config.make
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include ../Version
|
||||
|
||||
NEEDS_GUI=no
|
||||
BUNDLE_EXTENSION = .SOGo
|
||||
BUNDLE_INSTALL_DIR = $(SOGO_LIBDIR)
|
||||
WOBUNDLE_EXTENSION = $(BUNDLE_EXTENSION)
|
||||
WOBUNDLE_INSTALL_DIR = $(BUNDLE_INSTALL_DIR)
|
||||
|
||||
# SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += \
|
||||
-I.. \
|
||||
-I../.. \
|
||||
-I../../SOPE
|
||||
|
||||
ADDITIONAL_LIB_DIRS += \
|
||||
-L../SoObjects/SOGo/SOGo.framework/ \
|
||||
-L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L/usr/local/lib
|
||||
|
||||
BUNDLE_LIBS += \
|
||||
-lSOGo \
|
||||
-lGDLContentStore \
|
||||
-lGDLAccess \
|
||||
-lNGObjWeb \
|
||||
-lNGCards -lNGMime -lNGLdap \
|
||||
-lNGStreams -lNGExtensions -lEOControl \
|
||||
-lDOM -lSaxObjC -lSBJson
|
||||
|
||||
ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __ICALALARMACTIVESYNC_H__
|
||||
#define __ICALALARMACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface iCalAlarm (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "iCalAlarm+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
|
||||
@implementation iCalAlarm (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
if ([[self action] caseInsensitiveCompare: @"DISPLAY"] == NSOrderedSame)
|
||||
{
|
||||
NSCalendarDate *nextAlarmDate;
|
||||
NSInteger delta;
|
||||
|
||||
nextAlarmDate = [self nextAlarmDate];
|
||||
delta = (int)(([[(iCalEvent *)parent startDate] timeIntervalSince1970] - [nextAlarmDate timeIntervalSince1970])/60);
|
||||
|
||||
// don't send negative reminder - not supported
|
||||
if (delta > 0)
|
||||
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", delta];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
iCalTrigger *trigger;
|
||||
id o;
|
||||
|
||||
o = [theValues objectForKey: @"Reminder"];
|
||||
|
||||
// Outlook: if reminder is set to 0 minutes before start save it as 1 minute since -> 0 minutes in not accepted by SOGo
|
||||
if ([o isEqualToString: @"0"])
|
||||
o = @"1";
|
||||
|
||||
trigger = [iCalTrigger elementWithTag: @"TRIGGER"];
|
||||
[trigger setValueType: @"DURATION"];
|
||||
[self setTrigger: trigger];
|
||||
[self setAction: @"DISPLAY"];
|
||||
|
||||
// SOGo web ui only supports 1w but not 2w (custom reminder only supports min/hours/days)
|
||||
// 1week = -P1W
|
||||
// 2weeks > -PxD
|
||||
// xdays > -PxD
|
||||
// xhours -> -PTxH
|
||||
// xmin -> -PTxM
|
||||
if ([o intValue] == 10080)
|
||||
[trigger setSingleValue: [NSString stringWithFormat: @"-P1W" ] forKey: @""];
|
||||
else
|
||||
{
|
||||
if (([o intValue] % 1440) == 0)
|
||||
[trigger setSingleValue: [NSString stringWithFormat: @"-P%dD", ([o intValue] / 1440)] forKey: @""];
|
||||
else
|
||||
{
|
||||
if (([o intValue] % 60) == 0)
|
||||
[trigger setSingleValue: [NSString stringWithFormat: @"-PT%dH", ([o intValue] / 60)] forKey: @""];
|
||||
else
|
||||
[trigger setSingleValue: [NSString stringWithFormat: @"-PT%@M", o] forKey: @""];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ICALEVENTACTIVESYNC_H__
|
||||
#define __ICALEVENTACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/iCalEvent.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface iCalEvent (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#import "iCalEvent+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
|
||||
#include "iCalAlarm+ActiveSync.h"
|
||||
#include "iCalRecurrenceRule+ActiveSync.h"
|
||||
#include "iCalTimeZone+ActiveSync.h"
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
@implementation iCalEvent (ActiveSync)
|
||||
|
||||
- (int) _attendeeStatus: (iCalPerson *) attendee
|
||||
{
|
||||
int attendee_status;
|
||||
|
||||
attendee_status = 5;
|
||||
if ([[attendee partStat] caseInsensitiveCompare: @"ACCEPTED"] == NSOrderedSame)
|
||||
attendee_status = 3;
|
||||
else if ([[attendee partStat] caseInsensitiveCompare: @"DECLINED"] == NSOrderedSame)
|
||||
attendee_status = 4;
|
||||
else if ([[attendee partStat] caseInsensitiveCompare: @"TENTATIVE"] == NSOrderedSame)
|
||||
attendee_status = 2;
|
||||
|
||||
return attendee_status;
|
||||
}
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSArray *attendees;
|
||||
|
||||
iCalPerson *organizer, *attendee;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
int v;
|
||||
|
||||
NSTimeZone *userTimeZone;
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
[s appendFormat: @"<AllDayEvent xmlns=\"Calendar:\">%d</AllDayEvent>", ([self isAllDay] ? 1 : 0)];
|
||||
|
||||
// DTStamp -- http://msdn.microsoft.com/en-us/library/ee219470(v=exchg.80).aspx
|
||||
if ([self timeStampAsDate])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[self timeStampAsDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
else if ([self created])
|
||||
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[self created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
|
||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||
if ([self startDate])
|
||||
{
|
||||
if ([self isAllDay])
|
||||
[s appendFormat: @"<StartTime xmlns=\"Calendar:\">%@</StartTime>",
|
||||
[[[self startDate] dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: ([userTimeZone secondsFromGMTForDate: [self startDate]])*-1]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
else
|
||||
[s appendFormat: @"<StartTime xmlns=\"Calendar:\">%@</StartTime>", [[self startDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
|
||||
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
|
||||
if ([self endDate])
|
||||
{
|
||||
if ([self isAllDay])
|
||||
[s appendFormat: @"<EndTime xmlns=\"Calendar:\">%@</EndTime>",
|
||||
[[[self endDate] dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: ([userTimeZone secondsFromGMTForDate: [self endDate]])*-1]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
else
|
||||
[s appendFormat: @"<EndTime xmlns=\"Calendar:\">%@</EndTime>", [[self endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
|
||||
// Timezone
|
||||
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
|
||||
|
||||
if (!tz)
|
||||
tz = [iCalTimeZone timeZoneForName: @"Europe/London"];
|
||||
|
||||
[s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Organizer and other invitations related properties
|
||||
if ((organizer = [self organizer]))
|
||||
{
|
||||
o = [organizer rfc822Email];
|
||||
if ([o length])
|
||||
{
|
||||
[s appendFormat: @"<Organizer_Email xmlns=\"Calendar:\">%@</Organizer_Email>", o];
|
||||
|
||||
o = [organizer cn];
|
||||
if ([o length])
|
||||
[s appendFormat: @"<Organizer_Name xmlns=\"Calendar:\">%@</Organizer_Name>", o];
|
||||
}
|
||||
}
|
||||
|
||||
// Attendees
|
||||
attendees = [self attendees];
|
||||
|
||||
if ([attendees count])
|
||||
{
|
||||
int i, attendee_status, attendee_type;
|
||||
|
||||
[s appendString: @"<Attendees xmlns=\"Calendar:\">"];
|
||||
|
||||
for (i = 0; i < [attendees count]; i++)
|
||||
{
|
||||
[s appendString: @"<Attendee xmlns=\"Calendar:\">"];
|
||||
|
||||
attendee = [attendees objectAtIndex: i];
|
||||
[s appendFormat: @"<Attendee_Email xmlns=\"Calendar:\">%@</Attendee_Email>", [attendee rfc822Email]];
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [attendee cn]];
|
||||
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
|
||||
[s appendFormat: @"<Attendee_Status xmlns=\"Calendar:\">%d</Attendee_Status>", attendee_status];
|
||||
|
||||
// FIXME: handle resource
|
||||
if ([[attendee role] caseInsensitiveCompare: @"REQ-PARTICIPANT"] == NSOrderedSame)
|
||||
attendee_type = 1;
|
||||
else
|
||||
attendee_type = 2;
|
||||
|
||||
[s appendFormat: @"<Attendee_Type xmlns=\"Calendar:\">%d</Attendee_Type>", attendee_type];
|
||||
[s appendString: @"</Attendee>"];
|
||||
}
|
||||
[s appendString: @"</Attendees>"];
|
||||
}
|
||||
|
||||
// This depends on the 'NEEDS-ACTION' parameter.
|
||||
// This will trigger the SendMail command
|
||||
if ([self userIsAttendee: [context activeUser]])
|
||||
{
|
||||
iCalPerson *attendee;
|
||||
|
||||
int attendee_status;
|
||||
|
||||
attendee = [self userAsAttendee: [context activeUser]];
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
|
||||
[s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1];
|
||||
[s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status];
|
||||
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", 3];
|
||||
[s appendFormat: @"<DisallowNewTimeProposal xmlns=\"Calendar:\">%d</DisallowNewTimeProposal>", 1];
|
||||
|
||||
// BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
|
||||
[s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", 2];
|
||||
}
|
||||
|
||||
// Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx
|
||||
if ([[self summary] length])
|
||||
[s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Location
|
||||
if ([[self location] length])
|
||||
[s appendFormat: @"<Location xmlns=\"Calendar:\">%@</Location>", [[self location] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Importance - NOT SUPPORTED - DO NOT ENABLE
|
||||
//o = [self priority];
|
||||
//if ([o isEqualToString: @"9"])
|
||||
// v = 0;
|
||||
//else if ([o isEqualToString: @"1"])
|
||||
// v = 2;
|
||||
//else
|
||||
// v = 1;
|
||||
//[s appendFormat: @"<Importance xmlns=\"Calendar:\">%d</Importance>", v];
|
||||
|
||||
// UID -- http://msdn.microsoft.com/en-us/library/ee159919(v=exchg.80).aspx
|
||||
if ([[self uid] length])
|
||||
[s appendFormat: @"<UID xmlns=\"Calendar:\">%@</UID>", [self uid]];
|
||||
|
||||
// Sensitivity
|
||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v];
|
||||
|
||||
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
|
||||
// TODO: improve this to handle more alarm types
|
||||
if ([self hasAlarms])
|
||||
{
|
||||
iCalAlarm *alarm;
|
||||
|
||||
alarm = [[self alarms] objectAtIndex: 0];
|
||||
[s appendString: [alarm activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Recurrence rules
|
||||
if ([self isRecurrent])
|
||||
{
|
||||
[s appendString: [[[self recurrenceRules] lastObject] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Comment
|
||||
o = [self comment];
|
||||
if ([o length])
|
||||
{
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// To understand meeting requests/responses, see:
|
||||
//
|
||||
// http://blogs.msdn.com/b/exchangedev/archive/2011/07/22/working-with-meeting-requests-in-exchange-activesync.aspx
|
||||
// http://blogs.msdn.com/b/exchangedev/archive/2011/07/29/working-with-meeting-responses-in-exchange-activesync.aspx
|
||||
//
|
||||
//
|
||||
// Here is an example of a Sync call when sogo10 accepts an invitation from sogo3:
|
||||
//
|
||||
// <Change>
|
||||
// <ServerId>2978-52EA9D00-1-A253E70.ics</ServerId>
|
||||
// <ApplicationData>
|
||||
// <TimeZone xmlns="Calendar:">LAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w==</TimeZone>
|
||||
// <AllDayEvent xmlns="Calendar:">0</AllDayEvent>
|
||||
// <StartTime xmlns="Calendar:">20140207T130000Z</StartTime>
|
||||
// <EndTime xmlns="Calendar:">20140207T140000Z</EndTime>
|
||||
// <DTStamp xmlns="Calendar:">20140130T185245Z</DTStamp>
|
||||
// <Subject xmlns="Calendar:">test 8</Subject>
|
||||
// <Sensitivity xmlns="Calendar:">0</Sensitivity>
|
||||
// <Body xmlns="AirSyncBase:">
|
||||
// <Type>1</Type>
|
||||
// <Data/>
|
||||
// </Body>
|
||||
// <Organizer_Email xmlns="Calendar:">sogo3@example.com</Organizer_Email>
|
||||
// <UID xmlns="Calendar:">2978-52EA9D00-1-A253E70</UID>
|
||||
// <Attendees xmlns="Calendar:">
|
||||
// <Attendee>
|
||||
// <Attendee_Name>sogo10</Attendee_Name>
|
||||
// <Attendee_Email>sogo10@example.com</Attendee_Email>
|
||||
// <Attendee_Type>1</Attendee_Type>
|
||||
// </Attendee>
|
||||
// </Attendees>
|
||||
// <BusyStatus xmlns="Calendar:">2</BusyStatus>
|
||||
// <MeetingStatus xmlns="Calendar:">3</MeetingStatus>
|
||||
// <Organizer_Name xmlns="Calendar:">Wolfgang Fritz</Organizer_Name>
|
||||
// </ApplicationData>
|
||||
// </Change>
|
||||
//
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
iCalDateTime *start, *end;
|
||||
NSTimeZone *userTimeZone;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
BOOL isAllDay;
|
||||
|
||||
if ((o = [theValues objectForKey: @"UID"]))
|
||||
[self setUid: o];
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ((o = [theValues objectForKey: @"Subject"]))
|
||||
[self setSummary: o];
|
||||
|
||||
isAllDay = NO;
|
||||
if ([[theValues objectForKey: @"AllDayEvent"] intValue])
|
||||
{
|
||||
isAllDay = YES;
|
||||
}
|
||||
|
||||
//
|
||||
// 0- free, 1- tentative, 2- busy and 3- out of office
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"BusyStatus"]))
|
||||
{
|
||||
[o intValue];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||
{
|
||||
[o intValue];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- normal, 1- personal, 2- private and 3-confidential
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"Sensitivy"]))
|
||||
{
|
||||
switch ([o intValue])
|
||||
{
|
||||
case 2:
|
||||
[self setAccessClass: @"PRIVATE"];
|
||||
break;
|
||||
case 3:
|
||||
[self setAccessClass: @"CONFIDENTIAL"];
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
[self setAccessClass: @"PUBLIC"];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
{
|
||||
// Ugh, we ignore it for now.
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
}
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Location"]))
|
||||
[self setLocation: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"StartTime"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[start setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDate: o];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[start setDateTime: o];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"EndTime"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
end = (iCalDateTime *) [self uniqueChildWithTag: @"dtend"];
|
||||
[end setTimeZone: tz];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If an alarm is deinfed with an action != DISPLAY, we ignore the alarm - don't want to overwrite.
|
||||
//
|
||||
if ([self hasAlarms] && [[[[self alarms] objectAtIndex: 0] action] caseInsensitiveCompare: @"DISPLAY"] != NSOrderedSame)
|
||||
{
|
||||
// Ignore the alarm for now
|
||||
}
|
||||
else if ((o = [theValues objectForKey: @"Reminder"]))
|
||||
{
|
||||
// NOTE: Outlook sends a 15 min reminder (18 hour for allday) if no reminder is specified
|
||||
// although no default reminder is defined (File -> Options -> Clendar -> Calendar Options - > Default Reminders)
|
||||
//
|
||||
// http://answers.microsoft.com/en-us/office/forum/office_2013_release-outlook/desktop-outlook-calendar-creates-entries-with/9aef72d8-81bb-4a32-a6ab-bf7d216fb811?page=5&tm=1395690285088
|
||||
//
|
||||
iCalAlarm *alarm;
|
||||
|
||||
alarm = [[iCalAlarm alloc] init];
|
||||
[alarm takeActiveSyncValues: theValues inContext: context];
|
||||
|
||||
[self removeAllAlarms];
|
||||
[self addToAlarms: alarm];
|
||||
RELEASE(alarm);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We remove existing alarm since no reminder in the ActiveSync payload
|
||||
[self removeAllAlarms];
|
||||
}
|
||||
|
||||
// Recurrence
|
||||
if ((o = [theValues objectForKey: @"Recurrence"]))
|
||||
{
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
rule = [[iCalRecurrenceRule alloc] init];
|
||||
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
|
||||
RELEASE(rule);
|
||||
|
||||
[rule takeActiveSyncValues: o inContext: context];
|
||||
}
|
||||
|
||||
// Organizer - we don't touch the value unless we're the organizer
|
||||
if ((o = [theValues objectForKey: @"Organizer_Email"]) &&
|
||||
([self userIsOrganizer: [context activeUser]] || [[context activeUser] hasEmail: o]))
|
||||
{
|
||||
iCalPerson *person;
|
||||
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: o];
|
||||
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
// Attendees - we don't touch the values if we're an attendee. This is gonna
|
||||
// be done automatically by the ActiveSync client when invoking MeetingResponse.
|
||||
if (![self userIsAttendee: [context activeUser]])
|
||||
{
|
||||
if ((o = [theValues objectForKey: @"Attendees"]))
|
||||
{
|
||||
NSMutableArray *attendees;
|
||||
NSDictionary *attendee;
|
||||
iCalPerson *person;
|
||||
int status, i;
|
||||
|
||||
attendees = [NSMutableArray array];
|
||||
|
||||
for (i = 0; i < [o count]; i++)
|
||||
{
|
||||
// Each attendee has is a dictionary similar to this:
|
||||
// { "Attendee_Email" = "sogo3@example.com"; "Attendee_Name" = "Wolfgang Fritz"; "Attendee_Status" = 5; "Attendee_Type" = 1; }
|
||||
attendee = [o objectAtIndex: i];
|
||||
|
||||
person = [iCalPerson elementWithTag: @"attendee"];
|
||||
[person setCn: [attendee objectForKey: @"Attendee_Name"]];
|
||||
[person setEmail: [attendee objectForKey: @"Attendee_Email"]];
|
||||
|
||||
status = [[attendee objectForKey: @"Attendee_Status"] intValue];
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case 2:
|
||||
[person setPartStat: @"TENTATIVE"];
|
||||
break;
|
||||
case 3:
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
break;
|
||||
case 4:
|
||||
[person setPartStat: @"DECLINED"];
|
||||
break;
|
||||
case 0:
|
||||
case 5:
|
||||
default:
|
||||
[person setPartStat: @"NEEDS-ACTION"];
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: handle Attendee_Type
|
||||
|
||||
[attendees addObject: person];
|
||||
}
|
||||
|
||||
[self setAttendees: attendees];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ICALRECURRENCERULEACTIVESYNC_H__
|
||||
#define __ICALRECURRENCERULEACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
@class NSDictionary;
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface iCalRecurrenceRule (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext:(WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#import "iCalRecurrenceRule+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
|
||||
#import "NSCalendarDate+ActiveSync.h"
|
||||
#import "NSDate+ActiveSync.h"
|
||||
|
||||
@implementation iCalRecurrenceRule (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
int type;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
[s appendString: @"<Recurrence xmlns=\"Calendar:\">"];
|
||||
|
||||
// 0 -> daily, 1 -> weekly, 2 -> montly, 5 -> yearly
|
||||
type = 0;
|
||||
|
||||
if ([self frequency] == iCalRecurrenceFrequenceDaily)
|
||||
{
|
||||
type = 0;
|
||||
|
||||
// 1 -> sunday, 2 -> monday, 4 -> tuesday, 8 -> wednesday, 16 -> thursday, 32 -> friday, 64 -> saturday, 127 -> last day of month (montly or yearl recurrences only)
|
||||
if ([[self byDayMask] isWeekDays])
|
||||
{
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (2|4|8|16|32)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"Calendar:\">%d</Recurrence_Interval>", [self repeatInterval]];
|
||||
}
|
||||
}
|
||||
else if ([self frequency] == iCalRecurrenceFrequenceWeekly)
|
||||
{
|
||||
iCalWeekOccurrences *occurrences;
|
||||
int i, v;
|
||||
|
||||
type = 1;
|
||||
|
||||
occurrences = [[self byDayMask] weekDayOccurrences];
|
||||
v = 0;
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (occurrences[i])
|
||||
v += (1 << i);
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", v];
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"Calendar:\">%d</Recurrence_Interval>", [self repeatInterval]];
|
||||
}
|
||||
else if ([self frequency] == iCalRecurrenceFrequenceMonthly)
|
||||
{
|
||||
if ([[self byDay] length])
|
||||
{
|
||||
int firstOccurrence;
|
||||
iCalByDayMask *dm;
|
||||
|
||||
type = 3; // recurs monthly on the nth day
|
||||
dm = [self byDayMask];
|
||||
firstOccurrence = [dayMask firstOccurrence];
|
||||
|
||||
// Handle the case for "Last day of the month"
|
||||
if (firstOccurrence < 0)
|
||||
firstOccurrence = 5;
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", firstOccurrence];
|
||||
}
|
||||
else if ([[self byMonthDay] count])
|
||||
{
|
||||
NSArray *days;
|
||||
|
||||
type = 2; // recurs monthly
|
||||
days = [self byMonthDay];
|
||||
if ([days count] > 0 && [[days objectAtIndex: 0] intValue] < 0)
|
||||
{
|
||||
// Last day of the month
|
||||
iCalByDayMask *dm;
|
||||
|
||||
dm = [self byDayMask];
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", 5];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unsupported rule in ActiveSync/Outlook. Rule that says "Repeat on the 7th and 8th of each month".
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
|
||||
{
|
||||
type = 6; // Yearly on the nth day
|
||||
|
||||
if ([[self flattenedValuesForKey: @"bymonth"] length])
|
||||
{
|
||||
if ([[self byDay] length])
|
||||
{
|
||||
int firstOccurrence;
|
||||
iCalByDayMask *dm;
|
||||
|
||||
dm = [self byDayMask];
|
||||
firstOccurrence = [dm firstOccurrence];
|
||||
if (firstOccurrence < 0)
|
||||
firstOccurrence = 5;
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"Calendar:\">%d</Recurrence_DayOfWeek>", (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"Calendar:\">%d</Recurrence_WeekOfMonth>", firstOccurrence];
|
||||
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"Calendar:\">%@</Recurrence_MonthOfYear>",
|
||||
[self flattenedValuesForKey: @"bymonth"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
type = 5; // Yearly
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"Calendar:\">%@</Recurrence_DayOfMonth>",
|
||||
[self flattenedValuesForKey: @"bymonthday"]];
|
||||
|
||||
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"Calendar:\">%@</Recurrence_MonthOfYear>",
|
||||
[self flattenedValuesForKey: @"bymonth"]];
|
||||
}
|
||||
}
|
||||
else
|
||||
type = 5;
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Recurrence_Type xmlns=\"Calendar:\">%d</Recurrence_Type>", type];
|
||||
|
||||
// Occurrences / Until
|
||||
//[s appendFormat: @"<Recurrence_Occurrences xmlns=\"Calendar:\">%d</Recurrence_Occurrences>", 5];
|
||||
if ([self repeatCount])
|
||||
{
|
||||
[s appendFormat: @"<Recurrence_Occurrences xmlns=\"Calendar:\">%@</Recurrence_Occurrences>",
|
||||
[self flattenedValuesForKey: @"count"]];
|
||||
}
|
||||
else if ([self untilDate])
|
||||
{
|
||||
NSCalendarDate *date;
|
||||
|
||||
date = [self untilDate];
|
||||
//ud = [[context activeUser] userDefaults];
|
||||
//[date setTimeZone: [ud timeZone]];
|
||||
|
||||
[s appendFormat: @"<Recurrence_Until xmlns=\"Calendar:\">%@</Recurrence_Until>",
|
||||
[date activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
|
||||
|
||||
[s appendString: @"</Recurrence>"];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) _setByDayFromValues: (NSDictionary *) theValues
|
||||
{
|
||||
NSString *day;
|
||||
id o;
|
||||
|
||||
unsigned int day_of_week;
|
||||
int i, week_of_month;
|
||||
|
||||
o = [theValues objectForKey: @"Recurrence_DayOfWeek"];
|
||||
|
||||
// The documentation says WeekOfMonth must be between 1 and 5. The value
|
||||
// 5 means "last week of month"
|
||||
week_of_month = [[theValues objectForKey: @"Recurrence_WeekOfMonth"] intValue];
|
||||
|
||||
if (week_of_month > 4)
|
||||
week_of_month = -1;
|
||||
|
||||
// We find the correct day of the week
|
||||
day_of_week = [o intValue];
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if ((1<<i) == day_of_week)
|
||||
{
|
||||
day_of_week = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
day = [self iCalRepresentationForWeekDay: i];
|
||||
|
||||
[self setSingleValue: [NSString stringWithFormat: @"%d%@",
|
||||
week_of_month, day]
|
||||
forKey: @"byday"];
|
||||
}
|
||||
|
||||
- (void) _setByMonthFromValues: (NSDictionary *) theValues
|
||||
{
|
||||
unsigned int month_of_year;
|
||||
|
||||
month_of_year = [[theValues objectForKey: @"Recurrence_MonthOfYear"] intValue];
|
||||
|
||||
[self setSingleValue: [NSString stringWithFormat: @"%d", month_of_year]
|
||||
forKey: @"bymonth"];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
id o;
|
||||
|
||||
int recurrenceType;
|
||||
|
||||
recurrenceType = [[theValues objectForKey: @"Recurrence_Type"] intValue];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Recurrence_Interval"]))
|
||||
{
|
||||
[self setRepeatInterval: [o intValue]];
|
||||
}
|
||||
else
|
||||
[self setRepeatInterval: 1];
|
||||
|
||||
switch (recurrenceType)
|
||||
{
|
||||
//
|
||||
// Daily
|
||||
//
|
||||
case 0:
|
||||
[self setFrequency: iCalRecurrenceFrequenceDaily];
|
||||
|
||||
|
||||
// Every weekday
|
||||
if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
|
||||
{
|
||||
[self setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||
}
|
||||
break;
|
||||
//
|
||||
// Weekly
|
||||
//
|
||||
case 1:
|
||||
[self setFrequency: iCalRecurrenceFrequenceWeekly];
|
||||
|
||||
// 42 == Every Monday, Wednesday and Friday, for example
|
||||
if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
|
||||
{
|
||||
iCalWeekOccurrences days;
|
||||
unsigned int i, v;
|
||||
|
||||
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
|
||||
v = [o intValue];
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (v & (1<<i))
|
||||
days[i] = iCalWeekOccurrenceAll;
|
||||
}
|
||||
|
||||
[self setByDayMask: [iCalByDayMask byDayMaskWithDays: days]];
|
||||
}
|
||||
break;
|
||||
//
|
||||
// Montly
|
||||
//
|
||||
case 2:
|
||||
case 3:
|
||||
[self setFrequency: iCalRecurrenceFrequenceMonthly];
|
||||
|
||||
// The 5th of every X month(s)
|
||||
if ((o = [theValues objectForKey: @"Recurrence_DayOfMonth"]))
|
||||
{
|
||||
[self setValues: [NSArray arrayWithObject: o]
|
||||
atIndex: 0 forKey: @"bymonthday"];
|
||||
}
|
||||
// The 3rd Thursay every X month(s)
|
||||
else if ((o = [theValues objectForKey: @"Recurrence_DayOfWeek"]))
|
||||
{
|
||||
[self _setByDayFromValues: theValues];
|
||||
}
|
||||
|
||||
break;
|
||||
//
|
||||
// Yearly
|
||||
//
|
||||
case 5:
|
||||
case 6:
|
||||
default:
|
||||
[self setFrequency: iCalRecurrenceFrequenceYearly];
|
||||
|
||||
// On April 19th
|
||||
if ((o = [theValues objectForKey: @"Recurrence_DayOfMonth"]))
|
||||
{
|
||||
[self setValues: [NSArray arrayWithObject: o] atIndex: 0
|
||||
forKey: @"bymonthday"];
|
||||
|
||||
[self _setByMonthFromValues: theValues];
|
||||
}
|
||||
else
|
||||
{
|
||||
// On the Second Wednesday of April
|
||||
[self _setByDayFromValues: theValues];
|
||||
[self _setByMonthFromValues: theValues];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Recurrence_Occurrences"]))
|
||||
{
|
||||
[self setRepeatCount: [o intValue]];
|
||||
}
|
||||
else if ((o = [theValues objectForKey: @"Recurrence_Until"]))
|
||||
{
|
||||
[self setUntilDate: [o calendarDate]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __ICALTIMEZONEACTIVESYNC_H__
|
||||
#define __ICALTIMEZONEACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface iCalTimeZone (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "iCalTimeZone+ActiveSync.h"
|
||||
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSCalendarDate.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSDate.h>
|
||||
#include <Foundation/NSString.h>
|
||||
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
|
||||
#include "NSData+ActiveSync.h"
|
||||
|
||||
struct SYSTEMTIME {
|
||||
uint16_t wYear;
|
||||
uint16_t wMonth;
|
||||
uint16_t wDayOfWeek;
|
||||
uint16_t wDay;
|
||||
uint16_t wHour;
|
||||
uint16_t wMinute;
|
||||
uint16_t wSecond;
|
||||
uint16_t wMilliseconds;
|
||||
};
|
||||
|
||||
@interface iCalTimeZonePeriod (ActiveSync)
|
||||
|
||||
- (void) _fillTZDate: (struct SYSTEMTIME *) tzData;
|
||||
|
||||
@end
|
||||
|
||||
@implementation iCalTimeZonePeriod (ActiveSync)
|
||||
|
||||
//
|
||||
// FIXME - combine with iCalTimeZone+MAPIStore.m
|
||||
//
|
||||
- (void) _fillTZDate: (struct SYSTEMTIME *) tzData
|
||||
{
|
||||
iCalRecurrenceRule *rrule;
|
||||
NSArray *byMonth;
|
||||
iCalByDayMask *mask;
|
||||
NSCalendarDate *dateValue;
|
||||
|
||||
rrule = [self recurrenceRule];
|
||||
byMonth = [rrule byMonth];
|
||||
if ([byMonth count] > 0)
|
||||
{
|
||||
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
|
||||
mask = [rrule byDayMask];
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
tzData->wDay = [mask firstOccurrence];
|
||||
|
||||
dateValue = [self startDate];
|
||||
tzData->wHour = [dateValue hourOfDay];
|
||||
tzData->wMinute = [dateValue minuteOfHour];
|
||||
tzData->wSecond = [dateValue secondOfMinute];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation iCalTimeZone (ActiveSync)
|
||||
|
||||
//
|
||||
// FIXME - combine with iCalTimeZone+MAPIStore.m
|
||||
//
|
||||
- (iCalTimeZonePeriod *) _mostRecentPeriodWithName: (NSString *) periodName
|
||||
{
|
||||
NSArray *periods;
|
||||
iCalTimeZonePeriod *period;
|
||||
NSUInteger max;
|
||||
|
||||
periods = [self childrenWithTag: periodName];
|
||||
max = [periods count];
|
||||
if (max > 0)
|
||||
{
|
||||
periods = [periods sortedArrayUsingSelector: @selector (compare:)];
|
||||
period = (iCalTimeZonePeriod *) [periods objectAtIndex: (max - 1)];
|
||||
}
|
||||
else
|
||||
period = nil;
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
iCalTimeZonePeriod *period;
|
||||
NSMutableData *bytes;
|
||||
|
||||
uint32_t lBias;
|
||||
uint32_t lStandardBias;
|
||||
uint32_t lDaylightBias;
|
||||
//uint16_t wStandardYear;
|
||||
struct SYSTEMTIME stStandardDate;
|
||||
//uint16_t wDaylightYear;
|
||||
struct SYSTEMTIME stDaylightDate;
|
||||
|
||||
char standardName[64], daylightName[64];
|
||||
|
||||
bytes = [NSMutableData data];
|
||||
|
||||
memset(standardName, 0, 64);
|
||||
memset(daylightName, 0, 64);
|
||||
lStandardBias = 0;
|
||||
|
||||
period = [self _mostRecentPeriodWithName: @"STANDARD"];
|
||||
lBias = -[period secondsOffsetFromGMT] / 60;
|
||||
[period _fillTZDate: &stStandardDate];
|
||||
|
||||
period = [self _mostRecentPeriodWithName: @"DAYLIGHT"];
|
||||
if (!period)
|
||||
stStandardDate.wMonth = 0;
|
||||
|
||||
lDaylightBias = (uint32_t) -([period secondsOffsetFromGMT] / 60) - lBias;
|
||||
[period _fillTZDate: &stDaylightDate];
|
||||
//wStandardYear = stStandardDate.wYear;
|
||||
//wDaylightYear = stDaylightDate.wYear;
|
||||
|
||||
|
||||
// We build the timezone
|
||||
[bytes appendBytes: &lBias length: 4];
|
||||
[bytes appendBytes: standardName length: 64];
|
||||
[bytes appendBytes: &stStandardDate length: 16];
|
||||
[bytes appendBytes: &lStandardBias length: 4];
|
||||
[bytes appendBytes: daylightName length: 64];
|
||||
[bytes appendBytes: &stDaylightDate length: 16];
|
||||
[bytes appendBytes: &lDaylightBias length: 4];
|
||||
|
||||
return [bytes activeSyncRepresentationInContext: context];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef __ICALTODOACTIVESYNC_H__
|
||||
#define __ICALTODOACTIVESYNC_H__
|
||||
|
||||
#import <NGCards/iCalToDo.h>
|
||||
|
||||
@class NSString;
|
||||
@class WOContext;
|
||||
|
||||
@interface iCalToDo (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context;
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Inverse inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Inverse inc. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#import "iCalToDo+ActiveSync.h"
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
@implementation iCalToDo (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
id o;
|
||||
|
||||
int v;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
// Complete
|
||||
o = [self completed];
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", (o ? 1 : 0)];
|
||||
|
||||
// DateCompleted
|
||||
if (o)
|
||||
[s appendFormat: @"<DateCompleted xmlns=\"Tasks:\">%@</DateCompleted>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Start date
|
||||
if ((o = [self startDate]))
|
||||
{
|
||||
[s appendFormat: @"<StartDate xmlns=\"Tasks:\">%@</StartDate>", [o activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<UTCStartDate xmlns=\"Tasks:\">%@</UTCStartDate>", [o activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
|
||||
// Due date
|
||||
if ((o = [self due]))
|
||||
{
|
||||
[s appendFormat: @"<DueDate xmlns=\"Tasks:\">%@</DueDate>", [o activeSyncRepresentationInContext: context]];
|
||||
[s appendFormat: @"<UTCDueDate xmlns=\"Tasks:\">%@</UTCDueDate>", [o activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Importance
|
||||
o = [self priority];
|
||||
if ([o isEqualToString: @"9"])
|
||||
v = 0;
|
||||
else if ([o isEqualToString: @"1"])
|
||||
v = 2;
|
||||
else
|
||||
v = 1;
|
||||
[s appendFormat: @"<Importance xmlns=\"Tasks:\">%d</Importance>", v];
|
||||
|
||||
// Reminder - FIXME
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||
|
||||
// Sensitivity - FIXME
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", 0];
|
||||
|
||||
// Subject
|
||||
o = [self summary];
|
||||
if ([o length])
|
||||
[s appendFormat: @"<Subject xmlns=\"Tasks:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self comment]))
|
||||
{
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Truncated>%d</Truncated>", 0];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSTimeZone *userTimeZone;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
NSInteger tzOffset;
|
||||
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
|
||||
// FIXME: merge with iCalEvent
|
||||
if ((o = [theValues objectForKey: @"Subject"]))
|
||||
[self setSummary: o];
|
||||
|
||||
// FIXME: merge with iCalEvent
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
|
||||
|
||||
if ([[theValues objectForKey: @"Complete"] intValue] &&
|
||||
((o = [theValues objectForKey: @"DateCompleted"])) )
|
||||
{
|
||||
iCalDateTime *completed;
|
||||
|
||||
o = [o calendarDate];
|
||||
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"];
|
||||
//tzOffset = [[o timeZone] secondsFromGMTForDate: o];
|
||||
//o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
// hours: 0 minutes: 0
|
||||
// seconds: -tzOffset];
|
||||
[completed setDate: o];
|
||||
[self setStatus: @"COMPLETED"];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"DueDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
|
||||
[due setTimeZone: tz];
|
||||
|
||||
tzOffset = [userTimeZone secondsFromGMTForDate: o];
|
||||
o = [o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: tzOffset];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
// 2 == high, 1 == normal, 0 == low
|
||||
if ((o = [theValues objectForKey: @"Importance"]))
|
||||
{
|
||||
if ([o intValue] == 2)
|
||||
[self setPriority: @"1"];
|
||||
else if ([o intValue] == 1)
|
||||
[self setPriority: @"5"];
|
||||
else
|
||||
[self setPriority: @"9"];
|
||||
}
|
||||
|
||||
|
||||
if ((o = [theValues objectForKey: @"ReminderTime"]))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
requires = ( MAIN, Appointments, Contacts, Mailer );
|
||||
}
|
||||
@@ -1,4 +1,52 @@
|
||||
# use *:8843 for SSL
|
||||
#
|
||||
#
|
||||
# Keep only one of those vhost definition, comment out the other one
|
||||
#
|
||||
#
|
||||
|
||||
# for https
|
||||
# don't forget to add a Listen parameter for port 8843:
|
||||
# Listen 8843
|
||||
<VirtualHost *:8843>
|
||||
ServerName YOUR.SERVER.NAME
|
||||
|
||||
SSLEngine on
|
||||
|
||||
SSLProtocol -ALL +SSLv3 +TLSv1
|
||||
SSLHonorCipherOrder On
|
||||
SSLCipherSuite HIGH:MEDIUM:!ADH:!aNULL:!eNULL:!NULL
|
||||
SSLCertificateFile /path/to/your/cert/cert.pem
|
||||
SSLCertificateChainFile /path/to/your/cert/cert-chain-file.pem
|
||||
SSLCertificateKeyFile /path/to/your/key/file.key
|
||||
|
||||
RewriteEngine Off
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
ProxyPreserveHost On
|
||||
ProxyPassInterpolateEnv On
|
||||
ProxyPass /principals http://127.0.0.1:20000/SOGo/dav/ interpolate
|
||||
ProxyPass /SOGo http://127.0.0.1:20000/SOGo interpolate
|
||||
ProxyPass / http://127.0.0.1:20000/SOGo/dav/ interpolate
|
||||
<Location />
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Location>
|
||||
|
||||
<Proxy http://127.0.0.1:20000>
|
||||
RequestHeader set "x-webobjects-server-port" "8843"
|
||||
RequestHeader set "x-webobjects-server-name" "CHANGETHIS:8843"
|
||||
RequestHeader set "x-webobjects-server-url" "http://CHANGETHIS:8843"
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
AddDefaultCharset UTF-8
|
||||
</Proxy>
|
||||
|
||||
ErrorLog /var/log/apache2/ab-error.log
|
||||
CustomLog /var/log/apache2/ab-access.log combined
|
||||
</VirtualHost>
|
||||
|
||||
# plain http
|
||||
# Same here, don't forget to add a Listen parameter for port 8800:
|
||||
# Listen 8800
|
||||
<VirtualHost *:8800>
|
||||
RewriteEngine Off
|
||||
ProxyRequests Off
|
||||
|
||||
+14
-6
@@ -2,8 +2,6 @@ Alias /SOGo.woa/WebServerResources/ \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/
|
||||
Alias /SOGo/WebServerResources/ \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/
|
||||
AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
|
||||
/usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2
|
||||
|
||||
<Directory /usr/lib/GNUstep/SOGo/>
|
||||
AllowOverride None
|
||||
@@ -18,10 +16,6 @@ AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
<LocationMatch "^/SOGo/so/ControlPanel/Products/.*UI/Resources/.*\.(jpg|png|gif|css|js)">
|
||||
SetHandler default-handler
|
||||
</LocationMatch>
|
||||
|
||||
## Uncomment the following to enable proxy-side authentication, you will then
|
||||
## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and
|
||||
## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section
|
||||
@@ -48,6 +42,14 @@ ProxyPreserveHost On
|
||||
|
||||
ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
||||
|
||||
# Enable to use Microsoft ActiveSync support
|
||||
# Note that you MUST have many sogod workers to use ActiveSync.
|
||||
# See the SOGo Installation and Configuration guide for more details.
|
||||
#
|
||||
#ProxyPass /Microsoft-Server-ActiveSync \
|
||||
# http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync \
|
||||
# retry=60 connectiontimeout=5 timeout=360
|
||||
|
||||
<Proxy http://127.0.0.1:20000/SOGo>
|
||||
## adjust the following to your configuration
|
||||
RequestHeader set "x-webobjects-server-port" "443"
|
||||
@@ -66,3 +68,9 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
||||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
# For Apple autoconfiguration
|
||||
<IfModule rewrite_module>
|
||||
RewriteEngine On
|
||||
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
|
||||
</IfModule>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 433 KiB After Width: | Height: | Size: 401 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 311 KiB |
+1
-3
@@ -1,8 +1,6 @@
|
||||
/* SOGo+DAV.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2010-2013 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2005-2011 Inverse inc.
|
||||
Copyright (C) 2005-2014 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo
|
||||
|
||||
@@ -1,3 +1,265 @@
|
||||
2.2.3 (2014-04-03)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- updated Dutch, Hungarian, Russian and Spanish (Argentina) translations
|
||||
- initial support for ActiveSync event reminders support (#2681)
|
||||
- updated CKEditor to version 4.3.4
|
||||
|
||||
Bug fixes
|
||||
- fixed possible exception when retrieving the default event reminder value on 64bit architectures (#2678)
|
||||
- fixed calling unescapeHTML on null variables to avoid JavaScript exceptions in Contacts module
|
||||
- fixed detection of IMAP flags support on the client side (#2664)
|
||||
- fixed the ActiveSync issue marking all mails as read when downloading them
|
||||
- fixed ActiveSync's move operations not working for multiple selections (#2691)
|
||||
- fixed email validation regexp to allow gTLDs
|
||||
- improved all-day events support for ActiveSync (#2686)
|
||||
|
||||
2.2.2 (2014-03-21)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- updated French, Finnish, German and Spanish (Spain) translations
|
||||
- added sanitization support for Outlook/ActiveSync to circumvent Outlook bugs (#2667)
|
||||
- updated CKEditor to version 4.3.3
|
||||
- updated jQuery File Upload to version 9.5.7
|
||||
|
||||
Bug fixes
|
||||
- fixed possible exception when retrieving the default event reminder value on 64bit architectures (#2647, #2648)
|
||||
- disable file paste support in mail editor (#2641)
|
||||
- fixed copying/moving messages to a mail folder begining with a digit (#2658)
|
||||
- fixed unseen count for folders beginning with a digit and used in Sieve filters (#2652)
|
||||
- fixed decoding of HTML entities in reminder alerts (#2659)
|
||||
- fixed check for resource conflict when creating an event in the resource's calendar (#2541)
|
||||
- fixed construction of mail folders tree
|
||||
- fixed parsing of ORG attribute in cards (#2662)
|
||||
- disabled ActiveSync provisioning for now (#2663)
|
||||
- fixed messages move in Outlook which would create duplicates (#2650)
|
||||
- fixed translations for OtherUsersFolderName and SharedFoldersName folders (#2657)
|
||||
- fixed handling of accentuated characters when filtering contacts (#2656)
|
||||
- fixed classification icon of events (#2651)
|
||||
- fixed ActiveSync's SendMail with client version <= 12.1 (#2669)
|
||||
|
||||
2.2.1 (2014-03-07)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- updated Czech, Dutch, Finnish, and Hungarian translations
|
||||
- show current folder name in prompt dialog when renaming a mail folder
|
||||
|
||||
Bug fixes
|
||||
- fixed an issue with ActiveSync when the number of messages in the mailbox was greater than the window-size specified by the client
|
||||
- fixed sogo-tool operations on Sieve script (#2617)
|
||||
- fixed unsubscription when renaming an IMAP folder (#2630)
|
||||
- fixed sorting of events list by calendar name (#2629)
|
||||
- fixed wrong date format leading to Android email syncing issues (#2609)
|
||||
- fixed possible exception when retrieving the default event reminder value (#2624)
|
||||
- fixed encoding of mail folder name when creating a subfolder (#2637)
|
||||
- fixed returned date format for email messages in ActiveSync
|
||||
- fixed missing 'name part' in address for email messages in ActiveSync
|
||||
- fixed race condition when syncing huge amount of deleted messages over ActiveSync
|
||||
- fixed encoding of string as CSS identifier when the string starts with a digit
|
||||
- fixed auto-completion popupmenu when UID is a digit
|
||||
|
||||
2.2.0 (2014-02-24)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- initial implementation of Microsoft ActiveSync protocol
|
||||
- it's now possible to set a default reminder for calendar components
|
||||
using SOGoCalendarDefaultReminder
|
||||
- select multiple files to attach to a message or drag'n'drop files onto the
|
||||
mail editor; will also now display progress of uploads
|
||||
- new popup menu to download all attachments of a mail
|
||||
- move & copy messages between different accounts
|
||||
- support for the Sieve 'body' extension (mail filter based on the body content)
|
||||
|
||||
Enhancements
|
||||
- we now automatically convert <img src=data...> into file attachments
|
||||
using CIDs to prevent Outlook issues
|
||||
- updated French, Finnish, Polish, German, Russian, and Spanish (Spain) translations
|
||||
- XMLHttpRequest.js is now loaded conditionaly (< IE9)
|
||||
- format time in attendees invitation window according to the user's locale
|
||||
- improved IE11 support
|
||||
- respect signature placement when forwarding a message
|
||||
- respect Sieve server capabilities
|
||||
- encode messages in quoted-printable when content is bigger than 72 bytes
|
||||
- we now use binary encoding in memcached (#2587)
|
||||
- warn user when overbooking a resource by creating an event in its calendar (#2541)
|
||||
- converted JavaScript alerts to inline CSS dialogs in appointment editor
|
||||
- visually identify users with no freebusy information in autocompletion widget of attendees editor (#2565)
|
||||
- respect occurences of recurrent events when deleting selected events (#1950)
|
||||
- improved confirmation dialog box when deleting events and tasks
|
||||
- moved the DN cache to SOGoCache - avoiding sogod restarts after RDN operations
|
||||
- don't use the HTML editor with Internet Explorer 7
|
||||
- add message-id header to appointment notifications (#2535)
|
||||
- detect URLs in popup of events
|
||||
- improved display of a contact (#2350)
|
||||
|
||||
Bug fixes
|
||||
- don't load 'background' attribute (#2437)
|
||||
- fixed validation of subscribed folders (#2583)
|
||||
- fixed display of folder names in messages filter editor (#2569)
|
||||
- fixed contextual menu of the current calendar view (#2557)
|
||||
- fixed handling of the '=' character in cards/events/tasks (#2505)
|
||||
- simplify searches in the address book (#2187)
|
||||
- warn user when dnd failed because of a resource conflict (#1613)
|
||||
- respect the maximum number of bookings when viewing the freebusy information of a resource (#2560)
|
||||
- encode HTML entities when forwarding an HTML message inline in plain text composition mode (#2411)
|
||||
- encode HTML entities in JSON data (#2598)
|
||||
- fixed handling of ACLs on shared calendars with multiple groups (#1854)
|
||||
- fixed HTML formatting of appointment notifications for Outlook (#2233)
|
||||
- replace slashes by dashes in filenames of attachments to avoid a 404 return code (#2537)
|
||||
- avoid over-using LDAP connections when decomposing groups
|
||||
- fixed display of a contact's birthday when not defined (#2503)
|
||||
- fixed JavaScript error when switching views in calendar module (#2613)
|
||||
|
||||
2.1.1b (2013-12-04)
|
||||
-------------------
|
||||
|
||||
Enhancements
|
||||
- updated CKEditor to version 4.3.0 and added tab module
|
||||
|
||||
Bug fixes
|
||||
- HTML formatting is now retained when forwarding/replying to a mail using the HTML editor
|
||||
- put the text part before the HTML part when composing mail to fix a display issue with Thunderbird (#2512)
|
||||
|
||||
2.1.1a (2013-11-22)
|
||||
-------------------
|
||||
|
||||
Bug fixes
|
||||
- fixed Sieve filters editor (#2504)
|
||||
- moved missing translation to UI/Common (#2499)
|
||||
- fixed potential crasher in OpenChange
|
||||
|
||||
2.1.1 (2013-11-19)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- creation and modification of mail labels
|
||||
|
||||
Enhancements
|
||||
- the color picker is no longer a popup window
|
||||
|
||||
Bug fixes
|
||||
- fixed utf8 character handling in special folder names
|
||||
Special folder names can now be set as UTF8 or modified UTF7 in sogo.conf
|
||||
- fixed reply-to header not being set for auxiliary IMAP accounts
|
||||
- fixed handling of broken/invalid email addresses
|
||||
|
||||
2.1.0 (2013-11-07)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- improved order of user rights in calendar module (#1431)
|
||||
- increased height of alarm editor when email alarms are enabled
|
||||
- added SMTP AUTH support for sogo-ealarms-notify
|
||||
- added support for LDAP password change against AD/Samba4
|
||||
- added Apache configuration for Apple autoconfiguration (#2248)
|
||||
- the init scripts now start 3 sogod processes by default instead of 1
|
||||
- SOGo now also sends a plain/text parts when sending HTML mails (#2217)
|
||||
- SOGo now listens on 127.0.0.1:20000 by default (instead of *:20000)
|
||||
- SOGo new uses the latest WebDAV sync response type (#1275)
|
||||
- updated CKEditor to version 4.2.2 and added the tables-related modules (#2410)
|
||||
- improved display of vEvents in messages
|
||||
|
||||
Bug fixes
|
||||
- fixed handling of an incomplete attachment filename (#2385)
|
||||
- fixed Finnish mail reply/forward templates (#2401)
|
||||
- fixed position of red line of current time (#2373)
|
||||
- fixed crontab error (#2372)
|
||||
- avoid using too many LDAP connections while looping through LDAP results
|
||||
- don't encode HTML entities in mail subject of notification (#2402)
|
||||
- fixed crash of Samba when sending an invitation (#2398)
|
||||
- fixed selection of destination calendar when saving a task or an event (#2353)
|
||||
- fixed "display remote images" preference for message in a popup (#2417)
|
||||
- avoid crash when handling malformed or non-ASCII HTTP credentials (#2358)
|
||||
- fixed crash in DAV free-busy lookups when using SQL addressbooks (#2418)
|
||||
- disabled verbose logging of SMTP sessions by default
|
||||
- fixed high CPU usage when there are no available child processes and added logging when such a condition occurs
|
||||
- fixed memory consumption issues when doing dav lookups with huge result set
|
||||
- fixed S/MIME verification issues with certain OpenSSL versions
|
||||
- worked around an issue with chunked encoding of CAS replies (#2408)
|
||||
- fixed OpenChange corruption issue regarding predecessors change list (#2405)
|
||||
- avoid unnecessary UTF-7 conversions (#2318)
|
||||
- improved RTF parser to fix vCards (#2354)
|
||||
- fixed definition of the COMPLETED attribute of vTODO (#2240)
|
||||
- fixed DAV:resource-id property when sharing calendars (#2399)
|
||||
- fixed reload of multiple external web calendars (#2221)
|
||||
- fixed display of PDF files sent from Thunderbird (#2270)
|
||||
- fixed TLS support for IMAP (#2386)
|
||||
- fixed creation of web calendar when added using sogo-tool (#2007)
|
||||
- avoid crash when parsing HTML tags of a message (#2434)
|
||||
- fixed handling of LDAP groups with no email address (#1328)
|
||||
- fixed encoding of messages with non-ASCII characters (#2459)
|
||||
- fixed compilation with clang 3.2 (#2235)
|
||||
- truncated long fields of quick records to avoid an SQL error (#2461)
|
||||
- fixed IMAP ACLs (#2433)
|
||||
- removed inline JavaScript when viewing HTML messages (#2468)
|
||||
|
||||
2.0.7 (2013-07-19)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- print gridlines of calendar in 15-minute intervals
|
||||
- allow the events/tasks lists to be collapsable
|
||||
|
||||
Enhancements
|
||||
- bubble box of events no longer overlaps the current event
|
||||
- now pass the x-originating-ip using the IMAP ID extension (#2366)
|
||||
- updated BrazilianPortuguese, Czech, Dutch, German, Polish and Russian translations
|
||||
|
||||
Bug fixes
|
||||
- properly handle RFC2231 everywhere
|
||||
- fixed minor XSS issues
|
||||
- fixed jquery-ui not bluring the active element when clicking on a draggable
|
||||
|
||||
2.0.6b (2013-06-27)
|
||||
------------------
|
||||
|
||||
Bug fixes
|
||||
- properly escape the foldername to avoid XSS issues
|
||||
- fixed loading of MSExchangeFreeBusySOAPResponseMap
|
||||
|
||||
2.0.6a (2013-06-25)
|
||||
------------------
|
||||
|
||||
Bug fixes
|
||||
- documentation fixes
|
||||
- added missing file for CAS single logout
|
||||
|
||||
2.0.6 (2013-06-21)
|
||||
------------------
|
||||
|
||||
Enhancements
|
||||
- updated CKEditor to version 4.1.1 (#2333)
|
||||
- new failed login attemps rate-limiting options. See the new
|
||||
SOGoMaximumFailedLoginCount, SOGoMaximumFailedLoginInterval and
|
||||
SOGoFailedLoginBlockInterval defaults
|
||||
- new message submissions rate-limiting options. See the new
|
||||
SOGoMaximumMessageSubmissionCount, SOGoMaximumRecipientCount,
|
||||
SOGoMaximumSubmissionInterval and SOGoMessageSubmissionBlockInterval defaults
|
||||
- now possible to send or not event notifications on a per-event basis
|
||||
- now possible to see who created an event/task in a delegated calendar
|
||||
- multi-domain support in OpenChange (implemented using a trick)
|
||||
|
||||
Bug fixes
|
||||
- fixed decoding of the charset parameter when using single quotes (#2306)
|
||||
- fixed potential crash when sending MDN from Sent folder (#2209)
|
||||
- fixed handling of unicode separators (#2309)
|
||||
- fixed public access when SOGoTrustProxyAuthentication is used (#2237)
|
||||
- fixed access right issues with import feature (#2294)
|
||||
- fixed IMAP ACL issue when SOGoForceExternalLoginWithEmail is used (#2313)
|
||||
- fixed handling of CAS logoutRequest (#2346)
|
||||
- fixed many major OpenChange stability issues
|
||||
|
||||
2.0.5a (2013-04-17)
|
||||
------------------
|
||||
|
||||
Bug fixes
|
||||
- fixed an issue when parsing user CN with leading or trailing spaces (#2287)
|
||||
- fixed a crash that occured when saving contacts or tasks via Outlook
|
||||
|
||||
2.0.5 (2013-04-11)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_participants;
|
||||
sqlType = "VARCHAR(1000000)";
|
||||
sqlType = "TEXT";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
@@ -91,7 +91,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_cycleinfo;
|
||||
sqlType = "VARCHAR(1000000)";
|
||||
sqlType = "TEXT";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
@@ -126,7 +126,7 @@
|
||||
},
|
||||
{
|
||||
columnName = c_partmails;
|
||||
sqlType = "VARCHAR(100000)";
|
||||
sqlType = "TEXT";
|
||||
allowsNull = YES;
|
||||
},
|
||||
{
|
||||
|
||||
@@ -85,13 +85,17 @@ static NSDictionary *BSONTypes()
|
||||
@implementation NSObject (BSONObjectCoding)
|
||||
- (NSData *) BSONEncode
|
||||
{
|
||||
if (!class_conformsToProtocol([self class], @protocol(BSONObjectCoding)))
|
||||
if (![self conformsToProtocol: @protocol(BSONObjectCoding)])
|
||||
[NSException raise: NSInvalidArgumentException format: @"BSON encoding is only valid on objects conforming to the BSONObjectEncoding protocol."];
|
||||
|
||||
id <BSONObjectCoding> myself = (id <BSONObjectCoding>) self;
|
||||
NSMutableDictionary *values = [[myself BSONDictionary] mutableCopy];
|
||||
|
||||
#if (defined(__GNU_LIBOBJC__) && (__GNU_LIBOBJC__ >= 20100911)) || defined(APPLE_RUNTIME) || defined(__GNUSTEP_RUNTIME__)
|
||||
const char* className = class_getName([self class]);
|
||||
#else
|
||||
const char* className = [self class]->name;
|
||||
#endif
|
||||
[values setObject: [NSData dataWithBytes: (void *)className length: strlen(className)] forKey: CLASS_NAME_MARKER];
|
||||
NSData *retval = [values BSONEncode];
|
||||
[values release];
|
||||
|
||||
@@ -911,7 +911,7 @@ fromProperties: (NSDictionary *) attachmentProps
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) save
|
||||
- (void) save:(TALLOC_CTX *) memCtx
|
||||
{
|
||||
NSArray *elements, *units;
|
||||
CardElement *element;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
@@ -34,6 +35,7 @@
|
||||
#import "MAPIStoreDBMessage.h"
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "NSObject+MAPIStore.h"
|
||||
#import "NSString+MAPIStore.h"
|
||||
|
||||
#undef DEBUG
|
||||
#include <mapistore/mapistore.h>
|
||||
@@ -99,6 +101,21 @@
|
||||
return objectVersion;
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: how this can happen?
|
||||
//
|
||||
// We might get there if for some reasons, all classes weren't able
|
||||
// to tell us the message class.
|
||||
//
|
||||
- (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx
|
||||
{
|
||||
*data = [@"IPM.Note" asUnicodeInMemCtx: memCtx];
|
||||
|
||||
[self logWithFormat: @"METHOD '%s' - unable to determine message class. Falling back to IPM.Note", __FUNCTION__];
|
||||
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
- (int) getProperties: (struct mapistore_property_data *) data
|
||||
withTags: (enum MAPITAGS *) tags
|
||||
andCount: (uint16_t) columnCount
|
||||
|
||||
@@ -122,6 +122,8 @@
|
||||
[MAPIApp setUserContext: userContext];
|
||||
moduleName = [self MAPIModuleName];
|
||||
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
|
||||
nameInContainer = nil;
|
||||
|
||||
if (![parentFolder newFolderWithName: folderName
|
||||
nameInContainer: &nameInContainer])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/",
|
||||
|
||||
@@ -259,7 +259,6 @@ static Class NSNumberK;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
- (void) _setChangeKey: (NSData *) changeKey
|
||||
forMessageEntry: (NSMutableDictionary *) messageEntry
|
||||
inChangeListOnly: (BOOL) inChangeListOnly
|
||||
@@ -461,7 +460,10 @@ static Class NSNumberK;
|
||||
[mapping setObject: cLastModified forKey: changeNumber];
|
||||
[messageEntry setObject: changeNumber forKey: @"version"];
|
||||
|
||||
newChangeNum = [changeNumber unsignedLongValue];
|
||||
newChangeNum = [changeNumber unsignedLongLongValue];
|
||||
|
||||
// A GLOBCNT structure is a 6-byte global namespace counter,
|
||||
// we strip the first 2 bytes. The first two bytes is the ReplicaId
|
||||
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
|
||||
[self _setChangeKey: changeKey forMessageEntry: messageEntry
|
||||
inChangeListOnly: NO];
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
@@ -156,7 +157,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
stringData = [NSString stringWithFormat: @"%@%@",
|
||||
urlBase, [currentName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
context->url = [stringData asUnicodeInMemCtx: context];
|
||||
stringData = [[currentName substringFromIndex: 6] fromCSSIdentifier];
|
||||
stringData = [[[currentName substringFromIndex: 6] fromCSSIdentifier] stringByDecodingImap4FolderName];
|
||||
context->name = [stringData asUnicodeInMemCtx: context];
|
||||
context->main_folder = false;
|
||||
context->role = MAPISTORE_MAIL_ROLE;
|
||||
@@ -188,7 +189,7 @@ MakeDisplayFolderName (NSString *folderName)
|
||||
if ([newFolder create])
|
||||
mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/",
|
||||
userName, userName,
|
||||
[folderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
|
||||
else
|
||||
mapistoreURI = nil;
|
||||
[MAPIApp setUserContext: nil];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MAPIStoreMailFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2011-2012 Inverse inc
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2011-2013 Inverse inc
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -36,6 +34,7 @@
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <NGImap4/NGImap4Client.h>
|
||||
#import <NGImap4/NSString+Imap4.h>
|
||||
#import <Mailer/SOGoDraftsFolder.h>
|
||||
#import <Mailer/SOGoMailAccount.h>
|
||||
#import <Mailer/SOGoMailAccounts.h>
|
||||
@@ -177,7 +176,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
if (folderName)
|
||||
{
|
||||
nameInContainer = [NSString stringWithFormat: @"folder%@",
|
||||
[folderName asCSSIdentifier]];
|
||||
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
|
||||
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
|
||||
inContainer: sogoObject];
|
||||
if ([newFolder create])
|
||||
@@ -315,7 +314,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
||||
|
||||
if (![subfolderName hasPrefix: @"folder"])
|
||||
abort ();
|
||||
strippedName = [subfolderName substringFromIndex: 6];
|
||||
strippedName = [[subfolderName substringFromIndex: 6] stringByDecodingImap4FolderName];
|
||||
[representation appendFormat: @"/%@", strippedName];
|
||||
|
||||
return representation;
|
||||
|
||||
@@ -693,7 +693,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
|
||||
ngAddress = [[NGMailAddressParser mailAddressParserWithString: fullMail]
|
||||
parse];
|
||||
if ([ngAddress isKindOfClass: [NGMailAddress class]])
|
||||
cn = [ngAddress displayName];
|
||||
{
|
||||
cn = [ngAddress displayName];
|
||||
|
||||
// If we don't have a displayName, we use the email address instead. This
|
||||
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
|
||||
// nor in the recipient field if we reply to the email.
|
||||
if (![cn length])
|
||||
cn = [ngAddress address];
|
||||
}
|
||||
else
|
||||
cn = @"";
|
||||
|
||||
|
||||
@@ -543,6 +543,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
NSDictionary *recipients;
|
||||
NSUInteger type, bccLimit;
|
||||
SOGoUser *activeUser;
|
||||
NSNumber *priority;
|
||||
|
||||
activeUser
|
||||
= [SOGoUser
|
||||
@@ -596,6 +597,21 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
|
||||
[headers addObject: [date rfc822DateString] forKey: @"date"];
|
||||
}
|
||||
[headers addObject: @"1.0" forKey: @"MIME-Version"];
|
||||
|
||||
priority = [mailProperties objectForKey: MAPIPropertyKey (PidTagImportance)];
|
||||
|
||||
if ([priority intValue] == 2)
|
||||
{
|
||||
[headers addObject: @"1 (Highest)" forKey: @"X-Priority"];
|
||||
}
|
||||
else if ([priority intValue] == 1)
|
||||
{
|
||||
[headers removeAllObjectsForKey: @"X-Priority"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[headers addObject: @"5 (Lowest)" forKey: @"X-Priority"];
|
||||
}
|
||||
}
|
||||
|
||||
static NSArray *
|
||||
|
||||
@@ -284,6 +284,9 @@ static Class NSExceptionK, MAPIStoreFolderK;
|
||||
return MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// The GlobCnt is 6 bytes long.
|
||||
//
|
||||
- (NSData *) getReplicaKeyFromGlobCnt: (uint64_t) objectCnt
|
||||
{
|
||||
struct mapistore_connection_info *connInfo;
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (void) save
|
||||
- (void) save:(TALLOC_CTX *) memCtx
|
||||
{
|
||||
iCalCalendar *vCalendar;
|
||||
iCalToDo *vToDo;
|
||||
|
||||
@@ -148,6 +148,7 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
|
||||
- (struct XID *) asXIDInMemCtx: (void *) memCtx
|
||||
{
|
||||
struct XID *xid;
|
||||
uint8_t *bytes;
|
||||
NSUInteger max;
|
||||
|
||||
max = [self length];
|
||||
@@ -158,7 +159,9 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
|
||||
[self _extractGUID: &xid->GUID];
|
||||
|
||||
xid->Size = max - 16;
|
||||
xid->Data = talloc_memdup (xid, [self bytes] + 16, xid->Size);
|
||||
|
||||
bytes = (uint8_t *) [self bytes];
|
||||
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+17
-1
@@ -694,10 +694,18 @@ const unsigned short ansicpg874[256] = {
|
||||
}
|
||||
else if (*(_bytes+1) == '*')
|
||||
{
|
||||
while (*_bytes != '}')
|
||||
int cc = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (*_bytes == '{')
|
||||
cc++;
|
||||
if (*_bytes == '}')
|
||||
cc--;
|
||||
|
||||
ADVANCE;
|
||||
}
|
||||
while (cc != 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -777,6 +785,10 @@ const unsigned short ansicpg874[256] = {
|
||||
[_html appendBytes: v length: strlen(v)];
|
||||
free(v);
|
||||
}
|
||||
else if ([s hasPrefix: @"fcs"])
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
else if ([s hasPrefix: @"fs"])
|
||||
{
|
||||
// ignore
|
||||
@@ -789,6 +801,10 @@ const unsigned short ansicpg874[256] = {
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
else if ([s hasPrefix: @"fromtext"])
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
else if ([s hasPrefix: @"f"] && [s length] > 1)
|
||||
{
|
||||
RTFFontInfo *fontInfo;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#import "EOQualifier+GCS.h"
|
||||
|
||||
#if __GNU_LIBOBJC__ >= 20100911
|
||||
#if (defined(__GNU_LIBOBJC__) && (__GNU_LIBOBJC__ >= 20100911)) || defined(APPLE_RUNTIME) || defined(__GNUSTEP_RUNTIME__)
|
||||
# define sel_eq(__A__,__B__) sel_isEqual(__A__,__B__)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#import "EOQualifier+GCS.h"
|
||||
#import "GCSStringFormatter.h"
|
||||
|
||||
#if __GNU_LIBOBJC__ >= 20100911
|
||||
#if (defined(__GNU_LIBOBJC__) && (__GNU_LIBOBJC__ >= 20100911)) || defined(APPLE_RUNTIME) || defined(__GNUSTEP_RUNTIME__)
|
||||
# define sel_eq(__A__,__B__) sel_isEqual(__A__,__B__)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
@"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
|
||||
#endif
|
||||
|
||||
static NSArray *privilegedTagNames = nil;
|
||||
|
||||
@implementation NGCardsSaxHandler
|
||||
|
||||
- (id) init
|
||||
@@ -42,6 +44,12 @@
|
||||
if ((self = [super init]))
|
||||
topGroupClass = nil;
|
||||
|
||||
if (!privilegedTagNames)
|
||||
{
|
||||
privilegedTagNames = [NSArray arrayWithObjects: @"ADR", @"N", @"RRULE", @"ORG", nil];
|
||||
RETAIN(privilegedTagNames);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -210,8 +218,12 @@
|
||||
length: contentLength];
|
||||
free (content);
|
||||
content = NULL;
|
||||
// NSLog (@"content: '%@'", s);
|
||||
contentValues = [s vCardSubvalues];
|
||||
//NSLog(@"content: '%@'", s);
|
||||
if ([privilegedTagNames containsObject: [currentElement tag]])
|
||||
contentValues = [s vCardSubvalues];
|
||||
else
|
||||
contentValues = [NSMutableDictionary dictionaryWithObject: [NSMutableArray arrayWithObject: [s asCardAttributeValues]]
|
||||
forKey: @""];
|
||||
}
|
||||
else
|
||||
contentValues = nil;
|
||||
|
||||
@@ -155,20 +155,41 @@
|
||||
|
||||
- (NSString *) escapedForCards
|
||||
{
|
||||
NSString *string;
|
||||
NSMutableString *string;
|
||||
unsigned int len, i;
|
||||
unichar c;
|
||||
|
||||
string = [self stringByReplacingString: @"\\"
|
||||
withString: @"\\\\"];
|
||||
string = [string stringByReplacingString: @","
|
||||
withString: @"\\,"];
|
||||
// string = [string stringByReplacingString: @":"
|
||||
// withString: @"\\:"];
|
||||
string = [string stringByReplacingString: @";"
|
||||
withString: @"\\;"];
|
||||
string = [string stringByReplacingString: @"\n"
|
||||
withString: @"\\n"];
|
||||
string = [string stringByReplacingString: @"\r"
|
||||
withString: @"\\r"];
|
||||
len = [self length];
|
||||
string = [NSMutableString stringWithCapacity: len * 1.5];
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
c = [self characterAtIndex: i];
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\\':
|
||||
[string appendString: @"\\\\"];
|
||||
break;
|
||||
case ',':
|
||||
[string appendString: @"\\,"];
|
||||
break;
|
||||
//case ':':
|
||||
// [string appendString: @"\\:"];
|
||||
//break;
|
||||
case ';':
|
||||
[string appendString: @"\\;"];
|
||||
break;
|
||||
case'\n':
|
||||
[string appendString: @"\\n"];
|
||||
break;
|
||||
case'\r':
|
||||
[string appendString: @"\\r"];
|
||||
break;
|
||||
default:
|
||||
[string appendFormat: @"%C", c];
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
- (void) setCompleted: (NSCalendarDate *) newCompletedDate
|
||||
{
|
||||
[(iCalDateTime *) [self uniqueChildWithTag: @"completed"]
|
||||
setDate: newCompletedDate];
|
||||
setDateTime: newCompletedDate];
|
||||
if (newCompletedDate)
|
||||
[self setStatus: @"COMPLETED"];
|
||||
else
|
||||
|
||||
@@ -89,6 +89,16 @@ def main():
|
||||
except Exception as e:
|
||||
print "Error during sqlCleanup, continuing: %s" % str(e)
|
||||
|
||||
def getsep(client):
|
||||
seq = None
|
||||
(code, data) = client.list("", "")
|
||||
if code == "OK" and data is not None:
|
||||
# yes this is ugly but it works on cyrus and dovecot.
|
||||
# (\\Noinferiors \\HasNoChildren) "/" INBOX
|
||||
m = re.search(".*\s+[\"\']?(.)[\"\']?\s+[\"\']?.*[\"\']?$", data[0])
|
||||
sep = m.group(1)
|
||||
return sep
|
||||
|
||||
def extractmb(si):
|
||||
inparen = False
|
||||
inquote = False
|
||||
@@ -120,13 +130,13 @@ def extractmb(si):
|
||||
|
||||
return parts[-1]
|
||||
|
||||
def cleanupmb(mb, client):
|
||||
(code, data) = client.list("%s/" % mb, "%")
|
||||
def cleanupmb(mb, sep, client):
|
||||
(code, data) = client.list("%s%s" % (mb, sep), "%")
|
||||
if code == "OK":
|
||||
for si in data:
|
||||
if si is not None:
|
||||
submb = extractmb(si)
|
||||
cleanupmb(submb, client)
|
||||
cleanupmb(submb, sep, client)
|
||||
else:
|
||||
raise Exception, "Failure while cleaning up '%s'" % mb
|
||||
client.unsubscribe(mb)
|
||||
@@ -145,13 +155,20 @@ def imapCleanup(imaphost, imapport, username, userpass):
|
||||
|
||||
print "Logged in as '%s'" % username
|
||||
|
||||
for foldername in ("Sync Issues", "Junk E-mail"):
|
||||
sep = getsep(client)
|
||||
if not sep:
|
||||
client.logout()
|
||||
return
|
||||
|
||||
for foldername in ("Sync Issues", "Junk E-mail",
|
||||
"INBOX%sSync Issues" % sep, "INBOX%sJunk E-mail" % sep,
|
||||
"Probl&AOg-mes de synchronisation"):
|
||||
(code, data) = client.list(foldername, "%")
|
||||
if code == "OK":
|
||||
for si in data:
|
||||
if si is not None:
|
||||
mb = extractmb(si)
|
||||
cleanupmb(mb, client)
|
||||
cleanupmb(mb, sep, client)
|
||||
client.logout()
|
||||
|
||||
def mapistoreCleanup(mapistorefolder, username):
|
||||
@@ -4,7 +4,7 @@ set -o pipefail
|
||||
#set -x
|
||||
PROGNAME="$(basename $0)"
|
||||
|
||||
BACKUP_DIR=/home/sogo/backups
|
||||
BACKUP_DIR=~sogo/backups
|
||||
SOGO_TOOL=/usr/sbin/sogo-tool
|
||||
DAYS_TO_KEEP="30"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# The amount of processes that should be spawned (Default: 1)
|
||||
# PREFORK=1
|
||||
# The amount of processes that should be spawned (Default: 3)
|
||||
# PREFORK=3
|
||||
|
||||
# The name of the account under which SOGo will be running (Default: sogo)
|
||||
# USER=sogo
|
||||
@@ -34,7 +34,7 @@ DAEMON=/usr/sbin/sogod
|
||||
DESC="SOGo"
|
||||
|
||||
USER=$NAME
|
||||
PREFORK=1
|
||||
PREFORK=3
|
||||
|
||||
PIDFILE=/var/run/$NAME/$NAME.pid
|
||||
LOGFILE=/var/log/$NAME/$NAME.log
|
||||
|
||||
@@ -37,7 +37,7 @@ DAEMON=/usr/sbin/sogod
|
||||
DESC="SOGo"
|
||||
|
||||
USER=$NAME
|
||||
PREFORK=1
|
||||
PREFORK=3
|
||||
|
||||
PIDFILE=/var/run/$NAME/$NAME.pid
|
||||
LOGFILE=/var/log/$NAME/$NAME.log
|
||||
|
||||
+32
-4
@@ -33,8 +33,10 @@
|
||||
//SOGoMailSpoolPath = /var/spool/sogo;
|
||||
//NGImap4ConnectionStringSeparator = "/";
|
||||
|
||||
/* Notifications */
|
||||
//SOGoAppointmentSendEMailNotifications = NO;
|
||||
//SOGoACLsSendEMailNotifications = NO;
|
||||
//SOGoFoldersSendEMailNotifications = NO;
|
||||
|
||||
/* Authentication */
|
||||
//SOGoPasswordChangeEnabled = YES;
|
||||
@@ -44,8 +46,9 @@
|
||||
// {
|
||||
// type = ldap;
|
||||
// CNFieldName = cn;
|
||||
// IDFieldName = uid;
|
||||
// UIDFieldName = uid;
|
||||
// IDFieldName = uid; // first field of the DN for direct binds
|
||||
// bindFields = (uid, mail); // array of fields to use for indirect binds
|
||||
// baseDN = "ou=users,dc=acme,dc=com";
|
||||
// bindDN = "uid=sogo,ou=users,dc=acme,dc=com";
|
||||
// bindPassword = qwerty;
|
||||
@@ -57,13 +60,33 @@
|
||||
// }
|
||||
//);
|
||||
|
||||
/* LDAP AD/Samba4 example */
|
||||
//SOGoUserSources = (
|
||||
// {
|
||||
// type = ldap;
|
||||
// CNFieldName = cn;
|
||||
// UIDFieldName = sAMAccountName;
|
||||
// baseDN = "CN=users,dc=domain,dc=tld";
|
||||
// bindDN = "CN=sogo,CN=users,DC=domain,DC=tld";
|
||||
// bindFields = (sAMAccountName, mail);
|
||||
// bindPassword = password;
|
||||
// canAuthenticate = YES;
|
||||
// displayName = "Public";
|
||||
// hostname = ldap://127.0.0.1:389;
|
||||
// filter = "mail = '*'";
|
||||
// id = directory;
|
||||
// isAddressBook = YES;
|
||||
// }
|
||||
//);
|
||||
|
||||
|
||||
/* SQL authentication example */
|
||||
/* These database columns MUST be present in the view/table:
|
||||
* c_uid - will be used for authentication - it's the username or username@domain.tld)
|
||||
* c_name - which can be identical to c_uid - will be used to uniquely identify entries
|
||||
* c_password - password of the user, plain-text, md5 or sha encoded for now
|
||||
* c_cn - the user's common name - such as "John Doe"
|
||||
* mail - the user's mail address
|
||||
* mail - the user's mail address
|
||||
* See the installation guide for more details
|
||||
*/
|
||||
//SOGoUserSources =
|
||||
@@ -83,6 +106,8 @@
|
||||
//SOGoVacationEnabled = YES;
|
||||
//SOGoForwardEnabled = YES;
|
||||
//SOGoSieveScriptsEnabled = YES;
|
||||
//SOGoMailAuxiliaryUserAccountsEnabled = YES;
|
||||
//SOGoTrustProxyAuthentication = NO;
|
||||
|
||||
/* General */
|
||||
//SOGoLanguage = English;
|
||||
@@ -91,13 +116,16 @@
|
||||
// PublicDAndTViewer,
|
||||
// ConfidentialDAndTViewer
|
||||
//);
|
||||
//SOGoSuperUsernames = (sogo1, sogo2); //This is an array - keep the parens!
|
||||
//SOGoSuperUsernames = (sogo1, sogo2); // This is an array - keep the parens!
|
||||
//SxVMemLimit = 384;
|
||||
//WOPidFile = "/var/run/sogo/sogo.pid";
|
||||
//SOGoMemcachedHost = "/var/run/memcached.sock";
|
||||
|
||||
/* Debug */
|
||||
//SOGoDebugRequests = YES;
|
||||
//SoDebugBaseURL = YES;
|
||||
//ImapDebugEnabled = YES;
|
||||
//LDAPDebugEnabled = YES;
|
||||
//SOGoDebugRequests = YES;
|
||||
//PGDebugEnabled = YES;
|
||||
//MySQL4DebugEnabled = YES;
|
||||
//SOGoUIxDebugEnabled = YES;
|
||||
|
||||
+3
-1
@@ -10,10 +10,12 @@
|
||||
#* * * * * sogo /usr/sbin/sogo-tool expire-sessions 60
|
||||
|
||||
# Email alarms - runs every minutes
|
||||
# If you need to use SMTP AUTH for outgoing mails, specify credentials to use
|
||||
# with '-p /path/to/credentialsFile' (same format as the sieve credentials)
|
||||
#* * * * * sogo /usr/sbin/sogo-ealarms-notify
|
||||
|
||||
# Daily backups
|
||||
# - writes to /home/sogo/backups/ by default
|
||||
# - writes to ~sogo/backups/ by default
|
||||
# - will keep 31 days worth of backups by default
|
||||
# - runs once a day by default, but can run more frequently
|
||||
# - make sure to set the path to sogo-backup.sh correctly
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SOGOSPOOL must match the value of the configuration parameter SOGoMailSpoolPath
|
||||
SOGOSPOOL=/var/spool/sogo
|
||||
|
||||
/usr/sbin/tmpwatch 24 "$SOGOSPOOL"
|
||||
find "$SOGOSPOOL" -depth -mindepth 1 -type d -empty -exec /bin/rmdir {} \; 2> /dev/null
|
||||
find "$SOGOSPOOL" -mindepth 1 -type d -user sogo -empty -delete > /dev/null
|
||||
+8
-8
@@ -28,22 +28,22 @@ $plugins
|
||||
= array(
|
||||
"sogo-connector@inverse.ca"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "17.0.3",
|
||||
"filename" => "sogo-connector-17.0.3.xpi" ),
|
||||
"version" => "24.0.4",
|
||||
"filename" => "sogo-connector-24.0.4.xpi" ),
|
||||
"sogo-integrator@inverse.ca"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "17.0.3",
|
||||
"filename" => "sogo-integrator-17.0.3.xpi" ),
|
||||
"version" => "24.0.4",
|
||||
"filename" => "sogo-integrator-24.0.4.xpi" ),
|
||||
"{e2fda1a4-762b-4020-b5ad-a41df1933103}"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "1.9",
|
||||
"filename" => "lightning-1.9.xpi" )
|
||||
"version" => "2.6.4",
|
||||
"filename" => "lightning-2.6.4.xpi" )
|
||||
);
|
||||
|
||||
$applications
|
||||
= array( "thunderbird" => "<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
|
||||
<em:minVersion>17.0</em:minVersion>
|
||||
<em:maxVersion>17.*</em:maxVersion>" );
|
||||
<em:minVersion>24.0</em:minVersion>
|
||||
<em:maxVersion>24.*</em:maxVersion>" );
|
||||
|
||||
$pluginname = $_GET["plugin"];
|
||||
$plugin =& $plugins[$pluginname];
|
||||
|
||||
@@ -50,7 +50,7 @@ Appointments_OBJC_FILES = \
|
||||
|
||||
Appointments_RESOURCE_FILES += \
|
||||
product.plist \
|
||||
\
|
||||
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
|
||||
|
||||
@@ -27,13 +27,13 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Termineinladung: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(gesendet von %{SentBy}) ";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} Hat Sie eingeladen zu %{Summary}.\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} Hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate} um %{StartTime}\nEnde: %{EndDate} um %{EndTime}\nBeschreibung: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Termin abgesagt: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} hat den folgenden Termin abgesagt: %{Summary}.\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
= "%{Organizer} %{SentByText} hat den folgenden Termin abgesagt: \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} hat diesen Termin abgesagt: \"%{Summary}\".\n\nBeginn: %{StartDate} um %{StartTime}\nEnde: %{EndDate} um %{EndTime}\nBeschreibung: %{Description}";
|
||||
|
||||
@@ -45,7 +45,7 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Folgendes wurde am Termin \"%{Summary}\" geändert:";
|
||||
"Please accept or decline those changes."
|
||||
= "Bitte akzeptieren Sie diese Änderung oder lehnen Sie ab.";
|
||||
= "Bitte akzeptieren Sie diese Änderung oder lehnen Sie diese ab.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Einladung zugestimmt: \"%{Summary}\"";
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* MSExchangeFreeBusy.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012 Inverse inc.
|
||||
*
|
||||
* Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2012-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,6 +18,7 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSBundle.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
@@ -97,8 +96,10 @@ size_t curl_body_function_freebusy(void *ptr, size_t size, size_t nmemb, void *i
|
||||
MSExchangeFreeBusySOAPRequest *soapRequest;
|
||||
MSExchangeFreeBusyResponse *freeBusyResponse;
|
||||
NSString *rawRequest, *url, *body, *hostname, *httpauth, *authname, *password;
|
||||
NSString *mapFile;
|
||||
NSArray *infos = nil;
|
||||
NSDictionary *root;
|
||||
NSBundle *bundle;
|
||||
|
||||
CURL *curl;
|
||||
struct curl_slist *headerlist=NULL;
|
||||
@@ -161,7 +162,13 @@ size_t curl_body_function_freebusy(void *ptr, size_t size, size_t nmemb, void *i
|
||||
}
|
||||
if (sax == nil && parser != nil)
|
||||
{
|
||||
sax = [[SaxObjectDecoder alloc] initWithMappingAtPath:@"./MSExchangeFreeBusySOAPResponseMap.plist"];
|
||||
bundle = [NSBundle bundleForClass: [self class]];
|
||||
mapFile = [bundle pathForResource: @"MSExchangeFreeBusySOAPResponseMap" ofType: @"plist"];
|
||||
if (![mapFile length])
|
||||
{
|
||||
[self errorWithFormat: @"mapFile not found (MSExchangeFreeBusySOAPResponseMap.plist)"];
|
||||
}
|
||||
sax = [[SaxObjectDecoder alloc] initWithMappingAtPath: mapFile];
|
||||
[parser setContentHandler:sax];
|
||||
//[parser setErrorHandler:sax];
|
||||
}
|
||||
|
||||
@@ -8,23 +8,15 @@ vtodo_class1 = "(Privat oppgave)";
|
||||
vtodo_class2 = "(Konfidensiell oppgave)";
|
||||
|
||||
/* Receipts */
|
||||
"Title:" = "Tittel:";
|
||||
"Start:" = "Start:";
|
||||
"End:" = "Slutt:";
|
||||
|
||||
"Receipt: users invited to a meeting" = "Kvittering: brukere invitert til et møte";
|
||||
"You have invited the following attendees(s):" = "Du har invitert følende deltagere:";
|
||||
"... to attend the following event:" = "... til å delta i følgende hendelse:";
|
||||
|
||||
"Receipt: invitation updated" = "Kvittering: invitasjon oppdatert";
|
||||
"The following attendees(s):" = "Følgende deltager(e):";
|
||||
"... have been notified of the changes to the following event:" = "... har blitt informert om endringene i følgende hendelse:";
|
||||
|
||||
"Receipt: attendees removed from an event" = "Kvittering: deltagere fjernet fra en hendelse";
|
||||
"You have removed the following attendees(s):" = "Du har tatt bort følgende deltagere:";
|
||||
"... from the following event:" = "... fra følgende hendelse:";
|
||||
"The event \"%{Summary}\" was created" = "Hendelsen \"%{Summary}\" ble opprettet";
|
||||
"The event \"%{Summary}\" was deleted" = "Hendelsen \"%{Summary}\" ble slettet";
|
||||
"The event \"%{Summary}\" was updated" = "Hendelsen \"%{Summary}\" ble oppdatert";
|
||||
"The following attendees(s) were notified:" = "Følgende deltaker(e) ble varslet:";
|
||||
"The following attendees(s) were added:" = "Følgende deltaker(e) ble lagt til:";
|
||||
"The following attendees(s) were removed:" = "Følgende deltaker(e) ble fjernet:";
|
||||
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "Kalender:";
|
||||
"startDate_label" = "Start:";
|
||||
"endDate_label" = "Slutt:";
|
||||
"due_label" = "Forfallsdato:";
|
||||
@@ -35,27 +27,31 @@ vtodo_class2 = "(Konfidensiell oppgave)";
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "Hendelseinvitasjon: \"%{Summary}\"";
|
||||
"(sent by %{SentBy}) " = "(sendt av %{SentBy})";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} har invitert deg til %{Summary}.\n\nStart: %{StartDate} %{StartTime}\nSlutt: %{EndDate} %{EndTime}\nBeskrivelse: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} har invitert deg til %{Summary}.⏎ ⏎ Start: %{StartDate}⏎ Slutt: %{EndDate}⏎ Beskrivelse: %{Description}";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} har invitert deg til %{Summary}.\n\nStart: %{StartDate} klokken %{StartTime}\nSlutt: %{EndDate} klokken %{EndTime}\nBeskrivelse: %{Description}";
|
||||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Hendelse avlyst: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "";
|
||||
= "%{Organizer} %{SentByText} har avlyst hendelsen: %{Summary}.⏎ ⏎ Start: %{StartDate}⏎ Slutt: %{EndDate}⏎ Beskrivelse: %{Description}";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} har avlyst : %{Summary}.\n\nStart: %{StartDate} %{StartTime}\nEnd: %{EndDate} %{EndTime}\nBeskrivelse: %{Description}";
|
||||
= "%{Organizer} %{SentByText} har avlyst : %{Summary}.\n\nStart: %{StartDate} klokken %{StartTime}\nEnd: %{EndDate} klokken %{EndTime}\nBeskrivelse: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "";
|
||||
= "Avtalen \"%{Summary}\", %{OldStartDate} er endret";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "Avtalen \"%{Summary}\" for %{OldStartDate} klokken %{OldStartTime} er endret.";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "Følgende parametre er endret for \"%{Summary}\" møtet:";
|
||||
= "Følgende parametre er endret for \"%{Summary}\"-møtet:";
|
||||
"Please accept or decline those changes."
|
||||
= "Vennligst godta eller avvis endringene.";
|
||||
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "Aksepterte invitasjonen: \"%{Summary}\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "Avviste invitasjonen: \"%{Summary}\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "Delegerte invitasjonen: \"%{Summary}\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "Fortsatt ikke svart på invitasjonen: \"%{Summary}";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "%{Attendee} %{SentByText} har godtatt invitasjonen.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
@@ -63,7 +59,8 @@ vtodo_class2 = "(Konfidensiell oppgave)";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "%{Attendee} %{SentByText} har delegert invitasjonen til %{Delegate}.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "%{Attendee} %{SentByText} had ends ikke var på invitasjonen.";
|
||||
= "%{Attendee} %{SentByText} har ikke svart på invitasjonen.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maksimalt antall samtidige bookinger (%{NumberOfSimultaneousBookings}) er nåd for ressurs \"%{Cn} %{SystemEmail}\".";
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Kan ikke få tilgang til ressurs: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maksimum antall samtidige reservasjoner (%{NumberOfSimultaneousBookings}) er nådd for ressursen \"%{Cn} %{SystemEmail}\". Kolliderende hendelse er \"%{EventTitle}\", som starter %{StartDate}.";
|
||||
@@ -573,17 +573,27 @@ static iCalEvent *iCalEventK = nil;
|
||||
}
|
||||
grantedCount = [grantedClasses count];
|
||||
if (grantedCount == 3)
|
||||
filter = @"";
|
||||
{
|
||||
// User have access to all three classifications
|
||||
filter = @"";
|
||||
}
|
||||
else if (grantedCount == 2)
|
||||
filter
|
||||
= [NSString stringWithFormat: @"c_classification != %@",
|
||||
[deniedClasses objectAtIndex: 0]];
|
||||
{
|
||||
// User has access to all but one of the classifications
|
||||
filter = [NSString stringWithFormat: @"c_classification != %@",
|
||||
[deniedClasses objectAtIndex: 0]];
|
||||
}
|
||||
else if (grantedCount == 1)
|
||||
filter
|
||||
= [NSString stringWithFormat: @"c_classification = %@",
|
||||
[grantedClasses objectAtIndex: 0]];
|
||||
{
|
||||
// User has access to only one classification
|
||||
filter = [NSString stringWithFormat: @"c_classification = %@",
|
||||
[grantedClasses objectAtIndex: 0]];
|
||||
}
|
||||
else
|
||||
filter = nil;
|
||||
{
|
||||
// User has access to no classification
|
||||
filter = nil;
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
@@ -676,7 +686,6 @@ static iCalEvent *iCalEventK = nil;
|
||||
qualifier = nil;
|
||||
|
||||
/* fetch non-recurrent apts first */
|
||||
|
||||
records = [folder fetchFields: fields matchingQualifier: qualifier];
|
||||
}
|
||||
else
|
||||
@@ -871,7 +880,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
{
|
||||
NSCalendarDate *recurrenceId;
|
||||
NSMutableDictionary *newRecord;
|
||||
NSDictionary *oldRecord;
|
||||
NGCalendarDateRange *newRecordRange;
|
||||
NSComparisonResult compare;
|
||||
int recordIndex, secondsOffsetFromGMT;
|
||||
@@ -2236,10 +2244,39 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
return @"";
|
||||
}
|
||||
|
||||
/*
|
||||
RFC5842 states:
|
||||
|
||||
3.1. DAV:resource-id Property
|
||||
|
||||
The DAV:resource-id property is a REQUIRED property that enables
|
||||
clients to determine whether two bindings are to the same resource.
|
||||
The value of DAV:resource-id is a URI, and may use any registered URI
|
||||
scheme that guarantees the uniqueness of the value across all
|
||||
resources for all time (e.g., the urn:uuid: URN namespace defined in
|
||||
[RFC4122] or the opaquelocktoken: URI scheme defined in [RFC4918]).
|
||||
|
||||
<!ELEMENT resource-id (href)>
|
||||
|
||||
...
|
||||
|
||||
so we must STRIP any username prefix, to make the ID global.
|
||||
|
||||
*/
|
||||
- (NSString *) davResourceId
|
||||
{
|
||||
NSString *name, *prefix;
|
||||
|
||||
prefix = [NSString stringWithFormat: @"%@_", [self ownerInContext: context]];
|
||||
name = [self nameInContainer];
|
||||
|
||||
if ([name hasPrefix: prefix])
|
||||
{
|
||||
name = [name substringFromIndex: [prefix length]];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat: @"urn:uuid:%@:calendars:%@",
|
||||
[self ownerInContext: context], [self nameInContainer]];
|
||||
[self ownerInContext: context], name];
|
||||
}
|
||||
|
||||
- (NSArray *) davScheduleCalendarTransparency
|
||||
@@ -2504,7 +2541,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
unsigned int permStrIndex;
|
||||
|
||||
[super initializeQuickTablesAclsInContext: localContext];
|
||||
/* We assume "userIsOwner" will be set after calling the super method. */
|
||||
/* We assume "userCanAccessAllObjects" will be set after calling the super method. */
|
||||
if (!userCanAccessAllObjects)
|
||||
{
|
||||
login = [[localContext activeUser] login];
|
||||
@@ -2901,9 +2938,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
NSMutableString *content;
|
||||
NSString *uid;
|
||||
|
||||
uid = [self globallyUniqueObjectId];
|
||||
[event setUid: uid];
|
||||
|
||||
// We first look if there's an event with the same UID in our calendar. If not,
|
||||
// let's reuse what is in the event, otherwise generate a new GUID and use it.
|
||||
uid = [event uid];
|
||||
@@ -3097,6 +3131,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
{
|
||||
NSMutableArray *aclsForUser;
|
||||
NSArray *superAcls;
|
||||
static NSArray *rolesClassifications = nil;
|
||||
|
||||
superAcls = [super aclsForUser: uid forObjectAtPath: objectPathArray];
|
||||
if ([uid isEqualToString: [self defaultUserID]])
|
||||
@@ -3111,14 +3146,52 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
[aclsForUser addObject: SoRole_Authenticated];
|
||||
}
|
||||
else
|
||||
aclsForUser = (NSMutableArray *) superAcls;
|
||||
{
|
||||
aclsForUser = [NSMutableArray array];
|
||||
if (!rolesClassifications)
|
||||
{
|
||||
rolesClassifications =
|
||||
[NSArray arrayWithObjects:
|
||||
[NSArray arrayWithObjects:
|
||||
SOGoCalendarRole_PublicModifier,
|
||||
SOGoCalendarRole_PublicResponder,
|
||||
SOGoCalendarRole_PublicViewer,
|
||||
SOGoCalendarRole_PublicDAndTViewer,
|
||||
nil],
|
||||
[NSArray arrayWithObjects:
|
||||
SOGoCalendarRole_ConfidentialModifier,
|
||||
SOGoCalendarRole_ConfidentialResponder,
|
||||
SOGoCalendarRole_ConfidentialViewer,
|
||||
SOGoCalendarRole_ConfidentialDAndTViewer,
|
||||
nil],
|
||||
[NSArray arrayWithObjects:
|
||||
SOGoCalendarRole_PrivateModifier,
|
||||
SOGoCalendarRole_PrivateResponder,
|
||||
SOGoCalendarRole_PrivateViewer,
|
||||
SOGoCalendarRole_PrivateDAndTViewer,
|
||||
nil],
|
||||
[NSArray arrayWithObject: SOGoRole_ObjectCreator],
|
||||
[NSArray arrayWithObject: SOGoRole_ObjectEraser],
|
||||
nil];
|
||||
[rolesClassifications retain];
|
||||
}
|
||||
// When a user is a member of many groups for which there are access rights, multiple access rights
|
||||
// can be returned for each classification. In this case, we only keep the highest access right.
|
||||
int i, count = [rolesClassifications count];
|
||||
NSString *role;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
role = [[rolesClassifications objectAtIndex: i] firstObjectCommonWithArray: superAcls];
|
||||
if (role)
|
||||
[aclsForUser addObject: role];
|
||||
}
|
||||
}
|
||||
|
||||
return aclsForUser;
|
||||
}
|
||||
|
||||
/* caldav-proxy */
|
||||
- (SOGoAppointmentProxyPermission)
|
||||
proxyPermissionForUserWithLogin: (NSString *) login
|
||||
- (SOGoAppointmentProxyPermission) proxyPermissionForUserWithLogin: (NSString *) login
|
||||
{
|
||||
SOGoAppointmentProxyPermission permission;
|
||||
NSArray *roles;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoAppointmentFolders.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -35,8 +33,8 @@
|
||||
NSMutableArray *folderObjectKeys;
|
||||
}
|
||||
|
||||
- (SOGoWebAppointmentFolder *) newWebCalendarWithName: (NSString *) folderDisplayName
|
||||
atURL: (NSString *) url;
|
||||
- (SOGoWebAppointmentFolder *) newWebCalendarWithURL: (NSString *) urlString
|
||||
nameInContainer: (NSString *) name;
|
||||
|
||||
- (void) reloadWebCalendars: (BOOL) forceReload;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
|
||||
/* SOGoAppointmentFolders.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2007-2013 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -133,18 +131,29 @@ static SoSecurityManager *sm = nil;
|
||||
return [self labelForKey: @"Personal Calendar" inContext: context];
|
||||
}
|
||||
|
||||
- (SOGoWebAppointmentFolder *)
|
||||
newWebCalendarWithName: (NSString *) folderDisplayName
|
||||
atURL: (NSString *) urlString
|
||||
|
||||
|
||||
- (SOGoWebAppointmentFolder *) newWebCalendarWithURL: (NSString *) urlString
|
||||
nameInContainer: (NSString *) name
|
||||
{
|
||||
NSException *error;
|
||||
SOGoAppointmentFolder *aptFolder;
|
||||
NSString *folderDisplayName, *tmp;
|
||||
SOGoWebAppointmentFolder *webCalendar;
|
||||
NSString *name;
|
||||
SOGoAppointmentFolder *aptFolder;
|
||||
NSException *error;
|
||||
NSURL *url;
|
||||
|
||||
folderDisplayName = [self labelForKey: @"Web Calendar"];
|
||||
webCalendar = nil;
|
||||
|
||||
tmp = [urlString lastPathComponent];
|
||||
if (tmp)
|
||||
{
|
||||
if ([[tmp pathExtension] caseInsensitiveCompare: @"ics"] == NSOrderedSame)
|
||||
folderDisplayName = [tmp substringToIndex: [tmp length] - 4];
|
||||
else
|
||||
folderDisplayName = tmp;
|
||||
}
|
||||
|
||||
if ([folderDisplayName length] > 0 && [urlString length] > 0)
|
||||
{
|
||||
url = [NSURL URLWithString: urlString];
|
||||
@@ -154,6 +163,10 @@ static SoSecurityManager *sm = nil;
|
||||
nameInContainer: &name];
|
||||
if (!error)
|
||||
{
|
||||
// We make sure we initialized our subfolfers list. This can happen
|
||||
// when auto-creating Web calendars during the logon process.
|
||||
[self subFolders];
|
||||
|
||||
aptFolder = [subFolders objectForKey: name];
|
||||
[aptFolder setFolderPropertyValue: urlString
|
||||
inCategory: @"WebCalendars"];
|
||||
@@ -561,9 +574,25 @@ static SoSecurityManager *sm = nil;
|
||||
folder = [SOGoWebAppointmentFolder
|
||||
folderWithSubscriptionReference: ref
|
||||
inContainer: self];
|
||||
if (folder
|
||||
&& (forceReload || [folder reloadOnLogin]))
|
||||
[folder loadWebCalendar];
|
||||
|
||||
if (forceReload || [folder reloadOnLogin] || !folder)
|
||||
{
|
||||
if (!folder)
|
||||
{
|
||||
NSString *s, *urlString;
|
||||
|
||||
urlString = [[calSettings objectForKey: @"WebCalendars"] objectForKey: ref];
|
||||
s = [ref lastPathComponent];
|
||||
|
||||
// See bug #2007. Some admins want to subscribe users to Web calendars
|
||||
// using sogo-tool. If they do that, the db tables won't be created
|
||||
// so we must detect that here and create them upon login.
|
||||
folder = [self newWebCalendarWithURL: urlString
|
||||
nameInContainer: s];
|
||||
}
|
||||
|
||||
[folder loadWebCalendar];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -431,18 +431,34 @@
|
||||
forEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
iCalPerson *currentAttendee;
|
||||
NSMutableArray *attendees;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *currentUID;
|
||||
SOGoUser *user;
|
||||
SOGoUser *user, *currentUser, *ownerUser;
|
||||
|
||||
// Build a list of the attendees uids
|
||||
attendees = [NSMutableArray arrayWithCapacity: [theAttendees count]];
|
||||
enumerator = [theAttendees objectEnumerator];
|
||||
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
currentUID = [currentAttendee uid];
|
||||
|
||||
if (currentUID)
|
||||
{
|
||||
{
|
||||
[attendees addObject: currentUID];
|
||||
}
|
||||
}
|
||||
|
||||
// If the active user is not the owner of the calendar, check possible conflict when
|
||||
// the owner is a resource
|
||||
currentUser = [context activeUser];
|
||||
if (!activeUserIsOwner && ![currentUser isSuperUser])
|
||||
{
|
||||
[attendees addObject: owner];
|
||||
}
|
||||
|
||||
enumerator = [attendees objectEnumerator];
|
||||
while ((currentUID = [enumerator nextObject]))
|
||||
{
|
||||
user = [SOGoUser userWithLogin: currentUID];
|
||||
|
||||
if ([user isResource])
|
||||
@@ -461,8 +477,7 @@
|
||||
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
|
||||
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
|
||||
|
||||
folder = [[SOGoUser userWithLogin: currentUID]
|
||||
personalCalendarFolderInContext: context];
|
||||
folder = [user personalCalendarFolderInContext: context];
|
||||
|
||||
// Deny access to the resource if the ACLs don't allow the user
|
||||
if (![folder aclSQLListingFilter])
|
||||
@@ -526,52 +541,64 @@
|
||||
[fbInfo removeObjectAtIndex: i];
|
||||
}
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
|
||||
// Find the attendee associated to the current UID
|
||||
for (i = 0; i < [theAttendees count]; i++)
|
||||
{
|
||||
currentAttendee = [theAttendees objectAtIndex: i];
|
||||
if ([[currentAttendee uid] isEqualToString: currentUID])
|
||||
break;
|
||||
else
|
||||
currentAttendee = nil;
|
||||
}
|
||||
|
||||
if ([fbInfo count])
|
||||
{
|
||||
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
|
||||
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
|
||||
// overlapping events
|
||||
if ([user numberOfSimultaneousBookings] == 0 ||
|
||||
[user numberOfSimultaneousBookings] > [fbInfo count])
|
||||
{
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
if (currentAttendee)
|
||||
{
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
iCalEvent *event;
|
||||
|
||||
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
|
||||
event = [[calendar events] lastObject];
|
||||
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
|
||||
nil];
|
||||
else
|
||||
{
|
||||
iCalCalendar *calendar;
|
||||
NSDictionary *values;
|
||||
NSString *reason;
|
||||
iCalEvent *event;
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
|
||||
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
|
||||
event = [[calendar events] lastObject];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus:403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
values = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
|
||||
[user cn], @"Cn",
|
||||
[user systemEmail], @"SystemEmail",
|
||||
([event summary] ? [event summary] : @""), @"EventTitle",
|
||||
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
|
||||
nil];
|
||||
|
||||
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
|
||||
|
||||
return [NSException exceptionWithHTTPStatus: 403
|
||||
reason: reason];
|
||||
}
|
||||
}
|
||||
else if (currentAttendee)
|
||||
{
|
||||
// No conflict, we auto-accept. We do this for resources automatically if no
|
||||
// double-booking is observed. If it's not the desired behavior, just don't
|
||||
// set the resource as one!
|
||||
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
}
|
||||
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
@@ -787,7 +814,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
|
||||
[self expandGroupsInEvent: newEvent];
|
||||
|
||||
// We first update the event. It is important to this initially
|
||||
// We first update the event. It is important to do this initially
|
||||
// as the event's UID might get modified.
|
||||
[super updateComponent: newEvent];
|
||||
|
||||
@@ -795,13 +822,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
// New event -- send invitation to all attendees
|
||||
attendees = [newEvent attendeesWithoutUser: ownerUser];
|
||||
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
if ([attendees count])
|
||||
{
|
||||
// We catch conflicts and abort the save process immediately
|
||||
// in case of one with resources
|
||||
if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent]))
|
||||
return ex;
|
||||
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forObject: [newEvent itipEntryWithMethod: @"request"]
|
||||
previousObject: nil
|
||||
@@ -1375,9 +1403,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
{
|
||||
// We generate the updated iCalendar file and we save it in
|
||||
// the database. We do this ONLY when using SOGo from the
|
||||
// Web interface. Over DAV, it'll be handled directly in
|
||||
// PUTAction:
|
||||
if (![context request] || [[context request] handledByDefaultHandler])
|
||||
// Web interface or over ActiveSync.
|
||||
// Over DAV, it'll be handled directly in PUTAction:
|
||||
if (![context request]
|
||||
|| [[context request] handledByDefaultHandler]
|
||||
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
|
||||
ex = [self saveContentString: [[event parent] versitString]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2006-2012 Inverse inc.
|
||||
Copyright (C) 2006-2013 Inverse inc.
|
||||
Copyright (C) 2000-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of SOGo.
|
||||
@@ -140,6 +140,11 @@
|
||||
return newEndDate;
|
||||
}
|
||||
|
||||
- (NSString *) location
|
||||
{
|
||||
return [apt location];
|
||||
}
|
||||
|
||||
- (NSString *) summary
|
||||
{
|
||||
return [apt summary];
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* SOGoCalendarComponent.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2006-2013 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/* SOGoCalendarComponent.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2012 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
* Copyright (C) 2006-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,8 +18,10 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
@@ -62,6 +60,7 @@
|
||||
#import <SOGo/SOGoWebDAVAclManager.h>
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
|
||||
#import "SOGoAptMailICalReply.h"
|
||||
#import "SOGoAptMailNotification.h"
|
||||
@@ -219,29 +218,13 @@
|
||||
|
||||
- (NSString *) secureContentAsString
|
||||
{
|
||||
iCalCalendar *tmpCalendar;
|
||||
iCalRepeatableEntityObject *tmpComponent;
|
||||
// NSArray *roles;
|
||||
iCalCalendar *tmpCalendar;
|
||||
NSArray *allComponents;
|
||||
SoSecurityManager *sm;
|
||||
NSString *iCalString;
|
||||
|
||||
// uid = [[context activeUser] login];
|
||||
// roles = [self aclsForUser: uid];
|
||||
// if ([roles containsObject: SOGoCalendarRole_Organizer]
|
||||
// || [roles containsObject: SOGoCalendarRole_Participant]
|
||||
// || [roles containsObject: SOGoCalendarRole_ComponentViewer])
|
||||
// calContent = content;
|
||||
// else if ([roles containsObject: SOGoCalendarRole_ComponentDAndTViewer])
|
||||
// {
|
||||
// tmpCalendar = [[self calendar: NO] copy];
|
||||
// tmpComponent = (iCalRepeatableEntityObject *)
|
||||
// [tmpCalendar firstChildWithTag: [self componentTag]];
|
||||
// [self _filterComponent: tmpComponent];
|
||||
// calContent = [tmpCalendar versitString];
|
||||
// [tmpCalendar release];
|
||||
// }
|
||||
// else
|
||||
// calContent = nil;
|
||||
int i;
|
||||
|
||||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (activeUserIsOwner
|
||||
@@ -253,14 +236,21 @@
|
||||
onObject: self inContext: context])
|
||||
{
|
||||
tmpCalendar = [[self calendar: NO secure: NO] mutableCopy];
|
||||
tmpComponent = (iCalRepeatableEntityObject *)
|
||||
[tmpCalendar firstChildWithTag: [self componentTag]];
|
||||
[self _filterComponent: tmpComponent];
|
||||
|
||||
// We filter all components, in case we have RECURRENCE-ID
|
||||
allComponents = [tmpCalendar childrenWithTag: [self componentTag]];
|
||||
|
||||
for (i = 0; i < [allComponents count]; i++)
|
||||
{
|
||||
tmpComponent = (iCalRepeatableEntityObject *)[allComponents objectAtIndex:i];
|
||||
[self _filterComponent: tmpComponent];
|
||||
|
||||
// We add an additional header here to inform clients (if necessary) that
|
||||
// we churned the content of the calendar.
|
||||
[tmpComponent addChild: [CardElement simpleElementWithTag: @"X-SOGo-Secure"
|
||||
value: @"YES"]];
|
||||
// We add an additional header here to inform clients (if necessary) that
|
||||
// we churned the content of the calendar.
|
||||
[tmpComponent addChild: [CardElement simpleElementWithTag: @"X-SOGo-Secure"
|
||||
value: @"YES"]];
|
||||
}
|
||||
|
||||
iCalString = [tmpCalendar versitString];
|
||||
[tmpCalendar release];
|
||||
}
|
||||
@@ -499,18 +489,31 @@
|
||||
NSMutableArray *allAttendees;
|
||||
iCalPerson *currentAttendee;
|
||||
NSEnumerator *enumerator;
|
||||
NSAutoreleasePool *pool;
|
||||
SOGoGroup *group;
|
||||
|
||||
BOOL eventWasModified;
|
||||
unsigned int i;
|
||||
unsigned int i, j;
|
||||
|
||||
|
||||
domain = [[context activeUser] domain];
|
||||
organizerEmail = [[theEvent organizer] rfc822Email];
|
||||
eventWasModified = NO;
|
||||
allAttendees = [NSMutableArray arrayWithArray: [theEvent attendees]];
|
||||
enumerator = [[theEvent attendees] objectEnumerator];
|
||||
|
||||
j = 0;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
while ((currentAttendee = [enumerator nextObject]))
|
||||
{
|
||||
if (j%5 == 0)
|
||||
{
|
||||
RELEASE(pool);
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
group = [SOGoGroup groupWithEmail: [currentAttendee rfc822Email]
|
||||
inDomain: domain];
|
||||
if (group)
|
||||
@@ -554,11 +557,15 @@
|
||||
eventWasModified = YES;
|
||||
}
|
||||
}
|
||||
|
||||
j++;
|
||||
} // while (currentAttendee ...
|
||||
|
||||
if (eventWasModified)
|
||||
[theEvent setAttendees: allAttendees];
|
||||
|
||||
RELEASE(pool);
|
||||
|
||||
return eventWasModified;
|
||||
}
|
||||
|
||||
@@ -760,6 +767,10 @@
|
||||
SOGoUser *ownerUser;
|
||||
SOGoDomainDefaults *dd;
|
||||
|
||||
// If defined, we return immediately. When not defined, we send the notifications correctly
|
||||
if ([object firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"])
|
||||
return;
|
||||
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
dd = [ownerUser domainDefaults];
|
||||
if ([dd appointmentSendEMailNotifications] && [object isStillRelevant])
|
||||
@@ -837,6 +848,7 @@
|
||||
mailDate = [[NSCalendarDate date] rfc822DateString];
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: subject forKey: @"subject"];
|
||||
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
|
||||
if ([msgType length] > 0)
|
||||
[headerMap setObject: msgType forKey: @"x-sogo-message-type"];
|
||||
msg = [NGMimeMessage messageWithHeader: headerMap];
|
||||
@@ -866,8 +878,7 @@
|
||||
sendMimePart: msg
|
||||
toRecipients: [NSArray arrayWithObject: email]
|
||||
sender: shortSenderEmail
|
||||
withAuthenticator: [self
|
||||
authenticatorInContext: context]
|
||||
withAuthenticator: [self authenticatorInContext: context]
|
||||
inContext: context];
|
||||
}
|
||||
}
|
||||
@@ -892,7 +903,7 @@
|
||||
SOGoDomainDefaults *dd;
|
||||
|
||||
dd = [from domainDefaults];
|
||||
if ([dd appointmentSendEMailNotifications])
|
||||
if ([dd appointmentSendEMailNotifications] && [event isStillRelevant])
|
||||
{
|
||||
/* get WOApplication instance */
|
||||
app = [WOApplication application];
|
||||
@@ -921,6 +932,7 @@
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: [[p getSubject] asQPSubjectString: @"UTF-8"]
|
||||
forKey: @"subject"];
|
||||
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
|
||||
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
|
||||
[headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
|
||||
[headerMap setObject: @"calendar:invitation-reply" forKey: @"x-sogo-message-type"];
|
||||
@@ -1030,6 +1042,11 @@
|
||||
// Recipient is fixed, which is the calendar owner
|
||||
ownerUser = [SOGoUser userWithLogin: self->owner];
|
||||
recipientIdentity = [ownerUser primaryIdentity];
|
||||
|
||||
// Safety net for broken configurations
|
||||
if (!recipientIdentity)
|
||||
return;
|
||||
|
||||
recipientEmail = [recipientIdentity objectForKey: @"email"];
|
||||
fullRecipientEmail = [recipientIdentity keysWithFormat: @"%{fullName} <%{email}>"];
|
||||
|
||||
@@ -1038,6 +1055,7 @@
|
||||
mailDate = [[NSCalendarDate date] rfc822DateString];
|
||||
[headerMap setObject: mailDate forKey: @"date"];
|
||||
[headerMap setObject: [page getSubject] forKey: @"subject"];
|
||||
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
|
||||
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
|
||||
[headerMap setObject: @"text/html; charset=utf-8"
|
||||
forKey: @"content-type"];
|
||||
|
||||
@@ -294,7 +294,9 @@
|
||||
contact = [contacts lastObject];
|
||||
email = [contact valueForKey: @"c_email"];
|
||||
source = [contact objectForKey: @"source"];
|
||||
if ([email length] && [source MSExchangeHostname])
|
||||
if ([email length]
|
||||
&& [source conformsToProtocol: @protocol (SOGoDNSource)]
|
||||
&& [source MSExchangeHostname])
|
||||
{
|
||||
exchangeFreeBusy = [[MSExchangeFreeBusy alloc] init];
|
||||
[exchangeFreeBusy autorelease];
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2006-2013 Inverse inc.
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
This file is part of OpenGroupware.org.
|
||||
This file is part of SOGo.
|
||||
|
||||
OGo is free software; you can redistribute it and/or modify it under
|
||||
SOGo is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* iCalEntityObject+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -48,6 +46,8 @@ extern NSNumber *iCalDistantFutureNumber;
|
||||
- (NSMutableDictionary *) quickRecord;
|
||||
|
||||
- (int) priorityNumber;
|
||||
- (NSString *) createdBy;
|
||||
|
||||
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date
|
||||
withOffset: (int) offset
|
||||
forAllDay: (BOOL) allDay;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* iCalEntityObject+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -242,4 +240,19 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
return priorityNumber;
|
||||
}
|
||||
|
||||
- (NSString *) createdBy
|
||||
{
|
||||
NSString *created_by;
|
||||
|
||||
created_by = [[self firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""];
|
||||
|
||||
// We consider "SENT-BY" in case our custom header isn't found
|
||||
if (![created_by length])
|
||||
{
|
||||
created_by = [[self organizer] sentBy];
|
||||
}
|
||||
|
||||
return created_by;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -64,6 +64,9 @@
|
||||
return isStillRelevent;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSMutableDictionary *) quickRecord
|
||||
{
|
||||
NSMutableDictionary *row;
|
||||
@@ -88,7 +91,15 @@
|
||||
title = [self summary];
|
||||
if (![title isNotNull])
|
||||
title = @"";
|
||||
|
||||
if ([title length] > 1000)
|
||||
title = [title substringToIndex: 1000];
|
||||
|
||||
location = [self location];
|
||||
|
||||
if ([location length] > 255)
|
||||
location = [location substringToIndex: 255];
|
||||
|
||||
sequence = [self sequence];
|
||||
accessClass = [self symbolicAccessClass];
|
||||
isAllDay = [self isAllDay];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* NGVCard+SOGo.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
*
|
||||
* Author: Cyril Robert <crobert@inverse.ca>
|
||||
* Copyright (C) 2009-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -30,9 +28,23 @@
|
||||
|
||||
@interface NGVCard (SOGoExtensions)
|
||||
|
||||
- (CardElement *) elementWithTag: (NSString *) elementTag
|
||||
ofType: (NSString *) type;
|
||||
|
||||
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
|
||||
- (NSMutableDictionary *) asLDIFRecord;
|
||||
|
||||
- (NSString *) workCompany;
|
||||
- (NSString *) fullName;
|
||||
- (NSArray *) secondaryEmails;
|
||||
|
||||
- (NSString *) workPhone;
|
||||
- (NSString *) homePhone;
|
||||
- (NSString *) fax;
|
||||
- (NSString *) mobile;
|
||||
- (NSString *) pager;
|
||||
- (NSCalendarDate *) birthday;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* NGVCARD_SOGO_H */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* NGVCard+SOGo.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009 Inverse inc.
|
||||
*
|
||||
* Author: Cyril Robert <crobert@inverse.ca>
|
||||
* Copyright (C) 2009-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,6 +27,8 @@
|
||||
#import <NGCards/NSArray+NGCards.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
|
||||
#import <SOGo/NSCalendarDate+SOGo.h>
|
||||
|
||||
#import "NSDictionary+LDIF.h"
|
||||
|
||||
#import "NGVCard+SOGo.h"
|
||||
@@ -164,8 +164,8 @@ convention:
|
||||
@implementation NGVCard (SOGoExtensions)
|
||||
|
||||
/* LDIF -> VCARD */
|
||||
- (CardElement *) _elementWithTag: (NSString *) elementTag
|
||||
ofType: (NSString *) type
|
||||
- (CardElement *) elementWithTag: (NSString *) elementTag
|
||||
ofType: (NSString *) type
|
||||
{
|
||||
NSArray *elements;
|
||||
CardElement *element;
|
||||
@@ -188,16 +188,16 @@ convention:
|
||||
{
|
||||
CardElement *phone;
|
||||
|
||||
phone = [self _elementWithTag: @"tel" ofType: @"work"];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""];
|
||||
phone = [self _elementWithTag: @"tel" ofType: @"home"];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""];
|
||||
phone = [self _elementWithTag: @"tel" ofType: @"cell"];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""];
|
||||
phone = [self _elementWithTag: @"tel" ofType: @"fax"];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]
|
||||
forKey: @""];
|
||||
phone = [self _elementWithTag: @"tel" ofType: @"pager"];
|
||||
phone = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""];
|
||||
}
|
||||
|
||||
@@ -205,9 +205,9 @@ convention:
|
||||
{
|
||||
CardElement *mail, *homeMail;
|
||||
|
||||
mail = [self _elementWithTag: @"email" ofType: @"work"];
|
||||
mail = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""];
|
||||
homeMail = [self _elementWithTag: @"email" ofType: @"home"];
|
||||
homeMail = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""];
|
||||
[[self uniqueChildWithTag: @"x-mozilla-html"]
|
||||
setSingleValue: [ldifRecord objectForKey: @"mozillausehtmlmail"]
|
||||
@@ -230,7 +230,7 @@ convention:
|
||||
[self setFn: [ldifRecord objectForKey: @"displayname"]];
|
||||
[self setTitle: [ldifRecord objectForKey: @"title"]];
|
||||
|
||||
element = [self _elementWithTag: @"adr" ofType: @"home"];
|
||||
element = [self elementWithTag: @"adr" ofType: @"home"];
|
||||
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"]
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet"]
|
||||
@@ -244,7 +244,7 @@ convention:
|
||||
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomecountryname"]
|
||||
atIndex: 6 forKey: @""];
|
||||
|
||||
element = [self _elementWithTag: @"adr" ofType: @"work"];
|
||||
element = [self elementWithTag: @"adr" ofType: @"work"];
|
||||
[element setSingleValue: [ldifRecord objectForKey: @"mozillaworkstreet2"]
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [ldifRecord objectForKey: @"street"]
|
||||
@@ -268,9 +268,9 @@ convention:
|
||||
|
||||
[self _setPhoneValues: ldifRecord];
|
||||
[self _setEmails: ldifRecord];
|
||||
[[self _elementWithTag: @"url" ofType: @"home"]
|
||||
[[self elementWithTag: @"url" ofType: @"home"]
|
||||
setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""];
|
||||
[[self _elementWithTag: @"url" ofType: @"work"]
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""];
|
||||
|
||||
[[self uniqueChildWithTag: @"x-aim"]
|
||||
@@ -639,4 +639,153 @@ convention:
|
||||
return ldifRecord;
|
||||
}
|
||||
|
||||
- (NSString *) workCompany
|
||||
{
|
||||
CardElement *org;
|
||||
NSString *company;
|
||||
|
||||
org = [self org];
|
||||
company = [org flattenedValueAtIndex: 0 forKey: @""];
|
||||
if ([company length] == 0)
|
||||
company = nil;
|
||||
|
||||
return company;
|
||||
}
|
||||
|
||||
- (NSString *) fullName
|
||||
{
|
||||
CardElement *n;
|
||||
NSString *fn, *firstName, *lastName, *org;
|
||||
|
||||
fn = [self fn];
|
||||
if ([fn length] == 0)
|
||||
{
|
||||
n = [self n];
|
||||
lastName = [n flattenedValueAtIndex: 0 forKey: @""];
|
||||
firstName = [n flattenedValueAtIndex: 1 forKey: @""];
|
||||
if ([firstName length] > 0)
|
||||
{
|
||||
if ([lastName length] > 0)
|
||||
fn = [NSString stringWithFormat: @"%@ %@", firstName, lastName];
|
||||
else
|
||||
fn = firstName;
|
||||
}
|
||||
else if ([lastName length] > 0)
|
||||
fn = lastName;
|
||||
else
|
||||
{
|
||||
n = [self org];
|
||||
org = [n flattenedValueAtIndex: 0 forKey: @""];
|
||||
fn = org;
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
- (NSArray *) secondaryEmails
|
||||
{
|
||||
NSMutableArray *emails;
|
||||
NSString *email;
|
||||
int i;
|
||||
|
||||
emails = [NSMutableArray array];
|
||||
|
||||
[emails addObjectsFromArray: [self childrenWithTag: @"email"]];
|
||||
[emails removeObjectsInArray: [self childrenWithTag: @"email"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"pref"]];
|
||||
|
||||
for (i = [emails count]-1; i >= 0; i--)
|
||||
{
|
||||
email = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
|
||||
|
||||
if ([email caseInsensitiveCompare: [self preferredEMail]] == NSOrderedSame)
|
||||
[emails removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
return emails;
|
||||
}
|
||||
|
||||
- (NSString *) _phoneOfType: (NSString *) aType
|
||||
excluding: (NSString *) aTypeToExclude
|
||||
{
|
||||
NSArray *elements, *phones;
|
||||
NSString *phone;
|
||||
|
||||
phones = [self childrenWithTag: @"tel"];
|
||||
elements = [phones cardElementsWithAttribute: @"type"
|
||||
havingValue: aType];
|
||||
|
||||
phone = nil;
|
||||
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
CardElement *ce;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [elements count]; i++)
|
||||
{
|
||||
ce = [elements objectAtIndex: i];
|
||||
phone = [ce flattenedValuesForKey: @""];
|
||||
|
||||
if (!aTypeToExclude)
|
||||
break;
|
||||
|
||||
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
|
||||
break;
|
||||
|
||||
phone = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return phone;
|
||||
}
|
||||
|
||||
- (NSString *) workPhone
|
||||
{
|
||||
// We do this (exclude FAX) in order to avoid setting the WORK number as the FAX
|
||||
// one if we do see the FAX field BEFORE the WORK number.
|
||||
return [self _phoneOfType: @"work" excluding: @"fax"];
|
||||
}
|
||||
|
||||
- (NSString *) homePhone
|
||||
{
|
||||
return [self _phoneOfType: @"home" excluding: @"fax"];
|
||||
}
|
||||
|
||||
- (NSString *) fax
|
||||
{
|
||||
return [self _phoneOfType: @"fax" excluding: nil];
|
||||
}
|
||||
|
||||
- (NSString *) mobile
|
||||
{
|
||||
return [self _phoneOfType: @"cell" excluding: nil];
|
||||
}
|
||||
|
||||
- (NSString *) pager
|
||||
{
|
||||
return [self _phoneOfType: @"pager" excluding: nil];
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) birthday
|
||||
{
|
||||
NSString *bday, *value;
|
||||
NSCalendarDate *date;
|
||||
|
||||
bday = [self bday];
|
||||
date = nil;
|
||||
if ([bday length] > 0)
|
||||
{
|
||||
// Expected format of BDAY is YYYY[-]MM[-]DD
|
||||
value = [bday stringByReplacingString: @"-" withString: @""];
|
||||
date = [NSCalendarDate dateFromShortDateString: value
|
||||
andShortTimeString: nil
|
||||
inTimeZone: nil];
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
@end /* NGVCard */
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SOGoContactFolders.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006, 2007 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Copyright (C) 2006-2013 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -31,6 +29,8 @@
|
||||
withDisplayName: (NSString *) newDisplayName;
|
||||
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID;
|
||||
|
||||
- (NSDictionary *) systemSources;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCONTACTFOLDERS_H */
|
||||
Loaded 100 of 607 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user