From 9324fa4a74501241aa20ca9e1dd580af65862627 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 6 Jun 2018 14:16:25 +0200 Subject: [PATCH] Implement http storage in rust (#730) * Port http storage to rust (#729) * Port http storage to rust * implement rest of parameters as far as possible * stylefixes * rustup * fix invalid timestamp * fix header file * Fix compilation errors * basic impl of dav * dockerize xandikos * add xandikos build * Fix circleci build * Fix circleci config * fix nextcloud port * stylefix * implement upload, upload, delete in rust * fix exc handling * python stylefixes * move caldav.list to rust * fix exc again (fastmail) * stylefixes * add basic logging, fix fastmail * stylefixes * fix tests for etag=None (icloud) * overwrite busted cargo-install-update * install clippy from git * fix rustfmt * rustfmt * clear cache --- .circleci/config.yml | 39 +- Makefile | 23 +- docker-compose.yml | 9 +- docker/xandikos/Dockerfile | 13 + docs/config.rst | 19 +- rust/Cargo.lock | 1158 ++++++++++++++++++- rust/Cargo.toml | 5 + rust/src/errors.rs | 51 +- rust/src/item.rs | 6 +- rust/src/lib.rs | 14 +- rust/src/storage/dav/mod.rs | 465 ++++++++ rust/src/storage/dav/parser.rs | 110 ++ rust/src/storage/exports.rs | 14 +- rust/src/storage/filesystem.rs | 10 +- rust/src/storage/http.rs | 230 ++++ rust/src/storage/mod.rs | 6 +- rust/src/storage/singlefile.rs | 17 +- rust/vdirsyncer_rustext.h | 38 + setup.py | 15 +- tests/__init__.py | 1 + tests/storage/__init__.py | 37 +- tests/storage/dav/__init__.py | 17 - tests/storage/dav/test_caldav.py | 72 +- tests/storage/servers/nextcloud/__init__.py | 2 +- tests/storage/servers/xandikos/__init__.py | 28 +- tests/storage/servers/xandikos/install.sh | 13 - tests/storage/test_http.py | 122 -- tests/storage/test_http_with_singlefile.py | 81 -- vdirsyncer/exceptions.py | 4 + vdirsyncer/native.py | 6 + vdirsyncer/storage/_rust.py | 4 +- vdirsyncer/storage/dav.py | 317 +---- vdirsyncer/storage/http.py | 73 +- 33 files changed, 2204 insertions(+), 815 deletions(-) create mode 100644 docker/xandikos/Dockerfile create mode 100644 rust/src/storage/dav/mod.rs create mode 100644 rust/src/storage/dav/parser.rs create mode 100644 rust/src/storage/http.rs delete mode 100644 tests/storage/test_http.py delete mode 100644 tests/storage/test_http_with_singlefile.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 3801ca5..4559b07 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,15 +3,14 @@ version: 2 references: basic_env: &basic_env CI: true - DAV_SERVER: xandikos restore_caches: &restore_caches restore_cache: keys: - - cache-{{ arch }}-{{ .Branch }} + - cache2-{{ arch }}-{{ .Branch }} save_caches: &save_caches save_cache: - key: cache-{{ arch }}-{{ .Branch }} + key: cache2-{{ arch }}-{{ .Branch }} paths: - "rust/target/" - "~/.cargo/" @@ -89,6 +88,23 @@ jobs: - run: make -e storage-test + xandikos: + docker: + - image: circleci/python:3.6 + environment: + <<: *basic_env + DAV_SERVER: xandikos + - image: vdirsyncer/xandikos:0.0.1 + steps: + - checkout + - *restore_caches + - *basic_setup + - run: make -e install-dev install-test + - *save_caches + + - run: wget -O - --retry-connrefused http://localhost:5001/ + - run: make -e storage-test + style: docker: - image: circleci/python:3.6 @@ -178,22 +194,6 @@ jobs: - run: make -e test - py36-release-radicale: - docker: - - image: circleci/python:3.6 - environment: - <<: *basic_env - REQUIREMENTS: release - DAV_SERVER: radicale - steps: - - checkout - - *restore_caches - - *basic_setup - - run: make -e install-dev install-test - - *save_caches - - - run: make -e test - py36-devel: docker: - image: circleci/python:3.6 @@ -232,6 +232,7 @@ workflows: - fastmail - icloud - davical + - xandikos - style - py34-minimal - py34-release diff --git a/Makefile b/Makefile index 15c4f40..9f5b212 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,15 @@ export ETESYNC_TESTS := false # systemwide. export CI := false +# Enable debug symbols and backtrace printing for rust lib +export RUST_BACKTRACE := $(CI) + # Whether to generate coverage data while running tests. export COVERAGE := $(CI) +# Log everything +export RUST_LOG := vdirsyncer_rustext=debug + # Additional arguments that should be passed to py.test. PYTEST_ARGS = @@ -93,10 +99,8 @@ install-test: install-servers install-style: install-docs pip install -U flake8 flake8-import-order 'flake8-bugbear>=17.3.0' - which cargo-install-update || cargo install cargo-update - cargo +nightly install-update -i clippy - cargo +nightly install-update -i rustfmt-nightly - cargo +nightly install-update -i cargo-update + rustup component add rustfmt-preview + cargo install --force --git https://github.com/rust-lang-nursery/rust-clippy clippy style: flake8 @@ -104,7 +108,7 @@ style: ! git grep -i 'text/icalendar' */* sphinx-build -W -b html ./docs/ ./docs/_build/html/ cd rust/ && cargo +nightly clippy - cd rust/ && cargo +nightly fmt --all -- --write-mode=diff + cd rust/ && cargo +nightly fmt --all -- --check install-docs: pip install -Ur docs-requirements.txt @@ -149,6 +153,11 @@ install-rust: rustup update nightly rust/vdirsyncer_rustext.h: - cbindgen -c rust/cbindgen.toml rust/ > $@ + cd rust/ && cargo build # hack to work around cbindgen bugs + CARGO_EXPAND_TARGET_DIR=rust/target/ cbindgen -c rust/cbindgen.toml rust/ > $@ -.PHONY: docs rust/vdirsyncer_rustext.h +docker/xandikos: + docker build -t vdirsyncer/xandikos:0.0.1 $@ + docker push vdirsyncer/xandikos:0.0.1 + +.PHONY: docs rust/vdirsyncer_rustext.h docker/xandikos diff --git a/docker-compose.yml b/docker-compose.yml index c44e27c..75fee38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,15 @@ services: nextcloud: image: nextcloud ports: - - '8080:80' + - '5000:80' environment: - SQLITE_DATABASE=nextcloud - NEXTCLOUD_ADMIN_USER=asdf - NEXTCLOUD_ADMIN_PASSWORD=asdf + + xandikos: + build: + context: . + dockerfile: docker/xandikos/Dockerfile + ports: + - '5001:5001' diff --git a/docker/xandikos/Dockerfile b/docker/xandikos/Dockerfile new file mode 100644 index 0000000..df003b4 --- /dev/null +++ b/docker/xandikos/Dockerfile @@ -0,0 +1,13 @@ +# Original file copyright 2017 Jelmer Vernooij + +FROM ubuntu:latest +RUN apt-get update && apt-get -y install xandikos locales +EXPOSE 8080 + +RUN locale-gen en_US.UTF-8 +ENV PYTHONIOENCODING=utf-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +CMD xandikos -d /tmp/dav -l 0.0.0.0 -p 5001 --autocreate diff --git a/docs/config.rst b/docs/config.rst index 89a9c8f..2ee3b36 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -511,19 +511,10 @@ leads to an error. of the normalized item content. :param url: URL to the ``.ics`` file. - :param username: Username for authentication. - :param password: Password for authentication. - :param verify: Verify SSL certificate, default True. This can also be a - local path to a self-signed SSL certificate. See :ref:`ssl-tutorial` - for more information. - :param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the - expected server certificate. See :ref:`ssl-tutorial` for more - information. - :param auth: Optional. Either ``basic``, ``digest`` or ``guess``. The - default is preemptive Basic auth, sending credentials even if server - didn't request them. This saves from an additional roundtrip per - request. Consider setting ``guess`` if this causes issues with your - server. + :param username: Username for HTTP basic authentication. + :param password: Password for HTTP basic authentication. + :param useragent: Default ``vdirsyncer``. + :param verify_cert: Add one new root certificate file in PEM format. Useful + for servers with self-signed certificates. :param auth_cert: Optional. Either a path to a certificate with a client certificate and the key or a list of paths to the files with them. - :param useragent: Default ``vdirsyncer``. diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 64dafe8..054e85d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,8 +1,29 @@ +[[package]] +name = "adler32" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "aho-corasick" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "arrayref" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atomicwrites" version = "0.2.1" @@ -13,15 +34,25 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atty" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "backtrace" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -30,10 +61,19 @@ name = "backtrace-sys" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.9.1" @@ -41,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -53,21 +93,105 @@ dependencies = [ "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-tools" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chrono" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.7.2" @@ -76,12 +200,37 @@ dependencies = [ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding_rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "error-chain" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -89,7 +238,7 @@ name = "failure" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -108,12 +257,25 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -122,6 +284,20 @@ name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futures" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "generic-array" version = "0.9.0" @@ -130,17 +306,235 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "httparse" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "humantime" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper" +version = "0.11.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libflate" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime_guess" +version = "2.0.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -149,19 +543,128 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-integer" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.9.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.9.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quick-xml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.3.15" @@ -172,7 +675,17 @@ name = "quote" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -185,6 +698,47 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "relay" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remove_dir_all" version = "0.5.1" @@ -193,14 +747,108 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "reqwest" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "safemem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "schannel" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "security-framework" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha2" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -211,7 +859,7 @@ dependencies = [ [[package]] name = "shippai" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -224,10 +872,30 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "siphasher" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.11.11" @@ -240,10 +908,10 @@ dependencies = [ [[package]] name = "syn" -version = "0.13.1" +version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -270,12 +938,17 @@ name = "synstructure" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "take" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tempdir" version = "0.3.7" @@ -285,11 +958,242 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termcolor" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-proto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.0.4" @@ -300,29 +1204,75 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uuid" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "uuid" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "vcpkg" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vdirsyncer-rustext" version = "0.1.0" dependencies = [ "atomicwrites 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shippai 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shippai 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "vobject 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "version_check" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vobject" version = "0.4.2" @@ -336,6 +1286,11 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.4" @@ -345,6 +1300,11 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -355,49 +1315,179 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wincolor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" +"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f" +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum atomicwrites 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7739334b1bdd4f80a35a51f4459494e6cd78807cf57bd829e4f01945190b7de6" -"checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea58cd16fd6c9d120b5bcb01d63883ae4cc7ba2aed35c1841b862a3c7ef6639" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" +"checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" +"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" +"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" +"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" +"checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" +"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" "checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" +"checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" "checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" +"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" +"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" +"checksum hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)" = "66b16eb6213713f3c72d0ed14ce56423ae84dced8df73d2a2c8675f0495ae7ea" +"checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" +"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" +"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1a429b86418868c7ea91ee50e9170683f47fd9d94f5375438ec86ec3adb74e8e" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" +"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b28683d0b09bbc20be1c9b3f6f24854efb1356ffcffee08ea3f6e65596e85fa" +"checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd" +"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" +"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" -"checksum proc-macro2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b16749538926f394755373f0dfec0852d79b3bd512a5906ceaeb72ee64a4eaa0" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" +"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" +"checksum openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)" = "73ae718c3562989cd3a0a5c26610feca02f8116822f6f195e6cf4887481e57f5" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" +"checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" +"checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" +"checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" +"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" +"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quick-xml 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b14c27e04216596a49f2b82398a24f67ed9f131a5c0e0235496ea446bdacfb12" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3" +"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b" +"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" -"checksum sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7daca11f2fdb8559c4f6c588386bed5e2ad4b6605c1442935a7f08144a918688" -"checksum shippai 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6bb8291e9995ea961d67fdedad78ca67a6ea2b14a69bdfddb012151e1c8b5ede" +"checksum reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "241faa9a8ca28a03cbbb9815a5d085f271d4c0168a19181f106aa93240c22ddb" +"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" +"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" +"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" +"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" +"checksum serde 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "428d3d818cb94ee037a17bf4f2200db2552e19b1825d33df2196624290716f92" +"checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1" +"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480" +"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" +"checksum shippai 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fab6cea07a38cb2f45ab82a8396764b358ac3662c97c8c64ceaa5512c4248e74" "checksum shippai_derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7d00a1043d0e6407aa7312b3eb8c7dc7f63993e5060306d4a2cac0bf8b9d85e7" +"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" +"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" +"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" +"checksum syn 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "61b8f1b737f929c6516ba46a3133fd6d5215ad8a62f66760f851f7048aebedfb" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98cad891cd238c98e1f0aec9f7c0f620aa696e4e5f7daba56ac67b5e86a6b049" +"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" +"checksum tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d00555353b013e170ed8bc4e13f648a317d1fd12157dbcae13f7013f6cf29f5" +"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" +"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" +"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708" +"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a" +"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" +"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b" +"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" +"checksum tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5783254b10c7c84a56f62c74766ef7e5b83d1f13053218c7cab8d3f2c826fa0e" +"checksum tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535fed0ccee189f3d48447587697ba3fd234b3dbbb091f0ec4613ddfec0a7c4c" +"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" +"checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90d662d111b0dbb08a180f2761026cba648c258023c355954a7c00e00e354636" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" +"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" "checksum uuid 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8630752f979f1b6b87c49830a5e3784082545de63920d59fbaac252474319447" +"checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" +"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum vobject 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6041995691036270fabeb41975ca858f3b5113b82eea19a4f276bfb8b32e9ae4" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 807c71d..9fd160f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -16,3 +16,8 @@ atomicwrites = "0.2.0" uuid = { version = "0.6", features = ["v4"] } libc = "0.2" log = "0.4" +reqwest = "0.8" +quick-xml = "0.12.0" +url = "1.7" +chrono = "0.4.0" +env_logger = "0.5" diff --git a/rust/src/errors.rs b/rust/src/errors.rs index 1ed23ba..c21f348 100644 --- a/rust/src/errors.rs +++ b/rust/src/errors.rs @@ -10,40 +10,39 @@ pub enum Error { ItemUnparseable, #[fail(display = "Unexpected version {}, expected {}", found, expected)] - UnexpectedVobjectVersion { - found: String, - expected: String, - }, + UnexpectedVobjectVersion { found: String, expected: String }, #[fail(display = "Unexpected component {}, expected {}", found, expected)] - UnexpectedVobject { - found: String, - expected: String, - }, + UnexpectedVobject { found: String, expected: String }, #[fail(display = "Item '{}' not found", href)] - ItemNotFound { - href: String, - }, + ItemNotFound { href: String }, #[fail(display = "The href '{}' is already taken", href)] - ItemAlreadyExisting { - href: String, - }, + ItemAlreadyExisting { href: String }, - #[fail(display = "A wrong etag for '{}' was provided. Another client's requests might \ - conflict with vdirsyncer.", - href)] - WrongEtag { - href: String, - }, + #[fail( + display = "A wrong etag for '{}' was provided. Another client's requests might \ + conflict with vdirsyncer.", + href + )] + WrongEtag { href: String }, - #[fail(display = "The mtime for '{}' has unexpectedly changed. Please close other programs\ - accessing this file.", - filepath)] - MtimeMismatch { - filepath: String, - }, + #[fail( + display = "The mtime for '{}' has unexpectedly changed. Please close other programs\ + accessing this file.", + filepath + )] + MtimeMismatch { filepath: String }, + + #[fail( + display = "The item '{}' has been rejected by the server because the vobject type was unexpected", + href + )] + UnsupportedVobject { href: String }, + + #[fail(display = "This storage is read-only.")] + ReadOnly, } pub unsafe fn export_result( diff --git a/rust/src/item.rs b/rust/src/item.rs index 99550ae..d67b9c4 100644 --- a/rust/src/item.rs +++ b/rust/src/item.rs @@ -1,7 +1,7 @@ use vobject; -use std::fmt::Write; use sha2::{Digest, Sha256}; +use std::fmt::Write; use errors::*; @@ -190,10 +190,10 @@ fn hash_component(c: &vobject::Component) -> String { pub mod exports { use super::Item; - use std::ptr; + use errors::*; use std::ffi::{CStr, CString}; use std::os::raw::c_char; - use errors::*; + use std::ptr; const EMPTY_STRING: *const c_char = b"\0" as *const u8 as *const c_char; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 80e9d82..14c91ea 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "cargo-clippy", allow(single_match))] + extern crate atomicwrites; #[macro_use] extern crate failure; @@ -8,11 +10,16 @@ extern crate uuid; extern crate vobject; #[macro_use] extern crate log; +extern crate chrono; +extern crate env_logger; +extern crate quick_xml; +extern crate reqwest; extern crate sha2; +extern crate url; +pub mod errors; mod item; mod storage; -pub mod errors; pub mod exports { use std::ffi::CStr; @@ -25,4 +32,9 @@ pub mod exports { pub unsafe extern "C" fn vdirsyncer_free_str(s: *const c_char) { CStr::from_ptr(s); } + + #[no_mangle] + pub unsafe extern "C" fn vdirsyncer_init_logger() { + ::env_logger::init(); + } } diff --git a/rust/src/storage/dav/mod.rs b/rust/src/storage/dav/mod.rs new file mode 100644 index 0000000..79e58e5 --- /dev/null +++ b/rust/src/storage/dav/mod.rs @@ -0,0 +1,465 @@ +mod parser; + +use chrono; + +use std::collections::BTreeSet; +use std::io::{BufReader, Read}; +use std::str::FromStr; + +use quick_xml; +use reqwest; +use reqwest::header::{ContentType, ETag, EntityTag, IfMatch, IfNoneMatch}; +use url::Url; + +use super::http::{handle_http_error, send_request, HttpConfig}; +use super::utils::generate_href; +use super::Storage; +use errors::*; + +use item::Item; + +#[inline] +fn propfind() -> reqwest::Method { + reqwest::Method::Extension("PROPFIND".to_owned()) +} + +#[inline] +fn report() -> reqwest::Method { + reqwest::Method::Extension("REPORT".to_owned()) +} + +static CALDAV_DT_FORMAT: &'static str = "%Y%m%dT%H%M%SZ"; + +struct DavStorage { + pub url: String, + pub http_config: HttpConfig, + pub http: Option, +} + +impl DavStorage { + pub fn new(url: &str, http_config: HttpConfig) -> Self { + DavStorage { + url: format!("{}/", url.trim_right_matches('/')), + http_config, + http: None, + } + } +} + +impl DavStorage { + #[inline] + pub fn get_http(&mut self) -> Fallible { + if let Some(ref http) = self.http { + return Ok(http.clone()); + } + let client = self.http_config.clone().into_connection()?.build()?; + self.http = Some(client.clone()); + Ok(client) + } + + #[inline] + pub fn send_request(&mut self, request: reqwest::Request) -> Fallible { + let url = request.url().to_string(); + handle_http_error(&url, send_request(&self.get_http()?, request)?) + } + + pub fn get(&mut self, href: &str) -> Fallible<(Item, String)> { + let base = Url::parse(&self.url)?; + let url = base.join(href)?; + if href != url.path() { + Err(Error::ItemNotFound { + href: href.to_owned(), + })?; + } + + let request = self.get_http()?.get(url).build()?; + let mut response = self.send_request(request)?; + let mut s = String::new(); + response.read_to_string(&mut s)?; + let etag = match response.headers().get::() { + Some(x) => format!("\"{}\"", x.tag()), + None => Err(DavError::EtagNotFound)?, + }; + Ok((Item::from_raw(s), etag)) + } + + pub fn list<'a>( + &'a mut self, + mimetype_contains: &'a str, + ) -> Fallible + 'a>> { + let mut headers = reqwest::header::Headers::new(); + headers.set(ContentType::xml()); + headers.set_raw("Depth", "1"); + + let request = self + .get_http()? + .request(propfind(), &self.url) + .headers(headers) + .body( + r#" + + + + + + + "#, + ) + .build()?; + let response = self.send_request(request)?; + self.parse_prop_response(response, mimetype_contains) + } + + fn parse_prop_response<'a>( + &'a mut self, + response: reqwest::Response, + mimetype_contains: &'a str, + ) -> Fallible + 'a>> { + let buf_reader = BufReader::new(response); + let xml_reader = quick_xml::Reader::from_reader(buf_reader); + + let mut parser = parser::ListingParser::new(xml_reader); + let base = Url::parse(&self.url)?; + let mut seen_hrefs = BTreeSet::new(); + + Ok(Box::new( + parser + .get_all_responses()? + .into_iter() + .filter_map(move |response| { + if response.has_collection_tag { + return None; + } + if !response.mimetype?.contains(mimetype_contains) { + return None; + } + + let href = base.join(&response.href?).ok()?.path().to_owned(); + + if seen_hrefs.contains(&href) { + return None; + } + seen_hrefs.insert(href.clone()); + Some((href, response.etag?)) + }), + )) + } + + fn put( + &mut self, + href: &str, + item: &Item, + mimetype: &str, + etag: Option<&str>, + ) -> Fallible<(String, String)> { + let base = Url::parse(&self.url)?; + let url = base.join(href)?; + let mut request = self.get_http()?.request(reqwest::Method::Put, url); + request.header(ContentType(reqwest::mime::Mime::from_str(mimetype)?)); + if let Some(etag) = etag { + request.header(IfMatch::Items(vec![EntityTag::new( + false, + etag.trim_matches('"').to_owned(), + )])); + } else { + request.header(IfNoneMatch::Any); + } + + let raw = item.get_raw(); + let response = send_request(&self.get_http()?, request.body(raw).build()?)?; + + match (etag, response.status()) { + (Some(_), reqwest::StatusCode::PreconditionFailed) => Err(Error::WrongEtag { + href: href.to_owned(), + })?, + (None, reqwest::StatusCode::PreconditionFailed) => Err(Error::ItemAlreadyExisting { + href: href.to_owned(), + })?, + _ => (), + } + + let response = assert_multistatus_success(handle_http_error(href, response)?)?; + + // The server may not return an etag under certain conditions: + // + // An origin server MUST NOT send a validator header field (Section + // 7.2), such as an ETag or Last-Modified field, in a successful + // response to PUT unless the request's representation data was saved + // without any transformation applied to the body (i.e., the + // resource's new representation data is identical to the + // representation data received in the PUT request) and the validator + // field value reflects the new representation. + // + // -- https://tools.ietf.org/html/rfc7231#section-4.3.4 + // + // In such cases we return a constant etag. The next synchronization + // will then detect an etag change and will download the new item. + let etag = match response.headers().get::() { + Some(x) => format!("\"{}\"", x.tag()), + None => "".to_owned(), + }; + Ok((response.url().path().to_owned(), etag)) + } + + fn delete(&mut self, href: &str, etag: &str) -> Fallible<()> { + let base = Url::parse(&self.url)?; + let url = base.join(href)?; + let request = self + .get_http()? + .request(reqwest::Method::Delete, url) + .header(IfMatch::Items(vec![EntityTag::new( + false, + etag.trim_matches('"').to_owned(), + )])) + .build()?; + let response = send_request(&self.get_http()?, request)?; + + if response.status() == reqwest::StatusCode::PreconditionFailed { + Err(Error::WrongEtag { + href: href.to_owned(), + })?; + } + + assert_multistatus_success(handle_http_error(href, response)?)?; + Ok(()) + } +} + +fn assert_multistatus_success(r: reqwest::Response) -> Fallible { + // TODO + Ok(r) +} + +struct CarddavStorage { + inner: DavStorage, +} + +impl CarddavStorage { + pub fn new(url: &str, http_config: HttpConfig) -> Self { + CarddavStorage { + inner: DavStorage::new(url, http_config), + } + } +} + +impl Storage for CarddavStorage { + fn list<'a>(&'a mut self) -> Fallible + 'a>> { + self.inner.list("vcard") + } + + fn get(&mut self, href: &str) -> Fallible<(Item, String)> { + self.inner.get(href) + } + + fn upload(&mut self, item: Item) -> Fallible<(String, String)> { + let href = format!("{}.vcf", generate_href(&item.get_ident()?)); + self.inner.put(&href, &item, "text/vcard", None) + } + + fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible { + self.inner + .put(&href, &item, "text/vcard", Some(etag)) + .map(|x| x.1) + } + + fn delete(&mut self, href: &str, etag: &str) -> Fallible<()> { + self.inner.delete(href, etag) + } +} + +struct CaldavStorage { + inner: DavStorage, + start_date: Option>, // FIXME: store as Option<(start, end)> + end_date: Option>, + item_types: Vec<&'static str>, +} + +impl CaldavStorage { + pub fn new( + url: &str, + http_config: HttpConfig, + start_date: Option>, + end_date: Option>, + item_types: Vec<&'static str>, + ) -> Self { + CaldavStorage { + inner: DavStorage::new(url, http_config), + start_date, + end_date, + item_types, + } + } + + #[inline] + fn get_caldav_filters(&self) -> Vec { + let mut item_types = self.item_types.clone(); + let mut timefilter = "".to_owned(); + + if let (Some(start), Some(end)) = (self.start_date, self.end_date) { + timefilter = format!( + "", + start.format(CALDAV_DT_FORMAT), + end.format(CALDAV_DT_FORMAT) + ); + + if item_types.is_empty() { + item_types.push("VTODO"); + item_types.push("VEVENT"); + } + } + + item_types + .into_iter() + .map(|item_type| { + format!( + "\ + {}\ + ", + item_type, timefilter + ) + }) + .collect() + } +} + +impl Storage for CaldavStorage { + fn list<'a>(&'a mut self) -> Fallible + 'a>> { + let filters = self.get_caldav_filters(); + if filters.is_empty() { + // If we don't have any filters (which is the default), taking the + // risk of sending a calendar-query is not necessary. There doesn't + // seem to be a widely-usable way to send calendar-queries with the + // same semantics as a PROPFIND request... so why not use PROPFIND + // instead? + // + // See https://github.com/dmfs/tasks/issues/118 for backstory. + self.inner.list("text/calendar") + } else { + let mut rv = vec![]; + let mut headers = reqwest::header::Headers::new(); + headers.set(ContentType::xml()); + headers.set_raw("Depth", "1"); + + for filter in filters { + let data = + format!( + "\ + \ + \ + {}\ + ", filter); + + let request = self + .inner + .get_http()? + .request(report(), &self.inner.url) + .headers(headers.clone()) + .body(data) + .build()?; + let response = self.inner.send_request(request)?; + rv.extend(self.inner.parse_prop_response(response, "text/calendar")?); + } + + Ok(Box::new(rv.into_iter())) + } + } + + fn get(&mut self, href: &str) -> Fallible<(Item, String)> { + self.inner.get(href) + } + + fn upload(&mut self, item: Item) -> Fallible<(String, String)> { + let href = format!("{}.ics", generate_href(&item.get_ident()?)); + self.inner.put(&href, &item, "text/calendar", None) + } + + fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible { + self.inner + .put(href, &item, "text/calendar", Some(etag)) + .map(|x| x.1) + } + + fn delete(&mut self, href: &str, etag: &str) -> Fallible<()> { + self.inner.delete(href, etag) + } +} + +pub mod exports { + use super::super::http::init_http_config; + use super::*; + + #[derive(Debug, Fail, Shippai)] + pub enum DavError { + #[fail(display = "Server did not return etag.")] + EtagNotFound, + } + + use std::ffi::CStr; + use std::os::raw::c_char; + + #[no_mangle] + pub unsafe extern "C" fn vdirsyncer_init_carddav( + url: *const c_char, + username: *const c_char, + password: *const c_char, + useragent: *const c_char, + verify_cert: *const c_char, + auth_cert: *const c_char, + ) -> *mut Box { + let url = CStr::from_ptr(url); + + Box::into_raw(Box::new(Box::new(CarddavStorage::new( + url.to_str().unwrap(), + init_http_config(username, password, useragent, verify_cert, auth_cert), + )))) + } + + #[no_mangle] + pub unsafe extern "C" fn vdirsyncer_init_caldav( + url: *const c_char, + username: *const c_char, + password: *const c_char, + useragent: *const c_char, + verify_cert: *const c_char, + auth_cert: *const c_char, + start_date: i64, + end_date: i64, + include_vevent: bool, + include_vjournal: bool, + include_vtodo: bool, + ) -> *mut Box { + let url = CStr::from_ptr(url); + + let parse_date = |i| { + if i > 0 { + Some(chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp(i, 0), + chrono::Utc, + )) + } else { + None + } + }; + + let mut item_types = vec![]; + if include_vevent { + item_types.push("VEVENT"); + } + if include_vjournal { + item_types.push("VJOURNAL"); + } + if include_vtodo { + item_types.push("VTODO"); + } + + Box::into_raw(Box::new(Box::new(CaldavStorage::new( + url.to_str().unwrap(), + init_http_config(username, password, useragent, verify_cert, auth_cert), + parse_date(start_date), + parse_date(end_date), + item_types, + )))) + } +} + +use exports::DavError; diff --git a/rust/src/storage/dav/parser.rs b/rust/src/storage/dav/parser.rs new file mode 100644 index 0000000..34844c5 --- /dev/null +++ b/rust/src/storage/dav/parser.rs @@ -0,0 +1,110 @@ +use quick_xml; +use quick_xml::events::Event; + +use errors::*; + +use std::io::BufRead; + +#[derive(Debug)] +pub struct Response { + pub href: Option, + pub etag: Option, + pub mimetype: Option, + pub has_collection_tag: bool, +} + +impl Response { + pub fn new() -> Self { + Response { + href: None, + etag: None, + has_collection_tag: false, + mimetype: None, + } + } +} + +pub struct ListingParser { + reader: quick_xml::Reader, + ns_buf: Vec, +} + +impl ListingParser { + pub fn new(mut reader: quick_xml::Reader) -> Self { + reader.expand_empty_elements(true); + reader.trim_text(true); + reader.check_end_names(true); + reader.check_comments(false); + + ListingParser { + reader, + ns_buf: vec![], + } + } + + fn next_response(&mut self) -> Fallible> { + let mut buf = vec![]; + + #[derive(Debug, Clone, Copy)] + enum State { + Outer, + Response, + Href, + ContentType, + Etag, + }; + + let mut state = State::Outer; + let mut current_response = Response::new(); + + loop { + match self + .reader + .read_namespaced_event(&mut buf, &mut self.ns_buf)? + { + (ns, Event::Start(ref e)) => { + match (state, ns, e.local_name()) { + (State::Outer, Some(b"DAV:"), b"response") => state = State::Response, + (State::Response, Some(b"DAV:"), b"href") => state = State::Href, + (State::Response, Some(b"DAV:"), b"getetag") => state = State::Etag, + (State::Response, Some(b"DAV:"), b"getcontenttype") => { + state = State::ContentType + } + (State::Response, Some(b"DAV:"), b"collection") => { + current_response.has_collection_tag = true; + } + _ => (), + } + + debug!("State: {:?}", state); + } + (_, Event::Text(e)) => { + let txt = e.unescape_and_decode(&self.reader)?; + match state { + State::Href => current_response.href = Some(txt), + State::ContentType => current_response.mimetype = Some(txt), + State::Etag => current_response.etag = Some(txt), + _ => continue, + } + state = State::Response; + } + (ns, Event::End(e)) => match (state, ns, e.local_name()) { + (State::Response, Some(b"DAV:"), b"response") => { + return Ok(Some(current_response)) + } + _ => (), + }, + (_, Event::Eof) => return Ok(None), + _ => (), + } + } + } + + pub fn get_all_responses(&mut self) -> Fallible> { + let mut rv = vec![]; + while let Some(x) = self.next_response()? { + rv.push(x); + } + Ok(rv) + } +} diff --git a/rust/src/storage/exports.rs b/rust/src/storage/exports.rs index e0ba467..556cb53 100644 --- a/rust/src/storage/exports.rs +++ b/rust/src/storage/exports.rs @@ -1,11 +1,13 @@ -use std::os::raw::c_char; -use std::ptr; -use std::ffi::{CStr, CString}; +pub use super::dav::exports::*; +pub use super::filesystem::exports::*; +pub use super::http::exports::*; +pub use super::singlefile::exports::*; +use super::Storage; use errors::*; use item::Item; -use super::Storage; -pub use super::singlefile::exports::*; -pub use super::filesystem::exports::*; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use std::ptr; #[no_mangle] pub unsafe extern "C" fn vdirsyncer_storage_free(storage: *mut Box) { diff --git a/rust/src/storage/filesystem.rs b/rust/src/storage/filesystem.rs index 8597ad2..5e8a93f 100644 --- a/rust/src/storage/filesystem.rs +++ b/rust/src/storage/filesystem.rs @@ -1,13 +1,13 @@ -use std::path::{Path, PathBuf}; +use super::Storage; +use errors::*; +use failure; +use libc; use std::fs; use std::io; use std::io::{Read, Write}; use std::os::unix::fs::MetadataExt; +use std::path::{Path, PathBuf}; use std::process::Command; -use super::Storage; -use errors::*; -use libc; -use failure; use super::utils; diff --git a/rust/src/storage/http.rs b/rust/src/storage/http.rs new file mode 100644 index 0000000..078c8b7 --- /dev/null +++ b/rust/src/storage/http.rs @@ -0,0 +1,230 @@ +use std::collections::BTreeMap; +use std::fs::File; +use std::io::Read; + +use std::ffi::CStr; +use std::os::raw::c_char; + +use reqwest; + +use super::singlefile::split_collection; +use super::Storage; +use errors::*; + +use item::Item; + +type ItemCache = BTreeMap; +pub type Username = String; +pub type Password = String; +pub type Auth = (Username, Password); + +/// Wrapper around Client.execute to enable logging +#[inline] +pub fn send_request( + client: &reqwest::Client, + request: reqwest::Request, +) -> Fallible { + debug!("> {} {}", request.method(), request.url()); + for header in request.headers().iter() { + debug!("> {}: {}", header.name(), header.value_string()); + } + debug!("> {:?}", request.body()); + debug!("> ---"); + let response = client.execute(request)?; + debug!("< {:?}", response.status()); + for header in response.headers().iter() { + debug!("< {}: {}", header.name(), header.value_string()); + } + Ok(response) +} + +#[derive(Clone)] +pub struct HttpConfig { + pub auth: Option, + pub useragent: Option, + pub verify_cert: Option, + pub auth_cert: Option, +} + +impl HttpConfig { + pub fn into_connection(self) -> Fallible { + let mut headers = reqwest::header::Headers::new(); + + if let Some((username, password)) = self.auth { + headers.set(reqwest::header::Authorization(reqwest::header::Basic { + username, + password: Some(password), + })); + } + + if let Some(useragent) = self.useragent { + headers.set(reqwest::header::UserAgent::new(useragent)); + } + + let mut client = reqwest::Client::builder(); + client.default_headers(headers); + + if let Some(verify_cert) = self.verify_cert { + let mut buf = Vec::new(); + File::open(verify_cert)?.read_to_end(&mut buf)?; + let cert = reqwest::Certificate::from_pem(&buf)?; + client.add_root_certificate(cert); + } + + // TODO: auth_cert https://github.com/sfackler/rust-native-tls/issues/27 + Ok(client) + } +} + +pub struct HttpStorage { + url: String, + // href -> (item, etag) + items_cache: Option, + http_config: HttpConfig, +} + +impl HttpStorage { + pub fn new(url: String, http_config: HttpConfig) -> Self { + HttpStorage { + url, + items_cache: None, + http_config, + } + } + + fn get_items(&mut self) -> Fallible<&mut ItemCache> { + if self.items_cache.is_none() { + self.list()?; + } + Ok(self.items_cache.as_mut().unwrap()) + } +} + +impl Storage for HttpStorage { + fn list<'a>(&'a mut self) -> Fallible + 'a>> { + let client = self.http_config.clone().into_connection()?.build()?; + + let mut response = handle_http_error(&self.url, client.get(&self.url).send()?)?; + let s = response.text()?; + + let mut new_cache = BTreeMap::new(); + for component in split_collection(&s)? { + let mut item = Item::from_component(component); + item = item.with_uid(&item.get_hash()?)?; + let ident = item.get_ident()?; + let hash = item.get_hash()?; + new_cache.insert(ident, (item, hash)); + } + + self.items_cache = Some(new_cache); + Ok(Box::new(self.items_cache.as_ref().unwrap().iter().map( + |(href, &(_, ref etag))| (href.clone(), etag.clone()), + ))) + } + + fn get(&mut self, href: &str) -> Fallible<(Item, String)> { + match self.get_items()?.get(href) { + Some(&(ref href, ref etag)) => Ok((href.clone(), etag.clone())), + None => Err(Error::ItemNotFound { + href: href.to_owned(), + })?, + } + } + + fn upload(&mut self, _item: Item) -> Fallible<(String, String)> { + Err(Error::ReadOnly)? + } + + fn update(&mut self, _href: &str, _item: Item, _etag: &str) -> Fallible { + Err(Error::ReadOnly)? + } + + fn delete(&mut self, _href: &str, _etag: &str) -> Fallible<()> { + Err(Error::ReadOnly)? + } +} + +pub mod exports { + use super::*; + use std::ffi::CStr; + use std::os::raw::c_char; + + #[no_mangle] + pub unsafe extern "C" fn vdirsyncer_init_http( + url: *const c_char, + username: *const c_char, + password: *const c_char, + useragent: *const c_char, + verify_cert: *const c_char, + auth_cert: *const c_char, + ) -> *mut Box { + let url = CStr::from_ptr(url); + + Box::into_raw(Box::new(Box::new(HttpStorage::new( + url.to_str().unwrap().to_owned(), + init_http_config(username, password, useragent, verify_cert, auth_cert), + )))) + } +} + +pub fn handle_http_error(href: &str, mut r: reqwest::Response) -> Fallible { + if !r.status().is_success() { + debug!("< Error response, dumping body:"); + debug!("< {:?}", r.text()); + } + + match r.status() { + reqwest::StatusCode::NotFound => Err(Error::ItemNotFound { + href: href.to_owned(), + })?, + reqwest::StatusCode::UnsupportedMediaType => Err(Error::UnsupportedVobject { + href: href.to_owned(), + })?, + _ => Ok(r.error_for_status()?), + } +} + +pub unsafe fn init_http_config( + username: *const c_char, + password: *const c_char, + useragent: *const c_char, + verify_cert: *const c_char, + auth_cert: *const c_char, +) -> HttpConfig { + let username = CStr::from_ptr(username); + let password = CStr::from_ptr(password); + let username_dec = username.to_str().unwrap(); + let password_dec = password.to_str().unwrap(); + + let useragent = CStr::from_ptr(useragent); + let useragent_dec = useragent.to_str().unwrap(); + let verify_cert = CStr::from_ptr(verify_cert); + let verify_cert_dec = verify_cert.to_str().unwrap(); + let auth_cert = CStr::from_ptr(auth_cert); + let auth_cert_dec = auth_cert.to_str().unwrap(); + + let auth = if !username_dec.is_empty() && !password_dec.is_empty() { + Some((username_dec.to_owned(), password_dec.to_owned())) + } else { + None + }; + + HttpConfig { + auth, + useragent: if useragent_dec.is_empty() { + None + } else { + Some(useragent_dec.to_owned()) + }, + verify_cert: if verify_cert_dec.is_empty() { + None + } else { + Some(verify_cert_dec.to_owned()) + }, + auth_cert: if auth_cert_dec.is_empty() { + None + } else { + Some(auth_cert_dec.to_owned()) + }, + } +} diff --git a/rust/src/storage/mod.rs b/rust/src/storage/mod.rs index 40fc3e1..84ad7ce 100644 --- a/rust/src/storage/mod.rs +++ b/rust/src/storage/mod.rs @@ -1,6 +1,8 @@ -pub mod singlefile; +mod dav; pub mod exports; -pub mod filesystem; +mod filesystem; +mod http; +mod singlefile; mod utils; use errors::Fallible; use item::Item; diff --git a/rust/src/storage/singlefile.rs b/rust/src/storage/singlefile.rs index 7938fc7..857ddbd 100644 --- a/rust/src/storage/singlefile.rs +++ b/rust/src/storage/singlefile.rs @@ -1,11 +1,11 @@ -use std::path::{Path, PathBuf}; -use std::collections::{BTreeMap, BTreeSet}; -use std::collections::btree_map::Entry::*; -use std::fs::{metadata, File}; -use std::io::{Read, Write}; -use std::time::SystemTime; use super::Storage; use errors::*; +use std::collections::btree_map::Entry::*; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs::{metadata, File}; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::time::SystemTime; use vobject; use atomicwrites::{AllowOverwrite, AtomicFile}; @@ -180,7 +180,7 @@ impl Storage for SinglefileStorage { } } -fn split_collection(mut input: &str) -> Fallible> { +pub fn split_collection(mut input: &str) -> Fallible> { let mut rv = vec![]; while !input.is_empty() { let (component, remainder) = @@ -240,7 +240,8 @@ fn split_vcalendar(mut vcalendar: vobject::Component) -> Fallible x, diff --git a/rust/vdirsyncer_rustext.h b/rust/vdirsyncer_rustext.h index c767bdf..a7590d2 100644 --- a/rust/vdirsyncer_rustext.h +++ b/rust/vdirsyncer_rustext.h @@ -20,6 +20,8 @@ typedef struct { const char *etag; } VdirsyncerStorageUploadResult; +extern const uint8_t SHIPPAI_VARIANT_DavError_EtagNotFound; + extern const uint8_t SHIPPAI_VARIANT_Error_ItemAlreadyExisting; extern const uint8_t SHIPPAI_VARIANT_Error_ItemNotFound; @@ -28,10 +30,14 @@ extern const uint8_t SHIPPAI_VARIANT_Error_ItemUnparseable; extern const uint8_t SHIPPAI_VARIANT_Error_MtimeMismatch; +extern const uint8_t SHIPPAI_VARIANT_Error_ReadOnly; + extern const uint8_t SHIPPAI_VARIANT_Error_UnexpectedVobject; extern const uint8_t SHIPPAI_VARIANT_Error_UnexpectedVobjectVersion; +extern const uint8_t SHIPPAI_VARIANT_Error_UnsupportedVobject; + extern const uint8_t SHIPPAI_VARIANT_Error_WrongEtag; void shippai_free_failure(ShippaiError *t); @@ -42,8 +48,12 @@ const char *shippai_get_debug(ShippaiError *t); const char *shippai_get_display(ShippaiError *t); +uint8_t shippai_get_variant_DavError(ShippaiError *t); + uint8_t shippai_get_variant_Error(ShippaiError *t); +bool shippai_is_error_DavError(ShippaiError *t); + bool shippai_is_error_Error(ShippaiError *t); bool vdirsyncer_advance_storage_listing(VdirsyncerStorageListing *listing); @@ -64,10 +74,38 @@ const char *vdirsyncer_get_raw(Item *c); const char *vdirsyncer_get_uid(Item *c); +Box_Storage *vdirsyncer_init_caldav(const char *url, + const char *username, + const char *password, + const char *useragent, + const char *verify_cert, + const char *auth_cert, + int64_t start_date, + int64_t end_date, + bool include_vevent, + bool include_vjournal, + bool include_vtodo); + +Box_Storage *vdirsyncer_init_carddav(const char *url, + const char *username, + const char *password, + const char *useragent, + const char *verify_cert, + const char *auth_cert); + Box_Storage *vdirsyncer_init_filesystem(const char *path, const char *fileext, const char *post_hook); +Box_Storage *vdirsyncer_init_http(const char *url, + const char *username, + const char *password, + const char *useragent, + const char *verify_cert, + const char *auth_cert); + +void vdirsyncer_init_logger(void); + Box_Storage *vdirsyncer_init_singlefile(const char *path); Item *vdirsyncer_item_from_raw(const char *s); diff --git a/setup.py b/setup.py index 10c7842..11fb6fd 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ how to package vdirsyncer. ''' +import os from setuptools import Command, find_packages, setup milksnake = 'milksnake' @@ -42,15 +43,19 @@ requirements = [ def build_native(spec): - build = spec.add_external_build( - cmd=['cargo', 'build', '--release'], - path='./rust/' - ) + cmd = ['cargo', 'build'] + if os.environ.get('RUST_BACKTRACE', 'false') in ('true', '1', 'full'): + dylib_folder = 'target/debug' + else: + dylib_folder = 'target/release' + cmd.append('--release') + + build = spec.add_external_build(cmd=cmd, path='./rust/') spec.add_cffi_module( module_path='vdirsyncer._native', dylib=lambda: build.find_dylib('vdirsyncer_rustext', - in_path='target/release'), + in_path=dylib_folder), header_filename='rust/vdirsyncer_rustext.h', # Rust bug: If thread-local storage is used, this flag is necessary # (mitsuhiko) diff --git a/tests/__init__.py b/tests/__init__.py index d1c7e23..aa4d0e8 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -57,6 +57,7 @@ END:VCALENDAR''' BARE_EVENT_TEMPLATE = u'''BEGIN:VEVENT DTSTART:19970714T170000Z DTEND:19970715T035959Z +DTSTAMP:19970610T172345Z SUMMARY:Bastille Day Party X-SOMETHING:{r} UID:{uid} diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py index 41a2899..eb0b5c0 100644 --- a/tests/storage/__init__.py +++ b/tests/storage/__init__.py @@ -3,7 +3,6 @@ import uuid import textwrap -from urllib.parse import quote as urlquote, unquote as urlunquote import pytest @@ -133,6 +132,8 @@ class StorageTests(object): def test_delete(self, s, get_item): href, etag = s.upload(get_item()) + if etag is None: + _, etag = s.get(href) s.delete(href, etag) assert not list(s.list()) @@ -150,6 +151,8 @@ class StorageTests(object): def test_has(self, s, get_item): assert not s.has('asd') href, etag = s.upload(get_item()) + if etag is None: + _, etag = s.get(href) assert s.has(href) assert not s.has('asd') s.delete(href, etag) @@ -236,38 +239,6 @@ class StorageTests(object): assert len(items) == 2 assert len(set(items)) == 2 - def test_specialchars(self, monkeypatch, requires_collections, - get_storage_args, get_item): - if getattr(self, 'dav_server', '') == 'radicale': - pytest.skip('Radicale is fundamentally broken.') - if getattr(self, 'dav_server', '') in ('icloud', 'fastmail'): - pytest.skip('iCloud and FastMail reject this name.') - - monkeypatch.setattr('vdirsyncer.utils.generate_href', lambda x: x) - - uid = u'test @ foo ät bar град сатану' - collection = 'test @ foo ät bar' - - s = self.storage_class(**get_storage_args(collection=collection)) - item = get_item(uid=uid) - - href, etag = s.upload(item) - item2, etag2 = s.get(href) - if etag is not None: - assert etag2 == etag - assert_item_equals(item2, item) - - (_, etag3), = s.list() - assert etag2 == etag3 - - # etesync uses UUIDs for collection names - if self.storage_class.storage_name.startswith('etesync'): - return - - assert collection in urlunquote(s.collection) - if self.storage_class.storage_name.endswith('dav'): - assert urlquote(uid, '/@:') in href - def test_metadata(self, requires_metadata, s): if not getattr(self, 'dav_server', ''): assert not s.get_meta('color') diff --git a/tests/storage/dav/__init__.py b/tests/storage/dav/__init__.py index b59e4d3..1a2e6ea 100644 --- a/tests/storage/dav/__init__.py +++ b/tests/storage/dav/__init__.py @@ -1,13 +1,7 @@ # -*- coding: utf-8 -*- -import uuid - import os -import pytest - -from tests import assert_item_equals - from .. import StorageTests, get_server_mixin @@ -29,14 +23,3 @@ class DAVStorageTests(ServerMixin, StorageTests): finally: # Make sure monkeypatch doesn't interfere with DAV server teardown monkeypatch.undo() - - def test_dav_unicode_href(self, s, get_item, monkeypatch): - if self.dav_server == 'radicale': - pytest.skip('Radicale is unable to deal with unicode hrefs') - - monkeypatch.setattr(s, '_get_href', - lambda item: item.ident + s.fileext) - item = get_item(uid=u'град сатану' + str(uuid.uuid4())) - href, etag = s.upload(item) - item2, etag2 = s.get(href) - assert_item_equals(item, item2) diff --git a/tests/storage/dav/test_caldav.py b/tests/storage/dav/test_caldav.py index ed5657d..af552b8 100644 --- a/tests/storage/dav/test_caldav.py +++ b/tests/storage/dav/test_caldav.py @@ -5,12 +5,8 @@ from textwrap import dedent import pytest -import requests -import requests.exceptions - from tests import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE -from vdirsyncer import exceptions from vdirsyncer.storage.dav import CalDAVStorage from . import DAVStorageTests, dav_server @@ -29,33 +25,10 @@ class TestCalDAVStorage(DAVStorageTests): try: s.upload(format_item(item_template=VCARD_TEMPLATE)) - except (exceptions.Error, requests.exceptions.HTTPError): + except Exception: pass assert not list(s.list()) - # The `arg` param is not named `item_types` because that would hit - # https://bitbucket.org/pytest-dev/pytest/issue/745/ - @pytest.mark.parametrize('arg,calls_num', [ - (('VTODO',), 1), - (('VEVENT',), 1), - (('VTODO', 'VEVENT'), 2), - (('VTODO', 'VEVENT', 'VJOURNAL'), 3), - ((), 1) - ]) - def test_item_types_performance(self, get_storage_args, arg, calls_num, - monkeypatch): - s = self.storage_class(item_types=arg, **get_storage_args()) - old_parse = s._parse_prop_responses - calls = [] - - def new_parse(*a, **kw): - calls.append(None) - return old_parse(*a, **kw) - - monkeypatch.setattr(s, '_parse_prop_responses', new_parse) - list(s.list()) - assert len(calls) == calls_num - @pytest.mark.xfail(dav_server == 'radicale', reason='Radicale doesn\'t support timeranges.') def test_timerange_correctness(self, get_storage_args): @@ -113,40 +86,19 @@ class TestCalDAVStorage(DAVStorageTests): (actual_href, _), = s.list() assert actual_href == expected_href - def test_invalid_resource(self, monkeypatch, get_storage_args): - calls = [] - args = get_storage_args(collection=None) - - def request(session, method, url, **kwargs): - assert url == args['url'] - calls.append(None) - - r = requests.Response() - r.status_code = 200 - r._content = b'Hello World.' - return r - - monkeypatch.setattr('requests.sessions.Session.request', request) - - with pytest.raises(ValueError): - s = self.storage_class(**args) - list(s.list()) - assert len(calls) == 1 - @pytest.mark.skipif(dav_server == 'icloud', reason='iCloud only accepts VEVENT') - def test_item_types_general(self, s): + def test_item_types_general(self, get_storage_args): + args = get_storage_args() + s = self.storage_class(**args) event = s.upload(format_item(item_template=EVENT_TEMPLATE))[0] task = s.upload(format_item(item_template=TASK_TEMPLATE))[0] - s.item_types = ('VTODO', 'VEVENT') - def l(): - return set(href for href, etag in s.list()) - - assert l() == {event, task} - s.item_types = ('VTODO',) - assert l() == {task} - s.item_types = ('VEVENT',) - assert l() == {event} - s.item_types = () - assert l() == {event, task} + for item_types, expected_items in [ + (('VTODO', 'VEVENT'), {event, task}), + (('VTODO',), {task}), + (('VEVENT',), {event}), + ]: + args['item_types'] = item_types + s = self.storage_class(**args) + assert set(href for href, etag in s.list()) == expected_items diff --git a/tests/storage/servers/nextcloud/__init__.py b/tests/storage/servers/nextcloud/__init__.py index 476461b..90eca54 100644 --- a/tests/storage/servers/nextcloud/__init__.py +++ b/tests/storage/servers/nextcloud/__init__.py @@ -3,7 +3,7 @@ import requests import pytest -port = os.environ.get('NEXTCLOUD_HOST', None) or 'localhost:8080' +port = os.environ.get('NEXTCLOUD_HOST', None) or 'localhost:5000' user = os.environ.get('NEXTCLOUD_USER', None) or 'asdf' pwd = os.environ.get('NEXTCLOUD_PASS', None) or 'asdf' diff --git a/tests/storage/servers/xandikos/__init__.py b/tests/storage/servers/xandikos/__init__.py index 570d87f..05f3749 100644 --- a/tests/storage/servers/xandikos/__init__.py +++ b/tests/storage/servers/xandikos/__init__.py @@ -1,35 +1,15 @@ import pytest -from xandikos.web import XandikosApp, XandikosBackend, WellknownRedirector - -import wsgi_intercept -import wsgi_intercept.requests_intercept - class ServerMixin(object): @pytest.fixture def get_storage_args(self, request, tmpdir, slow_create_collection): - tmpdir.mkdir('xandikos') - backend = XandikosBackend(path=str(tmpdir)) - cup = '/user/' - backend.create_principal(cup, create_defaults=True) - app = XandikosApp(backend, cup) - - app = WellknownRedirector(app, '/') - - wsgi_intercept.requests_intercept.install() - wsgi_intercept.add_wsgi_intercept('127.0.0.1', 8080, lambda: app) - - def teardown(): - wsgi_intercept.remove_wsgi_intercept('127.0.0.1', 8080) - wsgi_intercept.requests_intercept.uninstall() - request.addfinalizer(teardown) - def inner(collection='test'): - url = 'http://127.0.0.1:8080/' - args = {'url': url, 'collection': collection} + url = 'http://127.0.0.1:5001/' + args = {'url': url} if collection is not None: - args = self.storage_class.create_collection(**args) + args = slow_create_collection(self.storage_class, args, + collection) return args return inner diff --git a/tests/storage/servers/xandikos/install.sh b/tests/storage/servers/xandikos/install.sh index 5191756..e69de29 100644 --- a/tests/storage/servers/xandikos/install.sh +++ b/tests/storage/servers/xandikos/install.sh @@ -1,13 +0,0 @@ -#!/bin/sh -set -e - -pip install wsgi_intercept - -if [ "$REQUIREMENTS" = "release" ] || [ "$REQUIREMENTS" = "minimal" ]; then - pip install -U xandikos -elif [ "$REQUIREMENTS" = "devel" ]; then - pip install -U git+https://github.com/jelmer/xandikos -else - echo "Invalid REQUIREMENTS value" - false -fi diff --git a/tests/storage/test_http.py b/tests/storage/test_http.py deleted file mode 100644 index d6e6ff9..0000000 --- a/tests/storage/test_http.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- - -import pytest - -from requests import Response - -from vdirsyncer.exceptions import UserError -from vdirsyncer.storage.http import HttpStorage, prepare_auth -from vdirsyncer.vobject import Item - - -def test_list(monkeypatch): - collection_url = 'http://127.0.0.1/calendar/collection.ics' - - items = [ - (u'BEGIN:VEVENT\n' - u'SUMMARY:Eine Kurzinfo\n' - u'DESCRIPTION:Beschreibung des Termines\n' - u'END:VEVENT'), - (u'BEGIN:VEVENT\n' - u'SUMMARY:Eine zweite Küèrzinfo\n' - u'DESCRIPTION:Beschreibung des anderen Termines\n' - u'BEGIN:VALARM\n' - u'ACTION:AUDIO\n' - u'TRIGGER:19980403T120000\n' - u'ATTACH;FMTTYPE=audio/basic:http://host.com/pub/ssbanner.aud\n' - u'REPEAT:4\n' - u'DURATION:PT1H\n' - u'END:VALARM\n' - u'END:VEVENT') - ] - - responses = [ - u'\n'.join([u'BEGIN:VCALENDAR'] + items + [u'END:VCALENDAR']) - ] * 2 - - def get(self, method, url, *a, **kw): - assert method == 'GET' - assert url == collection_url - r = Response() - r.status_code = 200 - assert responses - r._content = responses.pop().encode('utf-8') - r.headers['Content-Type'] = 'text/calendar' - r.encoding = 'ISO-8859-1' - return r - - monkeypatch.setattr('requests.sessions.Session.request', get) - - s = HttpStorage(url=collection_url) - - found_items = {} - - for href, etag in s.list(): - item, etag2 = s.get(href) - assert item.uid is not None - assert etag2 == etag - found_items[item.hash] = href - - expected = set(Item(u'BEGIN:VCALENDAR\n' + x + '\nEND:VCALENDAR').hash - for x in items) - - assert set(found_items) == expected - - for href, etag in s.list(): - item, etag2 = s.get(href) - assert item.uid is not None - assert etag2 == etag - assert found_items[item.hash] == href - - -def test_readonly_param(): - url = 'http://example.com/' - with pytest.raises(ValueError): - HttpStorage(url=url, read_only=False) - - a = HttpStorage(url=url, read_only=True).read_only - b = HttpStorage(url=url, read_only=None).read_only - assert a is b is True - - -def test_prepare_auth(): - assert prepare_auth(None, '', '') is None - - assert prepare_auth(None, 'user', 'pwd') == ('user', 'pwd') - assert prepare_auth('basic', 'user', 'pwd') == ('user', 'pwd') - - with pytest.raises(ValueError) as excinfo: - assert prepare_auth('basic', '', 'pwd') - assert 'you need to specify username and password' in \ - str(excinfo.value).lower() - - from requests.auth import HTTPDigestAuth - assert isinstance(prepare_auth('digest', 'user', 'pwd'), - HTTPDigestAuth) - - with pytest.raises(ValueError) as excinfo: - prepare_auth('ladida', 'user', 'pwd') - - assert 'unknown authentication method' in str(excinfo.value).lower() - - -def test_prepare_auth_guess(monkeypatch): - import requests_toolbelt.auth.guess - - assert isinstance(prepare_auth('guess', 'user', 'pwd'), - requests_toolbelt.auth.guess.GuessAuth) - - monkeypatch.delattr(requests_toolbelt.auth.guess, 'GuessAuth') - - with pytest.raises(UserError) as excinfo: - prepare_auth('guess', 'user', 'pwd') - - assert 'requests_toolbelt is too old' in str(excinfo.value).lower() - - -def test_verify_false_disallowed(): - with pytest.raises(ValueError) as excinfo: - HttpStorage(url='http://example.com', verify=False) - - assert 'forbidden' in str(excinfo.value).lower() - assert 'consider setting verify_fingerprint' in str(excinfo.value).lower() diff --git a/tests/storage/test_http_with_singlefile.py b/tests/storage/test_http_with_singlefile.py deleted file mode 100644 index 9f86ebd..0000000 --- a/tests/storage/test_http_with_singlefile.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- - -import pytest - -from requests import Response - -import vdirsyncer.storage.http -from vdirsyncer.storage.base import Storage -from vdirsyncer.storage.singlefile import SingleFileStorage - -from . import StorageTests - - -class CombinedStorage(Storage): - '''A subclass of HttpStorage to make testing easier. It supports writes via - SingleFileStorage.''' - _repr_attributes = ('url', 'path') - storage_name = 'http_and_singlefile' - - def __init__(self, url, path, **kwargs): - if kwargs.get('collection', None) is not None: - raise ValueError() - - super(CombinedStorage, self).__init__(**kwargs) - self.url = url - self.path = path - self._reader = vdirsyncer.storage.http.HttpStorage(url=url) - self._reader._ignore_uids = False - self._writer = SingleFileStorage(path=path) - - def list(self, *a, **kw): - return self._reader.list(*a, **kw) - - def get(self, *a, **kw): - self.list() - return self._reader.get(*a, **kw) - - def upload(self, *a, **kw): - return self._writer.upload(*a, **kw) - - def update(self, *a, **kw): - return self._writer.update(*a, **kw) - - def delete(self, *a, **kw): - return self._writer.delete(*a, **kw) - - -class TestHttpStorage(StorageTests): - storage_class = CombinedStorage - supports_collections = False - supports_metadata = False - - @pytest.fixture(autouse=True) - def setup_tmpdir(self, tmpdir, monkeypatch): - self.tmpfile = str(tmpdir.ensure('collection.txt')) - - def _request(method, url, *args, **kwargs): - assert method == 'GET' - assert url == 'http://localhost:123/collection.txt' - assert 'vdirsyncer' in kwargs['headers']['User-Agent'] - r = Response() - r.status_code = 200 - try: - with open(self.tmpfile, 'rb') as f: - r._content = f.read() - except IOError: - r._content = b'' - - r.headers['Content-Type'] = 'text/calendar' - r.encoding = 'utf-8' - return r - - monkeypatch.setattr(vdirsyncer.storage.http, 'request', _request) - - @pytest.fixture - def get_storage_args(self): - def inner(collection=None): - assert collection is None - return {'url': 'http://localhost:123/collection.txt', - 'path': self.tmpfile} - return inner diff --git a/vdirsyncer/exceptions.py b/vdirsyncer/exceptions.py index 3346cc3..4c0a43c 100644 --- a/vdirsyncer/exceptions.py +++ b/vdirsyncer/exceptions.py @@ -83,3 +83,7 @@ class CollectionRequired(Error): class VobjectParseError(Error, ValueError): '''The parsed vobject is invalid.''' + + +class UnsupportedVobjectError(Error, ValueError): + '''The server rejected the vobject because of its type''' diff --git a/vdirsyncer/native.py b/vdirsyncer/native.py index e3151ce..107adbe 100644 --- a/vdirsyncer/native.py +++ b/vdirsyncer/native.py @@ -3,6 +3,8 @@ import shippai from . import exceptions from ._native import ffi, lib +lib.vdirsyncer_init_logger() + errors = shippai.Shippai(ffi, lib) @@ -31,3 +33,7 @@ def check_error(e): raise exceptions.AlreadyExistingError(e) except errors.Error.WrongEtag as e: raise exceptions.WrongEtagError(e) + except errors.Error.ReadOnly as e: + raise exceptions.ReadOnlyError(e) + except errors.Error.UnsupportedVobject as e: + raise exceptions.UnsupportedVobjectError(e) diff --git a/vdirsyncer/storage/_rust.py b/vdirsyncer/storage/_rust.py index 9f63c98..9c29110 100644 --- a/vdirsyncer/storage/_rust.py +++ b/vdirsyncer/storage/_rust.py @@ -46,7 +46,7 @@ class RustStorageMixin: result, native.lib.vdirsyncer_free_storage_upload_result) href = native.string_rv(result.href) etag = native.string_rv(result.etag) - return href, etag + return href, etag or None def update(self, href, item, etag): href = href.encode('utf-8') @@ -54,7 +54,7 @@ class RustStorageMixin: e = native.get_error_pointer() etag = self._native('update')(href, item._native, etag, e) native.check_error(e) - return native.string_rv(etag) + return native.string_rv(etag) or None def delete(self, href, etag): href = href.encode('utf-8') diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 3180701..2417bb2 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -11,10 +11,10 @@ import requests from requests.exceptions import HTTPError from .base import Storage, normalize_meta_value -from .. import exceptions, http, utils +from ._rust import RustStorageMixin +from .. import exceptions, http, native, utils from ..http import USERAGENT, prepare_auth, \ prepare_client_cert, prepare_verify -from ..vobject import Item dav_logger = logging.getLogger(__name__) @@ -41,22 +41,6 @@ def _contains_quoted_reserved_chars(x): return False -def _assert_multistatus_success(r): - # Xandikos returns a multistatus on PUT. - try: - root = _parse_xml(r.content) - except InvalidXMLResponse: - return - for status in root.findall('.//{DAV:}status'): - parts = status.text.strip().split() - try: - st = int(parts[1]) - except (ValueError, IndexError): - continue - if st < 200 or st >= 400: - raise HTTPError('Server error: {}'.format(st)) - - def _normalize_href(base, href): '''Normalize the href to be a path only relative to hostname and schema.''' @@ -396,7 +380,7 @@ class DAVSession(object): } -class DAVStorage(Storage): +class DAVStorage(RustStorageMixin, Storage): # the file extension of items. Useful for testing against radicale. fileext = None # mimetype of items @@ -443,200 +427,9 @@ class DAVStorage(Storage): def _normalize_href(self, *args, **kwargs): return _normalize_href(self.session.url, *args, **kwargs) - def _get_href(self, item): - href = utils.generate_href(item.ident) - return self._normalize_href(href + self.fileext) - def _is_item_mimetype(self, mimetype): return _fuzzy_matches_mimetype(self.item_mimetype, mimetype) - def get(self, href): - ((actual_href, item, etag),) = self.get_multi([href]) - assert href == actual_href - return item, etag - - def get_multi(self, hrefs): - hrefs = set(hrefs) - href_xml = [] - for href in hrefs: - if href != self._normalize_href(href): - raise exceptions.NotFoundError(href) - href_xml.append('{}'.format(href)) - if not href_xml: - return () - - data = self.get_multi_template \ - .format(hrefs='\n'.join(href_xml)).encode('utf-8') - response = self.session.request( - 'REPORT', - '', - data=data, - headers=self.session.get_default_headers() - ) - root = _parse_xml(response.content) # etree only can handle bytes - rv = [] - hrefs_left = set(hrefs) - for href, etag, prop in self._parse_prop_responses(root): - raw = prop.find(self.get_multi_data_query) - if raw is None: - dav_logger.warning('Skipping {}, the item content is missing.' - .format(href)) - continue - - raw = raw.text or u'' - - if isinstance(raw, bytes): - raw = raw.decode(response.encoding) - if isinstance(etag, bytes): - etag = etag.decode(response.encoding) - - try: - hrefs_left.remove(href) - except KeyError: - if href in hrefs: - dav_logger.warning('Server sent item twice: {}' - .format(href)) - else: - dav_logger.warning('Server sent unsolicited item: {}' - .format(href)) - else: - rv.append((href, Item(raw), etag)) - for href in hrefs_left: - raise exceptions.NotFoundError(href) - return rv - - def _put(self, href, item, etag): - headers = self.session.get_default_headers() - headers['Content-Type'] = self.item_mimetype - if etag is None: - headers['If-None-Match'] = '*' - else: - headers['If-Match'] = etag - - response = self.session.request( - 'PUT', - href, - data=item.raw.encode('utf-8'), - headers=headers - ) - - _assert_multistatus_success(response) - - # The server may not return an etag under certain conditions: - # - # An origin server MUST NOT send a validator header field (Section - # 7.2), such as an ETag or Last-Modified field, in a successful - # response to PUT unless the request's representation data was saved - # without any transformation applied to the body (i.e., the - # resource's new representation data is identical to the - # representation data received in the PUT request) and the validator - # field value reflects the new representation. - # - # -- https://tools.ietf.org/html/rfc7231#section-4.3.4 - # - # In such cases we return a constant etag. The next synchronization - # will then detect an etag change and will download the new item. - etag = response.headers.get('etag', None) - href = self._normalize_href(response.url) - return href, etag - - def update(self, href, item, etag): - if etag is None: - raise ValueError('etag must be given and must not be None.') - href, etag = self._put(self._normalize_href(href), item, etag) - return etag - - def upload(self, item): - href = self._get_href(item) - return self._put(href, item, None) - - def delete(self, href, etag): - href = self._normalize_href(href) - headers = self.session.get_default_headers() - headers.update({ - 'If-Match': etag - }) - - self.session.request( - 'DELETE', - href, - headers=headers - ) - - def _parse_prop_responses(self, root, handled_hrefs=None): - if handled_hrefs is None: - handled_hrefs = set() - for response in root.iter('{DAV:}response'): - href = response.find('{DAV:}href') - if href is None: - dav_logger.error('Skipping response, href is missing.') - continue - - href = self._normalize_href(href.text) - - if href in handled_hrefs: - # Servers that send duplicate hrefs: - # - Zimbra - # https://github.com/pimutils/vdirsyncer/issues/88 - # - Davmail - # https://github.com/pimutils/vdirsyncer/issues/144 - dav_logger.warning('Skipping identical href: {!r}' - .format(href)) - continue - - props = response.findall('{DAV:}propstat/{DAV:}prop') - if props is None or not len(props): - dav_logger.debug('Skipping {!r}, properties are missing.' - .format(href)) - continue - else: - props = _merge_xml(props) - - if props.find('{DAV:}resourcetype/{DAV:}collection') is not None: - dav_logger.debug('Skipping {!r}, is collection.'.format(href)) - continue - - etag = getattr(props.find('{DAV:}getetag'), 'text', '') - if not etag: - dav_logger.debug('Skipping {!r}, etag property is missing.' - .format(href)) - continue - - contenttype = getattr(props.find('{DAV:}getcontenttype'), - 'text', None) - if not self._is_item_mimetype(contenttype): - dav_logger.debug('Skipping {!r}, {!r} != {!r}.' - .format(href, contenttype, - self.item_mimetype)) - continue - - handled_hrefs.add(href) - yield href, etag, props - - def list(self): - headers = self.session.get_default_headers() - headers['Depth'] = '1' - - data = ''' - - - - - - - - '''.encode('utf-8') - - # We use a PROPFIND request instead of addressbook-query due to issues - # with Zimbra. See https://github.com/pimutils/vdirsyncer/issues/83 - response = self.session.request('PROPFIND', '', data=data, - headers=headers) - root = _parse_xml(response.content) - - rv = self._parse_prop_responses(root) - for href, etag, _prop in rv: - yield href, etag - def get_meta(self, key): try: tagname, namespace = self._property_table[key] @@ -734,7 +527,7 @@ class CalDAVStorage(DAVStorage): if not isinstance(item_types, (list, tuple)): raise exceptions.UserError('item_types must be a list.') - self.item_types = tuple(item_types) + self.item_types = tuple(x.upper() for x in item_types) if (start_date is None) != (end_date is None): raise exceptions.UserError('If start_date is given, ' 'end_date has to be given too.') @@ -749,77 +542,22 @@ class CalDAVStorage(DAVStorage): if isinstance(end_date, (bytes, str)) else end_date) - @staticmethod - def _get_list_filters(components, start, end): - caldavfilter = ''' - - - {timefilter} - - - ''' - - timefilter = '' - - if start is not None and end is not None: - start = start.strftime(CALDAV_DT_FORMAT) - end = end.strftime(CALDAV_DT_FORMAT) - - timefilter = ('' - .format(start=start, end=end)) - if not components: - components = ('VTODO', 'VEVENT') - - for component in components: - yield caldavfilter.format(component=component, - timefilter=timefilter) - - def list(self): - caldavfilters = list(self._get_list_filters( - self.item_types, - self.start_date, - self.end_date - )) - if not caldavfilters: - # If we don't have any filters (which is the default), taking the - # risk of sending a calendar-query is not necessary. There doesn't - # seem to be a widely-usable way to send calendar-queries with the - # same semantics as a PROPFIND request... so why not use PROPFIND - # instead? - # - # See https://github.com/dmfs/tasks/issues/118 for backstory. - yield from DAVStorage.list(self) - return - - data = ''' - - - - - - - {caldavfilter} - - ''' - - headers = self.session.get_default_headers() - # https://github.com/pimutils/vdirsyncer/issues/166 - # The default in CalDAV's calendar-queries is 0, but the examples use - # an explicit value of 1 for querying items. it is extremely unclear in - # the spec which values from WebDAV are actually allowed. - headers['Depth'] = '1' - - handled_hrefs = set() - - for caldavfilter in caldavfilters: - xml = data.format(caldavfilter=caldavfilter).encode('utf-8') - response = self.session.request('REPORT', '', data=xml, - headers=headers) - root = _parse_xml(response.content) - rv = self._parse_prop_responses(root, handled_hrefs) - for href, etag, _prop in rv: - yield href, etag + self._native_storage = native.ffi.gc( + native.lib.vdirsyncer_init_caldav( + kwargs['url'].encode('utf-8'), + kwargs.get('username', '').encode('utf-8'), + kwargs.get('password', '').encode('utf-8'), + kwargs.get('useragent', '').encode('utf-8'), + kwargs.get('verify_cert', '').encode('utf-8'), + kwargs.get('auth_cert', '').encode('utf-8'), + int(self.start_date.timestamp()) if self.start_date else -1, + int(self.end_date.timestamp()) if self.end_date else -1, + 'VEVENT' in item_types, + 'VJOURNAL' in item_types, + 'VTODO' in item_types + ), + native.lib.vdirsyncer_storage_free + ) class CardDAVStorage(DAVStorage): @@ -839,3 +577,18 @@ class CardDAVStorage(DAVStorage): ''' get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data' + + def __init__(self, **kwargs): + self._native_storage = native.ffi.gc( + native.lib.vdirsyncer_init_carddav( + kwargs['url'].encode('utf-8'), + kwargs.get('username', '').encode('utf-8'), + kwargs.get('password', '').encode('utf-8'), + kwargs.get('useragent', '').encode('utf-8'), + kwargs.get('verify_cert', '').encode('utf-8'), + kwargs.get('auth_cert', '').encode('utf-8') + ), + native.lib.vdirsyncer_storage_free + ) + + super(CardDAVStorage, self).__init__(**kwargs) diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py index e2ea90f..1c52984 100644 --- a/vdirsyncer/storage/http.py +++ b/vdirsyncer/storage/http.py @@ -1,15 +1,12 @@ # -*- coding: utf-8 -*- -import urllib.parse as urlparse - from .base import Storage -from .. import exceptions -from ..http import USERAGENT, prepare_auth, \ - prepare_client_cert, prepare_verify, request -from ..vobject import Item, split_collection +from ._rust import RustStorageMixin +from .. import exceptions, native +from ..http import USERAGENT -class HttpStorage(Storage): +class HttpStorage(RustStorageMixin, Storage): storage_name = 'http' read_only = True _repr_attributes = ('username', 'url') @@ -18,49 +15,27 @@ class HttpStorage(Storage): # Required for tests. _ignore_uids = True - def __init__(self, url, username='', password='', verify=True, auth=None, - useragent=USERAGENT, verify_fingerprint=None, auth_cert=None, - **kwargs): + def __init__(self, url, username='', password='', useragent=USERAGENT, + verify_cert=None, auth_cert=None, **kwargs): + if kwargs.get('collection') is not None: + raise exceptions.UserError('HttpStorage does not support ' + 'collections.') + + assert auth_cert is None, "not yet supported" + super(HttpStorage, self).__init__(**kwargs) - self._settings = { - 'auth': prepare_auth(auth, username, password), - 'cert': prepare_client_cert(auth_cert), - 'latin1_fallback': False, - } - self._settings.update(prepare_verify(verify, verify_fingerprint)) + self._native_storage = native.ffi.gc( + native.lib.vdirsyncer_init_http( + url.encode('utf-8'), + (username or "").encode('utf-8'), + (password or "").encode('utf-8'), + (useragent or "").encode('utf-8'), + (verify_cert or "").encode('utf-8'), + (auth_cert or "").encode('utf-8') + ), + native.lib.vdirsyncer_storage_free + ) - self.username, self.password = username, password - self.useragent = useragent - - collection = kwargs.get('collection') - if collection is not None: - url = urlparse.urljoin(url, collection) + self.username = username self.url = url - self.parsed_url = urlparse.urlparse(self.url) - - def _default_headers(self): - return {'User-Agent': self.useragent} - - def list(self): - r = request('GET', self.url, headers=self._default_headers(), - **self._settings) - self._items = {} - - for item in split_collection(r.text): - item = Item(item) - if self._ignore_uids: - item = item.with_uid(item.hash) - - self._items[item.ident] = item, item.hash - - return ((href, etag) for href, (item, etag) in self._items.items()) - - def get(self, href): - if self._items is None: - self.list() - - try: - return self._items[href] - except KeyError: - raise exceptions.NotFoundError(href)