11use std:: collections:: HashMap ;
22use std:: net:: IpAddr ;
3- use log:: { debug, warn} ;
3+ use std:: ops:: Not ;
4+ use std:: sync:: { Arc , Mutex , RwLock } ;
5+ use std:: time:: Duration ;
6+ use lettre:: { Address , Message , SmtpTransport , Transport } ;
7+ use lettre:: message:: header:: ContentType ;
8+ use lettre:: message:: Mailbox ;
9+ use lettre:: transport:: smtp:: authentication:: Credentials ;
10+ use lettre:: transport:: smtp:: client:: { Tls , TlsParameters , TlsParametersBuilder } ;
11+ use log:: { debug, error, trace, warn} ;
12+ use rocket:: yansi:: Paint ;
13+ use crate :: config:: { Config , EmailEncryption } ;
414use crate :: printer:: Printer ;
15+ use std:: fmt:: Write ;
16+
17+
18+
19+ static PROGRESS_CHECK_INTERVAL : Duration = Duration :: from_secs ( 60 ) ;
20+
21+ pub type PrinterManager = Arc < Mutex < Printers > > ;
22+
23+ #[ derive( Debug ) ]
24+ enum NotificationType {
25+ PrintComplete
26+ }
27+
28+ impl NotificationType {
29+ pub fn get_subject ( & self , printer : & Printer ) -> String {
30+ match self {
31+ NotificationType :: PrintComplete => format ! ( "Print complete on {}" , printer. name( ) ) ,
32+ _ => printer. name ( ) . to_string ( )
33+ }
34+ }
35+
36+ pub fn get_message ( & self , printer : & Printer ) -> String {
37+ match self {
38+ NotificationType :: PrintComplete => {
39+ let status = printer. get_status ( ) . unwrap ( ) ;
40+ let mut str = String :: new ( ) ;
41+ write ! ( str , "File: {}\n " , status. current_file. unwrap_or( "unknown" . to_string( ) ) ) . unwrap ( ) ;
42+ write ! ( str , "IP: {}\n " , printer. ip( ) ) . unwrap ( ) ;
43+ str
44+ // TODO: send an image?
45+ }
46+ _ => "" . to_string ( )
47+ }
48+ }
49+ }
550
651pub struct Printers {
7- printers : HashMap < String , Printer >
52+ printers : HashMap < String , Printer > ,
53+ config : Arc < Config > ,
54+ mailer : Option < SmtpTransport > ,
55+
56+ notification_sent : HashMap < String , String > // If printer (key) has value, then a print done notification has been submitted for file (value)
857}
958
1059impl Printers {
11- pub fn new ( ) -> Printers {
12- Self {
13- printers : HashMap :: new ( )
60+ pub fn new ( config : Arc < Config > ) -> Printers {
61+ let mut s = Self {
62+ printers : HashMap :: new ( ) ,
63+ config,
64+ mailer : None ,
65+ notification_sent : HashMap :: new ( )
66+ } ;
67+ if let Some ( smtp) = & s. config . smtp {
68+ if smtp. port <= 0 {
69+ error ! ( "SMTP: Smtp port is invalid, smtp support not enabled" ) ;
70+ } else if smtp. user == "" {
71+ error ! ( "SMTP: Smtp user is empty, smtp support not enabled" ) ;
72+ } else if smtp. host == "" {
73+ error ! ( "SMTP: Smtp host is empty, smtp support not enabled" ) ;
74+ } else {
75+ let builder = match smtp. encryption {
76+ EmailEncryption :: None => SmtpTransport :: builder_dangerous ( & smtp. host ) ,
77+ EmailEncryption :: StartTLS => SmtpTransport :: starttls_relay ( & smtp. host ) . unwrap ( ) ,
78+ EmailEncryption :: TLS => SmtpTransport :: relay ( & smtp. host ) . unwrap ( )
79+ } ;
80+ s. mailer = Some ( builder
81+ . port ( smtp. port )
82+ . credentials ( Credentials :: new ( smtp. user . to_string ( ) , smtp. password . to_string ( ) ) )
83+ . build ( )
84+ )
85+ }
86+ }
87+ s
88+ }
89+
90+ pub fn start_watch_thread ( manager : PrinterManager ) {
91+ debug ! ( "Starting watch thread at interval {:?}" , PROGRESS_CHECK_INTERVAL ) ;
92+ std:: thread:: spawn ( move || {
93+ std:: thread:: sleep ( PROGRESS_CHECK_INTERVAL ) ;
94+ loop {
95+ trace ! ( "Checking printers" ) ;
96+ let mut lock = manager. lock ( ) . unwrap ( ) ;
97+ let mut has_sent = lock. notification_sent . clone ( ) ;
98+ for ( id, printer) in & lock. printers {
99+ if let Ok ( prog) = printer. get_progress ( ) {
100+ // Check if progress is 100%
101+ if prog. layer . 0 >= prog. layer . 1 {
102+ // Get current file from status
103+ let status = printer. get_status ( ) . unwrap ( ) ;
104+ if status. current_file . is_none ( ) {
105+ continue ;
106+ }
107+ // Check if we have already sent a notification
108+ let current_file = status. current_file . unwrap ( ) ;
109+ let has_notified = lock. has_notified ( id, & current_file) ;
110+
111+ if !has_notified {
112+ lock. send_notification ( printer, NotificationType :: PrintComplete ) ;
113+ has_sent. insert ( id. clone ( ) , current_file) ;
114+ }
115+ }
116+ }
117+ }
118+ lock. notification_sent = has_sent;
119+ drop ( lock) ;
120+ std:: thread:: sleep ( PROGRESS_CHECK_INTERVAL ) ;
121+ }
122+ } ) ;
123+ }
124+
125+ fn has_notified ( & self , printer_id : & str , file_name : & str ) -> bool {
126+ !self . notification_sent . contains_key ( printer_id) || self . notification_sent . get ( printer_id) . unwrap ( ) != file_name
127+ }
128+
129+ fn send_notification ( & self , printer : & Printer , notification_type : NotificationType ) {
130+ debug ! ( "Sending notification: {:?}" , notification_type) ;
131+ let Some ( notifications) = & self . config . notifications else { return ; } ;
132+ if let Some ( mailer) = & self . mailer {
133+ let user = & self . config . smtp . as_ref ( ) . unwrap ( ) . user ;
134+ trace ! ( "smtp configured, sending from {}" , user) ;
135+ match user. parse ( ) {
136+ Ok ( from_addr) => {
137+ let mut builder = Message :: builder ( )
138+ . from ( Mailbox :: new ( None , from_addr) )
139+ . subject ( notification_type. get_subject ( printer) )
140+ . header ( ContentType :: TEXT_PLAIN ) ;
141+ for email in notifications. emails . iter ( ) . flatten ( ) {
142+ builder = builder. bcc ( email. parse ( ) . unwrap ( ) )
143+ }
144+ let email = builder. body ( notification_type. get_message ( printer) ) . unwrap ( ) ;
145+
146+ mailer. send ( & email) . unwrap ( ) ;
147+ trace ! ( "Sent notification {:?} for printer {}" , notification_type, printer) ;
148+ } ,
149+ Err ( e) => {
150+ error ! ( "Could not parse from address \" {}\" : {}" , user, e) ;
151+ }
152+ }
14153 }
15154 }
16155
@@ -24,10 +163,9 @@ impl Printers {
24163
25164 pub fn add_printer ( & mut self , id : String , ip : IpAddr ) {
26165 debug ! ( "adding printer {} with ip {}" , id, ip) ;
27- let mut printer = Printer :: new ( ip) ;
28- if printer. get_meta ( ) . is_none ( ) {
29- warn ! ( "printer {} failed to get meta:" , id) ;
30- }
166+ let mut printer = Printer :: new ( id. clone ( ) , ip) ;
167+ printer. get_meta ( ) ;
31168 self . printers . insert ( id, printer) ;
32169 }
33- }
170+ }
171+
0 commit comments