1#![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
30use 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 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 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 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 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 {}