1use std::io;
9use std::path::PathBuf;
10use crate::session::{CachedPeer, DcEntry, PersistedSession, UpdatesStateSnap};
11
12pub trait SessionBackend: Send + Sync {
15 fn save(&self, session: &PersistedSession) -> io::Result<()>;
16 fn load(&self) -> io::Result<Option<PersistedSession>>;
17 fn delete(&self) -> io::Result<()>;
18 fn name(&self) -> &str;
19}
20
21pub struct BinaryFileBackend {
25 path: PathBuf,
26}
27
28impl BinaryFileBackend {
29 pub fn new(path: impl Into<PathBuf>) -> Self {
30 Self { path: path.into() }
31 }
32
33 pub fn path(&self) -> &std::path::Path { &self.path }
35}
36
37impl SessionBackend for BinaryFileBackend {
38 fn save(&self, session: &PersistedSession) -> io::Result<()> {
39 session.save(&self.path)
40 }
41 fn load(&self) -> io::Result<Option<PersistedSession>> {
42 if !self.path.exists() { return Ok(None); }
43 PersistedSession::load(&self.path).map(Some)
44 }
45 fn delete(&self) -> io::Result<()> {
46 if self.path.exists() { std::fs::remove_file(&self.path)?; }
47 Ok(())
48 }
49 fn name(&self) -> &str { "binary-file" }
50}
51
52#[derive(Default)]
60pub struct InMemoryBackend {
61 data: std::sync::Mutex<Option<MemData>>,
62}
63
64#[derive(Clone)]
65struct MemData {
66 home_dc_id: i32,
67 dcs: Vec<DcEntry>,
68 updates_state: UpdatesStateSnap,
69 peers: Vec<CachedPeer>,
70}
71
72impl InMemoryBackend {
73 pub fn new() -> Self { Self::default() }
74}
75
76impl SessionBackend for InMemoryBackend {
77 fn save(&self, s: &PersistedSession) -> io::Result<()> {
78 *self.data.lock().unwrap() = Some(MemData {
79 home_dc_id: s.home_dc_id,
80 dcs: s.dcs.clone(),
81 updates_state: s.updates_state.clone(),
82 peers: s.peers.clone(),
83 });
84 Ok(())
85 }
86 fn load(&self) -> io::Result<Option<PersistedSession>> {
87 Ok(self.data.lock().unwrap().as_ref().map(|d| PersistedSession {
88 home_dc_id: d.home_dc_id,
89 dcs: d.dcs.clone(),
90 updates_state: d.updates_state.clone(),
91 peers: d.peers.clone(),
92 }))
93 }
94 fn delete(&self) -> io::Result<()> {
95 *self.data.lock().unwrap() = None;
96 Ok(())
97 }
98 fn name(&self) -> &str { "in-memory" }
99}
100
101#[cfg(feature = "sqlite-session")]
104pub use sqlite_backend::SqliteBackend;
105
106#[cfg(feature = "sqlite-session")]
107mod sqlite_backend {
108 use super::*;
109 use rusqlite::{Connection, params};
110
111 pub struct SqliteBackend { path: PathBuf }
124
125 impl SqliteBackend {
126 pub fn new(path: impl Into<PathBuf>) -> io::Result<Self> {
127 let path = path.into();
128 let conn = Connection::open(&path)
129 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
130 conn.execute_batch(
131 "CREATE TABLE IF NOT EXISTS meta (
132 key TEXT PRIMARY KEY,
133 value TEXT NOT NULL
134 );
135 CREATE TABLE IF NOT EXISTS dc_entries (
136 dc_id INTEGER PRIMARY KEY,
137 addr TEXT NOT NULL,
138 auth_key BLOB,
139 first_salt INTEGER NOT NULL DEFAULT 0,
140 time_offset INTEGER NOT NULL DEFAULT 0
141 );
142 CREATE TABLE IF NOT EXISTS channel_pts (
143 channel_id INTEGER PRIMARY KEY,
144 pts INTEGER NOT NULL DEFAULT 0
145 );
146 CREATE TABLE IF NOT EXISTS peers (
147 id INTEGER PRIMARY KEY,
148 access_hash INTEGER NOT NULL,
149 is_channel INTEGER NOT NULL DEFAULT 0
150 );",
151 ).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
152 Ok(Self { path })
153 }
154
155 fn conn(&self) -> io::Result<Connection> {
156 Connection::open(&self.path)
157 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
158 }
159 }
160
161 impl SessionBackend for SqliteBackend {
162 fn save(&self, s: &PersistedSession) -> io::Result<()> {
163 let conn = self.conn()?;
164 let e = |e: rusqlite::Error| io::Error::new(io::ErrorKind::Other, e);
165
166 for (k, v) in [
168 ("home_dc_id", s.home_dc_id.to_string()),
169 ("pts", s.updates_state.pts.to_string()),
170 ("qts", s.updates_state.qts.to_string()),
171 ("date", s.updates_state.date.to_string()),
172 ("seq", s.updates_state.seq.to_string()),
173 ] {
174 conn.execute(
175 "INSERT OR REPLACE INTO meta (key, value) VALUES (?1, ?2)",
176 params![k, v],
177 ).map_err(e)?;
178 }
179
180 for dc in &s.dcs {
182 conn.execute(
183 "INSERT OR REPLACE INTO dc_entries
184 (dc_id, addr, auth_key, first_salt, time_offset)
185 VALUES (?1, ?2, ?3, ?4, ?5)",
186 params![
187 dc.dc_id,
188 dc.addr,
189 dc.auth_key.map(|k| k.to_vec()),
190 dc.first_salt,
191 dc.time_offset,
192 ],
193 ).map_err(e)?;
194 }
195
196 conn.execute_batch("DELETE FROM channel_pts").map_err(e)?;
198 for &(cid, cpts) in &s.updates_state.channels {
199 conn.execute(
200 "INSERT INTO channel_pts (channel_id, pts) VALUES (?1, ?2)",
201 params![cid, cpts],
202 ).map_err(e)?;
203 }
204
205 conn.execute_batch("DELETE FROM peers").map_err(e)?;
207 for p in &s.peers {
208 conn.execute(
209 "INSERT INTO peers (id, access_hash, is_channel) VALUES (?1, ?2, ?3)",
210 params![p.id, p.access_hash, p.is_channel as i32],
211 ).map_err(e)?;
212 }
213
214 Ok(())
215 }
216
217 fn load(&self) -> io::Result<Option<PersistedSession>> {
218 if !self.path.exists() { return Ok(None); }
219 let conn = self.conn()?;
220 let e = |err: rusqlite::Error| io::Error::new(io::ErrorKind::Other, err);
221
222 macro_rules! meta_i32 {
223 ($key:expr, $default:expr) => {
224 conn.query_row(
225 "SELECT value FROM meta WHERE key = ?1",
226 params![$key],
227 |row| row.get::<_, String>(0),
228 ).ok()
229 .and_then(|v| v.parse::<i32>().ok())
230 .unwrap_or($default)
231 };
232 }
233
234 let home_dc_id = meta_i32!("home_dc_id", 0);
235 if home_dc_id == 0 { return Ok(None); }
236
237 let mut stmt = conn.prepare(
239 "SELECT dc_id, addr, auth_key, first_salt, time_offset FROM dc_entries"
240 ).map_err(e)?;
241 let dcs: Vec<DcEntry> = stmt.query_map([], |row| {
242 let key_blob: Option<Vec<u8>> = row.get(2)?;
243 let auth_key = key_blob.and_then(|k| {
244 if k.len() == 256 {
245 let mut a = [0u8; 256]; a.copy_from_slice(&k); Some(a)
246 } else { None }
247 });
248 Ok(DcEntry {
249 dc_id: row.get(0)?,
250 addr: row.get(1)?,
251 auth_key,
252 first_salt: row.get(3)?,
253 time_offset: row.get(4)?,
254 })
255 }).map_err(e)?.filter_map(|r| r.ok()).collect();
256
257 let pts = meta_i32!("pts", 0);
259 let qts = meta_i32!("qts", 0);
260 let date = meta_i32!("date", 0);
261 let seq = meta_i32!("seq", 0);
262
263 let mut ch_stmt = conn.prepare(
264 "SELECT channel_id, pts FROM channel_pts"
265 ).map_err(e)?;
266 let channels: Vec<(i64, i32)> = ch_stmt
267 .query_map([], |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i32>(1)?)))
268 .map_err(e)?.filter_map(|r| r.ok()).collect();
269
270 let mut peer_stmt = conn.prepare(
272 "SELECT id, access_hash, is_channel FROM peers"
273 ).map_err(e)?;
274 let peers: Vec<CachedPeer> = peer_stmt
275 .query_map([], |row| Ok(CachedPeer {
276 id: row.get(0)?,
277 access_hash: row.get(1)?,
278 is_channel: row.get::<_, i32>(2)? != 0,
279 }))
280 .map_err(e)?.filter_map(|r| r.ok()).collect();
281
282 Ok(Some(PersistedSession {
283 home_dc_id,
284 dcs,
285 updates_state: UpdatesStateSnap { pts, qts, date, seq, channels },
286 peers,
287 }))
288 }
289
290 fn delete(&self) -> io::Result<()> {
291 if self.path.exists() { std::fs::remove_file(&self.path)?; }
292 Ok(())
293 }
294
295 fn name(&self) -> &str { "sqlite" }
296 }
297}