Compare commits
37 Commits
master
...
maint-2.1.
Author | SHA1 | Date |
---|---|---|
Francis Lachapelle | e94b74218c | |
Ludovic Marcotte | 50a84ceafa | |
Francis Lachapelle | 2643cff3f9 | |
Jean Raby | c04f5a4775 | |
Jean Raby | 0ce90d1707 | |
Jean Raby | ed6a2a6a88 | |
Francis Lachapelle | 9b312f8b0a | |
Francis Lachapelle | 1b3304c193 | |
Francis Lachapelle | 5f04b065cd | |
Francis Lachapelle | 3e1cd079a8 | |
Francis Lachapelle | b5e77ad86c | |
Francis Lachapelle | 5bc0a0a932 | |
Francis Lachapelle | 582af53af0 | |
Francis Lachapelle | 13f01b512b | |
Ludovic Marcotte | cdb25b141d | |
Francis Lachapelle | a9868b1c93 | |
Jean Raby | 2cf66e037d | |
Francis Lachapelle | 449d838dc2 | |
Francis Lachapelle | c4bc215a47 | |
Francis Lachapelle | c9936d7ecf | |
Francis Lachapelle | 885e59a2af | |
Jean Raby | bf398889bf | |
Ludovic Marcotte | e26e3d7307 | |
Ludovic Marcotte | 045b275b27 | |
Jean Raby | 7ec1973e0f | |
Ludovic Marcotte | a79a9c4eff | |
Jean Raby | f049e2430a | |
Jean Raby | 59b3219f2d | |
Francis Lachapelle | e9eb8bdc86 | |
Francis Lachapelle | 5e0c7ae4f2 | |
Francis Lachapelle | c1c0c1ab44 | |
Francis Lachapelle | a59fff785b | |
Francis Lachapelle | 6a70db80e6 | |
Francis Lachapelle | d610630c8d | |
Francis Lachapelle | 4dc7790015 | |
Francis Lachapelle | 9dd000a2ca | |
Wolfgang Sourdeau | ce4084e528 |
|
@ -1,114 +0,0 @@
|
|||
# Contributing to SOGo
|
||||
|
||||
## Reporting Bugs and Suggesting Enhancements
|
||||
|
||||
If you encounter a possible bug with SOGo, you can access our
|
||||
[bug tracking system](https://sogo.nu/bugs/).
|
||||
|
||||
Please make sure to respect the following guidelines when reporting a bug:
|
||||
|
||||
* verify that the bug you found is not already known or even fixed in the `master` version
|
||||
* make the actual facts very clear; be precise, we need to be able to reproduce the problem
|
||||
* explain your speculations, if any
|
||||
* add a screenshot to the ticket if appropriate
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Begin by reading [SOGo Developer's Guide](../Documentation/SOGoDevelopersGuide.asciidoc).
|
||||
|
||||
### Git Commit Guidelines
|
||||
|
||||
We have very precise rules over how our git commit messages can be formatted. This leads to **more
|
||||
readable messages** that are easy to follow when looking through the **project history**.
|
||||
|
||||
It is important to note that we use the git commit messages to **generate** the
|
||||
[CHANGELOG](../CHANGELOG.md) document. Improperly formatted commit messages may result in your
|
||||
change not appearing in the CHANGELOG of the next release.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
```html
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
> Any line of the commit message cannot be longer than 100 characters.
|
||||
> This allows the message to be easier to read on GitHub as well as in various Git tools.
|
||||
|
||||
#### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **docs**: Documentation only changes
|
||||
* **i18n**: Change in localizable strings
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests
|
||||
* **chore**: Changes to the build and packaging process or auxiliary tools (sogo-tool,
|
||||
sogo-ealarms-notify) and libraries such as documentation generation
|
||||
|
||||
#### Scope
|
||||
The scope could be anything that helps specifying the scope (or feature) that is changing.
|
||||
|
||||
Examples
|
||||
|
||||
* mail
|
||||
* mail(js)
|
||||
* calendar(css)
|
||||
* addressbook
|
||||
* preferences(js)
|
||||
* core
|
||||
* eas
|
||||
|
||||
#### Subject
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
#### Body
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
#### Footer
|
||||
The footer should contain any information about **Breaking Changes** and is also the
|
||||
place to reference [Mantis](https://sogo.nu/bus/) issues that this commit **Fixes** or **Resolves**.
|
||||
|
||||
> Breaking Changes are intended to be highlighted in the CHANGELOG as changes that will require
|
||||
> community users to modify their code after updating to a version that contains this commit.
|
||||
|
||||
#### Sample Commit messages
|
||||
```text
|
||||
fix(calendar): don't raise exception when renaming with same name
|
||||
|
||||
this would break Apple Calendar.app when creating a new calendar
|
||||
|
||||
Fixes #4813
|
||||
```
|
||||
```text
|
||||
feat(calendar(js)): optionally expand LDAP groups in attendees editor
|
||||
|
||||
* add `/members` action for LDIF groups
|
||||
* add button to expand invited LDAP groups
|
||||
|
||||
Fixes #2506
|
||||
```
|
||||
```text
|
||||
fix(core): set default Sieve port to 4190
|
||||
|
||||
BREAKING CHANGE: the default port for the SOGoSieveServer configuration default is now 4190 (was
|
||||
2000).
|
||||
|
||||
You need to explicitly set the port if you use a different port.
|
||||
|
||||
Closes #4826
|
||||
```
|
|
@ -1,30 +1,13 @@
|
|||
*._*
|
||||
*.css
|
||||
*.pyc
|
||||
*.sax
|
||||
*.swp
|
||||
*.wox/
|
||||
*/*/*.SOGo/
|
||||
*/*/*/obj/
|
||||
*/*/obj/
|
||||
config.make
|
||||
tags
|
||||
*/obj/
|
||||
.scss-lint-config.yml_
|
||||
ActiveSync/ActiveSync.SOGo
|
||||
Documentation/*.docbook
|
||||
Documentation/*.pdf
|
||||
*/*/obj/
|
||||
*/*/*/obj/
|
||||
*/*/*.SOGo/
|
||||
*.sax
|
||||
*.wox/
|
||||
*.swp
|
||||
SoObjects/SOGo/SOGo.framework/
|
||||
SoObjects/SOGo/derived_src/
|
||||
Tests/*/config.py
|
||||
UI/WebServerResources/.bowerrc
|
||||
UI/WebServerResources/.sass-cache/
|
||||
UI/WebServerResources/bower_components/
|
||||
UI/WebServerResources/css/
|
||||
UI/WebServerResources/js/*.js
|
||||
UI/WebServerResources/js/*.js.map
|
||||
UI/WebServerResources/js/vendor/*.js
|
||||
UI/WebServerResources/js/vendor/*.js.map
|
||||
UI/WebServerResources/node_modules/
|
||||
UI/WebServerResources/scss/.sass-cache/
|
||||
config.make
|
||||
doc
|
||||
tags
|
||||
*.pyc
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "UI/WebServerResources/angular-material"]
|
||||
path = UI/WebServerResources/angular-material
|
||||
url = https://github.com/angular/material
|
224
.tx/config
224
.tx/config
|
@ -5,7 +5,6 @@ host = https://www.transifex.com
|
|||
source_file = UI/MailerUI/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/MailerUI/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/MailerUI/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/MailerUI/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/MailerUI/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/MailerUI/Welsh.lproj/Localizable.strings
|
||||
|
@ -13,43 +12,25 @@ trans.da_DK = UI/MailerUI/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/MailerUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MailerUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MailerUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MailerUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MailerUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MailerUI/French.lproj/Localizable.strings
|
||||
trans.he = UI/MailerUI/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/MailerUI/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/MailerUI/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/MailerUI/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/MailerUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MailerUI/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/MailerUI/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/MailerUI/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/MailerUI/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MailerUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MailerUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MailerUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/MailerUI/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MailerUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/MailerUI/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/MailerUI/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/MailerUI/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/MailerUI/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/MailerUI/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-preferencesui]
|
||||
source_file = UI/PreferencesUI/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/PreferencesUI/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/PreferencesUI/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/PreferencesUI/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/PreferencesUI/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/PreferencesUI/Welsh.lproj/Localizable.strings
|
||||
|
@ -57,43 +38,25 @@ trans.da_DK = UI/PreferencesUI/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/PreferencesUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/PreferencesUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/PreferencesUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/PreferencesUI/French.lproj/Localizable.strings
|
||||
trans.he = UI/PreferencesUI/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/PreferencesUI/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/PreferencesUI/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/PreferencesUI/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/PreferencesUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/PreferencesUI/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/PreferencesUI/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/PreferencesUI/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/PreferencesUI/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/PreferencesUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/PreferencesUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/PreferencesUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/PreferencesUI/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/PreferencesUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/PreferencesUI/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/PreferencesUI/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/PreferencesUI/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/PreferencesUI/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/PreferencesUI/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-scheduler]
|
||||
source_file = UI/Scheduler/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/Scheduler/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/Scheduler/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/Scheduler/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/Scheduler/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/Scheduler/Welsh.lproj/Localizable.strings
|
||||
|
@ -101,43 +64,25 @@ trans.da_DK = UI/Scheduler/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/Scheduler/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Scheduler/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Scheduler/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Scheduler/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Scheduler/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Scheduler/French.lproj/Localizable.strings
|
||||
trans.he = UI/Scheduler/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/Scheduler/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/Scheduler/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/Scheduler/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/Scheduler/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Scheduler/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/Scheduler/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/Scheduler/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/Scheduler/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Scheduler/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Scheduler/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Scheduler/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/Scheduler/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Scheduler/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/Scheduler/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/Scheduler/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/Scheduler/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/Scheduler/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/Scheduler/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-contacts]
|
||||
source_file = UI/Contacts/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/Contacts/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/Contacts/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/Contacts/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/Contacts/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/Contacts/Welsh.lproj/Localizable.strings
|
||||
|
@ -145,43 +90,25 @@ trans.da_DK = UI/Contacts/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/Contacts/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Contacts/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Contacts/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Contacts/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Contacts/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Contacts/French.lproj/Localizable.strings
|
||||
trans.he = UI/Contacts/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/Contacts/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/Contacts/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/Contacts/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/Contacts/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Contacts/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/Contacts/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/Contacts/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/Contacts/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Contacts/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Contacts/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Contacts/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Contacts/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/Contacts/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/Contacts/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/Contacts/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/Contacts/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/Contacts/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/Contacts/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Contacts/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mainui]
|
||||
source_file = UI/MainUI/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/MainUI/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/MainUI/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/MainUI/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/MainUI/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/MainUI/Welsh.lproj/Localizable.strings
|
||||
|
@ -189,43 +116,25 @@ trans.da_DK = UI/MainUI/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/MainUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MainUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MainUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MainUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MainUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MainUI/French.lproj/Localizable.strings
|
||||
trans.he = UI/MainUI/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/MainUI/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/MainUI/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/MainUI/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/MainUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MainUI/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/MainUI/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/MainUI/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/MainUI/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MainUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MainUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MainUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MainUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/MainUI/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/MainUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MainUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/MainUI/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/MainUI/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/MainUI/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/MainUI/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/MainUI/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MainUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-common]
|
||||
source_file = UI/Common/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/Common/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/Common/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/Common/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/Common/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/Common/Welsh.lproj/Localizable.strings
|
||||
|
@ -233,43 +142,25 @@ trans.da_DK = UI/Common/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/Common/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/Common/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/Common/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/Common/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/Common/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/Common/French.lproj/Localizable.strings
|
||||
trans.he = UI/Common/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/Common/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/Common/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/Common/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/Common/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/Common/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/Common/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/Common/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/Common/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/Common/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/Common/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/Common/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/Common/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/Common/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/Common/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/Common/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/Common/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/Common/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/Common/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/Common/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/Common/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/Common/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/Common/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/Common/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/Common/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-administrationui]
|
||||
source_file = UI/AdministrationUI/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/AdministrationUI/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/AdministrationUI/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/AdministrationUI/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/AdministrationUI/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/AdministrationUI/Welsh.lproj/Localizable.strings
|
||||
|
@ -277,43 +168,25 @@ trans.da_DK = UI/AdministrationUI/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/AdministrationUI/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/AdministrationUI/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/AdministrationUI/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/AdministrationUI/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/AdministrationUI/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/AdministrationUI/French.lproj/Localizable.strings
|
||||
trans.he = UI/AdministrationUI/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/AdministrationUI/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/AdministrationUI/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/AdministrationUI/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/AdministrationUI/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/AdministrationUI/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/AdministrationUI/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/AdministrationUI/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/AdministrationUI/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/AdministrationUI/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/AdministrationUI/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/AdministrationUI/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/AdministrationUI/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/AdministrationUI/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/AdministrationUI/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/AdministrationUI/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/AdministrationUI/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/AdministrationUI/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/AdministrationUI/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/AdministrationUI/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/AdministrationUI/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-appointments]
|
||||
source_file = SoObjects/Appointments/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = SoObjects/Appointments/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = SoObjects/Appointments/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = SoObjects/Appointments/Catalan.lproj/Localizable.strings
|
||||
trans.cs = SoObjects/Appointments/Czech.lproj/Localizable.strings
|
||||
trans.cy = SoObjects/Appointments/Welsh.lproj/Localizable.strings
|
||||
|
@ -321,43 +194,25 @@ trans.da_DK = SoObjects/Appointments/Danish.lproj/Localizable.strings
|
|||
trans.de = SoObjects/Appointments/German.lproj/Localizable.strings
|
||||
trans.es_AR = SoObjects/Appointments/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = SoObjects/Appointments/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Appointments/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Appointments/French.lproj/Localizable.strings
|
||||
trans.he = SoObjects/Appointments/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = SoObjects/Appointments/Croatian.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Appointments/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = SoObjects/Appointments/Indonesian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Appointments/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Appointments/Italian.lproj/Localizable.strings
|
||||
trans.ja = SoObjects/Appointments/Japanese.lproj/Localizable.strings
|
||||
trans.lt = SoObjects/Appointments/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = SoObjects/Appointments/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Appointments/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Appointments/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Appointments/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = SoObjects/Appointments/Romanian.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Appointments/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = SoObjects/Appointments/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = SoObjects/Appointments/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = SoObjects/Appointments/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = SoObjects/Appointments/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = SoObjects/Appointments/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Appointments/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.soobjects-contacts]
|
||||
source_file = SoObjects/Contacts/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = SoObjects/Contacts/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = SoObjects/Contacts/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = SoObjects/Contacts/Catalan.lproj/Localizable.strings
|
||||
trans.cs = SoObjects/Contacts/Czech.lproj/Localizable.strings
|
||||
trans.cy = SoObjects/Contacts/Welsh.lproj/Localizable.strings
|
||||
|
@ -365,87 +220,25 @@ trans.da_DK = SoObjects/Contacts/Danish.lproj/Localizable.strings
|
|||
trans.de = SoObjects/Contacts/German.lproj/Localizable.strings
|
||||
trans.es_AR = SoObjects/Contacts/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = SoObjects/Contacts/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = SoObjects/Contacts/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Contacts/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Contacts/French.lproj/Localizable.strings
|
||||
trans.he = SoObjects/Contacts/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = SoObjects/Contacts/Croatian.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Contacts/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = SoObjects/Contacts/Indonesian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Contacts/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Contacts/Italian.lproj/Localizable.strings
|
||||
trans.ja = SoObjects/Contacts/Japanese.lproj/Localizable.strings
|
||||
trans.lt = SoObjects/Contacts/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = SoObjects/Contacts/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Contacts/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Contacts/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Contacts/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = SoObjects/Contacts/Romanian.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Contacts/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = SoObjects/Contacts/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = SoObjects/Contacts/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = SoObjects/Contacts/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = SoObjects/Contacts/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = SoObjects/Contacts/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Contacts/ChineseTaiwan.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.bg_BG = SoObjects/Mailer/Bulgarian.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.eu = SoObjects/Mailer/Basque.lproj/Localizable.strings
|
||||
trans.fi = SoObjects/Mailer/Finnish.lproj/Localizable.strings
|
||||
trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings
|
||||
trans.he = SoObjects/Mailer/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = SoObjects/Mailer/Croatian.lproj/Localizable.strings
|
||||
trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = SoObjects/Mailer/Indonesian.lproj/Localizable.strings
|
||||
trans.is = SoObjects/Mailer/Icelandic.lproj/Localizable.strings
|
||||
trans.it = SoObjects/Mailer/Italian.lproj/Localizable.strings
|
||||
trans.ja = SoObjects/Mailer/Japanese.lproj/Localizable.strings
|
||||
trans.lt = SoObjects/Mailer/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = SoObjects/Mailer/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = SoObjects/Mailer/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
|
||||
trans.pt = SoObjects/Mailer/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = SoObjects/Mailer/Romanian.lproj/Localizable.strings
|
||||
trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings
|
||||
trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = SoObjects/Mailer/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = SoObjects/Mailer/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = SoObjects/Mailer/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = SoObjects/Mailer/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = SoObjects/Mailer/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = SoObjects/Mailer/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = SoObjects/Mailer/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = SoObjects/Mailer/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = SoObjects/Mailer/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
||||
[sogo.ui-mailpartviewers]
|
||||
source_file = UI/MailPartViewers/English.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
trans.ar = UI/MailPartViewers/Arabic.lproj/Localizable.strings
|
||||
trans.bg_BG = UI/MailPartViewers/Bulgarian.lproj/Localizable.strings
|
||||
trans.ca = UI/MailPartViewers/Catalan.lproj/Localizable.strings
|
||||
trans.cs = UI/MailPartViewers/Czech.lproj/Localizable.strings
|
||||
trans.cy = UI/MailPartViewers/Welsh.lproj/Localizable.strings
|
||||
|
@ -453,34 +246,17 @@ trans.da_DK = UI/MailPartViewers/Danish.lproj/Localizable.strings
|
|||
trans.de = UI/MailPartViewers/German.lproj/Localizable.strings
|
||||
trans.es_AR = UI/MailPartViewers/SpanishArgentina.lproj/Localizable.strings
|
||||
trans.es_ES = UI/MailPartViewers/SpanishSpain.lproj/Localizable.strings
|
||||
trans.eu = UI/MailPartViewers/Basque.lproj/Localizable.strings
|
||||
trans.fi = UI/MailPartViewers/Finnish.lproj/Localizable.strings
|
||||
trans.fr = UI/MailPartViewers/French.lproj/Localizable.strings
|
||||
trans.he = UI/MailPartViewers/Hebrew.lproj/Localizable.strings
|
||||
trans.hr_HR = UI/MailPartViewers/Croatian.lproj/Localizable.strings
|
||||
trans.hu = UI/MailPartViewers/Hungarian.lproj/Localizable.strings
|
||||
trans.id_ID = UI/MailPartViewers/Indonesian.lproj/Localizable.strings
|
||||
trans.is = UI/MailPartViewers/Icelandic.lproj/Localizable.strings
|
||||
trans.it = UI/MailPartViewers/Italian.lproj/Localizable.strings
|
||||
trans.ja = UI/MailPartViewers/Japanese.lproj/Localizable.strings
|
||||
trans.lt = UI/MailPartViewers/Lithuanian.lproj/Localizable.strings
|
||||
trans.lv = UI/MailPartViewers/Latvian.lproj/Localizable.strings
|
||||
trans.mk_MK = UI/MailPartViewers/Macedonian.lproj/Localizable.strings
|
||||
trans.nb_NO = UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings
|
||||
trans.nl = UI/MailPartViewers/Dutch.lproj/Localizable.strings
|
||||
trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings
|
||||
trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
|
||||
trans.pt = UI/MailPartViewers/Portuguese.lproj/Localizable.strings
|
||||
trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings
|
||||
trans.ro_RO = UI/MailPartViewers/Romanian.lproj/Localizable.strings
|
||||
trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings
|
||||
trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings
|
||||
trans.sl_SI = UI/MailPartViewers/Slovenian.lproj/Localizable.strings
|
||||
trans.sr = UI/MailPartViewers/Serbian.lproj/Localizable.strings
|
||||
trans.sr@latin = UI/MailPartViewers/SerbianLatin.lproj/Localizable.strings
|
||||
trans.sr_ME@latin = UI/MailPartViewers/Montenegrin.lproj/Localizable.strings
|
||||
trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings
|
||||
trans.tr_TR = UI/MailPartViewers/TurkishTurkey.lproj/Localizable.strings
|
||||
trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings
|
||||
trans.zh_CN = UI/MailPartViewers/ChineseChina.lproj/Localizable.strings
|
||||
trans.zh_TW = UI/MailPartViewers/ChineseTaiwan.lproj/Localizable.strings
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
const tracker = {
|
||||
filename: "Version",
|
||||
updater: require("./Scripts/standard-version-updater.js")
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tagPrefix: "SOGo-",
|
||||
issueUrlFormat: "https://sogo.nu/bugs/view.php?id={{id}}",
|
||||
compareUrlFormat: "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
|
||||
types: [
|
||||
{type: "feat", section: "Features"},
|
||||
{type: "refactor", section: "Enhancements"},
|
||||
{type: "perf", section: "Enhancements"},
|
||||
{type: "i18n", section: "Localization"},
|
||||
{type: "fix", section: "Bug Fixes"},
|
||||
{type: "chore", hidden: true},
|
||||
{type: "docs", hidden: true},
|
||||
{type: "style", hidden: true},
|
||||
{type: "test", hidden: true}
|
||||
],
|
||||
skip: {
|
||||
commit: true,
|
||||
tag: true
|
||||
},
|
||||
packageFiles: [tracker],
|
||||
bumpFiles: [tracker]
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
|
||||
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 */
|
|
@ -1,42 +0,0 @@
|
|||
|
||||
# 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 \
|
||||
NSArray+SyncCache.m \
|
||||
NSString+ActiveSync.m \
|
||||
SOGoActiveSyncDispatcher.m \
|
||||
SOGoActiveSyncDispatcher+Sync.m \
|
||||
SOGoMailObject+ActiveSync.m \
|
||||
SOGoSyncCacheObject.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
|
|
@ -1,4 +0,0 @@
|
|||
ifeq ($(HAS_LIBRARY_ssl),yes)
|
||||
ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
|
||||
BUNDLE_LIBS += -lcrypto
|
||||
endif
|
|
@ -1,25 +0,0 @@
|
|||
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.
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
|
||||
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>
|
||||
|
||||
@class NSDictionary;
|
||||
|
||||
@interface NGDOMElement (ActiveSync)
|
||||
|
||||
- (NSDictionary *) applicationData;
|
||||
|
||||
@end
|
||||
|
||||
#endif // NGDOMELEMENTACTIVESYNC
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
|
||||
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;
|
||||
static NSArray *considerAsSameArray = nil;
|
||||
|
||||
@implementation NGDOMElement (ActiveSync)
|
||||
|
||||
- (BOOL) isTextNode
|
||||
{
|
||||
id <DOMNodeList> children;
|
||||
id <DOMElement> element;
|
||||
int i;
|
||||
|
||||
if ([self nodeType] == DOM_TEXT_NODE)
|
||||
return YES;
|
||||
|
||||
children = [self childNodes];
|
||||
|
||||
for (i = 0; i < [children length]; i++)
|
||||
{
|
||||
element = [children objectAtIndex: i];
|
||||
|
||||
if ([element nodeType] != DOM_TEXT_NODE)
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//
|
||||
// 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", @"Category", @"Exception", @"Add", @"Delete", nil];
|
||||
|
||||
// FIXME
|
||||
/* if the client sends
|
||||
<attachments>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
</attachments>
|
||||
|
||||
the result is a NSarray i.e. {add1; add2; ...}
|
||||
|
||||
if the client sends
|
||||
|
||||
<attachments>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<add>
|
||||
...
|
||||
</add>
|
||||
<delete>
|
||||
...
|
||||
</delete>
|
||||
<delete>
|
||||
...
|
||||
</delete>
|
||||
</attachments>
|
||||
|
||||
|
||||
the result is a NSDictionary ie. Add = { }; Delete = { }; the dictionary would contain only one entry for add
|
||||
*/
|
||||
if (!considerAsSameArray)
|
||||
considerAsSameArray = [[NSArray alloc] initWithObjects: @"Add", @"Delete", 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];
|
||||
|
||||
// We check if the node is a text one or if all its
|
||||
// children are text nodes. This is important to avoid side-effects
|
||||
// in SOPE where "foo & bar" would result into 3 childnodes instead
|
||||
// of just one.
|
||||
if ([(id)element isTextNode])
|
||||
{
|
||||
value = [(id)element textValue];
|
||||
}
|
||||
// Handle inner data - see above for samples
|
||||
else
|
||||
{
|
||||
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]] || [considerAsSameArray containsObject: innerTag])
|
||||
{
|
||||
if ([(id)innerElement isTextNode])
|
||||
[innerElements addObject: [(NGDOMElement *)innerElement textValue]];
|
||||
else
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
if (value && tag)
|
||||
[data setObject: value forKey: tag];
|
||||
|
||||
} // if ([element nodeType] == DOM_ELEMENT_NODE)
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015, 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 <NGMail/NGMailAddress.h>
|
||||
#import <NGMail/NGMailAddressParser.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
@implementation NGMimeMessage (ActiveSync)
|
||||
|
||||
- (void) _addRecipients: (NSEnumerator *) enumerator
|
||||
toArray: (NSMutableArray *) recipients
|
||||
{
|
||||
NGMailAddressParser *parser;
|
||||
NSEnumerator *addressList;
|
||||
NGMailAddress *address;
|
||||
NSString *s;
|
||||
|
||||
while ((s = [enumerator nextObject]))
|
||||
{
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: s];
|
||||
addressList = [[parser parseAddressList] objectEnumerator];
|
||||
|
||||
while ((address = [addressList nextObject]))
|
||||
[recipients addObject: [address address]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) allRecipients
|
||||
{
|
||||
NSMutableArray *recipients;
|
||||
|
||||
recipients = [NSMutableArray array];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"to"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"cc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
[self _addRecipients: [[self headersForKey: @"bcc"] objectEnumerator]
|
||||
toArray: recipients];
|
||||
|
||||
return recipients;
|
||||
}
|
||||
@end
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,619 +0,0 @@
|
|||
/*
|
||||
|
||||
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/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <Contacts/NGVCard+SOGo.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
@implementation NGVCard (ActiveSync)
|
||||
|
||||
//
|
||||
// This function is called for each elements which can be ghosted according to specs.
|
||||
// https://msdn.microsoft.com/en-us/library/gg650908%28v=exchg.80%29.aspx
|
||||
//
|
||||
- (BOOL) _isGhosted: (NSString *) element
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSArray *supportedElements;
|
||||
|
||||
supportedElements = [context objectForKey: @"SupportedElements"];
|
||||
|
||||
// If the client does not include a Supported element in the initial Sync command request for
|
||||
// a folder, then all of the elements that can be ghosted are considered not ghosted.
|
||||
if (!supportedElements)
|
||||
return NO;
|
||||
|
||||
// If the client includes an empty Supported element in the initial Sync command request for
|
||||
// a folder, then all elements that can be ghosted are considered ghosted.
|
||||
if (![supportedElements count])
|
||||
return YES;
|
||||
|
||||
// If the client includes a Supported element that contains child elements in the initial
|
||||
// Sync command request for a folder, then each child element of that Supported element is
|
||||
// considered not ghosted. All elements that can be ghosted that are not included as child
|
||||
// elements of the Supported element are considered ghosted.
|
||||
if (!([supportedElements indexOfObject: element] == NSNotFound))
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSArray *emails, *addresses, *categories, *elements;
|
||||
NSMutableArray *other_addresses;
|
||||
CardElement *n, *homeAdr, *workAdr, *otherAdr;
|
||||
NSMutableString *s, *a;
|
||||
NSString *url;
|
||||
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 = [n flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[s appendFormat: @"<MiddleName xmlns=\"Contacts:\">%@</MiddleName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<Title xmlns=\"Contacts:\">%@</Title>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [n flattenedValueAtIndex: 4 forKey: @""]))
|
||||
[s appendFormat: @"<Suffix xmlns=\"Contacts:\">%@</Suffix>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self fn]))
|
||||
[s appendFormat: @"<FileAs xmlns=\"Contacts:\">%@</FileAs>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self workCompany]))
|
||||
[s appendFormat: @"<CompanyName xmlns=\"Contacts:\">%@</CompanyName>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [[self org] flattenedValueAtIndex: 1 forKey: @""]))
|
||||
[s appendFormat: @"<Department xmlns=\"Contacts:\">%@</Department>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Contacts:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Contacts:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
elements = [self childrenWithTag: @"url"
|
||||
andAttribute: @"type"
|
||||
havingValue: @"work"];
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
url = [[elements objectAtIndex: 0] flattenedValuesForKey: @""];
|
||||
[s appendFormat: @"<WebPage xmlns=\"Contacts:\">%@</WebPage>", [url activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
|
||||
if ((o = [[self uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]))
|
||||
[s appendFormat: @"<IMAddress xmlns=\"Contacts:\">%@</IMAddress>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self nickname]))
|
||||
[s appendFormat: @"<NickName xmlns=\"Contacts:\">%@</NickName>", [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 activeSyncRepresentationInContext: context]];
|
||||
|
||||
|
||||
// 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 activeSyncRepresentationInContext: context], 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];
|
||||
a = [NSMutableString string];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[a appendString: [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [homeAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
|
||||
[a appendFormat: @"\n%@", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", a];
|
||||
|
||||
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];
|
||||
a = [NSMutableString string];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[a appendString: [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [workAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
|
||||
[a appendFormat: @"\n%@", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", a];
|
||||
|
||||
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 Address
|
||||
|
||||
other_addresses = [[self childrenWithTag: @"adr"] mutableCopy];
|
||||
[other_addresses removeObjectsInArray: [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]];
|
||||
[other_addresses removeObjectsInArray: [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"home"]];
|
||||
|
||||
if ([other_addresses count])
|
||||
{
|
||||
otherAdr = [other_addresses objectAtIndex: 0];
|
||||
a = [NSMutableString string];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 2 forKey: @""]))
|
||||
[a appendString: [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
|
||||
[a appendFormat: @"\n%@", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
[s appendFormat: @"<OtherStreet xmlns=\"Contacts:\">%@</OtherStreet>", a];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 3 forKey: @""]))
|
||||
[s appendFormat: @"<OtherCity xmlns=\"Contacts:\">%@</OtherCity>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 4 forKey: @""]))
|
||||
[s appendFormat: @"<OtherState xmlns=\"Contacts:\">%@</OtherState>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 5 forKey: @""]))
|
||||
[s appendFormat: @"<OtherPostalCode xmlns=\"Contacts:\">%@</OtherPostalCode>", [o activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [otherAdr flattenedValueAtIndex: 6 forKey: @""]))
|
||||
[s appendFormat: @"<OtherCountry xmlns=\"Contacts:\">%@</OtherCountry>", [o activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Other, less important fields
|
||||
if ((o = [self birthday]))
|
||||
{
|
||||
[s appendFormat: @"<Birthday xmlns=\"Contacts:\">%@</Birthday>",
|
||||
[[o dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: -12 minutes: 0
|
||||
seconds: 0]
|
||||
activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
if ((o = [self note]))
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
if ((o = [self photo]))
|
||||
[s appendFormat: @"<Picture xmlns=\"Contacts:\">%@</Picture>", o];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
CardElement *element;
|
||||
NSMutableArray *addressLines, *other_addresses;
|
||||
id o, l, m, f, p, s;
|
||||
|
||||
l = m = f = p = s = nil;
|
||||
|
||||
// Contact's note
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setNote: o];
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
else
|
||||
[[self children] removeObjectsInArray: [self childrenWithTag: @"Categories"]];
|
||||
|
||||
// Birthday
|
||||
if ((o = [theValues objectForKey: @"Birthday"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
|
||||
}
|
||||
else if (![self _isGhosted: @"Birthday" inContext: context])
|
||||
{
|
||||
[self setBday: @""];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Business address information
|
||||
//
|
||||
// BusinessStreet
|
||||
// BusinessCity
|
||||
// BusinessPostalCode
|
||||
// BusinessState
|
||||
// BusinessCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"work"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessStreet"]) || ![self _isGhosted: @"BusinessStreet" inContext: context])
|
||||
{
|
||||
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
|
||||
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
|
||||
atIndex: 2 forKey: @""];
|
||||
|
||||
// Extended address line. If there are more than 2 address lines we add them to the extended address line.
|
||||
if ([addressLines count] > 1)
|
||||
{
|
||||
[addressLines removeObjectAtIndex: 0];
|
||||
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
|
||||
atIndex: 1 forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessCity"]) || ![self _isGhosted: @"BusinessCity" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessState"]) || ![self _isGhosted: @"BusinessState" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessPostalCode"]) || ![self _isGhosted: @"BusinessPostalCode" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessCountry"]) || ![self _isGhosted: @"BusinessCountry" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
}
|
||||
|
||||
//
|
||||
// Home address information
|
||||
//
|
||||
// HomeStreet
|
||||
// HomeCity
|
||||
// HomePostalCode
|
||||
// HomeState
|
||||
// HomeCountry
|
||||
//
|
||||
element = [self elementWithTag: @"adr" ofType: @"home"];
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeStreet"]) || ![self _isGhosted: @"HomeStreet" inContext: context])
|
||||
{
|
||||
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
|
||||
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
|
||||
atIndex: 2 forKey: @""];
|
||||
|
||||
// Extended address line. If there are more then 2 address lines we add them to the extended address line.
|
||||
if ([addressLines count] > 1)
|
||||
{
|
||||
[addressLines removeObjectAtIndex: 0];
|
||||
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
|
||||
atIndex: 1 forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeCity"]) || ![self _isGhosted: @"HomeCity" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeState"]) || ![self _isGhosted: @"HomeState" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomePostalCode"]) || ![self _isGhosted: @"HomePostalCode" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomeCountry"]) || ![self _isGhosted: @"HomeCountry" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
}
|
||||
|
||||
// OtherCountry
|
||||
//
|
||||
|
||||
other_addresses = [[self childrenWithTag: @"adr"] mutableCopy];
|
||||
[other_addresses removeObjectsInArray: [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]];
|
||||
[other_addresses removeObjectsInArray: [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"home"]];
|
||||
|
||||
if ([other_addresses count])
|
||||
element = [other_addresses objectAtIndex: 0];
|
||||
else
|
||||
{
|
||||
element = [CardElement elementWithTag: @"adr"];
|
||||
[self addChild: element];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"OtherStreet"]) || ![self _isGhosted: @"OtherStreet" inContext: context])
|
||||
{
|
||||
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
|
||||
|
||||
[element setSingleValue: @""
|
||||
atIndex: 1 forKey: @""];
|
||||
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
|
||||
atIndex: 2 forKey: @""];
|
||||
|
||||
// Extended address line. If there are more then 2 address lines we add them to the extended address line.
|
||||
if ([addressLines count] > 1)
|
||||
{
|
||||
[addressLines removeObjectAtIndex: 0];
|
||||
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
|
||||
atIndex: 1 forKey: @""];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"OtherCity"]) || ![self _isGhosted: @"OtherCity" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"OtherCity"]
|
||||
atIndex: 3 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"OtherState"]) || ![self _isGhosted: @"OtherState" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"OtherState"]
|
||||
atIndex: 4 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"OtherPostalCode"]) || ![self _isGhosted: @"OtherPostalCode" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"OtherPostalCode"]
|
||||
atIndex: 5 forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"OtherCountry"]) || ![self _isGhosted: @"OtherCountry" inContext: context])
|
||||
{
|
||||
[element setSingleValue: [theValues objectForKey: @"OtherCountry"]
|
||||
atIndex: 6 forKey: @""];
|
||||
}
|
||||
|
||||
// Company's name
|
||||
if ((o = [theValues objectForKey: @"CompanyName"]))
|
||||
[self setOrg: o units: nil];
|
||||
else if (![self _isGhosted: @"CompanyName" inContext: context])
|
||||
[self setOrg: @"" units: nil];
|
||||
|
||||
// Department
|
||||
if ((o = [theValues objectForKey: @"Department"]))
|
||||
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
|
||||
else if (![self _isGhosted: @"Department" inContext: context])
|
||||
[self setOrg: nil units: [NSArray arrayWithObjects:@"",nil]];
|
||||
|
||||
// Email addresses
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]) || ![self _isGhosted: @"Email1Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]) || ![self _isGhosted: @"Email2Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
|
||||
// FIXME: revise this when the GUI revamp is done in SOGo
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]) || ![self _isGhosted: @"Email3Address" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"three"];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
// Formatted name
|
||||
// MiddleName
|
||||
// Suffix (II)
|
||||
// Title (Mr.)
|
||||
if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context])
|
||||
[self setFn: [theValues objectForKey: @"FileAs"]];
|
||||
|
||||
if ((o = [theValues objectForKey: @"LastName"]) || ![self _isGhosted: @"LastName" inContext: context])
|
||||
l = o ? o : @"";
|
||||
|
||||
if ((o = [theValues objectForKey: @"FirstName"]) || ![self _isGhosted: @"FirstName" inContext: context])
|
||||
f = o ? o : @"";
|
||||
|
||||
if ((o = [theValues objectForKey: @"MiddleName"]) || ![self _isGhosted: @"MiddleName" inContext: context])
|
||||
m = o ? o : @"";
|
||||
|
||||
if ((o = [theValues objectForKey: @"Title"]) || ![self _isGhosted: @"Title" inContext: context])
|
||||
p = o ? o : @"";
|
||||
|
||||
if ((o = [theValues objectForKey: @"Suffix"]) || ![self _isGhosted: @"Suffix" inContext: context])
|
||||
s = o ? o : @"";
|
||||
|
||||
[self setNWithFamily: l given: f additional: m prefixes: p suffixes: s];
|
||||
|
||||
// IM information
|
||||
if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context])
|
||||
[[self uniqueChildWithTag: @"x-aim"]
|
||||
setSingleValue: [theValues objectForKey: @"IMAddress"]
|
||||
forKey: @""];
|
||||
|
||||
//
|
||||
// Phone numbrrs
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"BusinessPhoneNumber"]) || ![self _isGhosted: @"BusinessPhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"work"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"HomePhoneNumber"]) || ![self _isGhosted: @"HomePhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"home"];
|
||||
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"MobilePhoneNumber"]) || ![self _isGhosted: @"MobilePhoneNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"cell"];
|
||||
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"BusinessFaxNumber"]) || ![self _isGhosted: @"BusinessFaxNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"fax"];
|
||||
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"PagerNumber"]) || ![self _isGhosted: @"PagerNumber" inContext: context])
|
||||
{
|
||||
element = [self elementWithTag: @"tel" ofType: @"pager"];
|
||||
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
|
||||
}
|
||||
|
||||
// Job's title
|
||||
if ((o = [theValues objectForKey: @"JobTitle"]) || ![self _isGhosted: @"JobTitle" inContext: context])
|
||||
[self setTitle: o];
|
||||
|
||||
// WebPage (work)
|
||||
if ((o = [theValues objectForKey: @"WebPage"]) || ![self _isGhosted: @"WebPage" inContext: context])
|
||||
[[self elementWithTag: @"url" ofType: @"work"]
|
||||
setSingleValue: o forKey: @""];
|
||||
|
||||
if ((o = [theValues objectForKey: @"NickName"]) || ![self _isGhosted: @"NickName" inContext: context])
|
||||
[self setNickname: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Picture"]))
|
||||
[self setPhoto: o];
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
|
||||
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 __NSARRAYSYNCCACHE_H__
|
||||
#define __NSARRAYSYNCCACHE_H__
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
@class NSDictionary;
|
||||
|
||||
@interface NSMutableArray (SyncCache)
|
||||
|
||||
- (id) initWithDictionary: (NSDictionary *) theDictionary;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSArray (SyncCache)
|
||||
|
||||
- (NSDictionary *) dictionaryValue;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
|
||||
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 "NSArray+SyncCache.h"
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
#include "SOGoSyncCacheObject.h"
|
||||
|
||||
@implementation NSMutableArray (SyncCache)
|
||||
|
||||
- (id) initWithDictionary: (NSDictionary *) theDictionary
|
||||
{
|
||||
SOGoSyncCacheObject *o;
|
||||
NSArray *allKeys;
|
||||
id key;
|
||||
int i;
|
||||
|
||||
self = [self initWithCapacity: [theDictionary count]];
|
||||
|
||||
allKeys = [theDictionary allKeys];
|
||||
|
||||
for (i = 0; i < [allKeys count]; i++)
|
||||
{
|
||||
key = [allKeys objectAtIndex: i];
|
||||
o = [SOGoSyncCacheObject syncCacheObjectWithUID: key
|
||||
sequence: [theDictionary objectForKey: key]];
|
||||
[self addObject: o];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@implementation NSArray (SyncCache)
|
||||
|
||||
- (NSDictionary *) dictionaryValue
|
||||
{
|
||||
NSMutableDictionary *d;
|
||||
SOGoSyncCacheObject *o;
|
||||
int i;
|
||||
|
||||
d = [NSMutableDictionary dictionary];
|
||||
|
||||
for (i = 0; i < [self count]; i++)
|
||||
{
|
||||
o = [self objectAtIndex: i];
|
||||
[d setObject: [o sequence] forKey: [o uid]];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
|
||||
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>
|
||||
|
||||
#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
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
|
||||
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 <SOGo/SOGoSystemDefaults.h>
|
||||
|
||||
#import <NGExtensions/NGBase64Coding.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#include <wbxml/wbxml.h>
|
||||
|
||||
#define WBXMLDEBUG 0
|
||||
|
||||
@implementation NSData (ActiveSync)
|
||||
|
||||
- (void) _dumpToFile
|
||||
{
|
||||
BOOL debugOn;
|
||||
|
||||
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
|
||||
|
||||
if (debugOn)
|
||||
{
|
||||
NSString *path;
|
||||
|
||||
path = [NSString stringWithFormat: @"/tmp/%@.data", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
[self writeToFile: path atomically: YES];
|
||||
[self errorWithFormat: @"Original data written to: %@", path];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Encodes the data in base64 and strip newline characters
|
||||
//
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSString *tmp, *s;
|
||||
unichar *buf, *start, c;
|
||||
int len, i, j;
|
||||
|
||||
tmp = [self stringByEncodingBase64] ;
|
||||
|
||||
len = [tmp length];
|
||||
|
||||
start = buf = (unichar *)malloc(len*sizeof(unichar));
|
||||
[tmp getCharacters: buf range: NSMakeRange(0, len)];
|
||||
|
||||
for (i = 0, j = 0; i < len; i++)
|
||||
{
|
||||
c = *buf;
|
||||
|
||||
if (!(c == 0xA))
|
||||
{
|
||||
*(start+j) = c;
|
||||
j++;
|
||||
}
|
||||
|
||||
buf++;
|
||||
}
|
||||
|
||||
s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES];
|
||||
|
||||
return AUTORELEASE(s);
|
||||
}
|
||||
|
||||
- (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)
|
||||
{
|
||||
[self errorWithFormat: @"wbxml2xmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [NSData dataWithBytesNoCopy: xml length: xml_len freeWhenDone: YES];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.decoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
return 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)
|
||||
{
|
||||
[self logWithFormat: @"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)
|
||||
{
|
||||
[self errorWithFormat: @"xml2wbxmlFromContent: failed: %s\n", wbxml_errors_string(ret)];
|
||||
[self _dumpToFile];
|
||||
free(wbxml);
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
return nil;
|
||||
}
|
||||
|
||||
data = [NSData dataWithBytesNoCopy: wbxml length: wbxml_len freeWhenDone: YES];
|
||||
|
||||
#if WBXMLDEBUG
|
||||
[data writeToFile: @"/tmp/protocol.encoded" atomically: YES];
|
||||
#endif
|
||||
|
||||
wbxml_conv_xml2wbxml_destroy(conv);
|
||||
|
||||
return data;
|
||||
}
|
||||
@end
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2015, 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 *) attachmentName;
|
||||
- (NSString *) command;
|
||||
- (NSString *) collectionid;
|
||||
- (NSString *) itemid;
|
||||
- (BOOL) acceptsMultiPart;
|
||||
- (NSData *) convertHexStringToBytes;
|
||||
- (NSString *) protocolVersion;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -1,457 +0,0 @@
|
|||
/*
|
||||
|
||||
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 <SOGo/NSString+Utilities.h>
|
||||
#include <SOGo/NSData+Crypto.h>
|
||||
|
||||
#include <NGExtensions/NGBase64Coding.h>
|
||||
#include <NGExtensions/NSString+misc.h>
|
||||
|
||||
static NSArray *easCommandCodes = nil;
|
||||
static NSArray *easCommandParameters = nil;
|
||||
|
||||
@implementation NSString (ActiveSync)
|
||||
|
||||
- (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
|
||||
{
|
||||
return [self safeStringByEscapingXMLString: YES];
|
||||
}
|
||||
|
||||
- (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
|
||||
{
|
||||
NSString *s;
|
||||
id o;
|
||||
|
||||
// We force parsing in the GMT timezone. If we don't do that, the date will be parsed
|
||||
// in the default timezone.
|
||||
s = [NSString stringWithFormat: @"%@ GMT", self];
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y%m%dT%H%M%SZ %Z"];
|
||||
|
||||
if (!o)
|
||||
o = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S.%FZ %Z"];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
- (NSString *) protocolVersion
|
||||
{
|
||||
NSMutableArray *components;
|
||||
|
||||
components = [NSMutableArray arrayWithArray: [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"]];
|
||||
|
||||
// We handle BASE64 encoded queryStrings. See http://msdn.microsoft.com/en-us/library/ee160227%28v=exchg.80%29.aspx for details.
|
||||
if ([components count] == 1)
|
||||
{
|
||||
NSData *queryString;
|
||||
const char* qs_bytes;
|
||||
|
||||
queryString = [[components objectAtIndex: 0] dataByDecodingBase64];
|
||||
|
||||
if (![queryString length])
|
||||
return nil;
|
||||
|
||||
qs_bytes = (const char*)[queryString bytes];
|
||||
|
||||
return [NSString stringWithFormat:@"%.1f", (float)((uint8_t)qs_bytes[0]/10)];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) _valueForParameter: (NSString *) theParameter
|
||||
{
|
||||
NSMutableArray *components;
|
||||
NSString *s;
|
||||
int i;
|
||||
|
||||
components = [NSMutableArray arrayWithArray: [[[self componentsSeparatedByString: @"?"] lastObject] componentsSeparatedByString: @"&"]];
|
||||
|
||||
// We handle BASE64 encoded queryStrings. See http://msdn.microsoft.com/en-us/library/ee160227%28v=exchg.80%29.aspx for details.
|
||||
if ([components count] == 1)
|
||||
{
|
||||
NSString *deviceType, *parameterValue;
|
||||
NSData *queryString;
|
||||
|
||||
int cmd_code, deviceid_length, policy_length, devicetype_length, parameter_code, parameter_length, i;
|
||||
const char* qs_bytes;
|
||||
|
||||
queryString = [[components objectAtIndex: 0] dataByDecodingBase64];
|
||||
|
||||
if (![queryString length])
|
||||
return nil;
|
||||
|
||||
qs_bytes = (const char*)[queryString bytes];
|
||||
|
||||
if (!easCommandCodes)
|
||||
{
|
||||
easCommandCodes = [NSArray arrayWithObjects:@"Sync", @"SendMail", @"SmartForward", @"SmartReply", @"GetAttachment", @"na", @"na", @"na", @"na",
|
||||
@"FolderSync", @"FolderCreate", @"FolderDelete", @"FolderUpdate", @"MoveItems", @"GetItemEstimate", @"MeetingResponse",
|
||||
@"Search", @"Settings", @"Ping", @"ItemOperations", @"Provision", @"ResolveRecipients", @"ValidateCert", nil];
|
||||
RETAIN(easCommandCodes);
|
||||
}
|
||||
|
||||
if (!easCommandParameters)
|
||||
{
|
||||
easCommandParameters = [NSArray arrayWithObjects:@"AttachmentName", @"CollectionId", @"na", @"ItemId", @"LongId", @"na", @"Occurrence", @"Options", @"User", nil];
|
||||
RETAIN(easCommandParameters);
|
||||
}
|
||||
|
||||
// Command code, 1 byte, ie.: cmd=
|
||||
cmd_code = qs_bytes[1];
|
||||
|
||||
// Check whether the command code is within the known range.
|
||||
if (cmd_code < 0 || cmd_code > 22)
|
||||
return nil;
|
||||
|
||||
[components addObject: [NSString stringWithFormat: @"cmd=%@", [easCommandCodes objectAtIndex: cmd_code]]];
|
||||
|
||||
// Device ID length and Device ID (variable)
|
||||
deviceid_length = qs_bytes[4];
|
||||
[components addObject: [NSString stringWithFormat: @"deviceId=%@", [[NSData encodeDataAsHexString: [queryString subdataWithRange: NSMakeRange(5, deviceid_length)]] uppercaseString]]];
|
||||
|
||||
// Device type length and type (variable)
|
||||
policy_length = qs_bytes[5+deviceid_length];
|
||||
devicetype_length = qs_bytes[5+deviceid_length+1+policy_length];
|
||||
deviceType = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(5+deviceid_length+1+policy_length+1, devicetype_length)]
|
||||
encoding: NSASCIIStringEncoding];
|
||||
AUTORELEASE(deviceType);
|
||||
|
||||
[components addObject: [NSString stringWithFormat: @"deviceType=%@", deviceType]];
|
||||
|
||||
// Command Parameters
|
||||
i = 5+deviceid_length+1+policy_length+1+devicetype_length;
|
||||
|
||||
while (i < [queryString length])
|
||||
{
|
||||
parameter_code = qs_bytes[i];
|
||||
parameter_length = qs_bytes[i+1];
|
||||
parameterValue = [[NSString alloc] initWithData: [queryString subdataWithRange: NSMakeRange(i+1+1, parameter_length)]
|
||||
encoding: NSASCIIStringEncoding];
|
||||
|
||||
AUTORELEASE(parameterValue);
|
||||
|
||||
// parameter_code 7 == Options
|
||||
// http://msdn.microsoft.com/en-us/library/ee237789(v=exchg.80).aspx
|
||||
if (parameter_code == 7 && ! [parameterValue isEqualToString: @"\000"] )
|
||||
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code],
|
||||
([parameterValue isEqualToString: @"\001"]) ? @"SaveInSent" : @"AcceptMultiPart"]];
|
||||
else
|
||||
[components addObject: [NSString stringWithFormat: @"%@=%@", [easCommandParameters objectAtIndex: parameter_code], parameterValue]];
|
||||
|
||||
i = i + 1 + 1 + parameter_length;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// This method extracts the "AttachmentName" from a URI:
|
||||
//
|
||||
// /SOGo/Microsoft-Server-ActiveSync?Cmd=GetAttachment&User=sogo&DeviceId=HTCa04b4932597acd3f2dc1a918b9728&DeviceType=htcvision&AttachmentName=mail/TestFldr/8/2
|
||||
//
|
||||
- (NSString *) attachmentName
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"ATTACHMENTNAME="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (NSString *) command
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"CMD="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString *) itemid
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"ITEMID="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString *) collectionid
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"COLLECTIONID="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (BOOL) acceptsMultiPart
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"OPTIONS="];
|
||||
|
||||
if (s && [s rangeOfString: @"AcceptMultiPart" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// FIXME: combine with our OpenChange code.
|
||||
//
|
||||
- (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
|
|
@ -1,12 +0,0 @@
|
|||
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/
|
||||
|
||||
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.
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
|
||||
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>
|
||||
|
||||
@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
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2015, 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 NSCalendarDate;
|
||||
@class NSException;
|
||||
@class NSMutableDictionary;
|
||||
@class NSURL;
|
||||
@class NSNumber;
|
||||
|
||||
static volatile BOOL easShouldTerminate = NO;
|
||||
|
||||
@interface SOGoActiveSyncDispatcher : NSObject
|
||||
{
|
||||
NSURL *folderTableURL;
|
||||
NSDictionary *imapFolderGUIDS;
|
||||
id context;
|
||||
|
||||
NSNumber *syncRequest;
|
||||
BOOL debugOn;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) globalMetadataForDevice;
|
||||
|
||||
- (id) collectionFromId: (NSString *) theCollectionId
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||
|
||||
- (NSException *) dispatchRequest: (id) theRequest
|
||||
inResponse: (id) theResponse
|
||||
context: (id) theContext;
|
||||
|
||||
- (NSURL *) folderTableURL;
|
||||
- (void) ensureFolderTableExists;
|
||||
- (BOOL) easShouldTerminate;
|
||||
|
||||
@end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
|
||||
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;
|
||||
- (NSString *) storeMail: (NSDictionary *) theValues
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
inContext: (WOContext *) _context;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
|
||||
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 __SOGOSYNCCACHEOBJECT_H__
|
||||
#define __SOGOSYNCCACHEOBJECT_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSNull;
|
||||
|
||||
@interface SOGoSyncCacheObject : NSObject
|
||||
{
|
||||
@public
|
||||
id _uid;
|
||||
id _sequence;
|
||||
}
|
||||
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
|
||||
- (id) uid;
|
||||
- (void) setUID: (id) theUID;
|
||||
- (id) sequence;
|
||||
- (void) setSequence: (id) theSequence;
|
||||
|
||||
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject;
|
||||
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
|
||||
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 "SOGoSyncCacheObject.h"
|
||||
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
static Class NSNullK;
|
||||
|
||||
@implementation SOGoSyncCacheObject
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSNullK = [NSNull class];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_uid = nil;
|
||||
_sequence = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [[self alloc] init];
|
||||
|
||||
[o setUID: [NSNumber numberWithInt: [theUID intValue]]];
|
||||
[o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
|
||||
|
||||
return [o autorelease];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(_uid);
|
||||
RELEASE(_sequence);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) uid
|
||||
{
|
||||
return [_uid description];
|
||||
}
|
||||
|
||||
- (void) setUID: (id) theUID
|
||||
{
|
||||
ASSIGN(_uid, theUID);
|
||||
}
|
||||
|
||||
- (id) sequence
|
||||
{
|
||||
return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
|
||||
}
|
||||
|
||||
- (void) setSequence: (id) theSequence
|
||||
{
|
||||
ASSIGN(_sequence, theSequence);
|
||||
}
|
||||
|
||||
|
||||
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
return [self->_uid compare: theSyncCacheObject->_uid];
|
||||
}
|
||||
|
||||
//
|
||||
// We might get NSNull values here, so if both are NSNull instances,
|
||||
// we sort by UID. If both sequences are equal, we also sort by UID.
|
||||
//
|
||||
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
|
||||
{
|
||||
if ([self->_sequence isEqual: [NSNull null]] &&
|
||||
[theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return [self compareUID: theSyncCacheObject];
|
||||
|
||||
if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return NSOrderedDescending;
|
||||
|
||||
if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
|
||||
return NSOrderedAscending;
|
||||
|
||||
// Must check this here, to avoid comparing NSNull objects
|
||||
if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
|
||||
return [self compareUID: theSyncCacheObject];
|
||||
|
||||
return [self->_sequence compare: theSyncCacheObject->_sequence];
|
||||
}
|
||||
|
||||
- (NSString *) description
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@-%@", _uid, _sequence];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
|
||||
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.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
|
|
@ -1,34 +0,0 @@
|
|||
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/Versions/Current/sogo \
|
||||
-L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L/usr/local/lib \
|
||||
-Wl,-rpath,$(SOGO_SYSLIBDIR)/sogo
|
||||
|
||||
BUNDLE_LIBS += \
|
||||
-lSOGo \
|
||||
-lGDLContentStore \
|
||||
-lGDLAccess \
|
||||
-lNGObjWeb \
|
||||
-lNGCards -lNGMime -lNGLdap \
|
||||
-lNGStreams -lNGExtensions -lEOControl \
|
||||
-lDOM -lSaxObjC -lSBJson
|
||||
|
||||
ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
|
||||
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 <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/NSCalendarDate+NGCards.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
|
||||
@implementation iCalAlarm (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSCalendarDate *nextAlarmDate;
|
||||
NSInteger delta;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
nextAlarmDate = [self nextAlarmDate];
|
||||
delta = (int)(([[(iCalEvent *)parent startDate] timeIntervalSince1970] - [nextAlarmDate timeIntervalSince1970])/60);
|
||||
|
||||
if ([parent isKindOfClass: [iCalEvent class]])
|
||||
{
|
||||
// don't send negative reminder - not supported
|
||||
if (delta > 0)
|
||||
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", (int)delta];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<ReminderTime xmlns=\"Task:\">%@</ReminderTime>", [nextAlarmDate activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
iCalTrigger *trigger;
|
||||
id o;
|
||||
|
||||
if ((o = [theValues objectForKey: @"Reminder"]))
|
||||
{
|
||||
// Outlook: if reminder is set to 0 minutes before starttime, 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];
|
||||
if (![[self action] length])
|
||||
[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: @""];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((o = [theValues objectForKey: @"ReminderTime"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
trigger = [iCalTrigger elementWithTag: @"TRIGGER"];
|
||||
[trigger setValueType: @"DATE-TIME"];
|
||||
[trigger setSingleValue: [NSString stringWithFormat: @"%@Z", [o iCalFormattedDateTimeString]] forKey: @""];
|
||||
|
||||
if ((o = [theValues objectForKey: @"ReminderSet"]))
|
||||
{
|
||||
if ([o intValue] == 0)
|
||||
[trigger setValue: 0 ofAttribute: @"x-webstatus" to: @"triggered"];
|
||||
}
|
||||
|
||||
[self setTrigger: trigger];
|
||||
if (![[self action] length])
|
||||
[self setAction: @"DISPLAY"];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
|
||||
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;
|
||||
- (void) changeParticipationStatus: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
component: (id) component;
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -1,959 +0,0 @@
|
|||
/*
|
||||
|
||||
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/NSDictionary.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/NSCalendarDate+NGCards.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
#import <Appointments/iCalRepeatableEntityObject+SOGo.h>
|
||||
#import <Appointments/SOGoAppointmentObject.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;
|
||||
}
|
||||
|
||||
//
|
||||
// Possible values are:
|
||||
// 0 Free
|
||||
// 1 Tentative
|
||||
// 2 Busy
|
||||
// 3 Out of Office
|
||||
// 4 Working elsewhere
|
||||
//
|
||||
- (int) _busyStatus: (iCalPerson *) attendee
|
||||
{
|
||||
int attendee_status;
|
||||
|
||||
attendee_status = 2;
|
||||
|
||||
if ([[attendee partStat] caseInsensitiveCompare: @"DECLINED"] == NSOrderedSame)
|
||||
attendee_status = 0;
|
||||
else if ([[attendee partStat] caseInsensitiveCompare: @"TENTATIVE"] == NSOrderedSame)
|
||||
attendee_status = 1;
|
||||
|
||||
return attendee_status;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSArray *attendees, *categories;
|
||||
|
||||
iCalPerson *organizer, *attendee;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
int v, i, meetingStatus;
|
||||
|
||||
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]];
|
||||
|
||||
// Timezone
|
||||
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
|
||||
|
||||
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
|
||||
if ([self startDate])
|
||||
{
|
||||
if ([self isAllDay] && !tz && [[context objectForKey: @"ASProtocolVersion"] floatValue] < 16.0)
|
||||
[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] && !tz && [[context objectForKey: @"ASProtocolVersion"] floatValue] < 16.0)
|
||||
[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]];
|
||||
}
|
||||
|
||||
if (!tz)
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
|
||||
if (![self recurrenceId])
|
||||
[s appendFormat: @"<TimeZone xmlns=\"Calendar:\">%@</TimeZone>", [tz activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Organizer and other invitations related properties
|
||||
if ((organizer = [self organizer]))
|
||||
{
|
||||
meetingStatus = 1; // meeting and the user is the meeting organizer.
|
||||
o = [organizer rfc822Email];
|
||||
if (![self recurrenceId] && [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 activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
}
|
||||
|
||||
// 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] activeSyncRepresentationInContext: context]];
|
||||
if ([[attendee cn] length])
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee cn] activeSyncRepresentationInContext: context]];
|
||||
else
|
||||
[s appendFormat: @"<Attendee_Name xmlns=\"Calendar:\">%@</Attendee_Name>", [[attendee rfc822Email] activeSyncRepresentationInContext: context]];
|
||||
|
||||
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>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
meetingStatus = 0; // The event is an appointment, which has no attendees.
|
||||
}
|
||||
|
||||
// This depends on the 'NEEDS-ACTION' parameter.
|
||||
// This will trigger the SendMail command
|
||||
if ([self userIsAttendee: [context activeUser]])
|
||||
{
|
||||
iCalPerson *attendee;
|
||||
|
||||
int attendee_status;
|
||||
|
||||
meetingStatus = 3; // event is a meeting, and the user is not the meeting organizer
|
||||
|
||||
attendee = [self userAsAttendee: [context activeUser]];
|
||||
attendee_status = [self _attendeeStatus: attendee];
|
||||
|
||||
[s appendFormat: @"<ResponseRequested xmlns=\"Calendar:\">%d</ResponseRequested>", 1];
|
||||
[s appendFormat: @"<ResponseType xmlns=\"Calendar:\">%d</ResponseType>", attendee_status];
|
||||
[s appendFormat: @"<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>", [self _busyStatus: attendee]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's a normal event (i.e. with no organizer/attendee) or we are the organizer of an event
|
||||
// invitation, we set the busy status depending on TRANSP.
|
||||
[s appendFormat: @"<BusyStatus xmlns=\"Calendar:\">%d</BusyStatus>", (([self isOpaque]) ? 2 : 0)];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<MeetingStatus xmlns=\"Calendar:\">%d</MeetingStatus>", meetingStatus];
|
||||
|
||||
// Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx
|
||||
if ([[self summary] length])
|
||||
[s appendFormat: @"<Subject xmlns=\"Calendar:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Location
|
||||
if ([[self location] length])
|
||||
{
|
||||
if ([[context objectForKey: @"ASProtocolVersion"] floatValue] >= 16.0)
|
||||
[s appendFormat: @"<Location xmlns=\"AirSyncBase:\"><DisplayName>%@</DisplayName></Location>", [[self location] activeSyncRepresentationInContext: context]];
|
||||
else
|
||||
[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 recurrenceId] && [[self uid] length])
|
||||
[s appendFormat: @"<UID xmlns=\"Calendar:\">%@</UID>", [[self uid] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Recurrence rules
|
||||
if ([self isRecurrent])
|
||||
[s appendString: [[[self recurrenceRules] lastObject] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Comment
|
||||
o = [self comment];
|
||||
//if (![self recurrenceId] && [o length])
|
||||
if ([o length])
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
|
||||
if ([[context objectForKey: @"ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Calendar:\">%@</Body>", o];
|
||||
[s appendString: @"<BodyTruncated xmlns=\"Calendar:\">0</BodyTruncated>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
}
|
||||
|
||||
// Sensitivity
|
||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Calendar:\">%d</Sensitivity>", v];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Calendar:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Calendar:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
|
||||
// TODO: improve this to handle more alarm types
|
||||
if ([self hasAlarms])
|
||||
{
|
||||
iCalAlarm *alarm;
|
||||
|
||||
alarm = [self firstSupportedAlarm];
|
||||
[s appendString: [alarm activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
|
||||
// Exceptions
|
||||
if ([self isRecurrent])
|
||||
{
|
||||
NSMutableArray *components, *exdates;
|
||||
iCalEvent *current_component;
|
||||
NSString *recurrence_id;
|
||||
|
||||
unsigned int count, max, i;
|
||||
|
||||
components = [NSMutableArray arrayWithArray: [[self parent] events]];
|
||||
max = [components count];
|
||||
|
||||
if (max > 1 || [self hasExceptionDates])
|
||||
{
|
||||
exdates = [NSMutableArray arrayWithArray: [self exceptionDates]];
|
||||
|
||||
[s appendString: @"<Exceptions xmlns=\"Calendar:\">"];
|
||||
|
||||
for (count = 1; count < max; count++)
|
||||
{
|
||||
current_component = [components objectAtIndex: count] ;
|
||||
|
||||
if ([self isAllDay])
|
||||
{
|
||||
recurrence_id = [NSString stringWithFormat: @"%@",
|
||||
[[[current_component recurrenceId] dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -[userTimeZone secondsFromGMTForDate:
|
||||
[current_component recurrenceId]]]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
else
|
||||
{
|
||||
recurrence_id = [NSString stringWithFormat: @"%@", [[current_component recurrenceId]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
|
||||
[s appendString: @"<Exception>"];
|
||||
[s appendFormat: @"<Exception_StartTime xmlns=\"Calendar:\">%@</Exception_StartTime>", recurrence_id];
|
||||
[s appendFormat: @"%@", [current_component activeSyncRepresentationInContext: context]];
|
||||
[s appendString: @"</Exception>"];
|
||||
}
|
||||
|
||||
for (i = 0; i < [exdates count]; i++)
|
||||
{
|
||||
[s appendString: @"<Exception>"];
|
||||
[s appendString: @"<Exception_Deleted>1</Exception_Deleted>"];
|
||||
|
||||
if ([self isAllDay])
|
||||
{
|
||||
recurrence_id = [NSString stringWithFormat: @"%@",
|
||||
[[[[exdates objectAtIndex: i] asCalendarDate] dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: -[userTimeZone secondsFromGMTForDate:
|
||||
[[exdates objectAtIndex: i] asCalendarDate]]]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
else
|
||||
{
|
||||
recurrence_id = [NSString stringWithFormat: @"%@", [[[exdates objectAtIndex: i] asCalendarDate]
|
||||
activeSyncRepresentationWithoutSeparatorsInContext: context]];
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Exception_StartTime xmlns=\"Calendar:\">%@</Exception_StartTime>", recurrence_id];
|
||||
[s appendString: @"</Exception>"];
|
||||
}
|
||||
|
||||
[s appendString: @"</Exceptions>"];
|
||||
}
|
||||
}
|
||||
|
||||
if (![self recurrenceId])
|
||||
[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; //, *oldstart;
|
||||
NSCalendarDate *oldstart;
|
||||
NSTimeZone *userTimeZone;
|
||||
iCalTimeZone *tz;
|
||||
iCalAlarm *alarm;
|
||||
id o;
|
||||
int deltasecs;
|
||||
|
||||
BOOL isAllDay;
|
||||
|
||||
NSMutableArray *occurences;
|
||||
|
||||
occurences = [NSMutableArray arrayWithArray: [[self parent] events]];
|
||||
|
||||
if ((o = [theValues objectForKey: ([[context objectForKey: @"ASProtocolVersion"] floatValue] >= 16.0) ? @"ClientUid" : @"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;
|
||||
}
|
||||
else if ([occurences count] && !([theValues objectForKey: @"AllDayEvent"]))
|
||||
{
|
||||
// If the occurence has no AllDay tag use it from master.
|
||||
isAllDay = [[occurences objectAtIndex: 0] isAllDay];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- free, 1- tentative, 2- busy and 3- out of office
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"BusyStatus"]))
|
||||
{
|
||||
if ([o boolValue])
|
||||
[self setTransparency: @"OPAQUE"];
|
||||
else
|
||||
[self setTransparency: @"TRANSPARENT"];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- normal, 1- personal, 2- private and 3-confidential
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"Sensitivity"]))
|
||||
{
|
||||
switch ([o intValue])
|
||||
{
|
||||
case 2:
|
||||
[self setAccessClass: @"PRIVATE"];
|
||||
break;
|
||||
case 3:
|
||||
[self setAccessClass: @"CONFIDENTIAL"];
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
[self setAccessClass: @"PUBLIC"];
|
||||
}
|
||||
}
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
|
||||
// We ignore TimeZone sent by mobile devices for now.
|
||||
// Some Windows devices don't send during event updates.
|
||||
//if ((o = [theValues objectForKey: @"TimeZone"]))
|
||||
// {
|
||||
// }
|
||||
//else
|
||||
{
|
||||
// We haven't received a timezone, let's use the user's timezone
|
||||
// specified in SOGo for now.
|
||||
userTimeZone = [[[context activeUser] userDefaults] timeZone];
|
||||
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
|
||||
[(iCalCalendar *) parent addTimeZone: tz];
|
||||
}
|
||||
|
||||
// FIXME: merge with iCalToDo
|
||||
if ([[context objectForKey: @"ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
if ((o = [theValues objectForKey: @"Body"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
|
||||
if ([[context objectForKey: @"ASProtocolVersion"] floatValue] < 16.0 && (o = [theValues objectForKey: @"Location"]))
|
||||
[self setLocation: o];
|
||||
else if ([[context objectForKey: @"ASProtocolVersion"] floatValue] >= 16.0 && (o = [theValues objectForKey: @"Location"]) && [o isKindOfClass: [NSDictionary class]])
|
||||
[self setLocation: [o objectForKey: @"DisplayName"]];
|
||||
|
||||
deltasecs = 0;
|
||||
start = nil;
|
||||
|
||||
if ((o = [theValues objectForKey: @"StartTime"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
oldstart = [start dateTime];
|
||||
[start setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
[start setDate: o];
|
||||
[start setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[start setDateTime: o];
|
||||
}
|
||||
|
||||
// Calculate delta if start date has been changed.
|
||||
if (oldstart)
|
||||
deltasecs = [[start dateTime ] timeIntervalSinceDate: oldstart] * -1;
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"EndTime"]))
|
||||
{
|
||||
o = [o calendarDate];
|
||||
end = (iCalDateTime *) [self uniqueChildWithTag: @"dtend"];
|
||||
[end setTimeZone: tz];
|
||||
|
||||
if (isAllDay)
|
||||
{
|
||||
[end setDate: o];
|
||||
[end setTimeZone: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[end setDateTime: o];
|
||||
}
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Reminder"]) && [o length])
|
||||
{
|
||||
if ([self hasAlarms])
|
||||
alarm = [[self firstSupportedAlarm] mutableCopy];
|
||||
else
|
||||
alarm = [[iCalAlarm alloc] init];
|
||||
|
||||
// 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
|
||||
//
|
||||
|
||||
[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"]) && [o isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
rule = [[iCalRecurrenceRule alloc] init];
|
||||
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
|
||||
RELEASE(rule);
|
||||
|
||||
[rule takeActiveSyncValues: o inContext: context];
|
||||
|
||||
// Exceptions
|
||||
if ((o = [theValues objectForKey: @"Exceptions"]) && [o isKindOfClass: [NSArray class]])
|
||||
{
|
||||
NSCalendarDate *recurrenceId, *adjustedRecurrenceId, *currentId;
|
||||
NSMutableArray *exdates, *exceptionstouched;
|
||||
iCalEvent *currentOccurence;
|
||||
NSDictionary *exception;
|
||||
|
||||
unsigned int i, count, max;
|
||||
|
||||
[self removeAllExceptionDates];
|
||||
|
||||
exdates = [NSMutableArray array];
|
||||
exceptionstouched = [NSMutableArray array];
|
||||
|
||||
for (i = 0; i < [o count]; i++)
|
||||
{
|
||||
exception = [o objectAtIndex: i];
|
||||
|
||||
if (![[exception objectForKey: @"Exception_Deleted"] intValue])
|
||||
{
|
||||
recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
|
||||
|
||||
if (isAllDay)
|
||||
recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0
|
||||
seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
|
||||
|
||||
// When moving the calendar entry (i.e. changing the startDate) iOS and Android sometimes send a value for
|
||||
// Exception_StartTime which is not what we expect.
|
||||
// With this we make sure the recurrenceId is always correct.
|
||||
//
|
||||
// iOS problem - If the master is a no-allday-event but the exception is, an invalid Exception_StartTime is in the request.
|
||||
// e.g. it sends 20150409T040000Z instead of 20150409T060000Z;
|
||||
// This might be a special case since iPhone doesn't allow an allday-exception on a non-allday-event.
|
||||
if (!([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay] minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
|
||||
recurrenceId = [recurrenceId hour:[[start dateTime] hourOfDay] minute:[[start dateTime] minuteOfHour]];
|
||||
|
||||
// We need to store the recurrenceIds and exception dates adjusted by deltasecs.
|
||||
// This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
|
||||
adjustedRecurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
|
||||
hours: 0 minutes: 0 seconds: deltasecs];
|
||||
|
||||
// search for an existing occurence and update it
|
||||
max = [occurences count];
|
||||
currentOccurence = nil;
|
||||
count = 1;
|
||||
while (count < max)
|
||||
{
|
||||
currentOccurence = [occurences objectAtIndex: count] ;
|
||||
currentId = [currentOccurence recurrenceId];
|
||||
|
||||
if ([currentId compare: adjustedRecurrenceId] == NSOrderedSame)
|
||||
{
|
||||
[exceptionstouched addObject: adjustedRecurrenceId];
|
||||
[currentOccurence takeActiveSyncValues: exception inContext: context];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
currentOccurence = nil;
|
||||
}
|
||||
|
||||
// Create a new occurence if we found none to update.
|
||||
if (!currentOccurence)
|
||||
{
|
||||
iCalDateTime *recid;
|
||||
|
||||
currentOccurence = [self mutableCopy];
|
||||
[currentOccurence removeAllRecurrenceRules];
|
||||
[currentOccurence removeAllExceptionRules];
|
||||
[currentOccurence removeAllExceptionDates];
|
||||
|
||||
[[self parent] addToEvents: currentOccurence];
|
||||
[currentOccurence takeActiveSyncValues: exception inContext: context];
|
||||
|
||||
recid = (iCalDateTime *)[currentOccurence uniqueChildWithTag: @"recurrence-id"];
|
||||
if (isAllDay)
|
||||
[recid setDate: adjustedRecurrenceId];
|
||||
else
|
||||
[recid setDateTime: adjustedRecurrenceId];
|
||||
|
||||
[exceptionstouched addObject: [recid dateTime]];
|
||||
}
|
||||
}
|
||||
else if ([[exception objectForKey: @"Exception_Deleted"] intValue])
|
||||
{
|
||||
recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
|
||||
|
||||
if (isAllDay)
|
||||
recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0
|
||||
days: 0 hours: 0 minutes: 0
|
||||
seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
|
||||
|
||||
// We add only valid exception dates.
|
||||
if ([self doesOccurOnDate: recurrenceId] &&
|
||||
([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay]
|
||||
minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
|
||||
{
|
||||
// We need to store the recurrenceIds and exception dates adjusted by deltasecs.
|
||||
// This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
|
||||
[exdates addObject: [recurrenceId dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: deltasecs]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update exception dates in master event.
|
||||
max = [exdates count];
|
||||
if (max > 0)
|
||||
{
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
if (([exceptionstouched indexOfObject: [exdates objectAtIndex: count]] == NSNotFound))
|
||||
[self addToExceptionDates: [exdates objectAtIndex: count]];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all exceptions included in the request.
|
||||
max = [occurences count];
|
||||
count = 1; // skip the master event
|
||||
while (count < max)
|
||||
{
|
||||
currentOccurence = [occurences objectAtIndex: count] ;
|
||||
currentId = [currentOccurence recurrenceId];
|
||||
|
||||
// Delete all occurences which are not touched (modified/added).
|
||||
if (([exceptionstouched indexOfObject: currentId] == NSNotFound))
|
||||
[[[self parent] children] removeObject: currentOccurence];
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else if ([self isRecurrent])
|
||||
{
|
||||
// We have no exceptions in request but there is a recurrence rule.
|
||||
// Remove all excpetions and exception dates.
|
||||
iCalEvent *currentOccurence;
|
||||
unsigned int count, max;
|
||||
|
||||
[self removeAllExceptionDates];
|
||||
max = [occurences count];
|
||||
count = 1; // skip the master event
|
||||
while (count < max)
|
||||
{
|
||||
currentOccurence = [occurences objectAtIndex: count];
|
||||
[[[self parent] children] removeObject: currentOccurence];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([self isRecurrent])
|
||||
{
|
||||
// We have no recurrence rule in request.
|
||||
// Remove all exceptions, exception dates and recurrence rules.
|
||||
iCalEvent *currentOccurence;
|
||||
unsigned int count, max;
|
||||
|
||||
[self removeAllRecurrenceRules];
|
||||
[self removeAllExceptionRules];
|
||||
[self removeAllExceptionDates];
|
||||
|
||||
max = [occurences count];
|
||||
count = 1; // skip the master event
|
||||
while (count < max)
|
||||
{
|
||||
currentOccurence = [occurences objectAtIndex: count];
|
||||
[[[self parent] children] removeObject: currentOccurence];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||
{
|
||||
//
|
||||
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
||||
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
||||
// and there's no organizer, we fake one.
|
||||
//
|
||||
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: [[[context activeUser] primaryIdentity] objectForKey: @"email"]];
|
||||
[person setCn: [[context activeUser] cn]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
|
||||
//
|
||||
// When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3.
|
||||
// Use the organizer from the request if the event has no organizer.
|
||||
//
|
||||
if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
||||
{
|
||||
iCalPerson *person;
|
||||
person = [iCalPerson elementWithTag: @"organizer"];
|
||||
[person setEmail: [theValues objectForKey: @"Organizer_Email"]];
|
||||
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
|
||||
[person setPartStat: @"ACCEPTED"];
|
||||
[self setOrganizer: person];
|
||||
}
|
||||
}
|
||||
|
||||
// Attendees - we don't touch the values if we're an attendee. This is gonna
|
||||
// be done automatically by the ActiveSync client when invoking MeetingResponse.
|
||||
if (![self userIsAttendee: [context activeUser]])
|
||||
{
|
||||
// Windows phones sens sometimes an empty Attendees tag.
|
||||
// We check it's an array before processing it.
|
||||
if ((o = [theValues objectForKey: @"Attendees"]) && [o isKindOfClass: [NSArray class]])
|
||||
{
|
||||
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];
|
||||
|
||||
if ([self isOrganizer: [attendee objectForKey: @"Attendee_Email"]])
|
||||
continue;
|
||||
|
||||
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"];
|
||||
[person setRsvp: @"TRUE"];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: handle Attendee_Type
|
||||
|
||||
[attendees addObject: person];
|
||||
}
|
||||
|
||||
[self setAttendees: attendees];
|
||||
}
|
||||
}
|
||||
|
||||
[self setLastModified: [NSCalendarDate calendarDate]];
|
||||
}
|
||||
|
||||
- (void) changeParticipationStatus: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
component: (id) component
|
||||
{
|
||||
NSString *status;
|
||||
iCalAlarm *alarm;
|
||||
iCalPerson *attendee;
|
||||
id o;
|
||||
|
||||
|
||||
attendee = [self userAsAttendee: [context activeUser]];
|
||||
status = [attendee partStat];
|
||||
alarm = nil;
|
||||
|
||||
// See: https://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx
|
||||
//
|
||||
// 0 == Free
|
||||
// 1 == Tentative
|
||||
// 2 == Busy
|
||||
// 3 == Out of Office
|
||||
// 4 == Working elsehwere
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"BusyStatus"]))
|
||||
{
|
||||
// We translate >= 2 into ACCEPTED
|
||||
if ([o intValue] == 0)
|
||||
status = @"NEEDS-ACTION";
|
||||
else if ([o intValue] >= 2)
|
||||
status = @"ACCEPTED";
|
||||
else
|
||||
status = @"TENTATIVE";
|
||||
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Reminder"]) && [o length])
|
||||
{
|
||||
if ([self hasAlarms])
|
||||
alarm = [[self firstSupportedAlarm] mutableCopy];
|
||||
else
|
||||
alarm = [[iCalAlarm alloc] init];
|
||||
|
||||
[alarm takeActiveSyncValues: theValues inContext: context];
|
||||
|
||||
|
||||
}
|
||||
|
||||
// There's no delegate in EAS
|
||||
[(SOGoAppointmentObject *) component changeParticipationStatus: status
|
||||
withDelegate: nil
|
||||
alarm: alarm];
|
||||
RELEASE(alarm);
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,399 +0,0 @@
|
|||
/*
|
||||
|
||||
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/iCalEvent.h>
|
||||
#import <NGCards/iCalToDo.h>
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
|
||||
#import "NSCalendarDate+ActiveSync.h"
|
||||
#import "NSDate+ActiveSync.h"
|
||||
|
||||
@implementation iCalRecurrenceRule (ActiveSync)
|
||||
|
||||
- (NSCalendarDate *) _adjustedStartDate
|
||||
{
|
||||
iCalTimeZone *timeZone;
|
||||
|
||||
timeZone = [(iCalDateTime *)[[self parent] firstChildWithTag: @"dtstart"] timeZone];
|
||||
if (timeZone)
|
||||
return [timeZone computedDateForDate: [[self parent] startDate]];
|
||||
else
|
||||
return [[self parent] startDate];
|
||||
}
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSString *t;
|
||||
int type;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
if ([[self parent] isKindOfClass: [iCalToDo class]])
|
||||
t = @"Tasks";
|
||||
else
|
||||
t = @"Calendar";
|
||||
|
||||
[s appendFormat: @"<Recurrence xmlns=\"%@:\">", t];
|
||||
|
||||
// 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=\"%@:\">%d</Recurrence_DayOfWeek>", t, (2|4|8|16|32)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"%@:\">%d</Recurrence_Interval>", t, [self repeatInterval]];
|
||||
}
|
||||
}
|
||||
else if ([self frequency] == iCalRecurrenceFrequenceWeekly)
|
||||
{
|
||||
iCalWeekOccurrences *occurrences;
|
||||
int i, v;
|
||||
|
||||
type = 1;
|
||||
|
||||
occurrences = [[self byDayMask] weekDayOccurrences];
|
||||
v = 0;
|
||||
|
||||
if (occurrences)
|
||||
{
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (occurrences[0][i])
|
||||
v += (1 << i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No byDayMask, we take the event's start date to compute the DayOfWeek
|
||||
// 0 == Sunday, 6 == Saturday
|
||||
v = (1 << [[self _adjustedStartDate] dayOfWeek]);
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfWeek xmlns=\"%@:\">%d</Recurrence_DayOfWeek>", t, v];
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"%@:\">%d</Recurrence_Interval>", t, [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=\"%@:\">%d</Recurrence_DayOfWeek>", t, (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"%@:\">%d</Recurrence_WeekOfMonth>", t, firstOccurrence];
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"%@:\">%d</Recurrence_Interval>", t, [self repeatInterval]];
|
||||
}
|
||||
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=\"%@:\">%d</Recurrence_DayOfWeek>", t, (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"%@:\">%d</Recurrence_WeekOfMonth>", t, 5];
|
||||
[s appendFormat: @"<Recurrence_Interval xmlns=\"%@:\">%d</Recurrence_Interval>", t, [self repeatInterval]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unsupported rule in ActiveSync/Outlook. Rule that says "Repeat on the 7th and 8th of each month".
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple reccurrence rule of type "Monthly"
|
||||
type = 2;
|
||||
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"%@:\">%d</Recurrence_DayOfMonth>", t, [[self _adjustedStartDate] dayOfMonth]];
|
||||
}
|
||||
}
|
||||
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=\"%@:\">%d</Recurrence_DayOfWeek>", t, (1 << [dm firstDay])];
|
||||
[s appendFormat: @"<Recurrence_WeekOfMonth xmlns=\"%@:\">%d</Recurrence_WeekOfMonth>", t, firstOccurrence];
|
||||
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"%@:\">%@</Recurrence_MonthOfYear>", t,
|
||||
[self flattenedValuesForKey: @"bymonth"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
type = 5; // Yearly
|
||||
|
||||
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"%@:\">%@</Recurrence_DayOfMonth>", t,
|
||||
[self flattenedValuesForKey: @"bymonthday"]];
|
||||
|
||||
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"%@:\">%@</Recurrence_MonthOfYear>", t,
|
||||
[self flattenedValuesForKey: @"bymonth"]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = 5;
|
||||
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"%@:\">%d</Recurrence_DayOfMonth>", t, [[self _adjustedStartDate] dayOfMonth]];
|
||||
[s appendFormat: @"<Recurrence_MonthOfYear xmlns=\"%@:\">%d</Recurrence_MonthOfYear>", t, [[self _adjustedStartDate] monthOfYear]];
|
||||
}
|
||||
}
|
||||
|
||||
[s appendFormat: @"<Recurrence_Type xmlns=\"%@:\">%d</Recurrence_Type>", t, type];
|
||||
|
||||
if ([[self parent] isKindOfClass: [iCalToDo class]])
|
||||
[s appendFormat: @"<Recurrence_Start xmlns=\"%@:\">%@</Recurrence_Start>", t, [[[self parent] startDate] activeSyncRepresentationInContext: context]];
|
||||
|
||||
// Occurrences / Until
|
||||
if ([self repeatCount])
|
||||
{
|
||||
[s appendFormat: @"<Recurrence_Occurrences xmlns=\"%@:\">%@</Recurrence_Occurrences>", t,
|
||||
[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=\"%@:\">%@</Recurrence_Until>", t,
|
||||
[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
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
|
||||
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/NSString.h>
|
||||
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.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->wYear = 0;
|
||||
tzData->wMonth = [[byMonth objectAtIndex: 0] intValue];
|
||||
mask = [rrule byDayMask];
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
tzData->wDay = ([mask firstOccurrence] == -1) ? 5 : [mask firstOccurrence];
|
||||
|
||||
dateValue = [self startDate];
|
||||
|
||||
if (![dateValue hourOfDay])
|
||||
{
|
||||
if ([mask firstDay]-1 < 0)
|
||||
tzData->wDayOfWeek = 6;
|
||||
else
|
||||
tzData->wDayOfWeek = [mask firstDay]-1;
|
||||
|
||||
tzData->wHour = 23;
|
||||
tzData->wMinute = 59;
|
||||
tzData->wSecond = 59;
|
||||
tzData->wMilliseconds = 999;
|
||||
}
|
||||
else
|
||||
{
|
||||
tzData->wDayOfWeek = [mask firstDay];
|
||||
|
||||
tzData->wHour = [dateValue hourOfDay];
|
||||
tzData->wMinute = [dateValue minuteOfHour];
|
||||
tzData->wSecond = [dateValue secondOfMinute];
|
||||
tzData->wMilliseconds = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 = {0,0,0,0,0,0,0,0};
|
||||
|
||||
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 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
|
||||
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
|
|
@ -1,314 +0,0 @@
|
|||
/*
|
||||
|
||||
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/NSDictionary.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
#include "iCalRecurrenceRule+ActiveSync.h"
|
||||
#include "iCalAlarm+ActiveSync.h"
|
||||
|
||||
@implementation iCalToDo (ActiveSync)
|
||||
|
||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||
{
|
||||
NSMutableString *s;
|
||||
NSArray *categories;
|
||||
id o;
|
||||
|
||||
int v, i;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
// Complete
|
||||
[s appendFormat: @"<Complete xmlns=\"Tasks:\">%d</Complete>", [[self status] isEqualToString: @"COMPLETED"] ? 1 : 0];
|
||||
|
||||
// DateCompleted
|
||||
if ((o = [self completed]))
|
||||
[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]];
|
||||
}
|
||||
|
||||
// Recurrence rules
|
||||
if ([self isRecurrent])
|
||||
[s appendString: [[[self recurrenceRules] lastObject] 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
|
||||
if ([self hasAlarms])
|
||||
{
|
||||
iCalAlarm *alarm;
|
||||
NSString *webstatus;
|
||||
|
||||
if ((alarm = [self firstSupportedAlarm]))
|
||||
{
|
||||
webstatus = [[alarm trigger] value: 0 ofAttribute: @"x-webstatus"];
|
||||
if (!webstatus || ([webstatus caseInsensitiveCompare: @"TRIGGERED"] != NSOrderedSame))
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 1];
|
||||
else
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||
|
||||
[s appendString: [alarm activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<ReminderSet xmlns=\"Tasks:\">%d</ReminderSet>", 0];
|
||||
}
|
||||
|
||||
// Sensitivity
|
||||
if ([[self accessClass] isEqualToString: @"PRIVATE"])
|
||||
v = 2;
|
||||
else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"])
|
||||
v = 3;
|
||||
else
|
||||
v = 0;
|
||||
|
||||
[s appendFormat: @"<Sensitivity xmlns=\"Tasks:\">%d</Sensitivity>", v];
|
||||
|
||||
categories = [self categories];
|
||||
|
||||
if ([categories count])
|
||||
{
|
||||
[s appendFormat: @"<Categories xmlns=\"Tasks:\">"];
|
||||
for (i = 0; i < [categories count]; i++)
|
||||
{
|
||||
[s appendFormat: @"<Category xmlns=\"Tasks:\">%@</Category>", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]];
|
||||
}
|
||||
[s appendFormat: @"</Categories>"];
|
||||
}
|
||||
|
||||
// Subject
|
||||
o = [self summary];
|
||||
if ([o length])
|
||||
[s appendFormat: @"<Subject xmlns=\"Tasks:\">%@</Subject>", [[self summary] activeSyncRepresentationInContext: context]];
|
||||
|
||||
if ((o = [self comment]))
|
||||
{
|
||||
// It is very important here to NOT set <Truncated>0</Truncated> in the response,
|
||||
// otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details.
|
||||
o = [o activeSyncRepresentationInContext: context];
|
||||
|
||||
if ([[context objectForKey: @"ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
[s appendFormat: @"<Body xmlns=\"Tasks:\">%@</Body>", o];
|
||||
[s appendString: @"<BodyTruncated xmlns=\"Tasks:\">0</BodyTruncated>"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
|
||||
[s appendFormat: @"<Type>%d</Type>", 1];
|
||||
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
- (void) takeActiveSyncValues: (NSDictionary *) theValues
|
||||
inContext: (WOContext *) context
|
||||
{
|
||||
NSTimeZone *userTimeZone;
|
||||
iCalTimeZone *tz;
|
||||
id o;
|
||||
|
||||
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 ([[context objectForKey: @"ASProtocolVersion"] isEqualToString: @"2.5"])
|
||||
{
|
||||
if ((o = [theValues objectForKey: @"Body"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((o = [[theValues objectForKey: @"Body"] objectForKey: @"Data"]))
|
||||
[self setComment: o];
|
||||
}
|
||||
|
||||
if ([[theValues objectForKey: @"Complete"] intValue] &&
|
||||
((o = [theValues objectForKey: @"DateCompleted"])) )
|
||||
{
|
||||
iCalDateTime *completed;
|
||||
|
||||
o = [o calendarDate];
|
||||
completed = (iCalDateTime *) [self uniqueChildWithTag: @"completed"];
|
||||
[completed setDate: o];
|
||||
[self setStatus: @"COMPLETED"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setStatus: @"IN-PROCESS"];
|
||||
[self setCompleted: nil];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"DueDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"due"];
|
||||
[due setTimeZone: tz];
|
||||
[due setDateTime: o];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"StartDate"]))
|
||||
{
|
||||
iCalDateTime *due;
|
||||
|
||||
o = [o calendarDate];
|
||||
due = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
[due setTimeZone: tz];
|
||||
[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"];
|
||||
}
|
||||
|
||||
//
|
||||
// 0- normal, 1- personal, 2- private and 3-confidential
|
||||
//
|
||||
if ((o = [theValues objectForKey: @"Sensitivity"]))
|
||||
{
|
||||
switch ([o intValue])
|
||||
{
|
||||
case 2:
|
||||
[self setAccessClass: @"PRIVATE"];
|
||||
break;
|
||||
case 3:
|
||||
[self setAccessClass: @"CONFIDENTIAL"];
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
[self setAccessClass: @"PUBLIC"];
|
||||
}
|
||||
}
|
||||
|
||||
// Categories
|
||||
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
|
||||
[self setCategories: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"ReminderTime"]))
|
||||
{
|
||||
iCalAlarm *alarm;
|
||||
|
||||
alarm = [[iCalAlarm alloc] init];
|
||||
[alarm takeActiveSyncValues: theValues inContext: context];
|
||||
|
||||
[self removeAllAlarms];
|
||||
[self addToAlarms: alarm];
|
||||
RELEASE(alarm);
|
||||
|
||||
}
|
||||
|
||||
// Recurrence
|
||||
if ((o = [theValues objectForKey: @"Recurrence"]) && !([[o objectForKey: @"Recurrence_DeadOccur"] intValue]))
|
||||
{
|
||||
iCalRecurrenceRule *rule;
|
||||
|
||||
rule = [[iCalRecurrenceRule alloc] init];
|
||||
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
|
||||
RELEASE(rule);
|
||||
|
||||
[rule takeActiveSyncValues: o inContext: context];
|
||||
|
||||
if (!([theValues objectForKey: @"StartDate"]))
|
||||
{
|
||||
iCalDateTime *start;
|
||||
|
||||
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
|
||||
|
||||
[start setTimeZone: tz];
|
||||
[start setDate: [[o objectForKey: @"Recurrence_Start"] calendarDate]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -28,13 +28,8 @@
|
|||
ProxyPass /SOGo http://127.0.0.1:20000/SOGo interpolate
|
||||
ProxyPass / http://127.0.0.1:20000/SOGo/dav/ interpolate
|
||||
<Location />
|
||||
<IfVersion < 2.4>
|
||||
Order deny,allow
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
</Location>
|
||||
|
||||
<Proxy http://127.0.0.1:20000>
|
||||
|
|
|
@ -2,17 +2,13 @@ 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
|
||||
|
||||
<IfVersion < 2.4>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
|
||||
# Explicitly allow caching of static content to avoid browser specific behavior.
|
||||
# A resource's URL MUST change in order to have the client load the new version.
|
||||
|
@ -22,30 +18,20 @@ Alias /SOGo/WebServerResources/ \
|
|||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
# Don't send the Referer header for cross-origin requests
|
||||
Header always set Referrer-Policy "same-origin"
|
||||
<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
|
||||
## below.
|
||||
#
|
||||
## For full proxy-side authentication:
|
||||
#<Location /SOGo>
|
||||
# AuthType XXX
|
||||
# Require valid-user
|
||||
# SetEnv proxy-nokeepalive 1
|
||||
# Allow from all
|
||||
#</Location>
|
||||
#
|
||||
## For proxy-side authentication only for CardDAV and GroupDAV from external
|
||||
## clients:
|
||||
#<Location /SOGo/dav>
|
||||
# AuthType XXX
|
||||
# Require valid-user
|
||||
# SetEnv proxy-nokeepalive 1
|
||||
# Allow from all
|
||||
#</Location>
|
||||
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
|
@ -60,35 +46,19 @@ ProxyPreserveHost On
|
|||
# Allow from your-cas-host-addr
|
||||
# </Proxy>
|
||||
|
||||
# Redirect / to /SOGo
|
||||
#RedirectMatch ^/$ https://mail.yourdomain.com/SOGo
|
||||
|
||||
# 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
|
||||
|
||||
ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
||||
|
||||
<Proxy http://127.0.0.1:20000/SOGo>
|
||||
## Adjust the following to your configuration
|
||||
## and make sure to enable the headers module
|
||||
<IfModule headers_module>
|
||||
## adjust the following to your configuration
|
||||
RequestHeader set "x-webobjects-server-port" "443"
|
||||
SetEnvIf Host (.*) HTTP_HOST=$1
|
||||
RequestHeader set "x-webobjects-server-name" "%{HTTP_HOST}e" env=HTTP_HOST
|
||||
RequestHeader set "x-webobjects-server-url" "https://%{HTTP_HOST}e" env=HTTP_HOST
|
||||
RequestHeader set "x-webobjects-server-name" "yourhostname"
|
||||
RequestHeader set "x-webobjects-server-url" "https://yourhostname"
|
||||
|
||||
## When using proxy-side autentication, you need to uncomment and
|
||||
## adjust the following line:
|
||||
RequestHeader unset "x-webobjects-remote-user"
|
||||
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" env=REMOTE_USER
|
||||
# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e"
|
||||
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
</IfModule>
|
||||
|
||||
AddDefaultCharset UTF-8
|
||||
|
||||
|
@ -96,9 +66,9 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
|||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
# For Apple autoconfiguration
|
||||
# For apple autoconfiguration
|
||||
<IfModule rewrite_module>
|
||||
RewriteEngine On
|
||||
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
|
||||
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
|
||||
</IfModule>
|
||||
|
||||
|
|
3384
CHANGELOG.md
3384
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,29 +0,0 @@
|
|||
all: pdf html
|
||||
|
||||
ASCIIDOCS = $(wildcard SOGo*.asciidoc)
|
||||
PDFS = $(patsubst %.asciidoc,%.pdf, $(ASCIIDOCS))
|
||||
HTML = $(patsubst %.asciidoc,%.html, $(ASCIIDOCS))
|
||||
|
||||
%.pdf: %.asciidoc asciidoctor-pdf-theme.yml $(wildcard includes/*.asciidoc)
|
||||
asciidoctor-pdf \
|
||||
-a pdf-fontsdir=fonts \
|
||||
-a release_version=`git describe --abbrev=0 --tags | cut -d'-' -f 2` \
|
||||
-a release_month=`date +%B` \
|
||||
-a pdf-theme=asciidoctor-pdf-theme.yml \
|
||||
--trace \
|
||||
$<
|
||||
|
||||
%.html: %.asciidoc $(wildcard includes/*.asciidoc)
|
||||
asciidoctor \
|
||||
-D . \
|
||||
-a release_version=`git describe --abbrev=0 --tags | cut -d'-' -f 2` \
|
||||
-a release_month=`date +%B` \
|
||||
-n \
|
||||
$<
|
||||
|
||||
pdf: $(PDFS)
|
||||
|
||||
html: $(HTML)
|
||||
|
||||
clean:
|
||||
rm -f *.pdf *.html
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,194 +0,0 @@
|
|||
Developer's Guide
|
||||
=================
|
||||
////
|
||||
|
||||
This file is part of the SOGo project.
|
||||
|
||||
See includes/global-attributes.asciidoc
|
||||
for authors, copyright and license information.
|
||||
|
||||
////
|
||||
include::includes/global-attributes.asciidoc[]
|
||||
|
||||
UI and UX
|
||||
---------
|
||||
|
||||
* https://material.io/design/
|
||||
|
||||
Frameworks & Tools
|
||||
------------------
|
||||
|
||||
GNUstep
|
||||
~~~~~~~
|
||||
|
||||
SOPE
|
||||
~~~~
|
||||
|
||||
JavaScript libraries
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* https://angularjs.org/
|
||||
* https://material.angularjs.org/
|
||||
* http://angular-ui.github.io/ui-router/
|
||||
* http://lodash.com/
|
||||
* https://github.com/nervgh/angular-file-upload
|
||||
* https://github.com/a5hik/ng-sortable
|
||||
|
||||
Code Style
|
||||
----------
|
||||
|
||||
Objective-C
|
||||
~~~~~~~~~~~
|
||||
|
||||
* To document the Web APIs, we follow the http://apidocjs.com/[APIDOC] standards.
|
||||
|
||||
apidoc -f ".*\\.m" -i UI -o Documentation/JSON-API
|
||||
|
||||
* When returning JSON from an object extending UIxComponent, use the following method:
|
||||
|
||||
- (WOResponse *) responseWithStatus: (unsigned int) status
|
||||
andJSONRepresentation: (NSObject *) contentObject;
|
||||
|
||||
HTML
|
||||
~~~~
|
||||
|
||||
* Localize your strings:
|
||||
|
||||
<var:string label:value="I'm localized!"/>
|
||||
|
||||
* Localize your attributes:
|
||||
|
||||
<input type="text" label:placeholder="I'm localized!"/>
|
||||
|
||||
* Reuse existing localized strings as much as possible. Otherwise don't forget to update the English Localizable.strings file of the appropriate module.
|
||||
|
||||
JavaScript
|
||||
~~~~~~~~~~
|
||||
|
||||
* http://eslint.org/
|
||||
* http://standardjs.com
|
||||
* https://github.com/toddmotto/angularjs-styleguide
|
||||
* https://github.com/angular/material/blob/master/docs/guides/CODING.md
|
||||
* http://jshint.com/
|
||||
* https://github.com/angular/material/tree/master/docs/guides
|
||||
|
||||
* We conform to https://github.com/airbnb/javascript[Airbnb coding standards]. Use https://github.com/jscs-dev/node-jscs[jscs] with the 'airbnb' preset to validate your code style:
|
||||
|
||||
jscs -p airbnb *.js
|
||||
|
||||
* For documentation, we follow the http://usejsdoc.org/[JSDoc3] standards:
|
||||
|
||||
jsdoc UI/WebServerResources/js/{Common,Contacts,Mailer,Preferences,Scheduler}/*.js -d Documentation/JavaScript-API
|
||||
|
||||
Models (facades)
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
////
|
||||
http://trochette.github.io/Angular-Design-Patterns-Best-Practices/
|
||||
////
|
||||
* Move business logic into models and share them.
|
||||
* Keep it simple, separate server interaction and error handling from the model. That way model only handle data processing and code is easier to maintain.
|
||||
|
||||
Controllers
|
||||
^^^^^^^^^^^
|
||||
|
||||
* Functions not exposed in the controller must be prefixed with an underscore.
|
||||
|
||||
SASS/CSS
|
||||
~~~~~~~~
|
||||
|
||||
* http://componentcss.com/
|
||||
* https://github.com/styleguide/css
|
||||
* https://smacss.com/
|
||||
* http://cssguidelin.es/
|
||||
* http://sixrevisions.com/css/css-methodologies/
|
||||
* https://css-tricks.com/what-a-css-code-review-might-look-like/
|
||||
* http://una.im/classy-css/
|
||||
* http://stylelint.io/
|
||||
|
||||
A `@mixin` is like a stamp: it creates a duplicated version of the property block (optionally) with arguments provided. An `@extend` appends the element you are extending to the property block. It is your "yes, and ___" statement.
|
||||
|
||||
* For documentation, we follow http://sassdoc.com/[SassDoc] annotations.
|
||||
* FlexBox compatibility http://caniuse.com/#feat=flexbox
|
||||
|
||||
Fonts
|
||||
^^^^^
|
||||
|
||||
* http://mozilla.github.io/Fira/[Mozilla Fira]
|
||||
* https://material.io/tools/icons/[Material icons]
|
||||
|
||||
Building frontend
|
||||
-----------------
|
||||
|
||||
* Install the latest stable release of https://nodejs.org[Node.js]
|
||||
* Install grunt
|
||||
|
||||
npm install -g grunt-cli
|
||||
npm install -g bower
|
||||
|
||||
* We need the SASS files of Angular Material to build our CSS. The git repository of Angular Material is included as a submodule of SOGo:
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
* Generate the JavaScript and CSS files
|
||||
|
||||
cd UI/WebServerResources
|
||||
npm install
|
||||
bower install
|
||||
grunt build
|
||||
|
||||
Defining an alternate color theme
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SOGo relies on the https://material.angularjs.org/latest/Theming/01_introduction[theming system of Angular Material] to define the colors of the Web interface.
|
||||
|
||||
To overwrite the default theme in SOGo, set the following parameter in `/etc/sogo/sogo.conf`:
|
||||
|
||||
SOGoUIAdditionalJSFiles = (js/theme.js);
|
||||
|
||||
Edit `theme.js` under `/usr/lib64/GNUstep/SOGo/WebServerResources/js` or `/usr/lib/GNUstep/SOGo/WebServerResources/js` depending on your platform and restart sogod.
|
||||
|
||||
If the configuration parameter `SOGoUIxDebugEnabled` is unset or set to `NO` in `/etc/sogo/sogo.conf`, you'll need to generate a new `theme-default.css` stylesheet for the new theme.
|
||||
|
||||
. Temporarily set `SOGoUIxDebugEnabled` to `YES`;
|
||||
. Restart sogod;
|
||||
. From your favorite browser, open the JavaScript console and type the following:
|
||||
|
||||
copy([].slice.call(document.styleSheets)
|
||||
.map(e => e.ownerNode)
|
||||
.filter(e => e.hasAttribute('md-theme-style'))
|
||||
.map(e => e.textContent)
|
||||
.join('\n')
|
||||
)
|
||||
|
||||
. Overwrite the content of `WebServerResources/css/theme-default.css` with the content of the clipboard;
|
||||
. Restore the value of `SOGoUIxDebugEnabled` (`NO` or unset);
|
||||
. Restart sogod.
|
||||
|
||||
Version Control
|
||||
---------------
|
||||
|
||||
* https://devcharm.com/articles/46/improve-your-git-workflow/
|
||||
* https://github.com/angular/material/blob/master/docs/guides/CONTRIBUTING.md#-commit-message-format
|
||||
* Each commit should cover only one thing;
|
||||
* Begin the commit message with a single short (less than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description;
|
||||
* When fixing a bug, commit to the devel branch as well as the maintenance branch of the latest release version (named maintenance/x.y). When a ticket is associated to the bug, add to the description a line saying *Fixes #1234*.
|
||||
* `git pull` may introduce http://stackoverflow.com/questions/15316601/in-what-cases-could-git-pull-be-harmful[inconsistencies and problems]. Replace it with the following alias:
|
||||
|
||||
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
* https://github.com/angular/protractor
|
||||
* http://karma-runner.github.io/
|
||||
|
||||
JSON API
|
||||
~~~~~~~~
|
||||
|
||||
One practical way to test the JSON API is to use `curl`. To do so, you need to enable `SOGoTrustProxyAuthentication` and configure HTTP authentifcation in Apache. You can pipe the result to http://stedolan.github.io/jq/[jq] to nicely format and filter the output:
|
||||
|
||||
curl -u username:password http://localhost/SOGo/so/username/Calendar/calendarslist | jq '.'
|
||||
|
||||
curl -u username:password -H 'Content-Type: application/json' -d '{}' http://localhost/SOGo/so/francis/Calendar/personal/71B6-54904400-1-7C308500.ics/save
|
File diff suppressed because it is too large
Load Diff
|
@ -1,328 +0,0 @@
|
|||
Mobile Devices Configuration Guide
|
||||
==================================
|
||||
////
|
||||
|
||||
This file is part of the SOGo project.
|
||||
|
||||
See includes/global-attributes.asciidoc
|
||||
for authors, copyright and license information.
|
||||
|
||||
////
|
||||
include::includes/global-attributes.asciidoc[]
|
||||
|
||||
About this Guide
|
||||
----------------
|
||||
|
||||
This guide will walk you through the installation and configuration of
|
||||
popular SyncML clients to be used with the SOGo solution.
|
||||
|
||||
This guide also include instructions for configuring Apple iPhone OS
|
||||
devices (iPhone, iPod touch, and iPad).
|
||||
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available at
|
||||
https://sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
SOGo is a free and modern scalable groupware server. It offers shared
|
||||
calendars, address books, and emails through your favourite Web browser
|
||||
and by using a native client such as Mozilla Thunderbird and Lightning.
|
||||
|
||||
SOGo is standard-compliant. It supports CalDAV, CardDAV, GroupDAV, iMIP
|
||||
and iTIP and reuses existing IMAP, SMTP and database servers - making
|
||||
the solution easy to deploy and interoperable with many applications.
|
||||
|
||||
SOGo features:
|
||||
|
||||
* Scalable architecture suitable for deployments from dozen to many
|
||||
thousand users
|
||||
* Rich Web-based interface that shares the look and feel, the features
|
||||
and the data of Mozilla Thunderbird and Lightning
|
||||
* Improved integration with Mozilla Thunderbird and Lighthing by using
|
||||
the SOGo Connector and the SOGo Integrator
|
||||
* Two-way synchronization support with any Microsoft ActiveSync-capable
|
||||
device, or Outlook 2013
|
||||
|
||||
SOGo is developed by a community of developers located mainly in North
|
||||
America and Europe. More information can be found on
|
||||
http://www.sogo.nu/.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
This section will guide you through the installation of popular SyncML
|
||||
clients on various devices.
|
||||
|
||||
Funambol SyncML Client
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The recommended SyncML client for BlackBerry, Apple iPhone (for
|
||||
contacts), mobile devices based on Microsoft Windows Mobile Smartphone
|
||||
or Windows Mobile PocketPC, is the one provided directly by Funambol,
|
||||
which is free and open source.
|
||||
|
||||
For Microsoft Window Mobile, you must identify which kind of device you
|
||||
have ("Smartphone" vs. "PocketPC").
|
||||
|
||||
Once you've identified what kind of device you have, download the
|
||||
appropriate client from:
|
||||
|
||||
http://www.funambol.com/opensource/downloads.php
|
||||
|
||||
The version 8.5 GA or later is required.
|
||||
|
||||
NextHaus SyncJe
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
For BlackBerry and Apple iPhone devices, you can also use the
|
||||
SyncJe SyncML client from NextHaus (http://www.nexthaus.com/). This
|
||||
client allows one to synchronize contacts, event and tasks with SOGo.
|
||||
|
||||
The version 2.43 or later is required. For BlackBerry 7XXX devices, the
|
||||
required version is 2.25 but isn't officially supported. You can install
|
||||
SyncJe on your BlackBerry device, over the air, by opening the following
|
||||
link from your BlackBerry device:
|
||||
|
||||
http://www.nexthaus.com/bb/syncjebb.jad
|
||||
|
||||
This will download and install the application on your mobile device.
|
||||
It's important to use the Web browser application and not the WAP
|
||||
browser to perform this operation. Once installed, restart the device.
|
||||
This procedure can also be used to update the SyncJe application. Note
|
||||
that an update of SyncJe will not affect the state of previous
|
||||
synchronizations. It's not necessary to perform a "slow sync" after an
|
||||
update of SyncJe.
|
||||
|
||||
Synthesis SyncML Client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Synthesis (http://www.synthesis.ch/) company provides a SyncML
|
||||
client for Palm OS-based devices.
|
||||
|
||||
"Over The Air" synchronization is possible, as well as synchronization
|
||||
through a cradle. For the latter, you must install Softick PPP using the
|
||||
Palm Desktop software. The version 3.01 or later is required and you can
|
||||
download it from http://www.softick.com/ppp/. Once installed, the
|
||||
desktop computer must be restarted.
|
||||
|
||||
Once completed, you must download the Synthesis client for Palm OS. The
|
||||
version 3.0.2.9 or later is required. Either the standard or the
|
||||
professional version can be used.
|
||||
|
||||
Once downloaded, uncompress the archive and install the `.prc` file on
|
||||
your Palm OS-based device. After, you should see a "SyncML" icon on your
|
||||
device.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
In this section, you'll learn how to configure the popular SyncML
|
||||
clients in order to fully synchronize your mobile device with SOGo.
|
||||
|
||||
Instructions for Apple iPhone OS based devices are also included. Note
|
||||
that those devices do not require a SyncML client for synchronizing
|
||||
calendars.
|
||||
|
||||
Funambol SyncML Client
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once the Funambol SyncML client is installed, start the application from
|
||||
your mobile device to configure it using the following steps:
|
||||
|
||||
From the _Tools_ menu, choose _Options..._:
|
||||
|
||||
* Specify the _Location_. If your SOGo server is `sogo.domain.com`, the
|
||||
location should be `http://sogo.domain.com/funambol/ds`.
|
||||
* Specify your username and password
|
||||
* Check the _Contacts_ check box and click on the _Details_ button.
|
||||
The synchronization type is _two-way_ and the _Remote name_ is
|
||||
`sogo-card`. The data format is vCard.
|
||||
* Check the _Calendar_ check box and click on the _Details_ button. The
|
||||
synchronization type is _two-way_ and the _Remote name_ is `sogo-cal`.
|
||||
The data format is vCalendar.
|
||||
* Check the _Tasks_ check box and click on the _Details_ button. The
|
||||
synchronization type is _two-way_ and the _Remote name_ is
|
||||
`sogo-todo`. The data format is SIF.
|
||||
* Save the preferences.
|
||||
|
||||
To prevent the BlackBerry to automatically add a new event in the
|
||||
calendar when an invitation is received by email (IMIP message) on the
|
||||
BlackBerry device, you should delete the CICAL service from the Service
|
||||
Book. To do so, proceed with the following steps:
|
||||
|
||||
From the _Options_ menu:
|
||||
|
||||
* Choose _Advanced Options_
|
||||
* Choose _Service Book_
|
||||
* Identify the line that mentions the email address of the BlackBerry
|
||||
user followed by the `[CICAL]` string. For example, `user@domain.com
|
||||
[CICAL]`.
|
||||
* Delete this entry.
|
||||
|
||||
Once completed, you must restart the device by removing its battery for
|
||||
a couple of seconds. If you ever want to reactivate the CICAL service,
|
||||
follow those instructions:
|
||||
|
||||
http://support.appriver.com/KB/a205/how-to-resend-service-books-on-blackberry.aspx
|
||||
|
||||
Once complete, the client is ready for an initial synchronization.
|
||||
|
||||
Ideally, you should *delete* all data from the mobile device before the
|
||||
initial synchronization. To do so:
|
||||
|
||||
* From the _Tools_ menu, choose _Recover..._
|
||||
* Choose _Replace with data from server_
|
||||
* Check the _Contacts_, _Calendar_ and _Tasks_ check boxes
|
||||
|
||||
Finally, click on the _Sync All_ button to proceed with the
|
||||
synchronization.
|
||||
|
||||
NextHaus SyncJe
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Once NextHaus SyncJe is installed, proceed with the following steps,
|
||||
from your BlackBerry device, to configure it:
|
||||
|
||||
* Open the _SyncJe Client_
|
||||
|
||||
From the _Settings_ menu:
|
||||
|
||||
* Specify the _Server URL_. If your SOGo server is `sogo.domain.com`,
|
||||
the URL should be `http://sogo.domain.com/funambol/ds`.
|
||||
* Specify your user name and password
|
||||
* Check the _Contacts_ check box and specify `sogo-card` as the
|
||||
_Foldername_
|
||||
* Check the _Calendar_ check box and specify `sogo-cal` as the
|
||||
_Foldername_
|
||||
* Check the _ToDo_ check box and specify `sogo-todo` as the _Foldername_
|
||||
* Check the _Auto Sync_ check box if you wish to automatically
|
||||
synchronize contacts, events and tasks at a predefined time interval
|
||||
* Do *not* check the _Sync events between_ check box
|
||||
* Check the _Skip saving attendees_ - this will prevent the BlackBerry
|
||||
device from sending emails to meeting participants for events
|
||||
downloaded from the SOGo server
|
||||
* Check the _BIS_ check box (for _BlackBerry Internet Services_)
|
||||
* Leave the other fields to the their default value
|
||||
* Then choose _Save_ from the menu to save the preferences
|
||||
|
||||
To prevent the BlackBerry to automatically add a new event in the
|
||||
calendar when an invitation is received by email (IMIP message) on the
|
||||
BlackBerry device, you should delete the CICAL service from the Service
|
||||
Book. To do so, proceed with the following steps:
|
||||
|
||||
From the _Options_ menu:
|
||||
|
||||
* Choose _Advanced Options_
|
||||
* Choose _Service Book_
|
||||
* Identify the line that mentions the email address of the BlackBerry
|
||||
user followed by the `[CICAL]` string. For example, `user@domain.com
|
||||
[CICAL]`.
|
||||
* Delete this entry
|
||||
|
||||
Once completed, you must restart the device by removing its battery for
|
||||
a couple of seconds. If you ever want to reactivate the CICAL service,
|
||||
follow those instructions:
|
||||
|
||||
http://support.appriver.com/KB/a205/how-to-resend-service-books-on-blackberry.aspx
|
||||
|
||||
Once done, you're now ready for your first synchronization.
|
||||
|
||||
Ideally, you must delete all data on the BlackBerry before proceeding
|
||||
with the initial synchronization. To do so, proceed with the following
|
||||
steps:
|
||||
|
||||
* Open the _SyncJe Client_
|
||||
|
||||
From the _Settings_ menu:
|
||||
|
||||
* Choose the _Clear databases_ option
|
||||
* Respond _Yes_ to all questions. This will delete all contacts, events
|
||||
and tasks from your BlackBerry device.
|
||||
* Go back in the previous menu
|
||||
* Choose the _Force Slow Sync_ option
|
||||
* Choose the _Start Sync_ option to start the synchronization. This
|
||||
could take a few minutes if you have many contacts, events or tasks.
|
||||
Subsequent synchronizations should be much faster.
|
||||
|
||||
After the initial _Slow Sync_, you can simply chose the _Start Sync_
|
||||
option if you do a manual synchronization to synchronize all changes.
|
||||
|
||||
Synthesis SyncML Client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once the Synthesis SyncML client is installed, proceed with the
|
||||
following steps to configure it:
|
||||
|
||||
* Open the _Synthesis SyncML Standard Edition_ application
|
||||
|
||||
From the _Settings..._ menu:
|
||||
|
||||
* Specify the _Server URL_. If your SOGo server is `sogo.domain.com`,
|
||||
the URL should be `http://sogo.domain.com/funambol/ds`.
|
||||
* Specify your user name and password
|
||||
* Check the _Contacts_, _Events_ and _Task_ check boxes
|
||||
* Click on the _more..._ button
|
||||
* For the _Contacts_, choose _reload device_ and specify `sogo-card` as
|
||||
the _Server Path_.
|
||||
* For the _Events_, choose _reload device_ and specify `sogo-cal` as
|
||||
the _Server Path_. Do *not* check the _Only from..._ check box.
|
||||
* For the _Tasks_, choose _reload device_ and specify `sogo-todo` as
|
||||
the _Server Path_.
|
||||
* Once completed, click on the _Done_ button
|
||||
|
||||
The _reload device_ value will *delete* all entries on the device during
|
||||
the initial synchronization and obtain contacts, events and tasks from
|
||||
the server. If you do not want to delete everything from the device,
|
||||
please choose _normal_ instead of _reload device_.
|
||||
|
||||
Once ready, click on the _Start_ button to proceed with the initial
|
||||
synchronization.
|
||||
|
||||
During the initial synchronization, the Palm device will ask you how to
|
||||
connect to the desktop system. From the Palm device, choose _Windows
|
||||
RAS_ without specifying an user name or a password. This will allow the
|
||||
Palm device to establish a PPP connection between itself and Softick PPP
|
||||
and then communicate using its TCP/IP stack to the Funambol server.
|
||||
|
||||
Apple iOS — Calendars
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Starting from iOS 3, Apple has added CalDAV support to the calendar
|
||||
application included in the iPhone/iPod/iPad.
|
||||
|
||||
To add a CalDAV account, follow those instructions:
|
||||
|
||||
* From the _Settings_ menu, choose _Mail, Contacts, Calendars_
|
||||
* Choose _Add Account..._
|
||||
* Choose _Other_
|
||||
* Choose _Add CalDAV Account_
|
||||
* Specify the _Server_, the URL should be
|
||||
`http://sogo.domain.com/SOGo/dav/<username>`
|
||||
|
||||
On iOS 3.1.2 and later, you must also specify the range of events you
|
||||
want to have.
|
||||
|
||||
* From the _Settings_ menu, choose _Mail, Contacts, Calendars_
|
||||
* From the _Sync_ menu, choose _All Events_
|
||||
|
||||
Note that other options (_Events 2 Weeks Back_, etc.) do not work right
|
||||
now.
|
||||
|
||||
Apple iOS — Contacts
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to synchronize contacts with your Apple mobile device, use
|
||||
the native address book application with SOGo using the CardDAV
|
||||
protocol. To configure the address book application so it works with
|
||||
SOGo, create a new CardDAV account and specify your server name. In
|
||||
the _Advanced Settings_, set the port to `8800`.
|
||||
|
||||
On iOS prior to version 4, you must install the Funambol client and
|
||||
configure the `sogo-card` source.
|
||||
|
||||
include::includes/additional-info.asciidoc[]
|
||||
|
||||
include::includes/commercial-support.asciidoc[]
|
|
@ -1,338 +0,0 @@
|
|||
Mozilla Thunderbird Configuration Guide
|
||||
=======================================
|
||||
////
|
||||
|
||||
This file is part of the SOGo project.
|
||||
|
||||
See includes/global-attributes.asciidoc
|
||||
for authors, copyright and license information.
|
||||
|
||||
////
|
||||
include::includes/global-attributes.asciidoc[]
|
||||
|
||||
About this Guide
|
||||
----------------
|
||||
|
||||
This guide will walk you through the installation and configuration of
|
||||
Mozilla Thunderbird and its associated extensions so it can be used with
|
||||
the SOGo solution.
|
||||
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available
|
||||
at http://www.sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
SOGo is a free and modern scalable groupware server. It offers shared
|
||||
calendars, address books, and emails through your favourite Web browser
|
||||
and by using a native client such as Mozilla Thunderbird and Lightning.
|
||||
|
||||
SOGo is standard-compliant. It supports CalDAV, CardDAV, GroupDAV, iMIP
|
||||
and iTIP and reuses existing IMAP, SMTP and database servers - making
|
||||
the solution easy to deploy and interoperable with many applications.
|
||||
|
||||
SOGo features:
|
||||
|
||||
* Scalable architecture suitable for deployments from dozen to many
|
||||
thousand users
|
||||
* Rich Web-based interface that shares the look and feel, the features
|
||||
and the data of Mozilla Thunderbird and Lightning
|
||||
* Improved integration with Mozilla Thunderbird and Lightning by using
|
||||
the SOGo Connector and the SOGo Integrator extensions
|
||||
* Two-way synchronization support with any Microsoft ActiveSync-capable
|
||||
device, or Outlook 2013
|
||||
|
||||
SOGo is developed by a community of developers located mainly in North
|
||||
America and Europe. More information can be found on
|
||||
http://www.sogo.nu/.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
This section will guide you through the installation of Thunderbird and
|
||||
its associated extensions.
|
||||
|
||||
Mozilla Thunderbird
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Mozilla Thunderbird is the official front end client of SOGo.
|
||||
|
||||
Mozilla Thunderbird versions 31, 38, 45 and above are supported
|
||||
and it is recommended to use the latest version of Thunderbird.
|
||||
|
||||
In order to download and install Mozilla Thunderbird, please visit:
|
||||
|
||||
https://www.mozilla.org/en-US/thunderbird/organizations/all-esr.html
|
||||
|
||||
Mozilla Lightning
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
When using Thunderbird, you can use the latest version of Mozilla
|
||||
Lightning. Use the Add-ons manager of Thunderbird or visit:
|
||||
|
||||
https://addons.mozilla.org/en-US/thunderbird/addon/lightning/
|
||||
|
||||
SOGo Connector and SOGo Integrator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The SOGo Connector and Integrator extensions are provided to perfect the
|
||||
integration of Mozilla Thunderbird with the SOGo groupware solution.
|
||||
|
||||
First of all, the SOGo Connector extension transforms Thunderbird into a
|
||||
full DAV client for groupware servers such as SOGo, eGroupware or
|
||||
Citadel. It does this by adding support for remote DAV address books and
|
||||
by adding features to be used along with the Lightning calendar
|
||||
extension.
|
||||
|
||||
Among supported features of the SOGo Connector extension, we have:
|
||||
|
||||
* Free / Busy URL field in the address book
|
||||
* Free / Busy functionality through HTTP
|
||||
* Event organizers
|
||||
* Synchronization of the address book using GroupDAV
|
||||
* CardDAV implementation for the address book
|
||||
* Support for WebDAV ACL
|
||||
|
||||
On the other hand, the SOGo Integrator extension transforms Thunderbird
|
||||
into a pure "heavy" client for SOGo. Whereas the SOGo Connector is meant
|
||||
for portability (horizontal integration), the SOGo Integrator makes use
|
||||
of the features and layout only available from SOGo (vertical
|
||||
integration).
|
||||
|
||||
Among the supported features of the SOGo Integrator extension, we have :
|
||||
|
||||
* Remote administration of folder subscriptions
|
||||
* Remote administration of folder access control lists (ACL)
|
||||
* Automatic replication of your local and subscribed folders
|
||||
* When correctly configured, it handles the propagation of updates to
|
||||
chosen extensions from a local update server
|
||||
* Automatic propagation of default settings
|
||||
|
||||
In order to download and install the SOGo Connector and Integrator
|
||||
extensions, please visit:
|
||||
|
||||
http://www.sogo.nu/downloads/frontends.html
|
||||
|
||||
Prior to installing the SOGo Integrator extension, you should read the
|
||||
following chapter - _SOGo Integrator Customization_.
|
||||
|
||||
SOGo Integrator Customization
|
||||
-----------------------------
|
||||
|
||||
In this section, you'll learn how to customize the SOGo Integrator
|
||||
extension so it can be used alongside your SOGo server.
|
||||
|
||||
Customization
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The customization steps described here involves understanding of XML and
|
||||
of text file edition. If you do not have those skills, it is recommended
|
||||
that you don't try to use this extension.
|
||||
|
||||
There are actually two steps in the configuration of a working
|
||||
environment for SOGo Integrator. First of all, the Integrator will use
|
||||
the same user name used to connect to the first email server configured
|
||||
in Thunderbird. It is a current limitation but it will fit nearly all
|
||||
configurations out there.
|
||||
|
||||
For testing purpose, it is strongly recommended to create and
|
||||
configure an additional user profile for Thunderbird. This is for two
|
||||
reasons. The first is because of the limitation mentioned above. The
|
||||
second is because the Integrator will synchronize your personal address
|
||||
book onto the SOGo server and then remove it so that only your personal
|
||||
address book on SOGo will be available. If you are not able to do that,
|
||||
you may want to make a copy of that address book beforehand.
|
||||
|
||||
The second part requires editing one file in the extension file sub tree
|
||||
to specify where the SOGo server is located. This is done by hand. In an
|
||||
enterprise environment, this step is only required once per release
|
||||
since the updates are expected to propagate automatically.
|
||||
|
||||
Uncompress (using a ZIP or jar tool) the SOGo Integrator XPI and locate
|
||||
the following file:
|
||||
|
||||
extensions.rdf
|
||||
|
||||
This file is used for locating the extension update server and the SOGo
|
||||
server, which we consider to be the same for the moment. There is a line
|
||||
starting with a "Seq" tag and with an attribute named "isi:updateURL".
|
||||
Replace the host part of that url with the SOGo server you want to
|
||||
connect to.
|
||||
|
||||
For example, one would replace:
|
||||
|
||||
<Seq about="http://inverse.ca/sogo-integrator/extensions" isi:updateURL="http://sogo-demo.inverse.ca/plugins/updates.php?plugin=%ITEM_ID%&version=%ITEM_VERSION%&platform=%PLATFORM%">
|
||||
|
||||
by:
|
||||
|
||||
<Seq about="http://inverse.ca/sogo-integrator/extensions" isi:updateURL="https://sogo.acme.com/plugins/updates.php?plugin=%ITEM_ID%&version=%ITEM_VERSION%&platform=%PLATFORM%">
|
||||
|
||||
if the SOGo server is accessible from the following URL:
|
||||
|
||||
https://sogo.acme.com/SOGo
|
||||
|
||||
Note that if you changed the `x-webobjects-server-url` configuration
|
||||
setting from your HTTP server configuration file for SOGo, the
|
||||
value *must* match the one you specify in `isi:updateURL` - even the
|
||||
port number.
|
||||
|
||||
Moreover, you *must* change the value of the
|
||||
`sogo-integrator.autocomplete.server.urlid` preference from
|
||||
`defaults/preferences/site.js` to match the identifier of your
|
||||
_SOGoUserSources_. This source will be used by Thunderbird for
|
||||
autocompletion.
|
||||
|
||||
Once you're done modifying the configuration file, save your changes and
|
||||
reconstruct the XPI file.
|
||||
|
||||
Now start Thunderbird and install your newly modified extension.
|
||||
|
||||
SOGo Update Server
|
||||
------------------
|
||||
|
||||
In this section, you will learn how to install and configure the SOGo
|
||||
Update Server.
|
||||
|
||||
The SOGo Update Server can be used to automatically install or uninstall
|
||||
Mozilla Thunderbird extensions, push user settings and more - all from a
|
||||
central place managed by system administrators.
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Installation is relatively straightforward. You need to make
|
||||
the `updates.php` script available through your HTTP server at the URL
|
||||
you've specified from the `extensions.rdf` file. The `updates.php`
|
||||
script can be found in the SOGo sources, under the `Scripts` directory.
|
||||
|
||||
The `updateLink` section of the XML payload returned to sogo-integrator
|
||||
is built dynamically using the `SCRIPT_URI` variable, which is only
|
||||
available when running with mod_rewrite. It should work out of the box
|
||||
as long as the script is placed in the same directory as the xpi files.
|
||||
|
||||
If it is not the case, then the link should be adjusted to fit the
|
||||
actual file layout:
|
||||
|
||||
<em:updateLink><?php echo dirname(getenv('SCRIPT_URI')) . '/relative/path/to/' . $plugin["filename"] ?></em:updateLink>
|
||||
|
||||
An http request similar to the following can be used to make sure that
|
||||
the generated link is correct:
|
||||
|
||||
http://sogo.host/path/to/updates.php?plugin=sogo-integrator@inverse.ca&version=0.00
|
||||
|
||||
Please refer to your HTTP server documentation for the installation and
|
||||
configuration of PHP.
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
To configure the SOGo update server, you need to modify the
|
||||
`updates.php` script directly.
|
||||
|
||||
Adding an extension to be pushed automatically by the update server
|
||||
require you to modify the `$plugins` array from updates.php and also
|
||||
adjust accordingly SOGo Integrator's `extensions.rdf` file.
|
||||
|
||||
For example, to automatically install SOGo Connector, SOGo Integrator
|
||||
and Lightning, you would have:
|
||||
|
||||
----
|
||||
$plugins
|
||||
= array( "sogo-connector@inverse.ca"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "31.0.0",
|
||||
"filename" => "sogo-connector-31.0.0.xpi" ),
|
||||
"sogo-integrator@inverse.ca"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "31.0.0",
|
||||
"filename" => "sogo-integrator-31.0.0-sogo-demo.xpi" ),
|
||||
"{e2fda1a4-762b-4020-b5ad-a41df1933103}"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "3.3.1",
|
||||
"filename" => "lightning.xpi" ));
|
||||
----
|
||||
|
||||
The syntax is:
|
||||
|
||||
----
|
||||
"<extension ID>"
|
||||
=> array( "application" => "thunderbird",
|
||||
"version" => "<exact version found in the extension's manifest.json file>",
|
||||
"filename" => "<exact filename on the filesystem>" )
|
||||
----
|
||||
|
||||
The path of the filename, specified in the `filename` parameter, is
|
||||
relative to the location of the `updates.php` script. For extensions
|
||||
that are dependant on the architecture and operating system (Microsoft
|
||||
Windows, Apple Mac OS X, etc.), they can be placed in subdirectories
|
||||
relative again to the `updates.php` script (but the filename must be
|
||||
identical in all subdirectories). For exemple, for Mozilla Lightning, we
|
||||
could have:
|
||||
|
||||
----
|
||||
Darwin_x86-gcc3/lightning.xpi
|
||||
Linux_x86-gcc3/lightning.xpi
|
||||
Linux_x86_64-gcc3/lightning.xpi
|
||||
WINNT_x86-msvc/lightning.xpi
|
||||
----
|
||||
|
||||
If you eventually want to disable an extension, that is, without
|
||||
removing it from your users' computers, you can specify `disabled` as
|
||||
the version number.
|
||||
|
||||
Next is to modify the `extensions.rdf` file. Again, to automatically
|
||||
install SOGo Connector, SOGo Integrator and Lightning, you would have:
|
||||
|
||||
----
|
||||
<li>
|
||||
<Description
|
||||
em:id="{e2fda1a4-762b-4020-b5ad-a41df1933103}"
|
||||
em:name="Lightning"/>
|
||||
</li>
|
||||
<li>
|
||||
<Description
|
||||
em:id="sogo-integrator@inverse.ca"
|
||||
em:name="SOGo Integrator"/>
|
||||
</li>
|
||||
<li>
|
||||
<Description
|
||||
em:id="sogo-connector@inverse.ca"
|
||||
em:name="SOGo Connector"/>
|
||||
</li>
|
||||
----
|
||||
|
||||
Using SOGo Integrator, you can also push user-settings for any part of
|
||||
Thunderbird or its extensions. There are two kind of user-settings
|
||||
push:
|
||||
|
||||
* Settings that are pushed during the initial configuration of
|
||||
Thunderbird
|
||||
* Settings that are pushed upon every restart (ie., forced) of
|
||||
Thunderbird
|
||||
|
||||
Settings that pushed are pushed during initial configuration are
|
||||
controlled by the `./defaults/preferences/site.js` file from SOGo
|
||||
Integrator. Here is an example:
|
||||
|
||||
----
|
||||
pref("calendar.alarms.showmissed", false);
|
||||
pref("calendar.caldav.sched.enabled", true);
|
||||
----
|
||||
|
||||
Preferences that are forced upon every restart of Thunderbird are
|
||||
controlled from the `./chrome/content/general/custom-preferences.js`
|
||||
configuration file.
|
||||
|
||||
Here is an example:
|
||||
|
||||
----
|
||||
force_int_pref("changequote.replyformat.format", 0);
|
||||
force_bool_pref("changequote.headers.withcc", true);
|
||||
force_char_pref(“foo.bar”, “zot”);
|
||||
----
|
||||
|
||||
include::includes/additional-info.asciidoc[]
|
||||
|
||||
include::includes/commercial-support.asciidoc[]
|
|
@ -1,495 +0,0 @@
|
|||
Microsoft Outlook CalDAVSynchronizer Configuration Guide
|
||||
========================================================
|
||||
////
|
||||
|
||||
This file is part of the SOGo project.
|
||||
|
||||
See includes/global-attributes.asciidoc
|
||||
for authors, copyright and license information.
|
||||
|
||||
////
|
||||
include::includes/global-attributes.asciidoc[]
|
||||
|
||||
About this Guide
|
||||
----------------
|
||||
|
||||
This guide will walk you through the installation and configuration of Microsoft Outlook for a better integration with SOGo through the use of the CalDavSynchronizer addon.
|
||||
|
||||
Prior going over this guide, you should have a working SOGo
|
||||
installation. Please refer to the _SOGo Installation and Configuration
|
||||
Guide_ for more information on installing and configuring SOGo.
|
||||
|
||||
The instructions are based on version {release_version} of SOGo.
|
||||
|
||||
The latest version of this guide is available
|
||||
at https://sogo.nu/downloads/documentation.html.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Outlook CalDav Synchronizer is an open source addon for Microsoft Windows Outlook. It synchronizes events, tasks and contacts between Microsoft Outlook and CalDAV or CardDAV servers. Supported Outlook versions are Outlook 2007 to Outlook 2016.
|
||||
Together with other open source solutions like SOGo, a full open source Microsoft Exchange server replacement for desktop users is finally possible and enterprise ready.
|
||||
Outlook CalDav Synchronizer is Free and Open-Source Software (FOSS) , licensed under AGPL3 and developed by
|
||||
|
||||
* https://sourceforge.net/u/nertsch/profile/[Gerhard Zehetbauer]
|
||||
* https://sourceforge.net/u/nimm/profile/[Alexander Nimmervoll]
|
||||
|
||||
Project Links
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The project source and binaries for installation are hosted on GitHub and SourceForge or can be downloaded from https://caldavsynchronizer.org[caldavsynchronizer.org]
|
||||
|
||||
* https://github.com/aluxnimm/outlookcaldavsynchronizer
|
||||
* https://sourceforge.net/projects/outlookcaldavsynchronizer/
|
||||
|
||||
Social Media
|
||||
~~~~~~~~~~~~
|
||||
|
||||
https://www.facebook.com/caldavsynchronizer/[CalDav Synchronizer on Facebook]
|
||||
|
||||
Enterprise Support
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Inverse (http://inverse.ca/[http://inverse.ca]) offers professional services around SOGo to help organizations deploy the solution and
|
||||
migrate from their legacy systems.
|
||||
|
||||
For enterprise support on the integration between SOGo and Microsoft Outlook using the Outlook CalDav Synchronizer, please visit https://caldavsynchronizer.org/enterprise
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The most important features are
|
||||
|
||||
* Open Source, the only free Outlook CalDAV and CardDAV addon
|
||||
* two-way-sync
|
||||
* support for CalDAV calendars and tasks, CardDAV contacts
|
||||
* support for Google native Contacts and Tasks API
|
||||
* full support for timezones and recurring events with exceptions
|
||||
* SSL/TLS support, support for self-signed certificates
|
||||
* proxy support
|
||||
* Autodiscovery of calendars and addressbooks
|
||||
* configurable sync timerange filter
|
||||
* time-triggered-sync and sync changes from Outlook immediately
|
||||
* Category Filtering (sync CalDAV calendar/tasks to Outlook categories)
|
||||
* map CalDAV server colors to Outlook category colors
|
||||
* Use server settings from Outlook IMAP/POP3 account profile
|
||||
* Mapping of Distribution Lists to SOGo VLIST contact groups
|
||||
* Configurable mapping of Outlook custom properties
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Download and extract the `OutlookCalDavSynchronizer-<Version>.zip` into any directory and start setup.exe. You can change the default install path, but you need to use a directory on the `C:\` drive. Follow the instructions and restart Outlook after the installation is finished.
|
||||
|
||||
image::images/installer1.png[scaledwidth="100%",alt="Installer Step 1"]
|
||||
image::images/installer2.png[scaledwidth="100%",alt="Installer Step 2"]
|
||||
|
||||
Next, you will see a ribbon CalDav Synchronizer in Outlook and can add synchronization profiles for you SOGo calendars, addressbooks and tasks.
|
||||
|
||||
image::images/outlook_ribbon.png[scaledwidth="100%",alt="Outlook Ribbon"]
|
||||
|
||||
In the Synchronization profiles dialog, press *Add new profile* if you just need to synchronize one resource or *Add multiple profiles* if you need to synchronize multiple calendars, addressbooks or task folders.
|
||||
|
||||
image::images/add_multiple.png[scaledwidth="75%",alt="Add multiple profiles"]
|
||||
|
||||
Next, select the SOGo profile type
|
||||
|
||||
image::images/select_profiletype.png[scaledwidth="100%",alt="Select Profile type"]
|
||||
|
||||
In the multiple profile setup dialog you can configure the details of the sync profiles.
|
||||
|
||||
image::images/configure_multiple.png[scaledwidth="100%",alt="Multiple Profile Configuration"]
|
||||
|
||||
First, you should change the profile name and configure the server settings, the DAV URL is prefilled with the default SOGo DAV path and you should only change the hostname of your SOGo server, enter Username and Password. If you also have configured an IMAP account and your domain supports autodiscovery using DNS SRV records you can also try to fetch the server settings from your existing mail account in Outlook by pressing *Get IMAP/POP3 account settings*. If successful, username and password will be used from the existing mail account and the DAV URL will be discovered.
|
||||
If all server settings are configured press *Discover resources and assign to Outlook folders* to start the autodiscovery of all server resources.
|
||||
|
||||
You can assign Outlook folders to found calendar, addressbook and task resources in the three tab views.
|
||||
|
||||
image::images/select_calendar.png[scaledwidth="100%",alt="Add calendar resources"]
|
||||
image::images/select_addressbook.png[scaledwidth="100%",alt="Add addressbook resources"]
|
||||
image::images/select_task.png[scaledwidth="100%",alt="Add task resources"]
|
||||
|
||||
To assign a folder click on the "..." button for the corresponding resource and either choose an existing Outlook folder or create a new one in the folder dialog.
|
||||
|
||||
image::images/select_folder.png[scaledwidth="60%",alt="Select Outlook folder"]
|
||||
image::images/new_folder.png[scaledwidth="60%",alt="Add new Outlook folder"]
|
||||
|
||||
If you chose *Add new profile* for only one resource you can also choose the Outlook folder and dependent on the folder type you will create a calendar, addressbook or task profile.
|
||||
|
||||
image::images/profile_config.png[scaledwidth="100%",alt="Single Profile Configuration"]
|
||||
|
||||
If you know the correct calendar CalDAV URL, you can also enter that directly into the DAV Url textbox or paste it from the SOGo Web Frontend, when selecting Link in the calendar properties. For the personal calendar it looks like
|
||||
https://server.example/remote.php/dav/calendars/caldav/personal/
|
||||
|
||||
Furthermore, you can configure sync settings and intervals or keep the default settings which are automatic Two-Way-Sync every 30 minutes for all events from 60 days in the past to 365 days in the future.
|
||||
|
||||
image::images/profile_advanced.png[scaledwidth="100%",alt="Advanced Settings"]
|
||||
|
||||
For addressbooks, the profile setup is similar, just choose an Outlook contacts folder for synchronization.
|
||||
|
||||
If needed, you can configure network and proxy options or special mapping configuration parameters in the corresponding option pages.
|
||||
|
||||
image::images/profile_network.png[scaledwidth="100%",alt="Network settings"]
|
||||
image::images/profile_advanced_event_mapping.png[scaledwidth="100%",alt="Event mapping configuration"]
|
||||
image::images/profile_advanced_contact_mapping.png[scaledwidth="100%",alt="Contact mapping configuration"]
|
||||
image::images/profile_advanced_task_mapping.png[scaledwidth="100%",alt="Task mapping configuration"]
|
||||
image::images/profile_advanced_custom_mapping.png[scaledwidth="100%",alt="Custom mapping configuration"]
|
||||
|
||||
See *Advanced settings* below for a detailed description of all available options.
|
||||
|
||||
After the configuration of the sync profile is finished you can start the synchronization with pressing *Synchronize now* in the CalDav Synchronizer ribbon and your Outlook resources will be in sync with your SOGo resources.
|
||||
|
||||
You can check the status of the last sync runs with the *Status* button in the ribbon.
|
||||
|
||||
image::images/status_report.png[scaledwidth="50%",alt="Status report"]
|
||||
|
||||
Should there be any errors or warnings during synchronization, you can check the *Reports* in the ribbon.
|
||||
|
||||
image::images/sync_report.png[scaledwidth="100%",alt="Sync report"]
|
||||
|
||||
Configuration Options
|
||||
---------------------
|
||||
|
||||
Synchronization Settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* *Outlook → Server (Replicate):* syncronize everything from Outlook to the server (one way)
|
||||
|
||||
* *Outlook ← Server (Replicate):* synchronize everything from the server to Outlook (one way)
|
||||
|
||||
* *Outlook → Server (Merge):* synchronize everything from Outlook to the server but don’t change events created on the server
|
||||
|
||||
* *Outlook ← Server (Merge):* synchronize everything from the server to Outlook but don’t change events created in Outlook
|
||||
|
||||
* *Outlook ←→ Server (Two-Way):* Two-Way synchronization between Outlook and the server with one of the following conflict resolution
|
||||
|
||||
* *Conflict resolution*
|
||||
(only used in Two-Way synchronization mode and only available in *advanced settings*)
|
||||
|
||||
** *Outlook Wins:* If an event is modified in Outlook and in the server since last snyc, use the Outlook version. If an event is modified in Outlook and deleted in the server since last snyc, also use the Outlook version. If an event is deleted in Outlook and modified in the server, also delete it in the server.
|
||||
|
||||
** *Server Wins:* If an event is modified in Outlook and in the server since last snyc, use the server version. If an event is modified in Outlook and deleted in the server since last snyc, also delete it in Outlook. If an event is deleted in Outlook and modified in the server, recreate it in Outlook.
|
||||
|
||||
** *Automatic:* If event is modified in Outlook and in the server since last snyc, use the last recent modified version. If an event is modified in Outlook and deleted in the server since last snyc, delete it also in Outlook. If an event is deleted in Outlook and modified in the server, also delete it in the server.
|
||||
* *Synchronization interval (minutes):* Choose the interval for synchronization in minutes, if 'Manual only' is choosen, there is no automatic sync but you can use the 'Synchronize now' menu item.
|
||||
|
||||
* *Perform synchronization in chunks* perform CalDAV/CardDAV sync in chunks with configurable chunk size to avoid OutOfMemoryEceptions, enabled by default because of lower memory consumption for huge resources. *(only in advanced settings)*
|
||||
|
||||
* *Use time range filter* *(only in advanced settings)*
|
||||
For performance reasons it is useful to sync only a given timespan of a big calendar, especially past events are normally not necessary to sync after a given timespan. But be aware that Outlook and Google and some other CalDAV servers calculate the intersection with the time-range differently for recurring events which can cause doubled or deleted events, so it is recommended to select a time-range which is larger than the largest interval of your recurring events (e.g. 1 year for birthdays).
|
||||
|
||||
Advanced Settings
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
When *Show advanced settings* is enabled, you can expand the tree view of the profile to configure network and proxy options and mapping configuration options.
|
||||
|
||||
Network and Proxy Options
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here you can configure advanced network options and proxy settings.
|
||||
|
||||
* *Close connection after each request* Don’t use KeepAlive, only useful for servers which don’t support it.
|
||||
|
||||
* *Use Preemptive Authentication* Send Authentication header with each request to avoid 401 responses and resending the request, disable only if the server has problems with preemptive authentication.
|
||||
|
||||
* *Force basic authentication* Set basic authentication headers to avoid problems with negotiation or digest authentication with servers like Apple OS X / macOS. This is only recommended if you use a secure HTTPS connection, otherwise passwords are sent in cleartext.
|
||||
|
||||
* *Use System Default Proxy* Use proxy settings from Internet Explorer or config file, uses default credentials if available for NTLM authentication.
|
||||
|
||||
* *Use manual proxy configuration* Specify proxy URL as `http://<your-proxy-domain>:<your-proxy-port>` and optional Username and Password for Basic Authentication.
|
||||
|
||||
Mapping Configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here you can configure what properties should be synced.
|
||||
|
||||
Event Mapping Configuration
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
For appointments you can choose if you want to map reminders (just upcoming, all or none) and the description body.
|
||||
|
||||
* *Export html description X-ALT-DESC converted from RTF Body* If enabled, convert formatted RTF Body of Outlook appointment to html and export it as X-ALT-DESC property. The RTF to html conversion is experimental, inline images and some formatting properties can’t be converted! Be aware that some servers like Google Calendar drop this attribute!
|
||||
* *Set RTF Body from X-ALT-DESC html description* If enabled, convert X-ALT-DESC description html property to RTF and set Outlook appointment RTF Body. The html to RTF conversion is experimental, not all html formatting options can be converted! This overwrites also the plaintext Body!
|
||||
|
||||
* *Timezone settings* See section Timezone mapping below.
|
||||
|
||||
*Use GlobalAppointmentID for UID attribute:* Use Outlook GlobalAppointmendID instead of random Guid for UID attribute in new CalDAV events. This can avoid duplicate events from invitations.
|
||||
|
||||
* In *Privacy settings* you can configure if you want to map Outlook private appointments to CLASS:CONFIDENTIAL and vice versa. This could be useful for Owncloud for example, if you share your calendar with others and they should see start/end dates of your private appointments. You can also map all CLASS:PUBLIC events to Outlook private appointments.
|
||||
|
||||
* In *Scheduling settings* you can configure if you want to map attendees and organizer and if notifications should be sent by the server.
|
||||
|
||||
* Use *Don’t send appointment notifications* for SOGo servers and *SCHEDULE-AGENT=CLIENT* for other servers if you want to send invitations from Outlook and avoid that the server sends invitations too, but be aware that not all servers (e.g. Google) support the SCHEDULE-AGENT=CLIENT setting.
|
||||
|
||||
* In *Outlook settings* you can also define a filter category so that multiple CalDAV-Calendars can be synchronized into one Outlook calendar via the defined category (see Category Filter and Color below).
|
||||
|
||||
* *Cleanup duplicate events after each sync run:* removes duplicate Outlook appointments based on start,end and subject of the events after each sync run, be aware of possible performance penalties with this option enabled.
|
||||
|
||||
Contact Mapping Configuration
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
* For contacts you can configure if birthdays should be mapped or not. If birthdays are mapped, Outlook also creates an recurring appointment for every contact with a defined birthday.
|
||||
|
||||
* You can also configure if contact photos should be mapped or not. Contact photo mapping from Outlook to the server doesn’t work in Outlook 2007. You can also add an option to not overwrite the contact photo in Outlook when it changes on the server, which could happen due to other mobile clients reducing the resolution for example.
|
||||
|
||||
* Don’t overwrite FileAs in Outlook uses the Outlook settings for FileAs and doesn’t overwrite the contact FileAs with the FN from the server.
|
||||
|
||||
* Fix imported phone number format adds round brackets to the area code of phone numbers, so that Outlook can show correct phone number details with country and area code, e.g. +1 23 45678 is mapped to +1 (23) 45678.
|
||||
|
||||
* Map OutlookEmailAddress1 to WORK instead of HOME, enable when you need to change the order of email address mapping.
|
||||
|
||||
* Write IM addresses as IMPP attributes. If enabled IMPP is used instead of X-AIM,X-ICQ,X-JABBER etc. for writing Instant messenger addresses in vCards. (SOGo is only using X-AIM atm.)
|
||||
|
||||
* Default IM protocol. Choose the default IM service type protocol which will be added to the chat address field from Outlook when writing vCards, defaults to AIM.
|
||||
|
||||
* Map Distribution Lists enables the sync of contact groups / Distribution Lists, right now the DAV contact group format SOGo VLIST or vCards with KIND:group are available, see *Distribution Lists* below.
|
||||
|
||||
Task Mapping Configuration
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
* For tasks (not for Google task profiles) you can configure if you want to map reminders (just upcoming, all or none), the priority of the task, the description body and if recurring tasks should be synchronized.
|
||||
|
||||
* You can also define if task start and due dates should be mapped as floating without timezone to avoid issues with tasks across different timezones.
|
||||
|
||||
* Similar to calendars you can also define a filter category so that multiple CalDAV Tasklists can be synchronized into one Outlook task folder via the defined category.
|
||||
|
||||
Timezone Settings
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Outlook and Windows use different timezone definitions than most CalDAV servers and other clients. When adding new events on the server you have different options how the timezone of the newly created VEVENT is generated. The default setting uses the default Windows timezone from Outlook (e.g. W. Europe Standard Time) or the selected timezones for the start and end of the appointment. Since some servers have problems with that timezone definitions you can change that behaviour in the event mapping configuration with the following options:
|
||||
|
||||
* *Create events on server in UTC* Use UTC instead of Outlook Appointment Timezone for creating events on CalDAV server. Not recommended for general use, because recurrence exceptions over DST changes can’t be mapped and Appointments with different start and end timezones can’t be represented.
|
||||
* *Create events on server in downloaded IANA Timezones* Use IANA instead of Windows Timezones for creating events on CalDAV server. Needed for servers which do not accept non standard Windows Timezones like GMX for example. Timezone definitions will be downloaded from http://tzurl.org.
|
||||
|
||||
* *Use IANA Timezone* Use this IANA timezone for default Outlook/Windows timezone. Manually selected different timezones in Outlook appointments will be mapped to first corresponding IANA timezone.
|
||||
|
||||
* *Include full IANA zone with historical data* Use full IANA timezone definition with historical data. Needs more bandwith and can be incompatible when manually importing in Outlook.
|
||||
|
||||
Custom Properties Mapping
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When you expand the tree view of the profile for events and tasks, you can configure the mapping of custom properties.
|
||||
|
||||
* *Map all Outlook custom properties to X-CALDAVSYNCHRONIZER attributes* If enabled, all Outlook custom text properties of the appointment/task are mapped to DAV attributes with the prefix X-CALDAVSYNCHRONIZER- and vice versa.
|
||||
|
||||
* You can also define manual mapping pairs of Outlook custom attributes and DAV X-Attributes. This will overrule the general mapping of all Outlook custom properties if both is activated. Outlook properties that don’t exist, will be created. DAV properties MUST start with X-. Only Outlook custom properties of type Text can be mapped.
|
||||
|
||||
Managing Meetings and Invites
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Outlook can only track meeting responses and invites in the main calender folder. If you schedule meetings from Outlook which are synced with the CalDAV server you have two possibilities to avoid double invitation mails for all attendees. First, you can enable the option *Don’t send appointment notifications (enabled by default for SOGo profiles)* or *SCHEDULE-AGENT=CLIENT* (for other servers) and let only Outlook send the meeting invites, if the server supports this option. Or you can disable this option and let the server schedule the meetings after syncing the meeting. Then you need to disable the invitation mails sent from Outlook. This is possible by unchecking the checkbox left to the attendee name in the meeting planning dialog. When syncing meetings created in Outlook to the server, the option *Use GlobalAppointmentID for UID attribute* is recommended. This can avoid duplicate events from invitations.
|
||||
|
||||
The response status of all attendees can be synced from Outlook to the server but only the status of the own Outlook identity (if included in the attendees) can be synced from the server to Outlook due to limitations of the Outlook Object Model.
|
||||
|
||||
When receiving invites from the CalDAV server and via Email in your INBOX, Outlook will automatically create a tentative meeting in the main calendar folder
|
||||
|
||||
To avoid double meetings the option *Cleanup duplicate events after each sync run* in event mapping configuration is recommended.
|
||||
|
||||
Free/busy Lookups
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can configure free/busy lookups globally in the Outlook options.
|
||||
|
||||
Select Options/Calendar and there free/busy information and use a free/busy URL of your server with placeholder like %Name%, e.g. http://myserver/freebusy.php/%Name%
|
||||
|
||||
For SOGo the URL looks like: http://<hostname>/SOGo/dav/public/%NAME%/freebusy.ifb
|
||||
|
||||
And `SOGoEnablePublicAccess` must be set to `YES`
|
||||
|
||||
Then every attendee in the Outlook planning view gets resolved with that URL for a free/busy lookup against your server.
|
||||
|
||||
Scheduling Settings and Resources
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If your server supports resources (for SOGo see http://wiki.sogo.nu/ResourceConfiguration disable *set SCHEDULE-AGENT=CLIENT* in Mapping Configuration, so that the server can handle the resource invitation mails, add the resource email adress as attendee in the Outlook appointment and choose type ressource (house icon) for it.
|
||||
|
||||
Category Filter and Color
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to sync multiple CalDAV calendars or task lists into one Outlook folder you can configure an Outlook category for filtering in the *Mapping Configuration*. You can choose a category from the dropdown list of all available Outlook categories or enter a new category name.
|
||||
|
||||
For all events/tasks from the server the defined category is added in Outlook, when syncing back from Outlook to the server only appointments/tasks with that category are considered but the filter category is removed. The category name must not contain any commas or semicolons!
|
||||
|
||||
With the checkbox *Sync also Appointments without any category* also all appointments/tasks without a category are synced to the server.
|
||||
|
||||
With the checkbox below you can alternatively negate the filter and sync all appointments/tasks except this category.
|
||||
|
||||
For calendars it is also possible to choose the color of the category or to fetch the calendar color from the server and map it to the nearest supported Outlook category color with the button *Fetch Color*. With *Set DAV Color* it is also possible to sync the choosen category color back to set the server calendar color accordingly. With *Category Shortcut Key* you can define the shortcut key of the selected category for easier access when creating appointments.
|
||||
|
||||
Reminders
|
||||
~~~~~~~~~
|
||||
|
||||
In event and task mapping configuration you can define if you want to map (all/non/just upcoming) reminders. If you get the following error message when trying to set reminders in Outlook
|
||||
|
||||
`The reminder will not appear because the item is in a folder that doesn’t support reminders.`
|
||||
|
||||
you can try to change the Outlook options as discussed in
|
||||
|
||||
http://answers.microsoft.com/en-us/office/forum/office_2016-outlook/outlook-2016-calendar-reminders/8f40bcdd-e3fc-4f29-acaf-544f48d63992
|
||||
|
||||
or try the following reported by user __Todo18__
|
||||
|
||||
1. Create a new storage folder in Outlook via the File menu, Info, Account Settings. In the Data Files tab, you can Add a new (.pst) data file. After the file has been added, Make it the default [data file], and close the dialog.
|
||||
|
||||
2. Go to the Calendar window, right click on the calendar that’s giving you problems, and select Move Calendar. In the dialog, pick the data file that you created in the first step, and confirm. Don’t forget to update the storage folder in the CalDav Synchronizer settings!
|
||||
|
||||
Distribution Lists
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When enabled in Contact Mapping configuration you can now also sync Outlook Distribution Lists with your server contact groups. Since different servers use different formats to store contact groups, you will be able to choose the used DAV contact group format. Right now, the VLIST format for SOGo servers and vCards with KIND:group are supported. Don’t enable any of these options when your server doesn’t support it!
|
||||
|
||||
Since Outlook Distribution Lists also support list members which aren’t in the addressbook but SOGo VLISTs don’t, we add them as custom X-Attributes. With this workaround those members aren’t displayed in SOGo but won’t get lost when syncing back to Outlook.
|
||||
|
||||
Since vCard in version 3.0 doesn’t support contact groups we use X-ADDRESSBOOK-SERVER attributes for KIND and MEMBER for contact groups.
|
||||
|
||||
General Options and SSL settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the General Options Dialog you can change settings which are used for all synchronization profiles.
|
||||
|
||||
* *Automatically check for newer versions* set to false to disable checking for updates.
|
||||
* *Check Internet connection before sync run* checks if an interface is up and try DNS query to dns.msftncsi.com first and if that fails try to download http://www.msftncsi.com/ncsi.txt with the configured proxy before each sync run to avoid error reports if network is unavailable after hibernate for example. Disable this option if you are in a local network where DNS and that URL is blocked.
|
||||
|
||||
* *Store data in roaming folder* set to true if you need to store state and profile data in the AppData\Roaming\ directory for roaming profiles in a AD domain for example. When changing this option, a restart of Outlook is required.
|
||||
|
||||
* *Include custom message classes in Outlook filter* Disabled by default, enable only if you have custom forms with message_classes other than the default IPM.Appointment/Contact/Task. For better performance, Windows Search Service shouldn’t be deactivated if this option is enabled.
|
||||
|
||||
* *Use fast queries for Outlook folders* Enabled by default, uses fast GetTable queries when accessing Outlook folders. Disable only if you get errors in GetVersions, when disabled every item needs to be requested which causes a performance penalty!
|
||||
|
||||
* *Trigger sync after Outlook Send/Receive and on Startup* If checked a manual sync is triggered after the Outlook Send/Receive finishes and on Outlook startup.
|
||||
|
||||
* *Show advanced settings as default* Show the advanced settings in synchronization profiles as default if enabled.
|
||||
|
||||
* *Expand all nodes in Synchronization profiles* Enabled by default, expands all nodes in the synchronization profiles to see the suboptions for network settings and mapping configuration.
|
||||
|
||||
* *Enable Tray Icon* Enabled by default, you can disable the tray icon in the Windows Taskbar if you don’t need it.
|
||||
|
||||
* *Fix invalid settings* Fixes invalid settings automatically, when synchronization profiles are edited.
|
||||
|
||||
* *Show Sync Progress Bar* and *Sync Progress Bar Threshold (Items)* Enabled by default, show a progress bar if more than the treshold of items need to be loaded during a synchronization run. If disabled, no progress bar is shown but be aware that for larger changes Outlook can freeze, since some operations need to be performed in the Outlook main thread.
|
||||
|
||||
* *Accept invalid chars in server response* If checked invalid characters in XML server responses are allowed. A typical invalid char, sent by some servers is Form feed (0x0C).
|
||||
|
||||
* * Enable useUnsafeHeaderParsing* Enable, if the server sends invalid http headers, see common network errors. Needed for Yahoo and cPanel Horde servers for example. The general option overrides the setting in the app.config file.
|
||||
|
||||
* *CalDav Connection Timeout (secs)* For slow server connections you can increaste the timeout value (default 90 secs).
|
||||
|
||||
SSL/TLS settings
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have problems with SSL/TLS and self-signed certificates, you can change the following settings at your own risk.
|
||||
|
||||
The recommended way would be to add the self signed cert to the Local Computer Trusted Root Certification Authorities
|
||||
|
||||
You can import the cert by running the MMC as Administrator.
|
||||
|
||||
* *Disable Certificate Validation* set to true to disable SSL/TLS certificate validation, major security risk, use with caution!
|
||||
|
||||
* *Enable Client Certificates* If enabled, the available client certificates from the Windows user certificate store will automatically be provided.
|
||||
|
||||
* *Enable Tls12* set to false to disable TLS12, not recommended!
|
||||
|
||||
* *Enable Ssl3* set to true to enable deprecated SSLv3, major security risk, use with caution!
|
||||
|
||||
Logging
|
||||
^^^^^^^
|
||||
|
||||
In the *General Logging* section you can show or clear the log file and define the log level. Possible log levels are `INFO` and `DEBUG`.
|
||||
|
||||
You can also configure Synchronization reports for all profiles, this can be configured via general Options:
|
||||
|
||||
* *Log* You can choose if you want to generate reports for *"Only sync runs with errors"* or *"Sync runs with errors or warnings"* or *"All sync runs"*.
|
||||
|
||||
* *Show immediately* configures if the Sync reports should be shown immediately after a sync run with errors, with warnings or errors, or not at all.
|
||||
|
||||
* *Delete reports older than (days)* Automatically delete reports which are older than the days configured.
|
||||
|
||||
You can show reports manually with the *Reports* button in the CalDav Synchronizer Ribbon. There you can choose from available reports (shown as profile name with timestamp of the sync run) and see informations about items synced and if there were any warnings or errors. You can also delete reports or add them to a zip file via the context menu. If the last sync run lead to any errors, a warning symbol is shown in the Ribbon or the Report window opens if configured in the general options.
|
||||
|
||||
Profile Import/Export
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the toolbar of the synchronization profiles you can export all profiles to a file and import profiles from an earlier exported file. When exporting, you can choose a filename, the extension is *.cdsp and all options are saved in an xml format into this file. When importing the file, existing profiles are merged with the imported ones. If the selected Outlook folder for the profile doesn’t exist during import, you need to manually select a folder before you can save the options, they are not automatically created. You need also be aware of the fact, that saved profile passwords won’t work on other accounts or machines, since the encryption is dependant on the current user. But you can use the account password from the IMAP/POP3 account if available. General options are not saved in that file, but in the registry in `HKEY_CURRENT_USER\Software\CalDavSynchronizer`.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
Synchronization Status
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With the *Status* button in the CalDav Synchronizer Ribbon or via doubleclick from the TrayIcon you can access the status of the active sync profiles with their last sync run shown in minutes ago and the status OK, error, or warning. When clicking on the profile name you get to the according sync profile settings, when clicking the status icon, you can open the according sync report. When a sync run has any errors or warnings you will get a notification from the CalDav Synchronizer TrayIcon.
|
||||
|
||||
Debugging
|
||||
~~~~~~~~~
|
||||
|
||||
Options and state information is normally stored in the following folder:
|
||||
|
||||
----
|
||||
C:\Users\<Your Username>\AppData\Local\CalDavSychronizer
|
||||
----
|
||||
|
||||
If you activated Store data in roaming folder the location is changed to the following folder:
|
||||
|
||||
----
|
||||
C:\Users\<Your Username>\AppData\Roaming\CalDavSychronizer
|
||||
----
|
||||
|
||||
There is one `options_<your outlook profile>.xml` file which stores the options for each outlook profile.
|
||||
|
||||
For each sync profile there is a subfolder with state information stored in a relations.xml file after the inital sync. If you delete that folder, a fresh inital sync is performed. In the Synchronization profiles dialog a context menu is available in each profile (right click), which allows to open the cache directory and read the relations.xml file.
|
||||
|
||||
Each synchronization attempt is logged in the `log.txt` file. There you can find information about sync duration and the amount of added, deleted or modified events. Errors and Exceptions are logged aswell. You can view and clear the log file in *General Options*. There you can also change the log level from `INFO` to `DEBUG`.
|
||||
|
||||
In the install dir (The default is `C:\Program Files (x86)\CalDavSynchronizer`) you will find the app config file
|
||||
` CalDavSynchronizer.dll.config`
|
||||
|
||||
In that xml file you can config timeout parameters and config options in the section `appSettings`
|
||||
|
||||
After changing parameters you have to restart Outlook.
|
||||
|
||||
* *wpfRenderModeSoftwareOnly*: When set to true, turn off hardware acceleration and use Software Rendering only. Useful if you have issues with WPF and your graphics card driver.
|
||||
|
||||
You can also change defaults for some of the general options like CheckForNewVersions, StoreAppDatainRoamingFolder, IncludeCustomMessageClasses and SSL/TLS options, useful for All Users deployment, because general options are stored per user in the HKCU registry hive.
|
||||
|
||||
In the section `system.net` you can define proxy settings, e.g. use of NTLM credentials
|
||||
|
||||
----
|
||||
<defaultProxy useDefaultCredentials="true">
|
||||
</defaultProxy>
|
||||
----
|
||||
|
||||
In this section you can also allow UnsafeHeaderParsing if the server sends invalid http headers.
|
||||
|
||||
----
|
||||
<system.net>
|
||||
<settings>
|
||||
<servicePointManager expect100Continue="false" />
|
||||
<httpWebRequest useUnsafeHeaderParsing="true" />
|
||||
</settings>
|
||||
</system.net>
|
||||
----
|
||||
|
||||
This setting can also be enabled in the general options, starting with version 2.10.0.
|
||||
|
||||
In the section `log4net` you can define the log level for the main log (also possible in general options now).
|
||||
level value can be DEBUG or INFO, e.g.:
|
||||
|
||||
----
|
||||
<root>
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="MainLogAppender" />
|
||||
</root>
|
||||
----
|
||||
|
||||
Common network errors
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* System.Net.Http.HttpRequestException: Response status code does not indicate success: '401' ('Unauthorized').
|
||||
** Wrong Username and/or Password provided.
|
||||
|
||||
* System.Net.Http.HttpRequestException: An error occurred while sending the request. --→ System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
|
||||
** The server has KeepAlive disabled. Use *"Close connection after each request"* in *Network and proxy options*.
|
||||
|
||||
* System.Net.Http.HttpRequestException: An error occurred while sending the request. --→ System.Net.WebException: The server committed a protocol violation. Section=ResponseStatusLine
|
||||
** The server sends invalid headers. Enable the general option *Enable useUnsafeHeaderParsing* or the commented out option *useUnsafeHeaderparsing* in the app config file.
|
Binary file not shown.
After Width: | Height: | Size: 433 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue