file.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. extern crate chrono;
  2. extern crate regex;
  3. use url::{
  4. ParseError as UrlParseError,
  5. Url,
  6. };
  7. use self::chrono::{DateTime, Utc};
  8. use self::regex::Regex;
  9. use crypto::b64;
  10. /// A pattern for Send download URL paths, capturing the file ID.
  11. // TODO: match any sub-path?
  12. // TODO: match URL-safe base64 chars for the file ID?
  13. // TODO: constrain the ID length?
  14. const DOWNLOAD_PATH_PATTERN: &'static str = r"^/?download/([[:alnum:]]{8,}={0,3})/?$";
  15. /// A pattern for Send download URL fragments, capturing the file secret.
  16. // TODO: constrain the secret length?
  17. const DOWNLOAD_FRAGMENT_PATTERN: &'static str = r"^([a-zA-Z0-9-_+/]+)?\s*$";
  18. /// A struct representing an uploaded file on a Send host.
  19. ///
  20. /// The struct contains the file ID, the file URL, the key that is required
  21. /// in combination with the file, and the owner key.
  22. #[derive(Debug)]
  23. pub struct File {
  24. /// The ID of the file on that server.
  25. id: String,
  26. /// The time the file was uploaded at.
  27. time: DateTime<Utc>,
  28. /// The host the file was uploaded to.
  29. host: Url,
  30. /// The file URL that was provided by the server.
  31. url: Url,
  32. /// The secret key that is required to download the file.
  33. secret: Vec<u8>,
  34. /// The owner key, that can be used to manage the file on the server.
  35. owner_key: String,
  36. }
  37. impl File {
  38. /// Construct a new file.
  39. pub fn new(
  40. id: String,
  41. time: DateTime<Utc>,
  42. host: Url,
  43. url: Url,
  44. secret: Vec<u8>,
  45. owner_key: String,
  46. ) -> Self {
  47. Self {
  48. id,
  49. time,
  50. host,
  51. url,
  52. secret,
  53. owner_key,
  54. }
  55. }
  56. /// Construct a new file, that was created at this exact time.
  57. pub fn new_now(
  58. id: String,
  59. host: Url,
  60. url: Url,
  61. secret: Vec<u8>,
  62. owner_key: String,
  63. ) -> Self {
  64. Self::new(
  65. id,
  66. Utc::now(),
  67. host,
  68. url,
  69. secret,
  70. owner_key,
  71. )
  72. }
  73. /// Get the raw secret.
  74. pub fn secret_raw(&self) -> &Vec<u8> {
  75. // A secret must have been set
  76. if !self.has_secret() {
  77. // TODO: don't panic, return an error instead
  78. panic!("missing secret");
  79. }
  80. &self.secret
  81. }
  82. /// Get the secret as base64 encoded string.
  83. pub fn secret(&self) -> String {
  84. b64::encode(self.secret_raw())
  85. }
  86. /// Check whether a file secret is set.
  87. /// This secret must be set to decrypt a downloaded Send file.
  88. pub fn has_secret(&self) -> bool {
  89. !self.secret.is_empty()
  90. }
  91. /// Get the download URL of the file.
  92. /// Set `secret` to `true`, to include it in the URL if known.
  93. pub fn download_url(&self, secret: bool) -> Url {
  94. // Get the download URL, and add the secret fragment
  95. let mut url = self.url.clone();
  96. if secret && self.has_secret() {
  97. url.set_fragment(Some(&self.secret()));
  98. } else {
  99. url.set_fragment(None);
  100. }
  101. url
  102. }
  103. }
  104. // TODO: merge this struct with `File`.
  105. pub struct DownloadFile {
  106. /// The ID of the file on that server.
  107. id: String,
  108. /// The host the file was uploaded to.
  109. host: Url,
  110. /// The file URL that was provided by the server.
  111. url: Url,
  112. /// The secret key that is required to download the file.
  113. secret: Vec<u8>,
  114. }
  115. impl DownloadFile {
  116. /// Construct a new instance.
  117. pub fn new(
  118. id: String,
  119. host: Url,
  120. url: Url,
  121. secret: Vec<u8>,
  122. ) -> Self {
  123. Self {
  124. id,
  125. host,
  126. url,
  127. secret,
  128. }
  129. }
  130. /// Try to parse the given Send download URL.
  131. ///
  132. /// The given URL is matched against a Send download URL pattern,
  133. /// this does not check whether the host is a valid and online Send host.
  134. ///
  135. /// If the URL fragmet contains a file secret, it is also parsed.
  136. /// If it does not, the secret is left empty and must be specified
  137. /// manually.
  138. pub fn parse_url(url: Url) -> Result<DownloadFile, FileParseError> {
  139. // Build the host
  140. let mut host = url.clone();
  141. host.set_fragment(None);
  142. host.set_query(None);
  143. host.set_path("");
  144. // Validate the path, get the file ID
  145. let re_path = Regex::new(DOWNLOAD_PATH_PATTERN).unwrap();
  146. let id = re_path.captures(url.path())
  147. .ok_or(FileParseError::InvalidDownloadUrl)?[1]
  148. .trim()
  149. .to_owned();
  150. // Get the file secret
  151. let mut secret = Vec::new();
  152. if let Some(fragment) = url.fragment() {
  153. let re_fragment = Regex::new(DOWNLOAD_FRAGMENT_PATTERN).unwrap();
  154. if let Some(raw) = re_fragment.captures(fragment)
  155. .ok_or(FileParseError::InvalidSecret)?
  156. .get(1)
  157. {
  158. secret = b64::decode(raw.as_str().trim())
  159. .map_err(|_| FileParseError::InvalidSecret)?
  160. }
  161. }
  162. // Construct the file
  163. Ok(Self::new(
  164. id,
  165. host,
  166. url,
  167. secret,
  168. ))
  169. }
  170. /// Get the raw secret.
  171. pub fn secret_raw(&self) -> &Vec<u8> {
  172. // A secret must have been set
  173. if !self.has_secret() {
  174. // TODO: don't panic, return an error instead
  175. panic!("missing secret");
  176. }
  177. &self.secret
  178. }
  179. /// Get the secret as base64 encoded string.
  180. pub fn secret(&self) -> String {
  181. b64::encode(self.secret_raw())
  182. }
  183. /// Check whether a file secret is set.
  184. /// This secret must be set to decrypt a downloaded Send file.
  185. pub fn has_secret(&self) -> bool {
  186. !self.secret.is_empty()
  187. }
  188. /// Set the secret for this file.
  189. /// An empty vector will clear the secret.
  190. pub fn set_secret(&mut self, secret: Vec<u8>) {
  191. self.secret = secret;
  192. }
  193. /// Get the download URL of the file.
  194. /// Set `secret` to `true`, to include it in the URL if known.
  195. pub fn download_url(&self, secret: bool) -> Url {
  196. // Get the download URL, and add the secret fragment
  197. let mut url = self.url.clone();
  198. if secret && self.has_secret() {
  199. url.set_fragment(Some(&self.secret()));
  200. } else {
  201. url.set_fragment(None);
  202. }
  203. url
  204. }
  205. /// Get the API metadata URL of the file.
  206. pub fn api_meta_url(&self) -> Url {
  207. // Get the download URL, and add the secret fragment
  208. let mut url = self.url.clone();
  209. url.set_path(format!("/api/metadata/{}", self.id).as_str());
  210. url.set_fragment(None);
  211. url
  212. }
  213. /// Get the API download URL of the file.
  214. pub fn api_download_url(&self) -> Url {
  215. // Get the download URL, and add the secret fragment
  216. let mut url = self.url.clone();
  217. url.set_path(format!("/api/download/{}", self.id).as_str());
  218. url.set_fragment(None);
  219. url
  220. }
  221. }
  222. #[derive(Debug)]
  223. pub enum FileParseError {
  224. /// An URL format error.
  225. UrlFormatError(UrlParseError),
  226. /// An error for an invalid download URL format.
  227. InvalidDownloadUrl,
  228. /// An error for an invalid secret format, if an URL fragmet exists.
  229. InvalidSecret,
  230. }