1extern crate mime;
2
3use std::fs::File;
4use std::io::{self, Error as IoError, Read};
5#[cfg(feature = "send3")]
6use std::mem;
7use std::path::PathBuf;
8use std::sync::{Arc, Mutex};
9
10#[cfg(feature = "send2")]
11use self::mime::APPLICATION_OCTET_STREAM;
12#[cfg(feature = "send3")]
13use chrono::Duration;
14use chrono::{DateTime, Utc};
15use mime_guess::{self, Mime};
16#[cfg(feature = "send2")]
17use reqwest::blocking::multipart::{Form, Part};
18#[cfg(feature = "send2")]
19use reqwest::blocking::Request;
20#[cfg(feature = "send2")]
21use reqwest::header::AUTHORIZATION;
22use reqwest::Error as ReqwestError;
23#[cfg(feature = "send3")]
24use serde_json;
25use thiserror::Error;
26use url::{ParseError as UrlParseError, Url};
27#[cfg(feature = "send3")]
28use websocket::{result::WebSocketError, OwnedMessage};
29
30#[cfg(feature = "send2")]
31use super::params::Params;
32use super::params::{Error as ParamsError, ParamsData};
33use super::password::{Error as PasswordError, Password};
34#[cfg(feature = "send2")]
35use crate::api::nonce::header_nonce;
36#[cfg(feature = "send2")]
37use crate::api::request::ensure_success;
38use crate::api::request::ResponseError;
39use crate::api::Version;
40use crate::client::Client;
41use crate::crypto::{self, api::MetadataError, key_set::KeySet};
42#[cfg(feature = "send3")]
43use crate::file::info::FileInfo;
44use crate::file::metadata::Metadata;
45use crate::file::remote_file::RemoteFile;
46#[cfg(feature = "send3")]
47use crate::io::ChunkRead;
48#[cfg(feature = "send2")]
49use crate::pipe::crypto::GcmCrypt;
50#[cfg(feature = "send3")]
51use crate::pipe::crypto::{ece, EceCrypt};
52use crate::pipe::{
53 prelude::*,
54 progress::{ProgressPipe, ProgressReporter},
55};
56
57pub struct Upload {
61 version: Version,
63
64 host: Url,
66
67 path: PathBuf,
69
70 name: Option<String>,
74
75 password: Option<String>,
77
78 params: Option<ParamsData>,
80}
81
82impl Upload {
83 pub fn new(
85 version: Version,
86 host: Url,
87 path: PathBuf,
88 name: Option<String>,
89 password: Option<String>,
90 params: Option<ParamsData>,
91 ) -> Self {
92 Self {
93 version,
94 host,
95 path,
96 name,
97 password,
98 params,
99 }
100 }
101
102 pub fn invoke(
104 self,
105 client: &Client,
106 reporter: Option<&Arc<Mutex<dyn ProgressReporter>>>,
107 ) -> Result<RemoteFile, Error> {
108 let file = FileData::from(&self.path)?;
110 let key = KeySet::generate(true);
111
112 let reader = self.create_reader(&key, reporter.cloned())?;
114 let reader_len = reader.len_in() as u64;
115
116 if let Some(reporter) = reporter {
118 reporter
119 .lock()
120 .map_err(|_| UploadError::Progress)?
121 .start(reader_len);
122 }
123
124 let (result, nonce) = match self.version {
126 #[cfg(feature = "send2")]
127 Version::V2 => self.upload_send2(client, &key, &file, reader)?,
128 #[cfg(feature = "send3")]
129 Version::V3 => self.upload_send3(client, &key, &file, reader)?,
130 };
131
132 if let Some(reporter) = reporter {
134 reporter.lock().map_err(|_| UploadError::Progress)?.finish();
135 }
136
137 if let Some(password) = self.password {
139 Password::new(&result, &password, nonce.clone()).invoke(client)?;
140 }
141
142 #[cfg(feature = "send2")]
144 {
145 #[allow(unreachable_patterns)]
146 match self.version {
147 Version::V2 => {
148 if let Some(mut params) = self.params {
149 params.normalize(self.version);
150 if !params.is_empty() {
151 Params::new(&result, params, nonce.clone()).invoke(client)?;
152 }
153 }
154 }
155 _ => {}
156 }
157 }
158
159 Ok(result)
160 }
161
162 #[cfg(feature = "send2")]
164 fn create_metadata(&self, key_set: &KeySet, file: &FileData) -> Result<String, MetadataError> {
165 let name = self.name.clone().unwrap_or_else(|| file.name().to_owned());
167
168 let metadata = Metadata::from_send2(key_set.nonce(), name, &file.mime());
170 crypto::api::encrypt_metadata(metadata, key_set)
171 }
172
173 #[cfg(feature = "send3")]
175 fn create_file_info(&self, key_set: &KeySet, file: &FileData) -> Result<String, MetadataError> {
176 let name = self.name.clone().unwrap_or_else(|| file.name().to_owned());
178
179 let mime = format!("{}", file.mime());
181 let metadata = Metadata::from_send3(name, mime, file.size());
182 let metadata = crypto::api::encrypt_metadata(metadata, key_set)?;
183
184 let expiry = self.params.as_ref().and_then(|p| p.expiry_time);
186 let downloads = self.params.as_ref().and_then(|p| p.download_limit);
187
188 Ok(FileInfo::from(expiry, downloads, metadata, key_set).to_json())
190 }
191
192 fn create_reader(
194 &self,
195 key: &KeySet,
196 reporter: Option<Arc<Mutex<dyn ProgressReporter>>>,
197 ) -> Result<Reader, Error> {
198 let file = match File::open(self.path.as_path()) {
200 Ok(file) => file,
201 Err(err) => return Err(FileError::Open(err).into()),
202 };
203
204 let len = file
206 .metadata()
207 .expect("failed to fetch file metadata")
208 .len();
209
210 let progress = ProgressPipe::zero(len, reporter);
212 let reader = progress.reader(Box::new(file));
213
214 match self.version {
216 #[cfg(feature = "send2")]
217 Version::V2 => {
218 let encrypt = GcmCrypt::encrypt(len as usize, key.file_key().unwrap(), key.nonce());
219 let reader = encrypt.reader(Box::new(reader));
220 Ok(Reader::new(Box::new(reader)))
221 }
222 #[cfg(feature = "send3")]
223 Version::V3 => {
224 let ikm = key.secret().to_vec();
225 let encrypt = EceCrypt::encrypt(len as usize, ikm, None);
226 let reader = encrypt.reader(Box::new(reader));
227 Ok(Reader::new(Box::new(reader)))
228 }
229 }
230 }
231
232 #[cfg(feature = "send2")]
234 fn upload_send2(
235 &self,
236 client: &Client,
237 key: &KeySet,
238 file: &FileData,
239 reader: Reader,
240 ) -> Result<(RemoteFile, Option<Vec<u8>>), Error> {
241 let metadata = self.create_metadata(&key, file)?;
243
244 let req = self.create_request_send2(client, &key, &metadata, reader)?;
246
247 self.execute_request_send2(req, client, &key)
249 .map_err(|e| e.into())
250 }
251
252 #[cfg(feature = "send2")]
254 fn create_request_send2(
255 &self,
256 client: &Client,
257 key: &KeySet,
258 metadata: &str,
259 reader: Reader,
260 ) -> Result<Request, UploadError> {
261 let len = reader.len_out() as u64;
263
264 let part = Part::reader_with_length(reader, len)
266 .mime_str(APPLICATION_OCTET_STREAM.as_ref())
267 .expect("failed to set request mime");
268 let form = Form::new().part("data", part);
269
270 let url = self.host.join("api/upload")?;
272
273 client
275 .post(url.as_str())
276 .header(
277 AUTHORIZATION.as_str(),
278 format!("send-v1 {}", key.auth_key_encoded().unwrap()),
279 )
280 .header("X-File-Metadata", metadata)
281 .multipart(form)
282 .build()
283 .map_err(|_| UploadError::Request)
284 }
285
286 #[cfg(feature = "send2")]
289 fn execute_request_send2(
290 &self,
291 req: Request,
292 client: &Client,
293 key: &KeySet,
294 ) -> Result<(RemoteFile, Option<Vec<u8>>), UploadError> {
295 let response = match client.execute(req) {
297 Ok(response) => response,
298 Err(_) => return Err(UploadError::Request),
300 };
301
302 ensure_success(&response).map_err(UploadError::Response)?;
304
305 let nonce = header_nonce(&response).ok();
307
308 let response: UploadResponse = match response.json() {
310 Ok(response) => response,
311 Err(err) => return Err(UploadError::Decode(err)),
312 };
313
314 Ok((response.into_file(self.host.clone(), &key, None)?, nonce))
316 }
317
318 #[cfg(feature = "send3")]
320 fn upload_send3(
321 &self,
322 client: &Client,
323 key: &KeySet,
324 file_data: &FileData,
325 mut reader: Reader,
326 ) -> Result<(RemoteFile, Option<Vec<u8>>), Error> {
327 let ws_url = self
329 .host
330 .join("api/ws")
331 .map_err(|e| Error::Upload(e.into()))?;
332
333 let mut client = client
335 .websocket(ws_url.as_str())
336 .map_err(|_| Error::Upload(UploadError::Request))?;
337
338 let file_info = self
340 .create_file_info(&key, file_data)
341 .map_err(|e| -> Error { e.into() })?;
342 let ws_metadata = OwnedMessage::Text(file_info);
343 client
344 .send_message(&ws_metadata)
345 .map_err(|e| Error::Upload(e.into()))?;
346
347 let result = client
349 .recv_message()
350 .map_err(|_| Error::Upload(UploadError::InvalidResponse))?;
351 let upload_response: UploadResponse = match result {
352 OwnedMessage::Text(ref data) => serde_json::from_str(data)
353 .map_err(|_| Error::Upload(UploadError::InvalidResponse))?,
354 _ => return Err(UploadError::InvalidResponse.into()),
355 };
356
357 let mut header = vec![0u8; ece::HEADER_LEN as usize];
359 reader
360 .read_exact(&mut header)
361 .expect("failed to read header from reader");
362 client
363 .send_message(&OwnedMessage::Binary(header))
364 .map_err(|e| Error::Upload(e.into()))?;
365
366 let result =
368 reader
369 .chunks(ece::RS as usize)
370 .fold(None, |result: Option<UploadError>, chunk| {
371 if result.is_some() {
373 return result;
374 }
375
376 let message = OwnedMessage::Binary(chunk.expect("invalid chunk"));
378 client.send_message(&message).err().map(|e| e.into())
379 });
380 if let Some(err) = result {
381 return Err(err.into());
382 }
383
384 client
386 .send_message(&OwnedMessage::Binary(vec![0]))
387 .map_err(|e| Error::Upload(e.into()))?;
388
389 let status = match client
391 .recv_message()
392 .map_err(|_| Error::Upload(UploadError::InvalidResponse))?
393 {
394 OwnedMessage::Text(status) => Some(status),
395 _ => None,
396 };
397 let ok = status
398 .and_then(|s| serde_json::from_str::<UploadStatusResponse>(&s).ok())
399 .map(|s| s.is_ok())
400 .unwrap_or(false);
401 if !ok {
402 return Err(UploadError::Response(ResponseError::Undefined).into());
403 }
404
405 let _ = client.shutdown();
407 mem::drop(client);
408
409 let remote_file = upload_response.into_file(
411 self.host.clone(),
412 &key,
413 self.params.as_ref().and_then(|p| {
414 p.expiry_time
415 .map(|s| Utc::now() + Duration::seconds(s as i64))
416 }),
417 )?;
418
419 Ok((remote_file, None))
420 }
421}
422
423#[derive(Debug, Deserialize)]
432struct UploadResponse {
433 id: String,
435
436 url: String,
439
440 #[serde(alias = "ownerToken", alias = "owner")]
444 owner_token: String,
445}
446
447impl UploadResponse {
448 pub fn into_file(
452 self,
453 host: Url,
454 key: &KeySet,
455 expiry_time: Option<DateTime<Utc>>,
456 ) -> Result<RemoteFile, UploadError> {
457 Ok(RemoteFile::new(
458 self.id,
459 Some(Utc::now()),
460 expiry_time,
461 host,
462 Url::parse(&self.url)?,
463 key.secret().to_vec(),
464 Some(self.owner_token),
465 ))
466 }
467}
468
469#[derive(Debug, Deserialize)]
472#[cfg(feature = "send3")]
473struct UploadStatusResponse {
474 ok: bool,
476}
477
478#[cfg(feature = "send3")]
479impl UploadStatusResponse {
480 pub fn is_ok(&self) -> bool {
482 self.ok
483 }
484}
485
486struct FileData<'a> {
489 name: &'a str,
491
492 mime: Mime,
494
495 #[allow(unused)]
497 size: u64,
498}
499
500impl<'a> FileData<'a> {
501 pub fn from(path: &'a PathBuf) -> Result<Self, FileError> {
503 if !path.is_file() {
505 return Err(FileError::NotAFile);
506 }
507
508 let name = match path.file_name() {
510 Some(name) => name.to_str().unwrap_or("file"),
511 None => "file",
512 };
513
514 let size = path.metadata()?.len();
516
517 Ok(Self {
518 name,
519 mime: mime_guess::from_path(path).first_or_octet_stream(),
520 size,
521 })
522 }
523
524 pub fn name(&self) -> &str {
526 self.name
527 }
528
529 pub fn mime(&self) -> &Mime {
531 &self.mime
532 }
533
534 #[cfg(feature = "send3")]
536 pub fn size(&self) -> u64 {
537 self.size
538 }
539}
540
541struct Reader {
543 inner: Box<dyn ReadLen>,
545}
546
547impl Reader {
548 pub fn new(inner: Box<dyn ReadLen>) -> Self {
550 Self { inner }
551 }
552}
553
554impl Read for Reader {
555 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
556 self.inner.read(buf)
557 }
558}
559
560impl PipeLen for Reader {
561 fn len_in(&self) -> usize {
562 self.inner.len_in()
563 }
564
565 fn len_out(&self) -> usize {
566 self.inner.len_out()
567 }
568}
569
570#[derive(Error, Debug)]
571pub enum Error {
572 #[error("failed to prepare uploading the file")]
574 Prepare(#[from] PrepareError),
575
576 #[error("")]
580 File(#[from] FileError),
581
582 #[error("failed to upload the file")]
584 Upload(#[from] UploadError),
585
586 #[error("failed to change file parameters")]
588 Params(#[from] ParamsError),
589
590 #[error("failed to set the password")]
592 Password(#[from] PasswordError),
593}
594
595impl From<MetadataError> for Error {
596 fn from(err: MetadataError) -> Error {
597 Error::Prepare(PrepareError::Meta(err))
598 }
599}
600
601impl From<ReaderError> for Error {
602 fn from(err: ReaderError) -> Error {
603 Error::Prepare(PrepareError::Reader(err))
604 }
605}
606
607#[derive(Error, Debug)]
608pub enum PrepareError {
609 #[error("failed to prepare file metadata")]
611 Meta(#[from] MetadataError),
612
613 #[error("failed to access the file to upload")]
616 Reader(#[from] ReaderError),
617
618 #[error("failed to create uploader client")]
620 Client,
621}
622
623#[derive(Error, Debug)]
624pub enum ReaderError {
625 #[error("failed to create file encryptor")]
627 Encrypt,
628
629 #[error("failed to create progress reader")]
632 Progress,
633}
634
635#[derive(Error, Debug)]
636pub enum FileError {
637 #[error("the given path is not an existing file")]
639 NotAFile,
640
641 #[error("failed to open the file to upload")]
643 Open(#[from] IoError),
644}
645
646#[derive(Error, Debug)]
647pub enum UploadError {
648 #[error("failed to update upload progress")]
651 Progress,
652
653 #[error("failed to request file upload")]
655 Request,
656
657 #[error("failed to stream file for upload over websocket")]
660 #[cfg(feature = "send3")]
661 UploadStream(#[from] WebSocketError),
662
663 #[error("got invalid response from server")]
666 InvalidResponse,
667
668 #[error("bad response from server for uploading")]
670 Response(#[from] ResponseError),
671
672 #[error("failed to decode upload response")]
675 Decode(#[from] ReqwestError),
676
677 #[error("failed to parse received URL")]
679 ParseUrl(#[from] UrlParseError),
680}