You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

116 lines
4.2 KiB

//! Decrypting/downloading WA media.
use whatsappweb::message::{MessageId, Peer, FileInfo};
use whatsappweb::{MediaType, crypto, Jid};
use std::thread;
use crate::comm::WhatsappCommand;
use futures::sync::mpsc::UnboundedSender;
use humansize::{FileSize, file_size_opts};
use reqwest;
use std::io::prelude::*;
use std::fs::File;
use crate::util::Result;
use uuid::Uuid;
use std::sync::Arc;
use reqwest::header::USER_AGENT;
use reqwest::StatusCode;
use mime_guess::get_mime_extensions_str;
use chrono::NaiveDateTime;
pub fn store_contact(path: &str, dl_path: &str, vcard: String) -> Result<String> {
let uu = Uuid::new_v4().to_simple().to_string();
let path = format!("{}/{}.vcf", path, uu);
let dl_path = format!("{}/{}.vcf", dl_path, uu);
debug!("Creating file {}", path);
let mut file = File::create(&path)?;
file.write_all(&vcard.as_bytes())?;
Ok(dl_path)
}
pub struct MediaInfo {
pub ty: MediaType,
pub fi: FileInfo,
pub mi: MessageId,
pub peer: Option<Peer>,
pub from: Jid,
pub group: Option<i32>,
pub path: String,
pub dl_path: String,
pub tx: Arc<UnboundedSender<WhatsappCommand>>,
pub name: Option<String>,
pub ts: NaiveDateTime,
}
pub struct MediaResult {
pub from: Jid,
pub group: Option<i32>,
pub mi: MessageId,
pub peer: Option<Peer>,
pub ts: NaiveDateTime,
pub result: Result<String>
}
impl MediaInfo {
fn run(&mut self) -> Result<String> {
let uu = Uuid::new_v4().to_simple().to_string();
let mime_ext = get_mime_extensions_str(&self.fi.mime)
.unwrap_or(&[])
.get(0)
.unwrap_or(&"bin");
let path = format!("{}/{}.{}", self.path, uu, mime_ext);
let dl_path = format!("{}/{}.{}", self.dl_path, uu, mime_ext);
debug!("Creating file {}", path);
let mut file = File::create(&path)?;
debug!("Downloading {}", self.fi.url);
let mut data = vec![];
let client = reqwest::Client::new();
let mut resp = client.get(&self.fi.url)
.header(USER_AGENT, "sms-irc")
.send()?;
debug!("response: {:?}", resp);
if resp.status() != StatusCode::OK {
Err(format_err!("Status code {} when downloading", resp.status().as_u16()))?
}
resp.copy_to(&mut data)?;
debug!("Checking encrypted SHA256");
let sha = crypto::sha256(&data);
if sha != self.fi.enc_sha256 {
Err(format_err!("Encrypted SHA256 mismatch"))?
}
debug!("Decrypting");
let dec = crypto::decrypt_media_message(&self.fi.key, self.ty, &data)
.map_err(|e| format_err!("decryption error: {}", e))?;
debug!("Checking SHA256");
if sha != self.fi.sha256 {
// FIXME: for some reason, this SHA256 check always fails.
// The files look fine though...
// Err(format_err!("SHA256 mismatch"))?
}
debug!("Writing to file");
file.write_all(&dec)?;
file.flush()?;
let size = self.fi.size.file_size(file_size_opts::BINARY)
.map_err(|e| format_err!("filesize error: {}", e))?;
let ret = match self.ty {
MediaType::Image => format!("\x01ACTION uploaded an image ({}, {}) < {} >\x01", size, self.fi.mime, dl_path),
MediaType::Audio => format!("\x01ACTION uploaded audio ({}, {}) < {} >\x01", size, self.fi.mime, dl_path),
MediaType::Document => format!("\x01ACTION uploaded a document '{}' ({}, {}) < {} >\x01", self.name.take().unwrap_or("unknown".into()), self.fi.mime, size, dl_path),
MediaType::Video => format!("\x01ACTION uploaded video ({}, {}) < {} >\x01", size, self.fi.mime, dl_path)
};
Ok(ret)
}
pub fn start(mut self) {
debug!("Starting media download/decryption job for {} / mid {:?}", self.from.to_string(), self.mi);
thread::spawn(move || {
let ret = self.run();
let ret = MediaResult {
group: self.group,
mi: self.mi,
from: self.from,
peer: self.peer,
ts: self.ts,
result: ret
};
self.tx.unbounded_send(WhatsappCommand::MediaFinished(ret))
.unwrap();
});
}
}