1use crate::errors::Errors;
2use crate::networking::connection::{Config, Connection, MessageSource};
3use crate::networking::data::channel_info::Channel;
4use crate::networking::data::companies::Company;
5use crate::networking::data::conversation_info::Conversation;
6use crate::networking::data::messages::{File, Message};
7use crate::networking::data::user::UserInfo;
8use base64::Engine;
9use openssl::error::ErrorStack;
10use openssl::pkey::Private;
11use openssl::rsa::{Padding, Rsa};
12use openssl::symm::{decrypt, Cipher};
13use serde_json::Value;
14use std::collections::HashMap;
15
16const BASE64ENGINE: base64::engine::GeneralPurpose = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, base64::engine::GeneralPurposeConfig::new());
17const MESSAGES_PER_REQUEST: u32 = 64;
18const CONVERSATIONS_PER_REQUEST: u32 = 64;
19
20pub struct Api {
21 conn: Connection,
22 user_info: Option<UserInfo>,
23 logged_in: bool,
24 private_key: Option<Rsa<Private>>,
25 channel_infos: Vec<Channel>,
26 conversation_infos: Vec<Conversation>,
27 key_store: HashMap<ChatID, Vec<u8>>,
28 message_store: HashMap<ChatID, Vec<Message>>,
29 companies: Vec<Company>,
30 passphrase: Option<String>,
31}
32
33impl Api {
34 async fn get_private_key(&mut self, passphrase: String) -> Result<Rsa<Private>, Errors> {
35 if let Some(key) = self.private_key.clone() {
36 return Ok(key);
37 }
38 let key_info = self.conn.get_private_key().await?;
39 let private_key_json: Result<Value, serde_json::Error> = serde_json::from_str(&*key_info.private_key);
40 if let Err(e) = private_key_json {
41 return Err(Errors::JsonDeserializeError(e));
42 }
43 let private_key_json = private_key_json.unwrap();
44 let encrypted_pem_key = private_key_json.get("private");
45 if encrypted_pem_key.is_none() {
46 return Err(Errors::OtherErrors("Couldn't load private key from field \"private\"".to_string()));
47 }
48 let encrypted_pem_key = encrypted_pem_key.unwrap();
49 let encrypted_pem_key: Option<&str> = encrypted_pem_key.as_str();
50 if encrypted_pem_key.is_none() {
51 return Err(Errors::OtherErrors("Private key is not in PEM format".to_string()));
52 }
53 let encrypted_pem_key = encrypted_pem_key.unwrap();
54 let key = Rsa::private_key_from_pem_passphrase(encrypted_pem_key.as_bytes(), passphrase.as_bytes());
55 if let Err(e) = key {
56 return Err(Errors::EncryptionError(e))
57 }
58 let key = key.unwrap();
59 self.private_key = Some(key.clone());
60 Ok(key)
61 }
62 async fn decrypt_key(&mut self, key: String) -> Result<Vec<u8>, Errors> {
63 if self.passphrase.is_none() {
64 return Err(Errors::ValueError("Passphrase is None! Passphrase needs to be set before accessing anything encrypted".to_string()))
65 }
66 let encrypted_data = BASE64ENGINE.decode(key);
67 if let Err(e) = encrypted_data {
68 return Err(Errors::Base64Error(e));
69 }
70 let encrypted_data = encrypted_data.unwrap();
71 let mut result_buf: Vec<u8> = Vec::new();
72 result_buf.resize(encrypted_data.clone().len(), 0);
73 let private_key = self.get_private_key(self.passphrase.clone().unwrap()).await?;
74 let decrypted_key_length = private_key.private_decrypt(&*encrypted_data, &mut result_buf, Padding::PKCS1_OAEP);
75 if let Err(e) = decrypted_key_length {
76 return Err(Errors::EncryptionError(e));
77 }
78 let decrypted_key_length = decrypted_key_length.unwrap();
79 result_buf.resize(decrypted_key_length, 0);
80 Ok(result_buf)
81 }
82 async fn decrypt(&mut self, chat: ChatID, data: Vec<u8>, iv: Option<String>) -> Result<Vec<u8>, Errors> {
83 if !self.key_store.contains_key(&chat) {
84 if chat.r#type == ChatType::Channel {
85 let channel = self.channel_infos.iter()
86 .find(|channel| channel.id == chat.id);
87 if channel.is_none() {
88 return Err(Errors::ValueError("Invalid channel id".to_string()));
89 }
90 let channel = channel.unwrap();
91 if !channel.encrypted.clone() {
92 return Ok(data);
93 }
94 if channel.key.is_none() {
95 return Err(Errors::ValueError("Key is none for channel \"".to_string() + &*channel.name + "\""))
96 }
97 let decrypted_key = self.decrypt_key(channel.key.clone().unwrap()).await?;
98 self.key_store.insert(chat.clone(), decrypted_key);
99 } else if chat.r#type == ChatType::Conversation {
100 let conversation = self.conversation_infos
101 .iter().find(|conversation| conversation.id == chat.id);
102 if conversation.is_none() {
103 return Err(Errors::ValueError("Invalid conversation id".to_string()));
104 }
105 let conversation = conversation.unwrap();
106 if !conversation.encrypted.clone() {
107 return Ok(data);
108 }
109 let decrypted_key = self.decrypt_key(conversation.key.clone().unwrap()).await?;
110 self.key_store.insert(chat.clone(), decrypted_key);
111 } else {
112 return Err(Errors::ValueError(format!("Unknown chat type: {:?}", chat.r#type)))
113 }
114 }
115
116 let key = self.key_store.get(&chat).expect("Key is none although key is set in keystore.");
118 let decrypted: Result<Vec<u8>, ErrorStack> = match iv {
119 None => decrypt(Cipher::aes_256_cbc(), key, None, &*data),
120 Some(iv) => match hex::decode(iv) {
121 Err(e) => return Err(Errors::HexError(e)),
122 Ok(iv) => decrypt(Cipher::aes_256_cbc(), key, Some(&*iv), &*data)
123 }
124 };
125 match decrypted {
126 Err(e) => Err(Errors::EncryptionError(e)),
127 Ok(v) => Ok(v)
128 }
129 }
130 async fn decrypt_message(&mut self, chat: ChatID, message: Message) -> Result<Option<String>, Errors>{
131 if message.encrypted.is_none() || !message.encrypted.unwrap() || message.text.is_none() || message.text.clone().unwrap().len() == 0{
132 return Ok(message.text);
133 }
134 let data = match hex::decode(message.text.unwrap()) {
135 Err(e) => return Err(Errors::HexError(e)),
136 Ok(d) => d
137 };
138 let decrypted = self.decrypt(chat, data, message.iv).await?;
139 match String::from_utf8(decrypted) {
140 Err(e) => Err(Errors::StringDecodeError(e)),
141 Ok(s) => Ok(Some(s))
142 }
143 }
144}
145impl Api {
146 pub fn new() -> Self {
147 Api {
148 conn: Connection::new(Config::new("https://api.stashcat.com/".to_string())),
149 user_info: None,
150 logged_in: false,
151 private_key: None,
152 channel_infos: Vec::new(),
153 conversation_infos: Vec::new(),
154 key_store: HashMap::new(),
155 message_store: HashMap::new(),
156 companies: Vec::new(),
157 passphrase: None,
158 }
159 }
160 pub async fn new_logged_in(device_id: String, client_key: String) -> Result<Self, Errors> {
161 let mut api = Api {
162 conn: Connection::new(Config{
163 base_url: "https://api.stashcat.com/".to_string(),
164 device_id,
165 client_key: Some(client_key),
166 }),
167 user_info: None,
168 logged_in: true,
169 private_key: None,
170 channel_infos: Vec::new(),
171 conversation_infos: Vec::new(),
172 key_store: HashMap::new(),
173 message_store: HashMap::new(),
174 companies: Vec::new(),
175 passphrase: None,
176 };
177 api.update_user_info().await?;
178 api.post_login().await?;
179 Ok(api)
180 }
181 pub async fn email_password_login(&mut self, email: String, password: String, app_name: String) -> Result<(), Errors> {
182 let dat = self.conn.email_password_login(email, password, app_name).await?;
183 self.user_info = Some(dat);
184 self.post_login().await?;
185 Ok(())
186 }
187 pub async fn update_user_info(&mut self) -> Result<(), Errors>{
188 self.user_info = Some(self.conn.get_user_info().await?);
189 Ok(())
190 }
191 async fn post_login(&mut self) -> Result<(), Errors>{
192 self.logged_in = true;
193 self.companies = self.conn.get_companies().await?;
194 for company in self.companies.clone() {
195 self.channel_infos.extend(self.conn.get_channels(company.id).await?);
196 };
197 let mut requested_conversations: u64= 0;
198 while self.conversation_infos.clone().len() == requested_conversations as usize {
199 self.conversation_infos.extend(self.conn.get_conversations(CONVERSATIONS_PER_REQUEST, requested_conversations, "0".to_string(), "[\"favorite_desc\",\"last_action_desc\"]".to_string()).await?);
200 requested_conversations += u64::from(CONVERSATIONS_PER_REQUEST);
201 };
202 Ok(())
203 }
204 pub fn set_passphrase(&mut self, passphrase: String) {self.passphrase = Some(passphrase)}
205
206 pub fn get_channel_ids(&self) -> Vec<String> {
207 self.channel_infos.iter().map(|channel| channel.id.clone()).collect()
208 }
209 pub fn get_conversation_ids(&self) -> Vec<String> {
210 self.conversation_infos
211 .iter()
212 .map(|conversation| conversation.id.clone())
213 .collect()
214 }
215 pub fn get_channels(&self) -> Vec<Channel> {
216 self.channel_infos.clone()
217 }
218 pub fn get_conversations(&self) -> Vec<Conversation> {
219 self.conversation_infos.clone()
220 }
221 pub async fn get_messages(&mut self, chat: ChatID) -> Result<Vec<Message>, Errors>{
222 if self.message_store.contains_key(&chat) {
223 return Ok(self.message_store.get(&chat).expect("Message Store contains Chat but has no data").clone());
224 }
225 let mut requested_message_count: u64 = 0;
226 let mut messages: Vec<Message> = Vec::new();
227 while messages.len() == requested_message_count as usize{
228 let mut new_messages: Vec<Message> = self.conn.get_messages(chat.id.clone(),
229 match chat.r#type {
230 ChatType::Channel => MessageSource::Channel,
231 ChatType::Conversation => MessageSource::Conversation
232 },
233 MESSAGES_PER_REQUEST,
234 requested_message_count,
235 ).await?;
236 requested_message_count += MESSAGES_PER_REQUEST as u64;
237 new_messages.extend(messages);
238 messages = new_messages;
239 }
240 self.message_store.insert(chat, messages.clone());
241 Ok(messages)
242 }
243 pub async fn get_decrypted_messages(&mut self, source: ChatID) -> Result<Vec<Option<String>>, Errors> {
244 let messages = self.get_messages(source.clone()).await?;
245 let mut decrypted: Vec<Option<String>> = Vec::new();
246 for message in messages {
247 decrypted.push(self.decrypt_message(source.clone(), message.clone()).await?);
248 }
249 Ok(decrypted)
250 }
251 pub async fn download_file(&mut self, chat: ChatID, file: File) -> Result<Vec<u8>, Errors> {
252 let encrypted = self.conn.download_file(file.id).await?;
253 if !file.encrypted {
254 return Ok(encrypted);
255 }
256 Ok(self.decrypt(chat, encrypted, file.e2e_iv).await?)
257 }
258 pub fn is_logged_in(&self) -> bool {self.logged_in}
259 pub fn has_passphrase_set(&self) -> bool {!self.passphrase.is_none()}
260 pub fn get_chat_name(&self, chat: ChatID) -> Result<String, Errors> {
261 match chat.r#type {
262 ChatType::Conversation => {
263 let conversation = self.conversation_infos.iter().find(|conversation| conversation.id == chat.id);
264 match conversation {
265 None => Err(Errors::ValueError(format!("No conversation found with id \"{}\"", chat.id))),
266 Some(conversation) => match conversation.name.clone() {
267 Some(s) => Ok(s),
268 None => Err(Errors::ValueError(format!("Conversation with id {} has no name", chat.id)))
269 }
270 }
271 }
272 ChatType::Channel => {
273 let channel = self.channel_infos.iter().find(|channel| channel.id == chat.id);
274 match channel {
275 None => Err(Errors::ValueError(format!("No channel found with id \"{}\"", chat.id))),
276 Some(channel) => Ok(channel.name.clone())
277 }
278 }
279 }
280 }
281}
282#[derive(Hash, Debug, Clone, Eq, PartialEq)]
283pub struct ChatID {
284 r#type: ChatType,
285 id: String,
286}
287impl ChatID {
288 pub fn new(r#type: ChatType, id: String) -> Self {
289 ChatID {
290 r#type,
291 id,
292 }
293 }
294 pub fn from_channel(channel: Channel) -> Self {
295 Self::new(ChatType::Channel, channel.id)
296 }
297 pub fn from_conversation(conversation: Conversation) -> Self {
298 Self::new(ChatType::Conversation, conversation.id)
299 }
300}
301#[derive(Hash, Debug, Clone, Eq, PartialEq)]
302pub enum ChatType {
303 Conversation,
304 Channel,
305}