mirror of
https://github.com/chatmail/core.git
synced 2026-04-04 22:42:11 +03:00
Compare commits
860 Commits
v1.125.0
...
link2xt/sq
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5820c4ce95 | ||
|
|
12ba33d9d4 | ||
|
|
60a7bbc9b5 | ||
|
|
e9f434b562 | ||
|
|
2423cb8175 | ||
|
|
65c9e72bf4 | ||
|
|
ea4d954c77 | ||
|
|
43523a96a2 | ||
|
|
2e2fa9e74f | ||
|
|
e43ffb20a1 | ||
|
|
2f0f247e70 | ||
|
|
5bda4f0c26 | ||
|
|
d39c8a3a19 | ||
|
|
e465415039 | ||
|
|
5cef77b8e6 | ||
|
|
60e733c30c | ||
|
|
8b98816eb9 | ||
|
|
50165b3e35 | ||
|
|
0be8b5a5c4 | ||
|
|
451bb6e9db | ||
|
|
83196d4cb5 | ||
|
|
0003e55ad5 | ||
|
|
02014eda6c | ||
|
|
f1c6cd69e9 | ||
|
|
ace281ff6c | ||
|
|
c9edd525e0 | ||
|
|
3f35b442c3 | ||
|
|
87e9365016 | ||
|
|
9806509f4a | ||
|
|
91600a34b6 | ||
|
|
d16351d207 | ||
|
|
4caf638201 | ||
|
|
375fcbd63c | ||
|
|
6ff3a2cf7c | ||
|
|
a890fe3a9a | ||
|
|
2b8bf29fce | ||
|
|
26400a9e4e | ||
|
|
f8b9bb9083 | ||
|
|
42f9047a54 | ||
|
|
6433a3a5f3 | ||
|
|
4b6a03c904 | ||
|
|
ff3df01d98 | ||
|
|
cdc99854b2 | ||
|
|
e7072bcb75 | ||
|
|
7950bde3c6 | ||
|
|
a259669c98 | ||
|
|
603e6be9b4 | ||
|
|
a78c484467 | ||
|
|
e78f07b343 | ||
|
|
8abf10aacb | ||
|
|
2fef4acdd6 | ||
|
|
de27be3a36 | ||
|
|
c62e8539a1 | ||
|
|
22c0aef9c0 | ||
|
|
87805bc36d | ||
|
|
99c4d24eab | ||
|
|
7bf9c4a2d9 | ||
|
|
304e902fce | ||
|
|
0155d93622 | ||
|
|
ebd097bdbe | ||
|
|
a11d01f8a3 | ||
|
|
38491b694b | ||
|
|
e702c1a8ca | ||
|
|
1adea3c678 | ||
|
|
9af812a3e7 | ||
|
|
36bdf8a67e | ||
|
|
20b30fc70a | ||
|
|
e59ff6ca74 | ||
|
|
0e5db36205 | ||
|
|
7960944b14 | ||
|
|
71c2383cbe | ||
|
|
5f5b272726 | ||
|
|
b34fe8f118 | ||
|
|
810be4f6c7 | ||
|
|
1ebbe26ebb | ||
|
|
0f5d5dd2b2 | ||
|
|
473dbe01af | ||
|
|
069ed7afa6 | ||
|
|
9313ece3cd | ||
|
|
900168c68c | ||
|
|
0bd137b4e5 | ||
|
|
75da205ff6 | ||
|
|
67e5fbbfe3 | ||
|
|
570daf42ec | ||
|
|
fcbbb91cde | ||
|
|
c3a7fc4c8d | ||
|
|
4b4c57a480 | ||
|
|
b95d58208c | ||
|
|
c468eb088e | ||
|
|
de37135ed6 | ||
|
|
33777d8759 | ||
|
|
8cc348bfa4 | ||
|
|
76bbd5fd72 | ||
|
|
eaed2381e7 | ||
|
|
6198ed0ef5 | ||
|
|
9f4af679a3 | ||
|
|
e158b889c9 | ||
|
|
9f7defa8da | ||
|
|
e9d7fe0561 | ||
|
|
7d7289bd51 | ||
|
|
ebdc52247c | ||
|
|
36bb4a7a32 | ||
|
|
c0832af634 | ||
|
|
b6db0152b0 | ||
|
|
bc7fd4495b | ||
|
|
e67e86422f | ||
|
|
2030de11d9 | ||
|
|
2c5a0cac5f | ||
|
|
251917e602 | ||
|
|
273719ae7c | ||
|
|
e639b58c6f | ||
|
|
5addfa8d1d | ||
|
|
02d68332c7 | ||
|
|
97abb9a0a9 | ||
|
|
d0e0cfafef | ||
|
|
f630b5fb39 | ||
|
|
d9bab938d5 | ||
|
|
215ec14b20 | ||
|
|
ea728e9b62 | ||
|
|
2af9ff1d01 | ||
|
|
7502234686 | ||
|
|
863a386d0f | ||
|
|
e4b49dfdef | ||
|
|
612aa1431e | ||
|
|
781d3abdb9 | ||
|
|
78d01933ad | ||
|
|
1a1467f7cf | ||
|
|
8d09291d1e | ||
|
|
4ccd2b8d02 | ||
|
|
794596ec69 | ||
|
|
3a787519b3 | ||
|
|
c03e163ed2 | ||
|
|
6cee295a5d | ||
|
|
f0be7daae9 | ||
|
|
0b279ec84e | ||
|
|
e919de78a3 | ||
|
|
6ea675a12f | ||
|
|
b970ebe67a | ||
|
|
3c4c701f9b | ||
|
|
01ac9c8b90 | ||
|
|
f6de23738d | ||
|
|
ddc2704278 | ||
|
|
3d2b164c05 | ||
|
|
2094bc3135 | ||
|
|
acff8205e2 | ||
|
|
255400028a | ||
|
|
d7615b223f | ||
|
|
00fbf540c4 | ||
|
|
288eccf722 | ||
|
|
99ee769580 | ||
|
|
345759d653 | ||
|
|
db0143f01a | ||
|
|
4da0c19766 | ||
|
|
08247a5d37 | ||
|
|
ceadd8928e | ||
|
|
c3d96814ca | ||
|
|
c2953623b9 | ||
|
|
1907d1859e | ||
|
|
a1970e998f | ||
|
|
1e9baefca0 | ||
|
|
e16322d99d | ||
|
|
ecfe3898c6 | ||
|
|
5499ca52bf | ||
|
|
4e8979f7c8 | ||
|
|
417db31098 | ||
|
|
cd9f6c3d5b | ||
|
|
07870a6d69 | ||
|
|
b08a4d6fcf | ||
|
|
b3a82b416f | ||
|
|
4e5d7fb821 | ||
|
|
1d73f97ef3 | ||
|
|
f5601e7683 | ||
|
|
0000c09ad3 | ||
|
|
a83884d7e9 | ||
|
|
9e00e8627f | ||
|
|
85c9622675 | ||
|
|
30432d8fa5 | ||
|
|
8b9f19be70 | ||
|
|
39c317e211 | ||
|
|
36ab7bdf47 | ||
|
|
f8f0ca08da | ||
|
|
2a0a05d03c | ||
|
|
7bc2f0cb6b | ||
|
|
4355bd77a9 | ||
|
|
f0091696c2 | ||
|
|
d2e86c5852 | ||
|
|
d4a505b52e | ||
|
|
08a30031eb | ||
|
|
44686d6caa | ||
|
|
9862d40f89 | ||
|
|
256c8c13f1 | ||
|
|
0b3a56c3c4 | ||
|
|
89024bbf37 | ||
|
|
cf16671d8d | ||
|
|
671feb68a4 | ||
|
|
ccd5158109 | ||
|
|
0a18e32d62 | ||
|
|
e9fadc0785 | ||
|
|
cfa13f0669 | ||
|
|
89e43c6678 | ||
|
|
8a67797cb1 | ||
|
|
b29bc19ef4 | ||
|
|
e765066f05 | ||
|
|
67aa785a9e | ||
|
|
c88c26426d | ||
|
|
b4e9a9764f | ||
|
|
06e79e8926 | ||
|
|
9427f7b587 | ||
|
|
bce22edfe3 | ||
|
|
656d4ed506 | ||
|
|
5e3fcafb3a | ||
|
|
660cfd4f01 | ||
|
|
7a1270f861 | ||
|
|
b35b893351 | ||
|
|
f45f9263db | ||
|
|
8289dc92ed | ||
|
|
862107c708 | ||
|
|
778660a8c9 | ||
|
|
6e55f0c6e3 | ||
|
|
3b0e740c17 | ||
|
|
2dd87b6b5e | ||
|
|
cdcacf2f83 | ||
|
|
51aaaf2e8d | ||
|
|
e6438f9981 | ||
|
|
9135cffaa4 | ||
|
|
73492ca4bc | ||
|
|
fe3c1f69c3 | ||
|
|
31ee3feb57 | ||
|
|
f4ed63c54c | ||
|
|
8f88cdd826 | ||
|
|
9933a4268f | ||
|
|
8a54c228fd | ||
|
|
b5f2c747e0 | ||
|
|
ba35e83db2 | ||
|
|
61a2c551fc | ||
|
|
20c91ba2fa | ||
|
|
969f8b916b | ||
|
|
b7b7a7e95d | ||
|
|
455b108a6c | ||
|
|
645ca7741b | ||
|
|
36643c551d | ||
|
|
0fcdee8857 | ||
|
|
26ae686687 | ||
|
|
b94bd9a659 | ||
|
|
f15e7d43e3 | ||
|
|
05c256dd5b | ||
|
|
37295f6967 | ||
|
|
dfdbb91f0a | ||
|
|
72f93dca7a | ||
|
|
ec2cf31cfa | ||
|
|
ecd4d2afe0 | ||
|
|
ec9d104cf3 | ||
|
|
11214c7d1f | ||
|
|
fba27ff884 | ||
|
|
f8907e3c83 | ||
|
|
f1688d2b3f | ||
|
|
693045b542 | ||
|
|
14dfb9abec | ||
|
|
c8ed3ed73b | ||
|
|
bce5203eeb | ||
|
|
74c0c2cc38 | ||
|
|
4f25072352 | ||
|
|
91c3a39134 | ||
|
|
ae94b2a7b3 | ||
|
|
3b013a1017 | ||
|
|
80aab220b6 | ||
|
|
34c3e44b9d | ||
|
|
78d304443a | ||
|
|
d6c24eb9f6 | ||
|
|
f7fd1ef2bf | ||
|
|
af7bf5bd2b | ||
|
|
ea666f1098 | ||
|
|
5bb80f94c7 | ||
|
|
2f29c56a36 | ||
|
|
de86b8a96e | ||
|
|
060c9c8aa1 | ||
|
|
727428a965 | ||
|
|
df455bbcf5 | ||
|
|
946eea4c9e | ||
|
|
5cbc87369e | ||
|
|
5cdd5e0564 | ||
|
|
f493d6bb40 | ||
|
|
8e073b9c3e | ||
|
|
ea2a692d18 | ||
|
|
1b7c5be9c5 | ||
|
|
f7903df805 | ||
|
|
d2c61dc90e | ||
|
|
7b68098785 | ||
|
|
48f2ea717e | ||
|
|
cb3f03fd39 | ||
|
|
06f1fe18d6 | ||
|
|
1dbf924c6a | ||
|
|
3f6814f421 | ||
|
|
782828ac4f | ||
|
|
bd3759d55e | ||
|
|
672993e69e | ||
|
|
987bdaf237 | ||
|
|
7cf382a3b8 | ||
|
|
19dce9ddfa | ||
|
|
0afc0dd65a | ||
|
|
73d612a07d | ||
|
|
3b1529ef81 | ||
|
|
15187c0adb | ||
|
|
c5f31c3d03 | ||
|
|
2c17e78347 | ||
|
|
4ee646ce0b | ||
|
|
1f7b4a74fa | ||
|
|
4bc90701cc | ||
|
|
490deb9347 | ||
|
|
28d9484a13 | ||
|
|
e67e684ee0 | ||
|
|
6cfe3e6a97 | ||
|
|
99ac524905 | ||
|
|
2faf7fdb78 | ||
|
|
6a8ea8a083 | ||
|
|
e0e56cd831 | ||
|
|
bbc6febb72 | ||
|
|
7f7f42d721 | ||
|
|
589236c27b | ||
|
|
c16c5e0802 | ||
|
|
36cab40ac1 | ||
|
|
4186d78305 | ||
|
|
06cccb77f8 | ||
|
|
1895f4c556 | ||
|
|
849a873e61 | ||
|
|
b5c0372c99 | ||
|
|
1ba9b69849 | ||
|
|
6345a4f5b3 | ||
|
|
382fc75b1e | ||
|
|
92fc9ea971 | ||
|
|
de7ac2a240 | ||
|
|
7b0e5adaee | ||
|
|
406b59501b | ||
|
|
d5da2bed75 | ||
|
|
924d5b9377 | ||
|
|
bb47299ee4 | ||
|
|
20065d3daa | ||
|
|
ccb267beab | ||
|
|
32bcb59601 | ||
|
|
c708c44f0a | ||
|
|
9415a71f9d | ||
|
|
1fd42f2c53 | ||
|
|
1e52502ab3 | ||
|
|
a144d7e4f3 | ||
|
|
e855b79f9c | ||
|
|
2f8a8f9f50 | ||
|
|
b9a58bf625 | ||
|
|
c8075e53d2 | ||
|
|
ff54cf24a1 | ||
|
|
af0833e821 | ||
|
|
da11542322 | ||
|
|
3bcdd1770a | ||
|
|
4dc596e646 | ||
|
|
2e69210825 | ||
|
|
625887d249 | ||
|
|
b7c34b7794 | ||
|
|
941cf38a3e | ||
|
|
7f61896ec8 | ||
|
|
b14b49cbf0 | ||
|
|
6de3510a5d | ||
|
|
dea519095c | ||
|
|
3f8ca0cee9 | ||
|
|
1b998da57a | ||
|
|
772747d42d | ||
|
|
3998258afb | ||
|
|
4e86de98c4 | ||
|
|
2a497989e9 | ||
|
|
361b19e455 | ||
|
|
c036b26ae5 | ||
|
|
dcf6ffef12 | ||
|
|
865ede39fe | ||
|
|
a27e84ad89 | ||
|
|
b83bd26325 | ||
|
|
44227d7b86 | ||
|
|
6bcf022523 | ||
|
|
ccec26ffa7 | ||
|
|
83e159e42f | ||
|
|
cbabd4219e | ||
|
|
548afe3153 | ||
|
|
35c5f42b35 | ||
|
|
b9ff8b1d6c | ||
|
|
bb6a20dc11 | ||
|
|
e97955f5a0 | ||
|
|
35bd56ffea | ||
|
|
78affb766e | ||
|
|
9b1704e3b2 | ||
|
|
55cdbdc085 | ||
|
|
58620988d7 | ||
|
|
467f313091 | ||
|
|
091578573a | ||
|
|
62c1237024 | ||
|
|
8d41d02397 | ||
|
|
fce3f80654 | ||
|
|
2a0a51bea0 | ||
|
|
91d94d5920 | ||
|
|
c59f21230d | ||
|
|
828cc1fbd1 | ||
|
|
57f4958fc6 | ||
|
|
3aeb57b4df | ||
|
|
1b85614db9 | ||
|
|
57ecf49eb1 | ||
|
|
f279b0d1e5 | ||
|
|
32071297e6 | ||
|
|
1d98c38ff3 | ||
|
|
c09e0e2b65 | ||
|
|
0c8f967391 | ||
|
|
aca34379e0 | ||
|
|
1edd7045be | ||
|
|
c784c499c2 | ||
|
|
36c751bcc3 | ||
|
|
8a14a84bec | ||
|
|
b00703cec2 | ||
|
|
05e783564f | ||
|
|
330fb02486 | ||
|
|
1447ab8dac | ||
|
|
d574ee4edb | ||
|
|
814fe953a9 | ||
|
|
280f13b8cf | ||
|
|
a96b44a482 | ||
|
|
4286d248e9 | ||
|
|
116537019b | ||
|
|
8b37b8c1fd | ||
|
|
63b4339ca0 | ||
|
|
fdd239f61f | ||
|
|
5ca5d95c5e | ||
|
|
3fcad50924 | ||
|
|
8e40540d24 | ||
|
|
04d22bb84d | ||
|
|
5415f1bfa1 | ||
|
|
ff3bf4791a | ||
|
|
eebea216cb | ||
|
|
fbcd7f46b8 | ||
|
|
846278b18e | ||
|
|
2f2b1e18bf | ||
|
|
073c250fa4 | ||
|
|
1f336f89a6 | ||
|
|
a47fec7f6c | ||
|
|
084434d3b4 | ||
|
|
ebfbc11973 | ||
|
|
9cc9579b2d | ||
|
|
7beccd9dbc | ||
|
|
0e195bc7a2 | ||
|
|
f89efd5fce | ||
|
|
48d278fca9 | ||
|
|
c84effdaa1 | ||
|
|
e9601ef138 | ||
|
|
44c5cd5526 | ||
|
|
1c9662a8f2 | ||
|
|
5d08b2ce33 | ||
|
|
bb9d7d7ef3 | ||
|
|
766bb5c8aa | ||
|
|
84144659cf | ||
|
|
1394137436 | ||
|
|
998614b923 | ||
|
|
5b346397b8 | ||
|
|
1f99269002 | ||
|
|
160cbe8125 | ||
|
|
b9fa05c3bb | ||
|
|
4287a4d3ad | ||
|
|
37d2aafb26 | ||
|
|
4332170691 | ||
|
|
9a7c0f4737 | ||
|
|
9e7e172a7b | ||
|
|
71fbaf572a | ||
|
|
2ab29e5bfa | ||
|
|
85f8f910b9 | ||
|
|
b779d08d7f | ||
|
|
3b5634f14b | ||
|
|
f91ba357cf | ||
|
|
616faff96b | ||
|
|
5e6869403e | ||
|
|
7ff7d82959 | ||
|
|
9b751c1865 | ||
|
|
d1d31096e0 | ||
|
|
30f8522626 | ||
|
|
d3c221e061 | ||
|
|
8a421224f8 | ||
|
|
7dfce71ac9 | ||
|
|
35ba97f76a | ||
|
|
41921eaf3d | ||
|
|
03221ea86c | ||
|
|
b50761e4d1 | ||
|
|
40dea771cc | ||
|
|
e011f8f42f | ||
|
|
09d4b4354a | ||
|
|
ab151654fb | ||
|
|
ea9556b1b9 | ||
|
|
3dc6fd5c10 | ||
|
|
f39acbc037 | ||
|
|
005f7ff07e | ||
|
|
144ca7c171 | ||
|
|
7012b99d73 | ||
|
|
72bacd56f7 | ||
|
|
cc75038ccc | ||
|
|
f4810125e3 | ||
|
|
acf1faf151 | ||
|
|
255fbe94f7 | ||
|
|
b5d1eba28e | ||
|
|
1509978738 | ||
|
|
607b9e55a9 | ||
|
|
7c4c980409 | ||
|
|
b8ad3ec1b1 | ||
|
|
87dd33f66e | ||
|
|
7d8d13759a | ||
|
|
2b4f2a9171 | ||
|
|
8e869de350 | ||
|
|
b0ef082b2a | ||
|
|
bf8e74198d | ||
|
|
e77805471c | ||
|
|
45a8004b33 | ||
|
|
990f4dce9b | ||
|
|
0f36197c54 | ||
|
|
890a2bcc15 | ||
|
|
224355e83a | ||
|
|
c6ea4e389a | ||
|
|
678142b3fb | ||
|
|
ae6f83cd21 | ||
|
|
626b2be1fe | ||
|
|
ac5c789c75 | ||
|
|
ce2878f1e8 | ||
|
|
d4162899b4 | ||
|
|
e900d50e38 | ||
|
|
a438a4746a | ||
|
|
cfb819506f | ||
|
|
b86b915f40 | ||
|
|
ad5a5ad3db | ||
|
|
74081d8a36 | ||
|
|
34a434f07c | ||
|
|
dc944d8ca7 | ||
|
|
b06a7e7197 | ||
|
|
fa61d90115 | ||
|
|
7977c9ab44 | ||
|
|
6273a7d54e | ||
|
|
4d1a9c2aa1 | ||
|
|
b26ded423b | ||
|
|
e4b6eba5d7 | ||
|
|
bc225024a1 | ||
|
|
e616ecf160 | ||
|
|
f93562c6bf | ||
|
|
ac39c3699b | ||
|
|
091bc1ab13 | ||
|
|
fcbb66a788 | ||
|
|
ab2bc3bfb2 | ||
|
|
42dd6f9d08 | ||
|
|
465bcd46f8 | ||
|
|
cc88a6cb58 | ||
|
|
ba8f1bfcfd | ||
|
|
d4d6ced957 | ||
|
|
0b664e75cb | ||
|
|
1a4c2953f7 | ||
|
|
765c95de39 | ||
|
|
b2ea8f54df | ||
|
|
d7aecabcaa | ||
|
|
9ca049051c | ||
|
|
790509676f | ||
|
|
ce016eb567 | ||
|
|
57e34abe98 | ||
|
|
fd92b7c455 | ||
|
|
0ee68d1dfc | ||
|
|
1856c622a1 | ||
|
|
0a48a2effa | ||
|
|
543864f0f5 | ||
|
|
0fe94e47cc | ||
|
|
fc09210aea | ||
|
|
3e194969c0 | ||
|
|
391cffb454 | ||
|
|
47486f8bab | ||
|
|
620e363ce6 | ||
|
|
0c2276775d | ||
|
|
ad51a7cd85 | ||
|
|
28952789a4 | ||
|
|
14adcdb517 | ||
|
|
cc80590488 | ||
|
|
48416289ac | ||
|
|
003a27f625 | ||
|
|
bff4a2259f | ||
|
|
9adf856705 | ||
|
|
2215de5285 | ||
|
|
013467d6c6 | ||
|
|
1f52b8af2f | ||
|
|
ce32f76265 | ||
|
|
836f65376c | ||
|
|
99940dd28c | ||
|
|
ffeb801b58 | ||
|
|
339bbcf070 | ||
|
|
7b83bddc2d | ||
|
|
5549733a0b | ||
|
|
4e21917c0e | ||
|
|
939b4b2aab | ||
|
|
fd0770859d | ||
|
|
d5854fb3c9 | ||
|
|
3aa22a27cc | ||
|
|
10b1a2f5f5 | ||
|
|
a28a34773c | ||
|
|
7c744d14d7 | ||
|
|
6c68f2eb7e | ||
|
|
eb2d2b7313 | ||
|
|
2e50abedaa | ||
|
|
ee53136ed2 | ||
|
|
e923983dca | ||
|
|
f4753862f1 | ||
|
|
16b40f3a19 | ||
|
|
9cd3a7550b | ||
|
|
d840a7e6b9 | ||
|
|
d875691955 | ||
|
|
c600bfa8ca | ||
|
|
973ffa1a64 | ||
|
|
caffc3d93c | ||
|
|
a4dcf656f3 | ||
|
|
7bb5d48966 | ||
|
|
71b7b0b393 | ||
|
|
bd02eea66b | ||
|
|
cdcb10fb58 | ||
|
|
6cd7296001 | ||
|
|
168021523f | ||
|
|
03f2635296 | ||
|
|
e3b08fa92b | ||
|
|
79cebe66de | ||
|
|
0619e2a129 | ||
|
|
64a81e4f61 | ||
|
|
0431ae53ca | ||
|
|
89c873acd0 | ||
|
|
2e70cf9388 | ||
|
|
2efd0461d1 | ||
|
|
196a34684d | ||
|
|
402fd6850c | ||
|
|
72515f440d | ||
|
|
045d919cdc | ||
|
|
9b9108320e | ||
|
|
5efb100f12 | ||
|
|
b747dd6ae8 | ||
|
|
ed2bc9e44d | ||
|
|
9e1a2149fa | ||
|
|
22b6d8c17b | ||
|
|
3876846410 | ||
|
|
a93c79e001 | ||
|
|
6aae0276da | ||
|
|
1d80659bc3 | ||
|
|
94d5e86d4f | ||
|
|
aecf7729d8 | ||
|
|
f130d537b7 | ||
|
|
f30f862e7e | ||
|
|
81e1164358 | ||
|
|
542bd4cbb8 | ||
|
|
771b57778e | ||
|
|
9be56a5e56 | ||
|
|
da744958c2 | ||
|
|
f6bda1e480 | ||
|
|
d2e24534c7 | ||
|
|
df6f974eca | ||
|
|
2f5c6b5e16 | ||
|
|
97176b13f1 | ||
|
|
18bb7e58be | ||
|
|
c2bab44bdd | ||
|
|
53bb8a9831 | ||
|
|
1b66120e7d | ||
|
|
1478f321ae | ||
|
|
5fb92c78ad | ||
|
|
a0a792b821 | ||
|
|
3feb0e648d | ||
|
|
fa5358a5bf | ||
|
|
7399a398a7 | ||
|
|
25a78aceb9 | ||
|
|
66708454dd | ||
|
|
bb5e3d11d8 | ||
|
|
ff54db2e5f | ||
|
|
434d8fc35f | ||
|
|
12eb813bc3 | ||
|
|
88d5576150 | ||
|
|
af35e4adeb | ||
|
|
eaeacb8848 | ||
|
|
f00e68e142 | ||
|
|
113356a24e | ||
|
|
b89c134e7f | ||
|
|
3748794048 | ||
|
|
ccca12176e | ||
|
|
c89dd331f7 | ||
|
|
fa81ed5f39 | ||
|
|
6c34f6b8d9 | ||
|
|
4f21a5691d | ||
|
|
b2a839971b | ||
|
|
8ad99be322 | ||
|
|
4e771e8727 | ||
|
|
e725bdfb2b | ||
|
|
ac557f73b3 | ||
|
|
0ba3501a46 | ||
|
|
b1fe12881e | ||
|
|
c1eb33c0da | ||
|
|
03bb92c942 | ||
|
|
b0da5a54cc | ||
|
|
44e056e210 | ||
|
|
48a8680ba4 | ||
|
|
b73bcc2c22 | ||
|
|
cff42936aa | ||
|
|
d3b04004b4 | ||
|
|
5cd92f10ef | ||
|
|
22a3ab983b | ||
|
|
83d2e6b8b4 | ||
|
|
4e979c5880 | ||
|
|
71e1089139 | ||
|
|
349c154a99 | ||
|
|
54410dbe49 | ||
|
|
4e08bb7b05 | ||
|
|
934ca6a7d7 | ||
|
|
e878caebe3 | ||
|
|
088eda2983 | ||
|
|
418cd24979 | ||
|
|
b4fe9e3eec | ||
|
|
c13bbd05cd | ||
|
|
58330fe8b2 | ||
|
|
680d024b05 | ||
|
|
defcd5764b | ||
|
|
e87f785a0a | ||
|
|
0227bbc305 | ||
|
|
d05afec289 | ||
|
|
64035d3ecb | ||
|
|
21e0bb28ad | ||
|
|
c6358169ad | ||
|
|
955f4fbb19 | ||
|
|
df7c44ae42 | ||
|
|
8573649bf7 | ||
|
|
52c46c6dca | ||
|
|
54ea3ec5d6 | ||
|
|
1632035784 | ||
|
|
b239535964 | ||
|
|
0751cc50b9 | ||
|
|
da5d844ec4 | ||
|
|
2775fd1fcf | ||
|
|
37383c10ac | ||
|
|
a119b24eeb | ||
|
|
275791595c | ||
|
|
f3fb0dc5fe | ||
|
|
24ea90bd68 | ||
|
|
ef3e94c7e1 | ||
|
|
883832f78d | ||
|
|
1f03267273 | ||
|
|
8bc2ce1c30 | ||
|
|
2ae92c06e3 | ||
|
|
4a6a214f3c | ||
|
|
c2d7011aa7 | ||
|
|
c0195ab23f | ||
|
|
e4e50d0e81 | ||
|
|
573746ce54 | ||
|
|
6b2df13cdb | ||
|
|
3166b44580 | ||
|
|
e500485c21 | ||
|
|
59e5a63d5f | ||
|
|
5daa6274e8 | ||
|
|
d109a1b27f | ||
|
|
c41687586c | ||
|
|
59a3bc0ff4 | ||
|
|
a42a6ca18c | ||
|
|
061d091c97 | ||
|
|
e7617f0abd | ||
|
|
790e867af0 | ||
|
|
4a0585404a | ||
|
|
dcbf5996c2 | ||
|
|
f7a47e60cd | ||
|
|
2587ebbacd | ||
|
|
4815e9b990 | ||
|
|
f05b0ddf04 | ||
|
|
f94d34c94b | ||
|
|
e9811fb6da | ||
|
|
178fc1736d | ||
|
|
54d632adaf | ||
|
|
ae939e79da | ||
|
|
e12ef805a9 | ||
|
|
b36acb2dc0 | ||
|
|
1b883ae3fa | ||
|
|
72c94e1037 | ||
|
|
0a4c993bb8 | ||
|
|
ec56134583 | ||
|
|
d8bf1c1691 | ||
|
|
8ac1754e18 | ||
|
|
e1f1143919 | ||
|
|
d9e38289c4 | ||
|
|
486050d0b8 | ||
|
|
828c90ac3d | ||
|
|
ab09ecce7e | ||
|
|
aebad2eb10 | ||
|
|
2a39a85d9e | ||
|
|
cb0270baa7 | ||
|
|
99302c9598 | ||
|
|
88ae653760 | ||
|
|
6881f9d70f | ||
|
|
60ddbe5729 | ||
|
|
5e5e557c8b | ||
|
|
bc8023644c | ||
|
|
83ef25e7de | ||
|
|
9a7d1faf75 | ||
|
|
e59c4ee858 | ||
|
|
a520f0268f | ||
|
|
5e3b1fa540 | ||
|
|
95f29f7b63 | ||
|
|
20513475ef | ||
|
|
3b806320ec | ||
|
|
c857d6e1bd | ||
|
|
94cd9a713f | ||
|
|
7676473ebd | ||
|
|
8c778b3f5c | ||
|
|
a66f8bd9fc | ||
|
|
95b2a15930 | ||
|
|
0179ec2da9 | ||
|
|
8f2313bb2a | ||
|
|
488a3d1118 | ||
|
|
9094df7bc7 | ||
|
|
16aad3fa67 | ||
|
|
3b47c3f21d | ||
|
|
987ce58926 | ||
|
|
20c88743df | ||
|
|
03395b95cb | ||
|
|
53f04a134a | ||
|
|
885f26ea8c | ||
|
|
3ab181fdf8 | ||
|
|
e12044e6af | ||
|
|
954067eb6d | ||
|
|
aecbebd566 | ||
|
|
3111bcde5e | ||
|
|
e6cffd537e | ||
|
|
70000d9ebb | ||
|
|
d95843b0bf | ||
|
|
13e766bc37 | ||
|
|
c34edc582e | ||
|
|
8eee389c09 | ||
|
|
8ed6d4d709 | ||
|
|
60bacbec47 | ||
|
|
af013559de | ||
|
|
b784415c57 | ||
|
|
85739ba6ad | ||
|
|
a02a593f47 | ||
|
|
67f28f501a | ||
|
|
9b9703a48e | ||
|
|
c55a3d3873 | ||
|
|
f27d304f3b | ||
|
|
6d51d19f01 | ||
|
|
170968dfc2 | ||
|
|
f930576fd1 | ||
|
|
d797de7a8d | ||
|
|
acc7bb00c5 | ||
|
|
8fb8a877be | ||
|
|
b96028cd87 | ||
|
|
682e241edb | ||
|
|
3a63628f1f | ||
|
|
3705616cd9 | ||
|
|
b8fcb660ad | ||
|
|
5673294623 | ||
|
|
7062bb0502 | ||
|
|
659cffe0cc | ||
|
|
a1663a98e0 | ||
|
|
3de1dbc9e4 | ||
|
|
6d37e8601e | ||
|
|
d762753103 | ||
|
|
a020d5ccce | ||
|
|
1e28ea9bb0 | ||
|
|
17f2d33731 | ||
|
|
976797d4cf | ||
|
|
31e3169433 | ||
|
|
d2b15cb629 | ||
|
|
9cd000c4f2 | ||
|
|
243c035b03 |
104
.github/workflows/ci.yml
vendored
104
.github/workflows/ci.yml
vendored
@@ -14,8 +14,7 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable
|
||||
- main
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
@@ -25,9 +24,11 @@ jobs:
|
||||
name: Lint Rust
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: 1.73.0
|
||||
RUSTUP_TOOLCHAIN: 1.77.1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Install rustfmt and clippy
|
||||
run: rustup toolchain install $RUSTUP_TOOLCHAIN --profile minimal --component rustfmt --component clippy
|
||||
- name: Cache rust cargo artifacts
|
||||
@@ -39,15 +40,13 @@ jobs:
|
||||
- name: Check
|
||||
run: cargo check --workspace --all-targets --all-features
|
||||
|
||||
# Check with musl libc target which is used for `deltachat-rpc-server` releases.
|
||||
- name: Check musl
|
||||
run: scripts/zig-musl-check.sh
|
||||
|
||||
cargo_deny:
|
||||
name: cargo deny
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
arguments: --all-features --workspace
|
||||
@@ -58,7 +57,9 @@ jobs:
|
||||
name: Check provider database
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Check provider database
|
||||
run: scripts/update-provider-database.sh
|
||||
|
||||
@@ -68,8 +69,9 @@ jobs:
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: Rustdoc
|
||||
@@ -81,22 +83,20 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
rust: 1.73.0
|
||||
rust: 1.77.1
|
||||
- os: windows-latest
|
||||
rust: 1.73.0
|
||||
rust: 1.77.1
|
||||
- os: macos-latest
|
||||
rust: 1.73.0
|
||||
rust: 1.77.1
|
||||
|
||||
# Minimum Supported Rust Version = 1.65.0
|
||||
#
|
||||
# Minimum Supported Python Version = 3.7
|
||||
# This is the minimum version for which manylinux Python wheels are
|
||||
# built.
|
||||
# Minimum Supported Rust Version = 1.77.0
|
||||
- os: ubuntu-latest
|
||||
rust: 1.65.0
|
||||
rust: 1.77.0
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
run: rustup toolchain install --profile minimal ${{ matrix.rust }}
|
||||
@@ -106,6 +106,8 @@ jobs:
|
||||
uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Tests
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Test cargo vendor
|
||||
@@ -118,7 +120,9 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
@@ -127,7 +131,7 @@ jobs:
|
||||
run: cargo build -p deltachat_ffi --features jsonrpc
|
||||
|
||||
- name: Upload C library
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-libdeltachat.a
|
||||
path: target/debug/libdeltachat.a
|
||||
@@ -137,10 +141,12 @@ jobs:
|
||||
name: Build deltachat-rpc-server
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
@@ -149,18 +155,19 @@ jobs:
|
||||
run: cargo build -p deltachat-rpc-server
|
||||
|
||||
- name: Upload deltachat-rpc-server
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-deltachat-rpc-server
|
||||
path: target/debug/deltachat-rpc-server
|
||||
path: ${{ matrix.os == 'windows-latest' && 'target/debug/deltachat-rpc-server.exe' || 'target/debug/deltachat-rpc-server' }}
|
||||
retention-days: 1
|
||||
|
||||
python_lint:
|
||||
name: Python lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Install tox
|
||||
run: pip install tox
|
||||
@@ -173,8 +180,8 @@ jobs:
|
||||
working-directory: deltachat-rpc-client
|
||||
run: tox -e lint
|
||||
|
||||
python_tests:
|
||||
name: Python tests
|
||||
cffi_python_tests:
|
||||
name: CFFI Python tests
|
||||
needs: ["c_library", "python_lint"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -200,16 +207,18 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Download libdeltachat.a
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-libdeltachat.a
|
||||
path: target/debug
|
||||
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
@@ -218,14 +227,14 @@ jobs:
|
||||
|
||||
- name: Run python tests
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
run: tox -e mypy,doc,py
|
||||
|
||||
aysnc_python_tests:
|
||||
name: Async Python tests
|
||||
rpc_python_tests:
|
||||
name: JSON-RPC Python tests
|
||||
needs: ["python_lint", "rpc_server"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -235,6 +244,8 @@ jobs:
|
||||
python: 3.12
|
||||
- os: macos-latest
|
||||
python: 3.12
|
||||
- os: windows-latest
|
||||
python: 3.12
|
||||
|
||||
# PyPy tests
|
||||
- os: ubuntu-latest
|
||||
@@ -248,10 +259,12 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
@@ -259,19 +272,26 @@ jobs:
|
||||
run: pip install tox
|
||||
|
||||
- name: Download deltachat-rpc-server
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-deltachat-rpc-server
|
||||
path: target/debug
|
||||
|
||||
- name: Make deltachat-rpc-server executable
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: chmod +x target/debug/deltachat-rpc-server
|
||||
|
||||
- name: Add deltachat-rpc-server to path
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: echo ${{ github.workspace }}/target/debug >> $GITHUB_PATH
|
||||
|
||||
- name: Add deltachat-rpc-server to path
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: |
|
||||
"${{ github.workspace }}/target/debug" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
|
||||
- name: Run deltachat-rpc-client tests
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
working-directory: deltachat-rpc-client
|
||||
run: tox -e py
|
||||
|
||||
282
.github/workflows/deltachat-rpc-server.yml
vendored
282
.github/workflows/deltachat-rpc-server.yml
vendored
@@ -21,118 +21,248 @@ jobs:
|
||||
# Build a version statically linked against musl libc
|
||||
# to avoid problems with glibc version incompatibility.
|
||||
build_linux:
|
||||
name: Cross-compile deltachat-rpc-server for x86_64, i686, aarch64 and armv7 Linux
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: sh scripts/zig-rpc-server.sh
|
||||
|
||||
- name: Upload x86_64 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64
|
||||
path: target/x86_64-unknown-linux-musl/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload i686 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-i686
|
||||
path: target/i686-unknown-linux-musl/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload aarch64 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-aarch64
|
||||
path: target/aarch64-unknown-linux-musl/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload armv7 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-armv7
|
||||
path: target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_windows:
|
||||
name: Build deltachat-rpc-server for Windows
|
||||
name: Linux
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
artifact: win32.exe
|
||||
path: deltachat-rpc-server.exe
|
||||
target: i686-pc-windows-msvc
|
||||
|
||||
- os: windows-latest
|
||||
artifact: win64.exe
|
||||
path: deltachat-rpc-server.exe
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
arch: [aarch64, armv7l, armv6l, i686, x86_64]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --package deltachat-rpc-server --target ${{ matrix.target }} --features vendored
|
||||
- name: Build deltachat-rpc-server binaries
|
||||
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}-linux
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-${{ matrix.artifact }}
|
||||
path: target/${{ matrix.target}}/release/${{ matrix.path }}
|
||||
name: deltachat-rpc-server-${{ matrix.arch }}-linux
|
||||
path: result/bin/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_windows:
|
||||
name: Windows
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [win32, win64]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Build deltachat-rpc-server binaries
|
||||
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-${{ matrix.arch }}
|
||||
path: result/bin/deltachat-rpc-server.exe
|
||||
if-no-files-found: error
|
||||
|
||||
build_macos:
|
||||
name: Build deltachat-rpc-server for macOS
|
||||
name: macOS
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [x86_64, aarch64]
|
||||
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add x86_64-apple-darwin
|
||||
run: rustup target add ${{ matrix.arch }}-apple-darwin
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --package deltachat-rpc-server --target x86_64-apple-darwin --features vendored
|
||||
run: cargo build --release --package deltachat-rpc-server --target ${{ matrix.arch }}-apple-darwin --features vendored
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64-macos
|
||||
path: target/x86_64-apple-darwin/release/deltachat-rpc-server
|
||||
name: deltachat-rpc-server-${{ matrix.arch }}-macos
|
||||
path: target/${{ matrix.arch }}-apple-darwin/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_android:
|
||||
name: Android
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [arm64-v8a, armeabi-v7a]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Build deltachat-rpc-server binaries
|
||||
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}-android
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-${{ matrix.arch }}-android
|
||||
path: result/bin/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
name: Upload binaries to the release
|
||||
name: Build wheels and upload binaries to the release
|
||||
needs: ["build_linux", "build_windows", "build_macos"]
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/deltachat-rpc-server
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Download built binaries
|
||||
uses: "actions/download-artifact@v3"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Compose dist/ directory
|
||||
- name: Download Linux aarch64 binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-aarch64-linux
|
||||
path: deltachat-rpc-server-aarch64-linux.d
|
||||
|
||||
- name: Download Linux armv7l binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-armv7l-linux
|
||||
path: deltachat-rpc-server-armv7l-linux.d
|
||||
|
||||
- name: Download Linux armv6l binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-armv6l-linux
|
||||
path: deltachat-rpc-server-armv6l-linux.d
|
||||
|
||||
- name: Download Linux i686 binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-i686-linux
|
||||
path: deltachat-rpc-server-i686-linux.d
|
||||
|
||||
- name: Download Linux x86_64 binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64-linux
|
||||
path: deltachat-rpc-server-x86_64-linux.d
|
||||
|
||||
- name: Download Win32 binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-win32
|
||||
path: deltachat-rpc-server-win32.d
|
||||
|
||||
- name: Download Win64 binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-win64
|
||||
path: deltachat-rpc-server-win64.d
|
||||
|
||||
- name: Download macOS binary for x86_64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64-macos
|
||||
path: deltachat-rpc-server-x86_64-macos.d
|
||||
|
||||
- name: Download macOS binary for aarch64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-aarch64-macos
|
||||
path: deltachat-rpc-server-aarch64-macos.d
|
||||
|
||||
- name: Download Android binary for arm64-v8a
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-arm64-v8a-android
|
||||
path: deltachat-rpc-server-arm64-v8a-android.d
|
||||
|
||||
- name: Download Android binary for armeabi-v7a
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: deltachat-rpc-server-armeabi-v7a-android
|
||||
path: deltachat-rpc-server-armeabi-v7a-android.d
|
||||
|
||||
- name: Create bin/ directory
|
||||
run: |
|
||||
mkdir dist
|
||||
for x in x86_64 i686 aarch64 armv7 win32.exe win64.exe x86_64-macos; do
|
||||
mv "deltachat-rpc-server-$x"/* "dist/deltachat-rpc-server-$x"
|
||||
done
|
||||
mkdir -p bin
|
||||
mv deltachat-rpc-server-aarch64-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-aarch64-linux
|
||||
mv deltachat-rpc-server-armv7l-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-armv7l-linux
|
||||
mv deltachat-rpc-server-armv6l-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-armv6l-linux
|
||||
mv deltachat-rpc-server-i686-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-i686-linux
|
||||
mv deltachat-rpc-server-x86_64-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-x86_64-linux
|
||||
mv deltachat-rpc-server-win32.d/deltachat-rpc-server.exe bin/deltachat-rpc-server-win32.exe
|
||||
mv deltachat-rpc-server-win64.d/deltachat-rpc-server.exe bin/deltachat-rpc-server-win64.exe
|
||||
mv deltachat-rpc-server-x86_64-macos.d/deltachat-rpc-server bin/deltachat-rpc-server-x86_64-macos
|
||||
mv deltachat-rpc-server-aarch64-macos.d/deltachat-rpc-server bin/deltachat-rpc-server-aarch64-macos
|
||||
mv deltachat-rpc-server-arm64-v8a-android.d/deltachat-rpc-server bin/deltachat-rpc-server-arm64-v8a-android
|
||||
mv deltachat-rpc-server-armeabi-v7a-android.d/deltachat-rpc-server bin/deltachat-rpc-server-armeabi-v7a-android
|
||||
|
||||
- name: List downloaded artifacts
|
||||
- name: List binaries
|
||||
run: ls -l bin/
|
||||
|
||||
# Python 3.11 is needed for tomllib used in scripts/wheel-rpc-server.py
|
||||
- name: Install python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
|
||||
- name: Install wheel
|
||||
run: pip install wheel
|
||||
|
||||
- name: Build deltachat-rpc-server Python wheels and source package
|
||||
run: |
|
||||
mkdir -p dist
|
||||
nix build .#deltachat-rpc-server-x86_64-linux-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-armv7l-linux-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-armv6l-linux-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-aarch64-linux-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-i686-linux-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-win64-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-win32-wheel
|
||||
cp result/*.whl dist/
|
||||
nix build .#deltachat-rpc-server-source
|
||||
cp result/*.tar.gz dist/
|
||||
python3 scripts/wheel-rpc-server.py x86_64-darwin bin/deltachat-rpc-server-x86_64-macos
|
||||
python3 scripts/wheel-rpc-server.py aarch64-darwin bin/deltachat-rpc-server-aarch64-macos
|
||||
mv *.whl dist/
|
||||
|
||||
- name: List artifacts
|
||||
run: ls -l dist/
|
||||
|
||||
- name: Upload binaries to the GitHub release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
run: |
|
||||
gh release upload ${{ github.ref_name }} \
|
||||
--repo ${{ github.repository }} \
|
||||
dist/*
|
||||
bin/* dist/*
|
||||
|
||||
- name: Publish deltachat-rpc-client to PyPI
|
||||
if: github.event_name == 'release'
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
11
.github/workflows/jsonrpc-client-npm-package.yml
vendored
11
.github/workflows/jsonrpc-client-npm-package.yml
vendored
@@ -13,11 +13,12 @@ jobs:
|
||||
steps:
|
||||
- name: Install tree
|
||||
run: sudo apt install tree
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
show-progress: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: Get tag
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
@@ -49,7 +50,7 @@ jobs:
|
||||
ls -lah
|
||||
mv $(find deltachat-jsonrpc-client-*) $DELTACHAT_JSONRPC_TAR_GZ
|
||||
- name: Upload Prebuild
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-jsonrpc-client.tgz
|
||||
path: deltachat-jsonrpc/typescript/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
|
||||
|
||||
16
.github/workflows/jsonrpc.yml
vendored
16
.github/workflows/jsonrpc.yml
vendored
@@ -2,9 +2,9 @@ name: JSON-RPC API Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -14,11 +14,13 @@ jobs:
|
||||
build_and_test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
show-progress: false
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
- name: Add Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: npm install
|
||||
@@ -31,7 +33,7 @@ jobs:
|
||||
working-directory: deltachat-jsonrpc/typescript
|
||||
run: npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
- name: make sure websocket server version still builds
|
||||
working-directory: deltachat-jsonrpc
|
||||
run: cargo build --bin deltachat-jsonrpc-server --features webserver
|
||||
|
||||
14
.github/workflows/node-docs.yml
vendored
14
.github/workflows/node-docs.yml
vendored
@@ -8,18 +8,20 @@ name: Generate & upload node.js documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
show-progress: false
|
||||
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: npm install and generate documentation
|
||||
working-directory: node
|
||||
|
||||
61
.github/workflows/node-package.yml
vendored
61
.github/workflows/node-package.yml
vendored
@@ -14,11 +14,12 @@ jobs:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
show-progress: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: System info
|
||||
run: |
|
||||
rustc -vV
|
||||
@@ -28,7 +29,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.APPDATA }}/npm-cache
|
||||
@@ -36,7 +37,7 @@ jobs:
|
||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/
|
||||
@@ -56,7 +57,7 @@ jobs:
|
||||
tar -zcvf "${{ matrix.os }}.tar.gz" -C prebuilds .
|
||||
|
||||
- name: Upload Prebuild
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}
|
||||
path: node/${{ matrix.os }}.tar.gz
|
||||
@@ -66,21 +67,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Build Linux prebuilds inside a container with old glibc for backwards compatibility.
|
||||
# Debian 10 contained glibc 2.28 at the time of the writing (2023-06-04): https://packages.debian.org/buster/libc6
|
||||
# Ubuntu 18.04 is at the End of Standard Support since June 2023, but it contains glibc 2.27,
|
||||
# so we are using it to support Ubuntu 18.04 setups that are still not upgraded.
|
||||
container: ubuntu:18.04
|
||||
# Debian 10 contained glibc 2.28: https://packages.debian.org/buster/libc6
|
||||
container: debian:10
|
||||
steps:
|
||||
# Working directory is owned by 1001:1001 by default.
|
||||
# Change it to our user.
|
||||
- name: Change working directory owner
|
||||
run: chown root:root .
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
show-progress: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- run: apt-get update
|
||||
|
||||
# Python is needed for node-gyp
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.APPDATA }}/npm-cache
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
tar -zcvf "linux.tar.gz" -C prebuilds .
|
||||
|
||||
- name: Upload Prebuild
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
path: node/linux.tar.gz
|
||||
@@ -139,11 +139,12 @@ jobs:
|
||||
steps:
|
||||
- name: Install tree
|
||||
run: sudo apt install tree
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
show-progress: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: Get tag
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
@@ -167,25 +168,25 @@ jobs:
|
||||
node --version
|
||||
echo $DELTACHAT_NODE_TAR_GZ
|
||||
- name: Download Linux prebuild
|
||||
uses: actions/download-artifact@v1
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
- name: Download macOS prebuild
|
||||
uses: actions/download-artifact@v1
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macos-latest
|
||||
- name: Download Windows prebuild
|
||||
uses: actions/download-artifact@v1
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-latest
|
||||
- shell: bash
|
||||
run: |
|
||||
mkdir node/prebuilds
|
||||
tar -xvzf linux/linux.tar.gz -C node/prebuilds
|
||||
tar -xvzf macos-latest/macos-latest.tar.gz -C node/prebuilds
|
||||
tar -xvzf windows-latest/windows-latest.tar.gz -C node/prebuilds
|
||||
tar -xvzf linux.tar.gz -C node/prebuilds
|
||||
tar -xvzf macos-latest.tar.gz -C node/prebuilds
|
||||
tar -xvzf windows-latest.tar.gz -C node/prebuilds
|
||||
tree node/prebuilds
|
||||
rm -rf linux macos-latest windows-latest
|
||||
rm -f linux.tar.gz macos-latest.tar.gz windows-latest.tar.gz
|
||||
- name: Install dependencies without running scripts
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
@@ -203,7 +204,7 @@ jobs:
|
||||
ls -lah
|
||||
mv $(find deltachat-node-*) $DELTACHAT_NODE_TAR_GZ
|
||||
- name: Upload prebuild
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deltachat-node.tgz
|
||||
path: ${{ env.DELTACHAT_NODE_TAR_GZ }}
|
||||
|
||||
17
.github/workflows/node-tests.yml
vendored
17
.github/workflows/node-tests.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
@@ -23,11 +23,12 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
show-progress: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: System info
|
||||
run: |
|
||||
rustc -vV
|
||||
@@ -37,7 +38,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.APPDATA }}/npm-cache
|
||||
@@ -45,7 +46,7 @@ jobs:
|
||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/
|
||||
@@ -63,5 +64,5 @@ jobs:
|
||||
working-directory: node
|
||||
run: npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
CHATMAIL_DOMAIN: ${{ secrets.CHATMAIL_DOMAIN }}
|
||||
NODE_OPTIONS: "--force-node-api-uncaught-exceptions-policy=true"
|
||||
|
||||
47
.github/workflows/publish-deltachat-rpc-client-pypi.yml
vendored
Normal file
47
.github/workflows/publish-deltachat-rpc-client-pypi.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Publish deltachat-rpc-client to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build distribution
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Install pypa/build
|
||||
run: python3 -m pip install build
|
||||
- name: Build a binary wheel and a source tarball
|
||||
working-directory: deltachat-rpc-client
|
||||
run: python3 -m build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: deltachat-rpc-client/dist/
|
||||
|
||||
publish-to-pypi:
|
||||
name: Publish Python distribution to PyPI
|
||||
if: github.event_name == 'release'
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/deltachat-rpc-client
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Publish deltachat-rpc-client to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
16
.github/workflows/repl.yml
vendored
16
.github/workflows/repl.yml
vendored
@@ -10,15 +10,17 @@ on:
|
||||
jobs:
|
||||
build_repl:
|
||||
name: Build REPL example
|
||||
runs-on: windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Build
|
||||
run: cargo build -p deltachat-repl --features vendored
|
||||
|
||||
run: nix build .#deltachat-repl-win64
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: repl.exe
|
||||
path: "target/debug/deltachat-repl.exe"
|
||||
path: "result/bin/deltachat-repl.exe"
|
||||
|
||||
49
.github/workflows/upload-docs.yml
vendored
49
.github/workflows/upload-docs.yml
vendored
@@ -1,17 +1,18 @@
|
||||
name: Build & Deploy Documentation on rs.delta.chat
|
||||
name: Build & Deploy Documentation on rs.delta.chat, c.delta.chat, py.delta.chat
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- docs-gh-action
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build-rs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat --no-deps --document-private-items
|
||||
@@ -23,3 +24,41 @@ jobs:
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/rs/"
|
||||
|
||||
build-python:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
fetch-depth: 0 # Fetch history to calculate VCS version number.
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Build Python documentation
|
||||
run: nix build .#python-docs
|
||||
- name: Upload to py.delta.chat
|
||||
run: |
|
||||
mkdir -p "$HOME/.ssh"
|
||||
echo "${{ secrets.CODESPEAK_KEY }}" > "$HOME/.ssh/key"
|
||||
chmod 600 "$HOME/.ssh/key"
|
||||
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/result/html/ "delta@py.delta.chat:/home/delta/build/master"
|
||||
|
||||
build-c:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
fetch-depth: 0 # Fetch history to calculate VCS version number.
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Build C documentation
|
||||
run: nix build .#docs
|
||||
- name: Upload to py.delta.chat
|
||||
run: |
|
||||
mkdir -p "$HOME/.ssh"
|
||||
echo "${{ secrets.CODESPEAK_KEY }}" > "$HOME/.ssh/key"
|
||||
chmod 600 "$HOME/.ssh/key"
|
||||
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/result/html/ "delta@c.delta.chat:/home/delta/build-c/master"
|
||||
|
||||
19
.github/workflows/upload-ffi-docs.yml
vendored
19
.github/workflows/upload-ffi-docs.yml
vendored
@@ -7,23 +7,22 @@ name: Build & Deploy Documentation on cffi.delta.chat
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- docs-gh-action
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat_ffi --no-deps
|
||||
- name: Upload to cffi.delta.chat
|
||||
uses: up9cloud/action-rsync@v1.3
|
||||
env:
|
||||
USER: ${{ secrets.USERNAME }}
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/cffi/"
|
||||
run: |
|
||||
mkdir -p "$HOME/.ssh"
|
||||
echo "${{ secrets.KEY }}" > "$HOME/.ssh/key"
|
||||
chmod 600 "$HOME/.ssh/key"
|
||||
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/target/doc/ "${{ secrets.USERNAME }}@delta.chat:/var/www/html/cffi/"
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
/build
|
||||
/dist
|
||||
|
||||
# ignore vi temporaries
|
||||
*~
|
||||
@@ -43,3 +44,10 @@ node/build/
|
||||
node/dist/
|
||||
node/prebuilds/
|
||||
node/.nyc_output/
|
||||
|
||||
# Nix symlink.
|
||||
result
|
||||
|
||||
# direnv
|
||||
.envrc
|
||||
.direnv
|
||||
961
CHANGELOG.md
961
CHANGELOG.md
@@ -1,5 +1,925 @@
|
||||
# Changelog
|
||||
|
||||
## [1.137.2] - 2024-04-05
|
||||
|
||||
### API-Changes
|
||||
|
||||
- [**breaking**] Increase Minimum Supported Rust Version to 1.77.0.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Show reactions in summaries ([#5387](https://github.com/deltachat/deltachat-core-rust/pull/5387)).
|
||||
|
||||
### Tests
|
||||
|
||||
- Test reactions for forwarded messages
|
||||
|
||||
### Refactor
|
||||
|
||||
- `is_probably_private_reply`: Remove reaction-specific code.
|
||||
- Use Rust 1.77.0 support for recursion in async functions.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- cargo: Bump rustyline from 13.0.0 to 14.0.0.
|
||||
- Update chrono from 0.4.34 to 0.4.37.
|
||||
- Update from brotli 3.4.0 to brotli 4.0.0.
|
||||
- Upgrade `h2` from 0.4.3 to 0.4.4.
|
||||
- Upgrade `image` from 0.24.9 to 0.25.1.
|
||||
- cargo: Bump fast-socks5 from 0.9.5 to 0.9.6.
|
||||
|
||||
## [1.137.1] - 2024-04-03
|
||||
|
||||
### CI
|
||||
|
||||
- Remove android builds for `x86` and `x86_64`.
|
||||
|
||||
## [1.137.0] - 2024-04-02
|
||||
|
||||
### API-Changes
|
||||
|
||||
- [**breaking**] Remove data from `DC_EVENT_INCOMING_MSG_BUNCH`.
|
||||
- [**breaking**] Remove unused `dc_accounts_all_work_done()` ([#5384](https://github.com/deltachat/deltachat-core-rust/pull/5384)).
|
||||
- deltachat-rpc-client: Add futures.
|
||||
|
||||
### Build system
|
||||
|
||||
- cmake: Build outside the source tree.
|
||||
- nix: Add outputs for Android binaries.
|
||||
- Add `repository` to Cargo.toml.
|
||||
- python: Remove `setuptools_scm` dependency.
|
||||
- Add development shell ([#5390](https://github.com/deltachat/deltachat-core-rust/pull/5390)).
|
||||
|
||||
### CI
|
||||
|
||||
- Update to Rust 1.77.0.
|
||||
- Build deltachat-rpc-server for Android.
|
||||
- Shorter names for deltachat-rpc-server jobs.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Do not include provider hostname in `Message-ID`.
|
||||
- Include 3 recent Message-IDs in `References` header.
|
||||
- Include more entries into DNS fallback cache.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Preserve upper-/lowercase of links parsed by `dehtml()` ([#5362](https://github.com/deltachat/deltachat-core-rust/pull/5362)).
|
||||
- Rescan folders after changing `Config::SentboxWatch`.
|
||||
- Do not ignore `Contact::get_by_id()` error in `from_field_to_contact_id()`.
|
||||
- Put overridden sender name into message info.
|
||||
- Don't send selfavatar in `SecureJoin` messages before contact verification ([#5354](https://github.com/deltachat/deltachat-core-rust/pull/5354)).
|
||||
- Always set correct `chat_id` for `DC_EVENT_REACTIONS_CHANGED` ([#5419](https://github.com/deltachat/deltachat-core-rust/pull/5419)).
|
||||
|
||||
### Refactor
|
||||
|
||||
- Remove `MessageObject::from_message_id()`.
|
||||
- jsonrpc: Add `msg_id` and `account_id` to `get_message()` errors.
|
||||
- Cleanup `jobs` and `Params` relicts.
|
||||
|
||||
### Tests
|
||||
|
||||
- `Test_mvbox_sentbox_threads`: Check that sentbox gets configured after setting `sentbox_watch` ([#5105](https://github.com/deltachat/deltachat-core-rust/pull/5105)).
|
||||
- Remove flaky time check from `test_list_from()`.
|
||||
- Add failing test for #5418 (wrong `DC_EVENT_REACTIONS_CHANGED`)
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Add `result` to .gitignore.
|
||||
- cargo: Bump thiserror from 1.0.57 to 1.0.58.
|
||||
- cargo: Bump tokio from 1.36.0 to 1.37.0.
|
||||
- cargo: Bump pin-project from 1.1.4 to 1.1.5.
|
||||
- cargo: Bump strum from 0.26.1 to 0.26.2.
|
||||
- cargo: Bump uuid from 1.7.0 to 1.8.0.
|
||||
- cargo: Bump toml from 0.8.10 to 0.8.12.
|
||||
- cargo: Bump tokio-stream from 0.1.14 to 0.1.15.
|
||||
- cargo: Bump smallvec from 1.13.1 to 1.13.2.
|
||||
- cargo: Bump async-smtp from 0.9.0 to 0.9.1.
|
||||
- cargo: Bump strum_macros from 0.26.1 to 0.26.2.
|
||||
- cargo: Bump serde_json from 1.0.114 to 1.0.115.
|
||||
- cargo: Bump anyhow from 1.0.80 to 1.0.81.
|
||||
- cargo: Bump syn from 2.0.52 to 2.0.57.
|
||||
- cargo: Bump futures-lite from 2.2.0 to 2.3.0.
|
||||
- cargo: Bump axum from 0.7.4 to 0.7.5.
|
||||
- cargo: Bump reqwest from 0.11.24 to 0.12.2.
|
||||
- cargo: Bump backtrace from 0.3.69 to 0.3.71.
|
||||
- cargo: Bump regex from 1.10.3 to 1.10.4.
|
||||
- cargo: Update aho-corasick from 1.1.2 to 1.1.3.
|
||||
- Update deny.toml.
|
||||
|
||||
## [1.136.6] - 2024-03-19
|
||||
|
||||
### Build system
|
||||
|
||||
- Add description to deltachat-rpc-server wheels.
|
||||
- Read version from Cargo.toml in wheel-rpc-server.py.
|
||||
|
||||
### CI
|
||||
|
||||
- Update actions/cache from v3 to v4.
|
||||
- Automate publishing of deltachat-rpc-server to PyPI.
|
||||
|
||||
### Documentation
|
||||
|
||||
- deltachat-rpc-server: Update deltachat-rpc-client URL.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Nix flake update.
|
||||
|
||||
## [1.136.5] - 2024-03-18
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Nicer summaries: prefer emoji over names
|
||||
- Add `save_mime_headers` to debug info ([#5350](https://github.com/deltachat/deltachat-core-rust/pull/5350))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Terminate ephemeral and location loop immediately on channel close.
|
||||
- Update MemberListTimestamp when sending a group message.
|
||||
- On iOS, use FILE (default) instead of MEMORY ([#5349](https://github.com/deltachat/deltachat-core-rust/pull/5349)).
|
||||
- Add white background to recoded avatars ([#3787](https://github.com/deltachat/deltachat-core-rust/pull/3787)).
|
||||
|
||||
### Build system
|
||||
|
||||
- Add README to deltachat-rpc-client Python packages.
|
||||
|
||||
### Documentation
|
||||
|
||||
- deltachat-rpc-client: Document that 0 is a special value of `set_ephemeral_timer()`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Test that reordering of Member added message results in square bracket error.
|
||||
|
||||
## [1.136.4] - 2024-03-11
|
||||
|
||||
### Build system
|
||||
|
||||
- nix: Make .#libdeltachat buildable on macOS.
|
||||
- Build deltachat-rpc-server wheels with nix.
|
||||
|
||||
### CI
|
||||
|
||||
- Add workflow for automatic publishing of deltachat-rpc-client.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Remove duplicate CHANGELOG entries for 1.135.1.
|
||||
|
||||
## [1.136.3] - 2024-03-09
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Start IMAP loop for sentbox only if it is configured ([#5105](https://github.com/deltachat/deltachat-core-rust/pull/5105)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Remove leading whitespace from Subject ([#5106](https://github.com/deltachat/deltachat-core-rust/pull/5106)).
|
||||
- Create new Peerstate for unencrypted message with already known Autocrypt key, but a new address.
|
||||
|
||||
### Build system
|
||||
|
||||
- nix: Cleanup cross-compilation code.
|
||||
- nix: Include SystemConfiguration framework on darwin systems.
|
||||
|
||||
### CI
|
||||
|
||||
- Wait for `build_windows` task before trying to publish it.
|
||||
- Remove artifacts from npm package.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Don't parse Autocrypt header for outgoing messages ([#5259](https://github.com/deltachat/deltachat-core-rust/pull/5259)).
|
||||
- Remove `deduplicate_peerstates()`.
|
||||
- Fix 2024-03-05 nightly clippy warnings.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- deps: Bump mio from 0.8.8 to 0.8.11 in /fuzz.
|
||||
- RPC client: Add missing constants ([#5110](https://github.com/deltachat/deltachat-core-rust/pull/5110)).
|
||||
|
||||
## [1.136.2] - 2024-03-05
|
||||
|
||||
### Build system
|
||||
|
||||
- Downgrade `cc` to 1.0.83 to fix build for Android.
|
||||
|
||||
### CI
|
||||
|
||||
- Update setup-node action.
|
||||
|
||||
## [1.136.1] - 2024-03-05
|
||||
|
||||
### Build system
|
||||
|
||||
- Revert to OpenSSL 3.1.
|
||||
- Restore MSRV 1.70.0.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update node constants.
|
||||
|
||||
## [1.136.0] - 2024-03-04
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Recognise Trash folder by name ([#5275](https://github.com/deltachat/deltachat-core-rust/pull/5275)).
|
||||
- Send Chat-Group-Avatar as inline base64 ([#5253](https://github.com/deltachat/deltachat-core-rust/pull/5253)).
|
||||
- Self-Reporting: Report number of protected/encrypted/unencrypted chats ([#5292](https://github.com/deltachat/deltachat-core-rust/pull/5292)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Don't send sync messages on self-{status,avatar} update from self-sent messages ([#5289](https://github.com/deltachat/deltachat-core-rust/pull/5289)).
|
||||
- imap: Allow `maybe_network` to interrupt connection ratelimit.
|
||||
- imap: Set connectivity to "connecting" only after ratelimit.
|
||||
- Remove `Group-ID` from `Message-ID`.
|
||||
- Prioritize protected `Message-ID` over `X-Microsoft-Original-Message-ID`.
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Make `store_self_keypair` private.
|
||||
- Add `ContextBuilder.build()` to build Context without opening.
|
||||
- `dc_accounts_set_push_device_token` and `dc_get_push_state` APIs for iOS push notifications.
|
||||
|
||||
### Build system
|
||||
|
||||
- Tag armv6 wheels with tags accepted by PyPI.
|
||||
- Unpin OpenSSL.
|
||||
- Remove deprecated `unmaintained` field from deny.toml.
|
||||
- Do not vendor OpenSSL when cross-compiling ([#5316](https://github.com/deltachat/deltachat-core-rust/pull/5316)).
|
||||
- Increase MSRV to 1.74.0.
|
||||
|
||||
### CI
|
||||
|
||||
- Upgrade setup-python GitHub Action.
|
||||
- Update to Rust 1.76 and fix clippy warnings.
|
||||
- Build Python docs with Nix.
|
||||
- Upload python docs without GH actions.
|
||||
- Upload cffi docs without GH actions.
|
||||
- Build c.delta.chat docs with nix.
|
||||
|
||||
### Other
|
||||
|
||||
- refactor: move more methods from Imap into Session.
|
||||
- Add deltachat-time to sources.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Remove Session from Imap structure.
|
||||
- Merge ImapConfig into Imap.
|
||||
- Get rid of ImapActionResult.
|
||||
- Build contexts using ContextBuilder.
|
||||
- Do not send `Secure-Join-Group` in `vg-request`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Fix `test_verified_oneonone_chat_broken_by_device_change()` ([#5280](https://github.com/deltachat/deltachat-core-rust/pull/5280)).
|
||||
- `get_protected_chat()`: Use FFIEventTracker instead of `dc_wait_next_msgs()` ([#5207](https://github.com/deltachat/deltachat-core-rust/pull/5207)).
|
||||
- Fixup `tests/test_3_offline.py::TestOfflineAccountBasic::test_wrong_db`.
|
||||
- Fix pytest compat ([#5317](https://github.com/deltachat/deltachat-core-rust/pull/5317)).
|
||||
|
||||
## [1.135.1] - 2024-02-20
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Sync self-avatar across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
|
||||
- Sync Config::Selfstatus across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
|
||||
- Remove webxdc sending limit.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Never encrypt `{vc,vg}-request` SecureJoin messages.
|
||||
- Apply Autocrypt headers if timestamp is unchanged.
|
||||
- `Context::get_info`: Report displayname as "displayname" (w/o underscore).
|
||||
|
||||
### Tests
|
||||
|
||||
- Mock `SystemTime::now()` for the tests.
|
||||
- Add a test on protection message sort timestamp ([#5088](https://github.com/deltachat/deltachat-core-rust/pull/5088)).
|
||||
|
||||
### Build system
|
||||
|
||||
- Add flake.nix.
|
||||
- Add footer template for git-cliff.
|
||||
|
||||
### CI
|
||||
|
||||
- Update GitHub Actions `actions/upload-artifact`, `actions/download-artifact`, `actions/checkout`.
|
||||
- Build deltachat-repl for Windows with nix.
|
||||
- Build deltachat-rpc-server with nix.
|
||||
- Try to upload deltachat-rpc-server only on release.
|
||||
- Fixup node-package.yml after artifact actions upgrade.
|
||||
- Update to actions/checkout@v4.
|
||||
- Replace download-artifact v1 with v4.
|
||||
|
||||
### Refactor
|
||||
|
||||
- `create_keypair`: Remove unnecessary `map_err`.
|
||||
- Return error with a cause when failing to export keys.
|
||||
- Rename incorrectly named variables in `create_keypair`.
|
||||
|
||||
## [1.135.0] - 2024-02-13
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Add wildcard pattern support to provider database.
|
||||
- Add device message about outgoing undecryptable messages ([#5164](https://github.com/deltachat/deltachat-core-rust/pull/5164)).
|
||||
- Context::set_config(): Restart IO scheduler if needed ([#5111](https://github.com/deltachat/deltachat-core-rust/pull/5111)).
|
||||
- Server_sent_unsolicited_exists(): Log folder name.
|
||||
- Cache system time instead of looking at the clock several times in a row.
|
||||
- Basic self-reporting ([#5129](https://github.com/deltachat/deltachat-core-rust/pull/5129)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Dehtml: Don't just truncate text when trying to decode ([#5223](https://github.com/deltachat/deltachat-core-rust/pull/5223)).
|
||||
- Mark the gossip keys from the message as verified, not the ones from the db ([#5247](https://github.com/deltachat/deltachat-core-rust/pull/5247)).
|
||||
- Guarantee immediate message deletion if delete_server_after == 0 ([#5201](https://github.com/deltachat/deltachat-core-rust/pull/5201)).
|
||||
- Never allow a message timestamp to be a lot in the future ([#5249](https://github.com/deltachat/deltachat-core-rust/pull/5249)).
|
||||
- Imap::configure_mvbox: Do select_with_uidvalidity() before return.
|
||||
- ImapSession::select_or_create_folder(): Don't fail if folder is created in parallel.
|
||||
- Emit ConfigSynced event on the second device.
|
||||
- Create mvbox on setting mvbox_move.
|
||||
- Use SystemTime instead of Instant everywhere.
|
||||
- Restore database rows removed in previous release; this ensures compatibility when adding second device or importing backup and not all devices run the new core ([#5254](https://github.com/deltachat/deltachat-core-rust/pull/5254))
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- cargo: Bump image from 0.24.7 to 0.24.8.
|
||||
- cargo: Bump chrono from 0.4.31 to 0.4.33.
|
||||
- cargo: Bump futures-lite from 2.1.0 to 2.2.0.
|
||||
- cargo: Bump pin-project from 1.1.3 to 1.1.4.
|
||||
- cargo: Bump iana-time-zone from yanked 0.1.59 to 0.1.60.
|
||||
- cargo: Bump smallvec from 1.11.2 to 1.13.1.
|
||||
- cargo: Bump base64 from 0.21.5 to 0.21.7.
|
||||
- cargo: Bump regex from 1.10.2 to 1.10.3.
|
||||
- cargo: Bump libc from 0.2.151 to 0.2.153.
|
||||
- cargo: Bump reqwest from 0.11.23 to 0.11.24.
|
||||
- cargo: Bump axum from 0.7.3 to 0.7.4.
|
||||
- cargo: Bump uuid from 1.6.1 to 1.7.0.
|
||||
- cargo: Bump fast-socks5 from 0.9.2 to 0.9.5.
|
||||
- cargo: Bump serde_json from 1.0.111 to 1.0.113.
|
||||
- cargo: Bump syn from 2.0.46 to 2.0.48.
|
||||
- cargo: Bump serde from 1.0.194 to 1.0.196.
|
||||
- cargo: Bump toml from 0.8.8 to 0.8.10.
|
||||
- cargo: Update to strum 0.26.
|
||||
- Cargo update.
|
||||
- scripts: Do not install deltachat-rpc-client twice.
|
||||
|
||||
### Other
|
||||
|
||||
- Update welcome image, thanks @paulaluap
|
||||
- Merge pull request #5243 from deltachat/dependabot/cargo/pin-project-1.1.4
|
||||
- Merge pull request #5241 from deltachat/dependabot/cargo/futures-lite-2.2.0
|
||||
- Merge pull request #5236 from deltachat/dependabot/cargo/chrono-0.4.33
|
||||
- Merge pull request #5235 from deltachat/dependabot/cargo/image-0.24.8
|
||||
|
||||
|
||||
### Refactor
|
||||
|
||||
- Resultify token::exists.
|
||||
|
||||
### Tests
|
||||
|
||||
- Delete_server_after="1" should cause immediate message deletion ([#5201](https://github.com/deltachat/deltachat-core-rust/pull/5201)).
|
||||
|
||||
## [1.134.0] - 2024-01-31
|
||||
|
||||
### API-Changes
|
||||
|
||||
- [**breaking**] JSON-RPC: device message api now requires `Option<MessageData>` instead of `String` for the message ([#5211](https://github.com/deltachat/deltachat-core-rust/pull/5211)).
|
||||
- CFFI: add `dc_accounts_background_fetch` and event `DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE`.
|
||||
- JSON-RPC: add `accounts_background_fetch`.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- `Qr::check_qr()`: Accept i.delta.chat invite links ([#5217](https://github.com/deltachat/deltachat-core-rust/pull/5217)).
|
||||
- Add support for IMAP METADATA, fetching `/shared/comment` and `/shared/admin` and displaying it in account info.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Add tolerance for macOS and iOS changing `#` to `%23`.
|
||||
- Do not drop unknown report attachments, such as TLS reports.
|
||||
- Treat only "Auto-Submitted: auto-generated" messages as bot-sent ([#5213](https://github.com/deltachat/deltachat-core-rust/pull/5213)).
|
||||
- `Chat::resend_msgs`: Guarantee strictly increasing time in the `Date` header.
|
||||
- Delete resent messages on receiver side ([#5155](https://github.com/deltachat/deltachat-core-rust/pull/5155)).
|
||||
- Fix iOS build issue.
|
||||
|
||||
### CI
|
||||
|
||||
- Add/remove necessary newlines to fix Python lint.
|
||||
|
||||
### Tests
|
||||
|
||||
- `test_import_export_online_all`: Send the message to the existing address to avoid errors ([#5220](https://github.com/deltachat/deltachat-core-rust/pull/5220)).
|
||||
|
||||
## [1.133.2] - 2024-01-24
|
||||
|
||||
### Fixes
|
||||
|
||||
- Downgrade OpenSSL from 3.2.0 to 3.1.4 ([#5206](https://github.com/deltachat/deltachat-core-rust/issues/5206))
|
||||
- No new chats for MDNs with alias ([#5196](https://github.com/deltachat/deltachat-core-rust/issues/5196)) ([#5199](https://github.com/deltachat/deltachat-core-rust/pull/5199)).
|
||||
|
||||
## [1.133.1] - 2024-01-21
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Add `is_bot` to cffi and jsonrpc ([#5197](https://github.com/deltachat/deltachat-core-rust/pull/5197)).
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Add system message when provider does not allow unencrypted messages ([#5195](https://github.com/deltachat/deltachat-core-rust/pull/5195)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- `Chat::send_msg`: Remove encryption-related params from already sent message. This allows to send received encrypted `dc_msg_t` object to unencrypted chat, e.g. in a Python bot.
|
||||
- Set message download state to Failure on IMAP errors. This avoids partially downloaded messages getting stuck in "Downloading..." state without actually being in a download queue.
|
||||
- BCC-to-self even if server deletion is set to "at once". This is a workaround for SMTP servers which do not return response in time, BCC-self works as a confirmation that message was sent out successfully and does not need more retries.
|
||||
- node: Run tests with native ESM modules instead of `esm` ([#5194](https://github.com/deltachat/deltachat-core-rust/pull/5194)).
|
||||
- Use Quoted-Printable MIME encoding for the text part ([#3986](https://github.com/deltachat/deltachat-core-rust/pull/3986)).
|
||||
|
||||
### Tests
|
||||
|
||||
- python: Add `get_protected_chat` to testplugin.py.
|
||||
|
||||
## [1.133.0] - 2024-01-14
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Securejoin protocol implementation refinements
|
||||
- Track forward and backward verification separately ([#5089](https://github.com/deltachat/deltachat-core-rust/pull/5089)) to avoid inconsistent states.
|
||||
- Mark 1:1 chat as verified for Bob early. 1:1 chat with Alice is verified as soon as Alice's key is verified rather than at the end of the protocol.
|
||||
- Put Message-ID into hidden headers and take it from there on receiver ([#4798](https://github.com/deltachat/deltachat-core-rust/pull/4798)). This works around servers which generate their own Message-ID and overwrite the one generated by Delta Chat.
|
||||
- deltachat-repl: Enable INFO logging by default and add timestamps.
|
||||
- Add `ConfigSynced` (`DC_EVENT_CONFIG_SYNCED`) event which is emitted when configuration is changed via synchronization message or synchronization message for configuration is sent. UI may refresh elments based on the configuration key which is a part of the event.
|
||||
- Sync contact creation/rename across devices ([#5163](https://github.com/deltachat/deltachat-core-rust/pull/5163)).
|
||||
- Encrypt MDNs ([#5175](https://github.com/deltachat/deltachat-core-rust/pull/5175)).
|
||||
- Only try to configure non-strict TLS checks if explicitly set ([#5181](https://github.com/deltachat/deltachat-core-rust/pull/5181)).
|
||||
|
||||
### Build system
|
||||
|
||||
- Use released version of iroh 0.4.2 for "setup second device" feature.
|
||||
|
||||
### CI
|
||||
|
||||
- Update to Rust 1.75.0.
|
||||
- Downgrade `chai` from 4.4.0 to 4.3.10.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add a link <https://www.ietf.org/archive/id/draft-bucksch-autoconfig-00.html> to autoconfig RFC draft.
|
||||
- Update securejoin link in `standards.md` from <https://countermitm.readthedocs.io/> to <https://securejoin.readthedocs.io>.
|
||||
- Restore "Constants" page in Doxygen >=1.9.8
|
||||
|
||||
### Fixes
|
||||
|
||||
- imap: Limit the rate of LOGIN attempts rather than connection attempts. This is to avoid having to wait for rate limiter right after switching from a bad or offline network to a working network while still guarding against reconnection loop.
|
||||
- Do not ignore `peerstate.save_to_db()` errors.
|
||||
- securejoin: Mark 1:1s as protected regardless of the Config::VerifiedOneOnOneChats.
|
||||
- Delete received outgoing messages from SMTP queue ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
|
||||
- imap: Fail fast on `LIST` errors to avoid busy loop when connection is lost.
|
||||
- Split SMTP jobs already in `chat::create_send_msg_jobs()` ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
|
||||
- Do not remove contents from unencrypted [Schleuder](https://schleuder.org/) mailing lists messages.
|
||||
- Reset message error when scheduling resending ([#5119](https://github.com/deltachat/deltachat-core-rust/pull/5119)).
|
||||
- Emit events more reliably when starting and stopping I/O ([#5101](https://github.com/deltachat/deltachat-core-rust/pull/5101)).
|
||||
- Fix timestamp of chat protection info message for correct message ordering after restoring a backup ([#5088](https://github.com/deltachat/deltachat-core-rust/pull/5088)).
|
||||
|
||||
### Refactor
|
||||
|
||||
- sql: Recreate `config` table with UNIQUE constraint.
|
||||
- sql: Recreate `keypairs` table to remove unused `addr` and `created` fields and move `is_default` flag to `config` table.
|
||||
- Send `Secure-Join-Fingerprint` only in `*-request-with-auth`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Test joining non-protected group.
|
||||
- Test that read receipts don't degrade encryption.
|
||||
- Test that changing default private key breaks backward verification.
|
||||
- Test recovery from lost vc-contact-confirm.
|
||||
- Use `wait_for_incoming_msg_event()` more.
|
||||
|
||||
## [1.132.1] - 2023-12-12
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Add "From:" to protected headers for signed-only messages.
|
||||
- Sync user actions for ad-hoc groups across devices ([#5065](https://github.com/deltachat/deltachat-core-rust/pull/5065)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Add padlock to empty part if the whole message is empty.
|
||||
- Renew IDLE timeout on keepalives and reduce it to 5 minutes.
|
||||
- connectivity: Return false from `all_work_done()` immediately after connecting (iOS notification fix).
|
||||
|
||||
### API-Changes
|
||||
|
||||
- deltachat-jsonrpc-client: add `Account.{import,export}_self_keys`.
|
||||
|
||||
### CI
|
||||
|
||||
- Update to Rust 1.74.1.
|
||||
|
||||
## [1.132.0] - 2023-12-06
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Increase TCP timeouts from 30 to 60 seconds.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Don't sort message creating a protected group over a protection message ([#4963](https://github.com/deltachat/deltachat-core-rust/pull/4963)).
|
||||
- Do not lock accounts.toml on iOS.
|
||||
- Protect groups even if some members are not verified and add `test_securejoin_after_contact_resetup` regression test.
|
||||
|
||||
## [1.131.9] - 2023-12-02
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Remove `dc_get_http_response()`, `dc_http_response_get_mimetype()`, `dc_http_response_get_encoding()`, `dc_http_response_get_blob()`, `dc_http_response_get_size()`, `dc_http_response_unref()` and `dc_http_response_t` from cffi.
|
||||
- Deprecate CFFI APIs `dc_send_reaction()`, `dc_get_msg_reactions()`, `dc_reactions_get_contacts()`, `dc_reactions_get_by_contact_id()`, `dc_reactions_unref` and `dc_reactions_t`.
|
||||
- Make `Contact.is_verified()` return bool.
|
||||
|
||||
### Build system
|
||||
|
||||
- Switch from fork of iroh to iroh 0.4.2 pre-release.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Send `Chat-Verified` headers in 1:1 chats.
|
||||
- Ratelimit IMAP connections ([#4940](https://github.com/deltachat/deltachat-core-rust/pull/4940)).
|
||||
- Remove receiver limit on `.xdc` size.
|
||||
- Don't affect MimeMessage with "From" and secured headers from encrypted unsigned messages.
|
||||
- Sync `Config::{MdnsEnabled,ShowEmails}` across devices ([#4954](https://github.com/deltachat/deltachat-core-rust/pull/4954)).
|
||||
- Sync `Config::Displayname` across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
|
||||
- `Chat::rename_ex`: Don't send sync message if usual message is sent.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Lock the database when INSERTing a webxdc update, avoid "Database is locked" errors.
|
||||
- Use keyring with all private keys when decrypting a message ([#5046](https://github.com/deltachat/deltachat-core-rust/pull/5046)).
|
||||
|
||||
### Tests
|
||||
|
||||
- Make Result-returning tests produce a line number.
|
||||
- Add `test_utils::sync()`.
|
||||
- Test inserting lots of webxdc updates.
|
||||
- Split `test_sync_alter_chat()` into smaller tests.
|
||||
|
||||
## [1.131.8] - 2023-11-27
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- webxdc: Add unique IDs to status updates sent outside and deduplicate based on IDs.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Allow IMAP servers not returning UIDNEXT on SELECT and STATUS such as mail.163.com.
|
||||
- Use the correct securejoin strings used in the UI, remove old TODO ([#5047](https://github.com/deltachat/deltachat-core-rust/pull/5047)).
|
||||
- Do not emit events about webxdc update events logged into debug log webxdc.
|
||||
|
||||
### Tests
|
||||
|
||||
- Check that `receive_status_update` has forward compatibility and unique webxdc IDs will be ignored by previous Delta Chat versions.
|
||||
|
||||
## [1.131.7] - 2023-11-24
|
||||
|
||||
### Fixes
|
||||
|
||||
- Revert "fix: check UIDNEXT with a STATUS command before going IDLE". This attempts to fix mail.163.com which has broken STATUS command.
|
||||
|
||||
## [1.131.6] - 2023-11-21
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fail fast if IMAP FETCH cannot be parsed instead of getting stuck in infinite loop.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Generate deltachat-rpc-client documentation and publish it to <https://py.delta.chat>.
|
||||
|
||||
## [1.131.5] - 2023-11-20
|
||||
|
||||
### API-Changes
|
||||
|
||||
- deltachat-rpc-client: Add `Message.get_sender_contact()`.
|
||||
- Turn `ContactAddress` into an owned type.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Lowercase addresses in Autocrypt and Autocrypt-Gossip headers.
|
||||
- Lowercase the address in member added/removed messages.
|
||||
- Lowercase `addr` when it is set.
|
||||
- Do not replace the message with an error in square brackets when the sender is not a member of the protected group.
|
||||
|
||||
### Fixes
|
||||
|
||||
- `Chat::sync_contacts()`: Fetch contact addresses in a single query.
|
||||
- `Chat::rename_ex()`: Sync improved chat name to other devices.
|
||||
- Recognize `Chat-Group-Member-Added` of self case-insensitively.
|
||||
- Compare verifier addr to peerstate addr case-insensitively.
|
||||
|
||||
### Tests
|
||||
|
||||
- Port [Secure-Join](https://securejoin.readthedocs.io/) tests to JSON-RPC.
|
||||
|
||||
### CI
|
||||
|
||||
- Test with Rust 1.74.
|
||||
|
||||
|
||||
## [1.131.4] - 2023-11-16
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document DC_DOWNLOAD_UNDECIPHERABLE.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Always add "Member added" as system message.
|
||||
|
||||
## [1.131.3] - 2023-11-15
|
||||
|
||||
### Fixes
|
||||
|
||||
- Update async-imap to 0.9.4 which does not ignore EOF on FETCH.
|
||||
- Reset gossiped timestamp on securejoin.
|
||||
- sync: Ignore unknown sync items to provide forward compatibility and avoid creating empty message bubbles.
|
||||
- sync: Skip sync when chat name is set to the current one.
|
||||
- Return connectivity HTML with an error when IO is stopped.
|
||||
|
||||
## [1.131.2] - 2023-11-14
|
||||
|
||||
### API-Changes
|
||||
|
||||
- deltachat-rpc-client: add `Account.get_chat_by_contact()`.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Do not post "... verified" messages on QR scan success.
|
||||
- Never drop better message from `apply_group_changes()`.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Assign MDNs to the trash chat early to prevent received MDNs from creating or unblocking 1:1 chats.
|
||||
- Allow to securejoin groups when 1:1 chat with the inviter is a contact request.
|
||||
- Add "setup changed" message for verified key before the message.
|
||||
- Ignore special chats when calculating similar chats.
|
||||
|
||||
## [1.131.1] - 2023-11-13
|
||||
|
||||
### Fixes
|
||||
|
||||
- Do not skip actual message parts when group change messages are inserted.
|
||||
|
||||
## [1.131.0] - 2023-11-13
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Sync chat contacts across devices ([#4953](https://github.com/deltachat/deltachat-core-rust/pull/4953)).
|
||||
- Sync creating broadcast lists across devices ([#4953](https://github.com/deltachat/deltachat-core-rust/pull/4953)).
|
||||
- Sync Chat::name across devices ([#4953](https://github.com/deltachat/deltachat-core-rust/pull/4953)).
|
||||
- Multi-device broadcast lists ([#4953](https://github.com/deltachat/deltachat-core-rust/pull/4953)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Encode chat name in the `List-ID` header to avoid SMTPUTF8 errors.
|
||||
- Ignore errors from generating sync messages.
|
||||
- `Context::execute_sync_items`: Ignore all errors ([#4817](https://github.com/deltachat/deltachat-core-rust/pull/4817)).
|
||||
- Allow to send unverified securejoin messages to protected chats ([#4982](https://github.com/deltachat/deltachat-core-rust/pull/4982)).
|
||||
|
||||
## [1.130.0] - 2023-11-10
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Emit JoinerProgress(1000) event when Bob verifies Alice.
|
||||
- JSON-RPC: add `ContactObject.is_profile_verified` property.
|
||||
- Hide `ChatId::get_for_contact()` from public API.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Add secondary verified key.
|
||||
- Add info messages about implicitly added members.
|
||||
- Treat reset state as encryption not preferred.
|
||||
- Grow sleep durations on errors in Imap::fake_idle() ([#4424](https://github.com/deltachat/deltachat-core-rust/pull/4424)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Mark 1:1 chat as protected when joining a group.
|
||||
- Raise lower auto-download limit to 160k.
|
||||
- Remove `Reporting-UA` from read receipts.
|
||||
- Do not apply group changes to special chats. Avoid adding members to the trash chat.
|
||||
- imap: make `UidGrouper` robust against duplicate UIDs.
|
||||
- Do not return hidden chat from `dc_get_chat_id_by_contact_id`.
|
||||
- Smtp_loop(): Don't grow timeout if interrupted early ([#4833](https://github.com/deltachat/deltachat-core-rust/pull/4833)).
|
||||
|
||||
### Refactor
|
||||
|
||||
- imap: Do not FETCH right after `scan_folders()`.
|
||||
- deltachat-rpc-client: Use `itertools` instead of `Lock` for thread-safe request ID generation.
|
||||
|
||||
### Tests
|
||||
|
||||
- Remove unused `--liveconfig` option.
|
||||
- Test chatlist can load for corrupted chats ([#4979](https://github.com/deltachat/deltachat-core-rust/pull/4979)).
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update provider-db ([#4949](https://github.com/deltachat/deltachat-core-rust/pull/4949)).
|
||||
|
||||
## [1.129.1] - 2023-11-06
|
||||
|
||||
### Fixes
|
||||
|
||||
- Update tokio-imap to fix Outlook STATUS parsing bug.
|
||||
- deltachat-rpc-client: Add the Lock around request ID.
|
||||
- `apply_group_changes`: Don't implicitly delete members locally, add absent ones instead ([#4934](https://github.com/deltachat/deltachat-core-rust/pull/4934)).
|
||||
- Partial messages do not change group state ([#4900](https://github.com/deltachat/deltachat-core-rust/pull/4900)).
|
||||
|
||||
### Tests
|
||||
|
||||
- Group chats device synchronisation.
|
||||
|
||||
## [1.129.0] - 2023-11-06
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Add JSON-RPC `get_chat_id_by_contact_id` API ([#4918](https://github.com/deltachat/deltachat-core-rust/pull/4918)).
|
||||
- [**breaking**] Remove deprecated `get_verifier_addr`.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Sync chat `Blocked` state, chat visibility, chat mute duration and contact blocked status across devices ([#4817](https://github.com/deltachat/deltachat-core-rust/pull/4817)).
|
||||
- Add 'group created instructions' as info message ([#4916](https://github.com/deltachat/deltachat-core-rust/pull/4916)).
|
||||
- Add hardcoded fallback DNS cache.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Switch to `EncryptionPreference::Mutual` on a receipt of encrypted+signed message ([#4707](https://github.com/deltachat/deltachat-core-rust/pull/4707)).
|
||||
- imap: Check UIDNEXT with a STATUS command before going IDLE.
|
||||
- Allow to change verified key via "member added" message.
|
||||
- json-rpc: Return verifier even if the contact is not "verified" (Autocrypt key does not equal Secure-Join key).
|
||||
|
||||
### Documentation
|
||||
|
||||
- Refine `Contact::get_verifier_id` and `Contact::is_verified` documentation ([#4922](https://github.com/deltachat/deltachat-core-rust/pull/4922)).
|
||||
- Contact profile view should not use `dc_contact_is_verified()`.
|
||||
- Remove documentation for non-existing `dc_accounts_new` `os_name` param.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Remove unused or useless code paths in Secure-Join ([#4897](https://github.com/deltachat/deltachat-core-rust/pull/4897)).
|
||||
- Improve error handling in Secure-Join code.
|
||||
- Add hostname to "no DNS resolution results" error message.
|
||||
- Accept `&str` instead of `Option<String>` in idle().
|
||||
|
||||
## [1.128.0] - 2023-11-02
|
||||
|
||||
### Build system
|
||||
- [**breaking**] Upgrade nodejs version to 18 ([#4903](https://github.com/deltachat/deltachat-core-rust/pull/4903)).
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- deltachat-rpc-client: Add `Account.wait_for_incoming_msg_event()`.
|
||||
- Decrease ratelimit for .testrun.org subdomains.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Do not fail securejoin due to unrelated pending bobstate ([#4896](https://github.com/deltachat/deltachat-core-rust/pull/4896)).
|
||||
- Allow other verified group recipients to be unverified, only check the sender verification.
|
||||
- Remove not working attempt to recover from verified key changes.
|
||||
|
||||
## [1.127.2] - 2023-10-29
|
||||
|
||||
### API-Changes
|
||||
|
||||
- [**breaking**] Jsonrpc `misc_set_draft` now requires setting the viewtype.
|
||||
- jsonrpc: Add `get_message_info_object`.
|
||||
|
||||
### Tests
|
||||
|
||||
- deltachat-rpc-client: Move pytest option from pyproject.toml to tox.ini and set log level.
|
||||
- deltachat-rpc-client: Test securejoin.
|
||||
- Increase pytest timeout to 10 minutes.
|
||||
- Compile deltachat-rpc-server in debug mode for tests.
|
||||
|
||||
## [1.127.1] - 2023-10-27
|
||||
|
||||
### API-Changes
|
||||
|
||||
- jsonrpc: add `.is_protection_broken` to `FullChat` and `BasicChat`.
|
||||
- jsonrpc: Add `id` to `ProviderInfo`.
|
||||
|
||||
## [1.127.0] - 2023-10-26
|
||||
|
||||
### API-Changes
|
||||
|
||||
- [**breaking**] `dc_accounts_new` API is changed. Unused `os_name` argument is removed and `writable` argument is added.
|
||||
- jsonrpc: Add `resend_messages`.
|
||||
- [**breaking**] Remove unused function `is_verified_ex()` ([#4551](https://github.com/deltachat/deltachat-core-rust/pull/4551))
|
||||
- [**breaking**] Make `MsgId.delete_from_db()` private.
|
||||
- [**breaking**] deltachat-jsonrpc: use `kind` as a tag for all union types
|
||||
- json-rpc: Force stickers to be sent as stickers ([#4819](https://github.com/deltachat/deltachat-core-rust/pull/4819)).
|
||||
- Add mailto parse api ([#4829](https://github.com/deltachat/deltachat-core-rust/pull/4829)).
|
||||
- [**breaking**] Remove unused `DC_STR_PROTECTION_(EN)ABLED` strings
|
||||
- [**breaking**] Remove unused `dc_set_chat_protection()`
|
||||
- Hide `DcSecretKey` trait from the API.
|
||||
- Verified 1:1 chats ([#4315](https://github.com/deltachat/deltachat-core-rust/pull/4315)). Disabled by default, enable with `verified_one_on_one_chats` config.
|
||||
- Add api `chat::Chat::is_protection_broken`
|
||||
- Add `dc_chat_is_protection_broken()` C API.
|
||||
|
||||
### CI
|
||||
|
||||
- Run Rust tests with `RUST_BACKTRACE` set.
|
||||
- Replace `master` branch with `main`. Run CI only on `main` branch pushes.
|
||||
- Test `deltachat-rpc-client` on Windows.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document how logs and error messages should be formatted in `CONTRIBUTING.md`.
|
||||
- Clarify transitive behaviour of `dc_contact_is_verfified()`.
|
||||
- Document `configured_addr`.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Add lockfile to account manager ([#4314](https://github.com/deltachat/deltachat-core-rust/pull/4314)).
|
||||
- Don't show a contact as verified if their key changed since the verification ([#4574](https://github.com/deltachat/deltachat-core-rust/pull/4574)).
|
||||
- deltachat-rpc-server: Add `--openrpc` option to print OpenRPC specification for JSON-RPC API. This specification can be used to generate JSON-RPC API clients.
|
||||
- Track whether contact is a bot or not ([#4821](https://github.com/deltachat/deltachat-core-rust/pull/4821)).
|
||||
- Replace `Config::SendSyncMsgs` with `SyncMsgs` ([#4817](https://github.com/deltachat/deltachat-core-rust/pull/4817)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Don't create 1:1 chat as protected for contact who doesn't prefer to encrypt ([#4538](https://github.com/deltachat/deltachat-core-rust/pull/4538)).
|
||||
- Allow to save a draft if the verification is broken ([#4542](https://github.com/deltachat/deltachat-core-rust/pull/4542)).
|
||||
- Fix info-message orderings of verified 1:1 chats ([#4545](https://github.com/deltachat/deltachat-core-rust/pull/4545)).
|
||||
- Fix example; this was changed some time ago, see https://docs.webxdc.org/spec.html#sendupdate
|
||||
- `receive_imf`: Update peerstate from db after handling Securejoin handshake ([#4600](https://github.com/deltachat/deltachat-core-rust/pull/4600)).
|
||||
- Sort old incoming messages below all outgoing ones ([#4621](https://github.com/deltachat/deltachat-core-rust/pull/4621)).
|
||||
- Do not mark non-verified group chats as verified when using securejoin.
|
||||
- `receive_imf`: Set protection only for Chattype::Single ([#4597](https://github.com/deltachat/deltachat-core-rust/pull/4597)).
|
||||
- Return from `dc_get_chatlist(DC_GCL_FOR_FORWARDING)` only chats where we can send ([#4616](https://github.com/deltachat/deltachat-core-rust/pull/4616)).
|
||||
- Clear VerifiedOneOnOneChats config on backup ([#4615](https://github.com/deltachat/deltachat-core-rust/pull/4615)).
|
||||
- Try removal of accounts multiple times with timeouts in case the database file is blocked (restore `try_many_times` workaround).
|
||||
|
||||
### Build system
|
||||
|
||||
- Remove examples/simple.rs.
|
||||
- Increase MSRV to 1.70.0.
|
||||
- Update dependencies.
|
||||
- Switch to iroh 0.4.x fork with updated dependencies.
|
||||
|
||||
## [1.126.1] - 2023-10-24
|
||||
|
||||
### Fixes
|
||||
|
||||
- Do not hardcode version in deltachat-rpc-server source package.
|
||||
- Do not interrupt IMAP loop from `get_connectivity_html()`.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- imap: Buffer `STARTTLS` command.
|
||||
|
||||
### Build system
|
||||
|
||||
- Build `deltachat-rpc-server` binary for aarch64 macOS.
|
||||
- Build `deltachat-rpc-server` wheels for macOS and Windows.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Remove job queue.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- cargo: Update `ahash` to make `cargo-deny` happy.
|
||||
|
||||
## [1.126.0] - 2023-10-22
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Allow to filter by unread in `chatlist:try_load` ([#4824](https://github.com/deltachat/deltachat-core-rust/pull/4824)).
|
||||
- Add `misc_send_draft()` to JSON-RPC API ([#4839](https://github.com/deltachat/deltachat-core-rust/pull/4839)).
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- [**breaking**] Make broadcast lists create their own chat ([#4644](https://github.com/deltachat/deltachat-core-rust/pull/4644)).
|
||||
- This means that UIs need to ask for the name when creating a broadcast list, similar to <https://github.com/deltachat/deltachat-android/pull/2653>.
|
||||
- Add self-address to backup filename ([#4820](https://github.com/deltachat/deltachat-core-rust/pull/4820))
|
||||
|
||||
### CI
|
||||
|
||||
- Build Python wheels for deltachat-rpc-server.
|
||||
|
||||
### Build system
|
||||
|
||||
- Strip release binaries.
|
||||
- Workaround OpenSSL crate expecting libatomic to be available.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Set `soft_heap_limit` on SQLite database.
|
||||
- imap: Fallback to `STATUS` if `SELECT` did not return UIDNEXT.
|
||||
|
||||
## [1.125.0] - 2023-10-14
|
||||
|
||||
### API-Changes
|
||||
@@ -963,7 +1883,7 @@ Bugfix release attempting to fix the [iOS build error](https://github.com/deltac
|
||||
|
||||
### Changes
|
||||
- Look at Authentication-Results. Don't accept Autocrypt key changes
|
||||
if they come with negative authentiation results while this contact
|
||||
if they come with negative authentication results while this contact
|
||||
sent emails with positive authentication results in the past. #3583
|
||||
- jsonrpc in cffi also sends events now #3662
|
||||
- jsonrpc: new format for events and better typescript autocompletion
|
||||
@@ -2549,7 +3469,7 @@ Bugfix release attempting to fix the [iOS build error](https://github.com/deltac
|
||||
|
||||
- delete all consumed secure-join handshake messagess #1209 #1212
|
||||
|
||||
- rust-level cleanups #1218 #1217 #1210 #1205
|
||||
- Rust-level cleanups #1218 #1217 #1210 #1205
|
||||
|
||||
- python-level cleanups #1204 #1202 #1201
|
||||
|
||||
@@ -2915,3 +3835,40 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.124.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.123.0...v1.124.0
|
||||
[1.124.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.124.0...v1.124.1
|
||||
[1.125.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.124.1...v1.125.0
|
||||
[1.126.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.125.0...v1.126.0
|
||||
[1.126.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.126.0...v1.126.1
|
||||
[1.127.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.126.1...v1.127.0
|
||||
[1.127.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.127.0...v1.127.1
|
||||
[1.127.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.127.1...v1.127.2
|
||||
[1.128.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.127.2...v1.128.0
|
||||
[1.129.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.128.0...v1.129.0
|
||||
[1.129.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.129.0...v1.129.1
|
||||
[1.130.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.129.1...v1.130.0
|
||||
[1.131.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.130.0...v1.131.0
|
||||
[1.131.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.0...v1.131.1
|
||||
[1.131.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.1...v1.131.2
|
||||
[1.131.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.2...v1.131.3
|
||||
[1.131.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.3...v1.131.4
|
||||
[1.131.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.4...v1.131.5
|
||||
[1.131.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.5...v1.131.6
|
||||
[1.131.7]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.6...v1.131.7
|
||||
[1.131.8]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.7...v1.131.8
|
||||
[1.131.9]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.8...v1.131.9
|
||||
[1.132.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.9...v1.132.0
|
||||
[1.132.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.0...v1.132.1
|
||||
[1.133.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.1...v1.133.0
|
||||
[1.133.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.0...v1.133.1
|
||||
[1.133.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.1...v1.133.2
|
||||
[1.134.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.2...v1.134.0
|
||||
[1.135.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.134.0...v1.135.0
|
||||
[1.135.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.135.0...v1.135.1
|
||||
[1.136.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.135.1...v1.136.0
|
||||
[1.136.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.0...v1.136.1
|
||||
[1.136.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.1...v1.136.2
|
||||
[1.136.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.2...v1.136.3
|
||||
[1.136.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.3...v1.136.4
|
||||
[1.136.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.4...v1.136.5
|
||||
[1.136.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.5...v1.136.6
|
||||
[1.137.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.6...v1.137.0
|
||||
[1.137.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.0...v1.137.1
|
||||
[1.137.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.1...v1.137.2
|
||||
|
||||
@@ -14,24 +14,14 @@ endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"target/release/libdeltachat.a"
|
||||
"target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"target/release/pkgconfig/deltachat.pc"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
|
||||
COMMAND
|
||||
PREFIX=${CMAKE_INSTALL_PREFIX}
|
||||
LIBDIR=${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
INCLUDEDIR=${CMAKE_INSTALL_FULL_INCLUDEDIR}
|
||||
${CARGO} build --release --no-default-features --features jsonrpc
|
||||
|
||||
# Build in `deltachat-ffi` directory instead of using
|
||||
# `--package deltachat_ffi` to avoid feature resolver version
|
||||
# "1" bug which makes `--no-default-features` affect only
|
||||
# `deltachat`, but not `deltachat-ffi` package.
|
||||
#
|
||||
# We can't enable version "2" resolver [1] because it is not
|
||||
# stable yet on rust 1.50.0.
|
||||
#
|
||||
# [1] https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags
|
||||
${CARGO} build --target-dir=${CMAKE_BINARY_DIR}/target --release --no-default-features --features jsonrpc
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deltachat-ffi
|
||||
)
|
||||
|
||||
@@ -39,12 +29,12 @@ add_custom_target(
|
||||
lib_deltachat
|
||||
ALL
|
||||
DEPENDS
|
||||
"target/release/libdeltachat.a"
|
||||
"target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"target/release/pkgconfig/deltachat.pc"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
|
||||
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
|
||||
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
|
||||
)
|
||||
|
||||
install(FILES "deltachat-ffi/deltachat.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(FILES "target/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "target/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "target/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
@@ -76,6 +76,40 @@ If you have multiple changes in one PR, create multiple conventional commits, an
|
||||
[Conventional Commits]: https://www.conventionalcommits.org/
|
||||
[git-cliff]: https://git-cliff.org/
|
||||
|
||||
### Errors
|
||||
|
||||
Delta Chat core mostly uses [`anyhow`](https://docs.rs/anyhow/) errors.
|
||||
When using [`Context`](https://docs.rs/anyhow/latest/anyhow/trait.Context.html),
|
||||
capitalize it but do not add a full stop as the contexts will be separated by `:`.
|
||||
For example:
|
||||
```
|
||||
.with_context(|| format!("Unable to trash message {msg_id}"))
|
||||
```
|
||||
|
||||
All errors should be handled in one of these ways:
|
||||
- With `if let Err() =` (incl. logging them into `warn!()`/`err!()`).
|
||||
- With `.log_err().ok()`.
|
||||
- Bubbled up with `?`.
|
||||
|
||||
`backtrace` feature is enabled for `anyhow` crate
|
||||
and `debug = 1` option is set in the test profile.
|
||||
This allows to run `RUST_BACKTRACE=1 cargo test`
|
||||
and get a backtrace with line numbers in resultified tests
|
||||
which return `anyhow::Result`.
|
||||
|
||||
### Logging
|
||||
|
||||
For logging, use `info!`, `warn!` and `error!` macros.
|
||||
Log messages should be capitalized and have a full stop in the end. For example:
|
||||
```
|
||||
info!(context, "Ignoring addition of {added_addr:?} to {chat_id}.");
|
||||
```
|
||||
|
||||
Format anyhow errors with `{:#}` to print all the contexts like this:
|
||||
```
|
||||
error!(context, "Failed to set selfavatar timestamp: {err:#}.");
|
||||
```
|
||||
|
||||
### Reviewing
|
||||
|
||||
Once a PR has an approval and passes CI, it can be merged.
|
||||
|
||||
2185
Cargo.lock
generated
2185
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
81
Cargo.toml
81
Cargo.toml
@@ -1,9 +1,10 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.125.0"
|
||||
version = "1.137.2"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.65"
|
||||
rust-version = "1.77"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
@@ -11,6 +12,10 @@ panic = 'abort'
|
||||
opt-level = 1
|
||||
|
||||
[profile.test]
|
||||
# Make anyhow `backtrace` feature useful.
|
||||
# With `debug = 0` there are no line numbers in the backtrace
|
||||
# produced with RUST_BACKTRACE=1.
|
||||
debug = 1
|
||||
opt-level = 0
|
||||
|
||||
# Always optimize dependencies.
|
||||
@@ -24,36 +29,38 @@ lto = true
|
||||
panic = 'abort'
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
|
||||
[patch.crates-io]
|
||||
quinn-udp = { git = "https://github.com/quinn-rs/quinn", branch="main" }
|
||||
quinn-proto = { git = "https://github.com/quinn-rs/quinn", branch="main" }
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
deltachat-time = { path = "./deltachat-time" }
|
||||
format-flowed = { path = "./format-flowed" }
|
||||
ratelimit = { path = "./deltachat-ratelimit" }
|
||||
|
||||
anyhow = "1"
|
||||
async-channel = "1.8.0"
|
||||
async-imap = { version = "0.9.1", default-features = false, features = ["runtime-tokio"] }
|
||||
async-channel = "2.0.0"
|
||||
async-imap = { version = "0.9.7", default-features = false, features = ["runtime-tokio"] }
|
||||
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
|
||||
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
||||
async_zip = { version = "0.0.12", default-features = false, features = ["deflate", "fs"] }
|
||||
backtrace = "0.3"
|
||||
base64 = "0.21"
|
||||
brotli = { version = "3.3", default-features=false, features = ["std"] }
|
||||
brotli = { version = "4", default-features=false, features = ["std"] }
|
||||
bitflags = "1.3"
|
||||
bstr = { version = "1.4.0", default-features=false, features = ["std", "alloc"] }
|
||||
chrono = { version = "0.4", default-features=false, features = ["clock", "std"] }
|
||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch = "master" }
|
||||
escaper = "0.1"
|
||||
fast-socks5 = "0.8"
|
||||
fast-socks5 = "0.9"
|
||||
fd-lock = "4"
|
||||
futures = "0.3"
|
||||
futures-lite = "1.13.0"
|
||||
futures-lite = "2.3.0"
|
||||
hex = "0.4.0"
|
||||
hickory-resolver = "0.24"
|
||||
humansize = "2"
|
||||
image = { version = "0.24.6", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh = { version = "0.4.1", default-features = false }
|
||||
image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh = { version = "0.4.2", default-features = false }
|
||||
kamadak-exif = "0.5"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
libc = "0.2"
|
||||
@@ -65,45 +72,55 @@ num-traits = "0.2"
|
||||
once_cell = "1.18.0"
|
||||
percent-encoding = "2.3"
|
||||
parking_lot = "0.12"
|
||||
pgp = { version = "0.10", default-features = false }
|
||||
pgp = { version = "0.11", default-features = false }
|
||||
pin-project = "1"
|
||||
pretty_env_logger = { version = "0.5", optional = true }
|
||||
qrcodegen = "1.7.0"
|
||||
quick-xml = "0.29"
|
||||
quick-xml = "0.31"
|
||||
quoted_printable = "0.5"
|
||||
rand = "0.8"
|
||||
regex = "1.8"
|
||||
reqwest = { version = "0.11.18", features = ["json"] }
|
||||
rusqlite = { version = "0.29", features = ["sqlcipher"] }
|
||||
regex = "1.10"
|
||||
reqwest = { version = "0.12.2", features = ["json"] }
|
||||
rusqlite = { version = "0.31", features = ["sqlcipher"] }
|
||||
rust-hsluv = "0.1"
|
||||
sanitize-filename = "0.4"
|
||||
serde_json = "1.0"
|
||||
sanitize-filename = "0.5"
|
||||
serde_json = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sha-1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
smallvec = "1"
|
||||
strum = "0.25"
|
||||
strum_macros = "0.25"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
tagger = "4.3.4"
|
||||
textwrap = "0.16.0"
|
||||
textwrap = "0.16.1"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tokio-io-timeout = "1.2.0"
|
||||
tokio-stream = { version = "0.1.14", features = ["fs"] }
|
||||
tokio-stream = { version = "0.1.15", features = ["fs"] }
|
||||
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
||||
tokio-util = "0.7.8"
|
||||
toml = "0.7"
|
||||
trust-dns-resolver = "0.22"
|
||||
tokio-util = "0.7.9"
|
||||
toml = "0.8"
|
||||
url = "2"
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
|
||||
# Pin OpenSSL to 3.1 releases.
|
||||
# OpenSSL 3.2 has a regression tracked at <https://github.com/openssl/openssl/issues/23376>
|
||||
# which results in broken `deltachat-rpc-server` binaries when cross-compiled using Zig toolchain.
|
||||
# See <https://github.com/deltachat/deltachat-core-rust/issues/5206> for Delta Chat issue.
|
||||
# According to <https://www.openssl.org/policies/releasestrat.html>
|
||||
# 3.1 branch will be supported until 2025-03-14.
|
||||
openssl-src = "~300.1"
|
||||
|
||||
[dev-dependencies]
|
||||
ansi_term = "0.12.0"
|
||||
anyhow = { version = "1", features = ["backtrace"] } # Enable `backtrace` feature in tests.
|
||||
criterion = { version = "0.5.1", features = ["async_tokio"] }
|
||||
futures-lite = "1.13"
|
||||
futures-lite = "2.3.0"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.5"
|
||||
proptest = { version = "1", default-features = false, features = ["std"] }
|
||||
tempfile = "3"
|
||||
testdir = "0.8.0"
|
||||
testdir = "0.9.0"
|
||||
tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "macros"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
@@ -115,14 +132,10 @@ members = [
|
||||
"deltachat-rpc-server",
|
||||
"deltachat-ratelimit",
|
||||
"deltachat-repl",
|
||||
"deltachat-time",
|
||||
"format-flowed",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "simple"
|
||||
path = "examples/simple.rs"
|
||||
|
||||
|
||||
[[bench]]
|
||||
name = "create_account"
|
||||
harness = false
|
||||
|
||||
18
README.md
18
README.md
@@ -1,8 +1,16 @@
|
||||
# Delta Chat Rust
|
||||
<p align="center">
|
||||
<img alt="Delta Chat Logo" height="200px" src="https://raw.githubusercontent.com/deltachat/deltachat-pages/master/assets/blog/rust-delta.png">
|
||||
</p>
|
||||
|
||||
> Deltachat-core written in Rust
|
||||
<p align="center">
|
||||
<a href="https://github.com/deltachat/deltachat-core-rust/actions/workflows/ci.yml">
|
||||
<img alt="Rust CI" src="https://github.com/deltachat/deltachat-core-rust/actions/workflows/ci.yml/badge.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[](https://github.com/deltachat/deltachat-core-rust/actions/workflows/ci.yml)
|
||||
<p align="center">
|
||||
The core library for Delta Chat, written in Rust
|
||||
</p>
|
||||
|
||||
## Installing Rust and Cargo
|
||||
|
||||
@@ -19,7 +27,7 @@ $ curl https://sh.rustup.rs -sSf | sh
|
||||
Compile and run Delta Chat Core command line utility, using `cargo`:
|
||||
|
||||
```
|
||||
$ RUST_LOG=deltachat_repl=info cargo run -p deltachat-repl -- ~/deltachat-db
|
||||
$ cargo run -p deltachat-repl -- ~/deltachat-db
|
||||
```
|
||||
where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist.
|
||||
|
||||
@@ -113,7 +121,7 @@ $ cargo build -p deltachat_ffi --release
|
||||
|
||||
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
|
||||
|
||||
- `RUST_LOG=deltachat_repl=info,async_imap=trace,async_smtp=trace`: enable IMAP and
|
||||
- `RUST_LOG=async_imap=trace,async_smtp=trace`: enable IMAP and
|
||||
SMTP tracing in addition to info messages.
|
||||
|
||||
### Expensive tests
|
||||
|
||||
13
RELEASE.md
13
RELEASE.md
@@ -9,13 +9,16 @@ For example, to release version 1.116.0 of the core, do the following steps.
|
||||
|
||||
3. Update the changelog: `git cliff --unreleased --tag 1.116.0 --prepend CHANGELOG.md` or `git cliff -u -t 1.116.0 -p CHANGELOG.md`.
|
||||
|
||||
4. Update the version by running `scripts/set_core_version.py 1.116.0`.
|
||||
4. add a link to compare previous with current version to the end of CHANGELOG.md:
|
||||
`[1.116.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.115.2...v1.116.0`
|
||||
|
||||
5. Commit the changes as `chore(release): prepare for 1.116.0`.
|
||||
5. Update the version by running `scripts/set_core_version.py 1.116.0`.
|
||||
|
||||
6. Commit the changes as `chore(release): prepare for 1.116.0`.
|
||||
Optionally, use a separate branch like `prep-1.116.0` for this commit and open a PR for review.
|
||||
|
||||
6. Tag the release: `git tag -a v1.116.0`.
|
||||
7. Tag the release: `git tag -a v1.116.0`.
|
||||
|
||||
7. Push the release tag: `git push origin v1.116.0`.
|
||||
8. Push the release tag: `git push origin v1.116.0`.
|
||||
|
||||
8. Create a GitHub release: `gh release create v1.116.0 -n ''`.
|
||||
9. Create a GitHub release: `gh release create v1.116.0 -n ''`.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 121 KiB |
@@ -8,7 +8,8 @@ async fn create_accounts(n: u32) {
|
||||
let dir = tempdir().unwrap();
|
||||
let p: PathBuf = dir.path().join("accounts");
|
||||
|
||||
let mut accounts = Accounts::new(p.clone()).await.unwrap();
|
||||
let writable = true;
|
||||
let mut accounts = Accounts::new(p.clone(), writable).await.unwrap();
|
||||
|
||||
for expected_id in 2..n {
|
||||
let id = accounts.add_account().await.unwrap();
|
||||
|
||||
16
cliff.toml
16
cliff.toml
@@ -54,7 +54,7 @@ header = """
|
||||
# Changelog\n
|
||||
"""
|
||||
# template for the changelog body
|
||||
# https://tera.netlify.app/docs/#introduction
|
||||
# https://keats.github.io/tera/docs/#templates
|
||||
body = """
|
||||
{% if version %}\
|
||||
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
@@ -77,3 +77,17 @@ body = """
|
||||
"""
|
||||
# remove the leading and trailing whitespace from the template
|
||||
trim = true
|
||||
footer = """
|
||||
{% for release in releases -%}
|
||||
{% if release.version -%}
|
||||
{% if release.previous.version -%}
|
||||
[{{ release.version | trim_start_matches(pat="v") }}]: \
|
||||
https://github.com/deltachat/deltachat-core-rust\
|
||||
/compare/{{ release.previous.version }}..{{ release.version }}
|
||||
{% endif -%}
|
||||
{% else -%}
|
||||
[unreleased]: https://github.com/deltachat/deltachat-core-rust\
|
||||
/compare/{{ release.previous.version }}..HEAD
|
||||
{% endif -%}
|
||||
{% endfor %}
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.125.0"
|
||||
version = "1.137.2"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -846,7 +846,7 @@ EXCLUDE_PATTERNS =
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
######################################################
|
||||
EXCLUDE_SYMBOLS = dc_aheader_t dc_apeerstate_t dc_e2ee_helper_t dc_imap_t dc_job*_t dc_key_t dc_keyring_t dc_loginparam_t dc_mime*_t
|
||||
EXCLUDE_SYMBOLS = dc_aheader_t dc_apeerstate_t dc_e2ee_helper_t dc_imap_t dc_job*_t dc_key_t dc_loginparam_t dc_mime*_t
|
||||
EXCLUDE_SYMBOLS += dc_saxparser_t dc_simplify_t dc_smtp_t dc_sqlite3_t dc_strbuilder_t dc_param_t dc_hash_t dc_hashelem_t
|
||||
EXCLUDE_SYMBOLS += _dc_* jsmn*
|
||||
######################################################
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="modules" visible="yes" title="Constants" intro="Here is a list of constants:"/>
|
||||
<tab type="topics" visible="yes" title="Constants" intro="Here is a list of constants:"/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
|
||||
@@ -25,7 +25,6 @@ typedef struct _dc_event dc_event_t;
|
||||
typedef struct _dc_event_emitter dc_event_emitter_t;
|
||||
typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t;
|
||||
typedef struct _dc_backup_provider dc_backup_provider_t;
|
||||
typedef struct _dc_http_response dc_http_response_t;
|
||||
|
||||
// Alias for backwards compatibility, use dc_event_emitter_t instead.
|
||||
typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
|
||||
@@ -384,7 +383,12 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
/**
|
||||
* Configure the context. The configuration is handled by key=value pairs as:
|
||||
*
|
||||
* - `addr` = address to display (always needed)
|
||||
* - `addr` = Email address to use for configuration.
|
||||
* If dc_configure() fails this is not the email address actually in use.
|
||||
* Use `configured_addr` to find out the email address actually in use.
|
||||
* - `configured_addr` = Email address actually in use.
|
||||
* Unless for testing, do not set this value using dc_set_config().
|
||||
* Instead, set `addr` and call dc_configure().
|
||||
* - `mail_server` = IMAP-server, guessed if left out
|
||||
* - `mail_user` = IMAP-username, guessed if left out
|
||||
* - `mail_pw` = IMAP-password (always needed)
|
||||
@@ -419,19 +423,16 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* Sending messages to self is needed for a proper multi-account setup,
|
||||
* however, on the other hand, may lead to unwanted notifications in non-delta clients.
|
||||
* - `sentbox_watch`= 1=watch `Sent`-folder for changes,
|
||||
* 0=do not watch the `Sent`-folder (default),
|
||||
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
|
||||
* 0=do not watch the `Sent`-folder (default).
|
||||
* - `mvbox_move` = 1=detect chat messages,
|
||||
* move them to the `DeltaChat` folder,
|
||||
* and watch the `DeltaChat` folder for updates (default),
|
||||
* 0=do not move chat-messages
|
||||
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
|
||||
* - `only_fetch_mvbox` = 1=Do not fetch messages from folders other than the
|
||||
* `DeltaChat` folder. Messages will still be fetched from the
|
||||
* spam folder and `sendbox_watch` will also still be respected
|
||||
* if enabled.
|
||||
* 0=watch all folders normally (default)
|
||||
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
|
||||
* - `show_emails` = DC_SHOW_EMAILS_OFF (0)=
|
||||
* show direct replies to chats only,
|
||||
* DC_SHOW_EMAILS_ACCEPTED_CONTACTS (1)=
|
||||
@@ -506,6 +507,13 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* - `gossip_period` = How often to gossip Autocrypt keys in chats with multiple recipients, in
|
||||
* seconds. 2 days by default.
|
||||
* This is not supposed to be changed by UIs and only used for testing.
|
||||
* - `verified_one_on_one_chats` = Feature flag for verified 1:1 chats; the UI should set it
|
||||
* to 1 if it supports verified 1:1 chats.
|
||||
* Regardless of this setting, `dc_chat_is_protected()` returns true while the key is verified,
|
||||
* and when the key changes, an info message is posted into the chat.
|
||||
* 0=Nothing else happens when the key changes.
|
||||
* 1=After the key changed, `dc_chat_can_send()` returns false and `dc_chat_is_protection_broken()` returns true
|
||||
* until `dc_accept_chat()` is called.
|
||||
* - `ui.*` = All keys prefixed by `ui.` can be used by the user-interfaces for system-specific purposes.
|
||||
* The prefix should be followed by the system and maybe subsystem,
|
||||
* e.g. `ui.desktop.foo`, `ui.desktop.linux.bar`, `ui.android.foo`, `ui.dc40.bar`, `ui.bot.simplebot.baz`.
|
||||
@@ -678,8 +686,25 @@ int dc_get_connectivity (dc_context_t* context);
|
||||
char* dc_get_connectivity_html (dc_context_t* context);
|
||||
|
||||
|
||||
#define DC_PUSH_NOT_CONNECTED 0
|
||||
#define DC_PUSH_HEARTBEAT 1
|
||||
#define DC_PUSH_CONNECTED 2
|
||||
|
||||
/**
|
||||
* Get the current push notification state.
|
||||
* One of:
|
||||
* - DC_PUSH_NOT_CONNECTED
|
||||
* - DC_PUSH_HEARTBEAT
|
||||
* - DC_PUSH_CONNECTED
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @return Push notification state.
|
||||
*/
|
||||
int dc_get_push_state (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Standalone version of dc_accounts_all_work_done().
|
||||
* Only used by the python tests.
|
||||
*/
|
||||
int dc_all_work_done (dc_context_t* context);
|
||||
@@ -884,7 +909,8 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha
|
||||
* - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT
|
||||
* is added as needed.
|
||||
* @param query_str An optional query for filtering the list. Only chats matching this query
|
||||
* are returned. Give NULL for no filtering.
|
||||
* are returned. Give NULL for no filtering. When `is:unread` is contained in the query,
|
||||
* the chatlist is filtered such that only chats with unread messages show up.
|
||||
* @param query_id An optional contact ID for filtering the list. Only chats including this contact ID
|
||||
* are returned. Give 0 for no filtering.
|
||||
* @return A chatlist as an dc_chatlist_t object.
|
||||
@@ -1099,6 +1125,7 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
|
||||
* received overrides all previously received reactions. It is
|
||||
* possible to remove all reactions by sending an empty string.
|
||||
*
|
||||
* @deprecated 2023-11-27, use jsonrpc method `send_reaction` instead
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param msg_id ID of the message you react to.
|
||||
@@ -1111,6 +1138,7 @@ uint32_t dc_send_reaction (dc_context_t* context, uint32_t msg_id, char *reactio
|
||||
/**
|
||||
* Get a structure with reactions to the message.
|
||||
*
|
||||
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param msg_id The message ID to get reactions for.
|
||||
@@ -1124,7 +1152,7 @@ dc_reactions_t* dc_get_msg_reactions (dc_context_t *context, int msg_id);
|
||||
*
|
||||
* In JS land, that would be mapped to something as:
|
||||
* ```
|
||||
* success = window.webxdc.sendUpdate('{"action":"move","src":"A3","dest":"B4"}', 'move A3 B4');
|
||||
* success = window.webxdc.sendUpdate('{payload: {"action":"move","src":"A3","dest":"B4"}}', 'move A3 B4');
|
||||
* ```
|
||||
* `context` and `msg_id` are not needed in JS as those are unique within a webxdc instance.
|
||||
* See dc_get_webxdc_status_updates() for the receiving counterpart.
|
||||
@@ -1506,24 +1534,6 @@ dc_array_t* dc_get_chat_media (dc_context_t* context, uint32_t ch
|
||||
uint32_t dc_get_next_media (dc_context_t* context, uint32_t msg_id, int dir, int msg_type, int msg_type2, int msg_type3);
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable protection against active attacks.
|
||||
* To enable protection, it is needed that all members are verified;
|
||||
* if this condition is met, end-to-end-encryption is always enabled
|
||||
* and only the verified keys are used.
|
||||
*
|
||||
* Sends out #DC_EVENT_CHAT_MODIFIED on changes
|
||||
* and #DC_EVENT_MSGS_CHANGED if a status message was sent.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param chat_id The ID of the chat to change the protection for.
|
||||
* @param protect 1=protect chat, 0=unprotect chat
|
||||
* @return 1=success, 0=error, e.g. some members may be unverified
|
||||
*/
|
||||
int dc_set_chat_protection (dc_context_t* context, uint32_t chat_id, int protect);
|
||||
|
||||
|
||||
/**
|
||||
* Set chat visibility to pinned, archived or normal.
|
||||
*
|
||||
@@ -1717,24 +1727,12 @@ uint32_t dc_create_group_chat (dc_context_t* context, int protect
|
||||
* Create a new broadcast list.
|
||||
*
|
||||
* Broadcast lists are similar to groups on the sending device,
|
||||
* however, recipients get the messages in normal one-to-one chats
|
||||
* and will not be aware of other members.
|
||||
* however, recipients get the messages in a read-only chat
|
||||
* and will see who the other members are.
|
||||
*
|
||||
* Replies to broadcasts go only to the sender
|
||||
* and not to all broadcast recipients.
|
||||
* Moreover, replies will not appear in the broadcast list
|
||||
* but in the one-to-one chat with the person answering.
|
||||
*
|
||||
* The name and the image of the broadcast list is set automatically
|
||||
* and is visible to the sender only.
|
||||
* Not asking for these data allows more focused creation
|
||||
* and we bypass the question who will get which data.
|
||||
* Also, many users will have at most one broadcast list
|
||||
* so, a generic name and image is sufficient at the first place.
|
||||
*
|
||||
* Later on, however, the name can be changed using dc_set_chat_name().
|
||||
* The image cannot be changed to have a unique, recognizable icon in the chat lists.
|
||||
* All in all, this is also what other messengers are doing here.
|
||||
* For historical reasons, this function does not take a name directly,
|
||||
* instead you have to set the name using dc_set_chat_name()
|
||||
* after creating the broadcast list.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
@@ -2277,8 +2275,7 @@ dc_contact_t* dc_get_contact (dc_context_t* context, uint32_t co
|
||||
* the backup is not encrypted.
|
||||
* The backup contains all contacts, chats, images and other data and device independent settings.
|
||||
* The backup does not contain device dependent settings as ringtones or LED notification settings.
|
||||
* The name of the backup is typically `delta-chat-<day>.tar`, if more than one backup is create on a day,
|
||||
* the format is `delta-chat-<day>-<number>.tar`
|
||||
* The name of the backup is `delta-chat-backup-<day>-<number>-<addr>.tar`.
|
||||
*
|
||||
* - **DC_IMEX_IMPORT_BACKUP** (12) - `param1` is the file (not: directory) to import. `param2` is the passphrase.
|
||||
* The file is normally created by DC_IMEX_EXPORT_BACKUP and detected by dc_imex_has_backup(). Importing a backup
|
||||
@@ -2579,7 +2576,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
|
||||
* the Verified-Group-Invite protocol is offered in the QR code;
|
||||
* works for protected groups as well as for normal groups.
|
||||
* If set to 0, the Setup-Contact protocol is offered in the QR code.
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
* See https://securejoin.readthedocs.io/en/latest/new.html
|
||||
* for details about both protocols.
|
||||
* @return The text that should go to the QR code,
|
||||
* On errors, an empty QR code is returned, NULL is never returned.
|
||||
@@ -2615,7 +2612,7 @@ char* dc_get_securejoin_qr_svg (dc_context_t* context, uint32_
|
||||
*
|
||||
* Subsequent calls of dc_join_securejoin() will abort previous, unfinished handshakes.
|
||||
*
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
* See https://securejoin.readthedocs.io/en/latest/new.html
|
||||
* for details about both protocols.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
@@ -2959,16 +2956,18 @@ int dc_receive_backup (dc_context_t* context, const char* qr);
|
||||
* use dc_accounts_remove_account().
|
||||
*
|
||||
* @memberof dc_accounts_t
|
||||
* @param os_name
|
||||
* @param dir The directory to create the context-databases in.
|
||||
* If the directory does not exist,
|
||||
* dc_accounts_new() will try to create it.
|
||||
* @param writable Whether the returned account manager is writable, i.e. calling these functions on
|
||||
* it is possible: dc_accounts_add_account(), dc_accounts_add_closed_account(),
|
||||
* dc_accounts_migrate_account(), dc_accounts_remove_account(), dc_accounts_select_account().
|
||||
* @return An account manager object.
|
||||
* The object must be passed to the other account manager functions
|
||||
* and must be freed using dc_accounts_unref() after usage.
|
||||
* On errors, NULL is returned.
|
||||
*/
|
||||
dc_accounts_t* dc_accounts_new (const char* os_name, const char* dir);
|
||||
dc_accounts_t* dc_accounts_new (const char* dir, int writable);
|
||||
|
||||
|
||||
/**
|
||||
@@ -3099,23 +3098,6 @@ dc_context_t* dc_accounts_get_selected_account (dc_accounts_t* accounts);
|
||||
int dc_accounts_select_account (dc_accounts_t* accounts, uint32_t account_id);
|
||||
|
||||
|
||||
/**
|
||||
* This is meant especially for iOS, because iOS needs to tell the system when its background work is done.
|
||||
*
|
||||
* iOS can:
|
||||
* - call dc_start_io() (in case IO was not running)
|
||||
* - call dc_maybe_network()
|
||||
* - while dc_accounts_all_work_done() returns false:
|
||||
* - Wait for #DC_EVENT_CONNECTIVITY_CHANGED
|
||||
*
|
||||
* @memberof dc_accounts_t
|
||||
* @param accounts The account manager as created by dc_accounts_new().
|
||||
* @return Whether all accounts finished their background work.
|
||||
* #DC_EVENT_CONNECTIVITY_CHANGED will be sent when this turns to true.
|
||||
*/
|
||||
int dc_accounts_all_work_done (dc_accounts_t* accounts);
|
||||
|
||||
|
||||
/**
|
||||
* Start job and IMAP/SMTP tasks for all accounts managed by the account manager.
|
||||
* If IO is already running, nothing happens.
|
||||
@@ -3166,6 +3148,33 @@ void dc_accounts_maybe_network (dc_accounts_t* accounts);
|
||||
void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
|
||||
|
||||
|
||||
/**
|
||||
* Perform a background fetch for all accounts in parallel with a timeout.
|
||||
* Pauses the scheduler, fetches messages from imap and then resumes the scheduler.
|
||||
*
|
||||
* dc_accounts_background_fetch() was created for the iOS Background fetch.
|
||||
*
|
||||
* The `DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE` event is emitted at the end
|
||||
* even in case of timeout, unless the function fails and returns 0.
|
||||
* Process all events until you get this one and you can safely return to the background
|
||||
* without forgetting to create notifications caused by timing race conditions.
|
||||
*
|
||||
* @memberof dc_accounts_t
|
||||
* @param timeout The timeout in seconds
|
||||
* @return Return 1 if DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE was emitted and 0 otherwise.
|
||||
*/
|
||||
int dc_accounts_background_fetch (dc_accounts_t* accounts, uint64_t timeout);
|
||||
|
||||
|
||||
/**
|
||||
* Sets device token for Apple Push Notification service.
|
||||
* Returns immediately.
|
||||
*
|
||||
* @memberof dc_accounts_t
|
||||
* @param token Hexadecimal device token
|
||||
*/
|
||||
void dc_accounts_set_push_device_token (dc_accounts_t* accounts, const char *token);
|
||||
|
||||
/**
|
||||
* Create the event emitter that is used to receive events.
|
||||
*
|
||||
@@ -3747,9 +3756,22 @@ int dc_chat_can_send (const dc_chat_t* chat);
|
||||
|
||||
/**
|
||||
* Check if a chat is protected.
|
||||
* Protected chats contain only verified members and encryption is always enabled.
|
||||
* Protected chats are created using dc_create_group_chat() by setting the 'protect' parameter to 1.
|
||||
* The status can be changed using dc_set_chat_protection().
|
||||
*
|
||||
* End-to-end encryption is guaranteed in protected chats
|
||||
* and only verified contacts
|
||||
* as determined by dc_contact_is_verified()
|
||||
* can be added to protected chats.
|
||||
*
|
||||
* Protected chats are created using dc_create_group_chat()
|
||||
* by setting the 'protect' parameter to 1.
|
||||
* 1:1 chats become protected or unprotected automatically
|
||||
* if `verified_one_on_one_chats` setting is enabled.
|
||||
*
|
||||
* UI should display a green checkmark
|
||||
* in the chat title,
|
||||
* in the chatlist item
|
||||
* and in the chat profile
|
||||
* if chat protection is enabled.
|
||||
*
|
||||
* @memberof dc_chat_t
|
||||
* @param chat The chat object.
|
||||
@@ -3758,6 +3780,26 @@ int dc_chat_can_send (const dc_chat_t* chat);
|
||||
int dc_chat_is_protected (const dc_chat_t* chat);
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the chat was protected, and then an incoming message broke this protection.
|
||||
*
|
||||
* This function is only useful if the UI enabled the `verified_one_on_one_chats` feature flag,
|
||||
* otherwise it will return false for all chats.
|
||||
*
|
||||
* 1:1 chats are automatically set as protected when a contact is verified.
|
||||
* When a message comes in that is not encrypted / signed correctly,
|
||||
* the chat is automatically set as unprotected again.
|
||||
* dc_chat_is_protection_broken() will return true until dc_accept_chat() is called.
|
||||
*
|
||||
* The UI should let the user confirm that this is OK with a message like
|
||||
* `Bob sent a message from another device. Tap to learn more` and then call dc_accept_chat().
|
||||
* @memberof dc_chat_t
|
||||
* @param chat The chat object.
|
||||
* @return 1=chat protection broken, 0=otherwise.
|
||||
*/
|
||||
int dc_chat_is_protection_broken (const dc_chat_t* chat);
|
||||
|
||||
|
||||
/**
|
||||
* Check if locations are sent to the chat
|
||||
* at the time the object was created using dc_get_chat().
|
||||
@@ -3962,7 +4004,7 @@ int64_t dc_msg_get_received_timestamp (const dc_msg_t* msg);
|
||||
* Get the message time used for sorting.
|
||||
* This function returns the timestamp that is used for sorting the message
|
||||
* into lists as returned e.g. by dc_get_chat_msgs().
|
||||
* This may be the reveived time, the sending time or another time.
|
||||
* This may be the received time, the sending time or another time.
|
||||
*
|
||||
* To get the receiving time, use dc_msg_get_received_timestamp().
|
||||
* To get the sending time, use dc_msg_get_timestamp().
|
||||
@@ -4352,7 +4394,7 @@ int dc_msg_is_forwarded (const dc_msg_t* msg);
|
||||
* Check if the message is an informational message, created by the
|
||||
* device or by another users. Such messages are not "typed" by the user but
|
||||
* created due to other actions,
|
||||
* e.g. dc_set_chat_name(), dc_set_chat_profile_image(), dc_set_chat_protection()
|
||||
* e.g. dc_set_chat_name(), dc_set_chat_profile_image(),
|
||||
* or dc_add_contact_to_chat().
|
||||
*
|
||||
* These messages are typically shown in the center of the chat view,
|
||||
@@ -4379,6 +4421,9 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
||||
* Currently, the following types are defined:
|
||||
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
|
||||
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
|
||||
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
||||
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
|
||||
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
||||
*
|
||||
* Even when you display an icon,
|
||||
* you should still display the text of the informational message using dc_msg_get_text()
|
||||
@@ -4405,6 +4450,7 @@ int dc_msg_get_info_type (const dc_msg_t* msg);
|
||||
#define DC_INFO_EPHEMERAL_TIMER_CHANGED 10
|
||||
#define DC_INFO_PROTECTION_ENABLED 11
|
||||
#define DC_INFO_PROTECTION_DISABLED 12
|
||||
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
|
||||
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
|
||||
|
||||
/**
|
||||
@@ -4563,15 +4609,18 @@ int dc_msg_has_html (dc_msg_t* msg);
|
||||
* if they are larger than the limit set by the dc_set_config()-option `download_limit`.
|
||||
*
|
||||
* The function returns one of:
|
||||
* - @ref DC_DOWNLOAD_DONE - The message does not need any further download action
|
||||
* and should be rendered as usual.
|
||||
* - @ref DC_DOWNLOAD_AVAILABLE - There is additional content to download.
|
||||
* In addition to the usual message rendering,
|
||||
* the UI shall show a download button that calls dc_download_full_msg()
|
||||
* - @ref DC_DOWNLOAD_IN_PROGRESS - Download was started with dc_download_full_msg() and is still in progress.
|
||||
* If the download fails or succeeds,
|
||||
* the event @ref DC_EVENT_MSGS_CHANGED is emitted.
|
||||
* - @ref DC_DOWNLOAD_FAILURE - Download error, the user may start over calling dc_download_full_msg() again.
|
||||
* - @ref DC_DOWNLOAD_DONE - The message does not need any further download action
|
||||
* and should be rendered as usual.
|
||||
* - @ref DC_DOWNLOAD_AVAILABLE - There is additional content to download.
|
||||
* In addition to the usual message rendering,
|
||||
* the UI shall show a download button that calls dc_download_full_msg()
|
||||
* - @ref DC_DOWNLOAD_IN_PROGRESS - Download was started with dc_download_full_msg() and is still in progress.
|
||||
* If the download fails or succeeds,
|
||||
* the event @ref DC_EVENT_MSGS_CHANGED is emitted.
|
||||
*
|
||||
* - @ref DC_DOWNLOAD_UNDECIPHERABLE - The message does not need any futher download action.
|
||||
* It was fully downloaded, but we failed to decrypt it.
|
||||
* - @ref DC_DOWNLOAD_FAILURE - Download error, the user may start over calling dc_download_full_msg() again.
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The message object.
|
||||
@@ -5029,10 +5078,16 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a contact was verified. E.g. by a secure-join QR code scan
|
||||
* and if the key has not changed since this verification.
|
||||
* Check if the contact
|
||||
* can be added to verified chats,
|
||||
* i.e. has a verified key
|
||||
* and Autocrypt key matches the verified key.
|
||||
*
|
||||
* The UI may draw a checkbox or something like that beside verified contacts.
|
||||
* If contact is verified
|
||||
* UI should display green checkmark after the contact name
|
||||
* in contact list items,
|
||||
* in chat member list items
|
||||
* and in profiles if no chat with the contact exist (otherwise, use dc_chat_is_protected()).
|
||||
*
|
||||
* @memberof dc_contact_t
|
||||
* @param contact The contact object.
|
||||
@@ -5041,28 +5096,29 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
|
||||
*/
|
||||
int dc_contact_is_verified (dc_contact_t* contact);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the address that verified a contact
|
||||
*
|
||||
* The UI may use this in addition to a checkmark showing the verification status
|
||||
* Returns whether contact is a bot.
|
||||
*
|
||||
* @memberof dc_contact_t
|
||||
* @param contact The contact object.
|
||||
* @return
|
||||
* A string containing the verifiers address. If it is the same address as the contact itself,
|
||||
* we verified the contact ourself. If it is an empty string, we don't have verifier
|
||||
* information or the contact is not verified.
|
||||
* @deprecated 2023-09-28, use dc_contact_get_verifier_id instead
|
||||
* @return 0 if the contact is not a bot, 1 otherwise.
|
||||
*/
|
||||
char* dc_contact_get_verifier_addr (dc_contact_t* contact);
|
||||
int dc_contact_is_bot (dc_contact_t* contact);
|
||||
|
||||
|
||||
/**
|
||||
* Return the `ContactId` that verified a contact
|
||||
* Return the contact ID that verified a contact.
|
||||
*
|
||||
* The UI may use this in addition to a checkmark showing the verification status
|
||||
* If the function returns non-zero result,
|
||||
* display green checkmark in the profile and "Introduced by ..." line
|
||||
* with the name and address of the contact
|
||||
* formatted by dc_contact_get_name_n_addr.
|
||||
*
|
||||
* If this function returns a verifier,
|
||||
* this does not necessarily mean
|
||||
* you can add the contact to verified chats.
|
||||
* Use dc_contact_is_verified() to check
|
||||
* if a contact can be added to a verified chat instead.
|
||||
*
|
||||
* @memberof dc_contact_t
|
||||
* @param contact The contact object.
|
||||
@@ -5167,72 +5223,6 @@ int dc_provider_get_status (const dc_provider_t* prov
|
||||
void dc_provider_unref (dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Return an HTTP(S) GET response.
|
||||
* This function can be used to download remote content for HTML emails.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object to take proxy settings from.
|
||||
* @param url HTTP or HTTPS URL.
|
||||
* @return The response must be released using dc_http_response_unref() after usage.
|
||||
* NULL is returned on errors.
|
||||
*/
|
||||
dc_http_response_t* dc_get_http_response (const dc_context_t* context, const char* url);
|
||||
|
||||
|
||||
/**
|
||||
* @class dc_http_response_t
|
||||
*
|
||||
* An object containing an HTTP(S) GET response.
|
||||
* Created by dc_get_http_response().
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTTP response MIME type as a string, e.g. "text/plain" or "text/html".
|
||||
*
|
||||
* @memberof dc_http_response_t
|
||||
* @param response HTTP response as returned by dc_get_http_response().
|
||||
* @return The string which must be released using dc_str_unref() after usage. May be NULL.
|
||||
*/
|
||||
char* dc_http_response_get_mimetype (const dc_http_response_t* response);
|
||||
|
||||
/**
|
||||
* Returns HTTP response encoding, e.g. "utf-8".
|
||||
*
|
||||
* @memberof dc_http_response_t
|
||||
* @param response HTTP response as returned by dc_get_http_response().
|
||||
* @return The string which must be released using dc_str_unref() after usage. May be NULL.
|
||||
*/
|
||||
char* dc_http_response_get_encoding (const dc_http_response_t* response);
|
||||
|
||||
/**
|
||||
* Returns HTTP response contents.
|
||||
*
|
||||
* @memberof dc_http_response_t
|
||||
* @param response HTTP response as returned by dc_get_http_response().
|
||||
* @return The blob which must be released using dc_str_unref() after usage. NULL is never returned.
|
||||
*/
|
||||
uint8_t* dc_http_response_get_blob (const dc_http_response_t* response);
|
||||
|
||||
/**
|
||||
* Returns HTTP response content size.
|
||||
*
|
||||
* @memberof dc_http_response_t
|
||||
* @param response HTTP response as returned by dc_get_http_response().
|
||||
* @return The blob size.
|
||||
*/
|
||||
size_t dc_http_response_get_size (const dc_http_response_t* response);
|
||||
|
||||
/**
|
||||
* Free an HTTP response object.
|
||||
*
|
||||
* @memberof dc_http_response_t
|
||||
* @param response HTTP response as returned by dc_get_http_response().
|
||||
*/
|
||||
void dc_http_response_unref (const dc_http_response_t* response);
|
||||
|
||||
|
||||
/**
|
||||
* @class dc_lot_t
|
||||
*
|
||||
@@ -5332,6 +5322,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
|
||||
|
||||
/**
|
||||
* @class dc_reactions_t
|
||||
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
|
||||
*
|
||||
* An object representing all reactions for a single message.
|
||||
*/
|
||||
@@ -5339,6 +5330,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
|
||||
/**
|
||||
* Returns array of contacts which reacted to the given message.
|
||||
*
|
||||
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
|
||||
* @memberof dc_reactions_t
|
||||
* @param reactions The object containing message reactions.
|
||||
* @return array of contact IDs. Use dc_array_get_cnt() to get array length and
|
||||
@@ -5350,6 +5342,7 @@ dc_array_t* dc_reactions_get_contacts(dc_reactions_t* reactions);
|
||||
/**
|
||||
* Returns a string containing space-separated reactions of a single contact.
|
||||
*
|
||||
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
|
||||
* @memberof dc_reactions_t
|
||||
* @param reactions The object containing message reactions.
|
||||
* @param contact_id ID of the contact.
|
||||
@@ -5365,6 +5358,7 @@ char* dc_reactions_get_by_contact_id(dc_reactions_t* reactions, uint32
|
||||
*
|
||||
* Reactions objects are created by dc_get_msg_reactions().
|
||||
*
|
||||
* @deprecated 2023-11-27
|
||||
* @memberof dc_reactions_t
|
||||
* @param reactions The object to free.
|
||||
* If NULL is given, nothing is done.
|
||||
@@ -6062,10 +6056,12 @@ void dc_event_unref(dc_event_t* event);
|
||||
* Downloading a bunch of messages just finished. This is an
|
||||
* event to allow the UI to only show one notification per message bunch,
|
||||
* instead of cluttering the user with many notifications.
|
||||
* For each of the msg_ids, an additional #DC_EVENT_INCOMING_MSG event was emitted before.
|
||||
* UI may store #DC_EVENT_INCOMING_MSG events
|
||||
* and display notifications for all messages at once
|
||||
* when this event arrives.
|
||||
*
|
||||
* @param data1 0
|
||||
* @param data2 (char*) msg_ids, a json object with the message ids.
|
||||
* @param data2 0
|
||||
*/
|
||||
#define DC_EVENT_INCOMING_MSG_BUNCH 2006
|
||||
|
||||
@@ -6225,6 +6221,7 @@ void dc_event_unref(dc_event_t* event);
|
||||
* @param data2 (int) The 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)
|
||||
* 1000=vg-member-added/vc-contact-confirm received
|
||||
*/
|
||||
#define DC_EVENT_SECUREJOIN_JOINER_PROGRESS 2061
|
||||
|
||||
@@ -6248,6 +6245,18 @@ void dc_event_unref(dc_event_t* event);
|
||||
#define DC_EVENT_SELFAVATAR_CHANGED 2110
|
||||
|
||||
|
||||
/**
|
||||
* A multi-device synced config value changed. Maybe the app needs to refresh smth. For uniformity
|
||||
* this is emitted on the source device too. The value isn't reported, otherwise it would be logged
|
||||
* which might not be good for privacy. You can get the new value with
|
||||
* `dc_get_config(context, data2)`.
|
||||
*
|
||||
* @param data1 0
|
||||
* @param data2 (char*) Configuration key.
|
||||
*/
|
||||
#define DC_EVENT_CONFIG_SYNCED 2111
|
||||
|
||||
|
||||
/**
|
||||
* webxdc status update received.
|
||||
* To get the received status update, use dc_get_webxdc_status_updates() with
|
||||
@@ -6272,6 +6281,16 @@ void dc_event_unref(dc_event_t* event);
|
||||
|
||||
#define DC_EVENT_WEBXDC_INSTANCE_DELETED 2121
|
||||
|
||||
/**
|
||||
* Tells that the Background fetch was completed (or timed out).
|
||||
*
|
||||
* This event acts as a marker, when you reach this event you can be sure
|
||||
* that all events emitted during the background fetch were processed.
|
||||
*
|
||||
* This event is only emitted by the account manager
|
||||
*/
|
||||
|
||||
#define DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE 2200
|
||||
|
||||
/**
|
||||
* @}
|
||||
@@ -6417,22 +6436,27 @@ void dc_event_unref(dc_event_t* event);
|
||||
/**
|
||||
* Download not needed, see dc_msg_get_download_state() for details.
|
||||
*/
|
||||
#define DC_DOWNLOAD_DONE 0
|
||||
#define DC_DOWNLOAD_DONE 0
|
||||
|
||||
/**
|
||||
* Download available, see dc_msg_get_download_state() for details.
|
||||
*/
|
||||
#define DC_DOWNLOAD_AVAILABLE 10
|
||||
#define DC_DOWNLOAD_AVAILABLE 10
|
||||
|
||||
/**
|
||||
* Download failed, see dc_msg_get_download_state() for details.
|
||||
*/
|
||||
#define DC_DOWNLOAD_FAILURE 20
|
||||
#define DC_DOWNLOAD_FAILURE 20
|
||||
|
||||
/**
|
||||
* Download not needed, see dc_msg_get_download_state() for details.
|
||||
*/
|
||||
#define DC_DOWNLOAD_UNDECIPHERABLE 30
|
||||
|
||||
/**
|
||||
* Download in progress, see dc_msg_get_download_state() for details.
|
||||
*/
|
||||
#define DC_DOWNLOAD_IN_PROGRESS 1000
|
||||
#define DC_DOWNLOAD_IN_PROGRESS 1000
|
||||
|
||||
|
||||
|
||||
@@ -6597,7 +6621,7 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// - %1$s will be replaced by the name of the verified contact
|
||||
#define DC_STR_CONTACT_VERIFIED 35
|
||||
|
||||
/// "Cannot verify %1$s."
|
||||
/// "Cannot establish guaranteed end-to-end encryption with %1$s."
|
||||
///
|
||||
/// Used in status messages.
|
||||
/// - %1$s will be replaced by the name of the contact that cannot be verified
|
||||
@@ -6787,15 +6811,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// Used in error strings.
|
||||
#define DC_STR_ERROR_NO_NETWORK 87
|
||||
|
||||
/// "Chat protection enabled."
|
||||
///
|
||||
|
||||
/// @deprecated Deprecated, replaced by DC_STR_MSG_YOU_ENABLED_PROTECTION and DC_STR_MSG_PROTECTION_ENABLED_BY.
|
||||
#define DC_STR_PROTECTION_ENABLED 88
|
||||
|
||||
/// @deprecated Deprecated, replaced by DC_STR_MSG_YOU_DISABLED_PROTECTION and DC_STR_MSG_PROTECTION_DISABLED_BY.
|
||||
#define DC_STR_PROTECTION_DISABLED 89
|
||||
|
||||
/// "Reply"
|
||||
///
|
||||
/// Used in summaries.
|
||||
@@ -7044,6 +7059,8 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// "You added member %1$s."
|
||||
///
|
||||
/// Used in status messages.
|
||||
///
|
||||
/// `%1$s` will be replaced by the added member's name.
|
||||
#define DC_STR_ADD_MEMBER_BY_YOU 128
|
||||
|
||||
/// "Member %1$s added by %2$s."
|
||||
@@ -7240,26 +7257,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// `%2$s` will be replaced by name and address of the contact.
|
||||
#define DC_STR_EPHEMERAL_TIMER_WEEKS_BY_OTHER 157
|
||||
|
||||
/// "You enabled chat protection."
|
||||
///
|
||||
/// Used in status messages.
|
||||
#define DC_STR_PROTECTION_ENABLED_BY_YOU 158
|
||||
|
||||
/// "Chat protection enabled by %1$s."
|
||||
///
|
||||
/// `%1$s` will be replaced by name and address of the contact.
|
||||
///
|
||||
/// Used in status messages.
|
||||
#define DC_STR_PROTECTION_ENABLED_BY_OTHER 159
|
||||
|
||||
/// "You disabled chat protection."
|
||||
#define DC_STR_PROTECTION_DISABLED_BY_YOU 160
|
||||
|
||||
/// "Chat protection disabled by %1$s."
|
||||
///
|
||||
/// `%1$s` will be replaced by name and address of the contact.
|
||||
#define DC_STR_PROTECTION_DISABLED_BY_OTHER 161
|
||||
|
||||
/// "Scan to set up second device for %1$s"
|
||||
///
|
||||
/// `%1$s` will be replaced by name and address of the account.
|
||||
@@ -7270,6 +7267,52 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// Used as a device message after a successful backup transfer.
|
||||
#define DC_STR_BACKUP_TRANSFER_MSG_BODY 163
|
||||
|
||||
/// "Messages are guaranteed to be end-to-end encrypted from now on."
|
||||
///
|
||||
/// Used in info messages.
|
||||
#define DC_STR_CHAT_PROTECTION_ENABLED 170
|
||||
|
||||
/// "%1$s sent a message from another device."
|
||||
///
|
||||
/// Used in info messages.
|
||||
#define DC_STR_CHAT_PROTECTION_DISABLED 171
|
||||
|
||||
/// "Others will only see this group after you sent a first message."
|
||||
///
|
||||
/// Used as the first info messages in newly created groups.
|
||||
#define DC_STR_NEW_GROUP_SEND_FIRST_MESSAGE 172
|
||||
|
||||
/// "Member %1$s added."
|
||||
///
|
||||
/// Used as info messages.
|
||||
///
|
||||
/// `%1$s` will be replaced by the added member's name.
|
||||
#define DC_STR_MESSAGE_ADD_MEMBER 173
|
||||
|
||||
/// "Your email provider %1$s requires end-to-end encryption which is not setup yet."
|
||||
///
|
||||
/// Used as info messages when a message cannot be sent because it cannot be encrypted.
|
||||
///
|
||||
/// `%1$s` will be replaced by the provider's domain.
|
||||
#define DC_STR_INVALID_UNENCRYPTED_MAIL 174
|
||||
|
||||
/// "You reacted %1$s to '%2$s'"
|
||||
///
|
||||
/// `%1$s` will be replaced by the reaction, usually an emoji
|
||||
/// `%2$s` will be replaced by the summary of the message the reaction refers to
|
||||
///
|
||||
/// Used in summaries.
|
||||
#define DC_STR_YOU_REACTED 176
|
||||
|
||||
/// "%1$s reacted %2$s to '%3$s'"
|
||||
///
|
||||
/// `%1$s` will be replaced by the name the contact who reacted
|
||||
/// `%2$s` will be replaced by the reaction, usually an emoji
|
||||
/// `%3$s` will be replaced by the summary of the message the reaction refers to
|
||||
///
|
||||
/// Used in summaries.
|
||||
#define DC_STR_REACTED_BY 177
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -26,16 +26,14 @@ use anyhow::Context as _;
|
||||
use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration, ProtectionStatus};
|
||||
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
||||
use deltachat::contact::{Contact, ContactId, Origin};
|
||||
use deltachat::context::Context;
|
||||
use deltachat::context::{Context, ContextBuilder};
|
||||
use deltachat::ephemeral::Timer as EphemeralTimer;
|
||||
use deltachat::imex::BackupProvider;
|
||||
use deltachat::key::{DcKey, DcSecretKey};
|
||||
use deltachat::key::preconfigure_keypair;
|
||||
use deltachat::message::MsgId;
|
||||
use deltachat::net::read_url_blob;
|
||||
use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
|
||||
use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions};
|
||||
use deltachat::stock_str::StockMessage;
|
||||
use deltachat::stock_str::StockStrings;
|
||||
use deltachat::webxdc::StatusUpdateSerial;
|
||||
use deltachat::*;
|
||||
use deltachat::{accounts::Accounts, log::LogExt};
|
||||
@@ -105,12 +103,11 @@ pub unsafe extern "C" fn dc_context_new(
|
||||
let ctx = if blobdir.is_null() || *blobdir == 0 {
|
||||
// generate random ID as this functionality is not yet available on the C-api.
|
||||
let id = rand::thread_rng().gen();
|
||||
block_on(Context::new(
|
||||
as_path(dbfile),
|
||||
id,
|
||||
Events::new(),
|
||||
StockStrings::new(),
|
||||
))
|
||||
block_on(
|
||||
ContextBuilder::new(as_path(dbfile).to_path_buf())
|
||||
.with_id(id)
|
||||
.open(),
|
||||
)
|
||||
} else {
|
||||
eprintln!("blobdir can not be defined explicitly anymore");
|
||||
return ptr::null_mut();
|
||||
@@ -134,12 +131,11 @@ pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *
|
||||
}
|
||||
|
||||
let id = rand::thread_rng().gen();
|
||||
match block_on(Context::new_closed(
|
||||
as_path(dbfile),
|
||||
id,
|
||||
Events::new(),
|
||||
StockStrings::new(),
|
||||
)) {
|
||||
match block_on(
|
||||
ContextBuilder::new(as_path(dbfile).to_path_buf())
|
||||
.with_id(id)
|
||||
.build(),
|
||||
) {
|
||||
Ok(context) => Box::into_raw(Box::new(context)),
|
||||
Err(err) => {
|
||||
eprintln!("failed to create context: {err:#}");
|
||||
@@ -388,7 +384,7 @@ pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> li
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
block_on(async move { ctx.get_connectivity().await as u32 as libc::c_int })
|
||||
block_on(ctx.get_connectivity()) as u32 as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -411,6 +407,16 @@ pub unsafe extern "C" fn dc_get_connectivity_html(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_get_push_state()");
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
block_on(ctx.push_state()) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c_int {
|
||||
if context.is_null() {
|
||||
@@ -489,7 +495,7 @@ pub unsafe extern "C" fn dc_start_io(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
return;
|
||||
}
|
||||
let ctx = &*context;
|
||||
let ctx = &mut *context;
|
||||
|
||||
block_on(ctx.start_io())
|
||||
}
|
||||
@@ -557,8 +563,10 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
|
||||
EventType::SecurejoinJoinerProgress { .. } => 2061,
|
||||
EventType::ConnectivityChanged => 2100,
|
||||
EventType::SelfavatarChanged => 2110,
|
||||
EventType::ConfigSynced { .. } => 2111,
|
||||
EventType::WebxdcStatusUpdate { .. } => 2120,
|
||||
EventType::WebxdcInstanceDeleted { .. } => 2121,
|
||||
EventType::AccountsBackgroundFetchDone => 2200,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,8 +592,10 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::Error(_)
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::ConfigSynced { .. }
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::ErrorSelfNotInGroup(_) => 0,
|
||||
| EventType::ErrorSelfNotInGroup(_)
|
||||
| EventType::AccountsBackgroundFetchDone => 0,
|
||||
EventType::MsgsChanged { chat_id, .. }
|
||||
| EventType::ReactionsChanged { chat_id, .. }
|
||||
| EventType::IncomingMsg { chat_id, .. }
|
||||
@@ -644,7 +654,9 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::WebxdcInstanceDeleted { .. }
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::SelfavatarChanged => 0,
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::AccountsBackgroundFetchDone
|
||||
| EventType::ConfigSynced { .. } => 0,
|
||||
EventType::ChatModified(_) => 0,
|
||||
EventType::MsgsChanged { msg_id, .. }
|
||||
| EventType::ReactionsChanged { msg_id, .. }
|
||||
@@ -706,7 +718,9 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::WebxdcStatusUpdate { .. }
|
||||
| EventType::WebxdcInstanceDeleted { .. }
|
||||
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
|
||||
| EventType::AccountsBackgroundFetchDone
|
||||
| EventType::ChatEphemeralTimerModified { .. }
|
||||
| EventType::IncomingMsgBunch { .. } => ptr::null_mut(),
|
||||
EventType::ConfigureProgress { comment, .. } => {
|
||||
if let Some(comment) = comment {
|
||||
comment.to_c_string().unwrap_or_default().into_raw()
|
||||
@@ -718,11 +732,10 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
let data2 = file.to_c_string().unwrap_or_default();
|
||||
data2.into_raw()
|
||||
}
|
||||
EventType::IncomingMsgBunch { msg_ids } => serde_json::to_string(msg_ids)
|
||||
.unwrap_or_default()
|
||||
.to_c_string()
|
||||
.unwrap_or_default()
|
||||
.into_raw(),
|
||||
EventType::ConfigSynced { key } => {
|
||||
let data2 = key.to_string().to_c_string().unwrap_or_default();
|
||||
data2.into_raw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,21 +826,12 @@ pub unsafe extern "C" fn dc_preconfigure_keypair(
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
block_on(async move {
|
||||
let addr = tools::EmailAddress::new(&to_string_lossy(addr))?;
|
||||
let secret = key::SignedSecretKey::from_asc(&to_string_lossy(secret_data))?.0;
|
||||
let public = secret.split_public_key()?;
|
||||
let keypair = key::KeyPair {
|
||||
addr,
|
||||
public,
|
||||
secret,
|
||||
};
|
||||
key::store_self_keypair(ctx, &keypair, key::KeyPairUse::Default).await?;
|
||||
Ok::<_, anyhow::Error>(1)
|
||||
})
|
||||
.context("Failed to save keypair")
|
||||
.log_err(ctx)
|
||||
.unwrap_or(0)
|
||||
let addr = to_string_lossy(addr);
|
||||
let secret_data = to_string_lossy(secret_data);
|
||||
block_on(preconfigure_keypair(ctx, &addr, &secret_data))
|
||||
.context("Failed to save keypair")
|
||||
.log_err(ctx)
|
||||
.is_ok() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1472,32 +1476,6 @@ pub unsafe extern "C" fn dc_get_next_media(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_set_chat_protection(
|
||||
context: *mut dc_context_t,
|
||||
chat_id: u32,
|
||||
protect: libc::c_int,
|
||||
) -> libc::c_int {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_set_chat_protection()");
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
let protect = if let Some(s) = ProtectionStatus::from_i32(protect) {
|
||||
s
|
||||
} else {
|
||||
warn!(ctx, "bad protect-value for dc_set_chat_protection()");
|
||||
return 0;
|
||||
};
|
||||
|
||||
block_on(async move {
|
||||
match ChatId::new(chat_id).set_protection(ctx, protect).await {
|
||||
Ok(()) => 1,
|
||||
Err(_) => 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_set_chat_visibility(
|
||||
context: *mut dc_context_t,
|
||||
@@ -3132,6 +3110,16 @@ pub unsafe extern "C" fn dc_chat_is_protected(chat: *mut dc_chat_t) -> libc::c_i
|
||||
ffi_chat.chat.is_protected() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_protection_broken(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
if chat.is_null() {
|
||||
eprintln!("ignoring careless call to dc_chat_is_protection_broken()");
|
||||
return 0;
|
||||
}
|
||||
let ffi_chat = &*chat;
|
||||
ffi_chat.chat.is_protection_broken() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
if chat.is_null() {
|
||||
@@ -4138,27 +4126,26 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l
|
||||
let ffi_contact = &*contact;
|
||||
let ctx = &*ffi_contact.context;
|
||||
|
||||
block_on(ffi_contact.contact.is_verified(ctx))
|
||||
if block_on(ffi_contact.contact.is_verified(ctx))
|
||||
.context("is_verified failed")
|
||||
.log_err(ctx)
|
||||
.unwrap_or_default() as libc::c_int
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// Return value is essentially a boolean,
|
||||
// but we return 2 for true for backwards compatibility.
|
||||
2
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_contact_get_verifier_addr(
|
||||
contact: *mut dc_contact_t,
|
||||
) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_contact_is_bot(contact: *mut dc_contact_t) -> libc::c_int {
|
||||
if contact.is_null() {
|
||||
eprintln!("ignoring careless call to dc_contact_get_verifier_addr()");
|
||||
return "".strdup();
|
||||
eprintln!("ignoring careless call to dc_contact_is_bot()");
|
||||
return 0;
|
||||
}
|
||||
let ffi_contact = &*contact;
|
||||
let ctx = &*ffi_contact.context;
|
||||
block_on(ffi_contact.contact.get_verifier_addr(ctx))
|
||||
.context("failed to get verifier for contact")
|
||||
.log_err(ctx)
|
||||
.unwrap_or_default()
|
||||
.strdup()
|
||||
(*contact).contact.is_bot() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -4632,96 +4619,6 @@ pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) {
|
||||
// this may change once we start localizing string.
|
||||
}
|
||||
|
||||
// dc_http_response_t
|
||||
|
||||
pub type dc_http_response_t = net::HttpResponse;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_get_http_response(
|
||||
context: *const dc_context_t,
|
||||
url: *const libc::c_char,
|
||||
) -> *mut dc_http_response_t {
|
||||
if context.is_null() || url.is_null() {
|
||||
eprintln!("ignoring careless call to dc_get_http_response()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let context = &*context;
|
||||
let url = to_string_lossy(url);
|
||||
if let Ok(response) = block_on(read_url_blob(context, &url))
|
||||
.context("read_url_blob")
|
||||
.log_err(context)
|
||||
{
|
||||
Box::into_raw(Box::new(response))
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_http_response_get_mimetype(
|
||||
response: *const dc_http_response_t,
|
||||
) -> *mut libc::c_char {
|
||||
if response.is_null() {
|
||||
eprintln!("ignoring careless call to dc_http_response_get_mimetype()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let response = &*response;
|
||||
response.mimetype.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_http_response_get_encoding(
|
||||
response: *const dc_http_response_t,
|
||||
) -> *mut libc::c_char {
|
||||
if response.is_null() {
|
||||
eprintln!("ignoring careless call to dc_http_response_get_encoding()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let response = &*response;
|
||||
response.encoding.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_http_response_get_blob(
|
||||
response: *const dc_http_response_t,
|
||||
) -> *mut libc::c_char {
|
||||
if response.is_null() {
|
||||
eprintln!("ignoring careless call to dc_http_response_get_blob()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let response = &*response;
|
||||
let blob_len = response.blob.len();
|
||||
let ptr = libc::malloc(blob_len);
|
||||
libc::memcpy(ptr, response.blob.as_ptr() as *mut libc::c_void, blob_len);
|
||||
ptr as *mut libc::c_char
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_http_response_get_size(
|
||||
response: *const dc_http_response_t,
|
||||
) -> libc::size_t {
|
||||
if response.is_null() {
|
||||
eprintln!("ignoring careless call to dc_http_response_get_size()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let response = &*response;
|
||||
response.blob.len()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_http_response_unref(response: *mut dc_http_response_t) {
|
||||
if response.is_null() {
|
||||
eprintln!("ignoring careless call to dc_http_response_unref()");
|
||||
return;
|
||||
}
|
||||
drop(Box::from_raw(response));
|
||||
}
|
||||
|
||||
// -- Accounts
|
||||
|
||||
/// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using
|
||||
@@ -4750,17 +4647,17 @@ pub type dc_accounts_t = AccountsWrapper;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_new(
|
||||
_os_name: *const libc::c_char,
|
||||
dbfile: *const libc::c_char,
|
||||
dir: *const libc::c_char,
|
||||
writable: libc::c_int,
|
||||
) -> *mut dc_accounts_t {
|
||||
setup_panic!();
|
||||
|
||||
if dbfile.is_null() {
|
||||
if dir.is_null() {
|
||||
eprintln!("ignoring careless call to dc_accounts_new()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let accs = block_on(Accounts::new(as_path(dbfile).into()));
|
||||
let accs = block_on(Accounts::new(as_path(dir).into(), writable != 0));
|
||||
|
||||
match accs {
|
||||
Ok(accs) => Box::into_raw(Box::new(AccountsWrapper::new(accs))),
|
||||
@@ -4954,16 +4851,6 @@ pub unsafe extern "C" fn dc_accounts_get_all(accounts: *mut dc_accounts_t) -> *m
|
||||
Box::into_raw(Box::new(array))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_all_work_done(accounts: *mut dc_accounts_t) -> libc::c_int {
|
||||
if accounts.is_null() {
|
||||
eprintln!("ignoring careless call to dc_accounts_all_work_done()");
|
||||
return 0;
|
||||
}
|
||||
let accounts = &*accounts;
|
||||
block_on(async move { accounts.read().await.all_work_done().await as libc::c_int })
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_start_io(accounts: *mut dc_accounts_t) {
|
||||
if accounts.is_null() {
|
||||
@@ -4971,8 +4858,8 @@ pub unsafe extern "C" fn dc_accounts_start_io(accounts: *mut dc_accounts_t) {
|
||||
return;
|
||||
}
|
||||
|
||||
let accounts = &*accounts;
|
||||
block_on(async move { accounts.read().await.start_io().await });
|
||||
let accounts = &mut *accounts;
|
||||
block_on(async move { accounts.write().await.start_io().await });
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -5008,6 +4895,49 @@ pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *mut dc_accoun
|
||||
block_on(async move { accounts.write().await.maybe_network_lost().await });
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_background_fetch(
|
||||
accounts: *mut dc_accounts_t,
|
||||
timeout_in_seconds: u64,
|
||||
) -> libc::c_int {
|
||||
if accounts.is_null() || timeout_in_seconds <= 2 {
|
||||
eprintln!("ignoring careless call to dc_accounts_background_fetch()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let accounts = &*accounts;
|
||||
block_on(async move {
|
||||
let accounts = accounts.read().await;
|
||||
accounts
|
||||
.background_fetch(Duration::from_secs(timeout_in_seconds))
|
||||
.await;
|
||||
});
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_set_push_device_token(
|
||||
accounts: *mut dc_accounts_t,
|
||||
token: *const libc::c_char,
|
||||
) {
|
||||
if accounts.is_null() {
|
||||
eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
|
||||
return;
|
||||
}
|
||||
|
||||
let accounts = &*accounts;
|
||||
let token = to_string_lossy(token);
|
||||
|
||||
block_on(async move {
|
||||
let mut accounts = accounts.write().await;
|
||||
if let Err(err) = accounts.set_push_device_token(&token).await {
|
||||
accounts.emit_event(EventType::Error(format!(
|
||||
"Failed to set notify token: {err:#}."
|
||||
)));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
|
||||
accounts: *mut dc_accounts_t,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.125.0"
|
||||
version = "1.137.2"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
[[bin]]
|
||||
name = "deltachat-jsonrpc-server"
|
||||
@@ -15,26 +16,26 @@ required-features = ["webserver"]
|
||||
anyhow = "1"
|
||||
deltachat = { path = ".." }
|
||||
num-traits = "0.2"
|
||||
schemars = "0.8.11"
|
||||
schemars = "0.8.13"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tempfile = "3.6.0"
|
||||
tempfile = "3.10.1"
|
||||
log = "0.4"
|
||||
async-channel = { version = "1.8.0" }
|
||||
futures = { version = "0.3.28" }
|
||||
serde_json = "1.0.99"
|
||||
yerpc = { version = "0.5.1", features = ["anyhow_expose", "openrpc"] }
|
||||
typescript-type-def = { version = "0.5.5", features = ["json_value"] }
|
||||
tokio = { version = "1.29.1" }
|
||||
sanitize-filename = "0.4"
|
||||
walkdir = "2.3.3"
|
||||
async-channel = { version = "2.0.0" }
|
||||
futures = { version = "0.3.30" }
|
||||
serde_json = "1"
|
||||
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
|
||||
typescript-type-def = { version = "0.5.8", features = ["json_value"] }
|
||||
tokio = { version = "1.37.0" }
|
||||
sanitize-filename = "0.5"
|
||||
walkdir = "2.5.0"
|
||||
base64 = "0.21"
|
||||
|
||||
# optional dependencies
|
||||
axum = { version = "0.6.18", optional = true, features = ["ws"] }
|
||||
axum = { version = "0.7", optional = true, features = ["ws"] }
|
||||
env_logger = { version = "0.10.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.29.1", features = ["full", "rt-multi-thread"] }
|
||||
tokio = { version = "1.37.0", features = ["full", "rt-multi-thread"] }
|
||||
|
||||
|
||||
[features]
|
||||
|
||||
@@ -108,10 +108,10 @@ This will build the `deltachat-jsonrpc-server` binary and then run a test suite
|
||||
|
||||
The test suite includes some tests that need online connectivity and a way to create test email accounts. To run these tests, talk to DeltaChat developers to get a token for the `testrun.org` service, or use a local instance of [`mailadm`](https://github.com/deltachat/docker-mailadm).
|
||||
|
||||
Then, set the `DCC_NEW_TMP_EMAIL` environment variable to your mailadm token before running the tests.
|
||||
Then, set the `CHATMAIL_DOMAIN` environment variable to your testing email server domain.
|
||||
|
||||
```
|
||||
DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=yourtoken npm run test
|
||||
CHATMAIL_DOMAIN=chat.example.org npm run test
|
||||
```
|
||||
|
||||
#### Test Coverage
|
||||
|
||||
@@ -4,30 +4,30 @@ use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
pub use deltachat::accounts::Accounts;
|
||||
use deltachat::message::get_msg_read_receipts;
|
||||
use deltachat::qr::Qr;
|
||||
use deltachat::{
|
||||
chat::{
|
||||
self, add_contact_to_chat, forward_msgs, get_chat_media, get_chat_msgs, get_chat_msgs_ex,
|
||||
marknoticed_chat, remove_contact_from_chat, Chat, ChatId, ChatItem, MessageListOptions,
|
||||
ProtectionStatus,
|
||||
},
|
||||
chatlist::Chatlist,
|
||||
config::Config,
|
||||
constants::DC_MSG_ID_DAYMARKER,
|
||||
contact::{may_be_valid_addr, Contact, ContactId, Origin},
|
||||
context::get_info,
|
||||
ephemeral::Timer,
|
||||
imex, location,
|
||||
message::{self, delete_msgs, markseen_msgs, Message, MessageState, MsgId, Viewtype},
|
||||
provider::get_provider_info,
|
||||
qr,
|
||||
qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg},
|
||||
reaction::{get_msg_reactions, send_reaction},
|
||||
securejoin,
|
||||
stock_str::StockMessage,
|
||||
webxdc::StatusUpdateSerial,
|
||||
use deltachat::chat::{
|
||||
self, add_contact_to_chat, forward_msgs, get_chat_media, get_chat_msgs, get_chat_msgs_ex,
|
||||
marknoticed_chat, remove_contact_from_chat, Chat, ChatId, ChatItem, MessageListOptions,
|
||||
ProtectionStatus,
|
||||
};
|
||||
use deltachat::chatlist::Chatlist;
|
||||
use deltachat::config::Config;
|
||||
use deltachat::constants::DC_MSG_ID_DAYMARKER;
|
||||
use deltachat::contact::{may_be_valid_addr, Contact, ContactId, Origin};
|
||||
use deltachat::context::get_info;
|
||||
use deltachat::ephemeral::Timer;
|
||||
use deltachat::imex;
|
||||
use deltachat::location;
|
||||
use deltachat::message::get_msg_read_receipts;
|
||||
use deltachat::message::{
|
||||
self, delete_msgs, markseen_msgs, Message, MessageState, MsgId, Viewtype,
|
||||
};
|
||||
use deltachat::provider::get_provider_info;
|
||||
use deltachat::qr::{self, Qr};
|
||||
use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
|
||||
use deltachat::reaction::{get_msg_reactions, send_reaction};
|
||||
use deltachat::securejoin;
|
||||
use deltachat::stock_str::StockMessage;
|
||||
use deltachat::webxdc::StatusUpdateSerial;
|
||||
use sanitize_filename::is_sanitized;
|
||||
use tokio::fs;
|
||||
use tokio::sync::{watch, Mutex, RwLock};
|
||||
@@ -47,7 +47,7 @@ use types::provider_info::ProviderInfo;
|
||||
use types::reactions::JSONRPCReactions;
|
||||
use types::webxdc::WebxdcMessageInfo;
|
||||
|
||||
use self::types::message::MessageLoadResult;
|
||||
use self::types::message::{MessageInfo, MessageLoadResult};
|
||||
use self::types::{
|
||||
chat::{BasicChat, JSONRPCChatVisibility, MuteDuration},
|
||||
location::JsonrpcLocation,
|
||||
@@ -142,11 +142,7 @@ impl CommandApi {
|
||||
}
|
||||
}
|
||||
|
||||
#[rpc(
|
||||
all_positional,
|
||||
ts_outdir = "typescript/generated",
|
||||
openrpc_outdir = "openrpc"
|
||||
)]
|
||||
#[rpc(all_positional, ts_outdir = "typescript/generated")]
|
||||
impl CommandApi {
|
||||
/// Test function.
|
||||
async fn sleep(&self, delay: f64) {
|
||||
@@ -157,12 +153,12 @@ impl CommandApi {
|
||||
// Misc top level functions
|
||||
// ---------------------------------------------
|
||||
|
||||
/// Check if an email address is valid.
|
||||
/// Checks if an email address is valid.
|
||||
async fn check_email_validity(&self, email: String) -> bool {
|
||||
may_be_valid_addr(&email)
|
||||
}
|
||||
|
||||
/// Get general system info.
|
||||
/// Returns general system info.
|
||||
async fn get_system_info(&self) -> BTreeMap<&'static str, String> {
|
||||
get_info()
|
||||
}
|
||||
@@ -223,13 +219,29 @@ impl CommandApi {
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
/// Starts background tasks for all accounts.
|
||||
async fn start_io_for_all_accounts(&self) -> Result<()> {
|
||||
self.accounts.read().await.start_io().await;
|
||||
self.accounts.write().await.start_io().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stops background tasks for all accounts.
|
||||
async fn stop_io_for_all_accounts(&self) -> Result<()> {
|
||||
self.accounts.read().await.stop_io().await;
|
||||
self.accounts.write().await.stop_io().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs a background fetch for all accounts in parallel with a timeout.
|
||||
///
|
||||
/// The `AccountsBackgroundFetchDone` event is emitted at the end even in case of timeout.
|
||||
/// Process all events until you get this one and you can safely return to the background
|
||||
/// without forgetting to create notifications caused by timing race conditions.
|
||||
async fn accounts_background_fetch(&self, timeout_in_seconds: f64) -> Result<()> {
|
||||
self.accounts
|
||||
.write()
|
||||
.await
|
||||
.background_fetch(std::time::Duration::from_secs_f64(timeout_in_seconds))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -237,14 +249,16 @@ impl CommandApi {
|
||||
// Methods that work on individual accounts
|
||||
// ---------------------------------------------
|
||||
|
||||
async fn start_io(&self, id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(id).await?;
|
||||
/// Starts background tasks for a single account.
|
||||
async fn start_io(&self, account_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ctx.start_io().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn stop_io(&self, id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(id).await?;
|
||||
/// Stops background tasks for a single account.
|
||||
async fn stop_io(&self, account_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ctx.stop_io().await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -311,11 +325,18 @@ impl CommandApi {
|
||||
ctx.get_info().await
|
||||
}
|
||||
|
||||
async fn draft_self_report(&self, account_id: u32) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
Ok(ctx.draft_self_report().await?.to_u32())
|
||||
}
|
||||
|
||||
/// Sets the given configuration key.
|
||||
async fn set_config(&self, account_id: u32, key: String, value: Option<String>) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
set_config(&ctx, &key, value.as_deref()).await
|
||||
}
|
||||
|
||||
/// Updates a batch of configuration values.
|
||||
async fn batch_set_config(
|
||||
&self,
|
||||
account_id: u32,
|
||||
@@ -347,6 +368,7 @@ impl CommandApi {
|
||||
Ok(qr_object)
|
||||
}
|
||||
|
||||
/// Returns configuration value for the given key.
|
||||
async fn get_config(&self, account_id: u32, key: String) -> Result<Option<String>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
get_config(&ctx, &key).await
|
||||
@@ -675,7 +697,7 @@ impl CommandApi {
|
||||
/// the Verified-Group-Invite protocol is offered in the QR code;
|
||||
/// works for protected groups as well as for normal groups.
|
||||
/// If not set, the Setup-Contact protocol is offered in the QR code.
|
||||
/// See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
/// See https://securejoin.readthedocs.io/en/latest/new.html
|
||||
/// for details about both protocols.
|
||||
///
|
||||
/// return format: `[code, svg]`
|
||||
@@ -704,7 +726,7 @@ impl CommandApi {
|
||||
///
|
||||
/// Subsequent calls of `secure_join()` will abort previous, unfinished handshakes.
|
||||
///
|
||||
/// See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
/// See https://securejoin.readthedocs.io/en/latest/new.html
|
||||
/// for details about both protocols.
|
||||
///
|
||||
/// **qr**: The text of the scanned QR code. Typically, the same string as given
|
||||
@@ -812,24 +834,12 @@ impl CommandApi {
|
||||
/// Create a new broadcast list.
|
||||
///
|
||||
/// Broadcast lists are similar to groups on the sending device,
|
||||
/// however, recipients get the messages in normal one-to-one chats
|
||||
/// and will not be aware of other members.
|
||||
/// however, recipients get the messages in a read-only chat
|
||||
/// and will see who the other members are.
|
||||
///
|
||||
/// Replies to broadcasts go only to the sender
|
||||
/// and not to all broadcast recipients.
|
||||
/// Moreover, replies will not appear in the broadcast list
|
||||
/// but in the one-to-one chat with the person answering.
|
||||
///
|
||||
/// The name and the image of the broadcast list is set automatically
|
||||
/// and is visible to the sender only.
|
||||
/// Not asking for these data allows more focused creation
|
||||
/// and we bypass the question who will get which data.
|
||||
/// Also, many users will have at most one broadcast list
|
||||
/// so, a generic name and image is sufficient at the first place.
|
||||
///
|
||||
/// Later on, however, the name can be changed using dc_set_chat_name().
|
||||
/// The image cannot be changed to have a unique, recognizable icon in the chat lists.
|
||||
/// All in all, this is also what other messengers are doing here.
|
||||
/// For historical reasons, this function does not take a name directly,
|
||||
/// instead you have to set the name using dc_set_chat_name()
|
||||
/// after creating the broadcast list.
|
||||
async fn create_broadcast_list(&self, account_id: u32) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
chat::create_broadcast_list(&ctx)
|
||||
@@ -905,19 +915,35 @@ impl CommandApi {
|
||||
.to_u32())
|
||||
}
|
||||
|
||||
// for now only text messages, because we only used text messages in desktop thusfar
|
||||
/// Add a message to the device-chat.
|
||||
/// Device-messages usually contain update information
|
||||
/// and some hints that are added during the program runs, multi-device etc.
|
||||
/// The device-message may be defined by a label;
|
||||
/// if a message with the same label was added or skipped before,
|
||||
/// the message is not added again, even if the message was deleted in between.
|
||||
/// If needed, the device-chat is created before.
|
||||
///
|
||||
/// Sends the `MsgsChanged` event on success.
|
||||
///
|
||||
/// Setting msg to None will prevent the device message with this label from being added in the future.
|
||||
async fn add_device_message(
|
||||
&self,
|
||||
account_id: u32,
|
||||
label: String,
|
||||
text: String,
|
||||
) -> Result<u32> {
|
||||
msg: Option<MessageData>,
|
||||
) -> Result<Option<u32>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.set_text(text);
|
||||
let message_id =
|
||||
deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut msg)).await?;
|
||||
Ok(message_id.to_u32())
|
||||
if let Some(msg) = msg {
|
||||
let mut message = msg.create_message(&ctx).await?;
|
||||
let message_id =
|
||||
deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut message)).await?;
|
||||
if !message_id.is_unset() {
|
||||
return Ok(Some(message_id.to_u32()));
|
||||
}
|
||||
} else {
|
||||
deltachat::chat::add_device_msg(&ctx, Some(&label), None).await?;
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Mark all messages in a chat as _noticed_.
|
||||
@@ -1071,9 +1097,12 @@ impl CommandApi {
|
||||
.collect::<Vec<JSONRPCMessageListItem>>())
|
||||
}
|
||||
|
||||
async fn get_message(&self, account_id: u32, message_id: u32) -> Result<MessageObject> {
|
||||
async fn get_message(&self, account_id: u32, msg_id: u32) -> Result<MessageObject> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
MessageObject::from_message_id(&ctx, message_id).await
|
||||
let msg_id = MsgId::new(msg_id);
|
||||
MessageObject::from_msg_id(&ctx, msg_id)
|
||||
.await
|
||||
.with_context(|| format!("Failed to load message {msg_id} for account {account_id}"))
|
||||
}
|
||||
|
||||
async fn get_message_html(&self, account_id: u32, message_id: u32) -> Result<Option<String>> {
|
||||
@@ -1093,7 +1122,7 @@ impl CommandApi {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut messages: HashMap<u32, MessageLoadResult> = HashMap::new();
|
||||
for message_id in message_ids {
|
||||
let message_result = MessageObject::from_message_id(&ctx, message_id).await;
|
||||
let message_result = MessageObject::from_msg_id(&ctx, MsgId::new(message_id)).await;
|
||||
messages.insert(
|
||||
message_id,
|
||||
match message_result {
|
||||
@@ -1135,6 +1164,16 @@ impl CommandApi {
|
||||
MsgId::new(message_id).get_info(&ctx).await
|
||||
}
|
||||
|
||||
/// Returns additional information for single message.
|
||||
async fn get_message_info_object(
|
||||
&self,
|
||||
account_id: u32,
|
||||
message_id: u32,
|
||||
) -> Result<MessageInfo> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
MessageInfo::from_msg_id(&ctx, MsgId::new(message_id)).await
|
||||
}
|
||||
|
||||
/// Returns contacts that sent read receipts and the time of reading.
|
||||
async fn get_message_read_receipts(
|
||||
&self,
|
||||
@@ -1390,6 +1429,19 @@ impl CommandApi {
|
||||
// chat
|
||||
// ---------------------------------------------
|
||||
|
||||
/// Returns the [`ChatId`] for the 1:1 chat with `contact_id` if it exists.
|
||||
///
|
||||
/// If it does not exist, `None` is returned.
|
||||
async fn get_chat_id_by_contact_id(
|
||||
&self,
|
||||
account_id: u32,
|
||||
contact_id: u32,
|
||||
) -> Result<Option<u32>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let chat_id = ChatId::lookup_by_contact(&ctx, ContactId::new(contact_id)).await?;
|
||||
Ok(chat_id.map(|id| id.to_u32()))
|
||||
}
|
||||
|
||||
/// Returns all message IDs of the given types in a chat.
|
||||
/// Typically used to show a gallery.
|
||||
///
|
||||
@@ -1753,6 +1805,9 @@ impl CommandApi {
|
||||
let mut msg = Message::new(Viewtype::Sticker);
|
||||
msg.set_file(&sticker_path, None);
|
||||
|
||||
// JSON-rpc does not need heuristics to turn [Viewtype::Sticker] into [Viewtype::Image]
|
||||
msg.force_sticker();
|
||||
|
||||
let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?;
|
||||
Ok(message_id.to_u32())
|
||||
}
|
||||
@@ -1791,38 +1846,7 @@ impl CommandApi {
|
||||
|
||||
async fn send_msg(&self, account_id: u32, chat_id: u32, data: MessageData) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut message = Message::new(if let Some(viewtype) = data.viewtype {
|
||||
viewtype.into()
|
||||
} else if data.file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
message.set_text(data.text.unwrap_or_default());
|
||||
if data.html.is_some() {
|
||||
message.set_html(data.html);
|
||||
}
|
||||
if data.override_sender_name.is_some() {
|
||||
message.set_override_sender_name(data.override_sender_name);
|
||||
}
|
||||
if let Some(file) = data.file {
|
||||
message.set_file(file, None);
|
||||
}
|
||||
if let Some((latitude, longitude)) = data.location {
|
||||
message.set_location(latitude, longitude);
|
||||
}
|
||||
if let Some(id) = data.quoted_message_id {
|
||||
message
|
||||
.set_quote(
|
||||
&ctx,
|
||||
Some(
|
||||
&Message::load_from_db(&ctx, MsgId::new(id))
|
||||
.await
|
||||
.context("message to quote could not be loaded")?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let mut message = data.create_message(&ctx).await?;
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
|
||||
.await?
|
||||
.to_u32();
|
||||
@@ -1886,7 +1910,7 @@ impl CommandApi {
|
||||
.context("path conversion to string failed")
|
||||
}
|
||||
|
||||
/// save a sticker to a collection/folder in the account's sticker folder
|
||||
/// Saves a sticker to a collection/folder in the account's sticker folder.
|
||||
async fn misc_save_sticker(
|
||||
&self,
|
||||
account_id: u32,
|
||||
@@ -2021,11 +2045,9 @@ impl CommandApi {
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
|
||||
.await?
|
||||
.to_u32();
|
||||
let message = MessageObject::from_message_id(&ctx, msg_id).await?;
|
||||
Ok((msg_id, message))
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message).await?;
|
||||
let message = MessageObject::from_msg_id(&ctx, msg_id).await?;
|
||||
Ok((msg_id.to_u32(), message))
|
||||
}
|
||||
|
||||
// mimics the old desktop call, will get replaced with something better in the composer rewrite,
|
||||
@@ -2039,13 +2061,19 @@ impl CommandApi {
|
||||
text: Option<String>,
|
||||
file: Option<String>,
|
||||
quoted_message_id: Option<u32>,
|
||||
view_type: Option<MessageViewtype>,
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut draft = Message::new(if file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
let mut draft = Message::new(view_type.map_or_else(
|
||||
|| {
|
||||
if file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
}
|
||||
},
|
||||
|v| v.into(),
|
||||
));
|
||||
draft.set_text(text.unwrap_or_default());
|
||||
if let Some(file) = file {
|
||||
draft.set_file(file, None);
|
||||
@@ -2065,6 +2093,23 @@ impl CommandApi {
|
||||
|
||||
ChatId::new(chat_id).set_draft(&ctx, Some(&mut draft)).await
|
||||
}
|
||||
|
||||
// send the chat's current set draft
|
||||
async fn misc_send_draft(&self, account_id: u32, chat_id: u32) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
if let Some(draft) = ChatId::new(chat_id).get_draft(&ctx).await? {
|
||||
let mut draft = draft;
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut draft)
|
||||
.await?
|
||||
.to_u32();
|
||||
Ok(msg_id)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"chat with id {} doesn't have draft message",
|
||||
chat_id
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions (to prevent code duplication)
|
||||
@@ -2081,13 +2126,6 @@ async fn set_config(
|
||||
value,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match key {
|
||||
"sentbox_watch" | "mvbox_move" | "only_fetch_mvbox" => {
|
||||
ctx.restart_io_if_running().await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use typescript_type_def::TypeDef;
|
||||
use super::color_int_to_hex_string;
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum Account {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Configured {
|
||||
|
||||
@@ -18,6 +18,17 @@ use super::contact::ContactObject;
|
||||
pub struct FullChat {
|
||||
id: u32,
|
||||
name: String,
|
||||
|
||||
/// True if the chat is protected.
|
||||
///
|
||||
/// UI should display a green checkmark
|
||||
/// in the chat title,
|
||||
/// in the chat profile title and
|
||||
/// in the chatlist item
|
||||
/// if chat protection is enabled.
|
||||
/// UI should also display a green checkmark
|
||||
/// in the contact profile
|
||||
/// if 1:1 chat with this contact exists and is protected.
|
||||
is_protected: bool,
|
||||
profile_image: Option<String>, //BLOBS ?
|
||||
archived: bool,
|
||||
@@ -31,6 +42,7 @@ pub struct FullChat {
|
||||
fresh_message_counter: usize,
|
||||
// is_group - please check over chat.type in frontend instead
|
||||
is_contact_request: bool,
|
||||
is_protection_broken: bool,
|
||||
is_device_chat: bool,
|
||||
self_in_group: bool,
|
||||
is_muted: bool,
|
||||
@@ -73,7 +85,7 @@ impl FullChat {
|
||||
let can_send = chat.can_send(context).await?;
|
||||
|
||||
let was_seen_recently = if chat.get_type() == Chattype::Single {
|
||||
match contact_ids.get(0) {
|
||||
match contact_ids.first() {
|
||||
Some(contact) => Contact::get_by_id(context, *contact)
|
||||
.await
|
||||
.context("failed to load contact for was_seen_recently")?
|
||||
@@ -100,6 +112,7 @@ impl FullChat {
|
||||
color,
|
||||
fresh_message_counter,
|
||||
is_contact_request: chat.is_contact_request(),
|
||||
is_protection_broken: chat.is_protection_broken(),
|
||||
is_device_chat: chat.is_device_talk(),
|
||||
self_in_group: contact_ids.contains(&ContactId::SELF),
|
||||
is_muted: chat.is_muted(),
|
||||
@@ -126,6 +139,17 @@ impl FullChat {
|
||||
pub struct BasicChat {
|
||||
id: u32,
|
||||
name: String,
|
||||
|
||||
/// True if the chat is protected.
|
||||
///
|
||||
/// UI should display a green checkmark
|
||||
/// in the chat title,
|
||||
/// in the chat profile title and
|
||||
/// in the chatlist item
|
||||
/// if chat protection is enabled.
|
||||
/// UI should also display a green checkmark
|
||||
/// in the contact profile
|
||||
/// if 1:1 chat with this contact exists and is protected.
|
||||
is_protected: bool,
|
||||
profile_image: Option<String>, //BLOBS ?
|
||||
archived: bool,
|
||||
@@ -134,6 +158,7 @@ pub struct BasicChat {
|
||||
is_self_talk: bool,
|
||||
color: String,
|
||||
is_contact_request: bool,
|
||||
is_protection_broken: bool,
|
||||
is_device_chat: bool,
|
||||
is_muted: bool,
|
||||
}
|
||||
@@ -160,6 +185,7 @@ impl BasicChat {
|
||||
is_self_talk: chat.is_self_talk(),
|
||||
color,
|
||||
is_contact_request: chat.is_contact_request(),
|
||||
is_protection_broken: chat.is_protection_broken(),
|
||||
is_device_chat: chat.is_device_talk(),
|
||||
is_muted: chat.is_muted(),
|
||||
})
|
||||
@@ -167,10 +193,11 @@ impl BasicChat {
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum MuteDuration {
|
||||
NotMuted,
|
||||
Forever,
|
||||
Until(i64),
|
||||
Until { duration: i64 },
|
||||
}
|
||||
|
||||
impl MuteDuration {
|
||||
@@ -178,13 +205,13 @@ impl MuteDuration {
|
||||
match self {
|
||||
MuteDuration::NotMuted => Ok(chat::MuteDuration::NotMuted),
|
||||
MuteDuration::Forever => Ok(chat::MuteDuration::Forever),
|
||||
MuteDuration::Until(n) => {
|
||||
if n <= 0 {
|
||||
MuteDuration::Until { duration } => {
|
||||
if duration <= 0 {
|
||||
bail!("failed to read mute duration")
|
||||
}
|
||||
|
||||
Ok(SystemTime::now()
|
||||
.checked_add(Duration::from_secs(n as u64))
|
||||
.checked_add(Duration::from_secs(duration as u64))
|
||||
.map_or(chat::MuteDuration::Forever, chat::MuteDuration::Until))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use super::color_int_to_hex_string;
|
||||
use super::message::MessageViewtype;
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum ChatListItemFetchResult {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatListItem {
|
||||
@@ -102,7 +102,7 @@ pub(crate) async fn get_chat_list_item_by_id(
|
||||
let self_in_group = chat_contacts.contains(&ContactId::SELF);
|
||||
|
||||
let (dm_chat_contact, was_seen_recently) = if chat.get_type() == Chattype::Single {
|
||||
let contact = chat_contacts.get(0);
|
||||
let contact = chat_contacts.first();
|
||||
let was_seen_recently = match contact {
|
||||
Some(contact) => Contact::get_by_id(ctx, *contact)
|
||||
.await
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use deltachat::contact::VerifiedStatus;
|
||||
use deltachat::context::Context;
|
||||
use serde::Serialize;
|
||||
use typescript_type_def::TypeDef;
|
||||
@@ -19,14 +18,36 @@ pub struct ContactObject {
|
||||
profile_image: Option<String>, // BLOBS
|
||||
name_and_addr: String,
|
||||
is_blocked: bool,
|
||||
|
||||
/// True if the contact can be added to verified groups.
|
||||
///
|
||||
/// If this is true
|
||||
/// UI should display green checkmark after the contact name
|
||||
/// in contact list items,
|
||||
/// in chat member list items
|
||||
/// and in profiles if no chat with the contact exist.
|
||||
is_verified: bool,
|
||||
/// the address that verified this contact
|
||||
verifier_addr: Option<String>,
|
||||
/// the id of the contact that verified this contact
|
||||
|
||||
/// True if the contact profile title should have a green checkmark.
|
||||
///
|
||||
/// This indicates whether 1:1 chat has a green checkmark
|
||||
/// or will have a green checkmark if created.
|
||||
is_profile_verified: bool,
|
||||
|
||||
/// The ID of the contact that verified this contact.
|
||||
///
|
||||
/// If this is present,
|
||||
/// display a green checkmark and "Introduced by ..."
|
||||
/// string followed by the verifier contact name and address
|
||||
/// in the contact profile.
|
||||
verifier_id: Option<u32>,
|
||||
|
||||
/// the contact's last seen timestamp
|
||||
last_seen: i64,
|
||||
was_seen_recently: bool,
|
||||
|
||||
/// If the contact is a bot.
|
||||
is_bot: bool,
|
||||
}
|
||||
|
||||
impl ContactObject {
|
||||
@@ -38,19 +59,13 @@ impl ContactObject {
|
||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
||||
None => None,
|
||||
};
|
||||
let is_verified = contact.is_verified(context).await? == VerifiedStatus::BidirectVerified;
|
||||
let is_verified = contact.is_verified(context).await?;
|
||||
let is_profile_verified = contact.is_profile_verified(context).await?;
|
||||
|
||||
let (verifier_addr, verifier_id) = if is_verified {
|
||||
(
|
||||
contact.get_verifier_addr(context).await?,
|
||||
contact
|
||||
.get_verifier_id(context)
|
||||
.await?
|
||||
.map(|contact_id| contact_id.to_u32()),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let verifier_id = contact
|
||||
.get_verifier_id(context)
|
||||
.await?
|
||||
.map(|contact_id| contact_id.to_u32());
|
||||
|
||||
Ok(ContactObject {
|
||||
address: contact.get_addr().to_owned(),
|
||||
@@ -64,10 +79,11 @@ impl ContactObject {
|
||||
name_and_addr: contact.get_name_n_addr(),
|
||||
is_blocked: contact.is_blocked(),
|
||||
is_verified,
|
||||
verifier_addr,
|
||||
is_profile_verified,
|
||||
verifier_id,
|
||||
last_seen: contact.last_seen(),
|
||||
was_seen_recently: contact.was_seen_recently(),
|
||||
is_bot: contact.is_bot(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,61 +22,43 @@ impl From<CoreEvent> for Event {
|
||||
}
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum EventType {
|
||||
/// The library-user may write an informational string to the log.
|
||||
///
|
||||
/// This event should *not* be reported to the end-user using a popup or something like
|
||||
/// that.
|
||||
Info {
|
||||
msg: String,
|
||||
},
|
||||
Info { msg: String },
|
||||
|
||||
/// Emitted when SMTP connection is established and login was successful.
|
||||
SmtpConnected {
|
||||
msg: String,
|
||||
},
|
||||
SmtpConnected { msg: String },
|
||||
|
||||
/// Emitted when IMAP connection is established and login was successful.
|
||||
ImapConnected {
|
||||
msg: String,
|
||||
},
|
||||
ImapConnected { msg: String },
|
||||
|
||||
/// Emitted when a message was successfully sent to the SMTP server.
|
||||
SmtpMessageSent {
|
||||
msg: String,
|
||||
},
|
||||
SmtpMessageSent { msg: String },
|
||||
|
||||
/// Emitted when an IMAP message has been marked as deleted
|
||||
ImapMessageDeleted {
|
||||
msg: String,
|
||||
},
|
||||
ImapMessageDeleted { msg: String },
|
||||
|
||||
/// Emitted when an IMAP message has been moved
|
||||
ImapMessageMoved {
|
||||
msg: String,
|
||||
},
|
||||
ImapMessageMoved { msg: String },
|
||||
|
||||
/// Emitted before going into IDLE on the Inbox folder.
|
||||
ImapInboxIdle,
|
||||
|
||||
/// Emitted when an new file in the $BLOBDIR was created
|
||||
NewBlobFile {
|
||||
file: String,
|
||||
},
|
||||
NewBlobFile { file: String },
|
||||
|
||||
/// Emitted when an file in the $BLOBDIR was deleted
|
||||
DeletedBlobFile {
|
||||
file: String,
|
||||
},
|
||||
DeletedBlobFile { file: String },
|
||||
|
||||
/// The library-user should write a warning string to the log.
|
||||
///
|
||||
/// This event should *not* be reported to the end-user using a popup or something like
|
||||
/// that.
|
||||
Warning {
|
||||
msg: String,
|
||||
},
|
||||
Warning { msg: String },
|
||||
|
||||
/// The library-user should report an error to the end-user.
|
||||
///
|
||||
@@ -88,18 +70,14 @@ pub enum EventType {
|
||||
/// 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.
|
||||
Error {
|
||||
msg: String,
|
||||
},
|
||||
Error { msg: String },
|
||||
|
||||
/// An action cannot be performed because the user is not in the group.
|
||||
/// Reported eg. after a call to
|
||||
/// setChatName(), setChatProfileImage(),
|
||||
/// addContactToChat(), removeContactFromChat(),
|
||||
/// and messages sending functions.
|
||||
ErrorSelfNotInGroup {
|
||||
msg: String,
|
||||
},
|
||||
ErrorSelfNotInGroup { msg: String },
|
||||
|
||||
/// Messages or chats changed. One or more messages or chats changed for various
|
||||
/// reasons in the database:
|
||||
@@ -110,10 +88,7 @@ pub enum EventType {
|
||||
/// `chatId` is set if only a single chat is affected by the changes, otherwise 0.
|
||||
/// `msgId` is set if only a single message is affected by the changes, otherwise 0.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgsChanged {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
MsgsChanged { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// Reactions for the message changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -126,60 +101,39 @@ pub enum EventType {
|
||||
/// 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.
|
||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event sent together with this event.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IncomingMsg {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
IncomingMsg { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// Downloading a bunch of messages just finished. This is an experimental
|
||||
/// Downloading a bunch of messages just finished. This is an
|
||||
/// event to allow the UI to only show one notification per message bunch,
|
||||
/// instead of cluttering the user with many notifications.
|
||||
///
|
||||
/// msg_ids contains the message ids.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
IncomingMsgBunch {
|
||||
msg_ids: Vec<u32>,
|
||||
},
|
||||
IncomingMsgBunch,
|
||||
|
||||
/// Messages were seen or noticed.
|
||||
/// chat id is always set.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgsNoticed {
|
||||
chat_id: u32,
|
||||
},
|
||||
MsgsNoticed { chat_id: u32 },
|
||||
|
||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||
/// DC_STATE_OUT_DELIVERED, see `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgDelivered {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
MsgDelivered { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// 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 `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgFailed {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
MsgFailed { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_MDN_RCVD, see `Message.state`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgRead {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
MsgRead { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// A single message is deleted.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
MsgDeleted {
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
},
|
||||
MsgDeleted { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// 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.
|
||||
@@ -189,24 +143,17 @@ pub enum EventType {
|
||||
/// This event does not include ephemeral timer modification, which
|
||||
/// is a separate event.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatModified {
|
||||
chat_id: u32,
|
||||
},
|
||||
ChatModified { chat_id: u32 },
|
||||
|
||||
/// Chat ephemeral timer changed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatEphemeralTimerModified {
|
||||
chat_id: u32,
|
||||
timer: u32,
|
||||
},
|
||||
ChatEphemeralTimerModified { chat_id: u32, timer: u32 },
|
||||
|
||||
/// Contact(s) created, renamed, blocked or deleted.
|
||||
///
|
||||
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ContactsChanged {
|
||||
contact_id: Option<u32>,
|
||||
},
|
||||
ContactsChanged { contact_id: Option<u32> },
|
||||
|
||||
/// Location of one or more contact has changed.
|
||||
///
|
||||
@@ -214,9 +161,7 @@ pub enum EventType {
|
||||
/// If the locations of several contacts have been changed,
|
||||
/// this parameter is set to `None`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
LocationChanged {
|
||||
contact_id: Option<u32>,
|
||||
},
|
||||
LocationChanged { contact_id: Option<u32> },
|
||||
|
||||
/// Inform about the configuration progress started by configure().
|
||||
ConfigureProgress {
|
||||
@@ -234,9 +179,7 @@ pub enum EventType {
|
||||
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ImexProgress {
|
||||
progress: usize,
|
||||
},
|
||||
ImexProgress { progress: usize },
|
||||
|
||||
/// A file has been exported. A file has been written by imex().
|
||||
/// This event may be sent multiple times by a single call to imex().
|
||||
@@ -246,9 +189,7 @@ pub enum EventType {
|
||||
///
|
||||
/// @param data2 0
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ImexFileWritten {
|
||||
path: String,
|
||||
},
|
||||
ImexFileWritten { path: String },
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the inviter
|
||||
/// (Alice, the person who shows the QR code).
|
||||
@@ -263,10 +204,7 @@ pub enum EventType {
|
||||
/// 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.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
SecurejoinInviterProgress {
|
||||
contact_id: u32,
|
||||
progress: usize,
|
||||
},
|
||||
SecurejoinInviterProgress { contact_id: u32, progress: usize },
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the joiner
|
||||
/// (Bob, the person who scans the QR code).
|
||||
@@ -277,10 +215,7 @@ pub enum EventType {
|
||||
/// 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)
|
||||
#[serde(rename_all = "camelCase")]
|
||||
SecurejoinJoinerProgress {
|
||||
contact_id: u32,
|
||||
progress: usize,
|
||||
},
|
||||
SecurejoinJoinerProgress { contact_id: u32, progress: usize },
|
||||
|
||||
/// The connectivity to the server changed.
|
||||
/// This means that you should refresh the connectivity view
|
||||
@@ -288,8 +223,17 @@ pub enum EventType {
|
||||
/// getConnectivityHtml() for details.
|
||||
ConnectivityChanged,
|
||||
|
||||
/// Deprecated by `ConfigSynced`.
|
||||
SelfavatarChanged,
|
||||
|
||||
/// A multi-device synced config value changed. Maybe the app needs to refresh smth. For
|
||||
/// uniformity this is emitted on the source device too. The value isn't here, otherwise it
|
||||
/// would be logged which might not be good for privacy.
|
||||
ConfigSynced {
|
||||
/// Configuration key.
|
||||
key: String,
|
||||
},
|
||||
|
||||
#[serde(rename_all = "camelCase")]
|
||||
WebxdcStatusUpdate {
|
||||
msg_id: u32,
|
||||
@@ -298,9 +242,14 @@ pub enum EventType {
|
||||
|
||||
/// Inform that a message containing a webxdc instance has been deleted
|
||||
#[serde(rename_all = "camelCase")]
|
||||
WebxdcInstanceDeleted {
|
||||
msg_id: u32,
|
||||
},
|
||||
WebxdcInstanceDeleted { msg_id: u32 },
|
||||
|
||||
/// Tells that the Background fetch was completed (or timed out).
|
||||
/// This event acts as a marker, when you reach this event you can be sure
|
||||
/// that all events emitted during the background fetch were processed.
|
||||
///
|
||||
/// This event is only emitted by the account manager
|
||||
AccountsBackgroundFetchDone,
|
||||
}
|
||||
|
||||
impl From<CoreEventType> for EventType {
|
||||
@@ -336,9 +285,7 @@ impl From<CoreEventType> for EventType {
|
||||
chat_id: chat_id.to_u32(),
|
||||
msg_id: msg_id.to_u32(),
|
||||
},
|
||||
CoreEventType::IncomingMsgBunch { msg_ids } => IncomingMsgBunch {
|
||||
msg_ids: msg_ids.into_iter().map(|id| id.to_u32()).collect(),
|
||||
},
|
||||
CoreEventType::IncomingMsgBunch => IncomingMsgBunch,
|
||||
CoreEventType::MsgsNoticed(chat_id) => MsgsNoticed {
|
||||
chat_id: chat_id.to_u32(),
|
||||
},
|
||||
@@ -396,6 +343,9 @@ impl From<CoreEventType> for EventType {
|
||||
},
|
||||
CoreEventType::ConnectivityChanged => ConnectivityChanged,
|
||||
CoreEventType::SelfavatarChanged => SelfavatarChanged,
|
||||
CoreEventType::ConfigSynced { key } => ConfigSynced {
|
||||
key: key.to_string(),
|
||||
},
|
||||
CoreEventType::WebxdcStatusUpdate {
|
||||
msg_id,
|
||||
status_update_serial,
|
||||
@@ -406,6 +356,7 @@ impl From<CoreEventType> for EventType {
|
||||
CoreEventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
|
||||
msg_id: msg_id.to_u32(),
|
||||
},
|
||||
CoreEventType::AccountsBackgroundFetchDone => AccountsBackgroundFetchDone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use super::reactions::JSONRPCReactions;
|
||||
use super::webxdc::WebxdcMessageInfo;
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename_all = "camelCase", tag = "variant")]
|
||||
#[serde(rename_all = "camelCase", tag = "kind")]
|
||||
pub enum MessageLoadResult {
|
||||
Message(MessageObject),
|
||||
LoadingError { error: String },
|
||||
@@ -105,11 +105,6 @@ enum MessageQuote {
|
||||
}
|
||||
|
||||
impl MessageObject {
|
||||
pub async fn from_message_id(context: &Context, message_id: u32) -> Result<Self> {
|
||||
let msg_id = MsgId::new(message_id);
|
||||
Self::from_msg_id(context, msg_id).await
|
||||
}
|
||||
|
||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
||||
let message = Message::load_from_db(context, msg_id).await?;
|
||||
|
||||
@@ -345,6 +340,7 @@ pub enum SystemMessageType {
|
||||
SecurejoinMessage,
|
||||
LocationStreamingEnabled,
|
||||
LocationOnly,
|
||||
InvalidUnencryptedMail,
|
||||
|
||||
/// Chat ephemeral message timer is changed.
|
||||
EphemeralTimerChanged,
|
||||
@@ -385,6 +381,7 @@ impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
|
||||
SystemMessage::MultiDeviceSync => SystemMessageType::MultiDeviceSync,
|
||||
SystemMessage::WebxdcStatusUpdate => SystemMessageType::WebxdcStatusUpdate,
|
||||
SystemMessage::WebxdcInfoMessage => SystemMessageType::WebxdcInfoMessage,
|
||||
SystemMessage::InvalidUnencryptedMail => SystemMessageType::InvalidUnencryptedMail,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,9 +543,115 @@ pub struct MessageData {
|
||||
pub quoted_message_id: Option<u32>,
|
||||
}
|
||||
|
||||
impl MessageData {
|
||||
pub(crate) async fn create_message(self, context: &Context) -> Result<Message> {
|
||||
let mut message = Message::new(if let Some(viewtype) = self.viewtype {
|
||||
viewtype.into()
|
||||
} else if self.file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
message.set_text(self.text.unwrap_or_default());
|
||||
if self.html.is_some() {
|
||||
message.set_html(self.html);
|
||||
}
|
||||
if self.override_sender_name.is_some() {
|
||||
message.set_override_sender_name(self.override_sender_name);
|
||||
}
|
||||
if let Some(file) = self.file {
|
||||
message.set_file(file, None);
|
||||
}
|
||||
if let Some((latitude, longitude)) = self.location {
|
||||
message.set_location(latitude, longitude);
|
||||
}
|
||||
if let Some(id) = self.quoted_message_id {
|
||||
message
|
||||
.set_quote(
|
||||
context,
|
||||
Some(
|
||||
&Message::load_from_db(context, MsgId::new(id))
|
||||
.await
|
||||
.context("message to quote could not be loaded")?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(message)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MessageReadReceipt {
|
||||
pub contact_id: u32,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MessageInfo {
|
||||
rawtext: String,
|
||||
ephemeral_timer: EphemeralTimer,
|
||||
/// When message is ephemeral this contains the timestamp of the message expiry
|
||||
ephemeral_timestamp: Option<i64>,
|
||||
error: Option<String>,
|
||||
rfc724_mid: String,
|
||||
server_urls: Vec<String>,
|
||||
hop_info: Option<String>,
|
||||
}
|
||||
|
||||
impl MessageInfo {
|
||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
||||
let message = Message::load_from_db(context, msg_id).await?;
|
||||
let rawtext = msg_id.rawtext(context).await?;
|
||||
let ephemeral_timer = message.get_ephemeral_timer().into();
|
||||
let ephemeral_timestamp = match message.get_ephemeral_timer() {
|
||||
deltachat::ephemeral::Timer::Disabled => None,
|
||||
deltachat::ephemeral::Timer::Enabled { .. } => Some(message.get_ephemeral_timestamp()),
|
||||
};
|
||||
|
||||
let server_urls =
|
||||
MsgId::get_info_server_urls(context, message.rfc724_mid().to_owned()).await?;
|
||||
|
||||
let hop_info = msg_id.hop_info(context).await?;
|
||||
|
||||
Ok(Self {
|
||||
rawtext,
|
||||
ephemeral_timer,
|
||||
ephemeral_timestamp,
|
||||
error: message.error(),
|
||||
rfc724_mid: message.rfc724_mid().to_owned(),
|
||||
server_urls,
|
||||
hop_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, TypeDef, schemars::JsonSchema,
|
||||
)]
|
||||
#[serde(rename_all = "camelCase", tag = "variant")]
|
||||
pub enum EphemeralTimer {
|
||||
/// Timer is disabled.
|
||||
Disabled,
|
||||
|
||||
/// Timer is enabled.
|
||||
Enabled {
|
||||
/// Timer duration in seconds.
|
||||
///
|
||||
/// The value cannot be 0.
|
||||
duration: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<deltachat::ephemeral::Timer> for EphemeralTimer {
|
||||
fn from(value: deltachat::ephemeral::Timer) -> Self {
|
||||
match value {
|
||||
deltachat::ephemeral::Timer::Disabled => EphemeralTimer::Disabled,
|
||||
deltachat::ephemeral::Timer::Enabled { duration } => {
|
||||
EphemeralTimer::Enabled { duration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use typescript_type_def::TypeDef;
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderInfo {
|
||||
/// Unique ID, corresponding to provider database filename.
|
||||
pub id: String,
|
||||
pub before_login_hint: String,
|
||||
pub overview_page: String,
|
||||
pub status: u32, // in reality this is an enum, but for simplicity and because it gets converted into a number anyway, we use an u32 here.
|
||||
@@ -14,6 +16,7 @@ pub struct ProviderInfo {
|
||||
impl ProviderInfo {
|
||||
pub fn from_dc_type(provider: Option<&Provider>) -> Option<Self> {
|
||||
provider.map(|p| ProviderInfo {
|
||||
id: p.id.to_owned(),
|
||||
before_login_hint: p.before_login_hint.to_owned(),
|
||||
overview_page: p.overview_page.to_owned(),
|
||||
status: p.status.to_u32().unwrap(),
|
||||
|
||||
@@ -4,7 +4,7 @@ use typescript_type_def::TypeDef;
|
||||
|
||||
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
|
||||
#[serde(rename = "Qr", rename_all = "camelCase")]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum QrObject {
|
||||
AskVerifyContact {
|
||||
contact_id: u32,
|
||||
|
||||
@@ -13,10 +13,11 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn basic_json_rpc_functionality() -> anyhow::Result<()> {
|
||||
let tmp_dir = TempDir::new().unwrap().path().into();
|
||||
let accounts = Accounts::new(tmp_dir).await?;
|
||||
let writable = true;
|
||||
let accounts = Accounts::new(tmp_dir, writable).await?;
|
||||
let api = CommandApi::new(accounts);
|
||||
|
||||
let (sender, mut receiver) = unbounded::<String>();
|
||||
let (sender, receiver) = unbounded::<String>();
|
||||
|
||||
let (client, mut rx) = RpcClient::new();
|
||||
let session = RpcSession::new(client, api);
|
||||
@@ -35,17 +36,17 @@ mod tests {
|
||||
let request = r#"{"jsonrpc":"2.0","method":"add_account","params":[],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","id":1,"result":1}"#;
|
||||
session.handle_incoming(request).await;
|
||||
let result = receiver.next().await;
|
||||
let result = receiver.recv().await?;
|
||||
println!("{result:?}");
|
||||
assert_eq!(result, Some(response.to_owned()));
|
||||
assert_eq!(result, response.to_owned());
|
||||
}
|
||||
{
|
||||
let request = r#"{"jsonrpc":"2.0","method":"get_all_account_ids","params":[],"id":2}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","id":2,"result":[1]}"#;
|
||||
session.handle_incoming(request).await;
|
||||
let result = receiver.next().await;
|
||||
let result = receiver.recv().await?;
|
||||
println!("{result:?}");
|
||||
assert_eq!(result, Some(response.to_owned()));
|
||||
assert_eq!(result, response.to_owned());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -54,10 +55,11 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_batch_set_config() -> anyhow::Result<()> {
|
||||
let tmp_dir = TempDir::new().unwrap().path().into();
|
||||
let accounts = Accounts::new(tmp_dir).await?;
|
||||
let writable = true;
|
||||
let accounts = Accounts::new(tmp_dir, writable).await?;
|
||||
let api = CommandApi::new(accounts);
|
||||
|
||||
let (sender, mut receiver) = unbounded::<String>();
|
||||
let (sender, receiver) = unbounded::<String>();
|
||||
|
||||
let (client, mut rx) = RpcClient::new();
|
||||
let session = RpcSession::new(client, api);
|
||||
@@ -76,15 +78,15 @@ mod tests {
|
||||
let request = r#"{"jsonrpc":"2.0","method":"add_account","params":[],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","id":1,"result":1}"#;
|
||||
session.handle_incoming(request).await;
|
||||
let result = receiver.next().await;
|
||||
assert_eq!(result, Some(response.to_owned()));
|
||||
let result = receiver.recv().await?;
|
||||
assert_eq!(result, response.to_owned());
|
||||
}
|
||||
{
|
||||
let request = r#"{"jsonrpc":"2.0","method":"batch_set_config","id":2,"params":[1,{"addr":"","mail_user":"","mail_pw":"","mail_server":"","mail_port":"","mail_security":"","imap_certificate_checks":"","send_user":"","send_pw":"","send_server":"","send_port":"","send_security":"","smtp_certificate_checks":"","socks5_enabled":"0","socks5_host":"","socks5_port":"","socks5_user":"","socks5_password":""}]}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","id":2,"result":null}"#;
|
||||
session.handle_incoming(request).await;
|
||||
let result = receiver.next().await;
|
||||
assert_eq!(result, Some(response.to_owned()));
|
||||
let result = receiver.recv().await?;
|
||||
assert_eq!(result, response.to_owned());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -19,7 +19,8 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.map(|port| port.parse::<u16>().expect("DC_PORT must be a number"))
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
log::info!("Starting with accounts directory `{path}`.");
|
||||
let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap();
|
||||
let writable = true;
|
||||
let accounts = Accounts::new(PathBuf::from(&path), writable).await.unwrap();
|
||||
let state = CommandApi::new(accounts);
|
||||
|
||||
let app = Router::new()
|
||||
@@ -27,15 +28,13 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.layer(Extension(state.clone()));
|
||||
|
||||
tokio::spawn(async move {
|
||||
state.accounts.read().await.start_io().await;
|
||||
state.accounts.write().await.start_io().await;
|
||||
});
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||
log::info!("JSON-RPC WebSocket server listening on {}", addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ async function run() {
|
||||
const accounts = await client.rpc.getAllAccounts();
|
||||
console.log("accounts loaded", accounts);
|
||||
for (const account of accounts) {
|
||||
if (account.type === "Configured") {
|
||||
if (account.kind === "Configured") {
|
||||
write(
|
||||
$head,
|
||||
`<a href="#" onclick="selectDeltaAccount(${account.id})">
|
||||
@@ -57,7 +57,7 @@ async function run() {
|
||||
clear($main);
|
||||
const selectedAccount = SELECTED_ACCOUNT;
|
||||
const info = await client.rpc.getAccountInfo(selectedAccount);
|
||||
if (info.type !== "Configured") {
|
||||
if (info.kind !== "Configured") {
|
||||
return write($main, "Account is not configured");
|
||||
}
|
||||
write($main, `<h2>${info.addr!}</h2>`);
|
||||
@@ -81,8 +81,7 @@ async function run() {
|
||||
messageIds
|
||||
);
|
||||
for (const [_messageId, message] of Object.entries(messages)) {
|
||||
if (message.variant === "message")
|
||||
write($main, `<p>${message.text}</p>`);
|
||||
if (message.kind === "message") write($main, `<p>${message.text}</p>`);
|
||||
else write($main, `<p>loading error: ${message.error}</p>`);
|
||||
}
|
||||
}
|
||||
@@ -93,9 +92,9 @@ async function run() {
|
||||
$side,
|
||||
`
|
||||
<p class="message">
|
||||
[<strong>${event.type}</strong> on account ${accountId}]<br>
|
||||
[<strong>${event.kind}</strong> on account ${accountId}]<br>
|
||||
<em>f1:</em> ${JSON.stringify(
|
||||
Object.assign({}, event, { type: undefined })
|
||||
Object.assign({}, event, { kind: undefined })
|
||||
)}
|
||||
</p>`
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"@types/chai": "^4.2.21",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/ws": "^7.2.4",
|
||||
"c8": "^7.10.0",
|
||||
"chai": "^4.3.4",
|
||||
@@ -17,7 +16,6 @@
|
||||
"esbuild": "^0.17.9",
|
||||
"http-server": "^14.1.1",
|
||||
"mocha": "^9.1.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.6.2",
|
||||
"typedoc": "^0.23.2",
|
||||
@@ -55,5 +53,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.125.0"
|
||||
"version": "1.137.2"
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ import { WebsocketTransport, BaseTransport, Request } from "yerpc";
|
||||
import { TinyEmitter } from "@deltachat/tiny-emitter";
|
||||
|
||||
type Events = { ALL: (accountId: number, event: EventType) => void } & {
|
||||
[Property in EventType["type"]]: (
|
||||
[Property in EventType["kind"]]: (
|
||||
accountId: number,
|
||||
event: Extract<EventType, { type: Property }>
|
||||
event: Extract<EventType, { kind: Property }>
|
||||
) => void;
|
||||
};
|
||||
|
||||
type ContextEvents = { ALL: (event: EventType) => void } & {
|
||||
[Property in EventType["type"]]: (
|
||||
event: Extract<EventType, { type: Property }>
|
||||
[Property in EventType["kind"]]: (
|
||||
event: Extract<EventType, { kind: Property }>
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type DcEvent = EventType;
|
||||
export type DcEventType<T extends EventType["type"]> = Extract<
|
||||
export type DcEventType<T extends EventType["kind"]> = Extract<
|
||||
EventType,
|
||||
{ type: T }
|
||||
{ kind: T }
|
||||
>;
|
||||
|
||||
export class BaseDeltaChat<
|
||||
@@ -46,12 +46,12 @@ export class BaseDeltaChat<
|
||||
while (true) {
|
||||
const event = await this.rpc.getNextEvent();
|
||||
//@ts-ignore
|
||||
this.emit(event.event.type, event.contextId, event.event);
|
||||
this.emit(event.event.kind, event.contextId, event.event);
|
||||
this.emit("ALL", event.contextId, event.event);
|
||||
|
||||
if (this.contextEmitters[event.contextId]) {
|
||||
this.contextEmitters[event.contextId].emit(
|
||||
event.event.type,
|
||||
event.event.kind,
|
||||
//@ts-ignore
|
||||
event.event as any
|
||||
);
|
||||
|
||||
@@ -79,6 +79,9 @@ describe("basic tests", () => {
|
||||
accountId = await dc.rpc.addAccount();
|
||||
});
|
||||
it("should block and unblock contact", async function () {
|
||||
// Cannot send sync messages to self as we do not have a self address.
|
||||
await dc.rpc.setConfig(accountId, "sync_msgs", "0");
|
||||
|
||||
const contactId = await dc.rpc.createContact(
|
||||
accountId,
|
||||
"example@delta.chat",
|
||||
|
||||
@@ -13,27 +13,27 @@ describe("online tests", function () {
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000);
|
||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||
if (!process.env.CHATMAIL_DOMAIN) {
|
||||
if (process.env.COVERAGE && !process.env.COVERAGE_OFFLINE) {
|
||||
console.error(
|
||||
"CAN NOT RUN COVERAGE correctly: Missing DCC_NEW_TMP_EMAIL environment variable!\n\n",
|
||||
"CAN NOT RUN COVERAGE correctly: Missing CHATMAIL_DOMAIN environment variable!\n\n",
|
||||
"You can set COVERAGE_OFFLINE=1 to circumvent this check and skip the online tests, but those coverage results will be wrong, because some functions can only be tested in the online test"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
"Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests"
|
||||
"Missing CHATMAIL_DOMAIN environment variable!, skip integration tests"
|
||||
);
|
||||
this.skip();
|
||||
}
|
||||
serverHandle = await startServer();
|
||||
dc = new DeltaChat(serverHandle.stdin, serverHandle.stdout, true);
|
||||
|
||||
dc.on("ALL", (contextId, { type }) => {
|
||||
if (type !== "Info") console.log(contextId, type);
|
||||
dc.on("ALL", (contextId, { kind }) => {
|
||||
if (kind !== "Info") console.log(contextId, kind);
|
||||
});
|
||||
|
||||
account1 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
account1 = createTempUser(process.env.CHATMAIL_DOMAIN);
|
||||
if (!account1 || !account1.email || !account1.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
@@ -41,7 +41,7 @@ describe("online tests", function () {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
account2 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
account2 = createTempUser(process.env.CHATMAIL_DOMAIN);
|
||||
if (!account2 || !account2.email || !account2.password) {
|
||||
console.log(
|
||||
"We didn't got back an account2 from the api, skip integration tests"
|
||||
@@ -148,7 +148,7 @@ describe("online tests", function () {
|
||||
waitForEvent(dc, "IncomingMsg", accountId1),
|
||||
]);
|
||||
dc.rpc.miscSendTextMessage(accountId2, chatId, "super secret message");
|
||||
// Check if answer arives at A and if it is encrypted
|
||||
// Check if answer arrives at A and if it is encrypted
|
||||
await eventPromise2;
|
||||
|
||||
const messageId = (
|
||||
@@ -177,12 +177,12 @@ describe("online tests", function () {
|
||||
});
|
||||
});
|
||||
|
||||
async function waitForEvent<T extends DcEvent["type"]>(
|
||||
async function waitForEvent<T extends DcEvent["kind"]>(
|
||||
dc: DeltaChat,
|
||||
eventType: T,
|
||||
accountId: number,
|
||||
timeout: number = EVENT_TIMEOUT
|
||||
): Promise<Extract<DcEvent, { type: T }>> {
|
||||
): Promise<Extract<DcEvent, { kind: T }>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rejectTimeout = setTimeout(
|
||||
() => reject(new Error("Timeout reached before event came in")),
|
||||
|
||||
@@ -2,7 +2,6 @@ import { tmpdir } from "os";
|
||||
import { join, resolve } from "path";
|
||||
import { mkdtemp, rm } from "fs/promises";
|
||||
import { spawn, exec } from "child_process";
|
||||
import fetch from "node-fetch";
|
||||
import { Readable, Writable } from "node:stream";
|
||||
|
||||
export type RpcServerHandle = {
|
||||
@@ -57,15 +56,14 @@ export async function startServer(): Promise<RpcServerHandle> {
|
||||
};
|
||||
}
|
||||
|
||||
export async function createTempUser(url: string) {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"cache-control": "no-cache",
|
||||
},
|
||||
});
|
||||
if (!response.ok) throw new Error("Received invalid response");
|
||||
return response.json();
|
||||
export function createTempUser(chatmailDomain: String) {
|
||||
const charset = "2345789acdefghjkmnpqrstuvwxyz";
|
||||
let user = "ci-";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
user += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
const email = user + "@" + chatmailDomain;
|
||||
return { email: email, password: user + "$" + user };
|
||||
}
|
||||
|
||||
function getTargetDir(): Promise<string> {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.125.0"
|
||||
version = "1.137.2"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
anyhow = "1"
|
||||
deltachat = { path = "..", features = ["internals"]}
|
||||
dirs = "5"
|
||||
log = "0.4.19"
|
||||
log = "0.4.21"
|
||||
pretty_env_logger = "0.5"
|
||||
rusqlite = "0.29"
|
||||
rustyline = "12"
|
||||
rusqlite = "0.31"
|
||||
rustyline = "14"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -3,7 +3,7 @@ extern crate dirs;
|
||||
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use deltachat::chat::{
|
||||
@@ -18,6 +18,7 @@ use deltachat::imex::*;
|
||||
use deltachat::location;
|
||||
use deltachat::log::LogExt;
|
||||
use deltachat::message::{self, Message, MessageState, MsgId, Viewtype};
|
||||
use deltachat::mimeparser::SystemMessage;
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::qr::*;
|
||||
use deltachat::reaction::send_reaction;
|
||||
@@ -32,14 +33,6 @@ use tokio::fs;
|
||||
/// e.g. bitmask 7 triggers actions defined with bits 1, 2 and 4.
|
||||
async fn reset_tables(context: &Context, bits: i32) {
|
||||
println!("Resetting tables ({bits})...");
|
||||
if 0 != bits & 1 {
|
||||
context
|
||||
.sql()
|
||||
.execute("DELETE FROM jobs;", ())
|
||||
.await
|
||||
.unwrap();
|
||||
println!("(1) Jobs reset.");
|
||||
}
|
||||
if 0 != bits & 2 {
|
||||
context
|
||||
.sql()
|
||||
@@ -210,7 +203,17 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
} else {
|
||||
"[FRESH]"
|
||||
},
|
||||
if msg.is_info() { "[INFO]" } else { "" },
|
||||
if msg.is_info() {
|
||||
if msg.get_info_type() == SystemMessage::ChatProtectionEnabled {
|
||||
"[INFO 🛡️]"
|
||||
} else if msg.get_info_type() == SystemMessage::ChatProtectionDisabled {
|
||||
"[INFO 🛡️❌]"
|
||||
} else {
|
||||
"[INFO]"
|
||||
}
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if msg.get_viewtype() == Viewtype::VideochatInvitation {
|
||||
format!(
|
||||
"[VIDEOCHAT-INVITATION: {}, type={}]",
|
||||
@@ -273,13 +276,8 @@ async fn log_contactlist(context: &Context, contacts: &[ContactId]) -> Result<()
|
||||
let contact = Contact::get_by_id(context, *contact_id).await?;
|
||||
let name = contact.get_display_name();
|
||||
let addr = contact.get_addr();
|
||||
let verified_state = contact.is_verified(context).await?;
|
||||
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
||||
if verified_state == VerifiedStatus::BidirectVerified {
|
||||
" √√"
|
||||
} else {
|
||||
" √"
|
||||
}
|
||||
let verified_str = if contact.is_verified(context).await? {
|
||||
" √"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
@@ -341,6 +339,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
export-keys\n\
|
||||
import-keys\n\
|
||||
export-setup\n\
|
||||
dump <filename>\n\n
|
||||
read <filename>\n\n
|
||||
poke [<eml-file>|<folder>|<addr> <key-file>]\n\
|
||||
reset <flags>\n\
|
||||
stop\n\
|
||||
@@ -395,8 +395,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
unpin <chat-id>\n\
|
||||
mute <chat-id> [<seconds>]\n\
|
||||
unmute <chat-id>\n\
|
||||
protect <chat-id>\n\
|
||||
unprotect <chat-id>\n\
|
||||
delchat <chat-id>\n\
|
||||
accept <chat-id>\n\
|
||||
decline <chat-id>\n\
|
||||
@@ -518,6 +516,14 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
&setup_code,
|
||||
);
|
||||
}
|
||||
"dump" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <filename> missing.");
|
||||
serialize_database(&context, arg1).await?;
|
||||
}
|
||||
"read" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <filename> missing.");
|
||||
deserialize_database(&context, arg1).await?;
|
||||
}
|
||||
"poke" => {
|
||||
ensure!(poke_spec(&context, Some(arg1)).await, "Poke failed");
|
||||
}
|
||||
@@ -1071,20 +1077,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
};
|
||||
chat::set_muted(&context, chat_id, duration).await?;
|
||||
}
|
||||
"protect" | "unprotect" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat_id
|
||||
.set_protection(
|
||||
&context,
|
||||
match arg0 {
|
||||
"protect" => ProtectionStatus::Protected,
|
||||
"unprotect" => ProtectionStatus::Unprotected,
|
||||
_ => unreachable!("arg0={:?}", arg0),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
"delchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
|
||||
@@ -9,7 +9,6 @@ extern crate deltachat;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use ansi_term::Color;
|
||||
@@ -20,8 +19,7 @@ use deltachat::context::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::qr_code_generator::get_securejoin_qr_svg;
|
||||
use deltachat::securejoin::*;
|
||||
use deltachat::stock_str::StockStrings;
|
||||
use deltachat::{EventType, Events};
|
||||
use deltachat::EventType;
|
||||
use log::{error, info, warn};
|
||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
||||
use rustyline::error::ReadlineError;
|
||||
@@ -299,8 +297,8 @@ impl Highlighter for DcHelper {
|
||||
self.highlighter.highlight(line, pos)
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, pos: usize) -> bool {
|
||||
self.highlighter.highlight_char(line, pos)
|
||||
fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
|
||||
self.highlighter.highlight_char(line, pos, forced)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +310,10 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
|
||||
println!("Error: Bad arguments, expected [db-name].");
|
||||
bail!("No db-name specified");
|
||||
}
|
||||
let context = Context::new(Path::new(&args[1]), 0, Events::new(), StockStrings::new()).await?;
|
||||
let context = ContextBuilder::new(args[1].clone().into())
|
||||
.with_id(1)
|
||||
.open()
|
||||
.await?;
|
||||
|
||||
let events = context.get_event_emitter();
|
||||
tokio::task::spawn(async move {
|
||||
@@ -481,7 +482,10 @@ async fn handle_cmd(
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
pretty_env_logger::formatted_timed_builder()
|
||||
.parse_default_env()
|
||||
.filter_module("deltachat_repl", log::LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let args = std::env::args().collect();
|
||||
start(args).await?;
|
||||
|
||||
@@ -25,7 +25,7 @@ $ pip install .
|
||||
## Testing
|
||||
|
||||
1. Build `deltachat-rpc-server` with `cargo build -p deltachat-rpc-server`.
|
||||
2. Run `PATH="../target/debug:$PATH" tox`.
|
||||
2. Run `CHATMAIL_DOMAIN=nine.testrun.org PATH="../target/debug:$PATH" tox`.
|
||||
|
||||
Additional arguments to `tox` are passed to pytest, e.g. `tox -- -s` does not capture test output.
|
||||
|
||||
|
||||
4
deltachat-rpc-client/examples/echobot_advanced.py
Normal file → Executable file
4
deltachat-rpc-client/examples/echobot_advanced.py
Normal file → Executable file
@@ -14,9 +14,9 @@ hooks = events.HookCollection()
|
||||
|
||||
@hooks.on(events.RawEvent)
|
||||
def log_event(event):
|
||||
if event.type == EventType.INFO:
|
||||
if event.kind == EventType.INFO:
|
||||
logging.info(event.msg)
|
||||
elif event.type == EventType.WARNING:
|
||||
elif event.kind == EventType.WARNING:
|
||||
logging.warning(event.msg)
|
||||
|
||||
|
||||
|
||||
8
deltachat-rpc-client/examples/echobot_no_hooks.py
Normal file → Executable file
8
deltachat-rpc-client/examples/echobot_no_hooks.py
Normal file → Executable file
@@ -40,13 +40,13 @@ def main():
|
||||
|
||||
while True:
|
||||
event = account.wait_for_event()
|
||||
if event["type"] == EventType.INFO:
|
||||
if event["kind"] == EventType.INFO:
|
||||
logging.info("%s", event["msg"])
|
||||
elif event["type"] == EventType.WARNING:
|
||||
elif event["kind"] == EventType.WARNING:
|
||||
logging.warning("%s", event["msg"])
|
||||
elif event["type"] == EventType.ERROR:
|
||||
elif event["kind"] == EventType.ERROR:
|
||||
logging.error("%s", event["msg"])
|
||||
elif event["type"] == EventType.INCOMING_MSG:
|
||||
elif event["kind"] == EventType.INCOMING_MSG:
|
||||
logging.info("Got an incoming message")
|
||||
process_messages()
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
|
||||
requires = ["setuptools>=45"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat-rpc-client"
|
||||
version = "1.137.2"
|
||||
description = "Python client for Delta Chat core JSON-RPC interface"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
@@ -19,9 +20,7 @@ classifiers = [
|
||||
"Topic :: Communications :: Chat",
|
||||
"Topic :: Communications :: Email"
|
||||
]
|
||||
dynamic = [
|
||||
"version"
|
||||
]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
deltachat_rpc_client = [
|
||||
@@ -31,9 +30,6 @@ deltachat_rpc_client = [
|
||||
[project.entry-points.pytest11]
|
||||
"deltachat_rpc_client.pytestplugin" = "deltachat_rpc_client.pytestplugin"
|
||||
|
||||
[tool.setuptools_scm]
|
||||
root = ".."
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
|
||||
@@ -71,6 +67,3 @@ line-length = 120
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
log_cli = true
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Delta Chat JSON-RPC high-level API"""
|
||||
|
||||
from ._utils import AttrDict, run_bot_cli, run_client_cli
|
||||
from .account import Account
|
||||
from .chat import Chat
|
||||
|
||||
@@ -168,3 +168,33 @@ def parse_system_add_remove(text: str) -> Optional[Tuple[str, str, str]]:
|
||||
return "removed", addr, addr
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class futuremethod: # noqa: N801
|
||||
"""Decorator for async methods."""
|
||||
|
||||
def __init__(self, func):
|
||||
self._func = func
|
||||
|
||||
def __get__(self, instance, owner=None):
|
||||
if instance is None:
|
||||
return self
|
||||
|
||||
def future(*args):
|
||||
generator = self._func(instance, *args)
|
||||
res = next(generator)
|
||||
|
||||
def f():
|
||||
try:
|
||||
generator.send(res())
|
||||
except StopIteration as e:
|
||||
return e.value
|
||||
|
||||
return f
|
||||
|
||||
def wrapper(*args):
|
||||
f = future(*args)
|
||||
return f()
|
||||
|
||||
wrapper.future = future
|
||||
return wrapper
|
||||
|
||||
@@ -2,9 +2,9 @@ from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
||||
from warnings import warn
|
||||
|
||||
from ._utils import AttrDict
|
||||
from ._utils import AttrDict, futuremethod
|
||||
from .chat import Chat
|
||||
from .const import ChatlistFlag, ContactFlag, SpecialContactId
|
||||
from .const import ChatlistFlag, ContactFlag, EventType, SpecialContactId
|
||||
from .contact import Contact
|
||||
from .message import Message
|
||||
|
||||
@@ -76,9 +76,16 @@ class Account:
|
||||
"""Get self avatar."""
|
||||
return self.get_config("selfavatar")
|
||||
|
||||
def configure(self) -> None:
|
||||
def check_qr(self, qr):
|
||||
return self._rpc.check_qr(self.id, qr)
|
||||
|
||||
def set_config_from_qr(self, qr: str):
|
||||
self._rpc.set_config_from_qr(self.id, qr)
|
||||
|
||||
@futuremethod
|
||||
def configure(self):
|
||||
"""Configure an account."""
|
||||
self._rpc.configure(self.id)
|
||||
yield self._rpc.configure.future(self.id)
|
||||
|
||||
def create_contact(self, obj: Union[int, str, Contact], name: Optional[str] = None) -> Contact:
|
||||
"""Create a new Contact or return an existing one.
|
||||
@@ -111,6 +118,20 @@ class Account:
|
||||
contacts = self._rpc.get_blocked_contacts(self.id)
|
||||
return [AttrDict(contact=Contact(self, contact["id"]), **contact) for contact in contacts]
|
||||
|
||||
def get_chat_by_contact(self, contact: Union[int, Contact]) -> Optional[Chat]:
|
||||
"""Return 1:1 chat for a contact if it exists."""
|
||||
if isinstance(contact, Contact):
|
||||
assert contact.account == self
|
||||
contact_id = contact.id
|
||||
elif isinstance(contact, int):
|
||||
contact_id = contact
|
||||
else:
|
||||
raise ValueError(f"{contact!r} is not a contact")
|
||||
chat_id = self._rpc.get_chat_id_by_contact_id(self.id, contact_id)
|
||||
if chat_id:
|
||||
return Chat(self, chat_id)
|
||||
return None
|
||||
|
||||
def get_contacts(
|
||||
self,
|
||||
query: Optional[str] = None,
|
||||
@@ -204,7 +225,7 @@ class Account:
|
||||
The function returns immediately and the handshake runs in background, sending
|
||||
and receiving several messages.
|
||||
Subsequent calls of `secure_join()` will abort previous, unfinished handshakes.
|
||||
See https://countermitm.readthedocs.io/en/latest/new.html for protocol details.
|
||||
See https://securejoin.readthedocs.io/en/latest/new.html for protocol details.
|
||||
|
||||
:param qrdata: The text of the scanned QR code.
|
||||
"""
|
||||
@@ -250,6 +271,25 @@ class Account:
|
||||
next_msg_ids = self._rpc.wait_next_msgs(self.id)
|
||||
return [Message(self, msg_id) for msg_id in next_msg_ids]
|
||||
|
||||
def wait_for_incoming_msg_event(self):
|
||||
"""Wait for incoming message event and return it."""
|
||||
while True:
|
||||
event = self.wait_for_event()
|
||||
if event.kind == EventType.INCOMING_MSG:
|
||||
return event
|
||||
|
||||
def wait_for_securejoin_inviter_success(self):
|
||||
while True:
|
||||
event = self.wait_for_event()
|
||||
if event["kind"] == "SecurejoinInviterProgress" and event["progress"] == 1000:
|
||||
break
|
||||
|
||||
def wait_for_securejoin_joiner_success(self):
|
||||
while True:
|
||||
event = self.wait_for_event()
|
||||
if event["kind"] == "SecurejoinJoinerProgress" and event["progress"] == 1000:
|
||||
break
|
||||
|
||||
def get_fresh_messages_in_arrival_order(self) -> List[Message]:
|
||||
"""Return fresh messages list sorted in the order of their arrival, with ascending IDs."""
|
||||
warn(
|
||||
@@ -267,3 +307,13 @@ class Account:
|
||||
def import_backup(self, path, passphrase: str = "") -> None:
|
||||
"""Import backup."""
|
||||
self._rpc.import_backup(self.id, str(path), passphrase)
|
||||
|
||||
def export_self_keys(self, path) -> None:
|
||||
"""Export keys."""
|
||||
passphrase = "" # Setting passphrase is currently not supported.
|
||||
self._rpc.export_self_keys(self.id, str(path), passphrase)
|
||||
|
||||
def import_self_keys(self, path) -> None:
|
||||
"""Import keys."""
|
||||
passphrase = "" # Importing passphrase-protected keys is currently not supported.
|
||||
self._rpc.import_self_keys(self.id, str(path), passphrase)
|
||||
|
||||
@@ -54,14 +54,14 @@ class Chat:
|
||||
"""
|
||||
if duration is not None:
|
||||
assert duration > 0, "Invalid duration"
|
||||
dur: Union[str, dict] = {"Until": duration}
|
||||
dur: dict = {"kind": "Until", "duration": duration}
|
||||
else:
|
||||
dur = "Forever"
|
||||
dur = {"kind": "Forever"}
|
||||
self._rpc.set_chat_mute_duration(self.account.id, self.id, dur)
|
||||
|
||||
def unmute(self) -> None:
|
||||
"""Unmute this chat."""
|
||||
self._rpc.set_chat_mute_duration(self.account.id, self.id, "NotMuted")
|
||||
self._rpc.set_chat_mute_duration(self.account.id, self.id, {"kind": "NotMuted"})
|
||||
|
||||
def pin(self) -> None:
|
||||
"""Pin this chat."""
|
||||
@@ -84,7 +84,9 @@ class Chat:
|
||||
self._rpc.set_chat_name(self.account.id, self.id, name)
|
||||
|
||||
def set_ephemeral_timer(self, timer: int) -> None:
|
||||
"""Set ephemeral timer of this chat."""
|
||||
"""Set ephemeral timer of this chat in seconds.
|
||||
|
||||
0 means the timer is disabled, use 1 for immediate deletion."""
|
||||
self._rpc.set_chat_ephemeral_timer(self.account.id, self.id, timer)
|
||||
|
||||
def get_encryption_info(self) -> str:
|
||||
@@ -160,11 +162,12 @@ class Chat:
|
||||
text: Optional[str] = None,
|
||||
file: Optional[str] = None,
|
||||
quoted_msg: Optional[int] = None,
|
||||
viewtype: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Set draft message."""
|
||||
if isinstance(quoted_msg, Message):
|
||||
quoted_msg = quoted_msg.id
|
||||
self._rpc.misc_set_draft(self.account.id, self.id, text, file, quoted_msg)
|
||||
self._rpc.misc_set_draft(self.account.id, self.id, text, file, quoted_msg, viewtype)
|
||||
|
||||
def remove_draft(self) -> None:
|
||||
"""Remove draft message."""
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Event loop implementations offering high level event handling/hooking."""
|
||||
|
||||
import logging
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Dict,
|
||||
Iterable,
|
||||
Optional,
|
||||
@@ -92,7 +92,7 @@ class Client:
|
||||
"""Process events forever."""
|
||||
self.run_until(lambda _: False)
|
||||
|
||||
def run_until(self, func: Callable[[AttrDict], Union[bool, Coroutine]]) -> AttrDict:
|
||||
def run_until(self, func: Callable[[AttrDict], bool]) -> AttrDict:
|
||||
"""Process events until the given callable evaluates to True.
|
||||
|
||||
The callable should accept an AttrDict object representing the
|
||||
@@ -105,10 +105,10 @@ class Client:
|
||||
self._process_messages() # Process old messages.
|
||||
while True:
|
||||
event = self.account.wait_for_event()
|
||||
event["type"] = EventType(event.type)
|
||||
event["kind"] = EventType(event.kind)
|
||||
event["account"] = self.account
|
||||
self._on_event(event)
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
if event.kind == EventType.INCOMING_MSG:
|
||||
self._process_messages()
|
||||
|
||||
stop = func(event)
|
||||
@@ -196,7 +196,7 @@ class Client:
|
||||
|
||||
|
||||
class Bot(Client):
|
||||
"""Simple bot implementation that listent to events of a single account."""
|
||||
"""Simple bot implementation that listens to events of a single account."""
|
||||
|
||||
def configure(self, email: str, password: str, **kwargs) -> None:
|
||||
kwargs.setdefault("bot", "1")
|
||||
|
||||
@@ -61,6 +61,15 @@ class EventType(str, Enum):
|
||||
WEBXDC_INSTANCE_DELETED = "WebxdcInstanceDeleted"
|
||||
|
||||
|
||||
class ChatId(IntEnum):
|
||||
"""Special chat ids"""
|
||||
|
||||
TRASH = 3
|
||||
ARCHIVED_LINK = 6
|
||||
ALLDONE_HINT = 7
|
||||
LAST_SPECIAL = 9
|
||||
|
||||
|
||||
class ChatType(IntEnum):
|
||||
"""Chat types"""
|
||||
|
||||
@@ -122,3 +131,107 @@ class SystemMessageType(str, Enum):
|
||||
EPHEMERAL_TIMER_CHANGED = "EphemeralTimerChanged"
|
||||
MULTI_DEVICE_SYNC = "MultiDeviceSync"
|
||||
WEBXDC_INFO_MESSAGE = "WebxdcInfoMessage"
|
||||
|
||||
|
||||
class MessageState(IntEnum):
|
||||
"""State of the message."""
|
||||
|
||||
UNDEFINED = 0
|
||||
IN_FRESH = 10
|
||||
IN_NOTICED = 13
|
||||
IN_SEEN = 16
|
||||
OUT_PREPARING = 18
|
||||
OUT_DRAFT = 19
|
||||
OUT_PENDING = 20
|
||||
OUT_FAILED = 24
|
||||
OUT_DELIVERED = 26
|
||||
OUT_MDN_RCVD = 28
|
||||
|
||||
|
||||
class MessageId(IntEnum):
|
||||
"""Special message ids"""
|
||||
|
||||
DAYMARKER = 9
|
||||
LAST_SPECIAL = 9
|
||||
|
||||
|
||||
class CertificateChecks(IntEnum):
|
||||
"""Certificate checks mode"""
|
||||
|
||||
AUTOMATIC = 0
|
||||
STRICT = 1
|
||||
ACCEPT_INVALID_CERTIFICATES = 3
|
||||
|
||||
|
||||
class Connectivity(IntEnum):
|
||||
"""Connectivity states"""
|
||||
|
||||
NOT_CONNECTED = 1000
|
||||
CONNECTING = 2000
|
||||
WORKING = 3000
|
||||
CONNECTED = 4000
|
||||
|
||||
|
||||
class KeyGenType(IntEnum):
|
||||
"""Type of the key to generate"""
|
||||
|
||||
DEFAULT = 0
|
||||
RSA2048 = 1
|
||||
ED25519 = 2
|
||||
RSA4096 = 3
|
||||
|
||||
|
||||
# "Lp" means "login parameters"
|
||||
class LpAuthFlag(IntEnum):
|
||||
"""Authorization flags"""
|
||||
|
||||
OAUTH2 = 0x2
|
||||
NORMAL = 0x4
|
||||
|
||||
|
||||
class MediaQuality(IntEnum):
|
||||
"""Media quality setting"""
|
||||
|
||||
BALANCED = 0
|
||||
WORSE = 1
|
||||
|
||||
|
||||
class ProviderStatus(IntEnum):
|
||||
"""Provider status according to manual testing"""
|
||||
|
||||
OK = 1
|
||||
PREPARATION = 2
|
||||
BROKEN = 3
|
||||
|
||||
|
||||
class PushNotifyState(IntEnum):
|
||||
"""Push notifications state"""
|
||||
|
||||
NOT_CONNECTED = 0
|
||||
HEARTBEAT = 1
|
||||
CONNECTED = 2
|
||||
|
||||
|
||||
class ShowEmails(IntEnum):
|
||||
"""Show emails mode"""
|
||||
|
||||
OFF = 0
|
||||
ACCEPTED_CONTACTS = 1
|
||||
ALL = 2
|
||||
|
||||
|
||||
class SocketSecurity(IntEnum):
|
||||
"""Socket security"""
|
||||
|
||||
AUTOMATIC = 0
|
||||
SSL = 1
|
||||
STARTTLS = 2
|
||||
PLAIN = 3
|
||||
|
||||
|
||||
class VideochatType(IntEnum):
|
||||
"""Video chat URL type"""
|
||||
|
||||
UNKNOWN = 0
|
||||
BASICWEBRTC = 1
|
||||
JITSI = 2
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""High-level classes for event processing and filtering."""
|
||||
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Optional, Set, Tuple, Union
|
||||
@@ -79,7 +80,7 @@ class RawEvent(EventFilter):
|
||||
return False
|
||||
|
||||
def filter(self, event: "AttrDict") -> bool:
|
||||
if self.types and event.type not in self.types:
|
||||
if self.types and event.kind not in self.types:
|
||||
return False
|
||||
return self._call_func(event)
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ class Message:
|
||||
return AttrDict(reactions)
|
||||
return None
|
||||
|
||||
def get_sender_contact(self) -> Contact:
|
||||
from_id = self.get_snapshot().from_id
|
||||
return self.account.get_contact_by_id(from_id)
|
||||
|
||||
def mark_seen(self) -> None:
|
||||
"""Mark the message as seen."""
|
||||
self._rpc.markseen_msgs(self.account.id, [self.id])
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
import random
|
||||
from typing import AsyncGenerator, List, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from . import Account, AttrDict, Bot, Client, DeltaChat, EventType, Message
|
||||
from ._utils import futuremethod
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
def get_temp_credentials() -> dict:
|
||||
url = os.getenv("DCC_NEW_TMP_EMAIL")
|
||||
assert url, "Failed to get online account, DCC_NEW_TMP_EMAIL is not set"
|
||||
|
||||
request = urllib.request.Request(url, method="POST")
|
||||
with urllib.request.urlopen(request, timeout=60) as f:
|
||||
return json.load(f)
|
||||
domain = os.getenv("CHATMAIL_DOMAIN")
|
||||
username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
|
||||
password = f"{username}${username}"
|
||||
addr = f"{username}@{domain}"
|
||||
return {"email": addr, "password": password}
|
||||
|
||||
|
||||
class ACFactory:
|
||||
@@ -23,7 +22,9 @@ class ACFactory:
|
||||
self.deltachat = deltachat
|
||||
|
||||
def get_unconfigured_account(self) -> Account:
|
||||
return self.deltachat.add_account()
|
||||
account = self.deltachat.add_account()
|
||||
account.set_config("verified_one_on_one_chats", "1")
|
||||
return account
|
||||
|
||||
def get_unconfigured_bot(self) -> Bot:
|
||||
return Bot(self.get_unconfigured_account())
|
||||
@@ -37,9 +38,10 @@ class ACFactory:
|
||||
assert not account.is_configured()
|
||||
return account
|
||||
|
||||
def new_configured_account(self) -> Account:
|
||||
@futuremethod
|
||||
def new_configured_account(self):
|
||||
account = self.new_preconfigured_account()
|
||||
account.configure()
|
||||
yield account.configure.future()
|
||||
assert account.is_configured()
|
||||
return account
|
||||
|
||||
@@ -49,17 +51,29 @@ class ACFactory:
|
||||
bot.configure(credentials["email"], credentials["password"])
|
||||
return bot
|
||||
|
||||
def get_online_account(self) -> Account:
|
||||
account = self.new_configured_account()
|
||||
@futuremethod
|
||||
def get_online_account(self):
|
||||
account = yield self.new_configured_account.future()
|
||||
account.start_io()
|
||||
while True:
|
||||
event = account.wait_for_event()
|
||||
if event.type == EventType.IMAP_INBOX_IDLE:
|
||||
if event.kind == EventType.IMAP_INBOX_IDLE:
|
||||
break
|
||||
return account
|
||||
|
||||
def get_online_accounts(self, num: int) -> List[Account]:
|
||||
return [self.get_online_account() for _ in range(num)]
|
||||
futures = [self.get_online_account.future() for _ in range(num)]
|
||||
return [f() for f in futures]
|
||||
|
||||
def resetup_account(self, ac: Account) -> Account:
|
||||
"""Resetup account from scratch, losing the encryption key."""
|
||||
ac.stop_io()
|
||||
ac_clone = self.get_unconfigured_account()
|
||||
for i in ["addr", "mail_pw"]:
|
||||
ac_clone.set_config(i, ac.get_config(i))
|
||||
ac.remove()
|
||||
ac_clone.configure()
|
||||
return ac_clone
|
||||
|
||||
def send_message(
|
||||
self,
|
||||
@@ -95,7 +109,7 @@ class ACFactory:
|
||||
group=group,
|
||||
)
|
||||
|
||||
return to_client.run_until(lambda e: e.type == EventType.INCOMING_MSG)
|
||||
return to_client.run_until(lambda e: e.kind == EventType.INCOMING_MSG)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -5,13 +6,55 @@ import subprocess
|
||||
import sys
|
||||
from queue import Queue
|
||||
from threading import Event, Thread
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Dict, Iterator, Optional
|
||||
|
||||
|
||||
class JsonRpcError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RpcFuture:
|
||||
def __init__(self, rpc: "Rpc", request_id: int, event: Event):
|
||||
self.rpc = rpc
|
||||
self.request_id = request_id
|
||||
self.event = event
|
||||
|
||||
def __call__(self):
|
||||
self.event.wait()
|
||||
response = self.rpc.request_results.pop(self.request_id)
|
||||
if "error" in response:
|
||||
raise JsonRpcError(response["error"])
|
||||
if "result" in response:
|
||||
return response["result"]
|
||||
return None
|
||||
|
||||
|
||||
class RpcMethod:
|
||||
def __init__(self, rpc: "Rpc", name: str):
|
||||
self.rpc = rpc
|
||||
self.name = name
|
||||
|
||||
def __call__(self, *args) -> Any:
|
||||
"""Synchronously calls JSON-RPC method."""
|
||||
future = self.future(*args)
|
||||
return future()
|
||||
|
||||
def future(self, *args) -> Any:
|
||||
"""Asynchronously calls JSON-RPC method."""
|
||||
request_id = next(self.rpc.id_iterator)
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": self.name,
|
||||
"params": args,
|
||||
"id": request_id,
|
||||
}
|
||||
event = Event()
|
||||
self.rpc.request_events[request_id] = event
|
||||
self.rpc.request_queue.put(request)
|
||||
|
||||
return RpcFuture(self.rpc, request_id, event)
|
||||
|
||||
|
||||
class Rpc:
|
||||
def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
|
||||
"""The given arguments will be passed to subprocess.Popen()"""
|
||||
@@ -23,7 +66,7 @@ class Rpc:
|
||||
|
||||
self._kwargs = kwargs
|
||||
self.process: subprocess.Popen
|
||||
self.id: int
|
||||
self.id_iterator: Iterator[int]
|
||||
self.event_queues: Dict[int, Queue]
|
||||
# Map from request ID to `threading.Event`.
|
||||
self.request_events: Dict[int, Event]
|
||||
@@ -54,7 +97,7 @@ class Rpc:
|
||||
preexec_fn=os.setpgrp, # noqa: PLW1509
|
||||
**self._kwargs,
|
||||
)
|
||||
self.id = 0
|
||||
self.id_iterator = itertools.count(start=1)
|
||||
self.event_queues = {}
|
||||
self.request_events = {}
|
||||
self.request_results = {}
|
||||
@@ -131,7 +174,9 @@ class Rpc:
|
||||
event = self.get_next_event()
|
||||
account_id = event["contextId"]
|
||||
queue = self.get_queue(account_id)
|
||||
queue.put(event["event"])
|
||||
event = event["event"]
|
||||
logging.debug("account_id=%d got an event %s", account_id, event)
|
||||
queue.put(event)
|
||||
except Exception:
|
||||
# Log an exception if the event loop dies.
|
||||
logging.exception("Exception in the event loop")
|
||||
@@ -142,26 +187,4 @@ class Rpc:
|
||||
return queue.get()
|
||||
|
||||
def __getattr__(self, attr: str):
|
||||
def method(*args) -> Any:
|
||||
self.id += 1
|
||||
request_id = self.id
|
||||
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": attr,
|
||||
"params": args,
|
||||
"id": self.id,
|
||||
}
|
||||
event = Event()
|
||||
self.request_events[request_id] = event
|
||||
self.request_queue.put(request)
|
||||
event.wait()
|
||||
|
||||
response = self.request_results.pop(request_id)
|
||||
if "error" in response:
|
||||
raise JsonRpcError(response["error"])
|
||||
if "result" in response:
|
||||
return response["result"]
|
||||
return None
|
||||
|
||||
return method
|
||||
return RpcMethod(self, attr)
|
||||
|
||||
618
deltachat-rpc-client/tests/test_securejoin.py
Normal file
618
deltachat-rpc-client/tests/test_securejoin.py
Normal file
@@ -0,0 +1,618 @@
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
from deltachat_rpc_client import Chat, EventType, SpecialContactId
|
||||
|
||||
|
||||
def test_qr_setup_contact(acfactory, tmp_path) -> None:
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
qr_code, _svg = alice.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
|
||||
alice.wait_for_securejoin_inviter_success()
|
||||
|
||||
# Test that Alice verified Bob's profile.
|
||||
alice_contact_bob = alice.get_contact_by_addr(bob.get_config("addr"))
|
||||
alice_contact_bob_snapshot = alice_contact_bob.get_snapshot()
|
||||
assert alice_contact_bob_snapshot.is_verified
|
||||
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
# Test that Bob verified Alice's profile.
|
||||
bob_contact_alice = bob.get_contact_by_addr(alice.get_config("addr"))
|
||||
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
|
||||
assert bob_contact_alice_snapshot.is_verified
|
||||
|
||||
# Test that if Bob changes the key, backwards verification is lost.
|
||||
logging.info("Bob 2 is created")
|
||||
bob2 = acfactory.new_configured_account()
|
||||
bob2.export_self_keys(tmp_path)
|
||||
|
||||
logging.info("Bob imports a key")
|
||||
bob.import_self_keys(tmp_path / "private-key-default.asc")
|
||||
|
||||
assert bob.get_config("key_id") == "2"
|
||||
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
|
||||
assert not bob_contact_alice_snapshot.is_verified
|
||||
|
||||
|
||||
@pytest.mark.parametrize("protect", [True, False])
|
||||
def test_qr_securejoin(acfactory, protect):
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
logging.info("Alice creates a verified group")
|
||||
alice_chat = alice.create_group("Verified group", protect=protect)
|
||||
assert alice_chat.get_basic_snapshot().is_protected == protect
|
||||
|
||||
logging.info("Bob joins verified group")
|
||||
qr_code, _svg = alice_chat.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
|
||||
# Check that at least some of the handshake messages are deleted.
|
||||
for ac in [alice, bob]:
|
||||
while True:
|
||||
event = ac.wait_for_event()
|
||||
if event["kind"] == "ImapMessageDeleted":
|
||||
break
|
||||
|
||||
alice.wait_for_securejoin_inviter_success()
|
||||
|
||||
# Test that Alice verified Bob's profile.
|
||||
alice_contact_bob = alice.get_contact_by_addr(bob.get_config("addr"))
|
||||
alice_contact_bob_snapshot = alice_contact_bob.get_snapshot()
|
||||
assert alice_contact_bob_snapshot.is_verified
|
||||
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
|
||||
assert snapshot.chat.get_basic_snapshot().is_protected == protect
|
||||
|
||||
# Test that Bob verified Alice's profile.
|
||||
bob_contact_alice = bob.get_contact_by_addr(alice.get_config("addr"))
|
||||
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
|
||||
assert bob_contact_alice_snapshot.is_verified
|
||||
|
||||
|
||||
def test_qr_securejoin_contact_request(acfactory) -> None:
|
||||
"""Alice invites Bob to a group when Bob's chat with Alice is in a contact request mode."""
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
bob_addr = bob.get_config("addr")
|
||||
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
alice_chat_bob.send_text("Hello!")
|
||||
|
||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello!"
|
||||
bob_chat_alice = snapshot.chat
|
||||
assert bob_chat_alice.get_basic_snapshot().is_contact_request
|
||||
|
||||
alice_chat = alice.create_group("Verified group", protect=True)
|
||||
logging.info("Bob joins verified group")
|
||||
qr_code, _svg = alice_chat.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event["kind"] == "SecurejoinJoinerProgress" and event["progress"] == 1000:
|
||||
break
|
||||
|
||||
# Chat stays being a contact request.
|
||||
assert bob_chat_alice.get_basic_snapshot().is_contact_request
|
||||
|
||||
|
||||
def test_qr_readreceipt(acfactory) -> None:
|
||||
alice, bob, charlie = acfactory.get_online_accounts(3)
|
||||
|
||||
logging.info("Bob and Charlie setup contact with Alice")
|
||||
qr_code, _svg = alice.get_qr_code()
|
||||
|
||||
bob.secure_join(qr_code)
|
||||
charlie.secure_join(qr_code)
|
||||
|
||||
for joiner in [bob, charlie]:
|
||||
joiner.wait_for_securejoin_joiner_success()
|
||||
|
||||
logging.info("Alice creates a verified group")
|
||||
group = alice.create_group("Group", protect=True)
|
||||
|
||||
bob_addr = bob.get_config("addr")
|
||||
charlie_addr = charlie.get_config("addr")
|
||||
|
||||
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
|
||||
alice_contact_charlie = alice.create_contact(charlie_addr, "Charlie")
|
||||
|
||||
group.add_contact(alice_contact_bob)
|
||||
group.add_contact(alice_contact_charlie)
|
||||
|
||||
# Promote a group.
|
||||
group.send_message(text="Hello")
|
||||
|
||||
logging.info("Bob and Charlie receive a group")
|
||||
|
||||
bob_msg_id = bob.wait_for_incoming_msg_event().msg_id
|
||||
bob_message = bob.get_message_by_id(bob_msg_id)
|
||||
bob_snapshot = bob_message.get_snapshot()
|
||||
assert bob_snapshot.text == "Hello"
|
||||
|
||||
# Charlie receives the same "Hello" message as Bob.
|
||||
charlie.wait_for_incoming_msg_event()
|
||||
|
||||
logging.info("Bob sends a message to the group")
|
||||
|
||||
bob_out_message = bob_snapshot.chat.send_message(text="Hi from Bob!")
|
||||
|
||||
charlie_msg_id = charlie.wait_for_incoming_msg_event().msg_id
|
||||
charlie_message = charlie.get_message_by_id(charlie_msg_id)
|
||||
charlie_snapshot = charlie_message.get_snapshot()
|
||||
assert charlie_snapshot.text == "Hi from Bob!"
|
||||
|
||||
bob_contact_charlie = bob.create_contact(charlie_addr, "Charlie")
|
||||
assert not bob.get_chat_by_contact(bob_contact_charlie)
|
||||
|
||||
logging.info("Charlie reads Bob's message")
|
||||
charlie_message.mark_seen()
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event["kind"] == "MsgRead" and event["msg_id"] == bob_out_message.id:
|
||||
break
|
||||
|
||||
# Receiving a read receipt from Charlie
|
||||
# should not unblock hidden chat with Charlie for Bob.
|
||||
assert not bob.get_chat_by_contact(bob_contact_charlie)
|
||||
|
||||
|
||||
def test_setup_contact_resetup(acfactory) -> None:
|
||||
"""Tests that setup contact works after Alice resets the device and changes the key."""
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
qr_code, _svg = alice.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
alice = acfactory.resetup_account(alice)
|
||||
|
||||
qr_code, _svg = alice.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
|
||||
def test_verified_group_recovery(acfactory) -> None:
|
||||
"""Tests verified group recovery by reverifying a member and sending a message in a group."""
|
||||
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
|
||||
|
||||
logging.info("ac1 creates verified group")
|
||||
chat = ac1.create_group("Verified group", protect=True)
|
||||
assert chat.get_basic_snapshot().is_protected
|
||||
|
||||
logging.info("ac2 joins verified group")
|
||||
qr_code, _svg = chat.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
# ac1 has ac2 directly verified.
|
||||
ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
|
||||
assert ac1_contact_ac2.get_snapshot().verifier_id == SpecialContactId.SELF
|
||||
|
||||
logging.info("ac3 joins verified group")
|
||||
ac3_chat = ac3.secure_join(qr_code)
|
||||
ac3.wait_for_securejoin_joiner_success()
|
||||
ac3.wait_for_incoming_msg_event() # Member added
|
||||
|
||||
logging.info("ac2 logs in on a new device")
|
||||
ac2 = acfactory.resetup_account(ac2)
|
||||
|
||||
logging.info("ac2 reverifies with ac3")
|
||||
qr_code, _svg = ac3.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
logging.info("ac3 sends a message to the group")
|
||||
assert len(ac3_chat.get_contacts()) == 3
|
||||
ac3_chat.send_text("Hi!")
|
||||
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hi!"
|
||||
|
||||
msg_id = ac2.wait_for_incoming_msg_event().msg_id
|
||||
message = ac2.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert snapshot.text == "Hi!"
|
||||
|
||||
# ac1 contact is verified for ac2 because ac3 gossiped ac1 key in the "Hi!" message.
|
||||
ac1_contact = ac2.get_contact_by_addr(ac1.get_config("addr"))
|
||||
assert ac1_contact.get_snapshot().is_verified
|
||||
|
||||
# ac2 can write messages to the group.
|
||||
snapshot.chat.send_text("Works again!")
|
||||
|
||||
snapshot = ac3.get_message_by_id(ac3.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Works again!"
|
||||
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Works again!"
|
||||
|
||||
ac1_chat_messages = snapshot.chat.get_messages()
|
||||
ac2_addr = ac2.get_config("addr")
|
||||
assert ac1_chat_messages[-2].get_snapshot().text == f"Changed setup for {ac2_addr}"
|
||||
|
||||
# ac2 is now verified by ac3 for ac1
|
||||
ac1_contact_ac3 = ac1.get_contact_by_addr(ac3.get_config("addr"))
|
||||
assert ac1_contact_ac2.get_snapshot().verifier_id == ac1_contact_ac3.id
|
||||
|
||||
|
||||
def test_verified_group_member_added_recovery(acfactory) -> None:
|
||||
"""Tests verified group recovery by reverifiying than removing and adding a member back."""
|
||||
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
|
||||
|
||||
logging.info("ac1 creates verified group")
|
||||
chat = ac1.create_group("Verified group", protect=True)
|
||||
assert chat.get_basic_snapshot().is_protected
|
||||
|
||||
logging.info("ac2 joins verified group")
|
||||
qr_code, _svg = chat.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
# ac1 has ac2 directly verified.
|
||||
ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
|
||||
assert ac1_contact_ac2.get_snapshot().verifier_id == SpecialContactId.SELF
|
||||
|
||||
logging.info("ac3 joins verified group")
|
||||
ac3_chat = ac3.secure_join(qr_code)
|
||||
ac3.wait_for_securejoin_joiner_success()
|
||||
ac3.wait_for_incoming_msg_event() # Member added
|
||||
|
||||
logging.info("ac2 logs in on a new device")
|
||||
ac2 = acfactory.resetup_account(ac2)
|
||||
|
||||
logging.info("ac2 reverifies with ac3")
|
||||
qr_code, _svg = ac3.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
logging.info("ac3 sends a message to the group")
|
||||
assert len(ac3_chat.get_contacts()) == 3
|
||||
ac3_chat.send_text("Hi!")
|
||||
|
||||
msg_id = ac2.wait_for_incoming_msg_event().msg_id
|
||||
message = ac2.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
logging.info("Received message %s", snapshot.text)
|
||||
assert snapshot.text == "Hi!"
|
||||
|
||||
ac1.wait_for_incoming_msg_event() # Hi!
|
||||
|
||||
ac3_contact_ac2 = ac3.get_contact_by_addr(ac2.get_config("addr"))
|
||||
ac3_chat.remove_contact(ac3_contact_ac2)
|
||||
ac3_chat.add_contact(ac3_contact_ac2)
|
||||
|
||||
msg_id = ac2.wait_for_incoming_msg_event().msg_id
|
||||
message = ac2.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert "removed" in snapshot.text
|
||||
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert "removed" in snapshot.text
|
||||
|
||||
event = ac2.wait_for_incoming_msg_event()
|
||||
msg_id = event.msg_id
|
||||
chat_id = event.chat_id
|
||||
message = ac2.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
logging.info("ac2 got event message: %s", snapshot.text)
|
||||
assert "added" in snapshot.text
|
||||
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert "added" in snapshot.text
|
||||
|
||||
chat = Chat(ac2, chat_id)
|
||||
chat.send_text("Works again!")
|
||||
|
||||
msg_id = ac3.wait_for_incoming_msg_event().msg_id
|
||||
message = ac3.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert snapshot.text == "Works again!"
|
||||
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Works again!"
|
||||
|
||||
ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
|
||||
ac1_contact_ac2_snapshot = ac1_contact_ac2.get_snapshot()
|
||||
assert ac1_contact_ac2_snapshot.is_verified
|
||||
assert ac1_contact_ac2_snapshot.verifier_id == ac1.get_contact_by_addr(ac3.get_config("addr")).id
|
||||
|
||||
# ac2 is now verified by ac3 for ac1
|
||||
ac1_contact_ac3 = ac1.get_contact_by_addr(ac3.get_config("addr"))
|
||||
assert ac1_contact_ac2.get_snapshot().verifier_id == ac1_contact_ac3.id
|
||||
|
||||
|
||||
def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
|
||||
"""Regression test for
|
||||
issue <https://github.com/deltachat/deltachat-core-rust/issues/4894>.
|
||||
"""
|
||||
ac1, ac2, ac3, ac4 = acfactory.get_online_accounts(4)
|
||||
|
||||
logging.info("ac3: verify with ac2")
|
||||
qr_code, _svg = ac2.get_qr_code()
|
||||
ac3.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_inviter_success()
|
||||
|
||||
# in order for ac2 to have pending bobstate with a verified group
|
||||
# we first create a fully joined verified group, and then start
|
||||
# joining a second time but interrupt it, to create pending bob state
|
||||
|
||||
logging.info("ac1: create verified group that ac2 fully joins")
|
||||
ch1 = ac1.create_group("Group", protect=True)
|
||||
qr_code, _svg = ch1.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac1.wait_for_securejoin_inviter_success()
|
||||
|
||||
# ensure ac1 can write and ac2 receives messages in verified chat
|
||||
ch1.send_text("ac1 says hello")
|
||||
while 1:
|
||||
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
if snapshot.text == "ac1 says hello":
|
||||
assert snapshot.chat.get_basic_snapshot().is_protected
|
||||
break
|
||||
|
||||
logging.info("ac1: let ac2 join again but shutoff ac1 in the middle of securejoin")
|
||||
qr_code, _svg = ch1.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac1.remove()
|
||||
logging.info("ac2 now has pending bobstate but ac1 is shutoff")
|
||||
|
||||
# we meanwhile expect ac3/ac2 verification started in the beginning to have completed
|
||||
assert ac3.get_contact_by_addr(ac2.get_config("addr")).get_snapshot().is_verified
|
||||
assert ac2.get_contact_by_addr(ac3.get_config("addr")).get_snapshot().is_verified
|
||||
|
||||
logging.info("ac3: create a verified group VG with ac2")
|
||||
vg = ac3.create_group("ac3-created", protect=True)
|
||||
vg.add_contact(ac3.get_contact_by_addr(ac2.get_config("addr")))
|
||||
|
||||
# ensure ac2 receives message in VG
|
||||
vg.send_text("hello")
|
||||
while 1:
|
||||
msg = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
if msg.text == "hello":
|
||||
assert msg.chat.get_basic_snapshot().is_protected
|
||||
break
|
||||
|
||||
logging.info("ac3: create a join-code for group VG and let ac4 join, check that ac2 got it")
|
||||
qr_code, _svg = vg.get_qr_code()
|
||||
ac4.secure_join(qr_code)
|
||||
ac3.wait_for_securejoin_inviter_success()
|
||||
while 1:
|
||||
ev = ac2.wait_for_event()
|
||||
if "added by unrelated SecureJoin" in str(ev):
|
||||
return
|
||||
|
||||
|
||||
def test_qr_new_group_unblocked(acfactory):
|
||||
"""Regression test for a bug introduced in core v1.113.0.
|
||||
ac2 scans a verified group QR code created by ac1.
|
||||
This results in creation of a blocked 1:1 chat with ac1 on ac2,
|
||||
but ac1 contact is not blocked on ac2.
|
||||
Then ac1 creates a group, adds ac2 there and promotes it by sending a message.
|
||||
ac2 should receive a message and create a contact request for the group.
|
||||
Due to a bug previously ac2 created a blocked group.
|
||||
"""
|
||||
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac1_chat = ac1.create_group("Group for joining", protect=True)
|
||||
qr_code, _svg = ac1_chat.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
|
||||
ac1.wait_for_securejoin_inviter_success()
|
||||
|
||||
ac1_new_chat = ac1.create_group("Another group")
|
||||
ac1_new_chat.add_contact(ac1.get_contact_by_addr(ac2.get_config("addr")))
|
||||
# Receive "Member added" message.
|
||||
ac2.wait_for_incoming_msg_event()
|
||||
|
||||
ac1_new_chat.send_text("Hello!")
|
||||
ac2_msg = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert ac2_msg.text == "Hello!"
|
||||
assert ac2_msg.chat.get_basic_snapshot().is_contact_request
|
||||
|
||||
|
||||
def test_aeap_flow_verified(acfactory):
|
||||
"""Test that a new address is added to a contact when it changes its address."""
|
||||
ac1, ac2, ac1new = acfactory.get_online_accounts(3)
|
||||
|
||||
logging.info("ac1: create verified-group QR, ac2 scans and joins")
|
||||
chat = ac1.create_group("hello", protect=True)
|
||||
assert chat.get_basic_snapshot().is_protected
|
||||
qr_code, _svg = chat.get_qr_code()
|
||||
logging.info("ac2: start QR-code based join-group protocol")
|
||||
ac2.secure_join(qr_code)
|
||||
ac1.wait_for_securejoin_inviter_success()
|
||||
|
||||
logging.info("sending first message")
|
||||
msg_out = chat.send_text("old address").get_snapshot()
|
||||
|
||||
logging.info("receiving first message")
|
||||
ac2.wait_for_incoming_msg_event() # member added message
|
||||
msg_in_1 = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert msg_in_1.text == msg_out.text
|
||||
|
||||
logging.info("changing email account")
|
||||
ac1.set_config("addr", ac1new.get_config("addr"))
|
||||
ac1.set_config("mail_pw", ac1new.get_config("mail_pw"))
|
||||
ac1.stop_io()
|
||||
ac1.configure()
|
||||
ac1.start_io()
|
||||
|
||||
logging.info("sending second message")
|
||||
msg_out = chat.send_text("changed address").get_snapshot()
|
||||
|
||||
logging.info("receiving second message")
|
||||
msg_in_2 = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id)
|
||||
msg_in_2_snapshot = msg_in_2.get_snapshot()
|
||||
assert msg_in_2_snapshot.text == msg_out.text
|
||||
assert msg_in_2_snapshot.chat.id == msg_in_1.chat.id
|
||||
assert msg_in_2.get_sender_contact().get_snapshot().address == ac1new.get_config("addr")
|
||||
assert len(msg_in_2_snapshot.chat.get_contacts()) == 2
|
||||
assert ac1new.get_config("addr") in [
|
||||
contact.get_snapshot().address for contact in msg_in_2_snapshot.chat.get_contacts()
|
||||
]
|
||||
|
||||
|
||||
def test_gossip_verification(acfactory) -> None:
|
||||
alice, bob, carol = acfactory.get_online_accounts(3)
|
||||
|
||||
# Bob verifies Alice.
|
||||
qr_code, _svg = alice.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
# Bob verifies Carol.
|
||||
qr_code, _svg = carol.get_qr_code()
|
||||
bob.secure_join(qr_code)
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
bob_contact_alice = bob.create_contact(alice.get_config("addr"), "Alice")
|
||||
bob_contact_carol = bob.create_contact(carol.get_config("addr"), "Carol")
|
||||
carol_contact_alice = carol.create_contact(alice.get_config("addr"), "Alice")
|
||||
|
||||
logging.info("Bob creates an Autocrypt group")
|
||||
bob_group_chat = bob.create_group("Autocrypt Group")
|
||||
assert not bob_group_chat.get_basic_snapshot().is_protected
|
||||
bob_group_chat.add_contact(bob_contact_alice)
|
||||
bob_group_chat.add_contact(bob_contact_carol)
|
||||
bob_group_chat.send_message(text="Hello Autocrypt group")
|
||||
|
||||
snapshot = carol.get_message_by_id(carol.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello Autocrypt group"
|
||||
assert snapshot.show_padlock
|
||||
|
||||
# Autocrypt group does not propagate verification.
|
||||
carol_contact_alice_snapshot = carol_contact_alice.get_snapshot()
|
||||
assert not carol_contact_alice_snapshot.is_verified
|
||||
|
||||
logging.info("Bob creates a Securejoin group")
|
||||
bob_group_chat = bob.create_group("Securejoin Group", protect=True)
|
||||
assert bob_group_chat.get_basic_snapshot().is_protected
|
||||
bob_group_chat.add_contact(bob_contact_alice)
|
||||
bob_group_chat.add_contact(bob_contact_carol)
|
||||
bob_group_chat.send_message(text="Hello Securejoin group")
|
||||
|
||||
snapshot = carol.get_message_by_id(carol.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello Securejoin group"
|
||||
assert snapshot.show_padlock
|
||||
|
||||
# Securejoin propagates verification.
|
||||
carol_contact_alice_snapshot = carol_contact_alice.get_snapshot()
|
||||
assert carol_contact_alice_snapshot.is_verified
|
||||
|
||||
|
||||
def test_securejoin_after_contact_resetup(acfactory) -> None:
|
||||
"""
|
||||
Regression test for a bug that prevented joining verified group with a QR code
|
||||
if the group is already created and contains
|
||||
a contact with inconsistent (Autocrypt and verified keys exist but don't match) key state.
|
||||
"""
|
||||
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
|
||||
|
||||
# ac3 creates protected group with ac1.
|
||||
ac3_chat = ac3.create_group("Verified group", protect=True)
|
||||
|
||||
# ac1 joins ac3 group.
|
||||
ac3_qr_code, _svg = ac3_chat.get_qr_code()
|
||||
ac1.secure_join(ac3_qr_code)
|
||||
ac1.wait_for_securejoin_joiner_success()
|
||||
|
||||
# ac1 waits for member added message and creates a QR code.
|
||||
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
ac1_qr_code, _svg = snapshot.chat.get_qr_code()
|
||||
|
||||
# ac2 verifies ac1
|
||||
qr_code, _svg = ac1.get_qr_code()
|
||||
ac2.secure_join(qr_code)
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
# ac1 is verified for ac2.
|
||||
ac2_contact_ac1 = ac2.create_contact(ac1.get_config("addr"), "")
|
||||
assert ac2_contact_ac1.get_snapshot().is_verified
|
||||
|
||||
# ac1 resetups the account.
|
||||
ac1 = acfactory.resetup_account(ac1)
|
||||
|
||||
# ac1 sends a message to ac2.
|
||||
ac1_contact_ac2 = ac1.create_contact(ac2.get_config("addr"), "")
|
||||
ac1_chat_ac2 = ac1_contact_ac2.create_chat()
|
||||
ac1_chat_ac2.send_text("Hello!")
|
||||
|
||||
# ac2 receives a message.
|
||||
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello!"
|
||||
|
||||
# ac1 is no longer verified for ac2 as new Autocrypt key is not the same as old verified key.
|
||||
assert not ac2_contact_ac1.get_snapshot().is_verified
|
||||
|
||||
# ac1 goes offline.
|
||||
ac1.remove()
|
||||
|
||||
# Scanning a QR code results in creating an unprotected group with an inviter.
|
||||
# In this case inviter is ac1 which has an inconsistent key state.
|
||||
# Normally inviter becomes verified as a result of Securejoin protocol
|
||||
# and then the group chat becomes verified when "Member added" is received,
|
||||
# but in this case ac1 is offline and this Securejoin process will never finish.
|
||||
logging.info("ac2 scans ac1 QR code, this is not expected to finish")
|
||||
ac2.secure_join(ac1_qr_code)
|
||||
|
||||
logging.info("ac2 scans ac3 QR code")
|
||||
ac2.secure_join(ac3_qr_code)
|
||||
|
||||
logging.info("ac2 waits for joiner success")
|
||||
ac2.wait_for_securejoin_joiner_success()
|
||||
|
||||
# Wait for member added.
|
||||
logging.info("ac2 waits for member added message")
|
||||
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.is_info
|
||||
ac2_chat = snapshot.chat
|
||||
assert ac2_chat.get_basic_snapshot().is_protected
|
||||
assert len(ac2_chat.get_contacts()) == 3
|
||||
|
||||
# ac1 is still "not verified" for ac2 due to inconsistent state.
|
||||
assert not ac2_contact_ac1.get_snapshot().is_verified
|
||||
|
||||
|
||||
def test_withdraw_securejoin_qr(acfactory):
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
logging.info("Alice creates a verified group")
|
||||
alice_chat = alice.create_group("Verified group", protect=True)
|
||||
assert alice_chat.get_basic_snapshot().is_protected
|
||||
logging.info("Bob joins verified group")
|
||||
|
||||
qr_code, _svg = alice_chat.get_qr_code()
|
||||
bob_chat = bob.secure_join(qr_code)
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
|
||||
assert snapshot.chat.get_basic_snapshot().is_protected
|
||||
bob_chat.leave()
|
||||
|
||||
snapshot = alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Group left by {}.".format(bob.get_config("addr"))
|
||||
|
||||
logging.info("Alice withdraws QR code.")
|
||||
qr = alice.check_qr(qr_code)
|
||||
assert qr["kind"] == "withdrawVerifyGroup"
|
||||
alice.set_config_from_qr(qr_code)
|
||||
|
||||
logging.info("Bob scans withdrawn QR code.")
|
||||
bob_chat = bob.secure_join(qr_code)
|
||||
|
||||
logging.info("Bob scanned withdrawn QR code")
|
||||
while True:
|
||||
event = alice.wait_for_event()
|
||||
if event.kind == EventType.MSGS_CHANGED and event.chat_id != 0:
|
||||
break
|
||||
snapshot = alice.get_message_by_id(event.msg_id).get_snapshot()
|
||||
assert snapshot.text == "Cannot establish guaranteed end-to-end encryption with {}".format(bob.get_config("addr"))
|
||||
@@ -1,4 +1,6 @@
|
||||
import concurrent.futures
|
||||
import json
|
||||
import subprocess
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
@@ -42,7 +44,7 @@ def test_acfactory(acfactory) -> None:
|
||||
account = acfactory.new_configured_account()
|
||||
while True:
|
||||
event = account.wait_for_event()
|
||||
if event.type == EventType.CONFIGURE_PROGRESS:
|
||||
if event.kind == EventType.CONFIGURE_PROGRESS:
|
||||
assert event.progress != 0 # Progress 0 indicates error.
|
||||
if event.progress == 1000: # Success
|
||||
break
|
||||
@@ -71,7 +73,7 @@ def test_account(acfactory) -> None:
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
if event.kind == EventType.INCOMING_MSG:
|
||||
chat_id = event.chat_id
|
||||
msg_id = event.msg_id
|
||||
break
|
||||
@@ -138,12 +140,9 @@ def test_chat(acfactory) -> None:
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
alice_chat_bob.send_text("Hello!")
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
chat_id = event.chat_id
|
||||
msg_id = event.msg_id
|
||||
break
|
||||
event = bob.wait_for_incoming_msg_event()
|
||||
chat_id = event.chat_id
|
||||
msg_id = event.msg_id
|
||||
message = bob.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert snapshot.chat_id == chat_id
|
||||
@@ -222,12 +221,9 @@ def test_message(acfactory) -> None:
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
alice_chat_bob.send_text("Hello!")
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
chat_id = event.chat_id
|
||||
msg_id = event.msg_id
|
||||
break
|
||||
event = bob.wait_for_incoming_msg_event()
|
||||
chat_id = event.chat_id
|
||||
msg_id = event.msg_id
|
||||
|
||||
message = bob.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
@@ -263,7 +259,7 @@ def test_is_bot(acfactory) -> None:
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
if event.kind == EventType.INCOMING_MSG:
|
||||
msg_id = event.msg_id
|
||||
message = bob.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
@@ -329,7 +325,7 @@ def test_wait_next_messages(acfactory) -> None:
|
||||
next_messages_task = executor.submit(bot.wait_next_messages)
|
||||
|
||||
bot_addr = bot.get_config("addr")
|
||||
alice_contact_bot = alice.create_contact(bot_addr, "Bob")
|
||||
alice_contact_bot = alice.create_contact(bot_addr, "Bot")
|
||||
alice_chat_bot = alice_contact_bot.create_chat()
|
||||
alice_chat_bot.send_text("Hello!")
|
||||
|
||||
@@ -339,10 +335,107 @@ def test_wait_next_messages(acfactory) -> None:
|
||||
assert snapshot.text == "Hello!"
|
||||
|
||||
|
||||
def test_import_export(acfactory, tmp_path) -> None:
|
||||
def test_import_export_backup(acfactory, tmp_path) -> None:
|
||||
alice = acfactory.new_configured_account()
|
||||
alice.export_backup(tmp_path)
|
||||
|
||||
files = list(tmp_path.glob("*.tar"))
|
||||
alice2 = acfactory.get_unconfigured_account()
|
||||
alice2.import_backup(files[0])
|
||||
|
||||
assert alice2.manager.get_system_info()
|
||||
|
||||
|
||||
def test_import_export_keys(acfactory, tmp_path) -> None:
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
bob_addr = bob.get_config("addr")
|
||||
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
alice_chat_bob.send_text("Hello Bob!")
|
||||
|
||||
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello Bob!"
|
||||
|
||||
# Alice resetups account, but keeps the key.
|
||||
alice_keys_path = tmp_path / "alice_keys"
|
||||
alice_keys_path.mkdir()
|
||||
alice.export_self_keys(alice_keys_path)
|
||||
alice = acfactory.resetup_account(alice)
|
||||
alice.import_self_keys(alice_keys_path)
|
||||
|
||||
snapshot.chat.accept()
|
||||
snapshot.chat.send_text("Hello Alice!")
|
||||
snapshot = alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot()
|
||||
assert snapshot.text == "Hello Alice!"
|
||||
assert snapshot.show_padlock
|
||||
|
||||
|
||||
def test_openrpc_command_line() -> None:
|
||||
"""Test that "deltachat-rpc-server --openrpc" command returns an OpenRPC specification."""
|
||||
out = subprocess.run(["deltachat-rpc-server", "--openrpc"], capture_output=True, check=True).stdout
|
||||
openrpc = json.loads(out)
|
||||
assert "openrpc" in openrpc
|
||||
assert "methods" in openrpc
|
||||
|
||||
|
||||
def test_provider_info(rpc) -> None:
|
||||
account_id = rpc.add_account()
|
||||
|
||||
provider_info = rpc.get_provider_info(account_id, "example.org")
|
||||
assert provider_info["id"] == "example.com"
|
||||
|
||||
provider_info = rpc.get_provider_info(account_id, "uep7oiw4ahtaizuloith.org")
|
||||
assert provider_info is None
|
||||
|
||||
# Test MX record resolution.
|
||||
provider_info = rpc.get_provider_info(account_id, "github.com")
|
||||
assert provider_info["id"] == "gmail"
|
||||
|
||||
# Disable MX record resolution.
|
||||
rpc.set_config(account_id, "socks5_enabled", "1")
|
||||
provider_info = rpc.get_provider_info(account_id, "github.com")
|
||||
assert provider_info is None
|
||||
|
||||
|
||||
def test_mdn_doesnt_break_autocrypt(acfactory) -> None:
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
bob_addr = bob.get_config("addr")
|
||||
|
||||
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
|
||||
|
||||
# Bob creates chat manually so chat with Alice is accepted.
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
|
||||
# Alice sends a message to Bob.
|
||||
alice_chat_bob.send_text("Hello Bob!")
|
||||
event = bob.wait_for_incoming_msg_event()
|
||||
msg_id = event.msg_id
|
||||
message = bob.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
|
||||
# Bob sends a message to Alice.
|
||||
bob_chat_alice = snapshot.chat
|
||||
bob_chat_alice.accept()
|
||||
bob_chat_alice.send_text("Hello Alice!")
|
||||
event = alice.wait_for_incoming_msg_event()
|
||||
msg_id = event.msg_id
|
||||
message = alice.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert snapshot.show_padlock
|
||||
|
||||
# Alice reads Bob's message.
|
||||
message.mark_seen()
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.kind == EventType.MSG_READ:
|
||||
break
|
||||
|
||||
# Bob sends a message to Alice, it should also be encrypted.
|
||||
bob_chat_alice.send_text("Hi Alice!")
|
||||
event = alice.wait_for_incoming_msg_event()
|
||||
msg_id = event.msg_id
|
||||
message = alice.get_message_by_id(msg_id)
|
||||
snapshot = message.get_snapshot()
|
||||
assert snapshot.show_padlock
|
||||
|
||||
@@ -11,7 +11,7 @@ def test_webxdc(acfactory) -> None:
|
||||
|
||||
while True:
|
||||
event = bob.wait_for_event()
|
||||
if event.type == EventType.INCOMING_MSG:
|
||||
if event.kind == EventType.INCOMING_MSG:
|
||||
bob_chat_alice = bob.get_chat_by_id(event.chat_id)
|
||||
message = bob.get_message_by_id(event.msg_id)
|
||||
break
|
||||
@@ -43,3 +43,15 @@ def test_webxdc(acfactory) -> None:
|
||||
assert status_updates == [
|
||||
{"payload": "Second update", "serial": 2, "max_serial": 2},
|
||||
]
|
||||
|
||||
|
||||
def test_webxdc_insert_lots_of_updates(acfactory) -> None:
|
||||
alice, bob = acfactory.get_online_accounts(2)
|
||||
|
||||
bob_addr = bob.get_config("addr")
|
||||
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
|
||||
alice_chat_bob = alice_contact_bob.create_chat()
|
||||
message = alice_chat_bob.send_message(text="Let's play chess!", file="../test-data/webxdc/chess.xdc")
|
||||
|
||||
for i in range(2000):
|
||||
message.send_webxdc_status_update({"payload": str(i)}, "description")
|
||||
|
||||
@@ -11,7 +11,7 @@ setenv =
|
||||
# Avoid stack overflow when Rust core is built without optimizations.
|
||||
RUST_MIN_STACK=8388608
|
||||
passenv =
|
||||
DCC_NEW_TMP_EMAIL
|
||||
CHATMAIL_DOMAIN
|
||||
deps =
|
||||
pytest
|
||||
pytest-timeout
|
||||
@@ -28,4 +28,6 @@ commands =
|
||||
ruff src/ examples/ tests/
|
||||
|
||||
[pytest]
|
||||
timeout = 60
|
||||
timeout = 300
|
||||
log_cli = true
|
||||
log_level = debug
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.125.0"
|
||||
version = "1.137.2"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
@@ -15,13 +15,13 @@ deltachat = { path = "..", default-features = false }
|
||||
|
||||
anyhow = "1"
|
||||
env_logger = { version = "0.10.0" }
|
||||
futures-lite = "1.13.0"
|
||||
futures-lite = "2.3.0"
|
||||
log = "0.4"
|
||||
serde_json = "1.0.99"
|
||||
serde_json = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.29.1", features = ["io-std"] }
|
||||
tokio-util = "0.7.8"
|
||||
yerpc = { version = "0.5.1", features = ["anyhow_expose"] }
|
||||
tokio = { version = "1.37.0", features = ["io-std"] }
|
||||
tokio-util = "0.7.9"
|
||||
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
|
||||
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
|
||||
@@ -30,5 +30,8 @@ deltachat-rpc-server
|
||||
The common use case for this program is to create bindings to use Delta Chat core from programming
|
||||
languages other than Rust, for example:
|
||||
|
||||
1. Python: https://github.com/deltachat/deltachat-core-rust/tree/master/deltachat-rpc-client/
|
||||
1. Python: https://pypi.org/project/deltachat-rpc-client/
|
||||
2. Go: https://github.com/deltachat/deltachat-rpc-client-go/
|
||||
|
||||
Run `deltachat-rpc-server --version` to check the version of the server.
|
||||
Run `deltachat-rpc-server --openrpc` to get [OpenRPC](https://open-rpc.org/) specification of the provided JSON-RPC API.
|
||||
|
||||
@@ -10,6 +10,7 @@ use deltachat::constants::DC_VERSION_STR;
|
||||
use deltachat_jsonrpc::api::{Accounts, CommandApi};
|
||||
use futures_lite::stream::StreamExt;
|
||||
use tokio::io::{self, AsyncBufReadExt, BufReader};
|
||||
use yerpc::RpcServer as _;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
use tokio::signal::unix as signal_unix;
|
||||
@@ -39,6 +40,12 @@ async fn main_impl() -> Result<()> {
|
||||
}
|
||||
eprintln!("{}", &*DC_VERSION_STR);
|
||||
return Ok(());
|
||||
} else if first_arg.to_str() == Some("--openrpc") {
|
||||
if let Some(arg) = args.next() {
|
||||
return Err(anyhow!("Unrecognized argument {:?}", arg));
|
||||
}
|
||||
println!("{}", CommandApi::openrpc_specification()?);
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(anyhow!("Unrecognized option {:?}", first_arg));
|
||||
}
|
||||
@@ -56,7 +63,8 @@ async fn main_impl() -> Result<()> {
|
||||
|
||||
let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "accounts".to_string());
|
||||
log::info!("Starting with accounts directory `{}`.", path);
|
||||
let accounts = Accounts::new(PathBuf::from(&path)).await?;
|
||||
let writable = true;
|
||||
let accounts = Accounts::new(PathBuf::from(&path), writable).await?;
|
||||
|
||||
log::info!("Creating JSON-RPC API.");
|
||||
let accounts = Arc::new(RwLock::new(accounts));
|
||||
|
||||
8
deltachat-time/Cargo.toml
Normal file
8
deltachat-time/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "deltachat-time"
|
||||
version = "1.0.0"
|
||||
description = "Time-related tools"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
35
deltachat-time/src/lib.rs
Normal file
35
deltachat-time/src/lib.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::sync::RwLock;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
static SYSTEM_TIME_SHIFT: RwLock<Duration> = RwLock::new(Duration::new(0, 0));
|
||||
|
||||
/// Fake struct for mocking `SystemTime::now()` for test purposes. You still need to use
|
||||
/// `SystemTime` as a struct representing a system time.
|
||||
pub struct SystemTimeTools();
|
||||
|
||||
impl SystemTimeTools {
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH;
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
return SystemTime::now() + *SYSTEM_TIME_SHIFT.read().unwrap();
|
||||
}
|
||||
|
||||
/// Simulates a system clock forward adjustment by `duration`.
|
||||
pub fn shift(duration: Duration) {
|
||||
*SYSTEM_TIME_SHIFT.write().unwrap() += duration;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
SystemTimeTools::shift(Duration::from_secs(60));
|
||||
let t = SystemTimeTools::now();
|
||||
assert!(t > SystemTime::now());
|
||||
}
|
||||
}
|
||||
53
deny.toml
53
deny.toml
@@ -1,8 +1,20 @@
|
||||
[advisories]
|
||||
unmaintained = "allow"
|
||||
ignore = [
|
||||
"RUSTSEC-2020-0071",
|
||||
"RUSTSEC-2022-0093",
|
||||
|
||||
# Timing attack on RSA.
|
||||
# Delta Chat does not use RSA for new keys
|
||||
# and this requires precise measurement of the decryption time by the attacker.
|
||||
# There is no fix at the time of writing this (2023-11-28).
|
||||
# <https://rustsec.org/advisories/RUSTSEC-2023-0071>
|
||||
"RUSTSEC-2023-0071",
|
||||
|
||||
# Unmaintained ansi_term
|
||||
"RUSTSEC-2021-0139",
|
||||
|
||||
# Unmaintained encoding
|
||||
"RUSTSEC-2021-0153",
|
||||
]
|
||||
|
||||
[bans]
|
||||
@@ -11,7 +23,7 @@ ignore = [
|
||||
# when upgrading.
|
||||
# Please keep this list alphabetically sorted.
|
||||
skip = [
|
||||
{ name = "ahash", version = "0.7.6" },
|
||||
{ name = "async-channel", version = "1.9.0" },
|
||||
{ name = "base16ct", version = "0.1.1" },
|
||||
{ name = "base64", version = "<0.21" },
|
||||
{ name = "bitflags", version = "1.3.2" },
|
||||
@@ -25,41 +37,41 @@ skip = [
|
||||
{ name = "digest", version = "<0.10" },
|
||||
{ name = "ed25519-dalek", version = "1.0.1" },
|
||||
{ name = "ed25519", version = "1.5.3" },
|
||||
{ name = "event-listener", version = "2.5.3" },
|
||||
{ name = "getrandom", version = "<0.2" },
|
||||
{ name = "hashbrown", version = "<0.14.0" },
|
||||
{ name = "idna", version = "<0.3" },
|
||||
{ name = "indexmap", version = "<2.0.0" },
|
||||
{ name = "linux-raw-sys", version = "0.3.8" },
|
||||
{ name = "num-derive", version = "0.3.3" },
|
||||
{ name = "idna", version = "0.4.0" },
|
||||
{ name = "pem-rfc7468", version = "0.6.0" },
|
||||
{ name = "pkcs8", version = "0.9.0" },
|
||||
{ name = "quick-error", version = "<2.0" },
|
||||
{ name = "rand_chacha", version = "<0.3" },
|
||||
{ name = "rand_core", version = "<0.6" },
|
||||
{ name = "rand", version = "<0.8" },
|
||||
{ name = "redox_syscall", version = "0.2.16" },
|
||||
{ name = "redox_syscall", version = "0.3.5" },
|
||||
{ name = "regex-automata", version = "0.1.10" },
|
||||
{ name = "regex-syntax", version = "0.6.29" },
|
||||
{ name = "rustix", version = "0.37.21" },
|
||||
{ name = "ring", version = "0.16.20" },
|
||||
{ name = "sec1", version = "0.3.0" },
|
||||
{ name = "sha2", version = "<0.10" },
|
||||
{ name = "signature", version = "1.6.4" },
|
||||
{ name = "socket2", version = "0.4.9" },
|
||||
{ name = "spin", version = "<0.9.6" },
|
||||
{ name = "spki", version = "0.6.0" },
|
||||
{ name = "sync_wrapper", version = "0.1.2" },
|
||||
{ name = "syn", version = "1.0.109" },
|
||||
{ name = "time", version = "<0.3" },
|
||||
{ name = "toml_edit", version = "0.21.1" },
|
||||
{ name = "untrusted", version = "0.7.1" },
|
||||
{ name = "wasi", version = "<0.11" },
|
||||
{ name = "windows_aarch64_gnullvm", version = "<0.48" },
|
||||
{ name = "windows_aarch64_msvc", version = "<0.48" },
|
||||
{ name = "windows_i686_gnu", version = "<0.48" },
|
||||
{ name = "windows_i686_msvc", version = "<0.48" },
|
||||
{ name = "windows-sys", version = "<0.48" },
|
||||
{ name = "windows-targets", version = "<0.48" },
|
||||
{ name = "windows_x86_64_gnullvm", version = "<0.48" },
|
||||
{ name = "windows_aarch64_gnullvm", version = "<0.52" },
|
||||
{ name = "windows_aarch64_msvc", version = "<0.52" },
|
||||
{ name = "windows_i686_gnu", version = "<0.52" },
|
||||
{ name = "windows_i686_msvc", version = "<0.52" },
|
||||
{ name = "windows-sys", version = "<0.52" },
|
||||
{ name = "windows-targets", version = "<0.52" },
|
||||
{ name = "windows", version = "0.32.0" },
|
||||
{ name = "windows_x86_64_gnu", version = "<0.48" },
|
||||
{ name = "windows_x86_64_msvc", version = "<0.48" },
|
||||
{ name = "winreg", version = "0.10.1" },
|
||||
{ name = "windows_x86_64_gnullvm", version = "<0.52" },
|
||||
{ name = "windows_x86_64_gnu", version = "<0.52" },
|
||||
{ name = "windows_x86_64_msvc", version = "<0.52" },
|
||||
{ name = "winnow", version = "0.5.40" },
|
||||
]
|
||||
|
||||
|
||||
@@ -91,5 +103,4 @@ license-files = [
|
||||
github = [
|
||||
"async-email",
|
||||
"deltachat",
|
||||
"quinn-rs",
|
||||
]
|
||||
|
||||
@@ -57,7 +57,7 @@ Note that usually a mail is signed by a key that has a UID matching the from add
|
||||
### Notes:
|
||||
|
||||
- We treat protected and non-protected chats the same
|
||||
- We leave the aeap transition statement away since it seems not to be needed, makes things harder on the sending side, wastes some network traffic, and is worse for privacy (since more pepole know what old addresses you had).
|
||||
- We leave the aeap transition statement away since it seems not to be needed, makes things harder on the sending side, wastes some network traffic, and is worse for privacy (since more people know what old addresses you had).
|
||||
- As soon as we encrypt read receipts, sending a read receipt will be enough to tell a lot of people that you transitioned
|
||||
- AEAP will make the problem of inconsistent group state worse, both because it doesn't work if the message is unencrypted (even if the design allowed it, it would be problematic security-wise) and because some chat partners may have gotten the transition and some not. We should do something against this at some point in the future, like asking the user whether they want to add/remove the members to restore consistent group state.
|
||||
|
||||
@@ -108,7 +108,7 @@ The most obvious alternative would be to create a new contact with the new addre
|
||||
#### Upsides:
|
||||
- With this approach, it's easier to switch to a model where the info about the transition is encoded in the PGP key. Since the key is gossiped, the information about the transition will spread virally.
|
||||
- (Also, less important: Slightly faster transition: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.)
|
||||
- It's easier to implement (if too many problems turn up, we can still switch to another approach and didn't wast that much development time.)
|
||||
- It's easier to implement (if too many problems turn up, we can still switch to another approach and didn't waste that much development time.)
|
||||
|
||||
[full messages](https://github.com/deltachat/deltachat-core-rust/pull/2896#discussion_r852002161)
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
use deltachat::chat::{self, ChatId};
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::message::Message;
|
||||
use deltachat::stock_str::StockStrings;
|
||||
use deltachat::{EventType, Events};
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn cb(event: EventType) {
|
||||
match event {
|
||||
EventType::ConfigureProgress { progress, .. } => {
|
||||
log::info!("progress: {}", progress);
|
||||
}
|
||||
EventType::Info(msg) => {
|
||||
log::info!("{}", msg);
|
||||
}
|
||||
EventType::Warning(msg) => {
|
||||
log::warn!("{}", msg);
|
||||
}
|
||||
EventType::Error(msg) => {
|
||||
log::error!("{}", msg);
|
||||
}
|
||||
event => {
|
||||
log::info!("{:?}", event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run with `RUST_LOG=simple=info cargo run --release --example simple -- email pw`.
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
pretty_env_logger::try_init_timed().ok();
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
log::info!("creating database {:?}", dbfile);
|
||||
let ctx = Context::new(&dbfile, 0, Events::new(), StockStrings::new())
|
||||
.await
|
||||
.expect("Failed to create context");
|
||||
let info = ctx.get_info().await;
|
||||
log::info!("info: {:#?}", info);
|
||||
|
||||
let events = ctx.get_event_emitter();
|
||||
let events_spawn = tokio::task::spawn(async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
cb(event.typ);
|
||||
}
|
||||
});
|
||||
|
||||
log::info!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 3, "requires email password");
|
||||
let email = args[1].clone();
|
||||
let pw = args[2].clone();
|
||||
ctx.set_config(config::Config::Addr, Some(&email))
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
ctx.configure().await.unwrap();
|
||||
|
||||
log::info!("------ RUN ------");
|
||||
ctx.start_io().await;
|
||||
log::info!("--- SENDING A MESSAGE ---");
|
||||
|
||||
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com")
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = ChatId::create_for_contact(&ctx, contact_id).await.unwrap();
|
||||
|
||||
for i in 0..1 {
|
||||
log::info!("sending message {}", i);
|
||||
chat::send_text_msg(&ctx, chat_id, format!("Hi, here is my {i}nth message!"))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// wait for the message to be sent out
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
log::info!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let msg = Message::load_from_db(&ctx, chats.get_msg_id(i).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
log::info!("[{}] msg: {:?}", i, msg);
|
||||
}
|
||||
|
||||
log::info!("stopping");
|
||||
ctx.stop_io().await;
|
||||
log::info!("closing");
|
||||
drop(ctx);
|
||||
events_spawn.await.unwrap();
|
||||
}
|
||||
288
flake.lock
generated
Normal file
288
flake.lock
generated
Normal file
@@ -0,0 +1,288 @@
|
||||
{
|
||||
"nodes": {
|
||||
"android": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710633978,
|
||||
"narHash": "sha256-yemnwSvW7cdWtXGpivFA5jDO35rGPs6fqxlQ4l6ODXs=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"rev": "e91fb3d8517538e5ad9b422c9a4f604b56008a9e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"android",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708939976,
|
||||
"narHash": "sha256-O5+nFozxz2Vubpdl1YZtPrilcIXPcRAjqNdNE8oCRoA=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "5ddecd67edbd568ebe0a55905273e56cc82aabe3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710742993,
|
||||
"narHash": "sha256-W0PQCe0bW3hKF5lHawXrKynBcdSP18Qa4sb8DcUfOqI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709126324,
|
||||
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698420672,
|
||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1710156097,
|
||||
"narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "3342559a24e85fc164b295c3444e8a139924675b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709237383,
|
||||
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1710631334,
|
||||
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1710765496,
|
||||
"narHash": "sha256-p7ryWEeQfMwTB6E0wIUd5V2cFTgq+DRRBz2hYGnJZyA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e367f7a1fb93137af22a3908f00b9a35e2d286a7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1710631334,
|
||||
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"android": "android",
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"naersk": "naersk",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1710708100,
|
||||
"narHash": "sha256-Jd6pmXlwKk5uYcjyO/8BfbUVmx8g31Qfk7auI2IG66A=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "b6d1887bc4f9543b6c6bf098179a62446f34a6c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
542
flake.nix
Normal file
542
flake.nix
Normal file
@@ -0,0 +1,542 @@
|
||||
{
|
||||
description = "Delta Chat core";
|
||||
inputs = {
|
||||
fenix.url = "github:nix-community/fenix";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
android.url = "github:tadfisher/android-nixpkgs";
|
||||
};
|
||||
outputs = { self, nixpkgs, flake-utils, nix-filter, naersk, fenix, android }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
inherit (pkgs.stdenv) isDarwin;
|
||||
fenixPkgs = fenix.packages.${system};
|
||||
naersk' = pkgs.callPackage naersk { };
|
||||
manifest = (pkgs.lib.importTOML ./Cargo.toml).package;
|
||||
androidSdk = android.sdk.${system} (sdkPkgs:
|
||||
builtins.attrValues {
|
||||
inherit (sdkPkgs) ndk-24-0-8215888 cmdline-tools-latest;
|
||||
});
|
||||
androidNdkRoot = "${androidSdk}/share/android-sdk/ndk/24.0.8215888";
|
||||
|
||||
rustSrc = nix-filter.lib {
|
||||
root = ./.;
|
||||
|
||||
# Include only necessary files
|
||||
# to avoid rebuilds e.g. when README.md or flake.nix changes.
|
||||
include = [
|
||||
./benches
|
||||
./assets
|
||||
./Cargo.lock
|
||||
./Cargo.toml
|
||||
./CMakeLists.txt
|
||||
./CONTRIBUTING.md
|
||||
./deltachat_derive
|
||||
./deltachat-ffi
|
||||
./deltachat-jsonrpc
|
||||
./deltachat-ratelimit
|
||||
./deltachat-repl
|
||||
./deltachat-rpc-client
|
||||
./deltachat-time
|
||||
./deltachat-rpc-server
|
||||
./format-flowed
|
||||
./release-date.in
|
||||
./src
|
||||
];
|
||||
exclude = [
|
||||
(nix-filter.lib.matchExt "nix")
|
||||
"flake.lock"
|
||||
];
|
||||
};
|
||||
|
||||
# Map from architecture name to rust targets and nixpkgs targets.
|
||||
arch2targets = {
|
||||
"x86_64-linux" = {
|
||||
rustTarget = "x86_64-unknown-linux-musl";
|
||||
crossTarget = "x86_64-unknown-linux-musl";
|
||||
};
|
||||
"armv7l-linux" = {
|
||||
rustTarget = "armv7-unknown-linux-musleabihf";
|
||||
crossTarget = "armv7l-unknown-linux-musleabihf";
|
||||
};
|
||||
"armv6l-linux" = {
|
||||
rustTarget = "arm-unknown-linux-musleabihf";
|
||||
crossTarget = "armv6l-unknown-linux-musleabihf";
|
||||
};
|
||||
"aarch64-linux" = {
|
||||
rustTarget = "aarch64-unknown-linux-musl";
|
||||
crossTarget = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
"i686-linux" = {
|
||||
rustTarget = "i686-unknown-linux-musl";
|
||||
crossTarget = "i686-unknown-linux-musl";
|
||||
};
|
||||
|
||||
"x86_64-darwin" = {
|
||||
rustTarget = "x86_64-apple-darwin";
|
||||
crossTarget = "x86_64-darwin";
|
||||
};
|
||||
"aarch64-darwin" = {
|
||||
rustTarget = "aarch64-apple-darwin";
|
||||
crossTarget = "aarch64-darwin";
|
||||
};
|
||||
};
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
outputHashes = {
|
||||
"email-0.0.20" = "sha256-rV4Uzqt2Qdrfi5Ti1r+Si1c2iW1kKyWLwOgLkQ5JGGw=";
|
||||
"encoded-words-0.2.0" = "sha256-KK9st0hLFh4dsrnLd6D8lC6pRFFs8W+WpZSGMGJcosk=";
|
||||
"lettre-0.9.2" = "sha256-+hU1cFacyyeC9UGVBpS14BWlJjHy90i/3ynMkKAzclk=";
|
||||
};
|
||||
};
|
||||
mkRustPackage = packageName:
|
||||
naersk'.buildPackage {
|
||||
pname = packageName;
|
||||
cargoBuildOptions = x: x ++ [ "--package" packageName ];
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
];
|
||||
buildInputs = pkgs.lib.optionals isDarwin [
|
||||
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
|
||||
];
|
||||
auditable = false; # Avoid cargo-auditable failures.
|
||||
doCheck = false; # Disable test as it requires network access.
|
||||
};
|
||||
pkgsWin64 = pkgs.pkgsCross.mingwW64;
|
||||
mkWin64RustPackage = packageName:
|
||||
let
|
||||
rustTarget = "x86_64-pc-windows-gnu";
|
||||
toolchainWin = fenixPkgs.combine [
|
||||
fenixPkgs.stable.rustc
|
||||
fenixPkgs.stable.cargo
|
||||
fenixPkgs.targets.${rustTarget}.stable.rust-std
|
||||
];
|
||||
naerskWin = pkgs.callPackage naersk {
|
||||
cargo = toolchainWin;
|
||||
rustc = toolchainWin;
|
||||
};
|
||||
in
|
||||
naerskWin.buildPackage rec {
|
||||
pname = packageName;
|
||||
cargoBuildOptions = x: x ++ [ "--package" packageName ];
|
||||
version = manifest.version;
|
||||
strictDeps = true;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
];
|
||||
depsBuildBuild = [
|
||||
pkgsWin64.stdenv.cc
|
||||
pkgsWin64.windows.pthreads
|
||||
];
|
||||
auditable = false; # Avoid cargo-auditable failures.
|
||||
doCheck = false; # Disable test as it requires network access.
|
||||
|
||||
CARGO_BUILD_TARGET = rustTarget;
|
||||
TARGET_CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
|
||||
CARGO_BUILD_RUSTFLAGS = [
|
||||
"-C"
|
||||
"linker=${TARGET_CC}"
|
||||
];
|
||||
|
||||
CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
|
||||
LD = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
|
||||
};
|
||||
|
||||
pkgsWin32 = pkgs.pkgsCross.mingw32;
|
||||
mkWin32RustPackage = packageName:
|
||||
let
|
||||
rustTarget = "i686-pc-windows-gnu";
|
||||
in
|
||||
let
|
||||
toolchainWin = fenixPkgs.combine [
|
||||
fenixPkgs.stable.rustc
|
||||
fenixPkgs.stable.cargo
|
||||
fenixPkgs.targets.${rustTarget}.stable.rust-std
|
||||
];
|
||||
naerskWin = pkgs.callPackage naersk {
|
||||
cargo = toolchainWin;
|
||||
rustc = toolchainWin;
|
||||
};
|
||||
|
||||
# Get rid of MCF Gthread library.
|
||||
# See <https://github.com/NixOS/nixpkgs/issues/156343>
|
||||
# and <https://discourse.nixos.org/t/statically-linked-mingw-binaries/38395>
|
||||
# for details.
|
||||
#
|
||||
# Use DWARF-2 instead of SJLJ for exception handling.
|
||||
winCC = pkgsWin32.buildPackages.wrapCC (
|
||||
(pkgsWin32.buildPackages.gcc-unwrapped.override
|
||||
({
|
||||
threadsCross = {
|
||||
model = "win32";
|
||||
package = null;
|
||||
};
|
||||
})).overrideAttrs (oldAttr: {
|
||||
configureFlags = oldAttr.configureFlags ++ [
|
||||
"--disable-sjlj-exceptions --with-dwarf2"
|
||||
];
|
||||
})
|
||||
);
|
||||
in
|
||||
naerskWin.buildPackage rec {
|
||||
pname = packageName;
|
||||
cargoBuildOptions = x: x ++ [ "--package" packageName ];
|
||||
version = manifest.version;
|
||||
strictDeps = true;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
];
|
||||
depsBuildBuild = [
|
||||
winCC
|
||||
pkgsWin32.windows.pthreads
|
||||
];
|
||||
auditable = false; # Avoid cargo-auditable failures.
|
||||
doCheck = false; # Disable test as it requires network access.
|
||||
|
||||
CARGO_BUILD_TARGET = rustTarget;
|
||||
TARGET_CC = "${winCC}/bin/${winCC.targetPrefix}cc";
|
||||
CARGO_BUILD_RUSTFLAGS = [
|
||||
"-C"
|
||||
"linker=${TARGET_CC}"
|
||||
];
|
||||
|
||||
CC = "${winCC}/bin/${winCC.targetPrefix}cc";
|
||||
LD = "${winCC}/bin/${winCC.targetPrefix}cc";
|
||||
};
|
||||
|
||||
mkCrossRustPackage = arch: packageName:
|
||||
let
|
||||
rustTarget = arch2targets."${arch}".rustTarget;
|
||||
crossTarget = arch2targets."${arch}".crossTarget;
|
||||
pkgsCross = import nixpkgs {
|
||||
system = system;
|
||||
crossSystem.config = crossTarget;
|
||||
};
|
||||
in
|
||||
let
|
||||
toolchain = fenixPkgs.combine [
|
||||
fenixPkgs.stable.rustc
|
||||
fenixPkgs.stable.cargo
|
||||
fenixPkgs.targets.${rustTarget}.stable.rust-std
|
||||
];
|
||||
naersk-lib = pkgs.callPackage naersk {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
in
|
||||
naersk-lib.buildPackage rec {
|
||||
pname = packageName;
|
||||
cargoBuildOptions = x: x ++ [ "--package" packageName ];
|
||||
version = manifest.version;
|
||||
strictDeps = true;
|
||||
src = rustSrc;
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
];
|
||||
auditable = false; # Avoid cargo-auditable failures.
|
||||
doCheck = false; # Disable test as it requires network access.
|
||||
|
||||
CARGO_BUILD_TARGET = rustTarget;
|
||||
TARGET_CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
|
||||
CARGO_BUILD_RUSTFLAGS = [
|
||||
"-C"
|
||||
"linker=${TARGET_CC}"
|
||||
];
|
||||
|
||||
CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
|
||||
LD = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
|
||||
};
|
||||
|
||||
androidAttrs = {
|
||||
armeabi-v7a = {
|
||||
cc = "armv7a-linux-androideabi19-clang";
|
||||
rustTarget = "armv7-linux-androideabi";
|
||||
};
|
||||
arm64-v8a = {
|
||||
cc = "aarch64-linux-android21-clang";
|
||||
rustTarget = "aarch64-linux-android";
|
||||
};
|
||||
};
|
||||
|
||||
mkAndroidRustPackage = arch: packageName:
|
||||
let
|
||||
rustTarget = androidAttrs.${arch}.rustTarget;
|
||||
toolchain = fenixPkgs.combine [
|
||||
fenixPkgs.stable.rustc
|
||||
fenixPkgs.stable.cargo
|
||||
fenixPkgs.targets.${rustTarget}.stable.rust-std
|
||||
];
|
||||
naersk-lib = pkgs.callPackage naersk {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
targetToolchain = "${androidNdkRoot}/toolchains/llvm/prebuilt/linux-x86_64";
|
||||
targetCcName = androidAttrs.${arch}.cc;
|
||||
targetCc = "${targetToolchain}/bin/${targetCcName}";
|
||||
in
|
||||
naersk-lib.buildPackage rec {
|
||||
pname = packageName;
|
||||
cargoBuildOptions = x: x ++ [ "--package" packageName ];
|
||||
version = manifest.version;
|
||||
strictDeps = true;
|
||||
src = rustSrc;
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
];
|
||||
auditable = false; # Avoid cargo-auditable failures.
|
||||
doCheck = false; # Disable test as it requires network access.
|
||||
|
||||
CARGO_BUILD_TARGET = rustTarget;
|
||||
TARGET_CC = "${targetCc}";
|
||||
CARGO_BUILD_RUSTFLAGS = [
|
||||
"-C"
|
||||
"linker=${TARGET_CC}"
|
||||
];
|
||||
|
||||
CC = "${targetCc}";
|
||||
LD = "${targetCc}";
|
||||
};
|
||||
|
||||
mkAndroidPackages = arch: {
|
||||
"deltachat-rpc-server-${arch}-android" = mkAndroidRustPackage arch "deltachat-rpc-server";
|
||||
"deltachat-repl-${arch}-android" = mkAndroidRustPackage arch "deltachat-repl";
|
||||
};
|
||||
|
||||
mkRustPackages = arch:
|
||||
let
|
||||
rpc-server = mkCrossRustPackage arch "deltachat-rpc-server";
|
||||
in
|
||||
{
|
||||
"deltachat-repl-${arch}" = mkCrossRustPackage arch "deltachat-repl";
|
||||
"deltachat-rpc-server-${arch}" = rpc-server;
|
||||
"deltachat-rpc-server-${arch}-wheel" =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "deltachat-rpc-server-${arch}-wheel";
|
||||
version = manifest.version;
|
||||
src = nix-filter.lib {
|
||||
root = ./.;
|
||||
include = [
|
||||
"scripts/wheel-rpc-server.py"
|
||||
"deltachat-rpc-server/README.md"
|
||||
"LICENSE"
|
||||
"Cargo.toml"
|
||||
];
|
||||
};
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.wheel
|
||||
];
|
||||
buildInputs = [
|
||||
rpc-server
|
||||
];
|
||||
buildPhase = ''
|
||||
mkdir tmp
|
||||
cp ${rpc-server}/bin/deltachat-rpc-server tmp/deltachat-rpc-server
|
||||
python3 scripts/wheel-rpc-server.py ${arch} tmp/deltachat-rpc-server
|
||||
'';
|
||||
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
|
||||
packages =
|
||||
mkRustPackages "aarch64-linux" //
|
||||
mkRustPackages "i686-linux" //
|
||||
mkRustPackages "x86_64-linux" //
|
||||
mkRustPackages "armv7l-linux" //
|
||||
mkRustPackages "armv6l-linux" //
|
||||
mkAndroidPackages "armeabi-v7a" //
|
||||
mkAndroidPackages "arm64-v8a" //
|
||||
mkAndroidPackages "x86" //
|
||||
mkAndroidPackages "x86_64" // rec {
|
||||
# Run with `nix run .#deltachat-repl foo.db`.
|
||||
deltachat-repl = mkRustPackage "deltachat-repl";
|
||||
deltachat-rpc-server = mkRustPackage "deltachat-rpc-server";
|
||||
|
||||
deltachat-repl-win64 = mkWin64RustPackage "deltachat-repl";
|
||||
deltachat-rpc-server-win64 = mkWin64RustPackage "deltachat-rpc-server";
|
||||
deltachat-rpc-server-win64-wheel =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "deltachat-rpc-server-win64-wheel";
|
||||
version = manifest.version;
|
||||
src = nix-filter.lib {
|
||||
root = ./.;
|
||||
include = [
|
||||
"scripts/wheel-rpc-server.py"
|
||||
"deltachat-rpc-server/README.md"
|
||||
"LICENSE"
|
||||
"Cargo.toml"
|
||||
];
|
||||
};
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.wheel
|
||||
];
|
||||
buildInputs = [
|
||||
deltachat-rpc-server-win64
|
||||
];
|
||||
buildPhase = ''
|
||||
mkdir tmp
|
||||
cp ${deltachat-rpc-server-win64}/bin/deltachat-rpc-server.exe tmp/deltachat-rpc-server.exe
|
||||
python3 scripts/wheel-rpc-server.py win64 tmp/deltachat-rpc-server.exe
|
||||
'';
|
||||
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
|
||||
};
|
||||
|
||||
deltachat-repl-win32 = mkWin32RustPackage "deltachat-repl";
|
||||
deltachat-rpc-server-win32 = mkWin32RustPackage "deltachat-rpc-server";
|
||||
deltachat-rpc-server-win32-wheel =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "deltachat-rpc-server-win32-wheel";
|
||||
version = manifest.version;
|
||||
src = nix-filter.lib {
|
||||
root = ./.;
|
||||
include = [
|
||||
"scripts/wheel-rpc-server.py"
|
||||
"deltachat-rpc-server/README.md"
|
||||
"LICENSE"
|
||||
"Cargo.toml"
|
||||
];
|
||||
};
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.wheel
|
||||
];
|
||||
buildInputs = [
|
||||
deltachat-rpc-server-win32
|
||||
];
|
||||
buildPhase = ''
|
||||
mkdir tmp
|
||||
cp ${deltachat-rpc-server-win32}/bin/deltachat-rpc-server.exe tmp/deltachat-rpc-server.exe
|
||||
python3 scripts/wheel-rpc-server.py win32 tmp/deltachat-rpc-server.exe
|
||||
'';
|
||||
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
|
||||
};
|
||||
# Run `nix build .#docs` to get C docs generated in `./result/`.
|
||||
docs =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "docs";
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
nativeBuildInputs = [ pkgs.doxygen ];
|
||||
buildPhase = ''scripts/run-doxygen.sh'';
|
||||
installPhase = ''mkdir -p $out; cp -av deltachat-ffi/html deltachat-ffi/xml $out'';
|
||||
};
|
||||
|
||||
libdeltachat =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "libdeltachat";
|
||||
version = manifest.version;
|
||||
src = rustSrc;
|
||||
cargoDeps = pkgs.rustPlatform.importCargoLock cargoLock;
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.perl # Needed to build vendored OpenSSL.
|
||||
pkgs.cmake
|
||||
pkgs.rustPlatform.cargoSetupHook
|
||||
pkgs.cargo
|
||||
];
|
||||
buildInputs = pkgs.lib.optionals isDarwin [
|
||||
pkgs.darwin.apple_sdk.frameworks.CoreFoundation
|
||||
pkgs.darwin.apple_sdk.frameworks.Security
|
||||
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
|
||||
pkgs.libiconv
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
substituteInPlace $out/include/deltachat.h \
|
||||
--replace __FILE__ '"${placeholder "out"}/include/deltachat.h"'
|
||||
'';
|
||||
};
|
||||
|
||||
# Source package for deltachat-rpc-server.
|
||||
# Fake package that downloads Linux version,
|
||||
# needed to install deltachat-rpc-server on Android with `pip`.
|
||||
deltachat-rpc-server-source =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "deltachat-rpc-server-source";
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.wheel
|
||||
];
|
||||
buildPhase = ''python3 scripts/wheel-rpc-server.py source deltachat-rpc-server-${manifest.version}.tar.gz'';
|
||||
installPhase = ''mkdir -p $out; cp -av deltachat-rpc-server-${manifest.version}.tar.gz $out'';
|
||||
};
|
||||
|
||||
deltachat-rpc-client =
|
||||
pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "deltachat-rpc-client";
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./deltachat-rpc-client;
|
||||
format = "pyproject";
|
||||
propagatedBuildInputs = [
|
||||
pkgs.python3Packages.setuptools
|
||||
];
|
||||
};
|
||||
|
||||
deltachat-python =
|
||||
pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "deltachat-python";
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./python;
|
||||
format = "pyproject";
|
||||
buildInputs = [
|
||||
libdeltachat
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
pkgs.pkg-config
|
||||
];
|
||||
propagatedBuildInputs = [
|
||||
pkgs.python3Packages.setuptools
|
||||
pkgs.python3Packages.pkgconfig
|
||||
pkgs.python3Packages.cffi
|
||||
pkgs.python3Packages.imap-tools
|
||||
pkgs.python3Packages.pluggy
|
||||
pkgs.python3Packages.requests
|
||||
];
|
||||
};
|
||||
python-docs =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "docs";
|
||||
version = manifest.version;
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
buildInputs = [
|
||||
deltachat-python
|
||||
deltachat-rpc-client
|
||||
pkgs.python3Packages.breathe
|
||||
pkgs.python3Packages.sphinx_rtd_theme
|
||||
];
|
||||
nativeBuildInputs = [ pkgs.sphinx ];
|
||||
buildPhase = ''sphinx-build -b html -a python/doc/ dist/html'';
|
||||
installPhase = ''mkdir -p $out; cp -av dist/html $out'';
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
(fenixPkgs.complete.withComponents [
|
||||
"cargo"
|
||||
"clippy"
|
||||
"rust-src"
|
||||
"rustc"
|
||||
"rustfmt"
|
||||
])
|
||||
cargo-deny
|
||||
fenixPkgs.rust-analyzer
|
||||
perl # needed to build vendored OpenSSL
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
1244
fuzz/Cargo.lock
generated
1244
fuzz/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@ npm install deltachat-node
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Nodejs >= `v16.0.0`
|
||||
- Nodejs >= `v18.0.0`
|
||||
- rustup (optional if you can't use the prebuilds)
|
||||
|
||||
> On Windows, you may need to also install **Perl** to be able to compile deltachat-core.
|
||||
@@ -113,8 +113,8 @@ Then, in the `deltachat-desktop` repository, run:
|
||||
deltachat doesn't support universal (fat) binaries (that contain builds for both cpu architectures) yet, until it does you can use the following workaround to get x86_64 builds:
|
||||
|
||||
```
|
||||
$ fnm install 17 --arch x64
|
||||
$ fnm use 17
|
||||
$ fnm install 19 --arch x64
|
||||
$ fnm use 19
|
||||
$ node -p process.arch
|
||||
# result should be x64
|
||||
$ rustup target add x86_64-apple-darwin
|
||||
@@ -127,8 +127,8 @@ $ npm run test
|
||||
If your node and electron are already build for arm64 you can also try building for arm:
|
||||
|
||||
```
|
||||
$ fnm install 16 --arch arm64
|
||||
$ fnm use 16
|
||||
$ fnm install 18 --arch arm64
|
||||
$ fnm use 18
|
||||
$ node -p process.arch
|
||||
# result should be arm64
|
||||
$ npm_config_arch=arm64 npm run build
|
||||
@@ -204,10 +204,10 @@ Running `npm test` ends with showing a code coverage report, which is produced b
|
||||
|
||||
The coverage report from `nyc` in the console is rather limited. To get a more detailed coverage report you can run `npm run coverage-html-report`. This will produce a html report from the `nyc` data and display it in a browser on your local machine.
|
||||
|
||||
To run the integration tests you need to set the `DCC_NEW_TMP_EMAIL` environment variables. E.g.:
|
||||
To run the integration tests you need to set the `CHATMAIL_DOMAIN` environment variables. E.g.:
|
||||
|
||||
```
|
||||
$ export DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=[token]
|
||||
$ export CHATMAIL_DOMAIN=chat.example.org
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
|
||||
@@ -28,9 +28,12 @@ module.exports = {
|
||||
DC_DOWNLOAD_DONE: 0,
|
||||
DC_DOWNLOAD_FAILURE: 20,
|
||||
DC_DOWNLOAD_IN_PROGRESS: 1000,
|
||||
DC_DOWNLOAD_UNDECIPHERABLE: 30,
|
||||
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE: 2200,
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED: 2021,
|
||||
DC_EVENT_CHAT_MODIFIED: 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS: 2041,
|
||||
DC_EVENT_CONFIG_SYNCED: 2111,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED: 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED: 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE: 151,
|
||||
@@ -78,6 +81,7 @@ module.exports = {
|
||||
DC_INFO_EPHEMERAL_TIMER_CHANGED: 10,
|
||||
DC_INFO_GROUP_IMAGE_CHANGED: 3,
|
||||
DC_INFO_GROUP_NAME_CHANGED: 2,
|
||||
DC_INFO_INVALID_UNENCRYPTED_MAIL: 13,
|
||||
DC_INFO_LOCATIONSTREAMING_ENABLED: 8,
|
||||
DC_INFO_LOCATION_ONLY: 9,
|
||||
DC_INFO_MEMBER_ADDED_TO_GROUP: 4,
|
||||
@@ -111,6 +115,9 @@ module.exports = {
|
||||
DC_PROVIDER_STATUS_BROKEN: 3,
|
||||
DC_PROVIDER_STATUS_OK: 1,
|
||||
DC_PROVIDER_STATUS_PREPARATION: 2,
|
||||
DC_PUSH_CONNECTED: 2,
|
||||
DC_PUSH_HEARTBEAT: 1,
|
||||
DC_PUSH_NOT_CONNECTED: 0,
|
||||
DC_QR_ACCOUNT: 250,
|
||||
DC_QR_ADDR: 320,
|
||||
DC_QR_ASK_VERIFYCONTACT: 200,
|
||||
@@ -159,6 +166,8 @@ module.exports = {
|
||||
DC_STR_BROADCAST_LIST: 115,
|
||||
DC_STR_CANNOT_LOGIN: 60,
|
||||
DC_STR_CANTDECRYPT_MSG_BODY: 29,
|
||||
DC_STR_CHAT_PROTECTION_DISABLED: 171,
|
||||
DC_STR_CHAT_PROTECTION_ENABLED: 170,
|
||||
DC_STR_CONFIGURATION_FAILED: 84,
|
||||
DC_STR_CONNECTED: 107,
|
||||
DC_STR_CONNTECTING: 108,
|
||||
@@ -222,11 +231,13 @@ module.exports = {
|
||||
DC_STR_GROUP_NAME_CHANGED_BY_YOU: 124,
|
||||
DC_STR_IMAGE: 9,
|
||||
DC_STR_INCOMING_MESSAGES: 103,
|
||||
DC_STR_INVALID_UNENCRYPTED_MAIL: 174,
|
||||
DC_STR_LAST_MSG_SENT_SUCCESSFULLY: 111,
|
||||
DC_STR_LOCATION: 66,
|
||||
DC_STR_LOCATION_ENABLED_BY_OTHER: 137,
|
||||
DC_STR_LOCATION_ENABLED_BY_YOU: 136,
|
||||
DC_STR_MESSAGES: 114,
|
||||
DC_STR_MESSAGE_ADD_MEMBER: 173,
|
||||
DC_STR_MSGACTIONBYME: 63,
|
||||
DC_STR_MSGACTIONBYUSER: 62,
|
||||
DC_STR_MSGADDMEMBER: 17,
|
||||
@@ -237,6 +248,7 @@ module.exports = {
|
||||
DC_STR_MSGGRPNAME: 15,
|
||||
DC_STR_MSGLOCATIONDISABLED: 65,
|
||||
DC_STR_MSGLOCATIONENABLED: 64,
|
||||
DC_STR_NEW_GROUP_SEND_FIRST_MESSAGE: 172,
|
||||
DC_STR_NOMESSAGES: 1,
|
||||
DC_STR_NOT_CONNECTED: 121,
|
||||
DC_STR_NOT_SUPPORTED_BY_PROVIDER: 113,
|
||||
@@ -244,13 +256,8 @@ module.exports = {
|
||||
DC_STR_OUTGOING_MESSAGES: 104,
|
||||
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY: 99,
|
||||
DC_STR_PART_OF_TOTAL_USED: 116,
|
||||
DC_STR_PROTECTION_DISABLED: 89,
|
||||
DC_STR_PROTECTION_DISABLED_BY_OTHER: 161,
|
||||
DC_STR_PROTECTION_DISABLED_BY_YOU: 160,
|
||||
DC_STR_PROTECTION_ENABLED: 88,
|
||||
DC_STR_PROTECTION_ENABLED_BY_OTHER: 159,
|
||||
DC_STR_PROTECTION_ENABLED_BY_YOU: 158,
|
||||
DC_STR_QUOTA_EXCEEDING_MSG_BODY: 98,
|
||||
DC_STR_REACTED_BY: 177,
|
||||
DC_STR_READRCPT: 31,
|
||||
DC_STR_READRCPT_MAILBODY: 32,
|
||||
DC_STR_REMOVE_MEMBER_BY_OTHER: 131,
|
||||
@@ -278,6 +285,7 @@ module.exports = {
|
||||
DC_STR_VIDEOCHAT_INVITE_MSG_BODY: 83,
|
||||
DC_STR_VOICEMESSAGE: 7,
|
||||
DC_STR_WELCOME_MESSAGE: 71,
|
||||
DC_STR_YOU_REACTED: 176,
|
||||
DC_TEXT1_DRAFT: 1,
|
||||
DC_TEXT1_SELF: 3,
|
||||
DC_TEXT1_USERNAME: 2,
|
||||
|
||||
@@ -34,6 +34,8 @@ module.exports = {
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2111: 'DC_EVENT_CONFIG_SYNCED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED'
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
|
||||
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE'
|
||||
}
|
||||
|
||||
@@ -28,9 +28,12 @@ export enum C {
|
||||
DC_DOWNLOAD_DONE = 0,
|
||||
DC_DOWNLOAD_FAILURE = 20,
|
||||
DC_DOWNLOAD_IN_PROGRESS = 1000,
|
||||
DC_DOWNLOAD_UNDECIPHERABLE = 30,
|
||||
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200,
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
|
||||
DC_EVENT_CHAT_MODIFIED = 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS = 2041,
|
||||
DC_EVENT_CONFIG_SYNCED = 2111,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED = 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED = 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE = 151,
|
||||
@@ -78,6 +81,7 @@ export enum C {
|
||||
DC_INFO_EPHEMERAL_TIMER_CHANGED = 10,
|
||||
DC_INFO_GROUP_IMAGE_CHANGED = 3,
|
||||
DC_INFO_GROUP_NAME_CHANGED = 2,
|
||||
DC_INFO_INVALID_UNENCRYPTED_MAIL = 13,
|
||||
DC_INFO_LOCATIONSTREAMING_ENABLED = 8,
|
||||
DC_INFO_LOCATION_ONLY = 9,
|
||||
DC_INFO_MEMBER_ADDED_TO_GROUP = 4,
|
||||
@@ -111,6 +115,9 @@ export enum C {
|
||||
DC_PROVIDER_STATUS_BROKEN = 3,
|
||||
DC_PROVIDER_STATUS_OK = 1,
|
||||
DC_PROVIDER_STATUS_PREPARATION = 2,
|
||||
DC_PUSH_CONNECTED = 2,
|
||||
DC_PUSH_HEARTBEAT = 1,
|
||||
DC_PUSH_NOT_CONNECTED = 0,
|
||||
DC_QR_ACCOUNT = 250,
|
||||
DC_QR_ADDR = 320,
|
||||
DC_QR_ASK_VERIFYCONTACT = 200,
|
||||
@@ -159,6 +166,8 @@ export enum C {
|
||||
DC_STR_BROADCAST_LIST = 115,
|
||||
DC_STR_CANNOT_LOGIN = 60,
|
||||
DC_STR_CANTDECRYPT_MSG_BODY = 29,
|
||||
DC_STR_CHAT_PROTECTION_DISABLED = 171,
|
||||
DC_STR_CHAT_PROTECTION_ENABLED = 170,
|
||||
DC_STR_CONFIGURATION_FAILED = 84,
|
||||
DC_STR_CONNECTED = 107,
|
||||
DC_STR_CONNTECTING = 108,
|
||||
@@ -222,11 +231,13 @@ export enum C {
|
||||
DC_STR_GROUP_NAME_CHANGED_BY_YOU = 124,
|
||||
DC_STR_IMAGE = 9,
|
||||
DC_STR_INCOMING_MESSAGES = 103,
|
||||
DC_STR_INVALID_UNENCRYPTED_MAIL = 174,
|
||||
DC_STR_LAST_MSG_SENT_SUCCESSFULLY = 111,
|
||||
DC_STR_LOCATION = 66,
|
||||
DC_STR_LOCATION_ENABLED_BY_OTHER = 137,
|
||||
DC_STR_LOCATION_ENABLED_BY_YOU = 136,
|
||||
DC_STR_MESSAGES = 114,
|
||||
DC_STR_MESSAGE_ADD_MEMBER = 173,
|
||||
DC_STR_MSGACTIONBYME = 63,
|
||||
DC_STR_MSGACTIONBYUSER = 62,
|
||||
DC_STR_MSGADDMEMBER = 17,
|
||||
@@ -237,6 +248,7 @@ export enum C {
|
||||
DC_STR_MSGGRPNAME = 15,
|
||||
DC_STR_MSGLOCATIONDISABLED = 65,
|
||||
DC_STR_MSGLOCATIONENABLED = 64,
|
||||
DC_STR_NEW_GROUP_SEND_FIRST_MESSAGE = 172,
|
||||
DC_STR_NOMESSAGES = 1,
|
||||
DC_STR_NOT_CONNECTED = 121,
|
||||
DC_STR_NOT_SUPPORTED_BY_PROVIDER = 113,
|
||||
@@ -244,13 +256,8 @@ export enum C {
|
||||
DC_STR_OUTGOING_MESSAGES = 104,
|
||||
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY = 99,
|
||||
DC_STR_PART_OF_TOTAL_USED = 116,
|
||||
DC_STR_PROTECTION_DISABLED = 89,
|
||||
DC_STR_PROTECTION_DISABLED_BY_OTHER = 161,
|
||||
DC_STR_PROTECTION_DISABLED_BY_YOU = 160,
|
||||
DC_STR_PROTECTION_ENABLED = 88,
|
||||
DC_STR_PROTECTION_ENABLED_BY_OTHER = 159,
|
||||
DC_STR_PROTECTION_ENABLED_BY_YOU = 158,
|
||||
DC_STR_QUOTA_EXCEEDING_MSG_BODY = 98,
|
||||
DC_STR_REACTED_BY = 177,
|
||||
DC_STR_READRCPT = 31,
|
||||
DC_STR_READRCPT_MAILBODY = 32,
|
||||
DC_STR_REMOVE_MEMBER_BY_OTHER = 131,
|
||||
@@ -278,6 +285,7 @@ export enum C {
|
||||
DC_STR_VIDEOCHAT_INVITE_MSG_BODY = 83,
|
||||
DC_STR_VOICEMESSAGE = 7,
|
||||
DC_STR_WELCOME_MESSAGE = 71,
|
||||
DC_STR_YOU_REACTED = 176,
|
||||
DC_TEXT1_DRAFT = 1,
|
||||
DC_TEXT1_SELF = 3,
|
||||
DC_TEXT1_USERNAME = 2,
|
||||
@@ -321,6 +329,8 @@ export const EventId2EventName: { [key: number]: string } = {
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2111: 'DC_EVENT_CONFIG_SYNCED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
|
||||
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
|
||||
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export class Context extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/** Opens a stanalone context (without an account manager)
|
||||
/** Opens a standalone context (without an account manager)
|
||||
* automatically starts the event handler */
|
||||
static open(cwd: string): Context {
|
||||
const dbFile = join(cwd, 'db.sqlite')
|
||||
@@ -699,23 +699,6 @@ export class Context extends EventEmitter {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chatId
|
||||
* @param protect
|
||||
* @returns success boolean
|
||||
*/
|
||||
setChatProtection(chatId: number, protect: boolean) {
|
||||
debug(`setChatProtection ${chatId} ${protect}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_protection(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
protect ? 1 : 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
getChatEphemeralTimer(chatId: number): number {
|
||||
debug(`getChatEphemeralTimer ${chatId}`)
|
||||
return binding.dcn_get_chat_ephemeral_timer(
|
||||
|
||||
@@ -21,12 +21,15 @@ export class AccountManager extends EventEmitter {
|
||||
accountDir: string
|
||||
jsonRpcStarted = false
|
||||
|
||||
constructor(cwd: string, os = 'deltachat-node') {
|
||||
constructor(cwd: string, writable = true) {
|
||||
super()
|
||||
debug('DeltaChat constructor')
|
||||
|
||||
this.accountDir = cwd
|
||||
this.dcn_accounts = binding.dcn_accounts_new(os, this.accountDir)
|
||||
this.dcn_accounts = binding.dcn_accounts_new(
|
||||
this.accountDir,
|
||||
writable ? 1 : 0
|
||||
)
|
||||
}
|
||||
|
||||
getAllAccountIds() {
|
||||
@@ -175,7 +178,7 @@ export class AccountManager extends EventEmitter {
|
||||
static newTemporary() {
|
||||
let directory = null
|
||||
while (true) {
|
||||
const randomString = Math.random().toString(36).substr(2, 5)
|
||||
const randomString = Math.random().toString(36).substring(2, 5)
|
||||
directory = join(tmpdir(), 'deltachat-' + randomString)
|
||||
if (!existsSync(directory)) break
|
||||
}
|
||||
|
||||
@@ -1399,18 +1399,6 @@ NAPI_METHOD(dcn_set_chat_name) {
|
||||
NAPI_RETURN_INT32(result);
|
||||
}
|
||||
|
||||
NAPI_METHOD(dcn_set_chat_protection) {
|
||||
NAPI_ARGV(3);
|
||||
NAPI_DCN_CONTEXT();
|
||||
NAPI_ARGV_UINT32(chat_id, 1);
|
||||
NAPI_ARGV_INT32(protect, 1);
|
||||
|
||||
int result = dc_set_chat_protection(dcn_context->dc_context,
|
||||
chat_id,
|
||||
protect);
|
||||
NAPI_RETURN_INT32(result);
|
||||
}
|
||||
|
||||
NAPI_METHOD(dcn_get_chat_ephemeral_timer) {
|
||||
NAPI_ARGV(2);
|
||||
NAPI_DCN_CONTEXT();
|
||||
@@ -2915,8 +2903,8 @@ NAPI_METHOD(dcn_msg_get_webxdc_blob){
|
||||
|
||||
NAPI_METHOD(dcn_accounts_new) {
|
||||
NAPI_ARGV(2);
|
||||
NAPI_ARGV_UTF8_MALLOC(os_name, 0);
|
||||
NAPI_ARGV_UTF8_MALLOC(dir, 1);
|
||||
NAPI_ARGV_UTF8_MALLOC(dir, 0);
|
||||
NAPI_ARGV_INT32(writable, 1);
|
||||
TRACE("calling..");
|
||||
|
||||
dcn_accounts_t* dcn_accounts = calloc(1, sizeof(dcn_accounts_t));
|
||||
@@ -2925,7 +2913,7 @@ NAPI_METHOD(dcn_accounts_new) {
|
||||
}
|
||||
|
||||
|
||||
dcn_accounts->dc_accounts = dc_accounts_new(os_name, dir);
|
||||
dcn_accounts->dc_accounts = dc_accounts_new(dir, writable);
|
||||
|
||||
napi_value result;
|
||||
NAPI_STATUS_THROWS(napi_create_external(env, dcn_accounts,
|
||||
@@ -3060,14 +3048,6 @@ NAPI_METHOD(dcn_accounts_select_account) {
|
||||
NAPI_RETURN_UINT32(result);
|
||||
}
|
||||
|
||||
NAPI_METHOD(dcn_accounts_all_work_done) {
|
||||
NAPI_ARGV(1);
|
||||
NAPI_DCN_ACCOUNTS();
|
||||
|
||||
int result = dc_accounts_all_work_done(dcn_accounts->dc_accounts);
|
||||
NAPI_RETURN_INT32(result);
|
||||
}
|
||||
|
||||
NAPI_METHOD(dcn_accounts_start_io) {
|
||||
NAPI_ARGV(1);
|
||||
NAPI_DCN_ACCOUNTS();
|
||||
@@ -3394,7 +3374,6 @@ NAPI_INIT() {
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_get_account);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_get_selected_account);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_select_account);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_all_work_done);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_start_io);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_stop_io);
|
||||
NAPI_EXPORT_FUNCTION(dcn_accounts_maybe_network);
|
||||
@@ -3491,7 +3470,6 @@ NAPI_INIT() {
|
||||
NAPI_EXPORT_FUNCTION(dcn_send_msg);
|
||||
NAPI_EXPORT_FUNCTION(dcn_send_videochat_invitation);
|
||||
NAPI_EXPORT_FUNCTION(dcn_set_chat_name);
|
||||
NAPI_EXPORT_FUNCTION(dcn_set_chat_protection);
|
||||
NAPI_EXPORT_FUNCTION(dcn_get_chat_ephemeral_timer);
|
||||
NAPI_EXPORT_FUNCTION(dcn_set_chat_ephemeral_timer);
|
||||
NAPI_EXPORT_FUNCTION(dcn_set_chat_profile_image);
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
// @ts-check
|
||||
import DeltaChat from '../dist'
|
||||
import { DeltaChat } from '../dist/index.js'
|
||||
|
||||
import { deepStrictEqual, strictEqual } from 'assert'
|
||||
import chai, { expect } from 'chai'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import { EventId2EventName, C } from '../dist/constants'
|
||||
import { EventId2EventName, C } from '../dist/constants.js'
|
||||
import { join } from 'path'
|
||||
import { statSync } from 'fs'
|
||||
import { Context } from '../dist/context'
|
||||
import fetch from 'node-fetch'
|
||||
import { Context } from '../dist/context.js'
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
chai.config.truncateThreshold = 0 // Do not truncate assertion errors.
|
||||
|
||||
async function createTempUser(url) {
|
||||
async function postData(url = '') {
|
||||
// Default options are marked with *
|
||||
const response = await fetch(url, {
|
||||
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||
headers: {
|
||||
'cache-control': 'no-cache',
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error('request failed: ' + response.body.read())
|
||||
}
|
||||
return response.json() // parses JSON response into native JavaScript objects
|
||||
function createTempUser(chatmailDomain) {
|
||||
const charset = "2345789acdefghjkmnpqrstuvwxyz";
|
||||
let user = "ci-";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
user += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
|
||||
return await postData(url)
|
||||
const email = user + "@" + chatmailDomain;
|
||||
return { email: email, password: user + "$" + user };
|
||||
}
|
||||
|
||||
describe('static tests', function () {
|
||||
@@ -244,7 +239,7 @@ describe('Basic offline Tests', function () {
|
||||
'delete_device_after',
|
||||
'delete_server_after',
|
||||
'deltachat_core_version',
|
||||
'display_name',
|
||||
'displayname',
|
||||
'download_limit',
|
||||
'e2ee_enabled',
|
||||
'entered_account_settings',
|
||||
@@ -255,6 +250,7 @@ describe('Basic offline Tests', function () {
|
||||
'journal_mode',
|
||||
'key_gen_type',
|
||||
'last_housekeeping',
|
||||
'last_cant_decrypt_outgoing_msgs',
|
||||
'level',
|
||||
'mdns_enabled',
|
||||
'media_quality',
|
||||
@@ -270,7 +266,7 @@ describe('Basic offline Tests', function () {
|
||||
'quota_exceeding',
|
||||
'scan_all_folders_debounce_secs',
|
||||
'selfavatar',
|
||||
'send_sync_msgs',
|
||||
'sync_msgs',
|
||||
'sentbox_watch',
|
||||
'show_emails',
|
||||
'socks5_enabled',
|
||||
@@ -676,9 +672,9 @@ describe('Offline Tests with unconfigured account', function () {
|
||||
|
||||
const lot = chatList.getSummary(0)
|
||||
strictEqual(lot.getId(), 0, 'lot has no id')
|
||||
strictEqual(lot.getState(), C.DC_STATE_UNDEFINED, 'correct state')
|
||||
strictEqual(lot.getState(), C.DC_STATE_IN_NOTICED, 'correct state')
|
||||
|
||||
const text = 'No messages.'
|
||||
const text = 'Others will only see this group after you sent a first message.'
|
||||
context.createGroupChat('groupchat1111')
|
||||
chatList = context.getChatList(0, 'groupchat1111', null)
|
||||
strictEqual(
|
||||
@@ -768,14 +764,7 @@ describe('Integration tests', function () {
|
||||
})
|
||||
|
||||
this.beforeAll(async function () {
|
||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||
console.log(
|
||||
'Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests'
|
||||
)
|
||||
this.skip()
|
||||
}
|
||||
|
||||
account = await createTempUser(process.env.DCC_NEW_TMP_EMAIL)
|
||||
account = createTempUser(process.env.CHATMAIL_DOMAIN)
|
||||
if (!account || !account.email || !account.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
@@ -13,10 +13,10 @@
|
||||
},
|
||||
"exclude": ["node_modules", "deltachat-core-rust", "dist", "scripts"],
|
||||
"typedocOptions": {
|
||||
"mode": "file",
|
||||
"out": "docs",
|
||||
"excludeNotExported": true,
|
||||
"excludePrivate": true,
|
||||
"defaultCategory": "index",
|
||||
"includeVersion": true
|
||||
"includeVersion": true,
|
||||
"entryPoints": ["lib/index.ts"]
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ E.g via <https://git-scm.com/download/win>
|
||||
|
||||
## install node
|
||||
|
||||
Download and install `v16` from <https://nodejs.org/en/>
|
||||
Download and install `v18` from <https://nodejs.org/en/>
|
||||
|
||||
## install rust
|
||||
|
||||
|
||||
31
package.json
31
package.json
@@ -2,28 +2,24 @@
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"napi-macros": "^2.0.0",
|
||||
"node-gyp-build": "^4.1.0"
|
||||
"node-gyp-build": "^4.6.1"
|
||||
},
|
||||
"description": "node.js bindings for deltachat-core",
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/node": "^16.11.26",
|
||||
"chai": "^4.2.0",
|
||||
"@types/node": "^20.8.10",
|
||||
"chai": "~4.3.10",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"esm": "^3.2.25",
|
||||
"hallmark": "^2.0.0",
|
||||
"mocha": "^8.2.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-gyp": "^9.0.0",
|
||||
"opn-cli": "^5.0.0",
|
||||
"prebuildify": "^3.0.0",
|
||||
"prebuildify-ci": "^1.0.4",
|
||||
"prettier": "^2.0.5",
|
||||
"typedoc": "^0.17.0",
|
||||
"typescript": "^3.9.10"
|
||||
"node-gyp": "^10.0.0",
|
||||
"prebuildify": "^5.0.1",
|
||||
"prebuildify-ci": "^1.0.5",
|
||||
"prettier": "^3.0.3",
|
||||
"typedoc": "^0.25.3",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"files": [
|
||||
"node/scripts/*",
|
||||
@@ -49,16 +45,15 @@
|
||||
"build:core:rust": "node node/scripts/rebuild-core.js",
|
||||
"clean": "rm -rf node/dist node/build node/prebuilds node/node_modules ./target",
|
||||
"download-prebuilds": "prebuildify-ci download",
|
||||
"hallmark": "hallmark --fix",
|
||||
"install": "node node/scripts/install.js",
|
||||
"install:prebuilds": "cd node && node-gyp-build \"npm run build:core\" \"npm run build:bindings:c:postinstall\"",
|
||||
"lint": "prettier --check \"node/lib/**/*.{ts,tsx}\"",
|
||||
"lint-fix": "prettier --write \"node/lib/**/*.{ts,tsx}\" \"node/test/**/*.js\"",
|
||||
"prebuildify": "cd node && prebuildify -t 16.13.0 --napi --strip --postinstall \"node scripts/postinstall.js --prebuild\"",
|
||||
"prebuildify": "cd node && prebuildify -t 18.0.0 --napi --strip --postinstall \"node scripts/postinstall.js --prebuild\"",
|
||||
"test": "npm run test:lint && npm run test:mocha",
|
||||
"test:lint": "npm run lint",
|
||||
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
|
||||
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.125.0"
|
||||
"version": "1.137.2"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
=========================
|
||||
DeltaChat Python bindings
|
||||
=========================
|
||||
============================
|
||||
CFFI Python Bindings
|
||||
============================
|
||||
|
||||
This package provides `Python bindings`_ to the `deltachat-core library`_
|
||||
which implements IMAP/SMTP/MIME/OpenPGP e-mail standards and offers
|
||||
@@ -8,157 +8,3 @@ a low-level Chat/Contact/Message API to user interfaces and bots.
|
||||
|
||||
.. _`deltachat-core library`: https://github.com/deltachat/deltachat-core-rust
|
||||
.. _`Python bindings`: https://py.delta.chat/
|
||||
|
||||
Installing pre-built packages (Linux-only)
|
||||
==========================================
|
||||
|
||||
If you have a Linux system you may install the ``deltachat`` binary "wheel" packages
|
||||
without any "build-from-source" steps.
|
||||
Otherwise you need to `compile the Delta Chat bindings yourself`__.
|
||||
|
||||
__ sourceinstall_
|
||||
|
||||
We recommend to first create a fresh Python virtual environment
|
||||
and activate it in your shell::
|
||||
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
|
||||
Afterwards, invoking ``python`` or ``pip install`` only
|
||||
modifies files in your ``env`` directory and leaves
|
||||
your system installation alone.
|
||||
|
||||
For Linux we build wheels for all releases and push them to a python package
|
||||
index. To install the latest release::
|
||||
|
||||
pip install deltachat
|
||||
|
||||
To verify it worked::
|
||||
|
||||
python -c "import deltachat"
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
Recommended way to run tests is using `scripts/run-python-test.sh`
|
||||
script provided in the core repository.
|
||||
|
||||
This script compiles the library in debug mode and runs the tests using `tox`_.
|
||||
By default it will run all "offline" tests and skip all functional
|
||||
end-to-end tests that require accounts on real e-mail servers.
|
||||
|
||||
.. _`tox`: https://tox.wiki
|
||||
.. _livetests:
|
||||
|
||||
Running "live" tests with temporary accounts
|
||||
--------------------------------------------
|
||||
|
||||
If you want to run live functional tests you can set ``DCC_NEW_TMP_EMAIL`` to a URL that creates e-mail accounts. Most developers use https://testrun.org URLs created and managed by `mailadm <https://mailadm.readthedocs.io/>`_.
|
||||
|
||||
Please feel free to contact us through a github issue or by e-mail and we'll send you a URL that you can then use for functional tests like this::
|
||||
|
||||
export DCC_NEW_TMP_EMAIL=<URL you got from us>
|
||||
|
||||
With this account-creation setting, pytest runs create ephemeral e-mail accounts on the http://testrun.org server.
|
||||
These accounts are removed automatically as they expire.
|
||||
After setting the variable, either rerun `scripts/run-python-test.sh`
|
||||
or run offline and online tests with `tox` directly::
|
||||
|
||||
tox -e py
|
||||
|
||||
Each test run creates new accounts.
|
||||
|
||||
Developing the bindings
|
||||
-----------------------
|
||||
|
||||
If you want to develop or debug the bindings,
|
||||
you can create a testing development environment using `tox`::
|
||||
|
||||
export DCC_RS_DEV="$PWD"
|
||||
export DCC_RS_TARGET=debug
|
||||
tox -c python --devenv env -e py
|
||||
. env/bin/activate
|
||||
|
||||
Inside this environment the bindings are installed
|
||||
in editable mode (as if installed with `python -m pip install -e`)
|
||||
together with the testing dependencies like `pytest` and its plugins.
|
||||
|
||||
You can then edit the source code in the development tree
|
||||
and quickly run `pytest` manually without waiting for `tox`
|
||||
to recreating the virtual environment each time.
|
||||
|
||||
.. _sourceinstall:
|
||||
|
||||
Installing bindings from source
|
||||
===============================
|
||||
|
||||
Install Rust and Cargo first.
|
||||
The easiest is probably to use `rustup <https://rustup.rs/>`_.
|
||||
|
||||
Bootstrap Rust and Cargo by using rustup::
|
||||
|
||||
curl https://sh.rustup.rs -sSf | sh
|
||||
|
||||
Then clone the deltachat-core-rust repo::
|
||||
|
||||
git clone https://github.com/deltachat/deltachat-core-rust
|
||||
cd deltachat-core-rust
|
||||
|
||||
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.
|
||||
|
||||
First, build the core library::
|
||||
|
||||
cargo build --release -p deltachat_ffi --features jsonrpc
|
||||
|
||||
`jsonrpc` feature is required even if not used by the bindings
|
||||
because `deltachat.h` includes JSON-RPC functions unconditionally.
|
||||
|
||||
Create the virtual environment and activate it:
|
||||
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
|
||||
Build and install the bindings:
|
||||
|
||||
export DCC_RS_DEV="$PWD"
|
||||
export DCC_RS_TARGET=release
|
||||
python -m pip install ./python
|
||||
|
||||
`DCC_RS_DEV` environment variable specifies the location of
|
||||
the core development tree. If this variable is not set,
|
||||
`libdeltachat` library and `deltachat.h` header are expected
|
||||
to be installed system-wide.
|
||||
|
||||
When `DCC_RS_DEV` is set, `DCC_RS_TARGET` specifies
|
||||
the build profile name to look up the artifacts
|
||||
in the target directory.
|
||||
In this case setting it can be skipped because
|
||||
`DCC_RS_TARGET=release` is the default.
|
||||
|
||||
Building manylinux based wheels
|
||||
===============================
|
||||
|
||||
Building portable manylinux wheels which come with libdeltachat.so
|
||||
can be done with Docker_ or Podman_.
|
||||
|
||||
.. _Docker: https://www.docker.com/
|
||||
.. _Podman: https://podman.io/
|
||||
|
||||
If you want to build your own wheels, build container image first::
|
||||
|
||||
$ cd deltachat-core-rust # cd to deltachat-core-rust working tree
|
||||
$ docker build -t deltachat/coredeps scripts/coredeps
|
||||
|
||||
This will use the ``scripts/coredeps/Dockerfile`` to build
|
||||
container image called ``deltachat/coredeps``. You can afterwards
|
||||
find it with::
|
||||
|
||||
$ docker images
|
||||
|
||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||
|
||||
$ docker run -e DCC_NEW_TMP_EMAIL \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps scripts/run_all.sh
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
VERSION = $(shell python -c "import conf ; print(conf.version)")
|
||||
DOCZIP = devpi-$(VERSION).doc.zip
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
RSYNCOPTS = -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
|
||||
export HOME=/tmp/home
|
||||
export TESTHOME=$(HOME)
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
# This variable is not auto generated as the order is important.
|
||||
USER_MAN_CHAPTERS = commands\
|
||||
user\
|
||||
indices\
|
||||
packages\
|
||||
# userman/index.rst\
|
||||
# userman/devpi_misc.rst\
|
||||
# userman/devpi_concepts.rst\
|
||||
|
||||
|
||||
#export DEVPI_CLIENTDIR=$(CURDIR)/.tmp_devpi_user_man/client
|
||||
#export DEVPI_SERVERDIR=$(CURDIR)/.tmp_devpi_user_man/server
|
||||
|
||||
chapter = commands
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \
|
||||
epub latex latexpdf text man changes linkcheck doctest gettext install \
|
||||
quickstart-releaseprocess quickstart-pypimirror quickstart-server regen \
|
||||
prepare-quickstart\
|
||||
regen.server-fresh regen.server-restart regen.server-clean\
|
||||
regen.uman-all regen.uman
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo
|
||||
@echo "User Manual Regen Targets"
|
||||
@echo " regen.uman regenerates page. of the user manual chapeter e.g. regen.uman chapter=..."
|
||||
@echo " regen.uman-all regenerates the user manual"
|
||||
@echo " regen.uman-clean stop temp server and clean up directory"
|
||||
@echo " Chapter List: $(USER_MAN_CHAPTERS)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
version:
|
||||
@echo "version $(VERSION)"
|
||||
|
||||
doczip: html
|
||||
python doczip.py $(DOCZIP) _build/html
|
||||
|
||||
install: html
|
||||
rsync -avz $(RSYNCOPTS) _build/html/ delta@py.delta.chat:build/master
|
||||
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/devpi.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/devpi.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/devpi"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/devpi"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
|
||||
17
python/doc/_templates/globaltoc.html
vendored
17
python/doc/_templates/globaltoc.html
vendored
@@ -1,17 +0,0 @@
|
||||
|
||||
<div class="globaltoc">
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ pathto('index') }}">index</a></li>
|
||||
<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>
|
||||
</ul>
|
||||
<b>external links:</b>
|
||||
<ul>
|
||||
<li><a href="https://github.com/deltachat/deltachat-core-rust">github repository</a></li>
|
||||
<li><a href="https://pypi.python.org/pypi/deltachat">pypi: deltachat</a></li>
|
||||
<li><a href="https://web.libera.chat/#deltachat">#deltachat</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
1
python/doc/_templates/sidebarintro.html
vendored
1
python/doc/_templates/sidebarintro.html
vendored
@@ -1 +0,0 @@
|
||||
<h3>deltachat {{release}}</h3>
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
high level API reference
|
||||
High Level API Reference
|
||||
========================
|
||||
|
||||
- :class:`deltachat.Account` (your main entry point, creates the
|
||||
@@ -8,28 +7,14 @@ high level API reference
|
||||
- :class:`deltachat.Chat`
|
||||
- :class:`deltachat.Message`
|
||||
|
||||
Account
|
||||
-------
|
||||
|
||||
.. autoclass:: deltachat.Account
|
||||
:members:
|
||||
|
||||
|
||||
Contact
|
||||
-------
|
||||
:members:
|
||||
|
||||
.. autoclass:: deltachat.Contact
|
||||
:members:
|
||||
|
||||
Chat
|
||||
----
|
||||
:members:
|
||||
|
||||
.. autoclass:: deltachat.Chat
|
||||
:members:
|
||||
|
||||
Message
|
||||
-------
|
||||
:members:
|
||||
|
||||
.. autoclass:: deltachat.Message
|
||||
:members:
|
||||
|
||||
:members:
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
examples
|
||||
Examples
|
||||
========
|
||||
|
||||
Once you have :doc:`installed deltachat bindings <install>`
|
||||
you need email/password credentials for an IMAP/SMTP account.
|
||||
Delta Chat developers and the CI system use a special URL to create
|
||||
temporary e-mail accounts on [testrun.org](https://testrun.org) for testing.
|
||||
temporary email accounts on `testrun.org <https://testrun.org/>`_ for testing.
|
||||
|
||||
Receiving a Chat message from the command line
|
||||
----------------------------------------------
|
||||
@@ -16,11 +15,11 @@ Here is a simple bot that:
|
||||
|
||||
- terminates the bot if the message `/quit` is sent
|
||||
|
||||
.. include:: ../examples/echo_and_quit.py
|
||||
.. include:: ../../examples/echo_and_quit.py
|
||||
:literal:
|
||||
|
||||
With this file in your working directory you can run the bot
|
||||
by specifying a database path, an e-mail address and password of
|
||||
by specifying a database path, an email address and password of
|
||||
a SMTP-IMAP account::
|
||||
|
||||
$ cd examples
|
||||
@@ -40,11 +39,11 @@ Here is a simple bot that:
|
||||
|
||||
- tracks member additions and removals for all chat groups
|
||||
|
||||
.. include:: ../examples/group_tracking.py
|
||||
.. include:: ../../examples/group_tracking.py
|
||||
:literal:
|
||||
|
||||
With this file in your working directory you can run the bot
|
||||
by specifying a database path, an e-mail address and password of
|
||||
by specifying a database path, an email address and password of
|
||||
a SMTP-IMAP account::
|
||||
|
||||
python group_tracking.py --email ADDRESS --password PASSWORD /tmp/db
|
||||
80
python/doc/cffi/install.rst
Normal file
80
python/doc/cffi/install.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
Install
|
||||
=======
|
||||
|
||||
Installing pre-built packages (Linux-only)
|
||||
------------------------------------------
|
||||
|
||||
If you have a Linux system you may install the ``deltachat`` binary "wheel" packages
|
||||
without any "build-from-source" steps.
|
||||
Otherwise you need to `compile the Delta Chat bindings yourself`__.
|
||||
|
||||
__ sourceinstall_
|
||||
|
||||
We recommend to first create a fresh Python virtual environment
|
||||
and activate it in your shell::
|
||||
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
|
||||
Afterwards, invoking ``python`` or ``pip install`` only
|
||||
modifies files in your ``env`` directory and leaves
|
||||
your system installation alone.
|
||||
|
||||
For Linux we build wheels for all releases and push them to a python package
|
||||
index. To install the latest release::
|
||||
|
||||
pip install deltachat
|
||||
|
||||
To verify it worked::
|
||||
|
||||
python -c "import deltachat"
|
||||
|
||||
.. _sourceinstall:
|
||||
|
||||
Installing bindings from source
|
||||
-------------------------------
|
||||
|
||||
Install Rust and Cargo first.
|
||||
The easiest is probably to use `rustup <https://rustup.rs/>`_.
|
||||
|
||||
Bootstrap Rust and Cargo by using rustup::
|
||||
|
||||
curl https://sh.rustup.rs -sSf | sh
|
||||
|
||||
Then clone the deltachat-core-rust repo::
|
||||
|
||||
git clone https://github.com/deltachat/deltachat-core-rust
|
||||
cd deltachat-core-rust
|
||||
|
||||
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.
|
||||
|
||||
First, build the core library::
|
||||
|
||||
cargo build --release -p deltachat_ffi --features jsonrpc
|
||||
|
||||
`jsonrpc` feature is required even if not used by the bindings
|
||||
because `deltachat.h` includes JSON-RPC functions unconditionally.
|
||||
|
||||
Create the virtual environment and activate it::
|
||||
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
|
||||
Build and install the bindings::
|
||||
|
||||
export DCC_RS_DEV="$PWD"
|
||||
export DCC_RS_TARGET=release
|
||||
python -m pip install ./python
|
||||
|
||||
`DCC_RS_DEV` environment variable specifies the location of
|
||||
the core development tree. If this variable is not set,
|
||||
`libdeltachat` library and `deltachat.h` header are expected
|
||||
to be installed system-wide.
|
||||
|
||||
When `DCC_RS_DEV` is set, `DCC_RS_TARGET` specifies
|
||||
the build profile name to look up the artifacts
|
||||
in the target directory.
|
||||
In this case setting it can be skipped because
|
||||
`DCC_RS_TARGET=release` is the default.
|
||||
11
python/doc/cffi/intro.rst
Normal file
11
python/doc/cffi/intro.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
CFFI bindings are available via the `deltachat <https://pypi.org/project/deltachat/>`_ Python package.
|
||||
The package contains both the Python bindings and the Delta Chat core.
|
||||
It is provided only for Linux.
|
||||
|
||||
The ``deltachat`` Python package provides two layers of bindings for the
|
||||
core Rust-library of the https://delta.chat messaging ecosystem:
|
||||
low-level CFFI bindings to the C interface of the Delta Chat core
|
||||
and high-level Python bindings built on top of CFFI bindings.
|
||||
@@ -1,8 +1,7 @@
|
||||
Low Level API Reference
|
||||
=======================
|
||||
|
||||
low level API reference
|
||||
===================================
|
||||
|
||||
for full doxygen-generated C-docs, defines and functions please checkout
|
||||
For full doxygen-generated C-docs, defines and functions please checkout
|
||||
|
||||
https://c.delta.chat
|
||||
|
||||
25
python/doc/cffi/manylinux.rst
Normal file
25
python/doc/cffi/manylinux.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
Building Manylinux-Based Wheels
|
||||
===============================
|
||||
|
||||
Building portable manylinux wheels which come with libdeltachat.so
|
||||
can be done with Docker_ or Podman_.
|
||||
|
||||
.. _Docker: https://www.docker.com/
|
||||
.. _Podman: https://podman.io/
|
||||
|
||||
If you want to build your own wheels, build container image first::
|
||||
|
||||
$ cd deltachat-core-rust # cd to deltachat-core-rust working tree
|
||||
$ docker build -t deltachat/coredeps scripts/coredeps
|
||||
|
||||
This will use the ``scripts/coredeps/Dockerfile`` to build
|
||||
container image called ``deltachat/coredeps``. You can afterwards
|
||||
find it with::
|
||||
|
||||
$ docker images
|
||||
|
||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||
|
||||
$ docker run -e CHATMAIL_DOMAIN \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps scripts/run_all.sh
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user