1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use std::fmt::Display;
use std::str::FromStr;

use serde::Deserialize;



/// A value that contains one of the ways to authenticate to Assemblyline
pub enum Authentication {
    /// Authenticate with a password
    Password{
        /// The name of the user account connecting
        username: String,
        /// The password of the user connecting
        password: String
    },
    /// Authenticate with an api key
    ApiKey{
        /// The name of the user account connecting
        username: String,
        /// The API key of the user connecting
        key: String
    },
    /// Authenticate with an oauth token
    OAuth{
        /// Oauth provider
        provider: String,
        /// Oauth token
        token: String
    }
}

/// sha256 hash of a file
#[derive(Debug)]
pub struct Sha256 {
    hex: String
}

impl std::fmt::Display for Sha256 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.hex)
    }
}

impl std::ops::Deref for Sha256 {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.hex
    }
}

impl FromStr for Sha256 {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let hex = s.trim().to_ascii_lowercase();
        if hex.len() != 64 || !hex.chars().all(|c|c.is_ascii_hexdigit()) {
            return Err(Error::InvalidSha256)
        }
        return Ok(Sha256{ hex })
    }
}

impl<'de> Deserialize<'de> for Sha256 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de> {
        let content = String::deserialize(deserializer)?;
        content.parse().map_err(serde::de::Error::custom)
    }
}

/// Short name for serde json's basic map type
pub type JsonMap = serde_json::Map<String, serde_json::Value>;

/// Set of possible errors returned by client
#[derive(Debug)]
pub enum Error {
    /// An error produced by the client's communication with the server
    Client{
        /// A message describing the error
        message: String,
        /// HTTP status code associated
        status: u32,
        /// Server's API version if available
        api_version: Option<String>,
        /// Server's response details if available
        api_response: Option<String>
    },
    /// An error that occured during a failed communication with the server
    TransportError(String),
    /// An invalid HTTP header name or value was provided
    InvalidHeader,
    /// The server's response was truncated, corrupted, or malformed
    MalformedResponse,
    /// A string could not be converted into a sha256
    InvalidSha256,
    /// A path was provided for submission that couldn't be used
    InvalidSubmitFilePath,
    /// A url was provided for submission and a file name couldn't be parsed
    InvalidSubmitUrl,
    /// An error that has bubbled up from an IO call
    IO(std::io::Error),
    /// An error caused by failing to serialize or deserialize a message
    Serialization(serde_json::Error),
    /// An unexpected state was reached serializing submission parameters
    ParameterSerialization,
}

impl Error {
    pub (crate) fn client_error(message: String, status: u32) -> Self {
        return Error::Client { message, status, api_response: None, api_version: None }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Client { message, status, .. } =>
                f.write_fmt(format_args!("Client error [{status}]: {message}")),
            Error::TransportError(message) =>
                f.write_fmt(format_args!("Error communicating with server: {message}")),
            Error::InvalidHeader =>
                f.write_str("An invalid HTTP header name or value was encountered"),
            Error::MalformedResponse =>
                f.write_str("A server response was malformed"),
            Error::InvalidSha256 =>
                f.write_str("An invalid SHA256 string was provided"),
            Error::InvalidSubmitFilePath =>
                f.write_str("An invalid path was given for submission"),
            Error::InvalidSubmitUrl =>
                f.write_str("An invalid URL was given for submission, try setting the file name explicitly"),
            Error::IO(error) =>
                f.write_fmt(format_args!("An IO error ocurred: {error}")),
            Error::Serialization(error) =>
                f.write_fmt(format_args!("An error occurred serializing a body: {error}")),
            Error::ParameterSerialization =>
                f.write_str("Parameter serialization yielded unexpected type."),
        }
    }
}

impl From<reqwest::Error> for Error {
    fn from(value: reqwest::Error) -> Self {
        if let Some(code) = value.status() {
            Error::client_error(value.to_string(), code.as_u16() as u32)
        } else {
            Error::TransportError(value.to_string())
        }
    }
}

impl From<reqwest::header::InvalidHeaderName> for Error {
    fn from(_value: reqwest::header::InvalidHeaderName) -> Self {
        Self::InvalidHeader
    }
}

impl From<reqwest::header::InvalidHeaderValue> for Error {
    fn from(_value: reqwest::header::InvalidHeaderValue) -> Self {
        Self::InvalidHeader
    }
}

impl From<serde_json::Error> for Error {
    fn from(value: serde_json::Error) -> Self {
        Self::Serialization(value)
    }
}

impl From<std::io::Error> for Error {
    fn from(value: std::io::Error) -> Self {
        Self::IO(value)
    }
}

impl From<url::ParseError> for Error {
    fn from(_value: url::ParseError) -> Self {
        Self::InvalidSubmitUrl
    }
}

impl std::error::Error for Error {

}