From 7838ca19786f4384f100049cb41b9dd81ba626b2 Mon Sep 17 00:00:00 2001 From: lemonsh Date: Sun, 30 Apr 2023 19:43:34 +0200 Subject: [PATCH] implement auto reauth --- connectbox/examples/devices.rs | 6 +-- connectbox/src/error.rs | 2 + connectbox/src/lib.rs | 88 ++++++++++++++++++++++------------ 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/connectbox/examples/devices.rs b/connectbox/examples/devices.rs index 490f695..afc722f 100644 --- a/connectbox/examples/devices.rs +++ b/connectbox/examples/devices.rs @@ -11,11 +11,11 @@ async fn main() -> Result<()> { let ip = args.next().expect("no ip specified"); let code = args.next().expect("no code specified"); - let connect_box = ConnectBox::new(ip)?; - connect_box.login(&code).await?; + let connect_box = ConnectBox::new(ip, code)?; + connect_box.login().await?; let devices = connect_box.get_devices().await?; - println!("{devices:?}"); + println!("{devices:#?}"); Ok(()) } diff --git a/connectbox/src/error.rs b/connectbox/src/error.rs index 6d5cff6..ab63fc4 100644 --- a/connectbox/src/error.rs +++ b/connectbox/src/error.rs @@ -9,6 +9,8 @@ pub enum Error { IncorrectCode, #[error("unexpected response from the server: {0:?}")] UnexpectedResponse(String), + #[error("you are not logged in, or perhaps the session has expired")] + NotAuthorized, #[error(transparent)] URLParseError(#[from] url::ParseError), diff --git a/connectbox/src/lib.rs b/connectbox/src/lib.rs index d563d56..0e33787 100644 --- a/connectbox/src/lib.rs +++ b/connectbox/src/lib.rs @@ -21,14 +21,16 @@ type Field<'a, 'b> = (Cow<'a, str>, Cow<'b, str>); /// The entry point of the library - the API client pub struct ConnectBox { http: Client, + code: String, cookie_store: Arc, base_url: Url, getter_url: Url, setter_url: Url, + auto_reauth: bool } impl ConnectBox { - pub fn new(address: impl Display) -> Result { + pub fn new(address: impl Display, code: String) -> Result { let cookie_store = Arc::new(Jar::default()); let http = Client::builder() .user_agent("Mozilla/5.0") @@ -44,6 +46,8 @@ impl ConnectBox { base_url, getter_url, setter_url, + code, + auto_reauth: false }) } @@ -64,46 +68,60 @@ impl ConnectBox { } async fn xml_getter(&self, function: u32) -> Result { - let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?; - let form: Vec = vec![ - ("token".into(), session_token.into()), - ("fun".into(), function.to_string().into()), - ]; - let req = self.http.post(self.getter_url.clone()).form(&form); - let resp = req.send().await?.text().await?; - let obj = quick_xml::de::from_str(&resp)?; - Ok(obj) + let mut reauthed = false; + loop { + let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?; + let form: Vec = vec![ + ("token".into(), session_token.into()), + ("fun".into(), function.to_string().into()), + ]; + let req = self.http.post(self.getter_url.clone()).form(&form); + let resp = req.send().await?; + if resp.status().is_redirection() { + if self.auto_reauth && !reauthed { + reauthed = true; + continue; + } + return Err(Error::NotAuthorized) + } + return Ok(quick_xml::de::from_str(&resp.text().await?)?); + } } async fn xml_setter( &self, function: u32, - fields: Option>>, + fields: Option]>>, ) -> Result { - let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?; - let mut form = vec![ - ("token".into(), session_token.into()), - ("fun".into(), function.to_string().into()), - ]; - if let Some(fields) = fields { - form.extend(fields); + let mut reauthed = false; + loop { + let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?; + let mut form: Vec<(Cow, Cow)> = vec![ + ("token".into(), session_token.into()), + ("fun".into(), function.to_string().into()), + ]; + if let Some(fields) = &fields { + for (key, value) in fields.as_ref() { + form.push((key.clone(), value.clone())); + } + } + let req = self.http.post(self.setter_url.clone()).form(&form); + let resp = req.send().await?; + if resp.status().is_redirection() { + if self.auto_reauth && !reauthed { + reauthed = true; + continue; + } + return Err(Error::NotAuthorized) + } + return Ok(resp.text().await?); } - let req = self.http.post(self.setter_url.clone()).form(&form); - let resp = req.send().await?; - Ok(resp.text().await?) } - pub async fn login(&self, code: impl AsRef) -> Result<()> { - // get the session cookie - self.http - .get(self.base_url.join("common_page/login.html")?) - .send() - .await?; - - // log in + async fn _login(&self) -> Result<()> { let fields = vec![ ("Username".into(), "NULL".into()), - ("Password".into(), code.as_ref().into()), + ("Password".into(), (&self.code).into()), ]; let response = self.xml_setter(functions::LOGIN, Some(fields)).await?; if response == "idloginincorrect" { @@ -118,6 +136,16 @@ impl ConnectBox { Ok(()) } + pub async fn login(&self) -> Result<()> { + // get the session cookie + self.http + .get(self.base_url.join("common_page/login.html")?) + .send() + .await?; + + self._login().await + } + pub async fn get_devices(&self) -> Result { self.xml_getter(functions::LAN_TABLE).await }