mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
feat(examples): refactor repl to use rustyline and safe rust
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -7,4 +7,7 @@ Cargo.lock
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
include
|
||||
include
|
||||
.dc-history.txt
|
||||
*.db
|
||||
*.db-blobs
|
||||
|
||||
@@ -29,6 +29,10 @@ percent-encoding = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
chrono = "0.4.6"
|
||||
failure = "0.1.5"
|
||||
# TODO: make optional
|
||||
rustyline = { git = "https://github.com/kkawakam/rustyline/" }
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
|
||||
31
README.md
31
README.md
@@ -2,27 +2,10 @@
|
||||
|
||||
> Project porting deltachat-core to rust
|
||||
|
||||
|
||||
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor]
|
||||
|
||||
Current commit on deltachat/deltachat-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`.
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
# run example
|
||||
$ cargo run --example simple
|
||||
# build c-ffi
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
# run tests
|
||||
$ cargo test --all
|
||||
```
|
||||
|
||||
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
||||
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
||||
|
||||
## Using the CLI client
|
||||
|
||||
Run using `cargo`:
|
||||
@@ -73,3 +56,17 @@ For more commands type `> help`.
|
||||
```
|
||||
> help
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
# run tests
|
||||
$ cargo test --all
|
||||
# build c-ffi
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
```
|
||||
|
||||
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
||||
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,19 @@
|
||||
//! This is a CLI program and a little testing frame. This file must not be
|
||||
//! included when using Delta Chat Core as a library.
|
||||
//!
|
||||
//! Usage: messenger-backend <databasefile>
|
||||
//! (for "Code::Blocks, use Project / Set programs' arguments")
|
||||
//! all further options can be set using the set-command (type ? for help).
|
||||
//! Usage: cargo run --example repl --release -- <databasefile>
|
||||
//! All further options can be set using the set-command (type ? for help).
|
||||
|
||||
#![allow(
|
||||
mutable_transmutes,
|
||||
non_camel_case_types,
|
||||
non_snake_case,
|
||||
non_upper_case_globals,
|
||||
non_upper_case_globals,
|
||||
non_camel_case_types,
|
||||
non_snake_case
|
||||
)]
|
||||
#[macro_use]
|
||||
extern crate deltachat;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
@@ -28,13 +24,19 @@ use deltachat::dc_tools::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
||||
use rustyline::config::OutputStreamType;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
|
||||
use rustyline::hint::{Hinter, HistoryHinter};
|
||||
use rustyline::{
|
||||
Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyPress,
|
||||
};
|
||||
|
||||
mod cmdline;
|
||||
use self::cmdline::*;
|
||||
|
||||
/* ******************************************************************************
|
||||
* Event Handler
|
||||
******************************************************************************/
|
||||
// Event Handler
|
||||
|
||||
unsafe extern "C" fn receive_event(
|
||||
_context: &Context,
|
||||
@@ -42,259 +44,479 @@ unsafe extern "C" fn receive_event(
|
||||
data1: uintptr_t,
|
||||
data2: uintptr_t,
|
||||
) -> uintptr_t {
|
||||
match event as u32 {
|
||||
2091 => {}
|
||||
100 => {
|
||||
match event {
|
||||
Event::GET_STRING => {}
|
||||
Event::INFO => {
|
||||
/* do not show the event as this would fill the screen */
|
||||
println!("{}", to_string(data2 as *const _),);
|
||||
}
|
||||
101 => {
|
||||
Event::SMTP_CONNECTED => {
|
||||
println!("[DC_EVENT_SMTP_CONNECTED] {}", to_string(data2 as *const _));
|
||||
}
|
||||
102 => {
|
||||
Event::IMAP_CONNECTED => {
|
||||
println!("[DC_EVENT_IMAP_CONNECTED] {}", to_string(data2 as *const _),);
|
||||
}
|
||||
103 => {
|
||||
Event::SMTP_MESSAGE_SENT => {
|
||||
println!(
|
||||
"[DC_EVENT_SMTP_MESSAGE_SENT] {}",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
}
|
||||
300 => {
|
||||
Event::WARNING => {
|
||||
println!("[Warning] {}", to_string(data2 as *const _),);
|
||||
}
|
||||
400 => {
|
||||
Event::ERROR => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
}
|
||||
401 => {
|
||||
Event::ERROR_NETWORK => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR_NETWORK] first={}, msg={}\x1b[0m",
|
||||
data1 as libc::c_int,
|
||||
data1 as usize,
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
}
|
||||
410 => {
|
||||
Event::ERROR_SELF_NOT_IN_GROUP => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
}
|
||||
2081 => {
|
||||
print!("\x1b[33m{{Received DC_EVENT_IS_OFFLINE()}}\n\x1b[0m");
|
||||
}
|
||||
2000 => {
|
||||
Event::MSGS_CHANGED => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED({}, {})}}\n\x1b[0m",
|
||||
data1 as libc::c_int, data2 as libc::c_int,
|
||||
data1 as usize, data2 as usize,
|
||||
);
|
||||
}
|
||||
2030 => {
|
||||
Event::CONTACTS_CHANGED => {
|
||||
print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m");
|
||||
}
|
||||
2035 => {
|
||||
Event::LOCATION_CHANGED => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={})}}\n\x1b[0m",
|
||||
data1 as libc::c_int,
|
||||
data1 as usize,
|
||||
);
|
||||
}
|
||||
2041 => {
|
||||
Event::CONFIGURE_PROGRESS => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
data1 as libc::c_int,
|
||||
data1 as usize,
|
||||
);
|
||||
}
|
||||
2051 => {
|
||||
Event::IMEX_PROGRESS => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
data1 as libc::c_int,
|
||||
data1 as usize,
|
||||
);
|
||||
}
|
||||
2052 => {
|
||||
Event::IMEX_FILE_WRITTEN => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m",
|
||||
to_string(data1 as *const _)
|
||||
);
|
||||
}
|
||||
2055 => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_FILE_COPIED({})}}\n\x1b[0m",
|
||||
to_string(data1 as *const _)
|
||||
);
|
||||
}
|
||||
2020 => {
|
||||
Event::CHAT_MODIFIED => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m",
|
||||
data1 as libc::c_int,
|
||||
data1 as usize,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_{}({}, {})}}\n\x1b[0m",
|
||||
event as libc::c_int, data1 as libc::c_int, data2 as libc::c_int,
|
||||
"\x1b[33m{{Received {:?}({}, {})}}\n\x1b[0m",
|
||||
event, data1 as usize, data2 as usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
return 0i32 as uintptr_t;
|
||||
}
|
||||
/* ******************************************************************************
|
||||
* Threads for waiting for messages and for jobs
|
||||
******************************************************************************/
|
||||
static mut run_threads: libc::c_int = 0i32;
|
||||
|
||||
unsafe fn start_threads(
|
||||
c: Arc<RwLock<Context>>,
|
||||
) -> (
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
) {
|
||||
run_threads = 1;
|
||||
0
|
||||
}
|
||||
|
||||
// Threads for waiting for messages and for jobs
|
||||
|
||||
lazy_static! {
|
||||
static ref HANDLE: Arc<Mutex<Option<Handle>>> = Arc::new(Mutex::new(None));
|
||||
static ref IS_RUNNING: AtomicBool = AtomicBool::new(true);
|
||||
}
|
||||
|
||||
struct Handle {
|
||||
handle_imap: Option<std::thread::JoinHandle<()>>,
|
||||
handle_mvbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_sentbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_smtp: Option<std::thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
macro_rules! while_running {
|
||||
($code:block) => {
|
||||
if IS_RUNNING.load(Ordering::Relaxed) {
|
||||
$code
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Starting threads");
|
||||
IS_RUNNING.store(true, Ordering::Relaxed);
|
||||
|
||||
let ctx = c.clone();
|
||||
let h1 = std::thread::spawn(move || {
|
||||
let context = ctx.read().unwrap();
|
||||
while 0 != run_threads {
|
||||
dc_perform_imap_jobs(&context);
|
||||
dc_perform_imap_fetch(&context);
|
||||
if 0 != run_threads {
|
||||
let handle_imap = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe {
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
dc_perform_imap_fetch(&ctx.read().unwrap());
|
||||
}
|
||||
while_running!({
|
||||
let context = ctx.read().unwrap();
|
||||
dc_perform_imap_idle(&context);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let h2 = std::thread::spawn(move || {
|
||||
let context = ctx.read().unwrap();
|
||||
while 0 != run_threads {
|
||||
dc_perform_mvbox_fetch(&context);
|
||||
if 0 != run_threads {
|
||||
dc_perform_mvbox_idle(&context);
|
||||
}
|
||||
}
|
||||
let handle_mvbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let h3 = std::thread::spawn(move || {
|
||||
let context = ctx.read().unwrap();
|
||||
while 0 != run_threads {
|
||||
dc_perform_sentbox_fetch(&context);
|
||||
if 0 != run_threads {
|
||||
dc_perform_sentbox_idle(&context);
|
||||
}
|
||||
}
|
||||
let handle_sentbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let h4 = std::thread::spawn(move || {
|
||||
let context = ctx.read().unwrap();
|
||||
while 0 != run_threads {
|
||||
dc_perform_smtp_jobs(&context);
|
||||
if 0 != run_threads {
|
||||
dc_perform_smtp_idle(&context);
|
||||
}
|
||||
}
|
||||
let ctx = c;
|
||||
let handle_smtp = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
(h1, h2, h3, h4)
|
||||
*HANDLE.clone().lock().unwrap() = Some(Handle {
|
||||
handle_imap: Some(handle_imap),
|
||||
handle_mvbox: Some(handle_mvbox),
|
||||
handle_sentbox: Some(handle_sentbox),
|
||||
handle_smtp: Some(handle_smtp),
|
||||
});
|
||||
}
|
||||
|
||||
unsafe fn stop_threads(
|
||||
context: &Context,
|
||||
handles: Option<(
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
std::thread::JoinHandle<()>,
|
||||
)>,
|
||||
) {
|
||||
run_threads = 0i32;
|
||||
dc_interrupt_imap_idle(context);
|
||||
dc_interrupt_mvbox_idle(context);
|
||||
dc_interrupt_sentbox_idle(context);
|
||||
dc_interrupt_smtp_idle(context);
|
||||
if let Some((h1, h2, h3, h4)) = handles {
|
||||
h1.join().unwrap();
|
||||
h2.join().unwrap();
|
||||
h3.join().unwrap();
|
||||
h4.join().unwrap();
|
||||
fn stop_threads(context: &Context) {
|
||||
if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() {
|
||||
println!("Stopping threads");
|
||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||
|
||||
unsafe {
|
||||
dc_interrupt_imap_idle(context);
|
||||
dc_interrupt_mvbox_idle(context);
|
||||
dc_interrupt_sentbox_idle(context);
|
||||
dc_interrupt_smtp_idle(context);
|
||||
}
|
||||
|
||||
handle.handle_imap.take().unwrap().join().unwrap();
|
||||
handle.handle_mvbox.take().unwrap().join().unwrap();
|
||||
handle.handle_sentbox.take().unwrap().join().unwrap();
|
||||
handle.handle_smtp.take().unwrap().join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* The main loop
|
||||
******************************************************************************/
|
||||
fn read_cmd() -> String {
|
||||
print!("> ");
|
||||
io::stdout().flush().unwrap();
|
||||
// === The main loop
|
||||
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input).unwrap();
|
||||
input.trim_end().to_string()
|
||||
struct DcHelper {
|
||||
completer: FilenameCompleter,
|
||||
highlighter: MatchingBracketHighlighter,
|
||||
hinter: HistoryHinter,
|
||||
colored_prompt: String,
|
||||
}
|
||||
|
||||
unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int {
|
||||
let mut cmd: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
impl Completer for DcHelper {
|
||||
type Candidate = Pair;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
ctx: &RustyContext<'_>,
|
||||
) -> Result<(usize, Vec<Pair>), ReadlineError> {
|
||||
self.completer.complete(line, pos, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
const IMEX_COMMANDS: [&'static str; 12] = [
|
||||
"initiate-key-transfer",
|
||||
"get-setupcodebegin",
|
||||
"continue-key-transfer",
|
||||
"has-backup",
|
||||
"export-backup",
|
||||
"import-backup",
|
||||
"export-keys",
|
||||
"import-keys",
|
||||
"export-setup",
|
||||
"poke",
|
||||
"reset",
|
||||
"stop",
|
||||
];
|
||||
|
||||
const DB_COMMANDS: [&'static str; 11] = [
|
||||
"info",
|
||||
"open",
|
||||
"close",
|
||||
"set",
|
||||
"get",
|
||||
"oauth2",
|
||||
"configure",
|
||||
"connect",
|
||||
"disconnect",
|
||||
"maybenetwork",
|
||||
"housekeeping",
|
||||
];
|
||||
|
||||
const CHAT_COMMANDS: [&'static str; 24] = [
|
||||
"listchats",
|
||||
"listarchived",
|
||||
"chat",
|
||||
"createchat",
|
||||
"createchatbymsg",
|
||||
"creategroup",
|
||||
"createverified",
|
||||
"addmember",
|
||||
"removemember",
|
||||
"groupname",
|
||||
"groupimage",
|
||||
"chatinfo",
|
||||
"sendlocations",
|
||||
"setlocation",
|
||||
"dellocations",
|
||||
"getlocations",
|
||||
"send",
|
||||
"sendimage",
|
||||
"sendfile",
|
||||
"draft",
|
||||
"listmedia",
|
||||
"archive",
|
||||
"unarchive",
|
||||
"delchat",
|
||||
];
|
||||
const MESSAGE_COMMANDS: [&'static str; 8] = [
|
||||
"listmsgs",
|
||||
"msginfo",
|
||||
"listfresh",
|
||||
"forward",
|
||||
"markseen",
|
||||
"star",
|
||||
"unstar",
|
||||
"delmsg",
|
||||
];
|
||||
const CONTACT_COMMANDS: [&'static str; 6] = [
|
||||
"listcontacts",
|
||||
"listverified",
|
||||
"addcontact",
|
||||
"contactinfo",
|
||||
"delcontact",
|
||||
"cleanupcontacts",
|
||||
];
|
||||
const MISC_COMMANDS: [&'static str; 8] = [
|
||||
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "help",
|
||||
];
|
||||
|
||||
impl Hinter for DcHelper {
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &RustyContext<'_>) -> Option<String> {
|
||||
if !line.is_empty() {
|
||||
for &cmds in &[
|
||||
&IMEX_COMMANDS[..],
|
||||
&DB_COMMANDS[..],
|
||||
&CHAT_COMMANDS[..],
|
||||
&MESSAGE_COMMANDS[..],
|
||||
&CONTACT_COMMANDS[..],
|
||||
&MISC_COMMANDS[..],
|
||||
] {
|
||||
if let Some(entry) = cmds.iter().find(|el| el.starts_with(&line[..pos])) {
|
||||
if *entry != line && *entry != &line[..pos] {
|
||||
return Some(entry[pos..].to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.hinter.hint(line, pos, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter for DcHelper {
|
||||
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
|
||||
&'s self,
|
||||
prompt: &'p str,
|
||||
default: bool,
|
||||
) -> Cow<'b, str> {
|
||||
if default {
|
||||
Borrowed(&self.colored_prompt)
|
||||
} else {
|
||||
Borrowed(prompt)
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
||||
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
|
||||
self.highlighter.highlight(line, pos)
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, pos: usize) -> bool {
|
||||
self.highlighter.highlight_char(line, pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Helper for DcHelper {}
|
||||
|
||||
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
let mut context = dc_context_new(
|
||||
receive_event,
|
||||
0 as *mut libc::c_void,
|
||||
b"CLI\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
|
||||
dc_cmdline_skip_auth();
|
||||
unsafe { dc_cmdline_skip_auth() };
|
||||
|
||||
if argc == 2i32 {
|
||||
if 0 == dc_open(&mut context, *argv.offset(1isize), 0 as *const libc::c_char) {
|
||||
println!(
|
||||
"ERROR: Cannot open {}.",
|
||||
to_string(*argv.offset(1isize) as *const _)
|
||||
);
|
||||
if args.len() == 2 {
|
||||
if 0 == unsafe {
|
||||
dc_open(
|
||||
&mut context,
|
||||
to_cstring(&args[1]).as_ptr(),
|
||||
0 as *const libc::c_char,
|
||||
)
|
||||
} {
|
||||
println!("Error: Cannot open {}.", args[0],);
|
||||
}
|
||||
} else if argc != 1i32 {
|
||||
println!("ERROR: Bad arguments");
|
||||
} else if args.len() != 1 {
|
||||
println!("Error: Bad arguments, expected [db-name].");
|
||||
}
|
||||
|
||||
println!("Delta Chat Core is awaiting your commands.");
|
||||
|
||||
let mut handles = None;
|
||||
|
||||
let ctx = Arc::new(RwLock::new(context));
|
||||
|
||||
let config = Config::builder()
|
||||
.history_ignore_space(true)
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.output_stream(OutputStreamType::Stdout)
|
||||
.build();
|
||||
let h = DcHelper {
|
||||
completer: FilenameCompleter::new(),
|
||||
highlighter: MatchingBracketHighlighter::new(),
|
||||
hinter: HistoryHinter {},
|
||||
colored_prompt: "".to_owned(),
|
||||
};
|
||||
let mut rl = Editor::with_config(config);
|
||||
rl.set_helper(Some(h));
|
||||
rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward);
|
||||
rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward);
|
||||
if rl.load_history(".dc-history.txt").is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
loop {
|
||||
/* read command */
|
||||
let cmdline = read_cmd();
|
||||
free(cmd as *mut libc::c_void);
|
||||
cmd = dc_strdup(CString::new(cmdline.clone()).unwrap().as_ptr());
|
||||
let mut arg1: *mut libc::c_char = strchr(cmd, ' ' as i32);
|
||||
if !arg1.is_null() {
|
||||
*arg1 = 0i32 as libc::c_char;
|
||||
arg1 = arg1.offset(1isize)
|
||||
let p = "> ";
|
||||
rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
|
||||
let readline = rl.readline(&p);
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
// TODO: ignore "set mail_pw"
|
||||
rl.add_history_entry(line.as_str());
|
||||
let ctx = ctx.clone();
|
||||
match unsafe { handle_cmd(line.as_str(), ctx) } {
|
||||
Ok(ExitResult::Continue) => {}
|
||||
Ok(ExitResult::Exit) => break,
|
||||
Err(err) => println!("Error: {}", err),
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
|
||||
println!("Exiting...");
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if strcmp(cmd, b"connect\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
handles = Some(start_threads(ctx.clone()));
|
||||
} else if strcmp(cmd, b"disconnect\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
stop_threads(&ctx.read().unwrap(), handles);
|
||||
handles = None;
|
||||
} else if strcmp(cmd, b"smtp-jobs\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
if 0 != run_threads {
|
||||
}
|
||||
rl.save_history(".dc-history.txt")?;
|
||||
println!("history saved");
|
||||
{
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
|
||||
unsafe {
|
||||
let mut ctx = ctx.write().unwrap();
|
||||
dc_close(&mut ctx);
|
||||
dc_context_unref(&mut ctx);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ExitResult {
|
||||
Continue,
|
||||
Exit,
|
||||
}
|
||||
|
||||
unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failure::Error> {
|
||||
let mut args = line.splitn(2, ' ');
|
||||
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()
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
"connect" => {
|
||||
start_threads(ctx);
|
||||
}
|
||||
"disconnect" => {
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
}
|
||||
"smtp-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("smtp-jobs are already running in a thread.",);
|
||||
} else {
|
||||
dc_perform_smtp_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
} else if strcmp(cmd, b"imap-jobs\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
if 0 != run_threads {
|
||||
}
|
||||
"imap-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("imap-jobs are already running in a thread.");
|
||||
} else {
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
} else if strcmp(cmd, b"configure\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
handles = { Some(start_threads(ctx.clone())) };
|
||||
}
|
||||
"configure" => {
|
||||
start_threads(ctx.clone());
|
||||
dc_configure(&ctx.read().unwrap());
|
||||
} else if strcmp(cmd, b"oauth2\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
let addr: *mut libc::c_char = dc_get_config(
|
||||
}
|
||||
"oauth2" => {
|
||||
let addr = dc_get_config(
|
||||
&ctx.read().unwrap(),
|
||||
b"addr\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
@@ -313,19 +535,17 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int
|
||||
}
|
||||
}
|
||||
free(addr as *mut libc::c_void);
|
||||
} else if strcmp(cmd, b"clear\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
}
|
||||
"clear" => {
|
||||
println!("\n\n\n");
|
||||
print!("\x1b[1;1H\x1b[2J");
|
||||
} else if strcmp(cmd, b"getqr\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
handles = Some(start_threads(ctx.clone()));
|
||||
let qrstr: *mut libc::c_char =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), dc_atoi_null_is_0(arg1) as u32);
|
||||
}
|
||||
"getqr" | "getbadqr" => {
|
||||
start_threads(ctx.clone());
|
||||
let qrstr =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default());
|
||||
if !qrstr.is_null() && 0 != *qrstr.offset(0isize) as libc::c_int {
|
||||
if strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
&& strlen(qrstr) > 40
|
||||
{
|
||||
if arg0 == "getbadqr" && strlen(qrstr) > 40 {
|
||||
let mut i: libc::c_int = 12i32;
|
||||
while i < 22i32 {
|
||||
*qrstr.offset(i as isize) = '0' as i32 as libc::c_char;
|
||||
@@ -333,7 +553,7 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int
|
||||
}
|
||||
}
|
||||
println!("{}", to_string(qrstr as *const _));
|
||||
let syscmd: *mut libc::c_char = dc_mprintf(
|
||||
let syscmd = dc_mprintf(
|
||||
b"qrencode -t ansiutf8 \"%s\" -o -\x00" as *const u8 as *const libc::c_char,
|
||||
qrstr,
|
||||
);
|
||||
@@ -341,55 +561,25 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int
|
||||
free(syscmd as *mut libc::c_void);
|
||||
}
|
||||
free(qrstr as *mut libc::c_void);
|
||||
} else if strcmp(cmd, b"joinqr\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
handles = Some(start_threads(ctx.clone()));
|
||||
if !arg1.is_null() {
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1);
|
||||
}
|
||||
} else {
|
||||
if strcmp(cmd, b"exit\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
break;
|
||||
}
|
||||
if !(*cmd.offset(0isize) as libc::c_int == 0i32) {
|
||||
let execute_result: *mut libc::c_char = dc_cmdline(&ctx.read().unwrap(), &cmdline);
|
||||
if !execute_result.is_null() {
|
||||
println!("{}", to_string(execute_result as *const _));
|
||||
free(execute_result as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
"joinqr" => {
|
||||
start_threads(ctx.clone());
|
||||
if !arg0.is_empty() {
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1_c_ptr);
|
||||
}
|
||||
}
|
||||
"exit" => return Ok(ExitResult::Exit),
|
||||
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
|
||||
}
|
||||
|
||||
let ctx = ctx.clone();
|
||||
|
||||
{
|
||||
let mut ctx = ctx.write().unwrap();
|
||||
free(cmd as *mut libc::c_void);
|
||||
stop_threads(&ctx, handles);
|
||||
dc_close(&mut ctx);
|
||||
dc_context_unref(&mut ctx);
|
||||
}
|
||||
0
|
||||
Ok(ExitResult::Continue)
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
pub fn main() -> Result<(), failure::Error> {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let mut args: Vec<*mut libc::c_char> = Vec::new();
|
||||
for arg in ::std::env::args() {
|
||||
args.push(
|
||||
::std::ffi::CString::new(arg)
|
||||
.expect("Failed to convert argument into CString.")
|
||||
.into_raw(),
|
||||
);
|
||||
}
|
||||
args.push(::std::ptr::null_mut());
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
main_0(args)?;
|
||||
|
||||
let res = unsafe {
|
||||
main_0(
|
||||
(args.len() - 1) as libc::c_int,
|
||||
args.as_mut_ptr() as *mut *mut libc::c_char,
|
||||
)
|
||||
};
|
||||
::std::process::exit(res)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -740,7 +740,7 @@ unsafe fn prepare_msg_raw(
|
||||
VALUES (?,?,?, ?,?,1);\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int64(stmt, 1, timestamp as libc::int64_t);
|
||||
sqlite3_bind_int64(stmt, 1, timestamp as i64);
|
||||
sqlite3_bind_int(stmt, 2, DC_CONTACT_ID_SELF as libc::c_int);
|
||||
sqlite3_bind_int(stmt, 3, (*chat).id as libc::c_int);
|
||||
sqlite3_bind_double(
|
||||
|
||||
@@ -96,10 +96,10 @@ macro_rules! info {
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
unsafe {
|
||||
($ctx.cb)($ctx, crate::constants::Event::INFO, $data1 as uintptr_t,
|
||||
crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
($ctx.cb)($ctx, $crate::constants::Event::INFO, $data1 as uintptr_t,
|
||||
$crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -111,10 +111,10 @@ macro_rules! warn {
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
unsafe {
|
||||
($ctx.cb)($ctx, crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as libc::uintptr_t)
|
||||
($ctx.cb)($ctx, $crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
$crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as libc::uintptr_t)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -126,10 +126,10 @@ macro_rules! error {
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
unsafe {
|
||||
($ctx.cb)($ctx, crate::constants::Event::ERROR, $data1 as uintptr_t,
|
||||
crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
($ctx.cb)($ctx, $crate::constants::Event::ERROR, $data1 as uintptr_t,
|
||||
$crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -141,10 +141,10 @@ macro_rules! log_event {
|
||||
};
|
||||
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
unsafe {
|
||||
($ctx.cb)($ctx, $event, $data1 as uintptr_t,
|
||||
crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
$crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,15 +38,15 @@ pub type dc_set_config_t =
|
||||
pub type dc_get_config_t =
|
||||
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
|
||||
|
||||
pub type sqlite_int64 = libc::int64_t;
|
||||
pub type sqlite_int64 = i64;
|
||||
pub type sqlite3_int64 = sqlite_int64;
|
||||
|
||||
pub type int32_t = libc::int32_t;
|
||||
pub type int64_t = libc::int64_t;
|
||||
pub type int32_t = i32;
|
||||
pub type int64_t = i64;
|
||||
pub type uintptr_t = libc::uintptr_t;
|
||||
pub type size_t = libc::size_t;
|
||||
pub type ssize_t = libc::ssize_t;
|
||||
pub type uint32_t = libc::c_uint;
|
||||
pub type uint8_t = libc::c_uchar;
|
||||
pub type uint16_t = libc::c_ushort;
|
||||
pub type uint64_t = libc::uint64_t;
|
||||
pub type uint64_t = u64;
|
||||
|
||||
Reference in New Issue
Block a user