diff --git a/Cargo.lock b/Cargo.lock index 1a2a526..efdfabe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,13 +15,68 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "aead" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher-trait", +] + +[[package]] +name = "aes-gcm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834a6bda386024dbb7c8fc51322856c10ffe69559f972261c868485f5759c638" +dependencies = [ + "aead", + "aes", + "block-cipher-trait", + "ghash", + "subtle 2.2.3", + "zeroize", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +dependencies = [ + "block-cipher-trait", + "opaque-debug", +] + [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -57,9 +112,9 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", ] [[package]] @@ -82,7 +137,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -105,6 +160,16 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + [[package]] name = "base64" version = "0.12.3" @@ -127,6 +192,36 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "blocking" version = "0.4.7" @@ -141,6 +236,12 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -201,6 +302,22 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "cookie" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5795cda0897252e34380a27baf884c53aa7ad9990329cdad96d4c5d027015d44" +dependencies = [ + "aes-gcm", + "base64 0.12.3", + "hkdf", + "hmac", + "percent-encoding 2.1.0", + "rand", + "sha2", + "time", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -289,12 +406,85 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array", + "subtle 1.0.0", +] + [[package]] name = "data-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4d0e2d24e5ee3b23a01de38eefdcd978907890701f08ffffd4cb457ca4ee8d6" +[[package]] +name = "devise" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7" +dependencies = [ + "devise_core", + "quote 0.6.13", +] + +[[package]] +name = "devise_core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487" +dependencies = [ + "bitflags", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "diesel" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2de9deab977a153492a1468d1b1c0662c1cf39e5ea87d0c060ecd59ef18d8c" +dependencies = [ + "byteorder", + "diesel_derives", + "libsqlite3-sys", +] + +[[package]] +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "encoding" version = "0.2.33" @@ -366,7 +556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "backtrace", - "version_check", + "version_check 0.9.2", ] [[package]] @@ -375,6 +565,12 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f14646a9e0430150a87951622ba9675472b68e384b7701b8423b30560805c7a" +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -393,6 +589,18 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bd3bdaaf0a72155260a1c098989b60db1cbb22d6a628e64f16237aa4da93cc7" +[[package]] +name = "filetime" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -408,6 +616,41 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futures" version = "0.3.5" @@ -478,9 +721,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", ] [[package]] @@ -518,6 +761,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -529,12 +781,36 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0930ed19a7184089ea46d2fedead2f6dc2b674c5db4276b7da336c7cd83252" +dependencies = [ + "polyval", +] + [[package]] name = "gimli" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" +dependencies = [ + "autocfg", +] + [[package]] name = "heck" version = "0.3.1" @@ -553,12 +829,135 @@ dependencies = [ "libc", ] +[[package]] +name = "hkdf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "hyper" +version = "0.10.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" +dependencies = [ + "base64 0.9.3", + "httparse", + "language-tags", + "log 0.3.9", + "mime", + "num_cpus", + "time", + "traitobject", + "typeable", + "unicase", + "url", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lexical-core" version = "0.7.4" @@ -584,7 +983,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -603,6 +1002,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.11", +] + [[package]] name = "log" version = "0.4.11" @@ -621,6 +1029,44 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "mailpot" +version = "0.1.0" +dependencies = [ + "chrono", + "diesel", + "error-chain", + "melib", + "rusqlite", + "serde", + "serde_json", + "toml 0.5.6", + "xdg", +] + +[[package]] +name = "mailpot-cli" +version = "0.1.0" +dependencies = [ + "mailpot", + "structopt", +] + +[[package]] +name = "mailpot-http" +version = "0.1.0" +dependencies = [ + "mailpot", + "rocket", + "rocket_contrib", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -630,10 +1076,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "melib" version = "0.6.1" -source = "git+https://github.com/meli/meli#30c390443adb390192b225178dfd461331170edf" dependencies = [ "async-stream", - "base64", + "base64 0.12.3", "bincode", "bitflags", "crossbeam", @@ -669,6 +1114,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +dependencies = [ + "log 0.3.9", +] + [[package]] name = "miniz_oxide" version = "0.4.0" @@ -679,16 +1133,46 @@ dependencies = [ ] [[package]] -name = "mpot" -version = "0.1.0" +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ - "chrono", - "error-chain", - "melib", - "rusqlite", - "serde", - "structopt", - "xdg", + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log 0.4.11", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log 0.4.11", + "mio", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] @@ -699,7 +1183,7 @@ checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" dependencies = [ "lazy_static", "libc", - "log", + "log 0.4.11", "openssl", "openssl-probe", "openssl-sys", @@ -709,6 +1193,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + [[package]] name = "nix" version = "0.17.0" @@ -730,7 +1225,25 @@ checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "lexical-core", "memchr", - "version_check", + "version_check 0.9.2", +] + +[[package]] +name = "notify" +version = "4.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio", + "mio-extras", + "walkdir", + "winapi 0.3.9", ] [[package]] @@ -752,6 +1265,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.20.0" @@ -764,6 +1287,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "openssl" version = "0.10.30" @@ -809,6 +1338,40 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "pear" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5320f212db967792b67cfe12bd469d08afd6318a249bd917d5c19bc92200ab8a" +dependencies = [ + "pear_codegen", +] + +[[package]] +name = "pear_codegen" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc1c836fdc3d1ef87c348b237b5b5c4dff922156fb2d968f57734f9669768ca" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", + "version_check 0.9.2", + "yansi", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "pin-project" version = "0.4.23" @@ -824,9 +1387,9 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", ] [[package]] @@ -847,6 +1410,16 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" +[[package]] +name = "polyval" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212" +dependencies = [ + "cfg-if", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.8" @@ -860,10 +1433,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", + "version_check 0.9.2", ] [[package]] @@ -872,9 +1445,9 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "proc-macro2 1.0.19", + "quote 1.0.7", + "version_check 0.9.2", ] [[package]] @@ -889,13 +1462,31 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.1", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", ] [[package]] @@ -904,7 +1495,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.19", ] [[package]] @@ -960,7 +1551,73 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "rocket" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6130967b369cfb8411b0b73e96fcba1229c32a9cc6f295d144f879bfced13c6e" +dependencies = [ + "atty", + "base64 0.12.3", + "log 0.4.11", + "memchr", + "num_cpus", + "pear", + "rocket_codegen", + "rocket_http", + "state", + "time", + "toml 0.4.10", + "version_check 0.9.2", + "yansi", +] + +[[package]] +name = "rocket_codegen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb852e6da168fb948a8f2b798ba2e2f0e4fc860eae0efa9cf2bf0f5466bb0425" +dependencies = [ + "devise", + "glob", + "indexmap", + "quote 0.6.13", + "rocket_http", + "version_check 0.9.2", + "yansi", +] + +[[package]] +name = "rocket_contrib" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3946ca815127041d8f64455561031d058c22ae1b135251502c5ea523cf9e14b" +dependencies = [ + "log 0.4.11", + "notify", + "rocket", + "serde", + "serde_json", +] + +[[package]] +name = "rocket_http" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aff5a5480175f2f553a876b251e9350c74196128806d176da3a51c82aab5428" +dependencies = [ + "cookie", + "hyper", + "indexmap", + "pear", + "percent-encoding 1.0.1", + "smallvec", + "state", + "time", + "unicode-xid 0.1.0", ] [[package]] @@ -990,6 +1647,21 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.19" @@ -997,7 +1669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1050,9 +1722,32 @@ version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", +] + +[[package]] +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] @@ -1088,7 +1783,7 @@ dependencies = [ "slab", "socket2", "wepoll-sys-stjepang", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1100,9 +1795,15 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "state" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1134,9 +1835,32 @@ checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99" dependencies = [ "heck", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.19", + "quote 1.0.7", + "syn 1.0.38", +] + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + +[[package]] +name = "subtle" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] @@ -1145,9 +1869,9 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.19", + "quote 1.0.7", + "unicode-xid 0.2.1", ] [[package]] @@ -1161,7 +1885,7 @@ dependencies = [ "rand", "redox_syscall", "remove_dir_all", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1180,7 +1904,76 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "tinyvec" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" + +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check 0.1.5", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +dependencies = [ + "tinyvec", ] [[package]] @@ -1195,12 +1988,39 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "universal-hash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01" +dependencies = [ + "generic-array", + "subtle 2.2.3", +] + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna", + "matches", + "percent-encoding 1.0.1", +] + [[package]] name = "uuid" version = "0.8.1" @@ -1223,6 +2043,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.2" @@ -1241,6 +2067,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1256,6 +2093,12 @@ dependencies = [ "cc", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -1266,20 +2109,57 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "xdg" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/Cargo.toml b/Cargo.toml index c77f4b6..fd216ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,6 @@ -[package] -name = "mailpot" -version = "0.1.0" -authors = ["Manos Pitsidianakis "] -edition = "2018" -license = "LICENSE" -readme = "README.md" -description = "mailing list manager" -repository = "https://github.com/meli/mailpot" -keywords = ["mail", "mailing-lists" ] -categories = ["email"] -default-run = "mpot" - -[dependencies] -chrono = { version = "0.4.15", features = ["serde", ] } -error-chain = "0.12.4" -melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms"], git="https://github.com/meli/meli", branch = "master" } -rusqlite = {version = "0.20.0"} -serde = { version = "1.0.114" } -structopt = "0.3.16" -xdg = "2.1.0" +[workspace] +members = [ + "core", + "cli", + "rest-http", +] diff --git a/README.md b/README.md index 625435f..a36991f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Mailpot -## flexible mailing list manager +## WIP mailing list manager + +Crates: + +- `core` +- `cli` a command line tool to manage lists +- `rest-http` a REST http server to manage lists ```text % mpot help diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..9ea7587 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "mailpot-cli" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +edition = "2018" +license = "LICENSE" +readme = "README.md" +description = "mailing list manager" +repository = "https://github.com/meli/mailpot" +keywords = ["mail", "mailing-lists" ] +categories = ["email"] +default-run = "mpot" + +[[bin]] +name = "mpot" +path = "src/main.rs" + +[dependencies] +mailpot = { version = "0.1.0", path = "../core" } +structopt = "0.3.16" diff --git a/src/main.rs b/cli/src/main.rs similarity index 92% rename from src/main.rs rename to cli/src/main.rs index 2139257..550e42b 100644 --- a/src/main.rs +++ b/cli/src/main.rs @@ -17,26 +17,16 @@ * along with this program. If not, see . */ -// `error_chain!` can recurse deeply -#![recursion_limit = "1024"] - -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate serde; -use structopt::StructOpt; - -pub mod config; -pub use config::*; -pub mod models; -pub mod post; -pub use models::*; -pub mod errors; -pub use errors::*; -pub mod db; -pub use db::*; +extern crate mailpot; +pub use mailpot::config::*; +pub use mailpot::db::*; +pub use mailpot::errors::*; +pub use mailpot::models::*; +pub use mailpot::post::*; +pub use mailpot::*; use std::path::PathBuf; +use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt( @@ -60,6 +50,8 @@ struct Opt { enum Command { ///Prints database filesystem location DbLocation, + ///Dumps database data to STDOUT + DumpDatabase, ///Lists all registered mailing lists ListLists, ///Mailing list management @@ -129,11 +121,21 @@ fn run_app(opt: Opt) -> Result<()> { if opt.debug { println!("DEBUG: {:?}", &opt); } + Configuration::init()?; use Command::*; match opt.cmd { DbLocation => { println!("{}", Database::db_path()?.display()); } + DumpDatabase => { + let db = Database::open_or_create_db()?; + let lists = db.list_lists()?; + let mut stdout = std::io::stdout(); + serde_json::to_writer_pretty(&mut stdout, &lists)?; + for l in &lists { + serde_json::to_writer_pretty(&mut stdout, &db.list_members(l.pk)?)?; + } + } ListLists => { let db = Database::open_or_create_db()?; let lists = db.list_lists()?; diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..5296a56 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "mailpot" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +edition = "2018" +license = "LICENSE" +readme = "README.md" +description = "mailing list manager" +repository = "https://github.com/meli/mailpot" +keywords = ["mail", "mailing-lists" ] +categories = ["email"] + +[dependencies] +chrono = { version = "0.4.15", features = ["serde", ] } +error-chain = "0.12.4" +diesel = { version = "1.4.5", features = ["sqlite", ] } +melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms"], path="../../meli/melib", branch = "master" } +#melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms"], git="https://github.com/meli/meli", branch = "master" } +rusqlite = {version = "0.20.0"} +serde = { version = "1.0.114" } +serde_json = "1.0.57" +toml = "^0.5" +xdg = "2.1.0" diff --git a/core/README.md b/core/README.md new file mode 100644 index 0000000..ce78022 --- /dev/null +++ b/core/README.md @@ -0,0 +1,17 @@ +# mailpot-core + +Initialize `sqlite3` database: + +Either + +```shell +sqlite3 mpot.db < ./src/schema.sql +``` + +or + + +```shell +# cargo install diesel_cli --no-default-features --features sqlite +diesel migration run +``` diff --git a/core/build.rs b/core/build.rs new file mode 100644 index 0000000..ce35cba --- /dev/null +++ b/core/build.rs @@ -0,0 +1,32 @@ +/* + * This file is part of mailpot + * + * Copyright 2020 - Manos Pitsidianakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use std::io::Write; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=src/schema.sql.m4"); + + let output = Command::new("m4") + .arg("./src/schema.sql.m4") + .output() + .unwrap(); + let mut file = std::fs::File::create("./src/schema.sql").unwrap(); + file.write_all(&output.stdout).unwrap(); +} diff --git a/core/diesel.toml b/core/diesel.toml new file mode 100644 index 0000000..92267c8 --- /dev/null +++ b/core/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" diff --git a/core/migrations/.gitkeep b/core/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/core/migrations/2020-08-16-164344_add_models/down.sql b/core/migrations/2020-08-16-164344_add_models/down.sql new file mode 100644 index 0000000..1214bae --- /dev/null +++ b/core/migrations/2020-08-16-164344_add_models/down.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS mailing_lists; +DROP TABLE IF EXISTS list_owner; +DROP TABLE IF EXISTS post_policy; +DROP TABLE IF EXISTS membership; +DROP TABLE IF EXISTS post; +DROP TABLE IF EXISTS post_event; +DROP INDEX IF EXISTS mailing_lists_idx; +DROP INDEX IF EXISTS membership_idx; diff --git a/core/migrations/2020-08-16-164344_add_models/up.sql b/core/migrations/2020-08-16-164344_add_models/up.sql new file mode 100644 index 0000000..da8ec67 --- /dev/null +++ b/core/migrations/2020-08-16-164344_add_models/up.sql @@ -0,0 +1,63 @@ +PRAGMA foreign_keys = true; +PRAGMA encoding = 'UTF-8'; + +CREATE TABLE IF NOT EXISTS mailing_lists ( + pk INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + id TEXT NOT NULL, + address TEXT NOT NULL, + archive_url TEXT, + description TEXT +); + +CREATE TABLE IF NOT EXISTS list_owner ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_policy ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL UNIQUE, + announce_only BOOLEAN CHECK (announce_only in (0, 1)) NOT NULL DEFAULT 0, + subscriber_only BOOLEAN CHECK (subscriber_only in (0, 1)) NOT NULL DEFAULT 0, + approval_needed BOOLEAN CHECK (approval_needed in (0, 1)) NOT NULL DEFAULT 0, + CHECK(((approval_needed) OR (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only))))), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS membership ( + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + digest BOOLEAN CHECK (digest in (0, 1)) NOT NULL DEFAULT 0, + hide_address BOOLEAN CHECK (hide_address in (0, 1)) NOT NULL DEFAULT 0, + receive_duplicates BOOLEAN CHECK (receive_duplicates in (0, 1)) NOT NULL DEFAULT 1, + receive_own_posts BOOLEAN CHECK (receive_own_posts in (0, 1)) NOT NULL DEFAULT 0, + receive_confirmation BOOLEAN CHECK (receive_confirmation in (0, 1)) NOT NULL DEFAULT 1, + PRIMARY KEY (list, address), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + message_id TEXT NOT NULL, + message BLOB NOT NULL, + FOREIGN KEY (list, address) REFERENCES membership(list, address) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_event ( + pk INTEGER PRIMARY KEY NOT NULL, + post INTEGER NOT NULL, + date INTEGER NOT NULL, + kind CHAR(1) CHECK (kind IN ('R', 'S', 'D', 'B', 'O')) NOT NULL, + content TEXT NOT NULL, + FOREIGN KEY (post) REFERENCES post(pk) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); +CREATE INDEX IF NOT EXISTS membership_idx ON membership(address); diff --git a/src/config.rs b/core/src/config.rs similarity index 72% rename from src/config.rs rename to core/src/config.rs index 0454814..4bcd452 100644 --- a/src/config.rs +++ b/core/src/config.rs @@ -19,10 +19,13 @@ use super::errors::*; use chrono::prelude::*; -use std::io::Write; +use std::cell::RefCell; +use std::io::{Read, Write}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +thread_local!(pub static CONFIG: RefCell = RefCell::new(Configuration::new())); + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "type", content = "value")] pub enum SendMail { @@ -32,10 +35,33 @@ pub enum SendMail { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Configuration { - send_mail: SendMail, + pub send_mail: SendMail, + #[serde(default = "default_storage_fn")] + pub storage: String, } impl Configuration { + pub(crate) fn new() -> Self { + Configuration { + send_mail: SendMail::ShellCommand(String::new()), + storage: "sqlite3".into(), + } + } + + pub fn init() -> Result<()> { + let path = + xdg::BaseDirectories::with_prefix("mailpot")?.place_config_file("config.toml")?; + let mut s = String::new(); + let mut file = std::fs::File::open(&path)?; + file.read_to_string(&mut s)?; + let config: Configuration = toml::from_str(&s)?; + CONFIG.with(|f| { + *f.borrow_mut() = config; + }); + + Ok(()) + } + pub fn data_directory() -> Result { Ok(xdg::BaseDirectories::with_prefix("mailpot")?.get_data_home()) } @@ -74,3 +100,7 @@ impl Configuration { Self::save_message_to_path(&msg, temp_path) } } + +fn default_storage_fn() -> String { + "sqlite3".to_string() +} diff --git a/core/src/db.rs b/core/src/db.rs new file mode 100644 index 0000000..5d4e7c5 --- /dev/null +++ b/core/src/db.rs @@ -0,0 +1,236 @@ +/* + * This file is part of mailpot + * + * Copyright 2020 - Manos Pitsidianakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use super::*; +use diesel::{prelude::*, Connection}; +use melib::Envelope; +use std::path::PathBuf; + +const DB_NAME: &str = "mpot.db"; + +pub struct Database { + connection: SqliteConnection, +} + +impl Database { + pub fn list_lists(&self) -> Result> { + use schema::mailing_lists; + + let ret = mailing_lists::table.load(&self.connection)?; + Ok(ret) + } + + pub fn get_list(&self, pk: i32) -> Result> { + use schema::mailing_lists; + + let ret = mailing_lists::table + .filter(mailing_lists::pk.eq(pk)) + .first(&self.connection) + .optional()?; + Ok(ret) + } + + pub fn get_list_policy(&self, pk: i32) -> Result> { + use schema::post_policy; + + let ret = post_policy::table + .filter(post_policy::list.eq(pk)) + .first(&self.connection) + .optional()?; + Ok(ret) + } + + pub fn get_list_owners(&self, pk: i32) -> Result> { + use schema::list_owner; + + let ret = list_owner::table + .filter(list_owner::list.eq(pk)) + .load(&self.connection)?; + Ok(ret) + } + + pub fn list_members(&self, pk: i32) -> Result> { + use schema::membership; + + let ret = membership::table + .filter(membership::list.eq(pk)) + .load(&self.connection)?; + Ok(ret) + } + + pub fn add_member(&self, list_pk: i32, mut new_val: ListMembership) -> Result<()> { + use schema::membership; + new_val.list = list_pk; + + diesel::insert_into(membership::table) + .values(&new_val) + .execute(&self.connection)?; + Ok(()) + } + + pub fn remove_member(&self, list_pk: i32, address: &str) -> Result<()> { + use schema::membership; + diesel::delete( + membership::table + .filter(membership::columns::list.eq(list_pk)) + .filter(membership::columns::address.eq(address)), + ) + .execute(&self.connection)?; + Ok(()) + } + + pub fn create_list(&self, new_val: MailingList) -> Result<()> { + use schema::mailing_lists; + + diesel::insert_into(mailing_lists::table) + .values(&new_val) + .execute(&self.connection)?; + Ok(()) + } + + pub fn db_path() -> Result { + let name = DB_NAME; + let data_dir = xdg::BaseDirectories::with_prefix("mailpot")?; + Ok(data_dir.place_data_file(name)?) + } + + pub fn open_db(db_path: PathBuf) -> Result { + if !db_path.exists() { + return Err("Database doesn't exist".into()); + } + Ok(Database { + connection: SqliteConnection::establish(&db_path.to_str().unwrap())?, + }) + } + + pub fn open_or_create_db() -> Result { + let db_path = Self::db_path()?; + let mut set_mode = false; + if !db_path.exists() { + println!("Creating {} database in {}", DB_NAME, db_path.display()); + set_mode = true; + } + let conn = SqliteConnection::establish(&db_path.to_str().unwrap())?; + if set_mode { + use std::os::unix::fs::PermissionsExt; + let file = std::fs::File::open(&db_path)?; + let metadata = file.metadata()?; + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o600); // Read/write for owner only. + file.set_permissions(permissions)?; + } + + + Ok(Database { connection: conn }) + } + + pub fn get_list_filters(&self, _list: &MailingList) -> Vec> { + use crate::post::*; + vec![ + Box::new(FixCRLF), + Box::new(PostRightsCheck), + Box::new(AddListHeaders), + Box::new(FinalizeRecipients), + ] + } + + pub fn post(&self, env: Envelope, raw: &[u8]) -> Result<()> { + let mut lists = self.list_lists()?; + let tos = env + .to() + .iter() + .map(|addr| addr.get_email()) + .collect::>(); + if tos.is_empty() { + return Err("Envelope To: field is empty!".into()); + } + if env.from().is_empty() { + return Err("Envelope From: field is empty!".into()); + } + + lists.retain(|list| tos.iter().any(|a| a == &list.address)); + if lists.is_empty() { + return Err("Envelope To: field doesn't contain any known lists!".into()); + } + + let mut configuration = crate::config::Configuration::new(); + crate::config::CONFIG.with(|f| { + configuration = f.borrow().clone(); + }); + use crate::post::{Post, PostAction}; + for mut list in lists { + let filters = self.get_list_filters(&list); + let memberships = self.list_members(list.pk)?; + let mut post = Post { + policy: self.get_list_policy(list.pk)?, + list_owners: self.get_list_owners(list.pk)?, + list: &mut list, + from: env.from()[0].clone(), + memberships: &memberships, + bytes: raw.to_vec(), + to: env.to().to_vec(), + action: PostAction::Hold, + }; + let result = filters + .into_iter() + .fold(Ok(&mut post), |p, f| p.and_then(|p| f.feed(p))); + eprintln!("result {:?}", result); + + let Post { bytes, action, .. } = post; + eprintln!("send_mail {:?}", &configuration.send_mail); + match configuration.send_mail { + crate::config::SendMail::Smtp(ref smtp_conf) => { + let smtp_conf = smtp_conf.clone(); + use melib::futures; + use melib::smol; + use melib::smtp::*; + std::thread::spawn(|| smol::run(futures::future::pending::<()>())); + let mut conn = futures::executor::block_on(SmtpConnection::new_connection( + smtp_conf.clone(), + ))?; + match action { + PostAction::Accept { + recipients, + digests: _digests, + } => { + futures::executor::block_on(conn.mail_transaction( + &String::from_utf8_lossy(&bytes), + Some(&recipients), + ))?; + /* - Save digest metadata in database */ + } + PostAction::Reject { reason: _ } => { + /* - Notify submitter */ + //futures::executor::block_on(conn.mail_transaction(&post.bytes, b)).unwrap(); + } + PostAction::Defer { reason: _ } => { + /* - Notify submitter + * - Save in database */ + } + PostAction::Hold => { /* - Save in database */ } + } + } + _ => {} + } + } + + Ok(()) + } +} diff --git a/src/errors.rs b/core/src/errors.rs similarity index 86% rename from src/errors.rs rename to core/src/errors.rs index a1513e5..32b73f5 100644 --- a/src/errors.rs +++ b/core/src/errors.rs @@ -20,9 +20,12 @@ // Create the Error, ErrorKind, ResultExt, and Result types error_chain! { foreign_links { - Sql(rusqlite::Error); + Db(diesel::ConnectionError); + Sql(diesel::result::Error); Io(::std::io::Error); Xdg(xdg::BaseDirectoriesError); Melib(melib::error::MeliError); + Configuration(toml::de::Error); + SerdeJson(serde_json::Error); } } diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..ebd2f8c --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,40 @@ +/* + * This file is part of mailpot + * + * Copyright 2020 - Manos Pitsidianakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +// `error_chain!` can recurse deeply +#![recursion_limit = "1024"] +//#![warn(missing_docs)] + +#[macro_use] +extern crate error_chain; +#[macro_use] +pub extern crate serde; +#[macro_use] +pub extern crate diesel; + +pub use melib; +pub use serde_json; + +pub mod config; +pub mod models; +pub mod post; +pub mod schema; +use models::*; +pub mod errors; +use errors::*; +pub mod db; diff --git a/src/models.rs b/core/src/models.rs similarity index 65% rename from src/models.rs rename to core/src/models.rs index c6479d3..908ef80 100644 --- a/src/models.rs +++ b/core/src/models.rs @@ -18,12 +18,12 @@ */ use super::*; -use rusqlite::Row; -use std::convert::TryFrom; +use schema::*; -#[derive(Debug)] +#[derive(Debug, Clone, Insertable, Queryable, Deserialize, Serialize)] +#[table_name = "mailing_lists"] pub struct MailingList { - pub pk: i64, + pub pk: i32, pub name: String, pub id: String, pub address: String, @@ -49,20 +49,6 @@ impl std::fmt::Display for MailingList { } } -impl TryFrom<&'_ Row<'_>> for MailingList { - type Error = rusqlite::Error; - fn try_from(row: &'_ Row<'_>) -> std::result::Result { - Ok(MailingList { - pk: row.get("pk")?, - name: row.get("name")?, - id: row.get("id")?, - address: row.get("address")?, - description: row.get("description")?, - archive_url: row.get("archive_url")?, - }) - } -} - impl MailingList { pub fn list_id(&self) -> String { format!("\"{}\" <{}>", self.name, self.address) @@ -85,9 +71,10 @@ impl MailingList { } } -#[derive(Debug)] +#[derive(Debug, Clone, Insertable, Queryable, Deserialize, Serialize)] +#[table_name = "membership"] pub struct ListMembership { - pub list: i64, + pub list: i32, pub address: String, pub name: Option, pub digest: bool, @@ -115,22 +102,6 @@ impl std::fmt::Display for ListMembership { } } -impl TryFrom<&'_ Row<'_>> for ListMembership { - type Error = rusqlite::Error; - fn try_from(row: &'_ Row<'_>) -> std::result::Result { - Ok(ListMembership { - list: row.get("list")?, - address: row.get("address")?, - name: row.get("name")?, - digest: row.get("digest")?, - hide_address: row.get("hide_address")?, - receive_duplicates: row.get("receive_duplicates")?, - receive_own_posts: row.get("receive_own_posts")?, - receive_confirmation: row.get("receive_confirmation")?, - }) - } -} - impl ListMembership { pub fn into_address(&self) -> melib::email::Address { use melib::email::Address; @@ -143,3 +114,57 @@ impl ListMembership { } } } + +#[derive(Debug, Clone, Insertable, Queryable, Deserialize, Serialize)] +#[table_name = "post_policy"] +pub struct PostPolicy { + pub pk: i32, + pub list: i32, + pub announce_only: bool, + pub subscriber_only: bool, + pub approval_needed: bool, +} + +impl std::fmt::Display for PostPolicy { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "{:?}", self) + } +} + +#[derive(Debug, Clone, Insertable, Queryable, Deserialize, Serialize)] +#[table_name = "list_owner"] +pub struct ListOwner { + pub pk: i32, + pub list: i32, + pub address: String, + pub name: Option, +} + +impl std::fmt::Display for ListOwner { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(ref name) = self.name { + write!( + fmt, + "[#{} {}] \"{}\" <{}>", + self.pk, self.list, name, self.address + ) + } else { + write!(fmt, "[#{} {}] {}", self.pk, self.list, self.address) + } + } +} + +impl From for ListMembership { + fn from(val: ListOwner) -> ListMembership { + ListMembership { + list: val.list, + address: val.address, + name: val.name, + digest: false, + hide_address: false, + receive_duplicates: true, + receive_own_posts: false, + receive_confirmation: true, + } + } +} diff --git a/src/post.rs b/core/src/post.rs similarity index 73% rename from src/post.rs rename to core/src/post.rs index c7ffd67..1306fc4 100644 --- a/src/post.rs +++ b/core/src/post.rs @@ -22,6 +22,7 @@ use melib::Address; #[derive(Debug)] pub enum PostAction { + Hold, Accept { recipients: Vec
, digests: Vec
, @@ -37,8 +38,10 @@ pub enum PostAction { ///Post to be considered by the list's `PostFilter` stack. pub struct Post<'list> { pub list: &'list mut MailingList, + pub list_owners: Vec, pub from: Address, pub memberships: &'list [ListMembership], + pub policy: Option, pub bytes: Vec, pub to: Vec
, pub action: PostAction, @@ -51,6 +54,7 @@ impl<'list> core::fmt::Debug for Post<'list> { .field("from", &self.from) .field("members", &format_args!("{}", self.memberships.len())) .field("bytes", &format_args!("{}", self.bytes.len())) + .field("policy", &self.policy) .field("to", &self.to.as_slice()) .field("action", &self.action) .finish() @@ -60,19 +64,43 @@ impl<'list> core::fmt::Debug for Post<'list> { ///Filter that modifies and/or verifies a post candidate. On rejection, return a string ///describing the error and optionally set `post.action` to `Reject` or `Defer` pub trait PostFilter { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String>; + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String>; } ///Check that submitter can post to list, for now it accepts everything. pub struct PostRightsCheck; impl PostFilter for PostRightsCheck { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String> { + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String> { + if let Some(ref policy) = post.policy { + if policy.announce_only { + let owner_addresses = post + .list_owners + .iter() + .map(|lo| { + let lm: ListMembership = lo.clone().into(); + lm.into_address() + }) + .collect::>(); + if !owner_addresses.iter().any(|addr| *addr == post.from) { + return Err("You are not allowed to post on this list.".to_string()); + } + } else if policy.subscriber_only { + let email_from = post.from.get_email(); + if !post.memberships.iter().any(|lm| lm.address == email_from) { + return Err("You are not subscribed to this list.".to_string()); + } + } else if policy.approval_needed { + post.action = PostAction::Defer { + reason: "Approval from the list's moderators is required.".to_string(), + }; + } + } Ok(post) } } @@ -80,10 +108,10 @@ impl PostFilter for PostRightsCheck { ///Ensure message contains only `\r\n` line terminators, required by SMTP. pub struct FixCRLF; impl PostFilter for FixCRLF { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String> { + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String> { use std::io::prelude::*; let mut new_vec = Vec::with_capacity(post.bytes.len()); for line in post.bytes.lines() { @@ -98,10 +126,10 @@ impl PostFilter for FixCRLF { ///Add `List-*` headers pub struct AddListHeaders; impl PostFilter for AddListHeaders { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String> { + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String> { let (mut headers, body) = melib::email::parser::mail(&post.bytes).unwrap(); let list_id = post.list.list_id(); headers.push((&b"List-ID"[..], list_id.as_bytes())); @@ -142,10 +170,10 @@ impl PostFilter for AddListHeaders { ///Adds `Archived-At` field, if configured. pub struct ArchivedAtLink; impl PostFilter for ArchivedAtLink { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String> { + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String> { Ok(post) } } @@ -154,10 +182,10 @@ impl PostFilter for ArchivedAtLink { ///will receive the post in `post.action` field. pub struct FinalizeRecipients; impl PostFilter for FinalizeRecipients { - fn feed<'list>( + fn feed<'p, 'list>( self: Box, - post: &'list mut Post<'list>, - ) -> std::result::Result<&'list mut Post<'list>, String> { + post: &'p mut Post<'list>, + ) -> std::result::Result<&'p mut Post<'list>, String> { let mut recipients = vec![]; let mut digests = vec![]; let email_from = post.from.get_email(); diff --git a/core/src/schema.rs b/core/src/schema.rs new file mode 100644 index 0000000..0208b8f --- /dev/null +++ b/core/src/schema.rs @@ -0,0 +1,76 @@ +table! { + list_owner (pk) { + pk -> Integer, + list -> Integer, + address -> Text, + name -> Nullable, + } +} + +table! { + mailing_lists (pk) { + pk -> Integer, + name -> Text, + id -> Text, + address -> Text, + archive_url -> Nullable, + description -> Nullable, + } +} + +table! { + membership (list, address) { + list -> Integer, + address -> Text, + name -> Nullable, + digest -> Bool, + hide_address -> Bool, + receive_duplicates -> Bool, + receive_own_posts -> Bool, + receive_confirmation -> Bool, + } +} + +table! { + post (pk) { + pk -> Integer, + list -> Integer, + address -> Text, + message_id -> Text, + message -> Binary, + } +} + +table! { + post_event (pk) { + pk -> Integer, + post -> Integer, + date -> Integer, + kind -> Text, + content -> Text, + } +} + +table! { + post_policy (pk) { + pk -> Integer, + list -> Integer, + announce_only -> Bool, + subscriber_only -> Bool, + approval_needed -> Bool, + } +} + +joinable!(list_owner -> mailing_lists (list)); +joinable!(membership -> mailing_lists (list)); +joinable!(post_event -> post (post)); +joinable!(post_policy -> mailing_lists (list)); + +allow_tables_to_appear_in_same_query!( + list_owner, + mailing_lists, + membership, + post, + post_event, + post_policy, +); diff --git a/core/src/schema.sql b/core/src/schema.sql new file mode 100644 index 0000000..da8ec67 --- /dev/null +++ b/core/src/schema.sql @@ -0,0 +1,63 @@ +PRAGMA foreign_keys = true; +PRAGMA encoding = 'UTF-8'; + +CREATE TABLE IF NOT EXISTS mailing_lists ( + pk INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + id TEXT NOT NULL, + address TEXT NOT NULL, + archive_url TEXT, + description TEXT +); + +CREATE TABLE IF NOT EXISTS list_owner ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_policy ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL UNIQUE, + announce_only BOOLEAN CHECK (announce_only in (0, 1)) NOT NULL DEFAULT 0, + subscriber_only BOOLEAN CHECK (subscriber_only in (0, 1)) NOT NULL DEFAULT 0, + approval_needed BOOLEAN CHECK (approval_needed in (0, 1)) NOT NULL DEFAULT 0, + CHECK(((approval_needed) OR (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscriber_only)) AND NOT ((announce_only) AND (subscriber_only))))), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS membership ( + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + digest BOOLEAN CHECK (digest in (0, 1)) NOT NULL DEFAULT 0, + hide_address BOOLEAN CHECK (hide_address in (0, 1)) NOT NULL DEFAULT 0, + receive_duplicates BOOLEAN CHECK (receive_duplicates in (0, 1)) NOT NULL DEFAULT 1, + receive_own_posts BOOLEAN CHECK (receive_own_posts in (0, 1)) NOT NULL DEFAULT 0, + receive_confirmation BOOLEAN CHECK (receive_confirmation in (0, 1)) NOT NULL DEFAULT 1, + PRIMARY KEY (list, address), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + message_id TEXT NOT NULL, + message BLOB NOT NULL, + FOREIGN KEY (list, address) REFERENCES membership(list, address) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_event ( + pk INTEGER PRIMARY KEY NOT NULL, + post INTEGER NOT NULL, + date INTEGER NOT NULL, + kind CHAR(1) CHECK (kind IN ('R', 'S', 'D', 'B', 'O')) NOT NULL, + content TEXT NOT NULL, + FOREIGN KEY (post) REFERENCES post(pk) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); +CREATE INDEX IF NOT EXISTS membership_idx ON membership(address); diff --git a/core/src/schema.sql.m4 b/core/src/schema.sql.m4 new file mode 100644 index 0000000..6fa1876 --- /dev/null +++ b/core/src/schema.sql.m4 @@ -0,0 +1,67 @@ +define(xor, `(($1) OR ($2)) AND NOT (($1) AND ($2))')dnl +define(BOOLEAN_TYPE, `$1 BOOLEAN CHECK ($1 in (0, 1)) NOT NULL')dnl +define(BOOLEAN_FALSE, `0')dnl +define(BOOLEAN_TRUE, `1')dnl +PRAGMA foreign_keys = true; +PRAGMA encoding = 'UTF-8'; + +CREATE TABLE IF NOT EXISTS mailing_lists ( + pk INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + id TEXT NOT NULL, + address TEXT NOT NULL, + archive_url TEXT, + description TEXT +); + +CREATE TABLE IF NOT EXISTS list_owner ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_policy ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL UNIQUE, + BOOLEAN_TYPE(announce_only) DEFAULT BOOLEAN_FALSE(), + BOOLEAN_TYPE(subscriber_only) DEFAULT BOOLEAN_FALSE(), + BOOLEAN_TYPE(approval_needed) DEFAULT BOOLEAN_FALSE(), + CHECK(xor(approval_needed, xor(announce_only, subscriber_only))), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS membership ( + list INTEGER NOT NULL, + address TEXT NOT NULL, + name TEXT, + BOOLEAN_TYPE(digest) DEFAULT BOOLEAN_FALSE(), + BOOLEAN_TYPE(hide_address) DEFAULT BOOLEAN_FALSE(), + BOOLEAN_TYPE(receive_duplicates) DEFAULT BOOLEAN_TRUE(), + BOOLEAN_TYPE(receive_own_posts) DEFAULT BOOLEAN_FALSE(), + BOOLEAN_TYPE(receive_confirmation) DEFAULT BOOLEAN_TRUE(), + PRIMARY KEY (list, address), + FOREIGN KEY (list) REFERENCES mailing_lists(pk) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post ( + pk INTEGER PRIMARY KEY NOT NULL, + list INTEGER NOT NULL, + address TEXT NOT NULL, + message_id TEXT NOT NULL, + message BLOB NOT NULL, + FOREIGN KEY (list, address) REFERENCES membership(list, address) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS post_event ( + pk INTEGER PRIMARY KEY NOT NULL, + post INTEGER NOT NULL, + date INTEGER NOT NULL, + kind CHAR(1) CHECK (kind IN ('R', 'S', 'D', 'B', 'O')) NOT NULL, + content TEXT NOT NULL, + FOREIGN KEY (post) REFERENCES post(pk) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); +CREATE INDEX IF NOT EXISTS membership_idx ON membership(address); diff --git a/rest-http/Cargo.toml b/rest-http/Cargo.toml new file mode 100644 index 0000000..5ec0709 --- /dev/null +++ b/rest-http/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "mailpot-http" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +edition = "2018" +license = "LICENSE" +readme = "README.md" +description = "mailing list manager" +repository = "https://github.com/meli/mailpot" +keywords = ["mail", "mailing-lists" ] +categories = ["email"] +default-run = "mpot-http" + +[[bin]] +name = "mpot-http" +path = "src/main.rs" + +[dependencies] +mailpot = { version = "0.1.0", path = "../core" } +rocket = "0.4.5" + +[dependencies.rocket_contrib] +version = "0.4.5" +default-features = false +features = ["json"] diff --git a/rest-http/README.md b/rest-http/README.md new file mode 100644 index 0000000..749b3e5 --- /dev/null +++ b/rest-http/README.md @@ -0,0 +1,7 @@ +# mailpot REST http server + +Current `rocket` requires Nightly. + +```shell +cargo +nightly run --bin mpot-http +``` diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs new file mode 100644 index 0000000..4248921 --- /dev/null +++ b/rest-http/src/main.rs @@ -0,0 +1,93 @@ +/* + * This file is part of mailpot + * + * Copyright 2020 - Manos Pitsidianakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#![feature(proc_macro_hygiene, decl_macro)] +extern crate mailpot; + +pub use mailpot::config::*; +pub use mailpot::db::*; +pub use mailpot::errors::*; +pub use mailpot::models::*; +pub use mailpot::post::*; +pub use mailpot::*; +use std::path::PathBuf; + +use rocket::response::content; +use rocket_contrib::json::Json; + +#[macro_use] +extern crate rocket; + +#[get("/")] +fn index() -> &'static str { + "Hello, world!" +} + +#[get("/lists")] +fn lists() -> Json> { + let db = Database::open_or_create_db().unwrap(); + let lists = db.list_lists().unwrap(); + Json(lists) +} + +#[get("/lists/")] +fn lists_num(num: u64) -> Json> { + let db = Database::open_or_create_db().unwrap(); + let list = db.get_list(num as i64).unwrap(); + Json(list) +} + +#[get("/lists//members")] +fn lists_members(num: u64) -> Option>> { + let db = Database::open_or_create_db().unwrap(); + db.list_members(num as i64).ok().map(|l| Json(l)) +} + +#[get("/lists//owners")] +fn lists_owners(num: u64) -> Option>> { + let db = Database::open_or_create_db().unwrap(); + db.get_list_owners(num as i64).ok().map(|l| Json(l)) +} + +#[post("/lists//owners/add", data = "")] +fn lists_owner_add(num: u64, new_owner: ListOwner) -> Result<()> { + todo!() +} + +#[get("/lists//policy")] +fn lists_policy(num: u64) -> Option>> { + let db = Database::open_or_create_db().unwrap(); + db.get_list_policy(num as i64).ok().map(|l| Json(l)) +} + +fn main() { + rocket::ignite() + .mount( + "/", + routes![ + index, + lists_members, + lists_num, + lists_owners, + lists_policy, + lists + ], + ) + .launch(); +} diff --git a/src/db.rs b/src/db.rs deleted file mode 100644 index 34ba6f5..0000000 --- a/src/db.rs +++ /dev/null @@ -1,220 +0,0 @@ -/* - * This file is part of mailpot - * - * Copyright 2020 - Manos Pitsidianakis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use super::*; -use melib::Envelope; -pub use rusqlite::{self, params, Connection}; -use std::convert::TryFrom; -use std::path::PathBuf; - -const DB_NAME: &str = "mpot.db"; -const INIT_SCRIPT: &str = "PRAGMA foreign_keys = true; - PRAGMA encoding = 'UTF-8'; - - CREATE TABLE IF NOT EXISTS mailing_lists ( - pk INTEGER PRIMARY KEY, - name TEXT NOT NULL, - id TEXT NOT NULL, - address TEXT NOT NULL, - archive_url TEXT, - description TEXT - ); - CREATE TABLE IF NOT EXISTS membership ( - list INTEGER NOT NULL, - address TEXT NOT NULL, - name TEXT, - digest BOOLEAN NOT NULL DEFAULT 0, - hide_address BOOLEAN NOT NULL DEFAULT 0, - receive_duplicates BOOLEAN NOT NULL DEFAULT 1, - receive_own_posts BOOLEAN NOT NULL DEFAULT 0, - receive_confirmation BOOLEAN NOT NULL DEFAULT 1, - PRIMARY KEY (list, address), - FOREIGN KEY (list) REFERENCES mailing_lists ON DELETE CASCADE - ); - CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id); - CREATE INDEX IF NOT EXISTS membership_idx ON membership(address);"; - -pub struct Database { - connection: Connection, -} - -impl Database { - pub fn list_lists(&self) -> Result> { - let mut stmt = self - .connection - .prepare("SELECT * FROM mailing_lists;") - .unwrap(); - let res = stmt - .query_map(params![], |row| MailingList::try_from(row))? - .map(|r| r.map_err(|err| err.into())) - .collect(); - res - } - - pub fn list_members(&self, pk: i64) -> Result> { - let mut stmt = self - .connection - .prepare("SELECT * FROM membership WHERE list = ?;") - .unwrap(); - let res = stmt - .query_map(params![pk], |row| ListMembership::try_from(row))? - .map(|r| r.map_err(|err| err.into())) - .collect(); - res - } - - pub fn add_member(&self, list_pk: i64, new_val: ListMembership) -> Result<()> { - self.connection.execute( - "INSERT INTO membership (list, address, name, digest, hide_address) VALUES (?1, ?2, ?3, ?4, ?5);", - params![ - &list_pk, - &new_val.address, - &new_val.name, - &new_val.digest, - &new_val.hide_address - ], - )?; - Ok(()) - } - - pub fn remove_member(&self, list_pk: i64, address: &str) -> Result<()> { - if self.connection.execute( - "DELETE FROM membership WHERE list = ?1 AND address = ?2;", - params![&list_pk, &address,], - )? == 0 - { - Err(format!("Address {} is not a member of this list.", address))?; - } - Ok(()) - } - - pub fn create_list(&self, new_val: MailingList) -> Result<()> { - self.connection.execute( - "INSERT INTO mailing_lists (name, id, address, description) VALUES (?1, ?2, ?3, ?4)", - params![ - &new_val.name, - &new_val.id, - &new_val.address, - &new_val.description - ], - )?; - Ok(()) - } - - pub fn db_path() -> Result { - let name = DB_NAME; - let data_dir = xdg::BaseDirectories::with_prefix("mailpot")?; - Ok(data_dir.place_data_file(name)?) - } - - pub fn open_db(db_path: PathBuf) -> Result { - if !db_path.exists() { - return Err("Database doesn't exist".into()); - } - Ok(Database { - connection: Connection::open(&db_path)?, - }) - } - - pub fn open_or_create_db() -> Result { - let db_path = Self::db_path()?; - let mut set_mode = false; - if !db_path.exists() { - println!("Creating {} database in {}", DB_NAME, db_path.display()); - set_mode = true; - } - let conn = Connection::open(&db_path)?; - if set_mode { - use std::os::unix::fs::PermissionsExt; - let file = std::fs::File::open(&db_path)?; - let metadata = file.metadata()?; - let mut permissions = metadata.permissions(); - - permissions.set_mode(0o600); // Read/write for owner only. - file.set_permissions(permissions)?; - } - - conn.execute_batch(INIT_SCRIPT)?; - - Ok(Database { connection: conn }) - } - - pub fn get_list_filters(&self, _list: &MailingList) -> Vec> { - use crate::post::*; - vec![ - Box::new(FixCRLF), - Box::new(PostRightsCheck), - Box::new(AddListHeaders), - Box::new(FinalizeRecipients), - ] - } - - pub fn post(&self, env: Envelope, raw: &[u8]) -> Result<()> { - let mut lists = self.list_lists()?; - let tos = env - .to() - .iter() - .map(|addr| addr.get_email()) - .collect::>(); - if tos.is_empty() { - return Err("Envelope To: field is empty!".into()); - } - if env.from().is_empty() { - return Err("Envelope From: field is empty!".into()); - } - - lists.retain(|list| tos.iter().any(|a| a == &list.address)); - if lists.is_empty() { - return Err("Envelope To: field doesn't contain any known lists!".into()); - } - - use crate::post::{Post, PostAction}; - for mut list in lists { - let filters = self.get_list_filters(&list); - let memberships = self.list_members(list.pk)?; - let mut post = Post { - list: &mut list, - from: env.from()[0].clone(), - memberships: &memberships, - bytes: raw.to_vec(), - to: env.to().to_vec(), - action: PostAction::Defer { - reason: "Default action.".into(), - }, - }; - let result = { - let result: std::result::Result<_, String> = filters - .into_iter() - .fold(Ok(&mut post), |post, f| post.and_then(|p| f.feed(p))); - format!("{:#?}", result) - }; - eprintln!("result for list {} is {}", list, result); - } - /* - - use melib::futures; - use melib::smol; - use melib::smtp::*; - std::thread::spawn(|| smol::run(futures::future::pending::<()>())); - let mut conn = futures::executor::block_on(SmtpConnection::new_connection(conf)).unwrap(); - futures::executor::block_on(conn.mail_transaction(raw, )).unwrap(); - */ - Ok(()) - } -}