1use anyhow::{Result, anyhow};
2use base64::Engine;
3use serde::Deserialize;
4use wacore::download::MediaType;
5
6use crate::client::Client;
7use crate::http::HttpRequest;
8
9#[derive(Debug, Clone)]
10pub struct UploadResponse {
11 pub url: String,
12 pub direct_path: String,
13 pub media_key: Vec<u8>,
14 pub file_enc_sha256: Vec<u8>,
15 pub file_sha256: Vec<u8>,
16 pub file_length: u64,
17}
18
19#[derive(Deserialize)]
20struct RawUploadResponse {
21 url: String,
22 direct_path: String,
23}
24
25impl Client {
26 pub async fn upload(&self, data: Vec<u8>, media_type: MediaType) -> Result<UploadResponse> {
27 let enc = tokio::task::spawn_blocking({
28 let data = data.clone();
29 move || wacore::upload::encrypt_media(&data, media_type)
30 })
31 .await??;
32
33 let media_conn = self.refresh_media_conn(false).await?;
34 let host = media_conn
35 .hosts
36 .first()
37 .ok_or_else(|| anyhow!("No media hosts"))?;
38
39 let token = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(enc.file_enc_sha256);
40 let mms_type = media_type.mms_type();
41 let scheme = "https";
42 let url = format!(
43 "{}://{}/mms/{}/{}?auth={}&token={}",
44 scheme, host.hostname, mms_type, token, media_conn.auth, token
45 );
46
47 let request = HttpRequest::post(url)
48 .with_header("Content-Type", "application/octet-stream")
49 .with_header("Origin", "https://web.whatsapp.com")
50 .with_body(enc.data_to_upload);
51
52 let response = self.http_client.execute(request).await?;
53
54 if response.status_code >= 400 {
55 let body_str = match response.body_string() {
56 Ok(body) => body,
57 Err(body_err) => {
58 return Err(anyhow!(
59 "Upload failed {} and failed to read response body: {}",
60 response.status_code,
61 body_err
62 ));
63 }
64 };
65 return Err(anyhow!(
66 "Upload failed {} body={}",
67 response.status_code,
68 body_str
69 ));
70 }
71
72 let raw: RawUploadResponse = serde_json::from_slice(&response.body)?;
73
74 let result = UploadResponse {
75 url: raw.url,
76 direct_path: raw.direct_path,
77 media_key: enc.media_key.to_vec(),
78 file_enc_sha256: enc.file_enc_sha256.to_vec(),
79 file_sha256: enc.file_sha256.to_vec(),
80 file_length: data.len() as u64,
81 };
82 Ok(result)
83 }
84}