mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 17:06:35 +03:00
first pass at resolving CR
This commit is contained in:
38
src/chat.rs
38
src/chat.rs
@@ -635,44 +635,6 @@ impl Chat {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_query(fields: &str) -> String {
|
|
||||||
// Check for server_uid guarantees that we don't
|
|
||||||
// select a draft or undelivered message.
|
|
||||||
format!(
|
|
||||||
"SELECT {} \
|
|
||||||
FROM msgs WHERE chat_id=?1 AND server_uid!=0 \
|
|
||||||
ORDER BY timestamp DESC, id DESC \
|
|
||||||
LIMIT 1;",
|
|
||||||
fields
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_parent_mime_headers(&self, context: &Context) -> Option<(String, String, String)> {
|
|
||||||
let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?));
|
|
||||||
let params = paramsv![self.id];
|
|
||||||
let sql = &context.sql;
|
|
||||||
|
|
||||||
let query = Self::parent_query("rfc724_mid, mime_in_reply_to, mime_references");
|
|
||||||
|
|
||||||
sql.query_row(&query, params, collect).await.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn parent_is_encrypted(&self, context: &Context) -> Result<bool, Error> {
|
|
||||||
let sql = &context.sql;
|
|
||||||
let params = paramsv![self.id];
|
|
||||||
let query = Self::parent_query("param");
|
|
||||||
|
|
||||||
let packed: Option<String> = sql.query_get_value_result(&query, params).await?;
|
|
||||||
|
|
||||||
if let Some(ref packed) = packed {
|
|
||||||
let param = packed.parse::<Params>()?;
|
|
||||||
Ok(param.exists(Param::GuaranteeE2ee))
|
|
||||||
} else {
|
|
||||||
// No messages
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_profile_image(&self, context: &Context) -> Option<PathBuf> {
|
pub async fn get_profile_image(&self, context: &Context) -> Option<PathBuf> {
|
||||||
if let Some(image_rel) = self.param.get(Param::ProfileImage) {
|
if let Some(image_rel) = self.param.get(Param::ProfileImage) {
|
||||||
if !image_rel.is_empty() {
|
if !image_rel.is_empty() {
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ impl Context {
|
|||||||
|
|
||||||
ensure!(
|
ensure!(
|
||||||
!self.scheduler.read().await.is_running(),
|
!self.scheduler.read().await.is_running(),
|
||||||
"Can not configure, already running"
|
"cannot configure, already running"
|
||||||
);
|
);
|
||||||
ensure!(
|
ensure!(
|
||||||
self.sql.is_open().await,
|
self.sql.is_open().await,
|
||||||
"Cannot configure, database not opened."
|
"cannot configure, database not opened."
|
||||||
);
|
);
|
||||||
let cancel_channel = self.alloc_ongoing().await?;
|
let cancel_channel = self.alloc_ongoing().await?;
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ pub struct InnerContext {
|
|||||||
pub(crate) running_state: RwLock<RunningState>,
|
pub(crate) running_state: RwLock<RunningState>,
|
||||||
/// Mutex to avoid generating the key for the user more than once.
|
/// Mutex to avoid generating the key for the user more than once.
|
||||||
pub(crate) generating_key_mutex: Mutex<()>,
|
pub(crate) generating_key_mutex: Mutex<()>,
|
||||||
|
/// Mutex to enforce only a single running oauth2 is running.
|
||||||
|
pub(crate) oauth2_mutex: Mutex<()>,
|
||||||
pub(crate) translated_stockstrings: RwLock<HashMap<usize, String>>,
|
pub(crate) translated_stockstrings: RwLock<HashMap<usize, String>>,
|
||||||
pub(crate) logs: (SyncSender<Event>, SyncReceiver<Event>),
|
pub(crate) logs: (SyncSender<Event>, SyncReceiver<Event>),
|
||||||
|
|
||||||
@@ -117,6 +119,7 @@ impl Context {
|
|||||||
bob: RwLock::new(Default::default()),
|
bob: RwLock::new(Default::default()),
|
||||||
last_smeared_timestamp: RwLock::new(0),
|
last_smeared_timestamp: RwLock::new(0),
|
||||||
generating_key_mutex: Mutex::new(()),
|
generating_key_mutex: Mutex::new(()),
|
||||||
|
oauth2_mutex: Mutex::new(()),
|
||||||
translated_stockstrings: RwLock::new(HashMap::new()),
|
translated_stockstrings: RwLock::new(HashMap::new()),
|
||||||
logs: unbounded(),
|
logs: unbounded(),
|
||||||
scheduler: RwLock::new(Scheduler::Stopped),
|
scheduler: RwLock::new(Scheduler::Stopped),
|
||||||
|
|||||||
@@ -1227,6 +1227,14 @@ impl Imap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.add_flag_finalized_with_set(context, SELECT_ALL, "\\Deleted")
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!(context, "Cannot mark messages for deletion {}", folder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// we now trigger expunge to actually delete messages
|
// we now trigger expunge to actually delete messages
|
||||||
self.config.selected_folder_needs_expunge = true;
|
self.config.selected_folder_needs_expunge = true;
|
||||||
match self.select_folder::<String>(context, None).await {
|
match self.select_folder::<String>(context, None).await {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ impl Imap {
|
|||||||
}
|
}
|
||||||
self.config.selected_folder = None;
|
self.config.selected_folder = None;
|
||||||
self.config.selected_folder_needs_expunge = false;
|
self.config.selected_folder_needs_expunge = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,26 +78,7 @@ impl Imap {
|
|||||||
// deselect existing folder, if needed (it's also done implicitly by SELECT, however, without EXPUNGE then)
|
// deselect existing folder, if needed (it's also done implicitly by SELECT, however, without EXPUNGE then)
|
||||||
let needs_expunge = { self.config.selected_folder_needs_expunge };
|
let needs_expunge = { self.config.selected_folder_needs_expunge };
|
||||||
if needs_expunge {
|
if needs_expunge {
|
||||||
if let Some(ref folder) = self.config.selected_folder {
|
self.close_folder(context).await?;
|
||||||
info!(context, "Expunge messages in \"{}\".", folder);
|
|
||||||
|
|
||||||
// A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see
|
|
||||||
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
|
||||||
if let Some(ref mut session) = &mut self.session {
|
|
||||||
match session.close().await {
|
|
||||||
Ok(_) => {
|
|
||||||
info!(context, "close/expunge succeeded");
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.trigger_reconnect();
|
|
||||||
return Err(Error::CloseExpungeFailed(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(Error::NoSession);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.config.selected_folder_needs_expunge = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// select new folder
|
// select new folder
|
||||||
|
|||||||
39
src/job.rs
39
src/job.rs
@@ -810,45 +810,6 @@ pub enum Connection<'a> {
|
|||||||
Smtp(&'a mut Smtp),
|
Smtp(&'a mut Smtp),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_imap_deletion_jobs(context: &Context) -> sql::Result<()> {
|
|
||||||
if let Some(delete_server_after) = context.get_config_delete_server_after().await {
|
|
||||||
let threshold_timestamp = time() - delete_server_after;
|
|
||||||
|
|
||||||
// Select all expired messages which don't have a
|
|
||||||
// corresponding message deletion job yet.
|
|
||||||
let msg_ids = context
|
|
||||||
.sql
|
|
||||||
.query_map(
|
|
||||||
"SELECT id FROM msgs \
|
|
||||||
WHERE timestamp < ? \
|
|
||||||
AND server_uid != 0 \
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM jobs WHERE foreign_id = msgs.id \
|
|
||||||
AND action = ?)",
|
|
||||||
paramsv![threshold_timestamp, Action::DeleteMsgOnImap],
|
|
||||||
|row| row.get::<_, MsgId>(0),
|
|
||||||
|ids| {
|
|
||||||
ids.collect::<std::result::Result<Vec<_>, _>>()
|
|
||||||
.map_err(Into::into)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Schedule IMAP deletion for expired messages.
|
|
||||||
for msg_id in msg_ids {
|
|
||||||
add(
|
|
||||||
context,
|
|
||||||
Action::DeleteMsgOnImap,
|
|
||||||
msg_id.to_u32() as i32,
|
|
||||||
Params::new(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load_imap_deletion_msgid(context: &Context) -> sql::Result<Option<MsgId>> {
|
async fn load_imap_deletion_msgid(context: &Context) -> sql::Result<Option<MsgId>> {
|
||||||
if let Some(delete_server_after) = context.get_config_delete_server_after().await {
|
if let Some(delete_server_after) = context.get_config_delete_server_after().await {
|
||||||
let threshold_timestamp = time() - delete_server_after;
|
let threshold_timestamp = time() - delete_server_after;
|
||||||
|
|||||||
@@ -75,8 +75,6 @@ pub async fn dc_get_oauth2_url(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following function may block due http-requests;
|
|
||||||
// must not be called from the main thread or by the ui!
|
|
||||||
pub async fn dc_get_oauth2_access_token(
|
pub async fn dc_get_oauth2_access_token(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
addr: impl AsRef<str>,
|
addr: impl AsRef<str>,
|
||||||
@@ -84,9 +82,7 @@ pub async fn dc_get_oauth2_access_token(
|
|||||||
regenerate: bool,
|
regenerate: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
if let Some(oauth2) = Oauth2::from_address(addr) {
|
if let Some(oauth2) = Oauth2::from_address(addr) {
|
||||||
// TODO: FIXME
|
let lock = context.oauth2_mutex.lock().await;
|
||||||
// let lock = context.oauth2_critical.clone();
|
|
||||||
// let _l = lock.lock().await;
|
|
||||||
|
|
||||||
// read generated token
|
// read generated token
|
||||||
if !regenerate && !is_expired(context).await {
|
if !regenerate && !is_expired(context).await {
|
||||||
@@ -243,6 +239,8 @@ pub async fn dc_get_oauth2_access_token(
|
|||||||
warn!(context, "Failed to find OAuth2 access token");
|
warn!(context, "Failed to find OAuth2 access token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
response.access_token
|
response.access_token
|
||||||
} else {
|
} else {
|
||||||
warn!(context, "Internal OAuth2 error: 2");
|
warn!(context, "Internal OAuth2 error: 2");
|
||||||
|
|||||||
Reference in New Issue
Block a user