extern crate anyhow; extern crate clokwerk; extern crate lettre; extern crate lettre_email; extern crate log; extern crate my_internet_ip; extern crate native_tls; extern crate serde; extern crate simplelog; extern crate toml; use self::native_tls::{Protocol, TlsConnector}; use anyhow::{bail, Result}; use clokwerk::{Scheduler, TimeUnits}; use lettre::smtp::authentication::Credentials; use lettre::{ClientSecurity, ClientTlsParameters, SmtpClient, Transport}; use lettre_email::EmailBuilder; use log::{error, info}; use serde::Deserialize; use simplelog::{ConfigBuilder, LevelFilter, WriteLogger}; use std::fs::{self, OpenOptions}; use std::net::IpAddr; use std::path::Path; use std::{thread, time::Duration}; fn main() -> Result<()> { // Init log let logfile = OpenOptions::new() .append(true) .create(true) .open("log.txt") .unwrap(); WriteLogger::init( LevelFilter::Info, ConfigBuilder::new() .set_time_format_str("%d/%m/%Y %H:%M:%S") .set_time_to_local(true) .build(), logfile, ) .unwrap(); // Init scheduler let mut scheduler = Scheduler::new(); info!("Init"); scheduler.every(1.day()).at("06:00").run(|| { if let Err(e) = check() { error!("Error {}", e); } }); // Initial check if let Err(e) = check() { error!("Error {}", e); bail!("Cannot get initial IP"); } // Run loop { scheduler.run_pending(); thread::sleep(Duration::from_secs(60)); } } fn check() -> Result<()> { info!("Check"); match get() { Ok((old_ip, new_ip)) => { if old_ip != new_ip { let msg = format!("IP changed from {} to {}", old_ip, new_ip); info!("{}", msg); send_mail("New IP".to_owned(), msg)?; } } Err(err) => { let msg = format!("Cannot get IP ({})", err); error!("{}", msg); send_mail("Error on getting IP".to_owned(), msg)?; } } Ok(()) } fn get() -> Result<(IpAddr, IpAddr)> { // Get new IP let new_ip = match my_internet_ip::get() { Ok(ip) => ip, Err(e) => bail!("Could not get IP: {:?}", e), }; info!("Current IP is {}", new_ip); // Get old IP let path = Path::new("./old_ip.txt"); let old_ip = if path.exists() { fs::read_to_string(path)?.parse()? } else { new_ip.clone() }; info!("Old IP is {}", old_ip); // Write new IP fs::write(path, format!("{}", new_ip))?; Ok((old_ip, new_ip)) } fn send_mail(subject: String, body: String) -> Result<()> { info!("Send mail {}", subject); let config: Config = toml::from_str(&fs::read_to_string("config.toml")?)?; let email = EmailBuilder::new() .to(config.mail.from) .from(config.mail.to) .subject(subject) .text(body) .build()?; // Create transport let creds = Credentials::new(config.mail.username, config.mail.password); let mut tls_builder = TlsConnector::builder(); tls_builder.min_protocol_version(Some(Protocol::Sslv3)); let tls_parameters = ClientTlsParameters::new(config.mail.server.clone(), tls_builder.build().unwrap()); let mut mailer = SmtpClient::new( (config.mail.server, config.mail.port), ClientSecurity::Required(tls_parameters), )? .credentials(creds) .transport(); mailer.send(email.into())?; Ok(()) } #[derive(Debug, Deserialize)] struct Config { mail: MailConfig, } #[derive(Debug, Deserialize)] struct MailConfig { server: String, port: u16, username: String, password: String, from: (String, String), to: String, }