implement auto reauth
This commit is contained in:
		| @@ -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), | ||||
|   | ||||
| @@ -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<Jar>, | ||||
|     base_url: Url, | ||||
|     getter_url: Url, | ||||
|     setter_url: Url, | ||||
|     auto_reauth: bool | ||||
| } | ||||
|  | ||||
| impl ConnectBox { | ||||
|     pub fn new(address: impl Display) -> Result<Self> { | ||||
|     pub fn new(address: impl Display, code: String) -> Result<Self> { | ||||
|         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<T: DeserializeOwned>(&self, function: u32) -> Result<T> { | ||||
|         let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?; | ||||
|         let form: Vec<Field> = 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<Field> = 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<impl IntoIterator<Item = Field<'_, '_>>>, | ||||
|         fields: Option<impl AsRef<[Field<'_, '_>]>>, | ||||
|     ) -> Result<String> { | ||||
|         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<str>, Cow<str>)> = 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<str>) -> 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<models::LanUserTable> { | ||||
|         self.xml_getter(functions::LAN_TABLE).await | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user