implement auto reauth

This commit is contained in:
lemonsh 2023-04-30 19:43:34 +02:00
parent 6ad2534983
commit 7838ca1978
3 changed files with 63 additions and 33 deletions

View File

@ -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(())
}

View File

@ -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),

View File

@ -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 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?.text().await?;
let obj = quick_xml::de::from_str(&resp)?;
Ok(obj)
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 mut reauthed = false;
loop {
let session_token = self.cookie("sessionToken")?.ok_or(Error::NoSessionToken)?;
let mut form = vec![
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 {
form.extend(fields);
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?;
Ok(resp.text().await?)
if resp.status().is_redirection() {
if self.auto_reauth && !reauthed {
reauthed = true;
continue;
}
return Err(Error::NotAuthorized)
}
return 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
}