mirror of
https://github.com/chatmail/core.git
synced 2026-05-20 15:26:30 +03:00
expose empty server functionality and test it (also introducing a new DC_EVENT_IMAP_FOLDER_EMPTIED event)
This commit is contained in:
@@ -1513,6 +1513,16 @@ char* dc_get_mime_headers (dc_context_t* context, uint32_t ms
|
|||||||
*/
|
*/
|
||||||
void dc_delete_msgs (dc_context_t* context, const uint32_t* msg_ids, int msg_cnt);
|
void dc_delete_msgs (dc_context_t* context, const uint32_t* msg_ids, int msg_cnt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty IMAP server folder: delete all messages.
|
||||||
|
*
|
||||||
|
* @memberof dc_context_t
|
||||||
|
* @param context The context object as created by dc_context_new()
|
||||||
|
* @param flags uint32_t with DC_EMPTY_* flags
|
||||||
|
* @return None.
|
||||||
|
*/
|
||||||
|
void dc_empty_server (dc_context_t* context, const uint32_t flags);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forward messages to another chat.
|
* Forward messages to another chat.
|
||||||
@@ -3929,6 +3939,30 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
|
|||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup DC_EMPTY
|
||||||
|
*
|
||||||
|
* These constants configure emptying imap folders.
|
||||||
|
*
|
||||||
|
* @addtogroup DC_EMPTY
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all mvbox messages.
|
||||||
|
*/
|
||||||
|
#define DC_EMPTY_MVBOX 0x01
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all INBOX messages.
|
||||||
|
*/
|
||||||
|
#define DC_EMPTY_INBOX 0x02
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The library-user may write an informational string to the log.
|
* The library-user may write an informational string to the log.
|
||||||
@@ -3995,6 +4029,16 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
|
|||||||
*/
|
*/
|
||||||
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
|
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when an IMAP folder was emptied.
|
||||||
|
*
|
||||||
|
* @param data1 0
|
||||||
|
* @param data2 (const char*) folder name.
|
||||||
|
* Must not be unref'd or modified and is valid only until the callback returns.
|
||||||
|
* @return 0
|
||||||
|
*/
|
||||||
|
#define DC_EVENT_IMAP_FOLDER_EMPTIED 106
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when a new blob file was successfully written
|
* Emitted when a new blob file was successfully written
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ impl ContextWrapper {
|
|||||||
| Event::SmtpMessageSent(msg)
|
| Event::SmtpMessageSent(msg)
|
||||||
| Event::ImapMessageDeleted(msg)
|
| Event::ImapMessageDeleted(msg)
|
||||||
| Event::ImapMessageMoved(msg)
|
| Event::ImapMessageMoved(msg)
|
||||||
|
| Event::ImapFolderEmptied(msg)
|
||||||
| Event::NewBlobFile(msg)
|
| Event::NewBlobFile(msg)
|
||||||
| Event::DeletedBlobFile(msg)
|
| Event::DeletedBlobFile(msg)
|
||||||
| Event::Warning(msg)
|
| Event::Warning(msg)
|
||||||
@@ -1286,6 +1287,18 @@ pub unsafe extern "C" fn dc_delete_msgs(
|
|||||||
.unwrap_or(())
|
.unwrap_or(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_empty_server(context: *mut dc_context_t, flags: u32) {
|
||||||
|
if context.is_null() || flags == 0 {
|
||||||
|
eprintln!("ignoring careless call to dc_empty_server()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ffi_context = &*context;
|
||||||
|
ffi_context
|
||||||
|
.with_inner(|ctx| message::dc_empty_server(ctx, flags))
|
||||||
|
.unwrap_or(())
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_forward_msgs(
|
pub unsafe extern "C" fn dc_forward_msgs(
|
||||||
context: *mut dc_context_t,
|
context: *mut dc_context_t,
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
|||||||
checkqr <qr-content>\n\
|
checkqr <qr-content>\n\
|
||||||
event <event-id to test>\n\
|
event <event-id to test>\n\
|
||||||
fileinfo <file>\n\
|
fileinfo <file>\n\
|
||||||
|
emptyserver <flags> (1=MVBOX 2=INBOX)\n\
|
||||||
clear -- clear screen\n\
|
clear -- clear screen\n\
|
||||||
exit or quit\n\
|
exit or quit\n\
|
||||||
============================================="
|
============================================="
|
||||||
@@ -976,6 +977,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
|||||||
bail!("Command failed.");
|
bail!("Command failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"emptyserver" => {
|
||||||
|
ensure!(!arg1.is_empty(), "Argument <flags> missing");
|
||||||
|
|
||||||
|
message::dc_empty_server(context, arg1.parse()?);
|
||||||
|
}
|
||||||
"" => (),
|
"" => (),
|
||||||
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,17 @@ class Account(object):
|
|||||||
if not self.is_configured():
|
if not self.is_configured():
|
||||||
raise ValueError("need to configure first")
|
raise ValueError("need to configure first")
|
||||||
|
|
||||||
|
def empty_server_folders(self, inbox=False, mvbox=False):
|
||||||
|
""" empty server folders. """
|
||||||
|
flags = 0
|
||||||
|
if inbox:
|
||||||
|
flags |= const.DC_EMPTY_INBOX
|
||||||
|
if mvbox:
|
||||||
|
flags |= const.DC_EMPTY_MVBOX
|
||||||
|
if not flags:
|
||||||
|
raise ValueError("no flags set")
|
||||||
|
lib.dc_empty_server(self._dc_context, flags)
|
||||||
|
|
||||||
def get_infostring(self):
|
def get_infostring(self):
|
||||||
""" return info of the configured account. """
|
""" return info of the configured account. """
|
||||||
self.check_is_configured()
|
self.check_is_configured()
|
||||||
|
|||||||
@@ -69,12 +69,15 @@ DC_CERTCK_AUTO = 0
|
|||||||
DC_CERTCK_STRICT = 1
|
DC_CERTCK_STRICT = 1
|
||||||
DC_CERTCK_ACCEPT_INVALID_HOSTNAMES = 2
|
DC_CERTCK_ACCEPT_INVALID_HOSTNAMES = 2
|
||||||
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
|
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
|
||||||
|
DC_EMPTY_MVBOX = 0x01
|
||||||
|
DC_EMPTY_INBOX = 0x02
|
||||||
DC_EVENT_INFO = 100
|
DC_EVENT_INFO = 100
|
||||||
DC_EVENT_SMTP_CONNECTED = 101
|
DC_EVENT_SMTP_CONNECTED = 101
|
||||||
DC_EVENT_IMAP_CONNECTED = 102
|
DC_EVENT_IMAP_CONNECTED = 102
|
||||||
DC_EVENT_SMTP_MESSAGE_SENT = 103
|
DC_EVENT_SMTP_MESSAGE_SENT = 103
|
||||||
DC_EVENT_IMAP_MESSAGE_DELETED = 104
|
DC_EVENT_IMAP_MESSAGE_DELETED = 104
|
||||||
DC_EVENT_IMAP_MESSAGE_MOVED = 105
|
DC_EVENT_IMAP_MESSAGE_MOVED = 105
|
||||||
|
DC_EVENT_IMAP_FOLDER_EMPTIED = 106
|
||||||
DC_EVENT_NEW_BLOB_FILE = 150
|
DC_EVENT_NEW_BLOB_FILE = 150
|
||||||
DC_EVENT_DELETED_BLOB_FILE = 151
|
DC_EVENT_DELETED_BLOB_FILE = 151
|
||||||
DC_EVENT_WARNING = 300
|
DC_EVENT_WARNING = 300
|
||||||
@@ -151,7 +154,7 @@ DC_STR_COUNT = 67
|
|||||||
|
|
||||||
|
|
||||||
def read_event_defines(f):
|
def read_event_defines(f):
|
||||||
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_CERTCK|DC_STATE|DC_STR|'
|
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_EMPTY|DC_CERTCK|DC_STATE|DC_STR|'
|
||||||
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER)_\S+)\s+([x\d]+).*')
|
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER)_\S+)\s+([x\d]+).*')
|
||||||
for line in f:
|
for line in f:
|
||||||
m = rex.match(line)
|
m = rex.match(line)
|
||||||
|
|||||||
@@ -180,6 +180,12 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
|||||||
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
|
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
|
||||||
return ac
|
return ac
|
||||||
|
|
||||||
|
def get_one_online_account(self):
|
||||||
|
ac1 = self.get_online_configuring_account()
|
||||||
|
wait_successful_IMAP_SMTP_connection(ac1)
|
||||||
|
wait_configuration_progress(ac1, 1000)
|
||||||
|
return ac1
|
||||||
|
|
||||||
def get_two_online_accounts(self):
|
def get_two_online_accounts(self):
|
||||||
ac1 = self.get_online_configuring_account()
|
ac1 = self.get_online_configuring_account()
|
||||||
ac2 = self.get_online_configuring_account()
|
ac2 = self.get_online_configuring_account()
|
||||||
|
|||||||
@@ -493,6 +493,18 @@ class TestOnlineAccount:
|
|||||||
assert msg_in.text == "message2"
|
assert msg_in.text == "message2"
|
||||||
assert msg_in.is_forwarded()
|
assert msg_in.is_forwarded()
|
||||||
|
|
||||||
|
def test_send_self_message_and_empty_folder(self, acfactory, lp):
|
||||||
|
ac1 = acfactory.get_one_online_account()
|
||||||
|
lp.sec("ac1: create self chat")
|
||||||
|
chat = ac1.create_chat_by_contact(ac1.get_self_contact())
|
||||||
|
chat.send_text("hello")
|
||||||
|
ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
|
||||||
|
ac1.empty_server_folders(inbox=True, mvbox=True)
|
||||||
|
ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED")
|
||||||
|
assert ev[2] == "DeltaChat"
|
||||||
|
ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED")
|
||||||
|
assert ev[2] == "INBOX"
|
||||||
|
|
||||||
def test_send_and_receive_message_markseen(self, acfactory, lp):
|
def test_send_and_receive_message_markseen(self, acfactory, lp):
|
||||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ pub enum Event {
|
|||||||
#[strum(props(id = "105"))]
|
#[strum(props(id = "105"))]
|
||||||
ImapMessageMoved(String),
|
ImapMessageMoved(String),
|
||||||
|
|
||||||
|
/// Emitted when an IMAP folder was emptied
|
||||||
|
///
|
||||||
|
/// @return 0
|
||||||
|
#[strum(props(id = "106"))]
|
||||||
|
ImapFolderEmptied(String),
|
||||||
|
|
||||||
/// Emitted when an new file in the $BLOBDIR was created
|
/// Emitted when an new file in the $BLOBDIR was created
|
||||||
///
|
///
|
||||||
/// @return 0
|
/// @return 0
|
||||||
|
|||||||
14
src/imap.rs
14
src/imap.rs
@@ -651,7 +651,8 @@ 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)
|
||||||
if self.config.read().unwrap().selected_folder_needs_expunge {
|
let needs_expunge = { self.config.read().unwrap().selected_folder_needs_expunge };
|
||||||
|
if needs_expunge {
|
||||||
if let Some(ref folder) = self.config.read().unwrap().selected_folder {
|
if let Some(ref folder) = self.config.read().unwrap().selected_folder {
|
||||||
info!(context, "Expunge messages in \"{}\".", folder);
|
info!(context, "Expunge messages in \"{}\".", folder);
|
||||||
|
|
||||||
@@ -659,16 +660,19 @@ impl Imap {
|
|||||||
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
||||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||||
match session.close() {
|
match session.close() {
|
||||||
Ok(_) => {}
|
Ok(_) => {
|
||||||
|
info!(context, "close/expunge succeeded");
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(context, "failed to close session: {:?}", err);
|
warn!(context, "failed to close session: {:?}", err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
self.config.write().unwrap().selected_folder_needs_expunge = true;
|
|
||||||
}
|
}
|
||||||
|
self.config.write().unwrap().selected_folder_needs_expunge = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select new folder
|
// select new folder
|
||||||
@@ -1510,10 +1514,10 @@ impl Imap {
|
|||||||
if self.select_folder::<String>(context, None) == 0 {
|
if self.select_folder::<String>(context, None) == 0 {
|
||||||
warn!(
|
warn!(
|
||||||
context,
|
context,
|
||||||
"could not perform expunge on empty folder {}", folder
|
"could not perform expunge on empty-marked folder {}", folder
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
info!(context, "Emptying folder '{}' done.", folder);
|
emit_event!(context, Event::ImapFolderEmptied(folder.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1276,6 +1276,12 @@ pub fn update_server_uid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn dc_empty_server(context: &Context, flags: u32) {
|
||||||
|
job_kill_action(context, Action::EmptyServer);
|
||||||
|
job_add(context, Action::EmptyServer, flags as i32, Params::new(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user