imsg_map/client/
messages.rs1use bytes::Bytes;
4use formats::bmessage::BMessage;
5use futures::SinkExt;
6use obex_core::{
7 client::{ObexClient, ObexError},
8 headers::Header,
9};
10use tokio::io::{AsyncRead, AsyncWrite};
11
12use super::MapClient;
13use crate::{
14 messages::{ListMessagesFilter, MessageEntry},
15 params::{
16 get_message_params, push_message_params, set_message_status_params,
17 INDICATOR_DELETED_STATUS, INDICATOR_READ_STATUS,
18 },
19 MapError, MessageStatus,
20};
21
22impl<T: AsyncRead + AsyncWrite + Unpin> MapClient<T> {
23 pub async fn list_messages(
32 &mut self,
33 filter: &ListMessagesFilter,
34 ) -> Result<Vec<MessageEntry>, MapError> {
35 let req = self.obex.get_request(
36 b"x-bt/MAP-msg-listing\x00",
37 None,
38 Some(filter.to_app_params()?),
39 )?;
40 self.transport.send(req).await?;
41 let body = self.collect_body().await?;
42 Ok(crate::xml::parse_message_listing(&body)?)
43 }
44
45 pub async fn get_message(&mut self, handle: &str) -> Result<BMessage, MapError> {
55 if handle.is_empty() {
56 return Err(MapError::InvalidInput("handle must not be empty"));
57 }
58 if handle.contains(['\r', '\n']) {
59 return Err(MapError::InvalidInput("handle must not contain CR or LF"));
60 }
61 let req =
62 self.obex.get_request(b"x-bt/message\x00", Some(handle), Some(get_message_params()))?;
63 self.transport.send(req).await?;
64 let body = self.collect_body().await?;
65 let text = std::str::from_utf8(&body).map_err(|_| MapError::InvalidEncoding)?;
66 Ok(BMessage::parse(text)?)
67 }
68
69 pub async fn push_message(&mut self, phone: &str, text: &str) -> Result<String, MapError> {
78 if phone.contains(['\r', '\n']) {
79 return Err(MapError::InvalidInput("phone must not contain CR or LF"));
80 }
81 let body = BMessage::outbound_sms(phone, text).encode();
82 let body_bytes = body.as_bytes();
83 let len = u32::try_from(body_bytes.len()).map_err(|_| ObexError::BodyTooLarge)?;
84 let req = self.obex.put_final_request(
85 b"x-bt/message\x00",
86 vec![
87 Header::Name(String::new()),
88 Header::Length(len),
89 Header::AppParams(push_message_params()),
90 Header::EndOfBody(Bytes::copy_from_slice(body_bytes)),
91 ],
92 )?;
93 self.transport.send(req).await?;
94 let rsp_bytes = Self::recv(&mut self.transport).await?;
95 let rsp = ObexClient::parse_response(&rsp_bytes)?;
96 if !rsp.opcode.is_ok() {
97 return Err(MapError::ServerError(rsp.opcode.to_byte()));
98 }
99 rsp.header_name().ok_or(MapError::MissingHandle)
100 }
101
102 pub async fn set_message_status_read(
111 &mut self,
112 handle: &str,
113 status: MessageStatus,
114 ) -> Result<(), MapError> {
115 let value = match status {
116 MessageStatus::Read => 0x01_u8,
117 MessageStatus::Unread => 0x00_u8,
118 };
119 self.do_set_message_status(handle, INDICATOR_READ_STATUS, value).await
120 }
121
122 pub async fn set_message_status_deleted(
131 &mut self,
132 handle: &str,
133 deleted: bool,
134 ) -> Result<(), MapError> {
135 self.do_set_message_status(handle, INDICATOR_DELETED_STATUS, u8::from(deleted)).await
136 }
137
138 async fn do_set_message_status(
139 &mut self,
140 handle: &str,
141 indicator: u8,
142 value: u8,
143 ) -> Result<(), MapError> {
144 if handle.is_empty() {
145 return Err(MapError::InvalidInput("handle must not be empty"));
146 }
147 if handle.contains(['\r', '\n']) {
148 return Err(MapError::InvalidInput("handle must not contain CR or LF"));
149 }
150 let req = self.obex.put_final_request(
151 b"x-bt/messageStatus\x00",
152 vec![
153 Header::Name(handle.to_owned()),
154 Header::AppParams(Bytes::from(set_message_status_params(indicator, value))),
155 ],
156 )?;
157 self.transport.send(req).await?;
158 let rsp_bytes = Self::recv(&mut self.transport).await?;
159 let rsp = ObexClient::parse_response(&rsp_bytes)?;
160 if !rsp.opcode.is_ok() {
161 return Err(MapError::ServerError(rsp.opcode.to_byte()));
162 }
163 Ok(())
164 }
165}