Initial framework based off pidgin-icq
This commit is contained in:
180
Cargo.lock
generated
180
Cargo.lock
generated
@@ -379,9 +379,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.48"
|
||||
version = "0.1.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
|
||||
checksum = "589652ce7ccb335d1e7ecb3be145425702b290dbcb7029bbeaae263fc1d87b48"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -873,9 +873,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.0.2"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f627126b946c25a4638eec0ea634fc52506dea98db118aae985118ce7c3d723f"
|
||||
checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
@@ -1295,7 +1295,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -1344,9 +1344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
|
||||
checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1359,9 +1359,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
|
||||
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1369,15 +1369,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
|
||||
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
|
||||
checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -1386,9 +1386,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
|
||||
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
@@ -1407,9 +1407,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
|
||||
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
@@ -1419,21 +1419,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
|
||||
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
|
||||
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
|
||||
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1507,6 +1507,41 @@ version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
"glib-macros",
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"itertools",
|
||||
"proc-macro-crate",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glib-sys"
|
||||
version = "0.10.1"
|
||||
@@ -1536,6 +1571,17 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gobject-sys"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
@@ -1926,9 +1972,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mailparse"
|
||||
version = "0.13.2"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31de1f9043c582efde7dbd93de56600df12b6c4488a67eeaefa74ea364019b22"
|
||||
checksum = "30286c2f0c485ae66dc96864ec64cd0b60497d8e0bc2073ead6999eff5c7b7a6"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"charset",
|
||||
@@ -2223,11 +2269,17 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
@@ -2406,6 +2458,39 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
|
||||
dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@@ -2431,17 +2516,34 @@ dependencies = [
|
||||
name = "purple-plugin-delta"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"deltachat",
|
||||
"glib-sys",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"purple-sys",
|
||||
"log",
|
||||
"purple-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "purple-sys"
|
||||
version = "0.0.5"
|
||||
source = "git+https://github.com/lupine/libpurple-rust?branch=with-flared#517054727b28079e67c66136be410eefbd637467"
|
||||
name = "purple-rs"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e506ad6d399bcbc5fcf62c4c2a846a5a9f354f2a479fc88395e809415b53399"
|
||||
dependencies = [
|
||||
"glib",
|
||||
"glib-sys",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"paste",
|
||||
"purple-sys-flared",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "purple-sys-flared"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f303166d1606c13323914c863ab57b0e3c4127500a9bf7de20689513210b91"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"glib-sys",
|
||||
@@ -2589,9 +2691,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -3212,7 +3314,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"rand 0.8.3",
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
@@ -3321,9 +3423,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
|
||||
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"pin-project-lite 0.2.6",
|
||||
@@ -3700,9 +3802,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "x25519-dalek"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc614d95359fd7afc321b66d2107ede58b246b844cf5d8a0adcca413e439f088"
|
||||
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"rand_core 0.5.1",
|
||||
|
17
Cargo.toml
17
Cargo.toml
@@ -4,21 +4,20 @@ version = "0.1.0"
|
||||
authors = ["Nick Thomas <delta@ur.gs>"]
|
||||
|
||||
[lib]
|
||||
name = "delta"
|
||||
path = "src/delta.rs"
|
||||
name = "purple_delta"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
libc = "*"
|
||||
glib-sys = "*"
|
||||
purple-sys = { git = "https://github.com/lupine/libpurple-rust", branch="with-flared" }
|
||||
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="v1.51.0" }
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.8"
|
||||
purple-rs = "*"
|
||||
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
# Keep in sync with deltachat-core-rust
|
||||
[dependencies.async-std]
|
||||
version = "~1.8"
|
||||
features = ["unstable"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
|
||||
|
188
src/delta.rs
188
src/delta.rs
@@ -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
1
src/delta/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
811
src/lib.rs
Normal file
811
src/lib.rs
Normal 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 <code>"));
|
||||
|
||||
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);
|
Reference in New Issue
Block a user