summaryrefslogtreecommitdiffstats
path: root/mail/examples/send_mail/cli.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mail/examples/send_mail/cli.rs')
-rw-r--r--mail/examples/send_mail/cli.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/mail/examples/send_mail/cli.rs b/mail/examples/send_mail/cli.rs
new file mode 100644
index 0000000..e2c14d6
--- /dev/null
+++ b/mail/examples/send_mail/cli.rs
@@ -0,0 +1,212 @@
+use std::io::{
+ self,
+ StdinLock, StdoutLock,
+ BufReader, BufRead,
+ Write
+};
+use std::str::FromStr;
+
+use rpassword::read_password_with_reader;
+
+
+use mail::{
+ Mailbox, Email, HeaderTryFrom,
+ smtp::misc::Domain,
+ header_components::{Phrase, Unstructured}
+};
+
+
+pub struct ClDialog<'a> {
+ stdin: BufReader<StdinLock<'a>>,
+ stdout: StdoutLock<'a>
+}
+
+pub fn with_dialog<FN, R>(func: FN) -> R
+ where FN: for<'a> FnOnce(ClDialog<'a>) -> R
+{
+ let stdin = io::stdin();
+ let stdout = io::stdout();
+ let dialog = ClDialog::new(stdin.lock(), stdout.lock());
+ func(dialog)
+}
+
+impl<'a> ClDialog<'a> {
+
+ pub fn new(stdin: StdinLock<'a>, stdout: StdoutLock<'a>) -> Self {
+ ClDialog { stdin: BufReader::new(stdin), stdout }
+ }
+
+ pub fn stdout(&mut self) -> &mut StdoutLock<'a> {
+ &mut self.stdout
+ }
+
+ // pub fn stdin(&mut self) -> &mut BufReader<StdinLock<'a>> {
+ // &mut self.stdin
+ // }
+
+ pub fn prompt(&mut self, prompt: &str) -> Result<(), io::Error> {
+ write!(self.stdout, "{}: ", prompt)?;
+ self.stdout.flush()
+ }
+
+ pub fn read_line(&mut self) -> Result<String, io::Error> {
+ let mut line = String::new();
+ self.stdin.read_line(&mut line)?;
+ Ok(line)
+ }
+
+ pub fn read_email(&mut self) -> Result<Email, io::Error> {
+ loop {
+ let line = self.read_line()?;
+ let line = line.trim();
+ if let Ok(email) = Email::try_from(line) {
+ return Ok(email);
+ }
+ self.prompt("[syntax error] email")?;
+ }
+ }
+
+ pub fn read_opt_phrase(&mut self) -> Result<Option<Phrase>, io::Error> {
+ loop {
+ let line = self.read_line()?;
+ let line = line.trim();
+ if line.is_empty() {
+ return Ok(None);
+ }
+ if let Ok(phrase) = Phrase::try_from(line) {
+ return Ok(Some(phrase));
+ }
+ self.prompt("[syntax error] optional phrase")?;
+ }
+ }
+
+ pub fn read_mailbox(&mut self) -> Result<Mailbox, io::Error> {
+ self.prompt("- Email Address")?;
+ let email = self.read_email()?;
+ self.prompt("- Display Name")?;
+ let display_name = self.read_opt_phrase()?;
+ Ok(Mailbox::from((display_name, email)))
+ }
+
+ pub fn read_password(&mut self) -> Result<String, io::Error> {
+ read_password_with_reader(Some(&mut self.stdin))
+ }
+
+
+ pub fn read_mail_text_body(&mut self) -> Result<String, io::Error> {
+ let mut buf = String::new();
+ while self.stdin.read_line(&mut buf)? != 0 {
+ if !buf.ends_with("\r\n") {
+ if buf.ends_with("\r") {
+ buf.push('\n')
+ } else if buf.ends_with("\n") {
+ let n_idx = buf.len() - 1;
+ buf.insert(n_idx, '\r');
+ } else {
+ buf.push_str("\r\n");
+ }
+ }
+ }
+ Ok(buf)
+ }
+
+ // pub fn read_ip_addr(&mut self) -> Result<IpAddr, io::Error> {
+ // loop {
+ // let line = self.read_line()?;
+ // let line = line.trim();
+ // if let Ok(ip_addr) = IpAddr::from_str(line) {
+ // return Ok(ip_addr)
+ // }
+ // self.prompt("[syntax error] address")?;
+ // }
+ // }
+
+ pub fn read_domain(&mut self) -> Result<Domain, io::Error> {
+ loop {
+ let line = self.read_line()?;
+ let line = line.trim();
+ if let Ok(domain) = Domain::from_str(line) {
+ return Ok(domain);
+ }
+ self.prompt("[syntax error] domain")?;
+ }
+ }
+
+ pub fn read_auth_data(&mut self) -> Result<AuthData, io::Error> {
+ self.prompt("- username")?;
+ let username = self.read_line()?.trim().to_owned();
+ self.prompt("- password")?;
+ let password = self.read_password()?;
+ Ok(AuthData { username, password })
+ }
+
+ pub fn read_msa_info(&mut self) -> Result<MsaInfo, io::Error> {
+ writeln!(self.stdout, "Mail Submission Agent (MSA) Information:")?;
+ self.prompt("MSA domain name")?;
+ let domain = self.read_domain()?;
+ writeln!(self.stdout, "[using port 587 and STARTTLS]")?;
+ writeln!(self.stdout, "MSA Authentication data")?;
+ let auth = self.read_auth_data()?;
+ Ok(MsaInfo { domain, auth })
+ }
+
+ pub fn read_simple_mail(&mut self) -> Result<SimpleMail, io::Error> {
+ writeln!(self.stdout, "From/Sender:")?;
+ let from = self.read_mailbox()?;
+ writeln!(self.stdout, "To/Recipient")?;
+ let to = self.read_mailbox()?;
+ self.prompt("Subject")?;
+ let subject = Unstructured
+ ::try_from(self.read_line()?.trim())
+ .unwrap();
+
+ writeln!(self.stdout, "Utf-8 text body [end with Ctrl-D]:")?;
+ self.stdout.flush()?;
+ let text_body = self.read_mail_text_body()?;
+ Ok(SimpleMail {
+ from,
+ to,
+ subject,
+ text_body
+ })
+ }
+
+ pub fn read_yn(&mut self) -> Result<bool, io::Error> {
+ loop {
+ let line = self.read_line()?;
+ let line = line.trim();
+ let valid =
+ match line {
+ "y" => true,
+ "n" => false,
+ _ => continue
+ };
+ return Ok(valid);
+ }
+ }
+}
+
+
+/// POD
+#[derive(Debug)]
+pub struct AuthData {
+ pub username: String,
+ pub password: String,
+}
+
+/// POD
+#[derive(Debug)]
+pub struct MsaInfo {
+ pub domain: Domain,
+ pub auth: AuthData
+}
+
+/// POD
+#[derive(Debug)]
+pub struct SimpleMail {
+ pub from: Mailbox,
+ pub to: Mailbox,
+ pub subject: Unstructured,
+ pub text_body: String,
+}
+