Skip to content

Commit 16ed021

Browse files
committed
api: dc_accounts_set_push_device_token and dc_get_push_state APIs
1 parent 9599239 commit 16ed021

File tree

11 files changed

+308
-14
lines changed

11 files changed

+308
-14
lines changed

deltachat-ffi/deltachat.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,24 @@ int dc_get_connectivity (dc_context_t* context);
686686
char* dc_get_connectivity_html (dc_context_t* context);
687687

688688

689+
#define DC_PUSH_NOT_CONNECTED 0
690+
#define DC_PUSH_HEARTBEAT 1
691+
#define DC_PUSH_CONNECTED 2
692+
693+
/**
694+
* Get the current push notification state.
695+
* One of:
696+
* - DC_PUSH_NOT_CONNECTED
697+
* - DC_PUSH_HEARTBEAT
698+
* - DC_PUSH_CONNECTED
699+
*
700+
* @memberof dc_context_t
701+
* @param context The context object.
702+
* @return Push notification state.
703+
*/
704+
int dc_get_push_state (dc_context_t* context);
705+
706+
689707
/**
690708
* Standalone version of dc_accounts_all_work_done().
691709
* Only used by the python tests.
@@ -3165,6 +3183,16 @@ void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
31653183
*/
31663184
int dc_accounts_background_fetch (dc_accounts_t* accounts, uint64_t timeout);
31673185

3186+
3187+
/**
3188+
* Sets device token for Apple Push Notification service.
3189+
* Returns immediately.
3190+
*
3191+
* @memberof dc_accounts_t
3192+
* @param token Hexadecimal device token
3193+
*/
3194+
void dc_accounts_set_push_device_token (dc_accounts_t* accounts, const char *token);
3195+
31683196
/**
31693197
* Create the event emitter that is used to receive events.
31703198
*

deltachat-ffi/src/lib.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> li
384384
return 0;
385385
}
386386
let ctx = &*context;
387-
block_on(async move { ctx.get_connectivity().await as u32 as libc::c_int })
387+
block_on(ctx.get_connectivity()) as u32 as libc::c_int
388388
}
389389

390390
#[no_mangle]
@@ -407,6 +407,16 @@ pub unsafe extern "C" fn dc_get_connectivity_html(
407407
})
408408
}
409409

410+
#[no_mangle]
411+
pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
412+
if context.is_null() {
413+
eprintln!("ignoring careless call to dc_get_push_state()");
414+
return 0;
415+
}
416+
let ctx = &*context;
417+
block_on(ctx.push_state()) as libc::c_int
418+
}
419+
410420
#[no_mangle]
411421
pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c_int {
412422
if context.is_null() {
@@ -4919,6 +4929,29 @@ pub unsafe extern "C" fn dc_accounts_background_fetch(
49194929
1
49204930
}
49214931

4932+
#[no_mangle]
4933+
pub unsafe extern "C" fn dc_accounts_set_push_device_token(
4934+
accounts: *mut dc_accounts_t,
4935+
token: *const libc::c_char,
4936+
) {
4937+
if accounts.is_null() {
4938+
eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
4939+
return;
4940+
}
4941+
4942+
let accounts = &*accounts;
4943+
let token = to_string_lossy(token);
4944+
4945+
block_on(async move {
4946+
let mut accounts = accounts.write().await;
4947+
if let Err(err) = accounts.set_push_device_token(&token).await {
4948+
accounts.emit_event(EventType::Error(format!(
4949+
"Failed to set notify token: {err:#}."
4950+
)));
4951+
}
4952+
})
4953+
}
4954+
49224955
#[no_mangle]
49234956
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
49244957
accounts: *mut dc_accounts_t,

src/accounts.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use tokio::time::{sleep, Duration};
1919

2020
use crate::context::{Context, ContextBuilder};
2121
use crate::events::{Event, EventEmitter, EventType, Events};
22+
use crate::push::PushSubscriber;
2223
use crate::stock_str::StockStrings;
2324

2425
/// Account manager, that can handle multiple accounts in a single place.
@@ -37,6 +38,9 @@ pub struct Accounts {
3738
/// This way changing a translation for one context automatically
3839
/// changes it for all other contexts.
3940
pub(crate) stockstrings: StockStrings,
41+
42+
/// Push notification subscriber shared between accounts.
43+
push_subscriber: PushSubscriber,
4044
}
4145

4246
impl Accounts {
@@ -73,8 +77,9 @@ impl Accounts {
7377
.context("failed to load accounts config")?;
7478
let events = Events::new();
7579
let stockstrings = StockStrings::new();
80+
let push_subscriber = PushSubscriber::new();
7681
let accounts = config
77-
.load_accounts(&events, &stockstrings, &dir)
82+
.load_accounts(&events, &stockstrings, push_subscriber.clone(), &dir)
7883
.await
7984
.context("failed to load accounts")?;
8085

@@ -84,6 +89,7 @@ impl Accounts {
8489
accounts,
8590
events,
8691
stockstrings,
92+
push_subscriber,
8793
})
8894
}
8995

@@ -124,6 +130,7 @@ impl Accounts {
124130
.with_id(account_config.id)
125131
.with_events(self.events.clone())
126132
.with_stock_strings(self.stockstrings.clone())
133+
.with_push_subscriber(self.push_subscriber.clone())
127134
.build()
128135
.await?;
129136
// Try to open without a passphrase,
@@ -144,6 +151,7 @@ impl Accounts {
144151
.with_id(account_config.id)
145152
.with_events(self.events.clone())
146153
.with_stock_strings(self.stockstrings.clone())
154+
.with_push_subscriber(self.push_subscriber.clone())
147155
.build()
148156
.await?;
149157
self.accounts.insert(account_config.id, ctx);
@@ -340,6 +348,12 @@ impl Accounts {
340348
pub fn get_event_emitter(&self) -> EventEmitter {
341349
self.events.get_emitter()
342350
}
351+
352+
/// Sets notification token for Apple Push Notification service.
353+
pub async fn set_push_device_token(&mut self, token: &str) -> Result<()> {
354+
self.push_subscriber.set_device_token(token).await;
355+
Ok(())
356+
}
343357
}
344358

345359
/// Configuration file name.
@@ -525,6 +539,7 @@ impl Config {
525539
&self,
526540
events: &Events,
527541
stockstrings: &StockStrings,
542+
push_subscriber: PushSubscriber,
528543
dir: &Path,
529544
) -> Result<BTreeMap<u32, Context>> {
530545
let mut accounts = BTreeMap::new();
@@ -535,6 +550,7 @@ impl Config {
535550
.with_id(account_config.id)
536551
.with_events(events.clone())
537552
.with_stock_strings(stockstrings.clone())
553+
.with_push_subscriber(push_subscriber.clone())
538554
.build()
539555
.await
540556
.with_context(|| format!("failed to create context from file {:?}", &dbfile))?;

src/context.rs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::login_param::LoginParam;
3030
use crate::message::{self, Message, MessageState, MsgId, Viewtype};
3131
use crate::param::{Param, Params};
3232
use crate::peerstate::Peerstate;
33+
use crate::push::PushSubscriber;
3334
use crate::quota::QuotaInfo;
3435
use crate::scheduler::{convert_folder_meaning, SchedulerState};
3536
use crate::sql::Sql;
@@ -86,6 +87,8 @@ pub struct ContextBuilder {
8687
events: Events,
8788
stock_strings: StockStrings,
8889
password: Option<String>,
90+
91+
push_subscriber: Option<PushSubscriber>,
8992
}
9093

9194
impl ContextBuilder {
@@ -101,6 +104,7 @@ impl ContextBuilder {
101104
events: Events::new(),
102105
stock_strings: StockStrings::new(),
103106
password: None,
107+
push_subscriber: None,
104108
}
105109
}
106110

@@ -155,10 +159,23 @@ impl ContextBuilder {
155159
self
156160
}
157161

162+
/// Sets push subscriber.
163+
pub(crate) fn with_push_subscriber(mut self, push_subscriber: PushSubscriber) -> Self {
164+
self.push_subscriber = Some(push_subscriber);
165+
self
166+
}
167+
158168
/// Builds the [`Context`] without opening it.
159169
pub async fn build(self) -> Result<Context> {
160-
let context =
161-
Context::new_closed(&self.dbfile, self.id, self.events, self.stock_strings).await?;
170+
let push_subscriber = self.push_subscriber.unwrap_or_default();
171+
let context = Context::new_closed(
172+
&self.dbfile,
173+
self.id,
174+
self.events,
175+
self.stock_strings,
176+
push_subscriber,
177+
)
178+
.await?;
162179
Ok(context)
163180
}
164181

@@ -263,6 +280,13 @@ pub struct InnerContext {
263280
/// Standard RwLock instead of [`tokio::sync::RwLock`] is used
264281
/// because the lock is used from synchronous [`Context::emit_event`].
265282
pub(crate) debug_logging: std::sync::RwLock<Option<DebugLogging>>,
283+
284+
/// Push subscriber to store device token
285+
/// and register for heartbeat notifications.
286+
pub(crate) push_subscriber: PushSubscriber,
287+
288+
/// True if account has subscribed to push notifications via IMAP.
289+
pub(crate) push_subscribed: AtomicBool,
266290
}
267291

268292
/// The state of ongoing process.
@@ -308,7 +332,8 @@ impl Context {
308332
events: Events,
309333
stock_strings: StockStrings,
310334
) -> Result<Context> {
311-
let context = Self::new_closed(dbfile, id, events, stock_strings).await?;
335+
let context =
336+
Self::new_closed(dbfile, id, events, stock_strings, Default::default()).await?;
312337

313338
// Open the database if is not encrypted.
314339
if context.check_passphrase("".to_string()).await? {
@@ -323,6 +348,7 @@ impl Context {
323348
id: u32,
324349
events: Events,
325350
stockstrings: StockStrings,
351+
push_subscriber: PushSubscriber,
326352
) -> Result<Context> {
327353
let mut blob_fname = OsString::new();
328354
blob_fname.push(dbfile.file_name().unwrap_or_default());
@@ -331,7 +357,14 @@ impl Context {
331357
if !blobdir.exists() {
332358
tokio::fs::create_dir_all(&blobdir).await?;
333359
}
334-
let context = Context::with_blobdir(dbfile.into(), blobdir, id, events, stockstrings)?;
360+
let context = Context::with_blobdir(
361+
dbfile.into(),
362+
blobdir,
363+
id,
364+
events,
365+
stockstrings,
366+
push_subscriber,
367+
)?;
335368
Ok(context)
336369
}
337370

@@ -374,6 +407,7 @@ impl Context {
374407
id: u32,
375408
events: Events,
376409
stockstrings: StockStrings,
410+
push_subscriber: PushSubscriber,
377411
) -> Result<Context> {
378412
ensure!(
379413
blobdir.is_dir(),
@@ -408,6 +442,8 @@ impl Context {
408442
last_full_folder_scan: Mutex::new(None),
409443
last_error: std::sync::RwLock::new("".to_string()),
410444
debug_logging: std::sync::RwLock::new(None),
445+
push_subscriber,
446+
push_subscribed: AtomicBool::new(false),
411447
};
412448

413449
let ctx = Context {
@@ -1509,7 +1545,14 @@ mod tests {
15091545
let tmp = tempfile::tempdir().unwrap();
15101546
let dbfile = tmp.path().join("db.sqlite");
15111547
let blobdir = PathBuf::new();
1512-
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
1548+
let res = Context::with_blobdir(
1549+
dbfile,
1550+
blobdir,
1551+
1,
1552+
Events::new(),
1553+
StockStrings::new(),
1554+
Default::default(),
1555+
);
15131556
assert!(res.is_err());
15141557
}
15151558

@@ -1518,7 +1561,14 @@ mod tests {
15181561
let tmp = tempfile::tempdir().unwrap();
15191562
let dbfile = tmp.path().join("db.sqlite");
15201563
let blobdir = tmp.path().join("blobs");
1521-
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
1564+
let res = Context::with_blobdir(
1565+
dbfile,
1566+
blobdir,
1567+
1,
1568+
Events::new(),
1569+
StockStrings::new(),
1570+
Default::default(),
1571+
);
15221572
assert!(res.is_err());
15231573
}
15241574

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

1744-
let id = 1;
1745-
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
1794+
let context = ContextBuilder::new(dbfile.clone())
1795+
.with_id(1)
1796+
.build()
17461797
.await
17471798
.context("failed to create context")?;
17481799
assert_eq!(context.open("foo".to_string()).await?, true);
17491800
assert_eq!(context.is_open().await, true);
17501801
drop(context);
17511802

1752-
let id = 2;
1753-
let context = Context::new(&dbfile, id, Events::new(), StockStrings::new())
1803+
let context = ContextBuilder::new(dbfile)
1804+
.with_id(2)
1805+
.build()
17541806
.await
17551807
.context("failed to create context")?;
17561808
assert_eq!(context.is_open().await, false);
@@ -1766,8 +1818,9 @@ mod tests {
17661818
let dir = tempdir()?;
17671819
let dbfile = dir.path().join("db.sqlite");
17681820

1769-
let id = 1;
1770-
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
1821+
let context = ContextBuilder::new(dbfile)
1822+
.with_id(1)
1823+
.build()
17711824
.await
17721825
.context("failed to create context")?;
17731826
assert_eq!(context.open("foo".to_string()).await?, true);

0 commit comments

Comments
 (0)