summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock436
-rw-r--r--Cargo.toml12
-rw-r--r--build.rs11
-rw-r--r--jwebmail.proto165
-rw-r--r--src/arguments.rs25
-rw-r--r--src/cmd.rs114
-rw-r--r--src/cmd/add_folder.rs53
-rw-r--r--src/cmd/count.rs27
-rw-r--r--src/cmd/folders.rs35
-rw-r--r--src/cmd/init.rs35
-rw-r--r--src/cmd/list.rs158
-rw-r--r--src/cmd/move.rs19
-rw-r--r--src/cmd/move_mail.rs18
-rw-r--r--src/cmd/raw.rs185
-rw-r--r--src/cmd/read.rs24
-rw-r--r--src/cmd/remove.rs29
-rw-r--r--src/cmd/search.rs17
-rw-r--r--src/cmd/show.rs26
-rw-r--r--src/cmd/stats.rs25
-rw-r--r--src/de.jmhoffmann.jwebmail.mail-storage.varlink86
-rw-r--r--src/de_jmhoffmann_jwebmail_mail-storage.rs1035
-rw-r--r--src/error.rs47
-rw-r--r--src/main.rs128
-rw-r--r--src/pb3/mod.rs3
-rw-r--r--src/rfc822.rs365
26 files changed, 1988 insertions, 1092 deletions
diff --git a/.gitignore b/.gitignore
index b57a302..d06aa5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
target/
-src/pb3/jwebmail.rs
+src/de_jmhoffmann_jwebmail_mail-storage.rs
diff --git a/Cargo.lock b/Cargo.lock
index 4592685..aa92063 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,15 +3,6 @@
version = 3
[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -27,61 +18,15 @@ dependencies = [
]
[[package]]
-name = "anstream"
-version = "0.6.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.1"
+name = "ansi_term"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
-dependencies = [
- "anstyle",
- "windows-sys 0.52.0",
+ "winapi",
]
[[package]]
-name = "anyhow"
-version = "1.0.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
-
-[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -116,11 +61,23 @@ dependencies = [
[[package]]
name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "chainerror"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce1bb7fb0c258a6600d699950da347a7a9dad66c3ce815769b5f11cf8fce78e"
+
+[[package]]
name = "charset"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -145,52 +102,6 @@ dependencies = [
]
[[package]]
-name = "clap"
-version = "4.5.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.5.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
-
-[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -212,27 +123,15 @@ dependencies = [
]
[[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-
-[[package]]
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
]
[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -259,24 +158,12 @@ dependencies = [
]
[[package]]
-name = "hashbrown"
-version = "0.14.5"
+name = "getopts"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "home"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
- "windows-sys 0.52.0",
+ "unicode-width",
]
[[package]]
@@ -303,22 +190,6 @@ dependencies = [
]
[[package]]
-name = "indexmap"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
-
-[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -337,16 +208,18 @@ dependencies = [
name = "jwebmail-extract"
version = "0.8.0"
dependencies = [
- "anyhow",
+ "base64",
"chrono",
- "clap",
"libc",
"log",
"maildir",
"mailparse",
- "protobuf",
- "protobuf-codegen",
+ "serde",
+ "serde_derive",
+ "serde_json",
"simplelog",
+ "varlink",
+ "varlink_generator",
]
[[package]]
@@ -395,6 +268,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -425,69 +307,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
-name = "powerfmt"
-version = "0.2.0"
+name = "peg"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367"
dependencies = [
- "unicode-ident",
+ "peg-macros",
+ "peg-runtime",
]
[[package]]
-name = "protobuf"
-version = "3.5.1"
+name = "peg-macros"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bcc343da15609eaecd65f8aa76df8dc4209d325131d8219358c0aaaebab0bf6"
+checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d"
dependencies = [
- "once_cell",
- "protobuf-support",
- "thiserror",
+ "peg-runtime",
+ "proc-macro2",
+ "quote",
]
[[package]]
-name = "protobuf-codegen"
-version = "3.5.1"
+name = "peg-runtime"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4d0cde5642ea4df842b13eb9f59ea6fafa26dcb43e3e1ee49120e9757556189"
-dependencies = [
- "anyhow",
- "once_cell",
- "protobuf",
- "protobuf-parse",
- "regex",
- "tempfile",
- "thiserror",
-]
+checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5"
[[package]]
-name = "protobuf-parse"
-version = "3.5.1"
+name = "powerfmt"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b0e9b447d099ae2c4993c0cbb03c7a9d6c937b17f2d56cfc0b1550e6fcfdb76"
-dependencies = [
- "anyhow",
- "indexmap",
- "log",
- "protobuf",
- "protobuf-support",
- "tempfile",
- "thiserror",
- "which",
-]
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
-name = "protobuf-support"
-version = "3.5.1"
+name = "proc-macro2"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0766e3675a627c327e4b3964582594b0e8741305d628a98a5de75a1d15f99b9"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
- "thiserror",
+ "unicode-ident",
]
[[package]]
@@ -506,35 +364,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73"
[[package]]
-name = "regex"
-version = "1.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
-
-[[package]]
name = "rustix"
version = "0.38.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -548,23 +377,41 @@ dependencies = [
]
[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
name = "serde"
-version = "1.0.209"
+version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.209"
+version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.87",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
]
[[package]]
@@ -585,16 +432,21 @@ dependencies = [
]
[[package]]
-name = "strsim"
-version = "0.11.1"
+name = "syn"
+version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
[[package]]
name = "syn"
-version = "2.0.77"
+version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
@@ -607,7 +459,7 @@ version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"fastrand",
"once_cell",
"rustix",
@@ -624,26 +476,6 @@ dependencies = [
]
[[package]]
-name = "thiserror"
-version = "1.0.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "time"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -677,16 +509,78 @@ dependencies = [
]
[[package]]
+name = "uds_windows"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "winapi",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
-name = "utf8parse"
-version = "0.2.2"
+name = "unicode-width"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "unix_socket"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+]
+
+[[package]]
+name = "varlink"
+version = "11.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "409e275987d74665c23610c0959c133360cafd761c1a6ddb1ca6d0685c8cef5d"
+dependencies = [
+ "libc",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tempfile",
+ "uds_windows",
+ "unix_socket",
+ "winapi",
+]
+
+[[package]]
+name = "varlink_generator"
+version = "10.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d8ff746c5b65d4bfb3a50f630b85cfb6a9d59f18720126e3ebd6bc98527fa51"
+dependencies = [
+ "chainerror",
+ "getopts",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "varlink_parser",
+]
+
+[[package]]
+name = "varlink_parser"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35fb9f3c1e8ccb33cdb6c84a4477ef3f3884ce6f4b70514ef1fbf7686eae921e"
+dependencies = [
+ "ansi_term",
+ "chainerror",
+ "peg",
+]
[[package]]
name = "wasm-bindgen"
@@ -694,7 +588,7 @@ version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"once_cell",
"wasm-bindgen-macro",
]
@@ -710,7 +604,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.87",
"wasm-bindgen-shared",
]
@@ -732,7 +626,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.87",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -744,18 +638,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
-name = "which"
-version = "4.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
-dependencies = [
- "either",
- "home",
- "once_cell",
- "rustix",
-]
-
-[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 91de65b..9a4795d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,15 +16,17 @@ codegen-units = 1
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
-anyhow = "1.0"
-protobuf-codegen = "3.4"
+varlink_generator = "10.1.0"
[dependencies]
chrono = "0.4"
-clap = { version = "4.5", features = ["derive"] }
libc = "0.2"
log = "0.4"
+simplelog = "0.12"
maildir = "0.6"
mailparse = "0.14"
-protobuf = "3.4"
-simplelog = "0.12"
+varlink = "11.0.1"
+serde = "1.0.215"
+serde_json = "1.0.133"
+serde_derive = "1.0.215"
+base64 = "0.22.1"
diff --git a/build.rs b/build.rs
deleted file mode 100644
index e8ef3be..0000000
--- a/build.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use anyhow::Result;
-use protobuf_codegen::Codegen;
-
-fn main() -> Result<()> {
- Codegen::new()
- .protoc()
- .include(".")
- .input("jwebmail.proto")
- .out_dir("src/pb3")
- .run()
-}
diff --git a/jwebmail.proto b/jwebmail.proto
deleted file mode 100644
index e4cba3b..0000000
--- a/jwebmail.proto
+++ /dev/null
@@ -1,165 +0,0 @@
-syntax = "proto3";
-
-package jwebmail;
-
-message MIMEHeader {
-
- enum ContentDisposition {
- CONTENT_DISPOSITION_NONE = 0;
- CONTENT_DISPOSITION_INLINE = 1;
- CONTENT_DISPOSITION_ATTACHMENT = 2;
- }
-
- string maintype = 1;
- string subtype = 2;
- ContentDisposition contentdispo = 3;
- optional string file_name = 4;
-}
-
-message MailHeader {
-
- message MailAddr {
- optional string name = 1;
- string address = 2;
- }
-
- string send_date = 1;
- repeated MailAddr written_from = 2;
- optional MailAddr sender = 3;
- repeated MailAddr reply_to = 4;
- repeated MailAddr send_to = 5;
- repeated MailAddr cc = 6;
- repeated MailAddr bcc = 7;
- string subject = 8;
- repeated string comments = 9;
- repeated string keywords = 10;
- MIMEHeader mime = 11;
-}
-
-message ListMailHeader {
- uint64 byte_size = 1;
- bool unread = 2;
- string rec_date = 3;
- string mid = 4;
- MailHeader header = 5;
-}
-
-message MailBody {
- message Multipart {
- optional string preamble = 1;
- repeated MIMEPart parts = 2;
- optional string epilogue = 3;
- }
-
- oneof Body {
- string discrete = 1;
- Multipart multipart = 2;
- Mail mail = 3;
- }
-}
-
-message Mail {
- MailHeader head = 1;
- MailBody body = 2;
-}
-
-message MIMEPart {
- MIMEHeader mime_header = 1;
- MailBody body = 2;
-}
-
-// Request-Response pairs
-
-message ListReq {
- string folder = 1;
- int32 start = 2;
- int32 end = 3;
- string sort = 4;
-}
-
-message ListResp {
- repeated ListMailHeader mail_heads = 1;
-}
-
-message StatsReq {
- string folder = 1;
-}
-
-message StatsResp {
- uint32 mail_count = 1;
- uint32 unread_count = 2;
- uint64 byte_size = 3;
-}
-
-message ShowReq {
- string folder = 1;
- string mid = 2;
-}
-
-message ShowResp {
- Mail mail = 1;
-}
-
-message RawReq {
- string folder = 1;
- string mid = 2;
- optional string path = 3;
-}
-
-message RawResp {
- MIMEHeader header = 1;
- bytes body = 2;
-}
-
-message SearchReq {
- string folder = 1;
- string pattern = 2;
-}
-
-message SearchResp {
- repeated ListMailHeader found = 1;
-}
-
-message FoldersReq {
-}
-
-message FoldersResp {
- repeated string folders = 1;
-}
-
-message MoveReq {
- string mid = 1;
- string from_f = 2;
- string to_f = 3;
-}
-
-message MoveResp {
-}
-
-message RemoveReq {
- string folder = 1;
- string mid = 2;
-}
-
-message RemoveResp {
-}
-
-message AddFolderReq {
- string name = 1;
-}
-
-message AddFolderResp {
- int32 status = 1;
-}
-
-service MailService {
- rpc List(ListReq) returns (ListResp);
- rpc Stats(StatsReq) returns (StatsResp);
- rpc Show(ShowReq) returns (ShowResp);
- rpc Raw(RawReq) returns (RawResp);
- rpc Search(SearchReq) returns (SearchResp);
- rpc Folders(FoldersReq) returns (FoldersResp);
- rpc Move(MoveReq) returns (MoveResp);
- rpc Remove(RemoveReq) returns (RemoveResp);
- rpc AddFolder(AddFolderReq) returns (AddFolderResp);
-}
diff --git a/src/arguments.rs b/src/arguments.rs
deleted file mode 100644
index a944abe..0000000
--- a/src/arguments.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use std::path::PathBuf;
-
-use clap::{Parser, ValueEnum};
-
-#[derive(Clone, ValueEnum)]
-pub enum Mode {
- List,
- Search,
- Count,
- Read,
- Raw,
- Folders,
- Move,
- Remove,
- AddFolder,
-}
-
-#[derive(Parser)]
-#[command(author, version, about, long_about = None)]
-pub struct Arguments {
- pub maildir_path: PathBuf,
- pub sys_user: String,
- pub mail_user: String,
- pub mode: Mode,
-}
diff --git a/src/cmd.rs b/src/cmd.rs
index 201f498..576c9d4 100644
--- a/src/cmd.rs
+++ b/src/cmd.rs
@@ -1,24 +1,30 @@
use std::path::PathBuf;
+use std::sync::RwLock;
+use crate::de_jmhoffmann_jwebmail_mailstorage::*;
use maildir::Maildir;
mod add_folder;
-mod count;
mod folders;
+mod init;
mod list;
-mod move_mail;
+mod r#move;
mod raw;
-mod read;
mod remove;
+mod show;
+mod stats;
+mod search;
-pub use add_folder::add_folder;
-pub use count::count;
-pub use folders::folders;
-pub use list::list;
-pub use move_mail::move_mail;
-pub use raw::raw;
-pub use read::read;
-pub use remove::remove;
+use add_folder::add_folder;
+use folders::folders;
+use init::init;
+use list::list;
+use r#move::r#move;
+use raw::raw;
+use remove::remove;
+use show::show;
+use stats::stats;
+use search::search;
pub fn open_submaildir(mut path: PathBuf, sub: &str) -> Maildir {
if !sub.is_empty() {
@@ -26,3 +32,89 @@ pub fn open_submaildir(mut path: PathBuf, sub: &str) -> Maildir {
}
Maildir::from(path)
}
+
+pub struct MailStorage {
+ maildir_path: RwLock<Option<PathBuf>>,
+}
+
+impl Default for MailStorage {
+ fn default() -> Self {
+ MailStorage { maildir_path: RwLock::new(None) }
+ }
+}
+
+impl VarlinkInterface for MailStorage {
+ fn init(
+ &self,
+ call: &mut dyn Call_Init,
+ unix_user: String,
+ mailbox_path: String,
+ ) -> varlink::Result<()> {
+ init(self, call, unix_user, mailbox_path)
+ }
+
+ fn add_folder(&self, call: &mut dyn Call_AddFolder, name: String) -> varlink::Result<()> {
+ add_folder(self, call, name)
+ }
+
+ fn stats(&self, call: &mut dyn Call_Stats, folder: String) -> varlink::Result<()> {
+ stats(self, call, folder)
+ }
+
+ fn folders(&self, call: &mut dyn Call_Folders) -> varlink::Result<()> {
+ folders(self, call)
+ }
+
+ fn list(
+ &self,
+ call: &mut dyn Call_List,
+ folder: String,
+ start: i64,
+ end: i64,
+ sort: Sort,
+ ) -> varlink::Result<()> {
+ list(self, call, folder, start, end, sort)
+ }
+
+ fn r#move(
+ &self,
+ call: &mut dyn Call_Move,
+ mid: String,
+ from_folder: String,
+ to_folder: String,
+ ) -> varlink::Result<()> {
+ r#move(self, call, mid, from_folder, to_folder)
+ }
+
+ fn raw(
+ &self,
+ call: &mut dyn Call_Raw,
+ folder: String,
+ mid: String,
+ path: Option<String>,
+ ) -> varlink::Result<()> {
+ raw(self, call, folder, mid, path)
+ }
+
+ fn remove(
+ &self,
+ call: &mut dyn Call_Remove,
+ folder: String,
+ mid: String,
+ ) -> varlink::Result<()> {
+ remove(self, call, folder, mid)
+ }
+
+ fn search(
+ &self,
+ call: &mut dyn Call_Search,
+ folder: String,
+ pattern: String,
+ ) -> varlink::Result<()> {
+ search(self, call, folder, pattern)
+ }
+
+ fn show(&self, call: &mut dyn Call_Show, folder: String, mid: String) -> varlink::Result<()> {
+ show(self, call, folder, mid)
+ }
+}
diff --git a/src/cmd/add_folder.rs b/src/cmd/add_folder.rs
index 82df1f9..07ad455 100644
--- a/src/cmd/add_folder.rs
+++ b/src/cmd/add_folder.rs
@@ -1,37 +1,40 @@
use std::fs::create_dir;
-use std::path::PathBuf;
-use protobuf::Message as _;
+use varlink;
-use crate::error::Result;
-use crate::pb3::jwebmail::{AddFolderReq, AddFolderResp};
+use crate::cmd::MailStorage;
+use crate::de_jmhoffmann_jwebmail_mailstorage::{AddFolder_Reply_status, Call_AddFolder};
-pub fn add_folder(mut p: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = AddFolderReq::parse_from_bytes(req)?;
- let mut resp = AddFolderResp::new();
+pub fn add_folder(
+ ms: &MailStorage,
+ call: &mut dyn Call_AddFolder,
+ name: String,
+) -> varlink::Result<()> {
+ if let Some(mut p) = ms.maildir_path.read().unwrap().clone() {
+ let mut folder = ".".to_owned();
+ folder.push_str(&name);
- let mut folder = ".".to_owned();
- folder.push_str(&r.name);
- p.push(folder);
+ p.push(folder);
- if p.is_dir() {
- resp.status = 1;
- return resp.write_to_bytes().map_err(|e| e.into());
- }
+ if p.is_dir() {
+ return call.reply(AddFolder_Reply_status::skiped);
+ }
- create_dir(&p)?;
+ create_dir(&p).map_err(varlink::map_context!())?;
- p.push("tmp");
- create_dir(&p)?;
+ p.push("tmp");
+ create_dir(&p).map_err(varlink::map_context!())?;
- p.pop();
- p.push("new");
- create_dir(&p)?;
+ p.pop();
+ p.push("new");
+ create_dir(&p).map_err(varlink::map_context!())?;
- p.pop();
- p.push("cur");
- create_dir(p)?;
+ p.pop();
+ p.push("cur");
+ create_dir(p).map_err(varlink::map_context!())?;
- resp.status = 0;
- resp.write_to_bytes().map_err(|e| e.into())
+ call.reply(AddFolder_Reply_status::created)
+ } else {
+ call.reply_not_initialized()
+ }
}
diff --git a/src/cmd/count.rs b/src/cmd/count.rs
deleted file mode 100644
index 5de542b..0000000
--- a/src/cmd/count.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use std::path::PathBuf;
-
-use protobuf::Message;
-
-use crate::cmd::open_submaildir;
-use crate::error::Result;
-use crate::pb3::jwebmail::{StatsReq, StatsResp};
-
-pub fn count(path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = StatsReq::parse_from_bytes(req)?;
-
- let md = open_submaildir(path, &r.folder);
-
- let mut resp = StatsResp::new();
- resp.mail_count = md.count_cur() as u32;
- resp.unread_count = md
- .list_cur()
- .filter(|x| x.as_ref().map_or(false, |z| !z.is_seen()))
- .count() as u32;
- resp.byte_size = md
- .path()
- .join("cur")
- .read_dir()?
- .map(|x| x.map_or(0, |z| z.metadata().map_or(0, |y| y.len())))
- .sum();
- resp.write_to_bytes().map_err(|e| e.into())
-}
diff --git a/src/cmd/folders.rs b/src/cmd/folders.rs
index 417203c..8bf9a30 100644
--- a/src/cmd/folders.rs
+++ b/src/cmd/folders.rs
@@ -1,9 +1,7 @@
-use std::path::{Path, PathBuf};
+use std::path::Path;
-use protobuf::Message as _;
-
-use crate::error::Result;
-use crate::pb3::jwebmail::{FoldersReq, FoldersResp};
+use crate::cmd::MailStorage;
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Folders;
fn is_mailsubdir(p: &Path) -> bool {
if !p.is_dir() {
@@ -32,19 +30,20 @@ fn is_mailsubdir(p: &Path) -> bool {
true
}
-pub fn folders(path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let _ = FoldersReq::parse_from_bytes(req)?;
-
- let mut subdirs: Vec<_> = path
- .read_dir()?
- .filter_map(|d| d.ok())
- .filter(|d| is_mailsubdir(&d.path()))
- .filter_map(|d| Some(d.path().file_name()?.to_string_lossy()[1..].to_owned()))
- .collect();
+pub fn folders(ms: &MailStorage, call: &mut dyn Call_Folders) -> varlink::Result<()> {
+ if let Some(path) = &*ms.maildir_path.read().unwrap() {
+ let mut subdirs: Vec<_> = path
+ .read_dir()
+ .map_err(varlink::map_context!())?
+ .filter_map(|d| d.ok())
+ .filter(|d| is_mailsubdir(&d.path()))
+ .filter_map(|d| Some(d.path().file_name()?.to_string_lossy()[1..].to_owned()))
+ .collect();
- subdirs.sort();
+ subdirs.sort();
- let mut res = FoldersResp::new();
- res.folders = subdirs;
- res.write_to_bytes().map_err(|e| e.into())
+ call.reply(subdirs)
+ } else {
+ call.reply_not_initialized()
+ }
}
diff --git a/src/cmd/init.rs b/src/cmd/init.rs
new file mode 100644
index 0000000..167d028
--- /dev/null
+++ b/src/cmd/init.rs
@@ -0,0 +1,35 @@
+use std::ffi::CString;
+
+use crate::cmd::MailStorage;
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Init;
+
+pub fn init(
+ ms: &MailStorage,
+ call: &mut dyn Call_Init,
+ unix_user: String,
+ mailbox_path: String,
+) -> varlink::Result<()> {
+ unsafe {
+ *libc::__errno_location() = 0;
+ }
+ if let Ok(c_sys_user) = CString::new(unix_user.clone()) {
+ let user_info: *const libc::passwd = unsafe { libc::getpwnam(c_sys_user.as_ptr()) };
+ let err = unsafe { *libc::__errno_location() };
+ if err != 0 {
+ return call.reply_invalid_user(unix_user);
+ }
+ if user_info.is_null() {
+ return call.reply_invalid_user(unix_user);
+ }
+ let rc = unsafe { libc::setuid((*user_info).pw_uid) };
+ if rc != 0 {
+ return call.reply_invalid_user(unix_user);
+ }
+
+ *ms.maildir_path.write().unwrap() = Some(mailbox_path.into());
+
+ call.reply()
+ } else {
+ call.reply_invalid_user(unix_user)
+ }
+}
diff --git a/src/cmd/list.rs b/src/cmd/list.rs
index 3c2d42a..8aeaf97 100644
--- a/src/cmd/list.rs
+++ b/src/cmd/list.rs
@@ -1,12 +1,11 @@
use std::cmp::Reverse;
-use std::path::PathBuf;
use log::warn;
-use protobuf::Message as _;
-use crate::cmd::open_submaildir;
-use crate::error::Result;
-use crate::pb3::jwebmail::{ListMailHeader, ListReq, ListResp, MailHeader};
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::{
+ Call_List, ListMailHeader, MailHeader, Sort, Sort_direction, Sort_parameter,
+};
use crate::rfc822::me_to_lmh;
fn from_or_sender(mh: &MailHeader) -> &str {
@@ -37,95 +36,110 @@ fn mid_to_rec_time(mid: &str) -> f64 {
fn sort_by_and_take(
mut entries: Vec<maildir::MailEntry>,
- sortby: &str,
+ sortby: Sort,
s: usize,
e: usize,
) -> Vec<ListMailHeader> {
- match sortby {
- "date" => {
- entries.sort_by(|a, b| {
- mid_to_rec_time(a.id())
- .partial_cmp(&mid_to_rec_time(b.id()))
- .unwrap()
- });
- entries
- .drain(s..e)
- .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
- .collect()
- }
- "!date" => {
- entries.sort_by(|b, a| {
- mid_to_rec_time(a.id())
- .partial_cmp(&mid_to_rec_time(b.id()))
- .unwrap()
- });
- entries
- .drain(s..e)
- .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
- .collect()
- }
- "size" => {
- entries.sort_by_cached_key(|a| a.path().metadata().map_or(0, |m| m.len()));
- entries
- .drain(s..e)
- .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
- .collect()
- }
- "!size" => {
- entries.sort_by_cached_key(|a| Reverse(a.path().metadata().map_or(0, |m| m.len())));
- entries
- .drain(s..e)
- .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
- .collect()
- }
- "subject" | "!subject" => {
+ match sortby.parameter {
+ Sort_parameter::date => match sortby.direction {
+ Sort_direction::asc => {
+ entries.sort_by(|a, b| {
+ mid_to_rec_time(a.id())
+ .partial_cmp(&mid_to_rec_time(b.id()))
+ .unwrap()
+ });
+ entries
+ .drain(s..e)
+ .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
+ .collect()
+ }
+ Sort_direction::desc => {
+ entries.sort_by(|b, a| {
+ mid_to_rec_time(a.id())
+ .partial_cmp(&mid_to_rec_time(b.id()))
+ .unwrap()
+ });
+ entries
+ .drain(s..e)
+ .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
+ .collect()
+ }
+ },
+ Sort_parameter::size => match sortby.direction {
+ Sort_direction::asc => {
+ entries.sort_by_cached_key(|a| a.path().metadata().map_or(0, |m| m.len()));
+ entries
+ .drain(s..e)
+ .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
+ .collect()
+ }
+ Sort_direction::desc => {
+ entries.sort_by_cached_key(|a| Reverse(a.path().metadata().map_or(0, |m| m.len())));
+ entries
+ .drain(s..e)
+ .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
+ .collect()
+ }
+ },
+ Sort_parameter::subject => {
let mut x: Vec<ListMailHeader> = entries
.drain(..)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect();
- if sortby.as_bytes().first().copied() == Some(b'!') {
- x.sort_by(|a, b| a.header.subject.cmp(&b.header.subject))
- } else {
- x.sort_by(|b, a| a.header.subject.cmp(&b.header.subject))
+ match sortby.direction {
+ Sort_direction::asc => x.sort_by(|a, b| a.header.subject.cmp(&b.header.subject)),
+ Sort_direction::desc => x.sort_by(|b, a| a.header.subject.cmp(&b.header.subject)),
}
x.drain(s..e).collect()
}
- "sender" | "!sender" => {
+ Sort_parameter::sender => {
let mut x: Vec<ListMailHeader> = entries
.drain(..)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect();
- if sortby.as_bytes().first().copied() != Some(b'!') {
- x.sort_by(|a, b| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
- } else {
- x.sort_by(|b, a| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
+ match sortby.direction {
+ Sort_direction::asc => {
+ x.sort_by(|a, b| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
+ }
+ Sort_direction::desc => {
+ x.sort_by(|b, a| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
+ }
}
x.drain(s..e).collect()
}
- _ => todo!(),
}
}
-pub fn list(path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = ListReq::parse_from_bytes(req)?;
- let md = open_submaildir(path, &r.folder);
+pub fn list(
+ ms: &MailStorage,
+ call: &mut dyn Call_List,
+ folder: String,
+ start: i64,
+ end: i64,
+ sort: Sort,
+) -> varlink::Result<()> {
+ if let Some(path) = ms.maildir_path.read().unwrap().clone() {
+ let md = open_submaildir(path, &folder);
- for r in md.list_new() {
- match r {
- Err(e) => warn!("{}", e),
- Ok(me) => {
- if let Err(e) = md.move_new_to_cur(me.id()) {
- warn!("{}", e);
+ for r in md.list_new() {
+ match r {
+ Err(e) => warn!("{}", e),
+ Ok(me) => {
+ if let Err(e) = md.move_new_to_cur(me.id()) {
+ warn!("{}", e);
+ }
}
- }
- };
- }
+ };
+ }
+
+ let a: Vec<_> = md.list_cur().filter_map(std::result::Result::ok).collect();
+ let start = std::cmp::min(a.len(), start as usize);
+ let end = std::cmp::min(a.len(), end as usize);
- let a: Vec<_> = md.list_cur().filter_map(std::result::Result::ok).collect();
- let start = std::cmp::min(a.len(), r.start as usize);
- let end = std::cmp::min(a.len(), r.end as usize);
+ let mail_heads = sort_by_and_take(a, sort, start, end);
- let mut resp = ListResp::new();
- resp.mail_heads = sort_by_and_take(a, &r.sort, start, end);
- resp.write_to_bytes().map_err(|e| e.into())
+ call.reply(mail_heads)
+ } else {
+ call.reply_not_initialized()
+ }
}
diff --git a/src/cmd/move.rs b/src/cmd/move.rs
new file mode 100644
index 0000000..c852d6c
--- /dev/null
+++ b/src/cmd/move.rs
@@ -0,0 +1,19 @@
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Move;
+
+pub fn r#move(
+ ms: &MailStorage,
+ call: &mut dyn Call_Move,
+ mid: String,
+ from_folder: String,
+ to_folder: String,
+) -> varlink::Result<()> {
+ if let Some(p) = ms.maildir_path.read().unwrap().clone() {
+ let from = open_submaildir(p.clone(), &from_folder);
+ let to = open_submaildir(p, &to_folder);
+ from.move_to(&mid, &to).unwrap();
+ call.reply()
+ } else {
+ call.reply_not_initialized()
+ }
+}
diff --git a/src/cmd/move_mail.rs b/src/cmd/move_mail.rs
deleted file mode 100644
index 146e906..0000000
--- a/src/cmd/move_mail.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use std::path::PathBuf;
-
-use protobuf::Message as _;
-
-use crate::cmd::open_submaildir;
-use crate::error::Result;
-use crate::pb3::jwebmail::{MoveReq, MoveResp};
-
-pub fn move_mail(p: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = MoveReq::parse_from_bytes(req)?;
-
- let from = open_submaildir(p.clone(), &r.from_f);
- let to = open_submaildir(p, &r.to_f);
- from.move_to(&r.mid, &to)?;
-
- let resp = MoveResp::new();
- resp.write_to_bytes().map_err(|e| e.into())
-}
diff --git a/src/cmd/raw.rs b/src/cmd/raw.rs
index 9fe7620..a20eded 100644
--- a/src/cmd/raw.rs
+++ b/src/cmd/raw.rs
@@ -1,106 +1,109 @@
-use std::fs::read;
-use std::io::ErrorKind as IOErrKind;
-use std::path::PathBuf;
+use base64::prelude::BASE64_STANDARD;
+use base64::Engine;
-use protobuf::Message as _;
-
-use crate::cmd::open_submaildir;
-use crate::error::{Error, Result};
-use crate::pb3::jwebmail::mimeheader::ContentDisposition::*;
-use crate::pb3::jwebmail::{MIMEHeader, RawReq, RawResp};
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::MIMEHeader_content_dispo as CD;
+use crate::de_jmhoffmann_jwebmail_mailstorage::{Call_Raw, MIMEHeader, MIMEHeader_mime_type};
use crate::rfc822::parse_mail_content;
-pub fn raw(md_path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = RawReq::parse_from_bytes(req)?;
-
- let md = open_submaildir(md_path, &r.folder);
+pub fn raw(
+ ms: &MailStorage,
+ call: &mut dyn Call_Raw,
+ folder: String,
+ mid: String,
+ path: Option<String>,
+) -> varlink::Result<()> {
+ if let Some(p) = ms.maildir_path.read().unwrap().clone() {
+ let md = open_submaildir(p, &folder);
- let mut mail = md.find(&r.mid).ok_or_else(|| {
- std::io::Error::new(IOErrKind::NotFound, format!("mail {} not found", &r.mid))
- })?;
+ if let Some(mut mail) = md.find(&mid) {
+ match path.as_deref() {
+ Some("") | None => call.reply(
+ MIMEHeader {
+ mime_type: MIMEHeader_mime_type {
+ main_type: "message".to_owned(),
+ sub_type: "rfc822".to_owned(),
+ },
+ file_name: Some(mail.id().to_owned()),
+ content_dispo: CD::none,
+ },
+ BASE64_STANDARD
+ .encode(std::fs::read(mail.path()).map_err(varlink::map_context!())?),
+ ),
+ Some(mime_path) => {
+ if let Ok(path) = mime_path
+ .split('.')
+ .map(|x| x.parse())
+ .collect::<std::result::Result<Vec<usize>, std::num::ParseIntError>>()
+ {
+ let mut m = mail.parsed().unwrap();
- match r.path.as_deref() {
- Some("") | None => {
- let mut mh = MIMEHeader::new();
+ if path[0] != 0 {
+ return call.reply_invalid_path_in_mail(
+ folder,
+ mid,
+ mime_path.to_owned(),
+ );
+ }
- mh.maintype = "message".to_owned();
- mh.subtype = "rfc822".to_owned();
- mh.file_name = Some(mail.id().to_owned());
- mh.contentdispo = CONTENT_DISPOSITION_NONE.into();
+ for i in &path[1..] {
+ match &m.ctype.mimetype {
+ x if x.starts_with("message/") => {
+ if *i != 0 {
+ return call.reply_invalid_path_in_mail(
+ folder,
+ mid,
+ mime_path.to_owned(),
+ );
+ }
+ let s: &'static _ = m.get_body_raw().unwrap().leak();
+ m = mailparse::parse_mail(s).unwrap();
+ }
+ x if x.starts_with("multipart/") => {
+ if *i >= m.subparts.len() {
+ return call.reply_invalid_path_in_mail(
+ folder,
+ mid,
+ mime_path.to_owned(),
+ );
+ }
+ m = m.subparts.swap_remove(*i);
+ }
+ _ => {
+ return call.reply_invalid_path_in_mail(
+ folder,
+ mid,
+ mime_path.to_owned(),
+ );
+ }
+ }
+ }
- let mut resp = RawResp::new();
- resp.header = Some(mh).into();
- resp.body = read(mail.path())?;
- resp.write_to_bytes().map_err(|e| e.into())
- }
- Some(mime_path) => {
- let path = mime_path
- .split('.')
- .map(|x| {
- x.parse()
- .map_err(|pe: std::num::ParseIntError| Error::PathError {
- msg: pe.to_string(),
- path: mime_path.to_owned(),
- })
- })
- .collect::<Result<Vec<_>>>()?;
- let mut m = mail.parsed()?;
+ if m.ctype.mimetype.starts_with("multipart/") {
+ return call.reply_invalid_path_in_mail(
+ folder,
+ mid,
+ mime_path.to_owned(),
+ );
+ }
- if path[0] != 0 {
- return Err(Error::PathError {
- msg: "Message must be accessed by a 0".to_owned(),
- path: mime_path.to_owned(),
- });
- }
+ let mime_part = parse_mail_content(&m);
+ let content = if m.ctype.mimetype.starts_with("text/") {
+ m.get_body().unwrap().into_bytes()
+ } else {
+ m.get_body_raw().unwrap()
+ };
- for i in &path[1..] {
- match &m.ctype.mimetype {
- x if x.starts_with("message/") => {
- if *i != 0 {
- return Err(Error::PathError {
- msg: "Message must be accessed by a 0".to_owned(),
- path: mime_path.to_owned(),
- });
- }
- let s: &'static _ = m.get_body_raw()?.leak();
- m = mailparse::parse_mail(s)?;
- }
- x if x.starts_with("multipart/") => {
- if *i >= m.subparts.len() {
- return Err(Error::PathError {
- msg: "Out of bounds access".to_owned(),
- path: mime_path.to_owned(),
- });
- }
- m = m.subparts.swap_remove(*i);
- }
- _ => {
- return Err(Error::PathError {
- msg: "Unable to descent into leaf component".to_owned(),
- path: mime_path.to_owned(),
- })
+ call.reply(mime_part, BASE64_STANDARD.encode(content))
+ } else {
+ call.reply_invalid_path_in_mail(folder, mid, mime_path.to_owned())
}
}
}
-
- if m.ctype.mimetype.starts_with("multipart/") {
- return Err(Error::PathError {
- msg: "Can not show multipart component".to_owned(),
- path: mime_path.to_owned(),
- });
- }
-
- let mime_part = parse_mail_content(&m)?;
- let content = if m.ctype.mimetype.starts_with("text/") {
- m.get_body()?.into_bytes()
- } else {
- m.get_body_raw()?
- };
-
- let mut resp = RawResp::new();
- resp.header = Some(mime_part).into();
- resp.body = content;
- resp.write_to_bytes().map_err(|e| e.into())
+ } else {
+ call.reply_invalid_mid(folder, mid)
}
+ } else {
+ call.reply_not_initialized()
}
}
diff --git a/src/cmd/read.rs b/src/cmd/read.rs
deleted file mode 100644
index 797f4d6..0000000
--- a/src/cmd/read.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use std::io::ErrorKind as IOErrKind;
-use std::path::PathBuf;
-
-use protobuf::Message as _;
-
-use crate::cmd::open_submaildir;
-use crate::error::Result;
-use crate::pb3::jwebmail::{ShowReq, ShowResp};
-use crate::rfc822::parsed_mail_to_mail;
-
-pub fn read(path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = ShowReq::parse_from_bytes(req)?;
- let md = open_submaildir(path, &r.folder);
-
- md.add_flags(&r.mid, "S")?;
-
- let mut mail = md.find(&r.mid).ok_or_else(|| {
- std::io::Error::new(IOErrKind::NotFound, format!("mail {} not found", &r.mid))
- })?;
-
- let mut resp = ShowResp::new();
- resp.mail = Some(parsed_mail_to_mail(mail.parsed()?)?).into();
- resp.write_to_bytes().map_err(|e| e.into())
-}
diff --git a/src/cmd/remove.rs b/src/cmd/remove.rs
index 8d26e68..73328a5 100644
--- a/src/cmd/remove.rs
+++ b/src/cmd/remove.rs
@@ -1,17 +1,18 @@
-use std::path::PathBuf;
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Remove;
-use protobuf::Message as _;
+pub fn remove(
+ ms: &MailStorage,
+ call: &mut dyn Call_Remove,
+ folder: String,
+ mid: String,
+) -> varlink::Result<()> {
+ if let Some(p) = ms.maildir_path.read().unwrap().clone() {
+ let md = open_submaildir(p, &folder);
+ md.add_flags(&mid, "T").map_err(varlink::map_context!())?;
-use crate::cmd::open_submaildir;
-use crate::error::Result;
-use crate::pb3::jwebmail::{RemoveReq, RemoveResp};
-
-pub fn remove(p: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
- let r = RemoveReq::parse_from_bytes(req)?;
-
- let md = open_submaildir(p, &r.folder);
- md.add_flags(&r.mid, "T")?;
-
- let resp = RemoveResp::new();
- resp.write_to_bytes().map_err(|e| e.into())
+ call.reply()
+ } else {
+ call.reply_not_initialized()
+ }
}
diff --git a/src/cmd/search.rs b/src/cmd/search.rs
new file mode 100644
index 0000000..7cf0fb0
--- /dev/null
+++ b/src/cmd/search.rs
@@ -0,0 +1,17 @@
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Search;
+
+pub fn search(
+ ms: &MailStorage,
+ call: &mut dyn Call_Search,
+ folder: String,
+ _pattern: String,
+) -> varlink::Result<()> {
+ if let Some(p) = ms.maildir_path.read().unwrap().clone() {
+ let md = open_submaildir(p, &folder);
+
+ todo!();
+ } else {
+ call.reply_not_initialized()
+ }
+}
diff --git a/src/cmd/show.rs b/src/cmd/show.rs
new file mode 100644
index 0000000..442eb21
--- /dev/null
+++ b/src/cmd/show.rs
@@ -0,0 +1,26 @@
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Show;
+use crate::rfc822::parsed_mail_to_mail;
+
+pub fn show(
+ ms: &MailStorage,
+ call: &mut dyn Call_Show,
+ folder: String,
+ mid: String,
+) -> varlink::Result<()> {
+ if let Some(path) = ms.maildir_path.read().unwrap().clone() {
+ let md = open_submaildir(path, &folder);
+
+ md.add_flags(&mid, "S").map_err(varlink::map_context!())?;
+
+ if let Some(mut mail) = md.find(&mid) {
+ let mail2 = parsed_mail_to_mail(mail.parsed().unwrap()).unwrap();
+
+ call.reply(mail2)
+ } else {
+ call.reply_invalid_mid(folder, mid)
+ }
+ } else {
+ call.reply_not_initialized()
+ }
+}
diff --git a/src/cmd/stats.rs b/src/cmd/stats.rs
new file mode 100644
index 0000000..e8e8325
--- /dev/null
+++ b/src/cmd/stats.rs
@@ -0,0 +1,25 @@
+use crate::cmd::{open_submaildir, MailStorage};
+use crate::de_jmhoffmann_jwebmail_mailstorage::Call_Stats;
+
+pub fn stats(ms: &MailStorage, call: &mut dyn Call_Stats, folder: String) -> varlink::Result<()> {
+ if let Some(maildir_path) = ms.maildir_path.read().unwrap().clone() {
+ let maildir = open_submaildir(maildir_path, &folder);
+
+ let mail_count = maildir.count_cur() as i64;
+ let unread_count = maildir
+ .list_cur()
+ .filter(|x| x.as_ref().map_or(false, |z| !z.is_seen()))
+ .count() as i64;
+ let byte_size: u64 = maildir
+ .path()
+ .join("cur")
+ .read_dir()
+ .map_err(varlink::map_context!())?
+ .map(|x| x.map_or(0, |z| z.metadata().map_or(0, |y| y.len())))
+ .sum();
+
+ call.reply(mail_count, unread_count, byte_size as i64)
+ } else {
+ call.reply_not_initialized()
+ }
+}
diff --git a/src/de.jmhoffmann.jwebmail.mail-storage.varlink b/src/de.jmhoffmann.jwebmail.mail-storage.varlink
new file mode 100644
index 0000000..f61f624
--- /dev/null
+++ b/src/de.jmhoffmann.jwebmail.mail-storage.varlink
@@ -0,0 +1,86 @@
+interface de.jmhoffmann.jwebmail.mail-storage
+
+
+type MIMEHeader (
+ mime_type: (main_type: string, sub_type: string),
+ content_dispo: (none, inline, attachment),
+ file_name: ?string
+)
+
+type MailAddr (
+ name: ?string,
+ address: string
+)
+
+# send_date is of ISO 8601 date-time format
+type MailHeader (
+ send_date: string,
+ written_from: []MailAddr,
+ sender: ?MailAddr,
+ reply_to: []MailAddr,
+ send_to: []MailAddr,
+ cc: []MailAddr,
+ bcc: []MailAddr,
+ subject: string,
+ comments: []string,
+ keywords: []string,
+ mime: MIMEHeader
+)
+
+type ListMailHeader (
+ byte_size: int,
+ unread: bool,
+ rec_date: string,
+ mid: string,
+ header: MailHeader
+)
+
+type Multipart (
+ preamble: ?string,
+ parts: []MIMEPart,
+ epilogue: ?string
+)
+
+# exactly one of these fileds must be present
+type MailBody (
+ discrete: ?string,
+ multipart: ?Multipart,
+ mail: ?Mail
+)
+
+type Mail (
+ head: MailHeader,
+ body: MailBody
+)
+
+type MIMEPart (
+ mime_header: MIMEHeader,
+ body: MailBody
+)
+
+type Sort (
+ direction: (asc, desc),
+ parameter: (date, size, sender)
+)
+
+
+method Init(unix_user: string, mailbox_path: string) -> ()
+method List(folder: string, start: int, end: int, sort: Sort) -> (mail_heads: []ListMailHeader)
+method Stats(folder: string) -> (mail_count: int, unread_count: int, byte_size: int)
+method Show(folder: string, mid: string) -> (mail: Mail)
+# body is base64 encoded
+method Raw(folder: string, mid: string, path: ?string) -> (header: MIMEHeader, body: string)
+method Search(folder: string, pattern: string) -> (found: []ListMailHeader)
+method Folders() -> (folders: []string)
+method Move(mid: string, from_folder: string, to_folder: string) -> ()
+method Remove(folder: string, mid: string) -> ()
+method AddFolder(name: string) -> (status: (created, skiped))
+
+
+error NotInitialized()
+error InvalidFolder(folder: string)
+error InvalidMID(folder: string, mid: string)
+error InvalidPathInMail(folder: string, mid: string, path: string)
+error InvalidSearchPattern(pattern: string)
+error InvalidUser(unix_user: string)
+error InvalidMailbox(path: string, not_a_mailbox: bool, user_mismatch: bool)
diff --git a/src/de_jmhoffmann_jwebmail_mail-storage.rs b/src/de_jmhoffmann_jwebmail_mail-storage.rs
new file mode 100644
index 0000000..bf3b148
--- /dev/null
+++ b/src/de_jmhoffmann_jwebmail_mail-storage.rs
@@ -0,0 +1,1035 @@
+#![doc = "This file was automatically generated by the varlink rust generator"]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+use serde_derive::{Deserialize, Serialize};
+use std::io::BufRead;
+use std::sync::{Arc, RwLock};
+use varlink::{self, CallTrait};
+#[allow(dead_code)]
+#[derive(Clone, PartialEq, Debug)]
+#[allow(clippy::enum_variant_names)]
+pub enum ErrorKind {
+ Varlink_Error,
+ VarlinkReply_Error,
+ InvalidFolder(Option<InvalidFolder_Args>),
+ InvalidMID(Option<InvalidMID_Args>),
+ InvalidMailbox(Option<InvalidMailbox_Args>),
+ InvalidPathInMail(Option<InvalidPathInMail_Args>),
+ InvalidSearchPattern(Option<InvalidSearchPattern_Args>),
+ InvalidUser(Option<InvalidUser_Args>),
+ NotInitialized(Option<NotInitialized_Args>),
+}
+impl ::std::fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match self {
+ ErrorKind::Varlink_Error => write!(f, "Varlink Error"),
+ ErrorKind::VarlinkReply_Error => write!(f, "Varlink error reply"),
+ ErrorKind::InvalidFolder(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidFolder: {:#?}",
+ v
+ ),
+ ErrorKind::InvalidMID(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidMID: {:#?}",
+ v
+ ),
+ ErrorKind::InvalidMailbox(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidMailbox: {:#?}",
+ v
+ ),
+ ErrorKind::InvalidPathInMail(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidPathInMail: {:#?}",
+ v
+ ),
+ ErrorKind::InvalidSearchPattern(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidSearchPattern: {:#?}",
+ v
+ ),
+ ErrorKind::InvalidUser(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidUser: {:#?}",
+ v
+ ),
+ ErrorKind::NotInitialized(v) => write!(
+ f,
+ "de.jmhoffmann.jwebmail.mail-storage.NotInitialized: {:#?}",
+ v
+ ),
+ }
+ }
+}
+pub struct Error(
+ pub ErrorKind,
+ pub Option<Box<dyn std::error::Error + 'static + Send + Sync>>,
+ pub Option<&'static str>,
+);
+impl Error {
+ #[allow(dead_code)]
+ pub fn kind(&self) -> &ErrorKind {
+ &self.0
+ }
+}
+impl From<ErrorKind> for Error {
+ fn from(e: ErrorKind) -> Self {
+ Error(e, None, None)
+ }
+}
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.1
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
+ }
+}
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+}
+impl std::fmt::Debug for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ use std::error::Error as StdError;
+ if let Some(ref o) = self.2 {
+ std::fmt::Display::fmt(o, f)?;
+ }
+ std::fmt::Debug::fmt(&self.0, f)?;
+ if let Some(e) = self.source() {
+ std::fmt::Display::fmt("\nCaused by:\n", f)?;
+ std::fmt::Debug::fmt(&e, f)?;
+ }
+ Ok(())
+ }
+}
+#[allow(dead_code)]
+pub type Result<T> = std::result::Result<T, Error>;
+impl From<varlink::Error> for Error {
+ fn from(e: varlink::Error) -> Self {
+ match e.kind() {
+ varlink::ErrorKind::VarlinkErrorReply(r) => Error(
+ ErrorKind::from(r),
+ Some(Box::from(e)),
+ Some(concat!(file!(), ":", line!(), ": ")),
+ ),
+ _ => Error(
+ ErrorKind::Varlink_Error,
+ Some(Box::from(e)),
+ Some(concat!(file!(), ":", line!(), ": ")),
+ ),
+ }
+ }
+}
+#[allow(dead_code)]
+impl Error {
+ pub fn source_varlink_kind(&self) -> Option<&varlink::ErrorKind> {
+ use std::error::Error as StdError;
+ let mut s: &dyn StdError = self;
+ while let Some(c) = s.source() {
+ let k = self
+ .source()
+ .and_then(|e| e.downcast_ref::<varlink::Error>())
+ .map(|e| e.kind());
+ if k.is_some() {
+ return k;
+ }
+ s = c;
+ }
+ None
+ }
+}
+impl From<&varlink::Reply> for ErrorKind {
+ #[allow(unused_variables)]
+ fn from(e: &varlink::Reply) -> Self {
+ match e {
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidFolder" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidFolder(v),
+ Err(_) => ErrorKind::InvalidFolder(None),
+ },
+ _ => ErrorKind::InvalidFolder(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidMID" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidMID(v),
+ Err(_) => ErrorKind::InvalidMID(None),
+ },
+ _ => ErrorKind::InvalidMID(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidMailbox" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidMailbox(v),
+ Err(_) => ErrorKind::InvalidMailbox(None),
+ },
+ _ => ErrorKind::InvalidMailbox(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidPathInMail" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidPathInMail(v),
+ Err(_) => ErrorKind::InvalidPathInMail(None),
+ },
+ _ => ErrorKind::InvalidPathInMail(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidSearchPattern" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidSearchPattern(v),
+ Err(_) => ErrorKind::InvalidSearchPattern(None),
+ },
+ _ => ErrorKind::InvalidSearchPattern(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.InvalidUser" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::InvalidUser(v),
+ Err(_) => ErrorKind::InvalidUser(None),
+ },
+ _ => ErrorKind::InvalidUser(None),
+ },
+ varlink::Reply {
+ error: Some(ref t), ..
+ } if t == "de.jmhoffmann.jwebmail.mail-storage.NotInitialized" => match e {
+ varlink::Reply {
+ parameters: Some(p),
+ ..
+ } => match serde_json::from_value(p.clone()) {
+ Ok(v) => ErrorKind::NotInitialized(v),
+ Err(_) => ErrorKind::NotInitialized(None),
+ },
+ _ => ErrorKind::NotInitialized(None),
+ },
+ _ => ErrorKind::VarlinkReply_Error,
+ }
+ }
+}
+pub trait VarlinkCallError: varlink::CallTrait {
+ fn reply_invalid_folder(&mut self, r#folder: String) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidFolder",
+ Some(
+ serde_json::to_value(InvalidFolder_Args { r#folder })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_invalid_mid(&mut self, r#folder: String, r#mid: String) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidMID",
+ Some(
+ serde_json::to_value(InvalidMID_Args { r#folder, r#mid })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_invalid_mailbox(
+ &mut self,
+ r#path: String,
+ r#not_a_mailbox: bool,
+ r#user_mismatch: bool,
+ ) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidMailbox",
+ Some(
+ serde_json::to_value(InvalidMailbox_Args {
+ r#path,
+ r#not_a_mailbox,
+ r#user_mismatch,
+ })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_invalid_path_in_mail(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ r#path: String,
+ ) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidPathInMail",
+ Some(
+ serde_json::to_value(InvalidPathInMail_Args {
+ r#folder,
+ r#mid,
+ r#path,
+ })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_invalid_search_pattern(&mut self, r#pattern: String) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidSearchPattern",
+ Some(
+ serde_json::to_value(InvalidSearchPattern_Args { r#pattern })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_invalid_user(&mut self, r#unix_user: String) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.InvalidUser",
+ Some(
+ serde_json::to_value(InvalidUser_Args { r#unix_user })
+ .map_err(varlink::map_context!())?,
+ ),
+ ))
+ }
+ fn reply_not_initialized(&mut self) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::error(
+ "de.jmhoffmann.jwebmail.mail-storage.NotInitialized",
+ None,
+ ))
+ }
+}
+impl<'a> VarlinkCallError for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#ListMailHeader {
+ pub r#byte_size: i64,
+ pub r#unread: bool,
+ pub r#rec_date: String,
+ pub r#mid: String,
+ pub r#header: MailHeader,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MIMEHeader_mime_type {
+ pub r#main_type: String,
+ pub r#sub_type: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub enum r#MIMEHeader_content_dispo {
+ r#none,
+ r#inline,
+ r#attachment,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MIMEHeader {
+ pub r#mime_type: MIMEHeader_mime_type,
+ pub r#content_dispo: MIMEHeader_content_dispo,
+ pub r#file_name: Option<String>,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MIMEPart {
+ pub r#mime_header: MIMEHeader,
+ pub r#body: MailBody,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#Mail {
+ pub r#head: MailHeader,
+ pub r#body: MailBody,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MailAddr {
+ pub r#name: Option<String>,
+ pub r#address: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MailBody {
+ pub r#discrete: Option<String>,
+ pub r#multipart: Option<Multipart>,
+ pub r#mail: Option<Box<Mail>>,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#MailHeader {
+ pub r#send_date: String,
+ pub r#written_from: Vec<MailAddr>,
+ pub r#sender: Option<MailAddr>,
+ pub r#reply_to: Vec<MailAddr>,
+ pub r#send_to: Vec<MailAddr>,
+ pub r#cc: Vec<MailAddr>,
+ pub r#bcc: Vec<MailAddr>,
+ pub r#subject: String,
+ pub r#comments: Vec<String>,
+ pub r#keywords: Vec<String>,
+ pub r#mime: MIMEHeader,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#Multipart {
+ pub r#preamble: Option<String>,
+ pub r#parts: Vec<MIMEPart>,
+ pub r#epilogue: Option<String>,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub enum r#Sort_direction {
+ r#asc,
+ r#desc,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub enum r#Sort_parameter {
+ r#date,
+ r#size,
+ r#sender,
+ r#subject,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct r#Sort {
+ pub r#direction: Sort_direction,
+ pub r#parameter: Sort_parameter,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidFolder_Args {
+ pub r#folder: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidMID_Args {
+ pub r#folder: String,
+ pub r#mid: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidMailbox_Args {
+ pub r#path: String,
+ pub r#not_a_mailbox: bool,
+ pub r#user_mismatch: bool,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidPathInMail_Args {
+ pub r#folder: String,
+ pub r#mid: String,
+ pub r#path: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidSearchPattern_Args {
+ pub r#pattern: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct InvalidUser_Args {
+ pub r#unix_user: String,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct NotInitialized_Args {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub enum r#AddFolder_Reply_status {
+ r#created,
+ r#skiped,
+}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct AddFolder_Reply {
+ pub r#status: AddFolder_Reply_status,
+}
+impl varlink::VarlinkReply for AddFolder_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct AddFolder_Args {
+ pub r#name: String,
+}
+pub trait Call_AddFolder: VarlinkCallError {
+ fn reply(&mut self, r#status: AddFolder_Reply_status) -> varlink::Result<()> {
+ self.reply_struct(AddFolder_Reply { r#status }.into())
+ }
+}
+impl<'a> Call_AddFolder for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Folders_Reply {
+ pub r#folders: Vec<String>,
+}
+impl varlink::VarlinkReply for Folders_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Folders_Args {}
+pub trait Call_Folders: VarlinkCallError {
+ fn reply(&mut self, r#folders: Vec<String>) -> varlink::Result<()> {
+ self.reply_struct(Folders_Reply { r#folders }.into())
+ }
+}
+impl<'a> Call_Folders for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Init_Reply {}
+impl varlink::VarlinkReply for Init_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Init_Args {
+ pub r#unix_user: String,
+ pub r#mailbox_path: String,
+}
+pub trait Call_Init: VarlinkCallError {
+ fn reply(&mut self) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::parameters(None))
+ }
+}
+impl<'a> Call_Init for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct List_Reply {
+ pub r#mail_heads: Vec<ListMailHeader>,
+}
+impl varlink::VarlinkReply for List_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct List_Args {
+ pub r#folder: String,
+ pub r#start: i64,
+ pub r#end: i64,
+ pub r#sort: Sort,
+}
+pub trait Call_List: VarlinkCallError {
+ fn reply(&mut self, r#mail_heads: Vec<ListMailHeader>) -> varlink::Result<()> {
+ self.reply_struct(List_Reply { r#mail_heads }.into())
+ }
+}
+impl<'a> Call_List for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Move_Reply {}
+impl varlink::VarlinkReply for Move_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Move_Args {
+ pub r#mid: String,
+ pub r#from_folder: String,
+ pub r#to_folder: String,
+}
+pub trait Call_Move: VarlinkCallError {
+ fn reply(&mut self) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::parameters(None))
+ }
+}
+impl<'a> Call_Move for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Raw_Reply {
+ pub r#header: MIMEHeader,
+ pub r#body: String,
+}
+impl varlink::VarlinkReply for Raw_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Raw_Args {
+ pub r#folder: String,
+ pub r#mid: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub r#path: Option<String>,
+}
+pub trait Call_Raw: VarlinkCallError {
+ fn reply(&mut self, r#header: MIMEHeader, r#body: String) -> varlink::Result<()> {
+ self.reply_struct(Raw_Reply { r#header, r#body }.into())
+ }
+}
+impl<'a> Call_Raw for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Remove_Reply {}
+impl varlink::VarlinkReply for Remove_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Remove_Args {
+ pub r#folder: String,
+ pub r#mid: String,
+}
+pub trait Call_Remove: VarlinkCallError {
+ fn reply(&mut self) -> varlink::Result<()> {
+ self.reply_struct(varlink::Reply::parameters(None))
+ }
+}
+impl<'a> Call_Remove for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Search_Reply {
+ pub r#found: Vec<ListMailHeader>,
+}
+impl varlink::VarlinkReply for Search_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Search_Args {
+ pub r#folder: String,
+ pub r#pattern: String,
+}
+pub trait Call_Search: VarlinkCallError {
+ fn reply(&mut self, r#found: Vec<ListMailHeader>) -> varlink::Result<()> {
+ self.reply_struct(Search_Reply { r#found }.into())
+ }
+}
+impl<'a> Call_Search for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Show_Reply {
+ pub r#mail: Mail,
+}
+impl varlink::VarlinkReply for Show_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Show_Args {
+ pub r#folder: String,
+ pub r#mid: String,
+}
+pub trait Call_Show: VarlinkCallError {
+ fn reply(&mut self, r#mail: Mail) -> varlink::Result<()> {
+ self.reply_struct(Show_Reply { r#mail }.into())
+ }
+}
+impl<'a> Call_Show for varlink::Call<'a> {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Stats_Reply {
+ pub r#mail_count: i64,
+ pub r#unread_count: i64,
+ pub r#byte_size: i64,
+}
+impl varlink::VarlinkReply for Stats_Reply {}
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Stats_Args {
+ pub r#folder: String,
+}
+pub trait Call_Stats: VarlinkCallError {
+ fn reply(
+ &mut self,
+ r#mail_count: i64,
+ r#unread_count: i64,
+ r#byte_size: i64,
+ ) -> varlink::Result<()> {
+ self.reply_struct(
+ Stats_Reply {
+ r#mail_count,
+ r#unread_count,
+ r#byte_size,
+ }
+ .into(),
+ )
+ }
+}
+impl<'a> Call_Stats for varlink::Call<'a> {}
+pub trait VarlinkInterface {
+ fn add_folder(&self, call: &mut dyn Call_AddFolder, r#name: String) -> varlink::Result<()>;
+ fn folders(&self, call: &mut dyn Call_Folders) -> varlink::Result<()>;
+ fn init(
+ &self,
+ call: &mut dyn Call_Init,
+ r#unix_user: String,
+ r#mailbox_path: String,
+ ) -> varlink::Result<()>;
+ fn list(
+ &self,
+ call: &mut dyn Call_List,
+ r#folder: String,
+ r#start: i64,
+ r#end: i64,
+ r#sort: Sort,
+ ) -> varlink::Result<()>;
+ fn r#move(
+ &self,
+ call: &mut dyn Call_Move,
+ r#mid: String,
+ r#from_folder: String,
+ r#to_folder: String,
+ ) -> varlink::Result<()>;
+ fn raw(
+ &self,
+ call: &mut dyn Call_Raw,
+ r#folder: String,
+ r#mid: String,
+ r#path: Option<String>,
+ ) -> varlink::Result<()>;
+ fn remove(
+ &self,
+ call: &mut dyn Call_Remove,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::Result<()>;
+ fn search(
+ &self,
+ call: &mut dyn Call_Search,
+ r#folder: String,
+ r#pattern: String,
+ ) -> varlink::Result<()>;
+ fn show(
+ &self,
+ call: &mut dyn Call_Show,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::Result<()>;
+ fn stats(&self, call: &mut dyn Call_Stats, r#folder: String) -> varlink::Result<()>;
+ fn call_upgraded(
+ &self,
+ _call: &mut varlink::Call,
+ _bufreader: &mut dyn BufRead,
+ ) -> varlink::Result<Vec<u8>> {
+ Ok(Vec::new())
+ }
+}
+pub trait VarlinkClientInterface {
+ fn add_folder(
+ &mut self,
+ r#name: String,
+ ) -> varlink::MethodCall<AddFolder_Args, AddFolder_Reply, Error>;
+ fn folders(&mut self) -> varlink::MethodCall<Folders_Args, Folders_Reply, Error>;
+ fn init(
+ &mut self,
+ r#unix_user: String,
+ r#mailbox_path: String,
+ ) -> varlink::MethodCall<Init_Args, Init_Reply, Error>;
+ fn list(
+ &mut self,
+ r#folder: String,
+ r#start: i64,
+ r#end: i64,
+ r#sort: Sort,
+ ) -> varlink::MethodCall<List_Args, List_Reply, Error>;
+ fn r#move(
+ &mut self,
+ r#mid: String,
+ r#from_folder: String,
+ r#to_folder: String,
+ ) -> varlink::MethodCall<Move_Args, Move_Reply, Error>;
+ fn raw(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ r#path: Option<String>,
+ ) -> varlink::MethodCall<Raw_Args, Raw_Reply, Error>;
+ fn remove(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::MethodCall<Remove_Args, Remove_Reply, Error>;
+ fn search(
+ &mut self,
+ r#folder: String,
+ r#pattern: String,
+ ) -> varlink::MethodCall<Search_Args, Search_Reply, Error>;
+ fn show(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::MethodCall<Show_Args, Show_Reply, Error>;
+ fn stats(&mut self, r#folder: String) -> varlink::MethodCall<Stats_Args, Stats_Reply, Error>;
+}
+#[allow(dead_code)]
+pub struct VarlinkClient {
+ connection: Arc<RwLock<varlink::Connection>>,
+}
+impl VarlinkClient {
+ #[allow(dead_code)]
+ pub fn new(connection: Arc<RwLock<varlink::Connection>>) -> Self {
+ VarlinkClient { connection }
+ }
+}
+impl VarlinkClientInterface for VarlinkClient {
+ fn add_folder(
+ &mut self,
+ r#name: String,
+ ) -> varlink::MethodCall<AddFolder_Args, AddFolder_Reply, Error> {
+ varlink::MethodCall::<AddFolder_Args, AddFolder_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.AddFolder",
+ AddFolder_Args { r#name },
+ )
+ }
+ fn folders(&mut self) -> varlink::MethodCall<Folders_Args, Folders_Reply, Error> {
+ varlink::MethodCall::<Folders_Args, Folders_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Folders",
+ Folders_Args {},
+ )
+ }
+ fn init(
+ &mut self,
+ r#unix_user: String,
+ r#mailbox_path: String,
+ ) -> varlink::MethodCall<Init_Args, Init_Reply, Error> {
+ varlink::MethodCall::<Init_Args, Init_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Init",
+ Init_Args {
+ r#unix_user,
+ r#mailbox_path,
+ },
+ )
+ }
+ fn list(
+ &mut self,
+ r#folder: String,
+ r#start: i64,
+ r#end: i64,
+ r#sort: Sort,
+ ) -> varlink::MethodCall<List_Args, List_Reply, Error> {
+ varlink::MethodCall::<List_Args, List_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.List",
+ List_Args {
+ r#folder,
+ r#start,
+ r#end,
+ r#sort,
+ },
+ )
+ }
+ fn r#move(
+ &mut self,
+ r#mid: String,
+ r#from_folder: String,
+ r#to_folder: String,
+ ) -> varlink::MethodCall<Move_Args, Move_Reply, Error> {
+ varlink::MethodCall::<Move_Args, Move_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Move",
+ Move_Args {
+ r#mid,
+ r#from_folder,
+ r#to_folder,
+ },
+ )
+ }
+ fn raw(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ r#path: Option<String>,
+ ) -> varlink::MethodCall<Raw_Args, Raw_Reply, Error> {
+ varlink::MethodCall::<Raw_Args, Raw_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Raw",
+ Raw_Args {
+ r#folder,
+ r#mid,
+ r#path,
+ },
+ )
+ }
+ fn remove(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::MethodCall<Remove_Args, Remove_Reply, Error> {
+ varlink::MethodCall::<Remove_Args, Remove_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Remove",
+ Remove_Args { r#folder, r#mid },
+ )
+ }
+ fn search(
+ &mut self,
+ r#folder: String,
+ r#pattern: String,
+ ) -> varlink::MethodCall<Search_Args, Search_Reply, Error> {
+ varlink::MethodCall::<Search_Args, Search_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Search",
+ Search_Args {
+ r#folder,
+ r#pattern,
+ },
+ )
+ }
+ fn show(
+ &mut self,
+ r#folder: String,
+ r#mid: String,
+ ) -> varlink::MethodCall<Show_Args, Show_Reply, Error> {
+ varlink::MethodCall::<Show_Args, Show_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Show",
+ Show_Args { r#folder, r#mid },
+ )
+ }
+ fn stats(&mut self, r#folder: String) -> varlink::MethodCall<Stats_Args, Stats_Reply, Error> {
+ varlink::MethodCall::<Stats_Args, Stats_Reply, Error>::new(
+ self.connection.clone(),
+ "de.jmhoffmann.jwebmail.mail-storage.Stats",
+ Stats_Args { r#folder },
+ )
+ }
+}
+#[allow(dead_code)]
+pub struct VarlinkInterfaceProxy {
+ inner: Box<dyn VarlinkInterface + Send + Sync>,
+}
+#[allow(dead_code)]
+pub fn new(inner: Box<dyn VarlinkInterface + Send + Sync>) -> VarlinkInterfaceProxy {
+ VarlinkInterfaceProxy { inner }
+}
+impl varlink::Interface for VarlinkInterfaceProxy {
+ fn get_description(&self) -> &'static str {
+ "interface de.jmhoffmann.jwebmail.mail-storage\n\n\ntype MIMEHeader (\n mime_type: (main_type: string, sub_type: string),\n content_dispo: (none, inline, attachment),\n file_name: ?string\n)\n\ntype MailAddr (\n name: ?string,\n address: string\n)\n\n# send_date is of ISO 8601 date-time format\ntype MailHeader (\n send_date: string,\n written_from: []MailAddr,\n sender: ?MailAddr,\n reply_to: []MailAddr,\n send_to: []MailAddr,\n cc: []MailAddr,\n bcc: []MailAddr,\n subject: string,\n comments: []string,\n keywords: []string,\n mime: MIMEHeader\n)\n\ntype ListMailHeader (\n byte_size: int,\n unread: bool,\n rec_date: string,\n mid: string,\n header: MailHeader\n)\n\ntype Multipart (\n preamble: ?string,\n parts: []MIMEPart,\n epilogue: ?string\n)\n\n# exactly one of these fileds must be present\ntype MailBody (\n discrete: ?string,\n multipart: ?Multipart,\n mail: ?Mail\n)\n\ntype Mail (\n head: MailHeader,\n body: MailBody\n)\n\ntype MIMEPart (\n mime_header: MIMEHeader,\n body: MailBody\n)\n\ntype Sort (\n direction: (asc, desc),\n parameter: (date, size, sender)\n)\n\n\nmethod Init(unix_user: string, mailbox_path: string) -> ()\nmethod List(folder: string, start: int, end: int, sort: Sort) -> (mail_heads: []ListMailHeader)\nmethod Stats(folder: string) -> (mail_count: int, unread_count: int, byte_size: int)\nmethod Show(folder: string, mid: string) -> (mail: Mail)\n# body is base64 encoded\nmethod Raw(folder: string, mid: string, path: ?string) -> (header: MIMEHeader, body: string)\nmethod Search(folder: string, pattern: string) -> (found: []ListMailHeader)\nmethod Folders() -> (folders: []string)\nmethod Move(mid: string, from_folder: string, to_folder: string) -> ()\nmethod Remove(folder: string, mid: string) -> ()\nmethod AddFolder(name: string) -> (status: (created, skiped))\n\n\nerror NotInitialized()\nerror InvalidFolder(folder: string)\nerror InvalidMID(folder: string, mid: string)\nerror InvalidPathInMail(folder: string, mid: string, path: string)\nerror InvalidSearchPattern(pattern: string)\nerror InvalidUser(unix_user: string)\nerror InvalidMailbox(path: string, not_a_mailbox: bool, user_mismatch: bool)\n"
+ }
+ fn get_name(&self) -> &'static str {
+ "de.jmhoffmann.jwebmail.mail-storage"
+ }
+ fn call_upgraded(
+ &self,
+ call: &mut varlink::Call,
+ bufreader: &mut dyn BufRead,
+ ) -> varlink::Result<Vec<u8>> {
+ self.inner.call_upgraded(call, bufreader)
+ }
+ fn call(&self, call: &mut varlink::Call) -> varlink::Result<()> {
+ let req = call.request.unwrap();
+ match req.method.as_ref() {
+ "de.jmhoffmann.jwebmail.mail-storage.AddFolder" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: AddFolder_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner
+ .add_folder(call as &mut dyn Call_AddFolder, args.r#name)
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Folders" => {
+ self.inner.folders(call as &mut dyn Call_Folders)
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Init" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Init_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner.init(
+ call as &mut dyn Call_Init,
+ args.r#unix_user,
+ args.r#mailbox_path,
+ )
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.List" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: List_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner.list(
+ call as &mut dyn Call_List,
+ args.r#folder,
+ args.r#start,
+ args.r#end,
+ args.r#sort,
+ )
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Move" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Move_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner.r#move(
+ call as &mut dyn Call_Move,
+ args.r#mid,
+ args.r#from_folder,
+ args.r#to_folder,
+ )
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Raw" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Raw_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner.raw(
+ call as &mut dyn Call_Raw,
+ args.r#folder,
+ args.r#mid,
+ args.r#path,
+ )
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Remove" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Remove_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner
+ .remove(call as &mut dyn Call_Remove, args.r#folder, args.r#mid)
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Search" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Search_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner
+ .search(call as &mut dyn Call_Search, args.r#folder, args.r#pattern)
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Show" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Show_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner
+ .show(call as &mut dyn Call_Show, args.r#folder, args.r#mid)
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ "de.jmhoffmann.jwebmail.mail-storage.Stats" => {
+ if let Some(args) = req.parameters.clone() {
+ let args: Stats_Args = match serde_json::from_value(args) {
+ Ok(v) => v,
+ Err(e) => {
+ let es = format!("{}", e);
+ let _ = call.reply_invalid_parameter(es.clone());
+ return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
+ }
+ };
+ self.inner.stats(call as &mut dyn Call_Stats, args.r#folder)
+ } else {
+ call.reply_invalid_parameter("parameters".into())
+ }
+ }
+ m => call.reply_method_not_found(String::from(m)),
+ }
+ }
+}
diff --git a/src/error.rs b/src/error.rs
deleted file mode 100644
index 0c0167f..0000000
--- a/src/error.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use maildir::MailEntryError;
-use mailparse::MailParseError;
-use protobuf::Error as PBError;
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug)]
-pub enum Error {
- IoError(std::io::Error),
- MailEntryError(MailEntryError),
- SortOrder(String),
- Setuid(String),
- Protobuf(PBError),
- PathError { msg: String, path: String },
-}
-
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
- write!(f, "{:?}", self)
- }
-}
-
-impl std::error::Error for Error {}
-
-impl From<std::io::Error> for Error {
- fn from(io_err: std::io::Error) -> Self {
- Error::IoError(io_err)
- }
-}
-
-impl From<MailEntryError> for Error {
- fn from(me_err: MailEntryError) -> Self {
- Error::MailEntryError(me_err)
- }
-}
-
-impl From<MailParseError> for Error {
- fn from(mp_err: MailParseError) -> Self {
- Error::MailEntryError(MailEntryError::ParseError(mp_err))
- }
-}
-
-impl From<PBError> for Error {
- fn from(pb_err: PBError) -> Self {
- Error::Protobuf(pb_err)
- }
-}
diff --git a/src/main.rs b/src/main.rs
index 5598b30..a8e33b2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,46 +1,19 @@
-use std::ffi::{CStr, CString};
-use std::io::{stdin, stdout, Read, Write};
+use std::io::{BufRead, BufReader};
+use std::net::Shutdown;
+use std::os::fd::FromRawFd;
+use std::os::unix::net::UnixStream;
+use std::str::FromStr;
-use clap::Parser as _;
+use varlink::{ConnectionHandler, ErrorKind, Stream, VarlinkService};
-mod arguments;
mod cmd;
-mod error;
-mod pb3;
+#[path = "de_jmhoffmann_jwebmail_mail-storage.rs"]
+mod de_jmhoffmann_jwebmail_mailstorage;
mod rfc822;
-use arguments::{Arguments, Mode};
-use error::{Error, Result};
+use cmd::MailStorage;
-fn switch_to_user(sys_user: &str) -> Result<()> {
- unsafe {
- *libc::__errno_location() = 0;
- }
- let c_sys_user =
- CString::new(sys_user).map_err(|e| Error::Setuid(format!("nul char in user, {}", e)))?;
- let user_info: *const libc::passwd = unsafe { libc::getpwnam(c_sys_user.as_ptr()) };
- let err = unsafe { *libc::__errno_location() };
- if err != 0 {
- return Err(Error::Setuid(format!(
- "error calling getpwnam {:?}",
- unsafe { libc::strerror(err) }
- )));
- };
- if user_info.is_null() {
- return Err(Error::Setuid(format!("user {:?} does not exist", sys_user)));
- };
- let rc = unsafe { libc::setuid((*user_info).pw_uid) };
- if rc != 0 {
- let err = unsafe { *libc::__errno_location() };
- return Err(Error::Setuid(format!(
- "error calling setuid {:?}",
- unsafe { CStr::from_ptr(libc::strerror(err)) }
- )));
- }
- Ok(())
-}
-
-fn main() -> Result<()> {
+fn main() {
simplelog::TermLogger::init(
simplelog::LevelFilter::Info,
simplelog::Config::default(),
@@ -49,29 +22,66 @@ fn main() -> Result<()> {
)
.unwrap();
- let args = Arguments::parse();
-
- switch_to_user(&args.sys_user)?;
+ let mail_storage = MailStorage::default();
- let path = args.maildir_path.join(args.mail_user);
+ let myinterface = de_jmhoffmann_jwebmail_mailstorage::new(Box::new(mail_storage));
+ let service = VarlinkService::new(
+ "de.jmhoffmann.jwebmail.mail-storage.varlink",
+ "jwebmails storage service",
+ "1.0.1",
+ "https://fehcom.de/cgit/jwebmail2/",
+ vec![Box::new(myinterface)],
+ );
- stdout().write_all(b"OPEN\n")?;
- stdout().flush()?;
- let mut req = Vec::with_capacity(2048);
- stdin().read_to_end(&mut req)?;
-
- let res = match args.mode {
- Mode::Read => cmd::read(path, &req),
- Mode::Raw => cmd::raw(path, &req),
- Mode::List => cmd::list(path, &req),
- Mode::Folders => cmd::folders(path, &req),
- Mode::Count => cmd::count(path, &req),
- //Mode::Search => cmd::search(&path, &req),
- Mode::Move => cmd::move_mail(path, &req),
- Mode::Remove => cmd::remove(path, &req),
- Mode::AddFolder => cmd::add_folder(path, &req),
- _ => todo!(),
- }?;
+ if let Ok(listen_fds) = std::env::var("LISTEN_FDS") {
+ let lfds = u8::from_str(&listen_fds).expect("env variable `LISTEN_FDS` must be an integer");
+ if lfds < 1 {
+ log::error!("No file descriptor to receive commands from!");
+ return;
+ } else if lfds == 1 {
+ let uds = unsafe { UnixStream::from_raw_fd(3) };
+ accept_con(&service, uds);
+ } else {
+ let listen_fdnames = std::env::var("LISTEN_FDNAMES")
+ .expect("when multiple `LISTEN_FDS` are available `LISTEN_FDNAMES` must be set");
+ listen_fdnames
+ .split(':')
+ .enumerate()
+ .filter(|(_, name)| *name == "varlink")
+ .for_each(|(i, _)| {
+ accept_con(&service, unsafe { UnixStream::from_raw_fd(3 + i as i32) })
+ });
+ }
+ } else {
+ log::error!("No file descriptor to receive commands from!");
+ }
+}
- stdout().write_all(&res).map_err(|e| e.into())
+/// mostly a copy from varlink::listen
+fn accept_con(service: &VarlinkService, mut uds: UnixStream) {
+ let (r, mut w) = uds.split().unwrap();
+ let mut br = BufReader::new(r);
+ let mut iface: Option<String> = None;
+ loop {
+ match service.handle(&mut br, &mut w, iface.clone()) {
+ Ok((_, i)) => {
+ iface = i;
+ match br.fill_buf() {
+ Err(_) => break,
+ Ok(buf) if buf.is_empty() => break,
+ _ => {}
+ }
+ }
+ Err(err) => {
+ match err.kind() {
+ ErrorKind::ConnectionClosed | ErrorKind::SerdeJsonDe(_) => {}
+ _ => {
+ eprintln!("Worker error: {:?}", err);
+ }
+ }
+ let _ = uds.shutdown(Shutdown::Both);
+ break;
+ }
+ }
+ }
}
diff --git a/src/pb3/mod.rs b/src/pb3/mod.rs
deleted file mode 100644
index 55a7aae..0000000
--- a/src/pb3/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-// @generated
-
-pub mod jwebmail;
diff --git a/src/rfc822.rs b/src/rfc822.rs
index 658b167..a8fa5c3 100644
--- a/src/rfc822.rs
+++ b/src/rfc822.rs
@@ -1,14 +1,15 @@
use chrono::DateTime;
-use mailparse::{addrparse_header, body::Body, dateparse, DispositionType, ParsedMail};
-
-use crate::error::{Error, Result};
-use crate::pb3::jwebmail::mimeheader::ContentDisposition::*;
-use crate::pb3::jwebmail::{
- mail_body::Multipart, mail_header::MailAddr, ListMailHeader, MIMEHeader, MIMEPart, Mail,
- MailBody, MailHeader,
+use maildir::MailEntryError;
+use mailparse::{
+ addrparse_header, body::Body, dateparse, DispositionType, MailParseError, ParsedMail,
};
-fn parse_mail_addrs(inp: &mailparse::MailHeader) -> Result<Vec<MailAddr>> {
+use crate::de_jmhoffmann_jwebmail_mailstorage::MIMEHeader_content_dispo as CD;
+use crate::de_jmhoffmann_jwebmail_mailstorage::*;
+
+fn parse_mail_addrs(
+ inp: &mailparse::MailHeader,
+) -> std::result::Result<Vec<MailAddr>, MailParseError> {
let mut mal = addrparse_header(inp)?;
Ok(mal
@@ -17,17 +18,16 @@ fn parse_mail_addrs(inp: &mailparse::MailHeader) -> Result<Vec<MailAddr>> {
mailparse::MailAddr::Group(mut g) => g
.addrs
.drain(..)
- .map(|s| {
- let mut r = MailAddr::new();
- r.name = Some(s.display_name.unwrap_or_default());
- r.address = s.addr;
- r
+ .map(|s| MailAddr {
+ name: Some(s.display_name.unwrap_or_default()),
+ address: s.addr,
})
.collect(),
mailparse::MailAddr::Single(s) => {
- let mut addr = MailAddr::new();
- addr.name = Some(s.display_name.unwrap_or_default());
- addr.address = s.addr;
+ let addr = MailAddr {
+ name: Some(s.display_name.unwrap_or_default()),
+ address: s.addr,
+ };
vec![addr]
}
})
@@ -44,43 +44,44 @@ fn get_received(me: &mut maildir::MailEntry) -> i64 {
})
}
-pub fn me_to_lmh(mut me: maildir::MailEntry) -> Result<ListMailHeader> {
- let mut lmh = ListMailHeader::new();
- lmh.byte_size = me.path().metadata()?.len();
- lmh.unread = !me.is_seen();
- lmh.rec_date = DateTime::from_timestamp(get_received(&mut me), 0)
- .unwrap()
- .to_rfc3339();
- lmh.mid = me.id().to_owned();
- lmh.header = Some(parse_mail_header(&me.parsed()?)?).into();
+pub fn me_to_lmh(
+ mut me: maildir::MailEntry,
+) -> std::result::Result<ListMailHeader, MailEntryError> {
+ let lmh = ListMailHeader {
+ byte_size: me.path().metadata()?.len() as i64,
+ unread: !me.is_seen(),
+ rec_date: DateTime::from_timestamp(get_received(&mut me), 0)
+ .unwrap()
+ .to_rfc3339(),
+ mid: me.id().to_owned(),
+ header: parse_mail_header(&me.parsed()?)?,
+ };
Ok(lmh)
}
-pub fn parse_mail_content(v: &ParsedMail) -> Result<MIMEHeader> {
- let mut c = MIMEHeader::new();
-
- {
- let mut val = v.ctype.mimetype.clone();
- if let Some(i) = val.find(';') {
- val.truncate(i);
- }
- let j = val.find('/').unwrap();
- c.subtype = val.split_off(j + 1);
- val.pop();
- c.maintype = val;
+pub fn parse_mail_content(v: &ParsedMail) -> MIMEHeader {
+ let mut val = v.ctype.mimetype.clone();
+ if let Some(i) = val.find(';') {
+ val.truncate(i);
}
+ let j = val.find('/').unwrap();
+ let subtype = val.split_off(j + 1);
+ val.pop();
+ let maintype = val;
+
+ let mut file_name = None;
- match v.get_content_disposition().disposition {
- DispositionType::Inline => c.contentdispo = CONTENT_DISPOSITION_INLINE.into(),
+ let cd = match v.get_content_disposition().disposition {
+ DispositionType::Inline => CD::inline,
DispositionType::Attachment => {
- c.contentdispo = CONTENT_DISPOSITION_ATTACHMENT.into();
if let Some(fname) = v.get_content_disposition().params.remove("filename") {
- c.file_name = Some(fname);
+ file_name = Some(fname);
}
+ CD::attachment
}
- _ => {}
- }
+ _ => CD::none,
+ };
for h in &v.headers {
let mut key = h.get_key();
@@ -89,29 +90,44 @@ pub fn parse_mail_content(v: &ParsedMail) -> Result<MIMEHeader> {
key.make_ascii_lowercase();
if key == "filename" {
- c.file_name = Some(val);
+ file_name = Some(val);
}
}
- Ok(c)
+ MIMEHeader {
+ mime_type: MIMEHeader_mime_type {
+ main_type: maintype,
+ sub_type: subtype,
+ },
+ content_dispo: cd,
+ file_name,
+ }
}
-fn parse_mail_header(pm: &ParsedMail) -> Result<MailHeader> {
+fn parse_mail_header(pm: &ParsedMail) -> std::result::Result<MailHeader, MailParseError> {
let v = &pm.headers;
- let mut mh = MailHeader::new();
- let mut mimeh = MIMEHeader::new();
-
- {
- let mut val = pm.ctype.mimetype.clone();
- if let Some(i) = val.find(';') {
- val.truncate(i);
- }
- let j = val.find('/').unwrap();
- mimeh.subtype = val.split_off(j + 1);
- val.pop();
- mimeh.maintype = val;
+ let mut val = pm.ctype.mimetype.clone();
+ if let Some(i) = val.find(';') {
+ val.truncate(i);
}
+ let j = val.find('/').unwrap();
+ let sub_type = val.split_off(j + 1);
+ val.pop();
+ let main_type = val;
+
+ let mut file_name = None;
+ let mut content_dispo = CD::none;
+ let mut cc = vec![];
+ let mut bcc = vec![];
+ let mut comments = vec![];
+ let mut keywords = vec![];
+ let mut reply_to = vec![];
+ let mut written_from = vec![];
+ let mut send_to = vec![];
+ let mut subject = String::new();
+ let mut sender = None;
+ let mut send_date = String::new();
let mut key = String::new();
@@ -123,102 +139,130 @@ fn parse_mail_header(pm: &ParsedMail) -> Result<MailHeader> {
match key.as_str() {
"date" => {
- mh.send_date = DateTime::from_timestamp(dateparse(&val)?, 0)
+ send_date = DateTime::from_timestamp(dateparse(&val)?, 0)
.unwrap()
.to_rfc3339()
}
"from" => {
- if !mh.written_from.is_empty() {
- return Err(Error::SortOrder("from already set".into()));
+ if !written_from.is_empty() {
+ const FROM_MULTIPLE_TIMES_ERROR: &str = "from already set";
+ return Err(MailParseError::Generic(FROM_MULTIPLE_TIMES_ERROR));
}
- mh.written_from = parse_mail_addrs(y)?
+ written_from = parse_mail_addrs(y)?
}
- "sender" => mh.sender = parse_mail_addrs(y)?.drain(0..1).next().into(),
- "reply-to" => mh.reply_to = parse_mail_addrs(y)?,
- "to" => mh.send_to = parse_mail_addrs(y)?,
- "cc" => mh.cc = parse_mail_addrs(y)?,
- "bcc" => mh.bcc = parse_mail_addrs(y)?,
+ "sender" => sender = parse_mail_addrs(y)?.drain(0..1).next(),
+ "reply-to" => reply_to = parse_mail_addrs(y)?,
+ "to" => send_to = parse_mail_addrs(y)?,
+ "cc" => cc = parse_mail_addrs(y)?,
+ "bcc" => bcc = parse_mail_addrs(y)?,
"subject" => {
- mh.subject = val;
+ subject = val;
}
"comments" => {
- mh.comments.push(val);
+ comments.push(val);
}
"keywords" => {
- mh.keywords.push(val);
+ keywords.push(val);
}
"mime-version" => {
strip_comments(&mut val);
if val.trim() != "1.0" {
- return Err(Error::MailEntryError(maildir::MailEntryError::DateError(
- "unknown mime version",
- )));
+ const UNKNOWN_MIME_VERSION_ERROR: &str = "unknown mime version";
+ return Err(MailParseError::Generic(UNKNOWN_MIME_VERSION_ERROR));
}
}
"content-disposition" => {
let cd = val.to_ascii_lowercase();
match cd.as_ref() {
- "inline" => mimeh.contentdispo = CONTENT_DISPOSITION_INLINE.into(),
- "attachment" => mimeh.contentdispo = CONTENT_DISPOSITION_ATTACHMENT.into(),
+ "inline" => content_dispo = CD::inline,
+ "attachment" => content_dispo = CD::attachment,
_ => {}
};
}
"filename" => {
- mimeh.file_name = Some(val);
+ file_name = Some(val);
}
_ => {}
};
key.clear();
}
- mh.mime = Some(mimeh).into();
- Ok(mh)
+ Ok(MailHeader {
+ written_from,
+ sender,
+ send_to,
+ cc,
+ bcc,
+ reply_to,
+ subject,
+ send_date,
+ keywords,
+ comments,
+ mime: MIMEHeader {
+ mime_type: MIMEHeader_mime_type {
+ main_type,
+ sub_type,
+ },
+ content_dispo,
+ file_name,
+ },
+ })
}
-fn parse_mail_body(pm: &ParsedMail) -> Result<MailBody> {
+fn parse_mail_body(pm: &ParsedMail) -> std::result::Result<MailBody, MailParseError> {
let body = if pm.ctype.mimetype.starts_with("message/") {
- let mut mb = MailBody::new();
- mb.set_mail(parsed_mail_to_mail(mailparse::parse_mail(
- pm.get_body()?.as_ref(),
- )?)?);
- mb
+ MailBody {
+ mail: Some(Box::new(parsed_mail_to_mail(mailparse::parse_mail(
+ pm.get_body()?.as_ref(),
+ )?)?)),
+ discrete: None,
+ multipart: None,
+ }
} else if pm.subparts.is_empty() && pm.ctype.mimetype.starts_with("text/") {
- let mut mb = MailBody::new();
- mb.set_discrete(pm.get_body()?);
- mb
+ MailBody {
+ discrete: Some(pm.get_body()?),
+ multipart: None,
+ mail: None,
+ }
} else if pm.subparts.is_empty() {
- let b = match pm.get_body_encoded() {
- Body::Base64(eb) => {
- let db = eb.get_raw();
- if db.len() < 512 * 1024 {
- String::from_utf8_lossy(db).into_owned()
- } else {
- String::new()
+ MailBody {
+ discrete: Some(match pm.get_body_encoded() {
+ Body::Base64(eb) => {
+ let db = eb.get_raw();
+ if db.len() < 512 * 1024 {
+ String::from_utf8_lossy(db).into_owned()
+ } else {
+ String::new()
+ }
}
- }
- Body::SevenBit(eb) => eb.get_as_string()?,
- Body::QuotedPrintable(eb) => eb.get_decoded_as_string()?,
- _ => todo!(),
- };
- let mut mb = MailBody::new();
- mb.set_discrete(b);
- mb
+ Body::SevenBit(eb) => eb.get_as_string()?,
+ Body::QuotedPrintable(eb) => eb.get_decoded_as_string()?,
+ _ => todo!(),
+ }),
+ multipart: None,
+ mail: None,
+ }
} else {
- let mut mb = MailBody::new();
- let mut mp = Multipart::new();
- mp.parts = pm
- .subparts
- .iter()
- .map(|part| -> Result<MIMEPart> {
- let mut mp = MIMEPart::new();
- mp.mime_header = Some(parse_mail_content(part)?).into();
- mp.body = Some(parse_mail_body(part)?).into();
- Ok(mp)
- })
- .filter_map(|p| p.ok())
- .collect();
- mb.set_multipart(mp);
- mb
+ let mp = Multipart {
+ parts: pm
+ .subparts
+ .iter()
+ .map(|part| -> std::result::Result<MIMEPart, MailParseError> {
+ Ok(MIMEPart {
+ mime_header: parse_mail_content(part),
+ body: parse_mail_body(part)?,
+ })
+ })
+ .filter_map(|p| p.ok())
+ .collect(),
+ preamble: None,
+ epilogue: None,
+ };
+ MailBody {
+ multipart: Some(mp),
+ discrete: None,
+ mail: None,
+ }
};
Ok(body)
}
@@ -316,86 +360,9 @@ fn strip_comments(s: &mut String) {
}
}
-pub fn parsed_mail_to_mail(pm: ParsedMail) -> Result<Mail> {
- let mut m = Mail::new();
- m.head = Some(parse_mail_header(&pm)?).into();
- m.body = Some(parse_mail_body(&pm)?).into();
-
- Ok(m)
-}
-
-/*
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn comment() {
- let mut x = r#"(this is ((some) text)) a "some text with (comment \" in) quotes)(" (example) \( included) (xx)b()"#.to_owned();
- strip_comments(&mut x);
- assert_eq!(
- &x,
- r#" a "some text with (comment \" in) quotes)(" \( included) b"#
- );
- }
-
- #[test]
- fn unclosed_comment() {
- let mut x = "(this is (some text) example b".to_owned();
- strip_comments(&mut x);
- assert_eq!(&x, "(this is example b");
- }
-
- #[test]
- fn find_first_pair() {
- let mut r = find_pair(0, "abc def");
- assert_eq!(r, None);
-
- r = find_pair(0, "abc ( def");
- assert_eq!(r, None);
-
- r = find_pair(0, "abc ) def");
- assert_eq!(r, None);
-
- let s = "(abc) def";
- if let Some(i) = find_pair(0, s) {
- assert_eq!(i, 0..5);
- assert_eq!(&s[i], "(abc)");
- } else {
- assert!(false, "Got None expected Some!");
- }
-
- let s = "abc (def) ghi";
- if let Some(i) = find_pair(0, s) {
- assert_eq!(i, 4..9);
- assert_eq!(&s[i], "(def)");
- } else {
- assert!(false, "Got None expected Some!");
- }
-
- let s = "(abc (def) ghi";
- if let Some(i) = find_pair(0, s) {
- assert_eq!(i, 5..10);
- assert_eq!(&s[i], "(def)");
- } else {
- assert!(false, "Got None expected Some!");
- }
-
- let s = "abc ((def) ghi)";
- if let Some(i) = find_pair(0, s) {
- assert_eq!(i, 4..15);
- assert_eq!(&s[i], "((def) ghi)");
- } else {
- assert!(false, "Got None expected Some!");
- }
-
- let s = r#" a "some text with (comment \" in) quotes)(" (example)"#;
- if let Some(i) = find_pair(0, s) {
- assert_eq!(i, 45..54);
- assert_eq!(&s[i], "(example)");
- } else {
- assert!(false, "Got None expected Some!");
- }
- }
+pub fn parsed_mail_to_mail(pm: ParsedMail) -> std::result::Result<Mail, MailParseError> {
+ Ok(Mail {
+ head: parse_mail_header(&pm)?,
+ body: parse_mail_body(&pm)?,
+ })
}
-*/