summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
21 files changed, 1821 insertions, 633 deletions
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)?,
+ })
}
-*/