rss2email_lib/email/
mail_cmd.rs1use super::{email_provider::EmailProvider, error::EmailError};
4
5#[derive(Default, Debug, Clone, Copy)]
6pub struct MailCommand {}
7
8impl EmailProvider for MailCommand {
9 fn send_email(
10 &self,
11 from_address: &str,
12 recipient_addresses: Vec<&str>,
13 subject: &str,
14 contents: &str,
15 ) -> Result<(), EmailError> {
16 send_email(from_address, &recipient_addresses, subject, contents)
17 }
18}
19
20#[cfg(not(target_os = "windows"))]
21fn send_email(
22 from_address: &str,
23 recipient_addresses: &[&str],
24 subject: &str,
25 contents: &str,
26) -> Result<(), EmailError> {
27 use crate::info;
28 use std::{fs::File, io::Write, process::Command};
29
30 const TEMPORARY_FILE_NAME: &str = "/tmp/rss2-email.txt";
31
32 let mut file = File::create(TEMPORARY_FILE_NAME).expect("Can't create temporary file");
33 file
34 .write_all(contents.as_bytes())
35 .map_err(|_e| EmailError::Io("Failed to write temporary email file".to_owned()))?;
36
37 let recipients = recipient_addresses.join(",");
38 let mail_command =
39 format!("mail -s \"{subject}\" \"{recipients}\" -aFrom:{from_address} < {TEMPORARY_FILE_NAME}");
40
41 let mut mail_sender = Command::new("sh")
42 .args(["-c", &mail_command])
43 .spawn()
44 .map_err(|_e| {
45 EmailError::Other("Could not start mail command, is it installed and configured?".to_owned())
46 })?;
47
48 let exit_status = mail_sender.wait().expect("Mail command failed");
49 info!("Mail command finished with status {exit_status}");
50
51 match std::fs::remove_file(TEMPORARY_FILE_NAME) {
52 Ok(()) => Ok(()),
53 Err(e) => Err(EmailError::Io(format!(
54 "Unable to delete {TEMPORARY_FILE_NAME} for error: {e}"
55 ))),
56 }
57}
58
59#[cfg(target_os = "windows")]
60fn send_email(
61 _from_address: &str,
62 _recipient_addresses: &[&str],
63 _subject: &str,
64 _contents: &str,
65) -> Result<(), EmailError> {
66 Err(EmailError::Config(
67 "No known mail/sendmail/smtp command for Windows OS".to_owned(),
68 ))
69}