+1196 -749 +/-25 browse
1 | diff --git a/.github/doc_extra.html b/.github/doc_extra.html |
2 | new file mode 100644 |
3 | index 0000000..05524a9 |
4 | --- /dev/null |
5 | +++ b/.github/doc_extra.html |
6 | @@ -0,0 +1,97 @@ |
7 | + <style> |
8 | + .rustdoc { flex-wrap: wrap; } |
9 | + @media (prefers-color-scheme: light) { |
10 | + :root { |
11 | + --border-primary: #cdcdcd; |
12 | + --border-secondary: #cdcdcd; |
13 | + --a-normal-text: #034575; |
14 | + --a-normal-underline: #bbb; |
15 | + --a-visited-underline: #707070; |
16 | + --a-hover-bg: #bfbfbf40; |
17 | + --a-active-text: #c00; |
18 | + --a-active-underline: #c00; |
19 | + --accent-primary: #0085f2; |
20 | + --accent-primary-engage: #0085f21a; |
21 | + --accent-secondary: #0085f2; |
22 | + --accent-tertiary: #0085f21a; |
23 | + color-scheme: light; |
24 | + } |
25 | + } |
26 | + |
27 | + @media (prefers-color-scheme: dark) { |
28 | + :root { |
29 | + --border-primary: #858585; |
30 | + --border-secondary: #696969; |
31 | + --a-normal-text: #4db4ff; |
32 | + --a-normal-underline: #8b8b8b; |
33 | + --a-visited-underline: #707070; |
34 | + --a-hover-bg: #bfbfbf40; |
35 | + --a-active-text: #c00; |
36 | + --a-active-underline: #c00; |
37 | + --accent-primary: #5e9eff; |
38 | + --accent-primary-engage: #5e9eff1a; |
39 | + --accent-secondary: #5e9eff; |
40 | + --accent-tertiary: #0085f21a; |
41 | + color-scheme: dark; |
42 | + } |
43 | + } |
44 | + |
45 | + nav.main-nav { |
46 | + padding: 0rem 1rem; |
47 | + border: 0.1rem solid var(--border-secondary); |
48 | + border-left: none; |
49 | + border-right: none; |
50 | + border-radius: 2px; |
51 | + padding: 10px 14px 10px 10px; |
52 | + margin-bottom: 10px; |
53 | + /*! width: 100%; */ |
54 | + height: max-content; |
55 | + width: 100vw; |
56 | + } |
57 | + |
58 | + nav.main-nav * { |
59 | + padding: 0; |
60 | + margin: 0; |
61 | + } |
62 | + |
63 | + nav.main-nav > ul { |
64 | + display: flex; |
65 | + flex-wrap: wrap; |
66 | + row-gap: 1rem; |
67 | + list-style: none; |
68 | + } |
69 | + |
70 | + nav.main-nav > ul > li > a { |
71 | + padding: 1rem; |
72 | + } |
73 | + nav.main-nav a[href] { |
74 | + text-decoration: underline; |
75 | + text-decoration-color: currentcolor; |
76 | + color: #034575; |
77 | + color: var(--a-normal-text); |
78 | + text-decoration-color: #707070; |
79 | + text-decoration-color: var(--accent-secondary); |
80 | + text-decoration-skip-ink: none; |
81 | + font-synthesis: none; |
82 | + font-feature-settings: "onum" 1; |
83 | + text-rendering: optimizeLegibility; |
84 | + font-family: -apple-system,BlinkMacSystemFont,Roboto,Roboto Slab,Droid Serif,Segoe UI,system-ui,Arial,sans-serif; |
85 | + font-size: 1.125em; |
86 | + } |
87 | + nav.main-nav > ul > li > a:hover { |
88 | + outline: 0.1rem solid; |
89 | + outline-offset: -0.5rem; |
90 | + } |
91 | + a[href]:focus, a[href]:hover { |
92 | + text-decoration-thickness: 2px; |
93 | + text-decoration-skip-ink: none; |
94 | + } |
95 | + </style> |
96 | + <nav class="main-nav"> |
97 | + <ul> |
98 | + <li><a href="https://git.meli.delivery/meli/mailpot">Gitea Repository</a></li> |
99 | + <li><a href="https://github.com/meli/mailpot">Github Repository</a></li> |
100 | + <li><a href="https://lists.meli.delivery/">Official Instance</a></li> |
101 | + <li><a href="https://meli.github.io/mailpot/lists/2/">Static Demo</a></li> |
102 | + </ul> |
103 | + </nav> |
104 | diff --git a/.github/workflows/grcov.yaml b/.github/workflows/grcov.yaml |
105 | index 88f9544..6646048 100644 |
106 | --- a/.github/workflows/grcov.yaml |
107 | +++ b/.github/workflows/grcov.yaml |
108 | @@ -84,8 +84,8 @@ jobs: |
109 | run: ls -R |
110 | - name: Push |
111 | run: | |
112 | - git config --global user.name 'Github Actions' |
113 | - git config --global user.email 'actions@users.noreply.github.com' |
114 | + git config --global user.name 'github-actions[bot]' |
115 | + git config --global user.email 'github-actions[bot]@users.noreply.github.com' |
116 | git show-ref |
117 | git add coverage |
118 | git commit -m "Update grcov report" |
119 | diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml |
120 | new file mode 100644 |
121 | index 0000000..d52d860 |
122 | --- /dev/null |
123 | +++ b/.github/workflows/rustdoc.yml |
124 | @@ -0,0 +1,81 @@ |
125 | + name: Build rustdoc for Github Pages |
126 | + |
127 | + env: |
128 | + RUST_BACKTRACE: 1 |
129 | + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse |
130 | + |
131 | + on: |
132 | + workflow_dispatch: |
133 | + |
134 | + jobs: |
135 | + build-docs: |
136 | + runs-on: ubuntu-latest |
137 | + steps: |
138 | + - uses: actions/checkout@v1 |
139 | + - id: cache-rustup |
140 | + name: Cache Rust toolchain |
141 | + uses: actions/cache@v3 |
142 | + with: |
143 | + path: ~/.rustup |
144 | + key: toolchain-grcov |
145 | + - id: cache-cargo |
146 | + name: Cache Cargo |
147 | + uses: actions/cache@v3 |
148 | + with: |
149 | + path: ~/.cargo |
150 | + key: cargo-grcov |
151 | + - uses: actions-rs/toolchain@v1 |
152 | + with: |
153 | + toolchain: nightly |
154 | + override: true |
155 | + - name: Make rustdocs |
156 | + run: | |
157 | + make rustdoc |
158 | + rm -rf docs |
159 | + ls -R |
160 | + mv target/doc docs |
161 | + - name: Upload report artifacts |
162 | + uses: actions/upload-artifact@v3 |
163 | + with: |
164 | + name: docs |
165 | + path: docs |
166 | + |
167 | + deploy-docs: |
168 | + needs: build-docs |
169 | + permissions: |
170 | + pages: write |
171 | + id-token: write |
172 | + |
173 | + environment: |
174 | + name: github-pages |
175 | + url: ${{ steps.deployment.outputs.page_url }} |
176 | + |
177 | + runs-on: ubuntu-latest |
178 | + steps: |
179 | + - name: Deploy to GitHub Pages |
180 | + id: deployment |
181 | + uses: actions/checkout@v3 |
182 | + with: |
183 | + ref: 'gh-pages' |
184 | + token: ${{ secrets.GRCOVGHPAGES }} |
185 | + - name: Download docs |
186 | + id: download |
187 | + uses: actions/download-artifact@v3 |
188 | + with: |
189 | + name: docs |
190 | + path: docs |
191 | + - name: 'Echo download path' |
192 | + run: echo ${{steps.download.outputs.download-path}} |
193 | + - name: Display structure of downloaded files |
194 | + run: ls -R |
195 | + - name: Push |
196 | + run: | |
197 | + git config --global user.name 'github-actions[bot]' |
198 | + git config --global user.email 'github-actions[bot]@users.noreply.github.com' |
199 | + git show-ref |
200 | + git add docs |
201 | + git commit -m "Update rustdoc" |
202 | + git show-ref |
203 | + git branch --verbose |
204 | + git remote set-url origin "https://${{github.actor}}:${{ secrets.GRCOVGHPAGES }}@github.com/${{github.repository}}.git" |
205 | + git push |
206 | diff --git a/Makefile b/Makefile |
207 | index 36c0206..54adb8b 100644 |
208 | --- a/Makefile |
209 | +++ b/Makefile |
210 | @@ -21,3 +21,7 @@ lint: |
211 | .PHONY: test |
212 | test: check lint |
213 | @cargo test --all --no-fail-fast --all-features |
214 | + |
215 | + .PHONY: rustdoc |
216 | + rustdoc: |
217 | + @RUSTDOCFLAGS="--html-before-content ./.github/doc_extra.html" cargo doc --workspace --all-features --no-deps --document-private-items |
218 | diff --git a/archive-http/src/cal.rs b/archive-http/src/cal.rs |
219 | index fc0ede5..571950d 100644 |
220 | --- a/archive-http/src/cal.rs |
221 | +++ b/archive-http/src/cal.rs |
222 | @@ -31,7 +31,7 @@ use chrono::*; |
223 | /// |
224 | /// # Examples |
225 | /// ``` |
226 | - /// use calendarize::calendarize; |
227 | + /// use mailpot_archives::cal::calendarize; |
228 | /// use chrono::*; |
229 | /// |
230 | /// let date = NaiveDate::parse_from_str("2021-01-02", "%Y-%m-%d").unwrap(); |
231 | @@ -59,7 +59,7 @@ pub fn calendarize(date: NaiveDate) -> Vec<[u32; 7]> { |
232 | /// |
233 | /// # Examples |
234 | /// ``` |
235 | - /// use calendarize::calendarize_with_offset; |
236 | + /// use mailpot_archives::cal::calendarize_with_offset; |
237 | /// use chrono::*; |
238 | /// |
239 | /// let date = NaiveDate::parse_from_str("2021-01-02", "%Y-%m-%d").unwrap(); |
240 | diff --git a/archive-http/src/gen.rs b/archive-http/src/gen.rs |
241 | index 6516a8e..9f9025a 100644 |
242 | --- a/archive-http/src/gen.rs |
243 | +++ b/archive-http/src/gen.rs |
244 | @@ -17,19 +17,11 @@ |
245 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
246 | */ |
247 | |
248 | - use chrono::Datelike; |
249 | + use std::{fs::OpenOptions, io::Write}; |
250 | |
251 | - mod cal; |
252 | - mod utils; |
253 | - |
254 | - use std::{borrow::Cow, fs::OpenOptions, io::Write}; |
255 | - |
256 | - pub use mailpot::{models::DbVal, *}; |
257 | - use minijinja::{ |
258 | - value::{Object, Value}, |
259 | - Environment, Error, Source, State, |
260 | - }; |
261 | - use utils::*; |
262 | + use mailpot::*; |
263 | + use mailpot_archives::utils::*; |
264 | + use minijinja::value::Value; |
265 | |
266 | fn run_app() -> std::result::Result<(), Box<dyn std::error::Error>> { |
267 | let args = std::env::args().collect::<Vec<_>>(); |
268 | diff --git a/archive-http/src/lib.rs b/archive-http/src/lib.rs |
269 | new file mode 100644 |
270 | index 0000000..bf855fd |
271 | --- /dev/null |
272 | +++ b/archive-http/src/lib.rs |
273 | @@ -0,0 +1,21 @@ |
274 | + /* |
275 | + * This file is part of mailpot |
276 | + * |
277 | + * Copyright 2020 - Manos Pitsidianakis |
278 | + * |
279 | + * This program is free software: you can redistribute it and/or modify |
280 | + * it under the terms of the GNU Affero General Public License as |
281 | + * published by the Free Software Foundation, either version 3 of the |
282 | + * License, or (at your option) any later version. |
283 | + * |
284 | + * This program is distributed in the hope that it will be useful, |
285 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
286 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
287 | + * GNU Affero General Public License for more details. |
288 | + * |
289 | + * You should have received a copy of the GNU Affero General Public License |
290 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
291 | + */ |
292 | + |
293 | + pub mod cal; |
294 | + pub mod utils; |
295 | diff --git a/archive-http/src/main.rs b/archive-http/src/main.rs |
296 | index 3bd3e27..b50ffcc 100644 |
297 | --- a/archive-http/src/main.rs |
298 | +++ b/archive-http/src/main.rs |
299 | @@ -17,20 +17,10 @@ |
300 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
301 | */ |
302 | |
303 | - use chrono::Datelike; |
304 | - |
305 | - mod cal; |
306 | - mod utils; |
307 | - |
308 | - use std::borrow::Cow; |
309 | - |
310 | - pub use mailpot::{models::DbVal, *}; |
311 | - use minijinja::{ |
312 | - value::{Object, Value}, |
313 | - Environment, Error, Source, State, |
314 | - }; |
315 | + use mailpot::*; |
316 | + use mailpot_archives::utils::*; |
317 | + use minijinja::value::Value; |
318 | use percent_encoding::percent_decode_str; |
319 | - use utils::*; |
320 | use warp::Filter; |
321 | |
322 | #[tokio::main] |
323 | diff --git a/archive-http/src/utils.rs b/archive-http/src/utils.rs |
324 | index c53f74b..7e2597b 100644 |
325 | --- a/archive-http/src/utils.rs |
326 | +++ b/archive-http/src/utils.rs |
327 | @@ -17,7 +17,14 @@ |
328 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
329 | */ |
330 | |
331 | - use super::*; |
332 | + use std::borrow::Cow; |
333 | + |
334 | + use chrono::{Datelike, Month}; |
335 | + use mailpot::{models::DbVal, *}; |
336 | + use minijinja::{ |
337 | + value::{Object, Value}, |
338 | + Environment, Error, Source, State, |
339 | + }; |
340 | |
341 | lazy_static::lazy_static! { |
342 | pub static ref TEMPLATES: Environment<'static> = { |
343 | @@ -131,8 +138,6 @@ impl minijinja::value::StructObject for MailingList { |
344 | } |
345 | |
346 | pub fn calendarize(_state: &State, args: Value, hists: Value) -> std::result::Result<Value, Error> { |
347 | - use chrono::Month; |
348 | - |
349 | macro_rules! month { |
350 | ($int:expr) => {{ |
351 | let int = $int; |
352 | @@ -175,7 +180,7 @@ pub fn calendarize(_state: &State, args: Value, hists: Value) -> std::result::Re |
353 | month => month, |
354 | month_int => date.month() as usize, |
355 | year => date.year(), |
356 | - weeks => cal::calendarize_with_offset(date, 1), |
357 | + weeks => crate::cal::calendarize_with_offset(date, 1), |
358 | hist => hist, |
359 | sum => sum, |
360 | }) |
361 | diff --git a/cli/build.rs b/cli/build.rs |
362 | index a913f38..0f3e9a4 100644 |
363 | --- a/cli/build.rs |
364 | +++ b/cli/build.rs |
365 | @@ -27,10 +27,10 @@ use clap::ArgAction; |
366 | use clap_mangen::{roff, Man}; |
367 | use roff::{bold, italic, roman, Inline, Roff}; |
368 | |
369 | - include!("src/args.rs"); |
370 | + include!("src/lib.rs"); |
371 | |
372 | fn main() -> std::io::Result<()> { |
373 | - println!("cargo:rerun-if-changed=./src/args.rs"); |
374 | + println!("cargo:rerun-if-changed=./src/lib.rs"); |
375 | println!("cargo:rerun-if-changed=./build.rs"); |
376 | std::env::set_current_dir("..").expect("could not chdir('..')"); |
377 | |
378 | diff --git a/cli/src/args.rs b/cli/src/args.rs |
379 | deleted file mode 100644 |
380 | index a1acaa4..0000000 |
381 | --- a/cli/src/args.rs |
382 | +++ /dev/null |
383 | @@ -1,438 +0,0 @@ |
384 | - /* |
385 | - * This file is part of mailpot |
386 | - * |
387 | - * Copyright 2020 - Manos Pitsidianakis |
388 | - * |
389 | - * This program is free software: you can redistribute it and/or modify |
390 | - * it under the terms of the GNU Affero General Public License as |
391 | - * published by the Free Software Foundation, either version 3 of the |
392 | - * License, or (at your option) any later version. |
393 | - * |
394 | - * This program is distributed in the hope that it will be useful, |
395 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
396 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
397 | - * GNU Affero General Public License for more details. |
398 | - * |
399 | - * You should have received a copy of the GNU Affero General Public License |
400 | - * along with this program. If not, see <https://www.gnu.org/licenses/>. |
401 | - */ |
402 | - |
403 | - use std::path::PathBuf; |
404 | - |
405 | - pub use clap::{Args, CommandFactory, Parser, Subcommand}; |
406 | - |
407 | - #[derive(Debug, Parser)] |
408 | - #[command( |
409 | - name = "mpot", |
410 | - about = "mailing list manager", |
411 | - long_about = "Tool for mailpot mailing list management.", |
412 | - before_long_help = "GNU Affero version 3 or later <https://www.gnu.org/licenses/>", |
413 | - author, |
414 | - version |
415 | - )] |
416 | - pub struct Opt { |
417 | - /// Print logs. |
418 | - #[arg(short, long)] |
419 | - pub debug: bool, |
420 | - /// Configuration file to use. |
421 | - #[arg(short, long, value_parser)] |
422 | - pub config: Option<PathBuf>, |
423 | - #[command(subcommand)] |
424 | - pub cmd: Command, |
425 | - /// Silence all output. |
426 | - #[arg(short, long)] |
427 | - pub quiet: bool, |
428 | - /// Verbose mode (-v, -vv, -vvv, etc). |
429 | - #[arg(short, long, action = clap::ArgAction::Count)] |
430 | - pub verbose: u8, |
431 | - /// Debug log timestamp (sec, ms, ns, none). |
432 | - #[arg(short, long)] |
433 | - pub ts: Option<stderrlog::Timestamp>, |
434 | - } |
435 | - |
436 | - #[derive(Debug, Subcommand)] |
437 | - pub enum Command { |
438 | - /// Prints a sample config file to STDOUT. |
439 | - /// |
440 | - /// You can generate a new configuration file by writing the output to a |
441 | - /// file, e.g: mpot sample-config > config.toml |
442 | - SampleConfig, |
443 | - /// Dumps database data to STDOUT. |
444 | - DumpDatabase, |
445 | - /// Lists all registered mailing lists. |
446 | - ListLists, |
447 | - /// Mailing list management. |
448 | - List { |
449 | - /// Selects mailing list to operate on. |
450 | - list_id: String, |
451 | - #[command(subcommand)] |
452 | - cmd: ListCommand, |
453 | - }, |
454 | - /// Create new list. |
455 | - CreateList { |
456 | - /// List name. |
457 | - #[arg(long)] |
458 | - name: String, |
459 | - /// List ID. |
460 | - #[arg(long)] |
461 | - id: String, |
462 | - /// List e-mail address. |
463 | - #[arg(long)] |
464 | - address: String, |
465 | - /// List description. |
466 | - #[arg(long)] |
467 | - description: Option<String>, |
468 | - /// List archive URL. |
469 | - #[arg(long)] |
470 | - archive_url: Option<String>, |
471 | - }, |
472 | - /// Post message from STDIN to list. |
473 | - Post { |
474 | - /// Show e-mail processing result without actually consuming it. |
475 | - #[arg(long)] |
476 | - dry_run: bool, |
477 | - }, |
478 | - /// Flush outgoing e-mail queue. |
479 | - FlushQueue { |
480 | - /// Show e-mail processing result without actually consuming it. |
481 | - #[arg(long)] |
482 | - dry_run: bool, |
483 | - }, |
484 | - /// Mail that has not been handled properly end up in the error queue. |
485 | - ErrorQueue { |
486 | - #[command(subcommand)] |
487 | - cmd: ErrorQueueCommand, |
488 | - }, |
489 | - /// Import a maildir folder into an existing list. |
490 | - ImportMaildir { |
491 | - /// List-ID or primary key value. |
492 | - list_id: String, |
493 | - /// Path to a maildir mailbox. |
494 | - /// Must contain {cur, tmp, new} folders. |
495 | - #[arg(long, value_parser)] |
496 | - maildir_path: PathBuf, |
497 | - }, |
498 | - /// Update postfix maps and master.cf (probably needs root permissions). |
499 | - UpdatePostfixConfig { |
500 | - #[arg(short = 'p', long)] |
501 | - /// Override location of master.cf file (default: |
502 | - /// /etc/postfix/master.cf) |
503 | - master_cf: Option<PathBuf>, |
504 | - #[clap(flatten)] |
505 | - config: PostfixConfig, |
506 | - }, |
507 | - /// Print postfix maps and master.cf entry to STDOUT. |
508 | - /// |
509 | - /// Map output should be added to transport_maps and local_recipient_maps |
510 | - /// parameters in postfix's main.cf. It must be saved in a plain text |
511 | - /// file. To make postfix be able to read them, the postmap application |
512 | - /// must be executed with the path to the map file as its sole argument. |
513 | - /// |
514 | - /// postmap /path/to/mylist_maps |
515 | - /// |
516 | - /// postmap is usually distributed along with the other postfix binaries. |
517 | - /// |
518 | - /// The master.cf entry must be manually appended to the master.cf file. See <https://www.postfix.org/master.5.html>. |
519 | - PrintPostfixConfig { |
520 | - #[clap(flatten)] |
521 | - config: PostfixConfig, |
522 | - }, |
523 | - /// All Accounts. |
524 | - Accounts, |
525 | - /// Account info. |
526 | - AccountInfo { |
527 | - /// Account address. |
528 | - address: String, |
529 | - }, |
530 | - /// Add account. |
531 | - AddAccount { |
532 | - /// E-mail address. |
533 | - #[arg(long)] |
534 | - address: String, |
535 | - /// SSH public key for authentication. |
536 | - #[arg(long)] |
537 | - password: String, |
538 | - /// Name. |
539 | - #[arg(long)] |
540 | - name: Option<String>, |
541 | - /// Public key. |
542 | - #[arg(long)] |
543 | - public_key: Option<String>, |
544 | - #[arg(long)] |
545 | - /// Is account enabled. |
546 | - enabled: Option<bool>, |
547 | - }, |
548 | - /// Remove account. |
549 | - RemoveAccount { |
550 | - #[arg(long)] |
551 | - /// E-mail address. |
552 | - address: String, |
553 | - }, |
554 | - /// Update account info. |
555 | - UpdateAccount { |
556 | - /// Address to edit. |
557 | - address: String, |
558 | - /// Public key for authentication. |
559 | - #[arg(long)] |
560 | - password: Option<String>, |
561 | - /// Name. |
562 | - #[arg(long)] |
563 | - name: Option<Option<String>>, |
564 | - /// Public key. |
565 | - #[arg(long)] |
566 | - public_key: Option<Option<String>>, |
567 | - #[arg(long)] |
568 | - /// Is account enabled. |
569 | - enabled: Option<Option<bool>>, |
570 | - }, |
571 | - } |
572 | - |
573 | - /// Postfix config values. |
574 | - #[derive(Debug, Args)] |
575 | - pub struct PostfixConfig { |
576 | - /// User that runs mailpot when postfix relays a message. |
577 | - /// |
578 | - /// Must not be the `postfix` user. |
579 | - /// Must have permissions to access the database file and the data |
580 | - /// directory. |
581 | - #[arg(short, long)] |
582 | - pub user: String, |
583 | - /// Group that runs mailpot when postfix relays a message. |
584 | - /// Optional. |
585 | - #[arg(short, long)] |
586 | - pub group: Option<String>, |
587 | - /// The path to the mailpot binary postfix will execute. |
588 | - #[arg(long)] |
589 | - pub binary_path: PathBuf, |
590 | - /// Limit the number of mailpot instances that can exist at the same time. |
591 | - /// |
592 | - /// Default is 1. |
593 | - #[arg(long, default_value = "1")] |
594 | - pub process_limit: Option<u64>, |
595 | - /// The directory in which the map files are saved. |
596 | - /// |
597 | - /// Default is `data_path` from [`Configuration`](mailpot::Configuration). |
598 | - #[arg(long)] |
599 | - pub map_output_path: Option<PathBuf>, |
600 | - /// The name of the postfix service name to use. |
601 | - /// Default is `mailpot`. |
602 | - /// |
603 | - /// A postfix service is a daemon managed by the postfix process. |
604 | - /// Each entry in the `master.cf` configuration file defines a single |
605 | - /// service. |
606 | - /// |
607 | - /// The `master.cf` file is documented in [`master(5)`](https://www.postfix.org/master.5.html): |
608 | - /// <https://www.postfix.org/master.5.html>. |
609 | - #[arg(long)] |
610 | - pub transport_name: Option<String>, |
611 | - } |
612 | - |
613 | - #[derive(Debug, Subcommand)] |
614 | - pub enum ErrorQueueCommand { |
615 | - /// List. |
616 | - List, |
617 | - /// Print entry in RFC5322 or JSON format. |
618 | - Print { |
619 | - /// index of entry. |
620 | - #[arg(long)] |
621 | - index: Vec<i64>, |
622 | - }, |
623 | - /// Delete entry and print it in stdout. |
624 | - Delete { |
625 | - /// index of entry. |
626 | - #[arg(long)] |
627 | - index: Vec<i64>, |
628 | - /// Do not print in stdout. |
629 | - #[arg(long)] |
630 | - quiet: bool, |
631 | - }, |
632 | - } |
633 | - |
634 | - /// Subscription options. |
635 | - #[derive(Debug, Args)] |
636 | - pub struct SubscriptionOptions { |
637 | - /// Name. |
638 | - #[arg(long)] |
639 | - pub name: Option<String>, |
640 | - /// Send messages as digest. |
641 | - #[arg(long, default_value = "false")] |
642 | - pub digest: Option<bool>, |
643 | - /// Hide message from list when posting. |
644 | - #[arg(long, default_value = "false")] |
645 | - pub hide_address: Option<bool>, |
646 | - /// Hide message from list when posting. |
647 | - #[arg(long, default_value = "false")] |
648 | - /// E-mail address verification status. |
649 | - pub verified: Option<bool>, |
650 | - #[arg(long, default_value = "true")] |
651 | - /// Receive confirmation email when posting. |
652 | - pub receive_confirmation: Option<bool>, |
653 | - #[arg(long, default_value = "true")] |
654 | - /// Receive posts from list even if address exists in To or Cc header. |
655 | - pub receive_duplicates: Option<bool>, |
656 | - #[arg(long, default_value = "false")] |
657 | - /// Receive own posts from list. |
658 | - pub receive_own_posts: Option<bool>, |
659 | - #[arg(long, default_value = "true")] |
660 | - /// Is subscription enabled. |
661 | - pub enabled: Option<bool>, |
662 | - } |
663 | - |
664 | - /// Account options. |
665 | - #[derive(Debug, Args)] |
666 | - pub struct AccountOptions { |
667 | - /// Name. |
668 | - #[arg(long)] |
669 | - pub name: Option<String>, |
670 | - /// Public key. |
671 | - #[arg(long)] |
672 | - pub public_key: Option<String>, |
673 | - #[arg(long)] |
674 | - /// Is account enabled. |
675 | - pub enabled: Option<bool>, |
676 | - } |
677 | - |
678 | - #[derive(Debug, Subcommand)] |
679 | - pub enum ListCommand { |
680 | - /// List subscriptions of list. |
681 | - Subscriptions, |
682 | - /// Add subscription to list. |
683 | - AddSubscription { |
684 | - /// E-mail address. |
685 | - #[arg(long)] |
686 | - address: String, |
687 | - #[clap(flatten)] |
688 | - subscription_options: SubscriptionOptions, |
689 | - }, |
690 | - /// Remove subscription from list. |
691 | - RemoveSubscription { |
692 | - #[arg(long)] |
693 | - /// E-mail address. |
694 | - address: String, |
695 | - }, |
696 | - /// Update subscription info. |
697 | - UpdateSubscription { |
698 | - /// Address to edit. |
699 | - address: String, |
700 | - #[clap(flatten)] |
701 | - subscription_options: SubscriptionOptions, |
702 | - }, |
703 | - /// Add a new post policy. |
704 | - AddPolicy { |
705 | - #[arg(long)] |
706 | - /// Only list owners can post. |
707 | - announce_only: bool, |
708 | - #[arg(long)] |
709 | - /// Only subscriptions can post. |
710 | - subscription_only: bool, |
711 | - #[arg(long)] |
712 | - /// Subscriptions can post. |
713 | - /// Other posts must be approved by list owners. |
714 | - approval_needed: bool, |
715 | - #[arg(long)] |
716 | - /// Anyone can post without restrictions. |
717 | - open: bool, |
718 | - #[arg(long)] |
719 | - /// Allow posts, but handle it manually. |
720 | - custom: bool, |
721 | - }, |
722 | - // Remove post policy. |
723 | - RemovePolicy { |
724 | - #[arg(long)] |
725 | - /// Post policy primary key. |
726 | - pk: i64, |
727 | - }, |
728 | - /// Add subscription policy to list. |
729 | - AddSubscribePolicy { |
730 | - #[arg(long)] |
731 | - /// Send confirmation e-mail when subscription is finalized. |
732 | - send_confirmation: bool, |
733 | - #[arg(long)] |
734 | - /// Anyone can subscribe without restrictions. |
735 | - open: bool, |
736 | - #[arg(long)] |
737 | - /// Only list owners can manually add subscriptions. |
738 | - manual: bool, |
739 | - #[arg(long)] |
740 | - /// Anyone can request to subscribe. |
741 | - request: bool, |
742 | - #[arg(long)] |
743 | - /// Allow subscriptions, but handle it manually. |
744 | - custom: bool, |
745 | - }, |
746 | - RemoveSubscribePolicy { |
747 | - #[arg(long)] |
748 | - /// Subscribe policy primary key. |
749 | - pk: i64, |
750 | - }, |
751 | - /// Add list owner to list. |
752 | - AddListOwner { |
753 | - #[arg(long)] |
754 | - address: String, |
755 | - #[arg(long)] |
756 | - name: Option<String>, |
757 | - }, |
758 | - RemoveListOwner { |
759 | - #[arg(long)] |
760 | - /// List owner primary key. |
761 | - pk: i64, |
762 | - }, |
763 | - /// Alias for update-subscription --enabled true. |
764 | - EnableSubscription { |
765 | - /// Subscription address. |
766 | - address: String, |
767 | - }, |
768 | - /// Alias for update-subscription --enabled false. |
769 | - DisableSubscription { |
770 | - /// Subscription address. |
771 | - address: String, |
772 | - }, |
773 | - /// Update mailing list details. |
774 | - Update { |
775 | - /// New list name. |
776 | - #[arg(long)] |
777 | - name: Option<String>, |
778 | - /// New List-ID. |
779 | - #[arg(long)] |
780 | - id: Option<String>, |
781 | - /// New list address. |
782 | - #[arg(long)] |
783 | - address: Option<String>, |
784 | - /// New list description. |
785 | - #[arg(long)] |
786 | - description: Option<String>, |
787 | - /// New list archive URL. |
788 | - #[arg(long)] |
789 | - archive_url: Option<String>, |
790 | - /// New owner address local part. |
791 | - /// If empty, it defaults to '+owner'. |
792 | - #[arg(long)] |
793 | - owner_local_part: Option<String>, |
794 | - /// New request address local part. |
795 | - /// If empty, it defaults to '+request'. |
796 | - #[arg(long)] |
797 | - request_local_part: Option<String>, |
798 | - /// Require verification of e-mails for new subscriptions. |
799 | - /// |
800 | - /// Subscriptions that are initiated from the subscription's address are |
801 | - /// verified automatically. |
802 | - #[arg(long)] |
803 | - verify: Option<bool>, |
804 | - /// Public visibility of list. |
805 | - /// |
806 | - /// If hidden, the list will not show up in public APIs unless |
807 | - /// requests to it won't work. |
808 | - #[arg(long)] |
809 | - hidden: Option<bool>, |
810 | - /// Enable or disable the list's functionality. |
811 | - /// |
812 | - /// If not enabled, the list will continue to show up in the database |
813 | - /// but e-mails and requests to it won't work. |
814 | - #[arg(long)] |
815 | - enabled: Option<bool>, |
816 | - }, |
817 | - /// Show mailing list health status. |
818 | - Health, |
819 | - /// Show mailing list info. |
820 | - Info, |
821 | - } |
822 | diff --git a/cli/src/lib.rs b/cli/src/lib.rs |
823 | new file mode 100644 |
824 | index 0000000..67c6de3 |
825 | --- /dev/null |
826 | +++ b/cli/src/lib.rs |
827 | @@ -0,0 +1,438 @@ |
828 | + /* |
829 | + * This file is part of mailpot |
830 | + * |
831 | + * Copyright 2020 - Manos Pitsidianakis |
832 | + * |
833 | + * This program is free software: you can redistribute it and/or modify |
834 | + * it under the terms of the GNU Affero General Public License as |
835 | + * published by the Free Software Foundation, either version 3 of the |
836 | + * License, or (at your option) any later version. |
837 | + * |
838 | + * This program is distributed in the hope that it will be useful, |
839 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
840 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
841 | + * GNU Affero General Public License for more details. |
842 | + * |
843 | + * You should have received a copy of the GNU Affero General Public License |
844 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
845 | + */ |
846 | + |
847 | + pub use std::path::PathBuf; |
848 | + |
849 | + pub use clap::{Args, CommandFactory, Parser, Subcommand}; |
850 | + |
851 | + #[derive(Debug, Parser)] |
852 | + #[command( |
853 | + name = "mpot", |
854 | + about = "mailing list manager", |
855 | + long_about = "Tool for mailpot mailing list management.", |
856 | + before_long_help = "GNU Affero version 3 or later <https://www.gnu.org/licenses/>", |
857 | + author, |
858 | + version |
859 | + )] |
860 | + pub struct Opt { |
861 | + /// Print logs. |
862 | + #[arg(short, long)] |
863 | + pub debug: bool, |
864 | + /// Configuration file to use. |
865 | + #[arg(short, long, value_parser)] |
866 | + pub config: Option<PathBuf>, |
867 | + #[command(subcommand)] |
868 | + pub cmd: Command, |
869 | + /// Silence all output. |
870 | + #[arg(short, long)] |
871 | + pub quiet: bool, |
872 | + /// Verbose mode (-v, -vv, -vvv, etc). |
873 | + #[arg(short, long, action = clap::ArgAction::Count)] |
874 | + pub verbose: u8, |
875 | + /// Debug log timestamp (sec, ms, ns, none). |
876 | + #[arg(short, long)] |
877 | + pub ts: Option<stderrlog::Timestamp>, |
878 | + } |
879 | + |
880 | + #[derive(Debug, Subcommand)] |
881 | + pub enum Command { |
882 | + /// Prints a sample config file to STDOUT. |
883 | + /// |
884 | + /// You can generate a new configuration file by writing the output to a |
885 | + /// file, e.g: mpot sample-config > config.toml |
886 | + SampleConfig, |
887 | + /// Dumps database data to STDOUT. |
888 | + DumpDatabase, |
889 | + /// Lists all registered mailing lists. |
890 | + ListLists, |
891 | + /// Mailing list management. |
892 | + List { |
893 | + /// Selects mailing list to operate on. |
894 | + list_id: String, |
895 | + #[command(subcommand)] |
896 | + cmd: ListCommand, |
897 | + }, |
898 | + /// Create new list. |
899 | + CreateList { |
900 | + /// List name. |
901 | + #[arg(long)] |
902 | + name: String, |
903 | + /// List ID. |
904 | + #[arg(long)] |
905 | + id: String, |
906 | + /// List e-mail address. |
907 | + #[arg(long)] |
908 | + address: String, |
909 | + /// List description. |
910 | + #[arg(long)] |
911 | + description: Option<String>, |
912 | + /// List archive URL. |
913 | + #[arg(long)] |
914 | + archive_url: Option<String>, |
915 | + }, |
916 | + /// Post message from STDIN to list. |
917 | + Post { |
918 | + /// Show e-mail processing result without actually consuming it. |
919 | + #[arg(long)] |
920 | + dry_run: bool, |
921 | + }, |
922 | + /// Flush outgoing e-mail queue. |
923 | + FlushQueue { |
924 | + /// Show e-mail processing result without actually consuming it. |
925 | + #[arg(long)] |
926 | + dry_run: bool, |
927 | + }, |
928 | + /// Mail that has not been handled properly end up in the error queue. |
929 | + ErrorQueue { |
930 | + #[command(subcommand)] |
931 | + cmd: ErrorQueueCommand, |
932 | + }, |
933 | + /// Import a maildir folder into an existing list. |
934 | + ImportMaildir { |
935 | + /// List-ID or primary key value. |
936 | + list_id: String, |
937 | + /// Path to a maildir mailbox. |
938 | + /// Must contain {cur, tmp, new} folders. |
939 | + #[arg(long, value_parser)] |
940 | + maildir_path: PathBuf, |
941 | + }, |
942 | + /// Update postfix maps and master.cf (probably needs root permissions). |
943 | + UpdatePostfixConfig { |
944 | + #[arg(short = 'p', long)] |
945 | + /// Override location of master.cf file (default: |
946 | + /// /etc/postfix/master.cf) |
947 | + master_cf: Option<PathBuf>, |
948 | + #[clap(flatten)] |
949 | + config: PostfixConfig, |
950 | + }, |
951 | + /// Print postfix maps and master.cf entry to STDOUT. |
952 | + /// |
953 | + /// Map output should be added to transport_maps and local_recipient_maps |
954 | + /// parameters in postfix's main.cf. It must be saved in a plain text |
955 | + /// file. To make postfix be able to read them, the postmap application |
956 | + /// must be executed with the path to the map file as its sole argument. |
957 | + /// |
958 | + /// postmap /path/to/mylist_maps |
959 | + /// |
960 | + /// postmap is usually distributed along with the other postfix binaries. |
961 | + /// |
962 | + /// The master.cf entry must be manually appended to the master.cf file. See <https://www.postfix.org/master.5.html>. |
963 | + PrintPostfixConfig { |
964 | + #[clap(flatten)] |
965 | + config: PostfixConfig, |
966 | + }, |
967 | + /// All Accounts. |
968 | + Accounts, |
969 | + /// Account info. |
970 | + AccountInfo { |
971 | + /// Account address. |
972 | + address: String, |
973 | + }, |
974 | + /// Add account. |
975 | + AddAccount { |
976 | + /// E-mail address. |
977 | + #[arg(long)] |
978 | + address: String, |
979 | + /// SSH public key for authentication. |
980 | + #[arg(long)] |
981 | + password: String, |
982 | + /// Name. |
983 | + #[arg(long)] |
984 | + name: Option<String>, |
985 | + /// Public key. |
986 | + #[arg(long)] |
987 | + public_key: Option<String>, |
988 | + #[arg(long)] |
989 | + /// Is account enabled. |
990 | + enabled: Option<bool>, |
991 | + }, |
992 | + /// Remove account. |
993 | + RemoveAccount { |
994 | + #[arg(long)] |
995 | + /// E-mail address. |
996 | + address: String, |
997 | + }, |
998 | + /// Update account info. |
999 | + UpdateAccount { |
1000 | + /// Address to edit. |
1001 | + address: String, |
1002 | + /// Public key for authentication. |
1003 | + #[arg(long)] |
1004 | + password: Option<String>, |
1005 | + /// Name. |
1006 | + #[arg(long)] |
1007 | + name: Option<Option<String>>, |
1008 | + /// Public key. |
1009 | + #[arg(long)] |
1010 | + public_key: Option<Option<String>>, |
1011 | + #[arg(long)] |
1012 | + /// Is account enabled. |
1013 | + enabled: Option<Option<bool>>, |
1014 | + }, |
1015 | + } |
1016 | + |
1017 | + /// Postfix config values. |
1018 | + #[derive(Debug, Args)] |
1019 | + pub struct PostfixConfig { |
1020 | + /// User that runs mailpot when postfix relays a message. |
1021 | + /// |
1022 | + /// Must not be the `postfix` user. |
1023 | + /// Must have permissions to access the database file and the data |
1024 | + /// directory. |
1025 | + #[arg(short, long)] |
1026 | + pub user: String, |
1027 | + /// Group that runs mailpot when postfix relays a message. |
1028 | + /// Optional. |
1029 | + #[arg(short, long)] |
1030 | + pub group: Option<String>, |
1031 | + /// The path to the mailpot binary postfix will execute. |
1032 | + #[arg(long)] |
1033 | + pub binary_path: PathBuf, |
1034 | + /// Limit the number of mailpot instances that can exist at the same time. |
1035 | + /// |
1036 | + /// Default is 1. |
1037 | + #[arg(long, default_value = "1")] |
1038 | + pub process_limit: Option<u64>, |
1039 | + /// The directory in which the map files are saved. |
1040 | + /// |
1041 | + /// Default is `data_path` from [`Configuration`](mailpot::Configuration). |
1042 | + #[arg(long)] |
1043 | + pub map_output_path: Option<PathBuf>, |
1044 | + /// The name of the postfix service name to use. |
1045 | + /// Default is `mailpot`. |
1046 | + /// |
1047 | + /// A postfix service is a daemon managed by the postfix process. |
1048 | + /// Each entry in the `master.cf` configuration file defines a single |
1049 | + /// service. |
1050 | + /// |
1051 | + /// The `master.cf` file is documented in [`master(5)`](https://www.postfix.org/master.5.html): |
1052 | + /// <https://www.postfix.org/master.5.html>. |
1053 | + #[arg(long)] |
1054 | + pub transport_name: Option<String>, |
1055 | + } |
1056 | + |
1057 | + #[derive(Debug, Subcommand)] |
1058 | + pub enum ErrorQueueCommand { |
1059 | + /// List. |
1060 | + List, |
1061 | + /// Print entry in RFC5322 or JSON format. |
1062 | + Print { |
1063 | + /// index of entry. |
1064 | + #[arg(long)] |
1065 | + index: Vec<i64>, |
1066 | + }, |
1067 | + /// Delete entry and print it in stdout. |
1068 | + Delete { |
1069 | + /// index of entry. |
1070 | + #[arg(long)] |
1071 | + index: Vec<i64>, |
1072 | + /// Do not print in stdout. |
1073 | + #[arg(long)] |
1074 | + quiet: bool, |
1075 | + }, |
1076 | + } |
1077 | + |
1078 | + /// Subscription options. |
1079 | + #[derive(Debug, Args)] |
1080 | + pub struct SubscriptionOptions { |
1081 | + /// Name. |
1082 | + #[arg(long)] |
1083 | + pub name: Option<String>, |
1084 | + /// Send messages as digest. |
1085 | + #[arg(long, default_value = "false")] |
1086 | + pub digest: Option<bool>, |
1087 | + /// Hide message from list when posting. |
1088 | + #[arg(long, default_value = "false")] |
1089 | + pub hide_address: Option<bool>, |
1090 | + /// Hide message from list when posting. |
1091 | + #[arg(long, default_value = "false")] |
1092 | + /// E-mail address verification status. |
1093 | + pub verified: Option<bool>, |
1094 | + #[arg(long, default_value = "true")] |
1095 | + /// Receive confirmation email when posting. |
1096 | + pub receive_confirmation: Option<bool>, |
1097 | + #[arg(long, default_value = "true")] |
1098 | + /// Receive posts from list even if address exists in To or Cc header. |
1099 | + pub receive_duplicates: Option<bool>, |
1100 | + #[arg(long, default_value = "false")] |
1101 | + /// Receive own posts from list. |
1102 | + pub receive_own_posts: Option<bool>, |
1103 | + #[arg(long, default_value = "true")] |
1104 | + /// Is subscription enabled. |
1105 | + pub enabled: Option<bool>, |
1106 | + } |
1107 | + |
1108 | + /// Account options. |
1109 | + #[derive(Debug, Args)] |
1110 | + pub struct AccountOptions { |
1111 | + /// Name. |
1112 | + #[arg(long)] |
1113 | + pub name: Option<String>, |
1114 | + /// Public key. |
1115 | + #[arg(long)] |
1116 | + pub public_key: Option<String>, |
1117 | + #[arg(long)] |
1118 | + /// Is account enabled. |
1119 | + pub enabled: Option<bool>, |
1120 | + } |
1121 | + |
1122 | + #[derive(Debug, Subcommand)] |
1123 | + pub enum ListCommand { |
1124 | + /// List subscriptions of list. |
1125 | + Subscriptions, |
1126 | + /// Add subscription to list. |
1127 | + AddSubscription { |
1128 | + /// E-mail address. |
1129 | + #[arg(long)] |
1130 | + address: String, |
1131 | + #[clap(flatten)] |
1132 | + subscription_options: SubscriptionOptions, |
1133 | + }, |
1134 | + /// Remove subscription from list. |
1135 | + RemoveSubscription { |
1136 | + #[arg(long)] |
1137 | + /// E-mail address. |
1138 | + address: String, |
1139 | + }, |
1140 | + /// Update subscription info. |
1141 | + UpdateSubscription { |
1142 | + /// Address to edit. |
1143 | + address: String, |
1144 | + #[clap(flatten)] |
1145 | + subscription_options: SubscriptionOptions, |
1146 | + }, |
1147 | + /// Add a new post policy. |
1148 | + AddPolicy { |
1149 | + #[arg(long)] |
1150 | + /// Only list owners can post. |
1151 | + announce_only: bool, |
1152 | + #[arg(long)] |
1153 | + /// Only subscriptions can post. |
1154 | + subscription_only: bool, |
1155 | + #[arg(long)] |
1156 | + /// Subscriptions can post. |
1157 | + /// Other posts must be approved by list owners. |
1158 | + approval_needed: bool, |
1159 | + #[arg(long)] |
1160 | + /// Anyone can post without restrictions. |
1161 | + open: bool, |
1162 | + #[arg(long)] |
1163 | + /// Allow posts, but handle it manually. |
1164 | + custom: bool, |
1165 | + }, |
1166 | + // Remove post policy. |
1167 | + RemovePolicy { |
1168 | + #[arg(long)] |
1169 | + /// Post policy primary key. |
1170 | + pk: i64, |
1171 | + }, |
1172 | + /// Add subscription policy to list. |
1173 | + AddSubscribePolicy { |
1174 | + #[arg(long)] |
1175 | + /// Send confirmation e-mail when subscription is finalized. |
1176 | + send_confirmation: bool, |
1177 | + #[arg(long)] |
1178 | + /// Anyone can subscribe without restrictions. |
1179 | + open: bool, |
1180 | + #[arg(long)] |
1181 | + /// Only list owners can manually add subscriptions. |
1182 | + manual: bool, |
1183 | + #[arg(long)] |
1184 | + /// Anyone can request to subscribe. |
1185 | + request: bool, |
1186 | + #[arg(long)] |
1187 | + /// Allow subscriptions, but handle it manually. |
1188 | + custom: bool, |
1189 | + }, |
1190 | + RemoveSubscribePolicy { |
1191 | + #[arg(long)] |
1192 | + /// Subscribe policy primary key. |
1193 | + pk: i64, |
1194 | + }, |
1195 | + /// Add list owner to list. |
1196 | + AddListOwner { |
1197 | + #[arg(long)] |
1198 | + address: String, |
1199 | + #[arg(long)] |
1200 | + name: Option<String>, |
1201 | + }, |
1202 | + RemoveListOwner { |
1203 | + #[arg(long)] |
1204 | + /// List owner primary key. |
1205 | + pk: i64, |
1206 | + }, |
1207 | + /// Alias for update-subscription --enabled true. |
1208 | + EnableSubscription { |
1209 | + /// Subscription address. |
1210 | + address: String, |
1211 | + }, |
1212 | + /// Alias for update-subscription --enabled false. |
1213 | + DisableSubscription { |
1214 | + /// Subscription address. |
1215 | + address: String, |
1216 | + }, |
1217 | + /// Update mailing list details. |
1218 | + Update { |
1219 | + /// New list name. |
1220 | + #[arg(long)] |
1221 | + name: Option<String>, |
1222 | + /// New List-ID. |
1223 | + #[arg(long)] |
1224 | + id: Option<String>, |
1225 | + /// New list address. |
1226 | + #[arg(long)] |
1227 | + address: Option<String>, |
1228 | + /// New list description. |
1229 | + #[arg(long)] |
1230 | + description: Option<String>, |
1231 | + /// New list archive URL. |
1232 | + #[arg(long)] |
1233 | + archive_url: Option<String>, |
1234 | + /// New owner address local part. |
1235 | + /// If empty, it defaults to '+owner'. |
1236 | + #[arg(long)] |
1237 | + owner_local_part: Option<String>, |
1238 | + /// New request address local part. |
1239 | + /// If empty, it defaults to '+request'. |
1240 | + #[arg(long)] |
1241 | + request_local_part: Option<String>, |
1242 | + /// Require verification of e-mails for new subscriptions. |
1243 | + /// |
1244 | + /// Subscriptions that are initiated from the subscription's address are |
1245 | + /// verified automatically. |
1246 | + #[arg(long)] |
1247 | + verify: Option<bool>, |
1248 | + /// Public visibility of list. |
1249 | + /// |
1250 | + /// If hidden, the list will not show up in public APIs unless |
1251 | + /// requests to it won't work. |
1252 | + #[arg(long)] |
1253 | + hidden: Option<bool>, |
1254 | + /// Enable or disable the list's functionality. |
1255 | + /// |
1256 | + /// If not enabled, the list will continue to show up in the database |
1257 | + /// but e-mails and requests to it won't work. |
1258 | + #[arg(long)] |
1259 | + enabled: Option<bool>, |
1260 | + }, |
1261 | + /// Show mailing list health status. |
1262 | + Health, |
1263 | + /// Show mailing list info. |
1264 | + Info, |
1265 | + } |
1266 | diff --git a/cli/src/main.rs b/cli/src/main.rs |
1267 | index d10f015..b46049c 100644 |
1268 | --- a/cli/src/main.rs |
1269 | +++ b/cli/src/main.rs |
1270 | @@ -24,15 +24,12 @@ use std::{ |
1271 | process::Stdio, |
1272 | }; |
1273 | |
1274 | - pub use mailpot::{ |
1275 | - mail::*, |
1276 | + use mailpot::{ |
1277 | melib::{backends::maildir::MaildirPathTrait, smol, Envelope, EnvelopeHash}, |
1278 | models::{changesets::*, *}, |
1279 | *, |
1280 | }; |
1281 | - |
1282 | - mod args; |
1283 | - use args::*; |
1284 | + use mailpot_cli::*; |
1285 | |
1286 | macro_rules! list { |
1287 | ($db:ident, $list_id:expr) => {{ |
1288 | diff --git a/cli/tests/out_queue_flush.rs b/cli/tests/out_queue_flush.rs |
1289 | index b1a82c7..dc99b01 100644 |
1290 | --- a/cli/tests/out_queue_flush.rs |
1291 | +++ b/cli/tests/out_queue_flush.rs |
1292 | @@ -21,6 +21,7 @@ |
1293 | |
1294 | use assert_cmd::assert::OutputAssertExt; |
1295 | use mailpot::{ |
1296 | + melib, |
1297 | models::{changesets::ListSubscriptionChangeset, *}, |
1298 | Configuration, Connection, Queue, SendMail, |
1299 | }; |
1300 | diff --git a/core/build.rs b/core/build.rs |
1301 | index 0e0d59c..77a3159 100644 |
1302 | --- a/core/build.rs |
1303 | +++ b/core/build.rs |
1304 | @@ -29,6 +29,12 @@ fn main() { |
1305 | .arg("./src/schema.sql.m4") |
1306 | .output() |
1307 | .unwrap(); |
1308 | + if String::from_utf8_lossy(&output.stdout).trim().is_empty() { |
1309 | + panic!( |
1310 | + "m4 output is empty. stderr was {}", |
1311 | + String::from_utf8_lossy(&output.stderr) |
1312 | + ); |
1313 | + } |
1314 | let mut verify = Command::new("sqlite3") |
1315 | .stdin(Stdio::piped()) |
1316 | .stdout(Stdio::piped()) |
1317 | diff --git a/core/src/db.rs b/core/src/db.rs |
1318 | index 38cb8e3..2f400bc 100644 |
1319 | --- a/core/src/db.rs |
1320 | +++ b/core/src/db.rs |
1321 | @@ -131,6 +131,13 @@ fn user_authorizer_callback( |
1322 | } |
1323 | |
1324 | impl Connection { |
1325 | + /// The database schema. |
1326 | + /// |
1327 | + /// ```sql |
1328 | + #[doc = include_str!("./schema.sql")] |
1329 | + /// ``` |
1330 | + pub const SCHEMA: &str = include_str!("./schema.sql"); |
1331 | + |
1332 | /// Creates a new database connection. |
1333 | /// |
1334 | /// `Connection` supports a limited subset of operations by default (see |
1335 | diff --git a/core/src/lib.rs b/core/src/lib.rs |
1336 | index a84b1a3..0139c2a 100644 |
1337 | --- a/core/src/lib.rs |
1338 | +++ b/core/src/lib.rs |
1339 | @@ -48,6 +48,18 @@ |
1340 | |
1341 | //! Mailing list manager library. |
1342 | //! |
1343 | + //! Data is stored in a `sqlite3` database. |
1344 | + //! You can inspect the schema in [`SCHEMA`](crate::Connection::SCHEMA). |
1345 | + //! |
1346 | + //! # Usage |
1347 | + //! |
1348 | + //! `mailpot` can be used with the CLI tool in [`mailpot-cli`](mailpot-cli), |
1349 | + //! and/or in the web interface of the [`mailpot-web`](mailpot-web) crate. |
1350 | + //! |
1351 | + //! You can also directly use this crate as a library. |
1352 | + //! |
1353 | + //! # Example |
1354 | + //! |
1355 | //! ``` |
1356 | //! use mailpot::{models::*, Configuration, Connection, SendMail}; |
1357 | //! # use tempfile::TempDir; |
1358 | diff --git a/core/src/schema.sql b/core/src/schema.sql |
1359 | index d556c54..9c5cf75 100644 |
1360 | --- a/core/src/schema.sql |
1361 | +++ b/core/src/schema.sql |
1362 | @@ -2,19 +2,19 @@ PRAGMA foreign_keys = true; |
1363 | PRAGMA encoding = 'UTF-8'; |
1364 | |
1365 | CREATE TABLE IF NOT EXISTS list ( |
1366 | - pk INTEGER PRIMARY KEY NOT NULL, |
1367 | - name TEXT NOT NULL, |
1368 | - id TEXT NOT NULL UNIQUE, |
1369 | - address TEXT NOT NULL UNIQUE, |
1370 | - owner_local_part TEXT, |
1371 | - request_local_part TEXT, |
1372 | - archive_url TEXT, |
1373 | - description TEXT, |
1374 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1375 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1376 | - verify BOOLEAN CHECK (verify in (0, 1)) NOT NULL DEFAULT 1, |
1377 | - hidden BOOLEAN CHECK (hidden in (0, 1)) NOT NULL DEFAULT 0, |
1378 | - enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1 |
1379 | + pk INTEGER PRIMARY KEY NOT NULL, |
1380 | + name TEXT NOT NULL, |
1381 | + id TEXT NOT NULL UNIQUE, |
1382 | + address TEXT NOT NULL UNIQUE, |
1383 | + owner_local_part TEXT, |
1384 | + request_local_part TEXT, |
1385 | + archive_url TEXT, |
1386 | + description TEXT, |
1387 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1388 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1389 | + verify BOOLEAN CHECK (verify IN (0, 1)) NOT NULL DEFAULT 1, |
1390 | + hidden BOOLEAN CHECK (hidden IN (0, 1)) NOT NULL DEFAULT 0, |
1391 | + enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL DEFAULT 1 |
1392 | ); |
1393 | |
1394 | CREATE TABLE IF NOT EXISTS owner ( |
1395 | @@ -28,100 +28,243 @@ CREATE TABLE IF NOT EXISTS owner ( |
1396 | ); |
1397 | |
1398 | CREATE TABLE IF NOT EXISTS post_policy ( |
1399 | - pk INTEGER PRIMARY KEY NOT NULL, |
1400 | - list INTEGER NOT NULL UNIQUE, |
1401 | - announce_only BOOLEAN CHECK (announce_only in (0, 1)) NOT NULL DEFAULT 0, |
1402 | - subscription_only BOOLEAN CHECK (subscription_only in (0, 1)) NOT NULL DEFAULT 0, |
1403 | - approval_needed BOOLEAN CHECK (approval_needed in (0, 1)) NOT NULL DEFAULT 0, |
1404 | - open BOOLEAN CHECK (open in (0, 1)) NOT NULL DEFAULT 0, |
1405 | - custom BOOLEAN CHECK (custom in (0, 1)) NOT NULL DEFAULT 0, |
1406 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1407 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
1408 | - CHECK(((custom) OR (((open) OR (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))) AND NOT ((open) AND (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))))) AND NOT ((custom) AND (((open) OR (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))) AND NOT ((open) AND (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only))))))))), |
1409 | + pk INTEGER PRIMARY KEY NOT NULL, |
1410 | + list INTEGER NOT NULL UNIQUE, |
1411 | + announce_only BOOLEAN CHECK (announce_only IN (0, 1)) NOT NULL |
1412 | + DEFAULT 0, |
1413 | + subscription_only BOOLEAN CHECK (subscription_only IN (0, 1)) NOT NULL |
1414 | + DEFAULT 0, |
1415 | + approval_needed BOOLEAN CHECK (approval_needed IN (0, 1)) NOT NULL |
1416 | + DEFAULT 0, |
1417 | + open BOOLEAN CHECK (open IN (0, 1)) NOT NULL DEFAULT 0, |
1418 | + custom BOOLEAN CHECK (custom IN (0, 1)) NOT NULL DEFAULT 0, |
1419 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1420 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
1421 | + CHECK(( |
1422 | + (custom) OR (( |
1423 | + (open) OR (( |
1424 | + (approval_needed) OR (( |
1425 | + (announce_only) OR (subscription_only) |
1426 | + ) |
1427 | + AND NOT |
1428 | + ( |
1429 | + (announce_only) AND (subscription_only) |
1430 | + )) |
1431 | + ) |
1432 | + AND NOT |
1433 | + ( |
1434 | + (approval_needed) AND (( |
1435 | + (announce_only) OR (subscription_only) |
1436 | + ) |
1437 | + AND NOT |
1438 | + ( |
1439 | + (announce_only) AND (subscription_only) |
1440 | + )) |
1441 | + )) |
1442 | + ) |
1443 | + AND NOT |
1444 | + ( |
1445 | + (open) AND (( |
1446 | + (approval_needed) OR (( |
1447 | + (announce_only) OR (subscription_only) |
1448 | + ) |
1449 | + AND NOT |
1450 | + ( |
1451 | + (announce_only) AND (subscription_only) |
1452 | + )) |
1453 | + ) |
1454 | + AND NOT |
1455 | + ( |
1456 | + (approval_needed) AND (( |
1457 | + (announce_only) OR (subscription_only) |
1458 | + ) |
1459 | + AND NOT |
1460 | + ( |
1461 | + (announce_only) AND (subscription_only) |
1462 | + )) |
1463 | + )) |
1464 | + )) |
1465 | + ) |
1466 | + AND NOT |
1467 | + ( |
1468 | + (custom) AND (( |
1469 | + (open) OR (( |
1470 | + (approval_needed) OR (( |
1471 | + (announce_only) OR (subscription_only) |
1472 | + ) |
1473 | + AND NOT |
1474 | + ( |
1475 | + (announce_only) AND (subscription_only) |
1476 | + )) |
1477 | + ) |
1478 | + AND NOT |
1479 | + ( |
1480 | + (approval_needed) AND (( |
1481 | + (announce_only) OR (subscription_only) |
1482 | + ) |
1483 | + AND NOT |
1484 | + ( |
1485 | + (announce_only) AND (subscription_only) |
1486 | + )) |
1487 | + )) |
1488 | + ) |
1489 | + AND NOT |
1490 | + ( |
1491 | + (open) AND (( |
1492 | + (approval_needed) OR (( |
1493 | + (announce_only) OR (subscription_only) |
1494 | + ) |
1495 | + AND NOT |
1496 | + ( |
1497 | + (announce_only) AND (subscription_only) |
1498 | + )) |
1499 | + ) |
1500 | + AND NOT |
1501 | + ( |
1502 | + (approval_needed) AND (( |
1503 | + (announce_only) OR (subscription_only) |
1504 | + ) |
1505 | + AND NOT |
1506 | + ( |
1507 | + (announce_only) AND (subscription_only) |
1508 | + )) |
1509 | + )) |
1510 | + )) |
1511 | + )), |
1512 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE |
1513 | ); |
1514 | |
1515 | CREATE TABLE IF NOT EXISTS subscription_policy ( |
1516 | - pk INTEGER PRIMARY KEY NOT NULL, |
1517 | - list INTEGER NOT NULL UNIQUE, |
1518 | - send_confirmation BOOLEAN CHECK (send_confirmation in (0, 1)) NOT NULL DEFAULT 1, |
1519 | - open BOOLEAN CHECK (open in (0, 1)) NOT NULL DEFAULT 0, |
1520 | - manual BOOLEAN CHECK (manual in (0, 1)) NOT NULL DEFAULT 0, |
1521 | - request BOOLEAN CHECK (request in (0, 1)) NOT NULL DEFAULT 0, |
1522 | - custom BOOLEAN CHECK (custom in (0, 1)) NOT NULL DEFAULT 0, |
1523 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1524 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1525 | - CHECK(((open) OR (((manual) OR (((request) OR (custom)) AND NOT ((request) AND (custom)))) AND NOT ((manual) AND (((request) OR (custom)) AND NOT ((request) AND (custom)))))) AND NOT ((open) AND (((manual) OR (((request) OR (custom)) AND NOT ((request) AND (custom)))) AND NOT ((manual) AND (((request) OR (custom)) AND NOT ((request) AND (custom))))))), |
1526 | + pk INTEGER PRIMARY KEY NOT NULL, |
1527 | + list INTEGER NOT NULL UNIQUE, |
1528 | + send_confirmation BOOLEAN CHECK (send_confirmation IN (0, 1)) NOT NULL |
1529 | + DEFAULT 1, |
1530 | + open BOOLEAN CHECK (open IN (0, 1)) NOT NULL DEFAULT 0, |
1531 | + manual BOOLEAN CHECK (manual IN (0, 1)) NOT NULL DEFAULT 0, |
1532 | + request BOOLEAN CHECK (request IN (0, 1)) NOT NULL DEFAULT 0, |
1533 | + custom BOOLEAN CHECK (custom IN (0, 1)) NOT NULL DEFAULT 0, |
1534 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1535 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1536 | + CHECK(( |
1537 | + (open) OR (( |
1538 | + (manual) OR (( |
1539 | + (request) OR (custom) |
1540 | + ) |
1541 | + AND NOT |
1542 | + ( |
1543 | + (request) AND (custom) |
1544 | + )) |
1545 | + ) |
1546 | + AND NOT |
1547 | + ( |
1548 | + (manual) AND (( |
1549 | + (request) OR (custom) |
1550 | + ) |
1551 | + AND NOT |
1552 | + ( |
1553 | + (request) AND (custom) |
1554 | + )) |
1555 | + )) |
1556 | + ) |
1557 | + AND NOT |
1558 | + ( |
1559 | + (open) AND (( |
1560 | + (manual) OR (( |
1561 | + (request) OR (custom) |
1562 | + ) |
1563 | + AND NOT |
1564 | + ( |
1565 | + (request) AND (custom) |
1566 | + )) |
1567 | + ) |
1568 | + AND NOT |
1569 | + ( |
1570 | + (manual) AND (( |
1571 | + (request) OR (custom) |
1572 | + ) |
1573 | + AND NOT |
1574 | + ( |
1575 | + (request) AND (custom) |
1576 | + )) |
1577 | + )) |
1578 | + )), |
1579 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE |
1580 | ); |
1581 | |
1582 | CREATE TABLE IF NOT EXISTS subscription ( |
1583 | - pk INTEGER PRIMARY KEY NOT NULL, |
1584 | - list INTEGER NOT NULL, |
1585 | - address TEXT NOT NULL, |
1586 | - name TEXT, |
1587 | - account INTEGER, |
1588 | - enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1, |
1589 | - verified BOOLEAN CHECK (verified in (0, 1)) NOT NULL DEFAULT 1, |
1590 | - digest BOOLEAN CHECK (digest in (0, 1)) NOT NULL DEFAULT 0, |
1591 | - hide_address BOOLEAN CHECK (hide_address in (0, 1)) NOT NULL DEFAULT 0, |
1592 | - receive_duplicates BOOLEAN CHECK (receive_duplicates in (0, 1)) NOT NULL DEFAULT 1, |
1593 | - receive_own_posts BOOLEAN CHECK (receive_own_posts in (0, 1)) NOT NULL DEFAULT 0, |
1594 | - receive_confirmation BOOLEAN CHECK (receive_confirmation in (0, 1)) NOT NULL DEFAULT 1, |
1595 | - last_digest INTEGER NOT NULL DEFAULT (unixepoch()), |
1596 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1597 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1598 | + pk INTEGER PRIMARY KEY NOT NULL, |
1599 | + list INTEGER NOT NULL, |
1600 | + address TEXT NOT NULL, |
1601 | + name TEXT, |
1602 | + account INTEGER, |
1603 | + enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL |
1604 | + DEFAULT 1, |
1605 | + verified BOOLEAN CHECK (verified IN (0, 1)) NOT NULL |
1606 | + DEFAULT 1, |
1607 | + digest BOOLEAN CHECK (digest IN (0, 1)) NOT NULL |
1608 | + DEFAULT 0, |
1609 | + hide_address BOOLEAN CHECK (hide_address IN (0, 1)) NOT NULL |
1610 | + DEFAULT 0, |
1611 | + receive_duplicates BOOLEAN CHECK (receive_duplicates IN (0, 1)) NOT NULL |
1612 | + DEFAULT 1, |
1613 | + receive_own_posts BOOLEAN CHECK (receive_own_posts IN (0, 1)) NOT NULL |
1614 | + DEFAULT 0, |
1615 | + receive_confirmation BOOLEAN CHECK (receive_confirmation IN (0, 1)) NOT NULL |
1616 | + DEFAULT 1, |
1617 | + last_digest INTEGER NOT NULL DEFAULT (unixepoch()), |
1618 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1619 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1620 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
1621 | FOREIGN KEY (account) REFERENCES account(pk) ON DELETE SET NULL, |
1622 | UNIQUE (list, address) ON CONFLICT ROLLBACK |
1623 | ); |
1624 | |
1625 | CREATE TABLE IF NOT EXISTS account ( |
1626 | - pk INTEGER PRIMARY KEY NOT NULL, |
1627 | - name TEXT, |
1628 | - address TEXT NOT NULL UNIQUE, |
1629 | - public_key TEXT, |
1630 | - password TEXT NOT NULL, |
1631 | - enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1, |
1632 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1633 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
1634 | + pk INTEGER PRIMARY KEY NOT NULL, |
1635 | + name TEXT, |
1636 | + address TEXT NOT NULL UNIQUE, |
1637 | + public_key TEXT, |
1638 | + password TEXT NOT NULL, |
1639 | + enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL DEFAULT 1, |
1640 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1641 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
1642 | ); |
1643 | |
1644 | CREATE TABLE IF NOT EXISTS candidate_subscription ( |
1645 | - pk INTEGER PRIMARY KEY NOT NULL, |
1646 | - list INTEGER NOT NULL, |
1647 | - address TEXT NOT NULL, |
1648 | - name TEXT, |
1649 | - accepted INTEGER UNIQUE, |
1650 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1651 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1652 | + pk INTEGER PRIMARY KEY NOT NULL, |
1653 | + list INTEGER NOT NULL, |
1654 | + address TEXT NOT NULL, |
1655 | + name TEXT, |
1656 | + accepted INTEGER UNIQUE, |
1657 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1658 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1659 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
1660 | FOREIGN KEY (accepted) REFERENCES subscription(pk) ON DELETE CASCADE, |
1661 | UNIQUE (list, address) ON CONFLICT ROLLBACK |
1662 | ); |
1663 | |
1664 | CREATE TABLE IF NOT EXISTS post ( |
1665 | - pk INTEGER PRIMARY KEY NOT NULL, |
1666 | - list INTEGER NOT NULL, |
1667 | - envelope_from TEXT, |
1668 | - address TEXT NOT NULL, |
1669 | - message_id TEXT NOT NULL, |
1670 | - message BLOB NOT NULL, |
1671 | - headers_json TEXT, |
1672 | - timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
1673 | - datetime TEXT NOT NULL DEFAULT (datetime()), |
1674 | - created INTEGER NOT NULL DEFAULT (unixepoch()) |
1675 | + pk INTEGER PRIMARY KEY NOT NULL, |
1676 | + list INTEGER NOT NULL, |
1677 | + envelope_from TEXT, |
1678 | + address TEXT NOT NULL, |
1679 | + message_id TEXT NOT NULL, |
1680 | + message BLOB NOT NULL, |
1681 | + headers_json TEXT, |
1682 | + timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
1683 | + datetime TEXT NOT NULL DEFAULT (datetime()), |
1684 | + created INTEGER NOT NULL DEFAULT (unixepoch()) |
1685 | ); |
1686 | |
1687 | CREATE TABLE IF NOT EXISTS templates ( |
1688 | - pk INTEGER PRIMARY KEY NOT NULL, |
1689 | - name TEXT NOT NULL, |
1690 | - list INTEGER, |
1691 | - subject TEXT, |
1692 | - headers_json TEXT, |
1693 | - body TEXT NOT NULL, |
1694 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
1695 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1696 | + pk INTEGER PRIMARY KEY NOT NULL, |
1697 | + name TEXT NOT NULL, |
1698 | + list INTEGER, |
1699 | + subject TEXT, |
1700 | + headers_json TEXT, |
1701 | + body TEXT NOT NULL, |
1702 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
1703 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
1704 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
1705 | UNIQUE (list, name) ON CONFLICT ROLLBACK |
1706 | ); |
1707 | @@ -130,47 +273,56 @@ CREATE TABLE IF NOT EXISTS templates ( |
1708 | -- |
1709 | -- ## The "maildrop" queue |
1710 | -- |
1711 | - -- Messages that have been submitted but not yet processed, await processing in |
1712 | - -- the "maildrop" queue. Messages can be added to the "maildrop" queue even when |
1713 | - -- mailpot is not running. |
1714 | + -- Messages that have been submitted but not yet processed, await processing |
1715 | + -- in the "maildrop" queue. Messages can be added to the "maildrop" queue |
1716 | + -- even when mailpot is not running. |
1717 | -- |
1718 | -- ## The "deferred" queue |
1719 | -- |
1720 | - -- When all the deliverable recipients for a message are delivered, and for some |
1721 | - -- recipients delivery failed for a transient reason (it might succeed later), the |
1722 | - -- message is placed in the "deferred" queue. |
1723 | + -- When all the deliverable recipients for a message are delivered, and for |
1724 | + -- some recipients delivery failed for a transient reason (it might succeed |
1725 | + -- later), the message is placed in the "deferred" queue. |
1726 | -- |
1727 | -- ## The "hold" queue |
1728 | -- |
1729 | - -- List administrators may introduce rules for emails to be placed indefinitely in |
1730 | - -- the "hold" queue. Messages placed in the "hold" queue stay there until the |
1731 | - -- administrator intervenes. No periodic delivery attempts are made for messages |
1732 | - -- in the "hold" queue. |
1733 | + -- List administrators may introduce rules for emails to be placed |
1734 | + -- indefinitely in the "hold" queue. Messages placed in the "hold" queue stay |
1735 | + -- there until the administrator intervenes. No periodic delivery attempts |
1736 | + -- are made for messages in the "hold" queue. |
1737 | |
1738 | -- ## The "out" queue |
1739 | -- |
1740 | -- Emails that must be sent as soon as possible. |
1741 | CREATE TABLE IF NOT EXISTS queue ( |
1742 | - pk INTEGER PRIMARY KEY NOT NULL, |
1743 | - which TEXT CHECK (which IN ('maildrop', 'hold', 'deferred', 'corrupt', 'error', 'out')) NOT NULL, |
1744 | - list INTEGER, |
1745 | - comment TEXT, |
1746 | - to_addresses TEXT NOT NULL, |
1747 | - from_address TEXT NOT NULL, |
1748 | - subject TEXT NOT NULL, |
1749 | - message_id TEXT NOT NULL, |
1750 | - message BLOB NOT NULL, |
1751 | - timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
1752 | - datetime TEXT NOT NULL DEFAULT (datetime()), |
1753 | + pk INTEGER PRIMARY KEY NOT NULL, |
1754 | + which TEXT |
1755 | + CHECK ( |
1756 | + which IN |
1757 | + ('maildrop', |
1758 | + 'hold', |
1759 | + 'deferred', |
1760 | + 'corrupt', |
1761 | + 'error', |
1762 | + 'out') |
1763 | + ) NOT NULL, |
1764 | + list INTEGER, |
1765 | + comment TEXT, |
1766 | + to_addresses TEXT NOT NULL, |
1767 | + from_address TEXT NOT NULL, |
1768 | + subject TEXT NOT NULL, |
1769 | + message_id TEXT NOT NULL, |
1770 | + message BLOB NOT NULL, |
1771 | + timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
1772 | + datetime TEXT NOT NULL DEFAULT (datetime()), |
1773 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
1774 | UNIQUE (to_addresses, message_id) ON CONFLICT ROLLBACK |
1775 | ); |
1776 | |
1777 | CREATE TABLE IF NOT EXISTS bounce ( |
1778 | - pk INTEGER PRIMARY KEY NOT NULL, |
1779 | - subscription INTEGER NOT NULL UNIQUE, |
1780 | - count INTEGER NOT NULL DEFAULT 0, |
1781 | - last_bounce TEXT NOT NULL DEFAULT (datetime()), |
1782 | + pk INTEGER PRIMARY KEY NOT NULL, |
1783 | + subscription INTEGER NOT NULL UNIQUE, |
1784 | + count INTEGER NOT NULL DEFAULT 0, |
1785 | + last_bounce TEXT NOT NULL DEFAULT (datetime()), |
1786 | FOREIGN KEY (subscription) REFERENCES subscription(pk) ON DELETE CASCADE |
1787 | ); |
1788 | |
1789 | @@ -179,7 +331,8 @@ CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id); |
1790 | CREATE INDEX IF NOT EXISTS list_idx ON list(id); |
1791 | CREATE INDEX IF NOT EXISTS subscription_idx ON subscription(address); |
1792 | |
1793 | - -- [tag:accept_candidate]: Update candidacy with 'subscription' foreign key on 'subscription' insert. |
1794 | + -- [tag:accept_candidate]: Update candidacy with 'subscription' foreign key on |
1795 | + -- 'subscription' insert. |
1796 | CREATE TRIGGER IF NOT EXISTS accept_candidate AFTER INSERT ON subscription |
1797 | FOR EACH ROW |
1798 | BEGIN |
1799 | @@ -187,13 +340,18 @@ BEGIN |
1800 | WHERE candidate_subscription.list = NEW.list AND candidate_subscription.address = NEW.address; |
1801 | END; |
1802 | |
1803 | - -- [tag:verify_subscription_email]: If list settings require e-mail to be verified, |
1804 | - -- update new subscription's 'verify' column value. |
1805 | + -- [tag:verify_subscription_email]: If list settings require e-mail to be |
1806 | + -- verified, update new subscription's 'verify' column value. |
1807 | CREATE TRIGGER IF NOT EXISTS verify_subscription_email AFTER INSERT ON subscription |
1808 | FOR EACH ROW |
1809 | BEGIN |
1810 | - UPDATE subscription SET verified = 0, last_modified = unixepoch() |
1811 | - WHERE subscription.pk = NEW.pk AND EXISTS (SELECT 1 FROM list WHERE pk = NEW.list AND verify = 1); |
1812 | + UPDATE subscription |
1813 | + SET verified = 0, last_modified = unixepoch() |
1814 | + WHERE |
1815 | + subscription.pk = NEW.pk |
1816 | + AND |
1817 | + EXISTS |
1818 | + (SELECT 1 FROM list WHERE pk = NEW.list AND verify = 1); |
1819 | END; |
1820 | |
1821 | -- [tag:add_account]: Update list subscription entries with 'account' foreign |
1822 | @@ -206,11 +364,14 @@ BEGIN |
1823 | END; |
1824 | |
1825 | -- [tag:add_account_to_subscription]: When adding a new 'subscription', auto |
1826 | - -- set 'account' value if there already exists an 'account' entry with the same |
1827 | - -- address. |
1828 | - CREATE TRIGGER IF NOT EXISTS add_account_to_subscription AFTER INSERT ON subscription |
1829 | + -- set 'account' value if there already exists an 'account' entry with the |
1830 | + -- same address. |
1831 | + CREATE TRIGGER IF NOT EXISTS add_account_to_subscription |
1832 | + AFTER INSERT ON subscription |
1833 | FOR EACH ROW |
1834 | - WHEN NEW.account IS NULL AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address) |
1835 | + WHEN |
1836 | + NEW.account IS NULL |
1837 | + AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address) |
1838 | BEGIN |
1839 | UPDATE subscription |
1840 | SET account = (SELECT pk FROM account WHERE address = NEW.address), |
1841 | @@ -218,8 +379,11 @@ BEGIN |
1842 | WHERE subscription.pk = NEW.pk; |
1843 | END; |
1844 | |
1845 | - -- [tag:last_modified_list] update last_modified on every change. |
1846 | - CREATE TRIGGER IF NOT EXISTS last_modified_list AFTER UPDATE ON list |
1847 | + |
1848 | + -- [tag:last_modified_list]: update last_modified on every change. |
1849 | + CREATE TRIGGER |
1850 | + IF NOT EXISTS last_modified_list |
1851 | + AFTER UPDATE ON list |
1852 | FOR EACH ROW |
1853 | WHEN NEW.last_modified != OLD.last_modified |
1854 | BEGIN |
1855 | @@ -227,8 +391,10 @@ BEGIN |
1856 | WHERE pk = NEW.pk; |
1857 | END; |
1858 | |
1859 | - -- [tag:last_modified_owner] update last_modified on every change. |
1860 | - CREATE TRIGGER IF NOT EXISTS last_modified_owner AFTER UPDATE ON owner |
1861 | + -- [tag:last_modified_owner]: update last_modified on every change. |
1862 | + CREATE TRIGGER |
1863 | + IF NOT EXISTS last_modified_owner |
1864 | + AFTER UPDATE ON owner |
1865 | FOR EACH ROW |
1866 | WHEN NEW.last_modified != OLD.last_modified |
1867 | BEGIN |
1868 | @@ -236,8 +402,10 @@ BEGIN |
1869 | WHERE pk = NEW.pk; |
1870 | END; |
1871 | |
1872 | - -- [tag:last_modified_post_policy] update last_modified on every change. |
1873 | - CREATE TRIGGER IF NOT EXISTS last_modified_post_policy AFTER UPDATE ON post_policy |
1874 | + -- [tag:last_modified_post_policy]: update last_modified on every change. |
1875 | + CREATE TRIGGER |
1876 | + IF NOT EXISTS last_modified_post_policy |
1877 | + AFTER UPDATE ON post_policy |
1878 | FOR EACH ROW |
1879 | WHEN NEW.last_modified != OLD.last_modified |
1880 | BEGIN |
1881 | @@ -245,8 +413,10 @@ BEGIN |
1882 | WHERE pk = NEW.pk; |
1883 | END; |
1884 | |
1885 | - -- [tag:last_modified_subscription_policy] update last_modified on every change. |
1886 | - CREATE TRIGGER IF NOT EXISTS last_modified_subscription_policy AFTER UPDATE ON subscription_policy |
1887 | + -- [tag:last_modified_subscription_policy]: update last_modified on every change. |
1888 | + CREATE TRIGGER |
1889 | + IF NOT EXISTS last_modified_subscription_policy |
1890 | + AFTER UPDATE ON subscription_policy |
1891 | FOR EACH ROW |
1892 | WHEN NEW.last_modified != OLD.last_modified |
1893 | BEGIN |
1894 | @@ -254,8 +424,10 @@ BEGIN |
1895 | WHERE pk = NEW.pk; |
1896 | END; |
1897 | |
1898 | - -- [tag:last_modified_subscription] update last_modified on every change. |
1899 | - CREATE TRIGGER IF NOT EXISTS last_modified_subscription AFTER UPDATE ON subscription |
1900 | + -- [tag:last_modified_subscription]: update last_modified on every change. |
1901 | + CREATE TRIGGER |
1902 | + IF NOT EXISTS last_modified_subscription |
1903 | + AFTER UPDATE ON subscription |
1904 | FOR EACH ROW |
1905 | WHEN NEW.last_modified != OLD.last_modified |
1906 | BEGIN |
1907 | @@ -263,8 +435,10 @@ BEGIN |
1908 | WHERE pk = NEW.pk; |
1909 | END; |
1910 | |
1911 | - -- [tag:last_modified_account] update last_modified on every change. |
1912 | - CREATE TRIGGER IF NOT EXISTS last_modified_account AFTER UPDATE ON account |
1913 | + -- [tag:last_modified_account]: update last_modified on every change. |
1914 | + CREATE TRIGGER |
1915 | + IF NOT EXISTS last_modified_account |
1916 | + AFTER UPDATE ON account |
1917 | FOR EACH ROW |
1918 | WHEN NEW.last_modified != OLD.last_modified |
1919 | BEGIN |
1920 | @@ -272,8 +446,10 @@ BEGIN |
1921 | WHERE pk = NEW.pk; |
1922 | END; |
1923 | |
1924 | - -- [tag:last_modified_candidate_subscription] update last_modified on every change. |
1925 | - CREATE TRIGGER IF NOT EXISTS last_modified_candidate_subscription AFTER UPDATE ON candidate_subscription |
1926 | + -- [tag:last_modified_candidate_subscription]: update last_modified on every change. |
1927 | + CREATE TRIGGER |
1928 | + IF NOT EXISTS last_modified_candidate_subscription |
1929 | + AFTER UPDATE ON candidate_subscription |
1930 | FOR EACH ROW |
1931 | WHEN NEW.last_modified != OLD.last_modified |
1932 | BEGIN |
1933 | @@ -281,8 +457,10 @@ BEGIN |
1934 | WHERE pk = NEW.pk; |
1935 | END; |
1936 | |
1937 | - -- [tag:last_modified_templates] update last_modified on every change. |
1938 | - CREATE TRIGGER IF NOT EXISTS last_modified_templates AFTER UPDATE ON templates |
1939 | + -- [tag:last_modified_templates]: update last_modified on every change. |
1940 | + CREATE TRIGGER |
1941 | + IF NOT EXISTS last_modified_templates |
1942 | + AFTER UPDATE ON templates |
1943 | FOR EACH ROW |
1944 | WHEN NEW.last_modified != OLD.last_modified |
1945 | BEGIN |
1946 | diff --git a/core/src/schema.sql.m4 b/core/src/schema.sql.m4 |
1947 | index 32df925..3d0fa1f 100644 |
1948 | --- a/core/src/schema.sql.m4 |
1949 | +++ b/core/src/schema.sql.m4 |
1950 | @@ -1,34 +1,54 @@ |
1951 | - define(xor, `(($1) OR ($2)) AND NOT (($1) AND ($2))')dnl |
1952 | - define(BOOLEAN_TYPE, `$1 BOOLEAN CHECK ($1 in (0, 1)) NOT NULL')dnl |
1953 | + define(xor, `dnl |
1954 | + ( |
1955 | + ($1) OR ($2) |
1956 | + ) |
1957 | + AND NOT |
1958 | + ( |
1959 | + ($1) AND ($2) |
1960 | + )')dnl |
1961 | + dnl |
1962 | + dnl # Define boolean column types and defaults |
1963 | + define(BOOLEAN_TYPE, `BOOLEAN CHECK ($1 IN (0, 1)) NOT NULL')dnl |
1964 | define(BOOLEAN_FALSE, `0')dnl |
1965 | define(BOOLEAN_TRUE, `1')dnl |
1966 | - define(__TAG, `tag')dnl # Write the string '['+'tag'+':'+... with a macro so that tagref check doesn't pick up on it as a duplicate. |
1967 | + dnl |
1968 | + dnl # defile comment functions |
1969 | + dnl |
1970 | + dnl # Write the string '['+'tag'+':'+... with a macro so that tagref check |
1971 | + dnl # doesn't pick up on it as a duplicate. |
1972 | + define(__TAG, `tag')dnl |
1973 | define(TAG, `['__TAG()`:$1]')dnl |
1974 | - define(update_last_modified, `-- 'TAG(last_modified_$1)` update last_modified on every change. |
1975 | - CREATE TRIGGER IF NOT EXISTS last_modified_$1 AFTER UPDATE ON $1 |
1976 | + dnl |
1977 | + dnl # define triggers |
1978 | + define(update_last_modified, ` |
1979 | + -- 'TAG(last_modified_$1)`: update last_modified on every change. |
1980 | + CREATE TRIGGER |
1981 | + IF NOT EXISTS last_modified_$1 |
1982 | + AFTER UPDATE ON $1 |
1983 | FOR EACH ROW |
1984 | WHEN NEW.last_modified != OLD.last_modified |
1985 | BEGIN |
1986 | UPDATE $1 SET last_modified = unixepoch() |
1987 | WHERE pk = NEW.pk; |
1988 | END;')dnl |
1989 | + dnl |
1990 | PRAGMA foreign_keys = true; |
1991 | PRAGMA encoding = 'UTF-8'; |
1992 | |
1993 | CREATE TABLE IF NOT EXISTS list ( |
1994 | - pk INTEGER PRIMARY KEY NOT NULL, |
1995 | - name TEXT NOT NULL, |
1996 | - id TEXT NOT NULL UNIQUE, |
1997 | - address TEXT NOT NULL UNIQUE, |
1998 | - owner_local_part TEXT, |
1999 | - request_local_part TEXT, |
2000 | - archive_url TEXT, |
2001 | - description TEXT, |
2002 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2003 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2004 | - BOOLEAN_TYPE(verify) DEFAULT BOOLEAN_TRUE(), |
2005 | - BOOLEAN_TYPE(hidden) DEFAULT BOOLEAN_FALSE(), |
2006 | - BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE() |
2007 | + pk INTEGER PRIMARY KEY NOT NULL, |
2008 | + name TEXT NOT NULL, |
2009 | + id TEXT NOT NULL UNIQUE, |
2010 | + address TEXT NOT NULL UNIQUE, |
2011 | + owner_local_part TEXT, |
2012 | + request_local_part TEXT, |
2013 | + archive_url TEXT, |
2014 | + description TEXT, |
2015 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2016 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2017 | + verify BOOLEAN_TYPE(verify) DEFAULT BOOLEAN_TRUE(), |
2018 | + hidden BOOLEAN_TYPE(hidden) DEFAULT BOOLEAN_FALSE(), |
2019 | + enabled BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE() |
2020 | ); |
2021 | |
2022 | CREATE TABLE IF NOT EXISTS owner ( |
2023 | @@ -42,100 +62,111 @@ CREATE TABLE IF NOT EXISTS owner ( |
2024 | ); |
2025 | |
2026 | CREATE TABLE IF NOT EXISTS post_policy ( |
2027 | - pk INTEGER PRIMARY KEY NOT NULL, |
2028 | - list INTEGER NOT NULL UNIQUE, |
2029 | - BOOLEAN_TYPE(announce_only) DEFAULT BOOLEAN_FALSE(), |
2030 | - BOOLEAN_TYPE(subscription_only) DEFAULT BOOLEAN_FALSE(), |
2031 | - BOOLEAN_TYPE(approval_needed) DEFAULT BOOLEAN_FALSE(), |
2032 | - BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(), |
2033 | - BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(), |
2034 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2035 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
2036 | + pk INTEGER PRIMARY KEY NOT NULL, |
2037 | + list INTEGER NOT NULL UNIQUE, |
2038 | + announce_only BOOLEAN_TYPE(announce_only) |
2039 | + DEFAULT BOOLEAN_FALSE(), |
2040 | + subscription_only BOOLEAN_TYPE(subscription_only) |
2041 | + DEFAULT BOOLEAN_FALSE(), |
2042 | + approval_needed BOOLEAN_TYPE(approval_needed) |
2043 | + DEFAULT BOOLEAN_FALSE(), |
2044 | + open BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(), |
2045 | + custom BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(), |
2046 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2047 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
2048 | CHECK(xor(custom, xor(open, xor(approval_needed, xor(announce_only, subscription_only))))), |
2049 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE |
2050 | ); |
2051 | |
2052 | CREATE TABLE IF NOT EXISTS subscription_policy ( |
2053 | - pk INTEGER PRIMARY KEY NOT NULL, |
2054 | - list INTEGER NOT NULL UNIQUE, |
2055 | - BOOLEAN_TYPE(send_confirmation) DEFAULT BOOLEAN_TRUE(), |
2056 | - BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(), |
2057 | - BOOLEAN_TYPE(manual) DEFAULT BOOLEAN_FALSE(), |
2058 | - BOOLEAN_TYPE(request) DEFAULT BOOLEAN_FALSE(), |
2059 | - BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(), |
2060 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2061 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2062 | + pk INTEGER PRIMARY KEY NOT NULL, |
2063 | + list INTEGER NOT NULL UNIQUE, |
2064 | + send_confirmation BOOLEAN_TYPE(send_confirmation) |
2065 | + DEFAULT BOOLEAN_TRUE(), |
2066 | + open BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(), |
2067 | + manual BOOLEAN_TYPE(manual) DEFAULT BOOLEAN_FALSE(), |
2068 | + request BOOLEAN_TYPE(request) DEFAULT BOOLEAN_FALSE(), |
2069 | + custom BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(), |
2070 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2071 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2072 | CHECK(xor(open, xor(manual, xor(request, custom)))), |
2073 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE |
2074 | ); |
2075 | |
2076 | CREATE TABLE IF NOT EXISTS subscription ( |
2077 | - pk INTEGER PRIMARY KEY NOT NULL, |
2078 | - list INTEGER NOT NULL, |
2079 | - address TEXT NOT NULL, |
2080 | - name TEXT, |
2081 | - account INTEGER, |
2082 | - BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(), |
2083 | - BOOLEAN_TYPE(verified) DEFAULT BOOLEAN_TRUE(), |
2084 | - BOOLEAN_TYPE(digest) DEFAULT BOOLEAN_FALSE(), |
2085 | - BOOLEAN_TYPE(hide_address) DEFAULT BOOLEAN_FALSE(), |
2086 | - BOOLEAN_TYPE(receive_duplicates) DEFAULT BOOLEAN_TRUE(), |
2087 | - BOOLEAN_TYPE(receive_own_posts) DEFAULT BOOLEAN_FALSE(), |
2088 | - BOOLEAN_TYPE(receive_confirmation) DEFAULT BOOLEAN_TRUE(), |
2089 | - last_digest INTEGER NOT NULL DEFAULT (unixepoch()), |
2090 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2091 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2092 | + pk INTEGER PRIMARY KEY NOT NULL, |
2093 | + list INTEGER NOT NULL, |
2094 | + address TEXT NOT NULL, |
2095 | + name TEXT, |
2096 | + account INTEGER, |
2097 | + enabled BOOLEAN_TYPE(enabled) |
2098 | + DEFAULT BOOLEAN_TRUE(), |
2099 | + verified BOOLEAN_TYPE(verified) |
2100 | + DEFAULT BOOLEAN_TRUE(), |
2101 | + digest BOOLEAN_TYPE(digest) |
2102 | + DEFAULT BOOLEAN_FALSE(), |
2103 | + hide_address BOOLEAN_TYPE(hide_address) |
2104 | + DEFAULT BOOLEAN_FALSE(), |
2105 | + receive_duplicates BOOLEAN_TYPE(receive_duplicates) |
2106 | + DEFAULT BOOLEAN_TRUE(), |
2107 | + receive_own_posts BOOLEAN_TYPE(receive_own_posts) |
2108 | + DEFAULT BOOLEAN_FALSE(), |
2109 | + receive_confirmation BOOLEAN_TYPE(receive_confirmation) |
2110 | + DEFAULT BOOLEAN_TRUE(), |
2111 | + last_digest INTEGER NOT NULL DEFAULT (unixepoch()), |
2112 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2113 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2114 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
2115 | FOREIGN KEY (account) REFERENCES account(pk) ON DELETE SET NULL, |
2116 | UNIQUE (list, address) ON CONFLICT ROLLBACK |
2117 | ); |
2118 | |
2119 | CREATE TABLE IF NOT EXISTS account ( |
2120 | - pk INTEGER PRIMARY KEY NOT NULL, |
2121 | - name TEXT, |
2122 | - address TEXT NOT NULL UNIQUE, |
2123 | - public_key TEXT, |
2124 | - password TEXT NOT NULL, |
2125 | - BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(), |
2126 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2127 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
2128 | + pk INTEGER PRIMARY KEY NOT NULL, |
2129 | + name TEXT, |
2130 | + address TEXT NOT NULL UNIQUE, |
2131 | + public_key TEXT, |
2132 | + password TEXT NOT NULL, |
2133 | + enabled BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(), |
2134 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2135 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()) |
2136 | ); |
2137 | |
2138 | CREATE TABLE IF NOT EXISTS candidate_subscription ( |
2139 | - pk INTEGER PRIMARY KEY NOT NULL, |
2140 | - list INTEGER NOT NULL, |
2141 | - address TEXT NOT NULL, |
2142 | - name TEXT, |
2143 | - accepted INTEGER UNIQUE, |
2144 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2145 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2146 | + pk INTEGER PRIMARY KEY NOT NULL, |
2147 | + list INTEGER NOT NULL, |
2148 | + address TEXT NOT NULL, |
2149 | + name TEXT, |
2150 | + accepted INTEGER UNIQUE, |
2151 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2152 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2153 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
2154 | FOREIGN KEY (accepted) REFERENCES subscription(pk) ON DELETE CASCADE, |
2155 | UNIQUE (list, address) ON CONFLICT ROLLBACK |
2156 | ); |
2157 | |
2158 | CREATE TABLE IF NOT EXISTS post ( |
2159 | - pk INTEGER PRIMARY KEY NOT NULL, |
2160 | - list INTEGER NOT NULL, |
2161 | - envelope_from TEXT, |
2162 | - address TEXT NOT NULL, |
2163 | - message_id TEXT NOT NULL, |
2164 | - message BLOB NOT NULL, |
2165 | - headers_json TEXT, |
2166 | - timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
2167 | - datetime TEXT NOT NULL DEFAULT (datetime()), |
2168 | - created INTEGER NOT NULL DEFAULT (unixepoch()) |
2169 | + pk INTEGER PRIMARY KEY NOT NULL, |
2170 | + list INTEGER NOT NULL, |
2171 | + envelope_from TEXT, |
2172 | + address TEXT NOT NULL, |
2173 | + message_id TEXT NOT NULL, |
2174 | + message BLOB NOT NULL, |
2175 | + headers_json TEXT, |
2176 | + timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
2177 | + datetime TEXT NOT NULL DEFAULT (datetime()), |
2178 | + created INTEGER NOT NULL DEFAULT (unixepoch()) |
2179 | ); |
2180 | |
2181 | CREATE TABLE IF NOT EXISTS templates ( |
2182 | - pk INTEGER PRIMARY KEY NOT NULL, |
2183 | - name TEXT NOT NULL, |
2184 | - list INTEGER, |
2185 | - subject TEXT, |
2186 | - headers_json TEXT, |
2187 | - body TEXT NOT NULL, |
2188 | - created INTEGER NOT NULL DEFAULT (unixepoch()), |
2189 | - last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2190 | + pk INTEGER PRIMARY KEY NOT NULL, |
2191 | + name TEXT NOT NULL, |
2192 | + list INTEGER, |
2193 | + subject TEXT, |
2194 | + headers_json TEXT, |
2195 | + body TEXT NOT NULL, |
2196 | + created INTEGER NOT NULL DEFAULT (unixepoch()), |
2197 | + last_modified INTEGER NOT NULL DEFAULT (unixepoch()), |
2198 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
2199 | UNIQUE (list, name) ON CONFLICT ROLLBACK |
2200 | ); |
2201 | @@ -144,47 +175,56 @@ CREATE TABLE IF NOT EXISTS templates ( |
2202 | -- |
2203 | -- ## The "maildrop" queue |
2204 | -- |
2205 | - -- Messages that have been submitted but not yet processed, await processing in |
2206 | - -- the "maildrop" queue. Messages can be added to the "maildrop" queue even when |
2207 | - -- mailpot is not running. |
2208 | + -- Messages that have been submitted but not yet processed, await processing |
2209 | + -- in the "maildrop" queue. Messages can be added to the "maildrop" queue |
2210 | + -- even when mailpot is not running. |
2211 | -- |
2212 | -- ## The "deferred" queue |
2213 | -- |
2214 | - -- When all the deliverable recipients for a message are delivered, and for some |
2215 | - -- recipients delivery failed for a transient reason (it might succeed later), the |
2216 | - -- message is placed in the "deferred" queue. |
2217 | + -- When all the deliverable recipients for a message are delivered, and for |
2218 | + -- some recipients delivery failed for a transient reason (it might succeed |
2219 | + -- later), the message is placed in the "deferred" queue. |
2220 | -- |
2221 | -- ## The "hold" queue |
2222 | -- |
2223 | - -- List administrators may introduce rules for emails to be placed indefinitely in |
2224 | - -- the "hold" queue. Messages placed in the "hold" queue stay there until the |
2225 | - -- administrator intervenes. No periodic delivery attempts are made for messages |
2226 | - -- in the "hold" queue. |
2227 | + -- List administrators may introduce rules for emails to be placed |
2228 | + -- indefinitely in the "hold" queue. Messages placed in the "hold" queue stay |
2229 | + -- there until the administrator intervenes. No periodic delivery attempts |
2230 | + -- are made for messages in the "hold" queue. |
2231 | |
2232 | -- ## The "out" queue |
2233 | -- |
2234 | -- Emails that must be sent as soon as possible. |
2235 | CREATE TABLE IF NOT EXISTS queue ( |
2236 | - pk INTEGER PRIMARY KEY NOT NULL, |
2237 | - which TEXT CHECK (which IN ('maildrop', 'hold', 'deferred', 'corrupt', 'error', 'out')) NOT NULL, |
2238 | - list INTEGER, |
2239 | - comment TEXT, |
2240 | - to_addresses TEXT NOT NULL, |
2241 | - from_address TEXT NOT NULL, |
2242 | - subject TEXT NOT NULL, |
2243 | - message_id TEXT NOT NULL, |
2244 | - message BLOB NOT NULL, |
2245 | - timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
2246 | - datetime TEXT NOT NULL DEFAULT (datetime()), |
2247 | + pk INTEGER PRIMARY KEY NOT NULL, |
2248 | + which TEXT |
2249 | + CHECK ( |
2250 | + which IN |
2251 | + ('maildrop', |
2252 | + 'hold', |
2253 | + 'deferred', |
2254 | + 'corrupt', |
2255 | + 'error', |
2256 | + 'out') |
2257 | + ) NOT NULL, |
2258 | + list INTEGER, |
2259 | + comment TEXT, |
2260 | + to_addresses TEXT NOT NULL, |
2261 | + from_address TEXT NOT NULL, |
2262 | + subject TEXT NOT NULL, |
2263 | + message_id TEXT NOT NULL, |
2264 | + message BLOB NOT NULL, |
2265 | + timestamp INTEGER NOT NULL DEFAULT (unixepoch()), |
2266 | + datetime TEXT NOT NULL DEFAULT (datetime()), |
2267 | FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE, |
2268 | UNIQUE (to_addresses, message_id) ON CONFLICT ROLLBACK |
2269 | ); |
2270 | |
2271 | CREATE TABLE IF NOT EXISTS bounce ( |
2272 | - pk INTEGER PRIMARY KEY NOT NULL, |
2273 | - subscription INTEGER NOT NULL UNIQUE, |
2274 | - count INTEGER NOT NULL DEFAULT 0, |
2275 | - last_bounce TEXT NOT NULL DEFAULT (datetime()), |
2276 | + pk INTEGER PRIMARY KEY NOT NULL, |
2277 | + subscription INTEGER NOT NULL UNIQUE, |
2278 | + count INTEGER NOT NULL DEFAULT 0, |
2279 | + last_bounce TEXT NOT NULL DEFAULT (datetime()), |
2280 | FOREIGN KEY (subscription) REFERENCES subscription(pk) ON DELETE CASCADE |
2281 | ); |
2282 | |
2283 | @@ -193,7 +233,8 @@ CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id); |
2284 | CREATE INDEX IF NOT EXISTS list_idx ON list(id); |
2285 | CREATE INDEX IF NOT EXISTS subscription_idx ON subscription(address); |
2286 | |
2287 | - -- TAG(accept_candidate): Update candidacy with 'subscription' foreign key on 'subscription' insert. |
2288 | + -- TAG(accept_candidate): Update candidacy with 'subscription' foreign key on |
2289 | + -- 'subscription' insert. |
2290 | CREATE TRIGGER IF NOT EXISTS accept_candidate AFTER INSERT ON subscription |
2291 | FOR EACH ROW |
2292 | BEGIN |
2293 | @@ -201,13 +242,18 @@ BEGIN |
2294 | WHERE candidate_subscription.list = NEW.list AND candidate_subscription.address = NEW.address; |
2295 | END; |
2296 | |
2297 | - -- TAG(verify_subscription_email): If list settings require e-mail to be verified, |
2298 | - -- update new subscription's 'verify' column value. |
2299 | + -- TAG(verify_subscription_email): If list settings require e-mail to be |
2300 | + -- verified, update new subscription's 'verify' column value. |
2301 | CREATE TRIGGER IF NOT EXISTS verify_subscription_email AFTER INSERT ON subscription |
2302 | FOR EACH ROW |
2303 | BEGIN |
2304 | - UPDATE subscription SET verified = BOOLEAN_FALSE(), last_modified = unixepoch() |
2305 | - WHERE subscription.pk = NEW.pk AND EXISTS (SELECT 1 FROM list WHERE pk = NEW.list AND verify = BOOLEAN_TRUE()); |
2306 | + UPDATE subscription |
2307 | + SET verified = BOOLEAN_FALSE(), last_modified = unixepoch() |
2308 | + WHERE |
2309 | + subscription.pk = NEW.pk |
2310 | + AND |
2311 | + EXISTS |
2312 | + (SELECT 1 FROM list WHERE pk = NEW.list AND verify = BOOLEAN_TRUE()); |
2313 | END; |
2314 | |
2315 | -- TAG(add_account): Update list subscription entries with 'account' foreign |
2316 | @@ -220,11 +266,14 @@ BEGIN |
2317 | END; |
2318 | |
2319 | -- TAG(add_account_to_subscription): When adding a new 'subscription', auto |
2320 | - -- set 'account' value if there already exists an 'account' entry with the same |
2321 | - -- address. |
2322 | - CREATE TRIGGER IF NOT EXISTS add_account_to_subscription AFTER INSERT ON subscription |
2323 | + -- set 'account' value if there already exists an 'account' entry with the |
2324 | + -- same address. |
2325 | + CREATE TRIGGER IF NOT EXISTS add_account_to_subscription |
2326 | + AFTER INSERT ON subscription |
2327 | FOR EACH ROW |
2328 | - WHEN NEW.account IS NULL AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address) |
2329 | + WHEN |
2330 | + NEW.account IS NULL |
2331 | + AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address) |
2332 | BEGIN |
2333 | UPDATE subscription |
2334 | SET account = (SELECT pk FROM account WHERE address = NEW.address), |
2335 | @@ -233,17 +282,10 @@ BEGIN |
2336 | END; |
2337 | |
2338 | update_last_modified(`list') |
2339 | - |
2340 | update_last_modified(`owner') |
2341 | - |
2342 | update_last_modified(`post_policy') |
2343 | - |
2344 | update_last_modified(`subscription_policy') |
2345 | - |
2346 | update_last_modified(`subscription') |
2347 | - |
2348 | update_last_modified(`account') |
2349 | - |
2350 | update_last_modified(`candidate_subscription') |
2351 | - |
2352 | update_last_modified(`templates') |
2353 | diff --git a/core/tests/smtp.rs b/core/tests/smtp.rs |
2354 | index 31527b1..b8c6469 100644 |
2355 | --- a/core/tests/smtp.rs |
2356 | +++ b/core/tests/smtp.rs |
2357 | @@ -18,7 +18,7 @@ |
2358 | */ |
2359 | |
2360 | use log::{trace, warn}; |
2361 | - use mailpot::{melib, Configuration, Connection, Queue, SendMail}; |
2362 | + use mailpot::{melib, models::*, Configuration, Connection, Queue, SendMail}; |
2363 | use mailpot_tests::*; |
2364 | use melib::smol; |
2365 | use tempfile::TempDir; |
2366 | diff --git a/mailpot-tests/src/lib.rs b/mailpot-tests/src/lib.rs |
2367 | index 20307a2..dccb32b 100644 |
2368 | --- a/mailpot-tests/src/lib.rs |
2369 | +++ b/mailpot-tests/src/lib.rs |
2370 | @@ -33,11 +33,6 @@ use mailin_embedded::{ |
2371 | response::{INTERNAL_ERROR, OK}, |
2372 | Handler, Response, Server, |
2373 | }; |
2374 | - pub use mailpot::{ |
2375 | - melib::{self, smol, smtp::SmtpServerConf}, |
2376 | - models::{changesets::ListSubscriptionChangeset, *}, |
2377 | - Configuration, Connection, Queue, SendMail, |
2378 | - }; |
2379 | pub use predicates; |
2380 | pub use tempfile::{self, TempDir}; |
2381 | |
2382 | @@ -84,9 +79,9 @@ pub struct TestSmtpHandler { |
2383 | address: Cow<'static, str>, |
2384 | ssl: SslConfig, |
2385 | envelope_from: Cow<'static, str>, |
2386 | - auth: melib::smtp::SmtpAuth, |
2387 | + auth: mailpot::melib::smtp::SmtpAuth, |
2388 | pub messages: Arc<Mutex<Vec<((IpAddr, String), Message)>>>, |
2389 | - pub stored: Arc<Mutex<Vec<(String, melib::Envelope)>>>, |
2390 | + pub stored: Arc<Mutex<Vec<(String, mailpot::melib::Envelope)>>>, |
2391 | } |
2392 | |
2393 | impl Handler for TestSmtpHandler { |
2394 | @@ -181,7 +176,7 @@ impl Handler for TestSmtpHandler { |
2395 | let last = self.messages.lock().unwrap().pop(); |
2396 | if let Some(((ip, domain), Message::Data { from: _, to, buf })) = last { |
2397 | for to in to { |
2398 | - match melib::Envelope::from_bytes(&buf, None) { |
2399 | + match mailpot::melib::Envelope::from_bytes(&buf, None) { |
2400 | Ok(env) => { |
2401 | self.stored.lock().unwrap().push((to.clone(), env)); |
2402 | } |
2403 | @@ -202,8 +197,8 @@ impl Handler for TestSmtpHandler { |
2404 | |
2405 | impl TestSmtpHandler { |
2406 | #[inline] |
2407 | - pub fn smtp_conf(&self) -> melib::smtp::SmtpServerConf { |
2408 | - use melib::smtp::*; |
2409 | + pub fn smtp_conf(&self) -> mailpot::melib::smtp::SmtpServerConf { |
2410 | + use mailpot::melib::smtp::*; |
2411 | let sockaddr = self |
2412 | .address |
2413 | .as_ref() |
2414 | @@ -234,7 +229,7 @@ impl TestSmtpHandler { |
2415 | pub struct TestSmtpHandlerBuilder { |
2416 | address: Cow<'static, str>, |
2417 | ssl: SslConfig, |
2418 | - auth: melib::smtp::SmtpAuth, |
2419 | + auth: mailpot::melib::smtp::SmtpAuth, |
2420 | envelope_from: Cow<'static, str>, |
2421 | } |
2422 | |
2423 | @@ -243,7 +238,7 @@ impl TestSmtpHandlerBuilder { |
2424 | Self { |
2425 | address: ADDRESS.into(), |
2426 | ssl: SslConfig::None, |
2427 | - auth: melib::smtp::SmtpAuth::None, |
2428 | + auth: mailpot::melib::smtp::SmtpAuth::None, |
2429 | envelope_from: "foo-chat@example.com".into(), |
2430 | } |
2431 | } |
2432 | diff --git a/rest-http/src/lib.rs b/rest-http/src/lib.rs |
2433 | new file mode 100644 |
2434 | index 0000000..48813d6 |
2435 | --- /dev/null |
2436 | +++ b/rest-http/src/lib.rs |
2437 | @@ -0,0 +1,18 @@ |
2438 | + /* |
2439 | + * This file is part of mailpot |
2440 | + * |
2441 | + * Copyright 2020 - Manos Pitsidianakis |
2442 | + * |
2443 | + * This program is free software: you can redistribute it and/or modify |
2444 | + * it under the terms of the GNU Affero General Public License as |
2445 | + * published by the Free Software Foundation, either version 3 of the |
2446 | + * License, or (at your option) any later version. |
2447 | + * |
2448 | + * This program is distributed in the hope that it will be useful, |
2449 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2450 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2451 | + * GNU Affero General Public License for more details. |
2452 | + * |
2453 | + * You should have received a copy of the GNU Affero General Public License |
2454 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
2455 | + */ |
2456 | diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs |
2457 | index 8de3a08..85a1065 100644 |
2458 | --- a/rest-http/src/main.rs |
2459 | +++ b/rest-http/src/main.rs |
2460 | @@ -17,7 +17,7 @@ |
2461 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
2462 | */ |
2463 | |
2464 | - pub use mailpot::{models::*, *}; |
2465 | + use mailpot::*; |
2466 | use warp::Filter; |
2467 | |
2468 | #[tokio::main] |
2469 | diff --git a/web/src/lib.rs b/web/src/lib.rs |
2470 | index dfc7f66..6b17d81 100644 |
2471 | --- a/web/src/lib.rs |
2472 | +++ b/web/src/lib.rs |
2473 | @@ -74,7 +74,7 @@ use std::{borrow::Cow, collections::HashMap, sync::Arc}; |
2474 | |
2475 | use chrono::Datelike; |
2476 | pub use http::{Request, Response, StatusCode}; |
2477 | - pub use mailpot::{models::DbVal, rusqlite::OptionalExtension, *}; |
2478 | + use mailpot::{models::DbVal, rusqlite::OptionalExtension, *}; |
2479 | use minijinja::{ |
2480 | value::{Object, Value}, |
2481 | Environment, Error, |
2482 | diff --git a/web/src/main.rs b/web/src/main.rs |
2483 | index c694c81..52b7a00 100644 |
2484 | --- a/web/src/main.rs |
2485 | +++ b/web/src/main.rs |
2486 | @@ -19,6 +19,7 @@ |
2487 | |
2488 | use std::{collections::HashMap, sync::Arc}; |
2489 | |
2490 | + use mailpot::{Configuration, Connection}; |
2491 | use mailpot_web::*; |
2492 | use minijinja::value::Value; |
2493 | use rand::Rng; |