Load libnotmuch dynamically

async
Manos Pitsidianakis 2020-02-26 14:18:00 +02:00
parent ac71d627f1
commit 303c530488
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
7 changed files with 1850 additions and 2513 deletions

11
Cargo.lock generated
View File

@ -607,6 +607,15 @@ dependencies = [
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libloading"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libsqlite3-sys"
version = "0.16.0"
@ -712,6 +721,7 @@ dependencies = [
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1897,6 +1907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum libdbus-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ce9439c6f4a1092dc1861272bef01034891da39f13aa1cdcf40ca3e4081de5f"

View File

@ -55,7 +55,7 @@ debug = false
members = ["melib", "testing", ]
[features]
default = ["sqlite3"]
default = ["sqlite3", "notmuch"]
notmuch = ["melib/notmuch_backend", ]
jmap = ["melib/jmap_backend",]
sqlite3 = ["rusqlite"]

View File

@ -42,6 +42,8 @@ serde_json = { version = "1.0", optional = true, features = ["raw_value",] }
smallvec = { version = "1.1.0", features = ["serde", ] }
nix = "0.16.1"
libloading = "0.5.2"
[features]
default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "vcard"]

View File

@ -23,10 +23,6 @@
include!("src/text_processing/types.rs");
fn main() -> Result<(), std::io::Error> {
#[cfg(feature = "notmuch_backend")]
{
println!("cargo:rustc-link-lib=notmuch");
}
#[cfg(feature = "unicode_algorithms")]
{
use std::fs::File;

View File

@ -99,6 +99,12 @@ impl Default for Backends {
}
}
#[cfg(feature = "notmuch_backend")]
pub const NOTMUCH_ERROR_MSG: &'static str =
"libnotmuch5 was not found in your system. Make sure it is installed and in the library paths.\n";
#[cfg(not(feature = "notmuch_backend"))]
pub const NOTMUCH_ERROR_MSG: &'static str = "this version of meli is not compiled with notmuch support. Use an appropriate version and make sure libnotmuch5 is installed and in the library paths.\n";
impl Backends {
pub fn new() -> Self {
let mut b = Backends {
@ -136,13 +142,15 @@ impl Backends {
}
#[cfg(feature = "notmuch_backend")]
{
b.register(
"notmuch".to_string(),
Backend {
create_fn: Box::new(|| Box::new(|f, i| NotmuchDb::new(f, i))),
validate_conf_fn: Box::new(NotmuchDb::validate_config),
},
);
if libloading::Library::new("libnotmuch.so.5").is_ok() {
b.register(
"notmuch".to_string(),
Backend {
create_fn: Box::new(|| Box::new(|f, i| NotmuchDb::new(f, i))),
validate_conf_fn: Box::new(NotmuchDb::validate_config),
},
);
}
}
#[cfg(feature = "jmap_backend")]
{
@ -159,6 +167,9 @@ impl Backends {
pub fn get(&self, key: &str) -> BackendCreator {
if !self.map.contains_key(key) {
if key == "notmuch" {
eprint!("{}", NOTMUCH_ERROR_MSG);
}
panic!("{} is not a valid mail backend", key);
}
(self.map[key].create_fn)()
@ -175,7 +186,17 @@ impl Backends {
(self
.map
.get(key)
.ok_or_else(|| MeliError::new(format!("{} is not a valid mail backend", key)))?
.ok_or_else(|| {
MeliError::new(format!(
"{}{} is not a valid mail backend",
if key == "notmuch" {
NOTMUCH_ERROR_MSG
} else {
""
},
key
))
})?
.validate_conf_fn)(s)
}
}

View File

@ -54,6 +54,7 @@ unsafe impl Sync for DbWrapper {}
#[derive(Debug)]
pub struct NotmuchDb {
database: DbWrapper,
lib: Arc<libloading::Library>,
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, NotmuchMailbox>>>,
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
@ -64,19 +65,26 @@ pub struct NotmuchDb {
unsafe impl Send for NotmuchDb {}
unsafe impl Sync for NotmuchDb {}
macro_rules! call {
($lib:expr, $func:ty) => {{
let func: libloading::Symbol<$func> = $lib.get(stringify!($func).as_bytes()).unwrap();
func
}};
}
impl Drop for NotmuchDb {
fn drop(&mut self) {
for f in self.mailboxes.write().unwrap().values_mut() {
if let Some(query) = f.query.take() {
unsafe {
notmuch_query_destroy(query);
call!(self.lib, notmuch_query_destroy)(query);
}
}
}
let inner = self.database.inner.write().unwrap();
unsafe {
notmuch_database_close(*inner);
notmuch_database_destroy(*inner);
call!(self.lib, notmuch_database_close)(*inner);
call!(self.lib, notmuch_database_destroy)(*inner);
}
}
}
@ -158,6 +166,7 @@ impl NotmuchDb {
s: &AccountSettings,
_is_subscribed: Box<dyn Fn(&str) -> bool>,
) -> Result<Box<dyn MailBackend>> {
let lib = Arc::new(libloading::Library::new("libnotmuch.so.5")?);
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
let path = Path::new(s.root_mailbox.as_str()).expand().to_path_buf();
if !path.exists() {
@ -171,7 +180,7 @@ impl NotmuchDb {
let path_c = std::ffi::CString::new(path.to_str().unwrap()).unwrap();
let path_ptr = path_c.as_ptr();
let status = unsafe {
bindings::notmuch_database_open(
call!(lib, notmuch_database_open)(
path_ptr,
notmuch_database_mode_t_NOTMUCH_DATABASE_MODE_READ_WRITE,
&mut database as *mut _,
@ -217,6 +226,7 @@ impl NotmuchDb {
}
}
Ok(Box::new(NotmuchDb {
lib,
database: DbWrapper {
inner: Arc::new(RwLock::new(database)),
database_ph: std::marker::PhantomData,
@ -254,12 +264,14 @@ impl NotmuchDb {
let database_lck = self.database.inner.read().unwrap();
let query_str = std::ffi::CString::new(query_s).unwrap();
let query: *mut notmuch_query_t =
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
unsafe { call!(self.lib, notmuch_query_create)(*database_lck, query_str.as_ptr()) };
if query.is_null() {
return Err(MeliError::new("Could not create query. Out of memory?"));
}
let mut messages: *mut notmuch_messages_t = std::ptr::null_mut();
let status = unsafe { notmuch_query_search_messages(query, &mut messages as *mut _) };
let status = unsafe {
call!(self.lib, notmuch_query_search_messages)(query, &mut messages as *mut _)
};
if status != 0 {
return Err(MeliError::new(format!(
"Search for {} returned {}",
@ -267,10 +279,13 @@ impl NotmuchDb {
)));
}
assert!(!messages.is_null());
let iter = MessageIterator { messages };
let iter = MessageIterator {
messages,
lib: self.lib.clone(),
};
let mut ret = SmallVec::new();
for message in iter {
let fs_path = unsafe { notmuch_message_get_filename(message) };
let fs_path = unsafe { call!(self.lib, notmuch_message_get_filename)(message) };
let c_str = unsafe { CStr::from_ptr(fs_path) };
let env_hash = {
let mut hasher = DefaultHasher::default();
@ -295,6 +310,7 @@ impl MailBackend for NotmuchDb {
let index = self.index.clone();
let tag_index = self.tag_index.clone();
let mailboxes = self.mailboxes.clone();
let lib = self.lib.clone();
let handle = {
let tx = w.tx();
let closure = move |_work_context| {
@ -304,7 +320,7 @@ impl MailBackend for NotmuchDb {
let mailbox = mailboxes_lck.get_mut(&mailbox_hash).unwrap();
let query_str = std::ffi::CString::new(mailbox.query_str.as_str()).unwrap();
let query: *mut notmuch_query_t =
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
unsafe { call!(lib, notmuch_query_create)(*database_lck, query_str.as_ptr()) };
if query.is_null() {
tx.send(AsyncStatus::Payload(Err(MeliError::new(
"Could not create query. Out of memory?",
@ -314,8 +330,9 @@ impl MailBackend for NotmuchDb {
return;
}
let mut messages: *mut notmuch_messages_t = std::ptr::null_mut();
let status =
unsafe { notmuch_query_search_messages(query, &mut messages as *mut _) };
let status = unsafe {
call!(lib, notmuch_query_search_messages)(query, &mut messages as *mut _)
};
if status != 0 {
tx.send(AsyncStatus::Payload(Err(MeliError::new(format!(
"Search for {} returned {}",
@ -327,10 +344,13 @@ impl MailBackend for NotmuchDb {
return;
}
assert!(!messages.is_null());
let iter = MessageIterator { messages };
let iter = MessageIterator {
messages,
lib: lib.clone(),
};
for message in iter {
let mut response = String::new();
let fs_path = unsafe { notmuch_message_get_filename(message) };
let fs_path = unsafe { call!(lib, notmuch_message_get_filename)(message) };
let mut f = match std::fs::File::open(unsafe {
CStr::from_ptr(fs_path)
.to_string_lossy()
@ -357,6 +377,7 @@ impl MailBackend for NotmuchDb {
index.write().unwrap().insert(env_hash, c_str);
let op = Box::new(NotmuchOp {
database: database.clone(),
lib: lib.clone(),
hash: env_hash,
index: index.clone(),
bytes: Some(response),
@ -365,7 +386,8 @@ impl MailBackend for NotmuchDb {
if let Some(mut env) = Envelope::from_token(op, env_hash) {
let mut tag_lock = tag_index.write().unwrap();
for tag in (TagIterator {
tags: unsafe { notmuch_message_get_tags(message) },
tags: unsafe { call!(lib, notmuch_message_get_tags)(message) },
lib: lib.clone(),
}) {
let tag = tag.to_string_lossy().into_owned();
@ -417,6 +439,7 @@ impl MailBackend for NotmuchDb {
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
Box::new(NotmuchOp {
database: self.database.clone(),
lib: self.lib.clone(),
hash,
index: self.index.clone(),
bytes: None,
@ -462,6 +485,7 @@ struct NotmuchOp {
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
database: DbWrapper,
bytes: Option<String>,
lib: Arc<libloading::Library>,
}
impl BackendOp for NotmuchOp {
@ -507,7 +531,7 @@ impl BackendOp for NotmuchOp {
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
let mut index_lck = self.index.write().unwrap();
unsafe {
notmuch_database_find_message_by_filename(
call!(self.lib, notmuch_database_find_message_by_filename)(
*self.database.inner.read().unwrap(),
index_lck[&self.hash].as_ptr(),
&mut message as *mut _,
@ -521,7 +545,8 @@ impl BackendOp for NotmuchOp {
}
let tags = (TagIterator {
tags: unsafe { notmuch_message_get_tags(message) },
tags: unsafe { call!(self.lib, notmuch_message_get_tags)(message) },
lib: self.lib.clone(),
})
.collect::<Vec<&CStr>>();
debug!(&tags);
@ -537,7 +562,7 @@ impl BackendOp for NotmuchOp {
if tags.contains(&cstr!($l)) {
return Ok(());
}
if notmuch_message_add_tag(message, cstr!($l).as_ptr())
if call!(self.lib, notmuch_message_add_tag)(message, cstr!($l).as_ptr())
!= _notmuch_status_NOTMUCH_STATUS_SUCCESS
{
return Err(MeliError::new("Could not set tag."));
@ -551,7 +576,7 @@ impl BackendOp for NotmuchOp {
if !tags.contains(&cstr!($l)) {
return Ok(());
}
if notmuch_message_remove_tag(message, cstr!($l).as_ptr())
if call!(self.lib, notmuch_message_remove_tag)(message, cstr!($l).as_ptr())
!= _notmuch_status_NOTMUCH_STATUS_SUCCESS
{
return Err(MeliError::new("Could not set tag."));
@ -577,13 +602,13 @@ impl BackendOp for NotmuchOp {
}
/* Update message filesystem path. */
if unsafe { notmuch_message_tags_to_maildir_flags(message) }
if unsafe { call!(self.lib, notmuch_message_tags_to_maildir_flags)(message) }
!= _notmuch_status_NOTMUCH_STATUS_SUCCESS
{
return Err(MeliError::new("Could not set tag."));
}
let fs_path = unsafe { notmuch_message_get_filename(message) };
let fs_path = unsafe { call!(self.lib, notmuch_message_get_filename)(message) };
let c_str = unsafe { CStr::from_ptr(fs_path) };
if let Some(p) = index_lck.get_mut(&self.hash) {
*p = c_str;
@ -602,7 +627,7 @@ impl BackendOp for NotmuchOp {
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
let index_lck = self.index.read().unwrap();
unsafe {
notmuch_database_find_message_by_filename(
call!(self.lib, notmuch_database_find_message_by_filename)(
*self.database.inner.read().unwrap(),
index_lck[&self.hash].as_ptr(),
&mut message as *mut _,
@ -616,7 +641,10 @@ impl BackendOp for NotmuchOp {
}
if value {
if unsafe {
notmuch_message_add_tag(message, CString::new(tag.as_str()).unwrap().as_ptr())
call!(self.lib, notmuch_message_add_tag)(
message,
CString::new(tag.as_str()).unwrap().as_ptr(),
)
} != _notmuch_status_NOTMUCH_STATUS_SUCCESS
{
return Err(MeliError::new("Could not set tag."));
@ -624,7 +652,10 @@ impl BackendOp for NotmuchOp {
debug!("added tag {}", &tag);
} else {
if unsafe {
notmuch_message_remove_tag(message, CString::new(tag.as_str()).unwrap().as_ptr())
call!(self.lib, notmuch_message_remove_tag)(
message,
CString::new(tag.as_str()).unwrap().as_ptr(),
)
} != _notmuch_status_NOTMUCH_STATUS_SUCCESS
{
return Err(MeliError::new("Could not set tag."));
@ -652,6 +683,7 @@ impl BackendOp for NotmuchOp {
}
pub struct MessageIterator {
lib: Arc<libloading::Library>,
messages: *mut notmuch_messages_t,
}
@ -660,10 +692,10 @@ impl Iterator for MessageIterator {
fn next(&mut self) -> Option<Self::Item> {
if self.messages.is_null() {
None
} else if unsafe { notmuch_messages_valid(self.messages) } == 1 {
let ret = Some(unsafe { notmuch_messages_get(self.messages) });
} else if unsafe { call!(self.lib, notmuch_messages_valid)(self.messages) } == 1 {
let ret = Some(unsafe { call!(self.lib, notmuch_messages_get)(self.messages) });
unsafe {
notmuch_messages_move_to_next(self.messages);
call!(self.lib, notmuch_messages_move_to_next)(self.messages);
}
ret
} else {
@ -674,6 +706,7 @@ impl Iterator for MessageIterator {
}
pub struct TagIterator {
lib: Arc<libloading::Library>,
tags: *mut notmuch_tags_t,
}
@ -682,10 +715,10 @@ impl Iterator for TagIterator {
fn next(&mut self) -> Option<Self::Item> {
if self.tags.is_null() {
None
} else if unsafe { notmuch_tags_valid(self.tags) } == 1 {
let ret = Some(unsafe { CStr::from_ptr(notmuch_tags_get(self.tags)) });
} else if unsafe { call!(self.lib, notmuch_tags_valid)(self.tags) } == 1 {
let ret = Some(unsafe { CStr::from_ptr(call!(self.lib, notmuch_tags_get)(self.tags)) });
unsafe {
notmuch_tags_move_to_next(self.tags);
call!(self.lib, notmuch_tags_move_to_next)(self.tags);
}
ret
} else {

File diff suppressed because it is too large Load Diff