2018-08-07 15:01:15 +03:00
/*
2020-02-04 15:52:12 +02:00
* meli
2018-08-07 15:01:15 +03:00
*
* Copyright 2017 - 2018 Manos Pitsidianakis
*
* This file is part of meli .
*
* meli is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* meli is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with meli . If not , see < http ://www.gnu.org/licenses/>.
* /
2018-07-18 10:42:52 +03:00
/*!
2019-03-14 12:19:25 +02:00
Notification handling components .
* /
2019-03-02 21:40:57 +02:00
use std ::process ::{ Command , Stdio } ;
2018-07-17 17:16:16 +03:00
2018-07-20 12:15:06 +03:00
use super ::* ;
2018-07-18 10:42:52 +03:00
2020-10-28 23:28:41 +02:00
#[ cfg(all(target_os = " linux " , feature = " dbus-notifications " )) ]
2020-09-10 20:31:12 +03:00
pub use dbus ::* ;
2018-07-17 17:16:16 +03:00
2020-10-28 23:28:41 +02:00
#[ cfg(all(target_os = " linux " , feature = " dbus-notifications " )) ]
2020-09-10 20:31:12 +03:00
mod dbus {
use super ::* ;
use crate ::types ::RateLimit ;
/// Passes notifications to the OS using Dbus
#[ derive(Debug) ]
pub struct DbusNotifications {
rate_limit : RateLimit ,
2018-08-11 18:00:21 +03:00
}
2020-09-10 20:31:12 +03:00
impl fmt ::Display for DbusNotifications {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
write! ( f , " " )
2020-01-15 12:36:31 +02:00
}
}
2020-09-10 20:31:12 +03:00
impl DbusNotifications {
2020-10-29 13:09:31 +02:00
pub fn new ( context : & Context ) -> Self {
2020-09-10 20:31:12 +03:00
DbusNotifications {
2020-10-29 13:09:31 +02:00
rate_limit : RateLimit ::new ( 1000 , 1000 , context . job_executor . clone ( ) ) ,
2020-01-15 12:36:31 +02:00
}
2020-09-10 20:31:12 +03:00
}
}
2020-01-15 12:36:31 +02:00
2020-09-10 20:31:12 +03:00
impl Component for DbusNotifications {
fn draw ( & mut self , _grid : & mut CellBuffer , _area : Area , _context : & mut Context ) { }
fn process_event ( & mut self , event : & mut UIEvent , context : & mut Context ) -> bool {
if ! context . settings . notifications . enable {
return false ;
2019-09-15 23:36:30 +03:00
}
2020-09-10 20:31:12 +03:00
if let UIEvent ::Notification ( ref title , ref body , ref kind ) = event {
if ! self . rate_limit . tick ( ) {
return false ;
}
let settings = & context . settings . notifications ;
let mut notification = notify_rust ::Notification ::new ( ) ;
notification
. appname ( " meli " )
. summary ( title . as_ref ( ) . map ( String ::as_str ) . unwrap_or ( " meli " ) )
. body ( & escape_str ( body ) ) ;
2020-09-13 15:23:14 +03:00
match * kind {
Some ( NotificationType ::NewMail ) = > {
notification . hint ( notify_rust ::Hint ::Category ( " email " . to_owned ( ) ) ) ;
notification . icon ( " mail-message-new " ) ;
notification . sound_name ( " message-new-email " ) ;
}
Some ( NotificationType ::SentMail ) = > {
notification . hint ( notify_rust ::Hint ::Category ( " email " . to_owned ( ) ) ) ;
notification . icon ( " mail-send " ) ;
notification . sound_name ( " message-sent-email " ) ;
}
Some ( NotificationType ::Saved ) = > {
notification . icon ( " document-save " ) ;
}
Some ( NotificationType ::Info ) = > {
notification . icon ( " dialog-information " ) ;
}
Some ( NotificationType ::Error ( melib ::ErrorKind ::Authentication ) ) = > {
notification . icon ( " dialog-password " ) ;
}
Some ( NotificationType ::Error ( melib ::ErrorKind ::Bug ) ) = > {
notification . icon ( " face-embarrassed " ) ;
}
Some ( NotificationType ::Error ( melib ::ErrorKind ::None ) )
| Some ( NotificationType ::Error ( melib ::ErrorKind ::External ) ) = > {
notification . icon ( " dialog-error " ) ;
}
Some ( NotificationType ::Error ( melib ::ErrorKind ::Network ) ) = > {
notification . icon ( " network-error " ) ;
}
Some ( NotificationType ::Error ( melib ::ErrorKind ::Timeout ) ) = > {
notification . icon ( " network-offline " ) ;
}
_ = > { }
2020-09-10 20:31:12 +03:00
}
if settings . play_sound . is_true ( ) {
if let Some ( ref sound_path ) = settings . sound_file {
notification . hint ( notify_rust ::Hint ::SoundFile ( sound_path . to_owned ( ) ) ) ;
}
2019-09-15 23:36:30 +03:00
} else {
2020-09-10 20:31:12 +03:00
notification . hint ( notify_rust ::Hint ::SuppressSound ( true ) ) ;
2019-09-15 23:36:30 +03:00
}
2020-09-10 20:31:12 +03:00
2020-09-12 21:06:50 +03:00
if let Err ( err ) = notification . show ( ) {
debug! ( " Could not show dbus notification: {:?} " , & err ) ;
melib ::log (
format! ( " Could not show dbus notification: {} " , err ) ,
melib ::ERROR ,
) ;
}
2019-09-15 23:36:30 +03:00
}
2020-09-10 20:31:12 +03:00
false
}
fn set_dirty ( & mut self , _value : bool ) { }
2019-09-15 23:35:30 +03:00
2020-09-10 20:31:12 +03:00
fn is_dirty ( & self ) -> bool {
false
2018-07-17 17:16:16 +03:00
}
2019-12-14 18:50:05 +02:00
2020-09-10 20:31:12 +03:00
fn id ( & self ) -> ComponentId {
ComponentId ::nil ( )
}
2019-04-10 22:01:02 +03:00
2020-09-10 20:31:12 +03:00
fn set_id ( & mut self , _id : ComponentId ) { }
2019-04-10 22:01:02 +03:00
}
2018-12-15 10:42:01 +02:00
2020-09-10 20:31:12 +03:00
fn escape_str ( s : & str ) -> String {
let mut ret : String = String ::with_capacity ( s . len ( ) ) ;
for c in s . chars ( ) {
match c {
'&' = > ret . push_str ( " & " ) ,
'<' = > ret . push_str ( " < " ) ,
'>' = > ret . push_str ( " > " ) ,
'\'' = > ret . push_str ( " ' " ) ,
'"' = > ret . push_str ( " " " ) ,
_ = > {
let i = c as u32 ;
2022-09-11 15:19:40 +03:00
if ( 0x1 ..= 0x8 ) . contains ( & i )
| | ( 0xb ..= 0xc ) . contains ( & i )
| | ( 0xe ..= 0x1f ) . contains ( & i )
| | ( 0x7f ..= 0x84 ) . contains ( & i )
| | ( 0x86 ..= 0x9f ) . contains ( & i )
2020-09-10 20:31:12 +03:00
{
2022-09-11 15:19:40 +03:00
use std ::fmt ::Write ;
let _ = write! ( ret , " &#{:x}%{:x}; " , i , i ) ;
2020-09-10 20:31:12 +03:00
} else {
ret . push ( c ) ;
}
2018-12-15 10:42:01 +02:00
}
2019-03-14 12:19:25 +02:00
}
2018-12-15 10:42:01 +02:00
}
2020-09-10 20:31:12 +03:00
ret
2018-12-15 10:42:01 +02:00
}
}
2020-09-10 20:31:12 +03:00
/// Passes notifications to a user defined shell command
2022-08-25 15:17:18 +03:00
#[ derive(Default, Debug) ]
2020-09-10 20:31:12 +03:00
pub struct NotificationCommand { }
2020-02-04 03:49:43 +02:00
2020-09-10 20:31:12 +03:00
impl NotificationCommand {
pub fn new ( ) -> Self {
NotificationCommand { }
2018-12-15 10:42:01 +02:00
}
}
2019-03-02 21:40:57 +02:00
2020-09-10 20:31:12 +03:00
impl fmt ::Display for NotificationCommand {
2019-03-02 21:40:57 +02:00
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
write! ( f , " " )
}
}
2020-09-10 20:31:12 +03:00
impl Component for NotificationCommand {
2019-03-02 21:40:57 +02:00
fn draw ( & mut self , _grid : & mut CellBuffer , _area : Area , _context : & mut Context ) { }
2020-09-10 20:31:12 +03:00
2019-03-02 21:40:57 +02:00
fn process_event ( & mut self , event : & mut UIEvent , context : & mut Context ) -> bool {
2019-09-15 23:35:30 +03:00
if let UIEvent ::Notification ( ref title , ref body , ref kind ) = event {
2021-01-13 22:34:19 +02:00
if context . settings . notifications . enable {
2022-08-28 17:34:53 +03:00
if * kind = = Some ( NotificationType ::NewMail ) {
if let Some ( ref path ) = context . settings . notifications . xbiff_file_path {
if let Err ( err ) = update_xbiff ( path ) {
debug! ( " Could not update xbiff file: {:?} " , & err ) ;
melib ::log ( format! ( " Could not update xbiff file: {} . " , err ) , ERROR ) ;
}
}
}
2021-01-13 22:34:19 +02:00
if let Some ( ref bin ) = context . settings . notifications . script {
match Command ::new ( bin )
. arg ( & kind . map ( | k | k . to_string ( ) ) . unwrap_or_default ( ) )
. arg ( title . as_ref ( ) . map ( String ::as_str ) . unwrap_or ( " meli " ) )
. arg ( body )
. stdin ( Stdio ::piped ( ) )
. stdout ( Stdio ::piped ( ) )
. spawn ( )
{
Ok ( child ) = > {
context . children . push ( child ) ;
}
Err ( err ) = > {
log (
2022-08-25 15:17:18 +03:00
format! ( " Could not run notification script: {} . " , err ) ,
2021-01-13 22:34:19 +02:00
ERROR ,
) ;
debug! ( " Could not run notification script: {:?} " , err ) ;
}
2020-03-01 17:56:58 +02:00
}
2022-06-04 17:43:48 +03:00
} else {
#[ cfg(target_os = " macos " ) ]
{
2022-07-03 13:32:01 +03:00
let applescript = format! ( " display notification \" {message} \" with title \" {title} \" subtitle \" {subtitle} \" " , message = body . replace ( '"' , " ' " ) , title = title . as_ref ( ) . map ( String ::as_str ) . unwrap_or ( " meli " ) . replace ( '"' , " ' " ) , subtitle = kind . map ( | k | k . to_string ( ) ) . unwrap_or_default ( ) . replace ( '"' , " ' " ) ) ;
2022-06-04 17:43:48 +03:00
match Command ::new ( " osascript " )
. arg ( " -e " )
. arg ( applescript )
. stdin ( Stdio ::piped ( ) )
. stdout ( Stdio ::piped ( ) )
. spawn ( )
{
Ok ( child ) = > {
context . children . push ( child ) ;
2022-08-28 17:34:53 +03:00
return false ;
2022-06-04 17:43:48 +03:00
}
Err ( err ) = > {
log (
2022-08-25 15:17:18 +03:00
format! ( " Could not run notification script: {} . " , err ) ,
2022-06-04 17:43:48 +03:00
ERROR ,
) ;
debug! ( " Could not run notification script: {:?} " , err ) ;
}
}
}
2019-09-15 23:37:14 +03:00
2022-08-28 17:34:53 +03:00
context
. replies
. push_back ( UIEvent ::StatusEvent ( StatusEvent ::DisplayMessage ( format! (
" {title}{}{body} " ,
if title . is_some ( ) { " " } else { " " } ,
title = title . as_deref ( ) . unwrap_or_default ( ) ,
body = body ,
) ) ) ) ;
2019-09-15 23:37:14 +03:00
}
}
2019-03-02 21:40:57 +02:00
}
2022-08-28 17:34:53 +03:00
2019-03-02 21:40:57 +02:00
false
}
2019-04-10 22:01:02 +03:00
fn id ( & self ) -> ComponentId {
ComponentId ::nil ( )
}
2019-09-25 23:14:24 +03:00
fn is_dirty ( & self ) -> bool {
false
}
2019-12-14 18:50:05 +02:00
fn set_dirty ( & mut self , _value : bool ) { }
2019-04-10 22:01:02 +03:00
fn set_id ( & mut self , _id : ComponentId ) { }
2019-03-02 21:40:57 +02:00
}
2020-09-12 21:06:50 +03:00
fn update_xbiff ( path : & str ) -> Result < ( ) > {
let mut file = std ::fs ::OpenOptions ::new ( )
. append ( true ) /* writes will append to a file instead of overwriting previous contents */
. create ( true ) /* a new file will be created if the file does not yet already exist. */
. open ( path ) ? ;
if file . metadata ( ) ? . len ( ) > 128 {
file . set_len ( 0 ) ? ;
} else {
std ::io ::Write ::write_all ( & mut file , b " z " ) ? ;
}
Ok ( ( ) )
}