mirror of
https://github.com/chatmail/core.git
synced 2026-04-06 15:42:10 +03:00
1218 lines
45 KiB
Rust
1218 lines
45 KiB
Rust
use deltachat::constants::*;
|
|
use deltachat::context::*;
|
|
use deltachat::dc_array::*;
|
|
use deltachat::dc_chat::*;
|
|
use deltachat::dc_chatlist::*;
|
|
use deltachat::dc_configure::*;
|
|
use deltachat::dc_contact::*;
|
|
use deltachat::dc_imex::*;
|
|
use deltachat::dc_job::*;
|
|
use deltachat::dc_location::*;
|
|
use deltachat::dc_lot::*;
|
|
use deltachat::dc_msg::*;
|
|
use deltachat::dc_qr::*;
|
|
use deltachat::dc_receive_imf::*;
|
|
use deltachat::dc_sqlite3::*;
|
|
use deltachat::dc_tools::*;
|
|
use deltachat::peerstate::*;
|
|
use deltachat::types::*;
|
|
use deltachat::x::*;
|
|
use num_traits::FromPrimitive;
|
|
|
|
/// Reset database tables. This function is called from Core cmdline.
|
|
/// Argument is a bitmask, executing single or multiple actions in one call.
|
|
/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
|
|
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
|
info!(context, 0, "Resetting tables ({})...", bits);
|
|
if 0 != bits & 1 {
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM jobs;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
info!(context, 0, "(1) Jobs reset.");
|
|
}
|
|
if 0 != bits & 2 {
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM acpeerstates;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
info!(context, 0, "(2) Peerstates reset.");
|
|
}
|
|
if 0 != bits & 4 {
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM keypairs;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
info!(context, 0, "(4) Private keypairs reset.");
|
|
}
|
|
if 0 != bits & 8 {
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM contacts WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM chats WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM chats_contacts;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM msgs WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM config WHERE keyname LIKE \'imap.%\' OR keyname LIKE \'configured%\';\x00"
|
|
as *const u8 as *const libc::c_char,
|
|
);
|
|
dc_sqlite3_execute(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"DELETE FROM leftgrps;\x00" as *const u8 as *const libc::c_char,
|
|
);
|
|
info!(context, 0, "(8) Rest but server config reset.");
|
|
}
|
|
|
|
(context.cb)(context, Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
|
|
|
1
|
|
}
|
|
|
|
unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> libc::c_int {
|
|
/* mainly for testing, may be called by dc_import_spec() */
|
|
let mut success: libc::c_int = 0i32;
|
|
let mut data: *mut libc::c_char = 0 as *mut libc::c_char;
|
|
let mut data_bytes: size_t = 0;
|
|
if !(dc_read_file(
|
|
context,
|
|
filename,
|
|
&mut data as *mut *mut libc::c_char as *mut *mut libc::c_void,
|
|
&mut data_bytes,
|
|
) == 0i32)
|
|
{
|
|
dc_receive_imf(
|
|
context,
|
|
data,
|
|
data_bytes,
|
|
b"import\x00" as *const u8 as *const libc::c_char,
|
|
0i32 as uint32_t,
|
|
0i32 as uint32_t,
|
|
);
|
|
success = 1;
|
|
}
|
|
free(data as *mut libc::c_void);
|
|
|
|
success
|
|
}
|
|
|
|
/// Import a file to the database.
|
|
/// For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on.
|
|
/// For normal importing, use dc_imex().
|
|
///
|
|
/// @private @memberof Context
|
|
/// @param context The context as created by dc_context_new().
|
|
/// @param spec The file or directory to import. NULL for the last command.
|
|
/// @return 1=success, 0=error.
|
|
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
|
if 0 == dc_sqlite3_is_open(&context.sql.clone().read().unwrap()) {
|
|
error!(context, 0, "Import: Database not opened.");
|
|
return 0;
|
|
}
|
|
|
|
let mut current_block: u64;
|
|
let mut success: libc::c_int = 0;
|
|
let real_spec: *mut libc::c_char;
|
|
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
|
|
let mut read_cnt: libc::c_int = 0;
|
|
|
|
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
|
if !spec.is_null() {
|
|
real_spec = dc_strdup(spec);
|
|
dc_sqlite3_set_config(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"import_spec\x00" as *const u8 as *const libc::c_char,
|
|
real_spec,
|
|
);
|
|
current_block = 7149356873433890176;
|
|
} else {
|
|
real_spec = dc_sqlite3_get_config(
|
|
context,
|
|
&context.sql.clone().read().unwrap(),
|
|
b"import_spec\x00" as *const u8 as *const libc::c_char,
|
|
0 as *const libc::c_char,
|
|
);
|
|
if real_spec.is_null() {
|
|
error!(context, 0, "Import: No file or folder given.");
|
|
current_block = 8522321847195001863;
|
|
} else {
|
|
current_block = 7149356873433890176;
|
|
}
|
|
}
|
|
match current_block {
|
|
8522321847195001863 => {}
|
|
_ => {
|
|
suffix = dc_get_filesuffix_lc(real_spec);
|
|
if !suffix.is_null()
|
|
&& strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
|
|
{
|
|
if 0 != dc_poke_eml_file(context, real_spec) {
|
|
read_cnt += 1
|
|
}
|
|
current_block = 1622411330066726685;
|
|
} else {
|
|
/* import a directory */
|
|
let dir_name = std::path::Path::new(to_str(real_spec));
|
|
let dir = std::fs::read_dir(dir_name);
|
|
if dir.is_err() {
|
|
error!(
|
|
context,
|
|
0,
|
|
"Import: Cannot open directory \"{}\".",
|
|
to_str(real_spec),
|
|
);
|
|
current_block = 8522321847195001863;
|
|
} else {
|
|
let dir = dir.unwrap();
|
|
for entry in dir {
|
|
if entry.is_err() {
|
|
break;
|
|
}
|
|
let entry = entry.unwrap();
|
|
let name_f = entry.file_name();
|
|
let name = name_f.to_string_lossy();
|
|
if name.ends_with(".eml") {
|
|
let path_plus_name = format!("{}/{}", to_str(real_spec), name);
|
|
info!(context, 0, "Import: {}", path_plus_name);
|
|
let path_plus_name_c = to_cstring(path_plus_name);
|
|
|
|
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
|
read_cnt += 1
|
|
}
|
|
}
|
|
}
|
|
current_block = 1622411330066726685;
|
|
}
|
|
}
|
|
match current_block {
|
|
8522321847195001863 => {}
|
|
_ => {
|
|
info!(
|
|
context,
|
|
0,
|
|
"Import: {} items read from \"{}\".",
|
|
read_cnt,
|
|
to_str(real_spec)
|
|
);
|
|
if read_cnt > 0 {
|
|
(context.cb)(context, Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
|
}
|
|
success = 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(real_spec as *mut libc::c_void);
|
|
free(suffix as *mut libc::c_void);
|
|
success
|
|
}
|
|
|
|
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t) {
|
|
let contact: *mut dc_contact_t = dc_get_contact(context, dc_msg_get_from_id(msg));
|
|
let contact_name: *mut libc::c_char = dc_contact_get_name(contact);
|
|
let contact_id: libc::c_int = dc_contact_get_id(contact) as libc::c_int;
|
|
let statestr = match dc_msg_get_state(msg) {
|
|
20 => " o",
|
|
26 => " √",
|
|
28 => " √√",
|
|
24 => " !!",
|
|
_ => "",
|
|
};
|
|
let temp2: *mut libc::c_char = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
|
|
let msgtext: *mut libc::c_char = dc_msg_get_text(msg);
|
|
info!(
|
|
context,
|
|
0,
|
|
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
|
|
prefix.as_ref(),
|
|
dc_msg_get_id(msg) as libc::c_int,
|
|
if 0 != dc_msg_get_showpadlock(msg) {
|
|
"🔒"
|
|
} else {
|
|
""
|
|
},
|
|
if dc_msg_has_location(msg) { "📍" } else { "" },
|
|
to_str(contact_name),
|
|
contact_id,
|
|
to_str(msgtext),
|
|
if 0 != dc_msg_is_starred(msg) {
|
|
"★"
|
|
} else {
|
|
""
|
|
},
|
|
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
|
|
""
|
|
} else if dc_msg_get_state(msg) == 16 {
|
|
"[SEEN]"
|
|
} else if dc_msg_get_state(msg) == 13 {
|
|
"[NOTICED]"
|
|
} else {
|
|
"[FRESH]"
|
|
},
|
|
if 0 != dc_msg_is_info(msg) {
|
|
"[INFO]"
|
|
} else {
|
|
""
|
|
},
|
|
statestr,
|
|
to_str(temp2),
|
|
);
|
|
free(msgtext as *mut libc::c_void);
|
|
free(temp2 as *mut libc::c_void);
|
|
free(contact_name as *mut libc::c_void);
|
|
dc_contact_unref(contact);
|
|
}
|
|
|
|
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
|
let cnt = dc_array_get_cnt(msglist) as usize;
|
|
let mut lines_out = 0;
|
|
for i in 0..cnt {
|
|
let msg_id = dc_array_get_id(msglist, i as size_t);
|
|
if msg_id == 9 as libc::c_uint {
|
|
info!(
|
|
context,
|
|
0,
|
|
"--------------------------------------------------------------------------------"
|
|
);
|
|
|
|
lines_out += 1
|
|
} else if msg_id > 0 as libc::c_uint {
|
|
if lines_out == 0 {
|
|
info!(
|
|
context, 0,
|
|
"--------------------------------------------------------------------------------",
|
|
);
|
|
lines_out += 1
|
|
}
|
|
let msg = dc_get_msg(context, msg_id);
|
|
log_msg(context, "Msg", msg);
|
|
dc_msg_unref(msg);
|
|
}
|
|
}
|
|
if lines_out > 0 {
|
|
info!(
|
|
context,
|
|
0, "--------------------------------------------------------------------------------"
|
|
);
|
|
}
|
|
}
|
|
|
|
unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
|
|
let mut contact: *mut dc_contact_t;
|
|
if !dc_array_search_id(contacts, 1 as uint32_t, 0 as *mut size_t) {
|
|
dc_array_add_id(contacts, 1 as uint32_t);
|
|
}
|
|
let cnt = dc_array_get_cnt(contacts);
|
|
for i in 0..cnt {
|
|
let contact_id = dc_array_get_id(contacts, i as size_t);
|
|
let line;
|
|
let mut line2 = "".to_string();
|
|
contact = dc_get_contact(context, contact_id);
|
|
if !contact.is_null() {
|
|
let name: *mut libc::c_char = dc_contact_get_name(contact);
|
|
let addr: *mut libc::c_char = dc_contact_get_addr(contact);
|
|
let verified_state: libc::c_int = dc_contact_is_verified(contact);
|
|
let verified_str = if 0 != verified_state {
|
|
if verified_state == 2 {
|
|
" √√"
|
|
} else {
|
|
" √"
|
|
}
|
|
} else {
|
|
""
|
|
};
|
|
line = format!(
|
|
"{}{} <{}>",
|
|
if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int {
|
|
to_str(name)
|
|
} else {
|
|
"<name unset>"
|
|
},
|
|
verified_str,
|
|
if !addr.is_null() && 0 != *addr.offset(0isize) as libc::c_int {
|
|
to_str(addr)
|
|
} else {
|
|
"addr unset"
|
|
}
|
|
);
|
|
let peerstate =
|
|
Peerstate::from_addr(context, &context.sql.clone().read().unwrap(), to_str(addr));
|
|
if peerstate.is_some() && contact_id != 1 as libc::c_uint {
|
|
line2 = format!(
|
|
", prefer-encrypt={}",
|
|
peerstate.as_ref().unwrap().prefer_encrypt
|
|
);
|
|
}
|
|
dc_contact_unref(contact);
|
|
free(name as *mut libc::c_void);
|
|
free(addr as *mut libc::c_void);
|
|
info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static mut S_IS_AUTH: libc::c_int = 0;
|
|
|
|
pub unsafe fn dc_cmdline_skip_auth() {
|
|
S_IS_AUTH = 1;
|
|
}
|
|
|
|
unsafe fn chat_prefix(chat: *const dc_chat_t) -> &'static str {
|
|
if (*chat).type_0 == 120 {
|
|
"Group"
|
|
} else if (*chat).type_0 == 130 {
|
|
"VerifiedGroup"
|
|
} else {
|
|
"Single"
|
|
}
|
|
}
|
|
|
|
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
|
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
|
let mut sel_chat = if chat_id > 0 {
|
|
dc_get_chat(context, chat_id)
|
|
} else {
|
|
std::ptr::null_mut()
|
|
};
|
|
|
|
let mut args = line.splitn(3, ' ');
|
|
let arg0 = args.next().unwrap_or_default();
|
|
let arg1 = args.next().unwrap_or_default();
|
|
let arg1_c = to_cstring(arg1);
|
|
let arg1_c_ptr = if arg1.is_empty() {
|
|
std::ptr::null()
|
|
} else {
|
|
arg1_c.as_ptr()
|
|
};
|
|
let arg2 = args.next().unwrap_or_default();
|
|
let arg2_c = to_cstring(arg2);
|
|
let arg2_c_ptr = if arg2.is_empty() {
|
|
std::ptr::null()
|
|
} else {
|
|
arg2_c.as_ptr()
|
|
};
|
|
|
|
match arg0 {
|
|
"help" | "?" => match arg1 {
|
|
// TODO: reuse commands definition in main.rs.
|
|
"imex" => println!(
|
|
"====================Import/Export commands==\n\
|
|
initiate-key-transfer\n\
|
|
get-setupcodebegin <msg-id>\n\
|
|
continue-key-transfer <msg-id> <setup-code>\n\
|
|
has-backup\n\
|
|
export-backup\n\
|
|
import-backup <backup-file>\n\
|
|
export-keys\n\
|
|
import-keys\n\
|
|
export-setup\n\
|
|
poke [<eml-file>|<folder>|<addr> <key-file>]\n\
|
|
reset <flags>\n\
|
|
stop\n\
|
|
============================================="
|
|
),
|
|
_ => println!(
|
|
"==========================Database commands==\n\
|
|
info\n\
|
|
open <file to open or create>\n\
|
|
close\n\
|
|
set <configuration-key> [<value>]\n\
|
|
get <configuration-key>\n\
|
|
oauth2\n\
|
|
configure\n\
|
|
connect\n\
|
|
disconnect\n\
|
|
maybenetwork\n\
|
|
housekeeping\n\
|
|
help imex (Import/Export)\n\
|
|
==============================Chat commands==\n\
|
|
listchats [<query>]\n\
|
|
listarchived\n\
|
|
chat [<chat-id>|0]\n\
|
|
createchat <contact-id>\n\
|
|
createchatbymsg <msg-id>\n\
|
|
creategroup <name>\n\
|
|
createverified <name>\n\
|
|
addmember <contact-id>\n\
|
|
removemember <contact-id>\n\
|
|
groupname <name>\n\
|
|
groupimage [<file>]\n\
|
|
chatinfo\n\
|
|
sendlocations <seconds>\n\
|
|
setlocation <lat> <lng>\n\
|
|
dellocations\n\
|
|
getlocations [<contact-id>]\n\
|
|
send <text>\n\
|
|
sendimage <file> [<text>]\n\
|
|
sendfile <file>\n\
|
|
draft [<text>]\n\
|
|
listmedia\n\
|
|
archive <chat-id>\n\
|
|
unarchive <chat-id>\n\
|
|
delchat <chat-id>\n\
|
|
===========================Message commands==\n\
|
|
listmsgs <query>\n\
|
|
msginfo <msg-id>\n\
|
|
listfresh\n\
|
|
forward <msg-id> <chat-id>\n\
|
|
markseen <msg-id>\n\
|
|
star <msg-id>\n\
|
|
unstar <msg-id>\n\
|
|
delmsg <msg-id>\n\
|
|
===========================Contact commands==\n\
|
|
listcontacts [<query>]\n\
|
|
listverified [<query>]\n\
|
|
addcontact [<name>] <addr>\n\
|
|
contactinfo <contact-id>\n\
|
|
delcontact <contact-id>\n\
|
|
cleanupcontacts\n\
|
|
======================================Misc.==\n\
|
|
getqr [<chat-id>]\n\
|
|
getbadqr\n\
|
|
checkqr <qr-content>\n\
|
|
event <event-id to test>\n\
|
|
fileinfo <file>\n\
|
|
clear -- clear screen\n\
|
|
exit\n\
|
|
============================================="
|
|
),
|
|
},
|
|
"auth" => {
|
|
if 0 == S_IS_AUTH {
|
|
let is_pw =
|
|
dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char);
|
|
if arg1 == to_str(is_pw) {
|
|
S_IS_AUTH = 1;
|
|
} else {
|
|
println!("Bad password.");
|
|
}
|
|
} else {
|
|
println!("Already authorized.");
|
|
}
|
|
}
|
|
"open" => {
|
|
ensure!(!arg1.is_empty(), "Argument <file> missing");
|
|
dc_close(context);
|
|
ensure!(
|
|
0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char),
|
|
"Open failed"
|
|
);
|
|
}
|
|
"close" => {
|
|
dc_close(context);
|
|
}
|
|
"initiate-key-transfer" => {
|
|
let setup_code = dc_initiate_key_transfer(context);
|
|
if !setup_code.is_null() {
|
|
println!(
|
|
"Setup code for the transferred setup message: {}",
|
|
to_str(setup_code),
|
|
);
|
|
free(setup_code as *mut libc::c_void);
|
|
} else {
|
|
bail!("Failed to generate setup code");
|
|
};
|
|
}
|
|
"get-setupcodebegin" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
|
let msg_id: u32 = arg1.parse().unwrap();
|
|
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
|
|
if 0 != dc_msg_is_setupmessage(msg) {
|
|
let setupcodebegin: *mut libc::c_char = dc_msg_get_setupcodebegin(msg);
|
|
println!(
|
|
"The setup code for setup message Msg#{} starts with: {}",
|
|
msg_id,
|
|
to_str(setupcodebegin),
|
|
);
|
|
free(setupcodebegin as *mut libc::c_void);
|
|
} else {
|
|
bail!("Msg#{} is no setup message.", msg_id,);
|
|
}
|
|
dc_msg_unref(msg);
|
|
}
|
|
"continue-key-transfer" => {
|
|
ensure!(
|
|
!arg1.is_empty() && !arg2.is_empty(),
|
|
"Arguments <msg-id> <setup-code> expected"
|
|
);
|
|
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c_ptr) {
|
|
bail!("Continue key transfer failed");
|
|
}
|
|
}
|
|
"has-backup" => {
|
|
let ret = dc_imex_has_backup(context, context.get_blobdir());
|
|
if ret.is_null() {
|
|
println!("No backup found.");
|
|
}
|
|
}
|
|
"export-backup" => {
|
|
dc_imex(context, 11, context.get_blobdir(), 0 as *const libc::c_char);
|
|
}
|
|
"import-backup" => {
|
|
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
|
dc_imex(context, 12, arg1_c_ptr, 0 as *const libc::c_char);
|
|
}
|
|
"export-keys" => {
|
|
dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char);
|
|
}
|
|
"import-keys" => {
|
|
dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char);
|
|
}
|
|
"export-setup" => {
|
|
let setup_code: *mut libc::c_char = dc_create_setup_code(context);
|
|
let file_name: *mut libc::c_char = dc_mprintf(
|
|
b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
|
|
context.get_blobdir(),
|
|
);
|
|
let file_content: *mut libc::c_char;
|
|
file_content = dc_render_setup_file(context, setup_code);
|
|
if !file_content.is_null()
|
|
&& 0 != dc_write_file(
|
|
context,
|
|
file_name,
|
|
file_content as *const libc::c_void,
|
|
strlen(file_content),
|
|
)
|
|
{
|
|
println!(
|
|
"Setup message written to: {}\nSetup code: {}",
|
|
to_str(file_name),
|
|
to_str(setup_code),
|
|
)
|
|
} else {
|
|
bail!("");
|
|
}
|
|
free(file_content as *mut libc::c_void);
|
|
free(file_name as *mut libc::c_void);
|
|
free(setup_code as *mut libc::c_void);
|
|
}
|
|
"poke" => {
|
|
ensure!(0 != poke_spec(context, arg1_c_ptr), "Poke failed");
|
|
}
|
|
"reset" => {
|
|
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
|
|
let bits: i32 = arg1.parse().unwrap();
|
|
ensure!(bits < 16, "<bits> must be lower than 16.");
|
|
ensure!(0 != dc_reset_tables(context, bits), "Reset failed");
|
|
}
|
|
"stop" => {
|
|
dc_stop_ongoing_process(context);
|
|
}
|
|
"set" => {
|
|
ensure!(!arg1.is_empty(), "Argument <key> missing.");
|
|
ensure!(
|
|
0 != dc_set_config(context, arg1_c_ptr, arg2_c_ptr),
|
|
"Set config failed"
|
|
);
|
|
}
|
|
"get" => {
|
|
ensure!(!arg1.is_empty(), "Argument <key> missing.");
|
|
let val = dc_get_config(context, arg1_c_ptr);
|
|
println!("{}={}", arg1, to_string(val));
|
|
free(val as *mut libc::c_void);
|
|
}
|
|
"info" => {
|
|
println!("{}", to_string(dc_get_info(context)));
|
|
}
|
|
"maybenetwork" => {
|
|
dc_maybe_network(context);
|
|
}
|
|
"housekeeping" => {
|
|
dc_housekeeping(context);
|
|
}
|
|
"listchats" | "listarchived" | "chats" => {
|
|
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
|
|
let chatlist = dc_get_chatlist(context, listflags, arg1_c_ptr, 0 as uint32_t);
|
|
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
|
|
|
|
let mut i: libc::c_int;
|
|
let cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
|
|
if cnt > 0 {
|
|
info!(
|
|
context, 0,
|
|
"================================================================================"
|
|
);
|
|
|
|
i = cnt - 1;
|
|
|
|
while i >= 0 {
|
|
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
|
|
let temp_subtitle = dc_chat_get_subtitle(chat);
|
|
let temp_name = dc_chat_get_name(chat);
|
|
info!(
|
|
context,
|
|
0,
|
|
"{}#{}: {} [{}] [{} fresh]",
|
|
chat_prefix(chat),
|
|
dc_chat_get_id(chat) as libc::c_int,
|
|
to_str(temp_name),
|
|
to_str(temp_subtitle),
|
|
dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int,
|
|
);
|
|
free(temp_subtitle as *mut libc::c_void);
|
|
free(temp_name as *mut libc::c_void);
|
|
let lot = dc_chatlist_get_summary(chatlist, i as size_t, chat);
|
|
let statestr = if 0 != dc_chat_get_archived(chat) {
|
|
" [Archived]"
|
|
} else {
|
|
match dc_lot_get_state(lot) {
|
|
20 => " o",
|
|
26 => " √",
|
|
28 => " √√",
|
|
24 => " !!",
|
|
_ => "",
|
|
}
|
|
};
|
|
let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot));
|
|
let text1 = dc_lot_get_text1(lot);
|
|
let text2 = dc_lot_get_text2(lot);
|
|
info!(
|
|
context,
|
|
0,
|
|
"{}{}{}{} [{}]{}",
|
|
to_string(text1),
|
|
if !text1.is_null() { ": " } else { "" },
|
|
to_string(text2),
|
|
statestr,
|
|
to_str(timestr),
|
|
if 0 != dc_chat_is_sending_locations(chat) {
|
|
"📍"
|
|
} else {
|
|
""
|
|
},
|
|
);
|
|
free(text1 as *mut libc::c_void);
|
|
free(text2 as *mut libc::c_void);
|
|
free(timestr as *mut libc::c_void);
|
|
dc_lot_unref(lot);
|
|
dc_chat_unref(chat);
|
|
info!(
|
|
context, 0,
|
|
"================================================================================"
|
|
);
|
|
|
|
i -= 1
|
|
}
|
|
}
|
|
if 0 != dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
|
info!(context, 0, "Location streaming enabled.");
|
|
}
|
|
println!("{} chats", cnt);
|
|
dc_chatlist_unref(chatlist);
|
|
}
|
|
"chat" => {
|
|
if sel_chat.is_null() && arg1.is_empty() {
|
|
bail!("Argument [chat-id] is missing.");
|
|
}
|
|
if !sel_chat.is_null() && !arg1.is_empty() {
|
|
dc_chat_unref(sel_chat);
|
|
}
|
|
if !arg1.is_empty() {
|
|
let chat_id = arg1.parse().unwrap();
|
|
println!("Selecting chat #{}", chat_id);
|
|
sel_chat = dc_get_chat(context, chat_id);
|
|
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
|
}
|
|
|
|
ensure!(!sel_chat.is_null(), "Failed to select chat");
|
|
|
|
let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0);
|
|
let temp2 = dc_chat_get_subtitle(sel_chat);
|
|
let temp_name = dc_chat_get_name(sel_chat);
|
|
info!(
|
|
context,
|
|
0,
|
|
"{}#{}: {} [{}]{}",
|
|
chat_prefix(sel_chat),
|
|
dc_chat_get_id(sel_chat),
|
|
to_str(temp_name),
|
|
to_str(temp2),
|
|
if 0 != dc_chat_is_sending_locations(sel_chat) {
|
|
"📍"
|
|
} else {
|
|
""
|
|
},
|
|
);
|
|
free(temp_name as *mut libc::c_void);
|
|
free(temp2 as *mut libc::c_void);
|
|
if !msglist.is_null() {
|
|
log_msglist(context, msglist);
|
|
dc_array_unref(msglist);
|
|
}
|
|
let draft = dc_get_draft(context, dc_chat_get_id(sel_chat));
|
|
if !draft.is_null() {
|
|
log_msg(context, "Draft", draft);
|
|
dc_msg_unref(draft);
|
|
}
|
|
println!(
|
|
"{} messages.",
|
|
dc_get_msg_cnt(context, dc_chat_get_id(sel_chat))
|
|
);
|
|
dc_marknoticed_chat(context, dc_chat_get_id(sel_chat));
|
|
}
|
|
"createchat" => {
|
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
|
let contact_id: libc::c_int = arg1.parse().unwrap();
|
|
let chat_id: libc::c_int =
|
|
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
|
|
if chat_id != 0 {
|
|
println!("Single#{} created successfully.", chat_id,);
|
|
} else {
|
|
bail!("Failed to create chat");
|
|
}
|
|
}
|
|
"createchatbymsg" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
|
|
let msg_id_0: libc::c_int = arg1.parse().unwrap();
|
|
let chat_id_0: libc::c_int =
|
|
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
|
|
if chat_id_0 != 0 {
|
|
let chat_0: *mut dc_chat_t = dc_get_chat(context, chat_id_0 as uint32_t);
|
|
println!(
|
|
"{}#{} created successfully.",
|
|
chat_prefix(chat_0),
|
|
chat_id_0,
|
|
);
|
|
dc_chat_unref(chat_0);
|
|
} else {
|
|
bail!("");
|
|
}
|
|
}
|
|
"creategroup" => {
|
|
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
|
let chat_id_1: libc::c_int =
|
|
dc_create_group_chat(context, 0, arg1_c_ptr) as libc::c_int;
|
|
if chat_id_1 != 0 {
|
|
println!("Group#{} created successfully.", chat_id_1,);
|
|
} else {
|
|
bail!("Failed to create group");
|
|
}
|
|
}
|
|
"createverified" => {
|
|
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
|
let chat_id_2: libc::c_int =
|
|
dc_create_group_chat(context, 1, arg1_c_ptr) as libc::c_int;
|
|
if chat_id_2 != 0 {
|
|
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
|
|
} else {
|
|
bail!("Failed to create verified group");
|
|
}
|
|
}
|
|
"addmember" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected");
|
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
|
|
|
let contact_id_0: libc::c_int = arg1.parse().unwrap();
|
|
if 0 != dc_add_contact_to_chat(
|
|
context,
|
|
dc_chat_get_id(sel_chat),
|
|
contact_id_0 as uint32_t,
|
|
) {
|
|
println!("Contact added to chat.");
|
|
} else {
|
|
bail!("Cannot add contact to chat.");
|
|
}
|
|
}
|
|
"removemember" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
|
let contact_id_1: libc::c_int = arg1.parse().unwrap();
|
|
if 0 != dc_remove_contact_from_chat(
|
|
context,
|
|
dc_chat_get_id(sel_chat),
|
|
contact_id_1 as uint32_t,
|
|
) {
|
|
println!("Contact added to chat.");
|
|
} else {
|
|
bail!("Cannot remove member from chat.");
|
|
}
|
|
}
|
|
"groupname" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
|
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) {
|
|
println!("Chat name set");
|
|
} else {
|
|
bail!("Failed to set chat name");
|
|
}
|
|
}
|
|
"groupimage" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty(), "Argument <image> missing.");
|
|
|
|
if 0 != dc_set_chat_profile_image(
|
|
context,
|
|
dc_chat_get_id(sel_chat),
|
|
if !arg1.is_empty() {
|
|
arg1_c_ptr
|
|
} else {
|
|
std::ptr::null_mut()
|
|
},
|
|
) {
|
|
println!("Chat image set");
|
|
} else {
|
|
bail!("Failed to set chat image");
|
|
}
|
|
}
|
|
"chatinfo" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
|
|
let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat));
|
|
ensure!(!contacts.is_null(), "Failed to retreive contacts");
|
|
info!(context, 0, "Memberlist:");
|
|
|
|
log_contactlist(context, contacts);
|
|
println!(
|
|
"{} contacts\nLocation streaming: {}",
|
|
dc_array_get_cnt(contacts),
|
|
dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)),
|
|
);
|
|
dc_array_unref(contacts);
|
|
}
|
|
"getlocations" => {
|
|
let contact_id = arg1.parse().unwrap_or_default();
|
|
let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0);
|
|
let mut j = 0;
|
|
while j < dc_array_get_cnt(loc) {
|
|
let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t));
|
|
let marker = dc_array_get_marker(loc, j as size_t);
|
|
info!(
|
|
context,
|
|
0,
|
|
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
|
|
dc_array_get_id(loc, j as size_t),
|
|
to_str(timestr_0),
|
|
dc_array_get_latitude(loc, j as size_t),
|
|
dc_array_get_longitude(loc, j as size_t),
|
|
dc_array_get_accuracy(loc, j as size_t),
|
|
dc_array_get_chat_id(loc, j as size_t),
|
|
dc_array_get_contact_id(loc, j as size_t),
|
|
dc_array_get_msg_id(loc, j as size_t),
|
|
if !marker.is_null() {
|
|
to_str(marker)
|
|
} else {
|
|
"-"
|
|
},
|
|
);
|
|
free(timestr_0 as *mut libc::c_void);
|
|
free(marker as *mut libc::c_void);
|
|
j += 1
|
|
}
|
|
if dc_array_get_cnt(loc) == 0 {
|
|
info!(context, 0, "No locations.");
|
|
}
|
|
dc_array_unref(loc);
|
|
}
|
|
"sendlocations" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty(), "No timeout given.");
|
|
|
|
let seconds = arg1.parse().unwrap();
|
|
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
|
|
println!("Locations will be sent to Chat#{} for {} seconds. Use \'setlocation <lat> <lng>\' to play around.", dc_chat_get_id(sel_chat), seconds);
|
|
}
|
|
"setlocation" => {
|
|
ensure!(
|
|
!arg1.is_empty() && !arg2.is_empty(),
|
|
"Latitude or longitude not given."
|
|
);
|
|
let latitude = arg1.parse().unwrap();
|
|
let longitude = arg2.parse().unwrap();
|
|
|
|
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
|
|
if 0 != continue_streaming {
|
|
println!("Success, streaming should be continued.");
|
|
} else {
|
|
println!("Success, streaming can be stoppped.");
|
|
}
|
|
}
|
|
"dellocations" => {
|
|
dc_delete_all_locations(context);
|
|
}
|
|
"send" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty(), "No message text given.");
|
|
|
|
let msg = to_cstring(format!("{} {}", arg1, arg2));
|
|
|
|
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) {
|
|
println!("Message sent.");
|
|
} else {
|
|
bail!("Sending failed.");
|
|
}
|
|
}
|
|
"sendempty" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
if 0 != dc_send_text_msg(
|
|
context,
|
|
dc_chat_get_id(sel_chat),
|
|
b"\x00" as *const u8 as *const libc::c_char,
|
|
) {
|
|
println!("Message sent.");
|
|
} else {
|
|
bail!("Sending failed.");
|
|
}
|
|
}
|
|
"sendimage" | "sendfile" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
|
|
|
let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 });
|
|
dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char);
|
|
dc_msg_set_text(msg_0, arg2_c_ptr);
|
|
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
|
|
dc_msg_unref(msg_0);
|
|
}
|
|
"listmsgs" => {
|
|
ensure!(!arg1.is_empty(), "Argument <query> missing.");
|
|
|
|
let chat = if !sel_chat.is_null() {
|
|
dc_chat_get_id(sel_chat)
|
|
} else {
|
|
0 as libc::c_uint
|
|
};
|
|
|
|
let msglist_0 = dc_search_msgs(context, chat, arg1_c_ptr);
|
|
|
|
if !msglist_0.is_null() {
|
|
log_msglist(context, msglist_0);
|
|
println!("{} messages.", dc_array_get_cnt(msglist_0));
|
|
dc_array_unref(msglist_0);
|
|
}
|
|
}
|
|
"draft" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
|
|
if !arg1.is_empty() {
|
|
let draft_0 = dc_msg_new(context, 10);
|
|
dc_msg_set_text(draft_0, arg1_c_ptr);
|
|
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
|
|
dc_msg_unref(draft_0);
|
|
println!("Draft saved.");
|
|
} else {
|
|
dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t);
|
|
println!("Draft deleted.");
|
|
}
|
|
}
|
|
"listmedia" => {
|
|
ensure!(!sel_chat.is_null(), "No chat selected.");
|
|
|
|
let images = dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20, 21, 50);
|
|
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
|
|
println!("{} images or videos: ", icnt);
|
|
for i in 0..icnt {
|
|
let data = dc_array_get_id(images, i as size_t);
|
|
if 0 == i {
|
|
print!("Msg#{}", data);
|
|
} else {
|
|
print!(", Msg#{}", data);
|
|
}
|
|
}
|
|
print!("\n");
|
|
dc_array_unref(images);
|
|
}
|
|
"archive" | "unarchive" => {
|
|
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
|
let chat_id = arg1.parse().unwrap();
|
|
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
|
|
}
|
|
"delchat" => {
|
|
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
|
let chat_id = arg1.parse().unwrap();
|
|
dc_delete_chat(context, chat_id);
|
|
}
|
|
"msginfo" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
|
let id = arg1.parse().unwrap();
|
|
let res = dc_get_msg_info(context, id);
|
|
println!("{}", to_str(res));
|
|
}
|
|
"listfresh" => {
|
|
let msglist = dc_get_fresh_msgs(context);
|
|
ensure!(!msglist.is_null(), "Failed to retrieve messages");
|
|
|
|
log_msglist(context, msglist);
|
|
print!("{} fresh messages.", dc_array_get_cnt(msglist));
|
|
dc_array_unref(msglist);
|
|
}
|
|
"forward" => {
|
|
ensure!(
|
|
!arg1.is_empty() && arg2.is_empty(),
|
|
"Arguments <msg-id> <chat-id> expected"
|
|
);
|
|
|
|
let mut msg_ids = [0; 1];
|
|
let chat_id = arg2.parse().unwrap();
|
|
msg_ids[0] = arg1.parse().unwrap();
|
|
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
|
}
|
|
"markseen" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
|
let mut msg_ids = [0; 1];
|
|
msg_ids[0] = arg1.parse().unwrap();
|
|
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
|
|
}
|
|
"star" | "unstar" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
|
let mut msg_ids = [0; 1];
|
|
msg_ids[0] = arg1.parse().unwrap();
|
|
dc_star_msgs(
|
|
context,
|
|
msg_ids.as_mut_ptr(),
|
|
1,
|
|
if arg0 == "star" { 1 } else { 0 },
|
|
);
|
|
}
|
|
"delmsg" => {
|
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
|
let mut ids = [0; 1];
|
|
ids[0] = arg1.parse().unwrap();
|
|
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
|
|
}
|
|
"listcontacts" | "contacts" | "listverified" => {
|
|
let contacts = dc_get_contacts(
|
|
context,
|
|
if arg0 == "listverified" {
|
|
0x1 | 0x2
|
|
} else {
|
|
0x2
|
|
},
|
|
arg1_c_ptr,
|
|
);
|
|
if !contacts.is_null() {
|
|
log_contactlist(context, contacts);
|
|
println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,);
|
|
dc_array_unref(contacts);
|
|
} else {
|
|
bail!("");
|
|
}
|
|
}
|
|
"addcontact" => {
|
|
ensure!(!arg1.is_empty(), "Arguments [<name>] <addr> expected.");
|
|
|
|
if !arg2.is_empty() {
|
|
let book = dc_mprintf(
|
|
b"%s\n%s\x00" as *const u8 as *const libc::c_char,
|
|
arg1_c_ptr,
|
|
arg2_c_ptr,
|
|
);
|
|
dc_add_address_book(context, book);
|
|
free(book as *mut libc::c_void);
|
|
} else {
|
|
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) {
|
|
bail!("Failed to create contact");
|
|
}
|
|
}
|
|
}
|
|
"contactinfo" => {
|
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
|
|
|
let contact_id = arg1.parse().unwrap();
|
|
let contact = dc_get_contact(context, contact_id);
|
|
let name_n_addr = dc_contact_get_name_n_addr(contact);
|
|
|
|
let mut res = format!("Contact info for: {}:\n\n", to_str(name_n_addr),);
|
|
free(name_n_addr as *mut libc::c_void);
|
|
dc_contact_unref(contact);
|
|
|
|
let encrinfo = dc_get_contact_encrinfo(context, contact_id);
|
|
res += to_str(encrinfo);
|
|
free(encrinfo as *mut libc::c_void);
|
|
|
|
let chatlist = dc_get_chatlist(context, 0, 0 as *const libc::c_char, contact_id);
|
|
let chatlist_cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
|
|
if chatlist_cnt > 0 {
|
|
res += &format!(
|
|
"\n\n{} chats shared with Contact#{}: ",
|
|
chatlist_cnt, contact_id,
|
|
);
|
|
for i in 0..chatlist_cnt {
|
|
if 0 != i {
|
|
res += ", ";
|
|
}
|
|
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
|
|
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
|
|
dc_chat_unref(chat);
|
|
}
|
|
}
|
|
dc_chatlist_unref(chatlist);
|
|
println!("{}", res);
|
|
}
|
|
"delcontact" => {
|
|
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
|
if !dc_delete_contact(context, arg1.parse().unwrap()) {
|
|
bail!("Failed to delete contact");
|
|
}
|
|
}
|
|
"checkqr" => {
|
|
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
|
let res = dc_check_qr(context, arg1_c_ptr);
|
|
println!(
|
|
"state={}, id={}, text1={}, text2={}",
|
|
(*res).state as libc::c_int,
|
|
(*res).id,
|
|
to_string((*res).text1),
|
|
to_string((*res).text2)
|
|
);
|
|
dc_lot_unref(res);
|
|
}
|
|
"event" => {
|
|
ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
|
let event = Event::from_u32(arg1.parse().unwrap()).unwrap();
|
|
let r = (context.cb)(context, event, 0 as uintptr_t, 0 as uintptr_t);
|
|
println!(
|
|
"Sending event {:?}({}), received value {}.",
|
|
event, event as usize, r as libc::c_int,
|
|
);
|
|
}
|
|
"fileinfo" => {
|
|
ensure!(!arg1.is_empty(), "Argument <file> missing.");
|
|
let mut buf = 0 as *mut libc::c_uchar;
|
|
let mut buf_bytes = 0;
|
|
let mut w = 0;
|
|
let mut h = 0;
|
|
|
|
if 0 != dc_read_file(
|
|
context,
|
|
arg1_c_ptr,
|
|
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
|
|
&mut buf_bytes,
|
|
) {
|
|
dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h);
|
|
println!("width={}, height={}", w, h,);
|
|
free(buf as *mut libc::c_void);
|
|
} else {
|
|
bail!("Command failed.");
|
|
}
|
|
}
|
|
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
|
}
|
|
|
|
if !sel_chat.is_null() {
|
|
dc_chat_unref(sel_chat);
|
|
}
|
|
|
|
Ok(())
|
|
}
|