Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add locations message #10

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,728 changes: 964 additions & 764 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 19 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ debug-assertions = false
codegen-units = 16

[dependencies]
ws = {version = "0.7.6", features = ["ssl"]}
log = "0.4.1"
simple_logger = "0.5.0"
url = "1.7.0"
json = "0.11.13"
ring = "0.12.1"
base64 = "0.9.2"
qrcode = "0.7"
image = "0.19"
untrusted = "0.5.1"
rust-crypto = "^0.2"
serde = "1.0.64"
serde_derive = "1.0.64"
bincode = "1.0.0"
byteorder = "1.2.1"
protobuf = "2.0.4"
ws = {version = "0.9.1", features = ["ssl"]}
log = "0.4"
simple_logger = "1.6"
url = "2.1"
json = "0.12"
ring = "0.13"
base64 = "0.13"
qrcode = "0.12"
image = "0.23"
untrusted = "0.6"
rust-crypto = "0.2"
serde = "1.0"
serde_derive = "1.0"
bincode = "1.3"
byteorder = "1.3"
protobuf = "2.18"
chrono = "0.4"
reqwest = { version = "0.8.6", optional = true}
error-chain = "0.12.0"
reqwest = { version = "0.10", optional = true}
error-chain = "0.12"

[features]
default-features = ["media"]
"media" = ["reqwest"]

[build-dependencies]
protobuf-codegen-pure = "2.0.4"
protobuf-codegen-pure = "2.18"
15 changes: 6 additions & 9 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
extern crate protobuf_codegen_pure;

fn main() {

protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
out_dir: "src",
input: &["proto/message_wire.proto"],
includes: &["proto"],
customize: protobuf_codegen_pure::Customize {
..Default::default()
}
}).expect("protoc");
protobuf_codegen_pure::Codegen::new()
.out_dir("src")
.inputs(&["proto/message_wire.proto"])
.include("proto")
.run()
.expect("protoc");
}
74 changes: 70 additions & 4 deletions proto/message_wire.proto
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,32 @@ message Chat {

message ProtocolMessage {
optional MessageKey key = 1;
enum TYPE {
enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0;
EPHEMERAL_SETTING = 3;
EPHEMERAL_SYNC_RESPONSE = 4;
HISTORY_SYNC_NOTIFICATION = 5;
}
optional TYPE type = 2;
optional PROTOCOL_MESSAGE_TYPE type = 2;
optional uint32 ephemeralExpiration = 4;
optional int64 ephemeralSettingTimestamp = 5;
optional HistorySyncNotification historySyncNotification = 6;
}

message HistorySyncNotification {
optional bytes fileSha256 = 1;
optional uint64 fileLength = 2;
optional bytes mediaKey = 3;
optional bytes fileEncSha256 = 4;
optional string directPath = 5;
enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE {
INITIAL_BOOTSTRAP = 0;
INITIAL_STATUS_V3 = 1;
FULL = 2;
RECENT = 3;
}
optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6;
optional uint32 chunkOrder = 7;
}

message ContactsArrayMessage {
Expand Down Expand Up @@ -306,15 +328,15 @@ message WebMessageInfo {
required MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
enum STATUS {
enum WEB_MESSAGE_INFO_STATUS {
ERROR = 0;
PENDING = 1;
SERVER_ACK = 2;
DELIVERY_ACK = 3;
READ = 4;
PLAYED = 5;
}
optional STATUS status = 4 [default=PENDING];
optional WEB_MESSAGE_INFO_STATUS status = 4;
optional string participant = 5;
optional bool ignore = 16;
optional bool starred = 17;
Expand Down Expand Up @@ -369,6 +391,32 @@ message WebMessageInfo {
CALL_MISSED_VIDEO = 41;
INDIVIDUAL_CHANGE_NUMBER = 42;
GROUP_DELETE = 43;
GROUP_ANNOUNCE_MODE_MESSAGE_BOUNCE = 44;
CALL_MISSED_GROUP_VOICE = 45;
CALL_MISSED_GROUP_VIDEO = 46;
PAYMENT_CIPHERTEXT = 47;
PAYMENT_FUTUREPROOF = 48;
PAYMENT_TRANSACTION_STATUS_UPDATE_FAILED = 49;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUNDED = 50;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUND_FAILED = 51;
PAYMENT_TRANSACTION_STATUS_RECEIVER_PENDING_SETUP = 52;
PAYMENT_TRANSACTION_STATUS_RECEIVER_SUCCESS_AFTER_HICCUP = 53;
PAYMENT_ACTION_ACCOUNT_SETUP_REMINDER = 54;
PAYMENT_ACTION_SEND_PAYMENT_REMINDER = 55;
PAYMENT_ACTION_SEND_PAYMENT_INVITATION = 56;
PAYMENT_ACTION_REQUEST_DECLINED = 57;
PAYMENT_ACTION_REQUEST_EXPIRED = 58;
PAYMENT_ACTION_REQUEST_CANCELLED = 59;
BIZ_VERIFIED_TRANSITION_TOP_TO_BOTTOM = 60;
BIZ_VERIFIED_TRANSITION_BOTTOM_TO_TOP = 61;
BIZ_INTRO_TOP = 62;
BIZ_INTRO_BOTTOM = 63;
BIZ_NAME_CHANGE = 64;
BIZ_MOVE_TO_CONSUMER_APP = 65;
BIZ_TWO_TIER_MIGRATION_TOP = 66;
BIZ_TWO_TIER_MIGRATION_BOTTOM = 67;
OVERSIZED = 68;
GROUP_CHANGE_NO_FREQUENTLY_FORWARDED = 69;
}
optional STUBTYPE messageStubType = 24;
optional bool clearMedia = 25;
Expand Down Expand Up @@ -415,3 +463,21 @@ message WebFeatures {
optional FLAG voipIndividualIncoming = 9;
optional FLAG quickRepliesQuery = 10;
}

message PaymentInfoStatus {
enum PAYMENT_INFO_STATUS {
UNKNOWN_STATUS = 0;
PROCESSING = 1;
SENT = 2;
NEED_TO_ACCEPT = 3;
COMPLETE = 4;
COULD_NOT_COMPLETE = 5;
REFUNDED = 6;
EXPIRED = 7;
REJECTED = 8;
CANCELLED = 9;
WAITING_FOR_PAYER = 10;
WAITING = 11;
}
optional PAYMENT_INFO_STATUS paymentInfoStatus = 0;
}
54 changes: 29 additions & 25 deletions src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub trait WhatsappWebHandler<H = Self> where H: WhatsappWebHandler<H> + Send + S
}

enum SessionState {
PendingNew { private_key: Option<agreement::EphemeralPrivateKey>, public_key: Vec<u8>, client_id: [u8; 8], qr_callback: Box<Fn(QrCode) + Send> },
PendingNew { private_key: Option<agreement::EphemeralPrivateKey>, public_key: Vec<u8>, client_id: [u8; 8], qr_callback: Box<dyn Fn(QrCode) + Send> },
PendingPersistent { persistent_session: PersistentSession },
Established { persistent_session: PersistentSession },
Teardown
Expand All @@ -108,7 +108,7 @@ enum WebsocketResponse {

struct WhatsappWebConnectionInner<H: WhatsappWebHandler<H> + Send + Sync + 'static> {
pub user_jid: Option<Jid>,
requests: HashMap<String, Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>>,
requests: HashMap<String, Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>>,
messages_tag_counter: u32,
session_state: SessionState,
websocket_state: WebsocketState,
Expand All @@ -117,7 +117,7 @@ struct WhatsappWebConnectionInner<H: WhatsappWebHandler<H> + Send + Sync + 'stat

impl<H: WhatsappWebHandler<H> + Send + Sync + 'static> WhatsappWebConnectionInner<H> {

fn send_json_message(&mut self, message: JsonValue, cb: Box<Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) {
fn send_json_message(&mut self, message: JsonValue, cb: Box<dyn Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) {
debug!("sending json {:?}", &message);
let tag = self.alloc_message_tag();
self.ws_send_message(WebsocketMessage {
Expand All @@ -140,19 +140,19 @@ impl<H: WhatsappWebHandler<H> + Send + Sync + 'static> WhatsappWebConnectionInne
}


fn send_app_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, app_message: AppMessage, cb: Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
fn send_app_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, app_message: AppMessage, cb: Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
self.epoch += 1;
let epoch = self.epoch;
self.send_node_message(tag, metric, app_message.serialize(epoch), cb);
}

#[inline]
fn send_node_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, node: Node, cb: Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
fn send_node_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, node: Node, cb: Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
debug!("sending node {:?}", &node);
self.send_binary_message(tag, metric, &node.serialize(), cb);
}

fn ws_send_message(&mut self, message: WebsocketMessage, callback: Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
fn ws_send_message(&mut self, message: WebsocketMessage, callback: Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
if let WebsocketState::Connected(ref sender, _) = self.websocket_state {
sender.send(message.serialize()).unwrap();
self.requests.insert(message.tag.into(), callback);
Expand All @@ -165,7 +165,7 @@ impl<H: WhatsappWebHandler<H> + Send + Sync + 'static> WhatsappWebConnectionInne
tag.to_string()
}

fn send_binary_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, message: &[u8], cb: Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
fn send_binary_message(&mut self, tag: Option<String>, metric: WebsocketMessageMetric, message: &[u8], cb: Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
let encrypted_message = if let SessionState::Established { ref persistent_session } = self.session_state {
crypto::sign_and_encrypt_message(&persistent_session.enc, &persistent_session.mac, &message)
} else {
Expand Down Expand Up @@ -260,21 +260,26 @@ impl<H: WhatsappWebHandler<H> + Send + Sync + 'static> WhatsappWebConnectionInne
}

fn ws_on_connected(&mut self, out: Sender) {
info!("ws_on_connected");

let timeout_manager = timeout::TimeoutManager::new(&out, timeout::PING_TIMEOUT, timeout::TimeoutState::Normal);

self.websocket_state = match self.websocket_state {
WebsocketState::Disconnected => WebsocketState::Connected(out, timeout_manager),
WebsocketState::Connected(_, _) => return
};
let message: (JsonValue, Box<Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) = match self.session_state {
let message: (JsonValue, Box<dyn Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) = match self.session_state {
SessionState::PendingNew { ref client_id, .. } => {
let mut init_command = json_protocol::build_init_request(base64::encode(&client_id).as_str());
let init_command = json_protocol::build_init_request(base64::encode(&client_id).as_str());

info!("{}", init_command);

(init_command, Box::new(move |response, connection| {
info!("{}", response);
if let Ok(reference) = json_protocol::parse_init_response(&response) {
match connection.inner.lock().unwrap().session_state {
SessionState::PendingNew { ref public_key, ref client_id, ref qr_callback, .. } => {
debug!("QRCode: {}", reference);
info!("QRCode: {}", reference);

qr_callback(QrCode::new(
format!("{},{},{}", reference, base64::encode(&public_key), base64::encode(&client_id))
Expand All @@ -285,23 +290,23 @@ impl<H: WhatsappWebHandler<H> + Send + Sync + 'static> WhatsappWebConnectionInne
}
}
} else {
error!("error");
error!("error ws_on_connected");
}
}))
}
SessionState::PendingPersistent { ref persistent_session } => {
let mut init_command = json_protocol::build_init_request(base64::encode(&persistent_session.client_id).as_str());
let init_command = json_protocol::build_init_request(base64::encode(&persistent_session.client_id).as_str());

(init_command, Box::new(move |response, connection| {
if let Err(err) = json_protocol::parse_response_status(&response) {
error!("error {:?}", err);
} else {
let mut inner = connection.inner.lock().unwrap();
let message: (JsonValue, Box<Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) = match inner.session_state {
let message: (JsonValue, Box<dyn Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) = match inner.session_state {
SessionState::PendingPersistent { ref persistent_session } => {
let mut login_command = json_protocol::build_takeover_request(persistent_session.client_token.as_str(),
let login_command = json_protocol::build_takeover_request(persistent_session.client_token.as_str(),
persistent_session.server_token.as_str(),
&base64::encode(&persistent_session.client_id));
&base64::encode(&persistent_session.client_id).as_str());
(login_command, Box::new(move |response, connection| {
if let Err(err) = json_protocol::parse_response_status(&response) {
error!("error {:?}", err);
Expand Down Expand Up @@ -363,11 +368,11 @@ impl<H: WhatsappWebHandler<H> + Send + Sync> WhatsappWebConnection<H> {
}
}

fn send_json_message(&self, message: JsonValue, cb: Box<Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) {
fn send_json_message(&self, message: JsonValue, cb: Box<dyn Fn(JsonValue, &WhatsappWebConnection<H>) + Send>) {
self.inner.lock().unwrap().send_json_message(message, cb);
}

fn send_app_message(&self, tag: Option<String>, metric: WebsocketMessageMetric, app_message: AppMessage, cb: Box<Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
fn send_app_message(&self, tag: Option<String>, metric: WebsocketMessageMetric, app_message: AppMessage, cb: Box<dyn Fn(WebsocketResponse, &WhatsappWebConnection<H>) + Send>) {
self.inner.lock().unwrap().send_app_message(tag, metric, app_message, cb)
}

Expand Down Expand Up @@ -397,10 +402,9 @@ impl<H: WhatsappWebHandler<H> + Send + Sync> WhatsappWebConnection<H> {
}
let message = WebsocketMessage::deserialize(message).unwrap();


match message.payload {
WebsocketMessagePayload::Json(payload) => {
debug!("received json: {:?}", &payload);
debug!("received json: {}", &payload);

if let Some(cb) = inner.requests.remove(message.tag.deref()) {
drop(inner);
Expand Down Expand Up @@ -580,7 +584,7 @@ impl<H: WhatsappWebHandler<H> + Send + Sync> WhatsappWebConnection<H> {
self.inner.lock().unwrap().send_group_command(GroupCommand::ParticipantsChange(jid, participants_change), participants);
}

pub fn get_messages_before(&self, jid: Jid, id: String, count: u16, callback: Box<Fn(Option<Vec<WhatsappMessage>>) + Send + Sync>) {
pub fn get_messages_before(&self, jid: Jid, id: String, count: u16, callback: Box<dyn Fn(Option<Vec<WhatsappMessage>>) + Send + Sync>) {
let msg = AppMessage::Query(Query::MessagesBefore { jid, id, count });
self.send_app_message(None, WebsocketMessageMetric::QueryMessages, msg, Box::new(move |response, _| {
match response {
Expand All @@ -592,25 +596,25 @@ impl<H: WhatsappWebHandler<H> + Send + Sync> WhatsappWebConnection<H> {
}));
}

pub fn request_file_upload(&self, hash: &[u8], media_type: MediaType, callback: Box<Fn(Result<&str>) + Send + Sync>) {
pub fn request_file_upload(&self, hash: &[u8], media_type: MediaType, callback: Box<dyn Fn(Result<&str>) + Send + Sync>) {
self.send_json_message(json_protocol::build_file_upload_request(hash, media_type), Box::new(move |response, _| {
callback(json_protocol::parse_file_upload_response(&response));
}));
}

pub fn get_profile_picture(&self, jid: &Jid, callback: Box<Fn(Option<&str>) + Send + Sync>) {
pub fn get_profile_picture(&self, jid: &Jid, callback: Box<dyn Fn(Option<&str>) + Send + Sync>) {
self.send_json_message(json_protocol::build_profile_picture_request(jid), Box::new(move |response, _| {
callback(json_protocol::parse_profile_picture_response(&response));
}));
}

pub fn get_profile_status(&self, jid: &Jid, callback: Box<Fn(Option<&str>) + Send + Sync>) {
pub fn get_profile_status(&self, jid: &Jid, callback: Box<dyn Fn(Option<&str>) + Send + Sync>) {
self.send_json_message(json_protocol::build_profile_status_request(jid), Box::new(move |response, _| {
callback(json_protocol::parse_profile_status_response(&response));
}));
}

pub fn get_group_metadata(&self, jid: &Jid, callback: Box<Fn(Option<GroupMetadata>) + Send + Sync>) {
pub fn get_group_metadata(&self, jid: &Jid, callback: Box<dyn Fn(Option<GroupMetadata>) + Send + Sync>) {
debug_assert!(jid.is_group);
self.send_json_message(json_protocol::build_group_metadata_request(jid), Box::new(move |response, _| {
callback(json_protocol::parse_group_metadata_response(&response).ok());
Expand Down Expand Up @@ -709,7 +713,7 @@ pub struct PersistentSession {
pub mac: [u8; 32]
}

const ENDPOINT_URL: &str = "wss://w7.web.whatsapp.com/ws";
const ENDPOINT_URL: &str = "wss://web.whatsapp.com/ws";

/// Create new connection and session.
/// Will eventual call ```qr_cb``` with the generated qr-code.
Expand Down
Loading