@@ -10,25 +10,30 @@ use std::time::{Duration, Instant, SystemTime};
10
10
11
11
use anyhow:: { bail, ensure, Context as _, Result } ;
12
12
use async_channel:: { self as channel, Receiver , Sender } ;
13
+ use pgp:: SignedPublicKey ;
13
14
use ratelimit:: Ratelimit ;
14
15
use tokio:: sync:: { Mutex , Notify , RwLock } ;
15
16
16
- use crate :: chat:: { get_chat_cnt, ChatId } ;
17
+ use crate :: aheader:: EncryptPreference ;
18
+ use crate :: chat:: { get_chat_cnt, ChatId , ProtectionStatus } ;
17
19
use crate :: config:: Config ;
18
- use crate :: constants:: { DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT , DC_VERSION_STR } ;
20
+ use crate :: constants:: {
21
+ DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT , DC_CHAT_ID_TRASH , DC_VERSION_STR ,
22
+ } ;
19
23
use crate :: contact:: Contact ;
20
24
use crate :: debug_logging:: DebugLogging ;
21
25
use crate :: events:: { Event , EventEmitter , EventType , Events } ;
22
26
use crate :: imap:: { FolderMeaning , Imap , ServerMetadata } ;
23
- use crate :: key:: { load_self_public_key, DcKey as _} ;
27
+ use crate :: key:: { load_self_public_key, load_self_secret_key , DcKey as _} ;
24
28
use crate :: login_param:: LoginParam ;
25
- use crate :: message:: { self , MessageState , MsgId } ;
29
+ use crate :: message:: { self , Message , MessageState , MsgId , Viewtype } ;
30
+ use crate :: peerstate:: Peerstate ;
26
31
use crate :: quota:: QuotaInfo ;
27
32
use crate :: scheduler:: { convert_folder_meaning, SchedulerState } ;
28
33
use crate :: sql:: Sql ;
29
34
use crate :: stock_str:: StockStrings ;
30
35
use crate :: timesmearing:: SmearedTimestamp ;
31
- use crate :: tools:: { duration_to_str, time} ;
36
+ use crate :: tools:: { create_id , duration_to_str, time} ;
32
37
33
38
/// Builder for the [`Context`].
34
39
///
@@ -859,6 +864,91 @@ impl Context {
859
864
Ok ( res)
860
865
}
861
866
867
+ async fn get_self_report ( & self ) -> Result < String > {
868
+ let mut res = String :: new ( ) ;
869
+ res += & format ! ( "core_version {}\n " , get_version_str( ) ) ;
870
+
871
+ let num_msgs: u32 = self
872
+ . sql
873
+ . query_get_value (
874
+ "SELECT COUNT(*) FROM msgs WHERE hidden=0 AND chat_id!=?" ,
875
+ ( DC_CHAT_ID_TRASH , ) ,
876
+ )
877
+ . await ?
878
+ . unwrap_or_default ( ) ;
879
+ res += & format ! ( "num_msgs {}\n " , num_msgs) ;
880
+
881
+ let num_chats: u32 = self
882
+ . sql
883
+ . query_get_value ( "SELECT COUNT(*) FROM chats WHERE id>9 AND blocked!=1" , ( ) )
884
+ . await ?
885
+ . unwrap_or_default ( ) ;
886
+ res += & format ! ( "num_chats {}\n " , num_chats) ;
887
+
888
+ let db_size = tokio:: fs:: metadata ( & self . sql . dbfile ) . await ?. len ( ) ;
889
+ res += & format ! ( "db_size_bytes {}\n " , db_size) ;
890
+
891
+ let secret_key = & load_self_secret_key ( self ) . await ?. primary_key ;
892
+ let key_created = secret_key. created_at ( ) . timestamp ( ) ;
893
+ res += & format ! ( "key_created {}\n " , key_created) ;
894
+
895
+ let self_reporting_id = match self . get_config ( Config :: SelfReportingId ) . await ? {
896
+ Some ( id) => id,
897
+ None => {
898
+ let id = create_id ( ) ;
899
+ self . set_config ( Config :: SelfReportingId , Some ( & id) ) . await ?;
900
+ id
901
+ }
902
+ } ;
903
+ res += & format ! ( "self_reporting_id {}" , self_reporting_id) ;
904
+
905
+ Ok ( res)
906
+ }
907
+
908
+ /// Drafts a message with statistics about the usage of Delta Chat.
909
+ /// The user can inspect the message if they want, and then hit "Send".
910
+ ///
911
+ /// On the other end, a bot will receive the message and make it available
912
+ /// to Delta Chat's developers.
913
+ pub async fn draft_self_report ( & self ) -> Result < ChatId > {
914
+ const SELF_REPORTING_BOT : & str = "self_reporting@testrun.org" ;
915
+
916
+ let contact_id = Contact :: create ( self , "Statistics bot" , SELF_REPORTING_BOT ) . await ?;
917
+ let chat_id = ChatId :: create_for_contact ( self , contact_id) . await ?;
918
+
919
+ // We're including the bot's public key in Delta Chat
920
+ // so that the first message to the bot can directly be encrypted:
921
+ let public_key = SignedPublicKey :: from_base64 (
922
+ "xjMEZbfBlBYJKwYBBAHaRw8BAQdABpLWS2PUIGGo4pslVt4R8sylP5wZihmhf1DTDr3oCM\
923
+ PNHDxzZWxmX3JlcG9ydGluZ0B0ZXN0cnVuLm9yZz7CiwQQFggAMwIZAQUCZbfBlAIbAwQLCQgHBhUI\
924
+ CQoLAgMWAgEWIQTS2i16sHeYTckGn284K3M5Z4oohAAKCRA4K3M5Z4oohD8dAQCQV7CoH6UP4PD+Nq\
925
+ I4kW5tbbqdh2AnDROg60qotmLExAEAxDfd3QHAK9f8b9qQUbLmHIztCLxhEuVbWPBEYeVW0gvOOARl\
926
+ t8GUEgorBgEEAZdVAQUBAQdAMBUhYoAAcI625vGZqnM5maPX4sGJ7qvJxPAFILPy6AcDAQgHwngEGB\
927
+ YIACAFAmW3wZQCGwwWIQTS2i16sHeYTckGn284K3M5Z4oohAAKCRA4K3M5Z4oohPwCAQCvzk1ObIkj\
928
+ 2GqsuIfaULlgdnfdZY8LNary425CEfHZDQD5AblXVrlMO1frdlc/Vo9z3pEeCrfYdD7ITD3/OeVoiQ\
929
+ 4=",
930
+ ) ?;
931
+ let mut peerstate = Peerstate :: from_public_key (
932
+ SELF_REPORTING_BOT ,
933
+ 0 ,
934
+ EncryptPreference :: Mutual ,
935
+ & public_key,
936
+ ) ;
937
+ let fingerprint = public_key. fingerprint ( ) ;
938
+ peerstate. set_verified ( public_key, fingerprint, "" . to_string ( ) ) ?;
939
+ peerstate. save_to_db ( & self . sql ) . await ?;
940
+ chat_id
941
+ . set_protection ( self , ProtectionStatus :: Protected , time ( ) , Some ( contact_id) )
942
+ . await ?;
943
+
944
+ let mut msg = Message :: new ( Viewtype :: Text ) ;
945
+ msg. text = self . get_self_report ( ) . await ?;
946
+
947
+ chat_id. set_draft ( self , Some ( & mut msg) ) . await ?;
948
+
949
+ Ok ( chat_id)
950
+ }
951
+
862
952
/// Get a list of fresh, unmuted messages in unblocked chats.
863
953
///
864
954
/// The list starts with the most recent message
@@ -1129,8 +1219,9 @@ mod tests {
1129
1219
use crate :: constants:: Chattype ;
1130
1220
use crate :: contact:: ContactId ;
1131
1221
use crate :: message:: { Message , Viewtype } ;
1222
+ use crate :: mimeparser:: SystemMessage ;
1132
1223
use crate :: receive_imf:: receive_imf;
1133
- use crate :: test_utils:: TestContext ;
1224
+ use crate :: test_utils:: { get_chat_msg , TestContext } ;
1134
1225
use crate :: tools:: create_outgoing_rfc724_mid;
1135
1226
1136
1227
#[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
@@ -1369,6 +1460,7 @@ mod tests {
1369
1460
"mail_security" ,
1370
1461
"notify_about_wrong_pw" ,
1371
1462
"save_mime_headers" ,
1463
+ "self_reporting_id" ,
1372
1464
"selfstatus" ,
1373
1465
"send_server" ,
1374
1466
"send_user" ,
@@ -1669,4 +1761,24 @@ mod tests {
1669
1761
1670
1762
Ok ( ( ) )
1671
1763
}
1764
+
1765
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
1766
+ async fn test_draft_self_report ( ) -> Result < ( ) > {
1767
+ let alice = TestContext :: new_alice ( ) . await ;
1768
+
1769
+ let chat_id = alice. draft_self_report ( ) . await ?;
1770
+ let msg = get_chat_msg ( & alice, chat_id, 0 , 1 ) . await ;
1771
+ assert_eq ! ( msg. get_info_type( ) , SystemMessage :: ChatProtectionEnabled ) ;
1772
+
1773
+ let chat = Chat :: load_from_db ( & alice, chat_id) . await ?;
1774
+ assert ! ( chat. is_protected( ) ) ;
1775
+
1776
+ let mut draft = chat_id. get_draft ( & alice) . await ?. unwrap ( ) ;
1777
+ assert ! ( draft. text. starts_with( "core_version" ) ) ;
1778
+
1779
+ // Test that sending into the protected chat works:
1780
+ let _sent = alice. send_msg ( chat_id, & mut draft) . await ;
1781
+
1782
+ Ok ( ( ) )
1783
+ }
1672
1784
}
0 commit comments