2021-12-25 22:55:36 +01:00
|
|
|
// Copyright (c) 2021 Maxime “pep” Buquet <pep@bouah.net>
|
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
use xso::{error::Error, AsXml, AsXmlText, FromXml, FromXmlText};
|
2024-06-23 09:52:41 +02:00
|
|
|
|
2021-12-25 22:55:36 +01:00
|
|
|
use crate::iq::{IqGetPayload, IqResultPayload};
|
2021-12-27 18:19:27 +01:00
|
|
|
use crate::ns;
|
2025-01-27 03:19:57 +01:00
|
|
|
use alloc::borrow::Cow;
|
2024-06-29 16:34:27 +02:00
|
|
|
|
|
|
|
|
/// Requesting a slot
|
2024-07-09 17:01:42 +02:00
|
|
|
#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
|
2024-06-29 16:34:27 +02:00
|
|
|
#[xml(namespace = ns::HTTP_UPLOAD, name = "request")]
|
|
|
|
|
pub struct SlotRequest {
|
|
|
|
|
/// The filename to be uploaded.
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
pub filename: String,
|
|
|
|
|
|
|
|
|
|
/// Size of the file to be uploaded.
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
pub size: u64,
|
|
|
|
|
|
|
|
|
|
/// Content-Type of the file.
|
|
|
|
|
#[xml(attribute(name = "content-type"))]
|
|
|
|
|
pub content_type: Option<String>,
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
|
|
|
|
|
impl IqGetPayload for SlotRequest {}
|
|
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
/// All three possible header names.
|
2021-12-27 18:19:27 +01:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2025-01-27 03:19:57 +01:00
|
|
|
pub enum HeaderName {
|
2021-12-27 18:19:27 +01:00
|
|
|
/// Authorization header
|
2025-01-27 03:19:57 +01:00
|
|
|
Authorization,
|
2021-12-27 18:19:27 +01:00
|
|
|
|
|
|
|
|
/// Cookie header
|
2025-01-27 03:19:57 +01:00
|
|
|
Cookie,
|
2021-12-27 18:19:27 +01:00
|
|
|
|
|
|
|
|
/// Expires header
|
2025-01-27 03:19:57 +01:00
|
|
|
Expires,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HeaderName {
|
|
|
|
|
/// Returns the string version of this enum value.
|
|
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
HeaderName::Authorization => "Authorization",
|
|
|
|
|
HeaderName::Cookie => "Cookie",
|
|
|
|
|
HeaderName::Expires => "Expires",
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-27 18:19:27 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
impl FromXmlText for HeaderName {
|
|
|
|
|
fn from_xml_text(mut s: String) -> Result<Self, Error> {
|
|
|
|
|
s.make_ascii_lowercase();
|
|
|
|
|
Ok(match s.as_str() {
|
|
|
|
|
"authorization" => HeaderName::Authorization,
|
|
|
|
|
"cookie" => HeaderName::Cookie,
|
|
|
|
|
"expires" => HeaderName::Expires,
|
2021-12-27 18:19:27 +01:00
|
|
|
_ => {
|
2024-06-21 16:27:43 +02:00
|
|
|
return Err(Error::Other(
|
2021-12-27 18:19:27 +01:00
|
|
|
"Header name must be either 'Authorization', 'Cookie', or 'Expires'.",
|
2024-06-21 16:27:43 +02:00
|
|
|
)
|
|
|
|
|
.into())
|
2021-12-27 18:19:27 +01:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
impl AsXmlText for HeaderName {
|
|
|
|
|
fn as_xml_text(&self) -> Result<Cow<'_, str>, Error> {
|
|
|
|
|
Ok(Cow::Borrowed(self.as_str()))
|
2021-12-27 18:19:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
/// Slot header
|
|
|
|
|
#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
|
|
|
|
|
#[xml(namespace = ns::HTTP_UPLOAD, name = "header")]
|
|
|
|
|
pub struct Header {
|
|
|
|
|
/// Name of the header
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
pub name: HeaderName,
|
2024-08-03 13:04:56 +02:00
|
|
|
|
2025-01-27 03:19:57 +01:00
|
|
|
/// Value of the header
|
|
|
|
|
#[xml(text)]
|
|
|
|
|
pub value: String,
|
2024-08-03 13:04:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Put URL
|
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
|
#[xml(namespace = ns::HTTP_UPLOAD, name = "put")]
|
|
|
|
|
pub struct Put {
|
|
|
|
|
/// URL
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
pub url: String,
|
|
|
|
|
|
|
|
|
|
/// Header list
|
|
|
|
|
#[xml(child(n = ..))]
|
|
|
|
|
pub headers: Vec<Header>,
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
|
2024-06-23 09:52:41 +02:00
|
|
|
/// Get URL
|
2024-07-09 17:01:42 +02:00
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-23 09:52:41 +02:00
|
|
|
#[xml(namespace = ns::HTTP_UPLOAD, name = "get")]
|
|
|
|
|
pub struct Get {
|
|
|
|
|
/// URL
|
|
|
|
|
#[xml(attribute)]
|
|
|
|
|
pub url: String,
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
|
2024-06-29 16:14:12 +02:00
|
|
|
/// Requesting a slot
|
|
|
|
|
#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
|
|
|
|
|
#[xml(namespace = ns::HTTP_UPLOAD, name = "slot")]
|
|
|
|
|
pub struct SlotResult {
|
|
|
|
|
/// Put URL and headers
|
|
|
|
|
#[xml(child)]
|
|
|
|
|
pub put: Put,
|
|
|
|
|
|
|
|
|
|
/// Get URL
|
|
|
|
|
#[xml(child)]
|
|
|
|
|
pub get: Get,
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
|
|
|
|
|
impl IqResultPayload for SlotResult {}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
2025-01-27 03:19:57 +01:00
|
|
|
use minidom::Element;
|
2021-12-25 22:55:36 +01:00
|
|
|
|
2025-01-27 03:24:06 +01:00
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_size() {
|
|
|
|
|
assert_size!(SlotRequest, 32);
|
2025-01-27 03:19:57 +01:00
|
|
|
assert_size!(HeaderName, 1);
|
2025-01-27 03:24:06 +01:00
|
|
|
assert_size!(Header, 16);
|
|
|
|
|
assert_size!(Put, 24);
|
|
|
|
|
assert_size!(Get, 12);
|
|
|
|
|
assert_size!(SlotResult, 36);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(target_pointer_width = "64")]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_size() {
|
|
|
|
|
assert_size!(SlotRequest, 56);
|
2025-01-27 03:19:57 +01:00
|
|
|
assert_size!(HeaderName, 1);
|
2025-01-27 03:24:06 +01:00
|
|
|
assert_size!(Header, 32);
|
|
|
|
|
assert_size!(Put, 48);
|
|
|
|
|
assert_size!(Get, 24);
|
|
|
|
|
assert_size!(SlotResult, 72);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-25 22:55:36 +01:00
|
|
|
#[test]
|
|
|
|
|
fn test_slot_request() {
|
|
|
|
|
let elem: Element = "<request xmlns='urn:xmpp:http:upload:0'
|
|
|
|
|
filename='très cool.jpg'
|
|
|
|
|
size='23456'
|
|
|
|
|
content-type='image/jpeg' />"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
let slot = SlotRequest::try_from(elem).unwrap();
|
|
|
|
|
assert_eq!(slot.filename, String::from("très cool.jpg"));
|
|
|
|
|
assert_eq!(slot.size, 23456);
|
|
|
|
|
assert_eq!(slot.content_type, Some(String::from("image/jpeg")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_slot_result() {
|
|
|
|
|
let elem: Element = "<slot xmlns='urn:xmpp:http:upload:0'>
|
|
|
|
|
<put url='https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg'>
|
|
|
|
|
<header name='Authorization'>Basic Base64String==</header>
|
|
|
|
|
<header name='Cookie'>foo=bar; user=romeo</header>
|
|
|
|
|
</put>
|
|
|
|
|
<get url='https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg' />
|
|
|
|
|
</slot>"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
let slot = SlotResult::try_from(elem).unwrap();
|
|
|
|
|
assert_eq!(slot.put.url, String::from("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg"));
|
|
|
|
|
assert_eq!(
|
2021-12-27 18:19:27 +01:00
|
|
|
slot.put.headers[0],
|
2025-01-27 03:19:57 +01:00
|
|
|
Header {
|
|
|
|
|
name: HeaderName::Authorization,
|
|
|
|
|
value: String::from("Basic Base64String==")
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2021-12-27 18:19:27 +01:00
|
|
|
slot.put.headers[1],
|
2025-01-27 03:19:57 +01:00
|
|
|
Header {
|
|
|
|
|
name: HeaderName::Cookie,
|
|
|
|
|
value: String::from("foo=bar; user=romeo")
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
);
|
|
|
|
|
assert_eq!(slot.get.url, String::from("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg"));
|
|
|
|
|
}
|
2021-12-27 20:15:34 +01:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_result_no_header() {
|
|
|
|
|
let elem: Element = "<slot xmlns='urn:xmpp:http:upload:0'>
|
|
|
|
|
<put url='https://URL' />
|
|
|
|
|
<get url='https://URL' />
|
|
|
|
|
</slot>"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
let slot = SlotResult::try_from(elem).unwrap();
|
|
|
|
|
assert_eq!(slot.put.url, String::from("https://URL"));
|
|
|
|
|
assert_eq!(slot.put.headers.len(), 0);
|
|
|
|
|
assert_eq!(slot.get.url, String::from("https://URL"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_result_bad_header() {
|
|
|
|
|
let elem: Element = "<slot xmlns='urn:xmpp:http:upload:0'>
|
|
|
|
|
<put url='https://URL'>
|
|
|
|
|
<header name='EvilHeader'>EvilValue</header>
|
|
|
|
|
</put>
|
|
|
|
|
<get url='https://URL' />
|
|
|
|
|
</slot>"
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap();
|
|
|
|
|
SlotResult::try_from(elem).unwrap_err();
|
|
|
|
|
}
|
2021-12-25 22:55:36 +01:00
|
|
|
}
|