Compare commits

..

1500 Commits

Author SHA1 Message Date
Alexander Krotov
ed092fe1ce test_encrypt_decrypt_fuzz_alice: load secret key directly from file 2020-02-29 17:46:55 +03:00
Alexander Krotov
56f589db4a test_encrypt_decrypt_fuzz_alice: load alice public key directly 2020-02-29 17:19:51 +03:00
Alexander Krotov
49b39b42bf Add test_encrypt_decrypt_fuzz_alice
Simplified test using pregenerated key without signature.
2020-02-28 21:28:24 +03:00
Alexander Krotov
7e4951e691 Replace test Alice key with ed25519 key 2020-02-28 05:17:48 +03:00
B. Petersen
f50874acf1 create a basic failing test for the ecc encryption bug 2020-02-27 19:56:50 +01:00
B. Petersen
4f8c5965ac get fix wrt aol from provider-database
aol is not proken, it just needs some prepararions :)

command for updating:
./src/provider/update.py ../provider-db/_providers/ > src/provider/data.rs
2020-02-24 23:39:40 +03:00
holger krekel
900a17fc00 another fix, thanks @adbenitez 2020-02-22 17:11:37 +01:00
holger krekel
78f36aaa0d another bug fix 2020-02-22 17:11:37 +01:00
holger krekel
e064e02794 fix eventlogger 2020-02-22 17:11:37 +01:00
holger krekel
e22e5045f1 add missing file, some streamlining 2020-02-22 17:11:37 +01:00
holger krekel
087f35482b factor out imex tracking 2020-02-22 17:11:37 +01:00
holger krekel
23ff5fea28 move towards pluggy 2020-02-22 17:11:37 +01:00
holger krekel
34347ccaf5 strike get_infostring 2020-02-22 17:11:37 +01:00
holger krekel
e704eb6cef move eventlogging to own module, start distinguishing ll events 2020-02-22 17:11:37 +01:00
holger krekel
bf63423fec strike footer and refine index page 2020-02-22 17:11:37 +01:00
holger krekel
f6d71ed8ef strike one Account parameter, always do eventlogging 2020-02-22 17:11:37 +01:00
Alexander Krotov
3c342339a1 imap: use parse_message_id from mimeparser 2020-02-22 16:41:07 +01:00
Alexander Krotov
33463856c5 Use mailparse::msgidparse to parse Message-IDs 2020-02-22 16:41:07 +01:00
holger krekel
a18f4c9b1b prepare py-0.800.0 2020-02-21 14:09:05 +01:00
bjoern
783c7ee4c5 Merge pull request #1303 from deltachat/prep-core25
prepare 1.25.0
2020-02-21 10:41:07 +01:00
B. Petersen
a0b2a692d0 bump version to 1.25.0 2020-02-20 23:49:49 +01:00
B. Petersen
a59d368101 changelog 2020-02-20 23:47:43 +01:00
B. Petersen
5c36fb29ed update python version examples 2020-02-20 23:47:23 +01:00
Alexander Krotov
508b8ef2e2 Improve documentation, mostly by hiding behind pub(crate) 2020-02-20 23:37:13 +03:00
holger krekel
e94c62e5b3 Update src/contact.rs
fix typo
2020-02-20 02:10:56 +01:00
B. Petersen
b65a6c2829 target comment of @hpk42 2020-02-20 02:10:56 +01:00
B. Petersen
c4a20d0798 fix updating names from incoming mails
- if a manual name was never given, always update names from incoming mails
- if a manual name is cleared, fall back to names from incoming mails
2020-02-20 02:10:56 +01:00
B. Petersen
9cb7ea524e add failing test where a contact-name is not updated as expected 2020-02-20 02:10:56 +01:00
holger krekel
0ac0eeda34 better naming, less code 2020-02-20 01:30:21 +01:00
holger krekel
4d066b4fd2 refine processing of errors and result handling 2020-02-20 01:30:21 +01:00
Alexander Krotov
840e321dd9 Process Permanent and Transient SMTP errors 2020-02-20 01:30:21 +01:00
holger krekel
4b6963122b Update src/smtp/mod.rs
Co-Authored-By: Alexander Krotov <ilabdsf@gmail.com>
2020-02-20 01:30:21 +01:00
holger krekel
d5d662bc41 fix ordering error 2020-02-20 01:30:21 +01:00
holger krekel
0b0ed56901 directly attempt to re-connect if the smtp connection is maybe stale
also refactor performing the job-action into own function
2020-02-20 01:30:21 +01:00
B. Petersen
3a25d6b275 target comment of @link2xt 2020-02-18 17:51:28 +01:00
B. Petersen
66e2f51233 adapt spec 2020-02-18 17:51:28 +01:00
B. Petersen
8fdb048b6a alter the memberlist more carefully
up to now, Chat-Group-Member-Added and -Removed commands
result in a complete recreation of the memberlist
by collecting all addresses from the From: and To: headers.

this easily results in missed and accidentally removed members,
esp. when several people at the same time scan a qr code to join a group.

this commit changes the behavior of adding members by
not removing members on the Chat-Group-Member-Added command.
instead the existing memberlist is conjuncted
with the memberlist seen in the message.

only adding the member from the Chat-Group-Member-Added
seems not to be sufficient - imaging a group of Alice and Bob:
- Alice adds Claire
- Bob adds Dave _before_ seeing that Alice added Claire
- Dave would never get the information that Claire is in the group

wrt Chat-Group-Member-Removed: this command
does no longer recreate the memberlist but just remove _exactly_ the member
mentioned in the header. there are situations, where a just removed member
will be readded by out-of-order-messages, however, compared to missed
members, this is seems to be acceptable - also as this is more visible
and easier to fix (just remove the member again).
might be that, in practise, this is not a big issue. while adding members
is typically done in masses on bootstraping a group,
this is typically not true for removing members.
2020-02-18 17:51:28 +01:00
B. Petersen
fa3d98a492 add a function to delete records from the chats_contacts table 2020-02-18 17:51:28 +01:00
Alexander Krotov
d9dda44409 Add integration test for RSA and Ed25519 keys
Test that two chat clients using different key types can communicate
using Autocrypt.
2020-02-18 17:51:06 +01:00
Alexander Krotov
7368c01a8f Add key_gen_type config option 2020-02-18 17:51:06 +01:00
Alexander Krotov
21ac5be7ca Change generated key type to Ed25519
rPGP generates EdDSA and and ECDH keys using Ed25519 only, so there is
no need to specify it explicitly anywhere.
2020-02-18 17:51:06 +01:00
B. Petersen
e14a113277 add testrun to provider-db 2020-02-18 17:50:38 +01:00
Alexander Krotov
66d3440675 Update const.py 2020-02-18 11:58:11 +03:00
Alexander Krotov
6b6be3b03d Fix some "`" code markup 2020-02-18 05:06:11 +03:00
bjoern
cda8158bec Merge pull request #1286 from deltachat/spec-location
add location handling to spec
2020-02-17 19:03:30 +01:00
B. Petersen
332e0dc4a8 update providers from provider-db
command:
./src/provider/update.py ../provider-db/_providers/ > src/provider/data.rs
2020-02-17 18:18:14 +01:00
B. Petersen
f7b4c6837b add location handling to spec 2020-02-17 17:58:25 +01:00
Asiel Díaz Benítez
531928bf0b Update proxy.py 2020-02-17 14:43:43 +01:00
Asiel Díaz Benítez
490c8e055b Create proxy.py 2020-02-17 14:43:43 +01:00
Alexander Krotov
bcbf192bbc Remove deprecated Chat-Group-Image header 2020-02-17 13:41:50 +01:00
Alexander Krotov
78d855c5ca Include prefer-encrypt attribute in Autocrypt-Gossip headers 2020-02-17 13:39:32 +01:00
Alexander Krotov
1fa9aa88a8 Search for Flag::Deleted and Flag::Seen with == instead of match 2020-02-17 10:50:34 +01:00
Alexander Krotov
08c77c2668 fetch_single_msg: use if let Some(...) instead of is_empty() 2020-02-17 10:50:34 +01:00
Alexander Krotov
793ebe1b0f imap: move IdleHandle from session.rs to idle.rs 2020-02-17 10:50:34 +01:00
Alexander Krotov
4c42acc7e1 Factor src/imap/session.rs out of src/imap/client.rs 2020-02-17 10:50:34 +01:00
Alexander Krotov
4eb9660bfa Move src/imap_client.rs into src/imap/client.rs 2020-02-17 10:50:34 +01:00
Alexander Krotov
8ed08f701d Do not use grpid to find last inserted row in chats table
Instead, use last_insert_rowid() function to find the row.

There is no race condition in using last_insert_rowid(), because
last_insert_rowid() returns row id last inserted in this connection. As
we hold the connection during the whole transaction, it is impossible
that some other thread will execute INSERT statement in parallel.

This commit is part of the effort to get rid of sql::get_rowid hack and
use transactions more for related SQL statements.
2020-02-17 01:35:56 +03:00
Alexander Krotov
784964efad Make sql::with_conn and sql::start_stmt public
Existing public methods that use these functions, like sql::execute, are
only suitable for executing a single statement.

Sometimes it is useful to execute multiple statements within one
connection, for example to begin a transaction, execute mutliple SELECT
and INSERT queries and commit or rollback the whole transaction.
2020-02-17 01:35:56 +03:00
Alexander Krotov
adb96e72b9 Pass mutable Connection reference to with_conn callback
This makes it possible to call .transaction() method on connection.
2020-02-17 01:35:56 +03:00
Alexander Krotov
439c6f7296 Fix a typo 2020-02-17 01:35:56 +03:00
Floris Bruynooghe
e2f1ea1444 Add .strdup() method to Option<AsRef<str>>
We already have a .strdup() method on AsRef<str>, this adds this
method also to an option of this.  In case the option is None a NULL
pointer is returned.

This is done by using a new trait, as the type system otherwise
considers such an implementation conflicting with the existing one.
2020-02-16 23:15:53 +01:00
Alexander Krotov
2977ceb459 Turn deltachat::configure functions into Context methods
Now configure module is no longer public. Users should call
Context.configure() and Context.is_configured() methods.

Configure module is completely hidden from documentation unless
--document-private-items option is specified.
2020-02-16 21:17:03 +01:00
Alexander Krotov
e00d4e0ed8 Remove AvatarAction::None
Where needed, Option<AvatarAction> can be used to extend it.
2020-02-16 15:03:37 +03:00
Alexander Krotov
772127d9d8 Remove outdated comment
delete_msg no longer returns an integer
2020-02-16 03:43:25 +03:00
bjoern
6ba45c88ec Merge pull request #1281 from deltachat/fix-pin-test
fix pinning test by forcing a reliable order
2020-02-16 01:32:37 +01:00
Alexander Krotov
5a4040cf0b cargo fmt 2020-02-16 03:22:30 +03:00
Alexander Krotov
b54f580e66 mimeparser: allow "inline" attachments
RFC 2183 specifically allows filenames to be specified for MIME parts
with "inline" Content-Disposition.

Previously we treated such attachments as an error, causing the whole
message parsing to fail. Now it is only an error if Content-Disposition
cannot be parsed.

MIME parts with filename are now considered to be attachments
regardless of their Content-Disposition type. However, "attachment"
Content-Disposition is still processed differently: we generate a
filename for it even if it is not specified.
2020-02-16 03:22:30 +03:00
B. Petersen
a9ac69fe9c fix pinning test by forcing a reliable order 2020-02-16 00:55:13 +01:00
B. Petersen
5c52b5e404 fix order of shared chats, move pinned up
we forgot to respect the new pinned-state for the list of shared chats
as it is shown eg. in the profile of a contact.
2020-02-15 21:15:01 +01:00
Alexander Krotov
b80360b7da Pass avatar file path to build_selfavatar_file as &str 2020-02-15 22:20:05 +03:00
Alexander Krotov
2753883687 Rename add_selfavatar into attach_selfavatar to match field name 2020-02-15 21:22:25 +03:00
bjoern
ced73ffb14 Merge pull request #1248 from deltachat/pinned_chats
feat: pin chats
2020-02-15 00:15:33 +01:00
Alexander Krotov
672fe2dfd7 Parse KML without converting to UTF-8 and back to bytes 2020-02-14 22:34:41 +01:00
Alexander Krotov
04bb6997a2 Remove unused imap::idle::Error::ImapError variant 2020-02-14 22:33:36 +01:00
B. Petersen
c8a8dbbbae adapt python bindings 2020-02-14 13:43:49 +01:00
B. Petersen
1f9520dc78 target comments from @flub 2020-02-14 13:43:48 +01:00
B. Petersen
84f8627890 fix repl tool 2020-02-14 13:43:48 +01:00
B. Petersen
a177df32b7 omit values in ChatVisibility enum as suggested by @dignifiedquire and @flub 2020-02-14 13:43:48 +01:00
B. Petersen
f25d5dd123 do not unpin chats on sending/receiving messages 2020-02-14 13:43:48 +01:00
B. Petersen
4cfa9e6165 send event as before, uis depend on that 2020-02-14 13:43:48 +01:00
B. Petersen
0303ea7f57 rename to ChatVisibility, simplify ffi 2020-02-14 13:43:48 +01:00
B. Petersen
2813e01e61 remove unneeded sql-roundtrip on getting archived state 2020-02-14 11:28:55 +01:00
B. Petersen
e3420da60f reword ffi from 'archived' to 'visibility' 2020-02-14 11:28:55 +01:00
B. Petersen
60493d30f6 target comment from @dignifiedquire, use ArchiveState inside core 2020-02-14 11:28:55 +01:00
Simon Laux
6efe8e7d7c change ChatInfo.archived to tri-state
and add to the changelog
2020-02-14 11:28:55 +01:00
Simon Laux
2e8409f146 address some of flubs comments 2020-02-14 11:28:55 +01:00
Simon Laux
ac4b2b9dfe python bindings for archive and py tests 2020-02-14 11:28:55 +01:00
Simon Laux
23b6178e78 add rust test for pin chat 2020-02-14 11:28:55 +01:00
Simon Laux
5e5d45fb0a better fallbacks 2020-02-14 11:28:54 +01:00
Simon Laux
1765b8f2cf show pinned chats again and order them to the top 2020-02-14 11:28:54 +01:00
Simon Laux
5678562ce2 represent archivestate as enum
before it was a boolean, even though it is a 3 state
2020-02-14 11:28:54 +01:00
Floris Bruynooghe
7274197da0 Remove comment about method being deprecated
I had a look for two mintues at converting things to .try_inner() and
I don't think this is currently worth the effort or would increase
readability a lot.  So let's leave this for now.
2020-02-14 01:02:28 +01:00
Floris Bruynooghe
c79fcb380b Clean up result traits
There is no need to have both a .log_warn() and .log_err(), even their
names are confusing by now.  Let's just have the most liberal one and
reduce one more thing we need to know about.

Also, these traits don't need to be pub.
2020-02-14 01:02:28 +01:00
Floris Bruynooghe
6a98eade07 Remove the error method from the ffi
No ffi method should be reporting errors to the user.  That's
exclusively the domain of core.  All errors in the ffi are programing
errors.
2020-02-14 01:02:28 +01:00
Alexander Krotov
9008a65c14 Remove DC_IMAP_SEEN constant
Replace "flags" integer with a "seen" boolean.
2020-02-13 13:55:06 +01:00
Alexander Krotov
4e07e4c7f3 Fix a typo (bbc-self instead of bcc-self) 2020-02-12 23:15:04 +03:00
dignifiedquire
e440d8503a fixup 2020-02-12 19:12:22 +01:00
dignifiedquire
e9bacff830 fixup 2020-02-12 19:12:22 +01:00
dignifiedquire
9cc99ffcd6 add more ser and de impls 2020-02-12 19:12:22 +01:00
Alexander Krotov
beb91271de Unlock session before calling add_flag_finalized
add_flag_finalized tries to lock session again and IMAP thread deadlocks
if session is not unlocked.
2020-02-12 11:38:18 +00:00
Alexander Krotov
7e9585ebc5 cargo fmt 2020-02-12 11:38:18 +00:00
Alexander Krotov
0c4b3f71e5 Check for MOVE capability before using MOVE command 2020-02-12 11:38:18 +00:00
holger krekel
5c17ec5f01 use new URL format and service provided by mailadm 2020-02-11 22:48:24 +01:00
B. Petersen
8b4edc46a7 implement dc_set_config_from_qr() 2020-02-11 21:04:18 +01:00
B. Petersen
2b7a0a4585 prototype dc_set_config_from_qr() 2020-02-11 21:04:18 +01:00
B. Petersen
1882176489 let dc_check_qr() accept DCACCOUNT-schemes 2020-02-11 21:04:18 +01:00
holger krekel
875e89e71a better event information for moved messages 2020-02-11 20:38:14 +01:00
Alexander Krotov
52520635ea Fix a typo ("requests") 2020-02-11 22:32:35 +03:00
Friedel Ziegelmayer
aa50a9ba83 Merge pull request #1258 from deltachat/cargo-check-ci
GitHub actions: check all packages, examples, tests and features
2020-02-11 11:50:26 +01:00
Alexander Krotov
489e5111ac GitHub actions: check all packages, examples, tests and features 2020-02-11 02:09:47 +03:00
Alexander Krotov
2d4c20af35 Revert "Make dc_receive_imf non-public"
This reverts commit cabb58a9aa.

dc_receive_imf() is used in the "repl" example.
This was not caught by CI because it does not build examples.
2020-02-11 02:03:36 +03:00
Alexander Krotov
66fdb447f7 Rename get_headerdef into get_header_value 2020-02-11 01:59:41 +03:00
Alexander Krotov
c801775a39 Implement get_headerdef method for MailHeader slices 2020-02-11 01:59:41 +03:00
Alexander Krotov
f5bb57d6a6 Fix a typo ("Messag") 2020-02-11 00:07:37 +03:00
Alexander Krotov
1071ab05db Move created_db_entries into cleanup closure instead of borrowing 2020-02-10 23:31:46 +03:00
Simon Laux
8461cf6443 Merge pull request #1143 from deltachat/feat_mute_chat
feat: mute chat
2020-02-10 01:39:50 +01:00
Alexander Krotov
bb10501f56 Improve test_one_account_send_bcc_setting
Clone the first account and check that second device actually sees the
message copied via BCC-to-self.
2020-02-10 01:17:28 +01:00
Alexander Krotov
c4d5f657da python tests: remove peek_online_config()
Using peek_online_config() results in a message being sent to some
other account, used by previous test. If tests are run in parallel,
for example with pytest-xdist, we could be sending a message to a still
online DC client.
2020-02-10 01:17:28 +01:00
Alexander Krotov
cabb58a9aa Make dc_receive_imf non-public 2020-02-09 23:30:06 +00:00
Alexander Krotov
e64ce5bb4f Reduce is_msgrmsg_rfc724_mid_in_list scope to pub(crate) 2020-02-09 23:28:29 +00:00
Alexander Krotov
bc750d61d2 Remove outdated comment 2020-02-09 23:28:29 +00:00
Alexander Krotov
3150901b6e Improve logs for prefetch checks 2020-02-09 23:28:29 +00:00
Alexander Krotov
4f6745b742 Check if contact is blocked or accepted before downloading it 2020-02-09 23:28:29 +00:00
Alexander Krotov
bcdc323a97 Factor from_field_to_contact_id out of dc_receive_imf 2020-02-09 23:28:29 +00:00
Alexander Krotov
4fd4cc709d Prefetch FROM field 2020-02-09 23:28:29 +00:00
Alexander Krotov
0fac463144 Prefetch Message-ID header instead of envelope
Envelope has the same Message-ID, but using other fields from the
envelope, such as From field, is error-prone. They may contain values
different from the message body. As we don't parse the envelope later on,
it is better not to fetch it during prefetch too.
2020-02-09 23:28:29 +00:00
Alexander Krotov
d809dfac65 Do not download messages that are not displayed 2020-02-09 23:28:29 +00:00
Alexander Krotov
983fd70260 Update imap-proto to 0.10.2
It adds support for parsing of BODY[HEADER.FIELDS (...)] fetches.
2020-02-09 23:28:29 +00:00
Alexander Krotov
ea11a5274e Remove unwrap() in prefetch_get_message_id 2020-02-09 23:28:29 +00:00
Alexander Krotov
42356c2a67 Fix a typo in prefetch_get_message_id 2020-02-09 23:28:29 +00:00
Alexander Krotov
5a84ab2011 Fix a typo (s/ideling/idling/) 2020-02-09 23:28:29 +00:00
Alexander Krotov
b4573e341f Return &'static str from HeaderDef.get_headername() 2020-02-09 19:17:27 +01:00
Alexander Krotov
df252c4704 Do not check for XLIST capability
This capability is not used by Delta Chat. Moreover, XLIST is deprecated
in favor of https://tools.ietf.org/html/rfc6154, which should be
supported instead. Delta Chat already looks for \Sent attribute in
get_folder_meaning().
2020-02-09 19:15:20 +01:00
Alexander Krotov
d1912f873b Resultify fetch_single_msg 2020-02-09 19:14:36 +01:00
Floris Bruynooghe
e525c42c7d Unmute should also raise exceptions
And tests should not care about return values.
2020-02-09 19:12:21 +01:00
Floris Bruynooghe
0242322d24 Tweak error halding a little
- Python should raise exceptions on error.  Not return False.
- Negative durations are nonense.
- ChatID validity is already checked by the Rust API, let's not duplicate.
2020-02-09 18:01:42 +01:00
Simon Laux
ded6fafc8a Merge pull request #1250 from deltachat/flub-feat-mute-chat
Use SystemTime instead of i64
2020-02-09 15:46:19 +01:00
Floris Bruynooghe
4aebd678c3 Use SystemTime instead of i64
This clarifies some behaviour with negative times and propagates
errors a bit better around places.
2020-02-09 13:35:37 +01:00
Simon Laux
afc9ed2274 fix python formatting 2020-02-09 12:36:37 +01:00
Simon Laux
d73d021e3c cargo fmt 2020-02-09 12:25:47 +01:00
bjoern
c8fe81e21d Merge pull request #1243 from Ampli-fier/master
Minor changes in comments
2020-02-09 12:01:31 +01:00
Simon Laux
621f1df913 rename MutedUntilTimestamp to Until 2020-02-09 12:01:09 +01:00
Simon Laux
5f4274b449 fix naming 2020-02-09 10:42:32 +01:00
Simon Laux
4acb37156f fix building of ffi 2020-02-09 10:39:21 +01:00
Alexander Krotov
1ca23a7479 Update link to C core issue 2020-02-09 03:28:59 +03:00
Alexander Krotov
61daf7218d Preconfigure the key in clone_online_account()
This avoids generating the key in test_ac_setup_message
2020-02-08 22:39:34 +00:00
Floris Bruynooghe
dc6671fc4e Add an integration tests which generates a key
Configuring an online account generates a key, we would like this
code-path tested too.  So add some functionality to the AccountManager
to not use the pre-generated keys.

Because this slows down interactively running the tests by hand add an
ignored marker which only runs if --ignored is used.  This name was
chosen because this matches the naming used by rust/cargo #[ignored].
The difference however is that --ignored on cargo *only* runs ignored
tests while here it runs *all* tests.

To ensure the ignored/slow tests are run on CI we add it as an
argument to the tox configuration, which is used by the CI to run the
tests.
2020-02-08 22:39:34 +00:00
Alexander Krotov
f34237ebc8 Run python tests on CI in debug mode
This should speed up compilation and enable additional integer overflow checks.
2020-02-08 16:58:55 +01:00
Alexander Krotov
aadeb3b87e job: do not render messages without recipients 2020-02-08 16:58:55 +01:00
Simon Laux
1fb75c1af3 rename dc_chat_get_mute_duration
to dc_chat_get_remaining_mute_duration
2020-02-08 13:29:24 +01:00
Simon Laux
e04d28c885 use from- and to-sql traits 2020-02-08 13:29:24 +01:00
Simon Laux
6d80b3675a dtransform mute chat to use relative durations
instead of absolute timestamps
2020-02-08 13:26:48 +01:00
Simon Laux
07d698f8dc add python tests 2020-02-08 13:26:21 +01:00
Simon Laux
ef158504e7 remove success variable
(replace with else statement)
2020-02-08 13:26:21 +01:00
Simon Laux
63be1ae5a9 change muted forever to -1 2020-02-08 13:25:22 +01:00
Simon Laux
b9ba1a4f69 implement ffi part 2020-02-08 13:25:22 +01:00
Simon Laux
e006d9b033 rustfmt 2020-02-08 13:25:22 +01:00
Simon Laux
1538684c6c simplify test code 2020-02-08 13:23:55 +01:00
Simon Laux
b37e83caab add possibility to mute chat in core 2020-02-08 13:23:55 +01:00
Simon Laux
d11d3ab08b fix typo 2020-02-08 13:11:22 +01:00
Floris Bruynooghe
1144a536a5 Pre-generate more keys for use in integration tests 2020-02-08 08:28:41 +00:00
Floris Bruynooghe
515c753d11 Use pre-generated keys for python integration tests
This changes the AccountMaker to use pre-generated keys when
available, speeding up test runs.

As a side-effect we no longer need to compile the integration tests in
release mode with debug symbols.  Losing debug symbols (-g) means
cargo no longer wants to recompile everything all the time too.

Tested locally and seems to works.
2020-02-08 08:28:41 +00:00
Alexander Krotov
0864e640ed Simplify Peerstate.peek_key() 2020-02-08 00:02:42 +01:00
Ampli-fier
b876e49393 Minor changes in comments 2020-02-07 20:01:17 +01:00
Floris Bruynooghe
fc0292bf8a Rename save_self_keypair
For the ffi rename to dc_preconfigure_keypair.  For the internal API
to store_self_keypair.
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
a903805cd9 Rename MessageChain to MessageWithCause 2020-02-06 22:00:29 +01:00
Floris Bruynooghe
abab34573e Remove unused method
Forgot to do this earlier.
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
fa1b94af60 Simplify returning None from a Result 2020-02-06 22:00:29 +01:00
Floris Bruynooghe
81ff5d1224 Let save_self_keypair() also remove a previously used key
The imex code was deleting the key if it was already used.  Turns out
none of the callers would not want this behaviour, so let's just
handle this case generally.
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
5c3a8819a4 Avoid pointless conversions, just return the result 2020-02-06 22:00:29 +01:00
Floris Bruynooghe
4d0a08d858 DcKey::to_base64 can not fail
As this can't fail, we don't have to expose Result on this and can
keep the API simpler.
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
c41bdaa2b7 Avoid for-else loop
And here I thought this pythonic
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
4bf2fc18e5 Write with_inner() in function of try_inner()
I guess this is a little neater.  Note that this does change all the
existing error logging to warning logging.  I believe this is correct
as these error log messages are really programming errors rather than
errors which need to be show to the user.

Also chose to make entire with_inner unsafe, there is little to be
gained from hiding the unsafe as this is only used from unsafe
functions in the first place.
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
145fd8657f Update deltachat-ffi/src/lib.rs
Co-Authored-By: Alexander Krotov <ilabdsf@gmail.com>
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
01b55d1d29 Update deltachat-ffi/src/lib.rs
Co-Authored-By: Alexander Krotov <ilabdsf@gmail.com>
2020-02-06 22:00:29 +01:00
Floris Bruynooghe
98b3151c5f Refactor keypair handling and expose storing keypairs on ffi
The user-visible change here is that it allows the FFI API to save
keys in the database for a context.  This is primarily intended for
testing purposes as it allows you to get a key without having to
generate it.

Internally the most important change is to start using the
SignedPublicKey and SignedPrivateKey types from rpgp instead of
wrapping them into a single Key object.  This allows APIs to be
specific about which they want instead of having to do runtime checks
like .is_public() or so.  This means some of the functionality of the
Key impl now needs to be a trait.

A thid API change is to introduce the KeyPair struct, which binds
together the email address, public and private key for a keypair.

All these changes result in a bunch of cleanups, though more more
should be done to completely replace the Key type with the
SignedPublicKye/SignedPrivateKey + traits.  But this change is large
enough already.

Testing-wise this adds two new keys which can be loaded from disk and
and avoids a few more key-generating tests.  The encrypt/decrypt tests
are moved from the stress tests into the pgp tests and split up.
2020-02-06 22:00:29 +01:00
Alexander Krotov
c7eca8deb3 chat: resultify parent_is_encrypted and don't ignore the error
This should prevent accidental sending of unencrypted messages when
parent_is_encrypted returns an error. For example, if the database is
busy due to other thread activity, parent_is_encrypted should not return
false. Instead, message sending job should retry later.
2020-02-04 02:54:56 +03:00
Alexander Krotov
627b54f712 sql: add resultified version of query_get_value 2020-02-04 02:54:56 +03:00
Alexander Krotov
8ef7b6fc54 mimeparser: pass the Context around explicitly 2020-02-03 23:48:55 +03:00
Alexander Krotov
d83652b0fc Add standard reference for UID FETCH quirk 2020-02-02 12:46:31 +00:00
B. Petersen
cce32229e0 do not bubble up errors on dc_has_import()
error!() will show the error directly to the user,
this is not useful in this case,
and also the message with the function-name is not for the end-user.

instead, the ui shall (and already does)
show some explaining text if dc_has_import() returns false;
the error!() is disturbing here as this results in two hints shown to the user.
2020-02-01 22:33:18 +00:00
bjoern
24edd83c8a Merge pull request #1207 from deltachat/provider-db
streamline provider-db
2020-01-30 23:33:04 +01:00
B. Petersen
268c5b6482 update provider-db 2020-01-30 23:31:44 +01:00
B. Petersen
a66a754126 use EmailAddress object correctly 2020-01-30 17:47:43 +01:00
B. Petersen
18059734ce make 'cargo fmt' happy with generated code: avoid two lineends at end of file 2020-01-30 17:47:43 +01:00
B. Petersen
c883e709c3 make sure, a domain is not used twice 2020-01-30 17:47:43 +01:00
B. Petersen
7be0bd3583 skip providers without data, cleanup 2020-01-30 17:47:43 +01:00
B. Petersen
d9ab37ea58 check for missing before_login_hint 2020-01-30 17:47:43 +01:00
B. Petersen
ff075ba612 update provider-db 2020-01-30 17:47:42 +01:00
B. Petersen
9c9294a730 use P_DOMAIN_NAME instead of P_123 to make a diff easier, make sure, domains are lowercase 2020-01-30 17:47:42 +01:00
B. Petersen
2a0842b8ae rough check for valid domains 2020-01-30 17:47:42 +01:00
B. Petersen
3cfe45ffc2 disable a clippy warning, add a comment on that 2020-01-30 17:47:42 +01:00
B. Petersen
80dc7bfc52 make 'cargo fmt' happy 2020-01-30 17:47:42 +01:00
B. Petersen
10f26f17ba replace provider/data.rs by auto-generated file 2020-01-30 17:47:42 +01:00
B. Petersen
4ba7402f28 tweak yaml->rust script, generated code compiles and works now :) 2020-01-30 17:47:42 +01:00
B. Petersen
d4da2e0d9c add update.py that takes a folder with yaml-md-files as argument 2020-01-30 17:47:41 +01:00
B. Petersen
b180d004ba use structure as suggested by @dignifiedquire 2020-01-30 17:47:41 +01:00
B. Petersen
de5bd96f08 add some more tests 2020-01-30 17:47:41 +01:00
B. Petersen
3a05b5dacc target comment of @Simon-Laux, use a subdirectory 2020-01-30 17:47:41 +01:00
B. Petersen
b3c4e32b68 split provider-database from code to allow easy generation 2020-01-30 17:47:41 +01:00
B. Petersen
375a48f135 add before_login_hint to device-chat on successfull logins 2020-01-30 17:47:41 +01:00
B. Petersen
1750ab92e6 actually use server/port/etc. from provider-db, remove hardcoded nauta-settings 2020-01-30 17:47:41 +01:00
B. Petersen
8364ddd10b get and test concrete server-data 2020-01-30 17:47:41 +01:00
B. Petersen
63043cb45d add after_login_hint, refine ffi 2020-01-30 17:47:40 +01:00
B. Petersen
aaa6497659 add server data for nauta to provider-db 2020-01-30 17:47:40 +01:00
dignifiedquire
4fc6fa9c8a lazy_static: make it compile 2020-01-30 17:47:40 +01:00
B. Petersen
7d0dcfb3a5 refine example 2020-01-30 17:47:40 +01:00
B. Petersen
a97ea0ad63 adapt python tests 2020-01-30 17:47:40 +01:00
B. Petersen
da66a4d22f make clippy happy 2020-01-30 17:47:40 +01:00
B. Petersen
748e54d4c2 add basic provider-functions 2020-01-30 17:47:40 +01:00
B. Petersen
0f172595d7 we need the provider-db also in the core, not only ffi. the idea is to add it directly to the core and to avoid an extra crate. this also avoids pulling in yaml-rust and some other dependencies. 2020-01-30 17:47:39 +01:00
B. Petersen
5ffdbd99e8 adapt python bindings, remove tests until we really have data 2020-01-30 17:47:39 +01:00
B. Petersen
fbe57c4c71 adapt provider-db api to real need 2020-01-30 17:47:39 +01:00
Alexander Krotov
61726168d6 examples/simple.rs: fix clippy warnings 2020-01-29 23:29:34 +03:00
B. Petersen
a80632ab36 use RECOMMENDED_FILE_SIZE also for sys.msgsize_max_recommended 2020-01-28 23:32:38 +03:00
Alexander Krotov
893eb8b73b Move message size limits to constants
Corresponding C code:
d31c82478c/src/dc_context.h (L165)
2020-01-28 23:32:38 +03:00
Alexander Krotov
07a5ee7d2c mimeparser: construct MimeMessage at the end of from_bytes()
This reduces the scope of code dealing with mutable structures.
2020-01-28 17:46:16 +03:00
Alexander Krotov
c7a300be2f mimeparser: split parse_headers() into multiple functions 2020-01-28 17:46:16 +03:00
Alexander Krotov
ef842fca89 Increase python test timeout to 90 seconds 2020-01-28 17:44:34 +03:00
Alexander Krotov
bdbe9e1ca5 fix(python): add more checks for _thread_quitflag 2020-01-28 17:44:34 +03:00
Alexander Krotov
0c7f65222c fix(python): add workaround for interrupt_idle race condition 2020-01-28 17:44:34 +03:00
dignifiedquire
a8fa644d25 feat: update to latest async-imap 2020-01-28 17:44:34 +03:00
Alexander Krotov
cf7ccb5b8c configure: do not format! an empty string 2020-01-26 16:51:51 +01:00
B. Petersen
49fdd6fc5b clarify documentation 2020-01-26 16:23:30 +01:00
B. Petersen
5e91c74304 instant success on simple qr-setup-contact
this changes the behavior when scanning a setup-contact qr-code.
instead of waiting, until the whole protocol is finished,
which may take something between 10 seconds and several minutes,
the dc_join_secure_join() returns instantly;
the uis typically show the created chat then.

the returned chat-id is the same than if we wait for the protocol to finish,
it is the opportunistic one-to-one chat-id, so no changes there.

all this works even when both devices are offline.

after dc_join_secure_join() returns, however,
the usual setup-contact continues. ux-wise, once the protocol finishes,
a system-info-messages is added to the chat (also unchanged),
this is directly visible, in the chat as well as in the chatlist.
while the prococol runs, the user can alredy send message to the chat,
or do other things in the app.

if the user scans a new qr-code while an existing protocol is not finished yet,
the old join will be aborted (however, of course, created chats are kept).
we could also allow multiple joins at the same time,
however, this would be much more effort that this little change
and i am not sure if it is worth the effort.

finally, if a verified-group shall be joined,
this is not possible instantly, this is not affected by this pr.
same for unverified-groups, however, this could maybe be improved,
but also here, not sure if it is worth the effort
(i think most scans are setup-contact scans)
2020-01-26 16:23:30 +01:00
Alexander Krotov
b83e6f6e7c Simplify MsgId.is_special() 2020-01-26 16:19:01 +01:00
Alexander Krotov
2687777a82 Remove unused EmailAddress::new() 2020-01-26 16:18:34 +01:00
bjoern
918b8036ea Merge pull request #1223 from deltachat/prep-beta24
prepare 1.0.0-beta24
2020-01-26 14:22:57 +01:00
B. Petersen
7ea0e4d4db bump version to 1.0.0-beta.24 2020-01-26 14:06:33 +01:00
B. Petersen
622c1705aa changelog 2020-01-26 13:49:10 +01:00
Alexander Krotov
3a08929b05 Print "email" JSON-value if it has a wrong type 2020-01-26 12:28:42 +00:00
B. Petersen
99e30d561d fix oauth2 login by fixing a Serde call
oauth2 crashes in beta23
because we did not let Serde remove the quotes from a parsed JSON email-address.

for autoconfig, we try to get a well-known URL
containing the domain of the used address - with the quote that is
https://autoconfig.gmail.com"/mail/config-v1.1.xml?...

unfortunately, instead of just returning an error,
(the url does not exist anyway)
reqwest crashes on the attempt to get this URL.

the Serde-thing is not obvious to me:
while serde_json::Value.as_str() removes the quotes,
serde_json::Value.to_string() does not.
2020-01-26 12:28:42 +00:00
Alexander Krotov
033a44580c Turn get_[fresh_]msg_cnt() into ChatId members 2020-01-25 22:11:13 +00:00
Alexander Krotov
4734bcfbb4 Update Chat.unarchive() comment 2020-01-25 22:11:13 +00:00
Alexander Krotov
099fe6f477 Rename Chat.set_blocking() into set_blocked() 2020-01-25 22:11:13 +00:00
Alexander Krotov
889327b5f6 Rename Chat.archive() into Chat.set_archived() 2020-01-25 22:11:13 +00:00
Alexander Krotov
a2845f44ab Turn ChatId-related functions into methods 2020-01-25 22:11:13 +00:00
Alexander Krotov
a7477516d1 mimefactory: factor out get_location_kml_part 2020-01-25 17:44:19 +00:00
bjoern
938d5828fc Merge pull request #1212 from deltachat/handshake-ignore
do not delete handshake messages possibly belonging to secure-joins on other devices
2020-01-25 15:41:20 +01:00
Alexander Krotov
bf3eab453c Avoid panic in sanitise_name()
.truncate() is not safe because it panics if string length does not
lie on character boundary. We convert the string to characters and take
first n character instead.
2020-01-25 13:04:56 +01:00
Alexander Krotov
ebab893330 dc_tools: remove unused dc_derive_safe_stem_ext()
It had the same .truncate() bug as BlobObject::sanitise_name()
2020-01-25 13:04:56 +01:00
Alexander Krotov
4c67b3a118 Add failing test_sanitise_name() test 2020-01-25 13:04:56 +01:00
B. Petersen
d74b06f8bf target comment of @flub 2020-01-24 21:22:12 +01:00
B. Petersen
c54e211147 do not delete handshake messages maybe belonging to secure-joins on other devices 2020-01-24 12:50:26 +01:00
B. Petersen
8817cf5116 don't make me think 2020-01-24 12:32:45 +01:00
Floris Bruynooghe
fb568513b2 When handling vc-contact-confirm the message is Done
The HandshaeMessage::Propagate return should only occur for
vg-member-added messages since it is for those that we still need to
handle the group add.
2020-01-24 11:26:27 +01:00
Floris Bruynooghe
b4ebfdb84a Fix out of sync cargo.lock
This should have been updated when the version of deltachat_derive was
changed.  Oops.
2020-01-24 11:25:08 +01:00
Floris Bruynooghe
7040bd804a Change to normal sematic version number for this sub-crate
Having a number that resembles the core version number is confusing.
This doesn't matter at all, it just needs to be a version number.
2020-01-22 23:49:01 +01:00
holger krekel
2f2fc17bd8 another try 2020-01-22 15:12:48 +01:00
holger krekel
d5d4b49aaf try to get on-tag-builds to work 2020-01-22 15:11:19 +01:00
holger krekel
042c4efddf update Python README 2020-01-22 14:00:55 +01:00
holger krekel
01251d162c fix wheel upload target, and change python versioning 2020-01-22 14:00:55 +01:00
Alexander Krotov
64026fde7c Store main part of the message outside of "parts" vector 2020-01-22 04:28:35 +00:00
Alexander Krotov
adcdae4abe Factor get_message_kml_part out of render_message 2020-01-22 04:28:35 +00:00
holger krekel
88d138b925 only upload manylinux1 packages as others are rejected by pypi anyway 2020-01-21 23:28:11 +01:00
holger krekel
2773b89815 prep beta.23 2020-01-21 21:40:32 +01:00
holger krekel
949d93fdaa - actually also upload py docs, not only c docs
- delete unused files on remote
- show a project number in doxygen
2020-01-21 19:14:16 +01:00
Alexander Krotov
90a4303c8e Make it possible to compile with stable Rust 2020-01-21 19:50:42 +03:00
holger krekel
d3b1972505 fix master build 2020-01-21 17:27:21 +01:00
holger krekel
3807d5fbd0 bringing back wheel building for linux, and fixing c.delta.chat 2020-01-21 17:25:01 +01:00
holger krekel
e49e2021e5 i don't think this fuzzy test is very useful -- the mime-parser will end very early on random-input and DC's parsing machinery probably almost never runs. 2020-01-21 16:00:59 +01:00
Alexander Krotov
84a5276ab0 Fix build
0f52f63863 rebase broke the build
2020-01-21 17:17:51 +03:00
Alexander Krotov
1732c3b350 Reduce the scope of clippy exceptions
This prevents creation of new overly complex functions.
2020-01-21 14:49:53 +01:00
Alexander Krotov
0f52f63863 Add "Auto-Submitted: auto-replied" header to MDNs
This header is specified in RFC 3834.
2020-01-21 16:29:18 +03:00
B. Petersen
0043e95ba7 fix message deletion on server
before a message is actually deleted,
we verify that server_folder+server_uid matches the Message-ID,
so that we do not delete the wrong message by accident.
the reason for that is that, to my knowledge, server_folder+server_uid
are not necessarily stable but Message-ID is.

anyway, this check was broken and set the server_uid to 0 in all cases -
where it should be set to 0 _only_ on mismatches.
2020-01-21 16:27:21 +03:00
Alexander Krotov
a3f2088046 Fix the message printed on job success
"Cannot be retried" does not make it clear if the job succeeded or failed.
2020-01-20 10:47:32 +01:00
Floris Bruynooghe
186f5553b8 Introduce a ChatId newtype
This doesn't try and change the way ChatId is used.  It still allows
creating them with 0 and lets some function use a ChatId(0) as error
return.
2020-01-19 23:42:08 +01:00
Alexander Krotov
d8454d9da5 Deduplicate SendMdn and SendMsgToSmtp code 2020-01-20 00:35:11 +03:00
Alexander Krotov
318194a216 Rename Param::MessageId into Param::MsgId
MsgId refers to database ID, same as crate::message::MsgId newtype.
2020-01-20 00:35:11 +03:00
Alexander Krotov
b50d2358d3 Rename X-Additional-Message-IDs into Additional-Message-IDs
X- prefixes are deprecated in https://tools.ietf.org/html/rfc6648
2020-01-20 00:35:11 +03:00
Alexander Krotov
763587ffb4 Load only one job at a time
As a result of job, other jobs can be added or deleted. To avoid
processing deleted jobs and to process other jobs in the correct order,
we should reload them after performing each job.

However, we don't use "LIMIT 1" in SQL queries, because we want to be
able to skip over invalid jobs, and checking if job is invalid can only
be done in Rust.
2020-01-20 00:35:11 +03:00
Alexander Krotov
8a375c12e9 Mark two messages as seen in test_send_and_receive_message_markseen
This may or may not send a combined MDN out.  We don't test for it,
but the test ensures that *if combined MDNs are sent in this case*,
then we receive them correctly.
2020-01-20 00:35:11 +03:00
Alexander Krotov
11afdb51f3 Log it when MDNs are combined 2020-01-20 00:35:11 +03:00
Alexander Krotov
e4353f4650 Aggregate SendMdn jobs 2020-01-20 00:35:11 +03:00
Alexander Krotov
9d4437a7f5 Make it possible to add X-Additional-Message-IDs to MDNs 2020-01-20 00:35:11 +03:00
Alexander Krotov
493bf5ed08 Parse additional message IDs in MDNs 2020-01-20 00:35:11 +03:00
Alexander Krotov
93800fd834 Construct list of recipients in SendMdn
There is always one recipient for MDNs, so it is easier to only handle
this case.
2020-01-20 00:35:11 +03:00
Alexander Krotov
79bc34ed76 Move check for blocked contact from mimefactory to SendMdn job 2020-01-20 00:35:11 +03:00
Alexander Krotov
b40ad7e87e Store contact ID in SendMdn job foreign_id
This change makes it possible to find all pending MDNs for the contact
with an SQL query.
2020-01-20 00:35:11 +03:00
Alexander Krotov
1ab3fba212 Move check for enabled MDNs from message rendering to MDN job 2020-01-20 00:35:11 +03:00
Alexander Krotov
3cd0bbc0f4 Move foreign_id handling out of mimefactory 2020-01-20 00:35:11 +03:00
Alexander Krotov
a806728e43 Make a new job type for MDN sending
Now MDN is generated on every try instead of being stored in a blob.
2020-01-20 00:35:11 +03:00
Alexander Krotov
683037ca69 Remove no-op jobs SendMdnOld and SendMsgToSmtpOld 2020-01-20 00:35:11 +03:00
Alexander Krotov
a113127df1 Implement Display for MessageState 2020-01-20 00:35:11 +03:00
Alexander Krotov
fecfaaf812 Test MDN parsing 2020-01-20 00:35:11 +03:00
Alexander Krotov
8df8f1f6f7 Update deltachat_derive metadata
Same as core and FFI.
2020-01-19 21:40:22 +01:00
Alexander Krotov
5502ca5f58 Update deltachat_derive dependencies 2020-01-19 21:40:22 +01:00
Alexander Krotov
75b8cfa92e Disable unnecessary image crate features
TIFF file format and parallel processing of JPEGs are not necessary to
recode avatars.
2020-01-18 00:30:52 +03:00
B. Petersen
f7b23fb0e2 check that recoding of jpg and png files actually works 2020-01-18 00:30:21 +03:00
B. Petersen
42f7abf2f5 for test_selfavatar*(), use the same function signature as for other tests 2020-01-18 00:30:21 +03:00
Alexander Krotov
08dd0e34d1 Make reqwest features "blocking" and "json" non-optional 2020-01-17 16:01:11 +03:00
Floris Bruynooghe
7540770dec Resultify get_chat_id_by_grpid
I want to avoid having to be able to represent a chat_id of 0 in order
to more nicely turn chat_id into a ChatId newtype.
2020-01-16 21:46:06 +01:00
Alexander Krotov
213c5df706 mimefactory: remove unused "increation" field 2020-01-16 23:37:53 +03:00
B. Petersen
578e4b2785 re-add some comment to the securejoin-flow 2020-01-16 17:06:24 +01:00
Floris Bruynooghe
8f8db0c431 Small language improvement 2020-01-15 07:27:07 +01:00
Alexander Krotov
df7253b13e Document MessageState and fix some typos 2020-01-15 00:48:10 +03:00
Alexander Krotov
fcafd63f41 Remove NOTE about third-party libraries
"libs" directory has been removed during transition to Rust
2020-01-14 10:29:08 +03:00
Floris Bruynooghe
c82fdb9fa1 Refactor return values from secure-join message handling
This return value was very complicated to understand.  Some failure
returns were returned as Err and some as Ok with no consistency, but
resulting in the same behaviour.

This refactor makes the handle_securejoin_handshake the sole place
responsible for maintaining the state of the secure join
process (context.bob) and also in charge of terminating the ongoing
process.  This is none of receive_imf's business.

The remaining returns are now cleanly classified in application-errors
and protocol errors:

Applications errors result in an Err and mean there is a bug or
something else serious went wrong, like database access suddenly
failed or so.  In this case receive_imf is still responsible for
clearing the state and resetting ongoing-process.  It may be possible
this should still be moved back to securejoin.rs so that recieve_imf
doesn't need to know anything about this either.

Protocol errors are not failures for receive_imf, it just means the
received message didn't follow the protocol.  Receive_imf in this case
is told to ignore the message: that is hide it but not delete it.

Other Ok returns also only say what needs to happen to the message:

- It's fully processed and needs no further processing, instead should
  be removed

- It should still be processed as a normal received message.

This changes some behaviour: if the chat creation/lookup for the
contact fails this is treated as an application error.  Previously
this was silently ignored and send_msg() would be called with a 0
chat_id without checking the response.  This resulted in the protocol
quitely being blocked.

This all shhould now make it easier to resultify more of the functions
called by this function, instead of having to deal with very
complicated application logic hidden in the return code.
2020-01-13 20:46:58 +01:00
Alexander Krotov
454c52f4ab Use correct SPDX license identifier in Cargo.toml 2020-01-13 10:48:16 +00:00
B. Petersen
73c69c3f93 fix optimisation 2020-01-13 01:43:27 +01:00
bjoern
614ec30e05 Update src/mimefactory.rs
Co-Authored-By: Alexander Krotov <ilabdsf@gmail.com>
2020-01-13 01:43:27 +01:00
B. Petersen
d7c07e0ed3 set subject to group-name, tweak prefixes
this commit sets the subject of groups to the name of the group.
if the message is a reply in the group, also the prefix `Re:` is added.
the Chat: prefix is removed for groups,
also the Fwd: prefix, as it this would be expected to be set to the
group name of the forwarded message, which is not really compatible with the
rule "group-name = subject" (forwarded messages are detected by the body,
not by the subject)
2020-01-13 01:43:27 +01:00
Alexander Krotov
08bd16c1b6 Update rusqlite 2020-01-12 21:54:38 +00:00
Alexander Krotov
4f06c72798 Remove top_evil_rs.py 2020-01-12 09:02:30 +00:00
Alexander Krotov
166fe2a4e5 Forbid unsafe code in deltachat crate 2020-01-12 09:02:30 +00:00
Alexander Krotov
b7635a71c8 Do not call unsafe rusqlite::ffi::sqlite3_threadsafe()
rusqlite already checks that SQLite is safe to use in multi-threaded mode when it opens the database and returns rusqlite::Error::SqliteSingleThreadedMode otherwise.
2020-01-12 09:02:30 +00:00
Alexander Krotov
6ad4bdea83 Make get_parent_mime_headers() private 2020-01-12 09:12:24 +01:00
Alexander Krotov
efb9a11d22 Do not select drafts as parent message
Also create a common parent_query() function to make sure we use the
same message as a parent when determining if it is encrypted and when
actually replying to it.
2020-01-12 09:12:24 +01:00
Alexander Krotov
047d09bcb1 Do not pass &Sql to parent_is_encrypted() 2020-01-12 09:12:24 +01:00
Alexander Krotov
e1ca6b5181 Rename last_msg_in_chat_encrypted into parent_is_encrypted 2020-01-12 09:12:24 +01:00
Alexander Krotov
36a2569537 Make last_msg_in_chat_encrypted a member function 2020-01-12 09:12:24 +01:00
Alexander Krotov
10ceddfa67 Set draft in test_reply_encrypted test
Adding unencrypted draft to chat makes the job of determining whether
last message is encrypted harder.
2020-01-12 09:12:24 +01:00
holger krekel
929677afe0 fix typo 2020-01-11 21:10:08 +00:00
Alexander Krotov
ee4adc1363 Return &str from extract_grpid 2020-01-11 14:59:53 +01:00
Alexander Krotov
c7b2bdfaac mimeparser: simplify get_subject() and get_rfc724_mid 2020-01-11 14:59:53 +01:00
Alexander Krotov
fde8fb960b oauth2: replace unwrap() and is_none() with "if let" 2020-01-11 14:59:53 +01:00
Alexander Krotov
9a43d26c60 dc_receive_imf: do not unwrap() Some(..) immediately after creation 2020-01-11 14:59:53 +01:00
Alexander Krotov
742f603b3b Use if let to avoid unwrap() of peerstate 2020-01-11 14:59:53 +01:00
Alexander Krotov
a82f2a5df3 Simplify needs_encoding() and add a test 2020-01-11 14:59:53 +01:00
Alexander Krotov
06bc5513ae Use Vec::first to avoid explicit is_empty() check 2020-01-11 14:59:53 +01:00
Alexander Krotov
47b937f880 Turn some enum Param comments into doc-comments 2020-01-11 15:27:09 +03:00
Alexander Krotov
775d27b6a9 Update group test 2020-01-10 15:20:23 +03:00
Alexander Krotov
6c838ab57c Never reset gossip timestamp for all chats at the same time 2020-01-10 15:20:23 +03:00
Alexander Krotov
8eebd2aa67 Do not reset gossip timestamp when group members change 2020-01-10 15:20:23 +03:00
Alexander Krotov
fc78a08657 Fix inverted should_do_gossip condition 2020-01-10 15:20:23 +03:00
Alexander Krotov
7b3cc95ab7 Derive Debug for Loaded and MimeFactory 2020-01-10 06:42:36 +00:00
Alexander Krotov
bd70765b7d Join recipients_names and recipients_addr into one vector
This change ensures on the type level that number of names and addresses
is the same.
2020-01-10 06:42:36 +00:00
Alexander Krotov
5b424aec22 Remove temporary variable "row" 2020-01-10 06:42:36 +00:00
Alexander Krotov
d6cc0694f0 Move Chat from MimeFactory to Loaded::Message 2020-01-10 06:42:36 +00:00
Alexander Krotov
9738515129 Construct immutable MimeFactory in from_msg 2020-01-10 06:42:36 +00:00
Alexander Krotov
b05ee10f41 Return Err from MimeFactory::from_msg on database errors 2020-01-10 06:42:36 +00:00
Alexander Krotov
67d85f0f86 Make all MimeFactory fields private 2020-01-10 06:42:36 +00:00
Alexander Krotov
2ddeef761f Fix gossiped_timestamp documentation 2020-01-09 15:40:58 +03:00
Alexander Krotov
43e0109d44 Update reqwest to 0.10.0 2020-01-09 12:17:17 +03:00
Alexander Krotov
02bb41697d Add MessengerMessage type for is_dc_message field 2020-01-08 19:00:45 +03:00
Alexander Krotov
12cd56e3e8 Rename type_0 into viewtype 2020-01-08 19:00:45 +03:00
Floris Bruynooghe
72cfb70e35 Refactor create_chat_from_msg_id
The goal here is to not have a `mut chat_id` as I want to change chat
id into an opaque newtype and we can't modify it like that anymore.
2020-01-08 14:58:44 +01:00
Alexander Krotov
40dc180b88 Update mailparse to 0.10.2
It does not allow parsing emails without @
2020-01-08 16:52:27 +03:00
Alexander Krotov
4529c326c6 Restore newlines and remove semicolon in test_parse_first_addr 2020-01-07 17:23:59 +01:00
Alexander Krotov
f6660af014 Update test_mdn_asymmetric
MDNs are processed now even when MDNs are disabled
2020-01-07 17:23:19 +01:00
Alexander Krotov
a52131b574 cargo fmt 2020-01-07 17:23:19 +01:00
Alexander Krotov
a48d0492c8 Create only one MarkseenMdnOnImap job even if message has multiple reports
All created jobs have the same parameters and move the same message,
so it does not make sense to create more than one.
2020-01-07 17:23:19 +01:00
Alexander Krotov
2bba1be817 Show received MDNs even if user has disabled them
Delta Chat does not request MDNs if they are disabled, so this change
does not make much difference, but simplifies the logic.
2020-01-07 17:23:19 +01:00
bjoern
8a394fb08f Merge pull request #1141 from deltachat/add-missing-event
send DC_EVENT_CHAT_MODIFIED when adding members to a group
2020-01-07 13:46:17 +01:00
Alexander Krotov
693ae9e8f2 Guess mimetype of .webm files as video/webm 2020-01-07 09:19:36 +00:00
B. Petersen
4b7b6d6cb3 send DC_EVENT_CHAT_MODIFIED when adding members to a group (removing is already fine) 2020-01-06 23:35:27 +01:00
Floris Bruynooghe
9a3bdfb14b Rename MimeParser to MimeMessage in mimeparser.rs
The struct really represents a parsed MIME message and is not used as
a parser itself.  After the from_bytes() call (which should arguably
use the FromStr trait instead) the struct is fully populated.
2020-01-06 09:46:47 +00:00
Alexander Krotov
7aeddc63ac Use DC_CHAT_ID_DEADDROP instead of constant "1" 2020-01-06 00:04:03 +00:00
Floris Bruynooghe
2990a1c255 Mark the ChatInfo struct non_exhaustive
This is a new feature in Rust 1.40, it means users outside the crate
will not be able to create these structs, allowing us to add fields
without breaking the public API.
2020-01-05 22:53:52 +00:00
B. Petersen
91eea03b18 hide messages with Secure-Join headers if the Secure-Join fails 2020-01-05 22:06:06 +01:00
Alexander Krotov
8702f290af mimeparser: remove unused parsed_protected_headers field 2020-01-05 22:03:10 +01:00
Alexander Krotov
d0b5b7ba03 Turn handle_reports comment into documentation comment 2020-01-04 22:24:16 +01:00
Alexander Krotov
f3d68c6f25 Add requests dependency to python/README.rst
It is used in tests/conftest.py
2020-01-04 20:18:31 +01:00
Alexander Krotov
91100d3fac Lowercase the addresses before gossip peerstate update
Normally it happens in addr_cmp function, but update_gossip_peerstates
forms a HashSet of addresses, so they should be lowercased beforehand.

Also adjust the mail_with_cc.txt to test for non-lowercase addresses.
2020-01-02 18:14:27 +00:00
Alexander Krotov
61833c32e5 Remove trailing space from deltachat.h 2020-01-01 23:05:08 +00:00
Alexander Krotov
1468f67b9b Warn about multiple From addresses only when necessary
The message says "only using first" so it should be printed right before the first address is taken with .get_index(0) operation.
2020-01-01 21:03:28 +00:00
Alexander Krotov
cb6376094c Get rid of DC_FP_* constants 2020-01-01 20:53:31 +00:00
björn petersen
ff175e661b Merge pull request #1130 from deltachat/fpr-without-addr
handle plain OPENPGP4FRP qr-codes correctly
2020-01-01 21:49:37 +01:00
B. Petersen
fe741f168a fix early exit on empty addresses in decode_openpgp() 2020-01-01 18:53:35 +01:00
B. Petersen
eaaf500d5e add failing test for plain OPENPGP4FRP-qr-code 2020-01-01 18:52:00 +01:00
björn petersen
05f85b33c5 Merge pull request #1124 from deltachat/mimeparser_message_kml
Location streaming fixes
2019-12-31 16:49:08 +01:00
Alexander Krotov
c4e0647bda Remove NULs from mimeparser tests 2019-12-30 17:32:39 +01:00
Alexander Krotov
6dcc1e09b9 mimeparser: add message.kml parsing test 2019-12-30 17:29:28 +01:00
Alexander Krotov
ea03e4d34a Send location.kml instead of message.kml for streaming
message.kml is sent when user manually selects a point on the map to
point to a location.
2019-12-30 17:20:46 +01:00
B. Petersen
b7a2d17e93 remove weird comment 2019-12-29 18:58:49 +00:00
B. Petersen
e1bd740249 init authname when received by From: header (it is also updated from there) 2019-12-29 18:58:49 +00:00
B. Petersen
de52c2da80 add failing test for receiving authnames 2019-12-29 18:58:49 +00:00
Alexander Krotov
a31c6d82c9 Remove get_origin_by_id 2019-12-28 22:33:57 +00:00
Alexander Krotov
25842894d2 Rename job::Try into job::Status 2019-12-28 14:35:14 +00:00
Alexander Krotov
83c98c2d55 Replace unstable try_trait with a job_try! macro 2019-12-28 14:35:14 +00:00
Alexander Krotov
fe2011742d Job error handling refactoring 2019-12-28 14:35:14 +00:00
B. Petersen
d87b676d60 do not abort search completely when updating a special name fails 2019-12-27 22:53:03 +00:00
Alexander Krotov
ffa6378108 Fix clippy warnings in deltachat-ffi 2019-12-27 10:28:50 +00:00
B. Petersen
f73ba895af remove some meanwhile unneeded allow-statements 2019-12-26 16:26:06 +00:00
B. Petersen
cb2a1147f0 fix searching for localized device chats 2019-12-26 16:23:42 +00:00
B. Petersen
6702ef4a71 add a failing test that does not fine localized device chats 2019-12-26 16:23:42 +00:00
Alexander Krotov
1d46791364 Add more Yandex domains 2019-12-26 01:48:28 +00:00
björn petersen
f61846dec9 Merge pull request #1107 from deltachat/list-language-bindings
list language bindings in README
2019-12-26 01:35:31 +01:00
B. Petersen
4776e196da add frontend projects to README 2019-12-26 00:42:07 +01:00
B. Petersen
839a48b678 list language bindings in README 2019-12-26 00:42:07 +01:00
Alexander Krotov
e203901224 Better logging message for job deletion
The logging message printed for successful job completion said that the
job "cannot be retried" even when it does not need to be retried.
2019-12-24 10:47:29 +00:00
Alexander Krotov
76d03f7fd2 Log SMTP connection failure errors 2019-12-23 07:14:11 +01:00
holger krekel
7f5e3aaaf7 add changelog for device-chat fix 2019-12-22 05:45:27 +01:00
B. Petersen
5bfbae4b00 add test for delete_and_reset_all_device_msgs() 2019-12-22 05:44:09 +01:00
B. Petersen
521a854635 delete and reset device-messages on import; this avoids wrong information in the device chat and allows adding correct information again 2019-12-22 05:44:09 +01:00
B. Petersen
ce15ef2db9 prep beta22 2019-12-22 05:42:33 +01:00
holger krekel
6d5cf89d33 enable link-time-optimization 2019-12-21 23:09:15 +00:00
holger krekel
b3b984f351 also "cargo update", pulls in aysnc-std 1.4.0 2019-12-21 23:09:15 +00:00
holger krekel
d5a0f1e711 use latest lettre master that contains the normalization fix 2019-12-21 23:09:15 +00:00
Alexander Krotov
67c36f3d98 Use usize to get pointer size
According to Rust documentation, usize is "The pointer-sized unsigned
integer type".

This change removes unnecessary libc dependency and makes top_evil_rs
script happier.
2019-12-21 23:07:35 +00:00
Alexander Krotov
8e0a29e9b5 Stop using Event callback return values
Since stock string callback has been deprecated, all event callbacks
return 0.

For compatibility, C declarations are not changed and FFI users are
expected to return 0 from their callbacks.
2019-12-21 22:51:44 +00:00
Alexander Krotov
47be879445 fix(job): log all job events in job_perform() 2019-12-21 15:24:30 +00:00
Alexander Krotov
860f8a7906 refactor(job): refer to std:: only once
This change unifies how we refer to std::time::Duration across the module.
2019-12-21 15:24:30 +00:00
Alexander Krotov
5d9baa053a refactor(job): implement Display for Job 2019-12-21 15:24:30 +00:00
Alexander Krotov
da174eae71 refactor(job): use Display to print Thread in job_perform() 2019-12-21 15:24:30 +00:00
björn petersen
7fac71aa81 Merge pull request #1094 from deltachat/tweak-repl
use println!() for all repl outputs
2019-12-21 16:00:27 +01:00
B. Petersen
7d51c1140d use println!() for all repl outputs
in the very beginning, we allowed using the cmdline partly from the ui
via a special function; this made info!() handy.
however, this is long gone and no longer needed.
2019-12-21 13:09:38 +01:00
Alexander Krotov
cd198223ea Remove unwrap() from time() function
This change is a part of the effort to enable clippy::option_unwrap_used and clippy::result_unwrap_used
2019-12-21 08:58:41 +01:00
Alexander Krotov
300fff40e3 refactor(smtp): remove unused SendTimeout Error variant
It was used when timeout was set on the whole smtp.send() operation.
Now only the operations inside smtp.send() can timeout, and such timeout
errors result in SendError, so SendTimeout is unused.
2019-12-21 08:57:29 +01:00
Floris Bruynooghe
08abac350d Tweak the python readme a bit more. 2019-12-21 00:10:45 +00:00
shubhank008
a57decf8be Up to date Readme.rst
-Moved wheels installation below source installation and added comment that they are outdated and not working atm
-Updated source compilation/installation to a more step by step process as current one was outdated and not working due to Rust now needing nightly build else failing to compile (subtle v2.2.2 do not work without nightly)
2019-12-20 21:14:16 +01:00
holger krekel
ee95e59243 "cargo update" 2019-12-20 18:11:29 +01:00
holger krekel
b0694bcf2c prep beta21 2019-12-20 15:17:59 +01:00
Alexander Krotov
c9f6e31ca9 Move dc_simplify.rs to simplify.rs 2019-12-20 12:55:57 +01:00
Alexander Krotov
fe4080d59f refactor(simplify): move dehtml dependency to mimeparser
This change also removes unnecessary String clone for HTML messages.
2019-12-20 12:55:57 +01:00
B. Petersen
7f6a1ad1a7 add some empty lines to enums & co to make things more readable and to avoid errors this way :) 2019-12-20 12:55:42 +01:00
Alexander Krotov
980bb35441 refactor(mimefactory): use iter::repeat_with instead of (0..) 2019-12-20 12:55:25 +01:00
holger krekel
01df2e2dc7 fix #1077 for unknown senders in a group chat (such as mailer daemons): don't recreate member list and show a special stockstring-ed message advising to hit "more info". 2019-12-20 11:59:10 +01:00
Floris Bruynooghe
ec40dd1b6f Change the JSON API function to be from a serialised struct
This is the first experiment towards using structs to define the API.
It adds it as a new method on the existing Chat struct.

The types in this struct could improve as well as many other things.
But this is a straight forward translation of the existing json! macro
into a struct.
2019-12-20 10:39:39 +01:00
Alexander Krotov
f2f8898004 fix(mimefactory): wrap base64-encoded attachments to 78 columns
RFC5322 requires emails to be wrapped to 78 columns excluding CRLF.
2019-12-20 09:11:35 +01:00
björn petersen
3afa37c6ea Merge pull request #1075 from deltachat/prepare_beta20
prep 1.0.0-beta.20
2019-12-19 17:17:26 +01:00
holger krekel
8c0ce38301 prep 1.0.0-beta.20 2019-12-19 16:55:24 +01:00
holger krekel
cc6aa3209c fix the fix 2019-12-19 16:53:55 +01:00
holger krekel
76a86763dd address @r10s comment 2019-12-19 16:53:55 +01:00
holger krekel
09fb039528 another reverse mut bites the dust 2019-12-19 16:53:55 +01:00
holger krekel
174d3300c4 not sure it's much better but using a static-sized array is probably better than a dynamically sized vec, thanks @dignifiedquire 2019-12-19 16:53:55 +01:00
holger krekel
8b57ce1792 remove unused include_in_contactlist 2019-12-19 16:53:55 +01:00
holger krekel
6c14e429eb Origin::is_verified() -> Origin::is_known() because this has nothing to do with verified groups or contacts 2019-12-19 16:53:55 +01:00
holger krekel
5f200c6bc3 don't pass incoming_origin as &mut as the caller doesn't need it 2019-12-19 16:53:55 +01:00
holger krekel
d52347ee1d also don't pass "to_id" and don't make it mut inside add_parts 2019-12-19 16:53:55 +01:00
holger krekel
d0d9aa4400 - move CC-parsing next to To-parsing where it blongs
- pass to_ids and from_id as immutable to add_parts
2019-12-19 16:53:55 +01:00
holger krekel
c3d909c818 add a test that contacts are properly created and fix ordering in dc_receive_imf to pass the test 2019-12-19 16:53:55 +01:00
dignifiedquire
d9e718b0e0 feat(imap): update async-imap for oauth2 fix 2019-12-19 13:51:59 +01:00
holger krekel
a7b55edb9b more call sites 2019-12-19 12:06:01 +01:00
holger krekel
000479d55e never block on interrupt_inbox_idle 2019-12-19 12:06:01 +01:00
Alexander Krotov
7ef22f2940 fix(smtp): reduce SMTP I/O timeout to 30 seconds
15 minute timeout was used because it applied to the whole send()
operation. Now timeout applies to each I/O operation, such as read()
or write(), so it can be made much shorter. In particular, this timeout
applies to read() call right after connection to plain or STARTTLS server,
in which case it is not reasonable to wait 15 minutes to receive one
line of data.

The timeout is triggered only if no progress is made within 30 seconds. It
likely indicates a network failure anyway.
2019-12-19 06:55:19 +01:00
Alexander Krotov
a242fcfd2c refactor(dehtml): remove Result unwrap in dehtml_starttag_cb() 2019-12-18 23:24:43 +03:00
Alexander Krotov
73c21ae0a9 refactor: enable clippy::unreadable_literal 2019-12-18 23:23:38 +03:00
dignifiedquire
2398454838 feat(smtp): update to use async-smtp based timeouts 2019-12-18 03:08:59 +03:00
holger krekel
c1ba5a30c5 prepare beta.19 2019-12-16 23:46:28 +01:00
holger krekel
2c0f847d3e remove too verbose logging for received messages 2019-12-16 23:12:17 +01:00
holger krekel
7d5e95f013 thread-name seems to be unnamed all the time so it's use is currently theoretic 2019-12-16 23:12:17 +01:00
Alexander Krotov
9000342de8 Get rid of unnecessary "async move" and ".await" 2019-12-16 18:02:50 +01:00
holger krekel
61b47aa0de try doing an smtp-send timeout 2019-12-16 18:02:50 +01:00
björn petersen
22f5c5fb74 Merge pull request #1057 from deltachat/beta18
prepare to tag 1.0.0-beta.18
2019-12-16 15:00:48 +01:00
holger krekel
801162d7be prepare to tag 1.0.0-beta.18 2019-12-16 13:20:14 +01:00
Alexander Krotov
f81f3fb060 Do not panic on failure to read IMAP server greeting 2019-12-16 13:19:34 +01:00
holger krekel
15792d8426 use dcc-stable branch that returns an error on missing greetings() "* OK Dovecot ready" (currently it panics) 2019-12-16 13:19:34 +01:00
Floris Bruynooghe
d7f345eef8 Add the thread id and name to info and warn log output 2019-12-16 13:11:44 +01:00
Alexander Krotov
e3031462c1 Replace expect() in select_with_uidvalidity with an Error 2019-12-16 14:44:52 +03:00
holger krekel
47d14271ab fix #1020 -- allow to set os_name in python bindings 2019-12-16 11:51:04 +01:00
holger krekel
2bf9fd6cbc revert printing file/lineno in Error-messages as these are typically user-visible
For info and warn it's fine
2019-12-16 03:17:08 +03:00
Floris Bruynooghe
19e716b522 Add filename and line no to log entries
This is done for all logging calls, also those which call error! which
is normally directly shown to the user.
2019-12-16 00:33:57 +01:00
Alexander Krotov
1ee15942cc Simplify simplify.rs
* Remove Simplify structure.

* Match for lines starting with 5 markers, not consisting of exactly 5 markers.

This is a regression from C to Rust conversion, see
2bb1c280d5/src/dc_simplify.c (L154)

* Add tests.
2019-12-16 02:31:38 +03:00
björn petersen
898e641256 Merge pull request #1048 from deltachat/beta17
add changelog for beta17 and bump versions
2019-12-15 23:32:11 +01:00
holger krekel
cda4ccff2a add changelog for beta17 and bump versions 2019-12-15 23:26:05 +01:00
B. Petersen
ba274482f7 make d.png much smaller in size to avoid avatar-recoding and to allow checking file contents 2019-12-15 23:12:37 +01:00
B. Petersen
435f734d60 adapt tests for new avatar recoding 2019-12-15 23:12:37 +01:00
B. Petersen
a5f949c4e2 recode group- and user-avatar to 192x192 pixel 2019-12-15 23:12:37 +01:00
B. Petersen
9fc556864e add image crate 2019-12-15 23:12:37 +01:00
Alexander Krotov
a0645dc713 Resultify dc_create_folder and don't ignore its errors 2019-12-16 00:49:51 +03:00
holger krekel
5893cd309d fixate async-imap to the working commit so that no one accidentally does "cargo update" and breaks things. 2019-12-16 00:49:37 +03:00
holger krekel
09c7ab1ee6 avoid addrparse to panic() and refactor according code a little with test. 2019-12-15 21:54:20 +01:00
holger krekel
4bacae3711 fix #1037 and simplify mdn-report related code and state 2019-12-15 22:18:34 +03:00
Alexander Krotov
8d3e536582 Do not panic on SystemTimeDifference 2019-12-15 19:56:13 +01:00
Alexander Krotov
697cc0a79b Make needs_encryption a bool 2019-12-15 19:55:49 +01:00
Alexander Krotov
a34ed5c02a Make recalc_fingerprints a bool 2019-12-15 19:55:49 +01:00
Alexander Krotov
256bb01606 Make prepend_subject a bool 2019-12-15 19:55:49 +01:00
Alexander Krotov
1de535363d Move parse_message_id from wrapmime.rs to imap/mod.rs 2019-12-15 19:13:49 +03:00
Alexander Krotov
24d9011939 Move get_autocrypt_mime from wrapmime.rs to e2ee.rs 2019-12-15 19:13:49 +03:00
holger krekel
6284bdc98f pull in latest async-smtp master to fix #1030 2019-12-15 15:27:25 +01:00
Alexander Krotov
e7351b1bb8 Restore constant and remove parenthesis 2019-12-15 16:43:12 +03:00
Alexander Krotov
b3ee89c6e5 Documentation improvements 2019-12-15 16:15:09 +03:00
Alexander Krotov
3f49492ccf cargo fmt 2019-12-14 22:28:15 +01:00
Alexander Krotov
ad700b45d0 Make Imap.connect() async 2019-12-14 22:25:33 +01:00
Alexander Krotov
74923b4575 Enable clippy::type_complexity error 2019-12-14 22:00:38 +01:00
Alexander Krotov
81199e7ee0 Update Cargo.lock 2019-12-14 18:16:54 +00:00
Alexander Krotov
ccca1b0bea Do not format! SQL queries 2019-12-14 18:16:54 +00:00
Alexander Krotov
ba1ced5e08 Switch to master branch of async-imap 2019-12-14 17:51:36 +01:00
Alexander Krotov
558466d506 Revert "Enable clippy::unreadable_literal error"
This reverts commit b424ef9ebb.
2019-12-14 15:55:33 +00:00
Alexander Krotov
b424ef9ebb Enable clippy::unreadable_literal error 2019-12-14 15:20:58 +00:00
Alexander Krotov
69f5fd86a4 Allow clippy::needless_range_loop only in simplify_plain_text 2019-12-14 15:20:58 +00:00
Alexander Krotov
f9ba9ae912 Enable clippy::large_enum_variant error 2019-12-14 15:20:58 +00:00
Alexander Krotov
b33fec6236 Enable clippy::block_in_if_condition_stmt error 2019-12-14 15:20:58 +00:00
Alexander Krotov
70e12485aa Switch to native_tls version of deltachat/lettre fork
This commit completely removes rusttls dependency
2019-12-14 16:12:13 +01:00
Alexander Krotov
b2b7674b59 Update Cargo.lock 2019-12-14 16:12:13 +01:00
holger krekel
8fa175f36d changelog and version up to 1.0.0-beta.16 2019-12-14 00:22:09 +01:00
B. Petersen
95fbc904d1 go to correct step so that param_autoconfig is actually used 2019-12-14 00:12:14 +01:00
B. Petersen
ce37a8dda2 use provider-database only if the user did not enter a server/port/whatever manually; this allows the user to override erroneous choices 2019-12-14 00:12:14 +01:00
holger krekel
e0499c9552 add preliminary support for offline-autoconfigure for nauta 2019-12-14 00:12:14 +01:00
Simon Laux
4825f2510f Merge pull request #1016 from deltachat/fix_displayname
fix displayname to use authname when availible
2019-12-13 13:21:03 +01:00
Alexander Krotov
7a12134795 Test that no gossip is sent in 1-to-1 chat 2019-12-13 12:51:44 +01:00
Alexander Krotov
66adfa074b Do not cache gossiped_timestamp 2019-12-13 12:51:44 +01:00
Alexander Krotov
9d201eb9c6 Add get_gossiped_timestamp 2019-12-13 12:51:44 +01:00
Alexander Krotov
789fc0a7e0 Resultify set_gossiped_timestamp 2019-12-13 12:51:44 +01:00
Alexander Krotov
9e309132f8 Only add gossip headers if chat has more than one peer 2019-12-13 12:51:44 +01:00
Alexander Krotov
88923173c2 Set is_gossiped to true when Autocrypt-Gossip header is added 2019-12-13 12:51:44 +01:00
Simon Laux
7b55863418 switch priority order 2019-12-13 11:58:21 +01:00
Simon Laux
38d5fad4e5 fix displayname to use authname when availible 2019-12-13 11:58:08 +01:00
holger krekel
4d1357568b use async-smtp master where @link2xt's branch has been merged 2019-12-13 11:03:46 +01:00
Alexander Krotov
ccc190f991 Switch to native_tls 2019-12-13 11:03:46 +01:00
B. Petersen
0a9f880fb4 remove another superfluous return value 2019-12-13 01:17:48 +01:00
holger krekel
b8faf54f0b remove superflous expression values 2019-12-13 01:17:48 +01:00
B. Petersen
888507f7ba test group- and user-avatars in the same mail 2019-12-13 01:17:48 +01:00
B. Petersen
29866092de adapt the protected-header test; protected-headers really require encryption now and parts of the tests are know to fail now. however, protected-headers are also tested in python implicitly in several places 2019-12-13 01:17:48 +01:00
B. Petersen
36fc2c83f2 return selfavatar from dc_get_info() 2019-12-13 01:17:48 +01:00
holger krekel
5e777b3c51 fix test also for avatar deletion, use that drain filter while we in this code area :) 2019-12-13 01:17:48 +01:00
holger krekel
5690c48863 fix merging of protected headers 2019-12-13 01:17:48 +01:00
holger krekel
8ab3363097 add a test that runs until an avatar is send encrypted 2019-12-13 01:17:48 +01:00
B. Petersen
f6861ca5f5 target comments from @flub and @dignifiedquire 2019-12-13 01:17:48 +01:00
B. Petersen
3bb58be2b5 add missing delete header 2019-12-13 01:17:48 +01:00
B. Petersen
409c96e571 add compatiblity headers 2019-12-13 01:17:48 +01:00
B. Petersen
d681fa6cba use Chat-User-Avatar and Chat-Group-Avatar
we change the name on the wire as the old Chat-Group-Image header
could not be used on random mails, it was the marker for a "Changed" message,
if we would keep this names, things will fail for exising installations
as messages are dropped and a "Group image changed" message is shown instead.
2019-12-13 01:17:48 +01:00
B. Petersen
7c3d8356c4 always handle incoming group-avatars 2019-12-13 01:17:48 +01:00
B. Petersen
a8842da50a && has a higher precedence than || - fix a logical error when resetting Unpromoted 2019-12-13 01:17:48 +01:00
B. Petersen
ff29b84146 share group image on group creation, fixes #602 2019-12-13 01:17:48 +01:00
B. Petersen
ab12a4eb39 move ImageAction parsing to a reusable function 2019-12-13 01:17:48 +01:00
B. Petersen
c3fd0889e2 use tri-state ImageAction instead of Option<Option> 2019-12-13 01:17:48 +01:00
B. Petersen
c62532a665 adapt to new HeaderDef api 2019-12-13 01:17:48 +01:00
B. Petersen
ca63d6ba1c handle incoming profile-images 2019-12-13 01:17:48 +01:00
B. Petersen
a1f496b019 let mimeparser handle profile-images 2019-12-13 01:17:48 +01:00
B. Petersen
da421438cd do not send selfavatar on existing installation without resetting 2019-12-13 01:17:48 +01:00
B. Petersen
7f723ef2bf attach selfavatar 2019-12-13 01:17:48 +01:00
B. Petersen
3cf39dace0 print profile-image in repl tool 2019-12-13 01:17:48 +01:00
B. Petersen
021ad4f12c set correct icon for DC_CONTACT_ID_DEVICE 2019-12-13 01:17:48 +01:00
B. Petersen
36edf447e7 load param incl. user-profile-image from db 2019-12-13 01:17:48 +01:00
B. Petersen
ea2273aef4 reset selfavatar_sent column when the selfavatar is updated 2019-12-13 01:17:48 +01:00
B. Petersen
251aa22c4c add column for selfavatar_sent timestamp 2019-12-13 01:17:48 +01:00
Alexander Krotov
541710147a Copy correct dc_contact_is_verified documentation from C core
instead of dc_contact_is_verified_ex documentation.
2019-12-11 17:54:28 +01:00
dignifiedquire
a197ca4cf7 feat: update dependencies, including rand@0.7 and rpgp@0.4 2019-12-11 15:33:17 +01:00
Simon Laux
775c36bb65 wip move some ci tasks to gh-actions (#998)
* add-action-rs-clippy-action

just to try this action out

* add --all argument

* rm --all argument again

* add cargo check and fmt

* disable circle ci counterparts(fetch, clippy, fmt)
2019-12-10 23:26:42 +01:00
holger krekel
1953b95c57 commit missing Cargo.lock 2019-12-10 21:42:52 +01:00
holger krekel
542a33952f prepare 1.0.0-beta.15 2019-12-10 21:35:20 +01:00
holger krekel
1819712667 make sure we send out <> around message-id's in references and in-reply-to 2019-12-10 21:25:36 +01:00
holger krekel
69a596fdff fix #994 -- avoid double-attachment messages in local chats 2019-12-10 21:25:36 +01:00
björn petersen
4f88b93495 Merge pull request #1003 from deltachat/flub-spec-their
Use 3rd person singular
2019-12-10 19:10:59 +01:00
Floris Bruynooghe
a388052e2a Use 3rd person singular 2019-12-10 19:07:22 +01:00
holger krekel
8b81ea3b6f actually update to 1.0.0-beta.14 2019-12-10 15:39:09 +01:00
holger krekel
b913de5928 prepare a 1.0.0-beta.14 with fixed deps 2019-12-10 14:42:55 +01:00
holger krekel
4e551cde66 update tags and rust-email dep 2019-12-10 14:17:16 +01:00
holger krekel
b681cbd47f prepare 1.0.0-beta.13 2019-12-10 14:02:21 +01:00
holger krekel
551253b4e0 use async-smtp with hostname fixed to version 0.1.5 2019-12-10 14:00:43 +01:00
Alexander Krotov
4ad9166b5a Move one chat test from stress.rs to chat module 2019-12-10 13:54:05 +01:00
holger krekel
8487255c33 prepare changelog for 1.0.0-beta.13 2019-12-10 13:52:57 +01:00
Alexander Krotov
2792d4ea1e Stop using create::error in login_param.rs 2019-12-10 12:39:58 +01:00
björn petersen
95180a850f Merge pull request #993 from deltachat/group_add_remove
Fix various issues around group add/remove
2019-12-10 12:14:52 +01:00
holger krekel
56ee7a0abd try to remove updates to lock file 2019-12-10 02:33:13 +01:00
holger krekel
d0a04be825 remove hack for decoding, and add a test that encoding/decoding works now 2019-12-10 01:35:09 +01:00
holger krekel
2cbf287998 don't cargo-update on CI jobs but use the Cargo.lock file we manually maintain/update 2019-12-10 00:40:18 +01:00
holger krekel
054cf98754 try to work around mailparser not decoding rfc2047 displaynames
this pulls in changes in our fork of rust-email  to also correctly
generate rfc2047 encoding
2019-12-10 00:26:46 +01:00
holger krekel
a95fbfe271 add a test 2019-12-09 22:28:29 +01:00
holger krekel
17ce02a87c add some comment and remove some code after quick a/v with @r10s 2019-12-09 21:45:01 +01:00
holger krekel
4dc5e0378f fix final problem, tests pass now 2019-12-09 20:25:46 +01:00
holger krekel
c33797ff84 remove reverse "check_self" return 2019-12-09 19:41:58 +01:00
holger krekel
f242b40d0a nicer print of imap capabilities 2019-12-09 18:36:26 +01:00
holger krekel
5f916f5a9c - create and use a ContactIds type as an ordered set instead of "Vec<u32>".
- recreate the group list more carefully, fixes #985

- resultify a few functions in the dc_receive pipeline

- don't quote displaynames in email-addresses, use utf8, preliminrarily addresses #976
2019-12-09 16:29:24 +01:00
holger krekel
6edb525540 snap 2019-12-09 14:16:46 +01:00
holger krekel
0c04d5b2ab add a failing test that creates 4 accounts and a group chat with remove/add
see also #985
2019-12-09 14:16:46 +01:00
Alexander Krotov
301852fd87 Add test for kebab_case serialization of From header 2019-12-09 15:43:06 +03:00
holger krekel
0e8df7d633 use automatic serialization, thanks @link2xt for the tip 2019-12-09 15:43:06 +03:00
holger krekel
e4155e0e16 remove lookup_field in favor of get(HeaderDef::...) with all headers defined in headerdef.rs 2019-12-09 15:43:06 +03:00
Floris Bruynooghe
2c4dbe6e68 Re-work some in-creation file handling
This effectively reverts
https://github.com/deltachat/deltachat-core-rust/pull/964 for chat.rs,
which in that PR was thought to fix something.  So maybe something is
still broken?  But after improving tests the previous code seems to be
correct.

- Update Python bindings to not always use dc_prepare_msg path when
  sending messages with attachements.  When using dc_prepare_msg the
  blobs need to be created in the blobdir since they will not get
  copied and many tests where not doing this.

- Add a test that ensures that calling dc_prepare_msg with a
  file **not** in the blobdir fails.

- Add a test that ensures that calling dc_send_msg directly with a
  file **not** in the blobdir copies the file to the blobdir.  This
  test cheats a little by knowing what the filename in the blobdir
  will be which is implementation-dependent and thus a bit brittle.
  But for now it proves correct behaviour so let's go with this.

- Improve the test_forward_increation test to ensure that the
  in-creation file only has it's final state before calling
  dc_send_msg.  This checks the correct file data is sent out and not
  the preparing data, this fails with the chat.rs changes in
  #964 (reverted here to make this work again).  Also fix the test to
  actually create the in-creation file in the blobdir.

- Fix test_send_file_twice_unicode_filename_mangling to not use
  in-creation.  It was not creating it's files in the blobdir and that
  is an error when using in-creation and it didn't seem it was trying
  to test something about the in-creation logic (which is tested in
  test_increation.py already).

- Fix Message._msgtate code which presumably was not used before?

- Rename `BlobObject::create_from_path` to
  `BlobObject::new_from_path`.  All the `BlobObject::create*` calls
  now always create new files which is much more consistent.  APIs
  should do what is obious.
2019-12-09 10:30:57 +01:00
Alexander Krotov
a99b96e36e Use bool for from_id_blocked flag 2019-12-09 09:54:46 +01:00
Alexander Krotov
0889467c7b Use bool for hidden flag 2019-12-09 09:54:46 +01:00
Alexander Krotov
d141e228de Use bool for incoming flag 2019-12-09 09:54:46 +01:00
Alexander Krotov
9b15c42801 Use bool for allow_creation flag 2019-12-09 09:54:46 +01:00
Alexander Krotov
a781b631e1 Return bool from is_reply_to_{known_message,messenger_message} 2019-12-09 09:54:46 +01:00
Alexander Krotov
08af5c8e09 Replace some integers with bools in dc_receive_imf.rs 2019-12-09 09:54:46 +01:00
holger krekel
93e8cca02f remove redundant state from smtp 2019-12-08 23:04:08 +01:00
Alexander Krotov
a8e9a1fbe5 Make Smtp.send async 2019-12-08 21:00:03 +01:00
Alexander Krotov
54eb30f3db Switch from lettre to async-smtp 2019-12-08 21:00:03 +01:00
Alexander Krotov
c08a1adc9b Deprecate AcceptInvalidHostnames option
Rustls does not offer a documented way to accept valid certificates with
invalid hostnames. Implementation of certificate verification in Rustls
does not have a public API and reimplementing it is error-prone.
2019-12-08 20:54:04 +01:00
Alexander Krotov
cd951ad396 Improve IMAP idle timeout error handling
Return proper cause and avoid matching on nested Result
2019-12-08 20:50:27 +01:00
holger krekel
33793d878b address @link2xt comment on done/async 2019-12-08 01:13:00 +01:00
holger krekel
357955015d [wip] initial detection of idle done() not working 2019-12-08 01:13:00 +01:00
holger krekel
d6d94adab0 remove encrypted attr from mimeparser. 2019-12-08 00:47:46 +01:00
holger krekel
099cc9f727 remove subject attribute 2019-12-08 00:47:46 +01:00
holger krekel
61f8d6f171 remove is_send_by_messenger attr from mimeparser 2019-12-08 00:47:46 +01:00
Alexander Krotov
a7af4685f1 Log IMAP certificate checks configuration 2019-12-08 00:31:22 +01:00
Alexander Krotov
2cebed4f77 Move dc_array.rs to deltachat-ffi package 2019-12-08 00:30:43 +01:00
holger krekel
3f2a371599 run lint without installing the package 2019-12-07 22:56:54 +01:00
holger krekel
a3ca3d9179 allow to use a different buildhost :) 2019-12-07 22:56:54 +01:00
holger krekel
86ace1a4af - test and fix receiving text/html attachment in multipart/mixed situations
They are now preserved as attachment, instead of diving into parsing-html
  and simplifying.

- adapt mime-debugging
2019-12-07 22:56:54 +01:00
holger krekel
1abdf62045 passes test but needs cleanup 2019-12-07 22:56:54 +01:00
holger krekel
9e2a96675d try fix incoming text/html in multipart/mixed 2019-12-07 22:56:54 +01:00
Alexander Krotov
f32876708a Update outdated comment
MIME wrapping is now done by the email crate, which implements RFC5322
2019-12-07 21:35:42 +01:00
Alexander Krotov
84e0e7e020 aheader.rs: do not generate lines longer than 78 characters 2019-12-07 21:35:42 +01:00
Alexander Krotov
38cddff6e9 Move keydata= wrapping from key.rs to aheader.rs 2019-12-07 21:35:42 +01:00
Alexander Krotov
f1aba8115b Use Option<EncryptPreference>.unwrap_or_default in aheader.rs 2019-12-07 07:58:53 +01:00
B. Petersen
0db5ff55fd unshape icons, used indexed colors 2019-12-06 20:10:19 +01:00
B. Petersen
7421b0de6c adapt the ffi-examples to reality, free stuff 2019-12-06 20:08:57 +01:00
Alexander Krotov
6c275c30a7 Do not include "prefer-encrypt=nopreference;" in Autocrypt header
Specification
https://autocrypt.org/level1.html#the-autocrypt-header
says that prefer-encrypt attribute can only occur with the value `mutual`.
2019-12-06 20:01:34 +03:00
Alexander Krotov
91f1d793e9 Test that internal "prefer-encrypt=reset" value is not parsed from header
This is a test for bugfix e8f2f7b24e
2019-12-06 14:23:45 +01:00
Alexander Krotov
cddfd04a7f Turn rawkey from function into a constant 2019-12-06 14:23:45 +01:00
Alexander Krotov
7fa94c33bf cargo fmt 2019-12-06 10:35:02 +01:00
Alexander Krotov
30dd20dc7b Make Imap.fetch() async 2019-12-06 10:35:02 +01:00
Alexander Krotov
339c0d3dc7 Remove unnecessary use of failure::Fail 2019-12-06 10:34:34 +01:00
Alexander Krotov
2304d63bb3 Simplify dc_get_filesuffix_lc 2019-12-06 09:45:24 +01:00
holger krekel
36014f9fe5 add next changelog entry 2019-12-06 09:00:23 +01:00
Alexander Krotov
351383dfa4 Split IMAP idle functions into imap::idle submodule
Also introduce Imap.can_idle() to avoid having to match on IdleAbilityMissing error
2019-12-06 08:59:22 +01:00
holger krekel
defad94f5a bump to beta12 2019-12-06 00:55:24 +01:00
holger krekel
a0517478e3 adding auto-copy-blob logic when preparing a message 2019-12-06 00:27:22 +01:00
holger krekel
7f4e68f21c failing test for sending out a file twice 2019-12-06 00:27:22 +01:00
holger krekel
c0a425c26d bump to beta11 2019-12-05 21:53:03 +01:00
holger krekel
e756859b16 don't split qr tests out anymore now 2019-12-05 21:50:17 +01:00
holger krekel
174bc017ad make setup_handle_if_needed async, call it ahead of select_folder and fetch_new_messages (renamed from fetch_from_single_folder), and be more eager triggering reconnect on error conditions 2019-12-05 21:47:32 +01:00
holger krekel
4a9fb0212f complete changelog and bump version 2019-12-05 19:32:16 +01:00
holger krekel
212848409f use encoded-words crate, which friedel ported from python 2019-12-05 19:29:12 +01:00
holger krekel
e45ee0eb81 try fix filename encoding bug -- fails in one test 2019-12-05 19:29:12 +01:00
dignifiedquire
3b8e37de58 switch to quoted-printable, which is already used by mailparse 2019-12-05 19:29:12 +01:00
holger krekel
c2e8cc9bd6 use rfc2047 crate from @valodim and remove dc_strencode.rs completely 2019-12-05 19:29:12 +01:00
holger krekel
ec81d29580 fix multi-line subject encoding and introduce MIME debugging env var 2019-12-05 19:29:12 +01:00
jikstra
c4de0f3b17 Apply remaining requested changes 2019-12-05 18:57:19 +01:00
jikstra
965d41990e change return to "" for errors 2019-12-05 18:57:19 +01:00
holger krekel
d96dba336b Update deltachat-ffi/deltachat.h
fix doc
2019-12-05 18:57:19 +01:00
jikstra
a7e1b4653e Apply requested changes 2019-12-05 18:57:19 +01:00
jikstra
e38b42bc21 Add api docu 2019-12-05 18:57:19 +01:00
jikstra
36bd502292 cargo fmt 2019-12-05 18:57:19 +01:00
jikstra
594bf3dfc8 fixup! Move to json_serde, add tests and implement missing python api 2019-12-05 18:57:19 +01:00
jikstra
6d30ccfc63 Move to json_serde, add tests and implement missing python api 2019-12-05 18:57:19 +01:00
jikstra
1b79f513a3 Implement more json key/value pairs 2019-12-05 18:57:19 +01:00
holger krekel
74825a0f57 working example 2019-12-05 18:57:19 +01:00
holger krekel
4a23d12df2 add a ffi-definiton for a new get-chat summary function that returns json 2019-12-05 18:57:19 +01:00
holger krekel
7f117574ab test and fix #956 2019-12-05 02:15:54 +01:00
holger krekel
f91474c2f8 add preliminary changelog for beta10 2019-12-05 01:22:50 +01:00
holger krekel
2a081aac2b fix grpid extraction from In-Reply-To and References headers 2019-12-05 01:18:53 +01:00
holger krekel
9b10f31fb3 more cleanups 2019-12-05 00:56:09 +01:00
holger krekel
63ad7b8d34 make to_ids const in some places, and simplify returns from create_or_lookup_adhoc_group 2019-12-05 00:56:09 +01:00
holger krekel
86baaab2e9 get rid of unsafe and indirect return values for create_or_lookup.*group 2019-12-05 00:56:09 +01:00
holger krekel
3e66d23367 make set_core_version return the versions if no args are specified 2019-12-04 22:32:56 +01:00
dignifiedquire
609b5588fa fix(mimefactory): only send Autocrypt-Gossip headers on encrypted messages 2019-12-04 13:58:33 +01:00
pabzm
798072b8ba Fix new event name in Changelog. 2019-12-04 13:52:02 +01:00
Alexander Krotov
d950a58613 Improve documentation 2019-12-04 10:15:40 +01:00
holger krekel
4c68e6fe41 update deps 2019-12-04 09:11:21 +01:00
holger krekel
914ce77b50 adapt changelog 2019-12-04 08:58:48 +01:00
holger krekel
d09989a4ed fix implementation of Autocrypt to encrypt if answering to an encrypted message.
Previously, if any of a chat's peers set prefer_encrypt to false
(i.e. "e2ee_enabled=0" in the config, a misnomer btw) then a
previously encrypted chat would drop to cleartext easily.
2019-12-04 08:57:42 +01:00
holger krekel
6999a4e3a9 streamline decrypt/encrypt logging 2019-12-04 08:57:42 +01:00
Alexander Krotov
069541f374 Create select_folder::Error 2019-12-04 06:13:24 +01:00
Alexander Krotov
49b9b28c99 Move select_folder to a separate submodule 2019-12-04 06:13:24 +01:00
B. Petersen
b274482125 add welcome-image created by @Simon-Laux 2019-12-04 06:11:42 +01:00
B. Petersen
b766a55b0a in set_chat_name(), bind value to sql using ?-operator, add a test to check the name is really changed 2019-12-04 06:11:01 +01:00
B. Petersen
b8057b7ddb remove >1y old file path migration which is now uneeded and has potential issues 2019-12-04 06:11:01 +01:00
B. Petersen
98266f47d6 bind value to sql using ?-operator 2019-12-04 06:11:01 +01:00
B. Petersen
7b83979e10 test that the device-chat is a one-to-one-chat 2019-12-04 06:01:37 +01:00
B. Petersen
79cdcca2d2 check that sending/preparing/forwarding on a chat that cannot send really fails 2019-12-04 06:01:37 +01:00
B. Petersen
4c0d00640b fix repl forward command 2019-12-04 06:01:37 +01:00
Alexander Krotov
ad87b7c4a5 Fix clippy warnings 2019-12-03 20:20:52 +03:00
holger krekel
74f36b264b fix get_recipients with test 2019-12-03 17:51:44 +01:00
holger krekel
2df43b6c5d avoids double-semicolon problem leading to empty messages in pre-master releases 2019-12-03 16:55:53 +01:00
holger krekel
73f80624b2 adding a draft changelog entry for beta.9 2019-12-03 14:48:57 +01:00
björn petersen
fe2a4b7a4a Merge pull request #904 from deltachat/mailparse
Switch mailparsing to mailparse
2019-12-03 14:16:07 +01:00
dignifiedquire
affdf7241f fixup: remove double mimefactory 2019-12-03 12:26:27 +01:00
holger krekel
1c5a3e6698 remove debug logging and fix update_gossip_headers location 2019-12-03 12:25:22 +01:00
dignifiedquire
db88212a64 refactor: unsafe, CStr and libc moved out 2019-12-03 12:25:22 +01:00
dignifiedquire
43074464ac cleanup mimeparser and use mime 2019-12-03 12:25:22 +01:00
dignifiedquire
1e7afa9da0 fix chat-verified 2019-12-03 12:25:22 +01:00
dignifiedquire
e985887739 fix content-type setting and some encryption 2019-12-03 12:25:22 +01:00
dignifiedquire
d5287256e0 fix various parsing and sending issues 2019-12-03 12:25:22 +01:00
dignifiedquire
6bb2adaf45 some fixes for mdn handling 2019-12-03 12:25:22 +01:00
dignifiedquire
64fcd56998 fix mailparsing test 2019-12-03 12:25:22 +01:00
dignifiedquire
7d3a56a870 switch to forked rust-email 2019-12-03 12:25:22 +01:00
dignifiedquire
48dd3b8506 less dead code, and update test 2019-12-03 12:25:22 +01:00
dignifiedquire
734bbff04d cleanup and finish missing parts 2019-12-03 12:25:22 +01:00
dignifiedquire
5adde12dff implement mdn rendering 2019-12-03 12:25:22 +01:00
dignifiedquire
5079e15401 restructure mimefactory 2019-12-03 12:25:22 +01:00
dignifiedquire
9196ed1e9f remove dead code 2019-12-03 12:22:55 +01:00
dignifiedquire
c69a2406ad integrate encryption when sending 2019-12-03 12:22:55 +01:00
dignifiedquire
47197aa495 Implement message generation using lettre_email 2019-12-03 12:22:55 +01:00
dignifiedquire
dbd6303829 remove mmime 2019-12-03 12:22:55 +01:00
dignifiedquire
1f8d2531cc some things 2019-12-03 12:22:55 +01:00
dignifiedquire
d2de2aef07 first compile 2019-12-03 12:22:55 +01:00
Alexander Krotov
e22b4e8430 Use map_err in Smtp.connect() instead of match 2019-12-03 13:35:55 +03:00
björn petersen
af0b7e4701 Merge pull request #941 from deltachat/calc-device-msg-dimensions
calculate image-size in device-messages
2019-12-03 11:10:02 +01:00
Alexander Krotov
acef386403 Remove useless comment from the configure module 2019-12-03 06:22:05 +00:00
Alexander Krotov
15ba9b6295 Resultify outlk_autodiscover 2019-12-03 06:22:05 +00:00
Alexander Krotov
2f47cf0be5 Resultify moz_autoconfigure 2019-12-03 06:22:05 +00:00
Alexander Krotov
774106fc26 Move read_autoconf_file to its own module 2019-12-03 06:22:05 +00:00
Alexander Krotov
1b0ff9f5be Make parse_xml in auto_mozilla.rs private 2019-12-03 06:22:05 +00:00
Alexander Krotov
dfaa6895ae Add an error type to configure::auto_outlook module 2019-12-03 06:22:05 +00:00
Alexander Krotov
cb52a299cc Add an error type to configure::auto_mozilla module 2019-12-03 06:22:05 +00:00
B. Petersen
0ac0851ef3 add empty lines to stock-strings-enum 2019-12-03 06:21:37 +00:00
B. Petersen
c0f177548a add empty lines to configure-enum 2019-12-03 06:21:37 +00:00
B. Petersen
8923c9e5af calculate image-size in device-messages
eg. android relies on that and fallsback to wrong aspect ratios otherwise.
also, this makes job.rs a little more readable
as some things are moved to message.rs :)
2019-12-03 00:58:04 +01:00
björn petersen
e609ebe3f9 Merge pull request #939 from deltachat/update-device-chats
and a function to create & update device chats
2019-12-03 00:52:52 +01:00
B. Petersen
8617c3d359 target comments of @hpk42, format strum manually as skipped by 'cargo fmt' for some reason 2019-12-02 18:52:07 +01:00
B. Petersen
61e97e5e6f add updatedevicechats command to repl tool 2019-12-02 16:33:24 +01:00
B. Petersen
521e44bd54 add ffi for update_device_chats() 2019-12-02 16:33:24 +01:00
B. Petersen
18e91c480b add tests for update_device_chats() 2019-12-02 16:33:24 +01:00
B. Petersen
9d2e6e1c31 add update_device_chats() 2019-12-02 16:33:24 +01:00
dignifiedquire
eb02100d68 fix(imap): merge error conversions properly 2019-12-01 21:40:56 +01:00
Alexander Krotov
ee18d60644 cargo fmt 2019-12-01 21:01:03 +01:00
Alexander Krotov
612600278a Move SQL errors into their own module 2019-12-01 21:01:03 +01:00
holger krekel
ea8adf39c2 address @link2xt comment 2019-12-01 20:50:31 +01:00
holger krekel
f7f61e0f85 move imap errors into imap module 2019-12-01 20:50:31 +01:00
Alexander Krotov
a7bb249070 Move another OAuth2 test from stress.rs to oauth2.rs 2019-12-01 20:46:35 +01:00
björn petersen
dfe7dfcfd3 Merge pull request #935 from deltachat/address_idle_race
address last(?) idle race condition heuristically
2019-12-01 19:26:40 +01:00
Alexander Krotov
7223a36a71 SMTP error refactoring 2019-12-01 18:24:26 +01:00
holger krekel
2423d197cd better logging, changed timeout 2019-12-01 18:12:10 +01:00
holger krekel
2582791501 address #925 heuristically 2019-12-01 18:12:10 +01:00
Alexander Krotov
3a08c92433 Replace u32 with a PeerstateKeyType enum 2019-12-01 17:07:33 +01:00
Alexander Krotov
694d8fd6fb Move dc_dehtml to dehtml and remove unnecessary is_empty check 2019-12-01 13:37:37 +01:00
Asiel Díaz Benítez
369bb9166e Update message.py
use `email.message_from_bytes()` instead of decoding the bytes to then call `message_from_string`
2019-12-01 12:08:34 +01:00
björn petersen
5f1e5ef206 Merge pull request #933 from deltachat/refactor-icons
Refactor icons
2019-12-01 12:06:20 +01:00
jikstra
5d04fd0d97 color tweaking 2019-12-01 02:46:04 +01:00
jikstra
d05b5e40df add new icon-device icons 2019-12-01 02:31:12 +01:00
jikstra
0dd436ac54 Add new icon-saved-messages 2019-12-01 02:20:01 +01:00
Alexander Krotov
d5359fb9ba Ensure that Peerstate has an address set on the type level 2019-12-01 01:52:54 +01:00
B. Petersen
72c29aca6a do not create device-chat on skipped messages 2019-12-01 01:38:50 +01:00
B. Petersen
32216a334d add a test that checks the device-chat is not recreated for messages that won't be added 2019-12-01 01:38:50 +01:00
holger krekel
603d55114b address @link2xt error comment 2019-11-30 23:53:35 +01:00
holger krekel
9d18db9cae introduce a select_with_uidvalidity function that helps us during configuration to know about last_seen_uid 2019-11-30 23:53:35 +01:00
holger krekel
d14c6ea202 refine uid_next handling and rename and resultify configure_folder to ensure_configurer_folders 2019-11-30 23:53:35 +01:00
holger krekel
7be5fe925a revert logic to get last_seen_uid 2019-11-30 23:53:35 +01:00
holger krekel
8f43d7fa34 remove commented errors and fix fmt 2019-11-30 23:53:35 +01:00
holger krekel
b6e9bcee3c when first looking at a folder, look at "uid_next" as returned from server
and otherwise properly fetch the last messages to determine the last seen uid.
also add some tracing.
2019-11-30 23:53:35 +01:00
holger krekel
2dbef704e9 rework select_folder error handling (thanks @flub and @link2xt for helping along) 2019-11-30 23:53:35 +01:00
Floris Bruynooghe
74a4691f29 Convert BlobError into an enum
This deletes a lot of code and complexity.  Though comes at some cost:

- The type no longer fits in a register and will always be on the
  stack.

- Constructing the errors is more verbose, no more auto Into casting.
2019-11-30 23:51:30 +01:00
B. Petersen
084a87ed61 add chatlist tests 2019-11-30 19:38:22 +01:00
B. Petersen
657b53ae0b sort drafts again to the top of the chatlist
this reverts the logical changes done in #811
but keeps the improvements done later eg. in #911.
the reason for the revert is that it is too hard to
find a started draft in a larget chatlist.
also the shown date would not be just descending.
2019-11-30 19:38:22 +01:00
Alexander Krotov
17cb1226c6 Move OAuth 2 stress tests to oauth2 module 2019-11-30 19:19:05 +01:00
Alexander Krotov
02e281e465 Resultify dc_write_file and related functions 2019-11-30 01:54:42 +01:00
B. Petersen
4e6d0c9c69 fix places where string-cmp was used instead of addr_cmp() for email-address-comparison 2019-11-30 01:49:45 +01:00
B. Petersen
81d069209c add some tests for addr_cmp() 2019-11-30 01:49:45 +01:00
B. Petersen
63e3c82f9d compare email-addresses case-insesitive, use this comparison also to check for SELF 2019-11-30 01:49:45 +01:00
Alexander Krotov
e8f2f7b24e Do not accept prefer-encrypt=reset value from emails
Reset is an internal value that received messages should not be able to set.

Also return an error on any value other than "mutual" and "nopreference", errors are converted to NoPreference by the caller.
2019-11-30 01:40:33 +01:00
Alexander Krotov
b7f7e607c1 Use map_err 2019-11-30 01:37:56 +01:00
Alexander Krotov
ac4108b05b Mark error cause as such
See failure crate documentation.
2019-11-30 01:37:56 +01:00
Alexander Krotov
14287b12ae Resultify Smtp::send 2019-11-30 01:37:56 +01:00
Alexander Krotov
20ce5f6967 Ignore .rsynclist 2019-11-30 01:32:51 +01:00
Floris Bruynooghe
1a296cbd4e Don't let the user wait for so long 2019-11-30 01:11:15 +01:00
Floris Bruynooghe
642276c90c Attempt to fix race in securejoin handling
The ongoing process of dc_join_securejoin() was stopped before the
corresponding chat was created.  This resulted in a race-condition
between the sqlite threads executing the creation and query
statements, thus sometimes the query would not find the group and
mysteriously fail.

Tripple-programming with hpk & r10s.
2019-11-30 00:48:14 +01:00
björn petersen
e4b2fd87de Merge pull request #911 from deltachat/draft-sorting
sort newly created chats atop of chatlist
2019-11-29 15:11:06 +01:00
Alexander Krotov
dacde72456 Respect CertificateChecks in IMAP Client::secure 2019-11-29 00:40:50 +01:00
Alexander Krotov
7e66af05ff Calculate job backoff relative to the current time
Otherwise it is possible that desired_timestamp is in the past.
2019-11-29 00:26:25 +01:00
B. Petersen
b6bb5b79af target comments of @flub 2019-11-28 23:56:12 +01:00
Floris Bruynooghe
f0486eb820 Skip bad jobs in the database
Be more defensive: if somehow we got corrupt jobs in the database skip
over them rather than fail to do anything.

This only modifies the query_map() call, the rest is only split off
into it's own function to make it testable.  Smaller functions are
good anyway.
2019-11-28 23:41:47 +01:00
B. Petersen
cdc2847b96 use created_timestamp as secondary sort criterion for chatlists 2019-11-28 22:38:53 +01:00
B. Petersen
1d996d9ed9 track created_timetamp for chats 2019-11-28 22:38:48 +01:00
Alexander Krotov
e06ac87c0d Convert the number of retries to u32 2019-11-28 21:44:16 +01:00
Alexander Krotov
0c19fcd79f Use Rust for instead of while 2019-11-28 21:44:16 +01:00
Alexander Krotov
02fe3d1b99 Process PerformJobsNeeded::AvoidDos case explicitly 2019-11-28 21:44:16 +01:00
Alexander Krotov
95b90a59dc Add PerformJobsNeeded enum (DC_JOBS_NEEDED_* in core) 2019-11-28 21:44:16 +01:00
Alexander Krotov
fe7852b64e Restore JOB_RETRIES constant 2019-11-28 21:44:16 +01:00
holger krekel
f87cb4231c rename jobs to make "rg Markseen" and other searches for the job enum produce all places dealing with the enum 2019-11-28 19:42:24 +01:00
B. Petersen
7484fb6120 remove boilderplate from sql-statements, see #852 2019-11-28 19:29:44 +01:00
Alexander Krotov
430d4e5f6e Replace DC_CREATE_MVBOX with boolean 2019-11-28 18:24:29 +01:00
holger krekel
6f6791c1b5 (flub/r10s/hpk42) remove MoveState and related functions because we don't actually need it anymore 2019-11-28 15:57:26 +01:00
holger krekel
bc11ae7245 add a test for bcc-self sent messages getting moved 2019-11-28 15:57:26 +01:00
B. Petersen
de228bdb4b also move messages sent to ourselves via bcc_self to the DeltaChat folder (other messages are moved in receive_imf()) 2019-11-28 15:57:26 +01:00
holger krekel
25fb199ba0 update to current async-imap master which should fix a crash 2019-11-28 13:04:37 +01:00
Alexander Krotov
d0795f5770 Automatically fix some clippy warnings with "cargo fix" 2019-11-27 21:43:18 +01:00
holger krekel
fbb8c8e80c be less verbose when generating python docs 2019-11-27 13:41:08 +01:00
holger krekel
76610f1e72 rename to more specific name, also not using the difficult to type "succeeded" word :) 2019-11-27 13:02:07 +01:00
holger krekel
618d74cd67 safer interruptability of fake-idle -- reusing the same skip_next_idle pattern as with idle 2019-11-27 05:52:14 +01:00
Alexander Krotov
59700cb477 Restore peerstate constants from C core 2019-11-27 05:41:54 +01:00
Floris Bruynooghe
fc1a136448 Remove unused (async)Arc
This identical naming of sync and async arcs is not confusing at all
btw.
2019-11-27 05:39:36 +01:00
holger krekel
42ef43bdf6 test the new event 2019-11-27 00:43:50 +01:00
B. Petersen
f53b3c2e7b add DC_EVENT_SECUREJOIN_SUCCEEDED 2019-11-27 00:43:50 +01:00
Alexander Krotov
22a0e3fe9c job: try_again refactoring
Introduce TryAgain type with C core constants
Make try_again field and method private.
Remove try_again == 2 processing, it was never used.
2019-11-26 22:14:34 +01:00
Floris Bruynooghe
e0601bab3d Leave the avatar in place if it already is in the blobdir 2019-11-25 12:59:55 +01:00
Floris Bruynooghe
69369b02ea Copy Selfavatar to blobdir and store as proper blob
When a new Selfavatar is set the file needs to be copied into the
blobdir and as a proper blob it should be stored in the database as
$BLOBDIR/$filename.

This should fix #868.
2019-11-25 12:59:55 +01:00
B. Petersen
966b9fac49 fix cast to libc::c_char, signed i8 fails eg. on android 2019-11-24 18:12:42 +01:00
holger krekel
e2a86dd803 prepare beta8 2019-11-24 14:41:25 +01:00
holger krekel
1b88cfd053 as recent fixes are merged to async-imap, let's use that cc @link2xt 2019-11-24 16:30:49 +03:00
Alexander Krotov
6412adcfa7 Use forked version of async-imap and imap-proto
This attempts to fix fetching when the inbox contains mail with non-utf8 subjects.
2019-11-24 12:05:30 +01:00
holger krekel
76de8f55f2 make ssh fail directly if a password is asked 2019-11-24 01:05:33 +01:00
holger krekel
ae43b83162 random change to test PR 2019-11-24 01:05:33 +01:00
Alexander Krotov
8d81c8c1e0 Do not crash on messages without message-id 2019-11-23 22:52:16 +01:00
Alexander Krotov
1e173524b5 Upgrade to hex 0.4
rpgp depends on hex 0.4. This prevents building two versions of the same library.
2019-11-23 22:51:27 +01:00
B. Petersen
490418d359 add icon source files 2019-11-22 16:27:54 +01:00
B. Petersen
51ef4d82f9 use flatter design and higher resolution for icons 2019-11-22 16:27:54 +01:00
holger krekel
e522920d49 fix the check for not installing bindings (used from CI), and refined rerun policy 2019-11-22 13:54:30 +01:00
holger krekel
47be554aff adapt README, comment previous python tests out 2019-11-22 12:28:19 +01:00
holger krekel
f44b2a63b0 revert back to split doc and lint 2019-11-22 12:28:19 +01:00
holger krekel
8d4b893658 fix missing renames and tox dep 2019-11-22 12:28:19 +01:00
holger krekel
7e3c61eb41 address @dignifiedquire err.to_string() comment and avoid extra virtualenv building 2019-11-22 12:28:19 +01:00
holger krekel
bb396685ab some comments fix imap->inbox naming in example 2019-11-22 12:28:19 +01:00
holger krekel
8ef0ea8aea simplify double-fetching 2019-11-22 12:28:19 +01:00
holger krekel
34c766dc2b merge JobThread::connect_to_imap with Imap::connect_configured for simplicity 2019-11-22 12:28:19 +01:00
holger krekel
a30fa710ad resultify fetch and simplify fake_idle 2019-11-22 12:28:19 +01:00
holger krekel
fa01884350 proper handling of IdleResponse codes 2019-11-22 12:28:19 +01:00
holger krekel
eae9ad6f8b remove context.inbox in favour of a context.inbox_thread following the mvbox_thread and sentbox_thread patterns. Also some streamlining of shutdown logic. 2019-11-22 12:28:19 +01:00
holger krekel
be533fa66a resultify some imap operations 2019-11-22 12:28:19 +01:00
holger krekel
254b061921 update docs and add a simple manual script to run python/rust tests 2019-11-22 12:28:19 +01:00
holger krekel
cefa03f45b run doxygen for core docs again 2019-11-22 12:28:19 +01:00
holger krekel
b67203b421 use the reponame in dir builddir to we can distinguish from desktop/android etc 2019-11-22 12:28:19 +01:00
holger krekel
e14c4d0683 re-enable cross, some streamlining of docs 2019-11-22 12:28:19 +01:00
holger krekel
4ed96b16f4 simplify/speedup python tests 2019-11-22 12:28:19 +01:00
holger krekel
932c86bb3b various cleanups, better parallelism and build-dir structure 2019-11-22 12:28:19 +01:00
holger krekel
590fd53dd4 don't invoke py36/py35 anymore here. 2019-11-22 12:28:19 +01:00
holger krekel
8e7dc5e86f reconfigure running of rust and python tests 2019-11-22 12:28:19 +01:00
holger krekel
e13ce3140b introduce a trigger_reconnect helper 2019-11-22 12:28:19 +01:00
holger krekel
2ebb43b613 update deps, in particular async-imap to 0.1.1 which contains
the fix by @dignifiedquire that is expected to fix #829
2019-11-22 12:28:19 +01:00
holger krekel
863a70b8fc new clean try to get circle-ci to work, disable and move gh actions to ci_scripts folder 2019-11-22 12:28:19 +01:00
dignifiedquire
89a4b6fee5 update github actions 2019-11-22 12:28:19 +01:00
holger krekel
0405c945e2 shortcut fetch/idle on mvbox/sentbox if we don't know the folder and prevent busy-looping 2019-11-22 12:28:19 +01:00
holger krekel
5293ea70ae steramline some teardown decision code, and add webpki_roots for cert-checking 2019-11-22 12:28:19 +01:00
holger krekel
b5cbc97333 also make smtp respect CertificateChecks setting roughly 2019-11-22 12:28:19 +01:00
holger krekel
a867452927 rough integration of async-tls CertChecks (strict and automatic but not more finegrained work) 2019-11-22 12:28:19 +01:00
dignifiedquire
b13ca0d077 update to released versions 2019-11-22 12:28:19 +01:00
holger krekel
c6f4d6d8bd * fix interrupt_idle by signalling "skip_next_idle_wait" to the potentially concurrently "fn idle" function
* fixes double-export issue
2019-11-22 12:28:19 +01:00
holger krekel
8723aa097e make select_folder return ImapActionResult's and early-return from idle if there is no selected folder 2019-11-22 12:28:19 +01:00
dignifiedquire
97e6bc2be3 bust ci cache, update deps, use a different rust version, remove rustup install 2019-11-22 12:28:19 +01:00
dignifiedquire
2c2555fad9 refactor: drop native-tls 2019-11-22 12:28:19 +01:00
dignifiedquire
f4f69a030a update docker image 2019-11-22 12:28:19 +01:00
dignifiedquire
86f66f4d78 cleanup imap impl 2019-11-22 12:28:19 +01:00
dignifiedquire
b4e2b69086 update async-imap 2019-11-22 12:28:19 +01:00
dignifiedquire
1a1a59a14e implement idle again 2019-11-22 12:28:19 +01:00
dignifiedquire
1687e8d26f it compiles with async-imap, remove local dependency 2019-11-22 12:28:19 +01:00
Alexander Krotov
4b8252e001 Implement public key selection
First, try to use subkeys, because they are usually
short-term encryption keys. If none of the subkeys
are encryption keys, try to use the primary key.

rPGP is updated to the master branch because the
latest release does not have .is_encryption_key() yet.
2019-11-22 10:20:40 +01:00
Floris Bruynooghe
f505ff03e4 Do not break double file extensions
Double extensions are sometimes used to identify files correctly,
e.g. .tar.gz or .html.zip.  Breaking those extensions is not very
nice.

This fixes #865.
2019-11-21 22:18:31 +01:00
björn petersen
ff8e282c43 Merge pull request #862 from deltachat/fix-bcc-self
mark messages as sent
2019-11-20 15:28:40 +01:00
B. Petersen
e39cb160f9 make messages as sent in groups with only SELF and bcc_self disabled 2019-11-20 02:48:43 +01:00
björn petersen
76ce0c3540 Merge pull request #856 from deltachat/fix-time-smearing
fix time smearing
2019-11-19 23:25:11 +01:00
B. Petersen
f7047bbf51 add detailed comments about time-smearing 2019-11-19 22:52:24 +01:00
B. Petersen
c3a53cefb0 fix time smearing
if two messages have the same time,
this results i all kinds of sorting failures,
esp. in non-delta-muas and on forwarding messages.
so, no two messages sent out from delta have the same timestamp.
normally, this is no problem, but when things are sent too fast,
eg. on forwarding, we lend us some time from the future.

however, all this did not work
because we forgot to write back the modified time,
this is fixed by this commit, as well as some cleanup.
2019-11-19 22:23:01 +01:00
björn petersen
a88153954e Merge pull request #854 from deltachat/tweak-device-msg
tweak device messages
2019-11-19 22:19:42 +01:00
Alexander Krotov
4732085421 Fix some clippy warnings 2019-11-19 19:52:13 +01:00
B. Petersen
e4e6a00fe4 rename has_device_msg() to was_device_msg_ever_added() what describes better what the function is doing 2019-11-19 13:21:02 +01:00
B. Petersen
700e10bc0e use dc_add_device_msg() also to add labels-only, this is clearer as having a function with 'skip' in the name 2019-11-19 13:05:01 +01:00
B. Petersen
2a4c193601 simplify check for existing device-message 2019-11-19 11:56:35 +01:00
B. Petersen
d3fa289f27 streamline dc_add_device_msg_once|unlabelled() to one function 2019-11-19 11:49:02 +01:00
B. Petersen
1534a07ded add dc_has_device_msg() 2019-11-18 17:31:03 +01:00
B. Petersen
a4e92694ba name unlabelled device-messages as such 2019-11-18 16:47:18 +01:00
B. Petersen
035da9e5d7 add dc_skip_device_msg() and belonging tests 2019-11-18 16:47:18 +01:00
B. Petersen
fed6f4ab8a do not re-add device-messages deleted by the user 2019-11-18 16:47:18 +01:00
Nico de Haen
d7c42f3c98 Use coalesce for optional columns
resolves #851
2019-11-17 20:57:50 +01:00
B. Petersen
ad852161f3 higher resolution for device-icon 2019-11-17 20:56:42 +01:00
B. Petersen
e9b2d6a594 adapt tests to saved-messages chat 2019-11-17 20:56:07 +01:00
B. Petersen
f9d2fe710d rename me-chat to saved-messages, use an appropriate icon 2019-11-17 20:56:07 +01:00
björn petersen
a4a1cd42db Merge pull request #844 from deltachat/move-location-to-trash
Delete msg related POI when message is deleted
2019-11-16 17:40:53 +01:00
Alexander Krotov
69e14dcb2d Replace some magic numbers with constants 2019-11-16 16:08:08 +00:00
Nico de Haen
e2673894b4 Delete message only if its a POI 2019-11-16 16:55:13 +01:00
B. Petersen
852b16c972 target comment of @flub 2019-11-16 13:55:59 +01:00
B. Petersen
5796c28391 test contact adding and modifying 2019-11-16 13:55:59 +01:00
holger krekel
b6095e29d7 Update src/pgp.rs 2019-11-16 13:53:47 +01:00
Alexander Krotov
f778957caf Improve documentation and comments 2019-11-16 13:53:47 +01:00
Alexander Krotov
47f8da6532 Expand tabs into spaces 2019-11-16 12:32:32 +01:00
Alexander Krotov
0b3f2a55df Do not use wildcard match in msgtype_has_file
This way there will be a compiler error notifying that the function needs to be updated when a new message type is added.
2019-11-16 12:31:13 +01:00
Nico de Haen
13ff2bd8c4 Fix wrong indents 2019-11-16 11:28:43 +01:00
Nico de Haen
c690a64462 Update msg related location when message is deleted
resolves #843
2019-11-16 11:04:56 +01:00
B. Petersen
d808bfe400 add some tests for me-chat and deaddrop 2019-11-13 22:31:18 +01:00
B. Petersen
1b30078c09 test archive functions 2019-11-13 10:16:57 +01:00
B. Petersen
0278875e03 add device-chat tests 2019-11-13 00:13:32 +01:00
B. Petersen
d0ccf28678 do not add random label for unlabelled device-messages 2019-11-13 00:13:32 +01:00
B. Petersen
5023255ebc fix accidentally called maybe_add_bcc_self_device_msg() 2019-11-13 00:13:32 +01:00
holger krekel
545376875a fix python tests 2019-11-12 16:10:48 +01:00
B. Petersen
a9fe77b62e use separate column for device-msg-labels 2019-11-12 16:10:48 +01:00
B. Petersen
b42d8799b4 add dc_add_device_msg_once() to fii 2019-11-12 16:10:48 +01:00
B. Petersen
a42e197634 add device-message only once 2019-11-12 16:10:48 +01:00
B. Petersen
fc32c85608 add device-msg 'consider enabling bcc_self' if bcc_self is disabled and an autocrypt-setup-message is received 2019-11-12 16:10:48 +01:00
B. Petersen
dabd431b1f change default for bcc_self, preserve values for existing installations 2019-11-12 16:10:48 +01:00
B. Petersen
85b4817a1e restore some comments 2019-11-12 16:10:48 +01:00
B. Petersen
84c6113271 use enum for show_emails 2019-11-12 16:10:48 +01:00
B. Petersen
9506f8c38e use bool for exists_before_update 2019-11-12 16:10:48 +01:00
Friedel Ziegelmayer
d330d890c0 setup minimal github action
Just so we can experiment with it on branches
2019-11-12 14:31:38 +01:00
Alexander Krotov
c6369b1c5a Implement TryFrom instead of TryInto
TryInto is derived automatically and its documentation recommends implementing TryFrom.
2019-11-10 15:38:58 +00:00
Alexander Krotov
bfa0f9d911 Use the first subkey for encryption instead of the primary key 2019-11-10 16:32:09 +01:00
B. Petersen
154cb2db83 add missing DC_STR_DEVICE_MESSAGES 2019-11-10 16:31:12 +01:00
B. Petersen
37ecfa6b67 fix gm2local offset calculations 2019-11-09 20:26:00 +01:00
B. Petersen
99ba2fb358 let dc_timestamp_to_str() print the local time, not UTC times. the function is used for outputs directly shown to the user, eg. dc_get_msg_info() 2019-11-09 20:26:00 +01:00
B. Petersen
85ebde29dc do not resort chatlist on draft changes
resorting the chatlist on changing drafts has some ux issues.
eg. when the chatlist is visible together with the input field,
if may come to flickering resorting during input
or to a resorting just when the user leave the chat
as this might trigger set_draft().

but also on mobiles, the resorting is visible and a bit unexpected.
also it is unclear what happens when a chat with a draft is entered
and left without modifications.

the solution proposed here is to ignore draft on sorting
while still showing them in the chatlist
if they're newer as the last message.

a possible disadvantage is
that the date for the chat with a draft does not follow the ordering
(the ordering is by the last message),
however, the date is not shown as a "primary sort" criterion or so,
so it might be that this is completely okay.
also, of course, it affects only draft :)
2019-11-09 19:32:13 +01:00
björn petersen
0876f45503 Merge pull request #810 from deltachat/sync-config-param
restore config-param if configure() fails
2019-11-08 19:36:43 +01:00
B. Petersen
2fae6890c2 the new MsgId type comes with a formatter that makes special formatting in the repl tool superfluous 2019-11-07 13:19:38 +01:00
B. Petersen
34f9961857 restore config-param if configure() fails 2019-11-06 22:59:14 +01:00
B. Petersen
515f0c5089 correct ffi return value of dc_add_device_msg() 2019-11-06 13:33:30 +01:00
B. Petersen
5a11551b4d block sending to chats that do not support sending (normally, this should already be avoided in the ui) 2019-11-06 13:33:30 +01:00
B. Petersen
49bf99588b show chat-profile-image in repl tool 2019-11-06 13:33:30 +01:00
B. Petersen
231110fb61 add profile-icon for device-chat 2019-11-06 13:33:30 +01:00
B. Petersen
4c30bf80ce target comments of @flub 2019-11-06 13:33:30 +01:00
B. Petersen
f8afefa2c1 get contact- and chat-info for device-messages 2019-11-06 13:33:30 +01:00
B. Petersen
89bb2d0ffe add devicemsg to repl tool 2019-11-06 13:33:30 +01:00
B. Petersen
b5d5d98645 implement add_device_msg() 2019-11-06 13:33:30 +01:00
B. Petersen
89f394ab86 create separate function for preparing a blob 2019-11-06 13:33:30 +01:00
B. Petersen
cbaa4e03b3 basic devicetalk implementation 2019-11-06 13:33:30 +01:00
B. Petersen
50539465b9 prototype a device-chat 2019-11-06 13:33:30 +01:00
B. Petersen
be08bcb22b rename DC_CONTACT_ID_DEVICE to DC_CONTACT_ID_INFO to be in-line with dc_msg_is_info() 2019-11-06 13:33:30 +01:00
holger krekel
dcd92a894e fix export: write backup_time to the destination not the source sql file
and perform slightly cleaner teardown in python
2019-11-06 13:26:32 +01:00
holger krekel
6336eeb568 better error on has_backup() failing 2019-11-06 13:26:32 +01:00
holger krekel
6b18cbda1f refine dc_copy along the lines @flub did for blobstore 2019-11-06 13:26:32 +01:00
Alexander Krotov
cf023ea557 sql: remove unnecessary ? in prepare() and prepare2() 2019-11-06 13:26:03 +01:00
Alexander Krotov
51a804a80f location: use "bool" for "independent" argument 2019-11-06 14:17:59 +03:00
B. Petersen
1a33b1c574 bump version 2019-11-05 17:04:44 +01:00
B. Petersen
67e2e4d415 target comment of @hpk42 2019-11-04 13:20:35 +01:00
B. Petersen
8c2efa707a name special contact-ids where easily possible
the point of this pr is to get an overview
how and where DC_CONTACT_ID_DEVICE is used,
to prepare introducing a device-"chat".

i did not change the sql statements for now
as this would require some more refactoring
and has the potential to introduce bugs.
2019-11-04 13:20:35 +01:00
B. Petersen
87abc6e4a2 adapt wording 2019-11-03 21:36:40 +01:00
B. Petersen
0ea017c53d add repl command for testing interrupt-idle 2019-11-03 21:36:40 +01:00
B. Petersen
b9c7510b58 use boolean for jobs_needed 2019-11-03 21:36:40 +01:00
holger krekel
01e7caf65a use job_id as mail_id for SendableEmail -- it's only an internal id and job_id is unique enough. 2019-11-03 21:36:01 +01:00
holger krekel
1cfeb730c3 try to fix some smtp todos and do better error logging 2019-11-03 21:36:01 +01:00
Floris Bruynooghe
a3b90a08b6 Copy the file contents manually
Before we created an empty file and asked the OS to copy the file.
The OS is very good at this so this is a good idea generally.  However
it seems that in some cases, possibly an Android Dowload folder, we
might be able to create a file but not overwrite it.  Thus refactor
this a bit so we are copying the file ourselves.

There are no new tests here since the behaviour remains identical.
The good news is that the existing tests were good enough to catch
some bugs already.
2019-11-03 20:16:56 +01:00
holger krekel
31571be71e add account.get_chat_by_id API 2019-11-03 20:14:57 +01:00
holger krekel
661fc45106 split "chatting.py" into "contact.py" and "chat.py" to be more reminiscent of the core-rust structure 2019-11-03 20:14:57 +01:00
holger krekel
da64dee3e0 start a changelog for the yet untagged beta.7 2019-11-03 01:32:49 +01:00
holger krekel
cb00f5da79 fix #786 by always succeeding to create a canonical deterministic message-id -- thanks @csb0730 for the analysis of the issue 2019-11-03 01:28:45 +01:00
B. Petersen
e1df41c209 fix tests 2019-11-03 01:19:29 +01:00
B. Petersen
3b64748427 add proptest-regression file 2019-11-03 01:19:29 +01:00
B. Petersen
70cef68eeb display failed messages 2019-11-03 01:19:29 +01:00
holger krekel
c5f64d2988 address @link2xt comments 2019-11-03 00:21:27 +01:00
holger krekel
4eb068613d extend and fix python/test side of location streaming 2019-11-03 00:21:27 +01:00
holger krekel
d774430ec2 simplify location parsing 2019-11-03 00:21:27 +01:00
holger krekel
d24a982757 fix various location-streaming issue, test passes now 2019-11-03 00:21:27 +01:00
holger krekel
d74c70a57c fix and streamline location-outgoing pipeline 2019-11-03 00:21:27 +01:00
holger krekel
a6f0f78588 addresses #757 test and add location streaming python api 2019-11-03 00:21:27 +01:00
B. Petersen
e6d9991581 fix conversion from nanoseconds to milliseconds
1_000_000 nanosecond = 1 millisecond
2019-11-02 15:22:27 +01:00
B. Petersen
ec8dbddcfb tweak ffi documentation wrt certificate checks 2019-11-01 18:06:01 +01:00
B. Petersen
bc699f17d9 avoid usage of get_subtitle() in repl-tool, remove dc_chat_get_subtitle() from documentation. 2019-11-01 13:19:47 +01:00
B. Petersen
832df41130 target comments of @hpk42 and @flub 2019-11-01 00:46:48 +01:00
B. Petersen
5709681076 streamline as_path_unicode(), delete unused as_str() and as_str_safe() 2019-11-01 00:46:48 +01:00
B. Petersen
858baf0c2c prefer to_opt_string_lossy() over as_opt_str() as the latter pancis on non-wellformatted utf-8, delete as_opt_str() 2019-11-01 00:46:48 +01:00
B. Petersen
e4b3e23769 prefer to_string_lossy() over as_str() as the latter pancis on non-wellformatted utf-8 2019-11-01 00:46:48 +01:00
holger krekel
8ce05796da ensure that especially qr tests are rerun 2019-10-31 23:31:09 +01:00
holger krekel
7f8c6d8cca some more display refinements 2019-10-31 23:31:09 +01:00
holger krekel
75ba040531 more error logging with file operations in general 2019-10-31 23:31:09 +01:00
Floris Bruynooghe
faa78e1c04 At least log the error 2019-10-31 22:38:32 +01:00
Floris Bruynooghe
b264d3be3c Be more accepting in creating blobs from existing names
This is an additional fix for #768 aka
commit eac8ad8369
2019-10-31 20:05:09 +01:00
holger krekel
80d7e84e5d prepare 1.0.0-beta.6 2019-10-31 12:34:03 +01:00
B. Petersen
4e37610f21 fix boolean error in chatlist::get_msg_id()
the error led to unusable contact requests,
at least on android and ios (probably also desktop)
because msg_id=dc_chatlist_get_msg_id() always returns 0
and create_chat_by_msg_id(msg_id)
or dc_marknoticed_contact(<get sender from msg_id>)
failed therefore.
2019-10-31 12:31:31 +01:00
Floris Bruynooghe
78030e4a31 Add test for #767
Without a test for this fix, a regression will happen again.

Original PR #767 aka commit 4fed875107
2019-10-30 21:08:25 +01:00
holger krekel
b01c842d7c bump version in toml's and changelog, and trigger deltachat-specific "cargo update". example: python set_core_version.py 1.0.0-beta.6 automates bumping the version and performs some quick sanity checks 2019-10-30 20:05:01 +01:00
holger krekel
c56c10bced remove unneccessary check of is_special() + cleanups 2019-10-30 19:29:13 +01:00
holger krekel
b0ccbc36d9 fix FFI-behaviour: return default empty messages when asked for special ones 2019-10-30 19:29:13 +01:00
holger krekel
9cdfc3409d systematically ignore invalid message ids when passed in through CFFI 2019-10-30 19:29:13 +01:00
björn petersen
fc851f542a Merge pull request #770 from deltachat/tweak-ffi
remove unneeded const attribtute
2019-10-30 19:12:43 +01:00
B. Petersen
7530abd581 remove unneeded const attribtute 2019-10-30 16:36:28 +01:00
holger krekel
a6594a9ae3 add changelog and bump to beta.4 2019-10-30 16:13:34 +01:00
B. Petersen
62019f57e9 fix some doxygen links and overviews 2019-10-30 16:09:04 +01:00
holger krekel
41443bb7f9 fix sending of autocrypt setup message 2019-10-30 15:48:06 +01:00
B. Petersen
8b5f7d98f6 do not escalate attemt to add self to a group to the user, just return false from add_contact_to_chat() 2019-10-30 14:03:54 +01:00
B. Petersen
6fea6f730d fix recognition of mailto-address-qr-codes, add tests 2019-10-30 13:13:02 +01:00
holger krekel
ad42a39a43 amend changelog 2019-10-30 13:12:00 +01:00
B. Petersen
ed9cfedbf3 update changelog 2019-10-30 13:12:00 +01:00
holger krekel
36510d8451 update links / use deltachat/rust-imap master branch 2019-10-30 13:07:44 +01:00
björn petersen
501a6eee69 Merge pull request #765 from deltachat/fix-qr2
fix plus-space-decoding in qr-code
2019-10-30 10:16:57 +01:00
B. Petersen
39cd8465f4 allow plus-space-encoding in qr-code, adapt tests 2019-10-30 00:37:35 +01:00
holger krekel
d3c0d2ebb1 bump version 2019-10-29 22:38:25 +01:00
holger krekel
911c0e45dc expose empty server functionality and test it (also introducing a new DC_EVENT_IMAP_FOLDER_EMPTIED event) 2019-10-29 22:19:13 +01:00
holger krekel
7628ee1e05 rust-part of empty_server 2019-10-29 22:19:13 +01:00
holger krekel
de3e5e1c39 fix deadlock issue with config access 2019-10-29 16:08:24 +01:00
B. Petersen
27627b4f74 show better error message for a simple 'bad credentials' error and give some more hints for other errors 2019-10-29 16:08:24 +01:00
B. Petersen
469f8ac31d make stock_string_repl_str2() public as the other members 2019-10-29 16:08:24 +01:00
Floris Bruynooghe
c8d296ea0e A MsgId newtype
This more strongly types the ubiquitous message id type by no longer
making it an integer.  It keeps the actual ID opaque.  Only for the
generic job API the number keeps being used.  Some locations also need
to create it from an integer and call MsgId::new().
2019-10-29 15:30:53 +01:00
holger krekel
c6adbe939d use latest rust-imap fork commits from @dignifiedquire 2019-10-28 20:51:17 +01:00
holger krekel
b4464ab0a3 address @dignifiedquire comments 2019-10-28 20:51:17 +01:00
holger krekel
bf7d57c560 update rust-imap 2019-10-28 20:51:17 +01:00
holger krekel
0e59819af4 use latest rust-imap fork 2019-10-28 20:51:17 +01:00
holger krekel
1cc4f56025 make imap-idle survive disconnects (during and at the beginning of an app) 2019-10-28 20:51:17 +01:00
holger krekel
1d03e0822e seems to work 2019-10-28 20:51:17 +01:00
björn petersen
b722da642a Merge pull request #750 from deltachat/tweak-summary
in summary, show hyphen only if there is a type and a text
2019-10-27 15:59:59 +01:00
Alexander Krotov
0aa1d1caa0 Merge pull request #749 from deltachat/fix-get-chat-id
let dc_get_chat_id_by_contact_id() returns 0 if no chat is found
2019-10-27 12:42:22 +00:00
B. Petersen
da28e1dd44 address comment of @flub and add some tests 2019-10-27 13:32:06 +01:00
B. Petersen
d223a286c0 in summary, show hyphen only if there is a type and a text; this avoids summaries as 'Voice message -' 2019-10-27 13:31:52 +01:00
Alexander Krotov
7916a7fa07 Fix spelling of Param::GuaranteeE2ee 2019-10-27 11:51:59 +01:00
Alexander Krotov
ee81895e1e Use DC_CONTACT_ID_SELF in do_initiate_key_transfer 2019-10-27 11:51:47 +01:00
Alexander Krotov
6ac4384769 location.rs cleanup
Use constants where possible, move "let" closer to assignments.
2019-10-27 11:51:35 +01:00
Alexander Krotov
99fababf0b to_base64: operate on characters instead of bytes to avoid unsafe code 2019-10-27 11:51:25 +01:00
Alexander Krotov
c85f1b20ca Add constants for certificate checks configuration 2019-10-27 11:51:14 +01:00
B. Petersen
51f43842cf cargo fmt 2019-10-27 11:42:56 +01:00
B. Petersen
8015ba1d64 dc_get_chat_id_by_contact_id() returns 0 if no chat is found.
this is no error;
in fact, the function is used to probe
if there is a chat with a given contact at several places
eg. in the android-ui.
2019-10-26 18:37:33 +02:00
Alexander Krotov
cfa69cf35a Add Params::set_cmd and use SystemMessage constants 2019-10-26 14:04:08 +02:00
B. Petersen
dced1932b3 if show_emails=ALL, show belonging contact-requests directly in the chatlist 2019-10-24 11:12:35 +02:00
B. Petersen
79a08f96c5 make ShowEmails an enum, use constant for trash 2019-10-24 11:12:35 +02:00
björn petersen
f5d98c1db6 Merge pull request #742 from deltachat/add-self-to-group
allow adding SELF to group
2019-10-23 22:13:25 +02:00
B. Petersen
df4273e986 fix logic error: adding a member to a group is okay if a real contact exists or for SELF 2019-10-23 14:03:42 +02:00
Floris Bruynooghe
5d79690260 Add Params::get_file(), ::get_path() and ::get_blob()
Turns out that anyone that uses these either justs wants a file or
wants a blob.  Consolidate those patterns into one place and simplify
all the callers.
2019-10-22 18:54:09 +02:00
Floris Bruynooghe
6c9e16d31a Introduce a BlobObject type for blobs
This creates a specific type for blobs, with well defined conversions
at the borders.  It also introduces a strong type for the Param::File
value since that param is often used used by the public API to set
filenames using absolute paths, but then core changes the param to a
blob before it gets to the database.

This eliminates a few more functions with very mallable C-like
arguments behaviour which combine a number of operations in one.
Because blob filenames are stored so often in arbitrary strings this
does add more code when receiving those, until the storage is fixed.

File name sanitisation is now deletated to the sanitize-filename crate
which should do a slightly better job at this.
2019-10-22 18:54:09 +02:00
B. Petersen
f0fc50d5a9 adapt to reality 2019-10-22 18:37:47 +02:00
björn petersen
7a4a4389fa Merge pull request #739 from deltachat/location
Rustify location.rs
2019-10-22 18:01:35 +02:00
holger krekel
131889cdfb add beta2 changelog, bump version to 1.0.0-beta.2 2019-10-22 17:50:23 +02:00
Alexander Krotov
bed14d5c02 Initialize continue_streaming with false
Otherwise this variable is constant.
2019-10-22 13:24:23 +03:00
Alexander Krotov
d3c831a0a2 Replace continue_streaming int with bool 2019-10-22 13:24:23 +03:00
Alexander Krotov
0007c12dea Replace FORCE_SCHEDULE #define from C core with bool 2019-10-22 13:24:23 +03:00
B. Petersen
049077f13b reconnect on io errors and broken pipes 2019-10-22 09:58:05 +02:00
holger krekel
e17c69f89c actually try connecting, instead of just preparing the connect 2019-10-21 23:17:18 +02:00
holger krekel
4b24f32d6c add tests and API for is_forwarded 2019-10-21 23:00:42 +02:00
Friedel Ziegelmayer
f404e31e30 chore(deps): switch back to rust-imap master (#735)
chore(deps): switch back to rust-imap master
2019-10-21 18:48:50 +02:00
dignifiedquire
7455b26ab2 chore(deps): switch back to rust-imap master 2019-10-21 16:52:43 +02:00
holger krekel
ee3259a74d fix rust-imap dep and remove Xargo.lock -- or is the latter used for anything? 2019-10-21 11:14:38 +02:00
dignifiedquire
391ba67ad5 update to fixed rust-imap for yandex 2019-10-20 13:10:17 +02:00
dignifiedquire
54f8c68151 switch to master 2019-10-20 13:10:17 +02:00
dignifiedquire
4a2e1897a6 fix(smtp): use correct auth mechanisms 2019-10-20 13:10:17 +02:00
dignifiedquire
076616bfb9 fix(imap): read server greeting 2019-10-20 13:10:17 +02:00
Dmitry Bogatov
a9dd78f622 Narrow return type of location::set: cint -> bool 2019-10-19 22:38:43 +02:00
B. Petersen
d16bdafaf0 simplify calling Simplifier, get is_forwarded flag correctly 2019-10-19 22:34:14 +02:00
B. Petersen
4f126c5292 show forwarded-state in repl-tool 2019-10-19 22:34:14 +02:00
B. Petersen
7b958a20fd prefer to_string_lossy() over as_str() as the latter pancis on non-wellformatted utf-8 2019-10-19 22:30:25 +02:00
B. Petersen
4519071718 prefer to_opt_string_lossy() over as_opt_str() as the latter pancis on non-wellformatted utf-8 2019-10-19 22:30:25 +02:00
B. Petersen
0108b4724e add function to convert NULL-able c-string to Option<String> 2019-10-19 22:30:25 +02:00
B. Petersen
bb08b39c71 remove duplicated code 2019-10-19 22:30:25 +02:00
B. Petersen
1908ac428b mark own forwarded messages as such 2019-10-17 19:41:17 +02:00
Alexander Krotov
dfc453c1d1 Merge pull request #727 from deltachat/strndup
Remove unused strndup
2019-10-17 12:33:51 +03:00
Alexander Krotov
9fa6289093 Remove unused strndup 2019-10-17 03:18:08 +03:00
björn petersen
6f92ce0fa8 Merge pull request #722 from deltachat/oauth2-even-better
make oauth2.get_addr work
2019-10-16 10:58:04 +02:00
B. Petersen
cde2c9137f make oauth2.get_addr work
oauth2.get_addr is used for gmail-oauth2
to retrieve the address really authorized in the oauth2 process.
2019-10-16 00:57:58 +02:00
björn petersen
120524ae00 Merge pull request #720 from deltachat/make-oauth2-better
make oauth2 POST successfully
2019-10-15 18:29:27 +02:00
holger krekel
7bb73f45a5 add LP smtp/imap connection flags to const 2019-10-15 13:46:39 +02:00
Dmitry Bogatov
2d0f563dfe Replace magic constant with symbolic name 2019-10-15 13:45:08 +02:00
VP-
cfe3c69f00 Don't use hard-coded email addresses in tests 2019-10-15 13:10:55 +02:00
B. Petersen
c266d2ca0d use POST instead GET to init or refresh oauth2 tokens 2019-10-15 00:22:37 +02:00
B. Petersen
85fc696975 more specific and distinguishable oauth2 error message 2019-10-15 00:20:08 +02:00
Alexander Krotov
9bf8bed0c3 Merge pull request #685 from deltachat/outlook_unit_test
Unit test Outlook autodiscovery
2019-10-14 21:34:43 +00:00
Alexander Krotov
c4d55f6ba4 auto_outlook: convert <Protocol> type to lowercase before comparison 2019-10-14 19:21:29 +03:00
B. Petersen
766d7cbd3a add some missing comments 2019-10-14 15:11:33 +02:00
Alexander Krotov
8e0e1bd58d Add test for Outlook autodiscovery without redirect 2019-10-14 13:42:35 +03:00
Alexander Krotov
a471ccc95a Test Outlook autoconfigure redirect parsing 2019-10-14 01:24:20 +03:00
Alexander Krotov
daac8c4824 rustfmt 2019-10-14 01:24:20 +03:00
Alexander Krotov
59c22a5626 Move Outlook autodiscovery into separate function 2019-10-14 01:24:19 +03:00
Alexander Krotov
5154f27f72 Merge pull request #712 from deltachat/moz_autoconfigure_unit
auto_mozilla: split XML parsing into separate function
2019-10-13 16:34:05 +00:00
Alexander Krotov
5f7279eb85 auto_mozilla: server is only configured if the type matches
This fixes the testcase introduced in previous commit
2019-10-12 17:07:20 +03:00
Alexander Krotov
ce67f593f6 Add XML parsing test for Mozilla autoconfig 2019-10-12 17:07:20 +03:00
Alexander Krotov
556ea57f37 auto_mozilla: split XML parsing into separate function 2019-10-12 17:07:20 +03:00
Dmitry Bogatov
a4257b619a Replace flags argument to Sql.open with single bool
Previously, "flags" argument of Sql.open was of type libc::c_int, but
only one bit was used: whether to open database read-only. This commit
makes it explicit by changing type to bool and renaming argument.
2019-10-12 09:56:18 +02:00
Alexander Krotov
8479c8afbf Do not convert 1 to libc::c_int
It is compared to u32 in this case
2019-10-11 21:52:47 +02:00
Alexander Krotov
eba012b965 Do not use libc::c_int in Simplify 2019-10-11 21:52:47 +02:00
Alexander Krotov
66e53e6804 Return bool from Imap::fetch()
It is unused anyway
2019-10-11 21:52:47 +02:00
Alexander Krotov
c8aa8b55f6 Use i32 instead of libc::c_int for try_again
It is already declared as i32 in the structure
2019-10-11 21:52:47 +02:00
Alexander Krotov
900e3905c0 Count number of libc references in top_evil_rs.py 2019-10-11 21:52:47 +02:00
holger krekel
088490721d bump to 1.0.0-beta.1, add initial Changelog 2019-10-10 19:08:47 +02:00
björn petersen
a40b99aae0 Merge pull request #709 from deltachat/fix/features
fix: disable default features for pgp, by default
2019-10-10 18:32:56 +02:00
dignifiedquire
b9646446f8 fix: disable default features for pgp, by default 2019-10-10 18:05:11 +02:00
holger krekel
4e36b35039 cargo-fmt only 2019-10-10 12:01:01 +02:00
holger krekel
d412ee6042 make dc_str_from_clist safe and return a string instead of c-string -- this allows to remove some unsafe and now unneccessary cleanup code 2019-10-10 12:01:01 +02:00
B. Petersen
67848e3333 use libc::c_char as i8 and u8 are used differently on x86 and arm 2019-10-10 08:44:31 +02:00
björn petersen
4d79c6e235 Merge pull request #705 from deltachat/safer-strn
len-check in strncasecmp()
2019-10-10 01:19:09 +02:00
B. Petersen
bc99d9d196 cargo fmt 2019-10-10 00:39:35 +02:00
B. Petersen
e1fc5863c2 target comments of @hpk42 2019-10-10 00:29:52 +02:00
B. Petersen
f0791149e6 document dc_str_unref() 2019-10-10 00:29:52 +02:00
B. Petersen
297b032bdc fix doxygen, some rewording 2019-10-10 00:29:52 +02:00
holger krekel
98180c175d make sure c-doc upload dirs exist even if branch name has / in it 2019-10-10 00:23:36 +02:00
dignifiedquire
46e8a436cb updates and fixes 2019-10-10 00:23:36 +02:00
dignifiedquire
dc2cf8ecfc unify naming in pgp 2019-10-10 00:23:36 +02:00
dignifiedquire
fd69ebfd1f fix and cleanup tests 2019-10-10 00:23:36 +02:00
dignifiedquire
03979fdc51 wip 2019-10-10 00:23:36 +02:00
B. Petersen
2c98e91276 target comments of @link2xt 2019-10-10 00:22:50 +02:00
B. Petersen
3270120d16 strncasecmp() compares the given number of characters but not after a 0-byte 2019-10-09 19:00:54 +02:00
holger krekel
77a7efc920 fix #694 by forking lettre and avoiding extra NOOP Smtp commands for connection checking.
If this works out and fixes our problem, we can submit upstream.
2019-10-09 18:25:41 +02:00
björn petersen
de604e744e Merge pull request #703 from deltachat/fix-strncasecmp
fix(mmime): correct implementation of strncasecmp
2019-10-09 17:31:41 +02:00
holger krekel
24c0a833bd address @flub comments 2019-10-09 15:59:31 +02:00
holger krekel
45f011c63c expose and test set_stock_translation to Python 2019-10-09 15:59:31 +02:00
holger krekel
bc86201b44 error out if %1 %2 replacements are not contained in default english version 2019-10-09 15:59:31 +02:00
holger krekel
b82af9fff3 introduce set_stock_translation and remove call to DC_EVENT_GET_STRING 2019-10-09 15:59:31 +02:00
dignifiedquire
3a1e74a306 fix(mmime): correct implementation of strncasecmp
Closes #523
2019-10-09 14:27:15 +02:00
Alexander Krotov
e4cca92910 Simplify to_string_lossy()
Call .to_string_lossy() without trying .to_str() first.
2019-10-09 08:56:02 +02:00
holger krekel
102220834c address https://github.com/deltachat/deltachat-ios/issues/328 and don't escalate xml-parsing errors to users through error! 2019-10-08 23:09:03 +02:00
holger krekel
24d744b94c try generating and uploading doxygen docs (#697) 2019-10-08 18:52:13 +02:00
holger krekel
1df6229e99 remove/push down some unsafe-fn 2019-10-08 17:37:37 +02:00
Alexander Krotov
c23e98ff83 Merge pull request #693 from deltachat/mozautoconfig
Use Rust types in auto_mozilla.rs
2019-10-07 23:03:17 +00:00
holger krekel
b7c81f37c0 ok_to_continue is 0, now start counting "unsafe fn" ... 2019-10-07 23:58:32 +02:00
Alexander Krotov
5c3a7e4119 Use Rust types in auto_mozilla.rs 2019-10-07 18:43:53 +03:00
holger krekel
a94acef49b cargo fmt 2019-10-07 13:39:01 +02:00
holger krekel
7f5b362eda remove ok_to_continue and simplify/refactor imap and smtp connection trying
address @r10s review comments and also remove unneccessary "unsafe fn"
2019-10-07 13:39:01 +02:00
holger krekel
ba5b3ad675 fix #690 by avoiding account.__del__ and registering/unregistering with atexit (a module that manages process/interpreter shutdown and calls into registered shutdown. Recommended way for user code still is to call account.shutdown() explcitely. 2019-10-07 12:24:50 +02:00
björn petersen
c1e4d1e7a4 Merge pull request #688 from deltachat/remove-predecessor
remove scanning for old Chat-Predecessor header
2019-10-07 02:02:15 +02:00
B. Petersen
dd8744b74e remove unused import 2019-10-07 01:15:42 +02:00
holger krekel
b775ecca08 fix tests 2019-10-07 00:55:33 +02:00
holger krekel
b8f211a013 cargo fmt 2019-10-07 00:55:33 +02:00
holger krekel
51534b2fae move all alloc/free/stop/has/shall_stop*ongoing* methods to context
introduce stop_ongoing to bindings and test for cancelation of configure
2019-10-07 00:55:33 +02:00
B. Petersen
710db2ba0a remove scanning for old Chat-Predecessor header
the Chat-Predecessor header was dropped about 10 month ago.
as a replacement, the standard headers are used.
2019-10-07 00:43:43 +02:00
holger krekel
32ef0d4dc3 cargo fmt 2019-10-07 00:22:19 +02:00
holger krekel
b3cd80ba6d remove ok_to_continue, shift code to a result-returning add_export_files to avoid if-branches, cleanup and simplify some logging 2019-10-07 00:22:19 +02:00
holger krekel
3f053f899e cargo fmt 2019-10-07 00:15:39 +02:00
holger krekel
22b4d1734c remove last ok_to_continue in mimeparser 2019-10-07 00:15:39 +02:00
björn petersen
46a71e81a0 Merge pull request #683 from deltachat/outlook_none
Fix and refactor Outlook autodiscovery
2019-10-06 15:54:01 +02:00
Alexander Krotov
b4851187ba Safe outlk_autodiscover 2019-10-06 05:46:45 +03:00
Alexander Krotov
0252969f7e Remove unused in_0 from outlk_autodiscover_t 2019-10-06 05:16:17 +03:00
Alexander Krotov
f86cec4844 Mark moz_autoconfigure as safe 2019-10-06 05:16:17 +03:00
Alexander Krotov
cd2e36da92 rustfmt 2019-10-06 05:16:17 +03:00
Alexander Krotov
1b13107181 Return Option<String> from read_autoconf_file 2019-10-06 05:16:17 +03:00
Alexander Krotov
6fbde21995 outlk_autodiscover: store url as String 2019-10-06 05:16:17 +03:00
Alexander Krotov
a6608513ac auto_outlook: restore and add some comments 2019-10-06 05:16:17 +03:00
Alexander Krotov
d43c225be3 Return None from outlk_autodiscover if no XML config was parsed 2019-10-06 05:16:17 +03:00
B. Petersen
1802d7658d add some tests for dc_create_outgoing_rfc724_mid()
as noticed in the last attempts to change the prefix,
this test is missing (as probably many more :)
2019-10-05 22:48:00 +02:00
B. Petersen
275f5d713f cargo fmt 2019-10-05 22:47:39 +02:00
B. Petersen
e40cfeec58 bubble up sql, get_abs_path errors 2019-10-05 22:47:39 +02:00
B. Petersen
275b4b8d36 bubble up sql-errors in reveive_imf() 2019-10-05 22:47:39 +02:00
B. Petersen
77cef632c7 bubble up update_param() error
bubble up the error instead of ignoring it -
but also do not panic as before #673
2019-10-05 22:47:39 +02:00
B. Petersen
db2064de14 use .to_string_lossy() member of Path, CStr etc.
instead of calling to_str().unwrap_or_default().
2019-10-05 22:46:29 +02:00
B. Petersen
e251c7b1c8 redo some c-comments 2019-10-05 15:53:18 +02:00
björn petersen
2fe98775f9 Merge pull request #673 from deltachat/dont-panic
Don't Panic!
2019-10-05 15:08:15 +02:00
björn petersen
187179d87b Merge pull request #674 from deltachat/to-lossy
always expect bad c-strings
2019-10-05 15:04:32 +02:00
holger krekel
dd03f6e8af test and fix that a group with mixed-encryption members (with some we can encrypt, others not) does not break 2019-10-05 15:02:03 +02:00
holger krekel
07b32241bd try fix #677 (this fixes only a test) -- seems like we passed pointers into Rust strings to code that was expecting null-terminated strings. Now do proper "strdup()" and free. The real proper fix is to make dc_split_armored_string into a rust-string function and do away with all c-pointers. 2019-10-05 15:01:28 +02:00
B. Petersen
abfff96cd4 cargo fmt 2019-10-05 12:44:08 +02:00
B. Petersen
735bdd1c20 always use to_string_lossy() for converting c-strings to String
the function to_string() is removed;
c-strings may always be badly formatted and this should never lead to a panic.
2019-10-05 00:12:32 +02:00
B. Petersen
9cae075b6f target detailed checks of @flub 2019-10-04 23:07:34 +02:00
B. Petersen
2317518e5e cargo fmt 2019-10-04 23:07:34 +02:00
B. Petersen
477af413c6 if in doubt, prefer unwrap_or_default()
if the past we had lots of crashes because of unexpected unwrap failures,
mostly related to string.
this commit avoids them eg. for string-conversions that may panic
eg. when encountering a null-byte or by logical programming errors
where an object is assumed to be set but is not under unexpected circumstances.
2019-10-04 23:07:33 +02:00
B. Petersen
93f0f5ccae rename sql-config-setters to set_raw_config*()
the rename is reasonable as the getter is called get_raw_config*()
and to make the functional difference to context.set|get_config() clearer.
2019-10-04 11:18:26 +02:00
B. Petersen
79b92727cc prefer get_config_bool() where appropriate
for db input/output, we still use get_config_int() to convert to/from 0/1.
also for info() we prefer get_config_int() to show the real value.
2019-10-04 11:15:11 +02:00
björn petersen
8dfd04672f Merge pull request #670 from deltachat/tweak-config
tweak config
2019-10-03 21:19:45 +02:00
holger krekel
603761e4b7 add DC_EVENT_DELETED_BLOB_FILE 2019-10-03 18:01:56 +02:00
holger krekel
467c09f491 introduce DC_EVENT_NEW_BLOB_FILE event and test for it 2019-10-03 18:01:56 +02:00
B. Petersen
a953b494cb cargo fmt 2019-10-03 17:41:09 +02:00
B. Petersen
dca9afa10b rename sql.get_config() to sql.get_raw_config() to make clearer that there is not default handling 2019-10-03 17:35:43 +02:00
B. Petersen
23d2d87c24 do not panic on missing or wrong formatted values in the database 2019-10-03 17:15:40 +02:00
B. Petersen
c6b2d640ae prefer context.get_config() over context.sql.get_config() as the latter does not handle default values. 2019-10-03 17:15:40 +02:00
björn petersen
b4b8a1d15b Merge pull request #667 from deltachat/resilient-writes
more resilient database writes
2019-10-03 14:48:21 +02:00
Alexander Krotov
130d485cac Fix some clippy warnings 2019-10-03 14:30:40 +02:00
B. Petersen
d5b92744ed increase number of simultan database-connections, wait for write if another thread writes
- increase the number of simultan database-connections handled by the r2d2 pool.
currently we have already at least 5 threads threads,
but also the ui may call from any thread.
- the busy-timeout for all connections is set to 10 seconds.
this means, if a connection-A wants to write,
but connection B-is already writing,
connection-A waits multiple times a few ms and tries over.
this is repeated until the 10 seconds are accumulated.
2019-10-03 14:09:43 +02:00
Alexander Krotov
a5c4e16405 Merge pull request #647 from deltachat/tls_checks_config
Add certificate check configuration options
2019-10-02 23:21:03 +00:00
Alexander Krotov
216266d7bf Apply imap_certificate_checks config to StartTLS connections 2019-10-03 02:02:51 +03:00
Alexander Krotov
bf1652a1be Move common code for IMAP and SMTP to login_param.rs 2019-10-03 01:26:38 +03:00
Alexander Krotov
f93f3d6012 Do not set minimal TLS version for SMTP manually 2019-10-03 01:26:07 +03:00
Alexander Krotov
41806f86ba Return certificate check information from get_info() 2019-10-03 01:26:07 +03:00
Alexander Krotov
59df97944f Enable strict certificate checks for test online accounts 2019-10-03 01:26:07 +03:00
Alexander Krotov
468651534e Manually specify values for CertificateChecks enum
This is what we are doing in constants.rs
2019-10-03 01:26:07 +03:00
Alexander Krotov
6343ae8161 Rename {mail,send}_certificate_checks into {imap,smtp}_certificate_checks 2019-10-03 01:26:07 +03:00
Alexander Krotov
641bd5eb15 Write configured_{mail,send}_certificate_checks to database 2019-10-03 01:26:07 +03:00
Alexander Krotov
063d989225 Use mail_certificate_checks configuration in imap.rs 2019-10-03 01:26:07 +03:00
Alexander Krotov
b8ca7b1591 Add CertificateChecks::Automatic option and make it default
It is the same as AcceptInvalidCertificates for now,
but can be replaced with better heuristics later,
such as a database of known providers or TOFU.
2019-10-03 01:25:19 +03:00
Alexander Krotov
e222f49c9d Use send_certificate_checks configuration 2019-10-03 01:25:19 +03:00
Alexander Krotov
297bc635e8 Add certificate check configuration options 2019-10-03 01:25:19 +03:00
holger krekel
230c65594c add test for markseen not sending out smtp on second call 2019-10-02 23:51:14 +02:00
holger krekel
509a21ff05 introduce DC_IMAP_MESSAGE_MOVED event and try harder to send out MDNs 2019-10-02 23:51:14 +02:00
holger krekel
96066712bd add and test for DC_IMAP_MESSAGE_DELETED event 2019-10-02 23:51:14 +02:00
holger krekel
d83aa1e898 perform heuristic moves only if a job is not delete 2019-10-02 23:51:14 +02:00
holger krekel
f0a7bdb6d6 properly parse message ids in imap prefetch 2019-10-02 23:51:14 +02:00
holger krekel
9c077c98cd remove MdnSent logic 2019-10-02 23:51:14 +02:00
holger krekel
6dc45642b7 rework API 2019-10-02 23:51:14 +02:00
holger krekel
5c1b9c83f7 fixes #661 and also contains a cleanup of config access (only for e2ee and mdns) 2019-10-02 22:51:09 +02:00
holger krekel
92438737c9 - introduce and test BccSelf config, to prevent bcc-self on outgoing mails
- introduce context.get_config_int() which respects default values
  declared in config.rs (Config)
2019-10-02 22:51:09 +02:00
holger krekel
489cdd1b24 - resultify send-out pipeline for better error reporting
- early ignore sending out smtp messages with no recipients
2019-10-02 22:51:09 +02:00
holger krekel
3f7995a7ea start with some refactoring of the outgoing pipeline 2019-10-02 22:51:09 +02:00
Alexander Krotov
f7ad93229d test_account.py: fix a typo in test message 2019-10-02 13:10:16 +02:00
Alexander Krotov
555b4bc8c7 python/README.rst: s/mail_pwd/mail_pw/
mail_pw is the option that is actually read by the tests
2019-10-02 13:09:45 +02:00
björn petersen
75f41bcb90 Merge pull request #658 from deltachat/fix-contact-requests
move contact-requests to the beginning of chatlists
2019-10-01 13:29:44 +02:00
B. Petersen
97e1fbc198 move contact-requests to beginning of chatlists
contact-requests of non-blocked senders are shown in the chatlist,
so that the user gets aware of them without opening the contact-request-chat.
however, for that, the contact-request virtual-chat-id
must be added to the beginning of the list,
otherwise it won't get noticed by the user.
2019-10-01 12:50:31 +02:00
holger krekel
ee6d16f1b1 on some call sites: peerstate.save_to_db() should bubble up errors instead of crashing.
also write a test that double-creation of an addr-row is fine.
2019-10-01 10:34:36 +02:00
björn petersen
22d2097132 log all database-migration actions (#654) 2019-09-30 20:56:19 +02:00
björn petersen
c376de9b5e add sticker type (#653)
* add sticker type

this pr adds the message type 'sticker'.
stickers are handled as normal images
but tagged with the header `Chat-Content: sticker`
it's up to the ui to render these stickers appropriate.

* cargo fmt
2019-09-30 20:55:27 +02:00
holger krekel
ab2ef1e1e4 shift most mmime functions to wrapmime 2019-09-30 00:52:14 +02:00
holger krekel
18030fa61e remove duplicate code and possibly a leak 2019-09-30 00:52:14 +02:00
holger krekel
064337b5d3 refactor one occassion of determinig transfer encoding 2019-09-30 00:52:14 +02:00
Dmitry Bogatov
a6a6fc48c1 Remove unused outlk_autodiscover_t.redirect field 2019-09-29 23:35:01 +02:00
holger krekel
d72e9bb05b remove dc_get_fine_* method and validate_filename 2019-09-29 22:49:01 +02:00
holger krekel
7a9fdb4acd add a new tested context.new_blob_dir method to simplify writing out blob files 2019-09-29 22:49:01 +02:00
Alexander Krotov
a6d0464735 Merge pull request #643 from deltachat/top_evil_rs-skip-safe
top_evil_rs.py: do not list safe files
2019-09-29 19:07:56 +00:00
Alexander Krotov
52f69cc7dc top_evil_rs.py: do not list safe files 2019-09-29 18:51:48 +03:00
Dmitry Bogatov
0beadde758 Remove _safe suffix from dc_decode_header function
There is no longer unsafe version of this function, so suffix is
useless now.
2019-09-28 23:18:41 +02:00
Dmitry Bogatov
618abd63cf Drop unsafe version of dc_decode_header_words
Replace all call sites with safe version and adjust tests as apporiate.
2019-09-28 23:18:41 +02:00
Friedel Ziegelmayer
34b3ddf63b refactor(e2ee): reduce unsafe spread 2019-09-28 13:20:33 -06:00
holger krekel
ca76cac314 address @flub's review comment from https://github.com/deltachat/deltachat-core-rust/pull/622 2019-09-28 03:01:52 +02:00
B. Petersen
3a16ad89bd make ffi-string-parameter const
typically, nearly all string-parameters in the ffi should be const.
one of the few exceptions is dc_str_unref() that really modifies the given data.
2019-09-28 02:56:27 +02:00
dignifiedquire
fb9369f333 refactor(imex): almost all unsafe gone here 2019-09-28 02:55:42 +02:00
B. Petersen
66897611d9 fix tests according to the changed verification structure 2019-09-28 00:55:36 +02:00
B. Petersen
6888554e9d use independent verification key
there are 3 key blobs in the database, gossip_key, public_key and verified_key.
the verification_key should not change if, for any reasons,
the public_key or the gossip_key changes.
2019-09-28 00:55:36 +02:00
holger krekel
f28a971b96 several fixes and streamlinings, probably verified-group encryption is fixed, or at least we should see better errors 2019-09-27 23:24:57 +02:00
holger krekel
18808d0a61 majorly rustify and simplify the incoming decryption pipeline 2019-09-27 23:24:57 +02:00
holger krekel
86369148ee fix #616 -- allow invalid certs for smtp and imap connections -- this is the behaviour of C-core. 2019-09-27 21:44:21 +02:00
holger krekel
f45ee2ab4d fix #615 -- like with c-core Chat-Version is left in unprotected headers because
it's eg used in server-filters for detecting DC messages
2019-09-27 18:28:47 +02:00
holger krekel
2b73fab913 cargo fmt 2019-09-27 18:28:29 +02:00
holger krekel
e0d750ac64 little cleanup dc_imex 2019-09-27 18:28:29 +02:00
Alexander Krotov
bb57c6e7b7 Merge pull request #627 from deltachat/dc_receive_imf-slice
Pass slice to dc_receive_imf
2019-09-27 16:27:14 +00:00
Alexander Krotov
f346a052c1 Return Result from dc_initiate_key_transfer 2019-09-27 17:57:45 +02:00
Alexander Krotov
3933353b5f Pass slice to dc_receive_imf
instead of pointer and length
2019-09-27 17:53:41 +03:00
Dmitry Bogatov
6c9c21c135 quote_word: avoid dependency on phf crate 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
d02a721eed Reimplement dc_encode_header_words in safe Rust
This change fixes proptest, introduced in [THIS~2] commit.
2019-09-27 04:11:50 +02:00
Dmitry Bogatov
8ffb4ae127 Add proptest seed that reveals strencoding bug 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
96fbeb583b Add proptest to check dc_header_{encode,decode} 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
33b98a15d3 Remove unused "print_hex" function 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e523ebe3c1 Implement safe version of quote_word 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e17c671b7c Rename local variables to not misleadingly refer to MMAPString 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e7565e1a2a Use rust strings instead of MMapString in src/dc_strencode.rs
Since Rust strings operations are assumed to never fail, this commit
removes a lot of checking, whether appending to mmapstring fails. Now it
is Rust runtime burden.
2019-09-27 04:11:50 +02:00
B. Petersen
b73d6377fc do not truncate messages in contact requests
core-c has truncated messages in the contact requests.
this is questionable in general, as
- all messages, including contact requests,
  are already truncated, unquoted, simplified etc.
- the ui should be capable of showing the full text anyway
  (when the contact request is accepted, the whole messase is shown)
- also, all current ui show the contact requests by name only in the
  chatlist; the user often does not even come to the contact request view.
- if the ui wants to show the contact request is a special way,
  it is probably better to leave this truncation up to the ui
2019-09-27 02:55:23 +02:00
holger krekel
31f5fffc45 cargo fmt 2019-09-26 20:45:03 +02:00
holger krekel
64c518c2f2 remove ok_to_continue 2019-09-26 20:45:03 +02:00
B. Petersen
1ed543b0e8 adapt group-id length to reality 2019-09-26 20:10:33 +02:00
Floris Bruynooghe
8b7cd2dd1a Revert back to only ffi-level checking of open context
The Rust context is always open, the return value of this function was
simply the wrong way around.
2019-09-26 20:08:36 +02:00
Florian Bruhin
8520b5211a python: Add .venv to .gitignore 2019-09-26 19:20:56 +02:00
Florian Bruhin
03661e2a71 python: Allow to configure debug logging via account 2019-09-26 19:20:56 +02:00
jikstra
20b82b3638 Fix ffi actually calling context.sql.is_open() 2019-09-26 18:36:31 +02:00
Alexander Krotov
cb499ae502 Return Result<String> from dc_decrypt_setup_file 2019-09-26 18:05:29 +02:00
holger krekel
02b73207f9 fixup this PR with tests, and returning None from get_filemime 2019-09-26 17:12:06 +02:00
jikstra
53b5cbc12a get_filemime() should return an empty string if no mimetype is present
and not default to `applicatopm/octet-stream`
2019-09-26 17:12:06 +02:00
Friedel Ziegelmayer
f4c6decd2d refactor(mmime): split up into modules (#609)
refactor(mmime): split up into modules
2019-09-26 15:28:36 +02:00
Floris Bruynooghe
69f1497986 Make dc_get_info() work on a closed context
There is very little API guarantees about this, but clients seem to
expect *something* to work on a closed context.  So split this up into
static info an more dynamic context-related info, but let's not
provide any guarantees about what keys are available when.

Fixes #599
2019-09-26 14:22:03 +02:00
Florian Bruhin
2b46f01fe3 Use sys.executable in install_python_bindings.py
When calling pip this way, the virtualenv is used even if not activated.
2019-09-26 13:09:08 +02:00
dignifiedquire
dd4adb57cf refactor(mmime): remove some duplication 2019-09-26 12:36:23 +02:00
dignifiedquire
452bce07e1 refactor(mmime): split up into modules 2019-09-26 12:36:23 +02:00
Simon Laux
8d702d0b77 rename and update providers crate 2019-09-26 01:42:39 +02:00
holger krekel
e1dc4b69f5 address all @dignifiedquire review comments 2019-09-25 23:46:44 +02:00
holger krekel
6cd3580263 rustifying dc_continue_key_transfer and fix master-conflict 2019-09-25 23:46:44 +02:00
holger krekel
d5383aecc9 finish dc_imex refactoring, fix linting, rustify some things 2019-09-25 23:46:44 +02:00
holger krekel
71cbbab2c9 fix #596 and some cleanups 2019-09-25 23:46:44 +02:00
holger krekel
8518d8f456 rustify imex and friends 2019-09-25 23:46:44 +02:00
holger krekel
adc0db04bc failing test 2019-09-25 23:46:44 +02:00
björn petersen
c61fc59003 Merge pull request #608 from deltachat/fix-get-setupcodebegin
fix boolean error that makes get_setupcodebegin() failing
2019-09-25 21:39:48 +02:00
B. Petersen
40f9072250 add get_setupcodebegin to python bindings, test the function 2019-09-25 21:13:05 +02:00
B. Petersen
ea30bb351e fix boolean error that makes get_setupcodebegin() failing 2019-09-25 20:37:36 +02:00
Alexander Krotov
b93550f6c8 Use DC_MSG_ID_LAST_SPECIAL in dc_continue_key_transfer 2019-09-25 03:29:37 +02:00
Alexander Krotov
60bd053095 Pass setup_code to dc_continue_key_transfer as &str 2019-09-25 03:29:37 +02:00
Alexander Krotov
8165b76001 Make dc_normalize_setup_code safe 2019-09-25 03:29:37 +02:00
jikstra
efc563f5ff fix test 2019-09-25 01:06:42 +02:00
jikstra
e52acc994c Make get_draft() return Ok(None) when called for a special chat id 2019-09-25 01:06:42 +02:00
holger krekel
646833d3ec remove phf crate macro usage: it introduced 7 deps and is really an optimization if you very large (100K+) tables -- we have 10 entries or so and it's called once per message. Let's not introduce crates just because we can -- it increases compile time and in the phf case also introduced a github dependency (for whatever reason -- don't want to know ;) 2019-09-25 00:19:47 +02:00
dignifiedquire
fd72c27afe chore(release): release 1.0.0-alpha.5 2019-09-24 17:26:18 +02:00
dignifiedquire
c13bcc25c6 chore(deps): update lock file 2019-09-24 17:25:03 +02:00
holger krekel
21c9ff6c85 cargo fmt 2019-09-23 23:13:41 +02:00
holger krekel
4d6b367654 remove ok_to_continue from job 2019-09-23 23:13:41 +02:00
holger krekel
e2fd22a78e cargo fmt 2019-09-23 21:23:55 +02:00
holger krekel
0759bdde01 cleanup chat.rs: remove ok_to_continue and return result from add_contact methods 2019-09-23 21:23:55 +02:00
holger krekel
faa03e0e14 no functional code change: rename dc_mimefactory to mimefactory and move some functions to become MimeFactory methods 2019-09-23 20:20:34 +02:00
holger krekel
f70897a6d3 rustify new_data_part() and related sanitizations 2019-09-23 18:43:04 +02:00
holger krekel
ba231d2c5f address @dignifiedquire comments 2019-09-23 17:10:21 +02:00
holger krekel
095cb759ed avoid cdata_to_free trick and some more cleanups 2019-09-23 17:10:21 +02:00
holger krekel
5cbcb76039 introduce safety and a particular EncryptHelper 2019-09-23 17:10:21 +02:00
holger krekel
3388b42f20 another rustification of encrypt() 2019-09-23 17:10:21 +02:00
holger krekel
e1d541b02e create wrapmime module and simplify some mailmime code 2019-09-23 17:10:21 +02:00
Friedel Ziegelmayer
cb784615ee feat: import mmime crate 2019-09-23 13:20:30 +02:00
B. Petersen
321c5e049b re-add some comments from core-c 2019-09-22 23:39:16 +02:00
holger krekel
ed7cf218f8 address three comments from @dignifiedquire 2019-09-22 23:39:16 +02:00
holger krekel
74d8368525 rustify references, in_reply_to, mimefactory's recipients_{addr,names} 2019-09-22 23:39:16 +02:00
holger krekel
dcbfa272f9 rustify parts of MimeFactory struct 2019-09-22 23:39:16 +02:00
holger krekel
42dd600e0c more rustifications 2019-09-22 23:39:16 +02:00
holger krekel
9689df601f streamline mimetype guessing and build_body_file 2019-09-22 23:39:16 +02:00
holger krekel
f6019583b7 better looping on some clists 2019-09-22 23:39:16 +02:00
holger krekel
2d50a3335d - add mailimf_field_add helper to reduce number of strdup()s
- make build_body_text avoid char*
2019-09-22 23:39:16 +02:00
holger krekel
202bfa987d dc_mimefactory+friends: simplify used strings, convert message_text and message_text2 to String, convert ints to bools 2019-09-22 23:39:16 +02:00
holger krekel
93f9c7cfbd e2ee_guaranteed -> bool, rustify set_error 2019-09-22 23:39:16 +02:00
Dmitry Bogatov
8f6a0bbf09 Remove unused argument of DC_JOB_CONFIGURE_IMAP 2019-09-22 22:22:00 +02:00
björn petersen
1a47c148e5 Merge pull request #574 from KAction/test-dc_remove_cr
Add test for `dc_remove_cr_chars`
2019-09-22 19:47:22 +02:00
björn petersen
2435ba1ea0 Merge pull request #576 from KAction/quote_word
Remove useless argument to `quote_word`
2019-09-22 19:46:48 +02:00
Dmitry Bogatov
9ba57a923b Remove useless argument to quote_word
Function "quote_word" accepts display charset argument, but on only call
site it is string constant. Change function to always use "utf-8"
display charset, and remove useless argument.
2019-09-21 22:20:56 +00:00
Dmitry Bogatov
90e2b6f26b Add test for dc_remove_cr_chars 2019-09-21 21:26:47 +00:00
dignifiedquire
05f9f454c3 refactor: remove x module and delete deadcode 2019-09-21 17:56:49 +02:00
Friedel Ziegelmayer
b85f59798c refactor(chat): remove c types and unsafe (#572)
refactor(chat): remove c types and unsafe
2019-09-21 17:14:52 +02:00
dignifiedquire
e80345a05b refactor(chat): remove c types and unsafe 2019-09-21 16:59:59 +02:00
dignifiedquire
0bdcc3d616 refactor(message): remove remaining unsafe and c types 2019-09-21 16:37:19 +02:00
dignifiedquire
987f12740e refactor(message): remove unsafe and c types from the Message api 2019-09-21 16:37:19 +02:00
dignifiedquire
1265016a55 refactor(message): rustiy api 2019-09-21 16:37:19 +02:00
Friedel Ziegelmayer
48d1de3678 Avoid ok-to-continue pattern in set_draft_raw (#570)
Avoid ok-to-continue pattern in `set_draft_raw`
2019-09-21 14:32:20 +02:00
Dmitry Bogatov
43f6db3252 cargo-fmt 2019-09-21 06:19:55 +00:00
Dmitry Bogatov
5b917e7d10 Remove last mutable variable from do_set_draft 2019-09-21 06:19:21 +00:00
Dmitry Bogatov
0523868a88 Avoid ok-to-continue pattern in "do_set_draft" function
Note: I strongly suggest reviewing this commit in side-by-side mode.

Note: This commit fails CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-21 06:16:15 +00:00
Dmitry Bogatov
26f176eb7e Factor another part of set_draft_raw into separate function 2019-09-21 06:04:55 +00:00
Dmitry Bogatov
ad32b5ca8f cargo-fmt 2019-09-21 05:44:31 +00:00
Dmitry Bogatov
dbf14179dc Make message argument to set_draft_raw() no longer optional
Previously, "set_draft_raw" function was used with "msg = None" only for
side-effect of removing current draft message in chat.

After draft removal functionality was factored into separate
"maybe_delete_draft" function, it is used directly in only call site,
which used to invoke "set_draft_raw" with optional message.

This function is private, and this refactoring does not change FFI
interface.

Note: This commit fails CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-21 05:34:48 +00:00
Dmitry Bogatov
45b0a4ec27 Factor part of set_draft_raw into new function maybe_delete_draft 2019-09-21 05:24:03 +00:00
dignifiedquire
1969ee02a5 refactor(mimeparser): rustify mailmime_get_type 2019-09-21 00:51:36 +02:00
dignifiedquire
266b205c75 refactor: rustify interface of maimlime_transfer_decode 2019-09-21 00:51:36 +02:00
Dmitry Bogatov
7dd3bad8bd Add factory method MimeFactory::new
* src/dc_mimefactory.rs(new): add factory method to have verbose
   initialization of all (more than 10) MimeFactory fields only in one place.
 * src/dc_mimefactory.rc(dc_mimefactory_load_msg, dc_mimefactory_load_mdn):
   simplify code (and reduce linecount) using Mimefactory::new
2019-09-20 22:49:33 +02:00
holger krekel
4b45be7cda cargo fmt only 2019-09-20 22:43:20 +02:00
holger krekel
497ffd86fa make logic and comments more like C (early returns instead of nestedness)
next commit: cargo fmt
2019-09-20 22:43:20 +02:00
dignifiedquire
0bdcc4269f refactor(mimeparser): split and cleanup parse_mime_recursive 2019-09-20 21:42:23 +02:00
Friedel Ziegelmayer
e583c99f94 Make return type of Image::mv an enum (#548)
Make return type of Image::mv an enum
2019-09-20 20:56:27 +02:00
Friedel Ziegelmayer
e22e50c3fa Rename dc_mimefactory_t -> MimeFactory (#563)
Rename dc_mimefactory_t -> MimeFactory
2019-09-20 20:56:12 +02:00
Dmitry Bogatov
e9c9a3e1ce Change type of MimeFactory.loaded to enum
* src/dc_mimefactory.rs(MimeFactory): change type of `loaded` field
 * src/dc_mimefactory.rs(Loaded): new enum, describing possible
   values of `loaded` field of `MimeFactory` structure
 * src/dc_mimefactory.rs(dc_mimefactory_loaded_t): remove unused type alias
 * src/job.rs(add_smtp_job): adjust call site by removing multiple casts
 * src/dc_mimefactory.rs(dc_mimefactory_render): ditto
2019-09-20 18:02:57 +00:00
Dmitry Bogatov
fa7bb71f3f Change type of MimeFactory.out_{gossip,encrypted} to bool 2019-09-20 18:02:57 +00:00
Dmitry Bogatov
34a3ad82e0 Rename dc_mimefactory_t -> MimeFactory
CamelCase naming convention is more natural for Rust.

https://rust-lang-nursery.github.io/api-guidelines/naming.html
2019-09-20 18:02:57 +00:00
Dmitry Bogatov
2f5d74dbf4 Remove unused constants from src/imap.rs 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
4bf5ba594c Make Imap::set_mdnseen return enum, not int 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
6e2da27f45 Change return type of Imap::set_seen to enum 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
6ee9465d43 Make return type of Image::mv an enum
Replace named constants with enum to improve type-safety and make
exhausiveness checks possible.

Note, that since this enum never pass FFI border, its numeric values
does not need to be specified explicitly and can be left on compiler's
discretion.
2019-09-20 17:35:25 +00:00
Dmitry Bogatov
391a6bf422 Replace numbers with named constants 2019-09-20 17:35:25 +00:00
Floris Bruynooghe
5001a0e37d Fix dc_make_rel_path
This was not substituting $BLOBDIR correctly.
2019-09-20 18:38:39 +02:00
holger krekel
fd8d16a7db replace weird pointer-loops with nice for-loops (thanks @dignifiedquire for guiding) 2019-09-20 17:52:07 +02:00
holger krekel
f3ac9306f3 use bool instead of int 2019-09-20 17:52:07 +02:00
holger krekel
59740d0b56 remove unused var, numbers to const-names 2019-09-20 17:52:07 +02:00
holger krekel
fb05a6c26f transfer docs to and cleanup some parts of e2ee::decrypt() 2019-09-20 17:52:07 +02:00
holger krekel
7943b708d2 dc_mimeparser: do a round of renames on numbers to constants and add comments from the C code 2019-09-20 15:23:34 +02:00
dignifiedquire
04e37d1eca chore: update mmime to released version and other deps 2019-09-20 12:02:08 +02:00
holger krekel
91b98e8c6d as discussed during camp and otherwise ... add dc_perform_{mvbox,sentbox}_jobs hooks which, however, for now have an empty implementation. They can already be called from UIs, though. Next step is refactoring imap-job handling to only execute jobs belonging to the respective imap folder. 2019-09-20 01:03:25 +02:00
Floris Bruynooghe
70234e5b19 Add a test for fix in #541
A fixed bug should have a test.  This is an easy test to write.
2019-09-20 00:46:54 +02:00
holger krekel
ceff85d892 add test and python API for verified group handling/chatting
add msg.is_encrypted() API and check for it from some tests
2019-09-20 00:33:33 +02:00
holger krekel
9f914dd42e fix test-state modification bug (online tests running after OnlineConfigureFails tests would break) 2019-09-19 23:01:51 +02:00
holger krekel
24f5d68fef add configure-failure tests 2019-09-19 23:01:51 +02:00
Dmitry Bogatov
735fc325b1 Add test for export_key_to_asc_file 2019-09-19 21:43:54 +02:00
Dmitry Bogatov
178b216e48 Reduce number of arguments of export_key_to_asc_file
Previously, this function accepted both key id (integer) and is_default
(boolean). If `is_default` flag was set, value `id` parameter wasn't
used. This commit compresses two argument into single `id: Option<i64>`.
2019-09-19 21:43:54 +02:00
Simon Laux
5d0481f7a2 use provider overview crate instead of git 2019-09-19 20:07:34 +02:00
holger krekel
711bc69750 address @dignifiedquire comment 2019-09-19 20:03:16 +02:00
holger krekel
7263c9490d refactor rfc724_mid parsing and creation to avoid char*, add tests 2019-09-19 20:03:16 +02:00
holger krekel
0c88bc6ac7 more rfc724_mid cleanup 2019-09-19 20:03:16 +02:00
holger krekel
fda8d0a2e2 factory.rfc724_mid is a String now (instead of C-Char*) 2019-09-19 20:03:16 +02:00
holger krekel
14bdf7fae8 make dc_create_outgoing_rfc724_mid safe and simplify call sites 2019-09-19 20:03:16 +02:00
holger krekel
d4ff7ecbaa split qr tests 2019-09-19 20:00:27 +02:00
Dmitry Bogatov
030aec7373 Read example GPG keys from files using include_str!
For consistency with other tests that use example public and private
keys, replace use of `concat!` macro with `include_str!`. This way, key
data embedded into source with single line of code.
2019-09-19 18:57:39 +02:00
B. Petersen
6a351de4f9 cargo fmt 2019-09-19 18:42:29 +02:00
B. Petersen
bbff1c9c3e test that bad credentials do not panic 2019-09-19 18:42:29 +02:00
B. Petersen
bbb8144129 do not panic when 0 (=failure) is passed to the progress! macro 2019-09-19 17:00:01 +02:00
Friedel Ziegelmayer
83f3e23297 improve python caching (#468)
(@dignifiedquire and @hpk42) 
- introduce rust-caching to python test runs 
- skip release and ffi runs, they are check using python bindings
- shuffle files such that ci_scripts/ contains all the ci scripts
- partly parallelize python tox runs
2019-09-19 13:10:19 +02:00
B. Petersen
4f880932ae change oder of typedefs, function-definitions and defines
as c is top-down, we start with the typedefs
so that functions can use them freely.
after that, the functions are declared
and finally the #defines (for less noise in the function sections).
grouping is done by classes.
2019-09-19 12:58:26 +02:00
B. Petersen
62e8c2497c add missing doxygen commands, fix typo 2019-09-19 12:58:26 +02:00
Simon Laux
37f854be3e Add provider info functions to the FFI API
A lot of work from @Hocuri and @Simon-Laux mostly.

This exposes the API of the deltachat-provider-overview crate on the
deltachat FFI API, allowing clients to use it to help users set up
their accounts.
2019-09-19 07:17:44 +02:00
Dmitry Bogatov
e0e82e1877 Fix ffi interoperability issue
Clients expect empty "dbfile" argument to be treated as NULL value.
This change fulfills their expectations.

Closes: #530
2019-09-19 07:15:35 +02:00
Dmitry Bogatov
95d8665dbe Refine signature of dc_get_oauth2_access_token()
Previously, `dc_get_oauth2_access_token` accepted "flags" argument,
that actually had only one possible field: 0x1 == DC_REGENERATE.

This change replaces "flags" argument with single boolean argument
"regenerate".
2019-09-18 19:29:39 +02:00
dignifiedquire
8667de994e use proper Result instead of Option for encryption and decryption 2019-09-18 19:21:41 +02:00
holger krekel
cee0e22ce7 fix #538 -- don't crash on wrong setup codes for ac-message, don't use "expect(), added test 2019-09-18 19:21:41 +02:00
holger krekel
dc8a2f54e5 use utf8 lossy strings for saved mime headers (as discussed on PR and IRC with @r10s) 2019-09-18 16:47:04 +02:00
dignifiedquire
b3bc5b2520 fix(receive_imf): do not attempt to convert raw body to utf8 2019-09-18 16:47:04 +02:00
B. Petersen
00e929afac reformat: use shorter lines (max. 80 chats) and try to add linebreaks semantically 2019-09-18 15:20:34 +02:00
Dmitry Bogatov
8a3bf6a5d9 Remove use of `dbg!' macro
According to documentation[^1], this macro should not be in version
control, and should only be used for debugging.

https://doc.rust-lang.org/std/macro.dbg.html
2019-09-18 14:35:49 +02:00
Dmitry Bogatov
d5f361d386 Use include_str! macro instead of embedding key into source
Additionally, this change reduces duplication: now test public key is
stored in only one place, and used in two instead of copy-paste of very
long line.
2019-09-18 14:35:49 +02:00
Dmitry Bogatov
7bb4a27b60 Make Context.cb field private (no code changes) 2019-09-18 14:35:49 +02:00
holger krekel
3bd36feede fix broken rust mimeparser fuzzy tests 2019-09-18 14:15:16 +02:00
holger krekel
fc1f1ce37c streamline fixtures for online accounts, reducing test functions 2019-09-18 14:15:16 +02:00
holger krekel
ee327dc87d remove an ancient hack on context-teardown, refine and use more regular API for finalizing online accounts 2019-09-18 14:15:16 +02:00
Dmitry Bogatov
d644ca5563 Add property test for simplify_plain_text 2019-09-18 07:34:56 +02:00
Dmitry Bogatov
b3b1e37192 Add tests for dc_dehtml 2019-09-18 07:34:56 +02:00
Dmitry Bogatov
38f39c8d32 Add smoke test for Context.get_fresh_msgs 2019-09-18 07:34:56 +02:00
Dmitry Bogatov
a773b7929c Add fuzzy-test for dc_mimeparser_parser 2019-09-18 07:33:22 +02:00
Dmitry Bogatov
7b73103133 Add test for mime parsing reportedly crash-inducing message 2019-09-18 07:33:22 +02:00
holger krekel
b6803191cb Merge branch 'refactor/remove-mprintf' 2019-09-17 16:23:25 +02:00
holger krekel
2b038a34c9 Merge branch 'master' into refactor/remove-mprintf 2019-09-17 15:46:05 +02:00
B. Petersen
8c2c3f8bee move spec.md from separate repo here 2019-09-17 10:49:34 +02:00
dignifiedquire
e710836276 cleanup and fix earlier introduced scoping error 2019-09-16 22:58:10 +02:00
dignifiedquire
6be3c9a48a refactor: improve mime field lookup 2019-09-16 22:58:10 +02:00
dignifiedquire
c0747bf68d refactor: use enum for system messages 2019-09-16 22:58:10 +02:00
dignifiedquire
0346dd15d9 refactor(mimeparser): some more sanity 2019-09-16 22:58:10 +02:00
dignifiedquire
ff0aa8423d refactor: remove now unused build.rs and extern declarations 2019-09-16 07:59:41 +02:00
dignifiedquire
0f718e0d08 refactor(mimefactory): remove dc_mprintf 2019-09-16 07:59:41 +02:00
dignifiedquire
9534a9ad30 refactor(mimeparser): remove dc_mprintf 2019-09-16 07:59:41 +02:00
dignifiedquire
d091857cef refactor(imex): remove dc_mprintf 2019-09-16 07:59:41 +02:00
dignifiedquire
72a9ca0aa5 refactor(receive_imf): remove dc_mprintf 2019-09-16 07:58:08 +02:00
dignifiedquire
ecaae42b80 refactor(examples): remove dc_mprintf 2019-09-16 07:58:08 +02:00
dignifiedquire
84bf1ec6e7 refactor(tools): no more dc_mprintf 2019-09-16 07:58:08 +02:00
dignifiedquire
0bf3d20e07 fix and implement ffi tranlation 2019-09-15 23:40:08 +02:00
dignifiedquire
5486ac5b9f refacor: use an enum for events 2019-09-15 23:40:08 +02:00
Alexander Krotov
ac12b2e643 chore(key): remove unused *fingerprint_c functions 2019-09-15 19:32:16 +02:00
dignifiedquire
16c281a9b7 refactor(context): safe interface 2019-09-15 16:36:31 +02:00
dignifiedquire
413e3eb62d apply more CR 2019-09-15 16:36:31 +02:00
dignifiedquire
f31f341a50 feat: enforce Debug implementations and remove mod types 2019-09-15 16:36:31 +02:00
dignifiedquire
c2501258b6 apply CR feedback 2019-09-15 16:36:31 +02:00
dignifiedquire
de1e3e1d4f refactor(context): remove last unsafe bits from the context struct 2019-09-15 16:36:31 +02:00
Floris Bruynooghe
a3f64d4e95 Handle new Context creation API on the FFI
This adopts the FFI API to use the new Context::new() and
Contest::drop() Rust-style APIs while preserving the semantics of the
existing FFI API.  It introduces a wrapper around the context which
keeps an optional reference to the actual context, changing dc_open()
and dc_close() to create/destroy the actual reference.

This changes the events emitted on closing slightly: they only get
emitted when dc_close() was called while the context was open.  While
dc_close() remains idempotent in that it can still be called safely
multiple times, the close events will only occur if the context is
actually closed.
2019-09-15 02:37:13 +02:00
Floris Bruynooghe
afc9a31080 Remove dc_open call
A new context is now created by calling Context::new and therefore you
always have a valid context.  This is much more in Rust style and will
allow a lot of furture simplifications on the context itself.

The FFI layer has not yet been adjusted in this commit and thus will
fail.
2019-09-15 02:37:13 +02:00
Simon Laux
e5699e8ba9 Merge pull request #517 from deltachat/fix_get_draft
Fix get_draft to just return null and not produce errors if there's no
2019-09-14 23:39:09 +02:00
Floris Bruynooghe
8a7143b791 Return a Result<Option<Message>> for get_draft
The function can fail, so we need to still have an error return as
well as a no-draft return.

Add tests.
2019-09-14 23:00:13 +02:00
björn petersen
79d23909b5 fix 'this message is not encrypted' error in verified groups (#522) 2019-09-14 18:55:19 +02:00
Alexander Krotov
24fe4740d3 Merge pull request #498 from deltachat/return_bool
Resolve "should return bool" TODOs
2019-09-14 14:31:50 +00:00
Alexander Krotov
1e1d3f4aa8 Merge pull request #521 from deltachat/server_flags
Restore DC_LP_* constants from C code
2019-09-14 14:19:02 +00:00
Alexander Krotov
51bf875826 Use DC_LP_* constants in configure module 2019-09-14 13:22:59 +03:00
Alexander Krotov
f9ce6c7c81 Use DC_LP_* constants in auto_outlook.rs 2019-09-14 13:16:21 +03:00
Alexander Krotov
2e91c3d334 Use DC_LP_* constants in auto_mozilla.rs 2019-09-14 13:14:35 +03:00
Alexander Krotov
d1b762af04 Make DC_LP_* constants public 2019-09-14 13:14:18 +03:00
Alexander Krotov
e1e02839d1 Return bool from dc_mimefactory_render 2019-09-14 12:06:33 +03:00
Alexander Krotov
50a812ea5e Return bool from mailmime_transfer_decode 2019-09-14 12:03:10 +03:00
Alexander Krotov
27a4adb9c6 Return bool from export_key_to_asc_file 2019-09-14 12:03:10 +03:00
Alexander Krotov
3932d8f48f Return bool from export_self_keys 2019-09-14 12:03:10 +03:00
Alexander Krotov
d978a8e0a2 Return bool from {import,export}_backup 2019-09-14 12:03:10 +03:00
Alexander Krotov
fad49e08d7 message.rs: resolve "should return bool" TODOs 2019-09-14 12:03:10 +03:00
Alexander Krotov
0f67b16f29 Return bool from add_contact_to_chat_ex 2019-09-14 12:03:10 +03:00
Alexander Krotov
97edd23910 Return bool from add_to_chat_contacts_table 2019-09-14 12:03:10 +03:00
Alexander Krotov
400ab2cdab Return bool from dc_msg_get_showpadlock 2019-09-14 12:03:10 +03:00
jikstra
db20ecf985 Remove println 2019-09-13 22:35:31 +02:00
jikstra
e59f421e79 cargo fmt 2019-09-13 22:25:18 +02:00
jikstra
75b7de712a Fix get_draft to just return null and not produce errors if there's no
draft
2019-09-13 18:19:52 +02:00
Alexander Krotov
fa8192177d Merge pull request #514 from deltachat/in_reply_to
Store In-Reply-To header as Option<String>
2019-09-13 10:21:32 +00:00
Alexander Krotov
48ffef7955 Store In-Reply-To header as Option<String> 2019-09-13 11:43:48 +03:00
Alexander Krotov
055796e6b3 Merge pull request #496 from KAction/safe
Make dc_msg_get_file return PathBuf, not char*
2019-09-13 00:43:28 +00:00
B. Petersen
34433c4962 support additional placeholder in stock-string 2019-09-12 23:44:40 +02:00
Alexander Krotov
6d1076e1f6 Mark dc_imex() as safe 2019-09-12 23:43:25 +02:00
Dmitry Bogatov
bb4081e503 Rename query_row_col to query_get_value
Since function `query_row_col` no longer accept column number argument,
it is misleading to mention column in its name.
2019-09-12 18:42:12 +00:00
Dmitry Bogatov
bef25ad5f6 Avoid excessive allocation in dc_search_msgs 2019-09-12 17:12:26 +00:00
Dmitry Bogatov
f47652e72d Change argument type of write_asc_to_file from const char * to &Path 2019-09-12 17:12:23 +00:00
Dmitry Bogatov
7c4d7fb3dd Make Aheader::from_imffields accept &str, not const char * 2019-09-12 06:32:13 +00:00
Dmitry Bogatov
cdfc7281d0 Simplify Sql.query_row_col function
Function `query_row` executes query and calls callback function to process
row returned. Function query_row_col() is special case, that provides
callback function, which returns value of one particular column of row,
ignoring others.

In all cases, that particular column was 0 (first and only column of
query result), since there is no point to select more than one column
with this function -- they are discarded anyway.

This commit removes that redundancy, removing column number argument of
query_row_col() function and adjusting call sites accordingly.
2019-09-12 05:01:06 +00:00
Dmitry Bogatov
500e80784a Replace some of context.sql.get_config() with context.get_config()
Pattern `context.sql.get_config(context, {foo})` is unnecessary
redundant in Rust: unlike C, Rust has associated functions (methods).
2019-09-12 04:41:06 +00:00
Dmitry Bogatov
c5803d9b4c Remove useless catch-all pattern-matching 2019-09-12 04:17:00 +00:00
Dmitry Bogatov
ebac00fbde Use safe version of dc_write_file
* src/key.rs(write_asc_to_file): simplify code using safe version of
   dc_write_file() function
2019-09-12 04:11:44 +00:00
Dmitry Bogatov
f198ce29b1 Drop unsafe version of dc_get_abs_path 2019-09-12 04:11:43 +00:00
Dmitry Bogatov
df47e0ed63 Use safe version of dc_get_abs_path.
* src/dc_mimefactory.rs(build_body_file): use safe version of
   dc_get_abs_path() function.
2019-09-12 04:10:05 +00:00
Dmitry Bogatov
477470ff70 Make dc_msg_get_file return PathBuf, not char*
Adjust call sites as apporiate:

 * src/dc_imex.rs(dc_continue_key_transfer): use if-let pattern
   with dc_read_file_safe() and dc_msg_get_file()

 * src/message.rs(dc_msg_get_setupcodebegin): ditto

 * src/message.rs(dc_get_msg_info): simplify code to print information
   about file inside a message.

 * src/message.rs(dc_msg_get_file): simplify function using
   dc_get_abs_path_safe()

 * deltachat-ffi/src/lib.rs(dc_msg_get_file): convert PathBuf to `char *`
   to preserve C API
2019-09-12 04:10:05 +00:00
Floris Bruynooghe
aefddf7f5e Remove context refrence from Contact struct
This will allow the Rust Context API to be refactored without breaking
the C API.  Full details in #476 aka
a0b5e32f98
2019-09-11 23:53:48 +02:00
Floris Bruynooghe
5ce27b16f1 Remove context refernce from Chatlist
See #476 aka a0b5e32f98 for full
rationale.  Tl;dr it allows us to evolve the Rust API while keeping
the FFI API identical.
2019-09-11 23:53:48 +02:00
Floris Bruynooghe
8302d6833d Remove context ref from Chat struct
Leaving the C API untouched.  See #476 aka
a0b5e32f98 for more extensive rationale.
2019-09-11 23:53:48 +02:00
holger krekel
649c2eb676 try running qr tests in a new process instead of with all the other tests 2019-09-11 22:42:51 +02:00
Floris Bruynooghe
a0b5e32f98 Remove the context reference from Message struct
The Message struct had a reference to the context which made a few
APIs a little easier.  However it has surprising consequences a long
way down the line as shown in #335: it means any object which has such
a reference needs to keep open a lock if we want to do this refactor
of no longer having a "closed" Context struct on the Rust API (which
has many benefits which will simply that Context struct and is more
the Rust way - RAII etc).

By refactoring away the context reference on the rust API as done in
here however, we push this behaviour of how these references are
handled back to the C-API pointer behaviour: that is unsafe but just
works in a C-like way.  The resulting complexity in the FFI layer is
also notably less than in the #335 alternative.

As a consequence all APIs which require the context, now explicitly
need to get the context passed in as an argument.  It looks like this
is certainly no downside and maybe even beneficial for further API
refactors.

For this strategy to work out the same should be done to
dc_chatlist_t, dc_chat_t and dc_contact_t.  But this working for
dc_msg_t give a reasonable confidence that this is a good approach.
2019-09-11 21:48:40 +02:00
björn petersen
e73671a6ff Merge pull request #485 from deltachat/really-fix-477
do not panic on bad-utf-8
2019-09-11 20:14:48 +02:00
björn petersen
9503aca78d Merge branch 'master' into really-fix-477 2019-09-11 19:52:13 +02:00
björn petersen
dfaa8e4529 Merge pull request #490 from deltachat/fix/location-sql
fix(location): do not sql recurse in location sending
2019-09-11 19:40:46 +02:00
björn petersen
cbc3579e9a Merge pull request #489 from deltachat/no-recursive-delete
do not recursive delete folders
2019-09-11 19:39:15 +02:00
dignifiedquire
dd2e3d35fd fix(location): another nesting 2019-09-11 18:13:21 +02:00
dignifiedquire
ca9dccfcd7 fix(location): another nested sql 2019-09-11 17:57:59 +02:00
dignifiedquire
64b00fce7d fix(location): do not sql recurse in location sending 2019-09-11 17:46:05 +02:00
B. Petersen
177ab0229a do not call fs::remove_dir_all() implicitly on non-files; deleting folders is not needed and calling remove_dir_all() is considered harmful 2019-09-11 17:02:27 +02:00
Alexander Krotov
68b5e34fed Merge pull request #488 from KAction/transfer
Reduce indentation in dc_continue_key_transfer
2019-09-11 05:36:02 +00:00
Dmitry Bogatov
2eda839303 cargo-fmt 2019-09-11 02:21:39 +00:00
Dmitry Bogatov
71a01d3002 Reduce indentation in dc_continue_key_transfer
Replace `if (ok) { }` pattern with `if (!ok) return` to reduce
indentation level.

Note: This commit fails CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-11 02:21:39 +00:00
Alexander Krotov
995548002b Merge pull request #484 from link2xt/safe_test_encryption_decryption
Safe test encryption decryption
2019-09-11 00:43:21 +00:00
Alexander Krotov
38ad16887b Merge pull request #487 from KAction/top_evil
Minor refactoring
2019-09-11 00:42:48 +00:00
Dmitry Bogatov
c20e8f7613 Use safe version of dc_decode_header_words on one call site 2019-09-10 23:31:29 +00:00
Dmitry Bogatov
5bd4606854 Implement safe version of `dc_decode_header_words' 2019-09-10 23:26:08 +00:00
Dmitry Bogatov
e7b198849d Copy comment for dc_encode_headers_words from C code 2019-09-10 22:49:53 +00:00
Dmitry Bogatov
3ab0d74af2 Remove unused `unsafe' block in log_event! macro 2019-09-10 22:35:54 +00:00
Dmitry Bogatov
7ed5a8e72f Reimplement logging macros in terms of log_event!
Avoid copy-paste message formatting code in src/log.rs, forwarding all
arguments of info! warn! error! macros to generic log_event! macro.
2019-09-10 22:34:38 +00:00
Dmitry Bogatov
57daa0f7f0 Remove useless argument of logging macros
Previously, logging macros (info! warn! error!) accepted integer
argument (data1), that was passed to callback function verbatim. In all
call sites this argument was 0.

With this change, that data1 argument is no longer part of macro
interface, 0 is always passed to callback in internals of these macros.
2019-09-10 22:26:47 +00:00
Dmitry Bogatov
2dd3f169db Use set_config_bool instead of set_config_int in boolean context 2019-09-10 22:26:47 +00:00
Dmitry Bogatov
b97b618b4b Use sql.get_config_bool to simplify dc_is_configured 2019-09-10 22:26:47 +00:00
Dmitry Bogatov
bb12488200 Add Sql.{set,get}_config_bool methods
Previously, boolean configurations were implemented on top of i32
(get_config_int, set_config_it) at call sites.

Having one canonical location, containing boolean <-> database
conversion may help to avoid serialization issues.
2019-09-10 22:26:47 +00:00
Alexander Krotov
706a97b013 Move part of test_encryption_decryption to key.rs 2019-09-11 00:40:46 +03:00
Dmitry Bogatov
1cdb9c733a Change return type of `dc_is_configured' to bool 2019-09-10 19:08:55 +00:00
holger krekel
ffc525af9e pragmatismatic: run flaky tests three times to see if we can get more "green" CI runs this way ...
thanks to @the-compiler also modernize plugin usage
2019-09-10 20:51:52 +02:00
Dmitry Bogatov
1576dc1d13 top_evil_rs: check for all-caps version of ok_to_continue pattern 2019-09-10 17:34:17 +00:00
Dmitry Bogatov
4fbb5fbb25 Make it easier to run src/top_evil_rs.py from git root
Currently, `src/top_evil_rs.py' script recursively scans current
directory for Rust sources and print statistics about them.

When run from git root, it also scans target/ directory, which is
useless.

This commit add cludge that checks if script is run from git root
directory, and `chdir' into `src/' before performing actions.

Unprincipled, but convenient.
2019-09-10 17:34:16 +00:00
B. Petersen
e9da21a02e cargo fmt 2019-09-10 19:14:51 +02:00
B. Petersen
6c4d7ad8cc do not panic on bad-utf-8 2019-09-10 16:48:54 +02:00
Alexander Krotov
f1c026c5ec Pass passphrase to dc_pgp_symm_{en,de}crypt as &str 2019-09-10 15:58:42 +02:00
björn petersen
3d61c06ea9 Merge pull request #478 from deltachat/fix477
fix crash when msg_raw is None
2019-09-10 15:50:33 +02:00
Alexander Krotov
22c1ee1f55 cargo fmt 2019-09-10 16:29:41 +03:00
Alexander Krotov
971960a242 key.rs: remove unsafe Key.from_binary 2019-09-10 16:29:24 +03:00
Alexander Krotov
69f8973339 test_encryption_decryption: use safe from_slice instead of from_binary 2019-09-10 16:29:24 +03:00
B. Petersen
bf1d9b6d06 fix crash when msg_raw is None 2019-09-10 15:20:38 +02:00
Alexander Krotov
6a2368f83c Return bool from dc_continue_key_transfer 2019-09-10 15:15:48 +02:00
Alexander Krotov
d0960f7f7f Merge pull request #483 from link2xt/dc_msg_exists_safe
Make dc_msg_exists safe and rusty
2019-09-10 15:15:33 +03:00
Alexander Krotov
e5ad697466 Make dc_msg_exists safe and rusty 2019-09-10 14:21:27 +03:00
Alexander Krotov
188eab5faf Fix test_encryption_decryption
It is broken since 28cae607a4
2019-09-10 12:00:59 +02:00
holger krekel
2b257e3d0d fix ffi 2019-09-09 19:45:43 +02:00
Alexander Krotov
77c9746be5 Make dc_msg_get_filemime safe 2019-09-09 19:45:43 +02:00
Alexander Krotov
28cae607a4 Pass buffers to pgp.rs as slices 2019-09-09 18:50:47 +02:00
Simon Laux
814281ed7d fixes #463 2019-09-09 18:44:30 +02:00
holger krekel
5b0c8dd9dd address @r10s and @flub review comments, and fix some docstrings/test meta docs 2019-09-09 18:07:32 +02:00
holger krekel
650d8c45ec fix test, and cleanup according profile-image API 2019-09-09 18:07:32 +02:00
holger krekel
383d8980d6 add profile image API to python, tests, Rust fixes/cleanups 2019-09-09 18:07:32 +02:00
Simon Laux
6ea706c646 remove macro 2019-09-09 18:07:32 +02:00
Simon Laux
1ed2af08b8 cargo fmt 2019-09-09 18:07:32 +02:00
Simon Laux
7563a5abe0 remove closure
Co-authored-by: @Jikstra
2019-09-09 18:07:32 +02:00
Simon Laux
0a8b187f80 fix remove chat profile img 2019-09-09 18:07:32 +02:00
björn petersen
275aa853f5 Merge pull request #471 from deltachat/fix417
fix crash when downloading message
2019-09-09 16:18:50 +02:00
Friedel Ziegelmayer
3614d57f9f Merge pull request #469 from deltachat/refctor/strencode
refactor(strencode): rustify some strencode methods
2019-09-09 11:52:30 +02:00
B. Petersen
1367873949 check bounds before accessing Vec 2019-09-09 01:47:50 +02:00
B. Petersen
d933183e0a mark safe functions as such 2019-09-09 01:47:50 +02:00
B. Petersen
7e11def527 make code more readable 2019-09-09 00:01:15 +02:00
dignifiedquire
f3e53a05a6 refactor(loginparam): rename dc_loginparam -> login_param 2019-09-08 18:48:57 +02:00
dignifiedquire
dd381a5c1c refactor(loginparam): simplify and rustify 2019-09-08 18:48:57 +02:00
dignifiedquire
8eee449305 refactor(token): rustify 2019-09-08 18:35:20 +02:00
dignifiedquire
96e02af0da refactor: rename dc_token to token 2019-09-08 18:35:20 +02:00
dignifiedquire
60fb1478c3 refactor(strencode): rustify some strencode methods 2019-09-08 15:50:28 +02:00
Friedel Ziegelmayer
aa7d0679df fix(tools): make sure dc_truncate can handle arbitrary utf8 valu… (#460)
fix(tools): make sure dc_truncate can handle arbitrary utf8 values
2019-09-08 15:23:46 +02:00
dignifiedquire
8e3cc192a5 fix(tools): make sure dc_truncate can handle arbitrary utf8 values
also adds proptests to make sure this is upheld

Should close #433
2019-09-08 14:00:04 +02:00
Simon Laux
0f939995d1 fix(job): "invalid job action" check 2019-09-08 13:39:59 +02:00
björn petersen
2e1bc9b14e Merge pull request #458 from deltachat/fix-unknown
fix reading unknown origin
2019-09-08 12:24:51 +02:00
Friedel Ziegelmayer
fd1ac6ab2d Remove some free() from Mozilla autoconfig (#457)
Remove some free() from Mozilla autoconfig
2019-09-08 11:53:41 +02:00
Friedel Ziegelmayer
d224924dc8 Change type of function from `const char *' to &str (#451)
Change type of function from `const char *' to &str
2019-09-08 11:51:16 +02:00
dignifiedquire
00e5ddd6f0 make enum reading from the db more robust 2019-09-08 11:29:40 +02:00
B. Petersen
c603ca0e7a remove dead code 2019-09-08 07:26:07 +02:00
B. Petersen
d07ef01204 cargo fmt 2019-09-07 23:25:19 +02:00
B. Petersen
d8630b5029 fix reading of unknown/outdated origin 2019-09-07 22:21:38 +02:00
Alexander Krotov
3b397326f8 Store email parts as Rust str's 2019-09-07 20:23:08 +03:00
Alexander Krotov
81cabd08a9 Accept str instead of char* in read_autoconf_file 2019-09-07 20:20:25 +03:00
björn petersen
5663c7dec3 Merge pull request #453 from deltachat/fix-backup
Fix backup and housekeeping
2019-09-07 15:38:01 +02:00
björn petersen
06673b2108 Merge pull request #455 from deltachat/fix-msg-loading
Fix msg loading
2019-09-07 15:37:38 +02:00
dignifiedquire
6b7498a4b1 fix(contact): fix logic for create or add contact
Closes #448
2019-09-07 14:07:56 +02:00
B. Petersen
7f4ef493b9 be tolerant when reading unexpected NULL from the database and treat this as an empty string, compatible to core-c 2019-09-07 13:54:43 +02:00
B. Petersen
d9d0dee0d5 fix: use empty string for messages without text everywhere 2019-09-07 13:40:50 +02:00
B. Petersen
9605370f0b fix cmdline: sendimage and sendfile really accept only a file 2019-09-07 12:34:31 +02:00
Dmitry Bogatov
7d9fc682a0 cargo-fmt 2019-09-07 03:09:01 +00:00
Dmitry Bogatov
c4c08f2552 Remove ok_to_continue pattern from msg_prepare_raw()
This commit will fail CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-07 03:06:13 +00:00
Dmitry Bogatov
400740fdba Simplify prepare_msg_raw() using early return
This commit will fail CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-07 03:03:35 +00:00
Dmitry Bogatov
42bce7c0bf Remove last C string from prepare_msg_raw() 2019-09-07 02:54:28 +00:00
Dmitry Bogatov
a2281489a6 Create safe version of msgid-generating function 2019-09-07 02:35:13 +00:00
Dmitry Bogatov
9bf7b0bf96 Use more of Rust, less of C strings in prepare_msg_raw() 2019-09-07 01:39:26 +00:00
Dmitry Bogatov
1f82ba74aa Remove redundant checks in prepare_msg_raw() 2019-09-06 23:24:39 +00:00
Dmitry Bogatov
1062ac6ade Drop unsafe version of get_parent_mime_headers function 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
aa5304a4f3 Use safe version of `get_parent_mime_headers()' function 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
3a57ba1142 Implement safe version of `get_parent_mime_headers' function 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
c0e7293360 Change return type of clist_search_string_nocase to `bool' 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
dc1839760c Simplify clist_search_string_nocase using Iterator interface 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
a4e4b0fc17 Rustify type of dc_mimeparser_t.subject 2019-09-06 23:15:31 +00:00
Dmitry Bogatov
743e4deb36 Remove dc_mimepart_unref function
Since there is no longer any manually-managed memory, associated with
`dc_mimepart_t' structure, default Drop instances does everything
automatically.
2019-09-06 23:15:30 +00:00
Dmitry Bogatov
1d75f8478c Rustify type of dc_mimepart_t.msg_raw 2019-09-06 23:15:27 +00:00
B. Petersen
cc0428aa50 really check all rows when searching for referenced files 2019-09-06 15:15:06 +02:00
B. Petersen
4be481275f clearer naming 2019-09-06 14:32:17 +02:00
Dmitry Bogatov
28cfe36f43 Change type of dc_mimeparser_t.decryption_failed to bool 2019-09-06 03:48:41 +00:00
Dmitry Bogatov
e0df78c5f7 Change type of dc_mimepart_t.is_meta to bool 2019-09-06 03:41:18 +00:00
Dmitry Bogatov
4d8b058b65 Change type of dc_mimeparser_t.is_forwarded to bool 2019-09-06 03:36:54 +00:00
Dmitry Bogatov
da25611758 Change type of function from `const char *' to &str 2019-09-06 03:05:12 +00:00
B. Petersen
27732c85af mark actually safe function as such 2019-09-06 00:55:50 +02:00
B. Petersen
5ffc84eb59 remove unused functions 2019-09-06 00:55:50 +02:00
holger krekel
0a6e540394 rename dc_securejoin to securejoin.rs 2019-09-05 22:55:25 +02:00
holger krekel
9f09c73ec1 make secure_join flow more readable by using and adding a few macros, tiny api changes 2019-09-05 22:55:25 +02:00
holger krekel
4bbab876ae majorly de-indent code structure in secure_join by introducing cleanup function, also majorly reducing unsafety in several places 2019-09-05 22:55:25 +02:00
holger krekel
b2fafeff19 use some BOB and VC constants instead of raw numbers 2019-09-05 22:55:25 +02:00
holger krekel
79510a83de - remove many *libc::char usages, and c-pointer from fingerprint
- rustify get_chat_id_by_grpid and streamline returned chat id handling (thereby apprently fixing the test, don't ask)
2019-09-05 22:55:25 +02:00
holger krekel
dd0afdfeb0 add QR based join-group API, with test and SEGFAULT fix to rust 2019-09-05 22:55:25 +02:00
holger krekel
b6997c4455 regen constants and improve high level API for QR setup contact 2019-09-05 22:55:25 +02:00
holger krekel
2920732435 (dignifiedquire, hpk, jikstra)
- fix and test peerstate::from_fingerprint
- add and test python API for secure-join QR + setup-contact
2019-09-05 22:55:25 +02:00
jikstra
b9cfcce284 fix ffi 2019-09-05 22:55:25 +02:00
jikstra
70d997964b cargo fmt 2019-09-05 22:55:25 +02:00
jikstra
4ffe71e1df Fix encoding for email & name, fix qrencode command in repl 2019-09-05 22:55:25 +02:00
jikstra
cc2339fbe2 Fix closure in dc_securejoin, make sure we return an empty string and
never null, make dc_get_securejoin_qr return an Option<String> and move
the logic to cast it to c_str into the ffi
2019-09-05 22:55:25 +02:00
Alexander Krotov
8fb859c0c4 Merge pull request #445 from deltachat/smtp_error
Avoid panic on SMTP error
2019-09-05 20:22:24 +00:00
jikstra
5ff472dae0 Implement helper method to easily check if a bit flag is inside
listflags. Make Contact::get_all use it

Add method documentation and tests
2019-09-05 18:02:50 +02:00
holger krekel
6be4a6ed00 switch to counting ok_to_continue instead of current_blocks -- this still reflects structural problems or missing-rustification problems 2019-09-05 18:00:18 +02:00
Alexander Krotov
094d46293e Set Smtp.error when SMTP fails to send message 2019-09-05 05:29:37 +03:00
Alexander Krotov
c8d945db56 Store Smtp.error as Option<String>
Without this change, when SMTP password is incorrect,
as_str(sock.error) is called with a null pointer,
and as_str panics.

Now it does not crash when the error is not set.
2019-09-05 05:28:57 +03:00
Alexander Krotov
f78f0079c1 Upgrade to the latest version of imap crate 2019-09-04 20:59:54 +02:00
Dmitry Bogatov
98d6bdb48a Simplify control flow in dc_DC_JOB_SEND function
Replace `ok_to_continue' control flow variables with early return from
function, since there is no longer need to free memory manually.
2019-09-04 16:44:51 +02:00
Dmitry Bogatov
0391aebaeb Remove C pointer manipulation from do_DC_JOB_SEND()
This change removes several `unsafe' blocks by using safe version of
`dc_read_file' function.

Note, that logic is changed slightly: if get(Param::File) returns
Some(""), it no longer triggers "missing filename warnings".
2019-09-04 16:44:51 +02:00
Alexander Krotov
46520edd87 Print error if CAPABILITY command fails. 2019-09-04 15:12:00 +02:00
Simon Laux
14daa99802 remove ok_to_continue7 comment 2019-09-04 15:07:45 +02:00
Simon Laux
4f9f67a477 switch to while instead of loop 2019-09-04 15:07:45 +02:00
Simon Laux
85f182067c typo fixes in comments 2019-09-04 15:07:45 +02:00
Simon Laux
66ab6874f8 reorganize dc_job_do_DC_JOB_CONFIGURE_IMAP 2019-09-04 15:07:45 +02:00
Simon Laux
906b901e3d move function order / filestructure like in c core 2019-09-04 15:07:45 +02:00
Alexander Krotov
65adff4bdd Merge pull request #437 from KAction/spelling
Fix minor spelling errors in python/README.rst
2019-09-04 10:55:58 +00:00
Dmitry Bogatov
e60fc0dc30 Fix minor spelling errors in python/README.rst 2019-09-04 06:19:21 +00:00
Alexander Krotov
ffd719962c Merge pull request #430 from deltachat/latest-imap
Update to the latest rust-imap
2019-09-03 17:12:12 +00:00
dignifiedquire
1a1f0c0a7c refactor(e2ee): rename dc_e2ee -> e2ee 2019-09-03 19:05:21 +02:00
dignifiedquire
3944592c09 refactor(e2ee): restructure types a and method slightly 2019-09-03 19:05:21 +02:00
holger krekel
a5f862a564 remove python2 testing 2019-09-03 19:04:09 +02:00
Alexander Krotov
489f25940f Update to the latest rust-imap
It uses imap-proto 0.8.1 instead of outdated 0.7.0.
2019-09-03 19:14:56 +03:00
Dmitry Bogatov
6288909481 Refactor is_known_rfc724_mid() to use Iterator interface 2019-09-03 16:08:52 +02:00
Dmitry Bogatov
c95f134963 Change return type of is_known_rfc724_mid_in_list() to bool 2019-09-03 16:08:52 +02:00
Simon Laux
024c2883c0 cargo fmt 2019-09-03 15:32:36 +02:00
Simon Laux
d7b0ecad75 transform current_block to ok_to_continue 2019-09-03 15:32:36 +02:00
B. Petersen
50947b81c0 set correct default value for inbox_watch 2019-09-03 15:31:17 +02:00
holger krekel
e5a1b721f1 try use py36 for uploads (#429)
* try use py36 for uploads

* another try

* another
2019-09-03 15:30:28 +02:00
Alexander Krotov
aeb1a88e7a Remove dc_move.rs
and move dc_do_heuristics_moves to context.rs
2019-09-03 13:39:14 +02:00
Alexander Krotov
c406675d6a Mark dc_do_heuristics_moves as safe 2019-09-03 13:39:14 +02:00
Alexander Krotov
1ec193991b Make dc_token.rs safe 2019-09-02 20:57:22 +02:00
173 changed files with 30079 additions and 23690 deletions

View File

@@ -4,6 +4,9 @@ executors:
docker:
- image: filecoin/rust:latest
working_directory: /mnt/crate
doxygen:
docker:
- image: hrektts/doxygen
restore-workspace: &restore-workspace
attach_workspace:
@@ -12,7 +15,7 @@ restore-workspace: &restore-workspace
restore-cache: &restore-cache
restore_cache:
keys:
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- repo-source-{{ .Branch }}-{{ .Revision }}
commands:
@@ -23,20 +26,9 @@ commands:
steps:
- *restore-workspace
- *restore-cache
- setup_remote_docker:
docker_layer_caching: true
# TODO: move into image
- run:
name: Install Docker client
command: |
set -x
VER="18.09.2"
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run:
name: Test (<< parameters.target >>)
command: TARGET=<< parameters.target >> ci/run.sh
command: TARGET=<< parameters.target >> ci_scripts/run-rust-test.sh
no_output_timeout: 15m
jobs:
@@ -44,30 +36,24 @@ jobs:
executor: default
steps:
- checkout
- run:
name: Update submodules
command: git submodule update --init --recursive
- run:
name: Calculate dependencies
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- run: rustup install $(cat rust-toolchain)
- run: rustup default $(cat rust-toolchain)
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
- run: cargo update
- run: cargo fetch
- run: rustc +stable --version
- run: rustc +$(cat rust-toolchain) --version
- run: rm -rf .git
# make sure this git repo doesn't grow too big
- run: git gc
- persist_to_workspace:
root: /mnt
paths:
- crate
- save_cache:
key: cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
key: cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
paths:
- "~/.cargo"
- "~/.rustup"
@@ -98,11 +84,10 @@ jobs:
curl https://sh.rustup.rs -sSf | sh -s -- -y
- run: rustup install $(cat rust-toolchain)
- run: rustup default $(cat rust-toolchain)
- run: cargo update
- run: cargo fetch
- run:
name: Test
command: TARGET=x86_64-apple-darwin ci/run.sh
command: TARGET=x86_64-apple-darwin ci_scripts/run-rust-test.sh
test_x86_64-unknown-linux-gnu:
executor: default
@@ -123,27 +108,25 @@ jobs:
target: "aarch64-linux-android"
build_test_docs_wheel:
machine: True
build_doxygen:
executor: doxygen
steps:
- checkout
# - run: docker pull deltachat/doxygen
- run: docker pull deltachat/coredeps
- run:
name: build docs, run tests and build wheels
command: ci_scripts/ci_run.sh
environment:
TESTS: 1
DOCS: 1
- run:
name: copying docs and wheels to workspace
command: |
mkdir -p workspace/python
# cp -av docs workspace/c-docs
cp -av python/.docker-tox/wheelhouse workspace/
cp -av python/doc/_build/ workspace/py-docs
- run: bash ci_scripts/run-doxygen.sh
- run: mkdir -p workspace/c-docs
- run: cp -av deltachat-ffi/{html,xml} workspace/c-docs/
- persist_to_workspace:
root: workspace
paths:
- c-docs
remote_python_packaging:
machine: true
steps:
- checkout
# the following commands on success produces
# workspace/{wheelhouse,py-docs} as artefact directories
- run: bash ci_scripts/remote_python_packaging.sh
- persist_to_workspace:
root: workspace
paths:
@@ -151,15 +134,28 @@ jobs:
- py-docs
- wheelhouse
remote_tests_rust:
machine: true
steps:
- checkout
- run: ci_scripts/remote_tests_rust.sh
remote_tests_python:
machine: true
steps:
- checkout
- run: ci_scripts/remote_tests_python.sh
upload_docs_wheels:
machine: true
steps:
- checkout
- attach_workspace:
at: workspace
- run: pyenv versions
- run: pyenv global 3.5.2
- run: ls -laR workspace
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
clippy:
executor: default
@@ -168,7 +164,7 @@ jobs:
- *restore-cache
- run:
name: Run cargo clippy
command: cargo clippy --all
command: cargo clippy
workflows:
@@ -176,23 +172,49 @@ workflows:
test:
jobs:
- build_test_docs_wheel
# - cargo_fetch
- remote_tests_rust:
filters:
tags:
only: /.*/
- remote_tests_python:
filters:
tags:
only: /.*/
- remote_python_packaging:
requires:
- remote_tests_python
- remote_tests_rust
filters:
tags:
only: /.*/
- upload_docs_wheels:
requires:
- build_test_docs_wheel
- cargo_fetch
- rustfmt:
requires:
- cargo_fetch
- remote_python_packaging
- build_doxygen
filters:
tags:
only: /.*/
# - rustfmt:
# requires:
# - cargo_fetch
# - clippy:
# requires:
# - cargo_fetch
- clippy:
requires:
- cargo_fetch
- build_doxygen:
filters:
tags:
only: /.*/
# Linux Desktop 64bit
- test_x86_64-unknown-linux-gnu:
requires:
- cargo_fetch
# - test_x86_64-unknown-linux-gnu:
# requires:
# - cargo_fetch
# Linux Desktop 32bit
# - test_i686-unknown-linux-gnu:

4
.gitattributes vendored
View File

@@ -2,6 +2,10 @@
# ensures this even if the user has not set core.autocrlf.
* text=auto
# This directory contains email messages verbatim, and changing CRLF to
# LF will corrupt them.
test-data/* text=false
# binary files should be detected by git, however, to be sure, you can add them here explicitly
*.png binary
*.jpg binary

48
.github/workflows/code-quality.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
on: push
name: Code Quality
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2019-11-06
override: true
- uses: actions-rs/cargo@v1
with:
command: check
args: --workspace --examples --tests --all-features
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2019-11-06
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
run_clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2019-11-06
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features

3
.gitignore vendored
View File

@@ -16,9 +16,12 @@ python/.tox
*.egg-info
__pycache__
python/src/deltachat/capi*.so
python/.venv/
python/liveconfig*
# ignore doxgen generated files
deltachat-ffi/html
deltachat-ffi/xml
.rsynclist

396
CHANGELOG.md Normal file
View File

@@ -0,0 +1,396 @@
# Changelog
## 1.25.0
- save traffic by downloading only messages that are really displayed #1236
- change generated key type to Ed25519, these keys are much shorter
than RSA keys, which results in saving traffic and speed improvements #1287
- improve key handling #1237 #1240 #1242 #1247
- mute handling, apis are dc_set_chat_mute_duration()
dc_chat_is_muted() and dc_chat_get_remaining_mute_duration() #1143
- pinning chats, new apis are dc_set_chat_visibility() and
dc_chat_get_visibility() #1248
- add dc_provider_new_from_email() api that queries the new, integrated
provider-database #1207
- account creation by scanning a qr code
in the DCACCOUNT scheme (https://mailadm.readthedocs.io),
new api is dc_set_config_from_qr() #1249
- if possible, dc_join_securejoin(), returns the new chat-id immediately
and does the handshake in background #1225
- update imap and smtp dependencies #1115
- check for MOVE capability before using MOVE command #1263
- allow inline attachments from RFC 2183 #1280
- fix updating names from incoming mails #1298
- fix error messages shown on import #1234
- directly attempt to re-connect if the smtp connection is maybe stale #1296
- improve adding group members #1291
- improve rust-api #1261
- cleanup #1302 #1283 #1282 #1276 #1270-#1274 #1267 #1258-#1260
#1257 #1239 #1231 #1224
- update spec #1286 #1291
## 1.0.0-beta.24
- fix oauth2/gmail bug introduced in beta23 (not used in releases) #1219
- fix panic when receiving eg. cyrillic filenames #1216
- delete all consumed secure-join handshake messagess #1209 #1212
- rust-level cleanups #1218 #1217 #1210 #1205
- python-level cleanups #1204 #1202 #1201
## 1.0.0-beta.23
- #1197 fix imap-deletion of messages
- #1171 Combine multiple MDNs into a single mail, reducing traffic
- #1155 fix to not send out gossip always, reducing traffic
- #1160 fix reply-to-encrypted determination
- #1182 Add "Auto-Submitted: auto-replied" header to MDNs
- #1194 produce python wheels again, fix c/py.delta.chat
master-deployment
- rust-level housekeeping and improvements #1161 #1186 #1185 #1190 #1194 #1199 #1191 #1190 #1184 and more
- #1063 clarify licensing
- #1147 use mailparse 0.10.2
## 1.0.0-beta.22
- #1095 normalize email lineends to CRLF
- #1095 enable link-time-optimization, saves eg. on android 11 mb
- #1099 fix import regarding devicechats
- #1092 improve logging
- #1096 #1097 #1094 #1090 #1091 internal cleanups
## 1.0.0-beta.21
- #1078 #1082 ensure RFC compliance by producing 78 column lines for
encoded attachments.
- #1080 don't recreate and thus break group membership if an unknown
sender (or mailer-daemon) sends a message referencing the group chat
- #1081 #1079 some internal cleanups
- update imap-proto dependency, to fix yandex/oauth
## 1.0.0-beta.20
- #1074 fix OAUTH2/gmail
- #1072 fix group members not appearing in contact list
- #1071 never block interrupt_idle (thus hopefully also not on maybe_network())
- #1069 reduce smtp-timeout to 30 seconds
- #1066 #1065 avoid unwrap in dehtml, make literals more readable
## 1.0.0-beta.19
- #1058 timeout smtp-send if it doesn't complete in 15 minutes
- #1059 trim down logging
## 1.0.0-beta.18
- #1056 avoid panicking when we couldn't read imap-server's greeting
message
- #1055 avoid panicking when we don't have a selected folder
- #1052 #1049 #1051 improve logging to add thread-id/name and
file/lineno to each info/warn message.
- #1050 allow python bindings to initialize Account with "os_name".
## 1.0.0-beta.17
- #1044 implement avatar recoding to 192x192 in core to keep file sizes small.
- #1024 fix #1021 SQL/injection malformed Chat-Group-Name breakage
- #1036 fix smtp crash by pulling in a fixed async-smtp
- #1039 fix read-receipts appearing as normal messages when you change
MDN settings
- #1040 do not panic on SystemTimeDifference
- #1043 avoid potential crashes in malformed From/Chat-Disposition... headers
- #1045 #1041 #1038 #1035 #1034 #1029 #1025 various cleanups and doc
improvments
## 1.0.0-beta.16
- alleviate login problems with providers which only
support RSA1024 keys by switching back from Rustls
to native-tls, by using the new async-email/async-native-tls
crate from @dignifiedquire. thanks @link2xt.
- introduce per-contact profile images to send out
own profile image heuristically, and fix sending
out of profile images in "in-prepare" groups.
this also extends the Chat-spec that is maintained
in core to specify Chat-Group-Image and Chat-Group-Avatar
headers. thanks @r10s and @hpk42.
- fix merging of protected headers from the encrypted
to the unencrypted parts, now not happening recursively
anymore. thanks @hpk and @r10s
- fix/optimize autocrypt gossip headers to only get
sent when there are more than 2 people in a chat.
thanks @link2xt
- fix displayname to use the authenticated name
when available (displayname as coming from contacts
themselves). thanks @simon-laux
- introduce preliminary support for offline autoconfig
for nauta provider. thanks @hpk42 @r10s
## 1.0.0-beta.15
- fix #994 attachment appeared doubled in chats (and where actually
downloaded after smtp-send). @hpk42
## 1.0.0-beta.14
- fix packaging issue with our rust-email fork, now we are tracking
master again there. hpk42
## 1.0.0-beta.13
- fix #976 -- unicode-issues in display-name of email addresses. @hpk42
- fix #985 group add/remove member bugs resulting in broken groups. @hpk42
- fix hanging IMAP connections -- we now detect with a 15second timeout
if we cannot terminate the IDLE IMAP protocol. @hpk42 @link2xt
- fix incoming multipart/mixed containing html, to show up as
attachments again. Fixes usage for simplebot which sends html
files for users to interact with the bot. @adbenitez @hpk42
- refinements to internal autocrypt-handling code, do not send
prefer-encrypt=nopreference as it is the default if no attribute
is present. @linkxt
- simplify, modularize and rustify several parts
of dc-core (general WIP). @link2xt @flub @hpk42 @r10s
- use async-email/async-smtp to handle SMTP connections, might
fix connection/reconnection issues. @link2xt
- more tests and refinements for dealing with blobstorage @flub @hpk42
- use a dedicated build-server for CI testing of core PRs
## 1.0.0-beta.12
- fix python bindings to use core for copying attachments to blobdir
and fix core to actually do it. @hpk42
## 1.0.0-beta.11
- trigger reconnect more often on imap error states. Should fix an
issue observed when trying to empty a folder. @hpk42
- un-split qr tests: we fixed qr-securejoin protocol flakyness
last weeks. @hpk42
## 1.0.0-beta.10
- fix grpid-determination from in-reply-to and references headers. @hpk42
- only send Autocrypt-gossip headers on encrypted messages. @dignifiedquire
- fix reply-to-encrypted message to also be encrypted. @hpk42
- remove last unsafe code from dc_receive_imf :) @hpk42
- add experimental new dc_chat_get_info_json FFI/API so that desktop devs
can play with using it. @jikstra
- fix encoding of subjects and attachment-filenames @hpk42
@dignifiedquire .
## 1.0.0-beta.9
- historic: we now use the mailparse crate and lettre-email to generate mime
messages. This got rid of mmime completely, the C2rust generated port of the libetpan
mime-parse -- IOW 22KLocs of cumbersome code removed! see
https://github.com/deltachat/deltachat-core-rust/pull/904#issuecomment-561163330
many thanks @dignifiedquire for making everybody's life easier
and @jonhoo (from rust-imap fame) for suggesting to use the mailparse crate :)
- lots of improvements and better error handling in many rust modules
thanks @link2xt @flub @r10s, @hpk42 and @dignifiedquire
- @r10s introduced a new device chat which has an initial
welcome message. See
https://c.delta.chat/classdc__context__t.html#a1a2aad98bd23c1d21ee42374e241f389
for the main new FFI-API.
- fix moving self-sent messages, thanks @r10s, @flub, @hpk42
- fix flakyness/sometimes-failing verified/join-protocols,
thanks @flub, @r10s, @hpk42
- fix reply-to-encrypted message to keep encryption
- new DC_EVENT_SECUREJOIN_MEMBER_ADDED event
- many little fixes and rustifications (@link2xt, @flub, @hpk42)
## 1.0.0-beta.8
- now uses async-email/async-imap as the new base
which makes imap-idle interruptible and thus fixes
several issues around the imap thread being in zombie state .
thanks @dignifiedquire, @hpk42 and @link2xt.
- fixes imap-protocol parsing bugs that lead to infinitely
repeated crashing while trying to receive messages with
a subjec that contained non-utf8. thanks @link2xt
- fixed logic to find encryption subkey -- previously
delta chat would use the primary key for encryption
(which works with RSA but not ECC). thanks @link2xt
- introduce a new device chat where core and UIs can
add "device" messages. Android uses it for an initial
welcome message. thanks @r10s
- fix time smearing (when two message are virtually send
in the same second, there would be misbehaviour because
we didn't persist smeared time). thanks @r10s
- fix double-dotted extensions like .html.zip or .tar.gz
to not mangle them when creating blobfiles. thanks @flub
- fix backup/exports where the wrong sql file would be modified,
leading to problems when exporting twice. thanks @hpk42
- several other little fixes and improvements
## 1.0.0-beta.7
- fix location-streaming #782
- fix display of messages that could not be decrypted #785
- fix smtp MAILER-DAEMON bug #786
- fix a logging of durations #783
- add more error logging #779
- do not panic on some bad utf-8 mime #776
## 1.0.0-beta.6
- fix chatlist.get_msg_id to return id, instead of wrongly erroring
## 1.0.0-beta.5
- fix dc_get_msg() to return empty messages when asked for special ones
## 1.0.0-beta.4
- fix more than one sending of autocrypt setup message
- fix recognition of mailto-address-qr-codes, add tests
- tune down error to warning when adding self to chat
## 1.0.0-beta.3
- add back `dc_empty_server()` #682
- if `show_emails` is set to `DC_SHOW_EMAILS_ALL`,
email-based contact requests are added to the chatlist directly
- fix IMAP hangs #717 and cleanups
- several rPGP fixes
- code streamlining and rustifications
## 1.0.0-beta.2
- https://c.delta.chat docs are now regenerated again through our CI
- several rPGP cleanups, security fixes and better multi-platform support
- reconnect on io errors and broken pipes (imap)
- probe SMTP with real connection not just setup
- various imap/smtp related fixes
- use to_string_lossy in most places instead of relying on valid utf-8
encodings
- rework, rustify and test autoconfig-reading and parsing
- some rustifications/boolifications of c-ints
## 1.0.0-beta.1
- first beta of the Delta Chat Rust core library. many fixes of crashes
and other issues compared to 1.0.0-alpha.5.
- Most code is now "rustified" and does not do manual memory allocation anymore.
- The `DC_EVENT_GET_STRING` event is not used anymore, removing the last
event where the core requested a return value from the event callback.
Please now use `dc_set_stock_translation()` API for core messages
to be properly localized.
- Deltachat FFI docs are automatically generated and available here:
https://c.delta.chat
- New events ImapMessageMoved and ImapMessageDeleted
For a full list of changes, please see our closed Pull Requests:
https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed

2756
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,32 @@
[package]
name = "deltachat"
version = "1.0.0-alpha.4"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
version = "1.25.0"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018"
license = "MPL"
license = "MPL-2.0"
[build-dependencies]
cc = "1.0.35"
pkg-config = "0.3"
[profile.release]
lto = true
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
libc = "0.2.51"
pgp = { version = "0.2", default-features = false }
hex = "0.3.2"
pgp = { version = "0.4.0", default-features = false }
hex = "0.4.0"
sha2 = "0.8.0"
rand = "0.6.5"
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
smallvec = "0.6.9"
reqwest = "0.9.15"
num-derive = "0.2.5"
rand = "0.7.0"
smallvec = "1.0.0"
reqwest = { version = "0.10.0", features = ["blocking", "json"] }
num-derive = "0.3.0"
num-traits = "0.2.6"
native-tls = "0.2.3"
lettre = "0.9.0"
imap = "1.0.1"
mmime = "0.1.0"
base64 = "0.10"
async-smtp = "0.2"
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
async-imap = "0.2"
async-native-tls = "0.3.1"
async-std = { version = "1.4", features = ["unstable"] }
base64 = "0.11"
charset = "0.1"
percent-encoding = "2.0"
serde = { version = "1.0", features = ["derive"] }
@@ -33,28 +34,38 @@ serde_json = "1.0"
chrono = "0.4.6"
failure = "0.1.5"
failure_derive = "0.1.5"
# TODO: make optional
rustyline = "4.1.0"
lazy_static = "1.3.0"
indexmap = "1.3.0"
lazy_static = "1.4.0"
regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
r2d2_sqlite = "0.12.0"
rusqlite = { version = "0.21", features = ["bundled"] }
r2d2_sqlite = "0.13.0"
r2d2 = "0.8.5"
strum = "0.15.0"
strum_macros = "0.15.0"
strum = "0.16.0"
strum_macros = "0.16.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
itertools = "0.8.0"
image-meta = "0.1.0"
quick-xml = "0.15.0"
quick-xml = "0.17.1"
escaper = "0.1.0"
bitflags = "1.1.0"
debug_stub_derive = "0.3.0"
sanitize-filename = "0.2.1"
stop-token = { version = "0.1.1", features = ["unstable"] }
mailparse = "0.10.2"
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
native-tls = "0.2.3"
image = { version = "0.22.4", default-features=false, features = ["gif_codec", "jpeg", "ico", "png_codec", "pnm", "webp", "bmp"] }
pretty_env_logger = "0.3.1"
rustyline = { version = "4.1.0", optional = true }
[dev-dependencies]
tempfile = "3.0"
pretty_assertions = "0.6.1"
pretty_env_logger = "0.3.0"
proptest = "0.9.4"
[workspace]
members = [
@@ -69,10 +80,11 @@ path = "examples/simple.rs"
[[example]]
name = "repl"
path = "examples/repl/main.rs"
required-features = ["rustyline"]
[features]
default = ["nightly", "ringbuf"]
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
vendored = ["async-native-tls/vendored", "reqwest/native-tls-vendored", "async-smtp/native-tls-vendored"]
nightly = ["pgp/nightly"]
ringbuf = ["pgp/ringbuf"]

View File

@@ -2,9 +2,6 @@ The files in this directory and under its subdirectories
are (c) 2019 by Bjoern Petersen and contributors and released under the
Mozilla Public License Version 2.0, see below for a copy.
NOTE that the files in the "libs" directory are copyrighted by third parties
and come with their own respective licenses.
Mozilla Public License Version 2.0
==================================

View File

@@ -1,11 +1,9 @@
# Delta Chat Rust
> Project porting deltachat-core to rust
> Deltachat-core written in Rust
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor]
Current commit on deltachat/deltachat-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`.
## Installing Rust and Cargo
To download and install the official compiler for the Rust programming language, and the Cargo package manager, run the command in your user environment:
@@ -16,7 +14,7 @@ curl https://sh.rustup.rs -sSf | sh
## Using the CLI client
Compile and run Delta Chat Core using `cargo`:
Compile and run Delta Chat Core command line utility, using `cargo`:
```
cargo run --example repl -- /path/to/db
@@ -89,6 +87,15 @@ $ cargo test --all
$ cargo build -p deltachat_ffi --release
```
## Debugging environment variables
- `DCC_IMAP_DEBUG`: if set IMAP protocol commands and responses will be
printed
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
### Expensive tests
Some tests are expensive and marked with `#[ignore]`, to run these
@@ -107,3 +114,22 @@ $ cargo test -- --ignored
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
## Language bindings and frontend projects
Language bindings are available for:
- [C](https://c.delta.chat)
- [Node.js](https://www.npmjs.com/package/deltachat-node)
- [Python](https://py.delta.chat)
- [Go](https://github.com/hugot/go-deltachat/)
- **Java** and **Swift** (contained in the Android/iOS repos)
The following "frontend" projects make use of the Rust-library
or its language bindings:
- [Android](https://github.com/deltachat/deltachat-android)
- [iOS](https://github.com/deltachat/deltachat-ios)
- [Desktop](https://github.com/deltachat/deltachat-desktop)
- [Pidgin](https://code.ur.gs/lupine/purple-plugin-delta/)
- several **Bots**

View File

@@ -1,6 +0,0 @@
[dependencies.std]
features = ["panic-unwind"]
# if using `cargo test`
[dependencies.test]
stage = 1

View File

@@ -8,12 +8,11 @@ install:
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -vV
- cargo -vV
- cargo update
build: false
test_script:
- cargo test --release
- cargo test --release --all
cache:
- target

BIN
assets/icon-device.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

83
assets/icon-device.svg Normal file
View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="409.60001"
inkscape:export-xdpi="409.60001"
inkscape:export-filename="/Users/bpetersen/projects/deltachat-core-rust/assets/icon-device.png"
version="1.0"
width="60"
height="60"
viewBox="0 0 45 45"
preserveAspectRatio="xMidYMid meet"
id="svg4344"
sodipodi:docname="icon-device.svg"
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
<defs
id="defs4348" />
<sodipodi:namedview
inkscape:snap-global="false"
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="1035"
id="namedview4346"
showgrid="false"
units="px"
inkscape:zoom="3.959798"
inkscape:cx="28.322498"
inkscape:cy="24.898474"
inkscape:window-x="45"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg4344" />
<metadata
id="metadata4336">
Created by potrace 1.15, written by Peter Selinger 2001-2017
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
y="-4.4408921e-16"
x="0"
height="45"
width="45"
id="rect860"
style="opacity:1;fill:#76868b;fill-opacity:1;stroke-width:0.819271" />
<g
fill="#000000"
stroke="none"
style="fill:#ffffff;fill-opacity:1"
transform="matrix(0.00255113,0,0,-0.00255113,5.586152,38.200477)"
id="g4342">
<path
style="fill:#ffffff;fill-opacity:1"
d="m 8175,12765 c -703,-114 -1248,-608 -1387,-1258 -17,-82 -21,-136 -22,-277 0,-202 15,-307 70,-470 149,-446 499,-733 1009,-828 142,-26 465,-23 619,6 691,131 1201,609 1328,1244 31,158 31,417 0,565 -114,533 -482,889 -1038,1004 -133,27 -448,35 -579,14 z"
id="path4338"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1"
d="m 7070,9203 c -212,-20 -275,-27 -397,-48 -691,-117 -1400,-444 -2038,-940 -182,-142 -328,-270 -585,-517 -595,-571 -911,-974 -927,-1181 -6,-76 11,-120 69,-184 75,-80 159,-108 245,-79 109,37 263,181 632,595 539,606 774,826 1035,969 135,75 231,105 341,106 82,1 94,-2 138,-27 116,-68 161,-209 122,-376 -9,-36 -349,-868 -757,-1850 -407,-982 -785,-1892 -838,-2021 -287,-694 -513,-1389 -615,-1889 -70,-342 -90,-683 -52,-874 88,-440 381,-703 882,-792 124,-23 401,-30 562,-16 783,69 1674,461 2561,1125 796,596 1492,1354 1607,1751 43,146 -33,308 -168,360 -61,23 -100,15 -173,-36 -105,-74 -202,-170 -539,-529 -515,-551 -762,-783 -982,-927 -251,-164 -437,-186 -543,-65 -56,64 -74,131 -67,247 13,179 91,434 249,815 135,324 1588,4102 1646,4280 106,325 151,561 159,826 9,281 -22,463 -112,652 -58,122 -114,199 -211,292 -245,233 -582,343 -1044,338 -91,-1 -181,-3 -200,-5 z"
id="path4340"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="409.60001"
inkscape:export-xdpi="409.60001"
inkscape:export-filename="/home/kerle/test-icon.png"
version="1.0"
width="60"
height="60"
viewBox="0 0 45 45"
preserveAspectRatio="xMidYMid meet"
id="svg4344"
sodipodi:docname="icon-saved-messages.svg"
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
<defs
id="defs4348" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1395"
inkscape:window-height="855"
id="namedview4346"
showgrid="false"
units="px"
inkscape:zoom="4"
inkscape:cx="29.308676"
inkscape:cy="49.03624"
inkscape:window-x="89"
inkscape:window-y="108"
inkscape:window-maximized="0"
inkscape:current-layer="svg4344"
inkscape:lockguides="false" />
<metadata
id="metadata4336">
Created by potrace 1.15, written by Peter Selinger 2001-2017
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
y="0"
x="0"
height="45"
width="45"
id="rect1420"
style="fill:#87aade;fill-opacity:1;stroke:none;stroke-width:0.968078" />
<path
id="rect846"
style="fill:#ffffff;stroke-width:0.58409804"
d="M 13.5,7.5 V 39 h 0.08654 L 22.533801,29.370239 31.482419,39 h 0.01758 V 7.5 Z m 9.004056,4.108698 1.879508,4.876388 5.039514,0.359779 -3.879358,3.363728 1.227764,5.095749 -4.276893,-2.796643 -4.280949,2.788618 1.237229,-5.093073 -3.873949,-3.371754 5.040866,-0.350417 z"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/welcome-image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -1,38 +0,0 @@
extern crate cc;
fn link_static(lib: &str) {
println!("cargo:rustc-link-lib=static={}", lib);
}
fn link_framework(fw: &str) {
println!("cargo:rustc-link-lib=framework={}", fw);
}
fn add_search_path(p: &str) {
println!("cargo:rustc-link-search={}", p);
}
fn build_tools() {
let mut config = cc::Build::new();
config.file("misc.c").compile("libtools.a");
println!("rerun-if-changed=build.rs");
println!("rerun-if-changed=misc.h");
println!("rerun-if-changed=misc.c");
}
fn main() {
build_tools();
add_search_path("/usr/local/lib");
let target = std::env::var("TARGET").unwrap();
if target.contains("-apple") || target.contains("-darwin") {
link_framework("CoreFoundation");
link_framework("CoreServices");
link_framework("Security");
}
// local tools
link_static("tools");
}

View File

@@ -1,52 +1,46 @@
# Continuous Integration Scripts for Delta Chat
Continuous Integration is run through CircleCI
but is largely independent of it.
Continuous Integration, run through CircleCI and an own build machine.
## Description of scripts
- `../.circleci/config.yml` describing the build jobs that are run
by Circle-CI
- `remote_tests_python.sh` rsyncs to a build machine and runs
`run-python-test.sh` remotely on the build machine.
- `remote_tests_rust.sh` rsyncs to the build machine and runs
`run-rust-test.sh` remotely on the build machine.
- `doxygen/Dockerfile` specifies an image that contains
the doxygen tool which is used by `run-doxygen.sh`
to generate C-docs which are then uploaded
via `ci_upload.sh` to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
(and the master branch is linked to https://c.delta.chat proper).
## Generating docker containers for performing build step work
## Triggering runs on the build machine locally (fast!)
All tests, docs and wheel building is run in docker containers:
There is experimental support for triggering a remote Python or Rust test run
from your local checkout/branch. You will need to be authorized to login to
the build machine (ask your friendly sysadmin on #deltachat freenode) to type::
- **coredeps/Dockerfile** specifies an image that contains all
of Delta Chat's core dependencies as linkable libraries.
It also serves to run python tests and build wheels
(binary packages for Python).
ci_scripts/manual_remote_tests.sh rust
ci_scripts/manual_remote_tests.sh python
- **doxygen/Dockerfile** specifies an image that contains
the doxygen tool which is used to generate C-docs.
This will **rsync** your current checkout to the remote build machine
(no need to commit before) and then run either rust or python tests.
To run tests locally you can pull existing images from "docker.io",
the hub for sharing Docker images::
# Outdated files (for later re-use)
docker pull deltachat/coredeps
docker pull deltachat/doxygen
`coredeps/Dockerfile` specifies an image that contains all
of Delta Chat's core dependencies. It used to run
python tests and build wheels (binary packages for Python)
or you can build the docker images yourself locally
You can build the docker images yourself locally
to avoid the relatively large download::
cd ci_scripts # where all CI things are
docker build -t deltachat/coredeps docker-coredeps
docker build -t deltachat/doxygen docker-doxygen
## ci_run.sh (main entrypoint called by circle-ci)
Once you have the docker images available
you can run python testing, documentation generation
and building binary wheels::
sh DOCS=1 TESTS=1 ci_scripts/ci_run.sh
## ci_upload.sh (uploading artifacts on success)
- python docs to `https://py.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
- doxygen docs to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
- python wheels to `https://m.devpi.net/dc/<BRANCH>`
so that you install fully self-contained wheels like this:
`pip install -U -i https://m.devpi.net/dc/<BRANCH> deltachat`

View File

@@ -1,22 +0,0 @@
# perform CI jobs on PRs and after merges to master.
# triggered from .circleci/config.yml
set -e -x
export BRANCH=${CIRCLE_BRANCH:-test7}
# run doxygen on c-source (needed by later doc-generation steps).
# XXX modifies the host filesystem docs/xml and docs/html directories
# XXX which you can then only remove with sudo as they belong to root
# XXX we don't do doxygen doc generation with Rust anymore, needs to be
# substituted with rust-docs
#if [ -n "$DOCS" ] ; then
# docker run --rm -it -v $PWD:/mnt -w /mnt/docs deltachat/doxygen doxygen
#fi
# run everything else inside docker (TESTS, DOCS, WHEELS)
docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
--rm -it -v $(pwd):/mnt -w /mnt \
deltachat/coredeps ci_scripts/run_all.sh

View File

@@ -7,9 +7,9 @@ fi
set -xe
#DOXYDOCDIR=${1:?directory where doxygen docs to be found}
PYDOCDIR=${1:?directory with python docs}
WHEELHOUSEDIR=${2:?directory with pre-built wheels}
DOXYDOCDIR=${3:?directory where doxygen docs to be found}
export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
@@ -17,15 +17,18 @@ export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
# python docs to py.delta.chat
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
rsync -avz \
--delete \
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
"$PYDOCDIR/html/" \
delta@py.delta.chat:build/${BRANCH}
# C docs to c.delta.chat
#rsync -avz \
# -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
# "$DOXYDOCDIR/html/" \
# delta@py.delta.chat:build-c/${BRANCH}
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
rsync -avz \
--delete \
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
"$DOXYDOCDIR/html/" \
delta@c.delta.chat:build-c/${BRANCH}
echo -----------------------
echo upload wheels
@@ -34,7 +37,7 @@ echo -----------------------
# Bundle external shared libraries into the wheels
pushd $WHEELHOUSEDIR
pip3 install -U setuptools
pip3 install -U pip
pip3 install devpi-client
devpi use https://m.devpi.net
devpi login dc --password $DEVPI_LOGIN
@@ -46,6 +49,9 @@ devpi use dc/$N_BRANCH || {
devpi use dc/$N_BRANCH
}
devpi index $N_BRANCH bases=/root/pypi
devpi upload deltachat*.whl
devpi upload deltachat*
popd
# remove devpi non-master dc indices if thy are too old
python ci_scripts/cleanup_devpi_indices.py

View File

@@ -0,0 +1,69 @@
"""
Remove old "dc" indices except for master which always stays.
"""
from requests import Session
import datetime
import sys
import subprocess
MAXDAYS=7
session = Session()
session.headers["Accept"] = "application/json"
def get_indexes(baseurl, username):
response = session.get(baseurl + username)
assert response.status_code == 200
result = response.json()["result"]
return result["indexes"]
def get_projectnames(baseurl, username, indexname):
response = session.get(baseurl + username + "/" + indexname)
assert response.status_code == 200
result = response.json()["result"]
return result["projects"]
def get_release_dates(baseurl, username, indexname, projectname):
response = session.get(baseurl + username + "/" + indexname + "/" + projectname)
assert response.status_code == 200
result = response.json()["result"]
dates = set()
for value in result.values():
if "+links" not in value:
continue
for link in value["+links"]:
for log in link["log"]:
dates.add(tuple(log["when"]))
return dates
def run():
baseurl = "https://m.devpi.net/"
username = "dc"
for indexname in get_indexes(baseurl, username):
projectnames = get_projectnames(baseurl, username, indexname)
if indexname == "master" or not indexname:
continue
assert projectnames == ["deltachat"]
for projectname in projectnames:
dates = get_release_dates(baseurl, username, indexname, projectname)
if not dates:
print(
"%s has no releases" % (baseurl + username + "/" + indexname),
file=sys.stderr)
date = datetime.datetime.now()
else:
date = datetime.datetime(*max(dates))
if (datetime.datetime.now() - date) > datetime.timedelta(days=MAXDAYS):
assert username and indexname
url = baseurl + username + "/" + indexname
subprocess.check_call(["devpi", "index", "-y", "--delete", url])
if __name__ == '__main__':
run()

View File

@@ -5,21 +5,17 @@ RUN echo /usr/local/lib64 > /etc/ld.so.conf.d/local.conf && \
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
# Install a recent Perl, needed to install the openssl crate
ADD deps/build_perl.sh /builder/build_perl.sh
RUN rm /usr/bin/perl
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# Install python tools (auditwheels,tox, ...)
ADD deps/build_python.sh /builder/build_python.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
# Install Rust nightly
# Install Rust nightly
ADD deps/build_rust.sh /builder/build_rust.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1
# Install a recent Perl, needed to install OpenSSL
ADD deps/build_perl.sh /builder/build_perl.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
# Install OpenSSL
ADD deps/build_openssl.sh /builder/build_openssl.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1

View File

@@ -1,11 +1,11 @@
#!/bin/bash
PERL_VERSION=5.28.0
PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
PERL_VERSION=5.30.0
# PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
curl -O https://www.cpan.org/src/5.0/perl-${PERL_VERSION}.tar.gz
echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
tar xzf perl-${PERL_VERSION}.tar.gz
cd perl-${PERL_VERSION}
# echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
tar -xzf perl-${PERL_VERSION}.tar.gz
cd perl-${PERL_VERSION}
./Configure -de
make

View File

@@ -1,11 +1,11 @@
#!/bin/bash
set -e -x
set -e -x
# Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
# Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-11-06 -y
export PATH=/root/.cargo/bin:$PATH
rustc --version
# remove some 300-400 MB that we don't need for automated builds
rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/
rm -rf /root/.rustup/toolchains/nightly-2019-11-06-x86_64-unknown-linux-gnu/share/

View File

@@ -1,49 +0,0 @@
#!/bin/bash
#
# Build the Delta Chat C/Rust library
#
set -e -x
# perform clean build of core and install
export TOXWORKDIR=.docker-tox
# build core library
cargo build --release -p deltachat_ffi
# configure access to a base python and
# to several python interpreters needed by tox below
export PATH=$PATH:/opt/python/cp35-cp35m/bin
export PYTHONDONTWRITEBYTECODE=1
pushd /bin
ln -s /opt/python/cp27-cp27m/bin/python2.7
ln -s /opt/python/cp36-cp36m/bin/python3.6
ln -s /opt/python/cp37-cp37m/bin/python3.7
popd
#
# run python tests
#
if [ -n "$TESTS" ]; then
echo ----------------
echo run python tests
echo ----------------
pushd python
# first run all tests ...
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
export PYTHONDONTWRITEBYTECODE=1
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37
popd
fi
if [ -n "$DOCS" ]; then
echo -----------------------
echo generating python docs
echo -----------------------
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
fi

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -xe
export CIRCLE_JOB=remote_tests_${1:?need to specify 'rust' or 'python'}
export CIRCLE_BUILD_NUM=$USER
export CIRCLE_BRANCH=`git branch | grep \* | cut -d ' ' -f2`
export CIRCLE_PROJECT_REPONAME=$(basename `git rev-parse --show-toplevel`)
time bash ci_scripts/$CIRCLE_JOB.sh

View File

@@ -0,0 +1,77 @@
name: CI
on:
pull_request:
push:
env:
RUSTFLAGS: -Dwarnings
jobs:
build_and_test:
name: Build and test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
rust: [nightly]
steps:
- uses: actions/checkout@master
- name: Install ${{ matrix.rust }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- name: check
uses: actions-rs/cargo@v1
if: matrix.rust == 'nightly'
with:
command: check
args: --all --bins --examples --tests
- name: tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all
- name: tests ignored
uses: actions-rs/cargo@v1
with:
command: test
args: --all --release -- --ignored
check_fmt:
name: Checking fmt and docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: fmt
run: cargo fmt --all -- --check
# clippy_check:
# name: Clippy check
# runs-on: ubuntu-latest
#
# steps:
# - uses: actions/checkout@v1
# - uses: actions-rs/toolchain@v1
# with:
# profile: minimal
# toolchain: nightly
# override: true
# components: clippy
#
# - name: clippy
# run: cargo clippy --all

60
ci_scripts/old/run-python.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
#
# Build the Delta Chat C/Rust library typically run in a docker
# container that contains all library deps but should also work
# outside if you have the dependencies installed on your system.
set -e -x
# Perform clean build of core and install.
export TOXWORKDIR=.docker-tox
# install core lib
export PATH=/root/.cargo/bin:$PATH
cargo build --release -p deltachat_ffi
# cargo test --all --all-features
# Statically link against libdeltachat.a.
export DCC_RS_DEV=$(pwd)
# Configure access to a base python and to several python interpreters
# needed by tox below.
export PATH=$PATH:/opt/python/cp35-cp35m/bin
export PYTHONDONTWRITEBYTECODE=1
pushd /bin
ln -s /opt/python/cp27-cp27m/bin/python2.7
ln -s /opt/python/cp36-cp36m/bin/python3.6
ln -s /opt/python/cp37-cp37m/bin/python3.7
popd
if [ -n "$TESTS" ]; then
pushd python
# prepare a clean tox run
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
export PYTHONDONTWRITEBYTECODE=1
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
# allows running of "liveconfig" tests but for speed reasons
# we run them only for the highest python version we support
# we split out qr-tests run to minimize likelyness of flaky tests
# (some qr tests are pretty heavy in terms of send/received
# messages and rust's imap code likely has concurrency problems)
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
tox --workdir "$TOXWORKDIR" -e auditwheels
popd
fi
# if [ -n "$DOCS" ]; then
# echo -----------------------
# echo generating python docs
# echo -----------------------
# (cd python && tox --workdir "$TOXWORKDIR" -e doc)
# fi

View File

@@ -0,0 +1,51 @@
#!/bin/bash
export BRANCH=${CIRCLE_BRANCH:?branch to build}
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
# we construct the BUILDDIR such that we can easily share the
# CARGO_TARGET_DIR between runs ("..")
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
set -xe
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
git ls-files >.rsynclist
# we seem to need .git for setuptools_scm versioning
find .git >>.rsynclist
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
set +x
# we have to create a remote file for the remote-docker run
# so we can do a simple ssh command with a TTY
# so that when our job dies, all container-runs are aborted.
# sidenote: the circle-ci machinery will kill ongoing jobs
# if there are new commits and we want to ensure that
# everything is terminated/cleaned up and we have no orphaned
# useless still-running docker-containers consuming resources.
ssh $SSHTARGET bash -c "cat >$BUILDDIR/exec_docker_run" <<_HERE
set +x -e
cd $BUILDDIR
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
set -x
# run everything else inside docker
docker run -e DCC_PY_LIVECONFIG \
--rm -it -v \$(pwd):/mnt -w /mnt \
deltachat/coredeps ci_scripts/run_all.sh
_HERE
echo "--- Running $CIRCLE_JOB remotely"
ssh -t $SSHTARGET bash "$BUILDDIR/exec_docker_run"
mkdir -p workspace
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/wheelhouse/*manylinux1*" workspace/wheelhouse/
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/dist/*" workspace/wheelhouse/
rsync -avz "$SSHTARGET:$BUILDDIR/python/doc/_build/" workspace/py-docs

View File

@@ -0,0 +1,46 @@
#!/bin/bash
export BRANCH=${CIRCLE_BRANCH:?branch to build}
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
# we construct the BUILDDIR such that we can easily share the
# CARGO_TARGET_DIR between runs ("..")
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
set -xe
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
git ls-files >.rsynclist
# we seem to need .git for setuptools_scm versioning
find .git >>.rsynclist
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
set +x
echo "--- Running $CIRCLE_JOB remotely"
ssh $SSHTARGET <<_HERE
set +x -e
cd $BUILDDIR
# let's share the target dir with our last run on this branch/job-type
# cargo will make sure to block/unblock us properly
export CARGO_TARGET_DIR=\`pwd\`/../target
export TARGET=release
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
#we rely on tox/virtualenv being available in the host
#rm -rf virtualenv venv
#virtualenv -q -p python3.7 venv
#source venv/bin/activate
#pip install -q tox virtualenv
set -x
which python
source \$HOME/venv/bin/activate
which python
bash ci_scripts/run-python-test.sh
_HERE

32
ci_scripts/remote_tests_rust.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
export BRANCH=${CIRCLE_BRANCH:?branch to build}
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
# we construct the BUILDDIR such that we can easily share the
# CARGO_TARGET_DIR between runs ("..")
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
set -e
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
git ls-files >.rsynclist
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
echo "--- Running $CIRCLE_JOB remotely"
ssh $SSHTARGET <<_HERE
set +x -e
cd $BUILDDIR
# let's share the target dir with our last run on this branch/job-type
# cargo will make sure to block/unblock us properly
export CARGO_TARGET_DIR=\`pwd\`/../target
export TARGET=x86_64-unknown-linux-gnu
export RUSTC_WRAPPER=sccache
bash ci_scripts/run-rust-test.sh
_HERE

6
ci_scripts/run-doxygen.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -ex
cd deltachat-ffi
PROJECT_NUMBER=$(git log -1 --format "%h (%cd)") doxygen

24
ci_scripts/run-python-test.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
#
# Run functional tests for Delta Chat core using the python bindings
# and tox/pytest.
set -e -x
# for core-building and python install step
export DCC_RS_TARGET=debug
export DCC_RS_DEV=`pwd`
cd python
python install_python_bindings.py onlybuild
# remove and inhibit writing PYC files
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
export PYTHONDONTWRITEBYTECODE=1
# run python tests (tox invokes pytest to run tests in python/tests)
#TOX_PARALLEL_NO_SPINNER=1 tox -e lint,doc
tox -e lint
tox -e doc,py37

View File

@@ -2,7 +2,7 @@
set -ex
export RUST_TEST_THREADS=1
#export RUST_TEST_THREADS=1
export RUST_BACKTRACE=1
export RUSTFLAGS='--deny warnings'
export OPT="--target=$TARGET"
@@ -31,16 +31,15 @@ fi
if [[ $NORUN == "1" ]]; then
export CARGO_SUBCMD="build"
else
export CARGO_SUBCMD="test"
export CARGO_SUBCMD="test --all"
export OPT="${OPT} "
export OPT_RELEASE="${OPT_RELEASE} "
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
fi
# Run all the test configurations:
# Run all the test configurations
# RUSTC_WRAPPER=SCCACHE seems to destroy parallelism / prolong the test
unset RUSTC_WRAPPER
$CARGO_CMD $CARGO_SUBCMD $OPT
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
# Build the ffi lib
$CARGO_CMD $CARGO_SUBCMD $OPT_FFI_RELEASE

View File

@@ -1,15 +1,13 @@
#!/bin/bash
#
# Build the Delta Chat C/Rust library typically run in a docker
# container that contains all library deps but should also work
# outside if you have the dependencies installed on your system.
# Build the Delta Chat Core Rust library, Python wheels and docs
set -e -x
# Perform clean build of core and install.
export TOXWORKDIR=.docker-tox
# install core lib
# compile core lib
export PATH=/root/.cargo/bin:$PATH
cargo build --release -p deltachat_ffi
@@ -26,29 +24,25 @@ pushd /bin
ln -s /opt/python/cp27-cp27m/bin/python2.7
ln -s /opt/python/cp36-cp36m/bin/python3.6
ln -s /opt/python/cp37-cp37m/bin/python3.7
ln -s /opt/python/cp38-cp38/bin/python3.8
popd
if [ -n "$TESTS" ]; then
pushd python
# prepare a clean tox run
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
mkdir -p $TOXWORKDIR
pushd python
# prepare a clean tox run
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
export PYTHONDONTWRITEBYTECODE=1
# run tox
# XXX we don't run liveconfig tests because they hang sometimes
# see https://github.com/deltachat/deltachat-core-rust/issues/331
# unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
popd
fi
# disable live-account testing to speed up test runs and wheel building
# XXX we may switch on some live-tests on for better ensurances
# Note that the independent remote_tests_python step does all kinds of
# live-testing already.
unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -e py35,py36,py37,py38,auditwheels
popd
if [ -n "$DOCS" ]; then
echo -----------------------
echo generating python docs
echo -----------------------
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
fi
echo -----------------------
echo generating python docs
echo -----------------------
(cd python && tox --workdir "$TOXWORKDIR" -e doc)

View File

@@ -1,11 +1,11 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-alpha.4"
version = "1.25.0"
description = "Deltachat FFI"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018"
readme = "README.md"
license = "MIT OR Apache-2.0"
license = "MPL-2.0"
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
categories = ["cryptography", "std", "email"]
@@ -19,6 +19,8 @@ deltachat = { path = "../", default-features = false }
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"
failure = "0.1.6"
serde_json = "1.0"
[features]
default = ["vendored", "nightly", "ringbuf"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,7 @@
use crate::location::Location;
use crate::types::*;
/* * the structure behind dc_array_t */
#[derive(Clone)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum dc_array_t {
Locations(Vec<Location>),
Uint(Vec<u32>),
@@ -19,7 +17,7 @@ impl dc_array_t {
dc_array_t::Locations(Vec::with_capacity(capacity))
}
pub fn add_id(&mut self, item: uint32_t) {
pub fn add_id(&mut self, item: u32) {
if let Self::Uint(array) = self {
array.push(item);
} else {
@@ -35,10 +33,10 @@ impl dc_array_t {
}
}
pub fn get_id(&self, index: usize) -> uint32_t {
pub fn get_id(&self, index: usize) -> u32 {
match self {
Self::Locations(array) => array[index].location_id,
Self::Uint(array) => array[index] as uint32_t,
Self::Uint(array) => array[index] as u32,
}
}

File diff suppressed because it is too large Load Diff

395
deltachat-ffi/src/string.rs Normal file
View File

@@ -0,0 +1,395 @@
use failure::Fail;
use std::ffi::{CStr, CString};
use std::ptr;
/// Duplicates a string
///
/// returns an empty string if NULL is given, never returns NULL (exits on errors)
///
/// # Examples
///
/// ```rust,norun
/// use deltachat::dc_tools::{dc_strdup, to_string_lossy};
/// unsafe {
/// let str_a = b"foobar\x00" as *const u8 as *const libc::c_char;
/// let str_a_copy = dc_strdup(str_a);
/// assert_eq!(to_string_lossy(str_a_copy), "foobar");
/// assert_ne!(str_a, str_a_copy);
/// }
/// ```
pub unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char {
let ret: *mut libc::c_char;
if !s.is_null() {
ret = libc::strdup(s);
assert!(!ret.is_null());
} else {
ret = libc::calloc(1, 1) as *mut libc::c_char;
assert!(!ret.is_null());
}
ret
}
/// Error type for the [OsStrExt] trait
#[derive(Debug, Fail, PartialEq)]
pub enum CStringError {
/// The string contains an interior null byte
#[fail(display = "String contains an interior null byte")]
InteriorNullByte,
/// The string is not valid Unicode
#[fail(display = "String is not valid unicode")]
NotUnicode,
}
/// Extra convenience methods on [std::ffi::OsStr] to work with `*libc::c_char`.
///
/// The primary function of this trait is to more easily convert
/// [OsStr], [OsString] or [Path] into pointers to C strings. This always
/// allocates a new string since it is very common for the source
/// string not to have the required terminal null byte.
///
/// It is implemented for `AsRef<std::ffi::OsStr>>` trait, which
/// allows any type which implements this trait to transparently use
/// this. This is how the conversion for [Path] works.
///
/// [OsStr]: std::ffi::OsStr
/// [OsString]: std::ffi::OsString
/// [Path]: std::path::Path
///
/// # Example
///
/// ```
/// use deltachat::dc_tools::{dc_strdup, OsStrExt};
/// let path = std::path::Path::new("/some/path");
/// let path_c = path.to_c_string().unwrap();
/// unsafe {
/// let mut c_ptr: *mut libc::c_char = dc_strdup(path_c.as_ptr());
/// }
/// ```
pub trait OsStrExt {
/// Convert a [std::ffi::OsStr] to an [std::ffi::CString]
///
/// This is useful to convert e.g. a [std::path::Path] to
/// [*libc::c_char] by using
/// [Path::as_os_str()](std::path::Path::as_os_str) and
/// [CStr::as_ptr()](std::ffi::CStr::as_ptr).
///
/// This returns [CString] and not [&CStr] because not all [OsStr]
/// slices end with a null byte, particularly those coming from
/// [Path] do not have a null byte and having to handle this as
/// the caller would defeat the point of this function.
///
/// On Windows this requires that the [OsStr] contains valid
/// unicode, which should normally be the case for a [Path].
///
/// [CString]: std::ffi::CString
/// [CStr]: std::ffi::CStr
/// [OsStr]: std::ffi::OsStr
/// [Path]: std::path::Path
///
/// # Errors
///
/// Since a C `*char` is terminated by a NULL byte this conversion
/// will fail, when the [OsStr] has an interior null byte. The
/// function will return
/// `[Err]([CStringError::InteriorNullByte])`. When converting
/// from a [Path] it should be safe to
/// [`.unwrap()`](std::result::Result::unwrap) this anyway since a
/// [Path] should not contain interior null bytes.
///
/// On windows when the string contains invalid Unicode
/// `[Err]([CStringError::NotUnicode])` is returned.
fn to_c_string(&self) -> Result<CString, CStringError>;
}
impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
#[cfg(not(target_os = "windows"))]
fn to_c_string(&self) -> Result<CString, CStringError> {
use std::os::unix::ffi::OsStrExt;
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<CString, CStringError> {
os_str_to_c_string_unicode(&self)
}
}
// Implementation for os_str_to_c_string on windows.
#[allow(dead_code)]
fn os_str_to_c_string_unicode(
os_str: &dyn AsRef<std::ffi::OsStr>,
) -> Result<CString, CStringError> {
match os_str.as_ref().to_str() {
Some(val) => CString::new(val.as_bytes()).map_err(|err| match err {
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
}),
None => Err(CStringError::NotUnicode),
}
}
/// Convenience methods/associated functions for working with [CString]
///
/// This is helps transitioning from unsafe code.
pub trait CStringExt {
/// Create a new [CString], yolo style
///
/// This unwrap the result, panicking when there are embedded NULL
/// bytes.
fn yolo<T: Into<Vec<u8>>>(t: T) -> CString {
CString::new(t).expect("String contains null byte, can not be CString")
}
}
impl CStringExt for CString {}
/// Convenience methods to turn strings into C strings.
///
/// To interact with (legacy) C APIs we often need to convert from
/// Rust strings to raw C strings. This can be clumsy to do correctly
/// and the compiler sometimes allows it in an unsafe way. These
/// methods make it more succinct and help you get it right.
pub trait StrExt {
/// Allocate a new raw C `*char` version of this string.
///
/// This allocates a new raw C string which must be freed using
/// `free`. It takes care of some common pitfalls with using
/// [CString.as_ptr].
///
/// [CString.as_ptr]: std::ffi::CString.as_ptr
///
/// # Panics
///
/// This function will panic when the original string contains an
/// interior null byte as this can not be represented in raw C
/// strings.
unsafe fn strdup(&self) -> *mut libc::c_char;
}
impl<T: AsRef<str>> StrExt for T {
unsafe fn strdup(&self) -> *mut libc::c_char {
let tmp = CString::yolo(self.as_ref());
dc_strdup(tmp.as_ptr())
}
}
/// Convenience methods to turn optional strings into C strings.
///
/// This is the same as the [StrExt] trait but a different trait name
/// to work around the type system not allowing to implement [StrExt]
/// for `Option<impl StrExt>` When we already have an [StrExt] impl
/// for `AsRef<&str>`.
///
/// When the [Option] is [Option::Some] this behaves just like
/// [StrExt::strdup], when it is [Option::None] a null pointer is
/// returned.
pub trait OptStrExt {
/// Allocate a new raw C `*char` version of this string, or NULL.
///
/// See [StrExt::strdup] for details.
unsafe fn strdup(&self) -> *mut libc::c_char;
}
impl<T: AsRef<str>> OptStrExt for Option<T> {
unsafe fn strdup(&self) -> *mut libc::c_char {
match self {
Some(s) => {
let tmp = CString::yolo(s.as_ref());
dc_strdup(tmp.as_ptr())
}
None => ptr::null_mut(),
}
}
}
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_string_lossy().to_string()
}
pub fn to_opt_string_lossy(s: *const libc::c_char) -> Option<String> {
if s.is_null() {
return None;
}
Some(to_string_lossy(s))
}
/// Convert a C `*char` pointer to a [std::path::Path] slice.
///
/// This converts a `*libc::c_char` pointer to a [Path] slice. This
/// essentially has to convert the pointer to [std::ffi::OsStr] to do
/// so and thus is the inverse of [OsStrExt::to_c_string]. Just like
/// [OsStrExt::to_c_string] requires valid Unicode on Windows, this
/// requires that the pointer contains valid UTF-8 on Windows.
///
/// Because this returns a reference the [Path] silce can not outlive
/// the original pointer.
///
/// [Path]: std::path::Path
#[cfg(not(target_os = "windows"))]
pub fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
assert!(!s.is_null(), "cannot be used on null pointers");
use std::os::unix::ffi::OsStrExt;
unsafe {
let c_str = std::ffi::CStr::from_ptr(s).to_bytes();
let os_str = std::ffi::OsStr::from_bytes(c_str);
std::path::Path::new(os_str)
}
}
// as_path() implementation for windows, documented above.
#[cfg(target_os = "windows")]
pub fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
as_path_unicode(s)
}
// Implementation for as_path() on Windows.
//
// Having this as a separate function means it can be tested on unix
// too.
#[allow(dead_code)]
fn as_path_unicode<'a>(s: *const libc::c_char) -> &'a std::path::Path {
assert!(!s.is_null(), "cannot be used on null pointers");
let cstr = unsafe { CStr::from_ptr(s) };
let str = cstr.to_str().unwrap_or_else(|err| panic!("{}", err));
std::path::Path::new(str)
}
#[cfg(test)]
mod tests {
use super::*;
use libc::{free, strcmp};
#[test]
fn test_os_str_to_c_string_cwd() {
let some_dir = std::env::current_dir().unwrap();
some_dir.as_os_str().to_c_string().unwrap();
}
#[test]
fn test_os_str_to_c_string_unicode() {
let some_str = String::from("/some/valid/utf8");
let some_dir = std::path::Path::new(&some_str);
assert_eq!(
some_dir.as_os_str().to_c_string().unwrap(),
CString::new("/some/valid/utf8").unwrap()
);
}
#[test]
fn test_os_str_to_c_string_nul() {
let some_str = std::ffi::OsString::from("foo\x00bar");
assert_eq!(
some_str.to_c_string().err().unwrap(),
CStringError::InteriorNullByte
)
}
#[test]
fn test_path_to_c_string_cwd() {
let some_dir = std::env::current_dir().unwrap();
some_dir.to_c_string().unwrap();
}
#[test]
fn test_path_to_c_string_unicode() {
let some_str = String::from("/some/valid/utf8");
let some_dir = std::path::Path::new(&some_str);
assert_eq!(
some_dir.as_os_str().to_c_string().unwrap(),
CString::new("/some/valid/utf8").unwrap()
);
}
#[test]
fn test_os_str_to_c_string_unicode_fn() {
let some_str = std::ffi::OsString::from("foo");
assert_eq!(
os_str_to_c_string_unicode(&some_str).unwrap(),
CString::new("foo").unwrap()
);
}
#[test]
fn test_path_to_c_string_unicode_fn() {
let some_str = String::from("/some/path");
let some_path = std::path::Path::new(&some_str);
assert_eq!(
os_str_to_c_string_unicode(&some_path).unwrap(),
CString::new("/some/path").unwrap()
);
}
#[test]
fn test_os_str_to_c_string_unicode_fn_nul() {
let some_str = std::ffi::OsString::from("fooz\x00bar");
assert_eq!(
os_str_to_c_string_unicode(&some_str).err().unwrap(),
CStringError::InteriorNullByte
);
}
#[test]
fn test_as_path() {
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 = CString::new("/some/path").unwrap();
let ptr = some_path.as_ptr();
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
}
#[test]
fn test_cstring_yolo() {
assert_eq!(CString::new("hello").unwrap(), CString::yolo("hello"));
}
#[test]
fn test_strdup_str() {
unsafe {
let s = "hello".strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_strdup_string() {
unsafe {
let s = String::from("hello").strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_strdup_opt_string() {
unsafe {
let s = Some("hello");
let c = s.strdup();
let cmp = strcmp(c, b"hello\x00" as *const u8 as *const libc::c_char);
free(c as *mut libc::c_void);
assert_eq!(cmp, 0);
let s: Option<&str> = None;
let c = s.strdup();
assert_eq!(c, ptr::null_mut());
}
}
}

View File

@@ -1,12 +1,13 @@
[package]
name = "deltachat_derive"
version = "0.1.0"
authors = ["Dmitry Bogatov <KAction@debian.org>"]
version = "2.0.0"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018"
license = "MPL-2.0"
[lib]
proc-macro = true
[dependencies]
syn = "0.14.4"
quote = "0.6.3"
syn = "1.0.13"
quote = "1.0.2"

View File

@@ -36,7 +36,7 @@ pub fn from_sql_derive(input: TokenStream) -> TokenStream {
impl rusqlite::types::FromSql for #name {
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
let inner = rusqlite::types::FromSql::column_result(col)?;
num_traits::FromPrimitive::from_i64(inner).ok_or(rusqlite::types::FromSqlError::InvalidType)
Ok(num_traits::FromPrimitive::from_i64(inner).unwrap_or_default())
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -14,20 +14,19 @@ extern crate lazy_static;
extern crate rusqlite;
use std::borrow::Cow::{self, Borrowed, Owned};
use std::ptr;
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use deltachat::chat::ChatId;
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_securejoin::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::oauth2::*;
use deltachat::types::*;
use deltachat::x::*;
use deltachat::securejoin::*;
use deltachat::Event;
use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
use rustyline::error::ReadlineError;
@@ -42,100 +41,76 @@ use self::cmdline::*;
// Event Handler
unsafe extern "C" fn receive_event(
_context: &Context,
event: Event,
data1: uintptr_t,
data2: uintptr_t,
) -> uintptr_t {
fn receive_event(_context: &Context, event: Event) {
match event {
Event::GET_STRING => {}
Event::INFO => {
Event::Info(msg) => {
/* do not show the event as this would fill the screen */
println!("{}", to_string(data2 as *const _),);
println!("{}", msg);
}
Event::SMTP_CONNECTED => {
println!("[DC_EVENT_SMTP_CONNECTED] {}", to_string(data2 as *const _));
Event::SmtpConnected(msg) => {
println!("[DC_EVENT_SMTP_CONNECTED] {}", msg);
}
Event::IMAP_CONNECTED => {
println!("[DC_EVENT_IMAP_CONNECTED] {}", to_string(data2 as *const _),);
Event::ImapConnected(msg) => {
println!("[DC_EVENT_IMAP_CONNECTED] {}", msg);
}
Event::SMTP_MESSAGE_SENT => {
println!(
"[DC_EVENT_SMTP_MESSAGE_SENT] {}",
to_string(data2 as *const _),
);
Event::SmtpMessageSent(msg) => {
println!("[DC_EVENT_SMTP_MESSAGE_SENT] {}", msg);
}
Event::WARNING => {
println!("[Warning] {}", to_string(data2 as *const _),);
Event::Warning(msg) => {
println!("[Warning] {}", msg);
}
Event::ERROR => {
println!(
"\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m",
to_string(data2 as *const _),
);
Event::Error(msg) => {
println!("\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", msg);
}
Event::ERROR_NETWORK => {
println!(
"\x1b[31m[DC_EVENT_ERROR_NETWORK] first={}, msg={}\x1b[0m",
data1 as usize,
to_string(data2 as *const _),
);
Event::ErrorNetwork(msg) => {
println!("\x1b[31m[DC_EVENT_ERROR_NETWORK] msg={}\x1b[0m", msg);
}
Event::ERROR_SELF_NOT_IN_GROUP => {
println!(
"\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m",
to_string(data2 as *const _),
);
Event::ErrorSelfNotInGroup(msg) => {
println!("\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", msg);
}
Event::MSGS_CHANGED => {
Event::MsgsChanged { chat_id, msg_id } => {
print!(
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED({}, {})}}\n\x1b[0m",
data1 as usize, data2 as usize,
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED(chat_id={}, msg_id={})}}\n\x1b[0m",
chat_id, msg_id,
);
}
Event::CONTACTS_CHANGED => {
Event::ContactsChanged(_) => {
print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m");
}
Event::LOCATION_CHANGED => {
Event::LocationChanged(contact) => {
print!(
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={})}}\n\x1b[0m",
data1 as usize,
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={:?})}}\n\x1b[0m",
contact,
);
}
Event::CONFIGURE_PROGRESS => {
Event::ConfigureProgress(progress) => {
print!(
"\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m",
data1 as usize,
progress,
);
}
Event::IMEX_PROGRESS => {
Event::ImexProgress(progress) => {
print!(
"\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m",
data1 as usize,
progress,
);
}
Event::IMEX_FILE_WRITTEN => {
Event::ImexFileWritten(file) => {
print!(
"\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m",
to_string(data1 as *const _)
file.display()
);
}
Event::CHAT_MODIFIED => {
Event::ChatModified(chat) => {
print!(
"\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m",
data1 as usize,
chat
);
}
_ => {
print!(
"\x1b[33m{{Received {:?}({}, {})}}\n\x1b[0m",
event, data1 as usize, data2 as usize,
);
print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event);
}
}
0
}
// Threads for waiting for messages and for jobs
@@ -173,11 +148,11 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_imap = std::thread::spawn(move || loop {
while_running!({
perform_imap_jobs(&ctx.read().unwrap());
perform_imap_fetch(&ctx.read().unwrap());
perform_inbox_jobs(&ctx.read().unwrap());
perform_inbox_fetch(&ctx.read().unwrap());
while_running!({
let context = ctx.read().unwrap();
perform_imap_idle(&context);
perform_inbox_idle(&context);
});
});
});
@@ -225,7 +200,7 @@ fn stop_threads(context: &Context) {
println!("Stopping threads");
IS_RUNNING.store(false, Ordering::Relaxed);
interrupt_imap_idle(context);
interrupt_inbox_idle(context);
interrupt_mvbox_idle(context);
interrupt_sentbox_idle(context);
interrupt_smtp_idle(context);
@@ -258,7 +233,7 @@ impl Completer for DcHelper {
}
}
const IMEX_COMMANDS: [&'static str; 12] = [
const IMEX_COMMANDS: [&str; 12] = [
"initiate-key-transfer",
"get-setupcodebegin",
"continue-key-transfer",
@@ -273,7 +248,7 @@ const IMEX_COMMANDS: [&'static str; 12] = [
"stop",
];
const DB_COMMANDS: [&'static str; 11] = [
const DB_COMMANDS: [&str; 11] = [
"info",
"open",
"close",
@@ -287,7 +262,7 @@ const DB_COMMANDS: [&'static str; 11] = [
"housekeeping",
];
const CHAT_COMMANDS: [&'static str; 24] = [
const CHAT_COMMANDS: [&str; 26] = [
"listchats",
"listarchived",
"chat",
@@ -311,9 +286,11 @@ const CHAT_COMMANDS: [&'static str; 24] = [
"listmedia",
"archive",
"unarchive",
"pin",
"unpin",
"delchat",
];
const MESSAGE_COMMANDS: [&'static str; 8] = [
const MESSAGE_COMMANDS: [&str; 8] = [
"listmsgs",
"msginfo",
"listfresh",
@@ -323,7 +300,7 @@ const MESSAGE_COMMANDS: [&'static str; 8] = [
"unstar",
"delmsg",
];
const CONTACT_COMMANDS: [&'static str; 6] = [
const CONTACT_COMMANDS: [&str; 6] = [
"listcontacts",
"listverified",
"addcontact",
@@ -331,7 +308,7 @@ const CONTACT_COMMANDS: [&'static str; 6] = [
"delcontact",
"cleanupcontacts",
];
const MISC_COMMANDS: [&'static str; 9] = [
const MISC_COMMANDS: [&str; 9] = [
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
];
@@ -357,8 +334,8 @@ impl Hinter for DcHelper {
}
}
static COLORED_PROMPT: &'static str = "\x1b[1;32m> \x1b[0m";
static PROMPT: &'static str = "> ";
static COLORED_PROMPT: &str = "\x1b[1;32m> \x1b[0m";
static PROMPT: &str = "> ";
impl Highlighter for DcHelper {
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
@@ -385,17 +362,15 @@ impl Highlighter for DcHelper {
impl Helper for DcHelper {}
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
let mut context = dc_context_new(Some(receive_event), ptr::null_mut(), Some("CLI".into()));
unsafe { dc_cmdline_skip_auth() };
if args.len() == 2 {
if unsafe { !dc_open(&mut context, &args[1], None) } {
println!("Error: Cannot open {}.", args[0],);
}
} else if args.len() != 1 {
if args.len() < 2 {
println!("Error: Bad arguments, expected [db-name].");
return Err(format_err!("No db-name specified"));
}
let context = Context::new(
Box::new(receive_event),
"CLI".into(),
Path::new(&args[1]).to_path_buf(),
)?;
println!("Delta Chat Core is awaiting your commands.");
@@ -428,7 +403,7 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
// TODO: ignore "set mail_pw"
rl.add_history_entry(line.as_str());
let ctx = ctx.clone();
match unsafe { handle_cmd(line.trim(), ctx) } {
match handle_cmd(line.trim(), ctx) {
Ok(ExitResult::Continue) => {}
Ok(ExitResult::Exit) => break,
Err(err) => println!("Error: {}", err),
@@ -459,15 +434,10 @@ enum ExitResult {
Exit,
}
unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failure::Error> {
fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failure::Error> {
let mut args = line.splitn(2, ' ');
let arg0 = args.next().unwrap_or_default();
let arg1 = args.next().unwrap_or_default();
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1.strdup()
};
match arg0 {
"connect" => {
@@ -485,14 +455,14 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
}
"imap-jobs" => {
if HANDLE.clone().lock().unwrap().is_some() {
println!("imap-jobs are already running in a thread.");
println!("inbox-jobs are already running in a thread.");
} else {
perform_imap_jobs(&ctx.read().unwrap());
perform_inbox_jobs(&ctx.read().unwrap());
}
}
"configure" => {
start_threads(ctx.clone());
configure(&ctx.read().unwrap());
ctx.read().unwrap().configure();
}
"oauth2" => {
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
@@ -516,38 +486,34 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
}
"getqr" | "getbadqr" => {
start_threads(ctx.clone());
let qrstr =
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default());
if !qrstr.is_null() && 0 != *qrstr.offset(0isize) as libc::c_int {
if arg0 == "getbadqr" && strlen(qrstr) > 40 {
let mut i: libc::c_int = 12i32;
while i < 22i32 {
*qrstr.offset(i as isize) = '0' as i32 as libc::c_char;
i += 1
if let Some(mut qr) = dc_get_securejoin_qr(
&ctx.read().unwrap(),
ChatId::new(arg1.parse().unwrap_or_default()),
) {
if !qr.is_empty() {
if arg0 == "getbadqr" && qr.len() > 40 {
qr.replace_range(12..22, "0000000000")
}
println!("{}", qr);
let output = Command::new("qrencode")
.args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"])
.output()
.expect("failed to execute process");
io::stdout().write_all(&output.stdout).unwrap();
io::stderr().write_all(&output.stderr).unwrap();
}
println!("{}", to_string(qrstr as *const _));
let syscmd = dc_mprintf(
b"qrencode -t ansiutf8 \"%s\" -o -\x00" as *const u8 as *const libc::c_char,
qrstr,
);
system(syscmd);
free(syscmd as *mut libc::c_void);
}
free(qrstr as *mut libc::c_void);
}
"joinqr" => {
start_threads(ctx.clone());
if !arg0.is_empty() {
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
dc_join_securejoin(&ctx.read().unwrap(), arg1);
}
}
"exit" | "quit" => return Ok(ExitResult::Exit),
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
}
free(arg1_c as *mut _);
Ok(ExitResult::Continue)
}

View File

@@ -1,6 +1,5 @@
extern crate deltachat;
use std::ffi::CStr;
use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
@@ -8,128 +7,105 @@ use tempfile::tempdir;
use deltachat::chat;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::Event;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::job::{
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
perform_inbox_fetch, perform_inbox_idle, perform_inbox_jobs, perform_smtp_idle,
perform_smtp_jobs,
};
use deltachat::Event;
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
println!("[{:?}]", event);
fn cb(_ctx: &Context, event: Event) {
print!("[{:?}]", event);
match event {
Event::CONFIGURE_PROGRESS => {
println!(" progress: {}", data1);
0
Event::ConfigureProgress(progress) => {
println!(" progress: {}", progress);
}
Event::INFO | Event::WARNING | Event::ERROR | Event::ERROR_NETWORK => {
println!(
" {}",
unsafe { CStr::from_ptr(data2 as *const _) }
.to_str()
.unwrap()
);
0
Event::Info(msg) | Event::Warning(msg) | Event::Error(msg) | Event::ErrorNetwork(msg) => {
println!(" {}", msg);
}
_ => {
println!();
}
_ => 0,
}
}
fn main() {
unsafe {
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let running = Arc::new(RwLock::new(true));
let info = dc_get_info(&ctx);
let info_s = CStr::from_ptr(info);
let duration = time::Duration::from_millis(4000);
println!("info: {}", info_s.to_str().unwrap());
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile);
let ctx =
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
let running = Arc::new(RwLock::new(true));
let info = ctx.get_info();
let duration = time::Duration::from_millis(4000);
println!("info: {:#?}", info);
let ctx = Arc::new(ctx);
let ctx1 = ctx.clone();
let r1 = running.clone();
let t1 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_inbox_jobs(&ctx1);
if *r1.read().unwrap() {
perform_inbox_fetch(&ctx1);
let ctx = Arc::new(ctx);
let ctx1 = ctx.clone();
let r1 = running.clone();
let t1 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_imap_jobs(&ctx1);
if *r1.read().unwrap() {
perform_imap_fetch(&ctx1);
if *r1.read().unwrap() {
perform_imap_idle(&ctx1);
}
perform_inbox_idle(&ctx1);
}
}
});
let ctx1 = ctx.clone();
let r1 = running.clone();
let t2 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_smtp_jobs(&ctx1);
if *r1.read().unwrap() {
perform_smtp_idle(&ctx1);
}
}
});
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("opening database {:?}", dbfile);
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
println!("configuring");
let args = std::env::args().collect::<Vec<String>>();
assert_eq!(args.len(), 2, "missing password");
let pw = args[1].clone();
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
.unwrap();
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
configure(&ctx);
thread::sleep(duration);
println!("sending a message");
let contact_id =
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
for i in 0..chats.len() {
let summary = chats.get_summary(0, None);
let text1 = summary.get_text1();
let text2 = summary.get_text2();
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
}
});
thread::sleep(duration);
let ctx1 = ctx.clone();
let r1 = running.clone();
let t2 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_smtp_jobs(&ctx1);
if *r1.read().unwrap() {
perform_smtp_idle(&ctx1);
}
}
});
// let msglist = dc_get_chat_msgs(&ctx, chat_id, 0, 0);
// for i in 0..dc_array_get_cnt(msglist) {
// let msg_id = dc_array_get_id(msglist, i);
// let msg = dc_get_msg(context, msg_id);
// let text = CStr::from_ptr(dc_msg_get_text(msg)).unwrap();
// println!("Message {}: {}\n", i + 1, text.to_str().unwrap());
// dc_msg_unref(msg);
// }
// dc_array_unref(msglist);
println!("configuring");
let args = std::env::args().collect::<Vec<String>>();
assert_eq!(args.len(), 2, "missing password");
let pw = args[1].clone();
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
.unwrap();
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
ctx.configure();
println!("stopping threads");
thread::sleep(duration);
*running.clone().write().unwrap() = false;
deltachat::job::interrupt_imap_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx);
println!("sending a message");
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
println!("joining");
t1.join().unwrap();
t2.join().unwrap();
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
println!("closing");
dc_close(&ctx);
for i in 0..chats.len() {
let summary = chats.get_summary(&ctx, 0, None);
let text1 = summary.get_text1();
let text2 = summary.get_text2();
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
}
thread::sleep(duration);
println!("stopping threads");
*running.write().unwrap() = false;
deltachat::job::interrupt_inbox_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx);
println!("joining");
t1.join().unwrap();
t2.join().unwrap();
println!("closing");
}

52
misc.c
View File

@@ -1,52 +0,0 @@
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "misc.h"
static char* internal_dc_strdup(const char* s) /* strdup(NULL) is undefined, save_strdup(NULL) returns an empty string in this case */
{
char* ret = NULL;
if (s) {
if ((ret=strdup(s))==NULL) {
exit(16); /* cannot allocate (little) memory, unrecoverable error */
}
}
else {
if ((ret=(char*)calloc(1, 1))==NULL) {
exit(17); /* cannot allocate little memory, unrecoverable error */
}
}
return ret;
}
char* dc_mprintf(const char* format, ...)
{
char testbuf[1];
char* buf = NULL;
int char_cnt_without_zero = 0;
va_list argp;
va_list argp_copy;
va_start(argp, format);
va_copy(argp_copy, argp);
char_cnt_without_zero = vsnprintf(testbuf, 0, format, argp);
va_end(argp);
if (char_cnt_without_zero < 0) {
va_end(argp_copy);
return internal_dc_strdup("ErrFmt");
}
buf = malloc(char_cnt_without_zero+2 /* +1 would be enough, however, protect against off-by-one-errors */);
if (buf==NULL) {
va_end(argp_copy);
return internal_dc_strdup("ErrMem");
}
vsnprintf(buf, char_cnt_without_zero+1, format, argp_copy);
va_end(argp_copy);
return buf;
}

1
misc.h
View File

@@ -1 +0,0 @@
char* dc_mprintf (const char* format, ...); /* The result must be free()'d. */

View File

@@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 03cab93c6d1f3a8245f63cf84dacb307944294fe6333c1e38f078a6600659c7a # shrinks to data = "a\t0aA\ta\t0 \ta\t0 \ta a\t\ta A\tAA0a0a 0\t a\t aA \t a\t A0\t AAa\taA0\taAAaA\t0\taa0a\ta Aa Aaaa A0A\t a aA 0\t A\t0\t0\t\t\t\t\t\tA \t\t a\tA Aa aAA0A0AA0aaA A\t\t aa0\ta\t \tAa\taA\t00 AA A a\tA a aAAa \t 00\t0 \t\t a A 0\t\t\t aAA Aa \taAAAA0a A0A\t\t1\\E$\t$R\tc\t^=\t\"\tQ<Uk\t\t>A\t\t&\t}v&\tM^\'|\tW5?dn\t\t+\t\tP\te`\t\t>:Brlq--?\t$#Q\tK=zJ\tb\"9*.\"\t`\tF&T*\tBs,\tg\'*\\\t:\t?l$\t\t|A\"HR:Hk\t\\KkV\t\t{=&!^e%:|_*0wV\t[$`\t:\t$%f\t\t[!\"Y. \tP\t\th\'?\'/?%:++NfQo#@\"+?\t(\\??{\t\'\'$Dzj\t0.?{s4?&?Y=/yj]Z=\t4n\t?Ja\"\t{I\t$\t;I}_8V\t&\t?N\'\tI2/\t9.\tFT%9%`\'\tz\to7Y\t|AXP&@G12g\t\'w\t\t%??\t\"h$?F\"\"6%q\\\\{\tT\t\"]$87$}h\'\t<\t$\tc%U:mT2:<v\t#Rl!;U\t\t\"^D\tRZ(BZ{n\t%[$fgH`\t{B}:*\t\t%%*F`%W\t//B}PQ\t\tsu2\tLz<1*!p-X\tnKv&&0\thm4<n\\.\\/.w\'\t<)E1g3#ood\t`?\t\\({N?,8_?h\ty\t0%\t*$A\t\t*w-ViQUj\tTiH\t%\t%&0p\'\'\tA%r**Fo\'Z\\\tNI*ely4=I?}$.o`\t$\ts\'&lt\t\",:~=Nv/0O%=+<LS\t%P\'\t$r%d.\t{G{/L:\t&I&8-`cy*\"{\t/%fP9.P<\t\t\'/`\t\t`\t\t`!t:\t::\t\tW\'X^\t@.uL&a\tN\t\t\t.\t?0*\tvUK>UJ\\\tQbj%w]P=Js\t\"R\t&s^?</=%\t\'VI:\" kT`{b*<\t\tF&?Y\t\t:/\t!C\'e0`\t\t\tx-\t*\\N\\wwu.I4\tb/y\t\"P:P%\"\t\tQ][\\\t^x\t\t):\t\t&s\t$1-\t\t\tXt`0\t;\t/UeCP*\"G\t\t\':\tk@91Hf&#\t(Uzz&`p2;\t{]\t\"I_%=\\%,XD\"\'06QfCd<;$:q^\t8:\"*\"H\t\to\t&xK/\t\ty0/<}<j<|*`~::b\t=S.=}=Ki\t<Y.\'{\tf\t{Ub*H%(_4*?*\tn2\t/\'\t\t\t/,4]\tt\t<y\t\t\tWi\t\tT&\"\t\t\t\t\t=/1Wu?\t\'A\"W-P\t$?e\\\t`\t6`vD\t8`\t\tccD J\tY&jI//?_\t\\j\t_\tsiq$\t?9\tQ\t.^%&..?%Jm??A%+<\tN&*\t.g\tS$W\"\"\tMpq\t\t:&\\\thTz&<iz%/%T&\'F\t\\]&\t\t}\t\t\tXr=dtp`:+<,\t%60Y*<(&K*kJ\todO\t=<V?&\tMU/\"\t= Y!)<\tV\t9\t)\t&v8q{\t\t&pT\t3\ttB,qcq\'i$;gv%j_%M_{[\"&;\t\t\t.B;6Y\\%\t\"\tY/a\t\\`:?\t<\t?]\taNwD;\\\t%l*74%5T?QS :~yR/E*R\t\t=u\t\\\t\t.Q<;\\\t_S/{&F$^\tw_>\'h=|%\t\t:W!\\<U2\'$\tb&`\t=|=L9\t\t\t\\WZ:! }{\t ;\t;\t\t 0.*\t.%\"\'r%{<Mh_t[[}\t-\tJo\"b/AC*-=`=T\tz$2\tC\t\t/k{47\"\t\t,q%\tZ\tT3\t\tf>%\t\'?%@^tx\t7\"1Bk{b{n\t\"Pj3\tHc\t\tt\tY<\t#?\tSh\\yk/N\\\t8 7=R4*9Cw&m\t\\-\'f\t|\'#t(Etu.Hdu(E\t%&v:\'aqW~A5\t\t w.s{J%lX<\"\t\'*%m<&:/B<&\':U}$&`.{)\t\t6S\t:/$*kQ-Z\t^\'t${/tH*\'v\t3\t=\t\tDyp:B\t`I_R&4SO\t\t&-j=*.\t87&\'e*( \t\t\t\'<$\\DJ<$p?{}\'&\tv\t\\Xk<Y:Y`!$K{\tF&\tzd\t\t*i$\tj\'\t<)R*\t%?\t!.\t=\"@#~:=*\t\tXO=_T,1\"\'.%%\"`{\\:\t\"\tfkeOb/\'$I~\ta\t|&\t[\\KK\"1&Z\t<k\t\t)%\'-~\"2n\tj\tW?*<@w{g%d\ta\\\'\'I\t;:ySR%ke:4\tc\t$=\t&9P]x4\tJ=\t6C6%a\t`0\tF\tm-\tTr\t}\t\tQum\t&@\typ|w2&\t\t3`i&t\t\tT5\"\t.&b&e*/==1.\'*\\[U*\tqPL%?$-0/}~|q`\t\t}\t$\tq==o+T$\'!H\t\ti&um\"?\"%%\t/\'p\tg>?{0{J{\t\t/\t\t{zKZ&>=\t[\"1h<H%z/8,/]s\tv{7\t\t:j*H,M//\t\t\td\'.)\t"

View File

@@ -0,0 +1,8 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 679506fe9ac59df773f8cfa800fdab5f0a32fe49d2ab370394000a1aa5bc2a72 # shrinks to buf = "%0A"
cc e34960438edb2426904b44fb4215154e7e2880f2fd1c3183b98bfcc76fec4882 # shrinks to input = " 0"

View File

@@ -0,0 +1,9 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc c310754465ee0261807b96fa9bcc4861ff9aa286e94667524b5960c69f9b6620 # shrinks to buf = "", approx_chars = 0, do_unwrap = false
cc 5fd8d730b0a9cdf7308ce58818ca9aefc0255c9ba2a0878944fc48d43a67315b # shrinks to buf = "𑒀ὐ¢🜀\u{1e01b}A a🟠", approx_chars = 0, do_unwrap = false
cc c6a0029a54137a4b9efc9ef2ea6d9a7dd1d60d1c937bb472b66a174618ba8013 # shrinks to buf = "𐠈0Aᝮa𫝀®!ꫛa¡0A𐢧00𐹠®A 丽ⷐએ ", approx_chars = 0, do_unwrap = false

View File

@@ -1,6 +1,17 @@
0.600.1
0.800.0
-------
- use latest core 1.25.0
- refine tests and some internal changes to core bindings
0.700.0
---------
- lots of new Python APIs
- use rust core-beta23
- introduce automatic versioning via setuptools_scm,
based on py-X.Y.Z tags

View File

@@ -3,41 +3,98 @@ deltachat python bindings
=========================
This package provides bindings to the deltachat-core_ Rust -library
which provides imap/smtp/crypto handling as well as chat/group/messages
handling to Android, Desktop and IO user interfaces.
which implements IMAP/SMTP/MIME/PGP e-mail standards and offers
a low-level Chat/Contact/Message API to user interfaces and bots.
Installing pre-built packages (linux-only)
==========================================
If you have a linux system you may install the ``deltachat`` binary "wheel" package
Installing bindings from source (Updated: 20-Jan-2020)
=========================================================
Install Rust and Cargo first. Deltachat needs a specific nightly
version, the easiest is probably to first install Rust stable from
rustup and then use this to install the correct nightly version.
Bootstrap Rust and Cargo by using rustup::
curl https://sh.rustup.rs -sSf | sh
Then GIT clone the deltachat-core-rust repo and get the actual
rust- and cargo-toolchain needed by deltachat::
git clone https://github.com/deltachat/deltachat-core-rust
cd deltachat-core-rust
rustup show
To install the Delta Chat Python bindings make sure you have Python3 installed.
E.g. on Debian-based systems `apt install python3 python3-pip
python3-venv` should give you a usable python installation.
Ensure you are in the deltachat-core-rust/python directory, create the
virtual environment and activate it in your shell::
cd python
python3 -m venv venv # or: virtualenv venv
source venv/bin/activate
You should now be able to build the python bindings using the supplied script::
./install_python_bindings.py
The installation might take a while, depending on your machine.
The bindings will be installed in release mode but with debug symbols.
The release mode is currently necessary because some tests generate RSA keys
which is prohibitively slow in non-release mode.
After successful binding installation you can install a few more
Python packages before running the tests::
python -m pip install pytest pytest-timeout pytest-rerunfailures requests
pytest -v tests
running "live" tests with temporary accounts
---------------------------------------------
If you want to run "liveconfig" functional tests you can set
``DCC_NEW_TMP_EMAIL`` to:
- a particular https-url that you can ask for from the delta
chat devs. This is implemented on the server side via
the [mailadm](https://github.com/deltachat/mailadm) command line tool.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pw=..." a test account login that will
be used for the live tests.
With ``DCC_NEW_TMP_EMAIL`` set pytest invocations will use real
e-mail accounts and run through all functional "liveconfig" tests.
Installing pre-built packages (Linux-only)
========================================================
If you have a Linux system you may try to install the ``deltachat`` binary "wheel" packages
without any "build-from-source" steps.
1. `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
then create a fresh python environment and activate it in your shell::
We suggest to `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
then create a fresh Python virtual environment and activate it in your shell::
virtualenv venv # or: python -m venv
source venv/bin/activate
Afterwards, invoking ``python`` or ``pip install`` will only
modify files in your ``venv`` directory and leave your system installation
alone.
2. Install the wheel for linux::
pip install deltachat
Verify it worked by typing::
python -c "import deltachat"
Installing a wheel from a PR/branch
---------------------------------------
Afterwards, invoking ``python`` or ``pip install`` only
modifies files in your ``venv`` directory and leaves
your system installation alone.
For Linux, we automatically build wheels for all github PR branches
and push them to a python package index. To install the latest github ``master`` branch::
and push them to a python package index. To install the latest
github ``master`` branch::
pip install -i https://m.devpi.net/dc/master deltachat
pip install --pre -i https://m.devpi.net/dc/master deltachat
To verify it worked::
python -c "import deltachat"
.. note::
@@ -46,65 +103,6 @@ and push them to a python package index. To install the latest github ``master``
`in contact with us <https://delta.chat/en/contribute>`_.
Installing bindings from source
===============================
If you can't use "binary" method above then you need to compile
to core deltachat library::
git clone https://github.com/deltachat/deltachat-core-rust
cd deltachat-core-rust
cd python
If you don't have one active, create and activate a python "virtualenv":
python virtualenv venv # or python -m venv
source venv/bin/activate
Afterwards ``which python`` tells you that it comes out of the "venv"
directory that contains all python install artifacts. Let's first
install test tools::
pip install pytest pytest-timeout requests
then cargo-build and install the deltachat bindings::
python install_python_bindings.py
The bindings will be installed in release mode but with debug symbols.
The release mode is neccessary because some tests generate RSA keys
which is prohibitively slow in debug mode.
After succcessul binding installation you can finally run the tests::
pytest -v tests
.. note::
Some tests are sometimes failing/hanging because of
https://github.com/deltachat/deltachat-core-rust/issues/331
and
https://github.com/deltachat/deltachat-core-rust/issues/326
running "live" tests (experimental)
-----------------------------------
If you want to run "liveconfig" functional tests you can set
``DCC_PY_LIVECONFIG`` to:
- a particular https-url that you can ask for from the delta
chat devs.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pwd=..." a test account login that will
be used for the live tests.
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
e-mail accounts and run through all functional "liveconfig" tests.
Code examples
=============
@@ -115,15 +113,11 @@ You may look at `examples <https://py.delta.chat/examples.html>`_.
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
Building manylinux1 wheels
==========================
.. note::
This section may not fully work.
Building manylinux1 based wheels
================================
Building portable manylinux1 wheels which come with libdeltachat.so
and all it's dependencies is easy using the provided docker tooling.
can be done with docker-tooling.
using docker pull / premade images
------------------------------------
@@ -136,9 +130,9 @@ organization::
This docker image can be used to run tests and build Python wheels for all interpreters::
$ bash ci_scripts/ci_run.sh
This command runs tests and build-wheel scripts in a docker container.
$ docker run -e DCC_NEW_TMP_EMAIL \
--rm -it -v \$(pwd):/mnt -w /mnt \
deltachat/coredeps ci_scripts/run_all.sh
Optionally build your own docker image

View File

@@ -15,3 +15,7 @@ div.globaltoc {
img.logo {
height: 120px;
}
div.footer {
display: none;
}

View File

@@ -6,7 +6,6 @@
<li><a href="{{ pathto('install') }}">install</a></li>
<li><a href="{{ pathto('api') }}">high level API</a></li>
<li><a href="{{ pathto('lapi') }}">low level API</a></li>
<li><a href="{{ pathto('capi') }}">C deltachat.h</a></li>
</ul>
<b>external links:</b>
<ul>

View File

@@ -8,8 +8,8 @@ high level API reference
- :class:`deltachat.account.Account` (your main entry point, creates the
other classes)
- :class:`deltachat.chatting.Contact`
- :class:`deltachat.chatting.Chat`
- :class:`deltachat.contact.Contact`
- :class:`deltachat.chat.Chat`
- :class:`deltachat.message.Message`
Account
@@ -22,13 +22,13 @@ Account
Contact
-------
.. autoclass:: deltachat.chatting.Contact
.. autoclass:: deltachat.contact.Contact
:members:
Chat
----
.. autoclass:: deltachat.chatting.Chat
.. autoclass:: deltachat.chat.Chat
:members:
Message

View File

@@ -55,7 +55,7 @@ master_doc = 'index'
# General information about the project.
project = u'deltachat'
copyright = u'2018, holger krekel and contributors'
copyright = u'2020, holger krekel and contributors'
# The language for content autogenerated by Sphinx. Refer to documentation

View File

@@ -1,15 +1,14 @@
deltachat python bindings
=========================
The ``deltachat`` Python package provides two bindings for the core Rust-library
of the https://delta.chat messaging ecosystem:
The ``deltachat`` Python package provides two layers of bindings for the
core Rust-library of the https://delta.chat messaging ecosystem:
- :doc:`api` is a high level interface to deltachat-core which aims
to be memory safe and thoroughly tested through continous tox/pytest runs.
- :doc:`capi` is a lowlevel CFFI-binding to the previous
`deltachat-core C-API <https://c.delta.chat>`_ (so far the Rust library
replicates exactly the same C-level API).
- :doc:`lapi` is a lowlevel CFFI-binding to the `Rust Core
<https://github.com/deltachat/deltachat-core-rust>`_.
@@ -28,7 +27,6 @@ getting started
links
changelog
api
capi
lapi
..

View File

@@ -2,7 +2,13 @@
low level API reference
===================================
for full C-docs, defines and function checkout :doc:`capi`
for full doxygen-generated C-docs, defines and functions please checkout
https://c.delta.chat
Python low-level capi calls
---------------------------
.. automodule:: deltachat.capi.lib

View File

@@ -6,20 +6,23 @@
import os
import subprocess
import os
import sys
if __name__ == "__main__":
os.environ["DCC_RS_TARGET"] = target = "release"
target = os.environ.get("DCC_RS_TARGET")
if target is None:
os.environ["DCC_RS_TARGET"] = target = "debug"
if "DCC_RS_DEV" not in os.environ:
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.environ["DCC_RS_DEV"] = dn
os.environ["RUSTFLAGS"] = "-g"
subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target
])
cmd = ["cargo", "build", "-p", "deltachat_ffi"]
if target == 'release':
cmd.append("--release")
subprocess.check_call(cmd)
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
subprocess.check_call([
"pip", "install", "-e", "."
])
if len(sys.argv) <= 1 or sys.argv[1] != "onlybuild":
subprocess.check_call([
sys.executable, "-m", "pip", "install", "-e", "."
])

View File

@@ -13,11 +13,12 @@ def main():
"root": "..",
"relative_to": __file__,
'tag_regex': r'^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
'git_describe_command': "git describe --dirty --tags --long --match py-*.*",
},
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
long_description=long_description,
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
install_requires=['cffi>=1.0.0', 'six'],
install_requires=['cffi>=1.0.0', 'pluggy'],
packages=setuptools.find_packages('src'),
package_dir={'': 'src'},
cffi_modules=['src/deltachat/_build.py:ffibuilder'],

View File

@@ -29,7 +29,10 @@ def ffibuilder():
extra_link_args = []
else:
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')]
target_dir = os.environ.get("CARGO_TARGET_DIR")
if target_dir is None:
target_dir = os.path.join(projdir, 'target')
objs = [os.path.join(target_dir, target, 'libdeltachat.a')]
assert os.path.exists(objs[0]), objs
incs = [os.path.join(projdir, 'deltachat-ffi')]
else:

View File

@@ -1,56 +1,65 @@
""" Account class implementation. """
from __future__ import print_function
import atexit
import threading
import os
import re
import time
from array import array
try:
from queue import Queue, Empty
except ImportError:
from Queue import Queue, Empty
from queue import Queue
import deltachat
from . import const
from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
from .chatting import Contact, Chat, Message
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot
from .chat import Chat
from .message import Message
from .contact import Contact
from .eventlogger import EventLogger
from .hookspec import get_plugin_manager, hookimpl
class Account(object):
""" Each account is tied to a sqlite database file which is fully managed
by the underlying deltachat c-library. All public Account methods are
by the underlying deltachat core library. All public Account methods are
meant to be memory-safe and return memory-safe objects.
"""
def __init__(self, db_path, logid=None, eventlogging=True):
def __init__(self, db_path, logid=None, os_name=None, debug=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
:param os_name: this will be put to the X-Mailer header in outgoing messages
:param debug: turn on debug logging for events.
"""
self._dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, as_dc_charpointer(os_name)),
_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)
self._evlogger = EventLogger(self, logid, debug)
self._threads = IOThreads(self._dc_context, self._evlogger._log_event)
# register event call back and initialize plugin system
def _ll_event(ctx, evt_name, data1, data2):
assert ctx == self._dc_context
self.pluggy.hook.process_low_level_event(
account=self, event_name=evt_name, data1=data1, data2=data2
)
self.pluggy = get_plugin_manager()
self.pluggy.register(self._evlogger)
deltachat.set_context_callback(self._dc_context, _ll_event)
# open database
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._configkeys = self.get_config("sys.config_keys").split()
self._imex_completed = threading.Event()
atexit.register(self.shutdown)
# XXX this can cause "illegal instructions" at test ends so we omit it for now
# def __del__(self):
# self.shutdown()
@@ -70,6 +79,18 @@ class Account(object):
d[key.lower()] = value
return d
def set_stock_translation(self, id, string):
""" set stock translation string.
:param id: id of stock string (const.DC_STR_*)
:param value: string to set as new transalation
:returns: None
"""
string = string.encode("utf8")
res = lib.dc_set_stock_translation(self._dc_context, id, string)
if res == 0:
raise ValueError("could not set translation string")
def set_config(self, name, value):
""" set configuration values.
@@ -79,9 +100,12 @@ class Account(object):
"""
self._check_config_key(name)
name = name.encode("utf8")
value = value.encode("utf8")
if name == b"addr" and self.is_configured():
raise ValueError("can not change 'addr' after account is configured.")
if value is not None:
value = value.encode("utf8")
else:
value = ffi.NULL
lib.dc_set_config(self._dc_context, name, value)
def get_config(self, name):
@@ -98,6 +122,18 @@ class Account(object):
assert res != ffi.NULL, "config value not found for: {!r}".format(name)
return from_dc_charpointer(res)
def _preconfigure_keypair(self, addr, public, secret):
"""See dc_preconfigure_keypair() in deltachat.h.
In other words, you don't need this.
"""
res = lib.dc_preconfigure_keypair(self._dc_context,
as_dc_charpointer(addr),
as_dc_charpointer(public),
as_dc_charpointer(secret))
if res == 0:
raise Exception("Failed to set key")
def configure(self, **kwargs):
""" set config values and configure this account.
@@ -117,15 +153,41 @@ class Account(object):
"""
return lib.dc_is_configured(self._dc_context)
def set_avatar(self, img_path):
"""Set self avatar.
:raises ValueError: if profile image could not be set
:returns: None
"""
if img_path is None:
self.set_config("selfavatar", None)
else:
assert os.path.exists(img_path), img_path
self.set_config("selfavatar", img_path)
def check_is_configured(self):
""" Raise ValueError if this account is not configured. """
if not self.is_configured():
raise ValueError("need to configure first")
def get_infostring(self):
""" return info of the configured account. """
self.check_is_configured()
return from_dc_charpointer(lib.dc_get_info(self._dc_context))
def empty_server_folders(self, inbox=False, mvbox=False):
""" empty server folders. """
flags = 0
if inbox:
flags |= const.DC_EMPTY_INBOX
if mvbox:
flags |= const.DC_EMPTY_MVBOX
if not flags:
raise ValueError("no flags set")
lib.dc_empty_server(self._dc_context, flags)
def get_latest_backupfile(self, backupdir):
""" return the latest backup file in a given directory.
"""
res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir))
if res == ffi.NULL:
return None
return from_dc_charpointer(res)
def get_blobdir(self):
""" return the directory for files.
@@ -136,9 +198,9 @@ class Account(object):
return from_dc_charpointer(lib.dc_get_blobdir(self._dc_context))
def get_self_contact(self):
""" return this account's identity as a :class:`deltachat.chatting.Contact`.
""" return this account's identity as a :class:`deltachat.contact.Contact`.
:returns: :class:`deltachat.chatting.Contact`
:returns: :class:`deltachat.contact.Contact`
"""
self.check_is_configured()
return Contact(self._dc_context, const.DC_CONTACT_ID_SELF)
@@ -150,7 +212,7 @@ class Account(object):
:param email: email-address (text type)
:param name: display name for this contact (optional)
:returns: :class:`deltachat.chatting.Contact` instance.
:returns: :class:`deltachat.contact.Contact` instance.
"""
name = as_dc_charpointer(name)
email = as_dc_charpointer(email)
@@ -176,7 +238,7 @@ class Account(object):
whose name or e-mail matches query.
:param only_verified: if true only return verified contacts.
:param with_self: if true the self-contact is also returned.
:returns: list of :class:`deltachat.chatting.Contact` objects.
:returns: list of :class:`deltachat.contact.Contact` objects.
"""
flags = 0
query = as_dc_charpointer(query)
@@ -194,7 +256,7 @@ class Account(object):
""" create or get an existing 1:1 chat object for the specified contact or contact id.
:param contact: chat_id (int) or contact object.
:returns: a :class:`deltachat.chatting.Chat` object.
:returns: a :class:`deltachat.chat.Chat` object.
"""
if hasattr(contact, "id"):
if contact._dc_context != self._dc_context:
@@ -211,7 +273,7 @@ class Account(object):
the specified message.
:param message: messsage id or message instance.
:returns: a :class:`deltachat.chatting.Chat` object.
:returns: a :class:`deltachat.chat.Chat` object.
"""
if hasattr(message, "id"):
if self._dc_context != message._dc_context:
@@ -229,16 +291,16 @@ class Account(object):
Chats are unpromoted until the first message is sent.
:param verified: if true only verified contacts can be added.
:returns: a :class:`deltachat.chatting.Chat` object.
:returns: a :class:`deltachat.chat.Chat` object.
"""
bytes_name = name.encode("utf8")
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
return Chat(self, chat_id)
def get_chats(self):
""" return list of chats.
:returns: a list of :class:`deltachat.chatting.Chat` objects.
:returns: a list of :class:`deltachat.chat.Chat` objects.
"""
dc_chatlist = ffi.gc(
lib.dc_get_chatlist(self._dc_context, 0, ffi.NULL, 0),
@@ -256,9 +318,24 @@ class Account(object):
return Chat(self, const.DC_CHAT_ID_DEADDROP)
def get_message_by_id(self, msg_id):
""" return Message instance. """
""" return Message instance.
:param msg_id: integer id of this message.
:returns: :class:`deltachat.message.Message` instance.
"""
return Message.from_db(self, msg_id)
def get_chat_by_id(self, chat_id):
""" return Chat instance.
:param chat_id: integer id of this chat.
:returns: :class:`deltachat.chat.Chat` instance.
:raises: ValueError if chat does not exist.
"""
res = lib.dc_get_chat(self._dc_context, chat_id)
if res == ffi.NULL:
raise ValueError("cannot get chat with id={}".format(chat_id))
lib.dc_chat_unref(res)
return Chat(self, chat_id)
def mark_seen_messages(self, messages):
""" mark the given set of messages as seen.
@@ -275,7 +352,7 @@ class Account(object):
""" Forward list of messages to a chat.
:param messages: list of :class:`deltachat.message.Message` object.
:param chat: :class:`deltachat.chatting.Chat` object.
:param chat: :class:`deltachat.chat.Chat` object.
:returns: None
"""
msg_ids = [msg.id for msg in messages]
@@ -290,31 +367,48 @@ class Account(object):
msg_ids = [msg.id for msg in messages]
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_to_dir(self, backupdir):
"""return after all delta chat state is exported to a new file in
the specified directory.
"""
snap_files = os.listdir(backupdir)
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
for x in os.listdir(backupdir):
if x not in snap_files:
return os.path.join(backupdir, x)
def export_self_keys(self, path):
""" export public and private keys to the specified directory. """
return self._export(path, imex_cmd=1)
def import_from_file(self, path):
"""import delta chat state from the specified backup file.
def export_all(self, path):
"""return new file containing a backup of all database state
(chats, contacts, keys, media, ...). The file is created in the
the `path` directory.
"""
export_files = self._export(path, 11)
if len(export_files) != 1:
raise RuntimeError("found more than one new file")
return export_files[0]
def _export(self, path, imex_cmd):
with ImexTracker(self) as imex_tracker:
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
return imex_tracker.wait_finish()
def import_self_keys(self, path):
""" Import private keys found in the `path` directory.
The last imported key is made the default keys unless its name
contains the string legacy. Public keys are not imported.
"""
self._import(path, imex_cmd=2)
def import_all(self, path):
"""import delta chat state from the specified backup `path` (a file).
The account must be in unconfigured state for import to attempted.
"""
assert not self.is_configured(), "cannot import into configured account"
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
self._import(path, imex_cmd=12)
def _import(self, path, imex_cmd):
with ImexTracker(self) as imex_tracker:
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
imex_tracker.wait_finish()
def initiate_key_transfer(self):
"""return setup code after a Autocrypt setup message
@@ -329,7 +423,69 @@ class Account(object):
raise RuntimeError("could not send out autocrypt setup message")
return from_dc_charpointer(res)
def start_threads(self):
def get_setup_contact_qr(self):
""" get/create Setup-Contact QR Code as ascii-string.
this string needs to be transferred to another DC account
in a second channel (typically used by mobiles with QRcode-show + scan UX)
where qr_setup_contact(qr) is called.
"""
res = lib.dc_get_securejoin_qr(self._dc_context, 0)
return from_dc_charpointer(res)
def check_qr(self, qr):
""" check qr code and return :class:`ScannedQRCode` instance representing the result"""
res = ffi.gc(
lib.dc_check_qr(self._dc_context, as_dc_charpointer(qr)),
lib.dc_lot_unref
)
lot = DCLot(res)
if lot.state() == const.DC_QR_ERROR:
raise ValueError("invalid or unknown QR code: {}".format(lot.text1()))
return ScannedQRCode(lot)
def qr_setup_contact(self, qr):
""" setup contact and return a Chat after contact is established.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
is returned.
:param qr: valid "setup contact" QR code (all other QR codes will result in an exception)
"""
assert self.check_qr(qr).is_ask_verifycontact()
chat_id = lib.dc_join_securejoin(self._dc_context, as_dc_charpointer(qr))
if chat_id == 0:
raise ValueError("could not setup secure contact")
return Chat(self, chat_id)
def qr_join_chat(self, qr):
""" join a chat group through a QR code.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
is returned which is the chat that we just joined.
:param qr: valid "join-group" QR code (all other QR codes will result in an exception)
"""
assert self.check_qr(qr).is_ask_verifygroup()
chat_id = lib.dc_join_securejoin(self._dc_context, as_dc_charpointer(qr))
if chat_id == 0:
raise ValueError("could not join group")
return Chat(self, chat_id)
def stop_ongoing(self):
lib.dc_stop_ongoing_process(self._dc_context)
#
# meta API for start/stop and event based processing
#
def wait_next_incoming_message(self):
""" wait for and return next incoming message. """
ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
return self.get_message_by_id(ev[2])
def start_threads(self, mvbox=False, sentbox=False):
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
@@ -337,34 +493,74 @@ class Account(object):
"""
if not self.is_configured():
self.configure()
self._threads.start()
self._threads.start(mvbox=mvbox, sentbox=sentbox)
def stop_threads(self, wait=True):
""" stop IMAP/SMTP threads. """
lib.dc_stop_ongoing_process(self._dc_context)
self._threads.stop(wait=wait)
if self._threads.is_started():
self.stop_ongoing()
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
# print("SHUTDOWN", self)
self.stop_threads(wait=False)
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
atexit.unregister(self.shutdown)
self.pluggy.unregister(self._evlogger)
def _process_event(self, ctx, evt_name, data1, data2):
assert ctx == self._dc_context
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 set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
"""set a new location. It effects all chats where we currently
have enabled location streaming.
def on_dc_event_imex_progress(self, data1, data2):
if data1 == 1000:
self._imex_completed.set()
:param latitude: float (use 0.0 if not known)
:param longitude: float (use 0.0 if not known)
:param accuracy: float (use 0.0 if not known)
:raises: ValueError if no chat is currently streaming locations
:returns: None
"""
dc_res = lib.dc_set_location(self._dc_context, latitude, longitude, accuracy)
if dc_res == 0:
raise ValueError("no chat is streaming locations")
class ImexTracker:
def __init__(self, account):
self._imex_events = Queue()
self.account = account
def __enter__(self):
self.account.pluggy.register(self)
return self
def __exit__(self, *args):
self.account.pluggy.unregister(self)
@hookimpl
def process_low_level_event(self, account, event_name, data1, data2):
# there could be multiple accounts instantiated
if self.account is not account:
return
if event_name == "DC_EVENT_IMEX_PROGRESS":
self._imex_events.put(data1)
elif event_name == "DC_EVENT_IMEX_FILE_WRITTEN":
self._imex_events.put(data1)
def wait_finish(self, progress_timeout=60):
""" Return list of written files, raise ValueError if ExportFailed. """
files_written = []
while True:
ev = self._imex_events.get(timeout=progress_timeout)
if isinstance(ev, str):
files_written.append(ev)
elif ev == 0:
raise ValueError("export failed, exp-files: {}".format(files_written))
elif ev == 1000:
return files_written
class IOThreads:
@@ -377,10 +573,14 @@ class IOThreads:
def is_started(self):
return len(self._name2thread) > 0
def start(self, imap=True, smtp=True):
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
assert not self.is_started()
if imap:
self._start_one_thread("imap", self.imap_thread_run)
self._start_one_thread("inbox", self.imap_thread_run)
if mvbox:
self._start_one_thread("mvbox", self.mvbox_thread_run)
if sentbox:
self._start_one_thread("sentbox", self.sentbox_thread_run)
if smtp:
self._start_one_thread("smtp", self.smtp_thread_run)
@@ -391,98 +591,58 @@ class IOThreads:
def stop(self, wait=False):
self._thread_quitflag = True
# Workaround for a race condition. Make sure that thread is
# not in between checking for quitflag and entering idle.
time.sleep(0.5)
lib.dc_interrupt_imap_idle(self._dc_context)
lib.dc_interrupt_smtp_idle(self._dc_context)
lib.dc_interrupt_mvbox_idle(self._dc_context)
lib.dc_interrupt_sentbox_idle(self._dc_context)
if wait:
for name, thread in self._name2thread.items():
thread.join()
def imap_thread_run(self):
self._log_event("py-bindings-info", 0, "IMAP THREAD START")
self._log_event("py-bindings-info", 0, "INBOX 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")
if not self._thread_quitflag:
lib.dc_perform_imap_fetch(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_imap_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED")
def mvbox_thread_run(self):
self._log_event("py-bindings-info", 0, "MVBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_mvbox_jobs(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_mvbox_fetch(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_mvbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "MVBOX THREAD FINISHED")
def sentbox_thread_run(self):
self._log_event("py-bindings-info", 0, "SENTBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_sentbox_jobs(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_sentbox_fetch(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_sentbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "SENTBOX 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)
if not self._thread_quitflag:
lib.dc_perform_smtp_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED")
class EventLogger:
_loglock = threading.RLock()
def __init__(self, dc_context, logid=None, debug=True):
self._dc_context = dc_context
self._event_queue = Queue()
self._debug = debug
if logid is None:
logid = str(self._dc_context).strip(">").split()[-1]
self.logid = logid
self._timeout = None
self.init_time = time.time()
def __call__(self, evt_name, data1, data2):
self._log_event(evt_name, data1, data2)
self._event_queue.put((evt_name, data1, data2))
def set_timeout(self, timeout):
self._timeout = timeout
def get(self, timeout=None, check_error=True):
timeout = timeout or self._timeout
ev = self._event_queue.get(timeout=timeout)
if check_error and ev[0] == "DC_EVENT_ERROR":
raise ValueError("{}({!r},{!r})".format(*ev))
return ev
def ensure_event_not_queued(self, event_name_regex):
__tracebackhide__ = True
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
try:
ev = self._event_queue.get(False)
except Empty:
break
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
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:
ev = self.get()
if rex.match(ev[0]):
return ev
def get_info_matching(self, regex):
rex = re.compile("(?:{}).*".format(regex))
while 1:
ev = self.get_matching("DC_EVENT_INFO")
if rex.match(ev[2]):
return ev
def _log_event(self, evt_name, data1, data2):
# don't show events that are anyway empty impls now
if evt_name == "DC_EVENT_GET_STRING":
return
if self._debug:
evpart = "{}({!r},{!r})".format(evt_name, data1, data2)
self._log(evpart)
def _log(self, msg):
t = threading.currentThread()
tname = getattr(t, "name", t)
if tname == "MainThread":
tname = "MAIN"
with self._loglock:
print("{:2.2f} [{}-{}] {}".format(time.time() - self.init_time, tname, self.logid, msg))
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
# destructor for dc_context
dc_context_unref(dc_context)
@@ -492,3 +652,18 @@ def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
# we are deep into Python Interpreter shutdown,
# so no need to clear the callback context mapping.
pass
class ScannedQRCode:
def __init__(self, dc_lot):
self._dc_lot = dc_lot
def is_ask_verifycontact(self):
return self._dc_lot.state() == const.DC_QR_ASK_VERIFYCONTACT
def is_ask_verifygroup(self):
return self._dc_lot.state() == const.DC_QR_ASK_VERIFYGROUP
@property
def contact_id(self):
return self._dc_lot.id()

View File

@@ -1,57 +1,16 @@
""" chatting related objects: Contact, Chat, Message. """
""" Chat and Location related API. """
import mimetypes
from . import props
import calendar
import json
from datetime import datetime
import os
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
from .capi import lib, ffi
from . import const
from .message import Message
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
@property
def _dc_contact(self):
return ffi.gc(
lib.dc_get_contact(self._dc_context, self.id),
lib.dc_contact_unref
)
@props.with_doc
def addr(self):
""" normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@props.with_doc
def display_name(self):
""" display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact))
def is_blocked(self):
""" Return True if the contact is blocked. """
return lib.dc_contact_is_blocked(self._dc_contact)
def is_verified(self):
""" Return True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact)
class Chat(object):
""" Chat object which manages members and through which you can send and retrieve messages.
@@ -99,6 +58,13 @@ class Chat(object):
"""
return self.id == const.DC_CHAT_ID_DEADDROP
def is_muted(self):
""" return true if this chat is muted.
:returns: True if chat is muted, False otherwise.
"""
return lib.dc_chat_is_muted(self._dc_chat)
def is_promoted(self):
""" return True if this chat is promoted, i.e.
the member contacts are aware of their membership,
@@ -108,6 +74,13 @@ class Chat(object):
"""
return not lib.dc_chat_is_unpromoted(self._dc_chat)
def is_verified(self):
""" return True if this chat is a verified group.
:returns: True if chat is verified, False otherwise.
"""
return lib.dc_chat_is_verified(self._dc_chat)
def get_name(self):
""" return name of this chat.
@@ -118,12 +91,43 @@ class Chat(object):
def set_name(self, name):
""" set name of this chat.
:param: name as a unicode string.
:param name: as a unicode string.
:returns: None
"""
name = as_dc_charpointer(name)
return lib.dc_set_chat_name(self._dc_context, self.id, name)
def mute(self, duration=None):
""" mutes the chat
:param duration: Number of seconds to mute the chat for. None to mute until unmuted again.
:returns: None
"""
if duration is None:
mute_duration = -1
else:
mute_duration = duration
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, mute_duration)
if not bool(ret):
raise ValueError("Call to dc_set_chat_mute_duration failed")
def unmute(self):
""" unmutes the chat
:returns: None
"""
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, 0)
if not bool(ret):
raise ValueError("Failed to unmute chat")
def get_mute_duration(self):
""" Returns the number of seconds until the mute of this chat is lifted.
:param duration:
:returns: Returns the number of seconds the chat is still muted for. (0 for not muted, -1 forever muted)
"""
return bool(lib.dc_chat_get_remaining_mute_duration(self.id))
def get_type(self):
""" return type of this chat.
@@ -131,8 +135,42 @@ class Chat(object):
"""
return lib.dc_chat_get_type(self._dc_chat)
def get_join_qr(self):
""" get/create Join-Group QR Code as ascii-string.
this string needs to be transferred to another DC account
in a second channel (typically used by mobiles with QRcode-show + scan UX)
where account.join_with_qrcode(qr) needs to be called.
"""
res = lib.dc_get_securejoin_qr(self._dc_context, self.id)
return from_dc_charpointer(res)
# ------ chat messaging API ------------------------------
def send_msg(self, msg):
"""send a message by using a ready Message object.
:param msg: a :class:`deltachat.message.Message` instance
previously returned by
e.g. :meth:`deltachat.message.Message.new_empty` or
:meth:`prepare_file`.
:raises ValueError: if message can not be sent.
:returns: a :class:`deltachat.message.Message` instance as
sent out. This is the same object as was passed in, which
has been modified with the new state of the core.
"""
if msg.is_out_preparing():
assert msg.id != 0
# get a fresh copy of dc_msg, the core needs it
msg = Message.from_db(self.account, msg.id)
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
# modify message in place to avoid bad state for the caller
msg._dc_msg = Message.from_db(self.account, sent_id)._dc_msg
return msg
def send_text(self, text):
""" send a text message and return the resulting Message instance.
@@ -154,9 +192,12 @@ class Chat(object):
:raises ValueError: if message can not be send/chat does not exist.
:returns: the resulting :class:`deltachat.message.Message` instance
"""
msg = self.prepare_message_file(path=path, mime_type=mime_type)
self.send_prepared(msg)
return msg
msg = Message.new_empty(self.account, view_type="file")
msg.set_file(path, mime_type)
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
return Message.from_db(self.account, sent_id)
def send_image(self, path):
""" send an image message and return the resulting Message instance.
@@ -166,9 +207,12 @@ class Chat(object):
:returns: the resulting :class:`deltachat.message.Message` instance
"""
mime_type = mimetypes.guess_type(path)[0]
msg = self.prepare_message_file(path=path, mime_type=mime_type, view_type="image")
self.send_prepared(msg)
return msg
msg = Message.new_empty(self.account, view_type="image")
msg.set_file(path, mime_type)
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
return Message.from_db(self.account, sent_id)
def prepare_message(self, msg):
""" create a new prepared message.
@@ -267,6 +311,12 @@ class Chat(object):
"""
return lib.dc_marknoticed_chat(self._dc_context, self.id)
def get_summary(self):
""" return dictionary with summary information. """
dc_res = lib.dc_chat_get_info_json(self._dc_context, self.id)
s = from_dc_charpointer(dc_res)
return json.loads(s)
# ------ group management API ------------------------------
def add_contact(self, contact):
@@ -294,10 +344,10 @@ class Chat(object):
def get_contacts(self):
""" get all contacts for this chat.
:params: contact object.
:raises ValueError: if contact could not be added
:returns: list of :class:`deltachat.chatting.Contact` objects for this chat
:returns: list of :class:`deltachat.contact.Contact` objects for this chat
"""
from .contact import Contact
dc_array = ffi.gc(
lib.dc_get_chat_contacts(self._dc_context, self.id),
lib.dc_array_unref
@@ -305,3 +355,123 @@ class Chat(object):
return list(iter_array(
dc_array, lambda id: Contact(self._dc_context, id))
)
def set_profile_image(self, img_path):
"""Set group profile image.
If the group is already promoted (any message was sent to the group),
all group members are informed by a special status message that is sent
automatically by this function.
:params img_path: path to image object
:raises ValueError: if profile image could not be set
:returns: None
"""
assert os.path.exists(img_path), img_path
p = as_dc_charpointer(img_path)
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, p)
if res != 1:
raise ValueError("Setting Profile Image {!r} failed".format(p))
def remove_profile_image(self):
"""remove group profile image.
If the group is already promoted (any message was sent to the group),
all group members are informed by a special status message that is sent
automatically by this function.
:raises ValueError: if profile image could not be reset
:returns: None
"""
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, ffi.NULL)
if res != 1:
raise ValueError("Removing Profile Image failed")
def get_profile_image(self):
"""Get group profile image.
For groups, this is the image set by any group member using
set_chat_profile_image(). For normal chats, this is the image
set by each remote user on their own using dc_set_config(context,
"selfavatar", image).
:returns: path to profile image, None if no profile image exists.
"""
dc_res = lib.dc_chat_get_profile_image(self._dc_chat)
if dc_res == ffi.NULL:
return None
return from_dc_charpointer(dc_res)
def get_color(self):
"""return the color of the chat.
:returns: color as 0x00rrggbb
"""
return lib.dc_chat_get_color(self._dc_chat)
def get_subtitle(self):
"""return the subtitle of the chat
:returns: the subtitle
"""
return from_dc_charpointer(lib.dc_chat_get_subtitle(self._dc_chat))
# ------ location streaming API ------------------------------
def is_sending_locations(self):
"""return True if this chat has location-sending enabled currently.
:returns: True if location sending is enabled.
"""
return lib.dc_is_sending_locations_to_chat(self._dc_context, self.id)
def is_archived(self):
"""return True if this chat is archived.
:returns: True if archived.
"""
return lib.dc_chat_get_visibility(self._dc_chat) == const.DC_CHAT_VISIBILITY_ARCHIVED
def enable_sending_locations(self, seconds):
"""enable sending locations for this chat.
all subsequent messages will carry a location with them.
"""
lib.dc_send_locations_to_chat(self._dc_context, self.id, seconds)
def get_locations(self, contact=None, timestamp_from=None, timestamp_to=None):
"""return list of locations for the given contact in the given timespan.
:param contact: the contact for which locations shall be returned.
:param timespan_from: a datetime object or None (indicating "since beginning")
:param timespan_to: a datetime object or None (indicating up till now)
:returns: list of :class:`deltachat.chat.Location` objects.
"""
if timestamp_from is None:
time_from = 0
else:
time_from = calendar.timegm(timestamp_from.utctimetuple())
if timestamp_to is None:
time_to = 0
else:
time_to = calendar.timegm(timestamp_to.utctimetuple())
if contact is None:
contact_id = 0
else:
contact_id = contact.id
dc_array = lib.dc_get_locations(self._dc_context, self.id, contact_id, time_from, time_to)
return [
Location(
latitude=lib.dc_array_get_latitude(dc_array, i),
longitude=lib.dc_array_get_longitude(dc_array, i),
accuracy=lib.dc_array_get_accuracy(dc_array, i),
timestamp=datetime.utcfromtimestamp(lib.dc_array_get_timestamp(dc_array, i)))
for i in range(lib.dc_array_get_cnt(dc_array))
]
class Location:
def __init__(self, latitude, longitude, accuracy, timestamp):
assert isinstance(timestamp, datetime)
self.latitude = latitude
self.longitude = longitude
self.accuracy = accuracy
self.timestamp = timestamp
def __eq__(self, other):
return self.__dict__ == other.__dict__

View File

@@ -13,6 +13,16 @@ DC_GCL_NO_SPECIALS = 0x02
DC_GCL_ADD_ALLDONE_HINT = 0x04
DC_GCL_VERIFIED_ONLY = 0x01
DC_GCL_ADD_SELF = 0x02
DC_QR_ASK_VERIFYCONTACT = 200
DC_QR_ASK_VERIFYGROUP = 202
DC_QR_FPR_OK = 210
DC_QR_FPR_MISMATCH = 220
DC_QR_FPR_WITHOUT_ADDR = 230
DC_QR_ACCOUNT = 250
DC_QR_ADDR = 320
DC_QR_TEXT = 330
DC_QR_URL = 332
DC_QR_ERROR = 400
DC_CHAT_ID_DEADDROP = 1
DC_CHAT_ID_TRASH = 3
DC_CHAT_ID_MSGS_IN_CREATION = 4
@@ -38,19 +48,39 @@ DC_STATE_OUT_FAILED = 24
DC_STATE_OUT_DELIVERED = 26
DC_STATE_OUT_MDN_RCVD = 28
DC_CONTACT_ID_SELF = 1
DC_CONTACT_ID_DEVICE = 2
DC_CONTACT_ID_INFO = 2
DC_CONTACT_ID_DEVICE = 5
DC_CONTACT_ID_LAST_SPECIAL = 9
DC_MSG_TEXT = 10
DC_MSG_IMAGE = 20
DC_MSG_GIF = 21
DC_MSG_STICKER = 23
DC_MSG_AUDIO = 40
DC_MSG_VOICE = 41
DC_MSG_VIDEO = 50
DC_MSG_FILE = 60
DC_LP_AUTH_OAUTH2 = 0x2
DC_LP_AUTH_NORMAL = 0x4
DC_LP_IMAP_SOCKET_STARTTLS = 0x100
DC_LP_IMAP_SOCKET_SSL = 0x200
DC_LP_IMAP_SOCKET_PLAIN = 0x400
DC_LP_SMTP_SOCKET_STARTTLS = 0x10000
DC_LP_SMTP_SOCKET_SSL = 0x20000
DC_LP_SMTP_SOCKET_PLAIN = 0x40000
DC_CERTCK_AUTO = 0
DC_CERTCK_STRICT = 1
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
DC_EMPTY_MVBOX = 0x01
DC_EMPTY_INBOX = 0x02
DC_EVENT_INFO = 100
DC_EVENT_SMTP_CONNECTED = 101
DC_EVENT_IMAP_CONNECTED = 102
DC_EVENT_SMTP_MESSAGE_SENT = 103
DC_EVENT_IMAP_MESSAGE_DELETED = 104
DC_EVENT_IMAP_MESSAGE_MOVED = 105
DC_EVENT_IMAP_FOLDER_EMPTIED = 106
DC_EVENT_NEW_BLOB_FILE = 150
DC_EVENT_DELETED_BLOB_FILE = 151
DC_EVENT_WARNING = 300
DC_EVENT_ERROR = 400
DC_EVENT_ERROR_NETWORK = 401
@@ -68,16 +98,73 @@ DC_EVENT_IMEX_PROGRESS = 2051
DC_EVENT_IMEX_FILE_WRITTEN = 2052
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_GET_STRING = 2091
DC_EVENT_HTTP_GET = 2100
DC_EVENT_HTTP_POST = 2110
DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062
DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081
DC_EVENT_GET_STRING = 2091
DC_STR_SELFNOTINGRP = 21
DC_KEY_GEN_DEFAULT = 0
DC_KEY_GEN_RSA2048 = 1
DC_KEY_GEN_ED25519 = 2
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3
DC_CHAT_VISIBILITY_NORMAL = 0
DC_CHAT_VISIBILITY_ARCHIVED = 1
DC_CHAT_VISIBILITY_PINNED = 2
DC_STR_NOMESSAGES = 1
DC_STR_SELF = 2
DC_STR_DRAFT = 3
DC_STR_MEMBER = 4
DC_STR_CONTACT = 6
DC_STR_VOICEMESSAGE = 7
DC_STR_DEADDROP = 8
DC_STR_IMAGE = 9
DC_STR_VIDEO = 10
DC_STR_AUDIO = 11
DC_STR_FILE = 12
DC_STR_STATUSLINE = 13
DC_STR_NEWGROUPDRAFT = 14
DC_STR_MSGGRPNAME = 15
DC_STR_MSGGRPIMGCHANGED = 16
DC_STR_MSGADDMEMBER = 17
DC_STR_MSGDELMEMBER = 18
DC_STR_MSGGROUPLEFT = 19
DC_STR_GIF = 23
DC_STR_ENCRYPTEDMSG = 24
DC_STR_E2E_AVAILABLE = 25
DC_STR_ENCR_TRANSP = 27
DC_STR_ENCR_NONE = 28
DC_STR_CANTDECRYPT_MSG_BODY = 29
DC_STR_FINGERPRINTS = 30
DC_STR_READRCPT = 31
DC_STR_READRCPT_MAILBODY = 32
DC_STR_MSGGRPIMGDELETED = 33
DC_STR_E2E_PREFERRED = 34
DC_STR_CONTACT_VERIFIED = 35
DC_STR_CONTACT_NOT_VERIFIED = 36
DC_STR_CONTACT_SETUP_CHANGED = 37
DC_STR_ARCHIVEDCHATS = 40
DC_STR_STARREDMSGS = 41
DC_STR_AC_SETUP_MSG_SUBJECT = 42
DC_STR_AC_SETUP_MSG_BODY = 43
DC_STR_SELFTALK_SUBTITLE = 50
DC_STR_CANNOT_LOGIN = 60
DC_STR_SERVER_RESPONSE = 61
DC_STR_MSGACTIONBYUSER = 62
DC_STR_MSGACTIONBYME = 63
DC_STR_MSGLOCATIONENABLED = 64
DC_STR_MSGLOCATIONDISABLED = 65
DC_STR_LOCATION = 66
DC_STR_STICKER = 67
DC_STR_DEVICE_MESSAGES = 68
DC_STR_COUNT = 68
# end const generated
def read_event_defines(f):
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_MSG|DC_STATE_|DC_CONTACT_ID_|DC_GCL|DC_CHAT)\S+)\s+([x\d]+).*')
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_EMPTY|DC_CERTCK|DC_STATE|DC_STR|'
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER|DC_KEY_GEN)_\S+)\s+([x\d]+).*')
for line in f:
m = rex.match(line)
if m:
@@ -90,7 +177,7 @@ if __name__ == "__main__":
if len(sys.argv) >= 2:
deltah = sys.argv[1]
else:
deltah = joinpath(dirname(dirname(dirname(here_dir))), "src", "deltachat.h")
deltah = joinpath(dirname(dirname(dirname(here_dir))), "deltachat-ffi", "deltachat.h")
assert os.path.exists(deltah)
lines = []

View File

@@ -0,0 +1,59 @@
""" Contact object. """
from . import props
from .cutil import from_dc_charpointer
from .capi import lib, ffi
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
@property
def _dc_contact(self):
return ffi.gc(
lib.dc_get_contact(self._dc_context, self.id),
lib.dc_contact_unref
)
@props.with_doc
def addr(self):
""" normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@props.with_doc
def display_name(self):
""" display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact))
def is_blocked(self):
""" Return True if the contact is blocked. """
return lib.dc_contact_is_blocked(self._dc_contact)
def is_verified(self):
""" Return True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact)
def get_profile_image(self):
"""Get contact profile image.
:returns: path to profile image, None if no profile image exists.
"""
dc_res = lib.dc_contact_get_profile_image(self._dc_contact)
if dc_res == ffi.NULL:
return None
return from_dc_charpointer(dc_res)

View File

@@ -1,5 +1,6 @@
from .capi import lib
from .capi import ffi
from datetime import datetime
def as_dc_charpointer(obj):
@@ -17,3 +18,29 @@ def iter_array(dc_array_t, constructor):
def from_dc_charpointer(obj):
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
class DCLot:
def __init__(self, dc_lot):
self._dc_lot = dc_lot
def id(self):
return lib.dc_lot_get_id(self._dc_lot)
def state(self):
return lib.dc_lot_get_state(self._dc_lot)
def text1(self):
return from_dc_charpointer(lib.dc_lot_get_text1(self._dc_lot))
def text1_meaning(self):
return lib.dc_lot_get_text1_meaning(self._dc_lot)
def text2(self):
return from_dc_charpointer(lib.dc_lot_get_text2(self._dc_lot))
def timestamp(self):
ts = lib.dc_lot_get_timestamp(self._dc_lot)
if ts == 0:
return None
return datetime.utcfromtimestamp(ts)

View File

@@ -0,0 +1,81 @@
import threading
import re
import time
from queue import Queue, Empty
from .hookspec import hookimpl
class EventLogger:
_loglock = threading.RLock()
def __init__(self, account, logid=None, debug=True):
self.account = account
self._event_queue = Queue()
self._debug = debug
if logid is None:
logid = str(self.account._dc_context).strip(">").split()[-1]
self.logid = logid
self._timeout = None
self.init_time = time.time()
@hookimpl
def process_low_level_event(self, account, event_name, data1, data2):
if self.account == account:
self._log_event(event_name, data1, data2)
self._event_queue.put((event_name, data1, data2))
def set_timeout(self, timeout):
self._timeout = timeout
def consume_events(self, check_error=True):
while not self._event_queue.empty():
self.get(check_error=check_error)
def get(self, timeout=None, check_error=True):
timeout = timeout or self._timeout
ev = self._event_queue.get(timeout=timeout)
if check_error and ev[0] == "DC_EVENT_ERROR":
raise ValueError("{}({!r},{!r})".format(*ev))
return ev
def ensure_event_not_queued(self, event_name_regex):
__tracebackhide__ = True
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
try:
ev = self._event_queue.get(False)
except Empty:
break
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
def get_matching(self, event_name_regex, check_error=True, timeout=None):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
ev = self.get(timeout=timeout, check_error=check_error)
if rex.match(ev[0]):
return ev
def get_info_matching(self, regex):
rex = re.compile("(?:{}).*".format(regex))
while 1:
ev = self.get_matching("DC_EVENT_INFO")
if rex.match(ev[2]):
return ev
def _log_event(self, evt_name, data1, data2):
# don't show events that are anyway empty impls now
if evt_name == "DC_EVENT_GET_STRING":
return
if self._debug:
evpart = "{}({!r},{!r})".format(evt_name, data1, data2)
self._log(evpart)
def _log(self, msg):
t = threading.currentThread()
tname = getattr(t, "name", t)
if tname == "MainThread":
tname = "MAIN"
with self._loglock:
print("{:2.2f} [{}-{}] {}".format(time.time() - self.init_time, tname, self.logid, msg))

View File

@@ -0,0 +1,25 @@
""" Hooks for python bindings """
import pluggy
name = "deltachat"
hookspec = pluggy.HookspecMarker(name)
hookimpl = pluggy.HookimplMarker(name)
_plugin_manager = None
def get_plugin_manager():
global _plugin_manager
if _plugin_manager is None:
_plugin_manager = pluggy.PluginManager(name)
_plugin_manager.add_hookspecs(DeltaChatHookSpecs)
return _plugin_manager
class DeltaChatHookSpecs:
""" Plugin Hook specifications for Python bindings to Delta Chat CFFI. """
@hookspec
def process_low_level_event(self, account, event_name, data1, data2):
""" process a CFFI low level events for a given account. """

View File

@@ -1,7 +1,6 @@
""" chatting related objects: Contact, Chat, Message. """
""" The Message object. """
import os
import shutil
from . import props
from .cutil import from_dc_charpointer, as_dc_charpointer
from .capi import lib, ffi
@@ -13,7 +12,7 @@ class Message(object):
""" Message object.
You obtain instances of it through :class:`deltachat.account.Account` or
:class:`deltachat.chatting.Chat`.
:class:`deltachat.chat.Chat`.
"""
def __init__(self, account, dc_msg):
self.account = account
@@ -58,8 +57,6 @@ class Message(object):
def set_text(self, text):
"""set text of this message. """
assert self.id > 0, "message not prepared"
assert self.is_out_preparing()
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
@props.with_doc
@@ -72,19 +69,6 @@ class Message(object):
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
blobdir = self.account.get_blobdir()
if not path.startswith(blobdir):
for i in range(50):
ext = "" if i == 0 else "-" + str(i)
dest = os.path.join(blobdir, os.path.basename(path) + ext)
if os.path.exists(dest):
continue
shutil.copyfile(path, dest)
break
else:
raise ValueError("could not create blobdir-path for {}".format(path))
path = dest
assert path.startswith(blobdir), path
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
@props.with_doc
@@ -101,6 +85,18 @@ class Message(object):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_setupcodebegin(self):
""" return the first characters of a setup code in a setup message. """
return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg))
def is_encrypted(self):
""" return True if this message was encrypted. """
return bool(lib.dc_msg_get_showpadlock(self._dc_msg))
def is_forwarded(self):
""" return True if this message was forwarded. """
return bool(lib.dc_msg_is_forwarded(self._dc_msg))
def get_message_info(self):
""" Return informational text for a single message.
@@ -110,7 +106,13 @@ class Message(object):
def continue_key_transfer(self, setup_code):
""" extract key and use it as primary key for this account. """
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
res = lib.dc_continue_key_transfer(
self._dc_context,
self.id,
as_dc_charpointer(setup_code)
)
if res == 0:
raise ValueError("could not decrypt")
@props.with_doc
def time_sent(self):
@@ -144,25 +146,25 @@ class Message(object):
if mime_headers:
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
if isinstance(s, bytes):
s = s.decode("ascii")
return email.message_from_bytes(s)
return email.message_from_string(s)
@property
def chat(self):
"""chat this message was posted in.
:returns: :class:`deltachat.chatting.Chat` object
:returns: :class:`deltachat.chat.Chat` object
"""
from .chatting import Chat
from .chat import Chat
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self.account, chat_id)
def get_sender_contact(self):
"""return the contact of who wrote the message.
:returns: :class:`deltachat.chatting.Contact` instance
:returns: :class:`deltachat.chat.Contact` instance
"""
from .chatting import Contact
from .contact import Contact
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
return Contact(self._dc_context, contact_id)
@@ -172,7 +174,7 @@ class Message(object):
@property
def _msgstate(self):
if self.id == 0:
dc_msg = self.message._dc_msg
dc_msg = self._dc_msg
else:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(

View File

@@ -0,0 +1,47 @@
"""Provider info class."""
from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer
class ProviderNotFoundError(Exception):
"""The provider information was not found."""
class Provider(object):
"""Provider information.
:param domain: The email to get the provider info for.
"""
def __init__(self, account, addr):
provider = ffi.gc(
lib.dc_provider_new_from_email(account._dc_context, as_dc_charpointer(addr)),
lib.dc_provider_unref,
)
if provider == ffi.NULL:
raise ProviderNotFoundError("Provider not found")
self._provider = provider
@property
def overview_page(self):
"""URL to the overview page of the provider on providers.delta.chat."""
return from_dc_charpointer(
lib.dc_provider_get_overview_page(self._provider))
@property
def get_before_login_hints(self):
"""Should be shown to the user on login."""
return from_dc_charpointer(
lib.dc_provider_get_before_login_hints(self._provider))
@property
def status(self):
"""The status of the provider information.
This is one of the
:attr:`deltachat.const.DC_PROVIDER_STATUS_OK`,
:attr:`deltachat.const.DC_PROVIDER_STATUS_PREPARATION` or
:attr:`deltachat.const.DC_PROVIDER_STATUS_BROKEN` constants.
"""
return lib.dc_provider_get_status(self._provider)

View File

@@ -1,10 +1,14 @@
from __future__ import print_function
import os
import sys
import py
import pytest
import requests
import time
from deltachat import Account
from deltachat import const
from deltachat.capi import lib
from _pytest.monkeypatch import MonkeyPatch
import tempfile
@@ -14,36 +18,41 @@ def pytest_addoption(parser):
help="a file with >=2 lines where each line "
"contains NAME=VALUE config settings for one account"
)
parser.addoption(
"--ignored", action="store_true",
help="Also run tests marked with the ignored marker",
)
def pytest_configure(config):
config.addinivalue_line(
"markers", "ignored: Mark test as bing slow, skipped unless --ignored is used."
)
cfg = config.getoption('--liveconfig')
if not cfg:
cfg = os.getenv('DCC_PY_LIVECONFIG')
cfg = os.getenv('DCC_NEW_TMP_EMAIL')
if cfg:
config.option.liveconfig = cfg
@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_runtest_setup(item):
if (list(item.iter_markers(name="ignored"))
and not item.config.getoption("ignored")):
pytest.skip("Ignored tests not requested, use --ignored")
def pytest_report_header(config, startdir):
summary = []
t = tempfile.mktemp()
m = MonkeyPatch()
try:
ac = Account(t, eventlogging=False)
m.setattr(sys.stdout, "write", lambda x: len(x))
ac = Account(t)
info = ac.get_info()
ac.shutdown()
finally:
m.undo()
os.remove(t)
summary.extend(['Deltachat core={} sqlite={}'.format(
info['deltachat_core_version'],
@@ -93,18 +102,22 @@ class SessionLiveConfigFromFile:
class SessionLiveConfigFromURL:
def __init__(self, url, create_token):
def __init__(self, url):
self.configlist = []
for i in range(2):
res = requests.post(url, json={"token_create_user": int(create_token)})
self.url = url
def get(self, index):
try:
return self.configlist[index]
except IndexError:
assert index == len(self.configlist), index
res = requests.post(self.url)
if res.status_code != 200:
pytest.skip("creating newtmpuser failed {!r}".format(res))
d = res.json()
config = dict(addr=d["email"], mail_pw=d["password"])
self.configlist.append(config)
def get(self, index):
return self.configlist[index]
return config
def exists(self):
return bool(self.configlist)
@@ -115,14 +128,24 @@ def session_liveconfig(request):
liveconfig_opt = request.config.option.liveconfig
if liveconfig_opt:
if liveconfig_opt.startswith("http"):
url, create_token = liveconfig_opt.split("#", 1)
return SessionLiveConfigFromURL(url, create_token)
return SessionLiveConfigFromURL(liveconfig_opt)
else:
return SessionLiveConfigFromFile(liveconfig_opt)
@pytest.fixture(scope='session')
def datadir():
"""The py.path.local object of the test-data/ directory."""
for path in reversed(py.path.local(__file__).parts()):
datadir = path.join('test-data')
if datadir.isdir():
return datadir
else:
pytest.skip('test-data directory not found')
@pytest.fixture
def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
def acfactory(pytestconfig, tmpdir, request, session_liveconfig, datadir):
class AccountMaker:
def __init__(self):
@@ -130,61 +153,110 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
self.offline_count = 0
self._finalizers = []
self.init_time = time.time()
self._generated_keys = ["alice", "bob", "charlie",
"dom", "elena", "fiona"]
def finalize(self):
while self._finalizers:
fin = self._finalizers.pop()
fin()
def make_account(self, path, logid):
ac = Account(path, logid=logid)
self._finalizers.append(ac.shutdown)
return ac
def get_unconfigured_account(self):
self.offline_count += 1
tmpdb = tmpdir.join("offlinedb%d" % self.offline_count)
ac = Account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
ac = self.make_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 _preconfigure_key(self, account, addr):
# Only set a key if we haven't used it yet for another account.
if self._generated_keys:
keyname = self._generated_keys.pop(0)
fname_pub = "key/{name}-public.asc".format(name=keyname)
fname_sec = "key/{name}-secret.asc".format(name=keyname)
account._preconfigure_keypair(addr,
datadir.join(fname_pub).read(),
datadir.join(fname_sec).read())
def get_configured_offline_account(self):
ac = self.get_unconfigured_account()
# do a pseudo-configured account
addr = "addr{}@offline.org".format(self.offline_count)
ac.set_config("addr", addr)
self._preconfigure_key(ac, addr)
lib.dc_set_config(ac._dc_context, b"configured_addr", addr.encode("ascii"))
ac.set_config("mail_pw", "123")
lib.dc_set_config(ac._dc_context, b"configured_mail_pw", b"123")
lib.dc_set_config(ac._dc_context, b"configured", b"1")
return ac
def get_online_configuring_account(self):
def get_online_config(self, pre_generated_key=True):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
pytest.skip("specify DCC_NEW_TMP_EMAIL or --liveconfig")
configdict = session_liveconfig.get(self.live_count)
self.live_count += 1
if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1"
# Enable strict certificate checks for online accounts
configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT)
configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT)
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
if pre_generated_key:
self._preconfigure_key(ac, configdict['addr'])
ac._evlogger.init_time = self.init_time
ac._evlogger.set_timeout(30)
return ac, dict(configdict)
def get_online_configuring_account(self, mvbox=False, sentbox=False,
pre_generated_key=True, config={}):
ac, configdict = self.get_online_config(
pre_generated_key=pre_generated_key)
configdict.update(config)
ac.configure(**configdict)
ac.start_threads()
self._finalizers.append(ac.shutdown)
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
return ac
def clone_online_account(self, account):
def get_one_online_account(self, pre_generated_key=True):
ac1 = self.get_online_configuring_account(
pre_generated_key=pre_generated_key)
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
return ac1
def get_two_online_accounts(self):
ac1 = self.get_online_configuring_account()
ac2 = self.get_online_configuring_account()
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
return ac1, ac2
def clone_online_account(self, account, pre_generated_key=True):
self.live_count += 1
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
if pre_generated_key:
self._preconfigure_key(ac, account.get_config("addr"))
ac._evlogger.init_time = self.init_time
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(ac.shutdown)
return ac
return AccountMaker()
am = AccountMaker()
request.addfinalizer(am.finalize)
return am
@pytest.fixture
@@ -204,12 +276,22 @@ def lp():
return Printer()
def wait_configuration_progress(account, target):
def wait_configuration_progress(account, min_target, max_target=1001):
min_target = min(min_target, max_target)
while 1:
evt_name, data1, data2 = \
account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
if data1 >= target:
print("** CONFIG PROGRESS {}".format(target), account)
if data1 >= min_target and data1 <= max_target:
print("** CONFIG PROGRESS {}".format(min_target), account)
break
def wait_securejoin_inviter_progress(account, target):
while 1:
evt_name, data1, data2 = \
account._evlogger.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS")
if data2 >= target:
print("** SECUREJOINT-INVITER PROGRESS {}".format(target), account)
break

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,49 @@
from __future__ import print_function
import os.path
import shutil
import pytest
from filecmp import cmp
from deltachat import const
from conftest import wait_configuration_progress, wait_msgs_changed
from deltachat import const
class TestInCreation:
class TestOnlineInCreation:
def test_increation_not_blobdir(self, tmpdir, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 1000)
wait_configuration_progress(ac2, 1000)
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
lp.sec("Creating in-creation file outside of blobdir")
assert tmpdir.strpath != ac1.get_blobdir()
src = tmpdir.join('file.txt').ensure(file=1)
with pytest.raises(Exception):
chat.prepare_message_file(src.strpath)
def test_no_increation_copies_to_blobdir(self, tmpdir, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 1000)
wait_configuration_progress(ac2, 1000)
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
lp.sec("Creating file outside of blobdir")
assert tmpdir.strpath != ac1.get_blobdir()
src = tmpdir.join('file.txt')
src.write("hello there\n")
chat.send_file(src.strpath)
blob_src = os.path.join(ac1.get_blobdir(), 'file.txt')
assert os.path.exists(blob_src), "file.txt not copied to blobdir"
def test_forward_increation(self, acfactory, data, lp):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
@@ -17,7 +56,10 @@ class TestInCreation:
wait_msgs_changed(ac1, 0, 0) # why no chat id?
lp.sec("create a message with a file in creation")
path = data.get_path("d.png")
orig = data.get_path("d.png")
path = os.path.join(ac1.get_blobdir(), 'd.png')
with open(path, "x") as fp:
fp.write("preparing")
prepared_original = chat.prepare_message_file(path)
assert prepared_original.is_out_preparing()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
@@ -38,6 +80,7 @@ class TestInCreation:
lp.sec("finish creating the file and send it")
assert prepared_original.is_out_preparing()
shutil.copyfile(orig, path)
chat.send_prepared(prepared_original)
assert prepared_original.is_out_pending() or prepared_original.is_out_delivered()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
@@ -57,13 +100,13 @@ class TestInCreation:
lp.sec("wait1 for original or forwarded messages to arrive")
ev1 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev1[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev1[1] > const.DC_CHAT_ID_LAST_SPECIAL
received_original = ac2.get_message_by_id(ev1[2])
assert cmp(received_original.filename, path, False)
assert cmp(received_original.filename, orig, shallow=False)
lp.sec("wait2 for original or forwarded messages to arrive")
ev2 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev2[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev2[1] > const.DC_CHAT_ID_LAST_SPECIAL
assert ev2[1] != ev1[1]
received_copy = ac2.get_message_by_id(ev2[2])
assert cmp(received_copy.filename, path, False)
assert cmp(received_copy.filename, orig, shallow=False)

View File

@@ -1,8 +1,7 @@
from __future__ import print_function
from deltachat import capi, const, set_context_callback, clear_context_callback
from deltachat import capi, cutil, 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():
@@ -17,14 +16,14 @@ def test_callback_None2int():
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 test_dc_close_events(tmpdir):
from deltachat.account import Account
p = tmpdir.join("hello.db")
ac1 = Account(p.strpath)
ac1.shutdown()
def find(info_string):
evlog = ac1._evlogger
while 1:
ev = evlog.get_matching("DC_EVENT_INFO", check_error=False)
data2 = ev[2]
@@ -33,7 +32,7 @@ def test_dc_close_events():
else:
print("skipping event", *ev)
find("disconnecting INBOX-watch")
find("disconnecting inbox-thread")
find("disconnecting sentbox-thread")
find("disconnecting mvbox-thread")
find("disconnecting SMTP")
@@ -51,6 +50,16 @@ def test_wrong_db(tmpdir):
assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL)
def test_empty_blobdir(tmpdir):
# Apparently some client code expects this to be the same as passing NULL.
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("hello.db")
assert lib.dc_open(ctx, db_fname.strpath.encode("ascii"), b"")
def test_event_defines():
assert const.DC_EVENT_INFO == 100
assert const.DC_CONTACT_ID_SELF
@@ -75,3 +84,58 @@ def test_markseen_invalid_message_ids(acfactory):
msg_ids = [9]
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")
def test_get_special_message_id_returns_empty_message(acfactory):
ac1 = acfactory.get_configured_offline_account()
for i in range(1, 10):
msg = ac1.get_message_by_id(i)
assert msg.id == 0
def test_provider_info_none():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
assert lib.dc_provider_new_from_email(ctx, cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
def test_get_info_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' not in info
def test_get_info_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' in info
def test_is_open_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
assert lib.dc_is_open(ctx) == 0
def test_is_open_actually_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
assert lib.dc_is_open(ctx) == 1

View File

@@ -1,35 +1,38 @@
[tox]
# make sure to update environment list in travis.yml and appveyor.yml
envlist =
py27
py35
py37
lint
auditwheels
[testenv]
commands =
pytest -v -rsXx {posargs:tests}
pytest -n6 --reruns 2 --reruns-delay 5 -v -rsXx --ignored {posargs:tests}
python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv =
TRAVIS
DCC_RS_DEV
DCC_RS_TARGET
DCC_PY_LIVECONFIG
CARGO_TARGET_DIR
RUSTC_WRAPPER
deps =
pytest
pytest-faulthandler
pytest-rerunfailures
pytest-timeout
pytest-xdist
pdbpp
requests
[testenv:auditwheels]
skipsdist = True
deps = auditwheel
commands =
python tests/auditwheels.py {toxworkdir}/wheelhouse
[testenv:lint]
skipsdist = True
usedevelop = True
skip_install = True
deps =
flake8
# pygments required by rst-lint
@@ -41,22 +44,33 @@ commands =
rst-lint --encoding 'utf-8' README.rst
[testenv:doc]
basepython = python3.5
changedir=doc
deps =
sphinx==2.0.1
sphinx==2.2.0
breathe
changedir = doc
commands =
sphinx-build -w docker-toxdoc-warnings.log -b html . _build/html
sphinx-build -Q -w toxdoc-warnings.log -b html . _build/html
[testenv:lintdoc]
skipsdist = True
usedevelop = True
deps =
{[testenv:lint]deps}
{[testenv:doc]deps}
commands =
{[testenv:lint]commands}
{[testenv:doc]commands}
[pytest]
addopts = -v -rs
python_files = tests/test_*.py
addopts = -v -ra --strict-markers
python_files = tests/test_*.py
norecursedirs = .tox
xfail_strict=true
timeout = 60
timeout = 90
timeout_method = thread
[flake8]
max-line-length = 120

View File

@@ -23,7 +23,7 @@ if [ $? != 0 ]; then
fi
pushd python
if [ -e "./liveconfig" && -z "$DCC_PY_LIVECONFIG" ]; then
if [ -e "./liveconfig" -a -z "$DCC_PY_LIVECONFIG" ]; then
export DCC_PY_LIVECONFIG=liveconfig
fi
tox "$@"

View File

@@ -1 +1 @@
nightly-2019-08-13
nightly-2019-11-06

80
scripts/proxy.py Normal file
View File

@@ -0,0 +1,80 @@
#!/usr/bin/env python3
# Examples:
#
# Original server that doesn't use SSL:
# ./proxy.py 8080 imap.nauta.cu 143
# ./proxy.py 8081 smtp.nauta.cu 25
#
# Original server that uses SSL:
# ./proxy.py 8080 testrun.org 993 --ssl
# ./proxy.py 8081 testrun.org 465 --ssl
from datetime import datetime
import argparse
import selectors
import ssl
import socket
import socketserver
class Proxy(socketserver.ThreadingTCPServer):
allow_reuse_address = True
def __init__(self, proxy_host, proxy_port, real_host, real_port, use_ssl):
self.real_host = real_host
self.real_port = real_port
self.use_ssl = use_ssl
super().__init__((proxy_host, proxy_port), RequestHandler)
class RequestHandler(socketserver.BaseRequestHandler):
def handle(self):
print('{} - {} CONNECTED.'.format(datetime.now(), self.client_address))
total = 0
real_server = (self.server.real_host, self.server.real_port)
with socket.create_connection(real_server) as sock:
if self.server.use_ssl:
context = ssl.create_default_context()
sock = context.wrap_socket(
sock, server_hostname=real_server[0])
forward = {self.request: sock, sock: self.request}
sel = selectors.DefaultSelector()
sel.register(self.request, selectors.EVENT_READ,
self.client_address)
sel.register(sock, selectors.EVENT_READ, real_server)
active = True
while active:
events = sel.select()
for key, mask in events:
print('\n{} - {} wrote:'.format(datetime.now(), key.data))
data = key.fileobj.recv(1024)
received = len(data)
total += received
print(data)
print('{} Bytes\nTotal: {} Bytes'.format(received, total))
if data:
forward[key.fileobj].sendall(data)
else:
print('\nCLOSING CONNECTION.\n\n')
forward[key.fileobj].close()
key.fileobj.close()
active = False
if __name__ == '__main__':
p = argparse.ArgumentParser(description='Simple Python Proxy')
p.add_argument(
"proxy_port", help="the port where the proxy will listen", type=int)
p.add_argument('host', help="the real host")
p.add_argument('port', help="the port of the real host", type=int)
p.add_argument("--ssl", help="use ssl to connect to the real host",
action="store_true")
args = p.parse_args()
with Proxy('', args.proxy_port, args.host, args.port, args.ssl) as proxy:
proxy.serve_forever()

65
set_core_version.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python
import os
import sys
import re
import pathlib
import subprocess
rex = re.compile(r'version = "(\S+)"')
def read_toml_version(relpath):
p = pathlib.Path(relpath)
assert p.exists()
for line in open(str(p)):
m = rex.match(line)
if m is not None:
return m.group(1)
raise ValueError("no version found in {}".format(relpath))
def replace_toml_version(relpath, newversion):
p = pathlib.Path(relpath)
assert p.exists()
tmp_path = str(p) + "_tmp"
with open(tmp_path, "w") as f:
for line in open(str(p)):
m = rex.match(line)
if m is not None:
f.write('version = "{}"\n'.format(newversion))
else:
f.write(line)
os.rename(tmp_path, str(p))
if __name__ == "__main__":
if len(sys.argv) < 2:
for x in ("Cargo.toml", "deltachat-ffi/Cargo.toml"):
print("{}: {}".format(x, read_toml_version(x)))
raise SystemExit("need argument: new version, example: 1.25.0")
newversion = sys.argv[1]
if newversion.count(".") < 2:
raise SystemExit("need at least two dots in version")
core_toml = read_toml_version("Cargo.toml")
ffi_toml = read_toml_version("deltachat-ffi/Cargo.toml")
assert core_toml == ffi_toml, (core_toml, ffi_toml)
for line in open("CHANGELOG.md"):
## 1.25.0
if line.startswith("## "):
if line[2:].strip().startswith(newversion):
break
else:
raise SystemExit("CHANGELOG.md contains no entry for version: {}".format(newversion))
replace_toml_version("Cargo.toml", newversion)
replace_toml_version("deltachat-ffi/Cargo.toml", newversion)
subprocess.call(["cargo", "check"])
subprocess.call(["git", "add", "-u"])
# subprocess.call(["cargo", "update", "-p", "deltachat"])
print("after commit make sure to: ")
print("")
print(" git tag {}".format(newversion))
print("")

441
spec.md Normal file
View File

@@ -0,0 +1,441 @@
# Chat-over-Email specification
Version 0.30.0
This document describes how emails can be used
to implement typical messenger functions
while staying compatible to existing MUAs.
- [Encryption](#encryption)
- [Outgoing messages](#outgoing-messages)
- [Incoming messages](#incoming-messages)
- [Forwarded messages](#forwarded-messages)
- [Groups](#groups)
- [Outgoing group messages](#outgoing-group-messages)
- [Incoming group messages](#incoming-group-messages)
- [Add and remove members](#add-and-remove-members)
- [Change group name](#change-group-name)
- [Set group image](#set-group-image)
- [Set profile image](#set-profile-image)
- [Locations](#locations)
- [User locations](#user-locations)
- [Points of interest](#points-of-interest)
- [Miscellaneous](#miscellaneous)
# Encryption
Messages SHOULD be encrypted by the
[Autocrypt](https://autocrypt.org/level1.html) standard;
`prefer-encrypt=mutual` MAY be set by default.
Meta data (at least the subject and all chat-headers) SHOULD be encrypted
by the [Memoryhole](https://github.com/autocrypt/memoryhole) standard.
If Memoryhole is not used,
the subject of encrypted messages SHOULD be replaced by the string `...`.
# Outgoing messages
Messengers MUST add a `Chat-Version: 1.0` header to outgoing messages.
For filtering and smart appearance of the messages in normal MUAs,
the `Subject` header SHOULD start with the characters `Chat:`
and SHOULD be an excerpt of the message.
Replies to messages MAY follow the typical `Re:`-format.
The body MAY contain text which MUST have the content type `text/plain`
or `mulipart/alternative` containing `text/plain`.
The text MAY be divided into a user-text-part and a footer-part using the
line `-- ` (minus, minus, space, lineend).
The user-text-part MUST contain only user generated content.
User generated content are eg. texts a user has actually typed
or pasted or forwarded from another user.
Full quotes, footers or sth. like that MUST NOT go to the user-text-part.
From: sender@domain
To: rcpt@domain
Chat-Version: 1.0
Content-Type: text/plain
Subject: Chat: Hello ...
Hello world!
# Incoming messages
The `Chat-Version` header MAY be used
to detect if a messages comes from a compatible messenger.
The `Subject` header MUST NOT be used
to detect compatible messengers, groups or whatever.
Messenger SHOULD show the `Subject`
if the message comes from a normal MUA together with the email-body.
The email-body SHOULD be converted
to plain text, full-quotes and similar regions SHOULD be cut.
Attachments SHOULD be shown where possible.
If an attachment cannot be shown, a non-distracting warning SHOULD be printed.
# Forwarded messages
Forwarded messages are outgoing messages that contain a forwarded-header
before the user generated content.
The forwarded header MUST contain two lines:
The first line contains the text
`---------- Forwarded message ----------`
(10 minus, space, text `Forwarded message`, space, 10 minus).
The second line starts with `From: ` followed by the original sender
which SHOULD be anonymized or just a placeholder.
From: sender@domain
To: rcpt@domain
Chat-Version: 1.0
Content-Type: text/plain
Subject: Chat: Forwarded message
---------- Forwarded message ----------
From: Messenger
Hello world!
Incoming forwarded messages are detected by the header.
The messenger SHOULD mark these messages in a way that
it becomes obvious that the message is not created by the sender.
Note that most messengers do not show the original sender of forwarded messages
but MUAs typically expose the sender in the UI.
# Groups
Groups are chats with usually more than one recipient,
each defined by an email-address.
The sender plus the recipients are the group members.
All group members form the member list.
To allow different groups with the same members,
groups are identified by a group-id.
The group-id MUST be created only from the characters
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`
and MUST have a length of at least 11 characters.
Groups MUST have a group-name.
The group-name is any non-zero-length UTF-8 string.
Groups MAY have a group-image.
## Outgoing groups messages
All group members MUST be added to the `From`/`To` headers.
The group-id MUST be written to the `Chat-Group-ID` header.
The group-name MUST be written to `Chat-Group-Name` header
(the forced presence of this header makes it easier
to join a group chat on a second device any time).
The `Subject` header of outgoing group messages
SHOULD be set to the group-name.
To identify the group-id on replies from normal MUAs,
the group-id MUST also be added to the message-id of outgoing messages.
The message-id MUST have the format `Gr.<group-id>.<unique data>`.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Message-ID: Gr.12345uvwxyZ.0001@domain
Subject: Chat: My Group: Hello group ...
Hello group - this group contains three members
Messengers adding the member list in the form `Name <email-address>`
MUST take care only to distribute the names authorized by the contacts themselves.
Otherwise, names as _Daddy_ or _Honey_ may be distributed
(this issue is also true for normal MUAs, however,
for more contact- and chat-centralized apps
such situations happen more frequently).
## Incoming group messages
The messenger MUST search incoming messages for the group-id
in the following headers: `Chat-Group-ID`,
`Message-ID`, `In-Reply-To` and `References` (in this order).
If the messenger finds a valid and existent group-id,
the message SHOULD be assigned to the given group.
If the messenger finds a valid but not existent group-id,
the messenger MAY create a new group.
If no group-id is found,
the message MAY be assigned
to a normal single-user chat with the email-address given in `From`.
## Add and remove members
Messenger clients MUST init the member list
from the `From`/`To` headers on the first group message.
When a member is added later,
a `Chat-Group-Member-Added` action header must be sent
with the value set to the email-address of the added member.
When receiving a `Chat-Group-Member-Added` header, however,
_all missing_ members the `From`/`To` headers has to be added.
This is to mitigate problems when receiving messages
in different orders, esp. on creating new groups.
To remove a member, a `Chat-Group-Member-Removed` header must be sent
with the value set to the email-address of the member to remove.
When receiving a `Chat-Group-Member-Removed` header,
only exaxtly the given member has to be removed from the member list.
Messenger clients MUST NOT construct the member list
on other group messages
(this is to avoid accidentally altered To-lists in normal MUAs;
the user does not expect adding a user to a _message_
will also add him to the _group_ "forever").
The messenger SHOULD send an explicit mail for each added or removed member.
The body of the message SHOULD contain
a localized description about what happened
and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain, member4@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Chat-Group-Member-Added: member4@domain
Message-ID: Gr.12345uvwxyZ.0002@domain
Subject: Chat: My Group: Hello, ...
Hello, I've added member4@domain to our group. Now we have 4 members.
To remove a member:
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Chat-Group-Member-Removed: member4@domain
Message-ID: Gr.12345uvwxyZ.0003@domain
Subject: Chat: My Group: Hello, ...
Hello, I've removed member4@domain from our group. Now we have 3 members.
## Change group name
To change the group-name,
the messenger MUST send the action header `Chat-Group-Name-Changed`
with the value set to the old group name to all group members.
The new group name goes to the header `Chat-Group-Name`.
The messenger SHOULD send an explicit mail for each name change.
The body of the message SHOULD contain
a localized description about what happened
and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: Our Group
Chat-Group-Name-Changed: My Group
Message-ID: Gr.12345uvwxyZ.0004@domain
Subject: Chat: Our Group: Hello, ...
Hello, I've changed the group name from "My Group" to "Our Group".
## Set group image
A group MAY have a group-image.
To change or set the group-image,
the messenger MUST attach an image file to a message
and MUST add the header `Chat-Group-Avatar`
with the value set to the image name.
To remove the group-image,
the messenger MUST add the header `Chat-Group-Avatar: 0`.
The messenger SHOULD send an explicit mail for each group image change.
The body of the message SHOULD contain
a localized description about what happened
and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: Our Group
Chat-Group-Avatar: image.jpg
Message-ID: Gr.12345uvwxyZ.0005@domain
Subject: Chat: Our Group: Hello, ...
Content-Type: multipart/mixed; boundary="==break=="
--==break==
Content-Type: text/plain
Hello, I've changed the group image.
--==break==
Content-Type: image/jpeg
Content-Disposition: attachment; filename="image.jpg"
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBw ...
--==break==--
The image format SHOULD be image/jpeg or image/png.
To save data, it is RECOMMENDED
to add a `Chat-Group-Avatar` only on image changes.
# Set profile image
A user MAY have a profile-image that MAY be distributed to their contacts.
To change or set the profile-image,
the messenger MUST attach an image file to a message
and MUST add the header `Chat-User-Avatar`
with the value set to the image name.
To remove the profile-image,
the messenger MUST add the header `Chat-User-Avatar: 0`.
To distribute the image,
the messenger MAY send the profile image
together with the next mail to a given contact
(to do this only once,
the messenger has to keep a `user_avatar_update_state` somewhere).
Alternatively, the messenger MAY send an explicit mail
for each profile-image change to all contacts using a compatible messenger.
The messenger SHOULD NOT send an explicit mail to normal MUAs.
From: sender@domain
To: rcpt@domain
Chat-Version: 1.0
Chat-User-Avatar: photo.jpg
Subject: Chat: Hello, ...
Content-Type: multipart/mixed; boundary="==break=="
--==break==
Content-Type: text/plain
Hello, I've changed my profile image.
--==break==
Content-Type: image/jpeg
Content-Disposition: attachment; filename="photo.jpg"
AKCgkJi3j4l5kjoldfUAKCgkJi3j4lldfHjgWICwgIEBQYFBA ...
--==break==--
The image format SHOULD be image/jpeg or image/png.
Note that `Chat-User-Avatar` may appear together with all other headers,
eg. there may be a `Chat-User-Avatar` and a `Chat-Group-Avatar` header
in the same message.
To save data, it is RECOMMENDED to add a `Chat-User-Avatar` header
only on image changes.
# Locations
Locations can be attachted to messages using
[standard kml-files](https://www.opengeospatial.org/standards/kml/)
with well-known names.
## User locations
To send the location of the sender,
the app can attach a file with the name `location.kml`.
The file can contain one or more locations.
Apps that support location streaming will typically collect some location events
and send them together in one file.
As each location has an independent timestamp,
the apps can show the location as a track.
Note that the `addr` attribute inside the `location.kml` file
MUST match the users email-address.
Otherwise, the file is discarded silently;
this is to protect against getting wrong locations,
eg. forwarded from a normal MUA.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document addr="ndh@deltachat.de">
<Placemark>
<Timestamp><when>2020-01-11T20:40:19Z</when></Timestamp>
<Point><coordinates accuracy="1.2">1.234,5.678</coordinates></Point>
</Placemark>
<Placemark>
<Timestamp><when>2020-01-11T20:40:25Z</when></Timestamp>
<Point><coordinates accuracy="5.4">7.654,3.21</coordinates></Point>
</Placemark>
</Document>
</kml>
## Points of interest
To send an "Point of interest", a POI,
use a normal message and attach a file with the name `message.kml`.
In contrast to user locations, this file should contain only one location
and an address-attribute is not needed -
as the location belongs to the message content,
it is fine if the location is detected on forwarding etc.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Placemark>
<Timestamp><when>2020-01-01T20:40:19Z</when></Timestamp>
<Point><coordinates accuracy="1.2">1.234,5.678</coordinates></Point>
</Placemark>
</Document>
</kml>
# Miscellaneous
Messengers SHOULD use the header `In-Reply-To` as usual.
Messengers SHOULD add a `Chat-Voice-message: 1` header
if an attached audio file is a voice message.
Messengers MAY add a `Chat-Duration` header
to specify the duration of attached audio or video files.
The value MUST be the duration in milliseconds.
This allows the receiver to show the time without knowing the file format.
In-Reply-To: Gr.12345uvwxyZ.0005@domain
Chat-Voice-Message: 1
Chat-Duration: 10000
Messengers MAY send and receive Message Disposition Notifications
(MDNs, [RFC 8098](https://tools.ietf.org/html/rfc8098),
[RFC 3503](https://tools.ietf.org/html/rfc3503))
using the `Chat-Disposition-Notification-To` header
instead of the `Disposition-Notification-To`
(which unfortunately forces many other MUAs
to send weird mails not following any standard).
## Sync messages
If some action is required by a message header,
the action should only be performed if the _effective date_ is newer
than the date the last action was performed.
We define the effective date of a message
as the sending time of the message as indicated by its Date header,
or the time of first receipt if that date is in the future or unavailable.
Copyright © 2017-2020 Delta Chat contributors.

View File

@@ -1,14 +1,15 @@
//! # Autocrypt header module
//!
//! Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
use std::collections::BTreeMap;
use std::ffi::CStr;
use std::str::FromStr;
use std::{fmt, str};
use mmime::mailimf_types::*;
use crate::constants::*;
use crate::contact::*;
use crate::dc_tools::as_str;
use crate::key::*;
use crate::context::Context;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::key::{DcKey, SignedPublicKey};
/// Possible values for encryption preference
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
@@ -41,22 +42,27 @@ impl str::FromStr for EncryptPreference {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"mutual" => Ok(EncryptPreference::Mutual),
"reset" => Ok(EncryptPreference::Reset),
_ => Ok(EncryptPreference::NoPreference),
"nopreference" => Ok(EncryptPreference::NoPreference),
_ => Err(()),
}
}
}
/// Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
/// Autocrypt header
#[derive(Debug)]
pub struct Aheader {
pub addr: String,
pub public_key: Key,
pub public_key: SignedPublicKey,
pub prefer_encrypt: EncryptPreference,
}
impl Aheader {
pub fn new(addr: String, public_key: Key, prefer_encrypt: EncryptPreference) -> Self {
/// Creates new autocrypt header
pub fn new(
addr: String,
public_key: SignedPublicKey,
prefer_encrypt: EncryptPreference,
) -> Self {
Aheader {
addr,
public_key,
@@ -64,69 +70,52 @@ impl Aheader {
}
}
pub fn from_imffields(
wanted_from: *const libc::c_char,
header: *const mailimf_fields,
pub fn from_headers(
context: &Context,
wanted_from: &str,
headers: &[mailparse::MailHeader<'_>],
) -> Option<Self> {
if wanted_from.is_null() || header.is_null() {
return None;
}
let mut fine_header = None;
let mut cur = unsafe { (*(*header).fld_list).first };
while !cur.is_null() {
let field = unsafe { (*cur).data as *mut mailimf_field };
if !field.is_null()
&& unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
{
let optional_field = unsafe { (*field).fld_data.fld_optional_field };
if !optional_field.is_null()
&& unsafe { !(*optional_field).fld_name.is_null() }
&& unsafe { CStr::from_ptr((*optional_field).fld_name).to_str().unwrap() }
== "Autocrypt"
{
let value = unsafe {
CStr::from_ptr((*optional_field).fld_value)
.to_str()
.unwrap()
};
match Self::from_str(value) {
Ok(test) => {
if addr_cmp(&test.addr, as_str(wanted_from)) {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
}
}
}
_ => {}
if let Ok(Some(value)) = headers.get_header_value(HeaderDef::Autocrypt) {
match Self::from_str(&value) {
Ok(header) => {
if addr_cmp(&header.addr, wanted_from) {
return Some(header);
}
}
Err(err) => {
warn!(
context,
"found invalid autocrypt header {}: {:?}", value, err
);
}
}
cur = unsafe { (*cur).next };
}
fine_header
None
}
}
impl fmt::Display for Aheader {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// TODO replace 78 with enum /rtn
// adds a whitespace every 78 characters, this allows libEtPan to
// wrap the lines according to RFC 5322
write!(fmt, "addr={};", self.addr)?;
if self.prefer_encrypt == EncryptPreference::Mutual {
write!(fmt, " prefer-encrypt=mutual;")?;
}
// adds a whitespace every 78 characters, this allows
// email crate to wrap the lines according to RFC 5322
// (which may insert a linebreak before every whitespace)
let keydata = self.public_key.to_base64(78);
write!(
fmt,
"addr={}; prefer-encrypt={}; keydata={}",
self.addr, self.prefer_encrypt, keydata
)
let keydata = self.public_key.to_base64().chars().enumerate().fold(
String::new(),
|mut res, (i, c)| {
if i % 78 == 78 - "keydata=".len() {
res.push(' ')
}
res.push(c);
res
},
);
write!(fmt, " keydata={}", keydata)
}
}
@@ -135,9 +124,9 @@ impl str::FromStr for Aheader {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut attributes: BTreeMap<String, String> = s
.split(";")
.split(';')
.filter_map(|a| {
let attribute: Vec<&str> = a.trim().splitn(2, "=").collect();
let attribute: Vec<&str> = a.trim().splitn(2, '=').collect();
if attribute.len() < 2 {
return None;
}
@@ -155,34 +144,20 @@ impl str::FromStr for Aheader {
return Err(());
}
};
let public_key = match attributes
let public_key: SignedPublicKey = attributes
.remove("keydata")
.and_then(|raw| Key::from_base64(&raw, KeyType::Public))
{
Some(key) => {
if key.verify() {
key
} else {
return Err(());
}
}
None => {
return Err(());
}
};
.ok_or(())
.and_then(|raw| SignedPublicKey::from_base64(&raw).or(Err(())))
.and_then(|key| key.verify().and(Ok(key)).or(Err(())))?;
let prefer_encrypt = match attributes
let prefer_encrypt = attributes
.remove("prefer-encrypt")
.and_then(|raw| raw.parse().ok())
{
Some(pref) => pref,
None => EncryptPreference::NoPreference,
};
.unwrap_or_default();
// Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored
// Autocrypt-Level0: unknown attribute, treat the header as invalid
if attributes.keys().find(|k| !k.starts_with("_")).is_some() {
if attributes.keys().any(|k| !k.starts_with('_')) {
return Err(());
}
@@ -198,15 +173,13 @@ impl str::FromStr for Aheader {
mod tests {
use super::*;
fn rawkey() -> String {
"xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu8880iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTllHOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+wsCJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiFNyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAgdLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgCu3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOtkb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKCLhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4sWVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWvBuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGrwdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktEk6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc=".into()
}
const RAWKEY: &str = "xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu8880iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTllHOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+wsCJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiFNyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAgdLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgCu3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOtkb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKCLhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4sWVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWvBuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiMLAAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGrwdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktEk6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc=";
#[test]
fn test_from_str() {
let h: Aheader = format!(
"addr=me@mail.com; prefer-encrypt=mutual; keydata={}",
rawkey()
RAWKEY
)
.parse()
.expect("failed to parse");
@@ -215,9 +188,22 @@ mod tests {
assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual);
}
// EncryptPreference::Reset is an internal value, parser should never return it
#[test]
fn test_from_str_reset() {
let raw = format!(
"addr=reset@example.com; prefer-encrypt=reset; keydata={}",
RAWKEY
);
let h: Aheader = raw.parse().expect("failed to parse");
assert_eq!(h.addr, "reset@example.com");
assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference);
}
#[test]
fn test_from_str_non_critical() {
let raw = format!("addr=me@mail.com; _foo=one; _bar=two; keydata={}", rawkey());
let raw = format!("addr=me@mail.com; _foo=one; _bar=two; keydata={}", RAWKEY);
let h: Aheader = raw.parse().expect("failed to parse");
assert_eq!(h.addr, "me@mail.com");
@@ -228,33 +214,57 @@ mod tests {
fn test_from_str_superflous_critical() {
let raw = format!(
"addr=me@mail.com; _foo=one; _bar=two; other=me; keydata={}",
rawkey()
RAWKEY
);
assert!(raw.parse::<Aheader>().is_err());
}
#[test]
fn test_good_headers() {
let fixed_header = "addr=a@b.example.org; prefer-encrypt=mutual; keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJgWL7O3y/g 4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6CJx8Wsv8 ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKKbhH7Lu88 80iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1KvVL3gTTll HOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbGUuZGU+ws CJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087LscCFPiF Nyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VNHtODXjAg dLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Dddfxo3ao72 rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCvSJa61i+v 81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vauf1zK8JgC u3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+G7uXMMOt kb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjmkRl3ToKC LhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09/JM/Fw4s WVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHRTR1pcrWv BuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaKrc9mtiML AAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivXurm8zwGr wdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9MtrmZkZw+ktE k6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb+F//f0k0 j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNgwm6qxo7x egRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc=";
let fixed_header = concat!(
"addr=a@b.example.org; prefer-encrypt=mutual; ",
"keydata=xsBNBFzG3j0BCAC6iNhT8zydvCXi8LI/gFnkadMbfmSE/rTJskRRra/utGbLyDta/yTrJg",
" WL7O3y/g4HdDW/dN2z26Y6W13IMzx9gLInn1KQZChtqWAcr/ReUucXcymwcfg1mdkBGk3TSLeLihN6",
" CJx8Wsv8ig+kgAzte4f5rqEEAJVQ9WZHuti7UiYs6oRzqTo06CRe9owVXxzdMf0VDQtf7ZFm9dpzKK",
" bhH7Lu8880iiotQ9/yRCkDGp9fNThsrLdZiK6OIAcIBAqi2rI89aS1dAmnRbktQieCx5izzyYkR1Kv",
" VL3gTTllHOzfKVEC2asmtWu2e4se/+O4WMIS1eGrn7GeWVb0Vwc5ABEBAAHNETxhQEBiLmV4YW1wbG",
" UuZGU+wsCJBBABCAAzAhkBBQJcxt5FAhsDBAsJCAcGFQgJCgsCAxYCARYhBI4xxYKBgH3ANh5cufaK",
" rc9mtiMLAAoJEPaKrc9mtiML938H/18F+3Wf9/JaAy/8hCO1v4S2PVBhxaKCokaNFtkfaMRne2l087",
" LscCFPiFNyb4mv6Z3YeK8Xpxlp2sI0ecvdiqLUOGfnxS6tQrj+83EjtIrZ/hXOk1h121QFWH9Zg2VN",
" HtODXjAgdLDC0NWUrclR0ZOqEDQHeo0ibTILdokVfXFN25wakPmGaYJP2y729cb1ve7RzvIvwn+Ddd",
" fxo3ao72rBfLi7l4NQ4S0KsY4cw+/6l5bRCKYCP77wZtvCwUvfVVosLdT43agtSiBI49+ayqvZ8OCv",
" SJa61i+v81brTiEy9GBod4eAp45Ibsuemkw+gon4ZOvUXHTjwFB+h63MrozOwE0EXMbePQEIAL/vau",
" f1zK8JgCu3V+G+SOX0iWw5xUlCPX+ERpBbWfwu3uAqn4wYXD3JDE/fVAF668xiV4eTPtlSUd5h0mn+",
" G7uXMMOtkb+20SoEt50f8zw8TrL9t+ZsV11GKZWJpCar5AhXWsn6EEi8I2hLL5vn55ZZmHuGgN4jjm",
" kRl3ToKCLhaXwTBjCJem7N5EH7F75wErEITa55v4Lb4Nfca7vnvtYrI1OA446xa8gHra0SINelTD09",
" /JM/Fw4sWVPBaRZmJK/Tnu79N23No9XBUubmFPv1pNexZsQclicnTpt/BEWhiun7d6lfGB63K1aoHR",
" TR1pcrWvBuALuuz0gqar2zlI0AEQEAAcLAdgQYAQgAIAUCXMbeRQIbDBYhBI4xxYKBgH3ANh5cufaK",
" rc9mtiMLAAoJEPaKrc9mtiMLKSEIAIyLCRO2OyZ0IYRvRPpMn4p7E+7Pfcz/0mSkOy+1hshgJnqivX",
" urm8zwGrwdMqeV4eslKR9H1RUdWGUQJNbtwmmjrt5DHpIhYHl5t3FpCBaGbV20Omo00Q38lBl9Mtrm",
" ZkZw+ktEk6X+0xCKssMF+2MADkSOIufbR5HrDVB89VZOHCO9DeXvCUUAw2hyJiL/LHmLzJ40zYoTmb",
" +F//f0k0j+tRdbkefyRoCmwG7YGiT+2hnCdgcezswnzah5J3ZKlrg7jOGo1LxtbvNUzxNBbC6S/aNg",
" wm6qxo7xegRhmEl5uZ16zwyj4qz+xkjGy25Of5mWfUDoNw7OT7sjUbHOOMc="
);
let ah = Aheader::from_str(fixed_header).expect("failed to parse");
assert_eq!(ah.addr, "a@b.example.org");
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
assert_eq!(format!("{}", ah), fixed_header);
let rendered = ah.to_string();
assert_eq!(rendered, fixed_header);
let ah = Aheader::from_str(&format!(" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = {}", rawkey())).expect("failed to parse");
let ah = Aheader::from_str(&format!(" _foo; __FOO=BAR ;;; addr = a@b.example.org ;\r\n prefer-encrypt = mutual ; keydata = {}", RAWKEY)).expect("failed to parse");
assert_eq!(ah.addr, "a@b.example.org");
assert_eq!(ah.prefer_encrypt, EncryptPreference::Mutual);
Aheader::from_str(&format!(
"addr=a@b.example.org; prefer-encrypt=ignoreUnknownValues; keydata={}",
rawkey()
RAWKEY
))
.expect("failed to parse");
Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", rawkey()))
Aheader::from_str(&format!("addr=a@b.example.org; keydata={}", RAWKEY))
.expect("failed to parse");
}
@@ -266,4 +276,30 @@ mod tests {
assert!(Aheader::from_str(" ;;").is_err());
assert!(Aheader::from_str("addr=a@t.de; unknwon=1; keydata=jau").is_err());
}
#[test]
fn test_display_aheader() {
assert!(format!(
"{}",
Aheader::new(
"test@example.com".to_string(),
SignedPublicKey::from_base64(RAWKEY).unwrap(),
EncryptPreference::Mutual
)
)
.contains("prefer-encrypt=mutual;"));
// According to Autocrypt Level 1 specification,
// only "prefer-encrypt=mutual;" can be used.
// If the setting is nopreference, the whole attribute is omitted.
assert!(!format!(
"{}",
Aheader::new(
"test@example.com".to_string(),
SignedPublicKey::from_base64(RAWKEY).unwrap(),
EncryptPreference::NoPreference
)
)
.contains("prefer-encrypt"));
}
}

640
src/blob.rs Normal file
View File

@@ -0,0 +1,640 @@
//! # Blob directory management
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use self::image::GenericImageView;
use crate::constants::AVATAR_SIZE;
use crate::context::Context;
use crate::events::Event;
extern crate image;
/// Represents a file in the blob directory.
///
/// The object has a name, which will always be valid UTF-8. Having a
/// blob object does not imply the respective file exists, however
/// when using one of the `create*()` methods a unique file is
/// created.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlobObject<'a> {
blobdir: &'a Path,
name: String,
}
impl<'a> BlobObject<'a> {
/// Creates a new blob object with a unique name.
///
/// Creates a new file in the blob directory. The name will be
/// derived from the platform-agnostic basename of the suggested
/// name, followed by a random number and followed by a possible
/// extension. The `data` will be written into the file without
/// race-conditions.
///
/// # Errors
///
/// [BlobError::CreateFailure] is used when the file could not
/// be created. You can expect [BlobError.cause] to contain an
/// underlying error.
///
/// [BlobError::WriteFailure] is used when the file could not
/// be written to. You can expect [BlobError.cause] to contain an
/// underlying error.
pub fn create(
context: &'a Context,
suggested_name: impl AsRef<str>,
data: &[u8],
) -> std::result::Result<BlobObject<'a>, BlobError> {
let blobdir = context.get_blobdir();
let (stem, ext) = BlobObject::sanitise_name(suggested_name.as_ref());
let (name, mut file) = BlobObject::create_new_file(&blobdir, &stem, &ext)?;
file.write_all(data)
.map_err(|err| BlobError::WriteFailure {
blobdir: blobdir.to_path_buf(),
blobname: name.clone(),
cause: err,
backtrace: failure::Backtrace::new(),
})?;
let blob = BlobObject {
blobdir,
name: format!("$BLOBDIR/{}", name),
};
context.call_cb(Event::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}
// Creates a new file, returning a tuple of the name and the handle.
fn create_new_file(dir: &Path, stem: &str, ext: &str) -> Result<(String, fs::File), BlobError> {
let max_attempt = 15;
let mut name = format!("{}{}", stem, ext);
for attempt in 0..max_attempt {
let path = dir.join(&name);
match fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&path)
{
Ok(file) => return Ok((name, file)),
Err(err) => {
if attempt == max_attempt {
return Err(BlobError::CreateFailure {
blobdir: dir.to_path_buf(),
blobname: name,
cause: err,
backtrace: failure::Backtrace::new(),
});
} else {
name = format!("{}-{}{}", stem, rand::random::<u32>(), ext);
}
}
}
}
// This is supposed to be unreachable, but the compiler doesn't know.
Err(BlobError::CreateFailure {
blobdir: dir.to_path_buf(),
blobname: name,
cause: std::io::Error::new(std::io::ErrorKind::Other, "supposedly unreachable"),
backtrace: failure::Backtrace::new(),
})
}
/// Creates a new blob object with unique name by copying an existing file.
///
/// This creates a new blob as described in [BlobObject::create]
/// but also copies an existing file into it. This is done in a
/// in way which avoids race-conditions when multiple files are
/// concurrently created.
///
/// # Errors
///
/// In addition to the errors in [BlobObject::create] the
/// [BlobError::CopyFailure] is used when the data can not be
/// copied.
pub fn create_and_copy(
context: &'a Context,
src: impl AsRef<Path>,
) -> std::result::Result<BlobObject<'a>, BlobError> {
let mut src_file = fs::File::open(src.as_ref()).map_err(|err| BlobError::CopyFailure {
blobdir: context.get_blobdir().to_path_buf(),
blobname: String::from(""),
src: src.as_ref().to_path_buf(),
cause: err,
backtrace: failure::Backtrace::new(),
})?;
let (stem, ext) = BlobObject::sanitise_name(&src.as_ref().to_string_lossy());
let (name, mut dst_file) = BlobObject::create_new_file(context.get_blobdir(), &stem, &ext)?;
let name_for_err = name.clone();
std::io::copy(&mut src_file, &mut dst_file).map_err(|err| {
{
// Attempt to remove the failed file, swallow errors resulting from that.
let path = context.get_blobdir().join(&name_for_err);
fs::remove_file(path).ok();
}
BlobError::CopyFailure {
blobdir: context.get_blobdir().to_path_buf(),
blobname: name_for_err,
src: src.as_ref().to_path_buf(),
cause: err,
backtrace: failure::Backtrace::new(),
}
})?;
let blob = BlobObject {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),
};
context.call_cb(Event::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}
/// Creates a blob from a file, possibly copying it to the blobdir.
///
/// If the source file is not a path to into the blob directory
/// the file will be copied into the blob directory first. If the
/// source file is already in the blobdir it will not be copied
/// and only be created if it is a valid blobname, that is no
/// subdirectory is used and [BlobObject::sanitise_name] does not
/// modify the filename.
///
/// # Errors
///
/// This merely delegates to the [BlobObject::create_and_copy] and
/// the [BlobObject::from_path] methods. See those for possible
/// errors.
pub fn new_from_path(
context: &Context,
src: impl AsRef<Path>,
) -> std::result::Result<BlobObject, BlobError> {
if src.as_ref().starts_with(context.get_blobdir()) {
BlobObject::from_path(context, src)
} else {
BlobObject::create_and_copy(context, src)
}
}
/// Returns a [BlobObject] for an existing blob from a path.
///
/// The path must designate a file directly in the blobdir and
/// must use a valid blob name. That is after sanitisation the
/// name must still be the same, that means it must be valid UTF-8
/// and not have any special characters in it.
///
/// # Errors
///
/// [BlobError::WrongBlobdir] is used if the path is not in
/// the blob directory.
///
/// [BlobError::WrongName] is used if the file name does not
/// remain identical after sanitisation.
pub fn from_path(
context: &Context,
path: impl AsRef<Path>,
) -> std::result::Result<BlobObject, BlobError> {
let rel_path = path
.as_ref()
.strip_prefix(context.get_blobdir())
.map_err(|_| BlobError::WrongBlobdir {
blobdir: context.get_blobdir().to_path_buf(),
src: path.as_ref().to_path_buf(),
backtrace: failure::Backtrace::new(),
})?;
if !BlobObject::is_acceptible_blob_name(&rel_path) {
return Err(BlobError::WrongName {
blobname: path.as_ref().to_path_buf(),
backtrace: failure::Backtrace::new(),
});
}
let name = rel_path.to_str().ok_or_else(|| BlobError::WrongName {
blobname: path.as_ref().to_path_buf(),
backtrace: failure::Backtrace::new(),
})?;
BlobObject::from_name(context, name.to_string())
}
/// Returns a [BlobObject] for an existing blob.
///
/// The `name` may optionally be prefixed with the `$BLOBDIR/`
/// prefixed, as returned by [BlobObject::as_name]. This is how
/// you want to create a [BlobObject] for a filename read from the
/// database.
///
/// # Errors
///
/// [BlobError::WrongName] is used if the name is not a valid
/// blobname, i.e. if [BlobObject::sanitise_name] does modify the
/// provided name.
pub fn from_name(
context: &'a Context,
name: String,
) -> std::result::Result<BlobObject<'a>, BlobError> {
let name: String = match name.starts_with("$BLOBDIR/") {
true => name.splitn(2, '/').last().unwrap().to_string(),
false => name,
};
if !BlobObject::is_acceptible_blob_name(&name) {
return Err(BlobError::WrongName {
blobname: PathBuf::from(name),
backtrace: failure::Backtrace::new(),
});
}
Ok(BlobObject {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),
})
}
/// Returns the absolute path to the blob in the filesystem.
pub fn to_abs_path(&self) -> PathBuf {
let fname = Path::new(&self.name).strip_prefix("$BLOBDIR/").unwrap();
self.blobdir.join(fname)
}
/// Returns the blob name, as stored in the database.
///
/// This returns the blob in the `$BLOBDIR/<name>` format used in
/// the database. Do not use this unless you're about to store
/// this string in the database or [Params]. Eventually even
/// those conversions should be handled by the type system.
///
/// [Params]: crate::param::Params
pub fn as_name(&self) -> &str {
&self.name
}
/// Returns the filename of the blob.
pub fn as_file_name(&self) -> &str {
self.name.rsplitn(2, '/').next().unwrap()
}
/// The path relative in the blob directory.
pub fn as_rel_path(&self) -> &Path {
Path::new(self.as_file_name())
}
/// Returns the extension of the blob.
///
/// If a blob's filename has an extension, it is always guaranteed
/// to be lowercase.
pub fn suffix(&self) -> Option<&str> {
let ext = self.name.rsplitn(2, '.').next();
if ext == Some(&self.name) {
None
} else {
ext
}
}
/// Create a safe name based on a messy input string.
///
/// The safe name will be a valid filename on Unix and Windows and
/// not contain any path separators. The input can contain path
/// segments separated by either Unix or Windows path separators,
/// the rightmost non-empty segment will be used as name,
/// sanitised for special characters.
///
/// The resulting name is returned as a tuple, the first part
/// being the stem or basename and the second being an extension,
/// including the dot. E.g. "foo.txt" is returned as `("foo",
/// ".txt")` while "bar" is returned as `("bar", "")`.
///
/// The extension part will always be lowercased.
fn sanitise_name(name: &str) -> (String, String) {
let mut name = name.to_string();
for part in name.rsplit('/') {
if !part.is_empty() {
name = part.to_string();
break;
}
}
for part in name.rsplit('\\') {
if !part.is_empty() {
name = part.to_string();
break;
}
}
let opts = sanitize_filename::Options {
truncate: true,
windows: true,
replacement: "",
};
let clean = sanitize_filename::sanitize_with_options(name, opts);
let mut iter = clean.splitn(2, '.');
let stem: String = iter.next().unwrap_or_default().chars().take(64).collect();
let ext: String = iter.next().unwrap_or_default().chars().take(32).collect();
if ext.is_empty() {
(stem, "".to_string())
} else {
(stem, format!(".{}", ext).to_lowercase())
}
}
/// Checks whether a name is a valid blob name.
///
/// This is slightly less strict than stanitise_name, presumably
/// someone already created a file with such a name so we just
/// ensure it's not actually a path in disguise is actually utf-8.
fn is_acceptible_blob_name(name: impl AsRef<OsStr>) -> bool {
let uname = match name.as_ref().to_str() {
Some(name) => name,
None => return false,
};
if uname.find('/').is_some() {
return false;
}
if uname.find('\\').is_some() {
return false;
}
if uname.find('\0').is_some() {
return false;
}
true
}
pub fn recode_to_avatar_size(&self, context: &Context) -> Result<(), BlobError> {
let blob_abs = self.to_abs_path();
let img = image::open(&blob_abs).map_err(|err| BlobError::RecodeFailure {
blobdir: context.get_blobdir().to_path_buf(),
blobname: blob_abs.to_str().unwrap_or_default().to_string(),
cause: err,
backtrace: failure::Backtrace::new(),
})?;
if img.width() <= AVATAR_SIZE && img.height() <= AVATAR_SIZE {
return Ok(());
}
let img = img.thumbnail(AVATAR_SIZE, AVATAR_SIZE);
img.save(&blob_abs).map_err(|err| BlobError::WriteFailure {
blobdir: context.get_blobdir().to_path_buf(),
blobname: blob_abs.to_str().unwrap_or_default().to_string(),
cause: err,
backtrace: failure::Backtrace::new(),
})?;
Ok(())
}
}
impl<'a> fmt::Display for BlobObject<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "$BLOBDIR/{}", self.name)
}
}
/// Errors for the [BlobObject].
#[derive(Fail, Debug)]
pub enum BlobError {
CreateFailure {
blobdir: PathBuf,
blobname: String,
#[cause]
cause: std::io::Error,
backtrace: failure::Backtrace,
},
WriteFailure {
blobdir: PathBuf,
blobname: String,
#[cause]
cause: std::io::Error,
backtrace: failure::Backtrace,
},
CopyFailure {
blobdir: PathBuf,
blobname: String,
src: PathBuf,
#[cause]
cause: std::io::Error,
backtrace: failure::Backtrace,
},
RecodeFailure {
blobdir: PathBuf,
blobname: String,
#[cause]
cause: image::ImageError,
backtrace: failure::Backtrace,
},
WrongBlobdir {
blobdir: PathBuf,
src: PathBuf,
backtrace: failure::Backtrace,
},
WrongName {
blobname: PathBuf,
backtrace: failure::Backtrace,
},
}
// Implementing Display is done by hand because the failure
// #[fail(display = "...")] syntax does not allow using
// `blobdir.display()`.
impl fmt::Display for BlobError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Match on the data rather than kind, they are equivalent for
// identifying purposes but contain the actual data we need.
match &self {
BlobError::CreateFailure {
blobdir, blobname, ..
} => write!(
f,
"Failed to create blob {} in {}",
blobname,
blobdir.display()
),
BlobError::WriteFailure {
blobdir, blobname, ..
} => write!(
f,
"Failed to write data to blob {} in {}",
blobname,
blobdir.display()
),
BlobError::CopyFailure {
blobdir,
blobname,
src,
..
} => write!(
f,
"Failed to copy data from {} to blob {} in {}",
src.display(),
blobname,
blobdir.display(),
),
BlobError::RecodeFailure {
blobdir, blobname, ..
} => write!(f, "Failed to recode {} in {}", blobname, blobdir.display(),),
BlobError::WrongBlobdir { blobdir, src, .. } => write!(
f,
"File path {} is not in blobdir {}",
src.display(),
blobdir.display(),
),
BlobError::WrongName { blobname, .. } => {
write!(f, "Blob has a bad name: {}", blobname.display(),)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn test_create() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo", b"hello").unwrap();
let fname = t.ctx.get_blobdir().join("foo");
let data = fs::read(fname).unwrap();
assert_eq!(data, b"hello");
assert_eq!(blob.as_name(), "$BLOBDIR/foo");
assert_eq!(blob.to_abs_path(), t.ctx.get_blobdir().join("foo"));
}
#[test]
fn test_lowercase_ext() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.TXT", b"hello").unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt");
}
#[test]
fn test_as_file_name() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(blob.as_file_name(), "foo.txt");
}
#[test]
fn test_as_rel_path() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(blob.as_rel_path(), Path::new("foo.txt"));
}
#[test]
fn test_suffix() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(blob.suffix(), Some("txt"));
let blob = BlobObject::create(&t.ctx, "bar", b"world").unwrap();
assert_eq!(blob.suffix(), None);
}
#[test]
fn test_create_dup() {
let t = dummy_context();
BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
let foo_path = t.ctx.get_blobdir().join("foo.txt");
assert!(foo_path.exists());
BlobObject::create(&t.ctx, "foo.txt", b"world").unwrap();
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() {
let fname = dirent.unwrap().file_name();
if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).unwrap(), b"hello");
} else {
let name = fname.to_str().unwrap();
assert!(name.starts_with("foo"));
assert!(name.ends_with(".txt"));
}
}
}
#[test]
fn test_double_ext_preserved() {
let t = dummy_context();
BlobObject::create(&t.ctx, "foo.tar.gz", b"hello").unwrap();
let foo_path = t.ctx.get_blobdir().join("foo.tar.gz");
assert!(foo_path.exists());
BlobObject::create(&t.ctx, "foo.tar.gz", b"world").unwrap();
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() {
let fname = dirent.unwrap().file_name();
if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).unwrap(), b"hello");
} else {
let name = fname.to_str().unwrap();
println!("{}", name);
assert!(name.starts_with("foo"));
assert!(name.ends_with(".tar.gz"));
}
}
}
#[test]
fn test_create_long_names() {
let t = dummy_context();
let s = "1".repeat(150);
let blob = BlobObject::create(&t.ctx, &s, b"data").unwrap();
let blobname = blob.as_name().split('/').last().unwrap();
assert!(blobname.len() < 128);
}
#[test]
fn test_create_and_copy() {
let t = dummy_context();
let src = t.dir.path().join("src");
fs::write(&src, b"boo").unwrap();
let blob = BlobObject::create_and_copy(&t.ctx, &src).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/src");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
let whoops = t.dir.path().join("whoops");
assert!(BlobObject::create_and_copy(&t.ctx, &whoops).is_err());
let whoops = t.ctx.get_blobdir().join("whoops");
assert!(!whoops.exists());
}
#[test]
fn test_create_from_path() {
let t = dummy_context();
let src_ext = t.dir.path().join("external");
fs::write(&src_ext, b"boo").unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_ext).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/external");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
let src_int = t.ctx.get_blobdir().join("internal");
fs::write(&src_int, b"boo").unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_int).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/internal");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
}
#[test]
fn test_create_from_name_long() {
let t = dummy_context();
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
fs::write(&src_ext, b"boo").unwrap();
let blob = BlobObject::new_from_path(&t.ctx, &src_ext).unwrap();
assert_eq!(
blob.as_name(),
"$BLOBDIR/autocrypt-setup-message-4137848473.html"
);
}
#[test]
fn test_is_blob_name() {
assert!(BlobObject::is_acceptible_blob_name("foo"));
assert!(BlobObject::is_acceptible_blob_name("foo.txt"));
assert!(BlobObject::is_acceptible_blob_name("f".repeat(128)));
assert!(!BlobObject::is_acceptible_blob_name("foo/bar"));
assert!(!BlobObject::is_acceptible_blob_name("foo\\bar"));
assert!(!BlobObject::is_acceptible_blob_name("foo\x00bar"));
}
#[test]
fn test_sanitise_name() {
let (_, ext) =
BlobObject::sanitise_name("Я ЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯ.txt");
assert_eq!(ext, ".txt");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,12 @@
//! # Chat list module
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::context::*;
use crate::error::Result;
use crate::lot::Lot;
use crate::message::*;
use crate::message::{Message, MessageState, MsgId};
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
@@ -31,17 +33,13 @@ use crate::stock::StockMessage;
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
/// present and sorted into the list by date. Rendering the deaddrop in the described way
/// would not add extra work in the UI then.
pub struct Chatlist<'a> {
context: &'a Context,
#[derive(Debug)]
pub struct Chatlist {
/// Stores pairs of `chat_id, message_id`
ids: Vec<(u32, u32)>,
ids: Vec<(ChatId, MsgId)>,
}
impl<'a> Chatlist<'a> {
pub fn get_context(&self) -> &Context {
self.context
}
impl Chatlist {
/// Get a list of chats.
/// The list can be filtered by query parameters.
///
@@ -62,7 +60,7 @@ impl<'a> Chatlist<'a> {
/// or "Not now".
/// The UI can also offer a "Close" button that calls dc_marknoticed_contact() then.
/// - DC_CHAT_ID_ARCHIVED_LINK (6) - this special chat is present if the user has
/// archived _any_ chat using dc_archive_chat(). The UI should show a link as
/// archived *any* chat using dc_set_chat_visibility(). The UI should show a link as
/// "Show archived chats", if the user clicks this item, the UI should show a
/// list of all archived chats that can be created by this function hen using
/// the DC_GCL_ARCHIVED_ONLY flag.
@@ -73,7 +71,7 @@ impl<'a> Chatlist<'a> {
/// The `listflags` is a combination of flags:
/// - if the flag DC_GCL_ARCHIVED_ONLY is set, only archived chats are returned.
/// if DC_GCL_ARCHIVED_ONLY is not set, only unarchived chats are returned and
/// the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are _any_ archived
/// the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are *any* archived
/// chats
/// - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added
/// to the list (may be used eg. for selecting chats on forwarding, the flag is
@@ -85,30 +83,17 @@ impl<'a> Chatlist<'a> {
/// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID
/// are returned.
pub fn try_load(
context: &'a Context,
context: &Context,
listflags: usize,
query: Option<&str>,
query_contact_id: Option<u32>,
) -> Result<Self> {
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let mut add_archived_link_item = false;
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id as u32, msg_id as u32))
let chat_id: ChatId = row.get(0)?;
let msg_id: MsgId = row.get(1).unwrap_or_default();
Ok((chat_id, msg_id))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
@@ -116,37 +101,60 @@ impl<'a> Chatlist<'a> {
.map_err(Into::into)
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// select with left join and minimum:
//
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same
// timestamp
// - the list starts with the newest chats
//
// nb: the query currently shows messages from blocked
// contacts in groups. however, for normal-groups, this is
// okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and
// tg do the same) for the deaddrop, however, they should
// really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let mut ids = if let Some(query_contact_id) = query_contact_id {
// show chats shared with a given contact
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)?
"SELECT c.id, m.id
FROM chats c
LEFT JOIN msgs m
ON c.id=m.chat_id
AND m.timestamp=(
SELECT MAX(timestamp)
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?1))
WHERE c.id>9
AND c.blocked=0
AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2)
GROUP BY c.id
ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
params![MessageState::OutDraft, query_contact_id as i32, ChatVisibility::Pinned],
process_row,
process_rows,
)?
} else if 0 != listflags & DC_GCL_ARCHIVED_ONLY {
// show archived chats
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
"SELECT c.id, m.id
FROM chats c
LEFT JOIN msgs m
ON c.id=m.chat_id
AND m.timestamp=(
SELECT MAX(timestamp)
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?))
WHERE c.id>9
AND c.blocked=0
AND c.archived=1
GROUP BY c.id
ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
params![MessageState::OutDraft],
process_row,
process_rows,
)?
@@ -154,53 +162,73 @@ impl<'a> Chatlist<'a> {
let query = query.trim().to_string();
ensure!(!query.is_empty(), "missing query");
// allow searching over special names that may change at any time
// when the ui calls set_stock_translation()
if let Err(err) = update_special_chat_names(context) {
warn!(context, "cannot update special chat names: {:?}", err)
}
let str_like_cmd = format!("%{}%", query);
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![str_like_cmd],
"SELECT c.id, m.id
FROM chats c
LEFT JOIN msgs m
ON c.id=m.chat_id
AND m.timestamp=(
SELECT MAX(timestamp)
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?))
WHERE c.id>9
AND c.blocked=0
AND c.name LIKE ?
GROUP BY c.id
ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
params![MessageState::OutDraft, str_like_cmd],
process_row,
process_rows,
)?
} else {
// show normal chatlist
let mut ids = context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
"SELECT c.id, m.id
FROM chats c
LEFT JOIN msgs m
ON c.id=m.chat_id
AND m.timestamp=(
SELECT MAX(timestamp)
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?1))
WHERE c.id>9
AND c.blocked=0
AND NOT c.archived=?2
GROUP BY c.id
ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
params![MessageState::OutDraft, ChatVisibility::Archived, ChatVisibility::Pinned],
process_row,
process_rows,
)?;
if 0 == listflags & DC_GCL_NO_SPECIALS {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
if last_deaddrop_fresh_msg_id > 0 {
ids.push((1, last_deaddrop_fresh_msg_id));
if let Some(last_deaddrop_fresh_msg_id) = get_last_deaddrop_fresh_msg(context) {
ids.insert(
0,
(ChatId::new(DC_CHAT_ID_DEADDROP), last_deaddrop_fresh_msg_id),
);
}
add_archived_link_item = 1;
add_archived_link_item = true;
}
ids
};
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
ids.push((DC_CHAT_ID_ALLDONE_HINT, 0));
ids.push((ChatId::new(DC_CHAT_ID_ALLDONE_HINT), MsgId::new(0)));
}
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
ids.push((ChatId::new(DC_CHAT_ID_ARCHIVED_LINK), MsgId::new(0)));
}
Ok(Chatlist { context, ids })
Ok(Chatlist { ids })
}
/// Find out the number of chats.
@@ -208,6 +236,7 @@ impl<'a> Chatlist<'a> {
self.ids.len()
}
/// Returns true if chatlist is empty.
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
@@ -215,9 +244,9 @@ impl<'a> Chatlist<'a> {
/// Get a single chat ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_chat().
pub fn get_chat_id(&self, index: usize) -> u32 {
pub fn get_chat_id(&self, index: usize) -> ChatId {
if index >= self.ids.len() {
return 0;
return ChatId::new(0);
}
self.ids[index].0
}
@@ -225,12 +254,9 @@ impl<'a> Chatlist<'a> {
/// Get a single message ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_msg().
pub fn get_msg_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].1
pub fn get_msg_id(&self, index: usize) -> Result<MsgId> {
ensure!(index < self.ids.len(), "Chatlist index out of range");
Ok(self.ids[index].1)
}
/// Get a summary for a chatlist index.
@@ -247,13 +273,13 @@ impl<'a> Chatlist<'a> {
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
// 0 if not applicable.
pub fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
pub fn get_summary(&self, context: &Context, index: usize, chat: Option<&Chat>) -> Lot {
// The summary is created by the chat, not by the last message.
// This is because we may want to display drafts here or stuff as
// "is typing".
// Also, sth. as "No messages" would not work if the summary comes from a message.
let mut ret = Lot::new();
if index >= self.ids.len() {
ret.text2 = Some("ErrBadChatlistIndex".to_string());
return ret;
@@ -262,78 +288,137 @@ impl<'a> Chatlist<'a> {
let chat_loaded: Chat;
let chat = if let Some(chat) = chat {
chat
} else if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
}
return ret;
};
let lastmsg_id = self.ids[index].1;
let mut lastcontact = None;
let lastmsg = if 0 != lastmsg_id {
if let Ok(lastmsg) = dc_msg_load_from_db(self.context, lastmsg_id) {
if lastmsg.from_id != 1 as libc::c_uint
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(self.context, lastmsg.from_id).ok();
}
Some(lastmsg)
} else {
None
let lastmsg = if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
if lastmsg.from_id != DC_CONTACT_ID_SELF
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
}
Some(lastmsg)
} else {
None
};
if chat.id == DC_CHAT_ID_ARCHIVED_LINK {
if chat.id.is_archived_link() {
ret.text2 = None;
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
{
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
ret.text2 = Some(context.stock_str(StockMessage::NoMessages).to_string());
} else {
ret.fill(
&mut lastmsg.unwrap(),
chat,
lastcontact.as_ref(),
self.context,
);
ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context);
}
ret
}
pub fn get_index_for_id(&self, id: ChatId) -> Option<usize> {
self.ids.iter().position(|(chat_id, _)| chat_id == &id)
}
}
/// Returns the number of archived chats
pub fn dc_get_archived_cnt(context: &Context) -> u32 {
context
.sql
.query_row_col(
.query_get_value(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// We have an index over the state-column, this should be sufficient as there are typically
// only few fresh messages.
context
.sql
.query_row_col(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
fn get_last_deaddrop_fresh_msg(context: &Context) -> Option<MsgId> {
// We have an index over the state-column, this should be
// sufficient as there are typically only few fresh messages.
context.sql.query_get_value(
context,
concat!(
"SELECT m.id",
" FROM msgs m",
" LEFT JOIN chats c",
" ON c.id=m.chat_id",
" WHERE m.state=10",
" AND m.hidden=0",
" AND c.blocked=2",
" ORDER BY m.timestamp DESC, m.id DESC;"
),
params![],
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn test_try_load() {
let t = dummy_context();
let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat").unwrap();
let chat_id2 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "b chat").unwrap();
let chat_id3 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "c chat").unwrap();
// check that the chatlist starts with the most recent message
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
assert_eq!(chats.len(), 3);
assert_eq!(chats.get_chat_id(0), chat_id3);
assert_eq!(chats.get_chat_id(1), chat_id2);
assert_eq!(chats.get_chat_id(2), chat_id1);
// drafts are sorted to the top
let mut msg = Message::new(Viewtype::Text);
msg.set_text(Some("hello".to_string()));
chat_id2.set_draft(&t.ctx, Some(&mut msg));
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
assert_eq!(chats.get_chat_id(0), chat_id2);
// check chatlist query and archive functionality
let chats = Chatlist::try_load(&t.ctx, 0, Some("b"), None).unwrap();
assert_eq!(chats.len(), 1);
let chats = Chatlist::try_load(&t.ctx, DC_GCL_ARCHIVED_ONLY, None, None).unwrap();
assert_eq!(chats.len(), 0);
chat_id1
.set_visibility(&t.ctx, ChatVisibility::Archived)
.ok();
let chats = Chatlist::try_load(&t.ctx, DC_GCL_ARCHIVED_ONLY, None, None).unwrap();
assert_eq!(chats.len(), 1);
}
#[test]
fn test_search_special_chat_names() {
let t = dummy_context();
t.ctx.update_device_chats().unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, Some("t-1234-s"), None).unwrap();
assert_eq!(chats.len(), 0);
let chats = Chatlist::try_load(&t.ctx, 0, Some("t-5678-b"), None).unwrap();
assert_eq!(chats.len(), 0);
t.ctx
.set_stock_translation(StockMessage::SavedMessages, "test-1234-save".to_string())
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, Some("t-1234-s"), None).unwrap();
assert_eq!(chats.len(), 1);
t.ctx
.set_stock_translation(StockMessage::DeviceMessages, "test-5678-babbel".to_string())
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, Some("t-5678-b"), None).unwrap();
assert_eq!(chats.len(), 1);
}
}

View File

@@ -1,12 +1,16 @@
//! # Key-value configuration management
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::blob::BlobObject;
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_tools::*;
use crate::error::Error;
use crate::job::*;
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
use crate::stock::StockMessage;
use rusqlite::NO_PARAMS;
/// The available configuration keys.
#[derive(
@@ -19,29 +23,48 @@ pub enum Config {
MailUser,
MailPw,
MailPort,
ImapCertificateChecks,
SendServer,
SendUser,
SendPw,
SendPort,
SmtpCertificateChecks,
ServerFlags,
#[strum(props(default = "INBOX"))]
ImapFolder,
Displayname,
Selfstatus,
Selfavatar,
#[strum(props(default = "0"))]
BccSelf,
#[strum(props(default = "1"))]
E2eeEnabled,
#[strum(props(default = "1"))]
MdnsEnabled,
#[strum(props(default = "1"))]
InboxWatch,
#[strum(props(default = "1"))]
SentboxWatch,
#[strum(props(default = "1"))]
MvboxWatch,
#[strum(props(default = "1"))]
MvboxMove,
#[strum(props(default = "0"))]
#[strum(props(default = "0"))] // also change ShowEmails.default() on changes
ShowEmails,
#[strum(props(default = "0"))]
KeyGenType,
SaveMimeHeaders,
ConfiguredAddr,
ConfiguredMailServer,
@@ -49,19 +72,23 @@ pub enum Config {
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredMailSecurity,
ConfiguredImapCertificateChecks,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredSmtpCertificateChecks,
ConfiguredServerFlags,
ConfiguredSendSecurity,
ConfiguredE2EEEnabled,
Configured,
// Deprecated
#[strum(serialize = "sys.version")]
SysVersion,
#[strum(serialize = "sys.msgsize_max_recommended")]
SysMsgsizeMaxRecommended,
#[strum(serialize = "sys.config_keys")]
SysConfigKeys,
}
@@ -71,13 +98,13 @@ impl Context {
pub fn get_config(&self, key: Config) -> Option<String> {
let value = match key {
Config::Selfavatar => {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
let rel_path = self.sql.get_raw_config(self, key);
rel_path.map(|p| dc_get_abs_path(self, &p).to_string_lossy().into_owned())
}
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", RECOMMENDED_FILE_SIZE)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_config(self, key),
_ => self.sql.get_raw_config(self, key),
};
if value.is_some() {
@@ -91,27 +118,46 @@ impl Context {
}
}
pub fn get_config_int(&self, key: Config) -> i32 {
self.get_config(key)
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
pub fn get_config_bool(&self, key: Config) -> bool {
self.get_config_int(key) != 0
}
/// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
pub fn set_config(&self, key: Config, value: Option<&str>) -> crate::sql::Result<()> {
match key {
Config::Selfavatar if value.is_some() => {
let rel_path = std::fs::canonicalize(value.unwrap())?;
Config::Selfavatar => {
self.sql
.set_config(self, key, Some(&rel_path.to_string_lossy()))
.execute("UPDATE contacts SET selfavatar_sent=0;", NO_PARAMS)?;
self.sql
.set_raw_config_bool(self, "attach_selfavatar", true)?;
match value {
Some(value) => {
let blob = BlobObject::new_from_path(&self, value)?;
blob.recode_to_avatar_size(self)?;
self.sql.set_raw_config(self, key, Some(blob.as_name()))
}
None => self.sql.set_raw_config(self, key, None),
}
}
Config::InboxWatch => {
let ret = self.sql.set_config(self, key, value);
interrupt_imap_idle(self);
let ret = self.sql.set_raw_config(self, key, value);
interrupt_inbox_idle(self);
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_config(self, key, value);
let ret = self.sql.set_raw_config(self, key, value);
interrupt_sentbox_idle(self);
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_config(self, key, value);
let ret = self.sql.set_raw_config(self, key, value);
interrupt_mvbox_idle(self);
ret
}
@@ -123,9 +169,9 @@ impl Context {
value
};
self.sql.set_config(self, key, val)
self.sql.set_raw_config(self, key, val)
}
_ => self.sql.set_config(self, key, value),
_ => self.sql.set_raw_config(self, key, value),
}
}
}
@@ -148,6 +194,12 @@ mod tests {
use std::str::FromStr;
use std::string::ToString;
use crate::constants::AVATAR_SIZE;
use crate::test_utils::*;
use image::GenericImageView;
use std::fs::File;
use std::io::Write;
#[test]
fn test_to_string() {
assert_eq!(Config::MailServer.to_string(), "mail_server");
@@ -164,4 +216,57 @@ mod tests {
fn test_default_prop() {
assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX"));
}
#[test]
fn test_selfavatar_outside_blobdir() {
let t = dummy_context();
let avatar_src = t.dir.path().join("avatar.jpg");
let avatar_bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
File::create(&avatar_src)
.unwrap()
.write_all(avatar_bytes)
.unwrap();
let avatar_blob = t.ctx.get_blobdir().join("avatar.jpg");
assert!(!avatar_blob.exists());
t.ctx
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))
.unwrap();
assert!(avatar_blob.exists());
assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64);
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
let img = image::open(avatar_src).unwrap();
assert_eq!(img.width(), 1000);
assert_eq!(img.height(), 1000);
let img = image::open(avatar_blob).unwrap();
assert_eq!(img.width(), AVATAR_SIZE);
assert_eq!(img.height(), AVATAR_SIZE);
}
#[test]
fn test_selfavatar_in_blobdir() {
let t = dummy_context();
let avatar_src = t.ctx.get_blobdir().join("avatar.png");
let avatar_bytes = include_bytes!("../test-data/image/avatar900x900.png");
File::create(&avatar_src)
.unwrap()
.write_all(avatar_bytes)
.unwrap();
let img = image::open(&avatar_src).unwrap();
assert_eq!(img.width(), 900);
assert_eq!(img.height(), 900);
t.ctx
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))
.unwrap();
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
assert_eq!(avatar_cfg, avatar_src.to_str().map(|s| s.to_string()));
let img = image::open(avatar_src).unwrap();
assert_eq!(img.width(), AVATAR_SIZE);
assert_eq!(img.height(), AVATAR_SIZE);
}
}

View File

@@ -1,88 +1,110 @@
//! # Thunderbird's Autoconfiguration implementation
//!
//! Documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::x::*;
use crate::login_param::LoginParam;
use super::read_autoconf_file;
/* ******************************************************************************
* Thunderbird's Autoconfigure
******************************************************************************/
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
#[repr(C)]
struct moz_autoconfigure_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub in_emaildomain: *mut libc::c_char,
pub in_emaillocalpart: *mut libc::c_char,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_server: libc::c_int,
pub tag_config: libc::c_int,
use super::read_url::read_url;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "Invalid email address: {:?}", _0)]
InvalidEmailAddress(String),
#[fail(display = "XML error at position {}", position)]
InvalidXml {
position: usize,
#[cause]
error: quick_xml::Error,
},
#[fail(display = "Bad or incomplete autoconfig")]
IncompleteAutoconfig(LoginParam),
#[fail(display = "Failed to get URL {}", _0)]
ReadUrlError(#[cause] super::read_url::Error),
}
pub unsafe fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut moz_ac = moz_autoconfigure_t {
in_0: param_in,
in_emaildomain: std::ptr::null_mut(),
in_emaillocalpart: std::ptr::null_mut(),
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_server: 0,
tag_config: 0,
};
pub type Result<T> = std::result::Result<T, Error>;
let url_c = url.strdup();
let xml_raw = read_autoconf_file(context, url_c);
free(url_c as *mut libc::c_void);
if xml_raw.is_null() {
return None;
impl From<super::read_url::Error> for Error {
fn from(err: super::read_url::Error) -> Error {
Error::ReadUrlError(err)
}
}
moz_ac.in_emaillocalpart = param_in.addr.strdup();
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
#[derive(Debug)]
struct MozAutoconfigure<'a> {
pub in_emailaddr: &'a str,
pub in_emaildomain: &'a str,
pub in_emaillocalpart: &'a str,
pub out: LoginParam,
pub out_imap_set: bool,
pub out_smtp_set: bool,
pub tag_server: MozServer,
pub tag_config: MozConfigTag,
}
if p.is_null() {
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
}
#[derive(Debug, PartialEq)]
enum MozServer {
Undefined,
Imap,
Smtp,
}
*p = 0 as libc::c_char;
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
#[derive(Debug)]
enum MozConfigTag {
Undefined,
Hostname,
Port,
Sockettype,
Username,
}
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam> {
let mut reader = quick_xml::Reader::from_str(xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
// Split address into local part and domain part.
let p = in_emailaddr
.find('@')
.ok_or_else(|| Error::InvalidEmailAddress(in_emailaddr.to_string()))?;
let (in_emaillocalpart, in_emaildomain) = in_emailaddr.split_at(p);
let in_emaildomain = &in_emaildomain[1..];
let mut moz_ac = MozAutoconfigure {
in_emailaddr,
in_emaildomain,
in_emaillocalpart,
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
tag_server: MozServer::Undefined,
tag_config: MozConfigTag::Undefined,
};
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
let event = reader
.read_event(&mut buf)
.map_err(|error| Error::InvalidXml {
position: reader.buffer_position(),
error,
})?;
match event {
quick_xml::events::Event::Start(ref e) => {
moz_autoconfigure_starttag_cb(e, &mut moz_ac, &reader)
}
Ok(quick_xml::events::Event::End(ref e)) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
Ok(quick_xml::events::Event::Text(ref e)) => {
quick_xml::events::Event::End(ref e) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
quick_xml::events::Event::Text(ref e) => {
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
}
Err(e) => {
error!(
context,
0,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
quick_xml::events::Event::Eof => break,
_ => (),
}
buf.clear();
@@ -93,30 +115,39 @@ pub unsafe fn moz_autoconfigure(
|| moz_ac.out.send_server.is_empty()
|| moz_ac.out.send_port == 0
{
let r = dc_loginparam_get_readable(&moz_ac.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
Err(Error::IncompleteAutoconfig(moz_ac.out))
} else {
Ok(moz_ac.out)
}
}
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
Some(moz_ac.out)
pub fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Result<LoginParam> {
let xml_raw = read_url(context, url)?;
let res = parse_xml(&param_in.addr, &xml_raw);
if let Err(err) = &res {
warn!(
context,
"Failed to parse Thunderbird autoconfiguration XML: {}", err
);
}
res
}
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
event: &BytesText,
moz_ac: &mut moz_autoconfigure_t,
moz_ac: &mut MozAutoconfigure,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
let addr = &moz_ac.in_0.addr;
let email_local = as_str(moz_ac.in_emaillocalpart);
let email_domain = as_str(moz_ac.in_emaildomain);
let addr = moz_ac.in_emailaddr;
let email_local = moz_ac.in_emaillocalpart;
let email_domain = moz_ac.in_emaildomain;
let val = val
.trim()
@@ -124,66 +155,70 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
.replace("%EMAILLOCALPART%", email_local)
.replace("%EMAILDOMAIN%", email_domain);
if moz_ac.tag_server == 1 {
match moz_ac.tag_config {
10 => moz_ac.out.mail_server = val,
11 => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.mail_user = val,
13 => {
match moz_ac.tag_server {
MozServer::Imap => match moz_ac.tag_config {
MozConfigTag::Hostname => moz_ac.out.mail_server = val,
MozConfigTag::Port => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
MozConfigTag::Username => moz_ac.out.mail_user = val,
MozConfigTag::Sockettype => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= 0x200
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
}
if val_lower == "starttls" {
moz_ac.out.server_flags |= 0x100
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_STARTTLS as i32
}
if val_lower == "plain" {
moz_ac.out.server_flags |= 0x400
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
}
}
_ => {}
}
} else if moz_ac.tag_server == 2 {
match moz_ac.tag_config {
10 => moz_ac.out.send_server = val,
11 => moz_ac.out.send_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.send_user = val,
13 => {
},
MozServer::Smtp => match moz_ac.tag_config {
MozConfigTag::Hostname => moz_ac.out.send_server = val,
MozConfigTag::Port => moz_ac.out.send_port = val.parse().unwrap_or_default(),
MozConfigTag::Username => moz_ac.out.send_user = val,
MozConfigTag::Sockettype => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= 0x20000
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
}
if val_lower == "starttls" {
moz_ac.out.server_flags |= 0x10000
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32
}
if val_lower == "plain" {
moz_ac.out.server_flags |= 0x40000
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
}
}
_ => {}
}
},
MozServer::Undefined => {}
}
}
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut moz_autoconfigure_t) {
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut MozAutoconfigure) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "incomingserver" {
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_imap_set = 1;
if moz_ac.tag_server == MozServer::Imap {
moz_ac.out_imap_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
} else if tag == "outgoingserver" {
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_smtp_set = 1;
if moz_ac.tag_server == MozServer::Smtp {
moz_ac.out_smtp_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
} else {
moz_ac.tag_config = 0;
moz_ac.tag_config = MozConfigTag::Undefined;
}
}
fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
moz_ac: &mut moz_autoconfigure_t,
moz_ac: &mut MozAutoconfigure,
reader: &quick_xml::Reader<B>,
) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
@@ -200,25 +235,115 @@ fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
.unwrap_or_default()
.to_lowercase();
if typ == "imap" && moz_ac.out_imap_set == 0 {
1
if typ == "imap" && !moz_ac.out_imap_set {
MozServer::Imap
} else {
0
MozServer::Undefined
}
} else {
0
MozServer::Undefined
};
moz_ac.tag_config = 0;
moz_ac.tag_config = MozConfigTag::Undefined;
} else if tag == "outgoingserver" {
moz_ac.tag_server = if moz_ac.out_smtp_set == 0 { 2 } else { 0 };
moz_ac.tag_config = 0;
moz_ac.tag_server = if !moz_ac.out_smtp_set {
MozServer::Smtp
} else {
MozServer::Undefined
};
moz_ac.tag_config = MozConfigTag::Undefined;
} else if tag == "hostname" {
moz_ac.tag_config = 10;
moz_ac.tag_config = MozConfigTag::Hostname;
} else if tag == "port" {
moz_ac.tag_config = 11;
moz_ac.tag_config = MozConfigTag::Port;
} else if tag == "sockettype" {
moz_ac.tag_config = 13;
moz_ac.tag_config = MozConfigTag::Sockettype;
} else if tag == "username" {
moz_ac.tag_config = 12;
moz_ac.tag_config = MozConfigTag::Username;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_outlook_autoconfig() {
// Copied from https://autoconfig.thunderbird.net/v1.1/outlook.com on 2019-10-11
let xml_raw =
"<clientConfig version=\"1.1\">
<emailProvider id=\"outlook.com\">
<domain>hotmail.com</domain>
<domain>hotmail.co.uk</domain>
<domain>hotmail.co.jp</domain>
<domain>hotmail.com.br</domain>
<domain>hotmail.de</domain>
<domain>hotmail.fr</domain>
<domain>hotmail.it</domain>
<domain>hotmail.es</domain>
<domain>live.com</domain>
<domain>live.co.uk</domain>
<domain>live.co.jp</domain>
<domain>live.de</domain>
<domain>live.fr</domain>
<domain>live.it</domain>
<domain>live.jp</domain>
<domain>msn.com</domain>
<domain>outlook.com</domain>
<displayName>Outlook.com (Microsoft)</displayName>
<displayShortName>Outlook</displayShortName>
<incomingServer type=\"exchange\">
<hostname>outlook.office365.com</hostname>
<port>443</port>
<username>%EMAILADDRESS%</username>
<socketType>SSL</socketType>
<authentication>OAuth2</authentication>
<owaURL>https://outlook.office365.com/owa/</owaURL>
<ewsURL>https://outlook.office365.com/ews/exchange.asmx</ewsURL>
<useGlobalPreferredServer>true</useGlobalPreferredServer>
</incomingServer>
<incomingServer type=\"imap\">
<hostname>outlook.office365.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<incomingServer type=\"pop3\">
<hostname>outlook.office365.com</hostname>
<port>995</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
<pop3>
<leaveMessagesOnServer>true</leaveMessagesOnServer>
<!-- Outlook.com docs specifically mention that POP3 deletes have effect on the main inbox on webmail and IMAP -->
</pop3>
</incomingServer>
<outgoingServer type=\"smtp\">
<hostname>smtp.office365.com</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</outgoingServer>
<documentation url=\"http://windows.microsoft.com/en-US/windows/outlook/send-receive-from-app\">
<descr lang=\"en\">Set up an email app with Outlook.com</descr>
</documentation>
</emailProvider>
<webMail>
<loginPage url=\"https://www.outlook.com/\"/>
<loginPageInfo url=\"https://www.outlook.com/\">
<username>%EMAILADDRESS%</username>
<usernameField id=\"i0116\" name=\"login\"/>
<passwordField id=\"i0118\" name=\"passwd\"/>
<loginButton id=\"idSIButton9\" name=\"SI\"/>
</loginPageInfo>
</webMail>
</clientConfig>";
let res = parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
assert_eq!(res.mail_server, "outlook.office365.com");
assert_eq!(res.mail_port, 993);
assert_eq!(res.send_server, "smtp.office365.com");
assert_eq!(res.send_port, 587);
}
}

View File

@@ -1,213 +1,269 @@
//! Outlook's Autodiscover
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use quick_xml::events::BytesEnd;
use crate::constants::*;
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::x::*;
use std::ptr;
use crate::login_param::LoginParam;
use super::read_autoconf_file;
/* ******************************************************************************
* Outlook's Autodiscover
******************************************************************************/
#[repr(C)]
struct outlk_autodiscover_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_config: libc::c_int,
pub config: [*mut libc::c_char; 6],
pub redirect: *mut libc::c_char,
use super::read_url::read_url;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "XML error at position {}", position)]
InvalidXml {
position: usize,
#[cause]
error: quick_xml::Error,
},
#[fail(display = "Bad or incomplete autoconfig")]
IncompleteAutoconfig(LoginParam),
#[fail(display = "Failed to get URL {}", _0)]
ReadUrlError(#[cause] super::read_url::Error),
#[fail(display = "Number of redirection is exceeded")]
RedirectionError,
}
pub unsafe fn outlk_autodiscover(
context: &Context,
url__: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut xml_raw: *mut libc::c_char = ptr::null_mut();
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_config: 0,
config: [ptr::null_mut(); 6],
redirect: ptr::null_mut(),
pub type Result<T> = std::result::Result<T, Error>;
impl From<super::read_url::Error> for Error {
fn from(err: super::read_url::Error) -> Error {
Error::ReadUrlError(err)
}
}
struct OutlookAutodiscover {
pub out: LoginParam,
pub out_imap_set: bool,
pub out_smtp_set: bool,
pub config_type: Option<String>,
pub config_server: String,
pub config_port: i32,
pub config_ssl: String,
pub config_redirecturl: Option<String>,
}
enum ParsingResult {
LoginParam(LoginParam),
RedirectUrl(String),
}
fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
let mut outlk_ad = OutlookAutodiscover {
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
config_type: None,
config_server: String::new(),
config_port: 0,
config_ssl: String::new(),
config_redirecturl: None,
};
let ok_to_continue;
let mut i = 0;
let mut reader = quick_xml::Reader::from_str(&xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
let mut current_tag: Option<String> = None;
loop {
if !(i < 10) {
ok_to_continue = true;
break;
}
memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),
);
xml_raw = read_autoconf_file(context, url);
if xml_raw.is_null() {
ok_to_continue = false;
break;
}
let event = reader
.read_event(&mut buf)
.map_err(|error| Error::InvalidXml {
position: reader.buffer_position(),
error,
})?;
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
match event {
quick_xml::events::Event::Start(ref e) => {
let tag = String::from_utf8_lossy(e.name()).trim().to_lowercase();
let mut buf = Vec::new();
if tag == "protocol" {
outlk_ad.config_type = None;
outlk_ad.config_server = String::new();
outlk_ad.config_port = 0;
outlk_ad.config_ssl = String::new();
outlk_ad.config_redirecturl = None;
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
outlk_autodiscover_starttag_cb(e, &mut outlk_ad)
current_tag = None;
} else {
current_tag = Some(tag);
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::Text(ref e)) => {
outlk_autodiscover_text_cb(e, &mut outlk_ad, &reader)
}
Err(e) => {
error!(
context,
0,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
quick_xml::events::Event::End(ref e) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad);
current_tag = None;
}
quick_xml::events::Event::Text(ref e) => {
let val = e.unescape_and_decode(&reader).unwrap_or_default();
if !(!outlk_ad.config[5].is_null()
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
{
ok_to_continue = true;
break;
if let Some(ref tag) = current_tag {
match tag.as_str() {
"type" => {
outlk_ad.config_type = Some(val.trim().to_lowercase().to_string())
}
"server" => outlk_ad.config_server = val.trim().to_string(),
"port" => outlk_ad.config_port = val.trim().parse().unwrap_or_default(),
"ssl" => outlk_ad.config_ssl = val.trim().to_string(),
"redirecturl" => outlk_ad.config_redirecturl = Some(val.trim().to_string()),
_ => {}
};
}
}
quick_xml::events::Event::Eof => break,
_ => (),
}
free(url as *mut libc::c_void);
url = dc_strdup(outlk_ad.config[5usize]);
outlk_clean_config(&mut outlk_ad);
free(xml_raw as *mut libc::c_void);
xml_raw = ptr::null_mut();
i += 1;
buf.clear();
}
if ok_to_continue {
// XML redirect via redirecturl
let res = if outlk_ad.config_redirecturl.is_none()
|| outlk_ad.config_redirecturl.as_ref().unwrap().is_empty()
{
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = dc_loginparam_get_readable(&outlk_ad.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
return Err(Error::IncompleteAutoconfig(outlk_ad.out));
}
ParsingResult::LoginParam(outlk_ad.out)
} else {
ParsingResult::RedirectUrl(outlk_ad.config_redirecturl.unwrap())
};
Ok(res)
}
return None;
pub fn outlk_autodiscover(
context: &Context,
url: &str,
_param_in: &LoginParam,
) -> Result<LoginParam> {
let mut url = url.to_string();
/* Follow up to 10 xml-redirects (http-redirects are followed in read_url() */
for _i in 0..10 {
let xml_raw = read_url(context, &url)?;
let res = parse_xml(&xml_raw);
if let Err(err) = &res {
warn!(context, "{}", err);
}
match res? {
ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
ParsingResult::LoginParam(login_param) => return Ok(login_param),
}
}
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
Some(outlk_ad.out)
Err(Error::RedirectionError)
}
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
for i in 0..6 {
free((*outlk_ad).config[i] as *mut libc::c_void);
(*outlk_ad).config[i] = ptr::null_mut();
}
}
fn outlk_autodiscover_text_cb<B: std::io::BufRead>(
event: &BytesText,
outlk_ad: &mut outlk_autodiscover_t,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
unsafe {
free(outlk_ad.config[outlk_ad.tag_config as usize].cast());
outlk_ad.config[outlk_ad.tag_config as usize] = val.trim().strdup();
}
}
unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_autodiscover_t) {
fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut OutlookAutodiscover) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "protocol" {
if !outlk_ad.config[1].is_null() {
let port = dc_atoi_null_is_0(outlk_ad.config[3]);
let ssl_on = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"on\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
let ssl_off = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"off\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
if strcasecmp(
outlk_ad.config[1],
b"imap\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_imap_set == 0
{
outlk_ad.out.mail_server = to_string(outlk_ad.config[2]);
if let Some(type_) = &outlk_ad.config_type {
let port = outlk_ad.config_port;
let ssl_on = outlk_ad.config_ssl == "on";
let ssl_off = outlk_ad.config_ssl == "off";
if type_ == "imap" && !outlk_ad.out_imap_set {
outlk_ad.out.mail_server =
std::mem::replace(&mut outlk_ad.config_server, String::new());
outlk_ad.out.mail_port = port;
if 0 != ssl_on {
outlk_ad.out.server_flags |= 0x200
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= 0x400
if ssl_on {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
} else if ssl_off {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
}
outlk_ad.out_imap_set = 1
} else if strcasecmp(
outlk_ad.config[1usize],
b"smtp\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_smtp_set == 0
{
outlk_ad.out.send_server = to_string(outlk_ad.config[2]);
outlk_ad.out.send_port = port;
if 0 != ssl_on {
outlk_ad.out.server_flags |= 0x20000
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= 0x40000
outlk_ad.out_imap_set = true
} else if type_ == "smtp" && !outlk_ad.out_smtp_set {
outlk_ad.out.send_server =
std::mem::replace(&mut outlk_ad.config_server, String::new());
outlk_ad.out.send_port = outlk_ad.config_port;
if ssl_on {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
} else if ssl_off {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
}
outlk_ad.out_smtp_set = 1
outlk_ad.out_smtp_set = true
}
}
outlk_clean_config(outlk_ad);
}
outlk_ad.tag_config = 0;
}
fn outlk_autodiscover_starttag_cb(event: &BytesStart, outlk_ad: &mut outlk_autodiscover_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
#[cfg(test)]
mod tests {
use super::*;
if tag == "protocol" {
unsafe { outlk_clean_config(outlk_ad) };
} else if tag == "type" {
outlk_ad.tag_config = 1
} else if tag == "server" {
outlk_ad.tag_config = 2
} else if tag == "port" {
outlk_ad.tag_config = 3
} else if tag == "ssl" {
outlk_ad.tag_config = 4
} else if tag == "redirecturl" {
outlk_ad.tag_config = 5
};
#[test]
fn test_parse_redirect() {
let res = parse_xml("
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>redirectUrl</Action>
<RedirectUrl>https://mail.example.com/autodiscover/autodiscover.xml</RedirectUrl>
</Account>
</Response>
</Autodiscover>
").expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(_lp) => {
panic!("redirecturl is not found");
}
ParsingResult::RedirectUrl(url) => {
assert_eq!(
url,
"https://mail.example.com/autodiscover/autodiscover.xml"
);
}
}
}
#[test]
fn test_parse_loginparam() {
let res = parse_xml(
"\
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<Protocol>
<Type>IMAP</Type>
<Server>example.com</Server>
<Port>993</Port>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<Server>smtp.example.com</Server>
<Port>25</Port>
<SSL>off</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
</Account>
</Response>
</Autodiscover>",
)
.expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(lp) => {
assert_eq!(lp.mail_server, "example.com");
assert_eq!(lp.mail_port, 993);
assert_eq!(lp.send_server, "smtp.example.com");
assert_eq!(lp.send_port, 25);
}
ParsingResult::RedirectUrl(_) => {
panic!("RedirectUrl is not expected");
}
}
}
}

File diff suppressed because it is too large Load Diff

26
src/configure/read_url.rs Normal file
View File

@@ -0,0 +1,26 @@
use crate::context::Context;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "URL request error")]
GetError(#[cause] reqwest::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn read_url(context: &Context, url: &str) -> Result<String> {
info!(context, "Requesting URL {}", url);
match reqwest::blocking::Client::new()
.get(url)
.send()
.and_then(|res| res.text())
{
Ok(res) => Ok(res),
Err(err) => {
info!(context, "Can\'t read URL {}", url);
Err(Error::GetError(err))
}
}
}

View File

@@ -1,32 +1,35 @@
//! Constants
#![allow(non_camel_case_types, dead_code)]
use lazy_static::lazy_static;
//! # Constants
#![allow(dead_code)]
use deltachat_derive::*;
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
lazy_static! {
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
}
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum MoveState {
Undefined = 0,
Pending = 1,
Stay = 2,
Moving = 3,
}
// some defaults
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[derive(
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
FromPrimitive,
ToPrimitive,
FromSql,
ToSql,
Serialize,
Deserialize,
)]
#[repr(u8)]
pub enum Blocked {
Not = 0,
@@ -40,29 +43,51 @@ impl Default for Blocked {
}
}
pub const DC_IMAP_SEEN: u32 = 0x1;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(u8)]
pub enum ShowEmails {
Off = 0,
AcceptedContacts = 1,
All = 2,
}
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
impl Default for ShowEmails {
fn default() -> Self {
ShowEmails::Off // also change Config.ShowEmails props(default) on changes
}
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(u8)]
pub enum KeyGenType {
Default = 0,
Rsa2048 = 1,
Ed25519 = 2,
}
impl Default for KeyGenType {
fn default() -> Self {
KeyGenType::Default
}
}
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
pub(crate) const DC_FROM_HANDSHAKE: i32 = 0x01;
pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
const DC_GCM_ADDDAYMARKER: usize = 0x01;
pub const DC_GCM_ADDDAYMARKER: u32 = 0x01;
const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_ADD_SELF: usize = 0x02;
/// param1 is a directory where the keys are written to
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
/// param1 is a directory where the keys are searched in and read from
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
/// param1 is a directory where the backup is written to
const DC_IMEX_EXPORT_BACKUP: usize = 11;
/// param1 is the file with the backup to import
const DC_IMEX_IMPORT_BACKUP: usize = 12;
// unchanged user avatars are resent to the recipients every some days
pub const DC_RESEND_USER_AVATAR_DAYS: i64 = 14;
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
@@ -71,7 +96,7 @@ pub const DC_CHAT_ID_TRASH: u32 = 3;
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
const DC_CHAT_ID_MSGS_IN_CREATION: u32 = 4;
/// virtual chat showing all messages flagged with msgs.starred=2
const DC_CHAT_ID_STARRED: u32 = 5;
pub const DC_CHAT_ID_STARRED: u32 = 5;
/// only an indicator in a chatlist
pub const DC_CHAT_ID_ARCHIVED_LINK: u32 = 6;
/// only an indicator in a chatlist
@@ -91,6 +116,8 @@ pub const DC_CHAT_ID_LAST_SPECIAL: u32 = 9;
FromSql,
ToSql,
IntoStaticStr,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum Chattype {
@@ -107,20 +134,28 @@ impl Default for Chattype {
}
pub const DC_MSG_ID_MARKER1: u32 = 1;
const DC_MSG_ID_DAYMARKER: u32 = 9;
pub const DC_MSG_ID_DAYMARKER: u32 = 9;
pub const DC_MSG_ID_LAST_SPECIAL: u32 = 9;
/// approx. max. length returned by dc_msg_get_text()
const DC_MAX_GET_TEXT_LEN: usize = 30000;
/// approx. max. length returned by dc_get_msg_info()
const DC_MAX_GET_INFO_LEN: usize = 100000;
const DC_MAX_GET_INFO_LEN: usize = 100_000;
pub const DC_CONTACT_ID_UNDEFINED: u32 = 0;
pub const DC_CONTACT_ID_SELF: u32 = 1;
const DC_CONTACT_ID_DEVICE: u32 = 2;
pub const DC_CONTACT_ID_INFO: u32 = 2;
pub const DC_CONTACT_ID_DEVICE: u32 = 5;
pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9;
pub const DC_CREATE_MVBOX: usize = 1;
// decorative address that is used for DC_CONTACT_ID_DEVICE
// when an api that returns an email is called.
pub const DC_CONTACT_ID_DEVICE_ADDR: &str = "device@localhost";
// Flags for empty server job
pub const DC_EMPTY_MVBOX: u32 = 0x01;
pub const DC_EMPTY_INBOX: u32 = 0x02;
// Flags for configuring IMAP and SMTP servers.
// These flags are optional
@@ -130,23 +165,23 @@ pub const DC_CREATE_MVBOX: usize = 1;
/// Force OAuth2 authorization. This flag does not skip automatic configuration.
/// Before calling configure() with DC_LP_AUTH_OAUTH2 set,
/// the user has to confirm access at the URL returned by dc_get_oauth2_url().
pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
pub const DC_LP_AUTH_OAUTH2: i32 = 0x2;
/// Force NORMAL authorization, this is the default.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_AUTH_NORMAL: usize = 0x4;
pub const DC_LP_AUTH_NORMAL: i32 = 0x4;
/// Connect to IMAP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
pub const DC_LP_IMAP_SOCKET_STARTTLS: i32 = 0x100;
/// Connect to IMAP via SSL.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
pub const DC_LP_IMAP_SOCKET_SSL: i32 = 0x200;
/// Connect to IMAP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_PLAIN: usize = 0x400;
pub const DC_LP_IMAP_SOCKET_PLAIN: i32 = 0x400;
/// Connect to SMTP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
@@ -154,25 +189,48 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
/// Connect to SMTP via SSL.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
/// Connect to SMTP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
/// if none of these flags are set, the default is chosen
const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
pub const DC_LP_AUTH_FLAGS: i32 = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
/// if none of these flags are set, the default is chosen
const DC_LP_IMAP_SOCKET_FLAGS: usize =
pub const DC_LP_IMAP_SOCKET_FLAGS: i32 =
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
/// if none of these flags are set, the default is chosen
const DC_LP_SMTP_SOCKET_FLAGS: usize =
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
// QR code scanning (view from Bob, the joiner)
pub const DC_VC_AUTH_REQUIRED: i32 = 2;
pub const DC_VC_CONTACT_CONFIRM: i32 = 6;
pub const DC_BOB_ERROR: i32 = 0;
pub const DC_BOB_SUCCESS: i32 = 1;
// max. width/height of an avatar
pub const AVATAR_SIZE: u32 = 192;
#[derive(
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
FromPrimitive,
ToPrimitive,
FromSql,
ToSql,
Serialize,
Deserialize,
)]
#[repr(i32)]
pub enum Viewtype {
Unknown = 0,
/// Text message.
/// The text of the message is set using dc_msg_set_text()
/// and retrieved with dc_msg_get_text().
@@ -189,6 +247,11 @@ pub enum Viewtype {
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
Gif = 21,
/// Message containing a sticker, similar to image.
/// If possible, the ui should display the image without borders in a transparent way.
/// A click on a sticker will offer to install the sticker set in some future.
Sticker = 23,
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
@@ -214,6 +277,12 @@ pub enum Viewtype {
File = 60,
}
impl Default for Viewtype {
fn default() -> Self {
Viewtype::Unknown
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -229,255 +298,14 @@ mod tests {
// If you do not want to handle an event, it is always safe to return 0,
// so there is no need to add a "case" for every event.
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[repr(u32)]
pub enum Event {
/// The library-user may write an informational string to the log.
/// Passed to the callback given to dc_context_new().
/// This event should not be reported to the end-user using a popup or something like that.
/// @param data1 0
/// @param data2 (const char*) Info string in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
INFO = 100,
/// Emitted when SMTP connection is established and login was successful.
///
/// @param data1 0
/// @param data2 (const char*) Info string in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
SMTP_CONNECTED = 101,
/// Emitted when IMAP connection is established and login was successful.
///
/// @param data1 0
/// @param data2 (const char*) Info string in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
IMAP_CONNECTED = 102,
/// Emitted when a message was successfully sent to the SMTP server.
///
/// @param data1 0
/// @param data2 (const char*) Info string in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
SMTP_MESSAGE_SENT = 103,
/// The library-user should write a warning string to the log.
/// Passed to the callback given to dc_context_new().
///
/// This event should not be reported to the end-user using a popup or something like that.
///
/// @param data1 0
/// @param data2 (const char*) Warning string in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
WARNING = 300,
/// The library-user should report an error to the end-user.
/// Passed to the callback given to dc_context_new().
///
/// As most things are asynchronous, things may go wrong at any time and the user
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
///
/// However, for ongoing processes (eg. configure())
/// or for functions that are expected to fail (eg. dc_continue_key_transfer())
/// it might be better to delay showing these events until the function has really
/// failed (returned false). It should be sufficient to report only the _last_ error
/// in a messasge box then.
///
/// @param data1 0
/// @param data2 (const char*) Error string, always set, never NULL. Frequent error strings are
/// localized using #DC_EVENT_GET_STRING, however, most error strings will be in english language.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
ERROR = 400,
/// An action cannot be performed because there is no network available.
///
/// The library will typically try over after a some time
/// and when dc_maybe_network() is called.
///
/// Network errors should be reported to users in a non-disturbing way,
/// however, as network errors may come in a sequence,
/// it is not useful to raise each an every error to the user.
/// For this purpose, data1 is set to 1 if the error is probably worth reporting.
///
/// Moreover, if the UI detects that the device is offline,
/// it is probably more useful to report this to the user
/// instead of the string from data2.
///
/// @param data1 (int) 1=first/new network error, should be reported the user;
/// 0=subsequent network error, should be logged only
/// @param data2 (const char*) Error string, always set, never NULL.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @return 0
ERROR_NETWORK = 401,
/// An action cannot be performed because the user is not in the group.
/// Reported eg. after a call to
/// dc_set_chat_name(), dc_set_chat_profile_image(),
/// dc_add_contact_to_chat(), dc_remove_contact_from_chat(),
/// dc_send_text_msg() or another sending function.
///
/// @param data1 0
/// @param data2 (const char*) Info string in english language.
/// Must not be free()'d or modified
/// and is valid only until the callback returns.
/// @return 0
ERROR_SELF_NOT_IN_GROUP = 410,
/// Messages or chats changed. One or more messages or chats changed for various
/// reasons in the database:
/// - Messages sent, received or removed
/// - Chats created, deleted or archived
/// - A draft has been set
///
/// @param data1 (int) chat_id for single added messages
/// @param data2 (int) msg_id for single added messages
/// @return 0
MSGS_CHANGED = 2000,
/// There is a fresh message. Typically, the user will show an notification
/// when receiving this message.
///
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
///
/// @param data1 (int) chat_id
/// @param data2 (int) msg_id
/// @return 0
INCOMING_MSG = 2005,
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
/// DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
///
/// @param data1 (int) chat_id
/// @param data2 (int) msg_id
/// @return 0
MSG_DELIVERED = 2010,
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
/// DC_STATE_OUT_FAILED, see dc_msg_get_state().
///
/// @param data1 (int) chat_id
/// @param data2 (int) msg_id
/// @return 0
MSG_FAILED = 2012,
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
/// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state().
///
/// @param data1 (int) chat_id
/// @param data2 (int) msg_id
/// @return 0
MSG_READ = 2015,
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
/// Or the verify state of a chat has changed.
/// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat()
/// and dc_remove_contact_from_chat().
///
/// @param data1 (int) chat_id
/// @param data2 0
/// @return 0
CHAT_MODIFIED = 2020,
/// Contact(s) created, renamed, blocked or deleted.
///
/// @param data1 (int) If not 0, this is the contact_id of an added contact that should be selected.
/// @param data2 0
/// @return 0
CONTACTS_CHANGED = 2030,
/// Location of one or more contact has changed.
///
/// @param data1 (int) contact_id of the contact for which the location has changed.
/// If the locations of several contacts have been changed,
/// eg. after calling dc_delete_all_locations(), this parameter is set to 0.
/// @param data2 0
/// @return 0
LOCATION_CHANGED = 2035,
/// Inform about the configuration progress started by configure().
///
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
/// @return 0
CONFIGURE_PROGRESS = 2041,
/// Inform about the import/export progress started by dc_imex().
///
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
/// @return 0
IMEX_PROGRESS = 2051,
/// A file has been exported. A file has been written by dc_imex().
/// This event may be sent multiple times by a single call to dc_imex().
///
/// A typical purpose for a handler of this event may be to make the file public to some system
/// services.
///
/// @param data1 (const char*) Path and file name.
/// Must not be free()'d or modified and is valid only until the callback returns.
/// @param data2 0
/// @return 0
IMEX_FILE_WRITTEN = 2052,
/// Progress information of a secure-join handshake from the view of the inviter
/// (Alice, the person who shows the QR code).
///
/// These events are typically sent after a joiner has scanned the QR code
/// generated by dc_get_securejoin_qr().
///
/// @param data1 (int) ID of the contact that wants to join.
/// @param data2 (int) Progress as:
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
/// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
/// 1000=Protocol finished for this contact.
/// @return 0
SECUREJOIN_INVITER_PROGRESS = 2060,
/// Progress information of a secure-join handshake from the view of the joiner
/// (Bob, the person who scans the QR code).
/// The events are typically sent while dc_join_securejoin(), which
/// may take some time, is executed.
/// @param data1 (int) ID of the inviting contact.
/// @param data2 (int) Progress as:
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
/// (Bob has verified alice and waits until Alice does the same for him)
/// @return 0
SECUREJOIN_JOINER_PROGRESS = 2061,
// the following events are functions that should be provided by the frontends
/// Requeste a localized string from the frontend.
/// @param data1 (int) ID of the string to request, one of the DC_STR_/// constants.
/// @param data2 (int) The count. If the requested string contains a placeholder for a numeric value,
/// the ui may use this value to return different strings on different plural forms.
/// @return (const char*) Null-terminated UTF-8 string.
/// The string will be free()'d by the core,
/// so it must be allocated using malloc() or a compatible function.
/// Return 0 if the ui cannot provide the requested string
/// the core will use a default string in english language then.
GET_STRING = 2091,
}
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
/// Values for dc_get|set_config("show_emails")
const DC_SHOW_EMAILS_OFF: usize = 0;
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
const DC_SHOW_EMAILS_ALL: usize = 2;
// TODO: Strings need some doumentation about used placeholders.
// These constants are used to request strings using #DC_EVENT_GET_STRING.
// These constants are used to set stock translation strings
const DC_STR_NOMESSAGES: usize = 1;
const DC_STR_SELF: usize = 2;
@@ -523,7 +351,8 @@ const DC_STR_MSGACTIONBYME: usize = 63;
const DC_STR_MSGLOCATIONENABLED: usize = 64;
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
const DC_STR_LOCATION: usize = 66;
const DC_STR_COUNT: usize = 66;
const DC_STR_STICKER: usize = 67;
const DC_STR_COUNT: usize = 67;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
@@ -533,12 +362,3 @@ pub enum KeyType {
Public = 0,
Private = 1,
}
pub const DC_CMD_GROUPNAME_CHANGED: libc::c_int = 2;
pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,214 +0,0 @@
use std::borrow::Cow;
use crate::context::Context;
use crate::sql::Sql;
#[derive(Default, Debug)]
#[allow(non_camel_case_types)]
pub struct dc_loginparam_t {
pub addr: String,
pub mail_server: String,
pub mail_user: String,
pub mail_pw: String,
pub mail_port: i32,
pub send_server: String,
pub send_user: String,
pub send_pw: String,
pub send_port: i32,
pub server_flags: i32,
}
impl dc_loginparam_t {
pub fn addr_str(&self) -> &str {
self.addr.as_str()
}
}
pub fn dc_loginparam_new() -> dc_loginparam_t {
Default::default()
}
pub fn dc_loginparam_read(
context: &Context,
sql: &Sql,
prefix: impl AsRef<str>,
) -> dc_loginparam_t {
let prefix = prefix.as_ref();
let key = format!("{}addr", prefix);
let addr = sql
.get_config(context, key)
.unwrap_or_default()
.trim()
.to_string();
let key = format!("{}mail_server", prefix);
let mail_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_port", prefix);
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}mail_user", prefix);
let mail_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_pw", prefix);
let mail_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_server", prefix);
let send_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_port", prefix);
let send_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}send_user", prefix);
let send_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_pw", prefix);
let send_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}server_flags", prefix);
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
dc_loginparam_t {
addr: addr.to_string(),
mail_server,
mail_user,
mail_pw,
mail_port,
send_server,
send_user,
send_pw,
send_port,
server_flags,
}
}
pub fn dc_loginparam_write(
context: &Context,
loginparam: &dc_loginparam_t,
sql: &Sql,
prefix: impl AsRef<str>,
) {
let prefix = prefix.as_ref();
let key = format!("{}addr", prefix);
sql.set_config(context, key, Some(&loginparam.addr)).ok();
let key = format!("{}mail_server", prefix);
sql.set_config(context, key, Some(&loginparam.mail_server))
.ok();
let key = format!("{}mail_port", prefix);
sql.set_config_int(context, key, loginparam.mail_port).ok();
let key = format!("{}mail_user", prefix);
sql.set_config(context, key, Some(&loginparam.mail_user))
.ok();
let key = format!("{}mail_pw", prefix);
sql.set_config(context, key, Some(&loginparam.mail_pw)).ok();
let key = format!("{}send_server", prefix);
sql.set_config(context, key, Some(&loginparam.send_server))
.ok();
let key = format!("{}send_port", prefix);
sql.set_config_int(context, key, loginparam.send_port).ok();
let key = format!("{}send_user", prefix);
sql.set_config(context, key, Some(&loginparam.send_user))
.ok();
let key = format!("{}send_pw", prefix);
sql.set_config(context, key, Some(&loginparam.send_pw)).ok();
let key = format!("{}server_flags", prefix);
sql.set_config_int(context, key, loginparam.server_flags)
.ok();
}
fn unset_empty(s: &String) -> Cow<String> {
if s.is_empty() {
Cow::Owned("unset".to_string())
} else {
Cow::Borrowed(s)
}
}
pub fn dc_loginparam_get_readable(loginparam: &dc_loginparam_t) -> String {
let unset = "0";
let pw = "***";
let flags_readable = get_readable_flags(loginparam.server_flags);
format!(
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
unset_empty(&loginparam.addr),
unset_empty(&loginparam.mail_user),
if !loginparam.mail_pw.is_empty() {
pw
} else {
unset
},
unset_empty(&loginparam.mail_server),
loginparam.mail_port,
unset_empty(&loginparam.send_user),
if !loginparam.send_pw.is_empty() {
pw
} else {
unset
},
unset_empty(&loginparam.send_server),
loginparam.send_port,
flags_readable,
)
}
fn get_readable_flags(flags: i32) -> String {
let mut res = String::new();
for bit in 0..31 {
if 0 != flags & 1 << bit {
let mut flag_added = 0;
if 1 << bit == 0x2 {
res += "OAUTH2 ";
flag_added = 1;
}
if 1 << bit == 0x4 {
res += "AUTH_NORMAL ";
flag_added = 1;
}
if 1 << bit == 0x100 {
res += "IMAP_STARTTLS ";
flag_added = 1;
}
if 1 << bit == 0x200 {
res += "IMAP_SSL ";
flag_added = 1;
}
if 1 << bit == 0x400 {
res += "IMAP_PLAIN ";
flag_added = 1;
}
if 1 << bit == 0x10000 {
res += "SMTP_STARTTLS ";
flag_added = 1
}
if 1 << bit == 0x20000 {
res += "SMTP_SSL ";
flag_added = 1
}
if 1 << bit == 0x40000 {
res += "SMTP_PLAIN ";
flag_added = 1
}
if 0 == flag_added {
res += &format!("{:#0x}", 1 << bit);
}
}
}
if res.is_empty() {
res += "0";
}
res
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +0,0 @@
use crate::constants::*;
use crate::context::*;
use crate::job::*;
use crate::message::*;
use crate::param::Params;
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
if context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1)
== 0
{
return;
}
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
return;
}
if let Ok(msg) = dc_msg_new_load(context, msg_id) {
if dc_msg_is_setupmessage(&msg) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
return;
}
if dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
if 0 != msg.is_dc_message {
job_add(
context,
Action::MoveMsg,
msg.id as libc::c_int,
Params::new(),
0,
);
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Moving);
}
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More