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
use mime_guess::Mime;
use serde_json;

use crate::crypto::b64;

/// The MIME type string for a tar file.
const MIME_TAR: &str = "application/x-tar";

/// File metadata, which is send to the server.
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Metadata {
    /// Metadata using in Send v2.
    V2 {
        /// The file name.
        name: String,

        /// The input vector.
        iv: String,

        /// The file mimetype.
        /// TODO: can we use the `Mime` type here?
        #[serde(rename = "type")]
        mime: String,
    },

    /// Metadata using in Send v3.
    V3 {
        /// The file name.
        name: String,

        /// The file mimetype.
        /// TODO: can we use the `Mime` type here?
        #[serde(rename = "type")]
        mime: String,

        /// The file size.
        size: u64,

        /// The share manifest.
        manifest: Manifest,
    },
}

impl Metadata {
    /// Construct metadata from the given properties.
    ///
    /// Parameters:
    /// * `iv`: initialisation vector
    /// * `name`: file name
    /// * `mime`: file mimetype
    pub fn from_send2(iv: &[u8], name: String, mime: &Mime) -> Self {
        Metadata::V2 {
            iv: b64::encode(iv),
            name,
            mime: mime.to_string(),
        }
    }

    /// Construct metadata from the given properties.
    ///
    /// Parameters:
    /// * `name`: file name
    /// * `mime`: file mimetype
    /// * `size`: file size
    pub fn from_send3(name: String, mime: String, size: u64) -> Self {
        Metadata::V3 {
            name: name.clone(),
            mime: mime.clone(),
            size,
            manifest: Manifest::from_file(name, mime, size),
        }
    }

    /// Convert this structure to a JSON string.
    pub fn to_json(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }

    /// Get the file name.
    pub fn name(&self) -> &str {
        match self {
            Metadata::V2 { name, .. } => &name,
            Metadata::V3 { name, .. } => &name,
        }
    }

    /// Get the file MIME type.
    pub fn mime(&self) -> &str {
        match self {
            Metadata::V2 { mime, .. } => &mime,
            Metadata::V3 { mime, .. } => &mime,
        }
    }

    /// Get the input vector if set.
    ///
    /// For Firefox Send v3 and above `None` is returned as no input vector is used.
    // TODO: use an input vector length from a constant
    pub fn iv(&self) -> Option<[u8; 12]> {
        // Get the input vector
        let iv = match self {
            Metadata::V2 { iv, .. } => iv,
            Metadata::V3 { .. } => return None,
        };

        // Decode the input vector
        let decoded = b64::decode(iv).unwrap();

        // Create a sized array
        Some(*array_ref!(decoded, 0, 12))
    }

    /// Get the file size if set (`>= Send v3`).
    pub fn size(&self) -> Option<u64> {
        match self {
            Metadata::V2 { .. } => None,
            Metadata::V3 { size, .. } => Some(*size),
        }
    }

    /**
     * Check whether this MIME type is recognized as supported archive type.
     * `true` is returned if it's an archive, `false` if not.
     */
    pub fn is_archive(&self) -> bool {
        self.mime().to_lowercase() == MIME_TAR.to_lowercase()
    }
}

/// Metadata manifest, used in Send v3.
#[derive(Debug, Serialize, Deserialize)]
pub struct Manifest {
    /// Files part of a share.
    files: Vec<ManifestFile>,
}

impl Manifest {
    /// Construct a new manifest for the given list of files.
    pub fn from(files: Vec<ManifestFile>) -> Self {
        Self { files }
    }

    /// Construct a manifest for a single file, with the given properties.
    pub fn from_file(name: String, mime: String, size: u64) -> Self {
        Self::from(vec![ManifestFile::from(name, mime, size)])
    }
}

/// Metadata manifest file, used in Send v3.
#[derive(Debug, Serialize, Deserialize)]
pub struct ManifestFile {
    /// The file name.
    name: String,

    /// The file mimetype.
    /// TODO: can we use the `Mime` type here?
    #[serde(rename = "type")]
    mime: String,

    /// The file size.
    size: u64,
}

impl ManifestFile {
    /// Construct a new manifest file.
    pub fn from(name: String, mime: String, size: u64) -> Self {
        ManifestFile { name, mime, size }
    }
}