wx_work/server/
recv_message.rs1use std::str::FromStr;
2
3use xmltree::Element;
4
5use super::crypto::Crypto;
6use super::error::{MessageError, Result};
7
8#[derive(Debug, Clone)]
9pub struct RecvMessage {
10 pub to_user_name: String,
11 pub from_user_name: String,
12 pub agent_id: u64,
13 pub create_time: u64,
14 pub msg_id: u64,
15 pub msg_ty: RecvMessageType,
16}
17
18#[derive(Debug, Clone)]
20#[non_exhaustive]
21pub enum RecvMessageType {
22 Text(String),
23 Picture(Picture),
24 Voice(Voice),
25 Video(RecvVideo),
26 Location(Location),
27 Link(Link),
28}
29
30#[derive(Debug, Clone)]
31pub struct Picture {
32 pub pic_url: String,
33 pub media_id: String,
34}
35
36#[derive(Debug, Clone)]
37pub struct Voice {
38 pub media_id: String,
39 pub format: String,
40}
41
42#[derive(Debug, Clone)]
43pub struct RecvVideo {
44 pub media_id: String,
45 pub thumb_media_id: String,
46}
47
48#[derive(Debug, Clone)]
49pub struct Location {
50 pub location_x: f64,
51 pub location_y: f64,
52 pub scale: u32,
53 pub label: String,
54 pub ty: Option<String>,
55}
56
57#[derive(Debug, Clone)]
58pub struct Link {
59 pub title: String,
60 pub description: String,
61 pub url: String,
62 pub pic_url: String,
63}
64
65macro_rules! try_field {
66 ($name:expr, $element:expr) => {
67 match fetch($name, &$element) {
68 Some(d) => d.to_string(),
69 None => return Err(MessageError::MissingField($name)),
70 }
71 };
72}
73
74macro_rules! try_field_parse {
75 ($name:expr, $element:expr, $ty:ident) => {
76 match fetch($name, &$element) {
77 Some(d) => match $ty::from_str(d) {
78 Ok(d) => d,
79 Err(_) => {
80 return Err(MessageError::InvalidFieldType(format!(
81 "{} parse failed",
82 $name
83 )))
84 }
85 },
86 None => return Err(MessageError::MissingField($name)),
87 }
88 };
89}
90
91fn fetch<'a>(name: &str, element: &'a Element) -> Option<&'a str> {
92 let child = element.get_child(name)?;
93 child.children.get(0)?.as_text()
94}
95
96impl RecvMessage {
97 pub(crate) fn parse(
98 data: impl AsRef<[u8]>,
99 crypto: &Crypto,
100 timestamp: u64,
101 nonce: u64,
102 msg_signature: &str,
103 ) -> Result<RecvMessage> {
104 let xml = Element::parse(data.as_ref())
105 .map_err(|e| MessageError::ParseFailed(format!("{}", e)))?;
106
107 let to_user_name = try_field!("ToUserName", xml);
108 let agent_id = try_field_parse!("AgentID", xml, u64);
109 let msg_encrypt = try_field!("Encrypt", xml);
110
111 let sign = crypto.sign(msg_encrypt.clone(), timestamp, nonce);
112
113 if sign != msg_signature {
114 return Err(MessageError::InvalidSignature);
115 }
116
117 let payload = crypto
118 .decrypt(&msg_encrypt)
119 .map_err(|e| MessageError::DecryptFailed(format!("{}", e)))?;
120 let msg = payload.data;
121 let receiver_id = payload.receiver_id;
122 if &*receiver_id != to_user_name.as_bytes() {
123 return Err(MessageError::InvalidReceiver);
124 }
125
126 let inner_xml = Element::parse(&*msg)
127 .map_err(|e| MessageError::ParseFailed(format!("inner: {}", e)))?;
128
129 let from_user_name = try_field!("FromUserName", inner_xml);
130 let create_time = try_field_parse!("CreateTime", inner_xml, u64);
131 let msg_id = try_field_parse!("MsgId", inner_xml, u64);
132
133 let msg_ty = match &*try_field!("MsgType", inner_xml) {
134 "text" => {
135 let content = try_field!("Content", inner_xml);
136 RecvMessageType::Text(content)
137 }
138 "image" => {
139 let pic_url = try_field!("PicUrl", inner_xml);
140 let media_id = try_field!("MediaId", inner_xml);
141 let pic = Picture { pic_url, media_id };
142 RecvMessageType::Picture(pic)
143 }
144 "voice" => {
145 let format = try_field!("Format", inner_xml);
146 let media_id = try_field!("MediaId", inner_xml);
147 let voice = Voice { format, media_id };
148 RecvMessageType::Voice(voice)
149 }
150 "video" => {
151 let media_id = try_field!("MediaId", inner_xml);
152 let thumb_media_id = try_field!("ThumbMediaId", inner_xml);
153 let video = RecvVideo {
154 media_id,
155 thumb_media_id,
156 };
157 RecvMessageType::Video(video)
158 }
159 ty => return Err(MessageError::InvalidMessageType(ty.to_string())), };
161
162 Ok(RecvMessage {
163 to_user_name,
164 agent_id,
165 from_user_name,
166 create_time,
167 msg_id,
168 msg_ty,
169 })
170 }
171}