grammers_session/
lib.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#![deny(unsafe_code)]
10
11mod chat;
12mod generated;
13mod message_box;
14
15pub use chat::{ChatHashCache, PackedChat, PackedType};
16pub use generated::types::UpdateState;
17pub use generated::types::User;
18pub use generated::LAYER as VERSION;
19use generated::{enums, types};
20use grammers_tl_types::deserialize::Error as DeserializeError;
21pub use message_box::{channel_id, PrematureEndReason};
22pub use message_box::{Gap, MessageBox};
23use std::fmt;
24use std::fs::{File, OpenOptions};
25use std::io::{self, Read, Seek, Write};
26use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
27use std::path::Path;
28use std::sync::Mutex;
29
30// Needed for auto-generated definitions.
31use grammers_tl_types::{deserialize, Deserializable, Identifiable, Serializable};
32
33pub struct Session {
34    session: Mutex<types::Session>,
35}
36
37#[allow(clippy::new_without_default)]
38impl Session {
39    pub fn new() -> Self {
40        Self {
41            session: Mutex::new(types::Session {
42                dcs: Vec::new(),
43                user: None,
44                state: None,
45            }),
46        }
47    }
48
49    /// Load a previous session instance from a file,
50    /// creating one if it doesn't exist
51    pub fn load_file_or_create<P: AsRef<Path>>(path: P) -> io::Result<Self> {
52        let path = path.as_ref();
53        if !path.exists() {
54            File::create(path)?;
55            let session = Session::new();
56            session.save_to_file(path)?;
57            Ok(session)
58        } else {
59            Self::load_file(path)
60        }
61    }
62
63    /// Load a previous session instance from a file.
64    pub fn load_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
65        let mut data = Vec::new();
66        File::open(path.as_ref())?.read_to_end(&mut data)?;
67
68        Self::load(&data).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
69    }
70
71    pub fn load(data: &[u8]) -> Result<Self, Error> {
72        Ok(Self {
73            session: Mutex::new(
74                enums::Session::from_bytes(data)
75                    .map_err(|e| match e {
76                        DeserializeError::UnexpectedEof => Error::MalformedData,
77                        DeserializeError::UnexpectedConstructor { .. } => Error::UnsupportedVersion,
78                    })?
79                    .into(),
80            ),
81        })
82    }
83
84    pub fn signed_in(&self) -> bool {
85        self.session.lock().unwrap().user.is_some()
86    }
87
88    pub fn dc_auth_key(&self, dc_id: i32) -> Option<[u8; 256]> {
89        self.session
90            .lock()
91            .unwrap()
92            .dcs
93            .iter()
94            .filter_map(|enums::DataCenter::Center(dc)| {
95                if dc.id == dc_id {
96                    if let Some(auth) = &dc.auth {
97                        let mut bytes = [0; 256];
98                        bytes.copy_from_slice(auth);
99                        Some(bytes)
100                    } else {
101                        None
102                    }
103                } else {
104                    None
105                }
106            })
107            .next()
108    }
109
110    pub fn insert_dc(&self, id: i32, addr: SocketAddr, auth: [u8; 256]) {
111        let mut session = self.session.lock().unwrap();
112        if let Some(pos) = session
113            .dcs
114            .iter()
115            .position(|enums::DataCenter::Center(dc)| dc.id == id)
116        {
117            session.dcs.remove(pos);
118        }
119
120        let (ip_v4, ip_v6): (Option<&SocketAddrV4>, Option<&SocketAddrV6>) = match &addr {
121            SocketAddr::V4(ip_v4) => (Some(ip_v4), None),
122            SocketAddr::V6(ip_v6) => (None, Some(ip_v6)),
123        };
124
125        session.dcs.push(
126            types::DataCenter {
127                id,
128                ipv4: ip_v4.map(|addr| i32::from_le_bytes(addr.ip().octets())),
129                ipv6: ip_v6.map(|addr| addr.ip().octets()),
130                port: addr.port() as i32,
131                auth: Some(auth.into()),
132            }
133            .into(),
134        );
135    }
136
137    pub fn set_user(&self, id: i64, dc: i32, bot: bool) {
138        self.session.lock().unwrap().user = Some(User { id, dc, bot }.into())
139    }
140
141    /// Returns the stored user
142    pub fn get_user(&self) -> Option<User> {
143        self.session
144            .lock()
145            .unwrap()
146            .user
147            .as_ref()
148            .map(|enums::User::User(user)| user.clone())
149    }
150
151    pub fn get_state(&self) -> Option<UpdateState> {
152        let session = self.session.lock().unwrap();
153        let enums::UpdateState::State(state) = session.state.clone()?;
154        Some(state)
155    }
156
157    pub fn set_state(&self, state: UpdateState) {
158        self.session.lock().unwrap().state = Some(state.into())
159    }
160
161    pub fn get_dcs(&self) -> Vec<types::DataCenter> {
162        self.session
163            .lock()
164            .unwrap()
165            .dcs
166            .iter()
167            .map(|enums::DataCenter::Center(dc)| dc.clone())
168            .collect()
169    }
170
171    #[must_use]
172    pub fn save(&self) -> Vec<u8> {
173        enums::Session::Session(self.session.lock().unwrap().clone()).to_bytes()
174    }
175
176    /// Saves the session to a file.
177    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
178        let mut file = OpenOptions::new().write(true).open(path.as_ref())?;
179        file.seek(io::SeekFrom::Start(0))?;
180        file.set_len(0)?;
181        file.write_all(&self.save())?;
182        file.sync_data()
183    }
184}
185
186#[derive(Debug)]
187pub enum Error {
188    MalformedData,
189    UnsupportedVersion,
190}
191
192impl fmt::Display for Error {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        match self {
195            Error::MalformedData => write!(f, "malformed data"),
196            Error::UnsupportedVersion => write!(f, "unsupported version"),
197        }
198    }
199}
200
201impl std::error::Error for Error {}