pax_global_header 0000666 0000000 0000000 00000000064 13623463701 0014517 g ustar 00root root 0000000 0000000 52 comment=73afb5a6b545e5298ff4d50c108c8fdcd8a18357
EAS-4-TbSync-1.12/ 0000775 0000000 0000000 00000000000 13623463701 0013413 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/.github/ 0000775 0000000 0000000 00000000000 13623463701 0014753 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/.github/issue_template.md 0000664 0000000 0000000 00000001063 13623463701 0020320 0 ustar 00root root 0000000 0000000 ## Your environment
TbSync version:
EAS-4-TbSync version:
Thunderbird version:
[ ] Yes, I have installed the latest available (beta) version from
- https://github.com/jobisoft/TbSync/releases
- https://github.com/jobisoft/EAS-4-TbSync/releases
and my issue is not yet fixed, I can still reproduce it.
## Expected behavior
...
## Actual behavior
...
## Steps to reproduce
...
To help resolving your issue, enable debug logging (TbSync Account Manager -> Help) and send me the debug.log via e-mail (use the title of your issue as subject of the email).
EAS-4-TbSync-1.12/.gitignore 0000664 0000000 0000000 00000000006 13623463701 0015377 0 ustar 00root root 0000000 0000000 *.xpi
EAS-4-TbSync-1.12/CONTRIBUTORS.md 0000664 0000000 0000000 00000000550 13623463701 0015672 0 ustar 00root root 0000000 0000000 ## Creator
* John Bieling
## Contributors
* Chris Allan (recurring events)
* John Bieling
* Emil Ljungdahl
* Mark Nethersole (initial implementation of contact sync)
* Dzamo Norton
* Puran2
* Tijuca
## Translators
* John Bieling (de, en-US)
* Wanderlei Hüttel (pt-BR)
* Alessandro Menti (it)
* Óvári (hu)
* Alexey Sinitsyn (ru)
* Daniel Wróblewski (pl)
EAS-4-TbSync-1.12/LICENSE 0000664 0000000 0000000 00000040525 13623463701 0014426 0 ustar 00root root 0000000 0000000 Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
EAS-4-TbSync-1.12/Makebeta 0000664 0000000 0000000 00000002614 13623463701 0015052 0 ustar 00root root 0000000 0000000 #!/bin/bash
# This file is part of EAS-4-TbSync.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# $1 link to base web server : https://tbsync.jobisoft.de/beta
# $2 local path of base web server : /var/www/jobisoft.de/tbsync/beta
# $3 name of XPI : EAS-4-TbSync.xpi
git clean -df
git checkout -- .
git pull
version=$(cat manifest.json | jq -r .version)
updatefile=update-eas.json
sed -i "s/%VERSION%/$version/g" "beta-release-channel-update.json"
sed -i "s|%LINK%|$1/$3|g" "beta-release-channel-update.json"
sed -i "s/static getApiVersion() { return \"/static getApiVersion() { return \"Beta /g" "content/provider.js"
echo "Releasing version $version via beta release channel (will include updateURL)"
sed -i "s|\"name\": \"__MSG_extensionName__\",|\"name\": \"EAS for TbSync [Beta Release Channel]\",|g" "manifest.json"
sed -i "s|\"gecko\": {|\"gecko\": {\n \"update_url\": \"$1/$updatefile\",|g" "manifest.json"
cp beta-release-channel-update.json $2/$updatefile
rm -f $3
rm -f $3.tar.gz
zip -r $3 content _locales skin chrome.manifest manifest.json bootstrap.js LICENSE CONTRIBUTORS.md
tar cfvz $3.tar.gz content _locales skin chrome.manifest manifest.json bootstrap.js LICENSE CONTRIBUTORS.md
cp $3 $2/$3
cp $3.tar.gz $2/$3.tar.gz
EAS-4-TbSync-1.12/Makefile.bat 0000664 0000000 0000000 00000000667 13623463701 0015631 0 ustar 00root root 0000000 0000000 @echo off
REM This file is part of EAS-4-TbSync.
REM
REM This Source Code Form is subject to the terms of the Mozilla Public
REM License, v. 2.0. If a copy of the MPL was not distributed with this
REM file, You can obtain one at http://mozilla.org/MPL/2.0/.
del EAS-4-TbSync.xpi
"C:\Program Files\7-Zip\7zG.exe" a -tzip EAS-4-TbSync.xpi content _locales skin chrome.manifest manifest.json CONTRIBUTORS.md LICENSE README.md bootstrap.js
EAS-4-TbSync-1.12/README.md 0000664 0000000 0000000 00000002372 13623463701 0014676 0 ustar 00root root 0000000 0000000 # EAS-4-TbSync
This provider add-on adds Exchange ActiveSync (EAS v2.5 & v14.0) synchronization capabilities to [TbSync](https://github.com/jobisoft/TbSync/).
More information can be found in the [wiki](https://github.com/jobisoft/EAS-4-TbSync/wiki/About:-Provider-for-Exchange-ActiveSync) of this repository
## Want to add or fix a localization?
To help translating this project, please visit [crowdin.com](https://crowdin.com/profile/jobisoft), where the localizations are managed. If you want to add a new language, just contact me and I will set it up.
## External data sources
* TbSync uses a [timezone mapping](https://github.com/mj1856/TimeZoneConverter/blob/master/src/TimeZoneConverter/Data/Mapping.csv.gz) provided by [Matt Johnson](https://github.com/mj1856)
## Icon sources and attributions
#### CC0 Public Domain
* [365_*.png] by [Microsoft / Wikimedia](https://commons.wikimedia.org/w/index.php?curid=21546299), converted from [SVG to PNG](https://ezgif.com/svg-to-png)
#### CC-BY 3.0
* [eas*.png] by [FatCow Web Hosting](https://www.iconfinder.com/icons/64484/exchange_ms_icon)
* [exchange_300.png] derived from [Microsoft Exchange Icon #270871](https://icon-library.net/icon/microsoft-exchange-icon-10.html), [resized](www.simpleimageresizer.com/)
EAS-4-TbSync-1.12/_locales/ 0000775 0000000 0000000 00000000000 13623463701 0015174 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/Readme.txt 0000664 0000000 0000000 00000000351 13623463701 0017131 0 ustar 00root root 0000000 0000000 Want to add or fix a localization?
To help translating this project, please visit
https://crowdin.com/profile/jobisoft
where the localizations are managed. If you want to add
a new language, just contact me and I will set it up.
EAS-4-TbSync-1.12/_locales/bg/ 0000775 0000000 0000000 00000000000 13623463701 0015564 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/bg/eas.dtd 0000664 0000000 0000000 00000010307 13623463701 0017032 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/bg/eas.properties 0000664 0000000 0000000 00000021062 13623463701 0020453 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Add account
newaccount.add_auto=Autodiscover settings and add account
autodiscover.Querying=Searching for settings…
autodiscover.Ok=Autodiscover completed successfully, you can now check the optional settings and establish the synchronization connection.
autodiscover.NeedEmail=Autodiscover needs a valid email address as user name.
autodiscover.Failed=Autodiscover for user <##user##> failed. Either the provided credentials were wrong or your ActiveSync provider has a temporary issue, or does not support Autodiscover.
deletefolder.notallowed=Please unsubscribe folder “##replace.1##” before trying to purge it from trash.
deletefolder.confirm=Do you really want to PERMANENTLY PURGE folder “##replace.1##” from trash?
deletefolder.menuentry=Permanently purge folder “##replace.1##” from trash
recyclebin = Trash
syncstate.prepare.request.setdeviceinfo=Sending device information
syncstate.send.request.setdeviceinfo=Sending device information
syncstate.eval.response.setdeviceinfo=Sending device information
syncstate.syncing=Initialize synchronization
syncstate.preparing=Preparing next item for synchronization
syncstate.done=Preparing next item for synchronization
syncstate.accountdone=Finished account
syncstate.prepare.request.autodiscover=Requesting updated server settings
syncstate.send.request.autodiscover=Waiting for updated server settings
syncstate.eval.response.autodiscover=Processing updated server settings
syncstate.prepare.request.options=Requesting server options
syncstate.send.request.options=Waiting for server options
syncstate.eval.response.options=Processing server options
syncstate.prepare.request.folders=Sending folder list update
syncstate.send.request.folders=Waiting for folder list update
syncstate.eval.response.folders=Processing folder list update
syncstate.prepare.request.synckey=Requesting SyncKey
syncstate.send.request.synckey=Waiting for SyncKey
syncstate.eval.response.synckey=Processing SyncKey
syncstate.prepare.request.deletefolder=Preparing to delete folder
syncstate.send.request.deletefolder=Waiting for folder to be deleted
syncstate.eval.response.deletefolder=Folder deleted
syncstate.prepare.request.provision=Requesting provision
syncstate.send.request.provision=Waiting for provision
syncstate.eval.response.provision=Processing provision
syncstate.prepare.request.estimate=Requesting change estimate
syncstate.send.request.estimate=Waiting for change estimate
syncstate.eval.response.estimate=Processing change estimate
syncstate.prepare.request.remotechanges=Requesting remote changes
syncstate.send.request.remotechanges=Waiting for remote changes
syncstate.eval.response.remotechanges=Processing remote changes
syncstate.prepare.request.localchanges=Sending local changes
syncstate.send.request.localchanges=Waiting for acknowledgment of local changes
syncstate.eval.response.localchanges=Processing acknowledgment of local changes
syncstate.prepare.request.revertlocalchanges=Collecting local changes
syncstate.send.request.revertlocalchanges=Waiting for most recent versions
syncstate.eval.response.revertlocalchanges=Reverting local changes
syncstate.prepare.request.localdeletes=Sending local deletes
syncstate.send.request.localdeletes=Waiting for acknowledgment of local deletes
syncstate.eval.response.localdeletes=Processing acknowledgment of local deletes
status.notsyncronized=Account needs to be synchronized, at least one item is out of sync.
status.disabled=Disabled
status.notargets=Aborting Sync, because sync targets could not be created.
status.nouserhost=Missing username and/or server. Please provide those values.
status.timeout=Communication timeout.
status.networkerror=Could not connect to server.
status.404=User not found (HTTP Error 404).
status.403=Server rejected connection (forbidden) (HTTP Error 403).
status.401=Could not authenticate, check username and password (HTTP Error 401).
status.449=Server requests provisioning (HTTP Error 449).
status.500=Unknown Server Error (HTTP Error 500).
status.503=Service unavailable (HTTP Error 503).
status.httperror=Communication error (HTTP status ##replace.1##).
status.empty-response=Server sends unexpected empty response.
status.wbxml-parse-error=Server sends unreadable response.
status.response-contains-no-data=Response from the server contains no data.
status.wbxmlerror=Sync failed. Server responded with status <##replace.1##>.
status.wbxmlmissingfield=ActiveSync protocol violation: Mandatory field <##replace.1##> is missing from server response.
status.OK=OK
status.nolightning=Lightning add-on not installed, calendars are not supported.
status.malformed-xml=Could not parse XML. Check event log for details.
status.invalid=Invalid server response (junk).
status.InvalidServerOptions=The server does not provide information about the supported ActiveSync versions. Is EAS blocked for this user or this client (TbSync)? You could try to set the ActiveSync version manually.
status.security=Could not establish a secure connection. Are you using a self-signed or otherwise untrusted certificate without importing it into Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Could not connect to server (##replace.1##).
status.syncing=Synchronizing
status.skipped=Not yet supported, skipped
status.aborted=Not synchronized
status.pending=Waiting to be synchronized
status.modified=Local modifications
status.resync-loop=There was an error from which we could not recover by resyncing the account. Please disable the account and try again. (Error: resync loop)
status.policy.2=There is no policy for this client. Contact your server administrator or disable provisioning for this account.
status.policy.3=Unknown PolicyType value. Contact your server administrator or disable provisioning for this account.
status.policy.4=The policy data on the server is corrupted (possibly tampered with). Contact your server administrator or disable provisioning for this account.
status.policy.5=The client is acknowledging the wrong policy key. Contact your server administrator or disable provisioning for this account.
status.provision=Provisioning failed with status <##replace.1##>
status.global.101=The request contains WBXML but it could not be decoded into XML (EAS Error 101).
status.global.102=The request contains WBXML but it could not be decoded into XML (EAS Error 102).
status.global.103=The XML provided in the request does not follow the protocol requirements (EAS Error 103).
status.global.110=The server reported an internal error and we should not retry immediately. Automatic periodic sync has been disabled for 30 minutes (EAS Error 110).
status.global.clientdenied=The EAS server reports <##replace.2##> (status ##replace.1##) and does not allow TbSync to access your account.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=The EAS server did not accept ##replace.1## elements.
status.ServerRejectedRequest=The EAS Server rejected the last request.
status.BadItemSkipped=Bad Item Skipped: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Server does not support ActiveSync v2.5 or v14.0 (only ##replace.1##). TbSync will not work with this ActiveSync server.
status.notsupportedeasversion=Server does not support selected ActiveSync v##replace.1## (only ##replace.2##).
status.Sync.3=Invalid synchronization key (status 3), resyncing
status.Sync.4=Malformed request (status 4)
status.Sync.5=Temporary server issues or invalid item (status 5)
status.Sync.6=Invalid item (status 6)
status.Sync.8=Object not found (status 8)
status.Sync.12=Folder hierarchy changed (status 12), resyncing
status.FolderDelete.3=Cannot delete a system folder (status 3)
status.FolderDelete.6=Command could not be completed, an error occurred on the server (status 6)
status.FolderDelete.4=Folder does not exist (status 4), resyncing
status.FolderDelete.9=Invalid synchronization key (status 9), resyncing
status.FolderSync.9=Invalid synchronization key (status 9), resyncing
status.forbiddenTasksItemInCalendarFolder=Forbidden task item in a calendar folder (please resort)
status.forbiddenCalendarItemInTasksFolder=Forbidden calendar item in a task folder (please resort)
config.auto=ActiveSync server configuration (Autodiscover)
config.custom=ActiveSync server configuration
acl.readwrite=Read from and write to server
acl.readonly=Read-only server access (revert local changes)
autocomplete.serverdirectory = global server directory
EAS-4-TbSync-1.12/_locales/bg/messages.json 0000664 0000000 0000000 00000000335 13623463701 0020267 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provider for Exchange ActiveSync"
},
"extensionDescription": {
"message": "Add sync support for Exchange ActiveSync accounts to TbSync (contacts, tasks and calendars)."
}
} EAS-4-TbSync-1.12/_locales/de/ 0000775 0000000 0000000 00000000000 13623463701 0015564 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/de/eas.dtd 0000664 0000000 0000000 00000010540 13623463701 0017031 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/de/eas.properties 0000664 0000000 0000000 00000023772 13623463701 0020465 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Konto hinzufügen
newaccount.add_auto=Einstellungen suchen und Konto hinzufügen
autodiscover.Querying=Einstellungen werden gesucht ...
autodiscover.Ok=Autodiscover war erfolgreich, Sie können nun die optionalen Einstellungen überprüfen und die Synchronisationsverbindung aktivieren.
autodiscover.NeedEmail=Autodiscover benötigt eine gültige E-Mail Adresse als Benutzername.
autodiscover.Failed=Autodiscover für den Benutzer <##user##> war nicht erfolgreich. Entweder war das Passwort nicht korrekt, Ihr ActiveSync Server hat ein temporäres Problem oder unterstützt kein Autodiscover.
deletefolder.notallowed=Bitte beenden Sie zunächst das Abonement des Ordner "##replace.1##", bevor Sie diesen aus dem Papierkorb entfernen.
deletefolder.confirm=Möchten Sie den Ordner "##replace.1##" wirklich ENDGÜLTIG aus dem Papierkorb ENTFERNEN?
deletefolder.menuentry=Den Ordner "##replace.1##" endgültig aus dem Papierkorb entfernen
recyclebin = Papierkorb
syncstate.prepare.request.setdeviceinfo=Sende Geräteinformationen
syncstate.send.request.setdeviceinfo=Sende Geräteinformationen
syncstate.eval.response.setdeviceinfo=Sende Geräteinformationen
syncstate.syncing=Initiiere Synchronisation
syncstate.preparing=Bereite das nächste Element für die Synchronisation vor
syncstate.done=Bereite das nächste Element für die Synchronisation vor
syncstate.accountdone=Kontosynchronisation abgeschlossen
syncstate.prepare.request.autodiscover=Sende Anfrage bzgl. aktualisierter Servereinstellungen
syncstate.send.request.autodiscover=Warte auf aktualisierte Servereinstellungen
syncstate.eval.response.autodiscover=Verarbeite aktualisierte Servereinstellungen
syncstate.prepare.request.options=Sende Anfrage bzgl. aktualisierter Serveroptionen
syncstate.send.request.options=Warte auf aktualisierte Serveroptionen
syncstate.eval.response.options=Verarbeite aktualisierte Serveroptionen
syncstate.prepare.request.folders=Sende Anfrage bzgl. aktualisierter Ordnerliste
syncstate.send.request.folders=Warte auf aktualisierte Ordnerliste
syncstate.eval.response.folders=Verarbeite aktualisierte Ordnerliste
syncstate.prepare.request.synckey=Sende Anfrage bzgl. Synchronisationsschlüssel
syncstate.send.request.synckey=Warte auf Synchronisationsschlüssel
syncstate.eval.response.synckey=Verarbeite Synchronisationsschlüssel
syncstate.prepare.request.deletefolder=Löschen des Ordners wird vorbereitet
syncstate.send.request.deletefolder=Ordner wird gelöscht
syncstate.eval.response.deletefolder=Ordner erfolgreich gelöscht
syncstate.prepare.request.provision=Sende Anfrage bzgl. Provisionierung
syncstate.send.request.provision=Warte auf Provisionierung
syncstate.eval.response.provision=Verarbeite Provisionierung
syncstate.prepare.request.estimate=Sende Anfrage bzgl. geschätzter Änderungen
syncstate.send.request.estimate=Warte auf geschätzte Änderungen
syncstate.eval.response.estimate=Verarbeite geschätzte Änderungen
syncstate.prepare.request.remotechanges=Sende Anfrage bzgl. Änderungen
syncstate.send.request.remotechanges=Warte auf aktualisierte Elemente
syncstate.eval.response.remotechanges=Verarbeite aktualisierte Elemente
syncstate.prepare.request.localchanges=Sende lokale Änderungen
syncstate.send.request.localchanges=Warte auf Bestätigung der lokalen Änderungen
syncstate.eval.response.localchanges=Verarbeite Bestätigung der lokalen Änderungen
syncstate.prepare.request.revertlocalchanges=Prüfe lokale Änderungen
syncstate.send.request.revertlocalchanges=Warte auf aktuelle Versionen
syncstate.eval.response.revertlocalchanges=Lokale Änderungen werden verworfen
syncstate.prepare.request.localdeletes=Sende die lokal gelöschten Elemente
syncstate.send.request.localdeletes=Warte auf Bestätigung lokal gelöschten Elemente
syncstate.eval.response.localdeletes=Verarbeite Bestätigung lokal gelöschten Elemente
status.notsyncronized=Konto muss synchronisiert werden.
status.disabled=Konto ist deaktiviert, Synchronisation ist ausgeschaltet.
status.notargets=Synchronisation abgebrochen da die Elemente zum Synchronisieren nicht erstellt werden konnten.
status.nouserhost=Angabe des Benutzernamens und/oder des Servers fehlt. Bitte die korrekten Informationen eintragen.
status.timeout=Kommunikations-Timeout.
status.networkerror=Verbindung zum Server fehlgeschlagen.
status.404=Unbekanner Benutzer (HTTP Fehler 404).
status.403=Verbindung vom Server abgelehnt (nicht erlaubt).
status.401=Authentifizierung fehlgeschlagen, überprüfen Sie den Benutzernamen und das Passwort.
status.449=Server erwartet Provisionierung.
status.500=Unbekannter Server Fehler (HTTP Fehler 500).
status.503=Service nicht erreichbar.
status.httperror=Kommunikationsfehler (HTTP Status ##replace.1##).
status.empty-response=Server sendet unerwartete leere Antwort.
status.wbxml-parse-error=Server sendet nicht lesbare Antwort.
status.response-contains-no-data=Antwort vom Server enthält keine Daten.
status.wbxmlerror=Synchronisation fehlgeschlagen. Der Server antwortete mit dem Status <##replace.1##>.
status.wbxmlmissingfield=Verletzung des ActiveSync Protokolls: Notwendiges Feld <##replace.1##> ist nicht in der Serverantwort enthalten.
status.OK=Ok
status.nolightning=Lightning Add-On nicht installiert, Kalender nicht nutzbar.
status.malformed-xml=Antwort enthält fehlerhaftes XML, Sync angebrochen. Prüfen Sie bitte das Ereignisprotokoll für weitere Details.
status.invalid=Ungültige Serverantwort.
status.InvalidServerOptions=Der Server sendet keine Informationen zu den unterstützten ActiveSync Versionen. Ist EAS für diesen Benutzer bzw. für dieses Programm (TbSync) freigeschaltet? Sie können versuchen, die ActiveSync Version manuell festzulegen.
status.security=Fehler beim Aufbau einer sicherern Verbindung. Benutzen Sie eventuell ein selbst signiertes Zertifikat oder ein andersartiges nicht vertrauenswürdiges Zertifikat welches nicht in Thunderbird importiert ist? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Verbindung zum Server fehlgeschlagen (##replace.1##).
status.syncing=Synchronisierung
status.skipped=Nicht unterstützt
status.aborted=Nicht synchronisiert
status.pending=Warten auf Synchronisation
status.modified=Lokale Änderungen
status.resync-loop=Es ist ein Fehler aufgetreten, der nicht durch wiederholtes Synchronisieren des Kontos behoben werden konnte. Bitte deaktivieren sie das Konto und wiederholen den Vorgang. (Fehler: Synchronisationsschleife)
status.policy.2=Es gibt keine Regel (policy) für diesen Klient. Kontaktieren Sie Ihren Server Administrator oder deaktivieren Sie die Provisionierungsoption für dieses Konto in TbSync.
status.policy.3=Unbekannter Wert für den Regeltyp (policy type). Kontaktieren Sie Ihren Server Administrator oder deaktivieren Sie die Provisionierungsoption für dieses Konto in TbSync.
status.policy.4=Die Daten für die Regeln auf dem Server sind beschädigt (möglicherweise manipuliert). Kontaktieren Sie Ihren Server Administrator oder deaktivieren Sie die Provisionierungsoption für dieses Konto in TbSync.
status.policy.5=Der Klient bestätigt einen falschen Richtlinienschlüssel (policy key). Kontaktieren Sie Ihren Server Administrator oder deaktivieren Sie die Provisionierungsoption für dieses Konto in TbSync.
status.provision=Provisionierung fehlerhaft mit folgendem Status <##replace.1##>
status.global.101=Das WBXML der Anfrage konnte nicht in gültiges XML dekodiert werden (EAS Fehler 101).
status.global.102=Das WBXML der Anfrage konnte nicht in gültiges XML dekodiert werden (EAS Fehler 102).
status.global.103=Das XML der Anfrage entspricht nicht den Protokollanforderungen (EAS Fehler 103).
status.global.110=Der Server meldet einen internen Fehler und es soll nicht unmittelbar ein erneuter Verbindungsaufbau durchgeführt werden. Die automatische periodische Synchronisation wird für 30 Minuten ausgesetzt (EAS Fehler 110).
status.global.clientdenied=Der EAS Server antwortet mit <##replace.2##> (status ##replace.1##) und verweigert TbSync den Zugriff auf Ihr Konto.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=Der EAS Server hat ##replace.1## Elemente nicht akzeptiert.
status.ServerRejectedRequest=Der EAS Server hat die letzte Anforderung zurückgewiesen.
status.BadItemSkipped=Ein Element wurde nicht synchronisiert: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Der Server unterstützt kein ActiveSync v2.5 oder v14.0 (nur ##replace.1##). TbSync kann nicht mit diesem Server arbeiten.
status.notsupportedeasversion=Der Server unterstützt nicht die ausgewählte ActiveSync Version v##replace.1## (nur ##replace.2##).
status.Sync.3=Ungültiger Synchronisationsschlüssel, es wird neu synchronisiert
status.Sync.4=Fehlerhafte Anfrage (Status 4)
status.Sync.5=Temporäres Serverproblem oder ungültiges Element (Status 5)
status.Sync.6=Ungültiges Element (Status 6)
status.Sync.8=Objekt nicht gefunden (Status 8)
status.Sync.12=Ordnerhierarchie hat sich geändert, es wird neu synchronisiert
status.FolderDelete.3=Systemordner können nicht gelöscht werden.
status.FolderDelete.6=Das Kommando konnte nicht abgeschlossen werden, auf dem Server ist ein Fehler aufgetreten.
status.FolderDelete.4=Ordner existiert nicht, es wird neu synchronisiert
status.FolderDelete.9=Ungültiger Synchronisationsschlüssel, es wird neu synchronisiert
status.FolderSync.9=Ungültiger Synchronisationsschlüssel, es wird neu synchronisiert
status.forbiddenTasksItemInCalendarFolder=Unerlaubtes Aufgaben-Element in einem Kalender-Objekt (bitte umsortieren)
status.forbiddenCalendarItemInTasksFolder=Unerlaubtes Kalender-Element in einem Aufgaben-Objekt (bitte umsortieren)
config.auto=ActiveSync Server Konfiguration (Autodiscover)
config.custom=ActiveSync Server Konfiguration
acl.readwrite=Serverzugriff lesend und schreibend
acl.readonly=Serverzugriff nur lesend (verwerfe lokale Änderungen)
autocomplete.serverdirectory = Globales Serververzeichnis
EAS-4-TbSync-1.12/_locales/de/messages.json 0000664 0000000 0000000 00000000364 13623463701 0020271 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provider für Exchange ActiveSync"
},
"extensionDescription": {
"message": "Erweitert TbSync und erlaubt die Synchronisation von Exchange ActiveSync Konten (Kontakte, Aufgaben und Kalender)."
}
} EAS-4-TbSync-1.12/_locales/en-US/ 0000775 0000000 0000000 00000000000 13623463701 0016123 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/en-US/eas.dtd 0000664 0000000 0000000 00000010307 13623463701 0017371 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/en-US/eas.properties 0000664 0000000 0000000 00000021062 13623463701 0021012 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Add account
newaccount.add_auto=Autodiscover settings and add account
autodiscover.Querying=Searching for settings…
autodiscover.Ok=Autodiscover completed successfully, you can now check the optional settings and establish the synchronization connection.
autodiscover.NeedEmail=Autodiscover needs a valid email address as user name.
autodiscover.Failed=Autodiscover for user <##user##> failed. Either the provided credentials were wrong or your ActiveSync provider has a temporary issue, or does not support Autodiscover.
deletefolder.notallowed=Please unsubscribe folder “##replace.1##” before trying to purge it from trash.
deletefolder.confirm=Do you really want to PERMANENTLY PURGE folder “##replace.1##” from trash?
deletefolder.menuentry=Permanently purge folder “##replace.1##” from trash
recyclebin = Trash
syncstate.prepare.request.setdeviceinfo=Sending device information
syncstate.send.request.setdeviceinfo=Sending device information
syncstate.eval.response.setdeviceinfo=Sending device information
syncstate.syncing=Initialize synchronization
syncstate.preparing=Preparing next item for synchronization
syncstate.done=Preparing next item for synchronization
syncstate.accountdone=Finished account
syncstate.prepare.request.autodiscover=Requesting updated server settings
syncstate.send.request.autodiscover=Waiting for updated server settings
syncstate.eval.response.autodiscover=Processing updated server settings
syncstate.prepare.request.options=Requesting server options
syncstate.send.request.options=Waiting for server options
syncstate.eval.response.options=Processing server options
syncstate.prepare.request.folders=Sending folder list update
syncstate.send.request.folders=Waiting for folder list update
syncstate.eval.response.folders=Processing folder list update
syncstate.prepare.request.synckey=Requesting SyncKey
syncstate.send.request.synckey=Waiting for SyncKey
syncstate.eval.response.synckey=Processing SyncKey
syncstate.prepare.request.deletefolder=Preparing to delete folder
syncstate.send.request.deletefolder=Waiting for folder to be deleted
syncstate.eval.response.deletefolder=Folder deleted
syncstate.prepare.request.provision=Requesting provision
syncstate.send.request.provision=Waiting for provision
syncstate.eval.response.provision=Processing provision
syncstate.prepare.request.estimate=Requesting change estimate
syncstate.send.request.estimate=Waiting for change estimate
syncstate.eval.response.estimate=Processing change estimate
syncstate.prepare.request.remotechanges=Requesting remote changes
syncstate.send.request.remotechanges=Waiting for remote changes
syncstate.eval.response.remotechanges=Processing remote changes
syncstate.prepare.request.localchanges=Sending local changes
syncstate.send.request.localchanges=Waiting for acknowledgment of local changes
syncstate.eval.response.localchanges=Processing acknowledgment of local changes
syncstate.prepare.request.revertlocalchanges=Collecting local changes
syncstate.send.request.revertlocalchanges=Waiting for most recent versions
syncstate.eval.response.revertlocalchanges=Reverting local changes
syncstate.prepare.request.localdeletes=Sending local deletes
syncstate.send.request.localdeletes=Waiting for acknowledgment of local deletes
syncstate.eval.response.localdeletes=Processing acknowledgment of local deletes
status.notsyncronized=Account needs to be synchronized, at least one item is out of sync.
status.disabled=Disabled
status.notargets=Aborting Sync, because sync targets could not be created.
status.nouserhost=Missing username and/or server. Please provide those values.
status.timeout=Communication timeout.
status.networkerror=Could not connect to server.
status.404=User not found (HTTP Error 404).
status.403=Server rejected connection (forbidden) (HTTP Error 403).
status.401=Could not authenticate, check username and password (HTTP Error 401).
status.449=Server requests provisioning (HTTP Error 449).
status.500=Unknown Server Error (HTTP Error 500).
status.503=Service unavailable (HTTP Error 503).
status.httperror=Communication error (HTTP status ##replace.1##).
status.empty-response=Server sends unexpected empty response.
status.wbxml-parse-error=Server sends unreadable response.
status.response-contains-no-data=Response from the server contains no data.
status.wbxmlerror=Sync failed. Server responded with status <##replace.1##>.
status.wbxmlmissingfield=ActiveSync protocol violation: Mandatory field <##replace.1##> is missing from server response.
status.OK=OK
status.nolightning=Lightning add-on not installed, calendars are not supported.
status.malformed-xml=Could not parse XML. Check event log for details.
status.invalid=Invalid server response (junk).
status.InvalidServerOptions=The server does not provide information about the supported ActiveSync versions. Is EAS blocked for this user or this client (TbSync)? You could try to set the ActiveSync version manually.
status.security=Could not establish a secure connection. Are you using a self-signed or otherwise untrusted certificate without importing it into Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Could not connect to server (##replace.1##).
status.syncing=Synchronizing
status.skipped=Not yet supported, skipped
status.aborted=Not synchronized
status.pending=Waiting to be synchronized
status.modified=Local modifications
status.resync-loop=There was an error from which we could not recover by resyncing the account. Please disable the account and try again. (Error: resync loop)
status.policy.2=There is no policy for this client. Contact your server administrator or disable provisioning for this account.
status.policy.3=Unknown PolicyType value. Contact your server administrator or disable provisioning for this account.
status.policy.4=The policy data on the server is corrupted (possibly tampered with). Contact your server administrator or disable provisioning for this account.
status.policy.5=The client is acknowledging the wrong policy key. Contact your server administrator or disable provisioning for this account.
status.provision=Provisioning failed with status <##replace.1##>
status.global.101=The request contains WBXML but it could not be decoded into XML (EAS Error 101).
status.global.102=The request contains WBXML but it could not be decoded into XML (EAS Error 102).
status.global.103=The XML provided in the request does not follow the protocol requirements (EAS Error 103).
status.global.110=The server reported an internal error and we should not retry immediately. Automatic periodic sync has been disabled for 30 minutes (EAS Error 110).
status.global.clientdenied=The EAS server reports <##replace.2##> (status ##replace.1##) and does not allow TbSync to access your account.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=The EAS server did not accept ##replace.1## elements.
status.ServerRejectedRequest=The EAS Server rejected the last request.
status.BadItemSkipped=Bad Item Skipped: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Server does not support ActiveSync v2.5 or v14.0 (only ##replace.1##). TbSync will not work with this ActiveSync server.
status.notsupportedeasversion=Server does not support selected ActiveSync v##replace.1## (only ##replace.2##).
status.Sync.3=Invalid synchronization key (status 3), resyncing
status.Sync.4=Malformed request (status 4)
status.Sync.5=Temporary server issues or invalid item (status 5)
status.Sync.6=Invalid item (status 6)
status.Sync.8=Object not found (status 8)
status.Sync.12=Folder hierarchy changed (status 12), resyncing
status.FolderDelete.3=Cannot delete a system folder (status 3)
status.FolderDelete.6=Command could not be completed, an error occurred on the server (status 6)
status.FolderDelete.4=Folder does not exist (status 4), resyncing
status.FolderDelete.9=Invalid synchronization key (status 9), resyncing
status.FolderSync.9=Invalid synchronization key (status 9), resyncing
status.forbiddenTasksItemInCalendarFolder=Forbidden task item in a calendar folder (please resort)
status.forbiddenCalendarItemInTasksFolder=Forbidden calendar item in a task folder (please resort)
config.auto=ActiveSync server configuration (Autodiscover)
config.custom=ActiveSync server configuration
acl.readwrite=Read from and write to server
acl.readonly=Read-only server access (revert local changes)
autocomplete.serverdirectory = global server directory
EAS-4-TbSync-1.12/_locales/en-US/messages.json 0000664 0000000 0000000 00000000336 13623463701 0020627 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provider for Exchange ActiveSync"
},
"extensionDescription": {
"message": "Add sync support for Exchange ActiveSync accounts to TbSync (contacts, tasks and calendars)."
}
}
EAS-4-TbSync-1.12/_locales/fr/ 0000775 0000000 0000000 00000000000 13623463701 0015603 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/fr/eas.dtd 0000664 0000000 0000000 00000011322 13623463701 0017047 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/fr/eas.properties 0000664 0000000 0000000 00000021070 13623463701 0020471 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Ajouter un compte
newaccount.add_auto=Autodiscover settings and add account
autodiscover.Querying=Searching for settings…
autodiscover.Ok=Autodiscover completed successfully, you can now check the optional settings and establish the synchronization connection.
autodiscover.NeedEmail=Autodiscover needs a valid email address as user name.
autodiscover.Failed=Autodiscover for user <##user##> failed. Either the provided credentials were wrong or your ActiveSync provider has a temporary issue, or does not support Autodiscover.
deletefolder.notallowed=Please unsubscribe folder “##replace.1##” before trying to purge it from trash.
deletefolder.confirm=Do you really want to PERMANENTLY PURGE folder “##replace.1##” from trash?
deletefolder.menuentry=Permanently purge folder “##replace.1##” from trash
recyclebin = Trash
syncstate.prepare.request.setdeviceinfo=Sending device information
syncstate.send.request.setdeviceinfo=Sending device information
syncstate.eval.response.setdeviceinfo=Sending device information
syncstate.syncing=Initialize synchronization
syncstate.preparing=Preparing next item for synchronization
syncstate.done=Preparing next item for synchronization
syncstate.accountdone=Finished account
syncstate.prepare.request.autodiscover=Requesting updated server settings
syncstate.send.request.autodiscover=Waiting for updated server settings
syncstate.eval.response.autodiscover=Processing updated server settings
syncstate.prepare.request.options=Requesting server options
syncstate.send.request.options=Waiting for server options
syncstate.eval.response.options=Processing server options
syncstate.prepare.request.folders=Sending folder list update
syncstate.send.request.folders=Waiting for folder list update
syncstate.eval.response.folders=Processing folder list update
syncstate.prepare.request.synckey=Requesting SyncKey
syncstate.send.request.synckey=Waiting for SyncKey
syncstate.eval.response.synckey=Processing SyncKey
syncstate.prepare.request.deletefolder=Preparing to delete folder
syncstate.send.request.deletefolder=Waiting for folder to be deleted
syncstate.eval.response.deletefolder=Folder deleted
syncstate.prepare.request.provision=Requesting provision
syncstate.send.request.provision=Waiting for provision
syncstate.eval.response.provision=Processing provision
syncstate.prepare.request.estimate=Requesting change estimate
syncstate.send.request.estimate=Waiting for change estimate
syncstate.eval.response.estimate=Processing change estimate
syncstate.prepare.request.remotechanges=Requesting remote changes
syncstate.send.request.remotechanges=Waiting for remote changes
syncstate.eval.response.remotechanges=Processing remote changes
syncstate.prepare.request.localchanges=Sending local changes
syncstate.send.request.localchanges=Waiting for acknowledgment of local changes
syncstate.eval.response.localchanges=Processing acknowledgment of local changes
syncstate.prepare.request.revertlocalchanges=Collecting local changes
syncstate.send.request.revertlocalchanges=Waiting for most recent versions
syncstate.eval.response.revertlocalchanges=Reverting local changes
syncstate.prepare.request.localdeletes=Sending local deletes
syncstate.send.request.localdeletes=Waiting for acknowledgment of local deletes
syncstate.eval.response.localdeletes=Processing acknowledgment of local deletes
status.notsyncronized=Account needs to be synchronized, at least one item is out of sync.
status.disabled=Disabled
status.notargets=Aborting Sync, because sync targets could not be created.
status.nouserhost=Missing username and/or server. Please provide those values.
status.timeout=Communication timeout.
status.networkerror=Could not connect to server.
status.404=User not found (HTTP Error 404).
status.403=Server rejected connection (forbidden) (HTTP Error 403).
status.401=Could not authenticate, check username and password (HTTP Error 401).
status.449=Server requests provisioning (HTTP Error 449).
status.500=Unknown Server Error (HTTP Error 500).
status.503=Service unavailable (HTTP Error 503).
status.httperror=Communication error (HTTP status ##replace.1##).
status.empty-response=Server sends unexpected empty response.
status.wbxml-parse-error=Server sends unreadable response.
status.response-contains-no-data=Response from the server contains no data.
status.wbxmlerror=Sync failed. Server responded with status <##replace.1##>.
status.wbxmlmissingfield=ActiveSync protocol violation: Mandatory field <##replace.1##> is missing from server response.
status.OK=OK
status.nolightning=Lightning add-on not installed, calendars are not supported.
status.malformed-xml=Could not parse XML. Check event log for details.
status.invalid=Invalid server response (junk).
status.InvalidServerOptions=The server does not provide information about the supported ActiveSync versions. Is EAS blocked for this user or this client (TbSync)? You could try to set the ActiveSync version manually.
status.security=Could not establish a secure connection. Are you using a self-signed or otherwise untrusted certificate without importing it into Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Could not connect to server (##replace.1##).
status.syncing=Synchronizing
status.skipped=Not yet supported, skipped
status.aborted=Not synchronized
status.pending=Waiting to be synchronized
status.modified=Local modifications
status.resync-loop=There was an error from which we could not recover by resyncing the account. Please disable the account and try again. (Error: resync loop)
status.policy.2=There is no policy for this client. Contact your server administrator or disable provisioning for this account.
status.policy.3=Unknown PolicyType value. Contact your server administrator or disable provisioning for this account.
status.policy.4=The policy data on the server is corrupted (possibly tampered with). Contact your server administrator or disable provisioning for this account.
status.policy.5=The client is acknowledging the wrong policy key. Contact your server administrator or disable provisioning for this account.
status.provision=Provisioning failed with status <##replace.1##>
status.global.101=The request contains WBXML but it could not be decoded into XML (EAS Error 101).
status.global.102=The request contains WBXML but it could not be decoded into XML (EAS Error 102).
status.global.103=The XML provided in the request does not follow the protocol requirements (EAS Error 103).
status.global.110=The server reported an internal error and we should not retry immediately. Automatic periodic sync has been disabled for 30 minutes (EAS Error 110).
status.global.clientdenied=The EAS server reports <##replace.2##> (status ##replace.1##) and does not allow TbSync to access your account.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=The EAS server did not accept ##replace.1## elements.
status.ServerRejectedRequest=The EAS Server rejected the last request.
status.BadItemSkipped=Bad Item Skipped: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Server does not support ActiveSync v2.5 or v14.0 (only ##replace.1##). TbSync will not work with this ActiveSync server.
status.notsupportedeasversion=Server does not support selected ActiveSync v##replace.1## (only ##replace.2##).
status.Sync.3=Invalid synchronization key (status 3), resyncing
status.Sync.4=Malformed request (status 4)
status.Sync.5=Temporary server issues or invalid item (status 5)
status.Sync.6=Invalid item (status 6)
status.Sync.8=Object not found (status 8)
status.Sync.12=Folder hierarchy changed (status 12), resyncing
status.FolderDelete.3=Cannot delete a system folder (status 3)
status.FolderDelete.6=Command could not be completed, an error occurred on the server (status 6)
status.FolderDelete.4=Folder does not exist (status 4), resyncing
status.FolderDelete.9=Invalid synchronization key (status 9), resyncing
status.FolderSync.9=Invalid synchronization key (status 9), resyncing
status.forbiddenTasksItemInCalendarFolder=Forbidden task item in a calendar folder (please resort)
status.forbiddenCalendarItemInTasksFolder=Forbidden calendar item in a task folder (please resort)
config.auto=ActiveSync server configuration (Autodiscover)
config.custom=ActiveSync server configuration
acl.readwrite=Read from and write to server
acl.readonly=Read-only server access (revert local changes)
autocomplete.serverdirectory = global server directory
EAS-4-TbSync-1.12/_locales/fr/messages.json 0000664 0000000 0000000 00000000404 13623463701 0020303 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Fournisseur pour Exchange ActiveSync"
},
"extensionDescription": {
"message": "Ajouter la prise en charge de la synchronisation des comptes Exchange ActiveSync à TbSync (contacts, tâches et calendriers)."
}
}
EAS-4-TbSync-1.12/_locales/hu/ 0000775 0000000 0000000 00000000000 13623463701 0015610 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/hu/eas.dtd 0000664 0000000 0000000 00000011061 13623463701 0017054 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/hu/eas.properties 0000664 0000000 0000000 00000024002 13623463701 0020474 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Fiók hozzáadása
newaccount.add_auto=Az önműködő felismerés beállításai és fiók hozzáadása
autodiscover.Querying=Beállítások keresése…
autodiscover.Ok=Az önműködő felderítés sikeresen befejeződött, most ellenőrizheti az választható beállításokat és létrehozhatja az összehangolási kapcsolatot.
autodiscover.NeedEmail=Az önműködő felismerésnek érvényes e-mail címnek kell lennie felhasználói névként.
autodiscover.Failed=A felhasználó <##user##> önműködő felderítése sikertelen. Vagy a megadott hitelesítő adatok hibásak voltak, vagy az ActiveSync szolgáltató ideiglenes problémát okozott, vagy nem támogatja az önműködő felderítést.
deletefolder.notallowed=Kérjük, törölje le a(z) „##replace.1##” mappát, mielőtt megpróbálnánk kuka ürítése.
deletefolder.confirm=Tényleg azt szeretné, hogy a(z) „##replace.1##” mappa véglegesen törölje a kukát?
deletefolder.menuentry=„##replace.1##” mappa kuka ürítése
recyclebin = Kuka
syncstate.prepare.request.setdeviceinfo=Eszköz tájékoztatás küldése
syncstate.send.request.setdeviceinfo=Eszköz tájékoztatás küldése
syncstate.eval.response.setdeviceinfo=Eszköz tájékoztatás küldése
syncstate.syncing=Összehangolás kezdése
syncstate.preparing=Készítse elő a következő elemet az összehangolásához
syncstate.done=Készítse elő a következő elemet az összehangolásához
syncstate.accountdone=Fiók kész
syncstate.prepare.request.autodiscover=Frissített kiszolgáló beállítások kérése
syncstate.send.request.autodiscover=Várakozás a frissített kiszolgáló beállításokra
syncstate.eval.response.autodiscover=Frissített kiszolgáló beállítások feldolgozása
syncstate.prepare.request.options=A kiszolgáló beállításainak megkeresése
syncstate.send.request.options=Várakozás a kiszolgáló beállításaira
syncstate.eval.response.options=A kiszolgáló beállításainak feldolgozása
syncstate.prepare.request.folders=A mappalisták frissítés elküldése
syncstate.send.request.folders=Várakozás a mappa listájának frissítésére
syncstate.eval.response.folders=A mappalista frissítés feldolgozása
syncstate.prepare.request.synckey=Összehangoláskulcs kérése
syncstate.send.request.synckey=Várakozás az összehangoláskulcsra
syncstate.eval.response.synckey=Összehangoláskulcs feldolgozása
syncstate.prepare.request.deletefolder=Előkészület a mappa törlésére
syncstate.send.request.deletefolder=Várakozás a mappa törlésére
syncstate.eval.response.deletefolder=A mappa törölve
syncstate.prepare.request.provision=A rendelkezés igénylése
syncstate.send.request.provision=Várakozás a rendelkezésre
syncstate.eval.response.provision=Feldolgozási rendelkezés
syncstate.prepare.request.estimate=Módosítási becslés kérése
syncstate.send.request.estimate=Várakozás a becslés megváltoztatására
syncstate.eval.response.estimate=Feldolgozási becslés változása
syncstate.prepare.request.remotechanges=Távoli módosítások kérése
syncstate.send.request.remotechanges=Várakozás a távoli módosításokra
syncstate.eval.response.remotechanges=A távoli módosítások feldolgozása
syncstate.prepare.request.localchanges=Helyi változások küldése
syncstate.send.request.localchanges=Várakozás a helyi változások elismerésére
syncstate.eval.response.localchanges=A helyi változások elismerésének feldolgozása
syncstate.prepare.request.revertlocalchanges=Helyi változások gyűjtése
syncstate.send.request.revertlocalchanges=Várja a legfrissebb verziókat
syncstate.eval.response.revertlocalchanges=A helyi változások visszaállítása
syncstate.prepare.request.localdeletes=Helyi törlések küldése
syncstate.send.request.localdeletes=Várakozás a helyi törlések nyugtázására
syncstate.eval.response.localdeletes=A helyi törlések feldolgozása
status.notsyncronized=A fiókot összehangolni kell, legalább egy elem nem összehangolva van.
status.disabled=A fiók nincs engedélyezve, az összehangolás le van tiltva.
status.notargets=Az összehangolás megszakítása, mert a szinkronizálási célokat nem lehetett létrehozni.
status.nouserhost=Hiányzik a felhasználónév és/vagy a kiszolgáló név. Kérjük, adja meg ezeket az értékeket.
status.timeout=Közlés időtúllépés.
status.networkerror=Nem tudott csatlakozni a kiszolgálóhoz.
status.404=A felhasználó nem található (404-es HTTP-hiba).
status.403=Kiszolgáló elutasította a kapcsolatot (tiltott) (403-ás HTTP-hiba).
status.401=Nem sikerült hitelesíteni, ellenőrizni a felhasználónevet és a jelszót (401-es HTTP-hiba).
status.449=Kiszolgáló kérések készítése (449-es HTTP-hiba).
status.500=Ismeretlen kiszolgálóhiba (500-as HTTP-hiba).
status.503=A szolgáltatás nem elérhető (503-ás HTTP-hiba).
status.httperror=Közlés hiba (HTTP állapot ##replace.1##).
status.empty-response=A kiszolgáló váratlan üres választ küld.
status.wbxml-parse-error=A kiszolgáló olvashatatlan választ küld.
status.response-contains-no-data=A szerverre adott válasz nem tartalmaz adatokat.
status.wbxmlerror=A összehangolás sikertelen. A kiszolgáló <##replace.1##> állapotban válaszolt.
status.wbxmlmissingfield=ActiveSync egyezmény megsértése: A(z) <##replace.1##> kötelező mező hiányzik a kiszolgáló válaszából.
status.OK=Rendben van
status.nolightning=A Lightning kiegészítő nincs telepítve, a naptárak nem támogatottak.
status.malformed-xml=Nem sikerült elemezni az XML-t. Ellenőrizze az eseménynaplót a részletekért.
status.invalid=Érvénytelen kiszolgáló válasz (szemét).
status.InvalidServerOptions=A kiszolgáló nem nyújt felvilágosítást a támogatott ActiveSync verziókról. EAS akadályozva van ez a felhasználó vagy az ügyfél (Thunderbird-összehangolás) számára? Megpróbálhatja kézzel beállítani az ActiveSync verziót.
status.security=Biztonsági kapcsolat létrehozása nem sikerült. Ön aláírta vagy egyébként nem megbízható tanúsítványt importálta a Thunderbirdbe? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Nem tudott csatlakozni a kiszolgálóhoz (##replace.1##).
status.syncing=Összehangol
status.skipped=Még nem támogatott, kihagyva
status.aborted=Nem összehangolva
status.pending=Várakozás összehangolásra
status.modified=Helyi módosítások
status.resync-loop=Hiba történt, amelyből nem sikerült visszaszerezni a fiók újbóli létrehozásával. Kérjük, tiltsd le a fiókot, és próbálkozz újra. (Hiba: újra összehangolja hurok)
status.policy.2=Az ügyfél számára nincs irányelv (Policy). Vegye fel a kapcsolatot a kiszolgáló rendszergazdájával, vagy tiltsa le a fiókhoz való hozzáférést.
status.policy.3=Ismeretlen irányelvtípus (PolicyType) érték. Vegye fel a kapcsolatot a kiszolgáló rendszergazdájával, vagy tiltsa le a fiókhoz való hozzáférést.
status.policy.4=A kiszolgáló házirend-adatai sérültek (esetleg meghamisították). Vegye fel a kapcsolatot a kiszolgáló rendszergazdájával, vagy tiltsa le a fiókhoz való hozzáférést.
status.policy.5=Az ügyfél elismeri a rossz irányelv kulcsot (PolicyKey). Vegye fel a kapcsolatot a kiszolgáló rendszergazdájával, vagy tiltsa le a fiókhoz való hozzáférést.
status.provision=A hozzáférést nem sikerült, állapota <##replace.1##>
status.global.101=A kérelem WBXML-t tartalmaz, de XML-ben nem sikerült dekódolni (101-es HTTP-hiba).
status.global.102=A kérelem WBXML-t tartalmaz, de XML-ben nem sikerült dekódolni (102-es HTTP-hiba).
status.global.103=A kérésben megadott XML nem követi a egyezménykövetelményeket (103-ás HTTP-hiba).
status.global.110=The server reported an internal error and we should not retry immediately. Automatic periodic sync has been disabled for 30 minutes (EAS Error 110).
status.global.clientdenied=Az EAS kiszolgáló jelentése <##replace.2##> (állapota ##replace.1##), és nem engedélyezi a Thunderbird-összehangolás számára a fiók elérését.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=Az EAS-kiszolgáló nem fogadta el a ##replace.1## elemeket.
status.ServerRejectedRequest=Az EAS szerver megtagadta az utolsó kérésünket.
status.BadItemSkipped=Bad Item Skipped: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=A kiszolgáló nem támogatja az ActiveSync 2.5 vagy 14.0 verziót (csak ##replace.1##). A Thunderbird-összehangolás nem fog működni az ActiveSync kiszolgálóval.
status.notsupportedeasversion=A kiszolgáló nem támogatja a kiválasztott ActiveSync ##replace.1## verziót (csak ##replace.2## verzió).
status.Sync.3=?? Invalid synchronization key (status 3), resyncing ??
status.Sync.4=?? Malformed request (status 4) ??
status.Sync.5=?? Temporary server issues or invalid item (status 5) ??
status.Sync.6=?? Invalid item (status 6) ??
status.Sync.8=?? Object not found (status 8) ??
status.Sync.12=?? Folder hierarchy changed (status 12), resyncing ??
status.FolderDelete.3=Nem lehet törölni egy rendszermappát.
status.FolderDelete.6=A parancs nem sikerült, hiba történt a kiszolgálón.
status.FolderDelete.4=?? Folder does not exist (status 4), resyncing ??
status.FolderDelete.9=?? Invalid synchronization key (status 9), resyncing ??
status.FolderSync.9=?? Invalid synchronization key (status 9), resyncing ??
status.forbiddenTasksItemInCalendarFolder=?? Forbidden task item in a calendar folder (please resort) ??
status.forbiddenCalendarItemInTasksFolder=?? Forbidden calendar item in a task folder (please resort) ??
config.auto=Az ActiveSync kiszolgáló beállítások (önműködő észlelés)
config.custom=Az ActiveSync kiszolgáló beállítások
acl.readwrite=Read from and write to server
acl.readonly=Read-only server access (revert local changes)
autocomplete.serverdirectory = global server directory
EAS-4-TbSync-1.12/_locales/hu/messages.json 0000664 0000000 0000000 00000000444 13623463701 0020314 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Az Exchange ActiveSync szolgáltató"
},
"extensionDescription": {
"message": "Az Exchange ActiveSync-fiókok összehangolás támogatásának hozzáadása a Thunderbird-összehangoláshoz (TbSync) (névjegyzék, feladatok és naptárak)."
}
} EAS-4-TbSync-1.12/_locales/it/ 0000775 0000000 0000000 00000000000 13623463701 0015610 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/it/eas.dtd 0000664 0000000 0000000 00000010733 13623463701 0017061 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/it/eas.properties 0000664 0000000 0000000 00000023503 13623463701 0020501 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Aggiungi account
newaccount.add_auto=Rileva automaticamente impostazioni e aggiungi account
autodiscover.Querying=Rilevamento impostazioni in corso...
autodiscover.Ok=Rilevamento automatico impostazioni completato con successo, è ora possibile verificare le impostazioni facoltative e stabilire la connessione per la sincronizzazione.
autodiscover.NeedEmail=Il rilevamento automatico impostazioni richiede che venga specificato un indirizzo di posta elettronica valido come nome utente.
autodiscover.Failed=Rilevamento automatico non riuscito per l'utente <##user##>. Le credenziali fornite sono errate o il proprio provider ActiveSync ha un problema temporaneo o non supporta il rilevamento automatico impostazioni.
deletefolder.notallowed=Annullare la sottoscrizione alla cartella "##replace.1##" prima di provare a eliminarla dal Cestino.
deletefolder.confirm=ELIMINARE PERMANENTEMENTE la cartella "##replace.1##" dal Cestino?
deletefolder.menuentry=Elimina permanentemente la cartella "##replace.1##" dal Cestino
recyclebin = Cestino
syncstate.prepare.request.setdeviceinfo=Invio informazioni dispositivo in corso
syncstate.send.request.setdeviceinfo=Invio informazioni dispositivo in corso
syncstate.eval.response.setdeviceinfo=Invio informazioni dispositivo in corso
syncstate.syncing=Inizializzazione sincronizzazione in corso
syncstate.preparing=Preparazione prossimo elemento per la sincronizzazione in corso
syncstate.done=Preparazione prossimo elemento per la sincronizzazione in corso
syncstate.accountdone=Sincronizzazione account completata
syncstate.prepare.request.autodiscover=Richiesta impostazioni server aggiornate in corso
syncstate.send.request.autodiscover=In attesa delle impostazioni server aggiornate
syncstate.eval.response.autodiscover=Elaborazione impostazioni server aggiornate in corso
syncstate.prepare.request.options=Richiesta opzioni server in corso
syncstate.send.request.options=In attesa delle opzioni server
syncstate.eval.response.options=Elaborazione opzioni server in corso
syncstate.prepare.request.folders=Invio aggiornamenti elenco cartelle in corso
syncstate.send.request.folders=In attesa degli aggiornamenti elenco cartelle
syncstate.eval.response.folders=Elaborazione aggiornamenti elenco cartelle in corso
syncstate.prepare.request.synckey=Richiesta chiave di sincronizzazione in corso
syncstate.send.request.synckey=In attesa della chiave di sincronizzazione
syncstate.eval.response.synckey=Elaborazione chiave di sincronizzazione in corso
syncstate.prepare.request.deletefolder=Preparazione eliminazione cartella in corso
syncstate.send.request.deletefolder=In attesa dell'eliminazione della cartella
syncstate.eval.response.deletefolder=Cartella eliminata
syncstate.prepare.request.provision=Richiesta provisioning in corso
syncstate.send.request.provision=In attesa del provisioning
syncstate.eval.response.provision=Elaborazione provisioning in corso
syncstate.prepare.request.estimate=Richiesta stima modifiche in corso
syncstate.send.request.estimate=In attesa della stima modifiche
syncstate.eval.response.estimate=Elaborazione stima modifiche in corso
syncstate.prepare.request.remotechanges=Richiesta modifiche remote in corso
syncstate.send.request.remotechanges=In attesa delle modifiche remote
syncstate.eval.response.remotechanges=Elaborazione modifiche remote in corso
syncstate.prepare.request.localchanges=Invio modifiche locali in corso
syncstate.send.request.localchanges=In attesa del riconoscimento delle modifiche locali
syncstate.eval.response.localchanges=Elaborazione riconoscimento modifiche locali in corso
syncstate.prepare.request.revertlocalchanges=Raccolta modifiche locali in corso
syncstate.send.request.revertlocalchanges=In attesa delle versioni più recenti
syncstate.eval.response.revertlocalchanges=Annullamento modifiche locali in corso
syncstate.prepare.request.localdeletes=Invio eliminazioni locali in corso
syncstate.send.request.localdeletes=In attesa del riconoscimento delle eliminazioni locali
syncstate.eval.response.localdeletes=Elaborazione riconoscimento eliminazioni locali in corso
status.notsyncronized=L'account deve essere sincronizzato, almeno un elemento non è sincronizzato.
status.disabled=L'account non è abilitato, la sincronizzazione è disabilitata.
status.notargets=Interruzione sincronizzazione in corso: non è possibile creare le destinazioni sincronizzazione.
status.nouserhost=Nome utente e/o server mancanti. Fornire tali valori.
status.timeout=Timeout durante la comunicazione.
status.networkerror=Impossibile connettersi al server.
status.404=Utente non trovato (errore HTTP 404).
status.403=Il server ha rifiutato la connessione (vietato).
status.401=Impossibile autenticarsi, controllare nome utente e password.
status.449=Il server richiede il provisioning.
status.500=Errore server sconosciuto (errore HTTP 500).
status.503=Servizio non disponibile.
status.httperror=Errore di comunicazione (stato HTTP ##replace.1##).
status.empty-response=Il server invia una risposta vuota inaspettata.
status.wbxml-parse-error=Il server invia una risposta illeggibile.
status.response-contains-no-data=La risposta dal server non contiene dati.
status.wbxmlerror=Sincronizzazione non riuscita. Il server ha risposto con lo stato <##replace.1##>.
status.wbxmlmissingfield=Violazione protocollo ActiveSync: il campo obbligatorio <##replace.1##> manca dalla risposta del server.
status.OK=OK
status.nolightning=Componente aggiuntivo Lightning non installato, i calendari non sono supportati.
status.malformed-xml=Impossibile analizzare XML. Controllare il registro eventi per i dettagli.
status.invalid=Risposta del server non valida (inutile).
status.InvalidServerOptions=Il server non fornisce informazioni sulle versioni di ActiveSync supportate. EAS è bloccato per questo utente o questo client (TbSync)? È possibile provare a impostare manualmente la versione di ActiveSync.
status.security=Impossibile stabilire una connessione sicura. Si sta utilizzando un certificato autofirmato o non affidabile senza averlo importato in Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Impossibile collegarsi al server (##replace.1##).
status.syncing=Sincronizzazione in corso
status.skipped=Non ancora supportato, omesso
status.aborted=Non sincronizzato
status.pending=In attesa di sincronizzazione
status.modified=Modifiche locali presenti
status.resync-loop=Si è verificato un errore che non è stato possibile correggere eseguendo una nuova sincronizzazione dell'account. Disabilitare l'account e riprovare. (Errore: ciclo di risincronizzazione)
status.policy.2=Non esiste alcuna policy per questo client. Contattare l'amministratore del proprio server o disabilitare il provisioning per questo account.
status.policy.3=Valore PolicyType sconosciuto. Contattare l'amministratore del proprio server o disabilitare il provisioning per questo account.
status.policy.4=I dati di policy sul server sono corrotti (forse alterati). Contattare l'amministratore del proprio server o disabilitare il provisioning per questo account.
status.policy.5=Il client sta riconoscendo una policy con nome errato. Contattare l'amministratore del proprio server o disabilitare il provisioning per questo account.
status.provision=Provisioning non riuscito: stato <##replace.1##>
status.global.101=La richiesta contiene WBXML ma non è possibile decodificarlo in XML (errore 101).
status.global.102=La richiesta contiene WBXML ma non è possibile decodificarlo in XML (errore 102).
status.global.103=L'XML fornito nella richiesta non rispetta i requisiti del protocollo (errore 103).
status.global.110=Il server ha segnalato un errore interno e non dovremmo riprovare immediatamente. La sincronizzazione periodica automatica è stata disattivata per 30 minuti (errore EAS 110).
status.global.clientdenied=Il server EAS segnala <##replace.2##> (stato ##replace.1##) e non consente a TbSync l'accesso al proprio account.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=Il server EAS non ha accettato ##replace.1## elementi.
status.ServerRejectedRequest=Il server EAS ha rifiutato l'ultima richiesta.
status.BadItemSkipped=Oggetto errato saltato: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Il server non supporta ActiveSync v2.5 o v14.0 (solo ##replace.1##). TbSync non funzionerà con questo server ActiveSync.
status.notsupportedeasversion=Il server non supporta la versione di ActiveSync selezionata ##replace.1## (solo ##replace.2##).
status.Sync.3=?? Invalid synchronization key (status 3), resyncing ??
status.Sync.4=?? Malformed request (status 4) ??
status.Sync.5=?? Temporary server issues or invalid item (status 5) ??
status.Sync.6=?? Invalid item (status 6) ??
status.Sync.8=?? Object not found (status 8) ??
status.Sync.12=?? Folder hierarchy changed (status 12), resyncing ??
status.FolderDelete.3=Impossibile eliminare una cartella di sistema.
status.FolderDelete.6=Impossibile completare il comando, si è verificato un errore sul server.
status.FolderDelete.4=?? Folder does not exist (status 4), resyncing ??
status.FolderDelete.9=?? Invalid synchronization key (status 9), resyncing ??
status.FolderSync.9=?? Invalid synchronization key (status 9), resyncing ??
status.forbiddenTasksItemInCalendarFolder=?? Forbidden task item in a calendar folder (please resort) ??
status.forbiddenCalendarItemInTasksFolder=?? Forbidden calendar item in a task folder (please resort) ??
config.auto=Configurazione server ActiveSync (rilevamento automatico)
config.custom=Configurazione server ActiveSync
acl.readwrite=Leggi e scrivi sul server
acl.readonly=Accesso al server in sola lettura (ripristina le modifiche locali)
autocomplete.serverdirectory = directory globale del server
EAS-4-TbSync-1.12/_locales/it/messages.json 0000664 0000000 0000000 00000000364 13623463701 0020315 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provider Exchange ActiveSync"
},
"extensionDescription": {
"message": "Aggiunge il supporto per la sincronizzazione di account Exchange ActiveSync (contatti, attività e calendari) a TbSync."
}
} EAS-4-TbSync-1.12/_locales/pl/ 0000775 0000000 0000000 00000000000 13623463701 0015607 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/pl/eas.dtd 0000664 0000000 0000000 00000010575 13623463701 0017064 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/pl/eas.properties 0000664 0000000 0000000 00000022536 13623463701 0020505 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Dodaj konto
newaccount.add_auto=Wykryj automatycznie ustawienia i dodaj konto
autodiscover.Querying=Wyszukiwanie ustawień…
autodiscover.Ok=Automatyczne wykrywanie zakończyło się pomyślnie, możesz teraz sprawdzić ustawienia opcjonalne i ustanowić połączenie synchronizacji.
autodiscover.NeedEmail=Automatyczne wykrywanie potrzebuje prawidłowego adresu e-mail jako nazwy użytkownika.
autodiscover.Failed=Auto-wykrywanie dla użytkownika <##user##> nie powiodło się. Podane dane uwierzytelniające były nieprawidłowe lub dostawca ActiveSync ma tymczasowy problem lub nie obsługuje funkcji automatycznego wykrywania.
deletefolder.notallowed=Anuluj subskrypcję folderu “##replace.1##” przed próbą usunięcia go z kosza.
deletefolder.confirm=Czy naprawdę chcesz TRWALE USUNĄĆ folder “##replace.1##” z kosza?
deletefolder.menuentry=Trwale usuń folder “##replace.1##” z kosza
recyclebin = Kosz
syncstate.prepare.request.setdeviceinfo=Wysyłanie informacji o urządzeniu
syncstate.send.request.setdeviceinfo=Wysyłanie informacji o urządzeniu
syncstate.eval.response.setdeviceinfo=Wysyłanie informacji o urządzeniu
syncstate.syncing=Zainicjuj synchronizację
syncstate.preparing=Przygotowuję następny element do synchronizacji
syncstate.done=Przygotowuję następny element do synchronizacji
syncstate.accountdone=Konto gotowe
syncstate.prepare.request.autodiscover=Żądanie zaktualizowanych ustawień serwera
syncstate.send.request.autodiscover=Oczekiwanie na zaktualizowane ustawienia serwera
syncstate.eval.response.autodiscover=Przetwarzanie zaktualizowanych ustawień serwera
syncstate.prepare.request.options=Żądanie opcji serwera
syncstate.send.request.options=Oczekiwanie na opcje serwera
syncstate.eval.response.options=Przetwarzanie opcji serwera
syncstate.prepare.request.folders=Wysyłanie aktualizacji listy folderów
syncstate.send.request.folders=Oczekiwanie na aktualizację listy folderów
syncstate.eval.response.folders=Przetwarzanie aktualizacji listy folderów
syncstate.prepare.request.synckey=Żądanie SyncKey
syncstate.send.request.synckey=Oczekiwanie na SyncKey
syncstate.eval.response.synckey=Przetwarzanie SyncKey
syncstate.prepare.request.deletefolder=Przygotowanie do usunięcia folderu
syncstate.send.request.deletefolder=Oczekiwanie na usunięcie folderu
syncstate.eval.response.deletefolder=Folder usunięty
syncstate.prepare.request.provision=Żądanie provision
syncstate.send.request.provision=Oczekiwanie na provision
syncstate.eval.response.provision=Przetwarzanie provision
syncstate.prepare.request.estimate=Żądanie oszacowanych zmian
syncstate.send.request.estimate=Oczekiwanie na oszacowanie zmian
syncstate.eval.response.estimate=Przetwarzanie oszacowanych zmian
syncstate.prepare.request.remotechanges=Żądanie zdalnych zmian
syncstate.send.request.remotechanges=Oczekiwanie na zdalne zmiany
syncstate.eval.response.remotechanges=Przetwarzanie zdalnych zmian
syncstate.prepare.request.localchanges=Wysyłanie zmian lokalnych
syncstate.send.request.localchanges=Oczekiwanie na potwierdzenie lokalnych zmian
syncstate.eval.response.localchanges=Przetwarzanie potwierdzenia lokalnych zmian
syncstate.prepare.request.revertlocalchanges=Zbieranie zmian lokalnych
syncstate.send.request.revertlocalchanges=Czekam na najnowsze wersje
syncstate.eval.response.revertlocalchanges=Wycofywanie zmian lokalnych
syncstate.prepare.request.localdeletes=Wysyłanie usuniętych lokalnie
syncstate.send.request.localdeletes=Oczekiwanie na potwierdzenie usunięcia lokalnego
syncstate.eval.response.localdeletes=Przetwarzanie potwierdzenia usunięcia lokalnego
status.notsyncronized=Konto musi zostać zsynchronizowane, co najmniej jeden element nie jest zsynchronizowany.
status.disabled=Wyłączony
status.notargets=Przerywam synchronizację, ponieważ nie można utworzyć celów synchronizacji.
status.nouserhost=Brak nazwy użytkownika i/lub serwera. Podaj te wartości.
status.timeout=Przekroczony limit czasu połączenia.
status.networkerror=Nie można połączyć z serwerem.
status.404=Użytkownik nie znaleziony (błąd HTTP 404).
status.403=Serwer odrzucił połączenie (zabronione) (błąd HTTP 403).
status.401=Nie można uwierzytelnić, sprawdź nazwę użytkownika i hasło (HTTP Error 401).
status.449=Server requests provisioning (HTTP Error 449).
status.500=Nieznany błąd serwera (błąd HTTP 500).
status.503=Usługa niedostępna (błąd HTTP 503).
status.httperror=Błąd komunikacji (status HTTP ##replace.1##).
status.empty-response=Serwer wysyła nieoczekiwaną pustą odpowiedź.
status.wbxml-parse-error=Serwer wysyła nieczytelną odpowiedź.
status.response-contains-no-data=Odpowiedź z serwera nie zawiera danych.
status.wbxmlerror=Synchronizacja nie powiodła się. Serwer odpowiedział statusem <##replace.1##>.
status.wbxmlmissingfield=Naruszenie protokołu ActiveSync: W odpowiedzi serwera brakuje obowiązkowego pola <##replace.1##>.
status.OK=OK
status.nolightning=Dodatek Lightning nie został zainstalowany, kalendarze nie są obsługiwane.
status.malformed-xml=Nie można przeanalizować XML. Sprawdź dziennik zdarzeń, aby uzyskać szczegółowe informacje.
status.invalid=Niepoprawna odpowiedź serwera (śmieci).
status.InvalidServerOptions=Serwer nie dostarcza informacji o obsługiwanych wersjach ActiveSync. Czy EAS jest zablokowany dla tego użytkownika lub tego klienta (TbSync)? Możesz spróbować ręcznie ustawić wersję ActiveSync.
status.security=Nie można nawiązać bezpiecznego połączenia. Czy używasz self-signed lub w inny sposób niezaufanego certyfikatu bez importowania go do Thunderbirda? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Nie można połączyć z serwerem (##replace.1##).
status.syncing=Synchronizuję
status.skipped=Jeszcze nie obsługiwane, pominięto
status.aborted=Nie zsynchronizowane
status.pending=Oczekiwanie na synchronizację
status.modified=Zmiany lokalne
status.resync-loop=Wystąpił błąd, którego nie udało się naprawić poprzez ponowne zsynchronizowanie konta. Wyłącz konto i spróbuj ponownie. (Błąd: pętla resynchronizacji)
status.policy.2=Brak zasad dla tego klienta. Skontaktuj się z administratorem serwera lub wyłącz provisioning dla tego konta.
status.policy.3=Nieznana wartość PolicyType. Skontaktuj się z administratorem serwera lub wyłącz provisioning dla tego konta.
status.policy.4=Dane zasad na serwerze są uszkodzone (prawdopodobnie zmienione/sfałszowane). Skontaktuj się z administratorem serwera lub wyłącz provisioning dla tego konta.
status.policy.5=Klient potwierdza zły klucz zasad. Skontaktuj się z administratorem serwera lub wyłącz provisioning dla tego konta.
status.provision=Niepowodzenie provisioning ze statusem <##replace.1##>
status.global.101=Żądanie zawiera WBXML, ale nie można go zdekodować do formatu XML (błąd EAS 101).
status.global.102=Żądanie zawiera WBXML, ale nie można go zdekodować do formatu XML (błąd EAS 102).
status.global.103=XML podany w żądaniu nie spełnia wymagań protokołu (błąd EAS 103).
status.global.110=Serwer zgłosił błąd wewnętrzny i nie powinniśmy natychmiast ponawiać próby. Automatyczna synchronizacja okresowa została wyłączona na 30 minut (błąd EAS 110).
status.global.clientdenied=Serwer EAS zgłasza <##replace.2##> (status ##replace.1##) i nie pozwala TbSync na dostęp do Twojego konta.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=Serwer EAS nie zaakceptował elementów #replace.1##.
status.ServerRejectedRequest=Serwer EAS odrzucił ostatnie żądanie.
status.BadItemSkipped=Błędny element pominięto: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Serwer nie obsługuje ActiveSync v2.5 lub v14.0 (tylko ##replace.1##). TbSync nie będzie działać z tym serwerem ActiveSync.
status.notsupportedeasversion=Serwer nie wspiera wybranego ActiveSync v##replace.1## (tylko ##replace.2##).
status.Sync.3=Nieprawidłowy klucz synchronizacji (status 3), ponowna synchronizacja
status.Sync.4=Zniekształcone żądanie (status 4)
status.Sync.5=Tymczasowe problemy z serwerem lub nieprawidłowy element (status 5)
status.Sync.6=Nieprawidłowy element (status 6)
status.Sync.8=Nie znaleziono obiektu (status 8)
status.Sync.12=Hierarchia folderów zmieniona (status 12), ponowna synchronizacja
status.FolderDelete.3=Nie można usunąć folderu systemowego (status 3)
status.FolderDelete.6=Nie można wykonać polecenia, wystąpił błąd na serwerze (status 6)
status.FolderDelete.4=Folder nie istnieje (status 4), ponowna synchronizacja
status.FolderDelete.9=Nieprawidłowy klucz synchronizacji (status 9), ponowna synchronizacja
status.FolderSync.9=Nieprawidłowy klucz synchronizacji (status 9), ponowna synchronizacja
status.forbiddenTasksItemInCalendarFolder=Niedozwolony element zadania w folderze kalendarza (please resort)
status.forbiddenCalendarItemInTasksFolder=Niedozwolony element kalendarza w folderze zadań (please resort)
config.auto=Konfiguracja serwera ActiveSync (Auto-wykrywanie)
config.custom=Konfiguracja serwera ActiveSync
acl.readwrite=Odczytuj i zapisuj na serwer
acl.readonly=Dostęp do serwera tylko do odczytu (cofnij zmiany lokalne)
autocomplete.serverdirectory = globalny katalog serwera
EAS-4-TbSync-1.12/_locales/pl/messages.json 0000664 0000000 0000000 00000000344 13623463701 0020312 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Dostawca dla Exchange ActiveSync"
},
"extensionDescription": {
"message": "Dodaj obsługę synchronizacji kont Exchange ActiveSync do TbSync (kontakty, zadania i kalendarze)."
}
} EAS-4-TbSync-1.12/_locales/pt_BR/ 0000775 0000000 0000000 00000000000 13623463701 0016202 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/pt_BR/eas.dtd 0000664 0000000 0000000 00000011046 13623463701 0017451 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/pt_BR/eas.properties 0000664 0000000 0000000 00000023060 13623463701 0021071 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Adicionar conta
newaccount.add_auto=Configurações de descoberta automática e adicionar conta
autodiscover.Querying=Procurando por configurações...
autodiscover.Ok=Descoberta automática concluída com êxito, agora você pode verificar as configurações opcionais e estabelecer a conexão de sincronização.
autodiscover.NeedEmail=A descoberta automática precisa de um endereço de email válido
autodiscover.Failed=Descoberta automática para usuário <##user##> falhou. As credenciais fornecidas estavam erradas ou seu provedor do ActiveSync tem um problema temporário ou não oferece suporte à descoberta automática.
deletefolder.notallowed=Por favor, cancele a subscrição da pasta "##replace.1##" antes de tentar limpá-lo do lixo.
deletefolder.confirm=Você realmente deseja limpar permanentemente a pasta "##replace.1##" da lixeira?
deletefolder.menuentry=Remover permanentemente a pasta "##replace.1##" da lixeira
recyclebin = Lixeira
syncstate.prepare.request.setdeviceinfo=Enviando informações do dispositivo
syncstate.send.request.setdeviceinfo=Enviando informações do dispositivo
syncstate.eval.response.setdeviceinfo=Enviando informações do dispositivo
syncstate.syncing=Inicializar sincronização
syncstate.preparing=Preparando o próximo item para sincronização
syncstate.done=Preparando o próximo item para sincronização
syncstate.accountdone=Sincronização finalizada
syncstate.prepare.request.autodiscover=Solicitando configurações do servidor atualizadas
syncstate.send.request.autodiscover=Aguardando as configurações do servidor atualizadas
syncstate.eval.response.autodiscover=Aguardando as configurações do servidor atualizadas
syncstate.prepare.request.options=Solicitando opções do servidor
syncstate.send.request.options=Aguardando por opções do servidor
syncstate.eval.response.options=Processando opções do servidor
syncstate.prepare.request.folders=Enviando lista de pastas atualizadas
syncstate.send.request.folders=Aguardando a atualização da lista de pastas
syncstate.eval.response.folders=Processando a atualização da lista de pastas
syncstate.prepare.request.synckey=Solicitando SyncKey
syncstate.send.request.synckey=Esperando SyncKey
syncstate.eval.response.synckey=Processando SyncKey
syncstate.prepare.request.deletefolder=Preparando-se para excluir pasta
syncstate.send.request.deletefolder=Aguardando a pasta ser excluída
syncstate.eval.response.deletefolder=Pasta excluída
syncstate.prepare.request.provision=Solicitando provisão
syncstate.send.request.provision=Agurdandando provisão
syncstate.eval.response.provision=Processando provisão
syncstate.prepare.request.estimate=Solicitando estimativa de alterações
syncstate.send.request.estimate=Aguardando a estimativa de alterações
syncstate.eval.response.estimate=Processando a estimativa de alterações
syncstate.prepare.request.remotechanges=Solicitando alterações remotas
syncstate.send.request.remotechanges=Aguardando alterações remotas
syncstate.eval.response.remotechanges=Processando alterações remotas
syncstate.prepare.request.localchanges=Enviando alterações locais
syncstate.send.request.localchanges=Aguardando confirmação de alterações locais
syncstate.eval.response.localchanges=Processando reconhecimento de alterações locais
syncstate.prepare.request.revertlocalchanges=Coletando alterações locais
syncstate.send.request.revertlocalchanges=Aguardando as versões mais recentes
syncstate.eval.response.revertlocalchanges=Revertendo alterações locais
syncstate.prepare.request.localdeletes=Enviando exclusões locais
syncstate.send.request.localdeletes=Aguardando o reconhecimento de exclusões locais
syncstate.eval.response.localdeletes=Processando reconhecimento de exclusões locais
status.notsyncronized=A conta precisa ser sincronizada, pelo menos, um item está fora de sincronia.
status.disabled=Desativado
status.notargets=Anulando a sincronização, porque os destinos de sincronização não puderam ser criados.
status.nouserhost=Nome de usuário e/ou servidor ausente. Por favor, forneça esses valores.
status.timeout=Tempo limite de comunicação.
status.networkerror=Não foi possível conectar ao servidor.
status.404=Usuário não encontrado (HTTP Erro 404).
status.403=Conexão rejeitada pelo servidor (proibido) (HTTP Erro 403).
status.401=Não foi possível autenticar, verifique o nome de usuário e senha (HTTP Erro 401).
status.449=O servidor solicita o provisionamento (HTTP Erro 449).
status.500=Erro de servidor desconhecido (HTTP Erro 500).
status.503=Serviço indisponível (HTTP Erro 503).
status.httperror=Erro de comunicação (HTTP status ##replace.1##).
status.empty-response=O servidor enviou uma resposta vazia inesperada.
status.wbxml-parse-error=O servidor enviou uma resposta ilegível.
status.response-contains-no-data=A resposta do servidor não contém dados.
status.wbxmlerror=A sincronização falhou. O servidor respondeu com status <##replace.1##>.
status.wbxmlmissingfield=Violação do protocolo ActiveSync: o campo obrigatório <##replace.1##> está faltando na resposta do servidor.
status.OK=OK
status.nolightning=O complemento Lightning não está instalado, os calendários não são suportados.
status.malformed-xml=Não foi possível analisar XML. Verifique o log de eventos para obter detalhes.
status.invalid=Resposta inválida do servidor (lixo).
status.InvalidServerOptions=O servidor não fornece informações sobre as versões do ActiveSync com suporte. O EAS está bloqueado para este usuário ou esse cliente (TbSync)? Você pode tentar definir a versão do ActiveSync manualmente.
status.security=Não foi possível estabelecer uma conexão segura. Você está usando um certificado autoassinado ou não confiável sem importá-lo para o Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Não foi possível conectar ao servidor (##replace.1##).
status.syncing=Sincronizando
status.skipped=Ainda não suportado, ignorado
status.aborted=Não sincronizado
status.pending=Esperando para ser sincronizado
status.modified=Modificações locais
status.resync-loop=Ocorreu um erro do qual não conseguimos recuperar ao reativar a conta. Por favor, desative a conta e tente novamente. (Erro: loop de ressincronização)
status.policy.2=Não há política para este cliente. Entre em contato com o administrador do servidor ou desabilite o provisionamento para essa conta.
status.policy.3=Valor de PolicyType desconhecido. Entre em contato com o administrador do servidor ou desabilite o provisionamento para essa conta.
status.policy.4=Os dados da política no servidor estão corrompidos (possivelmente adulterados). Entre em contato com o administrador do servidor ou desabilite o provisionamento para essa conta.
status.policy.5=O cliente está reconhecendo a chave de política incorreta. Entre em contato com o administrador do servidor ou desabilite o provisionamento para essa conta.
status.provision=O provisionamento falhou com o status <##replace.1##>
status.global.101=A solicitação contém WBXML, mas não pôde ser decodificada em XML (Erro EAS 101).
status.global.102=A solicitação contém WBXML, mas não pôde ser decodificada em XML (Erro EAS 102).
status.global.103=O XML fornecido na solicitação não segue os requisitos do protocolo (Erro EAS 103).
status.global.110=O servidor relatou um erro interno e não devemos tentar novamente imediatamente. A sincronização periódica automática foi desativada por 30 minutos (Erro 110 do EAS).
status.global.clientdenied=O servidor EAS reporta <##replace.2##> (status ##replace.1##) e não permite que o TbSync acesse sua conta.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=O servidor EAS não aceitou ##replace.1## elementos.
status.ServerRejectedRequest=O servidor EAS rejeitou a última solicitação.
status.BadItemSkipped=Item inválido ignorado: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=O servidor não suporta o ActiveSync v2.5 ou v14.0 (somente ##replace.1##). TbSync não funcionará com este servidor ActiveSync.
status.notsupportedeasversion=O servidor não suporta o ActiveSync selecionado v##replace.1## (somente ##replace.2##).
status.Sync.3=?? Invalid synchronization key (status 3), resyncing ??
status.Sync.4=?? Malformed request (status 4) ??
status.Sync.5=?? Temporary server issues or invalid item (status 5) ??
status.Sync.6=?? Invalid item (status 6) ??
status.Sync.8=?? Object not found (status 8) ??
status.Sync.12=?? Folder hierarchy changed (status 12), resyncing ??
status.FolderDelete.3=Não é possível excluir uma pasta do sistema.
status.FolderDelete.6=O comando não pôde ser concluído, ocorreu um erro no servidor.
status.FolderDelete.4=?? Folder does not exist (status 4), resyncing ??
status.FolderDelete.9=?? Invalid synchronization key (status 9), resyncing ??
status.FolderSync.9=?? Invalid synchronization key (status 9), resyncing ??
status.forbiddenTasksItemInCalendarFolder=?? Forbidden task item in a calendar folder (please resort) ??
status.forbiddenCalendarItemInTasksFolder=?? Forbidden calendar item in a task folder (please resort) ??
config.auto=Configuração do servidor ActiveSync (Descoberta Automática)
config.custom=Configuração do servidor ActiveSync
acl.readwrite=Ler e escrever no servidor
acl.readonly=Acesso ao servidor somente leitura (reverter alterações locais)
autocomplete.serverdirectory = diretório global do servidor
EAS-4-TbSync-1.12/_locales/pt_BR/messages.json 0000664 0000000 0000000 00000000356 13623463701 0020710 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provedor Exchange ActiveSync"
},
"extensionDescription": {
"message": "Adiciona suporte para sincronizar contas do Exchange ActiveSync (contatos, tarefas e calendários) para o TbSync."
}
} EAS-4-TbSync-1.12/_locales/ru/ 0000775 0000000 0000000 00000000000 13623463701 0015622 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/_locales/ru/eas.dtd 0000664 0000000 0000000 00000014136 13623463701 0017074 0 ustar 00root root 0000000 0000000
EAS-4-TbSync-1.12/_locales/ru/eas.properties 0000664 0000000 0000000 00000033306 13623463701 0020515 0 ustar 00root root 0000000 0000000 newaccount.add_custom=Добавить аккаунт
newaccount.add_auto=Настройки автообнаружения и добавление аккаунта
autodiscover.Querying=Поиск настроек…
autodiscover.Ok=Автообнаружение успешно завершено, теперь вы можете проверить дополнительные настройки и установить соединение синхронизации.
autodiscover.NeedEmail=Автообнаружению нужен действительный адрес электронной почты в качестве имени пользователя.
autodiscover.Failed=Автообнаружение для пользователя <##user##> не удалось. Либо предоставленные учетные данные были неправильными, либо у вашего провайдера ActiveSync была временная проблема, или не поддерживается автообнаружение.
deletefolder.notallowed=Откажитесь от подписки папки “##replace.1##” прежде чем пытаться очистить ее от мусора.
deletefolder.confirm=Вы действительно хотите ПЕРМАНЕНТНО ВЫЧИСТИТЬ папку “##replace.1##” из корзины?
deletefolder.menuentry=Перманентно вычистить папку “##replace.1##” из корзины
recyclebin = Корзина
syncstate.prepare.request.setdeviceinfo=Отправка информации об устройстве
syncstate.send.request.setdeviceinfo=Ожидание информации об устройстве
syncstate.eval.response.setdeviceinfo=Обработка информации об устройстве
syncstate.syncing=Инициализация синхронизации
syncstate.preparing=Подготовка следующего элемента для синхронизации
syncstate.done=Подготовка следующего элемента для синхронизации
syncstate.accountdone=Завершенный аккаунт
syncstate.prepare.request.autodiscover=Запрос обновленных настроек сервера
syncstate.send.request.autodiscover=Ожидание обновленных настроек сервера
syncstate.eval.response.autodiscover=Обработка обновленных настроек сервера
syncstate.prepare.request.options=Запрос настроек сервера
syncstate.send.request.options=Ожидание настроек сервера
syncstate.eval.response.options=Обработка настроек сервера
syncstate.prepare.request.folders=Отправка обновления листа папок
syncstate.send.request.folders=Ожидание обновления листа папок
syncstate.eval.response.folders=Обработка обновления листа папок
syncstate.prepare.request.synckey=Запрос элементов синхронизации
syncstate.send.request.synckey=Ожидание элементов синхронизации
syncstate.eval.response.synckey=Обработка элементов синхронизации
syncstate.prepare.request.deletefolder=Подготовка к удалению папки
syncstate.send.request.deletefolder=Ожидание удаления папки
syncstate.eval.response.deletefolder=Папка удалена
syncstate.prepare.request.provision=Запрос для резервирования
syncstate.send.request.provision=Ожидание резервирования
syncstate.eval.response.provision=Обработка резервирования
syncstate.prepare.request.estimate=Запрос оценки изменения
syncstate.send.request.estimate=Ожидание оценки изменений
syncstate.eval.response.estimate=Обработка оценки изменений
syncstate.prepare.request.remotechanges=Запрос удаленных изменений
syncstate.send.request.remotechanges=Ожидание удаленных изменений
syncstate.eval.response.remotechanges=Обработка удаленных изменений
syncstate.prepare.request.localchanges=Отправка локальных изменений
syncstate.send.request.localchanges=Ожидание подтверждения локальных изменений
syncstate.eval.response.localchanges=Обработка подтверждения локальных изменений
syncstate.prepare.request.revertlocalchanges=Сбор локальных изменений
syncstate.send.request.revertlocalchanges=Ожидание самых последних версий
syncstate.eval.response.revertlocalchanges=Откат локальных изменений
syncstate.prepare.request.localdeletes=Отправка локальных удалений
syncstate.send.request.localdeletes=Ожидание подтверждения локальных удалений
syncstate.eval.response.localdeletes=Обработка подтверждения локальных удалений
status.notsyncronized=Аккаунт должен быть синхронизирован, по крайней мере один элемент не синхронизирован.
status.disabled=Аккаунт не включен, синхронизация отключена.
status.notargets=Отмена синхронизации, поскольку цели синхронизации не могут быть созданы.
status.nouserhost=Отсутствует имя пользователя и/или сервера. Укажите эти значения.
status.timeout=Тайм-аут связи.
status.networkerror=Не удалось подключиться к серверу.
status.404=Пользователь не найден (HTTP Ошибка 404).
status.403=Сервер отклонил соединение (запрещено) (HTTP Ошибка 403).
status.401=Не удалось аутентифицировать, проверить имя пользователя и пароль. (HTTP Ошибка 401).
status.449=Серверные запросы дублируются (HTTP Ошибка 449).
status.500=Неизвестная ошибка сервера (HTTP Ошибка 500).
status.503=Сервис недоступен (HTTP Ошибка 503).
status.httperror=Ошибка связи (HTTP статус ##replace.1##).
status.empty-response=Сервер отправляет неожиданный пустой ответ.
status.wbxml-parse-error=Сервер отправляет нечитаемый ответ.
status.response-contains-no-data=Ответ от сервера не содержит данных.
status.wbxmlerror=Ошибка синхронизации. Сервер ответил статусом <##replace.1##>.
status.wbxmlmissingfield=ActiveSync протокол нарушен: Обязательное поле <##replace.1##> отсутствует в ответе сервера.
status.OK=Готово
status.nolightning=Lightning Add-On не установлен, календари не поддерживаются.
status.malformed-xml=Не удалось разобрать XML. Проверьте журнал событий для деталей.
status.invalid=Недопустимый ответ сервера (мусор в обмене).
status.InvalidServerOptions=Сервер не предоставляет информацию о поддерживаемых версиях ActiveSync. EAS блокирован для этого пользователя или этого клиента (TbSync)? Вы можете попробовать установить версию ActiveSync вручную.
status.security=Не удалось установить безопасное соединение. Вы используете самоподписанный или ненадежный сертификат без импорта его в Thunderbird? (##replace.1##)
helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
status.network=Не удалось подключиться к серверу (##replace.1##).
status.syncing=Синхронизация
status.skipped=Пока не поддерживается, пропущено
status.aborted=Не синхронизировано
status.pending=Ожидание пока синхронизируется
status.modified=Локальные изменения
status.resync-loop=Произошла ошибка, из-за которой не удалось пересинхронизировать аккаунт. Отключите аккаунт и повторите попытку. (Ошибка: повторная синхронизация)
status.policy.2=Для этого клиента нет политики. Обратитесь к администратору вашего сервера или отключите использование этого аккаунта.
status.policy.3=Неизвестное значение политики для этого клиента. Обратитесь к администратору вашего сервера или отключите использование этого аккаунта.
status.policy.4=Данные политики на сервере повреждены (возможно, подделаны). Обратитесь к администратору вашего сервера или отключите использование этого аккаунта.
status.policy.5=Клиент обнаружил неправильный ключ политики. Обратитесь к администратору вашего сервера или отключите использование этого аккаунта.
status.provision=Резервирование не удалось со статусом <##replace.1##>
status.global.101=Запрос содержит WBXML но он не может быть декодирован в XML (EAS Ошибка 101).
status.global.102=Запрос содержит WBXML но он не может быть декодирован в XML (EAS Ошибка 102).
status.global.103=XML, указанный в запросе, не соответствует требованиям протокола (EAS Ошибка 103).
status.global.110=Сервер сообщил о внутренней ошибке, и мы не должны немедленно повторять попытку. Автоматическая периодическая синхронизация была отключена на 30 минут (Ошибка EAS 110).
status.global.clientdenied=Отчеты сервера EAS <##replace.2##> (статус ##replace.1##) и не позволяют TbSync доступ к вашему аккаунту.
helplink.global.clientdenied=https://github.com/jobisoft/EAS-4-TbSync/wiki/What-if-TbSync-is-blocked-by-my-server%3F
status.ServerRejectedSomeItems=Сервер EAS не принял элементы ##replace.1##.
status.ServerRejectedRequest=Сервер EAS отклонил наш последний запрос.
status.BadItemSkipped=Плохой элемент пропущен: ##replace.1##
helplink.BadItemSkipped=https://github.com/jobisoft/EAS-4-TbSync/wiki/Error:-Bad-item-skipped
status.nosupportedeasversion=Сервер не поддерживает ActiveSync v2.5 или v14.0 (только ##replace.1##). TbSync не будет работать с этим ActiveSync сервером.
status.notsupportedeasversion=Сервер не поддерживает выбранный ActiveSync v##replace.1## (только ##replace.2##).
status.Sync.3=Неверный ключ синхронизации (статус 3), повторная синхронизация
status.Sync.4=Неверный запрос (статус 4)
status.Sync.5=Временные проблемы с сервером или недопустимый элемент (статус 5)
status.Sync.6=Недопустимый элемент (статус 6)
status.Sync.8=Объект не найден (статус 8)
status.Sync.12=Изменена иерархия папок (статус 12), повторная синхронизация
status.FolderDelete.3=Не удается удалить системную папку.
status.FolderDelete.6=Команда не может быть выполнена, на сервере возникла ошибка.
status.FolderDelete.4=Папка не существует (статус 4), повторная синхронизация
status.FolderDelete.9=Неверный ключ синхронизации (статус 9), повторная синхронизация
status.FolderSync.9=Неверный ключ синхронизации (статус 9), повторная синхронизация
status.forbiddenTasksItemInCalendarFolder=Запрещенный элемент задачи в папке календаря (пожалуйста, исправьте)
status.forbiddenCalendarItemInTasksFolder=Запрещенный элемент календаря в папке задач (пожалуйста, исправьте)
config.auto=ActiveSync конфигурация сервера (Автообнаружение)
config.custom=ActiveSync конфигурация сервера
acl.readwrite=Читать и писать на сервер
acl.readonly=Доступ к серверу только для чтения (отменить локальные изменения)
autocomplete.serverdirectory = global server directory
EAS-4-TbSync-1.12/_locales/ru/messages.json 0000664 0000000 0000000 00000000601 13623463701 0020321 0 ustar 00root root 0000000 0000000 {
"extensionName": {
"message": "Provider for Exchange ActiveSync"
},
"extensionDescription": {
"message": "Добавляет в TbSync поддержку синхронизации для учетных записей Exchange через базирующийся на http/https протокол ActiveSync (контакты, задачи и календари)."
}
} EAS-4-TbSync-1.12/beta-release-channel-update.json 0000664 0000000 0000000 00000000503 13623463701 0021523 0 ustar 00root root 0000000 0000000 {
"addons": {
"eas4tbsync@jobisoft.de": {
"updates": [
{ "version": "%VERSION%",
"update_info_url": "https://github.com/jobisoft/EAS-4-TbSync/releases",
"update_link": "%LINK%",
"applications": {
"gecko": { "strict_min_version": "68.0" } } }
]
}
}
} EAS-4-TbSync-1.12/bootstrap.js 0000664 0000000 0000000 00000004705 13623463701 0015774 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// no need to create namespace, we are in a sandbox
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
let thisID = "";
let onInitDoneObserver = {
observe: async function (aSubject, aTopic, aData) {
let valid = false;
try {
var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
valid = TbSync.enabled;
} catch (e) {
// If this fails, TbSync is not loaded yet and we will get the notification later again.
}
//load this provider add-on into TbSync
if (valid) {
await TbSync.providers.loadProvider(thisID, "eas", "chrome://eas4tbsync/content/provider.js");
}
}
}
function install(data, reason) {
}
function uninstall(data, reason) {
}
function startup(data, reason) {
// Possible reasons: APP_STARTUP, ADDON_ENABLE, ADDON_INSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE.
thisID = data.id;
Services.obs.addObserver(onInitDoneObserver, "tbsync.observer.initialized", false);
// The startup of TbSync is delayed until all add-ons have called their startup(),
// so all providers have registered the "tbsync.observer.initialized" observer.
// Once TbSync has finished its startup, all providers will be notified (also if
// TbSync itself is restarted) to load themself.
// If this is not startup, we need load manually.
if (reason != APP_STARTUP) {
onInitDoneObserver.observe();
}
}
function shutdown(data, reason) {
// Possible reasons: APP_SHUTDOWN, ADDON_DISABLE, ADDON_UNINSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE.
// When the application is shutting down we normally don't have to clean up.
if (reason == APP_SHUTDOWN) {
return;
}
Services.obs.removeObserver(onInitDoneObserver, "tbsync.observer.initialized");
//unload this provider add-on from TbSync
try {
var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
TbSync.providers.unloadProvider("eas");
} catch (e) {
//if this fails, TbSync has been unloaded already and has unloaded this addon as well
}
Services.obs.notifyObservers(null, "chrome-flush-caches", null);
}
EAS-4-TbSync-1.12/chrome.manifest 0000664 0000000 0000000 00000000675 13623463701 0016430 0 ustar 00root root 0000000 0000000 content eas4tbsync content/
skin eas4tbsync classic/1.0 skin/
locale eas4tbsync bg _locales/bg/
locale eas4tbsync de _locales/de/
locale eas4tbsync en-US _locales/en-US/
locale eas4tbsync fr _locales/fr/
locale eas4tbsync hu _locales/hu/
locale eas4tbsync it _locales/it/
locale eas4tbsync pl _locales/pl/
locale eas4tbsync pt-BR _locales/pt_BR/
locale eas4tbsync ru _locales/ru/
EAS-4-TbSync-1.12/content/ 0000775 0000000 0000000 00000000000 13623463701 0015065 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/content/OAuth2_1.jsm 0000664 0000000 0000000 00000016506 13623463701 0017132 0 ustar 00root root 0000000 0000000 /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Provides OAuth 2.0 authentication.
* @see RFC 6749
*/
var EXPORTED_SYMBOLS = ["OAuth2_1"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Log4Moz } = ChromeUtils.import("resource:///modules/gloda/log4moz.js");
Cu.importGlobalProperties(["fetch"]);
// Only allow one connecting window per endpoint.
var gConnecting = {};
function OAuth2_1(aBaseURI, aScope, aAppKey, aAppSecret) {
this.authURI = aBaseURI + "oauth2/auth";
this.tokenURI = aBaseURI + "oauth2/token";
this.consumerKey = aAppKey;
this.consumerSecret = aAppSecret;
this.scope = aScope;
this.extraAuthParams = [];
this.log = Log4Moz.getConfiguredLogger("TBOAuth");
}
OAuth2_1.prototype = {
consumerKey: null,
consumerSecret: null,
completionURI: "http://localhost",
requestWindowURI: "chrome://messenger/content/browserRequest.xul",
requestWindowFeatures: "chrome,private,centerscreen,width=980,height=750",
requestWindowTitle: "",
scope: null,
accessToken: null,
refreshToken: null,
tokenExpires: 0,
connect(aSuccess, aFailure, aWithUI, aRefresh) {
this.connectSuccessCallback = aSuccess;
this.connectFailureCallback = aFailure;
if (!aRefresh && this.accessToken) {
aSuccess();
} else if (this.refreshToken) {
this.requestAccessToken(this.refreshToken, true);
} else {
if (!aWithUI) {
aFailure('{ "error": "auth_noui" }');
return;
}
if (gConnecting[this.authURI]) {
aFailure("Window already open");
return;
}
this.requestAuthorization();
}
},
requestAuthorization() {
let params = new URLSearchParams({
response_type: "code",
client_id: this.consumerKey,
redirect_uri: this.completionURI,
});
// The scope is optional.
if (this.scope) {
params.append("scope", this.scope);
}
for (let [name, value] of this.extraAuthParams) {
params.append(name, value);
}
let authEndpointURI = this.authURI + "?" + params.toString();
this.log.info(
"Interacting with the resource owner to obtain an authorization grant " +
"from the authorization endpoint: " +
authEndpointURI
);
this._browserRequest = {
account: this,
url: authEndpointURI,
_active: true,
iconURI: "",
cancelled() {
if (!this._active) {
return;
}
this.account.finishAuthorizationRequest();
this.account.onAuthorizationFailed(
Cr.NS_ERROR_ABORT,
'{ "error": "cancelled"}'
);
},
loaded(aWindow, aWebProgress) {
if (!this._active) {
return;
}
this._listener = {
window: aWindow,
webProgress: aWebProgress,
_parent: this.account,
QueryInterface: ChromeUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
]),
_cleanUp() {
this.webProgress.removeProgressListener(this);
this.window.close();
delete this.window;
},
_checkForRedirect(aURL) {
if (aURL.indexOf(this._parent.completionURI) != 0) {
return;
}
this._parent.finishAuthorizationRequest();
this._parent.onAuthorizationReceived(aURL);
},
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
const wpl = Ci.nsIWebProgressListener;
if (aStateFlags & (wpl.STATE_START | wpl.STATE_IS_NETWORK)) {
this._checkForRedirect(aRequest.name);
}
},
onLocationChange(aWebProgress, aRequest, aLocation) {
this._checkForRedirect(aLocation.spec);
},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};
aWebProgress.addProgressListener(
this._listener,
Ci.nsIWebProgress.NOTIFY_ALL
);
aWindow.document.title = this.account.requestWindowTitle;
},
};
this.wrappedJSObject = this._browserRequest;
gConnecting[this.authURI] = true;
Services.ww.openWindow(
null,
this.requestWindowURI,
null,
this.requestWindowFeatures,
this
);
},
finishAuthorizationRequest() {
gConnecting[this.authURI] = false;
if (!("_browserRequest" in this)) {
return;
}
this._browserRequest._active = false;
if ("_listener" in this._browserRequest) {
this._browserRequest._listener._cleanUp();
}
delete this._browserRequest;
},
// @see RFC 6749 section 4.1.2: Authorization Response
onAuthorizationReceived(aURL) {
this.log.info("OAuth2_1 authorization received: url=" + aURL);
let params = new URLSearchParams(aURL.split("?", 2)[1]);
if (params.has("code")) {
this.requestAccessToken(params.get("code"), false);
} else {
this.onAuthorizationFailed(null, aURL);
}
},
onAuthorizationFailed(aError, aData) {
this.connectFailureCallback(aData);
},
/**
* Request a new access token, or refresh an existing one.
* @param {string} aCode - The token issued to the client.
* @param {boolean} aRefresh - Whether it's a refresh of a token or not.
*/
requestAccessToken(aCode, aRefresh) {
// @see RFC 6749 section 4.1.3. Access Token Request
// @see RFC 6749 section 6. Refreshing an Access Token
let data = new URLSearchParams();
data.append("client_id", this.consumerKey);
if (this.consumerSecret) data.append("client_secret", this.consumerSecret);
if (aRefresh) {
this.log.info(
`Making a refresh request to the token endpoint: ${this.tokenURI}`
);
data.append("grant_type", "refresh_token");
data.append("refresh_token", aCode);
} else {
this.log.info(
`Making access token request to the token endpoint: ${this.tokenURI}`
);
data.append("grant_type", "authorization_code");
data.append("code", aCode);
data.append("redirect_uri", this.completionURI);
}
fetch(this.tokenURI, {
method: "POST",
cache: "no-cache",
body: data,
})
.then(response => response.json())
.then(result => {
if ("error" in result) {
// RFC 6749 section 5.2. Error Response
this.log.info(
`The authorization server returned an error response: ${JSON.stringify(
result
)}`
);
this.connectFailureCallback(result);
return;
}
// RFC 6749 section 5.1. Successful Response
this.log.info("The authorization server issued an access token.");
this.accessToken = result.access_token;
if ("refresh_token" in result) {
this.refreshToken = result.refresh_token;
}
if ("expires_in" in result) {
this.tokenExpires = new Date().getTime() + result.expires_in * 1000;
} else {
this.tokenExpires = Number.MAX_VALUE;
}
this.connectSuccessCallback();
})
.catch(err => {
this.log.info(`Connection to authorization server failed: ${err}`);
this.connectFailureCallback(err);
});
},
};
EAS-4-TbSync-1.12/content/includes/ 0000775 0000000 0000000 00000000000 13623463701 0016673 5 ustar 00root root 0000000 0000000 EAS-4-TbSync-1.12/content/includes/calendarsync.js 0000664 0000000 0000000 00000052556 13623463701 0021714 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
const cal = TbSync.lightning.cal;
const ICAL = TbSync.lightning.ICAL;
var Calendar = {
// --------------------------------------------------------------------------- //
// Read WBXML and set Thunderbird item
// --------------------------------------------------------------------------- //
setThunderbirdItemFromWbxml: function (tbItem, data, id, syncdata) {
let item = tbItem instanceof TbSync.lightning.TbItem ? tbItem.nativeItem : tbItem;
let asversion = syncdata.accountData.getAccountProperty("asversion");
item.id = id;
let easTZ = new eas.tools.TimeZoneDataStructure();
eas.sync.setItemSubject(item, syncdata, data);
eas.sync.setItemLocation(item, syncdata, data);
eas.sync.setItemCategories(item, syncdata, data);
eas.sync.setItemBody(item, syncdata, data);
//timezone
let stdOffset = eas.defaultTimezoneInfo.std.offset;
let dstOffset = eas.defaultTimezoneInfo.dst.offset;
if (data.TimeZone) {
if (data.TimeZone == "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") {
TbSync.dump("Recieve TZ", "No timezone data received, using local default timezone.");
} else {
//load timezone struct into EAS TimeZone object
easTZ.easTimeZone64 = data.TimeZone;
if (TbSync.prefs.getIntPref("log.userdatalevel") > 2) TbSync.dump("Recieve TZ", item.title + easTZ.toString());
stdOffset = easTZ.utcOffset;
dstOffset = easTZ.daylightBias + easTZ.utcOffset;
}
}
if (data.StartTime) {
let utc = cal.createDateTime(data.StartTime); //format "19800101T000000Z" - UTC
item.startDate = utc.getInTimezone(eas.tools.guessTimezoneByStdDstOffset(stdOffset, dstOffset, easTZ.standardName));
if (data.AllDayEvent && data.AllDayEvent == "1") {
item.startDate.timezone = (cal.dtz && cal.dtz.floating) ? cal.dtz.floating : cal.floating();
item.startDate.isDate = true;
}
}
if (data.EndTime) {
let utc = cal.createDateTime(data.EndTime);
item.endDate = utc.getInTimezone(eas.tools.guessTimezoneByStdDstOffset(stdOffset, dstOffset, easTZ.standardName));
if (data.AllDayEvent && data.AllDayEvent == "1") {
item.endDate.timezone = (cal.dtz && cal.dtz.floating) ? cal.dtz.floating : cal.floating();
item.endDate.isDate = true;
}
}
//stamp time cannot be set and it is not needed, an updated version is only send to the server, if there was a change, so stamp will be updated
//EAS Reminder
item.clearAlarms();
if (data.Reminder && data.StartTime) {
let alarm = cal.createAlarm();
alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_START;
alarm.offset = cal.createDuration();
alarm.offset.inSeconds = (0-parseInt(data.Reminder)*60);
alarm.action ="DISPLAY";
item.addAlarm(alarm);
let alarmData = cal.alarms.calculateAlarmDate(item, alarm);
let startDate = cal.createDateTime(data.StartTime);
let nowDate = eas.tools.getNowUTC();
if (startDate.compare(nowDate) < 0) {
// Mark alarm as ACK if in the past.
item.alarmLastAck = nowDate;
}
}
eas.sync.mapEasPropertyToThunderbird ("BusyStatus", "TRANSP", data, item);
eas.sync.mapEasPropertyToThunderbird ("Sensitivity", "CLASS", data, item);
if (data.ResponseType) {
//store original EAS value
item.setProperty("X-EAS-ResponseType", eas.xmltools.checkString(data.ResponseType, "0")); //some server send empty ResponseType ???
}
//Attendees - remove all Attendees and re-add the ones from XML
item.removeAllAttendees();
if (data.Attendees && data.Attendees.Attendee) {
let att = [];
if (Array.isArray(data.Attendees.Attendee)) att = data.Attendees.Attendee;
else att.push(data.Attendees.Attendee);
for (let i = 0; i < att.length; i++) {
if (att[i].Email && eas.tools.isString(att[i].Email) && att[i].Name) { //req.
let attendee = cal.createAttendee();
//is this attendee the local EAS user?
let isSelf = (att[i].Email == syncdata.accountData.getAccountProperty("user"));
attendee["id"] = cal.email.prependMailTo(att[i].Email);
attendee["commonName"] = att[i].Name;
//default is "FALSE", only if THIS attendee isSelf, use ResponseRequested (we cannot respond for other attendee) - ResponseType is not send back to the server, it is just a local information
attendee["rsvp"] = (isSelf && data.ResponseRequested) ? "TRUE" : "FALSE";
//not supported in 2.5
switch (att[i].AttendeeType) {
case "1": //required
attendee["role"] = "REQ-PARTICIPANT";
attendee["userType"] = "INDIVIDUAL";
break;
case "2": //optional
attendee["role"] = "OPT-PARTICIPANT";
attendee["userType"] = "INDIVIDUAL";
break;
default : //resource or unknown
attendee["role"] = "NON-PARTICIPANT";
attendee["userType"] = "RESOURCE";
break;
}
//not supported in 2.5 - if attendeeStatus is missing, check if this isSelf and there is a ResponseType
if (att[i].AttendeeStatus)
attendee["participationStatus"] = eas.sync.MAP_EAS2TB.ATTENDEESTATUS[att[i].AttendeeStatus];
else if (isSelf && data.ResponseType)
attendee["participationStatus"] = eas.sync.MAP_EAS2TB.ATTENDEESTATUS[data.ResponseType];
else
attendee["participationStatus"] = "NEEDS-ACTION";
// status : [NEEDS-ACTION, ACCEPTED, DECLINED, TENTATIVE, DELEGATED, COMPLETED, IN-PROCESS]
// rolemap : [REQ-PARTICIPANT, OPT-PARTICIPANT, NON-PARTICIPANT, CHAIR]
// typemap : [INDIVIDUAL, GROUP, RESOURCE, ROOM]
// Add attendee to event
item.addAttendee(attendee);
} else {
TbSync.eventlog.add("info", syncdata, "Attendee without required name and/or email found. Skipped.");
}
}
}
if (data.OrganizerName && data.OrganizerEmail && eas.tools.isString(data.OrganizerEmail)) {
//Organizer
let organizer = cal.createAttendee();
organizer.id = cal.email.prependMailTo(data.OrganizerEmail);
organizer.commonName = data.OrganizerName;
organizer.rsvp = "FALSE";
organizer.role = "CHAIR";
organizer.userType = null;
organizer.participationStatus = "ACCEPTED";
organizer.isOrganizer = true;
item.organizer = organizer;
}
eas.sync.setItemRecurrence(item, syncdata, data, eas.tools.guessTimezoneByStdDstOffset(stdOffset, dstOffset, easTZ.standardName));
// BusyStatus is always representing the status of the current user in terms of availability.
// It has nothing to do with the status of a meeting. The user could be just the organizer, but does not need to attend, so he would be free.
// The correct map is between BusyStatus and TRANSP (show time as avail, busy, unset)
// A new event always sets TRANSP to busy, so unset is indeed a good way to store Tentiative
// However:
// - EAS Meetingstatus only knows ACTIVE or CANCELLED, but not CONFIRMED or TENTATIVE
// - TB STATUS has UNSET, CONFIRMED, TENTATIVE, CANCELLED
// -> Special case: User sets BusyStatus to TENTIATIVE -> TRANSP is unset and also set STATUS to TENTATIVE
// The TB STATUS is the correct map for EAS Meetingstatus and should be unset, if it is not a meeting EXCEPT if set to TENTATIVE
let tbStatus = (data.BusyStatus && data.BusyStatus == "1" ? "TENTATIVE" : null);
if (data.MeetingStatus) {
//store original EAS value
item.setProperty("X-EAS-MeetingStatus", data.MeetingStatus);
//bitwise representation for Meeting, Received, Cancelled:
let M = data.MeetingStatus & 0x1;
let R = data.MeetingStatus & 0x2;
let C = data.MeetingStatus & 0x4;
// We can map M+C to TB STATUS (TENTATIVE, CONFIRMED, CANCELLED, unset).
if (M) {
if (C) tbStatus = "CANCELLED";
else if (!tbStatus) tbStatus = "CONFIRMED"; // do not override "TENTIATIVE"
}
//we can also use the R information, to update our fallbackOrganizerName
if (!R && data.OrganizerName) syncdata.target.calendar.setProperty("fallbackOrganizerName", data.OrganizerName);
}
if (tbStatus) item.setProperty("STATUS", tbStatus)
else item.deleteProperty("STATUS");
//TODO: attachements (needs EAS 16.0!)
},
// --------------------------------------------------------------------------- //
//read TB event and return its data as WBXML
// --------------------------------------------------------------------------- //
getWbxmlFromThunderbirdItem: function (tbItem, syncdata, isException = false) {
let item = tbItem instanceof TbSync.lightning.TbItem ? tbItem.nativeItem : tbItem;
let asversion = syncdata.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncdata.type); //init wbxml with "" and not with precodes, and set initial codepage
let nowDate = new Date();
/*
* We do not use ghosting, that means, if we do not include a value in CHANGE, it is removed from the server.
* However, this does not seem to work on all fields. Furthermore, we need to include any (empty) container to blank its childs.
*/
//Order of tags taken from https://msdn.microsoft.com/en-us/library/dn338917(v=exchg.80).aspx
//timezone
if (!isException) {
let easTZ = new eas.tools.TimeZoneDataStructure();
//if there is no end and no start (or both are floating) use default timezone info
let tzInfo = null;
if (item.startDate && item.startDate.timezone.tzid != "floating") tzInfo = eas.tools.getTimezoneInfo(item.startDate.timezone);
else if (item.endDate && item.endDate.timezone.tzid != "floating") tzInfo = eas.tools.getTimezoneInfo(item.endDate.timezone);
if (!tzInfo) tzInfo = eas.defaultTimezoneInfo;
easTZ.utcOffset = tzInfo.std.offset;
easTZ.standardBias = 0;
easTZ.daylightBias = tzInfo.dst.offset - tzInfo.std.offset;
easTZ.standardName = tzInfo.std.displayname;
easTZ.daylightName = tzInfo.dst.displayname;
if (tzInfo.std.switchdate && tzInfo.dst.switchdate) {
easTZ.standardDate.wMonth = tzInfo.std.switchdate.month;
easTZ.standardDate.wDay = tzInfo.std.switchdate.weekOfMonth;
easTZ.standardDate.wDayOfWeek = tzInfo.std.switchdate.dayOfWeek;
easTZ.standardDate.wHour = tzInfo.std.switchdate.hour;
easTZ.standardDate.wMinute = tzInfo.std.switchdate.minute;
easTZ.standardDate.wSecond = tzInfo.std.switchdate.second;
easTZ.daylightDate.wMonth = tzInfo.dst.switchdate.month;
easTZ.daylightDate.wDay = tzInfo.dst.switchdate.weekOfMonth;
easTZ.daylightDate.wDayOfWeek = tzInfo.dst.switchdate.dayOfWeek;
easTZ.daylightDate.wHour = tzInfo.dst.switchdate.hour;
easTZ.daylightDate.wMinute = tzInfo.dst.switchdate.minute;
easTZ.daylightDate.wSecond = tzInfo.dst.switchdate.second;
}
wbxml.atag("TimeZone", easTZ.easTimeZone64);
if (TbSync.prefs.getIntPref("log.userdatalevel") > 2) TbSync.dump("Send TZ", item.title + easTZ.toString());
}
//AllDayEvent (for simplicity, we always send a value)
wbxml.atag("AllDayEvent", (item.startDate && item.startDate.isDate && item.endDate && item.endDate.isDate) ? "1" : "0");
//Body
wbxml.append(eas.sync.getItemBody(item, syncdata));
//BusyStatus (Free, Tentative, Busy) is taken from TRANSP (busy, free, unset=tentative)
//However if STATUS is set to TENTATIVE, overide TRANSP and set BusyStatus to TENTATIVE
if (item.hasProperty("STATUS") && item.getProperty("STATUS") == "TENTATIVE") {
wbxml.atag("BusyStatus","1");
} else {
wbxml.atag("BusyStatus", eas.sync.mapThunderbirdPropertyToEas("TRANSP", "BusyStatus", item));
}
//Organizer
if (!isException) {
if (item.organizer && item.organizer.commonName) wbxml.atag("OrganizerName", item.organizer.commonName);
if (item.organizer && item.organizer.id) wbxml.atag("OrganizerEmail", cal.email.removeMailTo(item.organizer.id));
}
//DtStamp in UTC
wbxml.atag("DtStamp", item.stampTime ? eas.tools.getIsoUtcString(item.stampTime) : eas.tools.dateToBasicISOString(nowDate));
//EndTime in UTC
wbxml.atag("EndTime", item.endDate ? eas.tools.getIsoUtcString(item.endDate) : eas.tools.dateToBasicISOString(nowDate));
//Location
wbxml.atag("Location", (item.hasProperty("location")) ? item.getProperty("location") : "");
//EAS Reminder (TB getAlarms) - at least with zpush blanking by omitting works, horde does not work
let alarms = item.getAlarms({});
if (alarms.length>0) {
let reminder = -1;
if (alarms[0].offset !== null) {
reminder = 0 - alarms[0].offset.inSeconds/60;
} else if (item.startDate) {
let timeDiff =item.startDate.getInTimezone(eas.utcTimezone).subtractDate(alarms[0].alarmDate.getInTimezone(eas.utcTimezone));
reminder = timeDiff.inSeconds/60;
TbSync.eventlog.add("info", syncdata, "Converting absolute alarm to relative alarm (not supported).", item.icalString);
}
if (reminder >= 0) wbxml.atag("Reminder", reminder.toString());
else TbSync.eventlog.add("info", syncdata, "Droping alarm after start date (not supported).", item.icalString);
}
//Sensitivity (CLASS)
wbxml.atag("Sensitivity", eas.sync.mapThunderbirdPropertyToEas("CLASS", "Sensitivity", item));
//Subject (obmitting these, should remove them from the server - that does not work reliably, so we send blanks)
wbxml.atag("Subject", (item.title) ? item.title : "");
//StartTime in UTC
wbxml.atag("StartTime", item.startDate ? eas.tools.getIsoUtcString(item.startDate) : eas.tools.dateToBasicISOString(nowDate));
//UID (limit to 300)
//each TB event has an ID, which is used as EAS serverId - however there is a second UID in the ApplicationData
//since we do not have two different IDs to use, we use the same ID
if (!isException) { //docs say it would be allowed in exception in 2.5, but it does not work, if present
wbxml.atag("UID", item.id);
}
//IMPORTANT in EAS v16 it is no longer allowed to send a UID
//Only allowed in exceptions in v2.5
//EAS MeetingStatus
// 0 (000) The event is an appointment, which has no attendees.
// 1 (001) The event is a meeting and the user is the meeting organizer.
// 3 (011) This event is a meeting, and the user is not the meeting organizer; the meeting was received from someone else.
// 5 (101) The meeting has been canceled and the user was the meeting organizer.
// 7 (111) The meeting has been canceled. The user was not the meeting organizer; the meeting was received from someone else
//there are 3 fields; Meeting, Owner, Cancelled
//M can be reconstructed from #of attendees (looking at the old value is not wise, since it could have been changed)
//C can be reconstucted from TB STATUS
//O can be reconstructed by looking at the original value, or (if not present) by comparing EAS ownerID with TB ownerID
let countAttendees = {};
let attendees = item.getAttendees(countAttendees);
//if (!(isException && asversion == "2.5")) { //MeetingStatus is not supported in exceptions in EAS 2.5
if (!isException) { //Exchange 2010 does not seem to support MeetingStatus at all in exceptions
if (countAttendees == 0) wbxml.atag("MeetingStatus", "0");
else {
//get owner information
let isReceived = false;
if (item.hasProperty("X-EAS-MEETINGSTATUS")) isReceived = item.getProperty("X-EAS-MEETINGSTATUS") & 0x2;
else isReceived = (item.organizer && item.organizer.id && cal.email.removeMailTo(item.organizer.id) != syncdata.accountData.getAccountProperty("user"));
//either 1,3,5 or 7
if (item.hasProperty("STATUS") && item.getProperty("STATUS") == "CANCELLED") {
//either 5 or 7
wbxml.atag("MeetingStatus", (isReceived ? "7" : "5"));
} else {
//either 1 or 3
wbxml.atag("MeetingStatus", (isReceived ? "3" : "1"));
}
}
}
//Attendees
let TB_responseType = null;
if (!(isException && asversion == "2.5")) { //attendees are not supported in exceptions in EAS 2.5
if (countAttendees.value > 0) {
wbxml.otag("Attendees");
for (let attendee of attendees) {
wbxml.otag("Attendee");
wbxml.atag("Email", cal.email.removeMailTo(attendee.id));
wbxml.atag("Name", (attendee.commonName ? attendee.commonName : cal.email.removeMailTo(attendee.id).split("@")[0]));
if (asversion != "2.5") {
//it's pointless to send AttendeeStatus,
// - if we are the owner of a meeting, TB does not have an option to actually set the attendee status (on behalf of an attendee) in the UI
// - if we are an attendee (of an invite) we cannot and should not set status of other attendees and or own status must be send through a MeetingResponse
// -> all changes of attendee status are send from the server to us, either via ResponseType or via AttendeeStatus
//wbxml.atag("AttendeeStatus", eas.sync.MAP_TB2EAS.ATTENDEESTATUS[attendee.participationStatus]);
if (attendee.userType == "RESOURCE" || attendee.userType == "ROOM" || attendee.role == "NON-PARTICIPANT") wbxml.atag("AttendeeType","3");
else if (attendee.role == "REQ-PARTICIPANT" || attendee.role == "CHAIR") wbxml.atag("AttendeeType","1");
else wbxml.atag("AttendeeType","2"); //leftovers are optional
}
wbxml.ctag();
}
wbxml.ctag();
} else {
wbxml.atag("Attendees");
}
}
//Categories (see https://github.com/jobisoft/TbSync/pull/35#issuecomment-359286374)
if (!isException) {
wbxml.append(eas.sync.getItemCategories(item, syncdata));
}
//recurrent events (implemented by Chris Allan)
if (!isException) {
wbxml.append(eas.sync.getItemRecurrence(item, syncdata));
}
//---------------------------
//TP PRIORITY (9=LOW, 5=NORMAL, 1=HIGH) not mapable to EAS Event
//TODO: attachements (needs EAS 16.0!)
//https://dxr.mozilla.org/comm-central/source/calendar/base/public/calIAlarm.idl
//TbSync.dump("ALARM ("+i+")", [, alarms[i].related, alarms[i].repeat, alarms[i].repeatOffset, alarms[i].repeatDate, alarms[i].action].join("|"));
return wbxml.getBytes();
}
/*
//loop over all properties
let propEnum = item.propertyEnumerator;
while (propEnum.hasMoreElements()) {
let prop = propEnum.getNext().QueryInterface(Components.interfaces.nsIProperty);
let pname = prop.name;
TbSync.dump("PROP", pname + " = " + prop.value);
}
*/
}
EAS-4-TbSync-1.12/content/includes/contactsync.js 0000664 0000000 0000000 00000034615 13623463701 0021572 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
const eas = TbSync.providers.eas;
var Contacts = {
//these functions handle categories compatible to the Category Manager add-on, which is compatible to lots of other sync tools (sogo, carddav-sync, roundcube)
categoriesFromString: function (catString) {
let catsArray = [];
if (catString.trim().length>0) catsArray = catString.trim().split("\u001A").filter(String);
return catsArray;
},
categoriesToString: function (catsArray) {
return catsArray.join("\u001A");
},
/* The following TB properties are not yet synced anywhere:
- , FamilyName
- _AimScreenName
- WebPage2 (home)
*/
//includes all properties, which can be mapped 1-to-1
map_TB_properties_to_EAS_properties : {
DisplayName: 'FileAs',
FirstName: 'FirstName',
LastName: 'LastName',
PrimaryEmail: 'Email1Address',
SecondEmail: 'Email2Address',
Email3Address: 'Email3Address',
WebPage1: 'WebPage',
SpouseName: 'Spouse',
CellularNumber: 'MobilePhoneNumber',
PagerNumber: 'PagerNumber',
HomeCity: 'HomeAddressCity',
HomeCountry: 'HomeAddressCountry',
HomeZipCode: 'HomeAddressPostalCode',
HomeState: 'HomeAddressState',
HomePhone: 'HomePhoneNumber',
Company: 'CompanyName',
Department: 'Department',
JobTitle: 'JobTitle',
WorkCity: 'BusinessAddressCity',
WorkCountry: 'BusinessAddressCountry',
WorkZipCode: 'BusinessAddressPostalCode',
WorkState: 'BusinessAddressState',
WorkPhone: 'BusinessPhoneNumber',
//Missusing so that "Custom1" is saved to the server
Custom1: 'OfficeLocation',
//As in TZPUSH
FaxNumber: 'HomeFaxNumber',
//Custom fields added to UI
AssistantName: 'AssistantName',
AssistantPhoneNumber: 'AssistantPhoneNumber',
BusinessFaxNumber: 'BusinessFaxNumber',
Business2PhoneNumber: 'Business2PhoneNumber',
Home2PhoneNumber: 'Home2PhoneNumber',
CarPhoneNumber: 'CarPhoneNumber',
MiddleName: 'MiddleName',
RadioPhoneNumber: 'RadioPhoneNumber',
OtherAddressCity: 'OtherAddressCity',
OtherAddressCountry: 'OtherAddressCountry',
OtherAddressPostalCode: 'OtherAddressPostalCode',
OtherAddressState: 'OtherAddressState'
},
//there are currently no TB fields for these values, TbSync will store (and resend) them, but will not allow to view/edit
unused_EAS_properties: [
'Suffix',
'Title',
'Alias', //pseudo field
'WeightedRank', //pseudo field
'YomiCompanyName', //japanese phonetic equivalent
'YomiFirstName', //japanese phonetic equivalent
'YomiLastName', //japanese phonetic equivalent
'CompressedRTF'
],
map_TB_properties_to_EAS_properties2 : {
NickName: 'NickName',
//Missusing so that "Custom2,3,4" is saved to the server
Custom2: 'CustomerId',
Custom3: 'GovernmentId',
Custom4: 'AccountName',
//custom fields added to UI
IMAddress: 'IMAddress',
IMAddress2: 'IMAddress2',
IMAddress3: 'IMAddress3',
ManagerName: 'ManagerName',
CompanyMainPhone: 'CompanyMainPhone',
MMS: 'MMS'
},
// --------------------------------------------------------------------------- //
// Read WBXML and set Thunderbird item
// --------------------------------------------------------------------------- //
setThunderbirdItemFromWbxml: function (abItem, data, id, syncdata) {
let asversion = syncdata.accountData.getAccountProperty("asversion");
abItem.primaryKey = id;
//loop over all known TB properties which map 1-to-1 (two EAS sets Contacts and Contacts2)
for (let set=0; set < 2; set++) {
let properties = (set == 0) ? this.TB_properties : this.TB_properties2;
for (let p=0; p < properties.length; p++) {
let TB_property = properties[p];
let EAS_property = (set == 0) ? this.map_TB_properties_to_EAS_properties[TB_property] : this.map_TB_properties_to_EAS_properties2[TB_property];
let value = eas.xmltools.checkString(data[EAS_property]);
//is this property part of the send data?
if (value) {
//do we need to manipulate the value?
switch (EAS_property) {
case "Email1Address":
case "Email2Address":
case "Email3Address":
let parsedInput = MailServices.headerParser.makeFromDisplayAddress(value);
let fixedValue = (parsedInput && parsedInput[0] && parsedInput[0].email) ? parsedInput[0].email : value;
if (fixedValue != value) {
if (TbSync.prefs.getIntPref("log.userdatalevel") > 2) TbSync.dump("Parsing email display string via RFC 2231 and RFC 2047 ("+EAS_property+")", value + " -> " + fixedValue);
value = fixedValue;
}
break;
}
abItem.setProperty(TB_property, value);
} else {
//clear
abItem.deleteProperty(TB_property);
}
}
}
//take care of birthday and anniversary
let dates = [];
dates.push(["Birthday", "BirthDay", "BirthMonth", "BirthYear"]); //EAS, TB1, TB2, TB3
dates.push(["Anniversary", "AnniversaryDay", "AnniversaryMonth", "AnniversaryYear"]);
for (let p=0; p < dates.length; p++) {
let value = eas.xmltools.checkString(data[dates[p][0]]);
if (value == "") {
//clear
abItem.deleteProperty(dates[p][1]);
abItem.deleteProperty(dates[p][2]);
abItem.deleteProperty(dates[p][3]);
} else {
//set
let dateObj = new Date(value);
abItem.setProperty(dates[p][3], dateObj.getFullYear().toString());
abItem.setProperty(dates[p][2], (dateObj.getMonth()+1).toString());
abItem.setProperty(dates[p][1], dateObj.getDate().toString());
}
}
//take care of multiline address fields
let streets = [];
let seperator = String.fromCharCode(syncdata.accountData.getAccountProperty("seperator")); // options are 44 (,) or 10 (\n)
streets.push(["HomeAddressStreet", "HomeAddress", "HomeAddress2"]); //EAS, TB1, TB2
streets.push(["BusinessAddressStreet", "WorkAddress", "WorkAddress2"]);
streets.push(["OtherAddressStreet", "OtherAddress", "OtherAddress2"]);
for (let p=0; p < streets.length; p++) {
let value = eas.xmltools.checkString(data[streets[p][0]]);
if (value == "") {
//clear
abItem.deleteProperty(streets[p][1]);
abItem.deleteProperty(streets[p][2]);
} else {
//set
let lines = value.split(seperator);
abItem.setProperty(streets[p][1], lines.shift());
abItem.setProperty(streets[p][2], lines.join(seperator));
}
}
//take care of photo
if (data.Picture) {
abItem.addPhoto(id, eas.xmltools.nodeAsArray(data.Picture)[0], "jpg"); //Kerio sends Picture as container
}
//take care of notes
if (asversion == "2.5") {
abItem.setProperty("Notes", eas.xmltools.checkString(data.Body));
} else {
if (data.Body && data.Body.Data) abItem.setProperty("Notes", eas.xmltools.checkString(data.Body.Data));
else abItem.setProperty("Notes", "");
}
//take care of categories and children
let containers = [];
containers.push(["Categories", "Category"]);
containers.push(["Children", "Child"]);
for (let c=0; c < containers.length; c++) {
if (data[containers[c][0]] && data[containers[c][0]][containers[c][1]]) {
let cats = [];
if (Array.isArray(data[containers[c][0]][containers[c][1]])) cats = data[containers[c][0]][containers[c][1]];
else cats.push(data[containers[c][0]][containers[c][1]]);
abItem.setProperty(containers[c][0], this.categoriesToString(cats));
}
}
//take care of unmapable EAS option (Contact)
for (let i=0; i < this.unused_EAS_properties.length; i++) {
if (data[this.unused_EAS_properties[i]]) abItem.setProperty("EAS-" + this.unused_EAS_properties[i], data[this.unused_EAS_properties[i]]);
}
//further manipulations
if (syncdata.accountData.getAccountProperty("displayoverride")) {
abItem.setProperty("DisplayName", abItem.getProperty("FirstName", "") + " " + abItem.getProperty("LastName", ""));
if (abItem.getProperty("DisplayName", "" ) == " " )
abItem.setProperty("DisplayName", abItem.getProperty("Company", abItem.getProperty("PrimaryEmail", "")));
}
},
// --------------------------------------------------------------------------- //
//read TB event and return its data as WBXML
// --------------------------------------------------------------------------- //
getWbxmlFromThunderbirdItem: function (abItem, syncdata, isException = false) {
let asversion = syncdata.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncdata.type); //init wbxml with "" and not with precodes, and set initial codepage
let nowDate = new Date();
//loop over all known TB properties which map 1-to-1 (send empty value if not set)
for (let p=0; p < this.TB_properties.length; p++) {
let TB_property = this.TB_properties[p];
let EAS_property = this.map_TB_properties_to_EAS_properties[TB_property];
let value = abItem.getProperty(TB_property,"");
if (value) wbxml.atag(EAS_property, value);
}
//take care of birthday and anniversary
let dates = [];
dates.push(["Birthday", "BirthDay", "BirthMonth", "BirthYear"]);
dates.push(["Anniversary", "AnniversaryDay", "AnniversaryMonth", "AnniversaryYear"]);
for (let p=0; p < dates.length; p++) {
let year = abItem.getProperty(dates[p][3], "");
let month = abItem.getProperty(dates[p][2], "");
let day = abItem.getProperty(dates[p][1], "");
if (year && month && day) {
//set
if (month.length<2) month="0"+month;
if (day.length<2) day="0"+day;
wbxml.atag(dates[p][0], year + "-" + month + "-" + day + "T00:00:00.000Z");
}
}
//take care of multiline address fields
let streets = [];
let seperator = String.fromCharCode(syncdata.accountData.getAccountProperty("seperator")); // options are 44 (,) or 10 (\n)
streets.push(["HomeAddressStreet", "HomeAddress", "HomeAddress2"]); //EAS, TB1, TB2
streets.push(["BusinessAddressStreet", "WorkAddress", "WorkAddress2"]);
streets.push(["OtherAddressStreet", "OtherAddress", "OtherAddress2"]);
for (let p=0; p < streets.length; p++) {
let values = [];
let s1 = abItem.getProperty(streets[p][1], "");
let s2 = abItem.getProperty(streets[p][2], "");
if (s1) values.push(s1);
if (s2) values.push(s2);
if (values.length>0) wbxml.atag(streets[p][0], values.join(seperator));
}
//take care of photo
if (abItem.getProperty("PhotoType", "") == "file") {
wbxml.atag("Picture", abItem.getPhoto());
}
//take care of unmapable EAS option
for (let i=0; i < this.unused_EAS_properties.length; i++) {
let value = abItem.getProperty("EAS-" + this.unused_EAS_properties[i], "");
if (value) wbxml.atag(this.unused_EAS_properties[i], value);
}
//take care of categories and children
let containers = [];
containers.push(["Categories", "Category"]);
containers.push(["Children", "Child"]);
for (let c=0; c < containers.length; c++) {
let cats = abItem.getProperty(containers[c][0], "");
if (cats) {
let catsArray = this.categoriesFromString(cats);
wbxml.otag(containers[c][0]);
for (let ca=0; ca < catsArray.length; ca++) wbxml.atag(containers[c][1], catsArray[ca]);
wbxml.ctag();
}
}
//take care of notes - SWITCHING TO AirSyncBase (if 2.5, we still need Contact group here!)
let description = abItem.getProperty("Notes", "");
if (asversion == "2.5") {
wbxml.atag("Body", description);
} else {
wbxml.switchpage("AirSyncBase");
wbxml.otag("Body");
wbxml.atag("Type", "1");
wbxml.atag("EstimatedDataSize", "" + description.length);
wbxml.atag("Data", description);
wbxml.ctag();
}
//take care of Contacts2 group - SWITCHING TO CONTACTS2
wbxml.switchpage("Contacts2");
//loop over all known TB properties of EAS group Contacts2 (send empty value if not set)
for (let p=0; p < this.TB_properties2.length; p++) {
let TB_property = this.TB_properties2[p];
let EAS_property = this.map_TB_properties_to_EAS_properties2[TB_property];
let value = abItem.getProperty(TB_property,"");
if (value) wbxml.atag(EAS_property, value);
}
return wbxml.getBytes();
}
}
Contacts.TB_properties = Object.keys(Contacts.map_TB_properties_to_EAS_properties);
Contacts.TB_properties2 = Object.keys(Contacts.map_TB_properties_to_EAS_properties2);
EAS-4-TbSync-1.12/content/includes/network.js 0000664 0000000 0000000 00000203220 13623463701 0020721 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var { OAuth2_1 } = ChromeUtils.import("chrome://eas4tbsync/content/OAuth2_1.jsm");
var network = {
getEasURL: function(accountData) {
let protocol = (accountData.getAccountProperty("https")) ? "https://" : "http://";
let h = protocol + accountData.getAccountProperty("host");
while (h.endsWith("/")) { h = h.slice(0,-1); }
if (h.endsWith("Microsoft-Server-ActiveSync")) return h;
return h + "/Microsoft-Server-ActiveSync";
},
getAuthData: function(accountData) {
let authData = {
// This is the host for the password manager, which could be different from
// the actual host property of the account. For EAS we want to couple the password
// with the ACCOUNT and not any sort of url, which could change via autodiscover
// at any time.
get host() {
return "TbSync#" + accountData.accountID;
},
get user() {
return accountData.getAccountProperty("user");
},
get password() {
return TbSync.passwordManager.getLoginInfo(this.host, "TbSync/EAS", this.user);
},
updateLoginData: function(newUsername, newPassword) {
let oldUsername = this.user;
TbSync.passwordManager.updateLoginInfo(this.host, "TbSync/EAS", oldUsername, newUsername, newPassword);
// Also update the username of this account. Add dedicated username setter?
accountData.setAccountProperty("user", newUsername);
},
removeLoginData: function() {
TbSync.passwordManager.removeLoginInfos(this.host, "TbSync/EAS");
}
};
return authData;
},
// prepare and patch OAuth2 object
getOAuthObj: function(configObject = null) {
let accountname, user, host, accountID, servertype;
let accountData = (configObject && configObject.hasOwnProperty("accountData")) ? configObject.accountData : null;
if (accountData) {
accountname = accountData.getAccountProperty("accountname");
user = accountData.getAccountProperty("user");
host = accountData.getAccountProperty("host");
servertype = accountData.getAccountProperty("servertype");
accountID = accountData.accountID;
} else {
accountname = (configObject && configObject.hasOwnProperty("accountname")) ? configObject.accountname : "";
user = (configObject && configObject.hasOwnProperty("user")) ? configObject.user : "";
host = (configObject && configObject.hasOwnProperty("host")) ? configObject.host : "";
servertype = (configObject && configObject.hasOwnProperty("servertype")) ? configObject.servertype : "";
accountID = "";
}
if (!["office365"].includes(servertype))
return null;
let config = {};
switch (host) {
case "outlook.office365.com":
config = {
auth_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
token_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/token",
redirect_uri : "https://login.microsoftonline.com/common/oauth2/nativeclient",
scope : "offline_access https://outlook.office.com/EAS.AccessAsUser.All",
client_id : "2980deeb-7460-4723-864a-f9b0f10cd992",
}
break;
default:
return null;
}
let oauth = new OAuth2_1("", config.scope, config.client_id, config.client_secret);
oauth.requestWindowFeatures = "chrome,private,centerscreen,width=500,height=750";
// The v2 endpoints are different and need manual override
oauth.authURI = config.auth_uri ;
oauth.tokenURI = config.token_uri;
oauth.completionURI = config.redirect_uri;
oauth.extraAuthParams = [
["prompt", "consent"],
["login_hint", user],
];
if (accountname) {
oauth.requestWindowTitle = "TbSync account <" + accountname + "> requests authorization.";
} else {
oauth.requestWindowTitle = "A TbSync account requests authorization.";
}
/* Adding custom methods to the oauth object */
oauth.asyncConnect = async function(rv) {
let self = this;
rv.error = "";
rv.tokens = "";
// If multiple resources need to authenticate they will all end here, even though they
// might share the same token. Due to the async nature, each process will refresh
// "its own" token again, which is not needed. We force clear the token here and each
// final connect process will actually check the acccessToken and abort the refresh,
// if it is already there, generated by some other process.
if (self.accessToken) self.accessToken = "";
try {
await new Promise(function(resolve, reject) {
// refresh = false will do nothing and resolve immediately, if an accessToken
// exists already, which must have been generated by another process, as
// we cleared it beforehand.
self.connect(resolve, reject, /* with UI */ true, /* refresh */ false);
});
rv.tokens = self.tokens;
return true;
} catch (e) {
rv.error = eas.tools.isString(e) ? e : JSON.stringify(e);
}
try {
switch (JSON.parse(rv.error).error) {
case "invalid_grant":
self.accessToken = "";
self.refreshToken = "";
return true;
case "cancelled":
rv.error = "OAuthAbortError";
break;
default:
rv.error = "OAuthServerError::"+rv.error;
break;
}
} catch (e) {
rv.error = "OAuthServerError::"+rv.error;
Components.utils.reportError(e);
}
return false;
};
oauth.isExpired = function() {
const OAUTH_GRACE_TIME = 30 * 1000;
return (this.tokenExpires - OAUTH_GRACE_TIME < new Date().getTime());
};
const OAUTHVALUES = [
["access", "", "accessToken"],
["refresh", "", "refreshToken"],
["expires", Number.MAX_VALUE, "tokenExpires"],
];
// returns a JSON string containing all the oauth values
Object.defineProperty(oauth, "tokens", {
get: function() {
let tokensObj = {};
for (let oauthValue of OAUTHVALUES) {
// use the system value or if not defined the default
tokensObj[oauthValue[0]] = this[oauthValue[2]] || oauthValue[1];
}
return JSON.stringify(tokensObj);
},
enumerable: true,
});
if (accountData) {
// authData allows us to access the password manager values belonging to this account/calendar
// simply by authdata.username and authdata.password
oauth.authData = TbSync.providers.eas.network.getAuthData(accountData);
oauth.parseAndSanitizeTokenString = function(tokenString) {
let _tokensObj = {};
try {
_tokensObj = JSON.parse(tokenString);
} catch (e) {}
let tokensObj = {};
for (let oauthValue of OAUTHVALUES) {
// use the provided value or if not defined the default
tokensObj[oauthValue[0]] = (_tokensObj && _tokensObj.hasOwnProperty(oauthValue[0]))
? _tokensObj[oauthValue[0]]
: oauthValue[1];
}
return tokensObj;
};
// Define getter/setter to act on the password manager password value belonging to this account/calendar
for (let oauthValue of OAUTHVALUES) {
Object.defineProperty(oauth, oauthValue[2], {
get: function() {
return this.parseAndSanitizeTokenString(this.authData.password)[oauthValue[0]];
},
set: function(val) {
let tokens = this.parseAndSanitizeTokenString(this.authData.password);
let valueChanged = (val != tokens[oauthValue[0]])
if (valueChanged) {
tokens[oauthValue[0]] = val;
this.authData.updateLoginData(this.authData.user, JSON.stringify(tokens));
}
},
enumerable: true,
});
}
}
return oauth;
},
getOAuthValue: function(currentTokenString, type = "access") {
try {
let tokens = JSON.parse(currentTokenString);
if (tokens.hasOwnProperty(type))
return tokens[type];
} catch (e) {
//NOOP
}
return "";
},
sendRequest: async function (wbxml, command, syncData, allowSoftFail = false) {
let ALLOWED_RETRIES = {
PasswordPrompt : 3,
NetworkError : 1,
}
let rv = {};
let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
let syncState = syncData.getSyncState().state;
for (;;) {
if (rv.errorType) {
let retry = false;
if (ALLOWED_RETRIES[rv.errorType] > 0) {
ALLOWED_RETRIES[rv.errorType]--;
switch (rv.errorType) {
case "PasswordPrompt":
{
if (oauthData) {
oauthData.accessToken = "";
retry = true;
} else {
let authData = eas.network.getAuthData(syncData.accountData);
syncData.setSyncState("passwordprompt");
let promptData = {
windowID: "auth:" + syncData.accountData.accountID,
accountname: syncData.accountData.getAccountProperty("accountname"),
usernameLocked: syncData.accountData.isConnected(),
username: authData.user
}
let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
if (credentials) {
retry = true;
authData.updateLoginData(credentials.username, credentials.password);
}
}
}
break;
case "NetworkError":
{
// Could not connect to server. Can we rerun autodiscover?
// Note: Autodiscover is currently not supported by OAuth
if (syncData.accountData.getAccountProperty( "servertype") == "auto" && !oauthData) {
let errorcode = await eas.network.updateServerConnectionViaAutodiscover(syncData);
console.log("ERR: " + errorcode);
if (errorcode == 200) {
// autodiscover succeeded, retry with new data
retry = true;
} else if (errorcode == 401) {
// manipulate rv to run password prompt
ALLOWED_RETRIES[rv.errorType]++;
rv.errorType = "PasswordPrompt";
rv.errorObj = eas.sync.finish("error", "401");
continue; // with the next loop, skip connection to the server
}
}
}
break;
}
}
if (!retry) throw rv.errorObj;
}
// check OAuth situation before connecting
if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
syncData.setSyncState("oauthprompt");
let _rv = {}
if (!(await oauthData.asyncConnect(_rv))) {
throw eas.sync.finish("error", _rv.error);
}
}
// Return to original syncstate
if (syncState != syncData.getSyncState().state) {
syncData.setSyncState(syncState);
}
rv = await this.sendRequestPromise(wbxml, command, syncData, allowSoftFail);
if (rv.errorType) {
// make sure, there is a valid ALLOWED_RETRIES setting for the returned error
if (rv.errorType && !ALLOWED_RETRIES.hasOwnProperty(rv.errorType)) {
ALLOWED_RETRIES[rv.errorType] = 1;
}
} else {
return rv;
}
}
},
sendRequestPromise: function (wbxml, command, syncData, allowSoftFail = false) {
let msg = "Sending data <" + syncData.getSyncState().state + "> for " + syncData.accountData.getAccountProperty("accountname");
if (syncData.currentFolderData) msg += " (" + syncData.currentFolderData.getFolderProperty("foldername") + ")";
syncData.request = eas.network.logXML(wbxml, msg);
syncData.response = "";
let connection = eas.network.getAuthData(syncData.accountData);
let userAgent = syncData.accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
let deviceType = syncData.accountData.getAccountProperty("devicetype");
let deviceId = syncData.accountData.getAccountProperty("deviceId");
TbSync.dump("Sending (EAS v"+syncData.accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true);
const textEncoder = new TextEncoder();
let encoded = textEncoder.encode(wbxml);
// console.log("wbxml: " + wbxml);
// console.log("byte array: " + encoded);
// console.log("length :" + wbxml.length + " vs " + encoded.byteLength + " vs " + encoded.length);
return new Promise(function(resolve,reject) {
// Create request handler - API changed with TB60 to new XMKHttpRequest()
syncData.req = new XMLHttpRequest();
syncData.req.mozBackgroundRequest = true;
syncData.req.open("POST", eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
syncData.req.overrideMimeType("text/plain");
syncData.req.setRequestHeader("User-Agent", userAgent);
syncData.req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
if (connection.password) {
if (eas.network.getOAuthObj({ accountData: syncData.accountData })) {
syncData.req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(connection.password, "access"));
} else {
syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(connection.user + ':' + connection.password));
}
}
if (syncData.accountData.getAccountProperty("asversion") == "2.5") {
syncData.req.setRequestHeader("MS-ASProtocolVersion", "2.5");
} else {
syncData.req.setRequestHeader("MS-ASProtocolVersion", "14.0");
}
syncData.req.setRequestHeader("Content-Length", encoded.length);
if (syncData.accountData.getAccountProperty("provision")) {
syncData.req.setRequestHeader("X-MS-PolicyKey", syncData.accountData.getAccountProperty("policykey"));
TbSync.dump("PolicyKey used", syncData.accountData.getAccountProperty("policykey"));
}
syncData.req.timeout = eas.Base.getConnectionTimeout();
syncData.req.ontimeout = function () {
if (allowSoftFail) {
resolve("");
} else {
reject(eas.sync.finish("error", "timeout"));
}
};
syncData.req.onerror = function () {
if (allowSoftFail) {
resolve("");
} else {
let error = TbSync.network.createTCPErrorFromFailedXHR(syncData.req) || "networkerror";
let rv = {};
rv.errorObj = eas.sync.finish("error", error);
rv.errorType = "NetworkError";
resolve(rv);
}
};
syncData.req.onload = function() {
let response = syncData.req.responseText;
switch(syncData.req.status) {
case 200: //OK
let msg = "Receiving data <" + syncData.getSyncState().state + "> for " + syncData.accountData.getAccountProperty("accountname");
if (syncData.currentFolderData) msg += " (" + syncData.currentFolderData.getFolderProperty("foldername") + ")";
syncData.response = eas.network.logXML(response, msg);
//What to do on error? IS this an error? Yes!
if (!allowSoftFail && response.length !== 0 && response.substr(0, 4) !== String.fromCharCode(0x03, 0x01, 0x6A, 0x00)) {
TbSync.dump("Recieved Data", "Expecting WBXML but got junk (request status = " + syncData.req.status + ", ready state = " + syncData.req.readyState + "\n>>>>>>>>>>\n" + response + "\n<<<<<<<<<<\n");
reject(eas.sync.finish("warning", "invalid"));
} else {
resolve(response);
}
break;
case 401: // AuthError
case 403: // Forbiddden (some servers send forbidden on AuthError, like Freenet)
let rv = {};
rv.errorObj = eas.sync.finish("error", "401");
rv.errorType = "PasswordPrompt";
resolve(rv);
break;
case 449: // Request for new provision (enable it if needed)
//enable provision
syncData.accountData.setAccountProperty("provision", true);
syncData.accountData.resetAccountProperty("policykey");
reject(eas.sync.finish("resyncAccount", syncData.req.status));
break;
case 451: // Redirect - update host and login manager
let header = syncData.req.getResponseHeader("X-MS-Location");
let newHost = header.slice(header.indexOf("://") + 3, header.indexOf("/M"));
TbSync.dump("redirect (451)", "header: " + header + ", oldHost: " +syncData.accountData.getAccountProperty("host") + ", newHost: " + newHost);
syncData.accountData.setAccountProperty("host", newHost);
reject(eas.sync.finish("resyncAccount", syncData.req.status));
break;
default:
if (allowSoftFail) {
resolve("");
} else {
reject(eas.sync.finish("error", "httperror::" + syncData.req.status));
}
}
};
syncData.req.send(encoded);
});
},
// RESPONSE EVALUATION
logXML : function (wbxml, what) {
let rawxml = eas.wbxmltools.convert2xml(wbxml);
let xml = null;
if (rawxml) {
xml = rawxml.split('><').join('>\n<');
}
//include xml in log, if userdatalevel 2 or greater
if (TbSync.prefs.getIntPref("log.userdatalevel") > 1) {
//log raw wbxml if userdatalevel is 3 or greater
if (TbSync.prefs.getIntPref("log.userdatalevel") > 2) {
let charcodes = [];
for (let i=0; i< wbxml.length; i++) charcodes.push(wbxml.charCodeAt(i).toString(16));
let bytestring = charcodes.join(" ");
TbSync.dump("WBXML: " + what, "\n" + bytestring);
}
if (xml) {
//raw xml is save xml with all special chars in user data encoded by encodeURIComponent - KEEP that in order to be able to analyze logged XML
//let xml = decodeURIComponent(rawxml.split('><').join('>\n<'));
TbSync.dump("XML: " + what, "\n" + xml);
} else {
TbSync.dump("XML: " + what, "\nFailed to convert WBXML to XML!\n");
}
}
return xml;
},
//returns false on parse error and null on empty response (if allowed)
getDataFromResponse: function (wbxml, allowEmptyResponse = !eas.flags.allowEmptyResponse) {
//check for empty wbxml
if (wbxml.length === 0) {
if (allowEmptyResponse) return null;
else throw eas.sync.finish("warning", "empty-response");
}
//convert to save xml (all special chars in user data encoded by encodeURIComponent) and check for parse errors
let xml = eas.wbxmltools.convert2xml(wbxml);
if (xml === false) {
throw eas.sync.finish("warning", "wbxml-parse-error");
}
//retrieve data and check for empty data (all returned data fields are already decoded by decodeURIComponent)
let wbxmlData = eas.xmltools.getDataFromXMLString(xml);
if (wbxmlData === null) {
if (allowEmptyResponse) return null;
else throw eas.sync.finish("warning", "response-contains-no-data");
}
//debug
eas.xmltools.printXmlData(wbxmlData, false); //do not include ApplicationData in log
return wbxmlData;
},
updateSynckey: function (syncData, wbxmlData) {
let synckey = eas.xmltools.getWbxmlDataField(wbxmlData,"Sync.Collections.Collection.SyncKey");
if (synckey) {
// This COULD be a cause of problems...
syncData.synckey = synckey;
syncData.currentFolderData.setFolderProperty("synckey", synckey);
} else {
throw eas.sync.finish("error", "wbxmlmissingfield::Sync.Collections.Collection.SyncKey");
}
},
checkStatus : function (syncData, wbxmlData, path, rootpath="", allowSoftFail = false) {
//path is relative to wbxmlData
//rootpath is the absolute path and must be specified, if wbxml is not the root node and thus path is not the rootpath
let status = eas.xmltools.getWbxmlDataField(wbxmlData,path);
let fullpath = (rootpath=="") ? path : rootpath;
let elements = fullpath.split(".");
let type = elements[0];
//check if fallback to main class status: the answer could just be a "Sync.Status" instead of a "Sync.Collections.Collections.Status"
if (status === false) {
let mainStatus = eas.xmltools.getWbxmlDataField(wbxmlData, type + "." + elements[elements.length-1]);
if (mainStatus === false) {
//both possible status fields are missing, abort
throw eas.sync.finish("warning", "wbxmlmissingfield::" + fullpath, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
} else {
//the alternative status could be extracted
status = mainStatus;
fullpath = type + "." + elements[elements.length-1];
}
}
//check if all is fine (not bad)
if (status == "1") {
return "";
}
TbSync.dump("wbxml status check", type + ": " + fullpath + " = " + status);
//handle errrors based on type
let statusType = type+"."+status;
switch (statusType) {
case "Sync.3": /*
MUST return to SyncKey element value of 0 for the collection. The client SHOULD either delete any items that were added
since the last successful Sync or the client MUST add those items back to the server after completing the full resynchronization
*/
TbSync.eventlog.add("warning", syncData.eventLogInfo, "Forced Folder Resync", "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
syncData.currentFolderData.remove();
throw eas.sync.finish("resyncFolder", statusType);
case "Sync.4": //Malformed request
case "Sync.5": //Temporary server issues or invalid item
case "Sync.6": //Invalid item
case "Sync.8": //Object not found
if (allowSoftFail) return statusType;
throw eas.sync.finish("warning", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
case "Sync.7": //The client has changed an item for which the conflict policy indicates that the server's changes take precedence.
case "Sync.9": //User account could be out of disk space, also send if no write permission (TODO)
return "";
case "FolderDelete.3": // special system folder - fatal error
case "FolderDelete.6": // error on server
throw eas.sync.finish("warning", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
case "FolderDelete.4": // folder does not exist - resync ( we allow delete only if folder is not subscribed )
case "FolderDelete.9": // invalid synchronization key - resync
case "FolderSync.9": // invalid synchronization key - resync
case "Sync.12": // folder hierarchy changed
{
let folders = syncData.accountData.getAllFoldersIncludingCache();
for (let folder of folders) {
folder.remove();
}
// reset account
eas.Base.onEnableAccount(syncData.accountData);
throw eas.sync.finish("resyncAccount", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
}
}
//handle global error (https://msdn.microsoft.com/en-us/library/ee218647(v=exchg.80).aspx)
let descriptions = {};
switch(status) {
case "101": //invalid content
case "102": //invalid wbxml
case "103": //invalid xml
throw eas.sync.finish("error", "global." + status, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
case "109": descriptions["109"]="DeviceTypeMissingOrInvalid";
case "112": descriptions["112"]="ActiveDirectoryAccessDenied";
case "126": descriptions["126"]="UserDisabledForSync";
case "127": descriptions["127"]="UserOnNewMailboxCannotSync";
case "128": descriptions["128"]="UserOnLegacyMailboxCannotSync";
case "129": descriptions["129"]="DeviceIsBlockedForThisUser";
case "130": descriptions["120"]="AccessDenied";
case "131": descriptions["131"]="AccountDisabled";
throw eas.sync.finish("error", "global.clientdenied"+ "::" + status + "::" + descriptions[status]);
case "110": //server error - abort and disable autoSync for 30 minutes
{
let noAutosyncUntil = 30 * 60000 + Date.now();
let humanDate = new Date(noAutosyncUntil).toUTCString();
syncData.accountData.setAccountProperty("noAutosyncUntil", noAutosyncUntil);
throw eas.sync.finish("error", "global." + status, "AutoSync disabled until: " + humanDate + " \n\nRequest:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
/* // reset account
* let folders = syncData.accountData.getAllFoldersIncludingCache();
* for (let folder of folders) {
* folder.remove();
* }
* // reset account
* eas.Base.onEnableAccount(syncData.accountData);
* throw eas.sync.finish("resyncAccount", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
*/
}
case "141": // The device is not provisionable
case "142": // DeviceNotProvisioned
case "143": // PolicyRefresh
case "144": // InvalidPolicyKey
//enable provision
syncData.accountData.setAccountProperty("provision", true);
syncData.accountData.resetAccountProperty("policykey");
throw eas.sync.finish("resyncAccount", statusType);
default:
if (allowSoftFail) return statusType;
throw eas.sync.finish("error", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
}
},
// WBXML COMM STUFF
setDeviceInformation: async function (syncData) {
if (syncData.accountData.getAccountProperty("asversion") == "2.5" || !syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("Settings")) {
return;
}
syncData.setSyncState("prepare.request.setdeviceinfo");
let wbxml = wbxmltools.createWBXML();
wbxml.switchpage("Settings");
wbxml.otag("Settings");
wbxml.otag("DeviceInformation");
wbxml.otag("Set");
wbxml.atag("Model", "Computer");
wbxml.atag("FriendlyName", "TbSync on Device " + syncData.accountData.getAccountProperty("deviceId").substring(4));
wbxml.atag("OS", OS.Constants.Sys.Name);
wbxml.atag("UserAgent", syncData.accountData.getAccountProperty("useragent"));
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
syncData.setSyncState("send.request.setdeviceinfo");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Settings", syncData);
syncData.setSyncState("eval.response.setdeviceinfo");
let wbxmlData = eas.network.getDataFromResponse(response);
eas.network.checkStatus(syncData, wbxmlData,"Settings.Status");
},
getPolicykey: async function (syncData) {
//build WBXML to request provision
syncData.setSyncState("prepare.request.provision");
let wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("Provision");
wbxml.otag("Provision");
wbxml.otag("Policies");
wbxml.otag("Policy");
wbxml.atag("PolicyType", (syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML" );
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
for (let loop=0; loop < 2; loop++) {
syncData.setSyncState("send.request.provision");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Provision", syncData);
syncData.setSyncState("eval.response.provision");
let wbxmlData = eas.network.getDataFromResponse(response);
let policyStatus = eas.xmltools.getWbxmlDataField(wbxmlData, "Provision.Policies.Policy.Status");
let provisionStatus = eas.xmltools.getWbxmlDataField(wbxmlData, "Provision.Status");
if (provisionStatus === false) {
throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Status");
} else if (provisionStatus != "1") {
//dump policy status as well
if (policyStatus) TbSync.dump("PolicyKey","Received policy status: " + policyStatus);
throw eas.sync.finish("error", "provision::" + provisionStatus);
}
//reaching this point: provision status was ok
let policykey = eas.xmltools.getWbxmlDataField(wbxmlData,"Provision.Policies.Policy.PolicyKey");
switch (policyStatus) {
case false:
throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Policies.Policy.Status");
case "2":
//server does not have a policy for this device: disable provisioning
syncData.accountData.setAccountProperty("provision", false)
syncData.accountData.resetAccountProperty("policykey");
throw eas.sync.finish("resyncAccount", "NoPolicyForThisDevice");
case "1":
if (policykey === false) {
throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Policies.Policy.PolicyKey");
}
TbSync.dump("PolicyKey","Received policykey (" + loop + "): " + policykey);
syncData.accountData.setAccountProperty("policykey", policykey);
break;
default:
throw eas.sync.finish("error", "policy." + policyStatus);
}
//build WBXML to acknowledge provision
syncData.setSyncState("prepare.request.provision");
wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("Provision");
wbxml.otag("Provision");
wbxml.otag("Policies");
wbxml.otag("Policy");
wbxml.atag("PolicyType",(syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML" );
wbxml.atag("PolicyKey", policykey);
wbxml.atag("Status", "1");
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
//this wbxml will be used by Send at the top of this loop
}
},
getSynckey: async function (syncData) {
syncData.setSyncState("prepare.request.synckey");
//build WBXML to request a new syncKey
let wbxml = eas.wbxmltools.createWBXML();
wbxml.otag("Sync");
wbxml.otag("Collections");
wbxml.otag("Collection");
if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
wbxml.atag("SyncKey","0");
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
syncData.setSyncState("send.request.synckey");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Sync", syncData);
syncData.setSyncState("eval.response.synckey");
// get data from wbxml response
let wbxmlData = eas.network.getDataFromResponse(response);
//check status
eas.network.checkStatus(syncData, wbxmlData,"Sync.Collections.Collection.Status");
//update synckey
eas.network.updateSynckey(syncData, wbxmlData);
},
getItemEstimate: async function (syncData) {
syncData.progressData.reset();
if (!syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("GetItemEstimate")) {
return; //do not throw, this is optional
}
syncData.setSyncState("prepare.request.estimate");
// BUILD WBXML
let wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("GetItemEstimate");
wbxml.otag("GetItemEstimate");
wbxml.otag("Collections");
wbxml.otag("Collection");
if (syncData.accountData.getAccountProperty("asversion") == "2.5") { //got this order for 2.5 directly from Microsoft support
wbxml.atag("Class", syncData.type); //only 2.5
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.switchpage("AirSync");
// required !
// https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ascmd/ffbefa62-e315-40b9-9cc6-f8d74b5f65d4
if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
else wbxml.atag("FilterType", "0"); // we may filter incomplete tasks
wbxml.atag("SyncKey", syncData.synckey);
wbxml.switchpage("GetItemEstimate");
} else { //14.0
wbxml.switchpage("AirSync");
wbxml.atag("SyncKey", syncData.synckey);
wbxml.switchpage("GetItemEstimate");
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.switchpage("AirSync");
wbxml.otag("Options");
// optional
if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
wbxml.atag("Class", syncData.type);
wbxml.ctag();
wbxml.switchpage("GetItemEstimate");
}
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
//SEND REQUEST
syncData.setSyncState("send.request.estimate");
let response = await eas.network.sendRequest(wbxml.getBytes(), "GetItemEstimate", syncData, /* allowSoftFail */ true);
//VALIDATE RESPONSE
syncData.setSyncState("eval.response.estimate");
// get data from wbxml response, some servers send empty response if there are no changes, which is not an error
let wbxmlData = eas.network.getDataFromResponse(response, eas.flags.allowEmptyResponse);
if (wbxmlData === null) return;
let status = eas.xmltools.getWbxmlDataField(wbxmlData, "GetItemEstimate.Response.Status");
let estimate = eas.xmltools.getWbxmlDataField(wbxmlData, "GetItemEstimate.Response.Collection.Estimate");
if (status && status == "1") { //do not throw on error, with EAS v2.5 I get error 2 for tasks and calendars ???
syncData.progressData.reset(0, estimate);
}
},
getUserInfo: async function (syncData) {
if (!syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("Settings")) {
return;
}
syncData.setSyncState("prepare.request.getuserinfo");
let wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("Settings");
wbxml.otag("Settings");
wbxml.otag("UserInformation");
wbxml.atag("Get");
wbxml.ctag();
wbxml.ctag();
syncData.setSyncState("send.request.getuserinfo");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Settings", syncData);
syncData.setSyncState("eval.response.getuserinfo");
let wbxmlData = eas.network.getDataFromResponse(response);
eas.network.checkStatus(syncData, wbxmlData,"Settings.Status");
},
// SEARCH
getSearchResults: async function (accountData, currentQuery) {
let _wbxml = eas.wbxmltools.createWBXML();
_wbxml.switchpage("Search");
_wbxml.otag("Search");
_wbxml.otag("Store");
_wbxml.atag("Name", "GAL");
_wbxml.atag("Query", currentQuery);
_wbxml.otag("Options");
_wbxml.atag("Range", "0-99"); //Z-Push needs a Range
//Not valid for GAL: https://msdn.microsoft.com/en-us/library/gg675461(v=exchg.80).aspx
//_wbxml.atag("DeepTraversal");
//_wbxml.atag("RebuildResults");
_wbxml.ctag();
_wbxml.ctag();
_wbxml.ctag();
let wbxml = _wbxml.getBytes();
eas.network.logXML(wbxml, "Send (GAL Search)");
let command = "Search";
let authData = eas.network.getAuthData(accountData);
let oauthData = eas.network.getOAuthObj({ accountData });
let userAgent = accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
let deviceType = accountData.getAccountProperty("devicetype");
let deviceId = accountData.getAccountProperty("deviceId");
TbSync.dump("Sending (EAS v" + accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true);
for (let i=0; i < 2; i++) {
// check OAuth situation before connecting
if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
let _rv = {}
if (!(await oauthData.asyncConnect(_rv))) {
throw eas.sync.finish("error", _rv.error);
}
}
try {
let response = await new Promise(function(resolve, reject) {
// Create request handler - API changed with TB60 to new XMKHttpRequest()
let req = new XMLHttpRequest();
req.mozBackgroundRequest = true;
req.open("POST", eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
req.overrideMimeType("text/plain");
req.setRequestHeader("User-Agent", userAgent);
req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
if (authData.password) {
if (eas.network.getOAuthObj({ accountData })) {
req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
} else {
req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
}
}
if (accountData.getAccountProperty("asversion") == "2.5") {
req.setRequestHeader("MS-ASProtocolVersion", "2.5");
} else {
req.setRequestHeader("MS-ASProtocolVersion", "14.0");
}
req.setRequestHeader("Content-Length", wbxml.length);
if (accountData.getAccountProperty("provision")) {
req.setRequestHeader("X-MS-PolicyKey", accountData.getAccountProperty("policykey"));
TbSync.dump("PolicyKey used", accountData.getAccountProperty("policykey"));
}
req.timeout = eas.Base.getConnectionTimeout();
req.ontimeout = function () {
reject("GAL Search timeout");
};
req.onerror = function () {
reject("GAL Search Error");
};
req.onload = function() {
let response = req.responseText;
switch(req.status) {
case 200: //OK
eas.network.logXML(response, "Received (GAL Search");
//What to do on error? IS this an error? Yes!
if (response.length !== 0 && response.substr(0, 4) !== String.fromCharCode(0x03, 0x01, 0x6A, 0x00)) {
TbSync.dump("Recieved Data", "Expecting WBXML but got junk (request status = " + req.status + ", ready state = " + req.readyState + "\n>>>>>>>>>>\n" + response + "\n<<<<<<<<<<\n");
reject("GAL Search Response Invalid");
} else {
resolve(response);
}
break;
case 401: // bad auth
resolve("401");
break;
default:
reject("GAL Search Failed: " + req.status);
}
};
req.send(wbxml);
});
if (response === "401") {
// try to recover from bad auth via token refresh
if (oauthData) {
oauthData.accessToken = "";
continue;
}
}
return response;
} catch (e) {
Components.utils.reportError(e);
return;
}
}
},
// OPTIONS
getServerOptions: async function (syncData) {
syncData.setSyncState("prepare.request.options");
let authData = eas.network.getAuthData(syncData.accountData);
let userAgent = syncData.accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
TbSync.dump("Sending", "OPTIONS " + eas.network.getEasURL(syncData.accountData));
let allowedRetries = 5;
let retry;
let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
do {
retry = false;
// Check OAuth situation before connecting
if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
let _rv = {};
syncData.setSyncState("oauthprompt");
if (!(await oauthData.asyncConnect(_rv))) {
throw eas.sync.finish("error", _rv.error);
}
}
let result = await new Promise(function(resolve,reject) {
syncData.req = new XMLHttpRequest();
syncData.req.mozBackgroundRequest = true;
syncData.req.open("OPTIONS", eas.network.getEasURL(syncData.accountData), true);
syncData.req.overrideMimeType("text/plain");
syncData.req.setRequestHeader("User-Agent", userAgent);
if (authData.password) {
if (eas.network.getOAuthObj({ accountData: syncData.accountData })) {
syncData.req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
} else {
syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
}
}
syncData.req.timeout = eas.Base.getConnectionTimeout();
syncData.req.ontimeout = function () {
resolve();
};
syncData.req.onerror = function () {
let responseData = {};
responseData["MS-ASProtocolVersions"] = syncData.req.getResponseHeader("MS-ASProtocolVersions");
responseData["MS-ASProtocolCommands"] = syncData.req.getResponseHeader("MS-ASProtocolCommands");
TbSync.dump("EAS OPTIONS with response (status: "+syncData.req.status+")", "\n" +
"responseText: " + syncData.req.responseText + "\n" +
"responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"]+"\n" +
"responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
resolve();
};
syncData.req.onload = function() {
syncData.setSyncState("eval.request.options");
let responseData = {};
switch(syncData.req.status) {
case 401: // AuthError
let rv = {};
rv.errorObj = eas.sync.finish("error", "401");
rv.errorType = "PasswordPrompt";
resolve(rv);
break;
case 200:
responseData["MS-ASProtocolVersions"] = syncData.req.getResponseHeader("MS-ASProtocolVersions");
responseData["MS-ASProtocolCommands"] = syncData.req.getResponseHeader("MS-ASProtocolCommands");
TbSync.dump("EAS OPTIONS with response (status: 200)", "\n" +
"responseText: " + syncData.req.responseText + "\n" +
"responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"]+"\n" +
"responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
if (responseData && responseData["MS-ASProtocolCommands"] && responseData["MS-ASProtocolVersions"]) {
syncData.accountData.setAccountProperty("allowedEasCommands", responseData["MS-ASProtocolCommands"]);
syncData.accountData.setAccountProperty("allowedEasVersions", responseData["MS-ASProtocolVersions"]);
syncData.accountData.setAccountProperty("lastEasOptionsUpdate", Date.now());
}
resolve();
break;
default:
resolve();
break;
}
};
syncData.setSyncState("send.request.options");
syncData.req.send();
});
if (result && result.hasOwnProperty("errorType") && result.errorType == "PasswordPrompt") {
if (allowedRetries > 0) {
if (oauthData) {
oauthData.accessToken = "";
retry = true;
} else {
syncData.setSyncState("passwordprompt");
let authData = eas.network.getAuthData(syncData.accountData);
let promptData = {
windowID: "auth:" + syncData.accountData.accountID,
accountname: syncData.accountData.getAccountProperty("accountname"),
usernameLocked: syncData.accountData.isConnected(),
username: authData.user
}
let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
if (credentials) {
authData.updateLoginData(credentials.username, credentials.password);
retry = true;
}
}
}
if (!retry) {
throw result.errorObj;
}
}
allowedRetries--;
} while (retry);
},
// AUTODISCOVER
updateServerConnectionViaAutodiscover: async function (syncData) {
syncData.setSyncState("prepare.request.autodiscover");
let authData = eas.network.getAuthData(syncData.accountData);
syncData.setSyncState("send.request.autodiscover");
let result = await eas.network.getServerConnectionViaAutodiscover(authData.user, authData.password, 30*1000);
syncData.setSyncState("eval.response.autodiscover");
if (result.errorcode == 200) {
//update account
syncData.accountData.setAccountProperty("host", eas.network.stripAutodiscoverUrl(result.server));
syncData.accountData.setAccountProperty("user", result.user);
syncData.accountData.setAccountProperty("https", (result.server.substring(0,5) == "https"));
}
return result.errorcode;
},
stripAutodiscoverUrl: function(url) {
let u = url;
while (u.endsWith("/")) { u = u.slice(0,-1); }
if (u.endsWith("/Microsoft-Server-ActiveSync")) u=u.slice(0, -28);
else TbSync.dump("Received non-standard EAS url via autodiscover:", url);
return u.split("//")[1]; //cut off protocol
},
getServerConnectionViaAutodiscover : async function (user, password, maxtimeout) {
let urls = [];
let parts = user.split("@");
urls.push({"url":"http://autodiscover."+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
urls.push({"url":"http://"+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
urls.push({"url":"http://autodiscover."+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
urls.push({"url":"http://"+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
urls.push({"url":"https://autodiscover."+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
urls.push({"url":"https://"+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
urls.push({"url":"https://autodiscover."+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
urls.push({"url":"https://"+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
let requests = [];
let responses = []; //array of objects {url, error, server}
for (let i=0; i< urls.length; i++) {
await TbSync.tools.sleep(200);
requests.push( eas.network.getServerConnectionViaAutodiscoverRedirectWrapper(urls[i].url, urls[i].user, password, maxtimeout) );
}
try {
responses = await Promise.all(requests);
} catch (e) {
responses.push(e.result); //this is actually a success, see return value of getServerConnectionViaAutodiscoverRedirectWrapper()
}
let result;
let log = [];
for (let r=0; r < responses.length; r++) {
log.push("* "+responses[r].url+" @ " + responses[r].user +" : " + (responses[r].server ? responses[r].server : responses[r].error));
if (responses[r].server) {
result = {"server": responses[r].server, "user": responses[r].user, "error": "", "errorcode": 200};
break;
}
if (responses[r].error == 403 || responses[r].error == 401) {
//we could still find a valid server, so just store this state
result = {"server": "", "user": responses[r].user, "errorcode": responses[r].error, "error": TbSync.getString("status." + responses[r].error, "eas")};
}
}
//this is only reached on fail, if no result defined yet, use general error
if (!result) {
result = {"server": "", "user": user, "error": TbSync.getString("autodiscover.Failed","eas").replace("##user##", user), "errorcode": 503};
}
TbSync.eventlog.add("error", new TbSync.EventLogInfo("eas"), result.error, log.join("\n"));
return result;
},
getServerConnectionViaAutodiscoverRedirectWrapper : async function (url, user, password, maxtimeout) {
//using HEAD to find URL redirects until response URL no longer changes
// * XHR should follow redirects transparently, but that does not always work, POST data could get lost, so we
// * need to find the actual POST candidates (example: outlook.de accounts)
let result = {};
let method = "HEAD";
let connection = { url, user };
do {
await TbSync.tools.sleep(200);
result = await eas.network.getServerConnectionViaAutodiscoverRequest(method, connection, password, maxtimeout);
method = "";
if (result.error == "redirect found") {
TbSync.dump("EAS autodiscover URL redirect", "\n" + connection.url + " @ " + connection.user + " => \n" + result.url + " @ " + result.user);
connection.url = result.url;
connection.user = result.user;
method = "HEAD";
} else if (result.error == "POST candidate found") {
method = "POST";
}
} while (method);
//invert reject and resolve, so we exit the promise group on success right away
if (result.server) {
let e = new Error("Not an error (early exit from promise group)");
e.result = result;
throw e;
} else {
return result;
}
},
getServerConnectionViaAutodiscoverRequest: function (method, connection, password, maxtimeout) {
TbSync.dump("Querry EAS autodiscover URL", connection.url + " @ " + connection.user);
return new Promise(function(resolve,reject) {
let xml = '\r\n';
xml += '\r\n';
xml += '\r\n';
xml += '' + connection.user + '\r\n';
xml += 'http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006\r\n';
xml += '\r\n';
xml += '\r\n';
let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
// Create request handler - API changed with TB60 to new XMKHttpRequest()
let req = new XMLHttpRequest();
req.mozBackgroundRequest = true;
req.open(method, connection.url, true);
req.timeout = maxtimeout;
req.setRequestHeader("User-Agent", userAgent);
let secure = (connection.url.substring(0,8).toLowerCase() == "https://");
if (method == "POST") {
req.setRequestHeader("Content-Length", xml.length);
req.setRequestHeader("Content-Type", "text/xml");
if (secure && password) {
// OAUTH accounts cannot authenticate against the standard discovery services
// updateServerConnectionViaAutodiscover() is not passing them on
req.setRequestHeader("Authorization", "Basic " + TbSync.tools.b64encode(connection.user + ":" + password));
}
}
req.ontimeout = function () {
TbSync.dump("EAS autodiscover with timeout", "\n" + connection.url + " => \n" + req.responseURL);
resolve({"url":req.responseURL, "error":"timeout", "server":"", "user":connection.user});
};
req.onerror = function () {
let error = TbSync.network.createTCPErrorFromFailedXHR(req);
if (!error) error = req.responseText;
TbSync.dump("EAS autodiscover with error ("+error+")", "\n" + connection.url + " => \n" + req.responseURL);
resolve({"url":req.responseURL, "error":error, "server":"", "user":connection.user});
};
req.onload = function() {
//initiate rerun on redirects
if (req.responseURL != connection.url) {
resolve({"url":req.responseURL, "error":"redirect found", "server":"", "user":connection.user});
return;
}
//initiate rerun on HEAD request without redirect (rerun and do a POST on this)
if (method == "HEAD") {
resolve({"url":req.responseURL, "error":"POST candidate found", "server":"", "user":connection.user});
return;
}
//ignore POST without autherization (we just do them to get redirect information)
if (!secure) {
resolve({"url":req.responseURL, "error":"unsecure POST", "server":"", "user":connection.user});
return;
}
//evaluate secure POST requests which have not been redirected
TbSync.dump("EAS autodiscover POST with status (" + req.status + ")", "\n" + connection.url + " => \n" + req.responseURL + "\n[" + req.responseText + "]");
if (req.status === 200) {
let data = null;
// getDataFromXMLString may throw an error which cannot be catched outside onload,
// because we are in an async callback of the surrounding Promise
// Alternatively we could just return the responseText and do any data analysis outside of the Promise
try {
data = eas.xmltools.getDataFromXMLString(req.responseText);
} catch (e) {
resolve({"url":req.responseURL, "error":"bad response", "server":"", "user":connection.user});
return;
}
if (!(data === null) && data.Autodiscover && data.Autodiscover.Response && data.Autodiscover.Response.Action) {
// "Redirect" or "Settings" are possible
if (data.Autodiscover.Response.Action.Redirect) {
// redirect, start again with new user
let newuser = action.Redirect;
resolve({"url":req.responseURL, "error":"redirect found", "server":"", "user":newuser});
} else if (data.Autodiscover.Response.Action.Settings) {
// get server settings
let server = eas.xmltools.nodeAsArray(data.Autodiscover.Response.Action.Settings.Server);
for (let count = 0; count < server.length; count++) {
if (server[count].Type == "MobileSync" && server[count].Url) {
resolve({"url":req.responseURL, "error":"", "server":server[count].Url, "user":connection.user});
return;
}
}
}
} else {
resolve({"url":req.responseURL, "error":"invalid", "server":"", "user":connection.user});
}
} else {
resolve({"url":req.responseURL, "error":req.status, "server":"", "user":connection.user});
}
};
if (method == "HEAD") req.send();
else req.send(xml);
});
},
getServerConnectionViaAutodiscoverV2JsonRequest: function (url, maxtimeout) {
TbSync.dump("Querry EAS autodiscover V2 URL", url);
return new Promise(function(resolve,reject) {
let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
// Create request handler - API changed with TB60 to new XMKHttpRequest()
let req = new XMLHttpRequest();
req.mozBackgroundRequest = true;
req.open("GET", url, true);
req.timeout = maxtimeout;
req.setRequestHeader("User-Agent", userAgent);
req.ontimeout = function () {
TbSync.dump("EAS autodiscover V2 with timeout", "\n" + url + " => \n" + req.responseURL);
resolve({"url":req.responseURL, "error":"timeout", "server":""});
};
req.onerror = function () {
let error = TbSync.network.createTCPErrorFromFailedXHR(req);
if (!error) error = req.responseText;
TbSync.dump("EAS autodiscover V2 with error ("+error+")", "\n" + url + " => \n" + req.responseURL);
resolve({"url":req.responseURL, "error":error, "server":""});
};
req.onload = function() {
if (req.status === 200) {
let data = JSON.parse(req.responseText);
if (data && data.Url) {
resolve({"url":req.responseURL, "error":"", "server": eas.network.stripAutodiscoverUrl(data.Url)});
} else {
resolve({"url":req.responseURL, "error":"invalid", "server":""});
}
return;
}
resolve({"url":req.responseURL, "error":req.status, "server":""});
};
req.send();
});
}
}
EAS-4-TbSync-1.12/content/includes/sync.js 0000664 0000000 0000000 00000212507 13623463701 0020214 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
// - https://dxr.mozilla.org/comm-central/source/calendar/base/public/calIEvent.idl
// - https://dxr.mozilla.org/comm-central/source/calendar/base/public/calIItemBase.idl
// - https://dxr.mozilla.org/comm-central/source/calendar/base/public/calICalendar.idl
// - https://dxr.mozilla.org/comm-central/source/calendar/base/modules/calAsyncUtils.jsm
// https://msdn.microsoft.com/en-us/library/dd299454(v=exchg.80).aspx
var sync = {
finish: function (aStatus = "", msg = "", details = "") {
let status = TbSync.StatusData.SUCCESS
switch (aStatus) {
case "":
case "ok":
status = TbSync.StatusData.SUCCESS;
break;
case "info":
status = TbSync.StatusData.INFO;
break;
case "resyncAccount":
status = TbSync.StatusData.ACCOUNT_RERUN;
break;
case "resyncFolder":
status = TbSync.StatusData.FOLDER_RERUN;
break;
case "warning":
status = TbSync.StatusData.WARNING;
break;
case "error":
status = TbSync.StatusData.ERROR;
break;
default:
console.log("TbSync/EAS: Unknown status <"+aStatus+">");
status = TbSync.StatusData.ERROR;
break;
}
let e = new Error();
e.name = "eas4tbsync";
e.message = status.toUpperCase() + ": " + msg.toString() + " (" + details.toString() + ")";
e.statusData = new TbSync.StatusData(status, msg.toString(), details.toString());
return e;
},
resetFolderSyncInfo: function (folderData) {
folderData.resetFolderProperty("synckey");
folderData.resetFolderProperty("lastsynctime");
},
// update folders avail on server and handle added, removed and renamed
// folders
folderList: async function(syncData) {
//should we recheck options/commands? Always check, if we have no info about asversion!
if (syncData.accountData.getAccountProperty("asversion", "") == "" || (Date.now() - syncData.accountData.getAccountProperty("lastEasOptionsUpdate")) > 86400000 ) {
await eas.network.getServerOptions(syncData);
}
//only update the actual used asversion, if we are currently not connected or it has not yet been set
if (syncData.accountData.getAccountProperty("asversion", "") == "" || !syncData.accountData.isConnected()) {
//eval the currently in the UI selected EAS version
let asversionselected = syncData.accountData.getAccountProperty("asversionselected");
let allowedVersionsString = syncData.accountData.getAccountProperty("allowedEasVersions").trim();
let allowedVersionsArray = allowedVersionsString.split(",");
if (asversionselected == "auto") {
if (allowedVersionsArray.includes("14.0")) syncData.accountData.setAccountProperty("asversion", "14.0");
else if (allowedVersionsArray.includes("2.5")) syncData.accountData.setAccountProperty("asversion", "2.5");
else if (allowedVersionsString == "") {
throw eas.sync.finish("error", "InvalidServerOptions");
} else {
throw eas.sync.finish("error", "nosupportedeasversion::"+allowedVersionsArray.join(", "));
}
} else if (allowedVersionsString != "" && !allowedVersionsArray.includes(asversionselected)) {
throw eas.sync.finish("error", "notsupportedeasversion::"+asversionselected+"::"+allowedVersionsArray.join(", "));
} else {
//just use the value set by the user
syncData.accountData.setAccountProperty("asversion", asversionselected);
}
}
//do we need to get a new policy key?
if (syncData.accountData.getAccountProperty("provision") && syncData.accountData.getAccountProperty("policykey") == "0") {
await eas.network.getPolicykey(syncData);
}
//set device info
await eas.network.setDeviceInformation(syncData);
syncData.setSyncState("prepare.request.folders");
let foldersynckey = syncData.accountData.getAccountProperty("foldersynckey");
//build WBXML to request foldersync
let wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("FolderHierarchy");
wbxml.otag("FolderSync");
wbxml.atag("SyncKey", foldersynckey);
wbxml.ctag();
syncData.setSyncState("send.request.folders");
let response = await eas.network.sendRequest(wbxml.getBytes(), "FolderSync", syncData);
syncData.setSyncState("eval.response.folders");
let wbxmlData = eas.network.getDataFromResponse(response);
eas.network.checkStatus(syncData, wbxmlData,"FolderSync.Status");
let synckey = eas.xmltools.getWbxmlDataField(wbxmlData,"FolderSync.SyncKey");
if (synckey) {
syncData.accountData.setAccountProperty("foldersynckey", synckey);
} else {
throw eas.sync.finish("error", "wbxmlmissingfield::FolderSync.SyncKey");
}
// If we reach this point, wbxmlData contains FolderSync node,
// so the next "if" will not fail with an javascript error, no need
// to use save getWbxmlDataField function.
// Are there any changes in folder hierarchy?
if (wbxmlData.FolderSync.Changes) {
// Looking for additions.
let add = eas.xmltools.nodeAsArray(wbxmlData.FolderSync.Changes.Add);
for (let count = 0; count < add.length; count++) {
// Only add allowed folder types to DB (include trash(4), so we can find trashed folders.
if (!["9","14","8","13","7","15", "4"].includes(add[count].Type))
continue;
let existingFolder = syncData.accountData.getFolder("serverID", add[count].ServerId);
if (existingFolder) {
// Server has send us an ADD for a folder we alreay have, treat as update.
existingFolder.setFolderProperty("foldername", add[count].DisplayName);
existingFolder.setFolderProperty("type", add[count].Type);
existingFolder.setFolderProperty("parentID", add[count].ParentId);
} else {
// Create folder obj for new folder settings.
let newFolder = syncData.accountData.createNewFolder();
switch (add[count].Type) {
case "9": // contact
case "14":
newFolder.setFolderProperty("targetType", "addressbook");
break;
case "8": // event
case "13":
newFolder.setFolderProperty("targetType", "calendar");
break;
case "7": // todo
case "15":
newFolder.setFolderProperty("targetType", "calendar");
break;
default:
newFolder.setFolderProperty("targetType", "unknown type ("+add[count].Type+")");
break;
}
newFolder.setFolderProperty("serverID", add[count].ServerId);
newFolder.setFolderProperty("foldername", add[count].DisplayName);
newFolder.setFolderProperty("type", add[count].Type);
newFolder.setFolderProperty("parentID", add[count].ParentId);
// Do we have a cached folder?
let cachedFolderData = syncData.accountData.getFolderFromCache("serverID", add[count].ServerId);
if (cachedFolderData) {
// Copy fields from cache which we want to re-use.
newFolder.setFolderProperty("targetColor", cachedFolderData.getFolderProperty("targetColor"));
newFolder.setFolderProperty("targetName", cachedFolderData.getFolderProperty("targetName"));
newFolder.setFolderProperty("downloadonly", cachedFolderData.getFolderProperty("downloadonly"));
}
}
}
// Looking for updates.
let update = eas.xmltools.nodeAsArray(wbxmlData.FolderSync.Changes.Update);
for (let count = 0; count < update.length; count++) {
let existingFolder = syncData.accountData.getFolder("serverID", update[count].ServerId);
if (existingFolder) {
// Update folder.
existingFolder.setFolderProperty("foldername", update[count].DisplayName);
existingFolder.setFolderProperty("type", update[count].Type);
existingFolder.setFolderProperty("parentID", update[count].ParentId);
}
}
// Looking for deletes. Do not delete the targets,
// but keep them as stale/unconnected elements.
let del = eas.xmltools.nodeAsArray(wbxmlData.FolderSync.Changes.Delete);
for (let count = 0; count < del.length; count++) {
let existingFolder = syncData.accountData.getFolder("serverID", del[count].ServerId);
if (existingFolder) {
existingFolder.remove("[deleted on server]");
}
}
}
},
deleteFolder: async function (syncData) {
if (!syncData.currentFolderData) {
return;
}
if (!syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("FolderDelete")) {
throw eas.sync.finish("error", "notsupported::FolderDelete");
}
syncData.setSyncState("prepare.request.deletefolder");
let foldersynckey = syncData.accountData.getAccountProperty("foldersynckey");
//request foldersync
let wbxml = eas.wbxmltools.createWBXML();
wbxml.switchpage("FolderHierarchy");
wbxml.otag("FolderDelete");
wbxml.atag("SyncKey", foldersynckey);
wbxml.atag("ServerId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.ctag();
syncData.setSyncState("send.request.deletefolder");
let response = await eas.network.sendRequest(wbxml.getBytes(), "FolderDelete", syncData);
syncData.setSyncState("eval.response.deletefolder");
let wbxmlData = eas.network.getDataFromResponse(response);
eas.network.checkStatus(syncData, wbxmlData,"FolderDelete.Status");
let synckey = eas.xmltools.getWbxmlDataField(wbxmlData,"FolderDelete.SyncKey");
if (synckey) {
syncData.accountData.setAccountProperty("foldersynckey", synckey);
syncData.currentFolderData.remove();
} else {
throw eas.sync.finish("error", "wbxmlmissingfield::FolderDelete.SyncKey");
}
},
singleFolder: async function (syncData) {
// add target to syncData (getTarget() will throw "nolightning" if lightning missing)
try {
// accessing the target for the first time will check if it is avail and if not will create it (if possible)
syncData.target = await syncData.currentFolderData.targetData.getTarget();
} catch (e) {
Components.utils.reportError(e);
throw eas.sync.finish("warning", e.message);
}
//get syncData type, which is also used in WBXML for the CLASS element
syncData.type = null;
switch (syncData.currentFolderData.getFolderProperty("type")) {
case "9": //contact
case "14":
syncData.type = "Contacts";
break;
case "8": //event
case "13":
syncData.type = "Calendar";
break;
case "7": //todo
case "15":
syncData.type = "Tasks";
break;
default:
throw eas.sync.finish("info", "skipped");
break;
}
syncData.setSyncState("preparing");
//get synckey if needed
syncData.synckey = syncData.currentFolderData.getFolderProperty("synckey");
if (syncData.synckey == "") {
await eas.network.getSynckey(syncData);
}
//sync folder
syncData.timeOfLastSync = syncData.currentFolderData.getFolderProperty( "lastsynctime") / 1000;
syncData.timeOfThisSync = (Date.now() / 1000) - 1;
let lightningBatch = false;
let lightningReadOnly = "";
let error = null;
// We ned to intercept any throw error, because lightning needs a few operations after sync finished
try {
switch (syncData.type) {
case "Contacts":
await eas.sync.easFolder(syncData);
break;
case "Calendar":
case "Tasks":
//save current value of readOnly (or take it from the setting)
lightningReadOnly = syncData.target.calendar.getProperty("readOnly") || syncData.currentFolderData.getFolderProperty( "downloadonly");
syncData.target.calendar.setProperty("readOnly", false);
lightningBatch = true;
syncData.target.calendar.startBatch();
await eas.sync.easFolder(syncData);
break;
}
} catch (report) {
error = report;
}
if (lightningBatch) {
syncData.target.calendar.endBatch();
syncData.target.calendar.setProperty("readOnly", lightningReadOnly);
}
if (error) throw error;
},
// ---------------------------------------------------------------------------
// MAIN FUNCTIONS TO SYNC AN EAS FOLDER
// ---------------------------------------------------------------------------
easFolder: async function (syncData) {
syncData.progressData.reset();
if (syncData.currentFolderData.getFolderProperty("downloadonly")) {
await eas.sync.revertLocalChanges(syncData);
}
await eas.network.getItemEstimate (syncData);
await eas.sync.requestRemoteChanges (syncData);
if (!syncData.currentFolderData.getFolderProperty("downloadonly")) {
await eas.sync.sendLocalChanges(syncData);
}
},
requestRemoteChanges: async function (syncData) {
do {
syncData.setSyncState("prepare.request.remotechanges");
syncData.request = "";
syncData.response = "";
// BUILD WBXML
let wbxml = eas.wbxmltools.createWBXML();
wbxml.otag("Sync");
wbxml.otag("Collections");
wbxml.otag("Collection");
if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
wbxml.atag("SyncKey", syncData.synckey);
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.atag("DeletesAsMoves");
wbxml.atag("GetChanges");
wbxml.atag("WindowSize", eas.prefs.getIntPref("maxitems").toString());
if (syncData.accountData.getAccountProperty("asversion") != "2.5") {
wbxml.otag("Options");
if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
wbxml.atag("Class", syncData.type);
wbxml.switchpage("AirSyncBase");
wbxml.otag("BodyPreference");
wbxml.atag("Type", "1");
wbxml.ctag();
wbxml.switchpage("AirSync");
wbxml.ctag();
} else if (syncData.type == "Calendar") { //in 2.5 we only send it to filter Calendar
wbxml.otag("Options");
wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
wbxml.ctag();
}
wbxml.ctag();
wbxml.ctag();
wbxml.ctag();
//SEND REQUEST
syncData.setSyncState("send.request.remotechanges");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Sync", syncData);
//VALIDATE RESPONSE
// get data from wbxml response, some servers send empty response if there are no changes, which is not an error
let wbxmlData = eas.network.getDataFromResponse(response, eas.flags.allowEmptyResponse);
if (wbxmlData === null) return;
//check status, throw on error
eas.network.checkStatus(syncData, wbxmlData,"Sync.Collections.Collection.Status");
//PROCESS COMMANDS
await eas.sync.processCommands(wbxmlData, syncData);
//Update count in UI
syncData.setSyncState("eval.response.remotechanges");
//update synckey
eas.network.updateSynckey(syncData, wbxmlData);
if (!eas.xmltools.hasWbxmlDataField(wbxmlData,"Sync.Collections.Collection.MoreAvailable")) {
//Feedback from users: They want to see the final count
await TbSync.tools.sleep(100);
return;
}
} while (true);
},
sendLocalChanges: async function (syncData) {
let maxnumbertosend = eas.prefs.getIntPref("maxitems");
syncData.progressData.reset(0, syncData.target.getItemsFromChangeLog().length);
//keep track of failed items
syncData.failedItems = [];
let done = false;
let numberOfItemsToSend = maxnumbertosend;
do {
syncData.setSyncState("prepare.request.localchanges");
syncData.request = "";
syncData.response = "";
//get changed items from ChangeLog
let changes = syncData.target.getItemsFromChangeLog(numberOfItemsToSend);
let c=0;
let e=0;
//keep track of send items during this request
let changedItems = [];
let addedItems = {};
let sendItems = [];
// BUILD WBXML
let wbxml = eas.wbxmltools.createWBXML();
wbxml.otag("Sync");
wbxml.otag("Collections");
wbxml.otag("Collection");
if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
wbxml.atag("SyncKey", syncData.synckey);
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.otag("Commands");
for (let i=0; i 0) { //if there was at least one actual local change, send request
//SEND REQUEST & VALIDATE RESPONSE
syncData.setSyncState("send.request.localchanges");
let response = await eas.network.sendRequest(wbxml.getBytes(), "Sync", syncData);
syncData.setSyncState("eval.response.localchanges");
//get data from wbxml response
let wbxmlData = eas.network.getDataFromResponse(response);
//check status and manually handle error states which support softfails
let errorcause = eas.network.checkStatus(syncData, wbxmlData, "Sync.Collections.Collection.Status", "", true);
switch (errorcause) {
case "":
break;
case "Sync.4": //Malformed request
case "Sync.6": //Invalid item
//some servers send a global error - to catch this, we reduce the number of items we send to the server
if (sendItems.length == 1) {
//the request contained only one item, so we know which one failed
if (sendItems[0].type == "deleted_by_user") {
//we failed to delete an item, discard and place message in log
syncData.target.removeItemFromChangeLog(sendItems[0].id);
TbSync.eventlog.add("warning", syncData.eventLogInfo, "ErrorOnDelete::"+sendItems[0].id);
} else {
let foundItem = await syncData.target.getItem(sendItems[0].id);
if (foundItem) {
eas.sync.updateFailedItems(syncData, errorcause, foundItem.primaryKey, foundItem.toString());
} else {
//should not happen
syncData.target.removeItemFromChangeLog(sendItems[0].id);
}
}
syncData.progressData.inc();
//restore numberOfItemsToSend
numberOfItemsToSend = maxnumbertosend;
} else if (sendItems.length > 1) {
//reduce further
numberOfItemsToSend = Math.min(1, Math.round(sendItems.length / 5));
} else {
//sendItems.length == 0 ??? recheck but this time let it handle all cases
eas.network.checkStatus(syncData, wbxmlData, "Sync.Collections.Collection.Status");
}
break;
default:
//recheck but this time let it handle all cases
eas.network.checkStatus(syncData, wbxmlData, "Sync.Collections.Collection.Status");
}
await TbSync.tools.sleep(10, true);
if (errorcause == "") {
//PROCESS RESPONSE
await eas.sync.processResponses(wbxmlData, syncData, addedItems, changedItems);
//PROCESS COMMANDS
await eas.sync.processCommands(wbxmlData, syncData);
//remove all items from changelog that did not fail
for (let a=0; a < changedItems.length; a++) {
syncData.target.removeItemFromChangeLog(changedItems[a]);
syncData.progressData.inc();
}
//update synckey
eas.network.updateSynckey(syncData, wbxmlData);
}
} else if (e==0) { //if there was no local change and also no error (which will not happen twice) finish
done = true;
}
} while (!done);
//was there an error?
if (syncData.failedItems.length > 0) {
throw eas.sync.finish("warning", "ServerRejectedSomeItems::" + syncData.failedItems.length);
}
},
revertLocalChanges: async function (syncData) {
let maxnumbertosend = eas.prefs.getIntPref("maxitems");
syncData.progressData.reset(0, syncData.target.getItemsFromChangeLog().length);
if (syncData.progressData.todo == 0) {
return;
}
let viaItemOperations = (syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("ItemOperations"));
//get changed items from ChangeLog
do {
syncData.setSyncState("prepare.request.revertlocalchanges");
let changes = syncData.target.getItemsFromChangeLog(maxnumbertosend);
let c=0;
syncData.request = "";
syncData.response = "";
// BUILD WBXML
let wbxml = eas.wbxmltools.createWBXML();
if (viaItemOperations) {
wbxml.switchpage("ItemOperations");
wbxml.otag("ItemOperations");
} else {
wbxml.otag("Sync");
wbxml.otag("Collections");
wbxml.otag("Collection");
if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
wbxml.atag("SyncKey", syncData.synckey);
wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
wbxml.otag("Commands");
}
for (let i=0; i 0) { //if there was at least one actual local change, send request
let error = false;
let wbxmlData = "";
//SEND REQUEST & VALIDATE RESPONSE
try {
syncData.setSyncState("send.request.revertlocalchanges");
let response = await eas.network.sendRequest(wbxml.getBytes(), (viaItemOperations) ? "ItemOperations" : "Sync", syncData);
syncData.setSyncState("eval.response.revertlocalchanges");
//get data from wbxml response
wbxmlData = eas.network.getDataFromResponse(response);
} catch (e) {
//we do not handle errors, IF there was an error, wbxmlData is empty and will trigger the fallback
}
let fetchPath = (viaItemOperations) ? "ItemOperations.Response.Fetch" : "Sync.Collections.Collection.Responses.Fetch";
if (eas.xmltools.hasWbxmlDataField(wbxmlData, fetchPath)) {
//looking for additions
let add = eas.xmltools.nodeAsArray(eas.xmltools.getWbxmlDataField(wbxmlData, fetchPath));
for (let count = 0; count < add.length; count++) {
await TbSync.tools.sleep(10, true);
let ServerId = add[count].ServerId;
let data = (viaItemOperations) ? add[count].Properties : add[count].ApplicationData;
if (data && ServerId) {
let foundItem = await syncData.target.getItem(ServerId);
if (!foundItem) { //do NOT add, if an item with that ServerId was found
let newItem = eas.sync.createItem(syncData);
try {
eas.sync[syncData.type].setThunderbirdItemFromWbxml(newItem, data, ServerId, syncData);
await syncData.target.addItem(newItem);
} catch (e) {
eas.xmltools.printXmlData(add[count], true); //include application data in log
TbSync.eventlog.add("warning", syncData.eventLogInfo, "BadItemSkipped::JavaScriptError", newItem.toString());
throw e; // unable to add item to Thunderbird - fatal error
}
} else {
//should not happen, since we deleted that item beforehand
syncData.target.removeItemFromChangeLog(ServerId);
}
syncData.progressData.inc();
} else {
error = true;
break;
}
}
} else {
error = true;
}
if (error) {
//if ItemOperations.Fetch fails, fall back to Sync.Fetch, if that fails, fall back to resync
if (viaItemOperations) {
viaItemOperations = false;
TbSync.eventlog.add("info", syncData.eventLogInfo, "Server returned error during ItemOperations.Fetch, falling back to Sync.Fetch.");
} else {
await eas.sync.revertLocalChangesViaResync(syncData);
return;
}
}
} else { //if there was no more local change we need to revert, return
return;
}
} while (true);
},
revertLocalChangesViaResync: async function (syncData) {
TbSync.eventlog.add("info", syncData.eventLogInfo, "Server does not support ItemOperations.Fetch and/or Sync.Fetch, must revert via resync.");
let changes = syncData.target.getItemsFromChangeLog();
syncData.progressData.reset(0, changes.length);
syncData.setSyncState("prepare.request.revertlocalchanges");
//remove all changes, so we can get them fresh from the server
for (let i=0; i-1) changedItems.splice(p,1);
}
}
}
//looking for deletions
let del = eas.xmltools.nodeAsArray(wbxmlData.Sync.Collections.Collection.Responses.Delete);
for (let count = 0; count < del.length; count++) {
//What can we do about failed deletes? SyncLog
eas.network.checkStatus(syncData, del[count],"Status","Sync.Collections.Collection.Responses.Delete["+count+"].Status", true);
}
}
},
// ---------------------------------------------------------------------------
// HELPER FUNCTIONS AND DEFINITIONS
// ---------------------------------------------------------------------------
MAP_EAS2TB : {
//EAS Importance: 0 = LOW | 1 = NORMAL | 2 = HIGH
Importance : { "0":"9", "1":"5", "2":"1"}, //to PRIORITY
//EAS Sensitivity : 0 = Normal | 1 = Personal | 2 = Private | 3 = Confidential
Sensitivity : { "0":"PUBLIC", "1":"unset", "2":"PRIVATE", "3":"CONFIDENTIAL"}, //to CLASS
//EAS BusyStatus: 0 = Free | 1 = Tentative | 2 = Busy | 3 = Work | 4 = Elsewhere
BusyStatus : {"0":"TRANSPARENT", "1":"unset", "2":"OPAQUE", "3":"OPAQUE", "4":"OPAQUE"}, //to TRANSP
//EAS AttendeeStatus: 0 =Response unknown (but needed) | 2 = Tentative | 3 = Accept | 4 = Decline | 5 = Not responded (and not needed) || 1 = Organizer in ResponseType
ATTENDEESTATUS : {"0": "NEEDS-ACTION", "1":"Orga", "2":"TENTATIVE", "3":"ACCEPTED", "4":"DECLINED", "5":"ACCEPTED"},
},
MAP_TB2EAS : {
//TB PRIORITY: 9 = LOW | 5 = NORMAL | 1 = HIGH
PRIORITY : { "9":"0", "5":"1", "1":"2","unset":"1"}, //to Importance
//TB CLASS: PUBLIC, PRIVATE, CONFIDENTIAL)
CLASS : { "PUBLIC":"0", "PRIVATE":"2", "CONFIDENTIAL":"3", "unset":"1"}, //to Sensitivity
//TB TRANSP : free = TRANSPARENT, busy = OPAQUE)
TRANSP : {"TRANSPARENT":"0", "unset":"1", "OPAQUE":"2"}, // to BusyStatus
//TB STATUS: NEEDS-ACTION, ACCEPTED, DECLINED, TENTATIVE, (DELEGATED, COMPLETED, IN-PROCESS - for todo)
ATTENDEESTATUS : {"NEEDS-ACTION":"0", "ACCEPTED":"3", "DECLINED":"4", "TENTATIVE":"2", "DELEGATED":"5","COMPLETED":"5", "IN-PROCESS":"5"},
},
mapEasPropertyToThunderbird : function (easProp, tbProp, data, item) {
if (data[easProp]) {
//store original EAS value
let easPropValue = eas.xmltools.checkString(data[easProp]);
item.setProperty("X-EAS-" + easProp, easPropValue);
//map EAS value to TB value (use setCalItemProperty if there is one option which can unset/delete the property)
eas.tools.setCalItemProperty(item, tbProp, eas.sync.MAP_EAS2TB[easProp][easPropValue]);
}
},
mapThunderbirdPropertyToEas: function (tbProp, easProp, item) {
if (item.hasProperty("X-EAS-" + easProp) && eas.tools.getCalItemProperty(item, tbProp) == eas.sync.MAP_EAS2TB[easProp][item.getProperty("X-EAS-" + easProp)]) {
//we can use our stored EAS value, because it still maps to the current TB value
return item.getProperty("X-EAS-" + easProp);
} else {
return eas.sync.MAP_TB2EAS[tbProp][eas.tools.getCalItemProperty(item, tbProp)];
}
},
getEasItemType(aItem) {
if (aItem instanceof TbSync.addressbook.AbItem) {
return "Contacts";
} else if (aItem instanceof TbSync.lightning.TbItem) {
return aItem.isTodo ? "Tasks" : "Calendar";
} else {
throw "Unknown aItem.";
}
},
createItem(syncData) {
switch (syncData.type) {
case "Contacts":
return syncData.target.createNewCard();
break;
case "Tasks":
return syncData.target.createNewTodo();
break;
case "Calendar":
return syncData.target.createNewEvent();
break;
default:
throw "Unknown item type <" + syncData.type + ">";
}
},
getWbxmlFromThunderbirdItem(item, syncData, isException = false) {
try {
let wbxml = eas.sync[syncData.type].getWbxmlFromThunderbirdItem(item, syncData, isException);
return wbxml;
} catch (e) {
TbSync.eventlog.add("warning", syncData.eventLogInfo, "BadItemSkipped::JavaScriptError", item.toString());
throw e; // unable to read item from Thunderbird - fatal error
}
},
// ---------------------------------------------------------------------------
// LIGHTNING HELPER FUNCTIONS AND DEFINITIONS
// These functions are needed only by tasks and events, so they
// are placed here, even though they are not type independent,
// but I did not want to add another "lightning" sub layer.
//
// The item in these functions is a native lightning item.
// ---------------------------------------------------------------------------
setItemSubject: function (item, syncData, data) {
if (data.Subject) item.title = eas.xmltools.checkString(data.Subject);
},
setItemLocation: function (item, syncData, data) {
if (data.Location) item.setProperty("location", eas.xmltools.checkString(data.Location));
},
setItemCategories: function (item, syncData, data) {
if (data.Categories && data.Categories.Category) {
let cats = [];
if (Array.isArray(data.Categories.Category)) cats = data.Categories.Category;
else cats.push(data.Categories.Category);
item.setCategories(cats.length, cats);
}
},
getItemCategories: function (item, syncData) {
let asversion = syncData.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncData.type); //init wbxml with "" and not with precodes, also activate type codePage (Calendar, Tasks, Contacts etc)
//to properly "blank" categories, we need to always include the container
let categories = item.getCategories({});
if (categories.length > 0) {
wbxml.otag("Categories");
for (let i=0; i 0 && */ data.Body.Data) item.setProperty("description", eas.xmltools.checkString(data.Body.Data)); //EstimatedDataSize is optional
}
},
getItemBody: function (item, syncData) {
let asversion = syncData.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncData.type); //init wbxml with "" and not with precodes, also activate type codePage (Calendar, Tasks, Contacts etc)
let description = (item.hasProperty("description")) ? item.getProperty("description") : "";
if (asversion == "2.5") {
wbxml.atag("Body", description);
} else {
wbxml.switchpage("AirSyncBase");
wbxml.otag("Body");
wbxml.atag("Type", "1");
wbxml.atag("EstimatedDataSize", "" + description.length);
wbxml.atag("Data", description);
wbxml.ctag();
//does not work with horde at the moment, does not work with task, does not work with exceptions
//if (syncData.accountData.getAccountProperty("horde") == "0") wbxml.atag("NativeBodyType", "1");
//return to code page of this type
wbxml.switchpage(syncData.type);
}
return wbxml.getBytes();
},
//item is a native lightning item
setItemRecurrence: function (item, syncData, data, timezone) {
if (data.Recurrence) {
item.recurrenceInfo = TbSync.lightning.cal.createRecurrenceInfo();
item.recurrenceInfo.item = item;
let recRule = TbSync.lightning.cal.createRecurrenceRule();
switch (data.Recurrence.Type) {
case "0":
recRule.type = "DAILY";
break;
case "1":
recRule.type = "WEEKLY";
break;
case "2":
case "3":
recRule.type = "MONTHLY";
break;
case "5":
case "6":
recRule.type = "YEARLY";
break;
}
if (data.Recurrence.CalendarType) {
// TODO
}
if (data.Recurrence.DayOfMonth) {
recRule.setComponent("BYMONTHDAY", 1, [data.Recurrence.DayOfMonth]);
}
if (data.Recurrence.DayOfWeek) {
let DOW = data.Recurrence.DayOfWeek;
if (DOW == 127 && (recRule.type == "MONTHLY" || recRule.type == "YEARLY")) {
recRule.setComponent("BYMONTHDAY", 1, [-1]);
}
else {
let days = [];
for (let i = 0; i < 7; ++i) {
if (DOW & 1 << i) days.push(i + 1);
}
if (data.Recurrence.WeekOfMonth) {
for (let i = 0; i < days.length; ++i) {
if (data.Recurrence.WeekOfMonth == 5) {
days[i] = -1 * (days[i] + 8);
}
else {
days[i] += 8 * (data.Recurrence.WeekOfMonth - 0);
}
}
}
recRule.setComponent("BYDAY", days.length, days);
}
}
if (data.Recurrence.FirstDayOfWeek) {
//recRule.setComponent("WKST", 1, [data.Recurrence.FirstDayOfWeek]); // WKST is not a valid component
//recRule.weekStart = data.Recurrence.FirstDayOfWeek; // - (NS_ERROR_NOT_IMPLEMENTED) [calIRecurrenceRule.weekStart]
TbSync.eventlog.add("info", syncData.eventLogInfo, "FirstDayOfWeek tag ignored (not supported).", item.icalString);
}
if (data.Recurrence.Interval) {
recRule.interval = data.Recurrence.Interval;
}
if (data.Recurrence.IsLeapMonth) {
// TODO
}
if (data.Recurrence.MonthOfYear) {
recRule.setComponent("BYMONTH", 1, [data.Recurrence.MonthOfYear]);
}
if (data.Recurrence.Occurrences) {
recRule.count = data.Recurrence.Occurrences;
}
if (data.Recurrence.Until) {
//time string could be in compact/basic or extended form of ISO 8601,
//cal.createDateTime only supports compact/basic, our own method takes both styles
recRule.untilDate = eas.tools.createDateTime(data.Recurrence.Until);
}
if (data.Recurrence.Start) {
TbSync.eventlog.add("info", syncData.eventLogInfo, "Start tag in recurring task is ignored, recurrence will start with first entry.", item.icalString);
}
item.recurrenceInfo.insertRecurrenceItemAt(recRule, 0);
if (data.Exceptions && syncData.type == "Calendar") { // only events, tasks cannot have exceptions
// Exception could be an object or an array of objects
let exceptions = [].concat(data.Exceptions.Exception);
for (let exception of exceptions) {
//exception.ExceptionStartTime is in UTC, but the Recurrence Object is in local timezone
let dateTime = TbSync.lightning.cal.createDateTime(exception.ExceptionStartTime).getInTimezone(timezone);
if (data.AllDayEvent == "1") {
dateTime.isDate = true;
// Pass to replacement event unless overriden
if (!exception.AllDayEvent) {
exception.AllDayEvent = "1";
}
}
if (exception.Deleted == "1") {
item.recurrenceInfo.removeOccurrenceAt(dateTime);
}
else {
let replacement = item.recurrenceInfo.getOccurrenceFor(dateTime);
// replacement is a native lightning item, so we can access its id via .id
eas.sync[syncData.type].setThunderbirdItemFromWbxml(replacement, exception, replacement.id, syncData);
// Reminders should carry over from parent, but setThunderbirdItemFromWbxml clears all alarms
if (!exception.Reminder && item.getAlarms({}).length) {
replacement.addAlarm(item.getAlarms({})[0]);
}
// Removing a reminder requires EAS 16.0
item.recurrenceInfo.modifyException(replacement, true);
}
}
}
}
},
getItemRecurrence: function (item, syncData, localStartDate = null) {
let asversion = syncData.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncData.type); //init wbxml with "" and not with precodes, also activate type codePage (Calendar, Tasks etc)
if (item.recurrenceInfo && (syncData.type == "Calendar" || syncData.type == "Tasks")) {
let deleted = [];
let hasRecurrence = false;
let startDate = (syncData.type == "Calendar") ? item.startDate : item.entryDate;
for (let recRule of item.recurrenceInfo.getRecurrenceItems({})) {
if (recRule.date) {
if (recRule.isNegative) {
// EXDATE
deleted.push(recRule);
}
else {
// RDATE
TbSync.eventlog.add("info", syncData.eventLogInfo, "Ignoring RDATE rule (not supported)", recRule.icalString);
}
continue;
}
if (recRule.isNegative) {
// EXRULE
TbSync.eventlog.add("info", syncData.eventLogInfo, "Ignoring EXRULE rule (not supported)", recRule.icalString);
continue;
}
// RRULE
wbxml.otag("Recurrence");
hasRecurrence = true;
let type = 0;
let monthDays = recRule.getComponent("BYMONTHDAY", {});
let weekDays = recRule.getComponent("BYDAY", {});
let months = recRule.getComponent("BYMONTH", {});
let weeks = [];
// Unpack 1MO style days
for (let i = 0; i < weekDays.length; ++i) {
if (weekDays[i] > 8) {
weeks[i] = Math.floor(weekDays[i] / 8);
weekDays[i] = weekDays[i] % 8;
}
else if (weekDays[i] < -8) {
// EAS only supports last week as a special value, treat
// all as last week or assume every month has 5 weeks?
// Change to last week
//weeks[i] = 5;
// Assumes 5 weeks per month for week <= -2
weeks[i] = 6 - Math.floor(-weekDays[i] / 8);
weekDays[i] = -weekDays[i] % 8;
}
}
if (monthDays[0] && monthDays[0] == -1) {
weeks = [5];
weekDays = [1, 2, 3, 4, 5, 6, 7]; // 127
monthDays[0] = null;
}
// Type
if (recRule.type == "WEEKLY") {
type = 1;
if (!weekDays.length) {
weekDays = [startDate.weekday + 1];
}
}
else if (recRule.type == "MONTHLY" && weeks.length) {
type = 3;
}
else if (recRule.type == "MONTHLY") {
type = 2;
if (!monthDays.length) {
monthDays = [startDate.day];
}
}
else if (recRule.type == "YEARLY" && weeks.length) {
type = 6;
}
else if (recRule.type == "YEARLY") {
type = 5;
if (!monthDays.length) {
monthDays = [startDate.day];
}
if (!months.length) {
months = [startDate.month + 1];
}
}
wbxml.atag("Type", type.toString());
//Tasks need a Start tag, but we cannot allow a start date different from the start of the main item (thunderbird does not support that)
if (localStartDate) wbxml.atag("Start", localStartDate);
// TODO: CalendarType: 14.0 and up
// DayOfMonth
if (monthDays[0]) {
// TODO: Multiple days of month - multiple Recurrence tags?
wbxml.atag("DayOfMonth", monthDays[0].toString());
}
// DayOfWeek
if (weekDays.length) {
let bitfield = 0;
for (let day of weekDays) {
bitfield |= 1 << (day - 1);
}
wbxml.atag("DayOfWeek", bitfield.toString());
}
// FirstDayOfWeek: 14.1 and up
//wbxml.atag("FirstDayOfWeek", recRule.weekStart); - (NS_ERROR_NOT_IMPLEMENTED) [calIRecurrenceRule.weekStart]
// Interval
wbxml.atag("Interval", recRule.interval.toString());
// TODO: IsLeapMonth: 14.0 and up
// MonthOfYear
if (months.length) {
wbxml.atag("MonthOfYear", months[0].toString());
}
// Occurrences
if (recRule.isByCount) {
wbxml.atag("Occurrences", recRule.count.toString());
}
// Until
else if (recRule.untilDate != null) {
//Events need the Until data in compact form, Tasks in the basic form
wbxml.atag("Until", eas.tools.getIsoUtcString(recRule.untilDate, (syncData.type == "Tasks")));
}
// WeekOfMonth
if (weeks.length) {
wbxml.atag("WeekOfMonth", weeks[0].toString());
}
wbxml.ctag();
}
if (syncData.type == "Calendar" && hasRecurrence) { //Exceptions only allowed in Calendar and only if a valid Recurrence was added
let modifiedIds = item.recurrenceInfo.getExceptionIds({});
if (deleted.length || modifiedIds.length) {
wbxml.otag("Exceptions");
for (let exception of deleted) {
wbxml.otag("Exception");
wbxml.atag("ExceptionStartTime", eas.tools.getIsoUtcString(exception.date));
wbxml.atag("Deleted", "1");
//Docs say it is allowed, but if present, it does not work
//if (asversion == "2.5") {
// wbxml.atag("UID", item.id); //item.id is not valid, use UID or primaryKey
//}
wbxml.ctag();
}
for (let exceptionId of modifiedIds) {
let replacement = item.recurrenceInfo.getExceptionFor(exceptionId);
wbxml.otag("Exception");
wbxml.atag("ExceptionStartTime", eas.tools.getIsoUtcString(exceptionId));
wbxml.append(eas.sync.getWbxmlFromThunderbirdItem(replacement, syncData, true));
wbxml.ctag();
}
wbxml.ctag();
}
}
}
return wbxml.getBytes();
}
}
EAS-4-TbSync-1.12/content/includes/tasksync.js 0000664 0000000 0000000 00000017466 13623463701 0021106 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var Tasks = {
// --------------------------------------------------------------------------- //
// Read WBXML and set Thunderbird item
// --------------------------------------------------------------------------- //
setThunderbirdItemFromWbxml: function (tbItem, data, id, syncdata) {
let item = tbItem instanceof TbSync.lightning.TbItem ? tbItem.nativeItem : tbItem;
let asversion = syncdata.accountData.getAccountProperty("asversion");
item.id = id;
eas.sync.setItemBody(item, syncdata, data);
eas.sync.setItemSubject(item, syncdata, data);
eas.sync.setItemCategories(item, syncdata, data);
eas.sync.setItemRecurrence(item, syncdata, data);
let dueDate = null;
if (data.DueDate && data.UtcDueDate) {
//extract offset from EAS data
let DueDate = new Date(data.DueDate);
let UtcDueDate = new Date(data.UtcDueDate);
let offset = (UtcDueDate.getTime() - DueDate.getTime())/60000;
//timezone is identified by its offset
let utc = cal.createDateTime(eas.tools.dateToBasicISOString(UtcDueDate)); //format "19800101T000000Z" - UTC
dueDate = utc.getInTimezone(eas.tools.guessTimezoneByCurrentOffset(offset, utc));
item.dueDate = dueDate;
}
if (data.StartDate && data.UtcStartDate) {
//extract offset from EAS data
let StartDate = new Date(data.StartDate);
let UtcStartDate = new Date(data.UtcStartDate);
let offset = (UtcStartDate.getTime() - StartDate.getTime())/60000;
//timezone is identified by its offset
let utc = cal.createDateTime(eas.tools.dateToBasicISOString(UtcStartDate)); //format "19800101T000000Z" - UTC
item.entryDate = utc.getInTimezone(eas.tools.guessTimezoneByCurrentOffset(offset, utc));
} else {
//there is no start date? if this is a recurring item, we MUST add an entryDate, otherwise Thunderbird will not display the recurring items
if (data.Recurrence) {
if (dueDate) {
item.entryDate = dueDate;
TbSync.eventlog.add("info", syncdata, "Copy task dueData to task startDate, because Thunderbird needs a startDate for recurring items.", item.icalString);
} else {
TbSync.eventlog.add("info", syncdata, "Task without startDate and without dueDate but with recurrence info is not supported by Thunderbird. Recurrence will be lost.", item.icalString);
}
}
}
eas.sync.mapEasPropertyToThunderbird ("Sensitivity", "CLASS", data, item);
eas.sync.mapEasPropertyToThunderbird ("Importance", "PRIORITY", data, item);
item.clearAlarms();
if (data.ReminderSet && data.ReminderTime && data.UtcStartDate) {
let UtcDate = eas.tools.createDateTime(data.UtcStartDate);
let UtcAlarmDate = eas.tools.createDateTime(data.ReminderTime);
let alarm = cal.createAlarm();
alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_START; //TB saves new alarms as offsets, so we add them as such as well
alarm.offset = UtcAlarmDate.subtractDate(UtcDate);
alarm.action = "DISPLAY";
item.addAlarm(alarm);
}
//status/percentage cannot be mapped
if (data.Complete) {
if (data.Complete == "0") {
item.isCompleted = false;
} else {
item.isCompleted = true;
if (data.DateCompleted) item.completedDate = eas.tools.createDateTime(data.DateCompleted);
}
}
},
/*
Regenerate: After complete, the completed task is removed from the series and stored as an new entry. The series starts an week (as set) after complete date with one less occurence
*/
// --------------------------------------------------------------------------- //
//read TB event and return its data as WBXML
// --------------------------------------------------------------------------- //
getWbxmlFromThunderbirdItem: function (tbItem, syncdata) {
let item = tbItem instanceof TbSync.lightning.TbItem ? tbItem.nativeItem : tbItem;
let asversion = syncdata.accountData.getAccountProperty("asversion");
let wbxml = eas.wbxmltools.createWBXML("", syncdata.type); //init wbxml with "" and not with precodes, and set initial codepage
//Order of tags taken from: https://msdn.microsoft.com/en-us/library/dn338924(v=exchg.80).aspx
//Subject
wbxml.atag("Subject", (item.title) ? item.title : "");
//Body
wbxml.append(eas.sync.getItemBody(item, syncdata));
//Importance
wbxml.atag("Importance", eas.sync.mapThunderbirdPropertyToEas("PRIORITY", "Importance", item));
//tasks is using extended ISO 8601 (2019-01-18T00:00:00.000Z) instead of basic (20190118T000000Z),
//eas.tools.getIsoUtcString returns extended if true as second parameter is present
let localStartDate = null;
if (item.entryDate || item.dueDate) {
wbxml.atag("UtcStartDate", eas.tools.getIsoUtcString(item.entryDate ? item.entryDate : item.dueDate, true));
//to fake the local time as UTC, getIsoUtcString needs the third parameter to be true
localStartDate = eas.tools.getIsoUtcString(item.entryDate ? item.entryDate : item.dueDate, true, true);
wbxml.atag("StartDate", localStartDate);
wbxml.atag("UtcDueDate", eas.tools.getIsoUtcString(item.dueDate ? item.dueDate : item.entryDate, true));
//to fake the local time as UTC, getIsoUtcString needs the third parameter to be true
wbxml.atag("DueDate", eas.tools.getIsoUtcString(item.dueDate ? item.dueDate : item.entryDate, true, true));
}
//Categories
wbxml.append(eas.sync.getItemCategories(item, syncdata));
//Recurrence (only if localStartDate has been set)
if (localStartDate) wbxml.append(eas.sync.getItemRecurrence(item, syncdata, localStartDate));
//Complete
if (item.isCompleted) {
wbxml.atag("Complete", "1");
wbxml.atag("DateCompleted", eas.tools.getIsoUtcString(item.completedDate, true));
} else {
wbxml.atag("Complete", "0");
}
//Sensitivity
wbxml.atag("Sensitivity", eas.sync.mapThunderbirdPropertyToEas("CLASS", "Sensitivity", item));
//ReminderTime and ReminderSet
let alarms = item.getAlarms({});
if (alarms.length>0 && (item.entryDate || item.dueDate)) {
let reminderTime;
if (alarms[0].offset) {
//create Date obj from entryDate by converting item.entryDate to an extended UTC ISO string, which can be parsed by Date
//if entryDate is missing, the startDate of this object is set to its dueDate
let UtcDate = new Date(eas.tools.getIsoUtcString(item.entryDate ? item.entryDate : item.dueDate, true));
//add offset
UtcDate.setSeconds(UtcDate.getSeconds() + alarms[0].offset.inSeconds);
reminderTime = UtcDate.toISOString();
} else {
reminderTime = eas.tools.getIsoUtcString(alarms[0].alarmDate, true);
}
wbxml.atag("ReminderTime", reminderTime);
wbxml.atag("ReminderSet", "1");
} else {
wbxml.atag("ReminderSet", "0");
}
return wbxml.getBytes();
},
}
EAS-4-TbSync-1.12/content/includes/tools.js 0000664 0000000 0000000 00000060635 13623463701 0020403 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var tools = {
setCalItemProperty: function (item, prop, value) {
if (value == "unset") item.deleteProperty(prop);
else item.setProperty(prop, value);
},
getCalItemProperty: function (item, prop) {
if (item.hasProperty(prop)) return item.getProperty(prop);
else return "unset";
},
isString: function (s) {
return (typeof s == 'string' || s instanceof String);
},
getIdentityKey: function (email) {
let acctMgr = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
let accounts = acctMgr.accounts;
for (let a = 0; a < accounts.length; a++) {
let account = accounts.queryElementAt(a, Components.interfaces.nsIMsgAccount);
if (account.defaultIdentity && account.defaultIdentity.email == email) return account.defaultIdentity.key;
}
return "";
},
parentIsTrash: function (folderData) {
let parentID = folderData.getFolderProperty("parentID");
if (parentID == "0") return false;
let parentFolder = folderData.accountData.getFolder("serverID", parentID);
if (parentFolder && parentFolder.getFolderProperty("type") == "4") return true;
return false;
},
getNewDeviceId: function () {
//taken from https://jsfiddle.net/briguy37/2MVFd/
let d = new Date().getTime();
let uuid = 'xxxxxxxxxxxxxxxxyxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return "MZTB" + uuid;
},
getUriFromDirectoryId: function(ownerId) {
let directories = MailServices.ab.directories;
while (directories.hasMoreElements()) {
let directory = directories.getNext();
if (directory instanceof Components.interfaces.nsIAbDirectory) {
if (ownerId.startsWith(directory.dirPrefId)) return directory.URI;
}
}
return null;
},
//function to get correct uri of current card for global book as well for mailLists
getSelectedUri : function(aUri, aCard) {
if (aUri == "moz-abdirectory://?") {
//get parent via card owner
return eas.tools.getUriFromDirectoryId(aCard.directoryId);
} else if (MailServices.ab.getDirectory(aUri).isMailList) {
//MailList suck, we have to cut the url to get the parent
return aUri.substring(0, aUri.lastIndexOf("/"))
} else {
return aUri;
}
},
//read file from within the XPI package
fetchFile: function (aURL, returnType = "Array") {
return new Promise((resolve, reject) => {
let uri = Services.io.newURI(aURL);
let channel = Services.io.newChannelFromURI(uri,
null,
Services.scriptSecurityManager.getSystemPrincipal(),
null,
Components.interfaces.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
Components.interfaces.nsIContentPolicy.TYPE_OTHER);
NetUtil.asyncFetch(channel, (inputStream, status) => {
if (!Components.isSuccessCode(status)) {
reject(status);
return;
}
try {
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
if (returnType == "Array") {
resolve(data.replace("\r","").split("\n"))
} else {
resolve(data);
}
} catch (ex) {
reject(ex);
}
});
});
},
// TIMEZONE STUFF
TimeZoneDataStructure : class {
constructor() {
this.buf = new DataView(new ArrayBuffer(172));
}
/*
Buffer structure:
@000 utcOffset (4x8bit as 1xLONG)
@004 standardName (64x8bit as 32xWCHAR)
@068 standardDate (16x8 as 1xSYSTEMTIME)
@084 standardBias (4x8bit as 1xLONG)
@088 daylightName (64x8bit as 32xWCHAR)
@152 daylightDate (16x8 as 1xSTRUCT)
@168 daylightBias (4x8bit as 1xLONG)
*/
set easTimeZone64 (b64) {
//clear buffer
for (let i=0; i<172; i++) this.buf.setUint8(i, 0);
//load content into buffer
let content = (b64 == "") ? "" : atob(b64);
for (let i=0; i if found, does the stdOffset match? -> if so, done
2. Try to parse our own format, split name and test each chunk for IANA -> if found, does the stdOffset match? -> if so, done
3. Try if one of the chunks matches international code -> if found, does the stdOffset match? -> if so, done
4. Fallback: Use just the offsets */
//check for windows timezone name
if (eas.windowsTimezoneMap[stdName] && eas.cachedTimezoneData.iana[eas.windowsTimezoneMap[stdName]] && eas.cachedTimezoneData.iana[eas.windowsTimezoneMap[stdName]].std.offset == stdOffset ) {
//the windows timezone maps multiple IANA zones to one (Berlin*, Rome, Bruessel)
//check the windowsZoneName of the default TZ and of the winning, if they match, use default TZ
//so Rome could win, even Berlin is the default IANA zone
if (eas.defaultTimezoneInfo.std.windowsZoneName && eas.windowsTimezoneMap[stdName] != eas.defaultTimezoneInfo.std.id && eas.cachedTimezoneData.iana[eas.windowsTimezoneMap[stdName]].std.offset == eas.defaultTimezoneInfo.std.offset && stdName == eas.defaultTimezoneInfo.std.windowsZoneName) {
TbSync.dump("Timezone matched via windows timezone name ("+stdName+") with default TZ overtake", eas.windowsTimezoneMap[stdName] + " -> " + eas.defaultTimezoneInfo.std.id);
return eas.defaultTimezoneInfo.timezone;
}
TbSync.dump("Timezone matched via windows timezone name ("+stdName+")", eas.windowsTimezoneMap[stdName]);
return eas.cachedTimezoneData.iana[eas.windowsTimezoneMap[stdName]].timezone;
}
let parts = stdName.replace(/[;,()\[\]]/g," ").split(" ");
for (let i = 0; i < parts.length; i++) {
//check for IANA
if (eas.cachedTimezoneData.iana[parts[i]] && eas.cachedTimezoneData.iana[parts[i]].std.offset == stdOffset) {
TbSync.dump("Timezone matched via IANA", parts[i]);
return eas.cachedTimezoneData.iana[parts[i]].timezone;
}
//check for international abbreviation for standard period (CET, CAT, ...)
if (eas.cachedTimezoneData.abbreviations[parts[i]] && eas.cachedTimezoneData.iana[eas.cachedTimezoneData.abbreviations[parts[i]]] && eas.cachedTimezoneData.iana[eas.cachedTimezoneData.abbreviations[parts[i]]].std.offset == stdOffset) {
TbSync.dump("Timezone matched via international abbreviation (" + parts[i] +")", eas.cachedTimezoneData.abbreviations[parts[i]]);
return eas.cachedTimezoneData.iana[eas.cachedTimezoneData.abbreviations[parts[i]]].timezone;
}
}
//fallback to zone based on stdOffset and dstOffset, if we have that cached
if (eas.cachedTimezoneData.bothOffsets[stdOffset+":"+dstOffset]) {
TbSync.dump("Timezone matched via both offsets (std:" + stdOffset +", dst:" + dstOffset + ")", eas.cachedTimezoneData.bothOffsets[stdOffset+":"+dstOffset].tzid);
return eas.cachedTimezoneData.bothOffsets[stdOffset+":"+dstOffset];
}
//fallback to zone based on stdOffset only, if we have that cached
if (eas.cachedTimezoneData.stdOffset[stdOffset]) {
TbSync.dump("Timezone matched via std offset (" + stdOffset +")", eas.cachedTimezoneData.stdOffset[stdOffset].tzid);
return eas.cachedTimezoneData.stdOffset[stdOffset];
}
//return default timezone, if everything else fails
TbSync.dump("Timezone could not be matched via offsets (std:" + stdOffset +", dst:" + dstOffset + "), using default timezone", eas.defaultTimezoneInfo.std.id);
return eas.defaultTimezoneInfo.timezone;
},
//extract standard and daylight timezone data
getTimezoneInfo: function (timezone) {
let tzInfo = {};
tzInfo.std = eas.tools.getTimezoneInfoObject(timezone, "standard");
tzInfo.dst = eas.tools.getTimezoneInfoObject(timezone, "daylight");
if (tzInfo.dst === null) tzInfo.dst = tzInfo.std;
tzInfo.timezone = timezone;
return tzInfo;
},
//get timezone info for standard/daylight
getTimezoneInfoObject: function (timezone, standardOrDaylight) {
//handle UTC
if (timezone.isUTC) {
let obj = {}
obj.id = "UTC";
obj.offset = 0;
obj.abbreviation = "UTC";
obj.displayname = "Coordinated Universal Time (UTC)";
return obj;
}
//we could parse the icalstring by ourself, but I wanted to use ICAL.parse - TODO try catch
let info = TbSync.lightning.ICAL.parse("BEGIN:VCALENDAR\r\n" + timezone.icalComponent.toString() + "\r\nEND:VCALENDAR");
let comp = new TbSync.lightning.ICAL.Component(info);
let vtimezone =comp.getFirstSubcomponent("vtimezone");
let id = vtimezone.getFirstPropertyValue("tzid").toString();
let zone = vtimezone.getFirstSubcomponent(standardOrDaylight);
if (zone) {
let obj = {};
obj.id = id;
//get offset
let utcOffset = zone.getFirstPropertyValue("tzoffsetto").toString();
let o = parseInt(utcOffset.replace(":","")); //-330 = - 3h 30min
let h = Math.floor(o / 100); //-3 -> -180min
let m = o - (h*100) //-330 - -300 = -30
obj.offset = -1*((h*60) + m);
//get international abbreviation (CEST, CET, CAT ... )
obj.abbreviation = "";
try {
obj.abbreviation = zone.getFirstPropertyValue("tzname").toString();
} catch(e) {
TbSync.dump("Failed TZ", timezone.icalComponent.toString());
}
//get displayname
obj.displayname = /*"("+utcOffset+") " +*/ obj.id;// + ", " + obj.abbreviation;
//get DST switch date
let rrule = zone.getFirstPropertyValue("rrule");
let dtstart = zone.getFirstPropertyValue("dtstart");
if (rrule && dtstart) {
/*
THE switchdate PART OF THE OBJECT IS MICROSOFT SPECIFIC, EVERYTHING ELSE IS THUNDERBIRD GENERIC, I LET IT SIT HERE ANYHOW
https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to
the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate
the occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the
month if that day of the week does not occur 5 times).
Using this notation, specify 02:00 on the first Sunday in April as follows:
wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1.
Specify 02:00 on the last Thursday in October as follows:
wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5.
So we have to parse the RRULE to exract wDay
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 */
let parts =rrule.toString().split(";");
let rules = {};
for (let i = 0; i< parts.length; i++) {
let sub = parts[i].split("=");
if (sub.length == 2) rules[sub[0]] = sub[1];
}
if (rules.FREQ == "YEARLY" && rules.BYDAY && rules.BYMONTH && rules.BYDAY.length > 2) {
obj.switchdate = {};
obj.switchdate.month = parseInt(rules.BYMONTH);
let days = ["SU","MO","TU","WE","TH","FR","SA"];
obj.switchdate.dayOfWeek = days.indexOf(rules.BYDAY.substring(rules.BYDAY.length-2));
obj.switchdate.weekOfMonth = parseInt(rules.BYDAY.substring(0, rules.BYDAY.length-2));
if (obj.switchdate.weekOfMonth<0 || obj.switchdate.weekOfMonth>5) obj.switchdate.weekOfMonth = 5;
//get switch time from dtstart
let dttime = eas.tools.createDateTime(dtstart.toString());
obj.switchdate.hour = dttime.hour;
obj.switchdate.minute = dttime.minute;
obj.switchdate.second = dttime.second;
}
}
return obj;
}
return null;
},
}
//TODO: Invites
/*
if (TbSync.lightningIsAvailable()) {
cal.itip.checkAndSendOrigial = cal.itip.checkAndSend;
cal.itip.checkAndSend = function(aOpType, aItem, aOriginalItem) {
//if this item is added_by_user, do not call checkAndSend yet, because the UID is wrong, we need to sync first to get the correct ID - TODO
TbSync.dump("cal.checkAndSend", aOpType);
cal.itip.checkAndSendOrigial(aOpType, aItem, aOriginalItem);
}
}
*/
EAS-4-TbSync-1.12/content/includes/wbxmltools.js 0000664 0000000 0000000 00000074177 13623463701 0021463 0 ustar 00root root 0000000 0000000 /*
* This file is part of EAS-4-TbSync.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var wbxmltools = {
// Convert a WBXML (WAP Binary XML) to plain XML - returns save xml with all special chars in the user data encoded by encodeURIComponent
convert2xml: function (wbxml) {
let num = 4; //skip the 4 first bytes which are mostly 0x03 (WBXML Version 1.3), 0x01 (unknown public identifier), 0x6A (utf-8), 0x00 (Length of string table)
//the main code page will be set to the the first codepage used
let mainCodePage = null;
let tagStack = [];
let xml = "";
let codepage = 0;
while (num < wbxml.length) {
let data = wbxml.substr(num, 1).charCodeAt(0);
let token = data & 0x3F; //removes content bit(6) and attribute bit(7)
let tokenHasContent = ((data & 0x40) != 0); //checks if content bit is set
let tokenHasAttributes = ((data & 0x80) != 0); //checks if attribute bit is set
switch(token) {
case 0x00: // switch of codepage (new codepage is next byte)
num = num + 1;
codepage = (wbxml.substr(num, 1)).charCodeAt(0) & 0xFF;
break;
case 0x01: // Indicates the end of an attribute list or the end of an element
// tagStack contains a list of opened tags, which await to be closed
xml = xml + tagStack.pop();
break;
case 0x02: // A character entity. Followed by a mb_u_int32 encoding the character entity number.
TbSync.dump("wbxml", "Encoded character entity has not yet been implemented. Sorry.");
return false;
break;
case 0x03: // Inline string followed by a termstr. (0x00)
let termpos = wbxml.indexOf(String.fromCharCode(0x00), num);
//encode all special chars in the user data by encodeURIComponent which does not encode the apostrophe, so we need to do that by hand
xml = xml + encodeURIComponent(wbxml.substring(num + 1, termpos)).replace(/'/g, "%27");
num = termpos;
break;
case 0x04: // An unknown tag or attribute name. Followed by an mb_u_int32 that encodes an offset into the string table.
case 0x40: // Inline string document-type-specific extension token. Token is followed by a termstr.
case 0x41: // Inline string document-type-specific extension token. Token is followed by a termstr.
case 0x42: // Inline string document-type-specific extension token. Token is followed by a termstr.
case 0x43: // Processing instruction.
case 0x44: // Unknown tag, with content.
case 0x80: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32.
case 0x81: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32.
case 0x82: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32.
case 0x83: // String table reference. Followed by a mb_u_int32 encoding a byte offset from the beginning of the string table.
case 0x84: // Unknown tag, with attributes.
case 0xC0: // Single-byte document-type-specific extension token.
case 0xC1: // Single-byte document-type-specific extension token.
case 0xC2: // Single-byte document-type-specific extension token.
case 0xC3: // Opaque document-type-specific data.
case 0xC4: // Unknown tag, with content and attributes.
TbSync.dump("wbxml", "Global token <" + token + "> has not yet been implemented. Sorry.");
return false;
break;
default:
// if this code page is not the mainCodePage (or mainCodePage is not yet set = very first tag), add codePageTag with current codepage
let codePageTag = (codepage != mainCodePage) ? " xmlns='" + this.getNamespace(codepage) + "'" : "";
// if no mainCodePage has been defined yet, use the current codepage, which is either the initialized/default value of codepage or a value set by SWITCH_PAGE
if (mainCodePage === null) mainCodePage = codepage;
if (!tokenHasContent) {
xml = xml + "<" + this. getCodepageToken(codepage, token) + codePageTag + "/>";
} else {
xml = xml + "<" +this. getCodepageToken(codepage, token) + codePageTag +">";
//add the closing tag to the stack, so it can get properly closed later
tagStack.push("" +this. getCodepageToken(codepage, token) + ">");
}
if (this.isUnknownToken(codepage, token)) {
TbSync.eventlog.add("warning", null, "WBXML: Unknown token <" + token + "> for codepage <"+codepage+">.");
}
}
num = num + 1;
}
return (xml == "") ? "" : '' + xml;
},
isUnknownToken: function (codepage, token) {
if (this.codepages[codepage] && token in this.codepages[codepage]) return false;
else return true;
},
getNamespace: function (codepage) {
return (this.namespaces[codepage]) ? this.namespaces[codepage] : "UnknownCodePage" + codepage ;
},
getCodepageToken: function (codepage, token) {
return this.isUnknownToken(codepage, token) ? "Unknown." + codepage + "." + token : this.codepages[codepage][token];
},
// This returns a wbxml object, which allows to add tags (using names), switch codepages, or open and close tags, it is also possible to append pure (binary) wbxml
// If no wbxmlstring is present, default to the "init" string ( WBXML Version 1.3, unknown public identifier, UTF-8, Length of string table)
createWBXML: function (wbxmlstring = String.fromCharCode(0x03, 0x01, 0x6A, 0x00), initialCodepage = "") {
let wbxml = {
_codepage : 0,
_wbxml : wbxmlstring,
append : function (wbxmlstring) {
this._wbxml = this._wbxml + wbxmlstring;
},
// adding a string content tag as contentstring
atag : function (tokenname, content = "") {
//check if tokenname is in current codepage
if ((this._codepage in wbxmltools.codepages2) == false) throw "[wbxmltools] Unknown codepage <"+this._codepage+">";
if ((tokenname in wbxmltools.codepages2[this._codepage]) == false) throw "[wbxmltools] Unknown tokenname <"+tokenname+"> for codepage <"+wbxmltools.namespaces[this._codepage]+">";
if (content == "") {
//empty, just add token
this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname]);
} else {
//not empty,add token with enabled content bit and also add inlinestringidentifier
this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname] | 0x40, 0x03);
//add content
for (let i=0; i< content.length; i++) this._wbxml += String.fromCharCode(content.charCodeAt(i));
//add string termination and tag close
this._wbxml += String.fromCharCode(0x00, 0x01);
}
},
switchpage : function (name) {
let codepage = wbxmltools.namespaces.indexOf(name);
if (codepage == -1) throw "[wbxmltools] Unknown codepage <"+ name +">";
this._codepage = codepage;
this._wbxml += String.fromCharCode(0x00, codepage);
},
ctag : function () {
this._wbxml += String.fromCharCode(0x01);
},
//opentag is assumed to add a token with content, otherwise use addtag
otag : function (tokenname) {
this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname] | 0x40);
},
getCharCodes : function () {
let value = "";
for (let i=0; i