address all @dignifiedquire review comments

This commit is contained in:
holger krekel
2019-09-25 22:15:13 +02:00
parent 6cd3580263
commit e1dc4b69f5
2 changed files with 105 additions and 130 deletions

View File

@@ -44,13 +44,10 @@ pub fn dc_imex(context: &Context, what: libc::c_int, param1: Option<impl AsRef<P
/// Returns the filename of the backup if found, nullptr otherwise. /// Returns the filename of the backup if found, nullptr otherwise.
pub fn dc_imex_has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> { pub fn dc_imex_has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> {
let dir_name = dir_name.as_ref(); let dir_name = dir_name.as_ref();
let dir_iter = std::fs::read_dir(dir_name); let dir_iter = std::fs::read_dir(dir_name)?;
if dir_iter.is_err() {
bail!("Backup check: Cannot open directory \"{:?}\"", dir_name);
}
let mut newest_backup_time = 0; let mut newest_backup_time = 0;
let mut newest_backup_path: Option<std::path::PathBuf> = None; let mut newest_backup_path: Option<std::path::PathBuf> = None;
for dirent in dir_iter.unwrap() { for dirent in dir_iter {
match dirent { match dirent {
Ok(dirent) => { Ok(dirent) => {
let path = dirent.path(); let path = dirent.path();
@@ -232,29 +229,26 @@ pub fn dc_create_setup_code(_context: &Context) -> String {
} }
pub fn dc_continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -> Result<()> { pub fn dc_continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -> Result<()> {
if msg_id <= DC_MSG_ID_LAST_SPECIAL { ensure!(msg_id > DC_MSG_ID_LAST_SPECIAL, "wrong id");
bail!("wrong id");
}
let msg = Message::load_from_db(context, msg_id); let msg = Message::load_from_db(context, msg_id);
if msg.is_err() { if msg.is_err() {
bail!("Message is no Autocrypt Setup Message."); bail!("Message is no Autocrypt Setup Message.");
} }
let msg = msg.unwrap(); let msg = msg.unwrap();
if !msg.is_setupmessage() { ensure!(
bail!("Message is no Autocrypt Setup Message."); msg.is_setupmessage(),
} "Message is no Autocrypt Setup Message."
);
if let Some(filename) = msg.get_file(context) { if let Some(filename) = msg.get_file(context) {
if let Ok(buf) = dc_read_file(context, filename) { if let Ok(buf) = dc_read_file(context, filename) {
let norm_sc = CString::yolo(dc_normalize_setup_code(setup_code)); let norm_sc = CString::yolo(dc_normalize_setup_code(setup_code));
let armored_key: &str; let armored_key: String;
unsafe { unsafe {
let sc = dc_decrypt_setup_file(context, norm_sc.as_ptr(), buf.as_ptr().cast()); let sc = dc_decrypt_setup_file(context, norm_sc.as_ptr(), buf.as_ptr().cast());
if sc.is_null() { ensure!(!sc.is_null(), "bad setup code");
bail!("bad setup code"); armored_key = to_string(sc);
}
armored_key = as_str(sc);
free(sc as *mut libc::c_void); free(sc as *mut libc::c_void);
} }
set_self_key(context, &armored_key, true, true)?; set_self_key(context, &armored_key, true, true)?;
@@ -278,9 +272,7 @@ fn set_self_key(
.and_then(|(k, h)| if k.verify() { Some((k, h)) } else { None }) .and_then(|(k, h)| if k.verify() { Some((k, h)) } else { None })
.and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h))); .and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h)));
if keys.is_none() { ensure!(keys.is_some(), "Not a valid private key");
bail!("File does not contain a valid private key.");
}
let (private_key, public_key, header) = keys.unwrap(); let (private_key, public_key, header) = keys.unwrap();
let preferencrypt = header.get("Autocrypt-Prefer-Encrypt"); let preferencrypt = header.get("Autocrypt-Prefer-Encrypt");
@@ -305,9 +297,7 @@ fn set_self_key(
}; };
let self_addr = context.get_config(Config::ConfiguredAddr); let self_addr = context.get_config(Config::ConfiguredAddr);
if self_addr.is_none() { ensure!(self_addr.is_some(), "Missing self addr");
bail!("Missing self addr");
}
// XXX maybe better make dc_key_save_self_keypair delete things // XXX maybe better make dc_key_save_self_keypair delete things
sql::execute( sql::execute(
@@ -413,50 +403,44 @@ pub fn dc_normalize_setup_code(s: &str) -> String {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> { pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
if !dc_alloc_ongoing(context) { ensure!(dc_alloc_ongoing(context), "could not allocate ongoing");
bail!("could not allocate ongoing")
}
let what = job.param.get_int(Param::Cmd).unwrap_or_default(); let what = job.param.get_int(Param::Cmd).unwrap_or_default();
let param1_s = job.param.get(Param::Arg).unwrap_or_default(); let param = job.param.get(Param::Arg).unwrap_or_default();
let param1 = CString::yolo(param1_s);
if param1_s.is_empty() { ensure!(!param.is_empty(), "No Import/export dir/file given.");
bail!("No Import/export dir/file given.");
}
info!(context, "Import/export process started."); info!(context, "Import/export process started.");
context.call_cb(Event::ImexProgress(10)); context.call_cb(Event::ImexProgress(10));
if !context.sql.is_open() { ensure!(context.sql.is_open(), "Database not opened.");
bail!("Database not opened.");
}
if what == DC_IMEX_EXPORT_BACKUP || what == DC_IMEX_EXPORT_SELF_KEYS { if what == DC_IMEX_EXPORT_BACKUP || what == DC_IMEX_EXPORT_SELF_KEYS {
/* before we export anything, make sure the private key exists */ /* before we export anything, make sure the private key exists */
if e2ee::ensure_secret_key_exists(context).is_err() { if e2ee::ensure_secret_key_exists(context).is_err() {
dc_free_ongoing(context); dc_free_ongoing(context);
bail!("Cannot create private key or private key not available."); bail!("Cannot create private key or private key not available.");
} else { } else {
dc_create_folder(context, &param1_s); dc_create_folder(context, &param);
} }
} }
let path = Path::new(param);
let success = match what { let success = match what {
DC_IMEX_EXPORT_SELF_KEYS => export_self_keys(context, &param1_s), DC_IMEX_EXPORT_SELF_KEYS => export_self_keys(context, path),
DC_IMEX_IMPORT_SELF_KEYS => import_self_keys(context, &param1_s), DC_IMEX_IMPORT_SELF_KEYS => import_self_keys(context, path),
DC_IMEX_EXPORT_BACKUP => unsafe { export_backup(context, param1.as_ptr()) }, DC_IMEX_EXPORT_BACKUP => unsafe { export_backup(context, path) },
DC_IMEX_IMPORT_BACKUP => unsafe { import_backup(context, param1.as_ptr()) }, DC_IMEX_IMPORT_BACKUP => import_backup(context, path),
_ => { _ => {
bail!("unknown IMEX type: {}", what); bail!("unknown IMEX type: {}", what);
} }
}; };
dc_free_ongoing(context); dc_free_ongoing(context);
match success { match success {
true => { Ok(()) => {
info!(context, "IMEX successfully completed"); info!(context, "IMEX successfully completed");
context.call_cb(Event::ImexProgress(1000)); context.call_cb(Event::ImexProgress(1000));
Ok(()) Ok(())
} }
false => { Err(err) => {
context.call_cb(Event::ImexProgress(0)); context.call_cb(Event::ImexProgress(0));
bail!("IMEX FAILED to complete"); bail!("IMEX FAILED to complete: {}", err);
} }
} }
} }
@@ -466,36 +450,35 @@ pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
******************************************************************************/ ******************************************************************************/
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char) -> bool { fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Result<()> {
info!( info!(
context, context,
"Import \"{}\" to \"{}\".", "Import \"{}\" to \"{}\".",
as_str(backup_to_import), backup_to_import.as_ref().display(),
context.get_dbfile().display() context.get_dbfile().display()
); );
if dc_is_configured(context) { ensure!(
error!(context, "Cannot import backups to accounts in use."); !dc_is_configured(context),
return false; "Cannot import backups to accounts in use."
} );
&context.sql.close(&context); &context.sql.close(&context);
dc_delete_file(context, context.get_dbfile()); dc_delete_file(context, context.get_dbfile());
if dc_file_exist(context, context.get_dbfile()) { ensure!(
error!( !dc_file_exist(context, context.get_dbfile()),
context, "Cannot delete old database."
"Cannot import backups: Cannot delete the old file.",
); );
return false;
}
if !dc_copy_file(context, as_path(backup_to_import), context.get_dbfile()) { ensure!(
return false; dc_copy_file(context, backup_to_import.as_ref(), context.get_dbfile()),
} "could not copy file"
);
/* error already logged */ /* error already logged */
/* re-open copied database file */ /* re-open copied database file */
if !context.sql.open(&context, &context.get_dbfile(), 0) { ensure!(
return false; context.sql.open(&context, &context.get_dbfile(), 0),
} "could not re-open db"
);
let total_files_cnt = context let total_files_cnt = context
.sql .sql
@@ -516,12 +499,10 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
Ok((name, blob)) Ok((name, blob))
}, },
|files| { |files| {
let mut loop_success = true;
let mut processed_files_cnt = 0; let mut processed_files_cnt = 0;
for file in files { for file in files {
let (file_name, file_blob) = file?; let (file_name, file_blob) = file?;
if context if context
.running_state .running_state
.clone() .clone()
@@ -529,8 +510,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
.unwrap() .unwrap()
.shall_stop_ongoing .shall_stop_ongoing
{ {
loop_success = false; bail!("received stop signal");
break;
} }
processed_files_cnt += 1; processed_files_cnt += 1;
let mut permille = processed_files_cnt * 1000 / total_files_cnt; let mut permille = processed_files_cnt * 1000 / total_files_cnt;
@@ -549,19 +529,11 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
if dc_write_file(context, &pathNfilename, &file_blob) { if dc_write_file(context, &pathNfilename, &file_blob) {
continue; continue;
} }
error!( bail!(
context,
"Storage full? Cannot write file {} with {} bytes.", "Storage full? Cannot write file {} with {} bytes.",
pathNfilename.display(), pathNfilename.display(),
file_blob.len(), file_blob.len(),
); );
// otherwise the user may believe the stuff is imported correctly, but there are files missing ...
loop_success = false;
break;
}
if !loop_success {
return Err(format_err!("fail"));
} }
Ok(()) Ok(())
}, },
@@ -573,7 +545,6 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
sql::try_execute(context, &context.sql, "VACUUM;").ok(); sql::try_execute(context, &context.sql, "VACUUM;").ok();
Ok(()) Ok(())
}) })
.is_ok()
} }
/******************************************************************************* /*******************************************************************************
@@ -582,7 +553,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
/* the FILE_PROGRESS macro calls the callback with the permille of files processed. /* the FILE_PROGRESS macro calls the callback with the permille of files processed.
The macro avoids weird values of 0% or 100% while still working. */ The macro avoids weird values of 0% or 100% while still working. */
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> bool { unsafe fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
let mut ok_to_continue: bool; let mut ok_to_continue: bool;
let mut success = false; let mut success = false;
@@ -594,7 +565,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> bool {
.format("delta-chat-%Y-%m-%d.bak") .format("delta-chat-%Y-%m-%d.bak")
.to_string(); .to_string();
let dest_path_filename = dc_get_fine_path_filename(context, as_path(dir), res); let dest_path_filename = dc_get_fine_path_filename(context, dir, res);
sql::housekeeping(context); sql::housekeeping(context);
@@ -743,28 +714,26 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> bool {
dc_delete_file(context, &dest_path_filename); dc_delete_file(context, &dest_path_filename);
} }
success ensure!(success, "failed");
Ok(())
} }
/******************************************************************************* /*******************************************************************************
* Classic key import * Classic key import
******************************************************************************/ ******************************************************************************/
fn import_self_keys(context: &Context, dir_name: &str) -> bool { fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
/* hint: even if we switch to import Autocrypt Setup Files, we should leave the possibility to import /* hint: even if we switch to import Autocrypt Setup Files, we should leave the possibility to import
plain ASC keys, at least keys without a password, if we do not want to implement a password entry function. plain ASC keys, at least keys without a password, if we do not want to implement a password entry function.
Importing ASC keys is useful to use keys in Delta Chat used by any other non-Autocrypt-PGP implementation. Importing ASC keys is useful to use keys in Delta Chat used by any other non-Autocrypt-PGP implementation.
Maybe we should make the "default" key handlong also a little bit smarter Maybe we should make the "default" key handlong also a little bit smarter
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */ (currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
if dir_name.is_empty() {
return false;
}
let mut set_default: bool; let mut set_default: bool;
let mut key: String; let mut key: String;
let mut imported_cnt = 0; let mut imported_cnt = 0;
let dir = std::path::Path::new(dir_name); let dir_name = dir.as_ref().to_string_lossy();
if let Ok(dir_handle) = std::fs::read_dir(dir) { if let Ok(dir_handle) = std::fs::read_dir(&dir) {
for entry in dir_handle { for entry in dir_handle {
let entry_fn = match entry { let entry_fn = match entry {
Err(err) => { Err(err) => {
@@ -774,7 +743,7 @@ fn import_self_keys(context: &Context, dir_name: &str) -> bool {
Ok(res) => res.file_name(), Ok(res) => res.file_name(),
}; };
let name_f = entry_fn.to_string_lossy(); let name_f = entry_fn.to_string_lossy();
let path_plus_name = dir.join(&entry_fn); let path_plus_name = dir.as_ref().join(&entry_fn);
match dc_get_filesuffix_lc(&name_f) { match dc_get_filesuffix_lc(&name_f) {
Some(suffix) => { Some(suffix) => {
if suffix != "asc" { if suffix != "asc" {
@@ -791,8 +760,9 @@ fn import_self_keys(context: &Context, dir_name: &str) -> bool {
continue; continue;
} }
} }
if let Ok(content) = dc_read_file(context, &path_plus_name) { let ccontent = if let Ok(content) = dc_read_file(context, &path_plus_name) {
key = String::from_utf8_lossy(&content).to_string(); key = String::from_utf8_lossy(&content).to_string();
CString::new(content).unwrap()
} else { } else {
continue; continue;
}; };
@@ -801,7 +771,7 @@ fn import_self_keys(context: &Context, dir_name: &str) -> bool {
let mut buf2_headerline = String::default(); let mut buf2_headerline = String::default();
let split_res: bool; let split_res: bool;
unsafe { unsafe {
let buf2 = "".strdup(); let buf2 = dc_strdup(ccontent.as_ptr());
split_res = dc_split_armored_data( split_res = dc_split_armored_data(
buf2, buf2,
&mut buf2_headerline, &mut buf2_headerline,
@@ -825,21 +795,21 @@ fn import_self_keys(context: &Context, dir_name: &str) -> bool {
} }
imported_cnt += 1 imported_cnt += 1
} }
if imported_cnt == 0i32 { ensure!(
error!(context, "No private keys found in \"{}\".", dir_name,); imported_cnt > 0,
} "No private keys found in \"{}\".",
dir_name
);
return Ok(());
} else { } else {
error!(context, "Import: Cannot open directory \"{}\".", dir_name,); bail!("Import: Cannot open directory \"{}\".", dir_name);
} }
imported_cnt != 0
} }
fn export_self_keys(context: &Context, dir: &str) -> bool { fn export_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
let mut export_errors = 0; let mut export_errors = 0;
context context.sql.query_map(
.sql
.query_map(
"SELECT id, public_key, private_key, is_default FROM keypairs;", "SELECT id, public_key, private_key, is_default FROM keypairs;",
params![], params![],
|row| { |row| {
@@ -857,14 +827,14 @@ fn export_self_keys(context: &Context, dir: &str) -> bool {
let (id, public_key, private_key, is_default) = key_pair?; let (id, public_key, private_key, is_default) = key_pair?;
let id = Some(id).filter(|_| is_default != 0); let id = Some(id).filter(|_| is_default != 0);
if let Some(key) = public_key { if let Some(key) = public_key {
if !export_key_to_asc_file(context, dir, id, &key) { if !export_key_to_asc_file(context, &dir, id, &key) {
export_errors += 1; export_errors += 1;
} }
} else { } else {
export_errors += 1; export_errors += 1;
} }
if let Some(key) = private_key { if let Some(key) = private_key {
if !export_key_to_asc_file(context, dir, id, &key) { if !export_key_to_asc_file(context, &dir, id, &key) {
export_errors += 1; export_errors += 1;
} }
} else { } else {
@@ -874,22 +844,27 @@ fn export_self_keys(context: &Context, dir: &str) -> bool {
Ok(()) Ok(())
}, },
) )?;
.unwrap();
export_errors == 0 ensure!(export_errors == 0, "errors while importing");
Ok(())
} }
/******************************************************************************* /*******************************************************************************
* Classic key export * Classic key export
******************************************************************************/ ******************************************************************************/
fn export_key_to_asc_file(context: &Context, dir: &str, id: Option<i64>, key: &Key) -> bool { fn export_key_to_asc_file(
context: &Context,
dir: impl AsRef<Path>,
id: Option<i64>,
key: &Key,
) -> bool {
let mut success = false; let mut success = false;
let file_name = { let file_name = {
let kind = if key.is_public() { "public" } else { "private" }; let kind = if key.is_public() { "public" } else { "private" };
let id = id.map_or("default".into(), |i| i.to_string()); let id = id.map_or("default".into(), |i| i.to_string());
Path::new(dir).join(format!("{}-key-{}.asc", kind, &id)) dir.as_ref().join(format!("{}-key-{}.asc", kind, &id))
}; };
info!(context, "Exporting key {}", file_name.display()); info!(context, "Exporting key {}", file_name.display());
dc_delete_file(context, &file_name); dc_delete_file(context, &file_name);

View File

@@ -536,7 +536,7 @@ pub(crate) fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path
if !path_abs.is_file() { if !path_abs.is_file() {
warn!( warn!(
context, context,
"refusing to deleting non-file \"{}\".", "refusing to delete non-file \"{}\".",
path.as_ref().display() path.as_ref().display()
); );
return false; return false;