Initial framework based off pidgin-icq

This commit is contained in:
2021-04-10 14:29:36 +01:00
parent 70b24f0e14
commit 3142134360
5 changed files with 961 additions and 236 deletions

View File

@@ -1,188 +0,0 @@
extern crate purple_sys;
extern crate glib_sys;
extern crate libc;
#[macro_use]
extern crate lazy_static;
/*
mod pointer;
mod server;
mod user;
mod chatroom;
mod message;
*/
use std::os::raw::{c_void, c_char};
use std::ptr::null_mut;
use std::boxed::Box;
use std::ffi::CString; // CStr
//use std::sync::RwLock;
use purple_sys::*;
use purple_sys::PurpleType;
//use purple_sys::PurpleConnectionState;
use purple_sys::PurpleStatusPrimitive;
//use purple_sys::PurpleRoomlistFieldType;
//use message::*;
//use pointer::Pointer;
//use server::ACCOUNT;
//use server::{send_im, send_chat, find_blist_chat, find_chat_token};
use glib_sys::{GHashTable, GList};
const TRUE: i32 = 1;
const FALSE: i32 = 0;
lazy_static!{
// static ref PLUGIN: RwLock<Pointer> = RwLock::new(Pointer::new());
static ref ICON_FILE: CString = CString::new("delta").unwrap();
// static ref DELTA_CATEGORY: CString = CString::new("Delta Chat").unwrap();
}
fn append_item(list: *mut GList, item: *mut c_void) -> *mut GList {
unsafe {
glib_sys::g_list_append(list as *mut glib_sys::GList, item as *mut libc::c_void) as
*mut GList
}
}
extern "C" fn list_icon(_: *mut PurpleAccount, _: *mut PurpleBuddy) -> *const c_char {
ICON_FILE.as_ptr()
}
extern "C" fn status_types(_: *mut PurpleAccount) -> *mut GList {
let mut list: *mut GList = null_mut();
let available = CString::new("available").unwrap();
let available_name = CString::new("Available").unwrap();
let offline = CString::new("offline").unwrap();
let offline_name = CString::new("Offline").unwrap();
let nick = CString::new("nick").unwrap();
let status = unsafe {
purple_status_type_new_with_attrs(
PurpleStatusPrimitive::PURPLE_STATUS_AVAILABLE,
available.as_ptr(),
available_name.as_ptr(),
TRUE,
TRUE,
FALSE,
nick.as_ptr(),
nick.as_ptr(),
purple_value_new(PurpleType::PURPLE_TYPE_STRING),
null_mut() as *mut c_void,
)
};
list = append_item(list, status as *mut c_void);
let status = unsafe {
purple_status_type_new_with_attrs(
PurpleStatusPrimitive::PURPLE_STATUS_OFFLINE,
offline.as_ptr(),
offline_name.as_ptr(),
TRUE,
TRUE,
FALSE,
nick.as_ptr(),
nick.as_ptr(),
purple_value_new(PurpleType::PURPLE_TYPE_STRING),
null_mut() as *mut c_void,
)
};
list = append_item(list, status as *mut c_void);
list
}
unsafe extern "C" fn login(account: *mut PurpleAccount) {
println!("account: {:?}", account);
}
extern "C" fn chat_info(_: *mut PurpleConnection) -> *mut GList {
let list: *mut GList = null_mut();
list
}
unsafe extern "C" fn join_chat(gc: *mut PurpleConnection, components: *mut GHashTable) {
println!("join_chat: {:?}, {:?}", gc, components);
}
extern "C" fn chat_info_defaults(_: *mut PurpleConnection, _: *const c_char) -> *mut GHashTable {
let table: *mut GHashTable = null_mut();
table
}
extern "C" fn close(_: *mut PurpleConnection) {}
extern "C" fn buddy_list(gc: *mut PurpleConnection) -> *mut PurpleRoomlist {
let buddies = unsafe { purple_roomlist_new(purple_connection_get_account(gc)) };
buddies
}
extern "C" fn callback(_plugin: *mut PurplePlugin) -> i32 {
TRUE
}
// extern "C" fn action_cb(_: *mut PurplePluginAction) {
//
// }
extern "C" fn actions(_: *mut PurplePlugin, _: *mut c_void) -> *mut GList {
let list: *mut GList = null_mut();
list
}
#[no_mangle]
pub extern "C" fn purple_init_plugin(plugin: *mut PurplePlugin) -> i32 {
// save plugin pointer
// PLUGIN.write().unwrap().set(plugin as *mut c_void);
let id = CString::new("prpl-delta").unwrap();
let name = CString::new("Delta Chat").unwrap();
let version = CString::new("0.1.0").unwrap();
let summary = CString::new("Delta Chat is an email-based instant messaging solution").unwrap();
let description = CString::new("See https://delta.chat for more information").unwrap();
let author = CString::new("Nick Thomas <delta@ur.gs>").unwrap();
let home_page = CString::new("https://delta.chat").unwrap();
let mut info = Box::new(PurplePluginInfo::new());
let mut extra_info = Box::new(PurplePluginProtocolInfo::new());
unsafe {
extra_info.list_icon = Some(list_icon);
extra_info.status_types = Some(status_types);
extra_info.login = Some(login);
extra_info.close = Some(close);
extra_info.roomlist_get_list = Some(buddy_list);
extra_info.chat_info = Some(chat_info);
extra_info.chat_info_defaults = Some(chat_info_defaults);
//extra_info.chat_send = Some(send_chat);
extra_info.join_chat = Some(join_chat);
//extra_info.find_blist_chat = Some(find_blist_chat);
//extra_info.send_im = Some(send_im);
info.id = id.into_raw();
info.name = name.into_raw();
info.version = version.into_raw();
info.summary = summary.into_raw();
info.description = description.into_raw();
info.author = author.into_raw();
info.homepage = home_page.into_raw();
info.load = Some(callback);
info.actions = Some(actions);
info.extra_info = Box::into_raw(extra_info) as *mut c_void;
(*plugin).info = Box::into_raw(info);
};
unsafe { purple_plugin_register(plugin) }
}

1
src/delta/mod.rs Normal file
View File

@@ -0,0 +1 @@

811
src/lib.rs Normal file
View File

@@ -0,0 +1,811 @@
extern crate async_std;
extern crate lazy_static;
extern crate log;
extern crate purple_rs as purple;
use async_std::sync::Arc; // RwLock
use chat_info::{ChatInfo, PartialChatInfo}; //ChatInfoVersion
use lazy_static::lazy_static;
//use messages::{AccountInfo, ICQSystemHandle, PurpleMessage, SystemMessage};
use purple::*;
//use std::cell::RefCell;
use std::ffi::{CStr, CString};
//use std::io::Read;
//use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
mod chat_info;
mod delta;
pub mod logging;
//mod messages;
pub mod status {
use lazy_static::lazy_static;
use std::ffi::CString;
lazy_static! {
pub static ref ONLINE_ID: CString = CString::new("online").unwrap();
pub static ref ONLINE_NAME: CString = CString::new("Online").unwrap();
pub static ref OFFLINE_ID: CString = CString::new("offline").unwrap();
pub static ref OFFLINE_NAME: CString = CString::new("Offline").unwrap();
}
}
lazy_static! {
static ref ICON_FILE: CString = CString::new("delta").unwrap();
}
mod blist_node {
pub const LAST_SEEN_TIMESTAMP: &str = "last_seen_timestamp";
}
mod commands {
pub const IMEX: &str = "imex";
}
pub mod chat_states {
pub const JOINED: &str = "joined";
}
pub mod conv_data {
use super::HistoryInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub const CHAT_INFO: &str = "chat_info";
pub const HISTORY_INFO: &str = "history_info";
pub type HistoryInfoType = Rc<RefCell<HistoryInfo>>;
}
#[derive(Debug, Clone, Default)]
pub struct HistoryInfo {
pub oldest_message_id: Option<String>,
pub oldest_message_timestamp: Option<i64>,
}
#[derive(Debug, Clone)]
pub struct MsgInfo {
pub chat_sn: String,
pub author_sn: String,
pub author_friendly: String,
pub text: String,
pub time: i64,
pub message_id: String,
}
#[derive(Debug, Default)]
pub struct AccountData {
display_name: String,
imap_host: String,
imap_port: String,
imap_user: String,
imap_pass: String,
smtp_host: String,
smtp_port: String,
smtp_user: String,
smtp_pass: String,
bcc_self: bool,
// Not exposed: server_flags, selfstatus, e2ee_enabled
session_closed: AtomicBool,
// session: RwLock<Option<icq::protocol::SessionInfo>>,
}
impl Drop for AccountData {
fn drop(&mut self) {
log::info!("AccountData dropped");
}
}
pub type AccountDataBox = Arc<AccountData>;
pub type Handle = purple::Handle<AccountDataBox>;
pub type ProtocolData = purple::ProtocolData<AccountDataBox>;
pub struct PurpleDelta {
// system: ICQSystemHandle,
connections: purple::Connections<AccountDataBox>,
input_handle: Option<u32>,
imex_command_handle: Option<PurpleCmdId>,
}
impl purple::PrplPlugin for PurpleDelta {
type Plugin = Self;
fn new() -> Self {
logging::init(log::LevelFilter::Debug).expect("Failed to initialize logging");
// let system = icq::system::spawn();
Self {
// system,
input_handle: None,
imex_command_handle: None,
connections: purple::Connections::new(),
}
}
fn register(&self, context: RegisterContext<Self>) -> RegisterContext<Self> {
let info = purple::PrplInfo {
id: "prpl-delta".into(),
name: "Delta Chat".into(),
version: "0.1.0".into(),
summary: "Delta Chat is an email-based instant messaging solution".into(),
description: "See https://delta.chat for more information".into(),
author: "Nick Thomas <delta@ur.gs>".into(),
homepage: "https://code.ur.gs/lupine/purple-plugin-delta".into(),
};
context
.with_info(info)
.with_password()
.with_string_option("Display Name".into(), "displayname".into(), "".into())
.with_string_option("IMAP server host".into(), "mail_server".into(), "".into())
.with_string_option("IMAP server port".into(), "mail_port".into(), "".into())
// Username and password are mail_user and mail_pw
.with_string_option("SMTP server host".into(), "send_server".into(), "".into())
.with_string_option("SMTP server port".into(), "send_port".into(), "".into())
.with_string_option("SMTP server username".into(), "send_user".into(), "".into())
.with_password_option("SMTP server password".into(), "send_pw".into(), "".into())
.with_bool_option("Copy messages to self".into(), "bcc_self".into(), false)
.enable_login()
.enable_load()
.enable_close()
//.enable_chat_info()
//.enable_chat_info_defaults()
//.enable_join_chat()
//.enable_chat_leave()
//.enable_send_im()
//.enable_chat_send()
//.enable_convo_closed()
//.enable_get_chat_name()
.enable_list_icon()
.enable_status_types()
}
}
impl purple::LoginHandler for PurpleDelta {
fn login(&mut self, account: &mut Account) {
let display_name = account.get_string("displayname", "");
let imap_host = account.get_string("mail_server", "");
let imap_port = account.get_string("mail_port", "");
let imap_user = account.get_username().unwrap().into();
let imap_pass = account.get_password().unwrap().into();
let smtp_host = account.get_string("send_server", "");
let smtp_port = account.get_string("send_port", "");
let smtp_user = account.get_string("send_user", "");
let smtp_pass = account.get_string("send_pw", "");
let bcc_self = account.get_bool("bcc_self", false);
let protocol_data: AccountDataBox = Arc::new(AccountData {
display_name,
imap_host,
imap_port,
imap_user,
imap_pass,
smtp_host,
smtp_port,
smtp_user,
smtp_pass,
bcc_self,
session_closed: AtomicBool::new(false),
// session: RwLock::new(None),
});
// Safe as long as we remove the account in "close".
unsafe {
self.connections
.add(account.get_connection().unwrap(), protocol_data.clone())
};
/*
self.system
.tx
.try_send(PurpleMessage::Login(AccountInfo {
handle: Handle::from(&mut *account),
protocol_data,
}))
.unwrap();*/
}
}
impl purple::CloseHandler for PurpleDelta {
fn close(&mut self, connection: &mut Connection) {
let handle = Handle::from(&mut *connection);
match self.connections.get(&handle) {
Some(protocol_data) => {
protocol_data
.data
.session_closed
.store(true, Ordering::Relaxed);
self.connections.remove(*connection);
}
None => {
log::error!("Tried closing a closed connection");
}
}
}
}
impl purple::StatusTypeHandler for PurpleDelta {
fn status_types(_account: &mut Account) -> Vec<StatusType> {
vec![
StatusType::new(
PurpleStatusPrimitive::PURPLE_STATUS_AVAILABLE,
Some(&status::ONLINE_ID),
Some(&status::ONLINE_NAME),
true,
),
StatusType::new(
PurpleStatusPrimitive::PURPLE_STATUS_OFFLINE,
Some(&status::OFFLINE_ID),
Some(&status::OFFLINE_NAME),
true,
),
]
}
}
impl purple::LoadHandler for PurpleDelta {
fn load(&mut self, _plugin: &purple::Plugin) -> bool {
logging::set_thread_logger(logging::PurpleDebugLogger);
//use std::os::unix::io::AsRawFd;
/*
self.input_handle = Some(self.enable_input(
self.system.input_rx.as_raw_fd(),
purple::PurpleInputCondition::PURPLE_INPUT_READ,
));
*/
self.imex_command_handle =
Some(self.enable_command(commands::IMEX, "w", "imex &lt;code&gt;"));
true
}
}
impl purple::ListIconHandler for PurpleDelta {
fn list_icon(_account: &mut Account) -> &'static CStr {
&ICON_FILE
}
}
/*
impl purple::ChatInfoHandler for PurpleDelta {
fn chat_info(&mut self, _connection: &mut Connection) -> Vec<purple::prpl::ChatEntry> {
vec![purple::prpl::ChatEntry {
label: &chat_info::SN_NAME,
identifier: &chat_info::SN,
required: true,
is_int: false,
min: 0,
max: 0,
secret: false,
}]
}
}
impl purple::ChatInfoDefaultsHandler for PurpleDelta {
fn chat_info_defaults(
&mut self,
_connection: &mut Connection,
chat_name: Option<&str>,
) -> purple::StrHashTable {
let mut defaults = purple::StrHashTable::default();
defaults.insert(chat_info::SN.as_c_str(), chat_name.unwrap_or(""));
defaults
}
}
impl purple::JoinChatHandler for PurpleDelta {
fn join_chat(&mut self, connection: &mut Connection, data: Option<&mut StrHashTable>) {
let data = match data {
Some(data) => data,
None => {
return;
}
};
let stamp = match Self::get_chat_name(Some(data)) {
Some(stamp) => stamp,
None => {
log::error!("No chat name provided");
return;
}
};
log::info!("Joining {}", stamp);
let handle = Handle::from(&mut *connection);
let protocol_data = self
.connections
.get(&handle)
.expect("Tried joining chat on closed connection");
if let Some(chat_states::JOINED) = data.lookup(&chat_info::STATE) {
match PartialChatInfo::from_hashtable(data) {
Some(chat_info) => {
self.conversation_joined(connection, &chat_info);
/*
self.system
.tx
.try_send(PurpleMessage::get_chat_info(
handle,
protocol_data.data.clone(),
chat_info.sn,
))
.unwrap(); */
return;
}
None => {
log::error!("Unable to load chat info");
}
}
}
/*
self.system
.tx
.try_send(PurpleMessage::join_chat(
handle,
protocol_data.data.clone(),
stamp,
))
.unwrap() */
}
}
impl purple::ChatLeaveHandler for PurpleDelta {
fn chat_leave(&mut self, connection: &mut Connection, id: i32) {
log::info!("Chat leave: {}", id);
match Conversation::find(connection, id) {
Some(mut conversation) => {
unsafe { conversation.remove_data::<ChatInfo>(conv_data::CHAT_INFO) };
}
None => {
log::warn!("Leaving chat without conversation");
}
}
}
}
impl purple::ConvoClosedHandler for PurpleDelta {
fn convo_closed(&mut self, _connection: &mut Connection, who: Option<&str>) {
log::info!("Convo closed: {:?}", who)
}
}
impl purple::GetChatNameHandler for PurpleDelta {
fn get_chat_name(data: Option<&mut purple::StrHashTable>) -> Option<String> {
data.and_then(|h| h.lookup(chat_info::SN.as_c_str()).map(Into::into))
}
}
impl purple::SendIMHandler for PurpleDelta {
fn send_im(
&mut self,
_connection: &mut Connection,
_who: &str,
_message: &str,
_flags: PurpleMessageFlags,
) -> i32 {
log::warn!("SendIM is not implemented");
-1
}
}
impl purple::ChatSendHandler for PurpleDelta {
fn chat_send(
&mut self,
connection: &mut Connection,
id: i32,
message: &str,
flags: PurpleMessageFlags,
) -> i32 {
log::info!("{}: {} [{:?}]", id, message, flags);
let mut conversation = match Conversation::find(connection, id) {
Some(c) => c,
None => {
log::error!("Conversation not found");
return -1;
}
};
let sn = match unsafe { conversation.get_data::<ChatInfo>(conv_data::CHAT_INFO) } {
Some(info) => info.sn.clone(),
None => {
log::error!("SN not found");
return -1;
}
};
let handle = Handle::from(&mut *connection);
let protocol_data = self.connections.get(&handle).expect("Connection closed");
/*
self.system
.tx
.try_send(PurpleMessage::send_msg(
handle,
protocol_data.data.clone(),
sn,
message.into(),
))
.unwrap(); */
1
}
}
impl purple::InputHandler for PurpleDelta {
fn input(&mut self, _fd: i32, _cond: purple::PurpleInputCondition) {
log::debug!("Input");
/*
// Consume the byte from the input pipe.
let mut buf = [0; 1];
self.system
.input_rx
.read_exact(&mut buf)
.expect("Failed to read input pipe");
// Consume the actual message.
match self.system.rx.try_recv() {
Ok(message) => self.process_message(message),
Err(async_std::sync::TryRecvError::Empty) => log::error!("Expected message, but empty"),
Err(async_std::sync::TryRecvError::Disconnected) => {
log::error!("System disconnected");
if let Some(input_handle) = self.input_handle {
self.disable_input(input_handle);
}
}
};
*/
}
}
*/
impl purple::CommandHandler for PurpleDelta {
fn command(
&mut self,
conversation: &mut Conversation,
command: &str,
args: &[&str],
) -> PurpleCmdRet {
log::debug!(
"command: conv={} cmd={} args={:?}",
conversation.get_title().unwrap_or("unknown"),
command,
args
);
match command {
commands::IMEX => self.command_imex(conversation, args),
_ => {
log::error!("Unknown command: {}", command);
PurpleCmdRet::PURPLE_CMD_RET_FAILED
}
}
}
}
impl PurpleDelta {
fn command_imex(&mut self, conversation: &mut Conversation, args: &[&str]) -> PurpleCmdRet {
log::debug!("command_imex");
if args.len() != 1 {
log::error!(
"command_imex: Unsupported number of args. Got {}",
args.len()
);
return PurpleCmdRet::PURPLE_CMD_RET_FAILED;
}
/*
let count = {
let input = match args[0].parse::<u32>() {
Ok(count) => count,
Err(_) => {
log::error!("command_history: Could not parse count: {}", args[0]);
return PurpleCmdRet::PURPLE_CMD_RET_FAILED;
}
};
0 - input as i32
};
let sn = match conversation.get_name() {
Some(name) => name.to_string(),
None => {
log::error!("command_history: SN not found");
return PurpleCmdRet::PURPLE_CMD_RET_FAILED;
}
};
let from_msg_id = {
match unsafe {
conversation.get_data::<conv_data::HistoryInfoType>(conv_data::HISTORY_INFO)
} {
Some(history_info) => {
let history_info = history_info.borrow_mut();
match &history_info.oldest_message_id {
Some(oldest_message_id) => oldest_message_id.clone(),
None => {
return PurpleCmdRet::PURPLE_CMD_RET_FAILED;
}
}
}
None => {
log::error!("command_history: Can't find message id");
return PurpleCmdRet::PURPLE_CMD_RET_FAILED;
}
}
};
let handle = Handle::from(&mut conversation.get_connection());
let protocol_data = self
.connections
.get(&handle)
.expect("Tried joining chat on closed connection");
self.system
.tx
.try_send(PurpleMessage::fetch_history(
handle,
protocol_data.data.clone(),
sn,
from_msg_id,
count,
))
.unwrap();
*/
PurpleCmdRet::PURPLE_CMD_RET_OK
}
/*
fn process_message(&mut self, message: SystemMessage) {
match message {
SystemMessage::ExecAccount { handle, function } => {
self.connections
.get(handle)
.map(|protocol_data| function(&mut protocol_data.account))
.or_else(|| {
log::warn!("The account connection has been closed");
None
});
}
SystemMessage::ExecConnection { handle, function } => {
self.connections
.get(handle)
.map(|protocol_data| function(&mut protocol_data.connection))
.or_else(|| {
log::warn!("The account connection has been closed");
None
});
}
SystemMessage::ExecHandle { handle, function } => {
self.connections
.get(handle)
.map(|mut protocol_data| function(self, &mut protocol_data))
.or_else(|| {
log::warn!("The account connection has been closed");
None
});
}
SystemMessage::FlushLogs => logging::flush(),
}
}
pub fn serv_got_chat_in(&mut self, connection: &mut Connection, msg_info: MsgInfo) {
match purple::Chat::find(&mut connection.get_account(), &msg_info.chat_sn) {
Some(mut chat) => {
// Get the chat and the last seen timestamp.
let mut node = chat.as_blist_node();
let last_timestamp: i64 = node
.get_string(&blist_node::LAST_SEEN_TIMESTAMP)
.and_then(|t| t.parse::<i64>().ok())
.unwrap_or(0);
let new_timestamp = msg_info.time;
// Only trigger conversation_joined if this is a new message.
let conversation = {
if new_timestamp > last_timestamp {
node.set_string(
&blist_node::LAST_SEEN_TIMESTAMP,
&new_timestamp.to_string(),
);
Some(self.conversation_joined(
connection,
&PartialChatInfo {
sn: msg_info.chat_sn.clone(),
title: msg_info.chat_sn.clone(),
..Default::default()
},
))
} else {
None
}
};
// Get the conversation and set the oldest *displayed* messageId.
// This is the oldest message that the user can see in the chat window.
//
// If there is no conversation yet, that is okay. It means that we haven't
// seen new messages yet.
if let Some(mut conversation) = conversation {
let history_info = {
match unsafe {
conversation
.get_data::<conv_data::HistoryInfoType>(conv_data::HISTORY_INFO)
} {
Some(history_info) => history_info.clone(),
None => {
let history_info = Rc::new(RefCell::new(HistoryInfo {
oldest_message_id: None,
oldest_message_timestamp: None,
}));
unsafe {
conversation.set_data::<conv_data::HistoryInfoType>(
conv_data::HISTORY_INFO,
history_info.clone(),
)
};
history_info
}
}
};
let mut history_info = history_info.borrow_mut();
match history_info.oldest_message_timestamp {
None => {
history_info.oldest_message_id = Some(msg_info.message_id.clone());
history_info.oldest_message_timestamp = Some(msg_info.time);
}
Some(existing_timestamp) => {
if msg_info.time < existing_timestamp {
history_info.oldest_message_id = Some(msg_info.message_id.clone());
history_info.oldest_message_timestamp = Some(msg_info.time);
}
}
}
}
}
None => {
// Don't log errors for DMs because they are not yet supported.
// It happens all the time.
if msg_info.chat_sn.ends_with("@chat.agent") {
log::error!("Got message for unknown chat {}", msg_info.chat_sn);
}
}
}
connection.serv_got_chat_in(msg_info);
}
pub fn chat_joined(&mut self, connection: &mut Connection, info: &PartialChatInfo) {
log::info!("chat joined: {}", info.sn);
if info.sn.ends_with("@chat.agent") {
self.group_chat_joined(connection, info)
} else {
todo!()
};
}
fn group_chat_joined(
&mut self,
connection: &mut Connection,
info: &PartialChatInfo,
) -> purple::Chat {
let mut account = connection.get_account();
match purple::Chat::find(&mut account, &info.sn) {
Some(mut chat) => {
// The chat already exists.
// Should we replace the blist group?
if let Some(info_group) = &info.group {
let should_replace_group = {
match chat.get_group() {
Some(mut chat_group) => !chat_group.get_name().eq(info_group),
None => true,
}
};
if should_replace_group {
chat.add_to_blist(&mut self.get_or_create_group(Some(&info_group)), None);
}
}
// Replace the alias
chat.set_alias(&info.title);
chat
}
None => {
let mut components = info.as_hashtable();
components.insert(&chat_info::STATE, chat_states::JOINED);
let mut chat = purple::Chat::new(&mut account, &info.title, components);
chat.add_to_blist(&mut self.get_or_create_group(info.group.as_deref()), None);
chat
}
}
}
fn get_or_create_group(&mut self, name: Option<&str>) -> purple::Group {
let name = name.unwrap_or("ICQ");
Group::find(name).unwrap_or_else(|| {
let mut group = purple::Group::new(name);
group.add_to_blist(None);
group
})
}
pub fn conversation_joined(
&mut self,
connection: &mut Connection,
info: &PartialChatInfo,
) -> Conversation {
match connection.get_account().find_chat_conversation(&info.sn) {
Some(mut conversation) => {
if conversation.get_chat_data().unwrap().has_left() {
log::error!("Trying to join left conversation");
} else {
conversation.present();
}
conversation
}
None => {
let mut conversation = connection.serv_got_joined_chat(&info.sn).unwrap();
conversation.set_title(&info.title);
conversation
}
}
}
pub fn check_chat_info(
&mut self,
connection: &mut Connection,
sn: &str,
version: &ChatInfoVersion,
) {
match connection.get_account().find_chat_conversation(&sn) {
Some(mut conversation) => {
let chat_info = unsafe { conversation.get_data::<ChatInfo>(conv_data::CHAT_INFO) };
if chat_info
.map(|chat_info| chat_info.need_update(version))
.unwrap_or(true)
{
log::info!("Fetching chat info: {}", sn);
let handle = Handle::from(&mut *connection);
let protocol_data = self
.connections
.get(&handle)
.expect("Tried get chat info on closed connection");
self.system
.tx
.try_send(PurpleMessage::get_chat_info(
handle,
protocol_data.data.clone(),
sn.to_string(),
))
.unwrap();
}
}
None => {
log::warn!("Checking chat info for no conversation");
}
}
}
pub fn load_chat_info(&mut self, connection: &mut Connection, info: &ChatInfo) {
log::debug!("loading chat info: {:?}", info);
match connection.get_account().find_chat_conversation(&info.sn) {
Some(mut conversation) => {
conversation.set_title(&info.title);
let mut chat_conversation = conversation.get_chat_data().unwrap();
unsafe { conversation.set_data(conv_data::CHAT_INFO, info.clone()) };
chat_conversation.clear_users();
for member in &info.members {
chat_conversation.add_user(&member.sn, "", member.role.as_flags(), false);
}
if let Some(about) = &info.about {
chat_conversation.set_topic("unknown", about);
}
}
None => {
log::warn!("Loaded chat info for no conversation");
}
}
}
*/
}
purple_prpl_plugin!(PurpleDelta);