+5222 -0 +/-24 browse
1 | diff --git a/.gitignore b/.gitignore |
2 | new file mode 100644 |
3 | index 0000000..ea8c4bf |
4 | --- /dev/null |
5 | +++ b/.gitignore |
6 | @@ -0,0 +1 @@ |
7 | + /target |
8 | diff --git a/Cargo.lock b/Cargo.lock |
9 | new file mode 100644 |
10 | index 0000000..ac35ea5 |
11 | --- /dev/null |
12 | +++ b/Cargo.lock |
13 | @@ -0,0 +1,2149 @@ |
14 | + # This file is automatically @generated by Cargo. |
15 | + # It is not intended for manual editing. |
16 | + version = 3 |
17 | + |
18 | + [[package]] |
19 | + name = "addr2line" |
20 | + version = "0.17.0" |
21 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
22 | + checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" |
23 | + dependencies = [ |
24 | + "gimli", |
25 | + ] |
26 | + |
27 | + [[package]] |
28 | + name = "adler" |
29 | + version = "1.0.2" |
30 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
31 | + checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" |
32 | + |
33 | + [[package]] |
34 | + name = "ahash" |
35 | + version = "0.7.6" |
36 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
37 | + checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" |
38 | + dependencies = [ |
39 | + "getrandom", |
40 | + "once_cell", |
41 | + "version_check", |
42 | + ] |
43 | + |
44 | + [[package]] |
45 | + name = "ansi_term" |
46 | + version = "0.12.1" |
47 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
48 | + checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" |
49 | + dependencies = [ |
50 | + "winapi", |
51 | + ] |
52 | + |
53 | + [[package]] |
54 | + name = "async-channel" |
55 | + version = "1.6.1" |
56 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
57 | + checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" |
58 | + dependencies = [ |
59 | + "concurrent-queue", |
60 | + "event-listener", |
61 | + "futures-core", |
62 | + ] |
63 | + |
64 | + [[package]] |
65 | + name = "async-executor" |
66 | + version = "1.4.1" |
67 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
68 | + checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" |
69 | + dependencies = [ |
70 | + "async-task", |
71 | + "concurrent-queue", |
72 | + "fastrand", |
73 | + "futures-lite", |
74 | + "once_cell", |
75 | + "slab", |
76 | + ] |
77 | + |
78 | + [[package]] |
79 | + name = "async-fs" |
80 | + version = "1.5.0" |
81 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
82 | + checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" |
83 | + dependencies = [ |
84 | + "async-lock", |
85 | + "blocking", |
86 | + "futures-lite", |
87 | + ] |
88 | + |
89 | + [[package]] |
90 | + name = "async-io" |
91 | + version = "1.6.0" |
92 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
93 | + checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" |
94 | + dependencies = [ |
95 | + "concurrent-queue", |
96 | + "futures-lite", |
97 | + "libc", |
98 | + "log", |
99 | + "once_cell", |
100 | + "parking", |
101 | + "polling", |
102 | + "slab", |
103 | + "socket2", |
104 | + "waker-fn", |
105 | + "winapi", |
106 | + ] |
107 | + |
108 | + [[package]] |
109 | + name = "async-lock" |
110 | + version = "2.5.0" |
111 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
112 | + checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" |
113 | + dependencies = [ |
114 | + "event-listener", |
115 | + ] |
116 | + |
117 | + [[package]] |
118 | + name = "async-net" |
119 | + version = "1.6.1" |
120 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
121 | + checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" |
122 | + dependencies = [ |
123 | + "async-io", |
124 | + "blocking", |
125 | + "futures-lite", |
126 | + ] |
127 | + |
128 | + [[package]] |
129 | + name = "async-process" |
130 | + version = "1.4.0" |
131 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
132 | + checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" |
133 | + dependencies = [ |
134 | + "async-io", |
135 | + "blocking", |
136 | + "cfg-if", |
137 | + "event-listener", |
138 | + "futures-lite", |
139 | + "libc", |
140 | + "once_cell", |
141 | + "signal-hook", |
142 | + "winapi", |
143 | + ] |
144 | + |
145 | + [[package]] |
146 | + name = "async-stream" |
147 | + version = "0.3.3" |
148 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
149 | + checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" |
150 | + dependencies = [ |
151 | + "async-stream-impl", |
152 | + "futures-core", |
153 | + ] |
154 | + |
155 | + [[package]] |
156 | + name = "async-stream-impl" |
157 | + version = "0.3.3" |
158 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
159 | + checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" |
160 | + dependencies = [ |
161 | + "proc-macro2", |
162 | + "quote", |
163 | + "syn", |
164 | + ] |
165 | + |
166 | + [[package]] |
167 | + name = "async-task" |
168 | + version = "4.2.0" |
169 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
170 | + checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" |
171 | + |
172 | + [[package]] |
173 | + name = "atomic-waker" |
174 | + version = "1.0.0" |
175 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
176 | + checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" |
177 | + |
178 | + [[package]] |
179 | + name = "atty" |
180 | + version = "0.2.14" |
181 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
182 | + checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" |
183 | + dependencies = [ |
184 | + "hermit-abi", |
185 | + "libc", |
186 | + "winapi", |
187 | + ] |
188 | + |
189 | + [[package]] |
190 | + name = "autocfg" |
191 | + version = "1.1.0" |
192 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
193 | + checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" |
194 | + |
195 | + [[package]] |
196 | + name = "backtrace" |
197 | + version = "0.3.65" |
198 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
199 | + checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" |
200 | + dependencies = [ |
201 | + "addr2line", |
202 | + "cc", |
203 | + "cfg-if", |
204 | + "libc", |
205 | + "miniz_oxide", |
206 | + "object", |
207 | + "rustc-demangle", |
208 | + ] |
209 | + |
210 | + [[package]] |
211 | + name = "base64" |
212 | + version = "0.13.0" |
213 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
214 | + checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" |
215 | + |
216 | + [[package]] |
217 | + name = "bincode" |
218 | + version = "1.3.3" |
219 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
220 | + checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" |
221 | + dependencies = [ |
222 | + "serde", |
223 | + ] |
224 | + |
225 | + [[package]] |
226 | + name = "bitflags" |
227 | + version = "1.3.2" |
228 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
229 | + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" |
230 | + |
231 | + [[package]] |
232 | + name = "block-buffer" |
233 | + version = "0.9.0" |
234 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
235 | + checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" |
236 | + dependencies = [ |
237 | + "generic-array", |
238 | + ] |
239 | + |
240 | + [[package]] |
241 | + name = "block-buffer" |
242 | + version = "0.10.2" |
243 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
244 | + checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" |
245 | + dependencies = [ |
246 | + "generic-array", |
247 | + ] |
248 | + |
249 | + [[package]] |
250 | + name = "blocking" |
251 | + version = "1.2.0" |
252 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
253 | + checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" |
254 | + dependencies = [ |
255 | + "async-channel", |
256 | + "async-task", |
257 | + "atomic-waker", |
258 | + "fastrand", |
259 | + "futures-lite", |
260 | + "once_cell", |
261 | + ] |
262 | + |
263 | + [[package]] |
264 | + name = "buf_redux" |
265 | + version = "0.8.4" |
266 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
267 | + checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" |
268 | + dependencies = [ |
269 | + "memchr", |
270 | + "safemem", |
271 | + ] |
272 | + |
273 | + [[package]] |
274 | + name = "byteorder" |
275 | + version = "1.4.3" |
276 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
277 | + checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" |
278 | + |
279 | + [[package]] |
280 | + name = "bytes" |
281 | + version = "1.1.0" |
282 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
283 | + checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" |
284 | + |
285 | + [[package]] |
286 | + name = "cache-padded" |
287 | + version = "1.2.0" |
288 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
289 | + checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" |
290 | + |
291 | + [[package]] |
292 | + name = "cc" |
293 | + version = "1.0.73" |
294 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
295 | + checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" |
296 | + |
297 | + [[package]] |
298 | + name = "cfg-if" |
299 | + version = "1.0.0" |
300 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
301 | + checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
302 | + |
303 | + [[package]] |
304 | + name = "chrono" |
305 | + version = "0.4.19" |
306 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
307 | + checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" |
308 | + dependencies = [ |
309 | + "libc", |
310 | + "num-integer", |
311 | + "num-traits", |
312 | + "serde", |
313 | + "time", |
314 | + "winapi", |
315 | + ] |
316 | + |
317 | + [[package]] |
318 | + name = "clap" |
319 | + version = "2.34.0" |
320 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
321 | + checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" |
322 | + dependencies = [ |
323 | + "ansi_term", |
324 | + "atty", |
325 | + "bitflags", |
326 | + "strsim", |
327 | + "textwrap", |
328 | + "unicode-width", |
329 | + "vec_map", |
330 | + ] |
331 | + |
332 | + [[package]] |
333 | + name = "concurrent-queue" |
334 | + version = "1.2.2" |
335 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
336 | + checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" |
337 | + dependencies = [ |
338 | + "cache-padded", |
339 | + ] |
340 | + |
341 | + [[package]] |
342 | + name = "core-foundation" |
343 | + version = "0.9.3" |
344 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
345 | + checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" |
346 | + dependencies = [ |
347 | + "core-foundation-sys", |
348 | + "libc", |
349 | + ] |
350 | + |
351 | + [[package]] |
352 | + name = "core-foundation-sys" |
353 | + version = "0.8.3" |
354 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
355 | + checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" |
356 | + |
357 | + [[package]] |
358 | + name = "cpufeatures" |
359 | + version = "0.2.2" |
360 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
361 | + checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" |
362 | + dependencies = [ |
363 | + "libc", |
364 | + ] |
365 | + |
366 | + [[package]] |
367 | + name = "crypto-common" |
368 | + version = "0.1.3" |
369 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
370 | + checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" |
371 | + dependencies = [ |
372 | + "generic-array", |
373 | + "typenum", |
374 | + ] |
375 | + |
376 | + [[package]] |
377 | + name = "data-encoding" |
378 | + version = "2.3.2" |
379 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
380 | + checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" |
381 | + |
382 | + [[package]] |
383 | + name = "digest" |
384 | + version = "0.9.0" |
385 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
386 | + checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" |
387 | + dependencies = [ |
388 | + "generic-array", |
389 | + ] |
390 | + |
391 | + [[package]] |
392 | + name = "digest" |
393 | + version = "0.10.3" |
394 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
395 | + checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" |
396 | + dependencies = [ |
397 | + "block-buffer 0.10.2", |
398 | + "crypto-common", |
399 | + ] |
400 | + |
401 | + [[package]] |
402 | + name = "dirs" |
403 | + version = "4.0.0" |
404 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
405 | + checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" |
406 | + dependencies = [ |
407 | + "dirs-sys", |
408 | + ] |
409 | + |
410 | + [[package]] |
411 | + name = "dirs-sys" |
412 | + version = "0.3.7" |
413 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
414 | + checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" |
415 | + dependencies = [ |
416 | + "libc", |
417 | + "redox_users", |
418 | + "winapi", |
419 | + ] |
420 | + |
421 | + [[package]] |
422 | + name = "encoding" |
423 | + version = "0.2.33" |
424 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
425 | + checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" |
426 | + dependencies = [ |
427 | + "encoding-index-japanese", |
428 | + "encoding-index-korean", |
429 | + "encoding-index-simpchinese", |
430 | + "encoding-index-singlebyte", |
431 | + "encoding-index-tradchinese", |
432 | + ] |
433 | + |
434 | + [[package]] |
435 | + name = "encoding-index-japanese" |
436 | + version = "1.20141219.5" |
437 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
438 | + checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" |
439 | + dependencies = [ |
440 | + "encoding_index_tests", |
441 | + ] |
442 | + |
443 | + [[package]] |
444 | + name = "encoding-index-korean" |
445 | + version = "1.20141219.5" |
446 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
447 | + checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" |
448 | + dependencies = [ |
449 | + "encoding_index_tests", |
450 | + ] |
451 | + |
452 | + [[package]] |
453 | + name = "encoding-index-simpchinese" |
454 | + version = "1.20141219.5" |
455 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
456 | + checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" |
457 | + dependencies = [ |
458 | + "encoding_index_tests", |
459 | + ] |
460 | + |
461 | + [[package]] |
462 | + name = "encoding-index-singlebyte" |
463 | + version = "1.20141219.5" |
464 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
465 | + checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" |
466 | + dependencies = [ |
467 | + "encoding_index_tests", |
468 | + ] |
469 | + |
470 | + [[package]] |
471 | + name = "encoding-index-tradchinese" |
472 | + version = "1.20141219.5" |
473 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
474 | + checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" |
475 | + dependencies = [ |
476 | + "encoding_index_tests", |
477 | + ] |
478 | + |
479 | + [[package]] |
480 | + name = "encoding_index_tests" |
481 | + version = "0.1.4" |
482 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
483 | + checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" |
484 | + |
485 | + [[package]] |
486 | + name = "error-chain" |
487 | + version = "0.12.4" |
488 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
489 | + checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" |
490 | + dependencies = [ |
491 | + "backtrace", |
492 | + "version_check", |
493 | + ] |
494 | + |
495 | + [[package]] |
496 | + name = "event-listener" |
497 | + version = "2.5.2" |
498 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
499 | + checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" |
500 | + |
501 | + [[package]] |
502 | + name = "fallible-iterator" |
503 | + version = "0.2.0" |
504 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
505 | + checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" |
506 | + |
507 | + [[package]] |
508 | + name = "fallible-streaming-iterator" |
509 | + version = "0.1.9" |
510 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
511 | + checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" |
512 | + |
513 | + [[package]] |
514 | + name = "fastrand" |
515 | + version = "1.7.0" |
516 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
517 | + checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" |
518 | + dependencies = [ |
519 | + "instant", |
520 | + ] |
521 | + |
522 | + [[package]] |
523 | + name = "fnv" |
524 | + version = "1.0.7" |
525 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
526 | + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" |
527 | + |
528 | + [[package]] |
529 | + name = "foreign-types" |
530 | + version = "0.3.2" |
531 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
532 | + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" |
533 | + dependencies = [ |
534 | + "foreign-types-shared", |
535 | + ] |
536 | + |
537 | + [[package]] |
538 | + name = "foreign-types-shared" |
539 | + version = "0.1.1" |
540 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
541 | + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" |
542 | + |
543 | + [[package]] |
544 | + name = "form_urlencoded" |
545 | + version = "1.0.1" |
546 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
547 | + checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" |
548 | + dependencies = [ |
549 | + "matches", |
550 | + "percent-encoding", |
551 | + ] |
552 | + |
553 | + [[package]] |
554 | + name = "futures" |
555 | + version = "0.3.21" |
556 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
557 | + checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" |
558 | + dependencies = [ |
559 | + "futures-channel", |
560 | + "futures-core", |
561 | + "futures-executor", |
562 | + "futures-io", |
563 | + "futures-sink", |
564 | + "futures-task", |
565 | + "futures-util", |
566 | + ] |
567 | + |
568 | + [[package]] |
569 | + name = "futures-channel" |
570 | + version = "0.3.21" |
571 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
572 | + checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" |
573 | + dependencies = [ |
574 | + "futures-core", |
575 | + "futures-sink", |
576 | + ] |
577 | + |
578 | + [[package]] |
579 | + name = "futures-core" |
580 | + version = "0.3.21" |
581 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
582 | + checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" |
583 | + |
584 | + [[package]] |
585 | + name = "futures-executor" |
586 | + version = "0.3.21" |
587 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
588 | + checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" |
589 | + dependencies = [ |
590 | + "futures-core", |
591 | + "futures-task", |
592 | + "futures-util", |
593 | + ] |
594 | + |
595 | + [[package]] |
596 | + name = "futures-io" |
597 | + version = "0.3.21" |
598 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
599 | + checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" |
600 | + |
601 | + [[package]] |
602 | + name = "futures-lite" |
603 | + version = "1.12.0" |
604 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
605 | + checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" |
606 | + dependencies = [ |
607 | + "fastrand", |
608 | + "futures-core", |
609 | + "futures-io", |
610 | + "memchr", |
611 | + "parking", |
612 | + "pin-project-lite", |
613 | + "waker-fn", |
614 | + ] |
615 | + |
616 | + [[package]] |
617 | + name = "futures-macro" |
618 | + version = "0.3.21" |
619 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
620 | + checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" |
621 | + dependencies = [ |
622 | + "proc-macro2", |
623 | + "quote", |
624 | + "syn", |
625 | + ] |
626 | + |
627 | + [[package]] |
628 | + name = "futures-sink" |
629 | + version = "0.3.21" |
630 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
631 | + checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" |
632 | + |
633 | + [[package]] |
634 | + name = "futures-task" |
635 | + version = "0.3.21" |
636 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
637 | + checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" |
638 | + |
639 | + [[package]] |
640 | + name = "futures-util" |
641 | + version = "0.3.21" |
642 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
643 | + checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" |
644 | + dependencies = [ |
645 | + "futures-channel", |
646 | + "futures-core", |
647 | + "futures-io", |
648 | + "futures-macro", |
649 | + "futures-sink", |
650 | + "futures-task", |
651 | + "memchr", |
652 | + "pin-project-lite", |
653 | + "pin-utils", |
654 | + "slab", |
655 | + ] |
656 | + |
657 | + [[package]] |
658 | + name = "generic-array" |
659 | + version = "0.14.5" |
660 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
661 | + checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" |
662 | + dependencies = [ |
663 | + "typenum", |
664 | + "version_check", |
665 | + ] |
666 | + |
667 | + [[package]] |
668 | + name = "getrandom" |
669 | + version = "0.2.6" |
670 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
671 | + checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" |
672 | + dependencies = [ |
673 | + "cfg-if", |
674 | + "libc", |
675 | + "wasi 0.10.2+wasi-snapshot-preview1", |
676 | + ] |
677 | + |
678 | + [[package]] |
679 | + name = "gimli" |
680 | + version = "0.26.1" |
681 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
682 | + checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" |
683 | + |
684 | + [[package]] |
685 | + name = "h2" |
686 | + version = "0.3.13" |
687 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
688 | + checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" |
689 | + dependencies = [ |
690 | + "bytes", |
691 | + "fnv", |
692 | + "futures-core", |
693 | + "futures-sink", |
694 | + "futures-util", |
695 | + "http", |
696 | + "indexmap", |
697 | + "slab", |
698 | + "tokio", |
699 | + "tokio-util 0.7.1", |
700 | + "tracing", |
701 | + ] |
702 | + |
703 | + [[package]] |
704 | + name = "hashbrown" |
705 | + version = "0.11.2" |
706 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
707 | + checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" |
708 | + dependencies = [ |
709 | + "ahash", |
710 | + ] |
711 | + |
712 | + [[package]] |
713 | + name = "hashlink" |
714 | + version = "0.7.0" |
715 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
716 | + checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" |
717 | + dependencies = [ |
718 | + "hashbrown", |
719 | + ] |
720 | + |
721 | + [[package]] |
722 | + name = "headers" |
723 | + version = "0.3.7" |
724 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
725 | + checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" |
726 | + dependencies = [ |
727 | + "base64", |
728 | + "bitflags", |
729 | + "bytes", |
730 | + "headers-core", |
731 | + "http", |
732 | + "httpdate", |
733 | + "mime", |
734 | + "sha-1 0.10.0", |
735 | + ] |
736 | + |
737 | + [[package]] |
738 | + name = "headers-core" |
739 | + version = "0.2.0" |
740 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
741 | + checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" |
742 | + dependencies = [ |
743 | + "http", |
744 | + ] |
745 | + |
746 | + [[package]] |
747 | + name = "heck" |
748 | + version = "0.3.3" |
749 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
750 | + checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" |
751 | + dependencies = [ |
752 | + "unicode-segmentation", |
753 | + ] |
754 | + |
755 | + [[package]] |
756 | + name = "hermit-abi" |
757 | + version = "0.1.19" |
758 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
759 | + checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" |
760 | + dependencies = [ |
761 | + "libc", |
762 | + ] |
763 | + |
764 | + [[package]] |
765 | + name = "http" |
766 | + version = "0.2.7" |
767 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
768 | + checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" |
769 | + dependencies = [ |
770 | + "bytes", |
771 | + "fnv", |
772 | + "itoa", |
773 | + ] |
774 | + |
775 | + [[package]] |
776 | + name = "http-body" |
777 | + version = "0.4.4" |
778 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
779 | + checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" |
780 | + dependencies = [ |
781 | + "bytes", |
782 | + "http", |
783 | + "pin-project-lite", |
784 | + ] |
785 | + |
786 | + [[package]] |
787 | + name = "httparse" |
788 | + version = "1.7.1" |
789 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
790 | + checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" |
791 | + |
792 | + [[package]] |
793 | + name = "httpdate" |
794 | + version = "1.0.2" |
795 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
796 | + checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" |
797 | + |
798 | + [[package]] |
799 | + name = "hyper" |
800 | + version = "0.14.18" |
801 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
802 | + checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" |
803 | + dependencies = [ |
804 | + "bytes", |
805 | + "futures-channel", |
806 | + "futures-core", |
807 | + "futures-util", |
808 | + "h2", |
809 | + "http", |
810 | + "http-body", |
811 | + "httparse", |
812 | + "httpdate", |
813 | + "itoa", |
814 | + "pin-project-lite", |
815 | + "socket2", |
816 | + "tokio", |
817 | + "tower-service", |
818 | + "tracing", |
819 | + "want", |
820 | + ] |
821 | + |
822 | + [[package]] |
823 | + name = "idna" |
824 | + version = "0.2.3" |
825 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
826 | + checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" |
827 | + dependencies = [ |
828 | + "matches", |
829 | + "unicode-bidi", |
830 | + "unicode-normalization", |
831 | + ] |
832 | + |
833 | + [[package]] |
834 | + name = "indexmap" |
835 | + version = "1.8.1" |
836 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
837 | + checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" |
838 | + dependencies = [ |
839 | + "autocfg", |
840 | + "hashbrown", |
841 | + "serde", |
842 | + ] |
843 | + |
844 | + [[package]] |
845 | + name = "instant" |
846 | + version = "0.1.12" |
847 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
848 | + checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" |
849 | + dependencies = [ |
850 | + "cfg-if", |
851 | + ] |
852 | + |
853 | + [[package]] |
854 | + name = "itoa" |
855 | + version = "1.0.1" |
856 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
857 | + checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" |
858 | + |
859 | + [[package]] |
860 | + name = "lazy_static" |
861 | + version = "1.4.0" |
862 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
863 | + checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" |
864 | + |
865 | + [[package]] |
866 | + name = "libc" |
867 | + version = "0.2.125" |
868 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
869 | + checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" |
870 | + |
871 | + [[package]] |
872 | + name = "libloading" |
873 | + version = "0.7.3" |
874 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
875 | + checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" |
876 | + dependencies = [ |
877 | + "cfg-if", |
878 | + "winapi", |
879 | + ] |
880 | + |
881 | + [[package]] |
882 | + name = "libsqlite3-sys" |
883 | + version = "0.24.2" |
884 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
885 | + checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" |
886 | + dependencies = [ |
887 | + "pkg-config", |
888 | + "vcpkg", |
889 | + ] |
890 | + |
891 | + [[package]] |
892 | + name = "lock_api" |
893 | + version = "0.4.7" |
894 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
895 | + checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" |
896 | + dependencies = [ |
897 | + "autocfg", |
898 | + "scopeguard", |
899 | + ] |
900 | + |
901 | + [[package]] |
902 | + name = "log" |
903 | + version = "0.4.16" |
904 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
905 | + checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" |
906 | + dependencies = [ |
907 | + "cfg-if", |
908 | + ] |
909 | + |
910 | + [[package]] |
911 | + name = "mailpot" |
912 | + version = "0.1.0" |
913 | + dependencies = [ |
914 | + "chrono", |
915 | + "error-chain", |
916 | + "log", |
917 | + "melib", |
918 | + "rusqlite", |
919 | + "serde", |
920 | + "serde_json", |
921 | + "toml", |
922 | + "xdg", |
923 | + ] |
924 | + |
925 | + [[package]] |
926 | + name = "mailpot-cli" |
927 | + version = "0.1.0" |
928 | + dependencies = [ |
929 | + "mailpot", |
930 | + "stderrlog", |
931 | + "structopt", |
932 | + ] |
933 | + |
934 | + [[package]] |
935 | + name = "mailpot-http" |
936 | + version = "0.1.0" |
937 | + dependencies = [ |
938 | + "mailpot", |
939 | + "tokio", |
940 | + "warp", |
941 | + ] |
942 | + |
943 | + [[package]] |
944 | + name = "matches" |
945 | + version = "0.1.9" |
946 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
947 | + checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" |
948 | + |
949 | + [[package]] |
950 | + name = "melib" |
951 | + version = "0.7.2" |
952 | + source = "git+https://github.com/meli/meli?branch=master#721891c2955e9f5e223949bde2dd43604cec8390" |
953 | + dependencies = [ |
954 | + "async-stream", |
955 | + "base64", |
956 | + "bincode", |
957 | + "bitflags", |
958 | + "data-encoding", |
959 | + "encoding", |
960 | + "futures", |
961 | + "indexmap", |
962 | + "libc", |
963 | + "libloading", |
964 | + "native-tls", |
965 | + "nix", |
966 | + "nom", |
967 | + "serde", |
968 | + "serde_derive", |
969 | + "smallvec", |
970 | + "smol", |
971 | + "unicode-segmentation", |
972 | + "uuid", |
973 | + "xdg", |
974 | + "xdg-utils", |
975 | + ] |
976 | + |
977 | + [[package]] |
978 | + name = "memchr" |
979 | + version = "2.5.0" |
980 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
981 | + checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" |
982 | + |
983 | + [[package]] |
984 | + name = "memoffset" |
985 | + version = "0.6.5" |
986 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
987 | + checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" |
988 | + dependencies = [ |
989 | + "autocfg", |
990 | + ] |
991 | + |
992 | + [[package]] |
993 | + name = "mime" |
994 | + version = "0.3.16" |
995 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
996 | + checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" |
997 | + |
998 | + [[package]] |
999 | + name = "mime_guess" |
1000 | + version = "2.0.4" |
1001 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1002 | + checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" |
1003 | + dependencies = [ |
1004 | + "mime", |
1005 | + "unicase", |
1006 | + ] |
1007 | + |
1008 | + [[package]] |
1009 | + name = "minimal-lexical" |
1010 | + version = "0.2.1" |
1011 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1012 | + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" |
1013 | + |
1014 | + [[package]] |
1015 | + name = "miniz_oxide" |
1016 | + version = "0.5.1" |
1017 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1018 | + checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" |
1019 | + dependencies = [ |
1020 | + "adler", |
1021 | + ] |
1022 | + |
1023 | + [[package]] |
1024 | + name = "mio" |
1025 | + version = "0.8.2" |
1026 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1027 | + checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" |
1028 | + dependencies = [ |
1029 | + "libc", |
1030 | + "log", |
1031 | + "miow", |
1032 | + "ntapi", |
1033 | + "wasi 0.11.0+wasi-snapshot-preview1", |
1034 | + "winapi", |
1035 | + ] |
1036 | + |
1037 | + [[package]] |
1038 | + name = "miow" |
1039 | + version = "0.3.7" |
1040 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1041 | + checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" |
1042 | + dependencies = [ |
1043 | + "winapi", |
1044 | + ] |
1045 | + |
1046 | + [[package]] |
1047 | + name = "multipart" |
1048 | + version = "0.18.0" |
1049 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1050 | + checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" |
1051 | + dependencies = [ |
1052 | + "buf_redux", |
1053 | + "httparse", |
1054 | + "log", |
1055 | + "mime", |
1056 | + "mime_guess", |
1057 | + "quick-error", |
1058 | + "rand", |
1059 | + "safemem", |
1060 | + "tempfile", |
1061 | + "twoway", |
1062 | + ] |
1063 | + |
1064 | + [[package]] |
1065 | + name = "native-tls" |
1066 | + version = "0.2.10" |
1067 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1068 | + checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" |
1069 | + dependencies = [ |
1070 | + "lazy_static", |
1071 | + "libc", |
1072 | + "log", |
1073 | + "openssl", |
1074 | + "openssl-probe", |
1075 | + "openssl-sys", |
1076 | + "schannel", |
1077 | + "security-framework", |
1078 | + "security-framework-sys", |
1079 | + "tempfile", |
1080 | + ] |
1081 | + |
1082 | + [[package]] |
1083 | + name = "nix" |
1084 | + version = "0.24.1" |
1085 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1086 | + checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" |
1087 | + dependencies = [ |
1088 | + "bitflags", |
1089 | + "cfg-if", |
1090 | + "libc", |
1091 | + "memoffset", |
1092 | + ] |
1093 | + |
1094 | + [[package]] |
1095 | + name = "nom" |
1096 | + version = "7.1.1" |
1097 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1098 | + checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" |
1099 | + dependencies = [ |
1100 | + "memchr", |
1101 | + "minimal-lexical", |
1102 | + ] |
1103 | + |
1104 | + [[package]] |
1105 | + name = "ntapi" |
1106 | + version = "0.3.7" |
1107 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1108 | + checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" |
1109 | + dependencies = [ |
1110 | + "winapi", |
1111 | + ] |
1112 | + |
1113 | + [[package]] |
1114 | + name = "num-integer" |
1115 | + version = "0.1.45" |
1116 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1117 | + checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" |
1118 | + dependencies = [ |
1119 | + "autocfg", |
1120 | + "num-traits", |
1121 | + ] |
1122 | + |
1123 | + [[package]] |
1124 | + name = "num-traits" |
1125 | + version = "0.2.14" |
1126 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1127 | + checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" |
1128 | + dependencies = [ |
1129 | + "autocfg", |
1130 | + ] |
1131 | + |
1132 | + [[package]] |
1133 | + name = "num_cpus" |
1134 | + version = "1.13.1" |
1135 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1136 | + checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" |
1137 | + dependencies = [ |
1138 | + "hermit-abi", |
1139 | + "libc", |
1140 | + ] |
1141 | + |
1142 | + [[package]] |
1143 | + name = "object" |
1144 | + version = "0.28.3" |
1145 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1146 | + checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" |
1147 | + dependencies = [ |
1148 | + "memchr", |
1149 | + ] |
1150 | + |
1151 | + [[package]] |
1152 | + name = "once_cell" |
1153 | + version = "1.10.0" |
1154 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1155 | + checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" |
1156 | + |
1157 | + [[package]] |
1158 | + name = "opaque-debug" |
1159 | + version = "0.3.0" |
1160 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1161 | + checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" |
1162 | + |
1163 | + [[package]] |
1164 | + name = "openssl" |
1165 | + version = "0.10.38" |
1166 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1167 | + checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" |
1168 | + dependencies = [ |
1169 | + "bitflags", |
1170 | + "cfg-if", |
1171 | + "foreign-types", |
1172 | + "libc", |
1173 | + "once_cell", |
1174 | + "openssl-sys", |
1175 | + ] |
1176 | + |
1177 | + [[package]] |
1178 | + name = "openssl-probe" |
1179 | + version = "0.1.5" |
1180 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1181 | + checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" |
1182 | + |
1183 | + [[package]] |
1184 | + name = "openssl-sys" |
1185 | + version = "0.9.72" |
1186 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1187 | + checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" |
1188 | + dependencies = [ |
1189 | + "autocfg", |
1190 | + "cc", |
1191 | + "libc", |
1192 | + "pkg-config", |
1193 | + "vcpkg", |
1194 | + ] |
1195 | + |
1196 | + [[package]] |
1197 | + name = "parking" |
1198 | + version = "2.0.0" |
1199 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1200 | + checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" |
1201 | + |
1202 | + [[package]] |
1203 | + name = "parking_lot" |
1204 | + version = "0.12.0" |
1205 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1206 | + checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" |
1207 | + dependencies = [ |
1208 | + "lock_api", |
1209 | + "parking_lot_core", |
1210 | + ] |
1211 | + |
1212 | + [[package]] |
1213 | + name = "parking_lot_core" |
1214 | + version = "0.9.3" |
1215 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1216 | + checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" |
1217 | + dependencies = [ |
1218 | + "cfg-if", |
1219 | + "libc", |
1220 | + "redox_syscall", |
1221 | + "smallvec", |
1222 | + "windows-sys", |
1223 | + ] |
1224 | + |
1225 | + [[package]] |
1226 | + name = "percent-encoding" |
1227 | + version = "2.1.0" |
1228 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1229 | + checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" |
1230 | + |
1231 | + [[package]] |
1232 | + name = "pin-project" |
1233 | + version = "1.0.10" |
1234 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1235 | + checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" |
1236 | + dependencies = [ |
1237 | + "pin-project-internal", |
1238 | + ] |
1239 | + |
1240 | + [[package]] |
1241 | + name = "pin-project-internal" |
1242 | + version = "1.0.10" |
1243 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1244 | + checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" |
1245 | + dependencies = [ |
1246 | + "proc-macro2", |
1247 | + "quote", |
1248 | + "syn", |
1249 | + ] |
1250 | + |
1251 | + [[package]] |
1252 | + name = "pin-project-lite" |
1253 | + version = "0.2.9" |
1254 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1255 | + checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" |
1256 | + |
1257 | + [[package]] |
1258 | + name = "pin-utils" |
1259 | + version = "0.1.0" |
1260 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1261 | + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" |
1262 | + |
1263 | + [[package]] |
1264 | + name = "pkg-config" |
1265 | + version = "0.3.25" |
1266 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1267 | + checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" |
1268 | + |
1269 | + [[package]] |
1270 | + name = "polling" |
1271 | + version = "2.2.0" |
1272 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1273 | + checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" |
1274 | + dependencies = [ |
1275 | + "cfg-if", |
1276 | + "libc", |
1277 | + "log", |
1278 | + "wepoll-ffi", |
1279 | + "winapi", |
1280 | + ] |
1281 | + |
1282 | + [[package]] |
1283 | + name = "ppv-lite86" |
1284 | + version = "0.2.16" |
1285 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1286 | + checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" |
1287 | + |
1288 | + [[package]] |
1289 | + name = "proc-macro-error" |
1290 | + version = "1.0.4" |
1291 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1292 | + checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" |
1293 | + dependencies = [ |
1294 | + "proc-macro-error-attr", |
1295 | + "proc-macro2", |
1296 | + "quote", |
1297 | + "syn", |
1298 | + "version_check", |
1299 | + ] |
1300 | + |
1301 | + [[package]] |
1302 | + name = "proc-macro-error-attr" |
1303 | + version = "1.0.4" |
1304 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1305 | + checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" |
1306 | + dependencies = [ |
1307 | + "proc-macro2", |
1308 | + "quote", |
1309 | + "version_check", |
1310 | + ] |
1311 | + |
1312 | + [[package]] |
1313 | + name = "proc-macro2" |
1314 | + version = "1.0.37" |
1315 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1316 | + checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" |
1317 | + dependencies = [ |
1318 | + "unicode-xid", |
1319 | + ] |
1320 | + |
1321 | + [[package]] |
1322 | + name = "quick-error" |
1323 | + version = "1.2.3" |
1324 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1325 | + checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" |
1326 | + |
1327 | + [[package]] |
1328 | + name = "quote" |
1329 | + version = "1.0.18" |
1330 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1331 | + checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" |
1332 | + dependencies = [ |
1333 | + "proc-macro2", |
1334 | + ] |
1335 | + |
1336 | + [[package]] |
1337 | + name = "rand" |
1338 | + version = "0.8.5" |
1339 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1340 | + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" |
1341 | + dependencies = [ |
1342 | + "libc", |
1343 | + "rand_chacha", |
1344 | + "rand_core", |
1345 | + ] |
1346 | + |
1347 | + [[package]] |
1348 | + name = "rand_chacha" |
1349 | + version = "0.3.1" |
1350 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1351 | + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
1352 | + dependencies = [ |
1353 | + "ppv-lite86", |
1354 | + "rand_core", |
1355 | + ] |
1356 | + |
1357 | + [[package]] |
1358 | + name = "rand_core" |
1359 | + version = "0.6.3" |
1360 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1361 | + checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" |
1362 | + dependencies = [ |
1363 | + "getrandom", |
1364 | + ] |
1365 | + |
1366 | + [[package]] |
1367 | + name = "redox_syscall" |
1368 | + version = "0.2.13" |
1369 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1370 | + checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" |
1371 | + dependencies = [ |
1372 | + "bitflags", |
1373 | + ] |
1374 | + |
1375 | + [[package]] |
1376 | + name = "redox_users" |
1377 | + version = "0.4.3" |
1378 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1379 | + checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" |
1380 | + dependencies = [ |
1381 | + "getrandom", |
1382 | + "redox_syscall", |
1383 | + "thiserror", |
1384 | + ] |
1385 | + |
1386 | + [[package]] |
1387 | + name = "remove_dir_all" |
1388 | + version = "0.5.3" |
1389 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1390 | + checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" |
1391 | + dependencies = [ |
1392 | + "winapi", |
1393 | + ] |
1394 | + |
1395 | + [[package]] |
1396 | + name = "rusqlite" |
1397 | + version = "0.27.0" |
1398 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1399 | + checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" |
1400 | + dependencies = [ |
1401 | + "bitflags", |
1402 | + "fallible-iterator", |
1403 | + "fallible-streaming-iterator", |
1404 | + "hashlink", |
1405 | + "libsqlite3-sys", |
1406 | + "memchr", |
1407 | + "smallvec", |
1408 | + ] |
1409 | + |
1410 | + [[package]] |
1411 | + name = "rustc-demangle" |
1412 | + version = "0.1.21" |
1413 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1414 | + checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" |
1415 | + |
1416 | + [[package]] |
1417 | + name = "ryu" |
1418 | + version = "1.0.9" |
1419 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1420 | + checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" |
1421 | + |
1422 | + [[package]] |
1423 | + name = "safemem" |
1424 | + version = "0.3.3" |
1425 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1426 | + checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" |
1427 | + |
1428 | + [[package]] |
1429 | + name = "schannel" |
1430 | + version = "0.1.19" |
1431 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1432 | + checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" |
1433 | + dependencies = [ |
1434 | + "lazy_static", |
1435 | + "winapi", |
1436 | + ] |
1437 | + |
1438 | + [[package]] |
1439 | + name = "scoped-tls" |
1440 | + version = "1.0.0" |
1441 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1442 | + checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" |
1443 | + |
1444 | + [[package]] |
1445 | + name = "scopeguard" |
1446 | + version = "1.1.0" |
1447 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1448 | + checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" |
1449 | + |
1450 | + [[package]] |
1451 | + name = "security-framework" |
1452 | + version = "2.6.1" |
1453 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1454 | + checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" |
1455 | + dependencies = [ |
1456 | + "bitflags", |
1457 | + "core-foundation", |
1458 | + "core-foundation-sys", |
1459 | + "libc", |
1460 | + "security-framework-sys", |
1461 | + ] |
1462 | + |
1463 | + [[package]] |
1464 | + name = "security-framework-sys" |
1465 | + version = "2.6.1" |
1466 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1467 | + checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" |
1468 | + dependencies = [ |
1469 | + "core-foundation-sys", |
1470 | + "libc", |
1471 | + ] |
1472 | + |
1473 | + [[package]] |
1474 | + name = "serde" |
1475 | + version = "1.0.137" |
1476 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1477 | + checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" |
1478 | + dependencies = [ |
1479 | + "serde_derive", |
1480 | + ] |
1481 | + |
1482 | + [[package]] |
1483 | + name = "serde_derive" |
1484 | + version = "1.0.137" |
1485 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1486 | + checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" |
1487 | + dependencies = [ |
1488 | + "proc-macro2", |
1489 | + "quote", |
1490 | + "syn", |
1491 | + ] |
1492 | + |
1493 | + [[package]] |
1494 | + name = "serde_json" |
1495 | + version = "1.0.80" |
1496 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1497 | + checksum = "f972498cf015f7c0746cac89ebe1d6ef10c293b94175a243a2d9442c163d9944" |
1498 | + dependencies = [ |
1499 | + "itoa", |
1500 | + "ryu", |
1501 | + "serde", |
1502 | + ] |
1503 | + |
1504 | + [[package]] |
1505 | + name = "serde_urlencoded" |
1506 | + version = "0.7.1" |
1507 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1508 | + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" |
1509 | + dependencies = [ |
1510 | + "form_urlencoded", |
1511 | + "itoa", |
1512 | + "ryu", |
1513 | + "serde", |
1514 | + ] |
1515 | + |
1516 | + [[package]] |
1517 | + name = "sha-1" |
1518 | + version = "0.9.8" |
1519 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1520 | + checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" |
1521 | + dependencies = [ |
1522 | + "block-buffer 0.9.0", |
1523 | + "cfg-if", |
1524 | + "cpufeatures", |
1525 | + "digest 0.9.0", |
1526 | + "opaque-debug", |
1527 | + ] |
1528 | + |
1529 | + [[package]] |
1530 | + name = "sha-1" |
1531 | + version = "0.10.0" |
1532 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1533 | + checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" |
1534 | + dependencies = [ |
1535 | + "cfg-if", |
1536 | + "cpufeatures", |
1537 | + "digest 0.10.3", |
1538 | + ] |
1539 | + |
1540 | + [[package]] |
1541 | + name = "sha1_smol" |
1542 | + version = "1.0.0" |
1543 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1544 | + checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" |
1545 | + |
1546 | + [[package]] |
1547 | + name = "signal-hook" |
1548 | + version = "0.3.13" |
1549 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1550 | + checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" |
1551 | + dependencies = [ |
1552 | + "libc", |
1553 | + "signal-hook-registry", |
1554 | + ] |
1555 | + |
1556 | + [[package]] |
1557 | + name = "signal-hook-registry" |
1558 | + version = "1.4.0" |
1559 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1560 | + checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" |
1561 | + dependencies = [ |
1562 | + "libc", |
1563 | + ] |
1564 | + |
1565 | + [[package]] |
1566 | + name = "slab" |
1567 | + version = "0.4.6" |
1568 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1569 | + checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" |
1570 | + |
1571 | + [[package]] |
1572 | + name = "smallvec" |
1573 | + version = "1.8.0" |
1574 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1575 | + checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" |
1576 | + dependencies = [ |
1577 | + "serde", |
1578 | + ] |
1579 | + |
1580 | + [[package]] |
1581 | + name = "smol" |
1582 | + version = "1.2.5" |
1583 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1584 | + checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" |
1585 | + dependencies = [ |
1586 | + "async-channel", |
1587 | + "async-executor", |
1588 | + "async-fs", |
1589 | + "async-io", |
1590 | + "async-lock", |
1591 | + "async-net", |
1592 | + "async-process", |
1593 | + "blocking", |
1594 | + "futures-lite", |
1595 | + "once_cell", |
1596 | + ] |
1597 | + |
1598 | + [[package]] |
1599 | + name = "socket2" |
1600 | + version = "0.4.4" |
1601 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1602 | + checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" |
1603 | + dependencies = [ |
1604 | + "libc", |
1605 | + "winapi", |
1606 | + ] |
1607 | + |
1608 | + [[package]] |
1609 | + name = "stderrlog" |
1610 | + version = "0.5.1" |
1611 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1612 | + checksum = "45a53e2eff3e94a019afa6265e8ee04cb05b9d33fe9f5078b14e4e391d155a38" |
1613 | + dependencies = [ |
1614 | + "atty", |
1615 | + "chrono", |
1616 | + "log", |
1617 | + "termcolor", |
1618 | + "thread_local", |
1619 | + ] |
1620 | + |
1621 | + [[package]] |
1622 | + name = "strsim" |
1623 | + version = "0.8.0" |
1624 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1625 | + checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" |
1626 | + |
1627 | + [[package]] |
1628 | + name = "structopt" |
1629 | + version = "0.3.26" |
1630 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1631 | + checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" |
1632 | + dependencies = [ |
1633 | + "clap", |
1634 | + "lazy_static", |
1635 | + "structopt-derive", |
1636 | + ] |
1637 | + |
1638 | + [[package]] |
1639 | + name = "structopt-derive" |
1640 | + version = "0.4.18" |
1641 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1642 | + checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" |
1643 | + dependencies = [ |
1644 | + "heck", |
1645 | + "proc-macro-error", |
1646 | + "proc-macro2", |
1647 | + "quote", |
1648 | + "syn", |
1649 | + ] |
1650 | + |
1651 | + [[package]] |
1652 | + name = "syn" |
1653 | + version = "1.0.92" |
1654 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1655 | + checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" |
1656 | + dependencies = [ |
1657 | + "proc-macro2", |
1658 | + "quote", |
1659 | + "unicode-xid", |
1660 | + ] |
1661 | + |
1662 | + [[package]] |
1663 | + name = "tempfile" |
1664 | + version = "3.3.0" |
1665 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1666 | + checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" |
1667 | + dependencies = [ |
1668 | + "cfg-if", |
1669 | + "fastrand", |
1670 | + "libc", |
1671 | + "redox_syscall", |
1672 | + "remove_dir_all", |
1673 | + "winapi", |
1674 | + ] |
1675 | + |
1676 | + [[package]] |
1677 | + name = "termcolor" |
1678 | + version = "1.1.3" |
1679 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1680 | + checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" |
1681 | + dependencies = [ |
1682 | + "winapi-util", |
1683 | + ] |
1684 | + |
1685 | + [[package]] |
1686 | + name = "textwrap" |
1687 | + version = "0.11.0" |
1688 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1689 | + checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" |
1690 | + dependencies = [ |
1691 | + "unicode-width", |
1692 | + ] |
1693 | + |
1694 | + [[package]] |
1695 | + name = "thiserror" |
1696 | + version = "1.0.31" |
1697 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1698 | + checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" |
1699 | + dependencies = [ |
1700 | + "thiserror-impl", |
1701 | + ] |
1702 | + |
1703 | + [[package]] |
1704 | + name = "thiserror-impl" |
1705 | + version = "1.0.31" |
1706 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1707 | + checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" |
1708 | + dependencies = [ |
1709 | + "proc-macro2", |
1710 | + "quote", |
1711 | + "syn", |
1712 | + ] |
1713 | + |
1714 | + [[package]] |
1715 | + name = "thread_local" |
1716 | + version = "1.0.1" |
1717 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1718 | + checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" |
1719 | + dependencies = [ |
1720 | + "lazy_static", |
1721 | + ] |
1722 | + |
1723 | + [[package]] |
1724 | + name = "time" |
1725 | + version = "0.1.43" |
1726 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1727 | + checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" |
1728 | + dependencies = [ |
1729 | + "libc", |
1730 | + "winapi", |
1731 | + ] |
1732 | + |
1733 | + [[package]] |
1734 | + name = "tinyvec" |
1735 | + version = "1.6.0" |
1736 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1737 | + checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" |
1738 | + dependencies = [ |
1739 | + "tinyvec_macros", |
1740 | + ] |
1741 | + |
1742 | + [[package]] |
1743 | + name = "tinyvec_macros" |
1744 | + version = "0.1.0" |
1745 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1746 | + checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" |
1747 | + |
1748 | + [[package]] |
1749 | + name = "tokio" |
1750 | + version = "1.18.1" |
1751 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1752 | + checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc" |
1753 | + dependencies = [ |
1754 | + "bytes", |
1755 | + "libc", |
1756 | + "memchr", |
1757 | + "mio", |
1758 | + "num_cpus", |
1759 | + "once_cell", |
1760 | + "parking_lot", |
1761 | + "pin-project-lite", |
1762 | + "signal-hook-registry", |
1763 | + "socket2", |
1764 | + "tokio-macros", |
1765 | + "winapi", |
1766 | + ] |
1767 | + |
1768 | + [[package]] |
1769 | + name = "tokio-macros" |
1770 | + version = "1.7.0" |
1771 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1772 | + checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" |
1773 | + dependencies = [ |
1774 | + "proc-macro2", |
1775 | + "quote", |
1776 | + "syn", |
1777 | + ] |
1778 | + |
1779 | + [[package]] |
1780 | + name = "tokio-stream" |
1781 | + version = "0.1.8" |
1782 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1783 | + checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" |
1784 | + dependencies = [ |
1785 | + "futures-core", |
1786 | + "pin-project-lite", |
1787 | + "tokio", |
1788 | + ] |
1789 | + |
1790 | + [[package]] |
1791 | + name = "tokio-tungstenite" |
1792 | + version = "0.15.0" |
1793 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1794 | + checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" |
1795 | + dependencies = [ |
1796 | + "futures-util", |
1797 | + "log", |
1798 | + "pin-project", |
1799 | + "tokio", |
1800 | + "tungstenite", |
1801 | + ] |
1802 | + |
1803 | + [[package]] |
1804 | + name = "tokio-util" |
1805 | + version = "0.6.9" |
1806 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1807 | + checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" |
1808 | + dependencies = [ |
1809 | + "bytes", |
1810 | + "futures-core", |
1811 | + "futures-sink", |
1812 | + "log", |
1813 | + "pin-project-lite", |
1814 | + "tokio", |
1815 | + ] |
1816 | + |
1817 | + [[package]] |
1818 | + name = "tokio-util" |
1819 | + version = "0.7.1" |
1820 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1821 | + checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" |
1822 | + dependencies = [ |
1823 | + "bytes", |
1824 | + "futures-core", |
1825 | + "futures-sink", |
1826 | + "pin-project-lite", |
1827 | + "tokio", |
1828 | + "tracing", |
1829 | + ] |
1830 | + |
1831 | + [[package]] |
1832 | + name = "toml" |
1833 | + version = "0.5.9" |
1834 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1835 | + checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" |
1836 | + dependencies = [ |
1837 | + "serde", |
1838 | + ] |
1839 | + |
1840 | + [[package]] |
1841 | + name = "tower-service" |
1842 | + version = "0.3.1" |
1843 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1844 | + checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" |
1845 | + |
1846 | + [[package]] |
1847 | + name = "tracing" |
1848 | + version = "0.1.34" |
1849 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1850 | + checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" |
1851 | + dependencies = [ |
1852 | + "cfg-if", |
1853 | + "log", |
1854 | + "pin-project-lite", |
1855 | + "tracing-attributes", |
1856 | + "tracing-core", |
1857 | + ] |
1858 | + |
1859 | + [[package]] |
1860 | + name = "tracing-attributes" |
1861 | + version = "0.1.21" |
1862 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1863 | + checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" |
1864 | + dependencies = [ |
1865 | + "proc-macro2", |
1866 | + "quote", |
1867 | + "syn", |
1868 | + ] |
1869 | + |
1870 | + [[package]] |
1871 | + name = "tracing-core" |
1872 | + version = "0.1.26" |
1873 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1874 | + checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" |
1875 | + dependencies = [ |
1876 | + "lazy_static", |
1877 | + ] |
1878 | + |
1879 | + [[package]] |
1880 | + name = "try-lock" |
1881 | + version = "0.2.3" |
1882 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1883 | + checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" |
1884 | + |
1885 | + [[package]] |
1886 | + name = "tungstenite" |
1887 | + version = "0.14.0" |
1888 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1889 | + checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" |
1890 | + dependencies = [ |
1891 | + "base64", |
1892 | + "byteorder", |
1893 | + "bytes", |
1894 | + "http", |
1895 | + "httparse", |
1896 | + "log", |
1897 | + "rand", |
1898 | + "sha-1 0.9.8", |
1899 | + "thiserror", |
1900 | + "url", |
1901 | + "utf-8", |
1902 | + ] |
1903 | + |
1904 | + [[package]] |
1905 | + name = "twoway" |
1906 | + version = "0.1.8" |
1907 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1908 | + checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" |
1909 | + dependencies = [ |
1910 | + "memchr", |
1911 | + ] |
1912 | + |
1913 | + [[package]] |
1914 | + name = "typenum" |
1915 | + version = "1.15.0" |
1916 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1917 | + checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" |
1918 | + |
1919 | + [[package]] |
1920 | + name = "unicase" |
1921 | + version = "2.6.0" |
1922 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1923 | + checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" |
1924 | + dependencies = [ |
1925 | + "version_check", |
1926 | + ] |
1927 | + |
1928 | + [[package]] |
1929 | + name = "unicode-bidi" |
1930 | + version = "0.3.8" |
1931 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1932 | + checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" |
1933 | + |
1934 | + [[package]] |
1935 | + name = "unicode-normalization" |
1936 | + version = "0.1.19" |
1937 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1938 | + checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" |
1939 | + dependencies = [ |
1940 | + "tinyvec", |
1941 | + ] |
1942 | + |
1943 | + [[package]] |
1944 | + name = "unicode-segmentation" |
1945 | + version = "1.9.0" |
1946 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1947 | + checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" |
1948 | + |
1949 | + [[package]] |
1950 | + name = "unicode-width" |
1951 | + version = "0.1.9" |
1952 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1953 | + checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" |
1954 | + |
1955 | + [[package]] |
1956 | + name = "unicode-xid" |
1957 | + version = "0.2.3" |
1958 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1959 | + checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" |
1960 | + |
1961 | + [[package]] |
1962 | + name = "url" |
1963 | + version = "2.2.2" |
1964 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1965 | + checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" |
1966 | + dependencies = [ |
1967 | + "form_urlencoded", |
1968 | + "idna", |
1969 | + "matches", |
1970 | + "percent-encoding", |
1971 | + ] |
1972 | + |
1973 | + [[package]] |
1974 | + name = "utf-8" |
1975 | + version = "0.7.6" |
1976 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1977 | + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" |
1978 | + |
1979 | + [[package]] |
1980 | + name = "uuid" |
1981 | + version = "1.0.0" |
1982 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1983 | + checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0" |
1984 | + dependencies = [ |
1985 | + "getrandom", |
1986 | + "serde", |
1987 | + "sha1_smol", |
1988 | + ] |
1989 | + |
1990 | + [[package]] |
1991 | + name = "vcpkg" |
1992 | + version = "0.2.15" |
1993 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
1994 | + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" |
1995 | + |
1996 | + [[package]] |
1997 | + name = "vec_map" |
1998 | + version = "0.8.2" |
1999 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2000 | + checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" |
2001 | + |
2002 | + [[package]] |
2003 | + name = "version_check" |
2004 | + version = "0.9.4" |
2005 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2006 | + checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" |
2007 | + |
2008 | + [[package]] |
2009 | + name = "waker-fn" |
2010 | + version = "1.1.0" |
2011 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2012 | + checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" |
2013 | + |
2014 | + [[package]] |
2015 | + name = "want" |
2016 | + version = "0.3.0" |
2017 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2018 | + checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" |
2019 | + dependencies = [ |
2020 | + "log", |
2021 | + "try-lock", |
2022 | + ] |
2023 | + |
2024 | + [[package]] |
2025 | + name = "warp" |
2026 | + version = "0.3.2" |
2027 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2028 | + checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" |
2029 | + dependencies = [ |
2030 | + "bytes", |
2031 | + "futures-channel", |
2032 | + "futures-util", |
2033 | + "headers", |
2034 | + "http", |
2035 | + "hyper", |
2036 | + "log", |
2037 | + "mime", |
2038 | + "mime_guess", |
2039 | + "multipart", |
2040 | + "percent-encoding", |
2041 | + "pin-project", |
2042 | + "scoped-tls", |
2043 | + "serde", |
2044 | + "serde_json", |
2045 | + "serde_urlencoded", |
2046 | + "tokio", |
2047 | + "tokio-stream", |
2048 | + "tokio-tungstenite", |
2049 | + "tokio-util 0.6.9", |
2050 | + "tower-service", |
2051 | + "tracing", |
2052 | + ] |
2053 | + |
2054 | + [[package]] |
2055 | + name = "wasi" |
2056 | + version = "0.10.2+wasi-snapshot-preview1" |
2057 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2058 | + checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" |
2059 | + |
2060 | + [[package]] |
2061 | + name = "wasi" |
2062 | + version = "0.11.0+wasi-snapshot-preview1" |
2063 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2064 | + checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" |
2065 | + |
2066 | + [[package]] |
2067 | + name = "wepoll-ffi" |
2068 | + version = "0.1.2" |
2069 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2070 | + checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" |
2071 | + dependencies = [ |
2072 | + "cc", |
2073 | + ] |
2074 | + |
2075 | + [[package]] |
2076 | + name = "winapi" |
2077 | + version = "0.3.9" |
2078 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2079 | + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" |
2080 | + dependencies = [ |
2081 | + "winapi-i686-pc-windows-gnu", |
2082 | + "winapi-x86_64-pc-windows-gnu", |
2083 | + ] |
2084 | + |
2085 | + [[package]] |
2086 | + name = "winapi-i686-pc-windows-gnu" |
2087 | + version = "0.4.0" |
2088 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2089 | + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" |
2090 | + |
2091 | + [[package]] |
2092 | + name = "winapi-util" |
2093 | + version = "0.1.5" |
2094 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2095 | + checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" |
2096 | + dependencies = [ |
2097 | + "winapi", |
2098 | + ] |
2099 | + |
2100 | + [[package]] |
2101 | + name = "winapi-x86_64-pc-windows-gnu" |
2102 | + version = "0.4.0" |
2103 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2104 | + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
2105 | + |
2106 | + [[package]] |
2107 | + name = "windows-sys" |
2108 | + version = "0.36.1" |
2109 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2110 | + checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" |
2111 | + dependencies = [ |
2112 | + "windows_aarch64_msvc", |
2113 | + "windows_i686_gnu", |
2114 | + "windows_i686_msvc", |
2115 | + "windows_x86_64_gnu", |
2116 | + "windows_x86_64_msvc", |
2117 | + ] |
2118 | + |
2119 | + [[package]] |
2120 | + name = "windows_aarch64_msvc" |
2121 | + version = "0.36.1" |
2122 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2123 | + checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" |
2124 | + |
2125 | + [[package]] |
2126 | + name = "windows_i686_gnu" |
2127 | + version = "0.36.1" |
2128 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2129 | + checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" |
2130 | + |
2131 | + [[package]] |
2132 | + name = "windows_i686_msvc" |
2133 | + version = "0.36.1" |
2134 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2135 | + checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" |
2136 | + |
2137 | + [[package]] |
2138 | + name = "windows_x86_64_gnu" |
2139 | + version = "0.36.1" |
2140 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2141 | + checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" |
2142 | + |
2143 | + [[package]] |
2144 | + name = "windows_x86_64_msvc" |
2145 | + version = "0.36.1" |
2146 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2147 | + checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" |
2148 | + |
2149 | + [[package]] |
2150 | + name = "xdg" |
2151 | + version = "2.4.1" |
2152 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2153 | + checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" |
2154 | + dependencies = [ |
2155 | + "dirs", |
2156 | + ] |
2157 | + |
2158 | + [[package]] |
2159 | + name = "xdg-utils" |
2160 | + version = "0.4.0" |
2161 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
2162 | + checksum = "db9fefe62d5969721e2cfc529e6a760901cc0da422b6d67e7bfd18e69490dba6" |
2163 | diff --git a/Cargo.toml b/Cargo.toml |
2164 | new file mode 100644 |
2165 | index 0000000..fd216ce |
2166 | --- /dev/null |
2167 | +++ b/Cargo.toml |
2168 | @@ -0,0 +1,6 @@ |
2169 | + [workspace] |
2170 | + members = [ |
2171 | + "core", |
2172 | + "cli", |
2173 | + "rest-http", |
2174 | + ] |
2175 | diff --git a/LICENSE b/LICENSE |
2176 | new file mode 100644 |
2177 | index 0000000..0ad25db |
2178 | --- /dev/null |
2179 | +++ b/LICENSE |
2180 | @@ -0,0 +1,661 @@ |
2181 | + GNU AFFERO GENERAL PUBLIC LICENSE |
2182 | + Version 3, 19 November 2007 |
2183 | + |
2184 | + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |
2185 | + Everyone is permitted to copy and distribute verbatim copies |
2186 | + of this license document, but changing it is not allowed. |
2187 | + |
2188 | + Preamble |
2189 | + |
2190 | + The GNU Affero General Public License is a free, copyleft license for |
2191 | + software and other kinds of works, specifically designed to ensure |
2192 | + cooperation with the community in the case of network server software. |
2193 | + |
2194 | + The licenses for most software and other practical works are designed |
2195 | + to take away your freedom to share and change the works. By contrast, |
2196 | + our General Public Licenses are intended to guarantee your freedom to |
2197 | + share and change all versions of a program--to make sure it remains free |
2198 | + software for all its users. |
2199 | + |
2200 | + When we speak of free software, we are referring to freedom, not |
2201 | + price. Our General Public Licenses are designed to make sure that you |
2202 | + have the freedom to distribute copies of free software (and charge for |
2203 | + them if you wish), that you receive source code or can get it if you |
2204 | + want it, that you can change the software or use pieces of it in new |
2205 | + free programs, and that you know you can do these things. |
2206 | + |
2207 | + Developers that use our General Public Licenses protect your rights |
2208 | + with two steps: (1) assert copyright on the software, and (2) offer |
2209 | + you this License which gives you legal permission to copy, distribute |
2210 | + and/or modify the software. |
2211 | + |
2212 | + A secondary benefit of defending all users' freedom is that |
2213 | + improvements made in alternate versions of the program, if they |
2214 | + receive widespread use, become available for other developers to |
2215 | + incorporate. Many developers of free software are heartened and |
2216 | + encouraged by the resulting cooperation. However, in the case of |
2217 | + software used on network servers, this result may fail to come about. |
2218 | + The GNU General Public License permits making a modified version and |
2219 | + letting the public access it on a server without ever releasing its |
2220 | + source code to the public. |
2221 | + |
2222 | + The GNU Affero General Public License is designed specifically to |
2223 | + ensure that, in such cases, the modified source code becomes available |
2224 | + to the community. It requires the operator of a network server to |
2225 | + provide the source code of the modified version running there to the |
2226 | + users of that server. Therefore, public use of a modified version, on |
2227 | + a publicly accessible server, gives the public access to the source |
2228 | + code of the modified version. |
2229 | + |
2230 | + An older license, called the Affero General Public License and |
2231 | + published by Affero, was designed to accomplish similar goals. This is |
2232 | + a different license, not a version of the Affero GPL, but Affero has |
2233 | + released a new version of the Affero GPL which permits relicensing under |
2234 | + this license. |
2235 | + |
2236 | + The precise terms and conditions for copying, distribution and |
2237 | + modification follow. |
2238 | + |
2239 | + TERMS AND CONDITIONS |
2240 | + |
2241 | + 0. Definitions. |
2242 | + |
2243 | + "This License" refers to version 3 of the GNU Affero General Public License. |
2244 | + |
2245 | + "Copyright" also means copyright-like laws that apply to other kinds of |
2246 | + works, such as semiconductor masks. |
2247 | + |
2248 | + "The Program" refers to any copyrightable work licensed under this |
2249 | + License. Each licensee is addressed as "you". "Licensees" and |
2250 | + "recipients" may be individuals or organizations. |
2251 | + |
2252 | + To "modify" a work means to copy from or adapt all or part of the work |
2253 | + in a fashion requiring copyright permission, other than the making of an |
2254 | + exact copy. The resulting work is called a "modified version" of the |
2255 | + earlier work or a work "based on" the earlier work. |
2256 | + |
2257 | + A "covered work" means either the unmodified Program or a work based |
2258 | + on the Program. |
2259 | + |
2260 | + To "propagate" a work means to do anything with it that, without |
2261 | + permission, would make you directly or secondarily liable for |
2262 | + infringement under applicable copyright law, except executing it on a |
2263 | + computer or modifying a private copy. Propagation includes copying, |
2264 | + distribution (with or without modification), making available to the |
2265 | + public, and in some countries other activities as well. |
2266 | + |
2267 | + To "convey" a work means any kind of propagation that enables other |
2268 | + parties to make or receive copies. Mere interaction with a user through |
2269 | + a computer network, with no transfer of a copy, is not conveying. |
2270 | + |
2271 | + An interactive user interface displays "Appropriate Legal Notices" |
2272 | + to the extent that it includes a convenient and prominently visible |
2273 | + feature that (1) displays an appropriate copyright notice, and (2) |
2274 | + tells the user that there is no warranty for the work (except to the |
2275 | + extent that warranties are provided), that licensees may convey the |
2276 | + work under this License, and how to view a copy of this License. If |
2277 | + the interface presents a list of user commands or options, such as a |
2278 | + menu, a prominent item in the list meets this criterion. |
2279 | + |
2280 | + 1. Source Code. |
2281 | + |
2282 | + The "source code" for a work means the preferred form of the work |
2283 | + for making modifications to it. "Object code" means any non-source |
2284 | + form of a work. |
2285 | + |
2286 | + A "Standard Interface" means an interface that either is an official |
2287 | + standard defined by a recognized standards body, or, in the case of |
2288 | + interfaces specified for a particular programming language, one that |
2289 | + is widely used among developers working in that language. |
2290 | + |
2291 | + The "System Libraries" of an executable work include anything, other |
2292 | + than the work as a whole, that (a) is included in the normal form of |
2293 | + packaging a Major Component, but which is not part of that Major |
2294 | + Component, and (b) serves only to enable use of the work with that |
2295 | + Major Component, or to implement a Standard Interface for which an |
2296 | + implementation is available to the public in source code form. A |
2297 | + "Major Component", in this context, means a major essential component |
2298 | + (kernel, window system, and so on) of the specific operating system |
2299 | + (if any) on which the executable work runs, or a compiler used to |
2300 | + produce the work, or an object code interpreter used to run it. |
2301 | + |
2302 | + The "Corresponding Source" for a work in object code form means all |
2303 | + the source code needed to generate, install, and (for an executable |
2304 | + work) run the object code and to modify the work, including scripts to |
2305 | + control those activities. However, it does not include the work's |
2306 | + System Libraries, or general-purpose tools or generally available free |
2307 | + programs which are used unmodified in performing those activities but |
2308 | + which are not part of the work. For example, Corresponding Source |
2309 | + includes interface definition files associated with source files for |
2310 | + the work, and the source code for shared libraries and dynamically |
2311 | + linked subprograms that the work is specifically designed to require, |
2312 | + such as by intimate data communication or control flow between those |
2313 | + subprograms and other parts of the work. |
2314 | + |
2315 | + The Corresponding Source need not include anything that users |
2316 | + can regenerate automatically from other parts of the Corresponding |
2317 | + Source. |
2318 | + |
2319 | + The Corresponding Source for a work in source code form is that |
2320 | + same work. |
2321 | + |
2322 | + 2. Basic Permissions. |
2323 | + |
2324 | + All rights granted under this License are granted for the term of |
2325 | + copyright on the Program, and are irrevocable provided the stated |
2326 | + conditions are met. This License explicitly affirms your unlimited |
2327 | + permission to run the unmodified Program. The output from running a |
2328 | + covered work is covered by this License only if the output, given its |
2329 | + content, constitutes a covered work. This License acknowledges your |
2330 | + rights of fair use or other equivalent, as provided by copyright law. |
2331 | + |
2332 | + You may make, run and propagate covered works that you do not |
2333 | + convey, without conditions so long as your license otherwise remains |
2334 | + in force. You may convey covered works to others for the sole purpose |
2335 | + of having them make modifications exclusively for you, or provide you |
2336 | + with facilities for running those works, provided that you comply with |
2337 | + the terms of this License in conveying all material for which you do |
2338 | + not control copyright. Those thus making or running the covered works |
2339 | + for you must do so exclusively on your behalf, under your direction |
2340 | + and control, on terms that prohibit them from making any copies of |
2341 | + your copyrighted material outside their relationship with you. |
2342 | + |
2343 | + Conveying under any other circumstances is permitted solely under |
2344 | + the conditions stated below. Sublicensing is not allowed; section 10 |
2345 | + makes it unnecessary. |
2346 | + |
2347 | + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
2348 | + |
2349 | + No covered work shall be deemed part of an effective technological |
2350 | + measure under any applicable law fulfilling obligations under article |
2351 | + 11 of the WIPO copyright treaty adopted on 20 December 1996, or |
2352 | + similar laws prohibiting or restricting circumvention of such |
2353 | + measures. |
2354 | + |
2355 | + When you convey a covered work, you waive any legal power to forbid |
2356 | + circumvention of technological measures to the extent such circumvention |
2357 | + is effected by exercising rights under this License with respect to |
2358 | + the covered work, and you disclaim any intention to limit operation or |
2359 | + modification of the work as a means of enforcing, against the work's |
2360 | + users, your or third parties' legal rights to forbid circumvention of |
2361 | + technological measures. |
2362 | + |
2363 | + 4. Conveying Verbatim Copies. |
2364 | + |
2365 | + You may convey verbatim copies of the Program's source code as you |
2366 | + receive it, in any medium, provided that you conspicuously and |
2367 | + appropriately publish on each copy an appropriate copyright notice; |
2368 | + keep intact all notices stating that this License and any |
2369 | + non-permissive terms added in accord with section 7 apply to the code; |
2370 | + keep intact all notices of the absence of any warranty; and give all |
2371 | + recipients a copy of this License along with the Program. |
2372 | + |
2373 | + You may charge any price or no price for each copy that you convey, |
2374 | + and you may offer support or warranty protection for a fee. |
2375 | + |
2376 | + 5. Conveying Modified Source Versions. |
2377 | + |
2378 | + You may convey a work based on the Program, or the modifications to |
2379 | + produce it from the Program, in the form of source code under the |
2380 | + terms of section 4, provided that you also meet all of these conditions: |
2381 | + |
2382 | + a) The work must carry prominent notices stating that you modified |
2383 | + it, and giving a relevant date. |
2384 | + |
2385 | + b) The work must carry prominent notices stating that it is |
2386 | + released under this License and any conditions added under section |
2387 | + 7. This requirement modifies the requirement in section 4 to |
2388 | + "keep intact all notices". |
2389 | + |
2390 | + c) You must license the entire work, as a whole, under this |
2391 | + License to anyone who comes into possession of a copy. This |
2392 | + License will therefore apply, along with any applicable section 7 |
2393 | + additional terms, to the whole of the work, and all its parts, |
2394 | + regardless of how they are packaged. This License gives no |
2395 | + permission to license the work in any other way, but it does not |
2396 | + invalidate such permission if you have separately received it. |
2397 | + |
2398 | + d) If the work has interactive user interfaces, each must display |
2399 | + Appropriate Legal Notices; however, if the Program has interactive |
2400 | + interfaces that do not display Appropriate Legal Notices, your |
2401 | + work need not make them do so. |
2402 | + |
2403 | + A compilation of a covered work with other separate and independent |
2404 | + works, which are not by their nature extensions of the covered work, |
2405 | + and which are not combined with it such as to form a larger program, |
2406 | + in or on a volume of a storage or distribution medium, is called an |
2407 | + "aggregate" if the compilation and its resulting copyright are not |
2408 | + used to limit the access or legal rights of the compilation's users |
2409 | + beyond what the individual works permit. Inclusion of a covered work |
2410 | + in an aggregate does not cause this License to apply to the other |
2411 | + parts of the aggregate. |
2412 | + |
2413 | + 6. Conveying Non-Source Forms. |
2414 | + |
2415 | + You may convey a covered work in object code form under the terms |
2416 | + of sections 4 and 5, provided that you also convey the |
2417 | + machine-readable Corresponding Source under the terms of this License, |
2418 | + in one of these ways: |
2419 | + |
2420 | + a) Convey the object code in, or embodied in, a physical product |
2421 | + (including a physical distribution medium), accompanied by the |
2422 | + Corresponding Source fixed on a durable physical medium |
2423 | + customarily used for software interchange. |
2424 | + |
2425 | + b) Convey the object code in, or embodied in, a physical product |
2426 | + (including a physical distribution medium), accompanied by a |
2427 | + written offer, valid for at least three years and valid for as |
2428 | + long as you offer spare parts or customer support for that product |
2429 | + model, to give anyone who possesses the object code either (1) a |
2430 | + copy of the Corresponding Source for all the software in the |
2431 | + product that is covered by this License, on a durable physical |
2432 | + medium customarily used for software interchange, for a price no |
2433 | + more than your reasonable cost of physically performing this |
2434 | + conveying of source, or (2) access to copy the |
2435 | + Corresponding Source from a network server at no charge. |
2436 | + |
2437 | + c) Convey individual copies of the object code with a copy of the |
2438 | + written offer to provide the Corresponding Source. This |
2439 | + alternative is allowed only occasionally and noncommercially, and |
2440 | + only if you received the object code with such an offer, in accord |
2441 | + with subsection 6b. |
2442 | + |
2443 | + d) Convey the object code by offering access from a designated |
2444 | + place (gratis or for a charge), and offer equivalent access to the |
2445 | + Corresponding Source in the same way through the same place at no |
2446 | + further charge. You need not require recipients to copy the |
2447 | + Corresponding Source along with the object code. If the place to |
2448 | + copy the object code is a network server, the Corresponding Source |
2449 | + may be on a different server (operated by you or a third party) |
2450 | + that supports equivalent copying facilities, provided you maintain |
2451 | + clear directions next to the object code saying where to find the |
2452 | + Corresponding Source. Regardless of what server hosts the |
2453 | + Corresponding Source, you remain obligated to ensure that it is |
2454 | + available for as long as needed to satisfy these requirements. |
2455 | + |
2456 | + e) Convey the object code using peer-to-peer transmission, provided |
2457 | + you inform other peers where the object code and Corresponding |
2458 | + Source of the work are being offered to the general public at no |
2459 | + charge under subsection 6d. |
2460 | + |
2461 | + A separable portion of the object code, whose source code is excluded |
2462 | + from the Corresponding Source as a System Library, need not be |
2463 | + included in conveying the object code work. |
2464 | + |
2465 | + A "User Product" is either (1) a "consumer product", which means any |
2466 | + tangible personal property which is normally used for personal, family, |
2467 | + or household purposes, or (2) anything designed or sold for incorporation |
2468 | + into a dwelling. In determining whether a product is a consumer product, |
2469 | + doubtful cases shall be resolved in favor of coverage. For a particular |
2470 | + product received by a particular user, "normally used" refers to a |
2471 | + typical or common use of that class of product, regardless of the status |
2472 | + of the particular user or of the way in which the particular user |
2473 | + actually uses, or expects or is expected to use, the product. A product |
2474 | + is a consumer product regardless of whether the product has substantial |
2475 | + commercial, industrial or non-consumer uses, unless such uses represent |
2476 | + the only significant mode of use of the product. |
2477 | + |
2478 | + "Installation Information" for a User Product means any methods, |
2479 | + procedures, authorization keys, or other information required to install |
2480 | + and execute modified versions of a covered work in that User Product from |
2481 | + a modified version of its Corresponding Source. The information must |
2482 | + suffice to ensure that the continued functioning of the modified object |
2483 | + code is in no case prevented or interfered with solely because |
2484 | + modification has been made. |
2485 | + |
2486 | + If you convey an object code work under this section in, or with, or |
2487 | + specifically for use in, a User Product, and the conveying occurs as |
2488 | + part of a transaction in which the right of possession and use of the |
2489 | + User Product is transferred to the recipient in perpetuity or for a |
2490 | + fixed term (regardless of how the transaction is characterized), the |
2491 | + Corresponding Source conveyed under this section must be accompanied |
2492 | + by the Installation Information. But this requirement does not apply |
2493 | + if neither you nor any third party retains the ability to install |
2494 | + modified object code on the User Product (for example, the work has |
2495 | + been installed in ROM). |
2496 | + |
2497 | + The requirement to provide Installation Information does not include a |
2498 | + requirement to continue to provide support service, warranty, or updates |
2499 | + for a work that has been modified or installed by the recipient, or for |
2500 | + the User Product in which it has been modified or installed. Access to a |
2501 | + network may be denied when the modification itself materially and |
2502 | + adversely affects the operation of the network or violates the rules and |
2503 | + protocols for communication across the network. |
2504 | + |
2505 | + Corresponding Source conveyed, and Installation Information provided, |
2506 | + in accord with this section must be in a format that is publicly |
2507 | + documented (and with an implementation available to the public in |
2508 | + source code form), and must require no special password or key for |
2509 | + unpacking, reading or copying. |
2510 | + |
2511 | + 7. Additional Terms. |
2512 | + |
2513 | + "Additional permissions" are terms that supplement the terms of this |
2514 | + License by making exceptions from one or more of its conditions. |
2515 | + Additional permissions that are applicable to the entire Program shall |
2516 | + be treated as though they were included in this License, to the extent |
2517 | + that they are valid under applicable law. If additional permissions |
2518 | + apply only to part of the Program, that part may be used separately |
2519 | + under those permissions, but the entire Program remains governed by |
2520 | + this License without regard to the additional permissions. |
2521 | + |
2522 | + When you convey a copy of a covered work, you may at your option |
2523 | + remove any additional permissions from that copy, or from any part of |
2524 | + it. (Additional permissions may be written to require their own |
2525 | + removal in certain cases when you modify the work.) You may place |
2526 | + additional permissions on material, added by you to a covered work, |
2527 | + for which you have or can give appropriate copyright permission. |
2528 | + |
2529 | + Notwithstanding any other provision of this License, for material you |
2530 | + add to a covered work, you may (if authorized by the copyright holders of |
2531 | + that material) supplement the terms of this License with terms: |
2532 | + |
2533 | + a) Disclaiming warranty or limiting liability differently from the |
2534 | + terms of sections 15 and 16 of this License; or |
2535 | + |
2536 | + b) Requiring preservation of specified reasonable legal notices or |
2537 | + author attributions in that material or in the Appropriate Legal |
2538 | + Notices displayed by works containing it; or |
2539 | + |
2540 | + c) Prohibiting misrepresentation of the origin of that material, or |
2541 | + requiring that modified versions of such material be marked in |
2542 | + reasonable ways as different from the original version; or |
2543 | + |
2544 | + d) Limiting the use for publicity purposes of names of licensors or |
2545 | + authors of the material; or |
2546 | + |
2547 | + e) Declining to grant rights under trademark law for use of some |
2548 | + trade names, trademarks, or service marks; or |
2549 | + |
2550 | + f) Requiring indemnification of licensors and authors of that |
2551 | + material by anyone who conveys the material (or modified versions of |
2552 | + it) with contractual assumptions of liability to the recipient, for |
2553 | + any liability that these contractual assumptions directly impose on |
2554 | + those licensors and authors. |
2555 | + |
2556 | + All other non-permissive additional terms are considered "further |
2557 | + restrictions" within the meaning of section 10. If the Program as you |
2558 | + received it, or any part of it, contains a notice stating that it is |
2559 | + governed by this License along with a term that is a further |
2560 | + restriction, you may remove that term. If a license document contains |
2561 | + a further restriction but permits relicensing or conveying under this |
2562 | + License, you may add to a covered work material governed by the terms |
2563 | + of that license document, provided that the further restriction does |
2564 | + not survive such relicensing or conveying. |
2565 | + |
2566 | + If you add terms to a covered work in accord with this section, you |
2567 | + must place, in the relevant source files, a statement of the |
2568 | + additional terms that apply to those files, or a notice indicating |
2569 | + where to find the applicable terms. |
2570 | + |
2571 | + Additional terms, permissive or non-permissive, may be stated in the |
2572 | + form of a separately written license, or stated as exceptions; |
2573 | + the above requirements apply either way. |
2574 | + |
2575 | + 8. Termination. |
2576 | + |
2577 | + You may not propagate or modify a covered work except as expressly |
2578 | + provided under this License. Any attempt otherwise to propagate or |
2579 | + modify it is void, and will automatically terminate your rights under |
2580 | + this License (including any patent licenses granted under the third |
2581 | + paragraph of section 11). |
2582 | + |
2583 | + However, if you cease all violation of this License, then your |
2584 | + license from a particular copyright holder is reinstated (a) |
2585 | + provisionally, unless and until the copyright holder explicitly and |
2586 | + finally terminates your license, and (b) permanently, if the copyright |
2587 | + holder fails to notify you of the violation by some reasonable means |
2588 | + prior to 60 days after the cessation. |
2589 | + |
2590 | + Moreover, your license from a particular copyright holder is |
2591 | + reinstated permanently if the copyright holder notifies you of the |
2592 | + violation by some reasonable means, this is the first time you have |
2593 | + received notice of violation of this License (for any work) from that |
2594 | + copyright holder, and you cure the violation prior to 30 days after |
2595 | + your receipt of the notice. |
2596 | + |
2597 | + Termination of your rights under this section does not terminate the |
2598 | + licenses of parties who have received copies or rights from you under |
2599 | + this License. If your rights have been terminated and not permanently |
2600 | + reinstated, you do not qualify to receive new licenses for the same |
2601 | + material under section 10. |
2602 | + |
2603 | + 9. Acceptance Not Required for Having Copies. |
2604 | + |
2605 | + You are not required to accept this License in order to receive or |
2606 | + run a copy of the Program. Ancillary propagation of a covered work |
2607 | + occurring solely as a consequence of using peer-to-peer transmission |
2608 | + to receive a copy likewise does not require acceptance. However, |
2609 | + nothing other than this License grants you permission to propagate or |
2610 | + modify any covered work. These actions infringe copyright if you do |
2611 | + not accept this License. Therefore, by modifying or propagating a |
2612 | + covered work, you indicate your acceptance of this License to do so. |
2613 | + |
2614 | + 10. Automatic Licensing of Downstream Recipients. |
2615 | + |
2616 | + Each time you convey a covered work, the recipient automatically |
2617 | + receives a license from the original licensors, to run, modify and |
2618 | + propagate that work, subject to this License. You are not responsible |
2619 | + for enforcing compliance by third parties with this License. |
2620 | + |
2621 | + An "entity transaction" is a transaction transferring control of an |
2622 | + organization, or substantially all assets of one, or subdividing an |
2623 | + organization, or merging organizations. If propagation of a covered |
2624 | + work results from an entity transaction, each party to that |
2625 | + transaction who receives a copy of the work also receives whatever |
2626 | + licenses to the work the party's predecessor in interest had or could |
2627 | + give under the previous paragraph, plus a right to possession of the |
2628 | + Corresponding Source of the work from the predecessor in interest, if |
2629 | + the predecessor has it or can get it with reasonable efforts. |
2630 | + |
2631 | + You may not impose any further restrictions on the exercise of the |
2632 | + rights granted or affirmed under this License. For example, you may |
2633 | + not impose a license fee, royalty, or other charge for exercise of |
2634 | + rights granted under this License, and you may not initiate litigation |
2635 | + (including a cross-claim or counterclaim in a lawsuit) alleging that |
2636 | + any patent claim is infringed by making, using, selling, offering for |
2637 | + sale, or importing the Program or any portion of it. |
2638 | + |
2639 | + 11. Patents. |
2640 | + |
2641 | + A "contributor" is a copyright holder who authorizes use under this |
2642 | + License of the Program or a work on which the Program is based. The |
2643 | + work thus licensed is called the contributor's "contributor version". |
2644 | + |
2645 | + A contributor's "essential patent claims" are all patent claims |
2646 | + owned or controlled by the contributor, whether already acquired or |
2647 | + hereafter acquired, that would be infringed by some manner, permitted |
2648 | + by this License, of making, using, or selling its contributor version, |
2649 | + but do not include claims that would be infringed only as a |
2650 | + consequence of further modification of the contributor version. For |
2651 | + purposes of this definition, "control" includes the right to grant |
2652 | + patent sublicenses in a manner consistent with the requirements of |
2653 | + this License. |
2654 | + |
2655 | + Each contributor grants you a non-exclusive, worldwide, royalty-free |
2656 | + patent license under the contributor's essential patent claims, to |
2657 | + make, use, sell, offer for sale, import and otherwise run, modify and |
2658 | + propagate the contents of its contributor version. |
2659 | + |
2660 | + In the following three paragraphs, a "patent license" is any express |
2661 | + agreement or commitment, however denominated, not to enforce a patent |
2662 | + (such as an express permission to practice a patent or covenant not to |
2663 | + sue for patent infringement). To "grant" such a patent license to a |
2664 | + party means to make such an agreement or commitment not to enforce a |
2665 | + patent against the party. |
2666 | + |
2667 | + If you convey a covered work, knowingly relying on a patent license, |
2668 | + and the Corresponding Source of the work is not available for anyone |
2669 | + to copy, free of charge and under the terms of this License, through a |
2670 | + publicly available network server or other readily accessible means, |
2671 | + then you must either (1) cause the Corresponding Source to be so |
2672 | + available, or (2) arrange to deprive yourself of the benefit of the |
2673 | + patent license for this particular work, or (3) arrange, in a manner |
2674 | + consistent with the requirements of this License, to extend the patent |
2675 | + license to downstream recipients. "Knowingly relying" means you have |
2676 | + actual knowledge that, but for the patent license, your conveying the |
2677 | + covered work in a country, or your recipient's use of the covered work |
2678 | + in a country, would infringe one or more identifiable patents in that |
2679 | + country that you have reason to believe are valid. |
2680 | + |
2681 | + If, pursuant to or in connection with a single transaction or |
2682 | + arrangement, you convey, or propagate by procuring conveyance of, a |
2683 | + covered work, and grant a patent license to some of the parties |
2684 | + receiving the covered work authorizing them to use, propagate, modify |
2685 | + or convey a specific copy of the covered work, then the patent license |
2686 | + you grant is automatically extended to all recipients of the covered |
2687 | + work and works based on it. |
2688 | + |
2689 | + A patent license is "discriminatory" if it does not include within |
2690 | + the scope of its coverage, prohibits the exercise of, or is |
2691 | + conditioned on the non-exercise of one or more of the rights that are |
2692 | + specifically granted under this License. You may not convey a covered |
2693 | + work if you are a party to an arrangement with a third party that is |
2694 | + in the business of distributing software, under which you make payment |
2695 | + to the third party based on the extent of your activity of conveying |
2696 | + the work, and under which the third party grants, to any of the |
2697 | + parties who would receive the covered work from you, a discriminatory |
2698 | + patent license (a) in connection with copies of the covered work |
2699 | + conveyed by you (or copies made from those copies), or (b) primarily |
2700 | + for and in connection with specific products or compilations that |
2701 | + contain the covered work, unless you entered into that arrangement, |
2702 | + or that patent license was granted, prior to 28 March 2007. |
2703 | + |
2704 | + Nothing in this License shall be construed as excluding or limiting |
2705 | + any implied license or other defenses to infringement that may |
2706 | + otherwise be available to you under applicable patent law. |
2707 | + |
2708 | + 12. No Surrender of Others' Freedom. |
2709 | + |
2710 | + If conditions are imposed on you (whether by court order, agreement or |
2711 | + otherwise) that contradict the conditions of this License, they do not |
2712 | + excuse you from the conditions of this License. If you cannot convey a |
2713 | + covered work so as to satisfy simultaneously your obligations under this |
2714 | + License and any other pertinent obligations, then as a consequence you may |
2715 | + not convey it at all. For example, if you agree to terms that obligate you |
2716 | + to collect a royalty for further conveying from those to whom you convey |
2717 | + the Program, the only way you could satisfy both those terms and this |
2718 | + License would be to refrain entirely from conveying the Program. |
2719 | + |
2720 | + 13. Remote Network Interaction; Use with the GNU General Public License. |
2721 | + |
2722 | + Notwithstanding any other provision of this License, if you modify the |
2723 | + Program, your modified version must prominently offer all users |
2724 | + interacting with it remotely through a computer network (if your version |
2725 | + supports such interaction) an opportunity to receive the Corresponding |
2726 | + Source of your version by providing access to the Corresponding Source |
2727 | + from a network server at no charge, through some standard or customary |
2728 | + means of facilitating copying of software. This Corresponding Source |
2729 | + shall include the Corresponding Source for any work covered by version 3 |
2730 | + of the GNU General Public License that is incorporated pursuant to the |
2731 | + following paragraph. |
2732 | + |
2733 | + Notwithstanding any other provision of this License, you have |
2734 | + permission to link or combine any covered work with a work licensed |
2735 | + under version 3 of the GNU General Public License into a single |
2736 | + combined work, and to convey the resulting work. The terms of this |
2737 | + License will continue to apply to the part which is the covered work, |
2738 | + but the work with which it is combined will remain governed by version |
2739 | + 3 of the GNU General Public License. |
2740 | + |
2741 | + 14. Revised Versions of this License. |
2742 | + |
2743 | + The Free Software Foundation may publish revised and/or new versions of |
2744 | + the GNU Affero General Public License from time to time. Such new versions |
2745 | + will be similar in spirit to the present version, but may differ in detail to |
2746 | + address new problems or concerns. |
2747 | + |
2748 | + Each version is given a distinguishing version number. If the |
2749 | + Program specifies that a certain numbered version of the GNU Affero General |
2750 | + Public License "or any later version" applies to it, you have the |
2751 | + option of following the terms and conditions either of that numbered |
2752 | + version or of any later version published by the Free Software |
2753 | + Foundation. If the Program does not specify a version number of the |
2754 | + GNU Affero General Public License, you may choose any version ever published |
2755 | + by the Free Software Foundation. |
2756 | + |
2757 | + If the Program specifies that a proxy can decide which future |
2758 | + versions of the GNU Affero General Public License can be used, that proxy's |
2759 | + public statement of acceptance of a version permanently authorizes you |
2760 | + to choose that version for the Program. |
2761 | + |
2762 | + Later license versions may give you additional or different |
2763 | + permissions. However, no additional obligations are imposed on any |
2764 | + author or copyright holder as a result of your choosing to follow a |
2765 | + later version. |
2766 | + |
2767 | + 15. Disclaimer of Warranty. |
2768 | + |
2769 | + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
2770 | + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
2771 | + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
2772 | + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
2773 | + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
2774 | + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
2775 | + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
2776 | + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
2777 | + |
2778 | + 16. Limitation of Liability. |
2779 | + |
2780 | + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
2781 | + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
2782 | + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
2783 | + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
2784 | + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
2785 | + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
2786 | + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
2787 | + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
2788 | + SUCH DAMAGES. |
2789 | + |
2790 | + 17. Interpretation of Sections 15 and 16. |
2791 | + |
2792 | + If the disclaimer of warranty and limitation of liability provided |
2793 | + above cannot be given local legal effect according to their terms, |
2794 | + reviewing courts shall apply local law that most closely approximates |
2795 | + an absolute waiver of all civil liability in connection with the |
2796 | + Program, unless a warranty or assumption of liability accompanies a |
2797 | + copy of the Program in return for a fee. |
2798 | + |
2799 | + END OF TERMS AND CONDITIONS |
2800 | + |
2801 | + How to Apply These Terms to Your New Programs |
2802 | + |
2803 | + If you develop a new program, and you want it to be of the greatest |
2804 | + possible use to the public, the best way to achieve this is to make it |
2805 | + free software which everyone can redistribute and change under these terms. |
2806 | + |
2807 | + To do so, attach the following notices to the program. It is safest |
2808 | + to attach them to the start of each source file to most effectively |
2809 | + state the exclusion of warranty; and each file should have at least |
2810 | + the "copyright" line and a pointer to where the full notice is found. |
2811 | + |
2812 | + <one line to give the program's name and a brief idea of what it does.> |
2813 | + Copyright (C) <year> <name of author> |
2814 | + |
2815 | + This program is free software: you can redistribute it and/or modify |
2816 | + it under the terms of the GNU Affero General Public License as published |
2817 | + by the Free Software Foundation, either version 3 of the License, or |
2818 | + (at your option) any later version. |
2819 | + |
2820 | + This program is distributed in the hope that it will be useful, |
2821 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
2822 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2823 | + GNU Affero General Public License for more details. |
2824 | + |
2825 | + You should have received a copy of the GNU Affero General Public License |
2826 | + along with this program. If not, see <https://www.gnu.org/licenses/>. |
2827 | + |
2828 | + Also add information on how to contact you by electronic and paper mail. |
2829 | + |
2830 | + If your software can interact with users remotely through a computer |
2831 | + network, you should also make sure that it provides a way for users to |
2832 | + get its source. For example, if your program is a web application, its |
2833 | + interface could display a "Source" link that leads users to an archive |
2834 | + of the code. There are many ways you could offer source, and different |
2835 | + solutions will be better for different programs; see section 13 for the |
2836 | + specific requirements. |
2837 | + |
2838 | + You should also get your employer (if you work as a programmer) or school, |
2839 | + if any, to sign a "copyright disclaimer" for the program, if necessary. |
2840 | + For more information on this, and how to apply and follow the GNU AGPL, see |
2841 | + <https://www.gnu.org/licenses/>. |
2842 | diff --git a/README.md b/README.md |
2843 | new file mode 100644 |
2844 | index 0000000..38b09c8 |
2845 | --- /dev/null |
2846 | +++ b/README.md |
2847 | @@ -0,0 +1,190 @@ |
2848 | + # Mailpot - WIP mailing list manager |
2849 | + |
2850 | + Crates: |
2851 | + |
2852 | + - `core` |
2853 | + - `cli` a command line tool to manage lists |
2854 | + - `rest-http` a REST http server to manage lists |
2855 | + |
2856 | + ## Project goals |
2857 | + |
2858 | + - easy setup |
2859 | + - extensible through Rust API as a library |
2860 | + - extensible through HTTP REST API as an HTTP server, with webhooks |
2861 | + - basic management through CLI |
2862 | + - replaceable lightweight web archiver |
2863 | + - custom storage? |
2864 | + - useful for both newsletters, discussions |
2865 | + |
2866 | + ## Initial setup |
2867 | + |
2868 | + Check where `mpot` expects your database file to be: |
2869 | + |
2870 | + ```shell |
2871 | + $ cargo run --bin mpot -- db-location |
2872 | + Configuration file /home/user/.config/mailpot/config.toml doesn't exist |
2873 | + ``` |
2874 | + |
2875 | + Uuugh, oops. |
2876 | + |
2877 | + ```shell |
2878 | + $ mkdir -p /home/user/.config/mailpot |
2879 | + $ echo 'send_mail = { "type" = "ShellCommand", "value" = "/usr/bin/false" }' > /home/user/.config/mailpot/config.toml |
2880 | + $ cargo run --bin mpot -- db-location |
2881 | + /home/user/.local/share/mailpot/mpot.db |
2882 | + ``` |
2883 | + |
2884 | + Now you can initialize the database file: |
2885 | + |
2886 | + ```shell |
2887 | + $ mkdir -p /home/user/.local/share/mailpot/ |
2888 | + $ sqlite3 /home/user/.local/share/mailpot/mpot.db < ./core/src/schema.sql |
2889 | + ``` |
2890 | + |
2891 | + ## Examples |
2892 | + |
2893 | + ```text |
2894 | + % mpot help |
2895 | + mailpot 0.1.0 |
2896 | + mini mailing list manager |
2897 | + |
2898 | + USAGE: |
2899 | + mpot [FLAGS] [OPTIONS] <SUBCOMMAND> |
2900 | + |
2901 | + FLAGS: |
2902 | + -d, --debug Activate debug mode |
2903 | + -h, --help Prints help information |
2904 | + -V, --version Prints version information |
2905 | + |
2906 | + OPTIONS: |
2907 | + -c, --config <config> Set config file |
2908 | + |
2909 | + SUBCOMMANDS: |
2910 | + create-list Create new list |
2911 | + db-location Prints database filesystem location |
2912 | + help Prints this message or the help of the given subcommand(s) |
2913 | + list Mailing list management |
2914 | + list-lists Lists all registered mailing lists |
2915 | + post Post message from STDIN to list |
2916 | + ``` |
2917 | + |
2918 | + ### Receiving mail |
2919 | + |
2920 | + ```shell |
2921 | + $ cat list-request.eml | cargo run --bin mpot -- -vvvvvv post --dry-run |
2922 | + ``` |
2923 | + |
2924 | + <details><summary>output</summary> |
2925 | + |
2926 | + ```shell |
2927 | + TRACE - Received envelope to post: Envelope { |
2928 | + Subject: "unsubscribe", |
2929 | + Date: "Tue, 04 Aug 2020 14:10:13 +0300", |
2930 | + From: [ |
2931 | + Address::Mailbox { |
2932 | + display_name: "Mxxxx Pxxxxxxxxxxxx", |
2933 | + address_spec: "exxxxx@localhost", |
2934 | + }, |
2935 | + ], |
2936 | + To: [ |
2937 | + Address::Mailbox { |
2938 | + display_name: "", |
2939 | + address_spec: "test-announce+request@localhost", |
2940 | + }, |
2941 | + ], |
2942 | + Message-ID: "<ejduu.fddf8sgen4j7@localhost>", |
2943 | + In-Reply-To: None, |
2944 | + References: None, |
2945 | + Hash: 12581897380059220314, |
2946 | + } |
2947 | + TRACE - unsubscribe action for addresses [Address::Mailbox { display_name: "Mxxxx Pxxxxxxxxxxxx", address_spec: "exxxxx@localhost" }] in list [#2 test-announce] test announcements <test-announce@localhost> |
2948 | + TRACE - Is post related to list [#1 test] Test list <test@localhost>? false |
2949 | + ``` |
2950 | + </details> |
2951 | + |
2952 | + ```shell |
2953 | + $ cat list-post.eml | cargo run --bin mpot -- -vvvvvv post --dry-run |
2954 | + ``` |
2955 | + |
2956 | + <details><summary>output</summary> |
2957 | + |
2958 | + ```shell |
2959 | + TRACE - Received envelope to post: Envelope { |
2960 | + Subject: "[test-announce] new test releases", |
2961 | + Date: "Tue, 04 Aug 2020 14:10:13 +0300", |
2962 | + From: [ |
2963 | + Address::Mailbox { |
2964 | + display_name: "Mxxxx Pxxxxxxxxxxxx", |
2965 | + address_spec: "exxxxx@localhost", |
2966 | + }, |
2967 | + ], |
2968 | + To: [ |
2969 | + Address::Mailbox { |
2970 | + display_name: "", |
2971 | + address_spec: "test-announce@localhost", |
2972 | + }, |
2973 | + ], |
2974 | + Message-ID: "<ejduu.sddf8sgen4j7@localhost>", |
2975 | + In-Reply-To: None, |
2976 | + References: None, |
2977 | + Hash: 10220641455578979007, |
2978 | + } |
2979 | + TRACE - Is post related to list [#1 test] Test list <test@localhost>? false |
2980 | + TRACE - Is post related to list [#2 test-announce] test announcements <test-announce@localhost>? true |
2981 | + TRACE - Examining list "test announcements" <test-announce@localhost> |
2982 | + TRACE - List members [ |
2983 | + ListMembership { |
2984 | + list: 2, |
2985 | + address: "exxxxx@localhost", |
2986 | + name: None, |
2987 | + digest: false, |
2988 | + hide_address: false, |
2989 | + receive_duplicates: false, |
2990 | + receive_own_posts: true, |
2991 | + receive_confirmation: true, |
2992 | + enabled: true, |
2993 | + }, |
2994 | + ] |
2995 | + TRACE - Running FixCRLF filter |
2996 | + TRACE - Running PostRightsCheck filter |
2997 | + TRACE - Running AddListHeaders filter |
2998 | + TRACE - Running FinalizeRecipients filter |
2999 | + TRACE - examining member ListMembership { list: 2, address: "exxxxx@localhost", name: None, digest: false, hide_address: false, receive_duplicates: false, receive_own_posts: true, receive_confirmation: true, enabled: true } |
3000 | + TRACE - member is submitter |
3001 | + TRACE - Member gets copy |
3002 | + TRACE - result Ok( |
3003 | + Post { |
3004 | + list: MailingList { |
3005 | + pk: 2, |
3006 | + name: "test announcements", |
3007 | + id: "test-announce", |
3008 | + address: "test-announce@localhost", |
3009 | + description: None, |
3010 | + archive_url: None, |
3011 | + }, |
3012 | + from: Address::Mailbox { |
3013 | + display_name: "Mxxxx Pxxxxxxxxxxxx", |
3014 | + address_spec: "exxxxx@localhost", |
3015 | + }, |
3016 | + members: 1, |
3017 | + bytes: 851, |
3018 | + policy: None, |
3019 | + to: [ |
3020 | + Address::Mailbox { |
3021 | + display_name: "", |
3022 | + address_spec: "test-announce@localhost", |
3023 | + }, |
3024 | + ], |
3025 | + action: Accept { |
3026 | + recipients: [ |
3027 | + Address::Mailbox { |
3028 | + display_name: "", |
3029 | + address_spec: "exxxxx@localhost", |
3030 | + }, |
3031 | + ], |
3032 | + digests: [], |
3033 | + }, |
3034 | + }, |
3035 | + ) |
3036 | + ``` |
3037 | + </details> |
3038 | diff --git a/cli/Cargo.toml b/cli/Cargo.toml |
3039 | new file mode 100644 |
3040 | index 0000000..b3743e0 |
3041 | --- /dev/null |
3042 | +++ b/cli/Cargo.toml |
3043 | @@ -0,0 +1,21 @@ |
3044 | + [package] |
3045 | + name = "mailpot-cli" |
3046 | + version = "0.1.0" |
3047 | + authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"] |
3048 | + edition = "2018" |
3049 | + license = "LICENSE" |
3050 | + readme = "README.md" |
3051 | + description = "mailing list manager" |
3052 | + repository = "https://github.com/meli/mailpot" |
3053 | + keywords = ["mail", "mailing-lists" ] |
3054 | + categories = ["email"] |
3055 | + default-run = "mpot" |
3056 | + |
3057 | + [[bin]] |
3058 | + name = "mpot" |
3059 | + path = "src/main.rs" |
3060 | + |
3061 | + [dependencies] |
3062 | + mailpot = { version = "0.1.0", path = "../core" } |
3063 | + structopt = "0.3.16" |
3064 | + stderrlog = "^0.5" |
3065 | diff --git a/cli/README.md b/cli/README.md |
3066 | new file mode 100644 |
3067 | index 0000000..f5e323d |
3068 | --- /dev/null |
3069 | +++ b/cli/README.md |
3070 | @@ -0,0 +1,5 @@ |
3071 | + # mailpot-cli |
3072 | + |
3073 | + ```shell |
3074 | + cargo run --bin mpot -- help |
3075 | + ``` |
3076 | diff --git a/cli/src/main.rs b/cli/src/main.rs |
3077 | new file mode 100644 |
3078 | index 0000000..f2302f4 |
3079 | --- /dev/null |
3080 | +++ b/cli/src/main.rs |
3081 | @@ -0,0 +1,472 @@ |
3082 | + /* |
3083 | + * This file is part of mailpot |
3084 | + * |
3085 | + * Copyright 2020 - Manos Pitsidianakis |
3086 | + * |
3087 | + * This program is free software: you can redistribute it and/or modify |
3088 | + * it under the terms of the GNU Affero General Public License as |
3089 | + * published by the Free Software Foundation, either version 3 of the |
3090 | + * License, or (at your option) any later version. |
3091 | + * |
3092 | + * This program is distributed in the hope that it will be useful, |
3093 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3094 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3095 | + * GNU Affero General Public License for more details. |
3096 | + * |
3097 | + * You should have received a copy of the GNU Affero General Public License |
3098 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
3099 | + */ |
3100 | + |
3101 | + extern crate mailpot; |
3102 | + extern crate stderrlog; |
3103 | + |
3104 | + pub use mailpot::config::*; |
3105 | + pub use mailpot::db::*; |
3106 | + pub use mailpot::errors::*; |
3107 | + pub use mailpot::mail::*; |
3108 | + pub use mailpot::models::changesets::*; |
3109 | + pub use mailpot::models::*; |
3110 | + pub use mailpot::*; |
3111 | + use std::path::PathBuf; |
3112 | + use structopt::StructOpt; |
3113 | + |
3114 | + #[derive(Debug, StructOpt)] |
3115 | + #[structopt( |
3116 | + name = "mailpot", |
3117 | + about = "mini mailing list manager", |
3118 | + author = "Manos Pitsidianakis <epilys@nessuent.xyz>" |
3119 | + )] |
3120 | + struct Opt { |
3121 | + /// Activate debug mode |
3122 | + #[structopt(short, long)] |
3123 | + debug: bool, |
3124 | + |
3125 | + /// Set config file |
3126 | + #[structopt(short, long, parse(from_os_str))] |
3127 | + #[allow(dead_code)] |
3128 | + config: Option<PathBuf>, |
3129 | + #[structopt(flatten)] |
3130 | + cmd: Command, |
3131 | + /// Silence all output |
3132 | + #[structopt(short = "q", long = "quiet")] |
3133 | + quiet: bool, |
3134 | + /// Verbose mode (-v, -vv, -vvv, etc) |
3135 | + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] |
3136 | + verbose: usize, |
3137 | + /// Timestamp (sec, ms, ns, none) |
3138 | + #[structopt(short = "t", long = "timestamp")] |
3139 | + ts: Option<stderrlog::Timestamp>, |
3140 | + } |
3141 | + |
3142 | + #[derive(Debug, StructOpt)] |
3143 | + enum Command { |
3144 | + ///Prints database filesystem location |
3145 | + DbLocation, |
3146 | + ///Dumps database data to STDOUT |
3147 | + DumpDatabase, |
3148 | + ///Lists all registered mailing lists |
3149 | + ListLists, |
3150 | + ///Mailing list management |
3151 | + List { |
3152 | + ///Selects mailing list to operate on |
3153 | + list_id: String, |
3154 | + #[structopt(subcommand)] |
3155 | + cmd: ListCommand, |
3156 | + }, |
3157 | + ///Create new list |
3158 | + CreateList { |
3159 | + ///List name |
3160 | + #[structopt(long)] |
3161 | + name: String, |
3162 | + ///List ID |
3163 | + #[structopt(long)] |
3164 | + id: String, |
3165 | + ///List e-mail address |
3166 | + #[structopt(long)] |
3167 | + address: String, |
3168 | + ///List description |
3169 | + #[structopt(long)] |
3170 | + description: Option<String>, |
3171 | + ///List archive URL |
3172 | + #[structopt(long)] |
3173 | + archive_url: Option<String>, |
3174 | + }, |
3175 | + ///Post message from STDIN to list |
3176 | + Post { |
3177 | + #[structopt(long)] |
3178 | + dry_run: bool, |
3179 | + }, |
3180 | + } |
3181 | + |
3182 | + #[derive(Debug, StructOpt)] |
3183 | + enum ListCommand { |
3184 | + /// List members of list. |
3185 | + Members, |
3186 | + /// Add member to list. |
3187 | + AddMember { |
3188 | + /// E-mail address |
3189 | + #[structopt(long)] |
3190 | + address: String, |
3191 | + /// Name |
3192 | + #[structopt(long)] |
3193 | + name: Option<String>, |
3194 | + /// Send messages as digest? |
3195 | + #[structopt(long)] |
3196 | + digest: bool, |
3197 | + /// Hide message from list when posting? |
3198 | + #[structopt(long)] |
3199 | + hide_address: bool, |
3200 | + /// Hide message from list when posting? |
3201 | + #[structopt(long)] |
3202 | + /// Receive confirmation email when posting? |
3203 | + receive_confirmation: Option<bool>, |
3204 | + #[structopt(long)] |
3205 | + /// Receive posts from list even if address exists in To or Cc header? |
3206 | + receive_duplicates: Option<bool>, |
3207 | + #[structopt(long)] |
3208 | + /// Receive own posts from list? |
3209 | + receive_own_posts: Option<bool>, |
3210 | + #[structopt(long)] |
3211 | + /// Is subscription enabled? |
3212 | + enabled: Option<bool>, |
3213 | + }, |
3214 | + /// Remove member from list. |
3215 | + RemoveMember { |
3216 | + #[structopt(long)] |
3217 | + /// E-mail address |
3218 | + address: String, |
3219 | + }, |
3220 | + /// Update membership info. |
3221 | + UpdateMembership { |
3222 | + address: String, |
3223 | + name: Option<String>, |
3224 | + digest: Option<bool>, |
3225 | + hide_address: Option<bool>, |
3226 | + receive_duplicates: Option<bool>, |
3227 | + receive_own_posts: Option<bool>, |
3228 | + receive_confirmation: Option<bool>, |
3229 | + enabled: Option<bool>, |
3230 | + }, |
3231 | + /// Alias for update-membership --enabled true |
3232 | + EnableMembership { address: String }, |
3233 | + /// Alias for update-membership --enabled false |
3234 | + DisableMembership { address: String }, |
3235 | + /// Update mailing list details. |
3236 | + Update { |
3237 | + name: Option<String>, |
3238 | + id: Option<String>, |
3239 | + address: Option<String>, |
3240 | + description: Option<String>, |
3241 | + archive_url: Option<String>, |
3242 | + }, |
3243 | + /// Show mailing list health status. |
3244 | + Health, |
3245 | + /// Show mailing list info. |
3246 | + Info, |
3247 | + } |
3248 | + |
3249 | + fn run_app(opt: Opt) -> Result<()> { |
3250 | + if opt.debug { |
3251 | + println!("DEBUG: {:?}", &opt); |
3252 | + } |
3253 | + Configuration::init()?; |
3254 | + use Command::*; |
3255 | + match opt.cmd { |
3256 | + DbLocation => { |
3257 | + println!("{}", Database::db_path()?.display()); |
3258 | + } |
3259 | + DumpDatabase => { |
3260 | + let db = Database::open_or_create_db()?; |
3261 | + let lists = db.list_lists()?; |
3262 | + let mut stdout = std::io::stdout(); |
3263 | + serde_json::to_writer_pretty(&mut stdout, &lists)?; |
3264 | + for l in &lists { |
3265 | + serde_json::to_writer_pretty(&mut stdout, &db.list_members(l.pk)?)?; |
3266 | + } |
3267 | + } |
3268 | + ListLists => { |
3269 | + let db = Database::open_or_create_db()?; |
3270 | + let lists = db.list_lists()?; |
3271 | + if lists.is_empty() { |
3272 | + println!("No lists found."); |
3273 | + } else { |
3274 | + for l in lists { |
3275 | + println!("- {} {:?}", l.id, l); |
3276 | + let list_owners = db.get_list_owners(l.pk)?; |
3277 | + if list_owners.is_empty() { |
3278 | + println!("\tList owners: None"); |
3279 | + } else { |
3280 | + println!("\tList owners:"); |
3281 | + for o in list_owners { |
3282 | + println!("\t- {}", o); |
3283 | + } |
3284 | + } |
3285 | + if let Some(s) = db.get_list_policy(l.pk)? { |
3286 | + println!("\tList policy: {}", s); |
3287 | + } else { |
3288 | + println!("\tList policy: None"); |
3289 | + } |
3290 | + println!(); |
3291 | + } |
3292 | + } |
3293 | + } |
3294 | + List { list_id, cmd } => { |
3295 | + let db = Database::open_or_create_db()?; |
3296 | + let mut lists = db.list_lists()?; |
3297 | + let list = if let Some(pos) = lists.iter().position(|l| l.id == list_id) { |
3298 | + lists.remove(pos) |
3299 | + } else { |
3300 | + return Err(format!("No list with id {} was found", list_id).into()); |
3301 | + }; |
3302 | + use ListCommand::*; |
3303 | + match cmd { |
3304 | + Members => { |
3305 | + let members = db.list_members(list.pk)?; |
3306 | + if members.is_empty() { |
3307 | + println!("No members found."); |
3308 | + } else { |
3309 | + println!("Members of list {}", list.id); |
3310 | + for l in members { |
3311 | + println!("- {}", &l); |
3312 | + } |
3313 | + } |
3314 | + } |
3315 | + AddMember { |
3316 | + address, |
3317 | + name, |
3318 | + digest, |
3319 | + hide_address, |
3320 | + receive_confirmation, |
3321 | + receive_duplicates, |
3322 | + receive_own_posts, |
3323 | + enabled, |
3324 | + } => { |
3325 | + db.add_member( |
3326 | + list.pk, |
3327 | + ListMembership { |
3328 | + pk: 0, |
3329 | + list: list.pk, |
3330 | + name, |
3331 | + address, |
3332 | + digest, |
3333 | + hide_address, |
3334 | + receive_confirmation: receive_confirmation.unwrap_or(true), |
3335 | + receive_duplicates: receive_duplicates.unwrap_or(true), |
3336 | + receive_own_posts: receive_own_posts.unwrap_or(false), |
3337 | + enabled: enabled.unwrap_or(true), |
3338 | + }, |
3339 | + )?; |
3340 | + } |
3341 | + RemoveMember { address } => { |
3342 | + loop { |
3343 | + println!( |
3344 | + "Are you sure you want to remove membership of {} from list {}? [Yy/n]", |
3345 | + address, list |
3346 | + ); |
3347 | + let mut input = String::new(); |
3348 | + std::io::stdin().read_line(&mut input)?; |
3349 | + if input.trim() == "Y" || input.trim() == "y" || input.trim() == "" { |
3350 | + break; |
3351 | + } else if input.trim() == "n" { |
3352 | + return Ok(()); |
3353 | + } |
3354 | + } |
3355 | + |
3356 | + db.remove_member(list.pk, &address)?; |
3357 | + } |
3358 | + Health => { |
3359 | + println!("{} health:", list); |
3360 | + let list_owners = db.get_list_owners(list.pk)?; |
3361 | + let list_policy = db.get_list_policy(list.pk)?; |
3362 | + if list_owners.is_empty() { |
3363 | + println!("\tList has no owners: you should add at least one."); |
3364 | + } |
3365 | + if list_policy.is_none() { |
3366 | + } else { |
3367 | + println!("\tList has no post policy: you should add one."); |
3368 | + } |
3369 | + } |
3370 | + Info => { |
3371 | + println!("{} info:", list); |
3372 | + let list_owners = db.get_list_owners(list.pk)?; |
3373 | + let list_policy = db.get_list_policy(list.pk)?; |
3374 | + let members = db.list_members(list.pk)?; |
3375 | + if members.is_empty() { |
3376 | + println!("No members."); |
3377 | + } else if members.len() == 1 { |
3378 | + println!("1 member."); |
3379 | + } else { |
3380 | + println!("{} members.", members.len()); |
3381 | + } |
3382 | + if list_owners.is_empty() { |
3383 | + println!("List owners: None"); |
3384 | + } else { |
3385 | + println!("List owners:"); |
3386 | + for o in list_owners { |
3387 | + println!("\t- {}", o); |
3388 | + } |
3389 | + } |
3390 | + if let Some(s) = list_policy { |
3391 | + println!("List policy: {}", s); |
3392 | + } else { |
3393 | + println!("List policy: None"); |
3394 | + } |
3395 | + } |
3396 | + UpdateMembership { |
3397 | + address, |
3398 | + name, |
3399 | + digest, |
3400 | + hide_address, |
3401 | + receive_duplicates, |
3402 | + receive_own_posts, |
3403 | + receive_confirmation, |
3404 | + enabled, |
3405 | + } => { |
3406 | + let name = if name |
3407 | + .as_ref() |
3408 | + .map(|s: &String| s.is_empty()) |
3409 | + .unwrap_or(false) |
3410 | + { |
3411 | + None |
3412 | + } else { |
3413 | + Some(name) |
3414 | + }; |
3415 | + let changeset = ListMembershipChangeset { |
3416 | + list: list.pk, |
3417 | + address, |
3418 | + name, |
3419 | + digest, |
3420 | + hide_address, |
3421 | + receive_duplicates, |
3422 | + receive_own_posts, |
3423 | + receive_confirmation, |
3424 | + enabled, |
3425 | + }; |
3426 | + db.update_member(changeset)?; |
3427 | + } |
3428 | + EnableMembership { address } => { |
3429 | + let changeset = ListMembershipChangeset { |
3430 | + list: list.pk, |
3431 | + address, |
3432 | + name: None, |
3433 | + digest: None, |
3434 | + hide_address: None, |
3435 | + receive_duplicates: None, |
3436 | + receive_own_posts: None, |
3437 | + receive_confirmation: None, |
3438 | + enabled: Some(true), |
3439 | + }; |
3440 | + db.update_member(changeset)?; |
3441 | + } |
3442 | + DisableMembership { address } => { |
3443 | + let changeset = ListMembershipChangeset { |
3444 | + list: list.pk, |
3445 | + address, |
3446 | + name: None, |
3447 | + digest: None, |
3448 | + hide_address: None, |
3449 | + receive_duplicates: None, |
3450 | + receive_own_posts: None, |
3451 | + receive_confirmation: None, |
3452 | + enabled: Some(false), |
3453 | + }; |
3454 | + db.update_member(changeset)?; |
3455 | + } |
3456 | + Update { |
3457 | + name, |
3458 | + id, |
3459 | + address, |
3460 | + description, |
3461 | + archive_url, |
3462 | + } => { |
3463 | + let description = if description |
3464 | + .as_ref() |
3465 | + .map(|s: &String| s.is_empty()) |
3466 | + .unwrap_or(false) |
3467 | + { |
3468 | + None |
3469 | + } else { |
3470 | + Some(description) |
3471 | + }; |
3472 | + let archive_url = if archive_url |
3473 | + .as_ref() |
3474 | + .map(|s: &String| s.is_empty()) |
3475 | + .unwrap_or(false) |
3476 | + { |
3477 | + None |
3478 | + } else { |
3479 | + Some(archive_url) |
3480 | + }; |
3481 | + let changeset = MailingListChangeset { |
3482 | + pk: list.pk, |
3483 | + name, |
3484 | + id, |
3485 | + address, |
3486 | + description, |
3487 | + archive_url, |
3488 | + }; |
3489 | + db.update_list(changeset)?; |
3490 | + } |
3491 | + } |
3492 | + } |
3493 | + CreateList { |
3494 | + name, |
3495 | + id, |
3496 | + address, |
3497 | + description, |
3498 | + archive_url, |
3499 | + } => { |
3500 | + let db = Database::open_or_create_db()?; |
3501 | + db.create_list(MailingList { |
3502 | + pk: 0, |
3503 | + name, |
3504 | + id, |
3505 | + description, |
3506 | + address, |
3507 | + archive_url, |
3508 | + })?; |
3509 | + } |
3510 | + Post { dry_run } => { |
3511 | + if opt.debug { |
3512 | + println!("post dry_run{:?}", dry_run); |
3513 | + } |
3514 | + |
3515 | + use melib::Envelope; |
3516 | + use std::io::Read; |
3517 | + |
3518 | + let mut input = String::new(); |
3519 | + std::io::stdin().read_to_string(&mut input)?; |
3520 | + match Envelope::from_bytes(input.as_bytes(), None) { |
3521 | + Ok(env) => { |
3522 | + let db = Database::open_or_create_db()?; |
3523 | + db.post(env, input.as_bytes(), dry_run)?; |
3524 | + } |
3525 | + Err(err) => { |
3526 | + eprintln!("Could not parse message: {}", err); |
3527 | + let p = Configuration::save_message(input)?; |
3528 | + eprintln!("Message saved at {}", p.display()); |
3529 | + return Err(err.into()); |
3530 | + } |
3531 | + } |
3532 | + } |
3533 | + } |
3534 | + |
3535 | + Ok(()) |
3536 | + } |
3537 | + |
3538 | + fn main() -> std::result::Result<(), i32> { |
3539 | + let opt = Opt::from_args(); |
3540 | + stderrlog::new() |
3541 | + .module(module_path!()) |
3542 | + .module("mailpot") |
3543 | + .quiet(opt.quiet) |
3544 | + .verbosity(opt.verbose) |
3545 | + .timestamp(opt.ts.unwrap_or(stderrlog::Timestamp::Off)) |
3546 | + .init() |
3547 | + .unwrap(); |
3548 | + if let Err(err) = run_app(opt) { |
3549 | + println!("{}", err); |
3550 | + std::process::exit(-1); |
3551 | + } |
3552 | + Ok(()) |
3553 | + } |
3554 | diff --git a/core/Cargo.toml b/core/Cargo.toml |
3555 | new file mode 100644 |
3556 | index 0000000..3669bc2 |
3557 | --- /dev/null |
3558 | +++ b/core/Cargo.toml |
3559 | @@ -0,0 +1,23 @@ |
3560 | + [package] |
3561 | + name = "mailpot" |
3562 | + version = "0.1.0" |
3563 | + authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"] |
3564 | + edition = "2018" |
3565 | + license = "LICENSE" |
3566 | + readme = "README.md" |
3567 | + description = "mailing list manager" |
3568 | + repository = "https://github.com/meli/mailpot" |
3569 | + keywords = ["mail", "mailing-lists" ] |
3570 | + categories = ["email"] |
3571 | + |
3572 | + [dependencies] |
3573 | + chrono = { version = "^0.4", features = ["serde", ] } |
3574 | + error-chain = "0.12.4" |
3575 | + #melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms"], path="../../meli/melib", branch = "master" } |
3576 | + melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms"], git="https://github.com/meli/meli", branch = "master" } |
3577 | + rusqlite = {version = "^0.27" } |
3578 | + serde = { version = "^1", features = ["derive", ]} |
3579 | + serde_json = "^1" |
3580 | + toml = "^0.5" |
3581 | + log = "0.4" |
3582 | + xdg = "2.1.0" |
3583 | diff --git a/core/README.md b/core/README.md |
3584 | new file mode 100644 |
3585 | index 0000000..452b1bc |
3586 | --- /dev/null |
3587 | +++ b/core/README.md |
3588 | @@ -0,0 +1,7 @@ |
3589 | + # mailpot-core |
3590 | + |
3591 | + Initialize `sqlite3` database |
3592 | + |
3593 | + ```shell |
3594 | + sqlite3 mpot.db < ./src/schema.sql |
3595 | + ``` |
3596 | diff --git a/core/build.rs b/core/build.rs |
3597 | new file mode 100644 |
3598 | index 0000000..ce35cba |
3599 | --- /dev/null |
3600 | +++ b/core/build.rs |
3601 | @@ -0,0 +1,32 @@ |
3602 | + /* |
3603 | + * This file is part of mailpot |
3604 | + * |
3605 | + * Copyright 2020 - Manos Pitsidianakis |
3606 | + * |
3607 | + * This program is free software: you can redistribute it and/or modify |
3608 | + * it under the terms of the GNU Affero General Public License as |
3609 | + * published by the Free Software Foundation, either version 3 of the |
3610 | + * License, or (at your option) any later version. |
3611 | + * |
3612 | + * This program is distributed in the hope that it will be useful, |
3613 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3614 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3615 | + * GNU Affero General Public License for more details. |
3616 | + * |
3617 | + * You should have received a copy of the GNU Affero General Public License |
3618 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
3619 | + */ |
3620 | + |
3621 | + use std::io::Write; |
3622 | + use std::process::Command; |
3623 | + |
3624 | + fn main() { |
3625 | + println!("cargo:rerun-if-changed=src/schema.sql.m4"); |
3626 | + |
3627 | + let output = Command::new("m4") |
3628 | + .arg("./src/schema.sql.m4") |
3629 | + .output() |
3630 | + .unwrap(); |
3631 | + let mut file = std::fs::File::create("./src/schema.sql").unwrap(); |
3632 | + file.write_all(&output.stdout).unwrap(); |
3633 | + } |
3634 | diff --git a/core/src/config.rs b/core/src/config.rs |
3635 | new file mode 100644 |
3636 | index 0000000..2e02cc6 |
3637 | --- /dev/null |
3638 | +++ b/core/src/config.rs |
3639 | @@ -0,0 +1,109 @@ |
3640 | + /* |
3641 | + * This file is part of mailpot |
3642 | + * |
3643 | + * Copyright 2020 - Manos Pitsidianakis |
3644 | + * |
3645 | + * This program is free software: you can redistribute it and/or modify |
3646 | + * it under the terms of the GNU Affero General Public License as |
3647 | + * published by the Free Software Foundation, either version 3 of the |
3648 | + * License, or (at your option) any later version. |
3649 | + * |
3650 | + * This program is distributed in the hope that it will be useful, |
3651 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3652 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3653 | + * GNU Affero General Public License for more details. |
3654 | + * |
3655 | + * You should have received a copy of the GNU Affero General Public License |
3656 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
3657 | + */ |
3658 | + |
3659 | + use super::errors::*; |
3660 | + use chrono::prelude::*; |
3661 | + use std::cell::RefCell; |
3662 | + use std::io::{Read, Write}; |
3663 | + use std::os::unix::fs::PermissionsExt; |
3664 | + use std::path::PathBuf; |
3665 | + |
3666 | + thread_local!(pub static CONFIG: RefCell<Configuration> = RefCell::new(Configuration::new())); |
3667 | + |
3668 | + #[derive(Debug, Serialize, Deserialize, Clone)] |
3669 | + #[serde(tag = "type", content = "value")] |
3670 | + pub enum SendMail { |
3671 | + Smtp(melib::smtp::SmtpServerConf), |
3672 | + ShellCommand(String), |
3673 | + } |
3674 | + |
3675 | + #[derive(Debug, Serialize, Deserialize, Clone)] |
3676 | + pub struct Configuration { |
3677 | + pub send_mail: SendMail, |
3678 | + #[serde(default = "default_storage_fn")] |
3679 | + pub storage: String, |
3680 | + } |
3681 | + |
3682 | + impl Configuration { |
3683 | + pub(crate) fn new() -> Self { |
3684 | + Configuration { |
3685 | + send_mail: SendMail::ShellCommand(String::new()), |
3686 | + storage: "sqlite3".into(), |
3687 | + } |
3688 | + } |
3689 | + |
3690 | + pub fn init() -> Result<()> { |
3691 | + let path = |
3692 | + xdg::BaseDirectories::with_prefix("mailpot")?.place_config_file("config.toml")?; |
3693 | + if !path.exists() { |
3694 | + return Err(format!("Configuration file {} doesn't exist", path.display()).into()); |
3695 | + } |
3696 | + let mut s = String::new(); |
3697 | + let mut file = std::fs::File::open(&path)?; |
3698 | + file.read_to_string(&mut s)?; |
3699 | + let config: Configuration = toml::from_str(&s)?; |
3700 | + CONFIG.with(|f| { |
3701 | + *f.borrow_mut() = config; |
3702 | + }); |
3703 | + |
3704 | + Ok(()) |
3705 | + } |
3706 | + |
3707 | + pub fn data_directory() -> Result<PathBuf> { |
3708 | + Ok(xdg::BaseDirectories::with_prefix("mailpot")?.get_data_home()) |
3709 | + } |
3710 | + |
3711 | + pub fn save_message_to_path(msg: &str, mut path: PathBuf) -> Result<PathBuf> { |
3712 | + let now = Local::now().timestamp(); |
3713 | + path.push(format!("{}-failed.eml", now)); |
3714 | + |
3715 | + let mut file = std::fs::File::create(&path)?; |
3716 | + let metadata = file.metadata()?; |
3717 | + let mut permissions = metadata.permissions(); |
3718 | + |
3719 | + permissions.set_mode(0o600); // Read/write for owner only. |
3720 | + file.set_permissions(permissions)?; |
3721 | + file.write_all(msg.as_bytes())?; |
3722 | + file.flush()?; |
3723 | + Ok(path) |
3724 | + } |
3725 | + |
3726 | + pub fn save_message(msg: String) -> Result<PathBuf> { |
3727 | + match Configuration::data_directory() |
3728 | + .and_then(|path| Self::save_message_to_path(&msg, path)) |
3729 | + { |
3730 | + Ok(p) => return Ok(p), |
3731 | + Err(err) => { |
3732 | + eprintln!("{}", err); |
3733 | + } |
3734 | + }; |
3735 | + match Self::save_message_to_path(&msg, PathBuf::from(".")) { |
3736 | + Ok(p) => return Ok(p), |
3737 | + Err(err) => { |
3738 | + eprintln!("{}", err); |
3739 | + } |
3740 | + } |
3741 | + let temp_path = std::env::temp_dir(); |
3742 | + Self::save_message_to_path(&msg, temp_path) |
3743 | + } |
3744 | + } |
3745 | + |
3746 | + fn default_storage_fn() -> String { |
3747 | + "sqlite3".to_string() |
3748 | + } |
3749 | diff --git a/core/src/db.rs b/core/src/db.rs |
3750 | new file mode 100644 |
3751 | index 0000000..9e3abdf |
3752 | --- /dev/null |
3753 | +++ b/core/src/db.rs |
3754 | @@ -0,0 +1,604 @@ |
3755 | + /* |
3756 | + * This file is part of mailpot |
3757 | + * |
3758 | + * Copyright 2020 - Manos Pitsidianakis |
3759 | + * |
3760 | + * This program is free software: you can redistribute it and/or modify |
3761 | + * it under the terms of the GNU Affero General Public License as |
3762 | + * published by the Free Software Foundation, either version 3 of the |
3763 | + * License, or (at your option) any later version. |
3764 | + * |
3765 | + * This program is distributed in the hope that it will be useful, |
3766 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3767 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3768 | + * GNU Affero General Public License for more details. |
3769 | + * |
3770 | + * You should have received a copy of the GNU Affero General Public License |
3771 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
3772 | + */ |
3773 | + |
3774 | + use super::*; |
3775 | + use melib::Envelope; |
3776 | + use models::changesets::*; |
3777 | + use rusqlite::Connection as DbConnection; |
3778 | + use rusqlite::OptionalExtension; |
3779 | + use std::path::PathBuf; |
3780 | + |
3781 | + const DB_NAME: &str = "mpot.db"; |
3782 | + |
3783 | + pub struct Database { |
3784 | + pub connection: DbConnection, |
3785 | + } |
3786 | + |
3787 | + impl Database { |
3788 | + pub fn list_lists(&self) -> Result<Vec<DbVal<MailingList>>> { |
3789 | + let mut stmt = self.connection.prepare("SELECT * FROM mailing_lists;")?; |
3790 | + let list_iter = stmt.query_map([], |row| { |
3791 | + let pk = row.get("pk")?; |
3792 | + Ok(DbVal( |
3793 | + MailingList { |
3794 | + pk, |
3795 | + name: row.get("name")?, |
3796 | + id: row.get("id")?, |
3797 | + address: row.get("address")?, |
3798 | + description: row.get("description")?, |
3799 | + archive_url: row.get("archive_url")?, |
3800 | + }, |
3801 | + pk, |
3802 | + )) |
3803 | + })?; |
3804 | + |
3805 | + let mut ret = vec![]; |
3806 | + for list in list_iter { |
3807 | + let list = list?; |
3808 | + ret.push(list); |
3809 | + } |
3810 | + Ok(ret) |
3811 | + } |
3812 | + |
3813 | + pub fn get_list(&self, pk: i64) -> Result<Option<DbVal<MailingList>>> { |
3814 | + let mut stmt = self |
3815 | + .connection |
3816 | + .prepare("SELECT * FROM mailing_lists WHERE pk = ?;")?; |
3817 | + let ret = stmt |
3818 | + .query_row([&pk], |row| { |
3819 | + let pk = row.get("pk")?; |
3820 | + Ok(DbVal( |
3821 | + MailingList { |
3822 | + pk, |
3823 | + name: row.get("name")?, |
3824 | + id: row.get("id")?, |
3825 | + address: row.get("address")?, |
3826 | + description: row.get("description")?, |
3827 | + archive_url: row.get("archive_url")?, |
3828 | + }, |
3829 | + pk, |
3830 | + )) |
3831 | + }) |
3832 | + .optional()?; |
3833 | + |
3834 | + Ok(ret) |
3835 | + } |
3836 | + |
3837 | + pub fn create_list(&self, new_val: MailingList) -> Result<DbVal<MailingList>> { |
3838 | + let mut stmt = self |
3839 | + .connection |
3840 | + .prepare("INSERT INTO mailing_lists(name, id, address, description, archive_url) VALUES(?, ?, ?, ?, ?) RETURNING *;")?; |
3841 | + let ret = stmt.query_row( |
3842 | + rusqlite::params![ |
3843 | + &new_val.name, |
3844 | + &new_val.id, |
3845 | + &new_val.address, |
3846 | + new_val.description.as_ref(), |
3847 | + new_val.archive_url.as_ref(), |
3848 | + ], |
3849 | + |row| { |
3850 | + let pk = row.get("pk")?; |
3851 | + Ok(DbVal( |
3852 | + MailingList { |
3853 | + pk, |
3854 | + name: row.get("name")?, |
3855 | + id: row.get("id")?, |
3856 | + address: row.get("address")?, |
3857 | + description: row.get("description")?, |
3858 | + archive_url: row.get("archive_url")?, |
3859 | + }, |
3860 | + pk, |
3861 | + )) |
3862 | + }, |
3863 | + )?; |
3864 | + |
3865 | + trace!("create_list {:?}.", &ret); |
3866 | + Ok(ret) |
3867 | + } |
3868 | + |
3869 | + pub fn update_list(&self, _change_set: MailingListChangeset) -> Result<()> { |
3870 | + /* |
3871 | + diesel::update(mailing_lists::table) |
3872 | + .set(&set) |
3873 | + .execute(&self.connection)?; |
3874 | + */ |
3875 | + Ok(()) |
3876 | + } |
3877 | + |
3878 | + pub fn get_list_policy(&self, pk: i64) -> Result<Option<DbVal<PostPolicy>>> { |
3879 | + let mut stmt = self |
3880 | + .connection |
3881 | + .prepare("SELECT * FROM post_policy WHERE list = ?;")?; |
3882 | + let ret = stmt |
3883 | + .query_row([&pk], |row| { |
3884 | + let pk = row.get("pk")?; |
3885 | + Ok(DbVal( |
3886 | + PostPolicy { |
3887 | + pk, |
3888 | + list: row.get("list")?, |
3889 | + announce_only: row.get("announce_only")?, |
3890 | + subscriber_only: row.get("subscriber_only")?, |
3891 | + approval_needed: row.get("approval_needed")?, |
3892 | + }, |
3893 | + pk, |
3894 | + )) |
3895 | + }) |
3896 | + .optional()?; |
3897 | + |
3898 | + Ok(ret) |
3899 | + } |
3900 | + |
3901 | + pub fn get_list_owners(&self, pk: i64) -> Result<Vec<DbVal<ListOwner>>> { |
3902 | + let mut stmt = self |
3903 | + .connection |
3904 | + .prepare("SELECT * FROM list_owner WHERE list = ?;")?; |
3905 | + let list_iter = stmt.query_map([&pk], |row| { |
3906 | + let pk = row.get("pk")?; |
3907 | + Ok(DbVal( |
3908 | + ListOwner { |
3909 | + pk, |
3910 | + list: row.get("list")?, |
3911 | + address: row.get("address")?, |
3912 | + name: row.get("name")?, |
3913 | + }, |
3914 | + pk, |
3915 | + )) |
3916 | + })?; |
3917 | + |
3918 | + let mut ret = vec![]; |
3919 | + for list in list_iter { |
3920 | + let list = list?; |
3921 | + ret.push(list); |
3922 | + } |
3923 | + Ok(ret) |
3924 | + } |
3925 | + |
3926 | + pub fn list_members(&self, pk: i64) -> Result<Vec<DbVal<ListMembership>>> { |
3927 | + let mut stmt = self |
3928 | + .connection |
3929 | + .prepare("SELECT * FROM membership WHERE list = ?;")?; |
3930 | + let list_iter = stmt.query_map([&pk], |row| { |
3931 | + let pk = row.get("pk")?; |
3932 | + Ok(DbVal( |
3933 | + ListMembership { |
3934 | + pk: row.get("pk")?, |
3935 | + list: row.get("list")?, |
3936 | + address: row.get("address")?, |
3937 | + name: row.get("name")?, |
3938 | + digest: row.get("digest")?, |
3939 | + hide_address: row.get("hide_address")?, |
3940 | + receive_duplicates: row.get("receive_duplicates")?, |
3941 | + receive_own_posts: row.get("receive_own_posts")?, |
3942 | + receive_confirmation: row.get("receive_confirmation")?, |
3943 | + enabled: row.get("enabled")?, |
3944 | + }, |
3945 | + pk, |
3946 | + )) |
3947 | + })?; |
3948 | + |
3949 | + let mut ret = vec![]; |
3950 | + for list in list_iter { |
3951 | + let list = list?; |
3952 | + ret.push(list); |
3953 | + } |
3954 | + Ok(ret) |
3955 | + } |
3956 | + |
3957 | + pub fn add_member( |
3958 | + &self, |
3959 | + list_pk: i64, |
3960 | + mut new_val: ListMembership, |
3961 | + ) -> Result<DbVal<ListMembership>> { |
3962 | + new_val.list = list_pk; |
3963 | + let mut stmt = self |
3964 | + .connection |
3965 | + .prepare("INSERT INTO membership(list, address, name, enabled, digest, hide_address, receive_duplicates, receive_own_posts, receive_confirmation) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *;")?; |
3966 | + let ret = stmt.query_row( |
3967 | + rusqlite::params![ |
3968 | + &new_val.list, |
3969 | + &new_val.address, |
3970 | + &new_val.name, |
3971 | + &new_val.enabled, |
3972 | + &new_val.digest, |
3973 | + &new_val.hide_address, |
3974 | + &new_val.receive_duplicates, |
3975 | + &new_val.receive_own_posts, |
3976 | + &new_val.receive_confirmation |
3977 | + ], |
3978 | + |row| { |
3979 | + let pk = row.get("pk")?; |
3980 | + Ok(DbVal( |
3981 | + ListMembership { |
3982 | + pk, |
3983 | + list: row.get("list")?, |
3984 | + address: row.get("address")?, |
3985 | + name: row.get("name")?, |
3986 | + digest: row.get("digest")?, |
3987 | + hide_address: row.get("hide_address")?, |
3988 | + receive_duplicates: row.get("receive_duplicates")?, |
3989 | + receive_own_posts: row.get("receive_own_posts")?, |
3990 | + receive_confirmation: row.get("receive_confirmation")?, |
3991 | + enabled: row.get("enabled")?, |
3992 | + }, |
3993 | + pk, |
3994 | + )) |
3995 | + }, |
3996 | + )?; |
3997 | + |
3998 | + trace!("add_member {:?}.", &ret); |
3999 | + Ok(ret) |
4000 | + } |
4001 | + |
4002 | + pub fn add_candidate_member(&self, list_pk: i64, mut new_val: ListMembership) -> Result<i64> { |
4003 | + new_val.list = list_pk; |
4004 | + let mut stmt = self |
4005 | + .connection |
4006 | + .prepare("INSERT INTO candidate_membership(list, address, name, accepted) VALUES(?, ?, ?, ?) RETURNING pk;")?; |
4007 | + let ret = stmt.query_row( |
4008 | + rusqlite::params![&new_val.list, &new_val.address, &new_val.name, &false,], |
4009 | + |row| { |
4010 | + let pk: i64 = row.get("pk")?; |
4011 | + Ok(pk) |
4012 | + }, |
4013 | + )?; |
4014 | + |
4015 | + trace!("add_candidate_member {:?}.", &ret); |
4016 | + Ok(ret) |
4017 | + } |
4018 | + |
4019 | + pub fn accept_candidate_member(&mut self, pk: i64) -> Result<DbVal<ListMembership>> { |
4020 | + let tx = self.connection.transaction()?; |
4021 | + let mut stmt = tx |
4022 | + .prepare("INSERT INTO membership(list, address, name, enabled, digest, hide_address, receive_duplicates, receive_own_posts, receive_confirmation) FROM (SELECT list, address, name FROM candidate_membership WHERE pk = ?) RETURNING *;")?; |
4023 | + let ret = stmt.query_row(rusqlite::params![&pk], |row| { |
4024 | + let pk = row.get("pk")?; |
4025 | + Ok(DbVal( |
4026 | + ListMembership { |
4027 | + pk, |
4028 | + list: row.get("list")?, |
4029 | + address: row.get("address")?, |
4030 | + name: row.get("name")?, |
4031 | + digest: row.get("digest")?, |
4032 | + hide_address: row.get("hide_address")?, |
4033 | + receive_duplicates: row.get("receive_duplicates")?, |
4034 | + receive_own_posts: row.get("receive_own_posts")?, |
4035 | + receive_confirmation: row.get("receive_confirmation")?, |
4036 | + enabled: row.get("enabled")?, |
4037 | + }, |
4038 | + pk, |
4039 | + )) |
4040 | + })?; |
4041 | + drop(stmt); |
4042 | + tx.execute( |
4043 | + "UPDATE candidate_membership SET accepted = ? WHERE pk = ?;", |
4044 | + [&pk], |
4045 | + )?; |
4046 | + tx.commit()?; |
4047 | + |
4048 | + trace!("accept_candidate_member {:?}.", &ret); |
4049 | + Ok(ret) |
4050 | + } |
4051 | + |
4052 | + pub fn remove_member(&self, list_pk: i64, address: &str) -> Result<()> { |
4053 | + self.connection.execute( |
4054 | + "DELETE FROM membership WHERE list_pk = ? AND address = ?;", |
4055 | + rusqlite::params![&list_pk, &address], |
4056 | + )?; |
4057 | + |
4058 | + Ok(()) |
4059 | + } |
4060 | + |
4061 | + pub fn update_member(&self, _change_set: ListMembershipChangeset) -> Result<()> { |
4062 | + /* |
4063 | + diesel::update(membership::table) |
4064 | + .set(&set) |
4065 | + .execute(&self.connection)?; |
4066 | + */ |
4067 | + Ok(()) |
4068 | + } |
4069 | + |
4070 | + pub fn db_path() -> Result<PathBuf> { |
4071 | + let name = DB_NAME; |
4072 | + let data_dir = xdg::BaseDirectories::with_prefix("mailpot")?; |
4073 | + Ok(data_dir.place_data_file(name)?) |
4074 | + } |
4075 | + |
4076 | + pub fn open_db(db_path: PathBuf) -> Result<Self> { |
4077 | + if !db_path.exists() { |
4078 | + return Err("Database doesn't exist".into()); |
4079 | + } |
4080 | + Ok(Database { |
4081 | + connection: DbConnection::open(&db_path.to_str().unwrap())?, |
4082 | + }) |
4083 | + } |
4084 | + |
4085 | + pub fn open_or_create_db() -> Result<Self> { |
4086 | + let db_path = Self::db_path()?; |
4087 | + let mut set_mode = false; |
4088 | + if !db_path.exists() { |
4089 | + info!("Creating {} database in {}", DB_NAME, db_path.display()); |
4090 | + set_mode = true; |
4091 | + } |
4092 | + let conn = DbConnection::open(&db_path.to_str().unwrap())?; |
4093 | + if set_mode { |
4094 | + use std::os::unix::fs::PermissionsExt; |
4095 | + let file = std::fs::File::open(&db_path)?; |
4096 | + let metadata = file.metadata()?; |
4097 | + let mut permissions = metadata.permissions(); |
4098 | + |
4099 | + permissions.set_mode(0o600); // Read/write for owner only. |
4100 | + file.set_permissions(permissions)?; |
4101 | + } |
4102 | + |
4103 | + Ok(Database { connection: conn }) |
4104 | + } |
4105 | + |
4106 | + pub fn get_list_filters( |
4107 | + &self, |
4108 | + _list: &DbVal<MailingList>, |
4109 | + ) -> Vec<Box<dyn crate::mail::message_filters::PostFilter>> { |
4110 | + use crate::mail::message_filters::*; |
4111 | + vec![ |
4112 | + Box::new(FixCRLF), |
4113 | + Box::new(PostRightsCheck), |
4114 | + Box::new(AddListHeaders), |
4115 | + Box::new(FinalizeRecipients), |
4116 | + ] |
4117 | + } |
4118 | + |
4119 | + pub fn insert_post(&self, list_pk: i64, message: &[u8], env: &Envelope) -> Result<i64> { |
4120 | + let address = env.from()[0].to_string(); |
4121 | + let message_id = env.message_id_display(); |
4122 | + let mut stmt = self.connection.prepare( |
4123 | + "INSERT INTO post(list, address, message_id, message) VALUES(?, ?, ?, ?) RETURNING pk;", |
4124 | + )?; |
4125 | + let pk = stmt.query_row( |
4126 | + rusqlite::params![&list_pk, &address, &message_id, &message], |
4127 | + |row| { |
4128 | + let pk: i64 = row.get("pk")?; |
4129 | + Ok(pk) |
4130 | + }, |
4131 | + )?; |
4132 | + |
4133 | + trace!( |
4134 | + "insert_post list_pk {}, from {:?} message_id {:?} post_pk {}.", |
4135 | + list_pk, |
4136 | + address, |
4137 | + message_id, |
4138 | + pk |
4139 | + ); |
4140 | + Ok(pk) |
4141 | + } |
4142 | + |
4143 | + pub fn post(&self, env: Envelope, raw: &[u8], _dry_run: bool) -> Result<()> { |
4144 | + trace!("Received envelope to post: {:#?}", &env); |
4145 | + let tos = env.to().to_vec(); |
4146 | + if tos.is_empty() { |
4147 | + return Err("Envelope To: field is empty!".into()); |
4148 | + } |
4149 | + if env.from().is_empty() { |
4150 | + return Err("Envelope From: field is empty!".into()); |
4151 | + } |
4152 | + let mut lists = self.list_lists()?; |
4153 | + for t in &tos { |
4154 | + if let Some((addr, subaddr)) = t.subaddress("+") { |
4155 | + lists.retain(|list| { |
4156 | + if !addr.contains_address(&list.list_address()) { |
4157 | + return true; |
4158 | + } |
4159 | + if let Err(err) = self.request( |
4160 | + list, |
4161 | + match subaddr.as_str() { |
4162 | + "subscribe" | "request" if env.subject().trim() == "subscribe" => { |
4163 | + ListRequest::Subscribe |
4164 | + } |
4165 | + "unsubscribe" | "request" if env.subject().trim() == "unsubscribe" => { |
4166 | + ListRequest::Unsubscribe |
4167 | + } |
4168 | + "request" => ListRequest::Other(env.subject().trim().to_string()), |
4169 | + _ => { |
4170 | + trace!( |
4171 | + "unknown action = {} for addresses {:?} in list {}", |
4172 | + subaddr, |
4173 | + env.from(), |
4174 | + list |
4175 | + ); |
4176 | + ListRequest::Other(subaddr.trim().to_string()) |
4177 | + } |
4178 | + }, |
4179 | + &env, |
4180 | + raw, |
4181 | + ) { |
4182 | + info!("Processing request returned error: {}", err); |
4183 | + } |
4184 | + false |
4185 | + }); |
4186 | + } |
4187 | + } |
4188 | + |
4189 | + lists.retain(|list| { |
4190 | + trace!( |
4191 | + "Is post related to list {}? {}", |
4192 | + &list, |
4193 | + tos.iter().any(|a| a.contains_address(&list.list_address())) |
4194 | + ); |
4195 | + |
4196 | + tos.iter().any(|a| a.contains_address(&list.list_address())) |
4197 | + }); |
4198 | + if lists.is_empty() { |
4199 | + return Ok(()); |
4200 | + } |
4201 | + |
4202 | + let mut configuration = crate::config::Configuration::new(); |
4203 | + crate::config::CONFIG.with(|f| { |
4204 | + configuration = f.borrow().clone(); |
4205 | + }); |
4206 | + trace!("Configuration is {:#?}", &configuration); |
4207 | + use crate::mail::{ListContext, Post, PostAction}; |
4208 | + for mut list in lists { |
4209 | + trace!("Examining list {}", list.list_id()); |
4210 | + let post_pk = self.insert_post(list.pk, raw, &env)?; |
4211 | + let filters = self.get_list_filters(&list); |
4212 | + let memberships = self.list_members(list.pk)?; |
4213 | + trace!("List members {:#?}", &memberships); |
4214 | + let mut list_ctx = ListContext { |
4215 | + policy: self.get_list_policy(list.pk)?, |
4216 | + list_owners: self.get_list_owners(list.pk)?, |
4217 | + list: &mut list, |
4218 | + memberships: &memberships, |
4219 | + scheduled_jobs: vec![], |
4220 | + }; |
4221 | + let mut post = Post { |
4222 | + pk: post_pk, |
4223 | + from: env.from()[0].clone(), |
4224 | + bytes: raw.to_vec(), |
4225 | + to: env.to().to_vec(), |
4226 | + action: PostAction::Hold, |
4227 | + }; |
4228 | + let result = filters |
4229 | + .into_iter() |
4230 | + .fold(Ok((&mut post, &mut list_ctx)), |p, f| { |
4231 | + p.and_then(|(p, c)| f.feed(p, c)) |
4232 | + }); |
4233 | + trace!("result {:#?}", result); |
4234 | + |
4235 | + let Post { bytes, action, .. } = post; |
4236 | + match configuration.send_mail { |
4237 | + crate::config::SendMail::Smtp(ref smtp_conf) => { |
4238 | + let smtp_conf = smtp_conf.clone(); |
4239 | + use melib::futures; |
4240 | + use melib::smol; |
4241 | + use melib::smtp::*; |
4242 | + let mut conn = smol::future::block_on(smol::spawn( |
4243 | + SmtpConnection::new_connection(smtp_conf.clone()), |
4244 | + ))?; |
4245 | + match action { |
4246 | + PostAction::Accept => { |
4247 | + for job in list_ctx.scheduled_jobs.iter() { |
4248 | + if let crate::mail::MailJob::Send { |
4249 | + message_pk: _, |
4250 | + recipients, |
4251 | + } = job |
4252 | + { |
4253 | + futures::executor::block_on(conn.mail_transaction( |
4254 | + &String::from_utf8_lossy(&bytes), |
4255 | + Some(recipients), |
4256 | + ))?; |
4257 | + } |
4258 | + } |
4259 | + /* - Save digest metadata in database */ |
4260 | + } |
4261 | + PostAction::Reject { reason: _ } => { |
4262 | + /* - Notify submitter */ |
4263 | + //futures::executor::block_on(conn.mail_transaction(&post.bytes, b)).unwrap(); |
4264 | + } |
4265 | + PostAction::Defer { reason: _ } => { |
4266 | + /* - Notify submitter |
4267 | + * - Save in database */ |
4268 | + } |
4269 | + PostAction::Hold => { /* - Save in database */ } |
4270 | + } |
4271 | + } |
4272 | + _ => {} |
4273 | + } |
4274 | + } |
4275 | + |
4276 | + Ok(()) |
4277 | + } |
4278 | + |
4279 | + pub fn request( |
4280 | + &self, |
4281 | + list: &DbVal<MailingList>, |
4282 | + request: ListRequest, |
4283 | + env: &Envelope, |
4284 | + _raw: &[u8], |
4285 | + ) -> Result<()> { |
4286 | + match request { |
4287 | + ListRequest::Subscribe => { |
4288 | + trace!( |
4289 | + "subscribe action for addresses {:?} in list {}", |
4290 | + env.from(), |
4291 | + list |
4292 | + ); |
4293 | + |
4294 | + let list_policy = self.get_list_policy(list.pk)?; |
4295 | + let approval_needed = list_policy |
4296 | + .as_ref() |
4297 | + .map(|p| p.approval_needed) |
4298 | + .unwrap_or(false); |
4299 | + for f in env.from() { |
4300 | + let membership = ListMembership { |
4301 | + pk: 0, |
4302 | + list: list.pk, |
4303 | + address: f.get_email(), |
4304 | + name: f.get_display_name(), |
4305 | + digest: false, |
4306 | + hide_address: false, |
4307 | + receive_duplicates: true, |
4308 | + receive_own_posts: false, |
4309 | + receive_confirmation: true, |
4310 | + enabled: !approval_needed, |
4311 | + }; |
4312 | + if approval_needed { |
4313 | + match self.add_candidate_member(list.pk, membership) { |
4314 | + Ok(_) => {} |
4315 | + Err(_err) => {} |
4316 | + } |
4317 | + //FIXME: send notification to list-owner |
4318 | + } else if let Err(_err) = self.add_member(list.pk, membership) { |
4319 | + //FIXME: send failure notice to f |
4320 | + } else { |
4321 | + //FIXME: send success notice |
4322 | + } |
4323 | + } |
4324 | + } |
4325 | + ListRequest::Unsubscribe => { |
4326 | + trace!( |
4327 | + "unsubscribe action for addresses {:?} in list {}", |
4328 | + env.from(), |
4329 | + list |
4330 | + ); |
4331 | + for f in env.from() { |
4332 | + if let Err(_err) = self.remove_member(list.pk, &f.get_email()) { |
4333 | + //FIXME: send failure notice to f |
4334 | + } else { |
4335 | + //FIXME: send success notice to f |
4336 | + } |
4337 | + } |
4338 | + } |
4339 | + ListRequest::Other(ref req) if req == "owner" => { |
4340 | + trace!( |
4341 | + "list-owner mail action for addresses {:?} in list {}", |
4342 | + env.from(), |
4343 | + list |
4344 | + ); |
4345 | + //FIXME: mail to list-owner |
4346 | + } |
4347 | + ListRequest::Other(ref req) => { |
4348 | + trace!( |
4349 | + "unknown request action {} for addresses {:?} in list {}", |
4350 | + req, |
4351 | + env.from(), |
4352 | + list |
4353 | + ); |
4354 | + } |
4355 | + } |
4356 | + Ok(()) |
4357 | + } |
4358 | + } |
4359 | diff --git a/core/src/errors.rs b/core/src/errors.rs |
4360 | new file mode 100644 |
4361 | index 0000000..ae61ca5 |
4362 | --- /dev/null |
4363 | +++ b/core/src/errors.rs |
4364 | @@ -0,0 +1,30 @@ |
4365 | + /* |
4366 | + * This file is part of mailpot |
4367 | + * |
4368 | + * Copyright 2020 - Manos Pitsidianakis |
4369 | + * |
4370 | + * This program is free software: you can redistribute it and/or modify |
4371 | + * it under the terms of the GNU Affero General Public License as |
4372 | + * published by the Free Software Foundation, either version 3 of the |
4373 | + * License, or (at your option) any later version. |
4374 | + * |
4375 | + * This program is distributed in the hope that it will be useful, |
4376 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4377 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4378 | + * GNU Affero General Public License for more details. |
4379 | + * |
4380 | + * You should have received a copy of the GNU Affero General Public License |
4381 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4382 | + */ |
4383 | + |
4384 | + // Create the Error, ErrorKind, ResultExt, and Result types |
4385 | + error_chain! { |
4386 | + foreign_links { |
4387 | + Sql(rusqlite::Error); |
4388 | + Io(::std::io::Error); |
4389 | + Xdg(xdg::BaseDirectoriesError); |
4390 | + Melib(melib::error::MeliError); |
4391 | + Configuration(toml::de::Error); |
4392 | + SerdeJson(serde_json::Error); |
4393 | + } |
4394 | + } |
4395 | diff --git a/core/src/lib.rs b/core/src/lib.rs |
4396 | new file mode 100644 |
4397 | index 0000000..f997cc3 |
4398 | --- /dev/null |
4399 | +++ b/core/src/lib.rs |
4400 | @@ -0,0 +1,38 @@ |
4401 | + /* |
4402 | + * This file is part of mailpot |
4403 | + * |
4404 | + * Copyright 2020 - Manos Pitsidianakis |
4405 | + * |
4406 | + * This program is free software: you can redistribute it and/or modify |
4407 | + * it under the terms of the GNU Affero General Public License as |
4408 | + * published by the Free Software Foundation, either version 3 of the |
4409 | + * License, or (at your option) any later version. |
4410 | + * |
4411 | + * This program is distributed in the hope that it will be useful, |
4412 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4413 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4414 | + * GNU Affero General Public License for more details. |
4415 | + * |
4416 | + * You should have received a copy of the GNU Affero General Public License |
4417 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4418 | + */ |
4419 | + // `error_chain!` can recurse deeply |
4420 | + #![recursion_limit = "1024"] |
4421 | + //#![warn(missing_docs)] |
4422 | + |
4423 | + use log::{info, trace}; |
4424 | + #[macro_use] |
4425 | + extern crate error_chain; |
4426 | + #[macro_use] |
4427 | + pub extern crate serde; |
4428 | + |
4429 | + pub use melib; |
4430 | + pub use serde_json; |
4431 | + |
4432 | + pub mod config; |
4433 | + pub mod mail; |
4434 | + pub mod models; |
4435 | + use models::*; |
4436 | + pub mod errors; |
4437 | + use errors::*; |
4438 | + pub mod db; |
4439 | diff --git a/core/src/mail.rs b/core/src/mail.rs |
4440 | new file mode 100644 |
4441 | index 0000000..6d8282c |
4442 | --- /dev/null |
4443 | +++ b/core/src/mail.rs |
4444 | @@ -0,0 +1,85 @@ |
4445 | + /* |
4446 | + * This file is part of mailpot |
4447 | + * |
4448 | + * Copyright 2020 - Manos Pitsidianakis |
4449 | + * |
4450 | + * This program is free software: you can redistribute it and/or modify |
4451 | + * it under the terms of the GNU Affero General Public License as |
4452 | + * published by the Free Software Foundation, either version 3 of the |
4453 | + * License, or (at your option) any later version. |
4454 | + * |
4455 | + * This program is distributed in the hope that it will be useful, |
4456 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4457 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4458 | + * GNU Affero General Public License for more details. |
4459 | + * |
4460 | + * You should have received a copy of the GNU Affero General Public License |
4461 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4462 | + */ |
4463 | + |
4464 | + use super::*; |
4465 | + use melib::Address; |
4466 | + pub mod message_filters; |
4467 | + |
4468 | + #[derive(Debug)] |
4469 | + pub enum PostAction { |
4470 | + Hold, |
4471 | + Accept, |
4472 | + Reject { reason: String }, |
4473 | + Defer { reason: String }, |
4474 | + } |
4475 | + |
4476 | + #[derive(Debug)] |
4477 | + pub struct ListContext<'list> { |
4478 | + pub list: &'list MailingList, |
4479 | + pub list_owners: Vec<DbVal<ListOwner>>, |
4480 | + pub memberships: &'list [DbVal<ListMembership>], |
4481 | + pub policy: Option<DbVal<PostPolicy>>, |
4482 | + pub scheduled_jobs: Vec<MailJob>, |
4483 | + } |
4484 | + |
4485 | + ///Post to be considered by the list's `PostFilter` stack. |
4486 | + pub struct Post { |
4487 | + pub pk: i64, |
4488 | + pub from: Address, |
4489 | + pub bytes: Vec<u8>, |
4490 | + pub to: Vec<Address>, |
4491 | + pub action: PostAction, |
4492 | + } |
4493 | + |
4494 | + impl core::fmt::Debug for Post { |
4495 | + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { |
4496 | + fmt.debug_struct("Post") |
4497 | + .field("pk", &self.pk) |
4498 | + .field("from", &self.from) |
4499 | + .field("bytes", &format_args!("{} bytes", self.bytes.len())) |
4500 | + .field("to", &self.to.as_slice()) |
4501 | + .field("action", &self.action) |
4502 | + .finish() |
4503 | + } |
4504 | + } |
4505 | + |
4506 | + #[derive(Debug)] |
4507 | + pub enum MailJob { |
4508 | + Send { |
4509 | + message_pk: i64, |
4510 | + recipients: Vec<Address>, |
4511 | + }, |
4512 | + Relay { |
4513 | + message: Vec<u8>, |
4514 | + recipients: Vec<Address>, |
4515 | + }, |
4516 | + Error { |
4517 | + description: String, |
4518 | + }, |
4519 | + StoreDigest { |
4520 | + message_pk: i64, |
4521 | + recipients: Vec<Address>, |
4522 | + }, |
4523 | + ConfirmSubscription { |
4524 | + recipient: Address, |
4525 | + }, |
4526 | + ConfirmUnsubscription { |
4527 | + recipient: Address, |
4528 | + }, |
4529 | + } |
4530 | diff --git a/core/src/mail/message_filters.rs b/core/src/mail/message_filters.rs |
4531 | new file mode 100644 |
4532 | index 0000000..dbe157a |
4533 | --- /dev/null |
4534 | +++ b/core/src/mail/message_filters.rs |
4535 | @@ -0,0 +1,206 @@ |
4536 | + /* |
4537 | + * This file is part of mailpot |
4538 | + * |
4539 | + * Copyright 2020 - Manos Pitsidianakis |
4540 | + * |
4541 | + * This program is free software: you can redistribute it and/or modify |
4542 | + * it under the terms of the GNU Affero General Public License as |
4543 | + * published by the Free Software Foundation, either version 3 of the |
4544 | + * License, or (at your option) any later version. |
4545 | + * |
4546 | + * This program is distributed in the hope that it will be useful, |
4547 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4548 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4549 | + * GNU Affero General Public License for more details. |
4550 | + * |
4551 | + * You should have received a copy of the GNU Affero General Public License |
4552 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4553 | + */ |
4554 | + |
4555 | + use super::*; |
4556 | + |
4557 | + ///Filter that modifies and/or verifies a post candidate. On rejection, return a string |
4558 | + ///describing the error and optionally set `post.action` to `Reject` or `Defer` |
4559 | + pub trait PostFilter { |
4560 | + fn feed<'p, 'list>( |
4561 | + self: Box<Self>, |
4562 | + post: &'p mut Post, |
4563 | + ctx: &'p mut ListContext<'list>, |
4564 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()>; |
4565 | + } |
4566 | + |
4567 | + ///Check that submitter can post to list, for now it accepts everything. |
4568 | + pub struct PostRightsCheck; |
4569 | + impl PostFilter for PostRightsCheck { |
4570 | + fn feed<'p, 'list>( |
4571 | + self: Box<Self>, |
4572 | + post: &'p mut Post, |
4573 | + ctx: &'p mut ListContext<'list>, |
4574 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()> { |
4575 | + trace!("Running PostRightsCheck filter"); |
4576 | + if let Some(ref policy) = ctx.policy { |
4577 | + if policy.announce_only { |
4578 | + trace!("post policy is announce_only"); |
4579 | + let owner_addresses = ctx |
4580 | + .list_owners |
4581 | + .iter() |
4582 | + .map(|lo| lo.into_address()) |
4583 | + .collect::<Vec<Address>>(); |
4584 | + trace!("Owner addresses are: {:#?}", &owner_addresses); |
4585 | + trace!("Envelope from is: {:?}", &post.from); |
4586 | + if !owner_addresses.iter().any(|addr| *addr == post.from) { |
4587 | + trace!("Envelope From does not include any owner"); |
4588 | + post.action = PostAction::Reject { |
4589 | + reason: "You are not allowed to post on this list.".to_string(), |
4590 | + }; |
4591 | + return Err(()); |
4592 | + } |
4593 | + } else if policy.subscriber_only { |
4594 | + trace!("post policy is subscriber_only"); |
4595 | + let email_from = post.from.get_email(); |
4596 | + trace!("post from is {:?}", &email_from); |
4597 | + trace!("post memberships are {:#?}", &ctx.memberships); |
4598 | + if !ctx.memberships.iter().any(|lm| lm.address == email_from) { |
4599 | + trace!("Envelope from is not subscribed to this list"); |
4600 | + post.action = PostAction::Reject { |
4601 | + reason: "Only subscribers can post to this list.".to_string(), |
4602 | + }; |
4603 | + return Err(()); |
4604 | + } |
4605 | + } else if policy.approval_needed { |
4606 | + trace!("post policy says approval_needed"); |
4607 | + post.action = PostAction::Defer { |
4608 | + reason: "Your posting has been deferred. Approval from the list's moderators is required before it is submitted.".to_string(), |
4609 | + }; |
4610 | + } |
4611 | + } |
4612 | + Ok((post, ctx)) |
4613 | + } |
4614 | + } |
4615 | + |
4616 | + ///Ensure message contains only `\r\n` line terminators, required by SMTP. |
4617 | + pub struct FixCRLF; |
4618 | + impl PostFilter for FixCRLF { |
4619 | + fn feed<'p, 'list>( |
4620 | + self: Box<Self>, |
4621 | + post: &'p mut Post, |
4622 | + ctx: &'p mut ListContext<'list>, |
4623 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()> { |
4624 | + trace!("Running FixCRLF filter"); |
4625 | + use std::io::prelude::*; |
4626 | + let mut new_vec = Vec::with_capacity(post.bytes.len()); |
4627 | + for line in post.bytes.lines() { |
4628 | + new_vec.extend_from_slice(line.unwrap().as_bytes()); |
4629 | + new_vec.extend_from_slice(b"\r\n"); |
4630 | + } |
4631 | + post.bytes = new_vec; |
4632 | + Ok((post, ctx)) |
4633 | + } |
4634 | + } |
4635 | + |
4636 | + ///Add `List-*` headers |
4637 | + pub struct AddListHeaders; |
4638 | + impl PostFilter for AddListHeaders { |
4639 | + fn feed<'p, 'list>( |
4640 | + self: Box<Self>, |
4641 | + post: &'p mut Post, |
4642 | + ctx: &'p mut ListContext<'list>, |
4643 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()> { |
4644 | + trace!("Running AddListHeaders filter"); |
4645 | + let (mut headers, body) = melib::email::parser::mail(&post.bytes).unwrap(); |
4646 | + let list_id = ctx.list.list_id(); |
4647 | + headers.push((&b"List-ID"[..], list_id.as_bytes())); |
4648 | + let list_post = ctx.list.list_post(); |
4649 | + let list_unsubscribe = ctx.list.list_unsubscribe(); |
4650 | + let list_archive = ctx.list.list_archive(); |
4651 | + if let Some(post) = list_post.as_ref() { |
4652 | + headers.push((&b"List-Post"[..], post.as_bytes())); |
4653 | + } |
4654 | + if let Some(unsubscribe) = list_unsubscribe.as_ref() { |
4655 | + headers.push((&b"List-Unsubscribe"[..], unsubscribe.as_bytes())); |
4656 | + } |
4657 | + if let Some(archive) = list_archive.as_ref() { |
4658 | + headers.push((&b"List-Archive"[..], archive.as_bytes())); |
4659 | + } |
4660 | + let mut new_vec = Vec::with_capacity( |
4661 | + headers |
4662 | + .iter() |
4663 | + .map(|(h, v)| h.len() + v.len() + ": \r\n".len()) |
4664 | + .sum::<usize>() |
4665 | + + "\r\n\r\n".len() |
4666 | + + body.len(), |
4667 | + ); |
4668 | + for (h, v) in headers { |
4669 | + new_vec.extend_from_slice(h); |
4670 | + new_vec.extend_from_slice(b": "); |
4671 | + new_vec.extend_from_slice(v); |
4672 | + new_vec.extend_from_slice(b"\r\n"); |
4673 | + } |
4674 | + new_vec.extend_from_slice(b"\r\n\r\n"); |
4675 | + new_vec.extend_from_slice(body); |
4676 | + |
4677 | + post.bytes = new_vec; |
4678 | + Ok((post, ctx)) |
4679 | + } |
4680 | + } |
4681 | + |
4682 | + ///Adds `Archived-At` field, if configured. |
4683 | + pub struct ArchivedAtLink; |
4684 | + impl PostFilter for ArchivedAtLink { |
4685 | + fn feed<'p, 'list>( |
4686 | + self: Box<Self>, |
4687 | + post: &'p mut Post, |
4688 | + ctx: &'p mut ListContext<'list>, |
4689 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()> { |
4690 | + trace!("Running ArchivedAtLink filter"); |
4691 | + Ok((post, ctx)) |
4692 | + } |
4693 | + } |
4694 | + |
4695 | + ///Assuming there are no more changes to be done on the post, it finalizes which list members |
4696 | + ///will receive the post in `post.action` field. |
4697 | + pub struct FinalizeRecipients; |
4698 | + impl PostFilter for FinalizeRecipients { |
4699 | + fn feed<'p, 'list>( |
4700 | + self: Box<Self>, |
4701 | + post: &'p mut Post, |
4702 | + ctx: &'p mut ListContext<'list>, |
4703 | + ) -> std::result::Result<(&'p mut Post, &'p mut ListContext<'list>), ()> { |
4704 | + trace!("Running FinalizeRecipients filter"); |
4705 | + let mut recipients = vec![]; |
4706 | + let mut digests = vec![]; |
4707 | + let email_from = post.from.get_email(); |
4708 | + for member in ctx.memberships { |
4709 | + trace!("examining member {:?}", &member); |
4710 | + if member.address != email_from { |
4711 | + trace!("member is submitter"); |
4712 | + } |
4713 | + if member.digest { |
4714 | + if member.address != email_from || member.receive_own_posts { |
4715 | + trace!("Member gets digest"); |
4716 | + digests.push(member.into_address()); |
4717 | + } |
4718 | + continue; |
4719 | + } |
4720 | + if member.address != email_from || member.receive_own_posts { |
4721 | + trace!("Member gets copy"); |
4722 | + recipients.push(member.into_address()); |
4723 | + } |
4724 | + // TODO: |
4725 | + // - check for duplicates (To,Cc,Bcc) |
4726 | + // - send confirmation to submitter |
4727 | + } |
4728 | + ctx.scheduled_jobs.push(MailJob::Send { |
4729 | + message_pk: post.pk, |
4730 | + recipients, |
4731 | + }); |
4732 | + if !digests.is_empty() { |
4733 | + ctx.scheduled_jobs.push(MailJob::StoreDigest { |
4734 | + message_pk: post.pk, |
4735 | + recipients: digests, |
4736 | + }); |
4737 | + } |
4738 | + post.action = PostAction::Accept; |
4739 | + Ok((post, ctx)) |
4740 | + } |
4741 | + } |
4742 | diff --git a/core/src/models.rs b/core/src/models.rs |
4743 | new file mode 100644 |
4744 | index 0000000..330db8f |
4745 | --- /dev/null |
4746 | +++ b/core/src/models.rs |
4747 | @@ -0,0 +1,226 @@ |
4748 | + /* |
4749 | + * This file is part of mailpot |
4750 | + * |
4751 | + * Copyright 2020 - Manos Pitsidianakis |
4752 | + * |
4753 | + * This program is free software: you can redistribute it and/or modify |
4754 | + * it under the terms of the GNU Affero General Public License as |
4755 | + * published by the Free Software Foundation, either version 3 of the |
4756 | + * License, or (at your option) any later version. |
4757 | + * |
4758 | + * This program is distributed in the hope that it will be useful, |
4759 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4760 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4761 | + * GNU Affero General Public License for more details. |
4762 | + * |
4763 | + * You should have received a copy of the GNU Affero General Public License |
4764 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4765 | + */ |
4766 | + |
4767 | + use super::*; |
4768 | + pub mod changesets; |
4769 | + |
4770 | + use melib::email::Address; |
4771 | + |
4772 | + pub struct DbVal<T>(pub T, pub i64); |
4773 | + |
4774 | + impl<T> std::ops::Deref for DbVal<T> { |
4775 | + type Target = T; |
4776 | + fn deref(&self) -> &T { |
4777 | + &self.0 |
4778 | + } |
4779 | + } |
4780 | + |
4781 | + impl<T> std::fmt::Display for DbVal<T> |
4782 | + where |
4783 | + T: std::fmt::Display, |
4784 | + { |
4785 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4786 | + write!(fmt, "{}", self.0) |
4787 | + } |
4788 | + } |
4789 | + |
4790 | + impl<T> std::fmt::Debug for DbVal<T> |
4791 | + where |
4792 | + T: std::fmt::Debug, |
4793 | + { |
4794 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4795 | + write!(fmt, "{:?}", self.0) |
4796 | + } |
4797 | + } |
4798 | + |
4799 | + impl<T> serde::Serialize for DbVal<T> |
4800 | + where |
4801 | + T: serde::Serialize, |
4802 | + { |
4803 | + fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> |
4804 | + where |
4805 | + S: serde::Serializer, |
4806 | + { |
4807 | + self.0.serialize(serializer) |
4808 | + } |
4809 | + } |
4810 | + |
4811 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
4812 | + pub struct MailingList { |
4813 | + pub pk: i64, |
4814 | + pub name: String, |
4815 | + pub id: String, |
4816 | + pub address: String, |
4817 | + pub description: Option<String>, |
4818 | + pub archive_url: Option<String>, |
4819 | + } |
4820 | + |
4821 | + impl std::fmt::Display for MailingList { |
4822 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4823 | + if let Some(description) = self.description.as_ref() { |
4824 | + write!( |
4825 | + fmt, |
4826 | + "[#{} {}] {} <{}>: {}", |
4827 | + self.pk, self.id, self.name, self.address, description |
4828 | + ) |
4829 | + } else { |
4830 | + write!( |
4831 | + fmt, |
4832 | + "[#{} {}] {} <{}>", |
4833 | + self.pk, self.id, self.name, self.address |
4834 | + ) |
4835 | + } |
4836 | + } |
4837 | + } |
4838 | + |
4839 | + impl MailingList { |
4840 | + pub fn list_id(&self) -> String { |
4841 | + format!("\"{}\" <{}>", self.name, self.address) |
4842 | + } |
4843 | + |
4844 | + pub fn list_post(&self) -> Option<String> { |
4845 | + Some(format!("<mailto:{}>", self.address)) |
4846 | + } |
4847 | + |
4848 | + pub fn list_unsubscribe(&self) -> Option<String> { |
4849 | + let p = self.address.split('@').collect::<Vec<&str>>(); |
4850 | + Some(format!( |
4851 | + "<mailto:{}-request@{}?subject=unsubscribe>", |
4852 | + p[0], p[1] |
4853 | + )) |
4854 | + } |
4855 | + |
4856 | + pub fn list_archive(&self) -> Option<String> { |
4857 | + self.archive_url.as_ref().map(|url| format!("<{}>", url)) |
4858 | + } |
4859 | + |
4860 | + pub fn list_address(&self) -> Address { |
4861 | + Address::new(Some(self.name.clone()), self.address.clone()) |
4862 | + } |
4863 | + } |
4864 | + |
4865 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
4866 | + pub struct ListMembership { |
4867 | + pub pk: i64, |
4868 | + pub list: i64, |
4869 | + pub address: String, |
4870 | + pub name: Option<String>, |
4871 | + pub digest: bool, |
4872 | + pub hide_address: bool, |
4873 | + pub receive_duplicates: bool, |
4874 | + pub receive_own_posts: bool, |
4875 | + pub receive_confirmation: bool, |
4876 | + pub enabled: bool, |
4877 | + } |
4878 | + |
4879 | + impl std::fmt::Display for ListMembership { |
4880 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4881 | + write!( |
4882 | + fmt, |
4883 | + "{} [digest: {}, hide_address: {} {}]", |
4884 | + self.into_address(), |
4885 | + self.digest, |
4886 | + self.hide_address, |
4887 | + if self.enabled { |
4888 | + "enabled" |
4889 | + } else { |
4890 | + "not enabled" |
4891 | + }, |
4892 | + ) |
4893 | + } |
4894 | + } |
4895 | + |
4896 | + impl ListMembership { |
4897 | + pub fn into_address(&self) -> Address { |
4898 | + Address::new(self.name.clone(), self.address.clone()) |
4899 | + } |
4900 | + } |
4901 | + |
4902 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
4903 | + pub struct PostPolicy { |
4904 | + pub pk: i64, |
4905 | + pub list: i64, |
4906 | + pub announce_only: bool, |
4907 | + pub subscriber_only: bool, |
4908 | + pub approval_needed: bool, |
4909 | + } |
4910 | + |
4911 | + impl std::fmt::Display for PostPolicy { |
4912 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4913 | + write!(fmt, "{:?}", self) |
4914 | + } |
4915 | + } |
4916 | + |
4917 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
4918 | + pub struct ListOwner { |
4919 | + pub pk: i64, |
4920 | + pub list: i64, |
4921 | + pub address: String, |
4922 | + pub name: Option<String>, |
4923 | + } |
4924 | + |
4925 | + impl std::fmt::Display for ListOwner { |
4926 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4927 | + write!(fmt, "[#{} {}] {}", self.pk, self.list, self.into_address()) |
4928 | + } |
4929 | + } |
4930 | + |
4931 | + impl From<ListOwner> for ListMembership { |
4932 | + fn from(val: ListOwner) -> ListMembership { |
4933 | + ListMembership { |
4934 | + pk: 0, |
4935 | + list: val.list, |
4936 | + address: val.address, |
4937 | + name: val.name, |
4938 | + digest: false, |
4939 | + hide_address: false, |
4940 | + receive_duplicates: true, |
4941 | + receive_own_posts: false, |
4942 | + receive_confirmation: true, |
4943 | + enabled: true, |
4944 | + } |
4945 | + } |
4946 | + } |
4947 | + |
4948 | + impl ListOwner { |
4949 | + pub fn into_address(&self) -> Address { |
4950 | + Address::new(self.name.clone(), self.address.clone()) |
4951 | + } |
4952 | + } |
4953 | + |
4954 | + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] |
4955 | + pub enum ListRequest { |
4956 | + Subscribe, |
4957 | + Unsubscribe, |
4958 | + Other(String), |
4959 | + } |
4960 | + |
4961 | + impl std::fmt::Display for ListRequest { |
4962 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
4963 | + write!(fmt, "{:?}", self) |
4964 | + } |
4965 | + } |
4966 | + |
4967 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
4968 | + pub struct NewListPost<'s> { |
4969 | + pub list: i64, |
4970 | + pub address: &'s str, |
4971 | + pub message_id: &'s str, |
4972 | + pub message: &'s [u8], |
4973 | + } |
4974 | diff --git a/core/src/models/changesets.rs b/core/src/models/changesets.rs |
4975 | new file mode 100644 |
4976 | index 0000000..af4929e |
4977 | --- /dev/null |
4978 | +++ b/core/src/models/changesets.rs |
4979 | @@ -0,0 +1,80 @@ |
4980 | + /* |
4981 | + * This file is part of mailpot |
4982 | + * |
4983 | + * Copyright 2020 - Manos Pitsidianakis |
4984 | + * |
4985 | + * This program is free software: you can redistribute it and/or modify |
4986 | + * it under the terms of the GNU Affero General Public License as |
4987 | + * published by the Free Software Foundation, either version 3 of the |
4988 | + * License, or (at your option) any later version. |
4989 | + * |
4990 | + * This program is distributed in the hope that it will be useful, |
4991 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4992 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4993 | + * GNU Affero General Public License for more details. |
4994 | + * |
4995 | + * You should have received a copy of the GNU Affero General Public License |
4996 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
4997 | + */ |
4998 | + |
4999 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
5000 | + pub struct MailingListChangeset { |
5001 | + pub pk: i64, |
5002 | + pub name: Option<String>, |
5003 | + pub id: Option<String>, |
5004 | + pub address: Option<String>, |
5005 | + pub description: Option<Option<String>>, |
5006 | + pub archive_url: Option<Option<String>>, |
5007 | + } |
5008 | + |
5009 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
5010 | + pub struct ListMembershipChangeset { |
5011 | + pub list: i64, |
5012 | + pub address: String, |
5013 | + pub name: Option<Option<String>>, |
5014 | + pub digest: Option<bool>, |
5015 | + pub hide_address: Option<bool>, |
5016 | + pub receive_duplicates: Option<bool>, |
5017 | + pub receive_own_posts: Option<bool>, |
5018 | + pub receive_confirmation: Option<bool>, |
5019 | + pub enabled: Option<bool>, |
5020 | + } |
5021 | + |
5022 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
5023 | + pub struct PostPolicyChangeset { |
5024 | + pub pk: i64, |
5025 | + pub list: i64, |
5026 | + pub announce_only: Option<bool>, |
5027 | + pub subscriber_only: Option<bool>, |
5028 | + pub approval_needed: Option<bool>, |
5029 | + } |
5030 | + |
5031 | + #[derive(Debug, Clone, Deserialize, Serialize)] |
5032 | + pub struct ListOwnerChangeset { |
5033 | + pub pk: i64, |
5034 | + pub list: i64, |
5035 | + pub address: Option<String>, |
5036 | + pub name: Option<Option<String>>, |
5037 | + } |
5038 | + |
5039 | + impl std::fmt::Display for MailingListChangeset { |
5040 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
5041 | + write!(fmt, "{:?}", self) |
5042 | + } |
5043 | + } |
5044 | + |
5045 | + impl std::fmt::Display for ListMembershipChangeset { |
5046 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
5047 | + write!(fmt, "{:?}", self) |
5048 | + } |
5049 | + } |
5050 | + impl std::fmt::Display for PostPolicyChangeset { |
5051 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
5052 | + write!(fmt, "{:?}", self) |
5053 | + } |
5054 | + } |
5055 | + impl std::fmt::Display for ListOwnerChangeset { |
5056 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
5057 | + write!(fmt, "{:?}", self) |
5058 | + } |
5059 | + } |
5060 | diff --git a/core/src/schema.sql b/core/src/schema.sql |
5061 | new file mode 100644 |
5062 | index 0000000..81d200b |
5063 | --- /dev/null |
5064 | +++ b/core/src/schema.sql |
5065 | @@ -0,0 +1,74 @@ |
5066 | + PRAGMA foreign_keys = true; |
5067 | + PRAGMA encoding = 'UTF-8'; |
5068 | + |
5069 | + CREATE TABLE IF NOT EXISTS mailing_lists ( |
5070 | + pk INTEGER PRIMARY KEY NOT NULL, |
5071 | + name TEXT NOT NULL, |
5072 | + id TEXT NOT NULL, |
5073 | + address TEXT NOT NULL, |
5074 | + archive_url TEXT, |
5075 | + description TEXT |
5076 | + ); |
5077 | + |
5078 | + CREATE TABLE IF NOT EXISTS list_owner ( |
5079 | + pk INTEGER PRIMARY KEY NOT NULL, |
5080 | + list INTEGER NOT NULL, |
5081 | + address TEXT NOT NULL, |
5082 | + name TEXT, |
5083 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5084 | + ); |
5085 | + |
5086 | + CREATE TABLE IF NOT EXISTS post_policy ( |
5087 | + pk INTEGER PRIMARY KEY NOT NULL, |
5088 | + list INTEGER NOT NULL UNIQUE, |
5089 | + announce_only BOOLEAN CHECK (announce_only in (0, 1)) NOT NULL DEFAULT 0, |
5090 | + subscriber_only BOOLEAN CHECK (subscriber_only in (0, 1)) NOT NULL DEFAULT 0, |
5091 | + approval_needed BOOLEAN CHECK (approval_needed in (0, 1)) NOT NULL DEFAULT 0, |
5092 | + CHECK(((approval_needed) OR (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only))))), |
5093 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5094 | + ); |
5095 | + |
5096 | + CREATE TABLE IF NOT EXISTS membership ( |
5097 | + pk INTEGER PRIMARY KEY NOT NULL, |
5098 | + list INTEGER NOT NULL, |
5099 | + address TEXT NOT NULL, |
5100 | + name TEXT, |
5101 | + enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1, |
5102 | + digest BOOLEAN CHECK (digest in (0, 1)) NOT NULL DEFAULT 0, |
5103 | + hide_address BOOLEAN CHECK (hide_address in (0, 1)) NOT NULL DEFAULT 0, |
5104 | + receive_duplicates BOOLEAN CHECK (receive_duplicates in (0, 1)) NOT NULL DEFAULT 1, |
5105 | + receive_own_posts BOOLEAN CHECK (receive_own_posts in (0, 1)) NOT NULL DEFAULT 0, |
5106 | + receive_confirmation BOOLEAN CHECK (receive_confirmation in (0, 1)) NOT NULL DEFAULT 1, |
5107 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5108 | + ); |
5109 | + |
5110 | + CREATE TABLE IF NOT EXISTS candidate_membership ( |
5111 | + pk INTEGER PRIMARY KEY NOT NULL, |
5112 | + list INTEGER NOT NULL, |
5113 | + address TEXT NOT NULL, |
5114 | + name TEXT, |
5115 | + accepted INTEGER, |
5116 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE, |
5117 | + FOREIGN KEY (accepted) REFERENCES membership(pk) ON DELETE CASCADE |
5118 | + ); |
5119 | + |
5120 | + CREATE TABLE IF NOT EXISTS post ( |
5121 | + pk INTEGER PRIMARY KEY NOT NULL, |
5122 | + list INTEGER NOT NULL, |
5123 | + address TEXT NOT NULL, |
5124 | + message_id TEXT NOT NULL, |
5125 | + message BLOB NOT NULL, |
5126 | + FOREIGN KEY (list, address) REFERENCES membership(list, address) ON DELETE CASCADE |
5127 | + ); |
5128 | + |
5129 | + CREATE TABLE IF NOT EXISTS post_event ( |
5130 | + pk INTEGER PRIMARY KEY NOT NULL, |
5131 | + post INTEGER NOT NULL, |
5132 | + date INTEGER NOT NULL, |
5133 | + kind CHAR(1) CHECK (kind IN ('R', 'S', 'D', 'B', 'O')) NOT NULL, |
5134 | + content TEXT NOT NULL, |
5135 | + FOREIGN KEY (post) REFERENCES post(pk) ON DELETE CASCADE |
5136 | + ); |
5137 | + |
5138 | + CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); |
5139 | + CREATE INDEX IF NOT EXISTS membership_idx ON membership(address); |
5140 | diff --git a/core/src/schema.sql.m4 b/core/src/schema.sql.m4 |
5141 | new file mode 100644 |
5142 | index 0000000..344e8a0 |
5143 | --- /dev/null |
5144 | +++ b/core/src/schema.sql.m4 |
5145 | @@ -0,0 +1,78 @@ |
5146 | + define(xor, `(($1) OR ($2)) AND NOT (($1) AND ($2))')dnl |
5147 | + define(BOOLEAN_TYPE, `$1 BOOLEAN CHECK ($1 in (0, 1)) NOT NULL')dnl |
5148 | + define(BOOLEAN_FALSE, `0')dnl |
5149 | + define(BOOLEAN_TRUE, `1')dnl |
5150 | + PRAGMA foreign_keys = true; |
5151 | + PRAGMA encoding = 'UTF-8'; |
5152 | + |
5153 | + CREATE TABLE IF NOT EXISTS mailing_lists ( |
5154 | + pk INTEGER PRIMARY KEY NOT NULL, |
5155 | + name TEXT NOT NULL, |
5156 | + id TEXT NOT NULL, |
5157 | + address TEXT NOT NULL, |
5158 | + archive_url TEXT, |
5159 | + description TEXT |
5160 | + ); |
5161 | + |
5162 | + CREATE TABLE IF NOT EXISTS list_owner ( |
5163 | + pk INTEGER PRIMARY KEY NOT NULL, |
5164 | + list INTEGER NOT NULL, |
5165 | + address TEXT NOT NULL, |
5166 | + name TEXT, |
5167 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5168 | + ); |
5169 | + |
5170 | + CREATE TABLE IF NOT EXISTS post_policy ( |
5171 | + pk INTEGER PRIMARY KEY NOT NULL, |
5172 | + list INTEGER NOT NULL UNIQUE, |
5173 | + BOOLEAN_TYPE(announce_only) DEFAULT BOOLEAN_FALSE(), |
5174 | + BOOLEAN_TYPE(subscriber_only) DEFAULT BOOLEAN_FALSE(), |
5175 | + BOOLEAN_TYPE(approval_needed) DEFAULT BOOLEAN_FALSE(), |
5176 | + CHECK(xor(approval_needed, xor(announce_only, subscriber_only))), |
5177 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5178 | + ); |
5179 | + |
5180 | + CREATE TABLE IF NOT EXISTS membership ( |
5181 | + pk INTEGER PRIMARY KEY NOT NULL, |
5182 | + list INTEGER NOT NULL, |
5183 | + address TEXT NOT NULL, |
5184 | + name TEXT, |
5185 | + BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(), |
5186 | + BOOLEAN_TYPE(digest) DEFAULT BOOLEAN_FALSE(), |
5187 | + BOOLEAN_TYPE(hide_address) DEFAULT BOOLEAN_FALSE(), |
5188 | + BOOLEAN_TYPE(receive_duplicates) DEFAULT BOOLEAN_TRUE(), |
5189 | + BOOLEAN_TYPE(receive_own_posts) DEFAULT BOOLEAN_FALSE(), |
5190 | + BOOLEAN_TYPE(receive_confirmation) DEFAULT BOOLEAN_TRUE(), |
5191 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE |
5192 | + ); |
5193 | + |
5194 | + CREATE TABLE IF NOT EXISTS candidate_membership ( |
5195 | + pk INTEGER PRIMARY KEY NOT NULL, |
5196 | + list INTEGER NOT NULL, |
5197 | + address TEXT NOT NULL, |
5198 | + name TEXT, |
5199 | + accepted INTEGER, |
5200 | + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE, |
5201 | + FOREIGN KEY (accepted) REFERENCES membership(pk) ON DELETE CASCADE |
5202 | + ); |
5203 | + |
5204 | + CREATE TABLE IF NOT EXISTS post ( |
5205 | + pk INTEGER PRIMARY KEY NOT NULL, |
5206 | + list INTEGER NOT NULL, |
5207 | + address TEXT NOT NULL, |
5208 | + message_id TEXT NOT NULL, |
5209 | + message BLOB NOT NULL, |
5210 | + FOREIGN KEY (list, address) REFERENCES membership(list, address) ON DELETE CASCADE |
5211 | + ); |
5212 | + |
5213 | + CREATE TABLE IF NOT EXISTS post_event ( |
5214 | + pk INTEGER PRIMARY KEY NOT NULL, |
5215 | + post INTEGER NOT NULL, |
5216 | + date INTEGER NOT NULL, |
5217 | + kind CHAR(1) CHECK (kind IN ('R', 'S', 'D', 'B', 'O')) NOT NULL, |
5218 | + content TEXT NOT NULL, |
5219 | + FOREIGN KEY (post) REFERENCES post(pk) ON DELETE CASCADE |
5220 | + ); |
5221 | + |
5222 | + CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); |
5223 | + CREATE INDEX IF NOT EXISTS membership_idx ON membership(address); |
5224 | diff --git a/rest-http/Cargo.toml b/rest-http/Cargo.toml |
5225 | new file mode 100644 |
5226 | index 0000000..f29d0ec |
5227 | --- /dev/null |
5228 | +++ b/rest-http/Cargo.toml |
5229 | @@ -0,0 +1,21 @@ |
5230 | + [package] |
5231 | + name = "mailpot-http" |
5232 | + version = "0.1.0" |
5233 | + authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"] |
5234 | + edition = "2018" |
5235 | + license = "LICENSE" |
5236 | + readme = "README.md" |
5237 | + description = "mailing list manager" |
5238 | + repository = "https://github.com/meli/mailpot" |
5239 | + keywords = ["mail", "mailing-lists" ] |
5240 | + categories = ["email"] |
5241 | + default-run = "mpot-http" |
5242 | + |
5243 | + [[bin]] |
5244 | + name = "mpot-http" |
5245 | + path = "src/main.rs" |
5246 | + |
5247 | + [dependencies] |
5248 | + mailpot = { version = "0.1.0", path = "../core" } |
5249 | + tokio = { version = "1", features = ["full"] } |
5250 | + warp = "0.3" |
5251 | diff --git a/rest-http/README.md b/rest-http/README.md |
5252 | new file mode 100644 |
5253 | index 0000000..63a4750 |
5254 | --- /dev/null |
5255 | +++ b/rest-http/README.md |
5256 | @@ -0,0 +1,5 @@ |
5257 | + # mailpot REST http server |
5258 | + |
5259 | + ```shell |
5260 | + cargo run --bin mpot-http |
5261 | + ``` |
5262 | diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs |
5263 | new file mode 100644 |
5264 | index 0000000..471f0ed |
5265 | --- /dev/null |
5266 | +++ b/rest-http/src/main.rs |
5267 | @@ -0,0 +1,99 @@ |
5268 | + /* |
5269 | + * This file is part of mailpot |
5270 | + * |
5271 | + * Copyright 2020 - Manos Pitsidianakis |
5272 | + * |
5273 | + * This program is free software: you can redistribute it and/or modify |
5274 | + * it under the terms of the GNU Affero General Public License as |
5275 | + * published by the Free Software Foundation, either version 3 of the |
5276 | + * License, or (at your option) any later version. |
5277 | + * |
5278 | + * This program is distributed in the hope that it will be useful, |
5279 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
5280 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5281 | + * GNU Affero General Public License for more details. |
5282 | + * |
5283 | + * You should have received a copy of the GNU Affero General Public License |
5284 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
5285 | + */ |
5286 | + |
5287 | + extern crate mailpot; |
5288 | + |
5289 | + pub use mailpot::config::*; |
5290 | + pub use mailpot::db::*; |
5291 | + pub use mailpot::errors::*; |
5292 | + pub use mailpot::models::*; |
5293 | + pub use mailpot::*; |
5294 | + |
5295 | + use warp::Filter; |
5296 | + |
5297 | + /* |
5298 | + fn json_body() -> impl Filter<Extract = (String,), Error = warp::Rejection> + Clone { |
5299 | + // When accepting a body, we want a JSON body |
5300 | + // (and to reject huge payloads)... |
5301 | + warp::body::content_length_limit(1024 * 16).and(warp::body::json()) |
5302 | + } |
5303 | + */ |
5304 | + |
5305 | + #[tokio::main] |
5306 | + async fn main() { |
5307 | + // GET /lists/:i64/policy |
5308 | + let policy = warp::path!("lists" / i64 / "policy").map(|list_pk| { |
5309 | + let db = Database::open_or_create_db().unwrap(); |
5310 | + db.get_list_policy(list_pk) |
5311 | + .ok() |
5312 | + .map(|l| warp::reply::json(&l.unwrap())) |
5313 | + .unwrap() |
5314 | + }); |
5315 | + |
5316 | + //get("/lists")] |
5317 | + let lists = warp::path!("lists").map(|| { |
5318 | + let db = Database::open_or_create_db().unwrap(); |
5319 | + let lists = db.list_lists().unwrap(); |
5320 | + warp::reply::json(&lists) |
5321 | + }); |
5322 | + |
5323 | + //get("/lists/<num>")] |
5324 | + let lists_num = warp::path!("lists" / i64).map(|list_pk| { |
5325 | + let db = Database::open_or_create_db().unwrap(); |
5326 | + let list = db.get_list(list_pk).unwrap(); |
5327 | + warp::reply::json(&list) |
5328 | + }); |
5329 | + |
5330 | + //get("/lists/<num>/members")] |
5331 | + let lists_members = warp::path!("lists" / i64 / "members").map(|list_pk| { |
5332 | + let db = Database::open_or_create_db().unwrap(); |
5333 | + db.list_members(list_pk) |
5334 | + .ok() |
5335 | + .map(|l| warp::reply::json(&l)) |
5336 | + .unwrap() |
5337 | + }); |
5338 | + |
5339 | + //get("/lists/<num>/owners")] |
5340 | + let lists_owners = warp::path!("lists" / i64 / "owners").map(|list_pk| { |
5341 | + let db = Database::open_or_create_db().unwrap(); |
5342 | + db.get_list_owners(list_pk) |
5343 | + .ok() |
5344 | + .map(|l| warp::reply::json(&l)) |
5345 | + .unwrap() |
5346 | + }); |
5347 | + |
5348 | + //post("/lists/<num>/owners/add", data = "<new_owner>")] |
5349 | + let lists_owner_add = |
5350 | + warp::post().and(warp::path!("lists" / i64 / "owners" / "add").map(|_list_pk| "todo")); |
5351 | + |
5352 | + let routes = warp::get().and( |
5353 | + lists |
5354 | + .or(policy) |
5355 | + .or(lists_num) |
5356 | + .or(lists_members) |
5357 | + .or(lists_owners) |
5358 | + .or(lists_owner_add), |
5359 | + ); |
5360 | + |
5361 | + // Note that composing filters for many routes may increase compile times (because it uses a lot of generics). |
5362 | + // If you wish to use dynamic dispatch instead and speed up compile times while |
5363 | + // making it slightly slower at runtime, you can use Filter::boxed(). |
5364 | + |
5365 | + warp::serve(routes).run(([127, 0, 0, 1], 3030)).await; |
5366 | + } |