Skip to content

Commit

Permalink
api: dc_accounts_set_push_device_token and dc_get_push_state APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
link2xt committed Mar 4, 2024
1 parent 9599239 commit 16ed021
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 14 deletions.
28 changes: 28 additions & 0 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,24 @@ int dc_get_connectivity (dc_context_t* context);
char* dc_get_connectivity_html (dc_context_t* context);


#define DC_PUSH_NOT_CONNECTED 0
#define DC_PUSH_HEARTBEAT 1
#define DC_PUSH_CONNECTED 2

/**
* Get the current push notification state.
* One of:
* - DC_PUSH_NOT_CONNECTED
* - DC_PUSH_HEARTBEAT
* - DC_PUSH_CONNECTED
*
* @memberof dc_context_t
* @param context The context object.
* @return Push notification state.
*/
int dc_get_push_state (dc_context_t* context);


/**
* Standalone version of dc_accounts_all_work_done().
* Only used by the python tests.
Expand Down Expand Up @@ -3165,6 +3183,16 @@ void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
*/
int dc_accounts_background_fetch (dc_accounts_t* accounts, uint64_t timeout);


/**
* Sets device token for Apple Push Notification service.
* Returns immediately.
*
* @memberof dc_accounts_t
* @param token Hexadecimal device token
*/
void dc_accounts_set_push_device_token (dc_accounts_t* accounts, const char *token);

/**
* Create the event emitter that is used to receive events.
*
Expand Down
35 changes: 34 additions & 1 deletion deltachat-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> li
return 0;
}
let ctx = &*context;
block_on(async move { ctx.get_connectivity().await as u32 as libc::c_int })
block_on(ctx.get_connectivity()) as u32 as libc::c_int
}

#[no_mangle]
Expand All @@ -407,6 +407,16 @@ pub unsafe extern "C" fn dc_get_connectivity_html(
})
}

#[no_mangle]
pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
if context.is_null() {
eprintln!("ignoring careless call to dc_get_push_state()");
return 0;
}
let ctx = &*context;
block_on(ctx.push_state()) as libc::c_int
}

#[no_mangle]
pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c_int {
if context.is_null() {
Expand Down Expand Up @@ -4919,6 +4929,29 @@ pub unsafe extern "C" fn dc_accounts_background_fetch(
1
}

#[no_mangle]
pub unsafe extern "C" fn dc_accounts_set_push_device_token(
accounts: *mut dc_accounts_t,
token: *const libc::c_char,
) {
if accounts.is_null() {
eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
return;
}

let accounts = &*accounts;
let token = to_string_lossy(token);

block_on(async move {
let mut accounts = accounts.write().await;
if let Err(err) = accounts.set_push_device_token(&token).await {
accounts.emit_event(EventType::Error(format!(
"Failed to set notify token: {err:#}."
)));
}
})
}

#[no_mangle]
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
accounts: *mut dc_accounts_t,
Expand Down
18 changes: 17 additions & 1 deletion src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use tokio::time::{sleep, Duration};

use crate::context::{Context, ContextBuilder};
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::push::PushSubscriber;
use crate::stock_str::StockStrings;

/// Account manager, that can handle multiple accounts in a single place.
Expand All @@ -37,6 +38,9 @@ pub struct Accounts {
/// This way changing a translation for one context automatically
/// changes it for all other contexts.
pub(crate) stockstrings: StockStrings,

/// Push notification subscriber shared between accounts.
push_subscriber: PushSubscriber,
}

impl Accounts {
Expand Down Expand Up @@ -73,8 +77,9 @@ impl Accounts {
.context("failed to load accounts config")?;
let events = Events::new();
let stockstrings = StockStrings::new();
let push_subscriber = PushSubscriber::new();
let accounts = config
.load_accounts(&events, &stockstrings, &dir)
.load_accounts(&events, &stockstrings, push_subscriber.clone(), &dir)
.await
.context("failed to load accounts")?;

Expand All @@ -84,6 +89,7 @@ impl Accounts {
accounts,
events,
stockstrings,
push_subscriber,
})
}

Expand Down Expand Up @@ -124,6 +130,7 @@ impl Accounts {
.with_id(account_config.id)
.with_events(self.events.clone())
.with_stock_strings(self.stockstrings.clone())
.with_push_subscriber(self.push_subscriber.clone())
.build()
.await?;
// Try to open without a passphrase,
Expand All @@ -144,6 +151,7 @@ impl Accounts {
.with_id(account_config.id)
.with_events(self.events.clone())
.with_stock_strings(self.stockstrings.clone())
.with_push_subscriber(self.push_subscriber.clone())
.build()
.await?;
self.accounts.insert(account_config.id, ctx);
Expand Down Expand Up @@ -340,6 +348,12 @@ impl Accounts {
pub fn get_event_emitter(&self) -> EventEmitter {
self.events.get_emitter()
}

/// Sets notification token for Apple Push Notification service.
pub async fn set_push_device_token(&mut self, token: &str) -> Result<()> {
self.push_subscriber.set_device_token(token).await;
Ok(())
}
}

/// Configuration file name.
Expand Down Expand Up @@ -525,6 +539,7 @@ impl Config {
&self,
events: &Events,
stockstrings: &StockStrings,
push_subscriber: PushSubscriber,
dir: &Path,
) -> Result<BTreeMap<u32, Context>> {
let mut accounts = BTreeMap::new();
Expand All @@ -535,6 +550,7 @@ impl Config {
.with_id(account_config.id)
.with_events(events.clone())
.with_stock_strings(stockstrings.clone())
.with_push_subscriber(push_subscriber.clone())
.build()
.await
.with_context(|| format!("failed to create context from file {:?}", &dbfile))?;
Expand Down
77 changes: 65 additions & 12 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::login_param::LoginParam;
use crate::message::{self, Message, MessageState, MsgId, Viewtype};
use crate::param::{Param, Params};
use crate::peerstate::Peerstate;
use crate::push::PushSubscriber;
use crate::quota::QuotaInfo;
use crate::scheduler::{convert_folder_meaning, SchedulerState};
use crate::sql::Sql;
Expand Down Expand Up @@ -86,6 +87,8 @@ pub struct ContextBuilder {
events: Events,
stock_strings: StockStrings,
password: Option<String>,

push_subscriber: Option<PushSubscriber>,
}

impl ContextBuilder {
Expand All @@ -101,6 +104,7 @@ impl ContextBuilder {
events: Events::new(),
stock_strings: StockStrings::new(),
password: None,
push_subscriber: None,
}
}

Expand Down Expand Up @@ -155,10 +159,23 @@ impl ContextBuilder {
self
}

/// Sets push subscriber.
pub(crate) fn with_push_subscriber(mut self, push_subscriber: PushSubscriber) -> Self {
self.push_subscriber = Some(push_subscriber);
self
}

/// Builds the [`Context`] without opening it.
pub async fn build(self) -> Result<Context> {
let context =
Context::new_closed(&self.dbfile, self.id, self.events, self.stock_strings).await?;
let push_subscriber = self.push_subscriber.unwrap_or_default();
let context = Context::new_closed(
&self.dbfile,
self.id,
self.events,
self.stock_strings,
push_subscriber,
)
.await?;
Ok(context)
}

Expand Down Expand Up @@ -263,6 +280,13 @@ pub struct InnerContext {
/// Standard RwLock instead of [`tokio::sync::RwLock`] is used
/// because the lock is used from synchronous [`Context::emit_event`].
pub(crate) debug_logging: std::sync::RwLock<Option<DebugLogging>>,

/// Push subscriber to store device token
/// and register for heartbeat notifications.
pub(crate) push_subscriber: PushSubscriber,

/// True if account has subscribed to push notifications via IMAP.
pub(crate) push_subscribed: AtomicBool,
}

/// The state of ongoing process.
Expand Down Expand Up @@ -308,7 +332,8 @@ impl Context {
events: Events,
stock_strings: StockStrings,
) -> Result<Context> {
let context = Self::new_closed(dbfile, id, events, stock_strings).await?;
let context =
Self::new_closed(dbfile, id, events, stock_strings, Default::default()).await?;

// Open the database if is not encrypted.
if context.check_passphrase("".to_string()).await? {
Expand All @@ -323,6 +348,7 @@ impl Context {
id: u32,
events: Events,
stockstrings: StockStrings,
push_subscriber: PushSubscriber,
) -> Result<Context> {
let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default());
Expand All @@ -331,7 +357,14 @@ impl Context {
if !blobdir.exists() {
tokio::fs::create_dir_all(&blobdir).await?;
}
let context = Context::with_blobdir(dbfile.into(), blobdir, id, events, stockstrings)?;
let context = Context::with_blobdir(
dbfile.into(),
blobdir,
id,
events,
stockstrings,
push_subscriber,
)?;
Ok(context)
}

Expand Down Expand Up @@ -374,6 +407,7 @@ impl Context {
id: u32,
events: Events,
stockstrings: StockStrings,
push_subscriber: PushSubscriber,
) -> Result<Context> {
ensure!(
blobdir.is_dir(),
Expand Down Expand Up @@ -408,6 +442,8 @@ impl Context {
last_full_folder_scan: Mutex::new(None),
last_error: std::sync::RwLock::new("".to_string()),
debug_logging: std::sync::RwLock::new(None),
push_subscriber,
push_subscribed: AtomicBool::new(false),
};

let ctx = Context {
Expand Down Expand Up @@ -1509,7 +1545,14 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new();
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
let res = Context::with_blobdir(
dbfile,
blobdir,
1,
Events::new(),
StockStrings::new(),
Default::default(),
);
assert!(res.is_err());
}

Expand All @@ -1518,7 +1561,14 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
let res = Context::with_blobdir(
dbfile,
blobdir,
1,
Events::new(),
StockStrings::new(),
Default::default(),
);
assert!(res.is_err());
}

Expand Down Expand Up @@ -1741,16 +1791,18 @@ mod tests {
let dir = tempdir()?;
let dbfile = dir.path().join("db.sqlite");

let id = 1;
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile.clone())
.with_id(1)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.open("foo".to_string()).await?, true);
assert_eq!(context.is_open().await, true);
drop(context);

let id = 2;
let context = Context::new(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile)
.with_id(2)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.is_open().await, false);
Expand All @@ -1766,8 +1818,9 @@ mod tests {
let dir = tempdir()?;
let dbfile = dir.path().join("db.sqlite");

let id = 1;
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile)
.with_id(1)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.open("foo".to_string()).await?, true);
Expand Down
Loading

0 comments on commit 16ed021

Please sign in to comment.