From 77a257892a2dcd4f7fe2ccb903bda18885eff364 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sat, 10 Apr 2021 14:28:20 +0100 Subject: [PATCH] Add some more code from pidgin-icq --- src/chat_info.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++ src/logging.rs | 121 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 src/chat_info.rs create mode 100644 src/logging.rs diff --git a/src/chat_info.rs b/src/chat_info.rs new file mode 100644 index 0000000..da51fb6 --- /dev/null +++ b/src/chat_info.rs @@ -0,0 +1,136 @@ +// This is a copy of https://github.com/Flared/purple-icq/blob/master/src/chat_info.rs + +use super::purple; +use lazy_static::lazy_static; +use std::ffi::CString; + +lazy_static! { + pub static ref SN: CString = CString::new("sn").unwrap(); + pub static ref SN_NAME: CString = CString::new("Chat ID").unwrap(); + pub static ref STAMP: CString = CString::new("stamp").unwrap(); + pub static ref TITLE: CString = CString::new("title").unwrap(); + pub static ref GROUP: CString = CString::new("group").unwrap(); + pub static ref STATE: CString = CString::new("state").unwrap(); +} + +#[derive(Debug, Clone)] +pub struct MemberRole(String); + +#[derive(Debug, Clone, Default)] +pub struct PartialChatInfo { + pub sn: String, + pub title: String, + pub group: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct ChatInfo { + pub stamp: Option, + pub group: Option, + pub sn: String, + pub title: String, + pub about: Option, + pub members_version: String, + pub info_version: String, + pub members: Vec, +} + +#[derive(Debug, Clone)] +pub struct ChatMember { + pub sn: String, + pub friendly_name: Option, + pub role: MemberRole, + pub last_seen: Option, + pub first_name: Option, + pub last_name: Option, +} + +#[derive(Debug, Clone)] +pub struct ChatInfoVersion { + pub members_version: String, + pub info_version: String, +} + +impl MemberRole { + pub fn as_flags(&self) -> purple::PurpleConvChatBuddyFlags { + match self.0.as_str() { + "admin" => purple::PurpleConvChatBuddyFlags::PURPLE_CBFLAGS_OP, + "readonly" => purple::PurpleConvChatBuddyFlags::PURPLE_CBFLAGS_NONE, + _ => purple::PurpleConvChatBuddyFlags::PURPLE_CBFLAGS_VOICE, + } + } +} + +impl PartialChatInfo { + pub fn from_hashtable(table: &purple::StrHashTable) -> Option { + Some(Self { + group: table.lookup(&GROUP).map(Into::into), + sn: table.lookup(&SN)?.into(), + title: table.lookup(&TITLE)?.into(), + }) + } + + pub fn as_hashtable(&self) -> purple::StrHashTable { + let mut table = purple::StrHashTable::default(); + table.insert(&SN, &self.sn); + if let Some(group) = &self.group { + table.insert(&GROUP, &group); + } + table.insert(&TITLE, &self.title); + table + } +} + +impl ChatInfo { + pub fn as_partial(&self) -> PartialChatInfo { + PartialChatInfo { + sn: self.sn.clone(), + title: self.title.clone(), + group: self.group.clone(), + } + } + + pub fn need_update(&self, new_version: &ChatInfoVersion) -> bool { + self.members_version < new_version.members_version + || self.info_version < new_version.info_version + } +} +/* +impl From for ChatInfo { + fn from(info: icq::client::GetChatInfoResponseData) -> Self { + Self { + sn: info.sn, + stamp: Some(info.stamp), + title: info.name, + members_version: info.members_version, + info_version: info.info_version, + about: info.about, + members: info + .members + .into_iter() + .map(|m| ChatMember { + sn: m.sn, + role: MemberRole(m.role), + last_seen: m.user_state.last_seen.and_then(|t| match t { + 0 => None, + t => Some(t), + }), + friendly_name: m.friendly, + first_name: m.anketa.first_name, + last_name: m.anketa.last_name, + }) + .collect(), + ..Default::default() + } + } +} + +impl From for ChatInfoVersion { + fn from(info: icq::client::events::HistDlgStateMChatState) -> Self { + Self { + members_version: info.members_version, + info_version: info.info_version, + } + } +} +*/ diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..76ce460 --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,121 @@ +// This is a copy of https://github.com/Flared/purple-icq/blob/master/src/logging.rs + +//use crate::messages::{FdSender, SystemMessage}; +use crate::purple; +use std::cell::RefCell; +use std::sync::Mutex; + +std::thread_local! { + pub static LOGGER: RefCell>> = RefCell::new(None); +} + +lazy_static::lazy_static! { + static ref PURPLE_BUFFER: Mutex> = Default::default(); +} + +static TLS_LOGGER: ThreadLocalLogger = ThreadLocalLogger; + +pub struct ThreadLocalLogger; + +impl log::Log for ThreadLocalLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + LOGGER.with(|cell| { + if let Some(ref logger) = cell.borrow().as_ref() { + logger.log(record); + } + }) + } + + fn flush(&self) { + LOGGER.with(|cell| { + if let Some(ref logger) = cell.borrow().as_ref() { + logger.flush() + } + }) + } +} + +pub struct PurpleDebugLogger; + +impl log::Log for PurpleDebugLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() < log::Level::Debug + } + + fn log(&self, record: &log::Record) { + let purple_level = match record.level() { + log::Level::Error => purple::PurpleDebugLevel::PURPLE_DEBUG_ERROR, + log::Level::Warn => purple::PurpleDebugLevel::PURPLE_DEBUG_WARNING, + log::Level::Info => purple::PurpleDebugLevel::PURPLE_DEBUG_INFO, + _ => purple::PurpleDebugLevel::PURPLE_DEBUG_MISC, + }; + + let target = if !record.target().is_empty() { + record.target() + } else { + record.module_path().unwrap_or_default() + }; + let line = format!("[{}] {}\n", target, record.args()); + purple::debug(purple_level, "", &line); + } + + fn flush(&self) { + let buffer = { + match PURPLE_BUFFER.lock() { + Ok(mut buffer) => buffer.split_off(0), + Err(_) => return, + } + }; + for (target, level, message) in buffer { + log::log!(target: &target, level, "{}", message); + } + } +} + +/* +pub struct RemoteLogger(pub FdSender); + +impl log::Log for RemoteLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() < log::Level::Debug + } + + fn log(&self, record: &log::Record) { + let target = if !record.target().is_empty() { + record.target() + } else { + record.module_path().unwrap_or_default() + }; + + if let Ok(mut buffer) = PURPLE_BUFFER.lock() { + buffer.push((target.into(), record.level(), record.args().to_string())); + } + } + + fn flush(&self) { + self.0.clone().try_send(SystemMessage::FlushLogs); + } +} +*/ +pub fn init(level: log::LevelFilter) -> Result<(), log::SetLoggerError> { + log::set_logger(&TLS_LOGGER).map(|()| log::set_max_level(level)) +} + +pub fn set_thread_logger(logger: T) +where + T: log::Log + 'static, +{ + LOGGER.with(|cell| *cell.borrow_mut() = Some(Box::new(logger))) +} + +pub fn flush() { + LOGGER.with(|cell| { + if let Some(ref logger) = cell.borrow().as_ref() { + logger.flush(); + } + }) +}