use std::collections::HashMap;
use json::Value;
use secrecy::{ExposeSecret, SecretString};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Serialize, Serializer};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::error::{ErrorCode, Result};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
pub enum NodeKind {
File = 0,
Folder = 1,
Root = 2,
Inbox = 3,
Trash = 4,
#[serde(other)]
Unknown = u8::MAX,
}
impl NodeKind {
pub fn is_file(self) -> bool {
matches!(self, NodeKind::File)
}
pub fn is_folder(self) -> bool {
matches!(self, NodeKind::Folder)
}
pub fn is_root(self) -> bool {
matches!(self, NodeKind::Root)
}
pub fn is_rubbish_bin(self) -> bool {
matches!(self, NodeKind::Trash)
}
pub fn is_inbox(self) -> bool {
matches!(self, NodeKind::Inbox)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "a")]
pub enum Request {
#[serde(rename = "us0")]
PreLogin {
#[serde(rename = "user", serialize_with = "serialize_secret_string")]
user: SecretString,
},
#[serde(rename = "us")]
Login {
#[serde(
rename = "user",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
user: Option<SecretString>,
#[serde(
rename = "uh",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
user_handle: Option<SecretString>,
#[serde(
rename = "sek",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
sek: Option<SecretString>,
#[serde(
rename = "si",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
si: Option<SecretString>,
#[serde(
rename = "mfa",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
mfa: Option<SecretString>,
},
#[serde(rename = "sml")]
Logout {},
#[serde(rename = "ug")]
UserInfo {
#[serde(rename = "v", skip_serializing_if = "Option::is_none")]
v: Option<i32>,
},
#[serde(rename = "usl")]
ListSessions {
#[serde(rename = "x", skip_serializing_if = "Option::is_none")]
x: Option<i32>,
},
#[serde(rename = "usr")]
KillSessions {
#[serde(rename = "ko", skip_serializing_if = "Option::is_none")]
ko: Option<i32>,
#[serde(
rename = "s",
skip_serializing_if = "Vec::is_empty",
serialize_with = "serialize_secret_string_vec"
)]
s: Vec<SecretString>,
},
#[serde(rename = "uga")]
UserAttributes {
#[serde(rename = "u", serialize_with = "serialize_secret_string")]
user_handle: SecretString,
#[serde(rename = "ua")]
attribute: String,
#[serde(rename = "v")]
v: i32,
},
#[serde(rename = "uq")]
Quota {
#[serde(rename = "xfer")]
xfer: i32,
#[serde(rename = "strg")]
strg: i32,
},
#[serde(rename = "f")]
FetchNodes {
#[serde(rename = "c")]
c: i32,
#[serde(rename = "r", skip_serializing_if = "Option::is_none")]
r: Option<i32>,
},
#[serde(rename = "g")]
Download {
#[serde(rename = "g")]
g: i32,
#[serde(rename = "ssl")]
ssl: i32,
#[serde(
rename = "p",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
p: Option<SecretString>,
#[serde(
rename = "n",
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_secret_string_optional"
)]
n: Option<SecretString>,
},
#[serde(rename = "u")]
Upload {
#[serde(rename = "s")]
s: u64,
#[serde(rename = "ssl")]
ssl: i32,
},
#[serde(rename = "p")]
UploadComplete {
#[serde(rename = "t", serialize_with = "serialize_secret_string")]
t: SecretString,
#[serde(rename = "n")]
n: [UploadAttributes; 1],
#[serde(rename = "i", skip_serializing_if = "String::is_empty")]
i: String,
},
#[serde(rename = "a")]
SetFileAttributes {
#[serde(rename = "attr")]
attr: String,
#[serde(rename = "key", skip_serializing_if = "Option::is_none")]
key: Option<String>,
#[serde(rename = "n", serialize_with = "serialize_secret_string")]
n: SecretString,
#[serde(rename = "i")]
i: String,
},
#[serde(rename = "m")]
Move {
#[serde(rename = "n", serialize_with = "serialize_secret_string")]
n: SecretString,
#[serde(rename = "t", serialize_with = "serialize_secret_string")]
t: SecretString,
#[serde(rename = "i")]
i: String,
},
#[serde(rename = "d")]
Delete {
#[serde(rename = "n", serialize_with = "serialize_secret_string")]
n: SecretString,
#[serde(rename = "i")]
i: String,
},
#[serde(rename = "ufa")]
UploadFileAttributes {
#[serde(rename = "h", serialize_with = "serialize_secret_string_optional")]
h: Option<SecretString>,
#[serde(rename = "fah")]
fah: Option<String>,
#[serde(rename = "s")]
s: Option<u64>,
#[serde(rename = "ssl")]
ssl: i32,
#[serde(rename = "r")]
r: Option<i32>,
},
#[serde(rename = "pfa")]
PutFileAttributes {
#[serde(rename = "n", serialize_with = "serialize_secret_string")]
n: SecretString,
#[serde(rename = "fa")]
fa: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Response {
Error(ErrorCode),
PreLogin(PreLoginResponse),
Login(LoginResponse),
Logout(LogoutResponse),
UserInfo(UserInfoResponse),
ListSessions(ListSessionsResponse),
KillSessions(KillSessionsResponse),
UserAttributes(UserAttributesResponse),
Quota(QuotaResponse),
FetchNodes(FetchNodesResponse),
Download(DownloadResponse),
Upload(UploadResponse),
UploadComplete(UploadCompleteResponse),
SetFileAttributes(SetFileAttributesResponse),
Move(MoveResponse),
Delete(DeleteResponse),
UploadFileAttributes(UploadFileAttributesResponse),
PutFileAttributes(PutFileAttributesResponse),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreLoginResponse {
#[serde(rename = "v")]
pub version: i32,
#[serde(rename = "s")]
pub salt: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LoginResponse {
#[serde(rename = "ach")]
pub ach: i32,
#[serde(rename = "csid")]
pub csid: Option<String>,
#[serde(rename = "k")]
pub key: Option<String>,
#[serde(rename = "sek")]
pub sek: Option<String>,
#[serde(rename = "privk")]
pub privk: String,
#[serde(rename = "u")]
pub u: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LogoutResponse {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserInfoResponse {
#[serde(rename = "u")]
pub u: String,
#[serde(rename = "s")]
pub s: i32,
#[serde(rename = "email")]
pub email: String,
#[serde(rename = "firstname", default)]
pub firstname: String,
#[serde(rename = "lastname", default)]
pub lastname: String,
#[serde(rename = "country")]
pub country: Option<String>,
#[serde(rename = "birthday")]
pub birthday: Option<String>,
#[serde(rename = "birthmonth")]
pub birthmonth: Option<String>,
#[serde(rename = "birthyear")]
pub birthyear: Option<String>,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "k")]
pub key: String,
#[serde(rename = "c")]
pub c: i32,
#[serde(rename = "pubk")]
pub pubk: String,
#[serde(rename = "privk")]
pub privk: String,
#[serde(rename = "terms")]
pub terms: Option<String>,
#[serde(rename = "ts")]
pub ts: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ListSessionsResponse {
pub sessions: Vec<SessionInfo>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SessionInfo {
#[serde(rename = "timestamp")]
pub timestamp: i64,
#[serde(rename = "mru")]
pub mru: i64,
#[serde(rename = "user_agent")]
pub user_agent: String,
#[serde(rename = "ip")]
pub ip: String,
#[serde(rename = "country")]
pub country: String,
#[serde(rename = "current")]
pub current: i32,
#[serde(rename = "id")]
pub id: String,
#[serde(rename = "alive")]
pub alive: i32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KillSessionsResponse {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserAttributesResponse {
#[serde(rename = "v")]
pub v: String,
#[serde(rename = "av")]
pub attr_value: String,
#[serde(flatten)]
pub other: HashMap<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct QuotaResponse {
#[serde(rename = "mstrg")]
pub mstrg: u64,
#[serde(rename = "cstrg")]
pub cstrg: u64,
#[serde(rename = "cstrgn")]
pub cstrgn: HashMap<String, Vec<u64>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FileMetadata {
#[serde(rename = "h")]
pub hash: String,
#[serde(rename = "ha")]
pub hash_auth: String,
#[serde(rename = "k")]
pub key: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FileS {
#[serde(rename = "h")]
pub hash: String,
#[serde(rename = "u")]
pub user: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FileUser {
#[serde(rename = "u")]
pub user: String,
#[serde(rename = "c")]
pub c: i32,
#[serde(rename = "m")]
pub email: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FileNode {
#[serde(rename = "t")]
pub kind: NodeKind,
#[serde(rename = "a")]
pub attr: String,
#[serde(rename = "fa")]
pub file_attr: Option<String>,
#[serde(rename = "h")]
pub handle: String,
#[serde(rename = "p")]
pub parent: String,
#[serde(rename = "ts")]
pub ts: i64,
#[serde(rename = "u")]
pub user: String,
#[serde(rename = "k")]
pub key: Option<String>,
#[serde(rename = "su")]
pub s_user: Option<String>,
#[serde(rename = "sk")]
pub s_key: Option<String>,
#[serde(rename = "s")]
pub sz: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FetchNodesResponse {
#[serde(rename = "f")]
pub nodes: Vec<FileNode>,
#[serde(rename = "ok")]
pub ok: Option<Vec<FileMetadata>>,
#[serde(rename = "s")]
pub s: Option<Vec<FileS>>,
#[serde(rename = "u")]
pub user: Option<Vec<FileUser>>,
#[serde(rename = "sn")]
pub sn: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DownloadResponse {
#[serde(rename = "g")]
pub download_url: String,
#[serde(rename = "s")]
pub size: u64,
#[serde(rename = "at")]
pub attr: String,
#[serde(rename = "e")]
pub err: Option<ErrorCode>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UploadResponse {
#[serde(rename = "p")]
pub upload_url: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UploadAttributes {
#[serde(rename = "t")]
pub kind: NodeKind,
#[serde(rename = "a")]
pub attr: String,
#[serde(rename = "k")]
pub key: String,
#[serde(rename = "h")]
pub completion_handle: String,
#[serde(rename = "fa")]
pub file_attr: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UploadCompleteResponse {
#[serde(rename = "f")]
pub f: Vec<FileNode>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetFileAttributesResponse {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UploadFileAttributesResponse {
pub p: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PutFileAttributesResponse {
#[serde(rename = "fa")]
fa: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MoveResponse {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeleteResponse {}
impl Request {
pub(crate) fn parse_response_data(&self, value: Value) -> Result<Response> {
if value.is_number() {
let code = json::from_value(value)?;
return Ok(Response::Error(code));
}
let response = match self {
Request::PreLogin { .. } => {
let response = json::from_value(value)?;
Response::PreLogin(response)
}
Request::Login { .. } => {
let response = json::from_value(value)?;
Response::Login(response)
}
Request::Logout { .. } => {
let response = json::from_value(value)?;
Response::Logout(response)
}
Request::UserInfo { .. } => {
let response = json::from_value(value)?;
Response::UserInfo(response)
}
Request::ListSessions { .. } => {
let response = json::from_value(value)?;
Response::ListSessions(response)
}
Request::KillSessions { .. } => {
let response = json::from_value(value)?;
Response::KillSessions(response)
}
Request::UserAttributes { .. } => {
let response = json::from_value(value)?;
Response::UserAttributes(response)
}
Request::Quota { .. } => {
let response = json::from_value(value)?;
Response::Quota(response)
}
Request::FetchNodes { .. } => {
let response = json::from_value(value)?;
Response::FetchNodes(response)
}
Request::Download { .. } => {
let response = json::from_value(value)?;
Response::Download(response)
}
Request::Upload { .. } => {
let response = json::from_value(value)?;
Response::Upload(response)
}
Request::UploadComplete { .. } => {
let response = json::from_value(value)?;
Response::UploadComplete(response)
}
Request::SetFileAttributes { .. } => {
let response = json::from_value(value)?;
Response::SetFileAttributes(response)
}
Request::Move { .. } => {
let response = json::from_value(value)?;
Response::Move(response)
}
Request::Delete { .. } => {
let response = json::from_value(value)?;
Response::Delete(response)
}
Request::UploadFileAttributes { .. } => {
let response = json::from_value(value)?;
Response::UploadFileAttributes(response)
}
Request::PutFileAttributes { .. } => {
let response = json::from_value(value)?;
Response::PutFileAttributes(PutFileAttributesResponse { fa: response })
}
};
Ok(response)
}
}
fn serialize_secret_string<S>(value: &SecretString, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.expose_secret().serialize(serializer)
}
fn serialize_secret_string_optional<S>(
value: &Option<SecretString>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value
.as_ref()
.map(|secret| secret.expose_secret())
.serialize(serializer)
}
fn serialize_secret_string_vec<S>(
value: &Vec<SecretString>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(value.len()))?;
for item in value {
seq.serialize_element(item.expose_secret())?;
}
seq.end()
}