From 8089559958bfc1fbb47d40bd15eaca95a40296db Mon Sep 17 00:00:00 2001 From: holger krekel Date: Sun, 21 Jul 2019 23:31:14 +0200 Subject: [PATCH] Squashed commit of the following: commit 6bc5d1b90edc9cfe77a7c2b1101d731977d7eeb1 Author: holger krekel Date: Sun Jul 21 22:56:37 2019 +0200 fix fmt commit 197d94ad9d9c5982c9caffe48eeb6921b672fa8b Merge: 7ce337c 686678c Author: holger krekel Date: Sun Jul 21 22:51:16 2019 +0200 Merge remote-tracking branch 'origin/master' into eventlogging commit 7ce337c6d0777f8aeee23cb3fd0f57f3f0caaab4 Author: holger krekel Date: Sun Jul 21 22:44:27 2019 +0200 left-over error logging commit 10148d2e43c94873d87aa96cb0929077059c81ca Author: holger krekel Date: Sun Jul 21 22:03:17 2019 +0200 ignore non-utf8 parts of header fields (add comment why it shouldn't happen) don't throw error if no sql rows are returned commit 69dc237ee3367e5ca8e8bc397fffbba8d7ccf4f7 Author: dignifiedquire Date: Sun Jul 21 12:56:04 2019 +0200 fix(receive_imf): remove recursive sql call commit df5464ea8030dccfb0ddfc7a7e3eb4be52f6c37b Author: dignifiedquire Date: Sat Jul 20 17:05:24 2019 +0200 fix: blocked is an optional value commit e4bf9956a5075b6aa7d07a3e2297831530984e51 Author: dignifiedquire Date: Sat Jul 20 16:50:56 2019 +0200 fix(msg): handle optional in_reply_to commit d353d9d9d8f671d14afddea2b33554cad4996a3e Author: dignifiedquire Date: Sat Jul 20 16:17:25 2019 +0200 fix(chat): remove recursive sql usage commit 1ad45ed4d67a077debce4fa9a2ee1a283f6442db Author: holger krekel Date: Sat Jul 20 15:14:11 2019 +0200 fix rust fmt commit 496e980a1703b167d2ba3716dbe9aece1c9ce99f Author: dignifiedquire Date: Sat Jul 20 14:34:20 2019 +0200 use forked rusqlite commit fa09e46ed9bf755f77cfd90dc3de9d7e9b76eaec Author: holger krekel Date: Sat Jul 20 12:37:51 2019 +0200 another pace where we might (and in my case did) get invalid utf8 commit d6de420b9a7ae2af9dc233baa2ef301d9539eb94 Author: holger krekel Date: Sat Jul 20 12:30:48 2019 +0200 fix some string issues, introduce to_string_lossy such that to_string() continues to panic on non-utf8 commit 38eb708db883a55c52e4a67461fbab52dea46f1a Author: holger krekel Date: Sat Jul 20 01:17:53 2019 +0200 for now make to_string() less strict as we often don't want to crash the whole app just because some non-proper utf8 came in (through a message we can't neccesarily congtrol) commit 7a59da5f8ff40beff6ed8ad2b934b6b203d8c5ea Author: holger krekel Date: Fri Jul 19 22:48:39 2019 +0200 fix linting commit f13a1d4a2f7f399246be584360de681900f349ba Author: holger krekel Date: Fri Jul 19 22:46:58 2019 +0200 fix some test flakyness commit 7b3a450918024a11fc5173b4b6ffd6d179d96b09 Author: holger krekel Date: Fri Jul 19 22:35:07 2019 +0200 - fix saved_mime test which broke to improper conversion of imf_raw_not_terminated - some cargo.toml updates no clue where they come from - log Message-ID for received messages commit 169923b1021952ae6457b77eb31f013fa9269cd7 Author: holger krekel Date: Fri Jul 19 12:31:22 2019 +0200 formatting commit 42688a0622d3ab2438fd6aad5f2a9a7ae5f8f8e2 Author: holger krekel Date: Fri Jul 19 12:24:56 2019 +0200 remove some print statements commit 35f3c0edd17695b10d419af2ad9f270f5913ac4c Merge: e7a2362 f58b1d6 Author: holger krekel Date: Fri Jul 19 10:25:21 2019 +0200 Merge branch 'master' into eventlogging commit e7a236264ae0a7306e5c84110ecf5d38a974985f Author: dignifiedquire Date: Thu Jul 18 23:20:20 2019 +0200 print invalid strings commit aaa5b820d9e8d39be90cacee88ca03f98b87def5 Author: dignifiedquire Date: Thu Jul 18 23:12:35 2019 +0200 cleanup commit e7f07450102471a130e3c1f497c04181f5023020 Author: dignifiedquire Date: Thu Jul 18 23:03:57 2019 +0200 reduce direc usage of CString commit c68e7ae14e5bc7557d53f2b3b483725968f1fcb7 Author: dignifiedquire Date: Thu Jul 18 22:47:47 2019 +0200 audit use of to_cstring and fix ub commit 618087e5a7b25465e5b5786ad68afe3fc1cbdd58 Author: dignifiedquire Date: Thu Jul 18 21:38:52 2019 +0200 fix(imap): body ptr lifetime commit 245abb8384643c71df083067fe41c3363af42e1b Author: dignifiedquire Date: Thu Jul 18 19:44:10 2019 +0200 remove debug commit a3e104200105cdf928b7e2017289e575b2894306 Author: dignifiedquire Date: Thu Jul 18 18:30:54 2019 +0200 fix some things, add more debugging statements commit 7b7ce9348f9396617543a93ad9108813e235000d Author: holger krekel Date: Thu Jul 18 15:11:57 2019 +0200 fix python lint issues commit 7a4808ba0d0c6ffbf3133329254d983aefeafe06 Author: holger krekel Date: Thu Jul 18 14:35:54 2019 +0200 cargofmt commit 8f240f7153e20b190bd5df8fa5950da51070ca37 Author: holger krekel Date: Thu Jul 18 14:03:57 2019 +0200 (dig,hpk) pull out job collection from sql query/lock logic commit 7d0b5d8abb40bc35fec2fc5c9d19525b8ab8eea1 Author: holger krekel Date: Thu Jul 18 12:52:02 2019 +0200 remove print statements and fix a crash commit ee317cb1b58daf2bc50222ae002d6721a6cb3da6 Author: holger krekel Date: Thu Jul 18 11:38:10 2019 +0200 fix some merge issues commit 7b736fe635ede3c68d50c457f3251e7dd9a22930 Author: holger krekel Date: Thu Jul 18 11:16:38 2019 +0200 (dig,hpk) add test and fix for wrong dbs commit c7db15352a7efc4c74e308fad5e2c85d9aa11744 Merge: 0b37167 0c5015d Author: holger krekel Date: Thu Jul 18 09:59:44 2019 +0200 Merge branch 'master' into eventlogging commit 0b37167be890785ab49334cf002a529f0dce8c92 Author: holger krekel Date: Thu Jul 18 00:06:05 2019 +0200 address @dignifiedquire comments commit 5cac4b507615bddd4ab83ce3f5be96ccb6b4a6a1 Author: holger krekel Date: Wed Jul 17 12:47:22 2019 +0200 remove spurious print commit 475a41beb3d6277fd0683ee06bedbd8cf52e8554 Author: holger krekel Date: Wed Jul 17 12:31:12 2019 +0200 address @dignifiedquire rustyness comment and fix changelog commit ad4be80b4e5468cf61560a85a9068db39ce113a5 Author: holger krekel Date: Wed Jul 17 10:25:25 2019 +0200 make smtp/imap connect() return bool instead of c-int commit 8737c1d1424725ec2f08b2c550f40d051256117f Author: holger krekel Date: Wed Jul 17 09:26:33 2019 +0200 cleanup some parts, add comments commit 964fe466cc0f1b0ad0611c8af642002ba8ad6de5 Author: holger krekel Date: Tue Jul 16 20:05:41 2019 +0200 wip-commit which passes all tests with proper finalization commit 43936e7db78cd11d102ae7d738562e0d22df65e3 Author: holger krekel Date: Tue Jul 16 16:17:42 2019 +0200 snapshot of my current debugging state commit 0e80ce9c39a1091fe60bb610aad036db9623a75b Author: holger krekel Date: Tue Jul 16 12:57:19 2019 +0200 more aggressively skip perform API when threads are closing commit c652bae68af2ab9e4cb8428fbbd6c075fd68425b Author: holger krekel Date: Tue Jul 16 12:06:05 2019 +0200 intermediate wip commit commit bc904a495d1db89956051157171aa5bd21320ab2 Author: holger krekel Date: Tue Jul 16 11:18:56 2019 +0200 add some logging, and a more precise teardown for online python tests commit 8d99444c6a541cb04d3ce7a9846a2dafdcf843ec Author: holger krekel Date: Tue Jul 16 00:22:12 2019 +0200 fix std commit 9dab53e0afee54745e168d70275f56ca77728413 Author: holger krekel Date: Tue Jul 16 00:20:54 2019 +0200 rustfmt commit 360089ac741d01fba4beb27ff928afdbec451e06 Author: holger krekel Date: Tue Jul 16 00:03:49 2019 +0200 remove some debugging commit e892c5cf4dfec51afa7b2499680000caac80f931 Author: holger krekel Date: Mon Jul 15 23:31:30 2019 +0200 fix test for events commit 9ad4c9a6fef287517254447ca91206df5baca2e9 Author: holger krekel Date: Mon Jul 15 22:51:57 2019 +0200 wip try test that we see INFO events from the core --- Cargo.lock | 140 +++++++++++------- Cargo.toml | 6 + deltachat-ffi/Cargo.toml | 1 + deltachat-ffi/src/lib.rs | 8 +- examples/repl/cmdline.rs | 63 ++++---- examples/repl/main.rs | 18 +-- python/CHANGELOG | 6 + python/install_py_bindings.sh | 7 - python/install_python_bindings.py | 36 +++++ python/src/deltachat/__init__.py | 8 +- python/src/deltachat/_build.py | 1 + python/src/deltachat/account.py | 43 ++++-- python/tests/conftest.py | 21 ++- python/tests/test_account.py | 8 +- python/tests/test_increation.py | 4 + python/tests/test_lowlevel.py | 47 +++++- python/tox.ini | 1 + src/config.rs | 7 +- src/context.rs | 8 +- src/dc_array.rs | 4 +- src/dc_chat.rs | 196 +++++++++++++------------ src/dc_configure.rs | 70 +++++---- src/dc_contact.rs | 27 ++-- src/dc_dehtml.rs | 2 +- src/dc_e2ee.rs | 19 +-- src/dc_imex.rs | 151 ++++++++++--------- src/dc_job.rs | 70 +++++---- src/dc_location.rs | 14 +- src/dc_mimefactory.rs | 58 +++----- src/dc_mimeparser.rs | 32 ++-- src/dc_msg.rs | 111 +++++++------- src/dc_qr.rs | 10 +- src/dc_receive_imf.rs | 153 ++++++++++---------- src/dc_securejoin.rs | 8 +- src/dc_simplify.rs | 5 +- src/dc_strencode.rs | 12 +- src/dc_token.rs | 21 ++- src/dc_tools.rs | 94 ++++++++---- src/imap.rs | 233 +++++++++++++++++------------- src/key.rs | 3 + src/log.rs | 46 +++--- src/peerstate.rs | 14 +- src/smtp.rs | 12 +- src/sql.rs | 125 ++++++++++------ tests/stress.rs | 78 ++++++---- 45 files changed, 1165 insertions(+), 836 deletions(-) delete mode 100755 python/install_py_bindings.sh create mode 100755 python/install_python_bindings.py diff --git a/Cargo.lock b/Cargo.lock index 539bc132f..384b1c560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "psl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rental 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -194,12 +194,12 @@ dependencies = [ [[package]] name = "buf_redux" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slice-deque 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -409,7 +409,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -443,7 +443,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -453,7 +453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -461,6 +461,7 @@ name = "deltachat" version = "1.0.0-alpha.3" dependencies = [ "addr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -484,9 +485,9 @@ dependencies = [ "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2_sqlite 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", "rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -495,6 +496,7 @@ dependencies = [ "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -515,7 +517,7 @@ dependencies = [ "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -526,7 +528,7 @@ dependencies = [ "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -608,7 +610,7 @@ dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -637,7 +639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -898,7 +900,7 @@ dependencies = [ "imap-proto 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -974,8 +976,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libsqlite3-sys" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.16.0" +source = "git+http://github.com/dignifiedquire/rusqlite?branch=fix/text#d81b8cdb175f7c27a3ace9ad46818e2a01434cdc" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1158,9 +1160,9 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1225,7 +1227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1278,7 +1280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.23" +version = "0.10.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1286,7 +1288,7 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1304,7 +1306,7 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.47" +version = "0.9.48" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1320,7 +1322,7 @@ name = "os_type" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1433,7 +1435,7 @@ dependencies = [ "block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "buf_redux 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "buf_redux 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "cast5 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1583,7 +1585,7 @@ dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1616,7 +1618,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", ] [[package]] @@ -1792,22 +1794,22 @@ dependencies = [ [[package]] name = "regex" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1834,12 +1836,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "reqwest" -version = "0.9.18" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1867,6 +1869,7 @@ dependencies = [ "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1899,12 +1902,12 @@ dependencies = [ [[package]] name = "rusqlite" version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+http://github.com/dignifiedquire/rusqlite?branch=fix/text#d81b8cdb175f7c27a3ace9ad46818e2a01434cdc" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libsqlite3-sys 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsqlite3-sys 0.16.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2033,7 +2036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2103,7 +2106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "slice-deque" -version = "0.1.16" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2160,7 +2163,7 @@ dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2180,7 +2183,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.39" +version = "0.15.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2195,7 +2198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2237,6 +2240,14 @@ dependencies = [ "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-local-object" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -2386,6 +2397,11 @@ dependencies = [ "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "try-lock" version = "0.2.2" @@ -2416,7 +2432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ucd-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2466,6 +2482,14 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "url" version = "1.7.2" @@ -2582,6 +2606,14 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winreg" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winutil" version = "0.1.1" @@ -2624,7 +2656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] @@ -2651,7 +2683,7 @@ dependencies = [ "checksum block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" -"checksum buf_redux 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f25c67abbf523ff8457771622fb731ac4a2391439de33bc60febcdee1749c9" +"checksum buf_redux 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cb6b0b3650a857c5f3eb2083d6a51dc86a9967eafdd42919be63e3b3e6599752" "checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" @@ -2735,7 +2767,7 @@ dependencies = [ "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c66afaa5dfadbb81d4e00fd1d1ab057c7cd4c799c5a44e0009386d553587e728" "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum libsqlite3-sys 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72b1e07fcc60484f42e246f0cf1f133940c98117c81b2cefcdf71be288069680" +"checksum libsqlite3-sys 0.16.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)" = "" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" @@ -2768,10 +2800,10 @@ dependencies = [ "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" -"checksum openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)" = "97c140cbb82f3b3468193dd14c1b88def39f341f68257f8a7fe8ed9ed3f628a5" +"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-src 111.3.0+1.1.1c (registry+https://github.com/rust-lang/crates.io-index)" = "53ed5f31d294bdf5f7a4ba0a206c2754b0f60e9a63b7e3076babc5317873c797" -"checksum openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)" = "75bdd6dbbb4958d38e47a1d2348847ad1eb4dc205dc5d37473ae504391865acc" +"checksum openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)" = "b5ba300217253bcc5dc68bed23d782affa45000193866e025329aa8a7a9f05b8" "checksum os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7edc011af0ae98b7f88cf7e4a83b70a54a75d2b8cb013d6efd02e5956207e9eb" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" @@ -2819,15 +2851,15 @@ dependencies = [ "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" -"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" -"checksum regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9b01330cce219c1c6b2e209e5ed64ccd587ae5c67bed91c0b49eecf02ae40e21" +"checksum regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b23da8dfd98a84bd7e08700190a5d9f7d2d38abd4369dd1dae651bc40bfd2cc" +"checksum regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5485bf1523a9ed51c4964273f22f63f24e31632adb5dad134f488f86a3875c" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rental 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "01916ebd9fc2e81978a5dc9542a2fa47f5bb2ca3402e14c7cc42d6e3c5123e1f" "checksum rental-impl 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "82260d54cf2cbe9608df161f7e7c98e81fae702aa13af9e4d5d39dc2ffb25ab6" -"checksum reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "00eb63f212df0e358b427f0f40aa13aaea010b470be642ad422bcbca2feff2e4" +"checksum reqwest 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0777154c2c3eb54f5c480db01de845652d941e47191277cc673634c3853939" "checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" "checksum rsa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6ad8d3632f6745bb671c8637e2aa44015537c5e384789d2ea3235739301ed1e0" -"checksum rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ebca2e7e3deb7241b7fa5929c088548c590728b1b740c479594c23f813eb8a7" +"checksum rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)" = "" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f47ea1ceb347d2deae482d655dc8eef4bd82363d3329baffa3818bd76fea48b" @@ -2852,7 +2884,7 @@ dependencies = [ "checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slice-deque 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d39fca478d10e201944a8e21f4393d6bfe38fa3b16a152050e4d097fe2bbf494" +"checksum slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffddf594f5f597f63533d897427a570dbaa9feabaaa06595b74b71b7014507d7" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -2863,12 +2895,13 @@ dependencies = [ "checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" "checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" +"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" +"checksum thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7da3caa820d0308c84c8654f6cafd81cc3195d45433311cbe22fcf44fc8be071" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" @@ -2882,11 +2915,12 @@ dependencies = [ "checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" @@ -2894,6 +2928,7 @@ dependencies = [ "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde" "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" @@ -2911,6 +2946,7 @@ dependencies = [ "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 wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum winreg 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73f1f3c6c4d3cab118551b96c476a2caab920701e28875b64a458f2ecb96ec9d" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" diff --git a/Cargo.toml b/Cargo.toml index 12a2e462b..2865fe893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ r2d2_sqlite = "0.11.0" r2d2 = "0.8.5" strum = "0.15.0" strum_macros = "0.15.0" +thread-local-object = "0.1.0" +backtrace = "0.3.33" [dev-dependencies] tempfile = "3.0" @@ -52,6 +54,10 @@ members = [ "deltachat-ffi" ] +[patch.crates-io] +rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] } + + [[example]] name = "simple" path = "examples/simple.rs" diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index f6aac51be..c4e0d8580 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -24,3 +24,4 @@ default = ["vendored", "nightly", "ringbuf"] vendored = ["deltachat/vendored"] nightly = ["deltachat/nightly"] ringbuf = ["deltachat/ringbuf"] + diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index c1efcc9be..3918b56c6 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -111,7 +111,7 @@ pub unsafe extern "C" fn dc_get_config( match config::Config::from_str(dc_tools::as_str(key)) { Ok(key) => { let value = context.get_config(key).unwrap_or_default(); - into_cstring(value) + dc_tools::to_cstring(value) } Err(_) => std::ptr::null_mut(), } @@ -137,7 +137,7 @@ pub unsafe extern "C" fn dc_get_oauth2_url( let addr = dc_tools::to_string(addr); let redirect = dc_tools::to_string(redirect); match oauth2::dc_get_oauth2_url(context, addr, redirect) { - Some(res) => libc::strdup(dc_tools::to_cstring(res).as_ptr()), + Some(res) => dc_tools::to_cstring(res), None => std::ptr::null_mut(), } } @@ -1546,7 +1546,3 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> { Some(dc_tools::as_str(s)) } - -unsafe fn into_cstring(s: impl AsRef) -> *mut libc::c_char { - dc_tools::dc_strdup(dc_tools::to_cstring(s).as_ptr()) -} diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 3138be865..9d6be9097 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -139,7 +139,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int } else { current_block = 7149356873433890176; } - real_spec = strdup(to_cstring(rs.unwrap_or_default()).as_ptr()); + real_spec = to_cstring(rs.unwrap_or_default()); } match current_block { 8522321847195001863 => {} @@ -177,10 +177,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int let path_plus_name = format!("{}/{}", as_str(real_spec), name); info!(context, 0, "Import: {}", path_plus_name); let path_plus_name_c = to_cstring(path_plus_name); - - if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) { + if 0 != dc_poke_eml_file(context, path_plus_name_c) { read_cnt += 1 } + free(path_plus_name_c as *mut _); } } current_block = 1622411330066726685; @@ -380,18 +380,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let mut args = line.splitn(3, ' '); let arg0 = args.next().unwrap_or_default(); let arg1 = args.next().unwrap_or_default(); - let arg1_c = to_cstring(arg1); - let arg1_c_ptr = if arg1.is_empty() { + let arg1_c = if arg1.is_empty() { std::ptr::null() } else { - arg1_c.as_ptr() + to_cstring(arg1) as *const _ }; let arg2 = args.next().unwrap_or_default(); - let arg2_c = to_cstring(arg2); - let arg2_c_ptr = if arg2.is_empty() { + let arg2_c = if arg2.is_empty() { std::ptr::null() } else { - arg2_c.as_ptr() + to_cstring(arg2) as *const _ }; match arg0 { @@ -497,7 +495,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty(), "Argument missing"); dc_close(context); ensure!( - 0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char), + 0 != dc_open(context, arg1_c, 0 as *const libc::c_char), "Open failed" ); } @@ -538,7 +536,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E !arg1.is_empty() && !arg2.is_empty(), "Arguments expected" ); - if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c_ptr) { + if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c) { bail!("Continue key transfer failed"); } } @@ -553,7 +551,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "import-backup" => { ensure!(!arg1.is_empty(), "Argument missing."); - dc_imex(context, 12, arg1_c_ptr, 0 as *const libc::c_char); + dc_imex(context, 12, arg1_c, 0 as *const libc::c_char); } "export-keys" => { dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char); @@ -590,7 +588,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E free(setup_code as *mut libc::c_void); } "poke" => { - ensure!(0 != poke_spec(context, arg1_c_ptr), "Poke failed"); + ensure!(0 != poke_spec(context, arg1_c), "Poke failed"); } "reset" => { ensure!(!arg1.is_empty(), "Argument missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config"); @@ -624,7 +622,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "listchats" | "listarchived" | "chats" => { let listflags = if arg0 == "listarchived" { 0x01 } else { 0 }; - let chatlist = dc_get_chatlist(context, listflags, arg1_c_ptr, 0 as uint32_t); + let chatlist = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t); ensure!(!chatlist.is_null(), "Failed to retrieve chatlist"); let mut i: libc::c_int; @@ -782,8 +780,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "creategroup" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id_1: libc::c_int = - dc_create_group_chat(context, 0, arg1_c_ptr) as libc::c_int; + let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int; if chat_id_1 != 0 { println!("Group#{} created successfully.", chat_id_1,); } else { @@ -792,8 +789,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "createverified" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id_2: libc::c_int = - dc_create_group_chat(context, 1, arg1_c_ptr) as libc::c_int; + let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int; if chat_id_2 != 0 { println!("VerifiedGroup#{} created successfully.", chat_id_2,); } else { @@ -832,7 +828,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "groupname" => { ensure!(!sel_chat.is_null(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { + if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) { println!("Chat name set"); } else { bail!("Failed to set chat name"); @@ -846,7 +842,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E context, dc_chat_get_id(sel_chat), if !arg1.is_empty() { - arg1_c_ptr + arg1_c } else { std::ptr::null_mut() }, @@ -937,9 +933,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let msg = to_cstring(format!("{} {}", arg1, arg2)); - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) { + if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) { println!("Message sent."); + free(msg as *mut _); } else { + free(msg as *mut _); bail!("Sending failed."); } } @@ -960,8 +958,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given."); let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 }); - dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char); - dc_msg_set_text(msg_0, arg2_c_ptr); + dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char); + dc_msg_set_text(msg_0, arg2_c); dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); dc_msg_unref(msg_0); } @@ -974,7 +972,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 0 as libc::c_uint }; - let msglist_0 = dc_search_msgs(context, chat, arg1_c_ptr); + let msglist_0 = dc_search_msgs(context, chat, arg1_c); if !msglist_0.is_null() { log_msglist(context, msglist_0); @@ -987,7 +985,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if !arg1.is_empty() { let draft_0 = dc_msg_new(context, 10); - dc_msg_set_text(draft_0, arg1_c_ptr); + dc_msg_set_text(draft_0, arg1_c); dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0); dc_msg_unref(draft_0); println!("Draft saved."); @@ -1079,7 +1077,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } else { 0x2 }, - arg1_c_ptr, + arg1_c, ); if !contacts.is_null() { log_contactlist(context, contacts); @@ -1095,13 +1093,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if !arg2.is_empty() { let book = dc_mprintf( b"%s\n%s\x00" as *const u8 as *const libc::c_char, - arg1_c_ptr, - arg2_c_ptr, + arg1_c, + arg2_c, ); dc_add_address_book(context, book); free(book as *mut libc::c_void); } else { - if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) { + if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) { bail!("Failed to create contact"); } } @@ -1148,7 +1146,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "checkqr" => { ensure!(!arg1.is_empty(), "Argument missing."); - let res = dc_check_qr(context, arg1_c_ptr); + let res = dc_check_qr(context, arg1_c); println!( "state={}, id={}, text1={}, text2={}", (*res).state as libc::c_int, @@ -1176,7 +1174,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if 0 != dc_read_file( context, - arg1_c_ptr, + arg1_c, &mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void, &mut buf_bytes, ) { @@ -1194,5 +1192,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E dc_chat_unref(sel_chat); } + free(arg1_c as *mut _); + free(arg2_c as *mut _); + Ok(()) } diff --git a/examples/repl/main.rs b/examples/repl/main.rs index ea57e2d02..28f923c3f 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -398,11 +398,10 @@ fn main_0(args: Vec) -> Result<(), failure::Error> { if args.len() == 2 { if 0 == unsafe { - dc_open( - &mut context, - to_cstring(&args[1]).as_ptr(), - 0 as *const libc::c_char, - ) + let a = to_cstring(&args[1]); + let res = dc_open(&mut context, a, 0 as *const _); + free(a as *mut _); + res } { println!("Error: Cannot open {}.", args[0],); } @@ -482,11 +481,10 @@ unsafe fn handle_cmd(line: &str, ctx: Arc>) -> Result>) -> Result { start_threads(ctx.clone()); if !arg0.is_empty() { - dc_join_securejoin(&ctx.read().unwrap(), arg1_c_ptr); + dc_join_securejoin(&ctx.read().unwrap(), arg1_c); } } "exit" => return Ok(ExitResult::Exit), _ => dc_cmdline(&ctx.read().unwrap(), line)?, } + free(arg1_c as *mut _); + Ok(ExitResult::Continue) } diff --git a/python/CHANGELOG b/python/CHANGELOG index 0dfffc4e1..7a0c73b2a 100644 --- a/python/CHANGELOG +++ b/python/CHANGELOG @@ -4,6 +4,12 @@ - introduce automatic versioning via setuptools_scm, based on py-X.Y.Z tags +- integrate latest DCC core-rust with dc_close() fixes + +- provide a account.shutdown() method and improve termination + logic also in tests. also fixes output-clubbering during + test runs. + 0.600.0 --------- diff --git a/python/install_py_bindings.sh b/python/install_py_bindings.sh deleted file mode 100755 index 718229252..000000000 --- a/python/install_py_bindings.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -cargo build -p deltachat_ffi --release -rm -rf build/ src/deltachat/*.so -DCC_RS_DEV=`pwd`/.. pip install -e . diff --git a/python/install_python_bindings.py b/python/install_python_bindings.py new file mode 100755 index 000000000..8effe5c4b --- /dev/null +++ b/python/install_python_bindings.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" + setup a python binding development in-place install with cargo debug symbols. +""" + +import os +import subprocess + +if __name__ == "__main__": + os.environ["DCC_RS_TARGET"] = target = "release" + + toml = os.path.join(os.getcwd(), "..", "Cargo.toml") + assert os.path.exists(toml) + with open(toml) as f: + s = orig = f.read() + s += "\n" + s += "[profile.release]\n" + s += "debug = true\n" + with open(toml, "w") as f: + f.write(s) + print("temporarily modifying Cargo.toml to provide release build with debug symbols ") + try: + subprocess.check_call([ + "cargo", "build", "-p", "deltachat_ffi", "--" + target + ]) + finally: + with open(toml, "w") as f: + f.write(orig) + print("\nreseted Cargo.toml to previous original state") + + subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True) + + subprocess.check_call([ + "pip", "install", "-e", "." + ]) diff --git a/python/src/deltachat/__init__.py b/python/src/deltachat/__init__.py index a8d338aba..ea8786454 100644 --- a/python/src/deltachat/__init__.py +++ b/python/src/deltachat/__init__.py @@ -43,6 +43,9 @@ def py_dc_callback(ctx, evt, data1, data2): try: ret = callback(ctx, evt_name, data1, data2) + if ret is None: + ret = 0 + assert isinstance(ret, int), repr(ret) if event_sig_types & 4: return ffi.cast('uintptr_t', ret) elif event_sig_types & 8: @@ -58,7 +61,10 @@ def set_context_callback(dc_context, func): def clear_context_callback(dc_context): - _DC_CALLBACK_MAP.pop(dc_context, None) + try: + _DC_CALLBACK_MAP.pop(dc_context, None) + except AttributeError: + pass def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}): diff --git a/python/src/deltachat/_build.py b/python/src/deltachat/_build.py index 651d8edde..cc01ecb07 100644 --- a/python/src/deltachat/_build.py +++ b/python/src/deltachat/_build.py @@ -25,6 +25,7 @@ def ffibuilder(): else: raise NotImplementedError("Compilation not supported yet on Windows, can you help?") objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')] + assert os.path.exists(objs[0]), objs incs = [os.path.join(projdir, 'deltachat-ffi')] else: libs = ['deltachat'] diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 6fb32b73e..c98303181 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -23,28 +23,36 @@ class Account(object): by the underlying deltachat c-library. All public Account methods are meant to be memory-safe and return memory-safe objects. """ - def __init__(self, db_path, logid=None): + def __init__(self, db_path, logid=None, eventlogging=True): """ initialize account object. :param db_path: a path to the account database. The database will be created if it doesn't exist. :param logid: an optional logging prefix that should be used with the default internal logging. + :param eventlogging: if False no eventlogging and no context callback will be configured """ self._dc_context = ffi.gc( lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL), _destroy_dc_context, ) + if eventlogging: + self._evlogger = EventLogger(self._dc_context, logid) + deltachat.set_context_callback(self._dc_context, self._process_event) + self._threads = IOThreads(self._dc_context, self._evlogger._log_event) + else: + self._threads = IOThreads(self._dc_context) + if hasattr(db_path, "encode"): db_path = db_path.encode("utf8") if not lib.dc_open(self._dc_context, db_path, ffi.NULL): raise ValueError("Could not dc_open: {}".format(db_path)) - self._evlogger = EventLogger(self._dc_context, logid) - deltachat.set_context_callback(self._dc_context, self._process_event) - self._threads = IOThreads(self._dc_context) self._configkeys = self.get_config("sys.config_keys").split() self._imex_completed = threading.Event() + def __del__(self): + self.shutdown() + def _check_config_key(self, name): if name not in self._configkeys: raise KeyError("{!r} not a valid config key, existing keys: {!r}".format( @@ -333,12 +341,22 @@ class Account(object): lib.dc_stop_ongoing_process(self._dc_context) self._threads.stop(wait=wait) + def shutdown(self, wait=True): + """ stop threads and close and remove underlying dc_context and callbacks. """ + if hasattr(self, "_dc_context") and hasattr(self, "_threads"): + self.stop_threads(wait=False) # to interrupt idle and tell python threads to stop + lib.dc_close(self._dc_context) + self.stop_threads(wait=wait) # to wait for threads + deltachat.clear_context_callback(self._dc_context) + del self._dc_context + def _process_event(self, ctx, evt_name, data1, data2): assert ctx == self._dc_context - self._evlogger(evt_name, data1, data2) - method = getattr(self, "on_" + evt_name.lower(), None) - if method is not None: - method(data1, data2) + if hasattr(self, "_evlogger"): + self._evlogger(evt_name, data1, data2) + method = getattr(self, "on_" + evt_name.lower(), None) + if method is not None: + method(data1, data2) return 0 def on_dc_event_imex_progress(self, data1, data2): @@ -347,10 +365,11 @@ class Account(object): class IOThreads: - def __init__(self, dc_context): + def __init__(self, dc_context, log_event=lambda *args: None): self._dc_context = dc_context self._thread_quitflag = False self._name2thread = {} + self._log_event = log_event def is_started(self): return len(self._name2thread) > 0 @@ -376,15 +395,19 @@ class IOThreads: thread.join() def imap_thread_run(self): + self._log_event("py-bindings-info", 0, "IMAP THREAD START") while not self._thread_quitflag: lib.dc_perform_imap_jobs(self._dc_context) lib.dc_perform_imap_fetch(self._dc_context) lib.dc_perform_imap_idle(self._dc_context) + self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED") def smtp_thread_run(self): + self._log_event("py-bindings-info", 0, "SMTP THREAD START") while not self._thread_quitflag: lib.dc_perform_smtp_jobs(self._dc_context) lib.dc_perform_smtp_idle(self._dc_context) + self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED") class EventLogger: @@ -414,7 +437,7 @@ class EventLogger: raise ValueError("{}({!r},{!r})".format(*ev)) return ev - def get_matching(self, event_name_regex): + def get_matching(self, event_name_regex, check_error=True): self._log("-- waiting for event with regex: {} --".format(event_name_regex)) rex = re.compile("(?:{}).*".format(event_name_regex)) while 1: diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 64668d938..a74cf65d6 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -16,12 +16,23 @@ def pytest_addoption(parser): ) +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item): + # perform early finalization because we otherwise get cloberred + # output from concurrent threads printing between execution + # of the test function and the teardown phase of that test function + if "acfactory" in item.funcargs: + print("*"*30, "finalizing", "*"*30) + acfactory = item.funcargs["acfactory"] + acfactory.finalize() + + def pytest_report_header(config, startdir): t = tempfile.mktemp() try: - ac = Account(t) + ac = Account(t, eventlogging=False) info = ac.get_info() - del ac + ac.shutdown() finally: os.remove(t) return "Deltachat core={} sqlite={}".format( @@ -52,7 +63,6 @@ def acfactory(pytestconfig, tmpdir, request): self.live_count = 0 self.offline_count = 0 self._finalizers = [] - request.addfinalizer(self.finalize) self.init_time = time.time() def finalize(self): @@ -78,6 +88,7 @@ def acfactory(pytestconfig, tmpdir, request): ac = Account(tmpdb.strpath, logid="ac{}".format(self.offline_count)) ac._evlogger.init_time = self.init_time ac._evlogger.set_timeout(2) + self._finalizers.append(ac.shutdown) return ac def get_configured_offline_account(self): @@ -103,7 +114,7 @@ def acfactory(pytestconfig, tmpdir, request): ac._evlogger.set_timeout(30) ac.configure(**configdict) ac.start_threads() - self._finalizers.append(lambda: ac.stop_threads(wait=False)) + self._finalizers.append(ac.shutdown) return ac def clone_online_account(self, account): @@ -114,7 +125,7 @@ def acfactory(pytestconfig, tmpdir, request): ac._evlogger.set_timeout(30) ac.configure(addr=account.get_config("addr"), mail_pw=account.get_config("mail_pw")) ac.start_threads() - self._finalizers.append(lambda: ac.stop_threads(wait=False)) + self._finalizers.append(ac.shutdown) return ac return AccountMaker() diff --git a/python/tests/test_account.py b/python/tests/test_account.py index b24ebb367..ef101c776 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -1,12 +1,18 @@ from __future__ import print_function import pytest import os -from deltachat import const +from deltachat import const, Account from datetime import datetime, timedelta from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection class TestOfflineAccount: + def test_wrong_db(self, tmpdir): + p = tmpdir.join("hello.db") + p.write("123") + with pytest.raises(ValueError): + Account(p.strpath) + def test_getinfo(self, acfactory): ac1 = acfactory.get_unconfigured_account() d = ac1.get_info() diff --git a/python/tests/test_increation.py b/python/tests/test_increation.py index 095a9592b..b7181e2b4 100644 --- a/python/tests/test_increation.py +++ b/python/tests/test_increation.py @@ -32,7 +32,11 @@ class TestInCreation: chat2.add_contact(c2) wait_msgs_changed(ac1, 0, 0) # why not chat id? ac1.forward_messages([prepared_original], chat2) + # XXX there might be two EVENT_MSGS_CHANGED and only one of them + # is the one caused by forwarding forwarded_id = wait_msgs_changed(ac1, chat2.id) + if forwarded_id == 0: + forwarded_id = wait_msgs_changed(ac1, chat2.id) forwarded_msg = ac1.get_message_by_id(forwarded_id) assert forwarded_msg.get_state().is_out_preparing() diff --git a/python/tests/test_lowlevel.py b/python/tests/test_lowlevel.py index e3c5705b8..a21efdd3c 100644 --- a/python/tests/test_lowlevel.py +++ b/python/tests/test_lowlevel.py @@ -1,6 +1,8 @@ from __future__ import print_function -import pytest -from deltachat import capi, Account, const +from deltachat import capi, const, set_context_callback, clear_context_callback +from deltachat.capi import ffi +from deltachat.capi import lib +from deltachat.account import EventLogger def test_empty_context(): @@ -8,10 +10,45 @@ def test_empty_context(): capi.lib.dc_close(ctx) +def test_callback_None2int(): + ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL) + set_context_callback(ctx, lambda *args: None) + capi.lib.dc_close(ctx) + clear_context_callback(ctx) + + +def test_dc_close_events(): + ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL) + evlog = EventLogger(ctx) + evlog.set_timeout(5) + set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2)) + capi.lib.dc_close(ctx) + + def find(info_string): + while 1: + ev = evlog.get_matching("DC_EVENT_INFO", check_error=False) + data2 = ev[2] + if info_string in data2: + return + else: + print("skipping event", *ev) + + find("disconnecting INBOX-watch") + find("disconnecting sentbox-thread") + find("disconnecting mvbox-thread") + find("disconnecting SMTP") + find("Database closed") + + def test_wrong_db(tmpdir): - tmpdir.join("hello.db").write("123") - with pytest.raises(ValueError): - Account(db_path=tmpdir.strpath) + dc_context = ffi.gc( + lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL), + lib.dc_context_unref, + ) + p = tmpdir.join("hello.db") + # write an invalid database file + p.write("x123" * 10) + assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL) def test_event_defines(): diff --git a/python/tox.ini b/python/tox.ini index ccac3dee6..7dce3df91 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -52,6 +52,7 @@ commands = python_files = tests/test_*.py norecursedirs = .tox xfail_strict=true +timeout = 60 [flake8] max-line-length = 120 diff --git a/src/config.rs b/src/config.rs index 0f0cc2aa8..3c55c1fb4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -71,7 +71,12 @@ impl Context { Config::Selfavatar => { let rel_path = self.sql.get_config(self, key); rel_path.map(|p| { - let v = unsafe { dc_get_abs_path(self, to_cstring(p).as_ptr()) }; + let v = unsafe { + let n = to_cstring(p); + let res = dc_get_abs_path(self, n); + free(n as *mut libc::c_void); + res + }; let r = to_string(v); unsafe { free(v as *mut _) }; r diff --git a/src/context.rs b/src/context.rs index f21aea4f6..cd64e1883 100644 --- a/src/context.rs +++ b/src/context.rs @@ -279,7 +279,7 @@ unsafe fn cb_get_config( .sql .get_config(context, as_str(key)) .unwrap_or_else(|| to_string(def)); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub unsafe fn dc_context_unref(context: &mut Context) { @@ -291,13 +291,16 @@ pub unsafe fn dc_context_unref(context: &mut Context) { } pub unsafe fn dc_close(context: &Context) { + info!(context, 0, "disconnecting INBOX-watch",); context.inbox.read().unwrap().disconnect(context); + info!(context, 0, "disconnecting sentbox-thread",); context .sentbox_thread .read() .unwrap() .imap .disconnect(context); + info!(context, 0, "disconnecting mvbox-thread",); context .mvbox_thread .read() @@ -305,6 +308,7 @@ pub unsafe fn dc_close(context: &Context) { .imap .disconnect(context); + info!(context, 0, "disconnecting SMTP"); context.smtp.clone().lock().unwrap().disconnect(); context.sql.close(context); @@ -511,7 +515,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { fingerprint_str, ); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub unsafe fn dc_get_version_str() -> *mut libc::c_char { diff --git a/src/dc_array.rs b/src/dc_array.rs index a23c24544..225e71743 100644 --- a/src/dc_array.rs +++ b/src/dc_array.rs @@ -376,7 +376,7 @@ pub unsafe fn dc_array_get_string( } res }); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } /// return comma-separated value-string from integer array @@ -398,7 +398,7 @@ pub unsafe fn dc_arr_to_string(arr: *const uint32_t, cnt: libc::c_int) -> *mut l res }, ); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } #[cfg(test)] diff --git a/src/dc_chat.rs b/src/dc_chat.rs index f0c5aab39..625b09da0 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -130,20 +130,30 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { c.type_0 = row.get(1)?; c.name = { let raw: String = row.get(2)?; - unsafe { strdup(to_cstring(raw).as_ptr()) } + unsafe { to_cstring(raw) } }; c.grpid = { let raw: String = row.get(3)?; - unsafe { strdup(to_cstring(raw).as_ptr()) } + unsafe { to_cstring(raw) } }; let packed: String = row.get(4)?; - unsafe { dc_param_set_packed((*chat).param, to_cstring(&packed).as_ptr()) }; + unsafe { + let p = to_cstring(&packed); + dc_param_set_packed((*chat).param, p); + free(p as *mut _); + }; c.archived = row.get(5)?; - c.blocked = row.get(6)?; + c.blocked = row.get::<_, Option>(6)?.unwrap_or_default(); c.gossiped_timestamp = row.get(7)?; c.is_sending_locations = row.get(8)?; + Ok(()) + }, + ); + match res { + Ok(_) => { + let c = unsafe { &mut *chat }; match c.id { 1 => unsafe { free((*chat).name as *mut libc::c_void); @@ -172,19 +182,18 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { } } } - Ok(()) - }, - ); - - match res { - Ok(_) => true, - Err(err) => { - error!( - context, - 0, "chat: failed to load from db {}: {:?}", chat_id, err - ); - false + true } + Err(err) => match err { + QueryReturnedNoRows => false, + _ => { + error!( + context, + 0, "chat: failed to load from db {}: {:?}", chat_id, err + ); + false + } + }, } } @@ -324,7 +333,7 @@ pub fn dc_lookup_real_nchat_by_contact_id( if let Ok((id, blocked)) = context.sql.query_row( "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", params![contact_id as i32], - |row| Ok((row.get(0)?, row.get(1)?)), + |row| Ok((row.get(0)?, row.get::<_, Option>(1)?.unwrap_or_default())), ) { unsafe { *ret_chat_id = id }; unsafe { *ret_chat_blocked = blocked }; @@ -496,8 +505,9 @@ unsafe fn prepare_msg_raw( } else { 0 as *mut libc::c_char }, - from_c.as_ptr(), + from_c, ); + free(from_c as *mut _); if (*chat).type_0 == 100 { if let Some(id) = context.sql.query_row_col( @@ -761,9 +771,9 @@ unsafe fn get_parent_mime_headers( FROM msgs WHERE chat_id=? AND from_id!=?);", params![(*chat).id as i32, 1], |row| { - *parent_rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); - *parent_in_reply_to = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); - *parent_references = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); + *parent_rfc724_mid = to_cstring(row.get::<_, String>(0)?); + *parent_in_reply_to = to_cstring(row.get::<_, String>(1)?); + *parent_references = to_cstring(row.get::<_, String>(2)?); Ok(()) }, ) @@ -779,12 +789,9 @@ unsafe fn get_parent_mime_headers( FROM msgs WHERE chat_id=? AND from_id==?);", params![(*chat).id as i32, 1], |row| { - *parent_rfc724_mid = - dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); - *parent_in_reply_to = - dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); - *parent_references = - dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); + *parent_rfc724_mid = to_cstring(row.get::<_, String>(0)?); + *parent_in_reply_to = to_cstring(row.get::<_, String>(1)?); + *parent_references = to_cstring(row.get::<_, String>(2)?); Ok(()) }, ) @@ -823,7 +830,8 @@ unsafe fn last_msg_in_chat_encrypted( if let Some(packed) = packed { let msg_param = dc_param_new(); let packed_c = to_cstring(packed); - dc_param_set_packed(msg_param, packed_c.as_ptr()); + dc_param_set_packed(msg_param, packed_c); + free(packed_c as *mut _); if 0 != dc_param_exists(msg_param, 'c' as i32) { last_is_encrypted = 1; @@ -1962,73 +1970,69 @@ pub unsafe fn dc_forward_msgs( curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); idsstr = dc_arr_to_string(msg_ids, msg_cnt); - context - .sql - .query_map( - format!( - "SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id", - as_str(idsstr) - ), - params![], - |row| row.get::<_, i32>(0), - |ids| { - for id in ids { - let src_msg_id = id?; - if !dc_msg_load_from_db(msg, context, src_msg_id as u32) { - break; - } - dc_param_set_packed(original_param, (*(*msg).param).packed); - if (*msg).from_id != 1i32 as libc::c_uint { - dc_param_set_int((*msg).param, 'a' as i32, 1i32); - } - dc_param_set((*msg).param, 'c' as i32, 0 as *const libc::c_char); - dc_param_set((*msg).param, 'u' as i32, 0 as *const libc::c_char); - dc_param_set((*msg).param, 'S' as i32, 0 as *const libc::c_char); - let new_msg_id: uint32_t; - if (*msg).state == 18i32 { - let fresh9 = curr_timestamp; - curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh9); - let save_param: *mut dc_param_t = (*msg).param; - (*msg).param = original_param; - (*msg).id = src_msg_id as uint32_t; - let old_fwd: *mut libc::c_char = dc_param_get( - (*msg).param, - 'P' as i32, - b"\x00" as *const u8 as *const libc::c_char, - ); - let new_fwd: *mut libc::c_char = dc_mprintf( - b"%s %d\x00" as *const u8 as *const libc::c_char, - old_fwd, - new_msg_id, - ); - dc_param_set((*msg).param, 'P' as i32, new_fwd); - dc_msg_save_param_to_disk(msg); - free(new_fwd as *mut libc::c_void); - free(old_fwd as *mut libc::c_void); - (*msg).param = save_param - } else { - (*msg).state = 20i32; - let fresh10 = curr_timestamp; - curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); - dc_job_send_msg(context, new_msg_id); - } - carray_add( - created_db_entries, - chat_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); - carray_add( - created_db_entries, - new_msg_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); - } - Ok(()) - }, - ) - .unwrap(); // TODO: better error handling + let ids = context.sql.query_map( + format!( + "SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id", + as_str(idsstr) + ), + params![], + |row| row.get::<_, i32>(0), + |ids| ids.collect::, _>>().map_err(Into::into), + ); + + for id in ids.unwrap() { + let src_msg_id = id; + if !dc_msg_load_from_db(msg, context, src_msg_id as u32) { + break; + } + dc_param_set_packed(original_param, (*(*msg).param).packed); + if (*msg).from_id != 1i32 as libc::c_uint { + dc_param_set_int((*msg).param, 'a' as i32, 1i32); + } + dc_param_set((*msg).param, 'c' as i32, 0 as *const libc::c_char); + dc_param_set((*msg).param, 'u' as i32, 0 as *const libc::c_char); + dc_param_set((*msg).param, 'S' as i32, 0 as *const libc::c_char); + let new_msg_id: uint32_t; + if (*msg).state == 18i32 { + let fresh9 = curr_timestamp; + curr_timestamp = curr_timestamp + 1; + new_msg_id = prepare_msg_raw(context, chat, msg, fresh9); + let save_param: *mut dc_param_t = (*msg).param; + (*msg).param = original_param; + (*msg).id = src_msg_id as uint32_t; + let old_fwd: *mut libc::c_char = dc_param_get( + (*msg).param, + 'P' as i32, + b"\x00" as *const u8 as *const libc::c_char, + ); + let new_fwd: *mut libc::c_char = dc_mprintf( + b"%s %d\x00" as *const u8 as *const libc::c_char, + old_fwd, + new_msg_id, + ); + dc_param_set((*msg).param, 'P' as i32, new_fwd); + dc_msg_save_param_to_disk(msg); + free(new_fwd as *mut libc::c_void); + free(old_fwd as *mut libc::c_void); + (*msg).param = save_param + } else { + (*msg).state = 20i32; + let fresh10 = curr_timestamp; + curr_timestamp = curr_timestamp + 1; + new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); + dc_job_send_msg(context, new_msg_id); + } + carray_add( + created_db_entries, + chat_id as uintptr_t as *mut libc::c_void, + 0 as *mut libc::c_uint, + ); + carray_add( + created_db_entries, + new_msg_id as uintptr_t as *mut libc::c_void, + 0 as *mut libc::c_uint, + ); + } } if !created_db_entries.is_null() { @@ -2094,7 +2098,7 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { 0, ) .unwrap_or_else(|| "Err".into()); - ret = dc_strdup(to_cstring(ret_raw).as_ptr()); + ret = to_cstring(ret_raw); } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { if (*chat).id == 1 { ret = dc_stock_str((*chat).context, 8) diff --git a/src/dc_configure.rs b/src/dc_configure.rs index 5276159a5..afe339383 100644 --- a/src/dc_configure.rs +++ b/src/dc_configure.rs @@ -102,14 +102,14 @@ pub fn dc_stop_ongoing_process(context: &Context) { pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) { let flags: libc::c_int; let mut current_block: u64; - let mut success: libc::c_int = 0i32; - let mut imap_connected_here: libc::c_int = 0i32; - let mut smtp_connected_here: libc::c_int = 0i32; - let mut ongoing_allocated_here: libc::c_int = 0i32; + let mut success = false; + let mut imap_connected_here = false; + let mut smtp_connected_here = false; + let mut ongoing_allocated_here = false; let mut param_autoconfig = None; if !(0 == dc_alloc_ongoing(context)) { - ongoing_allocated_here = 1i32; + ongoing_allocated_here = true; if !context.sql.is_open() { error!(context, 0, "Cannot configure, database not opened.",); } else { @@ -634,7 +634,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j let r_0 = dc_loginparam_get_readable(¶m); info!(context, 0, "Trying: {}", r_0,); - if 0 != context + if context .inbox .read() .unwrap() @@ -671,7 +671,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j let r_1 = dc_loginparam_get_readable(¶m); info!(context, 0, "Trying: {}", r_1,); - if 0 != context + if context .inbox .read() .unwrap() @@ -703,7 +703,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j let r_2 = dc_loginparam_get_readable(¶m); info!(context, 0, "Trying: {}", r_2,); - if 0 != context + if context .inbox .read() .unwrap() @@ -759,7 +759,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - imap_connected_here = 1; + imap_connected_here = true; if !s.shall_stop_ongoing { context.call_cb( Event::CONFIGURE_PROGRESS, @@ -774,7 +774,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j 0 as uintptr_t, ); /* try to connect to SMTP - if we did not got an autoconfig, the first try was SSL-465 and we do a second try with STARTTLS-587 */ - if 0 == context + if !context .smtp .clone() .lock() @@ -814,7 +814,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j 0, "Trying: {}", r_3, ); - if 0 == context + if !context .smtp .clone() .lock() @@ -860,7 +860,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j r_4 ); - if 0 == context + if !context .smtp .clone() .lock() @@ -887,7 +887,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j match current_block { 2927484062889439186 => {} _ => { - smtp_connected_here = 1; + smtp_connected_here = true; if !s.shall_stop_ongoing { context.call_cb( Event::CONFIGURE_PROGRESS, @@ -991,7 +991,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j uintptr_t ); dc_ensure_secret_key_exists(context); - success = 1; + success = true; info!( context, 0, @@ -1039,21 +1039,37 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j } } } - - if 0 != imap_connected_here { + if imap_connected_here { context.inbox.read().unwrap().disconnect(context); } - if 0 != smtp_connected_here { + if smtp_connected_here { context.smtp.clone().lock().unwrap().disconnect(); } - if 0 != ongoing_allocated_here { + /* + if !success { + // disconnect if configure did not succeed + if imap_connected_here { + // context.inbox.read().unwrap().disconnect(context); + } + if smtp_connected_here { + // context.smtp.clone().lock().unwrap().disconnect(); + } + } else { + assert!(imap_connected_here && smtp_connected_here); + info!( + context, + 0, "Keeping IMAP/SMTP connections open after successful configuration" + ); + } + */ + if ongoing_allocated_here { dc_free_ongoing(context); } context.call_cb( Event::CONFIGURE_PROGRESS, - (if 0 != success { 1000 } else { 0 }) as uintptr_t, + (if success { 1000 } else { 0 }) as uintptr_t, 0 as uintptr_t, ); } @@ -1082,12 +1098,14 @@ unsafe fn moz_autoconfigure( tag_config: 0, }; - let xml_raw = read_autoconf_file(context, to_cstring(url).as_ptr()); + let url_c = to_cstring(url); + let xml_raw = read_autoconf_file(context, url_c); + free(url_c as *mut libc::c_void); if xml_raw.is_null() { return None; } - moz_ac.in_emaillocalpart = dc_strdup(to_cstring(¶m_in.addr).as_ptr()); + moz_ac.in_emaillocalpart = to_cstring(¶m_in.addr); let p = strchr(moz_ac.in_emaillocalpart, '@' as i32); if p.is_null() { @@ -1144,11 +1162,13 @@ unsafe fn moz_autoconfigure_text_cb( let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t; let mut val: *mut libc::c_char = dc_strdup(text); dc_trim(val); + let addr = to_cstring(&(*moz_ac).in_0.addr); dc_str_replace( &mut val, b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char, - to_cstring(&(*moz_ac).in_0.addr).as_ptr(), + addr, ); + free(addr as *mut libc::c_void); dc_str_replace( &mut val, b"%EMAILLOCALPART%\x00" as *const u8 as *const libc::c_char, @@ -1282,7 +1302,7 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc: .send() .and_then(|mut res| res.text()) { - Ok(res) => unsafe { libc::strdup(to_cstring(res).as_ptr()) }, + Ok(res) => unsafe { to_cstring(res) }, Err(_err) => { info!(context, 0, "Can\'t read file.",); @@ -1298,7 +1318,7 @@ unsafe fn outlk_autodiscover( ) -> Option { let current_block: u64; let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char; - let mut url = dc_strdup(to_cstring(url__).as_ptr()); + let mut url = to_cstring(url__); let mut outlk_ad = outlk_autodiscover_t { in_0: param_in, out: dc_loginparam_new(), @@ -1502,7 +1522,7 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_ let param = dc_loginparam_read(context, &context.sql, "configured_"); // the trailing underscore is correct - if 0 != imap.connect(context, ¶m) { + if imap.connect(context, ¶m) { ret_connected = 2; } } diff --git a/src/dc_contact.rs b/src/dc_contact.rs index 5e0565aa6..e6e3d2185 100644 --- a/src/dc_contact.rs +++ b/src/dc_contact.rs @@ -277,15 +277,12 @@ pub unsafe fn dc_contact_load_from_db( if contact_id == 1 as libc::c_uint { (*contact).id = contact_id; (*contact).name = dc_stock_str((*contact).context, 2); - (*contact).addr = dc_strdup( - to_cstring( - (*contact) - .context - .sql - .get_config((*contact).context, "configured_addr") - .unwrap_or_default(), - ) - .as_ptr(), + (*contact).addr = to_cstring( + (*contact) + .context + .sql + .get_config((*contact).context, "configured_addr") + .unwrap_or_default(), ); true } else { @@ -294,11 +291,11 @@ pub unsafe fn dc_contact_load_from_db( params![contact_id as i32], |row| { (*contact).id = contact_id; - (*contact).name = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); - (*contact).addr = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); + (*contact).name = to_cstring(row.get::<_, String>(0)?); + (*contact).addr = to_cstring(row.get::<_, String>(1)?); (*contact).origin = row.get(2)?; - (*contact).blocked = row.get(3)?; - (*contact).authname = dc_strdup(to_cstring(row.get::<_, String>(4)?).as_ptr()); + (*contact).blocked = row.get::<_, Option>(3)?.unwrap_or_default(); + (*contact).authname = to_cstring(row.get::<_, String>(4)?); Ok(()) } ).is_ok() @@ -735,7 +732,7 @@ pub unsafe fn dc_get_contact_encrinfo( free(fingerprint_other_verified as *mut libc::c_void); free(fingerprint_other_unverified as *mut libc::c_void); - strdup(to_cstring(ret).as_ptr()) + to_cstring(ret) } unsafe fn cat_fingerprint( @@ -905,7 +902,7 @@ pub fn dc_contact_get_profile_image(contact: *const dc_contact_t) -> *mut libc:: if unsafe { (*contact).id } == 1 { let context = unsafe { (*contact) }.context; if let Some(avatar) = context.get_config(config::Config::Selfavatar) { - image_abs = unsafe { dc_strdup(to_cstring(avatar).as_ptr()) }; + image_abs = unsafe { to_cstring(avatar) }; } } // TODO: else get image_abs from contact param diff --git a/src/dc_dehtml.rs b/src/dc_dehtml.rs index 805ea5c6b..ae0cd7546 100644 --- a/src/dc_dehtml.rs +++ b/src/dc_dehtml.rs @@ -53,7 +53,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char dc_saxparser_parse(&mut saxparser, buf_terminated); free(dehtml.last_href as *mut libc::c_void); - strdup(to_cstring(dehtml.strbuilder).as_ptr()) + to_cstring(dehtml.strbuilder) } unsafe fn dehtml_text_cb( diff --git a/src/dc_e2ee.rs b/src/dc_e2ee.rs index af31b6415..979e374a6 100644 --- a/src/dc_e2ee.rs +++ b/src/dc_e2ee.rs @@ -1,5 +1,5 @@ use std::collections::HashSet; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::str::FromStr; use mmime::clist::*; @@ -184,7 +184,7 @@ pub unsafe fn dc_e2ee_encrypt( b"Autocrypt-Gossip\x00" as *const u8 as *const libc::c_char, ), - strdup(header.as_ptr()), + header, ), ); } @@ -294,10 +294,8 @@ pub unsafe fn dc_e2ee_encrypt( sign_key.as_ref(), ) { let ctext_bytes = ctext_v.len(); - let ctext_c = CString::new(ctext_v).unwrap(); - let ctext = strdup(ctext_c.as_ptr()); - - (*helper).cdata_to_free = ctext as *mut libc::c_void; + let ctext = to_cstring(ctext_v); + (*helper).cdata_to_free = ctext as *mut _; /* create MIME-structure that will contain the encrypted text */ let mut encrypted_part: *mut mailmime = new_data_part( @@ -354,13 +352,13 @@ pub unsafe fn dc_e2ee_encrypt( 14181132614457621749 => {} _ => { let aheader = Aheader::new(addr, public_key, prefer_encrypt); - let rendered = CString::new(aheader.to_string()).unwrap(); + let rendered = to_cstring(aheader.to_string()); mailimf_fields_add( imffields_unprotected, mailimf_field_new_custom( strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char), - strdup(rendered.as_ptr()), + rendered, ), ); } @@ -935,13 +933,12 @@ unsafe fn decrypt_part( add_signatures, ) { let plain_bytes = plain.len(); - let plain_c = CString::new(plain).unwrap(); - let plain_buf = strdup(plain_c.as_ptr()); + let plain_buf = plain.as_ptr() as *const libc::c_char; let mut index: size_t = 0i32 as size_t; let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime; if mailmime_parse( - plain_buf as *const libc::c_char, + plain_buf as *const _, plain_bytes, &mut index, &mut decrypted_mime, diff --git a/src/dc_imex.rs b/src/dc_imex.rs index f55bcdfc1..2d9de702d 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -309,7 +309,7 @@ pub unsafe fn dc_create_setup_code(_context: &Context) -> *mut libc::c_char { ); } - strdup(to_cstring(ret).as_ptr()) + to_cstring(ret) } // TODO should return bool /rtn @@ -539,7 +539,7 @@ pub unsafe fn dc_normalize_setup_code( p1 = p1.offset(1); } - strdup(to_cstring(out).as_ptr()) + to_cstring(out) } pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) { @@ -806,77 +806,75 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char 0, "***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt, ); - context - .sql - .query_map( - "SELECT file_name, file_content FROM backup_blobs ORDER BY id;", - params![], - |row| { - let name: String = row.get(0)?; - let blob: Vec = row.get(1)?; + let res = context.sql.query_map( + "SELECT file_name, file_content FROM backup_blobs ORDER BY id;", + params![], + |row| { + let name: String = row.get(0)?; + let blob: Vec = row.get(1)?; - Ok((name, blob)) - }, - |files| { - let mut loop_success = true; - let mut processed_files_cnt = 0; + Ok((name, blob)) + }, + |files| { + let mut loop_success = true; + let mut processed_files_cnt = 0; - for file in files { - if file.is_err() { - loop_success = false; - break; - } - let (file_name, file_blob) = file.unwrap(); + for file in files { + let (file_name, file_blob) = file?; - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - loop_success = false; - break; - } - processed_files_cnt += 1; - let mut permille = processed_files_cnt * 1000 / total_files_cnt; - if permille < 10 { - permille = 10 - } - if permille > 990 { - permille = 990 - } - context.call_cb(Event::IMEX_PROGRESS, permille as uintptr_t, 0); - if file_blob.is_empty() { - continue; - } - - let pathNfilename = format!("{}/{}", as_str(context.get_blobdir()), file_name); - if dc_write_file_safe(context, &pathNfilename, &file_blob) { - continue; - } - - error!( - context, - 0, - "Storage full? Cannot write file {} with {} bytes.", - &pathNfilename, - file_blob.len(), - ); - // otherwise the user may believe the stuff is imported correctly, but there are files missing ... + if context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { loop_success = false; break; } - - if !loop_success { - return Err(format_err!("fail").into()); + processed_files_cnt += 1; + let mut permille = processed_files_cnt * 1000 / total_files_cnt; + if permille < 10 { + permille = 10 } - sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; - sql::try_execute(context, &context.sql, "VACUUM;"); - Ok(()) - }, - ) - .is_ok() as libc::c_int + if permille > 990 { + permille = 990 + } + context.call_cb(Event::IMEX_PROGRESS, permille as uintptr_t, 0); + if file_blob.is_empty() { + continue; + } + + let pathNfilename = format!("{}/{}", as_str(context.get_blobdir()), file_name); + if dc_write_file_safe(context, &pathNfilename, &file_blob) { + continue; + } + + error!( + context, + 0, + "Storage full? Cannot write file {} with {} bytes.", + &pathNfilename, + file_blob.len(), + ); + // otherwise the user may believe the stuff is imported correctly, but there are files missing ... + loop_success = false; + break; + } + + if !loop_success { + return Err(format_err!("fail").into()); + } + Ok(()) + }, + ); + + res.and_then(|_| { + sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; + sql::try_execute(context, &context.sql, "VACUUM;")?; + Ok(()) + }) + .is_ok() as libc::c_int } /******************************************************************************* @@ -897,7 +895,8 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ .format("delta-chat-%Y-%m-%d.bak") .to_string(); let buffer = to_cstring(res); - let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer.as_ptr()); + let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer); + free(buffer as *mut _); if dest_pathNfilename.is_null() { error!(context, 0, "Cannot get backup file name.",); @@ -973,7 +972,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ sql.prepare( "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);", - move |mut stmt| { + move |mut stmt, _| { let mut processed_files_cnt = 0; for entry in dir_handle { if entry.is_err() { @@ -1096,6 +1095,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let mut imported_cnt: libc::c_int = 0; let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char; + let mut name_c: *mut libc::c_char = 0 as *mut libc::c_char; let mut set_default: libc::c_int; let mut buf: *mut libc::c_char = 0 as *mut libc::c_char; let mut buf_bytes: size_t = 0 as size_t; @@ -1123,8 +1123,9 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let entry = entry.unwrap(); free(suffix as *mut libc::c_void); let name_f = entry.file_name(); - let name_c = to_cstring(name_f.to_string_lossy()); - suffix = dc_get_filesuffix_lc(name_c.as_ptr()); + free(name_c as *mut libc::c_void); + name_c = to_cstring(name_f.to_string_lossy()); + suffix = dc_get_filesuffix_lc(name_c); if suffix.is_null() || strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0 { @@ -1134,7 +1135,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> path_plus_name = dc_mprintf( b"%s/%s\x00" as *const u8 as *const libc::c_char, dir_name, - name_c.as_ptr(), + name_c, ); info!(context, 0, "Checking: {}", as_str(path_plus_name)); free(buf as *mut libc::c_void); @@ -1172,12 +1173,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> } } set_default = 1; - if !strstr( - name_c.as_ptr(), - b"legacy\x00" as *const u8 as *const libc::c_char, - ) - .is_null() - { + if !strstr(name_c, b"legacy\x00" as *const u8 as *const libc::c_char).is_null() { info!( context, 0, @@ -1202,6 +1198,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> } } + free(name_c as *mut libc::c_void); free(suffix as *mut libc::c_void); free(path_plus_name as *mut libc::c_void); free(buf as *mut libc::c_void); diff --git a/src/dc_job.rs b/src/dc_job.rs index b65d31595..13859933f 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -48,14 +48,14 @@ pub struct dc_job_t { } pub unsafe fn dc_perform_imap_jobs(context: &Context) { - info!(context, 0, "INBOX-jobs started...",); + info!(context, 0, "dc_perform_imap_jobs starting.",); let probe_imap_network = *context.probe_imap_network.clone().read().unwrap(); *context.probe_imap_network.write().unwrap() = 0; *context.perform_inbox_jobs_needed.write().unwrap() = 0; dc_job_perform(context, 100, probe_imap_network); - info!(context, 0, "INBOX-jobs ended.",); + info!(context, 0, "dc_perform_imap_jobs ended.",); } unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) { @@ -80,36 +80,42 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: params_probe }; - let jobs: Vec = context - .sql - .query_map( - query, - params, - |row| { - let job = dc_job_t { - job_id: row.get(0)?, - action: row.get(1)?, - foreign_id: row.get(2)?, - desired_timestamp: row.get(5)?, - added_timestamp: row.get(4)?, - tries: row.get(6)?, - param: dc_param_new(), - try_again: 0, - pending_error: 0 as *mut libc::c_char, - }; + let jobs: Result, _> = context.sql.query_map( + query, + params, + |row| { + let job = dc_job_t { + job_id: row.get(0)?, + action: row.get(1)?, + foreign_id: row.get(2)?, + desired_timestamp: row.get(5)?, + added_timestamp: row.get(4)?, + tries: row.get(6)?, + param: dc_param_new(), + try_again: 0, + pending_error: 0 as *mut libc::c_char, + }; - let packed: String = row.get(3)?; - dc_param_set_packed(job.param, to_cstring(packed).as_ptr()); - Ok(job) - }, - |jobs| { - jobs.collect::, _>>() - .map_err(Into::into) - }, - ) - .unwrap_or_default(); - - for mut job in jobs { + let packed: String = row.get(3)?; + let packed_c = to_cstring(packed); + dc_param_set_packed(job.param, packed_c); + free(packed_c as *mut _); + Ok(job) + }, + |jobs| { + let res = jobs + .collect::, _>>() + .map_err(Into::into); + res + }, + ); + match jobs { + Ok(ref res) => {} + Err(ref err) => { + info!(context, 0, "query failed: {:?}", err); + } + } + for mut job in jobs.unwrap_or_default() { info!( context, 0, @@ -285,7 +291,7 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { let loginparam = dc_loginparam_read(context, &context.sql, "configured_"); let connected = context.smtp.lock().unwrap().connect(context, &loginparam); - if 0 == connected { + if !connected { dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char); current_block = 14216916617354591294; } else { diff --git a/src/dc_location.rs b/src/dc_location.rs index e9924ae45..0b030d41f 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -215,8 +215,10 @@ pub fn dc_get_locations( if 0 != (*loc).msg_id { let txt: String = row.get(9)?; let txt_c = to_cstring(txt); - if 0 != is_marker(txt_c.as_ptr()) { - (*loc).marker = strdup(txt_c.as_ptr()); + if 0 != is_marker(txt_c) { + (*loc).marker = txt_c; + } else { + free(txt_c as *mut _); } } Ok(loc) @@ -330,9 +332,9 @@ pub fn dc_get_location_kml( } if 0 != success { - unsafe { strdup(to_cstring(ret).as_ptr()) } + unsafe { to_cstring(ret) } } else { - 0 as *mut libc::c_char + std::ptr::null_mut() } } @@ -344,7 +346,7 @@ unsafe fn get_kml_timestamp(utc: i64) -> *mut libc::c_char { let res = chrono::NaiveDateTime::from_timestamp(utc, 0) .format("%Y-%m-%dT%H:%M:%SZ") .to_string(); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub unsafe fn dc_get_message_kml( @@ -661,7 +663,7 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu AND timestamp>? \ AND independent=0 \ ORDER BY timestamp;", - |mut stmt_locations| { + |mut stmt_locations, _| { for (chat_id, locations_send_begin, locations_last_sent) in rows.filter_map(|r| match r { Ok(Some(v)) => Some(v), diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 53b0f48cf..08012e972 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -161,24 +161,20 @@ pub unsafe fn dc_mimefactory_load_msg( for row in rows { let (authname, addr) = row?; let addr_c = to_cstring(addr); - if clist_search_string_nocase( - (*factory).recipients_addr, - addr_c.as_ptr(), - ) == 0 - { + if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 { clist_insert_after( (*factory).recipients_names, (*(*factory).recipients_names).last, if !authname.is_empty() { - dc_strdup(to_cstring(authname).as_ptr()) + to_cstring(authname) } else { - 0 as *mut libc::c_char + std::ptr::null_mut() } as *mut libc::c_void, ); clist_insert_after( (*factory).recipients_addr, (*(*factory).recipients_addr).last, - dc_strdup(addr_c.as_ptr()) as *mut libc::c_void, + addr_c as *mut libc::c_void, ); } } @@ -241,8 +237,8 @@ pub unsafe fn dc_mimefactory_load_msg( ); match row { Ok((in_reply_to, references)) => { - (*factory).in_reply_to = dc_strdup(to_cstring(in_reply_to).as_ptr()); - (*factory).references = dc_strdup(to_cstring(references).as_ptr()); + (*factory).in_reply_to = to_cstring(in_reply_to); + (*factory).references = to_cstring(references); } Err(err) => { error!( @@ -266,32 +262,24 @@ pub unsafe fn dc_mimefactory_load_msg( unsafe fn load_from(mut factory: *mut dc_mimefactory_t) { let context = (*factory).context; - (*factory).from_addr = strdup( - to_cstring( - context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(), - ) - .as_ptr(), + (*factory).from_addr = to_cstring( + context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(), ); - (*factory).from_displayname = strdup( - to_cstring( - context - .sql - .get_config(context, "displayname") - .unwrap_or_default(), - ) - .as_ptr(), + + (*factory).from_displayname = to_cstring( + context + .sql + .get_config(context, "displayname") + .unwrap_or_default(), ); - (*factory).selfstatus = strdup( - to_cstring( - context - .sql - .get_config(context, "selfstatus") - .unwrap_or_default(), - ) - .as_ptr(), + (*factory).selfstatus = to_cstring( + context + .sql + .get_config(context, "selfstatus") + .unwrap_or_default(), ); if (*factory).selfstatus.is_null() { (*factory).selfstatus = dc_stock_str((*factory).context, 13) @@ -1185,7 +1173,7 @@ unsafe fn build_body_file( let res = ts .format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix)) .to_string(); - filename_to_send = strdup(to_cstring(res).as_ptr()); + filename_to_send = to_cstring(res); } else if (*msg).type_0 == DC_MSG_AUDIO as libc::c_int { filename_to_send = dc_get_filename(pathNfilename) } else if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index ce6b80b11..12478da69 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -1,5 +1,5 @@ use std::collections::{HashMap, HashSet}; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use charset::Charset; use mmime::mailimf::*; @@ -833,6 +833,7 @@ unsafe fn hash_header( 18 => key = b"References\x00" as *const u8 as *const libc::c_char, 19 => key = b"Subject\x00" as *const u8 as *const libc::c_char, 22 => { + // MAILIMF_FIELD_OPTIONAL_FIELD let optional_field: *const mailimf_optional_field = (*field).fld_data.fld_optional_field; if !optional_field.is_null() { @@ -842,17 +843,16 @@ unsafe fn hash_header( _ => {} } if !key.is_null() { - let key_len: libc::c_int = strlen(key) as libc::c_int; - if out.contains_key(as_str(key)) { - if (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int - || key_len > 5i32 - && strncasecmp(key, b"Chat-\x00" as *const u8 as *const libc::c_char, 5) - == 0i32 - { - out.insert(to_string(key), field); - } - } else { - out.insert(to_string(key), field); + // XXX the optional field sometimes contains invalid UTF8 + // which should not happen (according to the mime standard). + // This might point to a bug in our mime parsing/processing + // logic. As mmime/dc_mimeparser is scheduled fore replacement + // anyway we just use a lossy conversion. + let key_r = &to_string_lossy(key); + if !out.contains_key(key_r) || // key already exists, only overwrite known types (protected headers) + (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key_r.starts_with("Chat-") + { + out.insert(key_r.to_string(), field); } } cur1 = if !cur1.is_null() { @@ -1204,8 +1204,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( current_block = 8795901732489102124; } else { decoded_data_bytes = res.len(); - let res_c = CString::new(res.as_bytes()).unwrap(); - decoded_data = strdup(res_c.as_ptr()); + decoded_data = res.as_ptr() as *const libc::c_char; current_block = 17788412896529399552; } } else { @@ -1341,8 +1340,9 @@ unsafe fn dc_mimeparser_add_single_part_if_known( } if !filename_parts.is_empty() { free(desired_filename as *mut libc::c_void); - desired_filename = - dc_decode_ext_header(to_cstring(filename_parts).as_ptr()); + let parts_c = to_cstring(filename_parts); + desired_filename = dc_decode_ext_header(parts_c); + free(parts_c as *mut _); } if desired_filename.is_null() { let param = mailmime_find_ct_parameter( diff --git a/src/dc_msg.rs b/src/dc_msg.rs index a470b62c2..d8a47d777 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -63,7 +63,7 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch ret += &format!("Cannot load message #{}.", msg_id as usize); dc_msg_unref(msg); dc_contact_unref(contact_from); - return strdup(to_cstring(ret).as_ptr()); + return to_cstring(ret); } let rawtxt = rawtxt.unwrap(); let rawtxt = dc_truncate_str(rawtxt.trim(), 100000); @@ -91,7 +91,7 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch // device-internal message, no further details needed dc_msg_unref(msg); dc_contact_unref(contact_from); - return strdup(to_cstring(ret).as_ptr()); + return to_cstring(ret); } context @@ -209,7 +209,7 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch dc_msg_unref(msg); dc_contact_unref(contact_from); - strdup(to_cstring(ret).as_ptr()) + to_cstring(ret) } pub unsafe fn dc_msg_new_untyped<'a>(context: &'a Context) -> *mut dc_msg_t<'a> { @@ -445,9 +445,12 @@ pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: dc_msg_empty(msg); (*msg).id = row.get::<_, i32>(0)? as u32; - (*msg).rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); - (*msg).in_reply_to = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); - (*msg).server_folder = dc_strdup(to_cstring(row.get::<_, String>(3)?).as_ptr()); + (*msg).rfc724_mid = to_cstring(row.get::<_, String>(1)?); + (*msg).in_reply_to = match row.get::<_, Option>(2)? { + Some(s) => to_cstring(s), + None => std::ptr::null_mut(), + }; + (*msg).server_folder = to_cstring(row.get::<_, String>(3)?); (*msg).server_uid = row.get(4)?; (*msg).move_state = row.get(5)?; (*msg).chat_id = row.get(6)?; @@ -459,15 +462,15 @@ pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: (*msg).type_0 = row.get(12)?; (*msg).state = row.get(13)?; (*msg).is_dc_message = row.get(14)?; - (*msg).text = dc_strdup(to_cstring(row.get::<_, String>(15).unwrap_or_default()).as_ptr()); + (*msg).text = to_cstring(row.get::<_, String>(15).unwrap_or_default()); dc_param_set_packed( (*msg).param, - to_cstring(row.get::<_, String>(16)?).as_ptr() + to_cstring(row.get::<_, String>(16)?) ); (*msg).starred = row.get(17)?; (*msg).hidden = row.get(18)?; (*msg).location_id = row.get(19)?; - (*msg).chat_blocked = row.get(20)?; + (*msg).chat_blocked = row.get::<_, Option>(20)?.unwrap_or_default(); if (*msg).chat_blocked == 2 { dc_truncate_n_unwrap_str((*msg).text, 256, 0); } @@ -478,10 +481,7 @@ pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: match res { Ok(_) => true, - Err(err) => { - error!(context, 0, "msg: load from db failed: {:?}", err); - false - } + Err(err) => false, } } @@ -494,7 +494,10 @@ pub unsafe fn dc_get_mime_headers(context: &Context, msg_id: uint32_t) -> *mut l ); if let Some(headers) = headers { - dc_strdup_keep_null(to_cstring(headers).as_ptr()) + let h = to_cstring(headers); + let res = dc_strdup_keep_null(h); + free(h as *mut _); + res } else { std::ptr::null_mut() } @@ -538,46 +541,48 @@ pub fn dc_markseen_msgs(context: &Context, msg_ids: *const u32, msg_cnt: usize) if msg_ids.is_null() || msg_cnt <= 0 { return false; } - context.sql.prepare( + let msgs = context.sql.prepare( "SELECT m.state, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=? AND m.chat_id>9", - |mut stmt| { - let mut send_event = false; - + |mut stmt, _| { + let mut res = Vec::with_capacity(msg_cnt); for i in 0..msg_cnt { - // TODO: do I need to reset? let id = unsafe { *msg_ids.offset(i as isize) }; - if let Ok((curr_state, curr_blocked)) = stmt - .query_row(params![id as i32], |row| { - Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) - }) - { - if curr_blocked == 0 { - if curr_state == 10 || curr_state == 13 { - dc_update_msg_state(context, id, 16); - info!(context, 0, "Seen message #{}.", id); - - unsafe { dc_job_add( - context, - 130, - id as i32, - 0 as *const libc::c_char, - 0, - ) }; - send_event = true; - } - } else if curr_state == 10 { - dc_update_msg_state(context, id, 13); - send_event = true; - } - } + let (state, blocked) = stmt.query_row(params![id as i32], |row| { + Ok((row.get::<_, i32>(0)?, row.get::<_, Option>(1)?.unwrap_or_default())) + })?; + res.push((id, state, blocked)); } - - if send_event { - context.call_cb(Event::MSGS_CHANGED, 0, 0); - } - Ok(()) + Ok(res) } - ).is_ok() + ); + + if msgs.is_err() { + warn!(context, 0, "markseen_msgs failed: {:?}", msgs); + return false; + } + let mut send_event = false; + let msgs = msgs.unwrap(); + + for (id, curr_state, curr_blocked) in msgs.into_iter() { + if curr_blocked == 0 { + if curr_state == 10 || curr_state == 13 { + dc_update_msg_state(context, id, 16); + info!(context, 0, "Seen message #{}.", id); + + unsafe { dc_job_add(context, 130, id as i32, 0 as *const libc::c_char, 0) }; + send_event = true; + } + } else if curr_state == 10 { + dc_update_msg_state(context, id, 13); + send_event = true; + } + } + + if send_event { + context.call_cb(Event::MSGS_CHANGED, 0, 0); + } + + true } pub fn dc_update_msg_state(context: &Context, msg_id: uint32_t, state: libc::c_int) -> bool { @@ -601,7 +606,7 @@ pub fn dc_star_msgs( } context .sql - .prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt| { + .prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt, _| { for i in 0..msg_cnt { stmt.execute(params![star, unsafe { *msg_ids.offset(i as isize) as i32 }])?; } @@ -689,7 +694,7 @@ pub unsafe fn dc_msg_get_text(msg: *const dc_msg_t) -> *mut libc::c_char { } let res = dc_truncate_str(as_str((*msg).text), 30000); - dc_strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub unsafe fn dc_msg_get_filename(msg: *const dc_msg_t) -> *mut libc::c_char { @@ -1355,9 +1360,7 @@ pub fn dc_rfc724_mid_exists( &[as_str(rfc724_mid)], |row| { if !ret_server_folder.is_null() { - unsafe { - *ret_server_folder = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()) - }; + unsafe { *ret_server_folder = to_cstring(row.get::<_, String>(0)?) }; } if !ret_server_uid.is_null() { unsafe { *ret_server_uid = row.get(1)? }; diff --git a/src/dc_qr.rs b/src/dc_qr.rs index eb7e38102..3c8d39d14 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -239,13 +239,8 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc if addr.is_null() || invitenumber.is_null() || auth.is_null() { if let Some(peerstate) = peerstate { (*qr_parsed).state = 210i32; - let c_addr = peerstate - .addr - .as_ref() - .map(to_cstring) - .unwrap_or_default(); - let addr_ptr = if peerstate.addr.is_some() { - c_addr.as_ptr() + let addr_ptr = if let Some(ref addr) = peerstate.addr { + to_cstring(addr) } else { std::ptr::null() }; @@ -256,6 +251,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc 0x80i32, 0 as *mut libc::c_int, ); + free(addr_ptr as *mut _); dc_create_or_lookup_nchat_by_contact_id( context, (*qr_parsed).id, diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index f11652162..913d76380 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -443,7 +443,7 @@ pub unsafe fn dc_receive_imf( timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, \ bytes, hidden, mime_headers, mime_in_reply_to, mime_references) \ VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);", - |mut stmt| { + |mut stmt, conn| { let mut i = 0; loop { if !(i < icnt) { @@ -503,16 +503,21 @@ pub unsafe fn dc_receive_imf( } else { "" }, + // txt_raw might contain invalid utf8 if !txt_raw.is_null() { - as_str(txt_raw) + to_string_lossy(txt_raw) } else { - "" + String::new() }, as_str((*(*part).param).packed), (*part).bytes, hidden, if 0 != save_mime_headers { - Some(to_string(imf_raw_not_terminated)) + let body_string = unsafe { + std::str::from_utf8(std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes)).unwrap() + }; + + Some(body_string) } else { None }, @@ -528,9 +533,9 @@ pub unsafe fn dc_receive_imf( } else { free(txt_raw as *mut libc::c_void); txt_raw = 0 as *mut libc::c_char; - insert_msg_id = sql::get_rowid( + insert_msg_id = sql::get_rowid_with_conn( context, - &context.sql, + conn, "msgs", "rfc724_mid", as_str(rfc724_mid), @@ -739,11 +744,9 @@ pub unsafe fn dc_receive_imf( } if 0 != mime_parser.is_send_by_messenger || 0 != mdn_consumed { let param = dc_param_new(); - dc_param_set( - param, - 'Z' as i32, - to_cstring(server_folder.as_ref()).as_ptr(), - ); + let server_folder_c = to_cstring(server_folder.as_ref()); + dc_param_set(param, 'Z' as i32, server_folder_c); + free(server_folder_c as *mut _); dc_param_set_int(param, 'z' as i32, server_uid as i32); if 0 != mime_parser.is_send_by_messenger && 0 != context @@ -827,6 +830,14 @@ pub unsafe fn dc_receive_imf( } } + info!( + context, + 0, + "received message {} has Message-Id: {}", + server_uid, + to_string(rfc724_mid) + ); + free(rfc724_mid as *mut libc::c_void); free(mime_in_reply_to as *mut libc::c_void); free(mime_references as *mut libc::c_void); @@ -1375,7 +1386,7 @@ unsafe fn create_or_lookup_adhoc_group( ), params![], |row| { - Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) + Ok((row.get::<_, i32>(0)?, row.get::<_, Option>(1)?.unwrap_or_default())) } ); @@ -1518,9 +1529,7 @@ fn hex_hash(s: impl AsRef) -> *const libc::c_char { let bytes = s.as_ref().as_bytes(); let result = Sha256::digest(bytes); let result_hex = hex::encode(&result[..8]); - let result_cstring = to_cstring(result_hex); - - unsafe { strdup(result_cstring.as_ptr()) } + unsafe { to_cstring(result_hex) as *const _ } } unsafe fn search_chat_ids_by_contact_ids( @@ -1604,8 +1613,7 @@ unsafe fn check_verified_properties( let contact = dc_contact_new(context); let verify_fail = |reason: String| { - *failure_reason = - strdup(to_cstring(format!("{}. See \"Info\" for details.", reason)).as_ptr()); + *failure_reason = to_cstring(format!("{}. See \"Info\" for details.", reason)); warn!(context, 0, "{}", reason); }; @@ -1651,68 +1659,61 @@ unsafe fn check_verified_properties( let to_ids_str = to_string(to_ids_str_c); free(to_ids_str_c as *mut libc::c_void); - let ok = context - .sql - .query_map( - format!( - "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ - LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ", - &to_ids_str, - ), - params![], - |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)), - |rows| { - for row in rows { - let (to_addr, mut is_verified) = row?; - let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); - if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) - && peerstate.is_some() - { - let peerstate = peerstate.as_mut().unwrap(); + let rows = context.sql.query_map( + format!( + "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ", + &to_ids_str, + ), + params![], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)), + |rows| rows.collect::, _>>().map_err(Into::into), + ); - // if we're here, we know the gossip key is verified: - // - use the gossip-key as verified-key if there is no verified-key - // - OR if the verified-key does not match public-key or gossip-key - // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, - // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) - if 0 == is_verified - || peerstate.verified_key_fingerprint - != peerstate.public_key_fingerprint - && peerstate.verified_key_fingerprint - != peerstate.gossip_key_fingerprint - { - info!( - context, - 0, - "{} has verfied {}.", - as_str((*contact).addr), - to_addr, - ); - let fp = peerstate.gossip_key_fingerprint.clone(); - if let Some(fp) = fp { - peerstate.set_verified(0, &fp, 2); - peerstate.save_to_db(&context.sql, false); - is_verified = 1; - } - } - } - if 0 == is_verified { - verify_fail(format!( - "{} is not a member of this verified group", - to_addr - )); - cleanup(); - return Err(failure::format_err!("not a valid member").into()); - } + if rows.is_err() { + cleanup(); + return 0; + } + for (to_addr, mut is_verified) in rows.unwrap().into_iter() { + let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); + if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() { + let peerstate = peerstate.as_mut().unwrap(); + + // if we're here, we know the gossip key is verified: + // - use the gossip-key as verified-key if there is no verified-key + // - OR if the verified-key does not match public-key or gossip-key + // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, + // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) + if 0 == is_verified + || peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint + && peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint + { + info!( + context, + 0, + "{} has verfied {}.", + as_str((*contact).addr), + to_addr, + ); + let fp = peerstate.gossip_key_fingerprint.clone(); + if let Some(fp) = fp { + peerstate.set_verified(0, &fp, 2); + peerstate.save_to_db(&context.sql, false); + is_verified = 1; } - Ok(()) - }, - ) - .is_ok(); // TODO: Better default + } + } + if 0 == is_verified { + verify_fail(format!( + "{} is not a member of this verified group", + to_addr + )); + cleanup(); + return 0; + } + } - cleanup(); - - ok as libc::c_int + 1 } unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) { diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 07d34637d..af8bacdf7 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -62,7 +62,7 @@ pub unsafe fn dc_get_securejoin_qr( free(group_name_urlencoded as *mut libc::c_void); if let Some(qr) = qr { - strdup(to_cstring(qr).as_ptr()) + to_cstring(qr) } else { std::ptr::null_mut() } @@ -939,15 +939,15 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) &mut contact_chat_id, 0 as *mut libc::c_int, ); - let c_addr = peerstate.addr.as_ref().map(to_cstring).unwrap_or_default(); - let c_addr_ptr = if peerstate.addr.is_some() { - c_addr.as_ptr() + let c_addr_ptr = if let Some(ref addr) = peerstate.addr { + to_cstring(addr) } else { std::ptr::null_mut() }; let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr); dc_add_device_msg(context, contact_chat_id, msg); free(msg as *mut libc::c_void); + free(c_addr_ptr as *mut _); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, diff --git a/src/dc_simplify.rs b/src/dc_simplify.rs index 48abc7373..705c53648 100644 --- a/src/dc_simplify.rs +++ b/src/dc_simplify.rs @@ -225,7 +225,8 @@ unsafe fn dc_simplify_simplify_plain_text( pending_linebreaks -= 1 } } - ret += &to_string(line); + // the incoming message might contain invalid UTF8 + ret += &to_string_lossy(line); content_lines_added += 1; pending_linebreaks = 1i32 } @@ -238,7 +239,7 @@ unsafe fn dc_simplify_simplify_plain_text( } dc_free_splitted_lines(lines); - strdup(to_cstring(ret).as_ptr()) + to_cstring(ret) } /** diff --git a/src/dc_strencode.rs b/src/dc_strencode.rs index bf9b962ef..0af94debd 100644 --- a/src/dc_strencode.rs +++ b/src/dc_strencode.rs @@ -1,4 +1,4 @@ -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use charset::Charset; use mmime::mailmime_decode::*; @@ -689,10 +689,9 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc: std::slice::from_raw_parts(decoded as *const u8, strlen(decoded)); let (res, _, _) = encoding.decode(data); - free(decoded as *mut libc::c_void); - let res_c = CString::new(res.as_bytes()).unwrap(); - - decoded = strdup(res_c.as_ptr()); + free(decoded as *mut _); + let r = std::ffi::CString::new(res.as_bytes()).unwrap(); + decoded = dc_strdup(r.as_ptr()); } } } @@ -712,7 +711,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) { let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur)); let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2])); - libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4); + libc::memcpy(target as *mut _, raw as *const _, 4); + free(raw as *mut libc::c_void); } #[cfg(test)] diff --git a/src/dc_token.rs b/src/dc_token.rs index 33583c7ae..ebbe2748e 100644 --- a/src/dc_token.rs +++ b/src/dc_token.rs @@ -1,7 +1,6 @@ use crate::context::Context; use crate::dc_tools::*; use crate::sql; -use crate::x::strdup; // Token namespaces pub type dc_tokennamespc_t = usize; @@ -34,16 +33,16 @@ pub fn dc_token_lookup( namespc: dc_tokennamespc_t, foreign_id: u32, ) -> *mut libc::c_char { - if let Some(token) = context.sql.query_row_col::<_, String>( - context, - "SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;", - params![namespc as i32, foreign_id as i32], - 0, - ) { - unsafe { strdup(to_cstring(token).as_ptr()) } - } else { - std::ptr::null_mut() - } + context + .sql + .query_row_col::<_, String>( + context, + "SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;", + params![namespc as i32, foreign_id as i32], + 0, + ) + .map(|s| unsafe { to_cstring(s) }) + .unwrap_or_else(|| std::ptr::null_mut()) } pub fn dc_token_exists( diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 42c875b5d..7eed3cf8d 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::ffi::{CStr, CString}; use std::fs; use std::time::SystemTime; @@ -176,14 +177,14 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) { /* the result must be free()'d */ pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char { - let raw = to_cstring(to_string(in_0).to_lowercase()); - strdup(raw.as_ptr()) + to_cstring(to_string(in_0).to_lowercase()) } pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) { let raw = to_cstring(to_string(in_0).to_lowercase()); - assert_eq!(strlen(in_0), strlen(raw.as_ptr())); - memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0)); + assert_eq!(strlen(in_0), strlen(raw)); + memcpy(in_0 as *mut _, raw as *const _, strlen(in_0)); + free(raw as *mut _); } pub unsafe fn dc_str_contains( @@ -231,7 +232,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li let buf = std::slice::from_raw_parts(buf, bytes); let raw = hex::encode_upper(buf); - strdup(to_cstring(raw).as_ptr()) + to_cstring(raw) } /* remove all \r characters from string */ @@ -527,7 +528,7 @@ pub unsafe fn dc_str_from_clist( } } - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub unsafe fn dc_str_to_clist( @@ -669,7 +670,7 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 { /* the return value must be free()'d */ pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char { let res = dc_timestamp_to_str_safe(wanted); - strdup(to_cstring(res).as_ptr()) + to_cstring(res) } pub fn dc_timestamp_to_str_safe(wanted: i64) -> String { @@ -1254,8 +1255,12 @@ pub unsafe fn dc_write_file( } pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef, buf: &[u8]) -> bool { - let pathNfilename_abs = - unsafe { dc_get_abs_path(context, to_cstring(pathNfilename.as_ref()).as_ptr()) }; + let pathNfilename_abs = unsafe { + let n = to_cstring(pathNfilename.as_ref()); + let res = dc_get_abs_path(context, n); + free(n as *mut _); + res + }; if pathNfilename_abs.is_null() { return false; } @@ -1299,8 +1304,13 @@ pub unsafe fn dc_read_file( } pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef) -> Option> { - let pathNfilename_abs = - unsafe { dc_get_abs_path(context, to_cstring(pathNfilename.as_ref()).as_ptr()) }; + let pathNfilename_abs = unsafe { + let n = to_cstring(pathNfilename.as_ref()); + let p = dc_get_abs_path(context, n); + free(n as *mut _); + p + }; + if pathNfilename_abs.is_null() { return None; } @@ -1506,20 +1516,20 @@ pub trait OsStrExt { /// /// On windows when the string contains invalid Unicode /// `[Err]([CStringError::NotUnicode])` is returned. - fn to_c_string(&self) -> Result; + fn to_c_string(&self) -> Result; } impl> OsStrExt for T { #[cfg(not(target_os = "windows"))] - fn to_c_string(&self) -> Result { + fn to_c_string(&self) -> Result { use std::os::unix::ffi::OsStrExt; - std::ffi::CString::new(self.as_ref().as_bytes()).map_err(|err| match err { + CString::new(self.as_ref().as_bytes()).map_err(|err| match err { std::ffi::NulError { .. } => CStringError::InteriorNullByte, }) } #[cfg(target_os = "windows")] - fn to_c_string(&self) -> Result { + fn to_c_string(&self) -> Result { os_str_to_c_string_unicode(&self) } } @@ -1528,29 +1538,57 @@ impl> OsStrExt for T { #[allow(dead_code)] fn os_str_to_c_string_unicode( os_str: &dyn AsRef, -) -> Result { +) -> Result { match os_str.as_ref().to_str() { - Some(val) => std::ffi::CString::new(val.as_bytes()).map_err(|err| match err { + Some(val) => CString::new(val.as_bytes()).map_err(|err| match err { std::ffi::NulError { .. } => CStringError::InteriorNullByte, }), None => Err(CStringError::NotUnicode), } } -pub fn to_cstring>(s: S) -> std::ffi::CString { - std::ffi::CString::new(s.as_ref()).unwrap() +/// Needs to free the result after use! +pub unsafe fn to_cstring>(s: S) -> *mut libc::c_char { + let cstr = CString::new(s.as_ref()).expect("invalid string converted"); + dc_strdup(cstr.as_ref().as_ptr()) } pub fn to_string(s: *const libc::c_char) -> String { if s.is_null() { return "".into(); } - unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap().to_string() } + + let cstr = unsafe { CStr::from_ptr(s) }; + + cstr.to_str().map(|s| s.to_string()).unwrap_or_else(|err| { + panic!( + "Non utf8 string: '{:?}' ({:?})", + cstr.to_string_lossy(), + err + ); + }) +} + +pub fn to_string_lossy(s: *const libc::c_char) -> String { + if s.is_null() { + return "".into(); + } + + let cstr = unsafe { CStr::from_ptr(s) }; + + cstr.to_str() + .map(|s| s.to_string()) + .unwrap_or_else(|_| cstr.to_string_lossy().to_string()) } pub fn as_str<'a>(s: *const libc::c_char) -> &'a str { assert!(!s.is_null(), "cannot be used on null pointers"); - unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap() } + + let cstr = unsafe { CStr::from_ptr(s) }; + + cstr.to_str().unwrap_or_else(|err| { + panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err); + }) } /// Convert a C `*char` pointer to a [std::path::Path] slice. @@ -1981,7 +2019,7 @@ mod tests { let some_dir = std::path::Path::new(&some_str); assert_eq!( some_dir.as_os_str().to_c_string().unwrap(), - std::ffi::CString::new("/some/valid/utf8").unwrap() + CString::new("/some/valid/utf8").unwrap() ); } @@ -2006,7 +2044,7 @@ mod tests { let some_dir = std::path::Path::new(&some_str); assert_eq!( some_dir.as_os_str().to_c_string().unwrap(), - std::ffi::CString::new("/some/valid/utf8").unwrap() + CString::new("/some/valid/utf8").unwrap() ); } @@ -2015,7 +2053,7 @@ mod tests { let some_str = std::ffi::OsString::from("foo"); assert_eq!( os_str_to_c_string_unicode(&some_str).unwrap(), - std::ffi::CString::new("foo").unwrap() + CString::new("foo").unwrap() ); } @@ -2025,7 +2063,7 @@ mod tests { let some_path = std::path::Path::new(&some_str); assert_eq!( os_str_to_c_string_unicode(&some_path).unwrap(), - std::ffi::CString::new("/some/path").unwrap() + CString::new("/some/path").unwrap() ); } @@ -2040,15 +2078,15 @@ mod tests { #[test] fn test_as_path() { - let some_path = std::ffi::CString::new("/some/path").unwrap(); + let some_path = CString::new("/some/path").unwrap(); let ptr = some_path.as_ptr(); assert_eq!(as_path(ptr), std::ffi::OsString::from("/some/path")) } #[test] fn test_as_path_unicode_fn() { - let some_path = std::ffi::CString::new("/some/path").unwrap(); + let some_path = CString::new("/some/path").unwrap(); let ptr = some_path.as_ptr(); - assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path")) + assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path")); } } diff --git a/src/imap.rs b/src/imap.rs index 9f52938fb..c002d0ff4 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1,4 +1,3 @@ -use std::ffi::CString; use std::net; use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::time::{Duration, SystemTime}; @@ -6,9 +5,10 @@ use std::time::{Duration, SystemTime}; use crate::constants::*; use crate::context::Context; use crate::dc_loginparam::*; -use crate::dc_tools::as_str; +use crate::dc_tools::{as_str, to_cstring}; use crate::oauth2::dc_get_oauth2_access_token; use crate::types::*; +use crate::x::free; pub const DC_IMAP_SEEN: usize = 0x0001; pub const DC_REGENERATE: usize = 0x01; @@ -32,7 +32,8 @@ pub struct Imap { precheck_imf: dc_precheck_imf_t, receive_imf: dc_receive_imf_t, - session: Arc, Option)>>, + session: Arc>>, + stream: Arc>>, connected: Arc>, } @@ -350,7 +351,8 @@ impl Imap { receive_imf: dc_receive_imf_t, ) -> Self { Imap { - session: Arc::new(Mutex::new((None, None))), + session: Arc::new(Mutex::new(None)), + stream: Arc::new(RwLock::new(None)), config: Arc::new(RwLock::new(ImapConfig::default())), watch: Arc::new((Mutex::new(false), Condvar::new())), get_config, @@ -369,18 +371,18 @@ impl Imap { self.config.read().unwrap().should_reconnect } - fn setup_handle_if_needed(&self, context: &Context) -> libc::c_int { + fn setup_handle_if_needed(&self, context: &Context) -> bool { if self.config.read().unwrap().imap_server.is_empty() { - return 0; + return false; } if self.should_reconnect() { self.unsetup_handle(context); } - if self.is_connected() && self.session.lock().unwrap().1.is_some() { + if self.is_connected() && self.stream.read().unwrap().is_some() { self.config.write().unwrap().should_reconnect = false; - return 1; + return true; } let server_flags = self.config.read().unwrap().server_flags; @@ -424,7 +426,7 @@ impl Imap { }; client.authenticate("XOAUTH2", &auth) } else { - return 0; + return false; } } else { client.login(imap_user, imap_pw) @@ -445,7 +447,7 @@ impl Imap { err ); - return 0; + return false; } }; @@ -453,29 +455,27 @@ impl Imap { match login_res { Ok((session, stream)) => { - *self.session.lock().unwrap() = (Some(session), Some(stream)); - 1 + *self.session.lock().unwrap() = Some(session); + *self.stream.write().unwrap() = Some(stream); + true } Err((err, _)) => { log_event!(context, Event::ERROR_NETWORK, 0, "Cannot login ({})", err); self.unsetup_handle(context); - 0 + false } } } fn unsetup_handle(&self, context: &Context) { - let session = self.session.lock().unwrap().0.take(); - if session.is_some() { - match session.unwrap().close() { - Ok(_) => {} - Err(err) => { - eprintln!("failed to close connection: {:?}", err); - } - } - } - let stream = self.session.lock().unwrap().1.take(); + info!(context, 0, "IMAP unsetup_handle starts"); + + info!( + context, + 0, "IMAP unsetup_handle step 1 (closing down stream)." + ); + let stream = self.stream.write().unwrap().take(); if stream.is_some() { match stream.unwrap().shutdown(net::Shutdown::Both) { Ok(_) => {} @@ -484,11 +484,24 @@ impl Imap { } } } + info!( + context, + 0, "IMAP unsetup_handle step 2 (acquiring session.lock)" + ); + let session = self.session.lock().unwrap().take(); + if session.is_some() { + match session.unwrap().close() { + Ok(_) => {} + Err(err) => { + eprintln!("failed to close connection: {:?}", err); + } + } + } - let mut cfg = self.config.write().unwrap(); - cfg.selected_folder = None; - cfg.selected_mailbox = None; - info!(context, 0, "IMAP disconnected.",); + info!(context, 0, "IMAP unsetup_handle step 3 (clearing config)."); + self.config.write().unwrap().selected_folder = None; + self.config.write().unwrap().selected_mailbox = None; + info!(context, 0, "IMAP unsetup_handle step 4 (disconnected).",); } fn free_connect_params(&self) { @@ -506,13 +519,13 @@ impl Imap { cfg.watch_folder = None; } - pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> libc::c_int { + pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> bool { if lp.mail_server.is_empty() || lp.mail_user.is_empty() || lp.mail_pw.is_empty() { - return 0; + return false; } if self.is_connected() { - return 1; + return true; } { @@ -532,50 +545,55 @@ impl Imap { config.server_flags = server_flags; } - if self.setup_handle_if_needed(context) == 0 { + if !self.setup_handle_if_needed(context) { self.free_connect_params(); - return 0; + return false; } - match self.session.lock().unwrap().0 { + let teardown: bool; + + match &mut *self.session.lock().unwrap() { Some(ref mut session) => { if let Ok(caps) = session.capabilities() { - let can_idle = caps.has("IDLE"); - let has_xlist = caps.has("XLIST"); - - let caps_list = caps.iter().fold(String::new(), |mut s, c| { - s += " "; - s += c; - s - }); - - log_event!( - context, - Event::IMAP_CONNECTED, - 0, - "IMAP-LOGIN as {} ok", - lp.mail_user, - ); - info!(context, 0, "IMAP-capabilities:{}", caps_list); - - let mut config = self.config.write().unwrap(); - config.can_idle = can_idle; - config.has_xlist = has_xlist; - *self.connected.lock().unwrap() = true; - - 1 + if !context.sql.is_open() { + warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,); + teardown = true; + } else { + let can_idle = caps.has("IDLE"); + let has_xlist = caps.has("XLIST"); + let caps_list = caps.iter().fold(String::new(), |mut s, c| { + s += " "; + s += c; + s + }); + log_event!( + context, + Event::IMAP_CONNECTED, + 0, + "IMAP-LOGIN as {}, capabilities: {}", + lp.mail_user, + caps_list, + ); + self.config.write().unwrap().can_idle = can_idle; + self.config.write().unwrap().has_xlist = has_xlist; + *self.connected.lock().unwrap() = true; + teardown = false; + } } else { - self.unsetup_handle(context); - self.free_connect_params(); - 0 + teardown = true; } } None => { - self.unsetup_handle(context); - self.free_connect_params(); - 0 + teardown = true; } } + if teardown { + self.unsetup_handle(context); + self.free_connect_params(); + false + } else { + true + } } pub fn disconnect(&self, context: &Context) { @@ -591,7 +609,7 @@ impl Imap { } pub fn fetch(&self, context: &Context) -> libc::c_int { - if !self.is_connected() { + if !self.is_connected() || !context.sql.is_open() { return 0; } @@ -615,7 +633,7 @@ impl Imap { } fn select_folder>(&self, context: &Context, folder: Option) -> usize { - if self.session.lock().unwrap().0.is_none() { + if self.session.lock().unwrap().is_none() { let mut cfg = self.config.write().unwrap(); cfg.selected_folder = None; cfg.selected_folder_needs_expunge = false; @@ -639,7 +657,7 @@ impl Imap { // A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see // https://tools.ietf.org/html/rfc3501#section-6.4.2 - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.close() { Ok(_) => {} Err(err) => { @@ -655,7 +673,7 @@ impl Imap { // select new folder if let Some(ref folder) = folder { - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.select(folder) { Ok(mailbox) => { let mut config = self.config.write().unwrap(); @@ -688,17 +706,19 @@ impl Imap { fn get_config_last_seen_uid>(&self, context: &Context, folder: S) -> (u32, u32) { let key = format!("imap.mailbox.{}", folder.as_ref()); let val1 = unsafe { - (self.get_config)( - context, - CString::new(key).unwrap().as_ptr(), - 0 as *const libc::c_char, - ) + let key_c = to_cstring(key); + let val = (self.get_config)(context, key_c, 0 as *const libc::c_char); + free(key_c as *mut _); + val }; if val1.is_null() { return (0, 0); } let entry = as_str(val1); + if entry.is_empty() { + return (0, 0); + } // the entry has the format `imap.mailbox.=:` let mut parts = entry.split(':'); ( @@ -761,7 +781,7 @@ impl Imap { return 0; } - let list = if let Some(ref mut session) = self.session.lock().unwrap().0 { + let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() { // `FETCH (UID)` let set = format!("{}", mailbox.exists); match session.fetch(set, PREFETCH_FLAGS) { @@ -805,7 +825,7 @@ impl Imap { let mut read_errors = 0; let mut new_last_seen_uid = 0; - let list = if let Some(ref mut session) = self.session.lock().unwrap().0 { + let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() { // fetch messages with larger UID than the last one seen // (`UID FETCH lastseenuid+1:*)`, see RFC 4549 let set = format!("{}:*", last_seen_uid + 1); @@ -832,9 +852,11 @@ impl Imap { .message_id .expect("missing message id"); - let message_id_c = CString::new(message_id).unwrap(); if 0 == unsafe { - (self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid) + let message_id_c = to_cstring(message_id); + let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid); + free(message_id_c as *mut _); + res } { // check passed, go fetch the rest if self.fetch_single_msg(context, &folder, cur_uid) == 0 { @@ -903,11 +925,11 @@ impl Imap { let val = format!("{}:{}", uidvalidity, lastseenuid); unsafe { - (self.set_config)( - context, - CString::new(key).unwrap().as_ptr(), - CString::new(val).unwrap().as_ptr(), - ) + let key_c = to_cstring(key); + let val_c = to_cstring(val); + (self.set_config)(context, key_c, val_c); + free(key_c as *mut _); + free(val_c as *mut _); }; } @@ -928,7 +950,7 @@ impl Imap { let set = format!("{}", server_uid); - let msgs = if let Some(ref mut session) = self.session.lock().unwrap().0 { + let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.uid_fetch(set, BODY_FLAGS) { Ok(msgs) => msgs, Err(err) => { @@ -986,11 +1008,12 @@ impl Imap { let flags = if is_seen { DC_IMAP_SEEN } else { 0 }; if !is_deleted && msg.body().is_some() { + let body = msg.body().unwrap(); unsafe { (self.receive_imf)( context, - msg.body().unwrap().as_ptr() as *const libc::c_char, - msg.body().unwrap().len(), + body.as_ptr() as *const libc::c_char, + body.len(), folder.as_ref(), server_uid, flags as u32, @@ -1025,13 +1048,15 @@ impl Imap { let (sender, receiver) = std::sync::mpsc::channel(); let v = self.watch.clone(); + info!(context, 0, "IMAP-IDLE SPAWNING"); std::thread::spawn(move || { let &(ref lock, ref cvar) = &*v; - if let Some(ref mut session) = session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *session.lock().unwrap() { let mut idle = match session.idle() { Ok(idle) => idle, Err(err) => { - panic!("failed to setup idle: {:?}", err); + eprintln!("failed to setup idle: {:?}", err); + return; } }; @@ -1139,7 +1164,7 @@ impl Imap { // check for new messages. fetch_from_single_folder() has the side-effect that messages // are also downloaded, however, typically this would take place in the FETCH command // following IDLE otherwise, so this seems okay here. - if self.setup_handle_if_needed(context) != 0 { + if self.setup_handle_if_needed(context) { if let Some(ref watch_folder) = self.config.read().unwrap().watch_folder { if 0 != self.fetch_from_single_folder(context, watch_folder) { do_fake_idle = false; @@ -1205,7 +1230,7 @@ impl Imap { folder.as_ref() ); } else { - let moved = if let Some(ref mut session) = self.session.lock().unwrap().0 { + let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.uid_mv(&set, &dest_folder) { Ok(_) => { res = DC_SUCCESS; @@ -1230,7 +1255,7 @@ impl Imap { }; if !moved { - let copied = if let Some(ref mut session) = self.session.lock().unwrap().0 { + let copied = if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.uid_copy(&set, &dest_folder) { Ok(_) => true, Err(err) => { @@ -1275,7 +1300,7 @@ impl Imap { if server_uid == 0 { return 0; } - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { let set = format!("{}", server_uid); let query = format!("+FLAGS ({})", flag.as_ref()); match session.uid_store(&set, &query) { @@ -1387,18 +1412,18 @@ impl Imap { .expect("just selected folder"); if can_create_flag { - let fetched_msgs = if let Some(ref mut session) = self.session.lock().unwrap().0 - { - match session.uid_fetch(set, FETCH_FLAGS) { - Ok(res) => Some(res), - Err(err) => { - eprintln!("fetch error: {:?}", err); - None + let fetched_msgs = + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { + match session.uid_fetch(set, FETCH_FLAGS) { + Ok(res) => Some(res), + Err(err) => { + eprintln!("fetch error: {:?}", err); + None + } } - } - } else { - unreachable!(); - }; + } else { + unreachable!(); + }; if let Some(msgs) = fetched_msgs { let flag_set = msgs @@ -1479,7 +1504,7 @@ impl Imap { ); } else { let set = format!("{}", server_uid); - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.uid_fetch(set, PREFETCH_FLAGS) { Ok(msgs) => { if msgs.is_empty() @@ -1561,7 +1586,7 @@ impl Imap { if mvbox_folder.is_none() && 0 != (flags as usize & DC_CREATE_MVBOX) { info!(context, 0, "Creating MVBOX-folder \"DeltaChat\"...",); - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { match session.create("DeltaChat") { Ok(_) => { mvbox_folder = Some("DeltaChat".into()); @@ -1616,7 +1641,7 @@ impl Imap { &self, context: &Context, ) -> Option>> { - if let Some(ref mut session) = self.session.lock().unwrap().0 { + if let Some(ref mut session) = &mut *self.session.lock().unwrap() { // TODO: use xlist when available match session.list(Some(""), Some("*")) { Ok(list) => { diff --git a/src/key.rs b/src/key.rs index 4df2823db..5d4dd2b12 100644 --- a/src/key.rs +++ b/src/key.rs @@ -89,6 +89,9 @@ impl Key { } pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option { + if 0 == bytes.len() { + return None; + } let res: Result = match key_type { KeyType::Public => SignedPublicKey::from_bytes(Cursor::new(bytes)).map(Into::into), KeyType::Private => SignedSecretKey::from_bytes(Cursor::new(bytes)).map(Into::into), diff --git a/src/log.rs b/src/log.rs index 0ea011482..6ab8c4702 100644 --- a/src/log.rs +++ b/src/log.rs @@ -3,11 +3,14 @@ macro_rules! info { ($ctx:expr, $data1:expr, $msg:expr) => { info!($ctx, $data1, $msg,) }; - ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{ - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t) + ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { + #[allow(unused_unsafe)] + unsafe { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t, + formatted_c as libc::uintptr_t); + libc::free(formatted_c as *mut libc::c_void); }}; } @@ -17,11 +20,14 @@ macro_rules! warn { warn!($ctx, $data1, $msg,) }; ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t) - }; + #[allow(unused_unsafe)] + unsafe { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t, + formatted_c as libc::uintptr_t); + libc::free(formatted_c as *mut libc::c_void) ; + }}; } #[macro_export] @@ -30,11 +36,14 @@ macro_rules! error { error!($ctx, $data1, $msg,) }; ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { + #[allow(unused_unsafe)] + unsafe { let formatted = format!($msg, $($args),*); let formatted_c = $crate::dc_tools::to_cstring(formatted); $ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t) - }; + formatted_c as libc::uintptr_t); + libc::free(formatted_c as *mut libc::c_void); + }}; } #[macro_export] @@ -43,9 +52,12 @@ macro_rules! log_event { log_event!($ctx, $data1, $msg,) }; ($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { - let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); - $ctx.call_cb($event, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t) - }; + #[allow(unused_unsafe)] + unsafe { + let formatted = format!($msg, $($args),*); + let formatted_c = $crate::dc_tools::to_cstring(formatted); + $ctx.call_cb($event, $data1 as libc::uintptr_t, + formatted_c as libc::uintptr_t); + libc::free(formatted_c as *mut libc::c_void); + }}; } diff --git a/src/peerstate.rs b/src/peerstate.rs index b5518486a..876a300b1 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -462,10 +462,12 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - use std::ffi::{CStr, CString}; + use std::ffi::CStr; use tempfile::{tempdir, TempDir}; use crate::context::*; + use crate::dc_tools::to_cstring; + use crate::x::free; #[test] fn test_peerstate_save_to_db() { @@ -520,16 +522,16 @@ mod tests { unsafe fn create_test_context() -> TestContext { let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); let dir = tempdir().unwrap(); - let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap(); + let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap()); assert_eq!( - dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()), + dc_open(&mut ctx, dbfile, std::ptr::null()), 1, "Failed to open {}", - CStr::from_ptr(dbfile.as_ptr() as *const libc::c_char) - .to_str() - .unwrap() + CStr::from_ptr(dbfile as *const _).to_str().unwrap() ); + free(dbfile as *mut _); + TestContext { ctx: ctx, dir: dir } } } diff --git a/src/smtp.rs b/src/smtp.rs index 9b902e428..ebe7a3fca 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -43,10 +43,10 @@ impl Smtp { } /// Connect using the provided login params - pub fn connect(&mut self, context: &Context, lp: &dc_loginparam_t) -> usize { + pub fn connect(&mut self, context: &Context, lp: &dc_loginparam_t) -> bool { if self.is_connected() { warn!(context, 0, "SMTP already connected."); - return 1; + return true; } if lp.send_server.is_empty() || lp.send_port == 0 { @@ -61,7 +61,7 @@ impl Smtp { if self.from.is_none() { // TODO: print error - return 0; + return false; } let domain = &lp.send_server; @@ -82,7 +82,7 @@ impl Smtp { let send_pw = &lp.send_pw; let access_token = dc_get_oauth2_access_token(context, addr, send_pw, 0); if access_token.is_none() { - return 0; + return false; } let user = &lp.send_user; @@ -116,11 +116,11 @@ impl Smtp { "SMTP-LOGIN as {} ok", lp.send_user, ); - 1 + true } Err(err) => { warn!(context, 0, "SMTP: failed to establish connection {:?}", err); - 0 + false } } } diff --git a/src/sql.rs b/src/sql.rs index 2dd8fb025..34ab2c001 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -1,7 +1,8 @@ use std::collections::HashSet; -use std::sync::RwLock; +use std::sync::{Arc, RwLock}; use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS}; +use thread_local_object::ThreadLocal; use crate::constants::*; use crate::context::Context; @@ -16,12 +17,14 @@ const DC_OPEN_READONLY: usize = 0x01; /// A wrapper around the underlying Sqlite3 object. pub struct Sql { pool: RwLock>>, + in_use: Arc>, } impl Sql { pub fn new() -> Sql { Sql { pool: RwLock::new(None), + in_use: Arc::new(ThreadLocal::new()), } } @@ -31,6 +34,7 @@ impl Sql { pub fn close(&self, context: &Context) { let _ = self.pool.write().unwrap().take(); + self.in_use.remove(); // drop closes the connection info!(context, 0, "Database closed."); @@ -53,6 +57,7 @@ impl Sql { P: IntoIterator, P::Item: rusqlite::ToSql, { + self.start_stmt(sql.to_string()); self.with_conn(|conn| conn.execute(sql, params).map_err(Into::into)) } @@ -60,22 +65,25 @@ impl Sql { where G: FnOnce(&Connection) -> Result, { - match &*self.pool.read().unwrap() { + let res = match &*self.pool.read().unwrap() { Some(pool) => { let conn = pool.get()?; g(&conn) } None => Err(Error::SqlNoConnection), - } + }; + self.in_use.remove(); + res } pub fn prepare(&self, sql: &str, g: G) -> Result where - G: FnOnce(Statement<'_>) -> Result, + G: FnOnce(Statement<'_>, &Connection) -> Result, { + self.start_stmt(sql.to_string()); self.with_conn(|conn| { let stmt = conn.prepare(sql)?; - let res = g(stmt)?; + let res = g(stmt, conn)?; Ok(res) }) } @@ -84,6 +92,7 @@ impl Sql { where G: FnOnce(Statement<'_>, Statement<'_>, &Connection) -> Result, { + self.start_stmt(format!("{} - {}", sql1, sql2)); self.with_conn(|conn| { let stmt1 = conn.prepare(sql1)?; let stmt2 = conn.prepare(sql2)?; @@ -109,8 +118,8 @@ impl Sql { F: FnMut(&rusqlite::Row) -> rusqlite::Result, G: FnMut(rusqlite::MappedRows) -> Result, { + self.start_stmt(sql.as_ref().to_string()); self.with_conn(|conn| { - eprintln!("query_map {}", sql.as_ref()); let mut stmt = conn.prepare(sql.as_ref())?; let res = stmt.query_map(params, f)?; g(res) @@ -124,6 +133,7 @@ impl Sql { P: IntoIterator, P::Item: rusqlite::ToSql, { + self.start_stmt(sql.to_string()); self.with_conn(|conn| { let mut stmt = conn.prepare(sql)?; let res = stmt.exists(params)?; @@ -137,11 +147,12 @@ impl Sql { P::Item: rusqlite::ToSql, F: FnOnce(&rusqlite::Row) -> rusqlite::Result, { + self.start_stmt(sql.as_ref().to_string()); self.with_conn(|conn| conn.query_row(sql.as_ref(), params, f).map_err(Into::into)) } pub fn table_exists(&self, name: impl AsRef) -> bool { - self.with_conn(|conn| Ok(table_exists(conn, name))) + self.with_conn(|conn| table_exists(conn, name)) .unwrap_or_default() } @@ -259,18 +270,27 @@ impl Sql { pub fn get_config_int64(&self, context: &Context, key: impl AsRef) -> Option { self.get_config(context, key).and_then(|r| r.parse().ok()) } + + fn start_stmt(&self, stmt: impl AsRef) { + if let Some(query) = self.in_use.get_cloned() { + let bt = backtrace::Backtrace::new(); + eprintln!("old query: {}", query); + eprintln!("Connection is already used from this thread: {:?}", bt); + panic!("Connection is already used from this thread"); + } + + self.in_use.set(stmt.as_ref().to_string()); + } } -fn table_exists(conn: &Connection, name: impl AsRef) -> bool { +fn table_exists(conn: &Connection, name: impl AsRef) -> Result { let mut exists = false; conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| { // will only be executed if the info was found exists = true; Ok(()) - }) - .expect("bad sqlite state"); - - exists + })?; + Ok(exists) } fn open( @@ -841,6 +861,18 @@ pub fn get_rowid( table: impl AsRef, field: impl AsRef, value: impl AsRef, +) -> u32 { + sql.start_stmt("get rowid".to_string()); + sql.with_conn(|conn| Ok(get_rowid_with_conn(context, conn, table, field, value))) + .unwrap_or_else(|_| 0) +} + +pub fn get_rowid_with_conn( + context: &Context, + conn: &Connection, + table: impl AsRef, + field: impl AsRef, + value: impl AsRef, ) -> u32 { // alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above. // the ORDER BY ensures, this function always returns the most recent id, @@ -852,7 +884,7 @@ pub fn get_rowid( value.as_ref() ); - match sql.query_row(&query, NO_PARAMS, |row| row.get::<_, u32>(0)) { + match conn.query_row(&query, NO_PARAMS, |row| row.get::<_, u32>(0)) { Ok(id) => id, Err(err) => { error!( @@ -863,7 +895,6 @@ pub fn get_rowid( } } } - pub fn get_rowid2( context: &Context, sql: &Sql, @@ -873,6 +904,7 @@ pub fn get_rowid2( field2: impl AsRef, value2: i32, ) -> u32 { + sql.start_stmt("get rowid2".to_string()); sql.with_conn(|conn| { Ok(get_rowid2_with_conn( context, conn, table, field, value, field2, value2, @@ -972,31 +1004,36 @@ pub fn housekeeping(context: &Context) { } let entry = entry.unwrap(); let name_f = entry.file_name(); - let name_c = to_cstring(name_f.to_string_lossy()); + let name_c = unsafe { to_cstring(name_f.to_string_lossy()) }; - if unsafe { - is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c.as_ptr()) - } || unsafe { - is_file_in_use( - &mut files_in_use, - b".increation\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - } || unsafe { - is_file_in_use( - &mut files_in_use, - b".waveform\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - } || unsafe { - is_file_in_use( - &mut files_in_use, - b"-preview.jpg\x00" as *const u8 as *const libc::c_char, - name_c.as_ptr(), - ) - } { + if unsafe { is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c) } + || unsafe { + is_file_in_use( + &mut files_in_use, + b".increation\x00" as *const u8 as *const libc::c_char, + name_c, + ) + } + || unsafe { + is_file_in_use( + &mut files_in_use, + b".waveform\x00" as *const u8 as *const libc::c_char, + name_c, + ) + } + || unsafe { + is_file_in_use( + &mut files_in_use, + b"-preview.jpg\x00" as *const u8 as *const libc::c_char, + name_c, + ) + } + { + unsafe { free(name_c as *mut _) }; continue; } + unsafe { free(name_c as *mut _) }; + unreferenced_count += 1; match std::fs::metadata(entry.path()) { @@ -1028,8 +1065,11 @@ pub fn housekeeping(context: &Context) { unreferenced_count, entry.file_name() ); - let path = to_cstring(entry.path().to_str().unwrap()); - unsafe { dc_delete_file(context, path.as_ptr()) }; + unsafe { + let path = to_cstring(entry.path().to_str().unwrap()); + dc_delete_file(context, path); + free(path as *mut _); + } } } Err(err) => { @@ -1087,14 +1127,16 @@ fn maybe_add_from_param( context .sql .query_row(query, NO_PARAMS, |row| { - let v = to_cstring(row.get::<_, String>(0)?); unsafe { - dc_param_set_packed(param, v.as_ptr() as *const libc::c_char); - let file = dc_param_get(param, param_id, 0 as *const libc::c_char); + let v = to_cstring(row.get::<_, String>(0)?); + dc_param_set_packed(param, v as *const _); + let file = dc_param_get(param, param_id, 0 as *const _); if !file.is_null() { maybe_add_file(files_in_use, as_str(file)); free(file as *mut libc::c_void); } + + free(v as *mut _); } Ok(()) }) @@ -1128,7 +1170,6 @@ mod test { maybe_add_file(&mut files, "$BLOBDIR/world.txt"); maybe_add_file(&mut files, "world2.txt"); - println!("{:?}", files); assert!(unsafe { is_file_in_use( &mut files, diff --git a/tests/stress.rs b/tests/stress.rs index 88f6a26b9..ca8240424 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -1,7 +1,6 @@ //! Stress some functions for testing; if used as a lib, this file is obsolete. use std::collections::HashSet; -use std::ffi::CString; use mmime::mailimf_types::*; use tempfile::{tempdir, TempDir}; @@ -175,7 +174,7 @@ unsafe fn stress_functions(context: &Context) { "content" ); - free(buf); + free(buf as *mut _); assert_ne!( 0, dc_delete_file( @@ -691,7 +690,7 @@ fn test_encryption_decryption() { assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----")); let ctext_signed_bytes = ctext.len(); - let ctext_signed = CString::new(ctext).unwrap(); + let ctext_signed = to_cstring(ctext); let ctext = dc_pgp_pk_encrypt( original_text as *const libc::c_void, @@ -704,7 +703,7 @@ fn test_encryption_decryption() { assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----")); let ctext_unsigned_bytes = ctext.len(); - let ctext_unsigned = CString::new(ctext).unwrap(); + let ctext_unsigned = to_cstring(ctext); let mut keyring = Keyring::default(); keyring.add_owned(private_key); @@ -718,7 +717,7 @@ fn test_encryption_decryption() { let mut valid_signatures: HashSet = Default::default(); let plain = dc_pgp_pk_decrypt( - ctext_signed.as_ptr() as *const _, + ctext_signed as *const _, ctext_signed_bytes, &keyring, &public_keyring, @@ -733,7 +732,7 @@ fn test_encryption_decryption() { let empty_keyring = Keyring::default(); let plain = dc_pgp_pk_decrypt( - ctext_signed.as_ptr() as *const _, + ctext_signed as *const _, ctext_signed_bytes, &keyring, &empty_keyring, @@ -746,7 +745,7 @@ fn test_encryption_decryption() { valid_signatures.clear(); let plain = dc_pgp_pk_decrypt( - ctext_signed.as_ptr() as *const _, + ctext_signed as *const _, ctext_signed_bytes, &keyring, &public_keyring2, @@ -761,7 +760,7 @@ fn test_encryption_decryption() { public_keyring2.add_ref(&public_key); let plain = dc_pgp_pk_decrypt( - ctext_signed.as_ptr() as *const _, + ctext_signed as *const _, ctext_signed_bytes, &keyring, &public_keyring2, @@ -774,13 +773,15 @@ fn test_encryption_decryption() { valid_signatures.clear(); let plain = dc_pgp_pk_decrypt( - ctext_unsigned.as_ptr() as *const _, + ctext_unsigned as *const _, ctext_unsigned_bytes, &keyring, &public_keyring, Some(&mut valid_signatures), ) .unwrap(); + + free(ctext_unsigned as *mut _); assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),); valid_signatures.clear(); @@ -791,13 +792,15 @@ fn test_encryption_decryption() { public_keyring.add_ref(&public_key); let plain = dc_pgp_pk_decrypt( - ctext_signed.as_ptr() as *const _, + ctext_signed as *const _, ctext_signed_bytes, &keyring, &public_keyring, None, ) .unwrap(); + + free(ctext_signed as *mut _); assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),); } } @@ -820,14 +823,14 @@ struct TestContext { unsafe fn create_test_context() -> TestContext { let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); let dir = tempdir().unwrap(); - let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap(); + let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap()); assert_eq!( - dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()), + dc_open(&mut ctx, dbfile, std::ptr::null()), 1, "Failed to open {}", - as_str(dbfile.as_ptr() as *const libc::c_char) + as_str(dbfile as *const libc::c_char) ); - + free(dbfile as *mut _); TestContext { ctx: ctx, dir: dir } } @@ -952,24 +955,29 @@ fn test_stress_tests() { fn test_get_contacts() { unsafe { let context = create_test_context(); - let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("some2").as_ptr()); + let name = to_cstring("some2"); + let contacts = dc_get_contacts(&context.ctx, 0, name); assert_eq!(dc_array_get_cnt(contacts), 0); dc_array_unref(contacts); + free(name as *mut _); - let id = dc_create_contact( - &context.ctx, - to_cstring("bob").as_ptr(), - to_cstring("bob@mail.de").as_ptr(), - ); + let name = to_cstring("bob"); + let email = to_cstring("bob@mail.de"); + let id = dc_create_contact(&context.ctx, name, email); assert_ne!(id, 0); - let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("bob").as_ptr()); + let contacts = dc_get_contacts(&context.ctx, 0, name); assert_eq!(dc_array_get_cnt(contacts), 1); dc_array_unref(contacts); - let contacts = dc_get_contacts(&context.ctx, 0, to_cstring("alice").as_ptr()); + let name2 = to_cstring("alice"); + let contacts = dc_get_contacts(&context.ctx, 0, name2); assert_eq!(dc_array_get_cnt(contacts), 0); dc_array_unref(contacts); + + free(name as *mut _); + free(name2 as *mut _); + free(email as *mut _); } } @@ -977,11 +985,12 @@ fn test_get_contacts() { fn test_chat() { unsafe { let context = create_test_context(); - let contact1 = dc_create_contact( - &context.ctx, - to_cstring("bob").as_ptr(), - to_cstring("bob@mail.de").as_ptr(), - ); + let name = to_cstring("bob"); + let email = to_cstring("bob@mail.de"); + + let contact1 = dc_create_contact(&context.ctx, name, email); + free(name as *mut _); + free(email as *mut _); assert_ne!(contact1, 0); let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1); @@ -998,6 +1007,21 @@ fn test_chat() { } } +#[test] +fn test_wrong_db() { + unsafe { + let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); + let dir = tempdir().unwrap(); + let dbfile = dir.path().join("db.sqlite"); + std::fs::write(&dbfile, b"123").unwrap(); + + let dbfile_c = to_cstring(dbfile.to_str().unwrap()); + let res = dc_open(&mut ctx, dbfile_c, std::ptr::null()); + free(dbfile_c as *mut _); + assert_eq!(res, 0); + } +} + #[test] fn test_arr_to_string() { let arr2: [uint32_t; 4] = [