use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use crate::{AUTH_KEY_SIZE, Result};
const DC_ADDRESSES: [(i32, Ipv4Addr, u16); 5] = [
(1, Ipv4Addr::new(149, 154, 175, 53), 443),
(2, Ipv4Addr::new(149, 154, 167, 51), 443),
(3, Ipv4Addr::new(149, 154, 175, 100), 443),
(4, Ipv4Addr::new(149, 154, 167, 91), 443),
(5, Ipv4Addr::new(91, 108, 56, 130), 443),
];
fn get_dc_addr(dc_id: i32) -> SocketAddr {
DC_ADDRESSES.iter().find(|(id, _, _)| *id == dc_id).map_or_else(
|| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(149, 154, 167, 51)), 443),
|(_, ip, port)| SocketAddr::new(IpAddr::V4(*ip), *port),
)
}
#[derive(Debug)]
pub struct Account {
index: i32,
dc_id: i32,
user_id: i64,
auth_key: [u8; AUTH_KEY_SIZE],
}
impl Account {
pub(crate) const fn new(
index: i32,
dc_id: i32,
user_id: i64,
auth_key: [u8; AUTH_KEY_SIZE],
) -> Self {
Self { index, dc_id, user_id, auth_key }
}
#[must_use]
pub const fn index(&self) -> i32 {
self.index
}
#[must_use]
pub const fn dc_id(&self) -> i32 {
self.dc_id
}
#[must_use]
pub const fn user_id(&self) -> i64 {
self.user_id
}
#[must_use]
pub const fn auth_key_bytes(&self) -> &[u8; AUTH_KEY_SIZE] {
&self.auth_key
}
pub fn to_grammers_session(&self) -> Result<grammers_session::Session> {
use grammers_session::Session;
let session = Session::new();
let addr = get_dc_addr(self.dc_id);
session.insert_dc(self.dc_id, addr, self.auth_key);
if self.user_id != 0 {
session.set_user(self.user_id, self.dc_id, false);
}
Ok(session)
}
pub fn to_session_string(&self) -> Result<String> {
let session = self.to_grammers_session()?;
let data = session.save();
Ok(base64_encode(&data))
}
}
fn base64_encode(data: &[u8]) -> String {
const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = String::new();
let mut i = 0;
while i < data.len() {
let b0 = data[i] as usize;
let b1 = if i + 1 < data.len() { data[i + 1] as usize } else { 0 };
let b2 = if i + 2 < data.len() { data[i + 2] as usize } else { 0 };
result.push(ALPHABET[b0 >> 2] as char);
result.push(ALPHABET[((b0 & 0x03) << 4) | (b1 >> 4)] as char);
if i + 1 < data.len() {
result.push(ALPHABET[((b1 & 0x0f) << 2) | (b2 >> 6)] as char);
} else {
result.push('=');
}
if i + 2 < data.len() {
result.push(ALPHABET[b2 & 0x3f] as char);
} else {
result.push('=');
}
i += 3;
}
result
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::unreadable_literal,
reason = "test assertions with controlled inputs"
)]
mod tests {
use super::*;
#[test]
fn test_account_creation() {
let auth_key = [0xAB; AUTH_KEY_SIZE];
let account = Account::new(0, 2, 12345678, auth_key);
assert_eq!(account.index(), 0);
assert_eq!(account.dc_id(), 2);
assert_eq!(account.user_id(), 12345678);
assert_eq!(account.auth_key_bytes(), &auth_key);
}
#[test]
fn test_base64_encode() {
assert_eq!(base64_encode(b""), "");
assert_eq!(base64_encode(b"f"), "Zg==");
assert_eq!(base64_encode(b"fo"), "Zm8=");
assert_eq!(base64_encode(b"foo"), "Zm9v");
assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
}
}