grammers_session/storages/
tl_session.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![allow(deprecated)]
10
11use crate::dc_options::DEFAULT_DC;
12use crate::defs::PeerAuth;
13use crate::generated::{enums, types};
14use crate::{KNOWN_DC_OPTIONS, Session};
15use grammers_tl_types::deserialize::Error as DeserializeError;
16use grammers_tl_types::{Deserializable, Serializable};
17use std::fmt;
18use std::fs::{File, OpenOptions};
19use std::io::{self, Read, Seek, Write};
20use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
21use std::path::Path;
22use std::sync::Mutex;
23
24/// Original session storage.
25///
26/// This storage leverages Telegram's own serialization format
27/// to persist data to a file on-disk.
28#[cfg_attr(
29    feature = "impl-serde",
30    derive(serde_derive::Serialize, serde_derive::Deserialize)
31)]
32#[deprecated(note = "Migrate to a different storage")]
33pub struct TlSession {
34    session: Mutex<types::Session>,
35}
36
37#[allow(clippy::new_without_default)]
38impl TlSession {
39    pub fn new() -> Self {
40        let this = Self {
41            session: Mutex::new(types::Session {
42                dcs: Vec::with_capacity(KNOWN_DC_OPTIONS.len()),
43                user: None,
44                state: None,
45            }),
46        };
47        KNOWN_DC_OPTIONS
48            .iter()
49            .for_each(|dc_option| this.set_dc_option(dc_option));
50        this
51    }
52
53    /// Load a previous session instance from a file,
54    /// creating one if it doesn't exist
55    pub fn load_file_or_create<P: AsRef<Path>>(path: P) -> io::Result<Self> {
56        let path = path.as_ref();
57        if !path.exists() {
58            File::create(path)?;
59            let session = TlSession::new();
60            session.save_to_file(path)?;
61            Ok(session)
62        } else {
63            Self::load_file(path)
64        }
65    }
66
67    /// Load a previous session instance from a file.
68    pub fn load_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
69        let mut data = Vec::new();
70        File::open(path.as_ref())?.read_to_end(&mut data)?;
71
72        Self::load(&data).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
73    }
74
75    pub fn load(data: &[u8]) -> Result<Self, Error> {
76        let this = Self {
77            session: Mutex::new(
78                enums::Session::from_bytes(data)
79                    .map_err(|e| match e {
80                        DeserializeError::UnexpectedEof => Error::MalformedData,
81                        DeserializeError::UnexpectedConstructor { .. } => Error::UnsupportedVersion,
82                    })?
83                    .into(),
84            ),
85        };
86        KNOWN_DC_OPTIONS.iter().for_each(|dc_option| {
87            if this.dc_option(dc_option.id).is_none() {
88                this.set_dc_option(dc_option);
89            }
90        });
91        Ok(this)
92    }
93
94    #[must_use]
95    pub fn save(&self) -> Vec<u8> {
96        enums::Session::Session(self.session.lock().unwrap().clone()).to_bytes()
97    }
98
99    /// Saves the session to a file.
100    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
101        let mut file = OpenOptions::new().write(true).open(path.as_ref())?;
102        file.seek(io::SeekFrom::Start(0))?;
103        file.set_len(0)?;
104        file.write_all(&self.save())?;
105        file.sync_data()
106    }
107}
108
109impl crate::Session for TlSession {
110    fn home_dc_id(&self) -> i32 {
111        let session = self.session.lock().unwrap();
112        session
113            .user
114            .as_ref()
115            .map(|enums::User::User(user)| user.dc)
116            .unwrap_or(DEFAULT_DC)
117    }
118
119    fn set_home_dc_id(&self, dc_id: i32) {
120        let mut session = self.session.lock().unwrap();
121        if let Some(enums::User::User(user)) = &mut session.user {
122            user.dc = dc_id
123        } else {
124            session.user = Some(enums::User::User(types::User {
125                id: 0,
126                bot: false,
127                dc: dc_id,
128            }))
129        }
130    }
131
132    fn dc_option(&self, dc_id: i32) -> Option<crate::defs::DcOption> {
133        let session = self.session.lock().unwrap();
134        session.dcs.iter().find_map(|dc| match dc {
135            enums::DataCenter::Center(center) if center.id == dc_id => {
136                Some(crate::defs::DcOption {
137                    id: center.id,
138                    ipv4: SocketAddrV4::new(
139                        Ipv4Addr::from_bits(center.ipv4.unwrap() as _),
140                        center.port as _,
141                    ),
142                    ipv6: SocketAddrV6::new(
143                        center
144                            .ipv6
145                            .map(|ipv6| Ipv6Addr::from_bits(u128::from_le_bytes(ipv6)))
146                            .unwrap_or_else(|| {
147                                Ipv4Addr::from_bits(center.ipv4.unwrap() as _).to_ipv6_mapped()
148                            }),
149                        center.port as _,
150                        0,
151                        0,
152                    ),
153                    auth_key: center.auth.as_deref().map(|auth| auth.try_into().unwrap()),
154                })
155            }
156            _ => None,
157        })
158    }
159
160    fn set_dc_option(&self, dc_option: &crate::defs::DcOption) {
161        let mut session = self.session.lock().unwrap();
162
163        if let Some(pos) = session.dcs.iter().position(|dc| dc.id() == dc_option.id) {
164            session.dcs.remove(pos);
165        }
166        session
167            .dcs
168            .push(enums::DataCenter::Center(types::DataCenter {
169                id: dc_option.id,
170                ipv4: Some(dc_option.ipv4.ip().to_bits() as _),
171                ipv6: Some(dc_option.ipv6.ip().to_bits().to_le_bytes()),
172                port: dc_option.ipv4.port() as _,
173                auth: dc_option.auth_key.map(|auth| auth.to_vec()),
174            }));
175    }
176
177    fn peer(&self, peer: crate::defs::PeerId) -> Option<crate::defs::PeerInfo> {
178        let session = self.session.lock().unwrap();
179        if peer.kind() == crate::defs::PeerKind::UserSelf {
180            session
181                .user
182                .as_ref()
183                .map(|enums::User::User(user)| crate::defs::PeerInfo::User {
184                    id: user.id,
185                    auth: Some(PeerAuth::default()),
186                    bot: Some(user.bot),
187                    is_self: Some(true),
188                })
189        } else {
190            None
191        }
192    }
193
194    fn cache_peer(&self, peer: &crate::defs::PeerInfo) {
195        let mut session = self.session.lock().unwrap();
196        match peer {
197            crate::defs::PeerInfo::User {
198                id,
199                auth: _,
200                bot,
201                is_self,
202            } if *is_self == Some(true) => {
203                if let Some(enums::User::User(user)) = &mut session.user {
204                    user.id = *id;
205                    user.bot = bot.unwrap_or_default();
206                } else {
207                    session.user = Some(enums::User::User(types::User {
208                        id: *id,
209                        bot: bot.unwrap_or_default(),
210                        dc: DEFAULT_DC,
211                    }))
212                }
213            }
214            _ => {}
215        }
216    }
217
218    fn updates_state(&self) -> crate::defs::UpdatesState {
219        let session = self.session.lock().unwrap();
220        session
221            .state
222            .as_ref()
223            .map(
224                |enums::UpdateState::State(state)| crate::defs::UpdatesState {
225                    pts: state.pts,
226                    qts: state.qts,
227                    date: state.date,
228                    seq: state.seq,
229                    channels: state
230                        .channels
231                        .iter()
232                        .map(
233                            |enums::ChannelState::State(channel)| crate::defs::ChannelState {
234                                id: channel.channel_id,
235                                pts: channel.pts,
236                            },
237                        )
238                        .collect(),
239                },
240            )
241            .unwrap_or_default()
242    }
243
244    fn set_update_state(&self, update: crate::defs::UpdateState) {
245        match update {
246            crate::defs::UpdateState::All(updates_state) => {
247                let mut session = self.session.lock().unwrap();
248                session.state = Some(
249                    types::UpdateState {
250                        pts: updates_state.pts,
251                        qts: updates_state.qts,
252                        date: updates_state.date,
253                        seq: updates_state.seq,
254                        channels: updates_state
255                            .channels
256                            .iter()
257                            .map(|channel| {
258                                types::ChannelState {
259                                    channel_id: channel.id,
260                                    pts: channel.pts,
261                                }
262                                .into()
263                            })
264                            .collect(),
265                    }
266                    .into(),
267                )
268            }
269            crate::defs::UpdateState::Primary { pts, date, seq } => {
270                let mut current = self.updates_state();
271                current.pts = pts;
272                current.date = date;
273                current.seq = seq;
274                self.set_update_state(crate::defs::UpdateState::All(current));
275            }
276            crate::defs::UpdateState::Secondary { qts } => {
277                let mut current = self.updates_state();
278                current.qts = qts;
279                self.set_update_state(crate::defs::UpdateState::All(current));
280            }
281            crate::defs::UpdateState::Channel { id, pts } => {
282                let mut current = self.updates_state();
283                if let Some(pos) = current.channels.iter().position(|channel| channel.id == id) {
284                    current.channels[pos] = crate::defs::ChannelState { id: id, pts }
285                } else {
286                    current
287                        .channels
288                        .push(crate::defs::ChannelState { id: id, pts });
289                }
290                self.set_update_state(crate::defs::UpdateState::All(current));
291            }
292        }
293    }
294}
295
296#[derive(Debug)]
297pub enum Error {
298    MalformedData,
299    UnsupportedVersion,
300}
301
302impl fmt::Display for Error {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            Error::MalformedData => write!(f, "malformed data"),
306            Error::UnsupportedVersion => write!(f, "unsupported version"),
307        }
308    }
309}
310
311impl std::error::Error for Error {}