use goose::prelude::*;
use regex::Regex;
use std::env;
pub fn get_form(html: &str, name: &str) -> Option<String> {
let re = Regex::new(&format!(
r#"<form.*data-drupal-selector="{}".*>(.*?)</form>"#,
name
))
.unwrap();
let line = html.replace("\n", "");
re.captures(&line).map(|value| value[0].to_string())
}
pub fn get_form_value(form_html: &str, name: &str) -> Option<String> {
let re = Regex::new(&format!(r#"name="{}" value=['"](.*?)['"]"#, name)).unwrap();
re.captures(&form_html).map(|value| value[1].to_string())
}
pub struct Login<'a> {
username: Option<&'a str>,
password: Option<&'a str>,
url: Option<&'a str>,
}
impl<'a> Login<'a> {
pub fn new(
username: Option<&'a str>,
password: Option<&'a str>,
url: Option<&'a str>,
) -> Option<Login<'a>> {
Some(Login {
username,
password,
url,
})
}
pub fn username(username: &'a str) -> Option<Login<'a>> {
Login::new(Some(username), None, None)
}
pub fn password(password: &'a str) -> Option<Login<'a>> {
Login::new(None, Some(password), None)
}
pub fn username_password(username: &'a str, password: &'a str) -> Option<Login<'a>> {
Login::new(Some(username), Some(password), None)
}
pub fn url(url: &'a str) -> Option<Login<'a>> {
Login::new(None, None, Some(url))
}
pub fn username_url(username: &'a str, url: &'a str) -> Option<Login<'a>> {
Login::new(Some(username), None, Some(url))
}
pub fn password_url(password: &'a str, url: &'a str) -> Option<Login<'a>> {
Login::new(None, Some(password), Some(url))
}
pub fn username_password_url(
username: &'a str,
password: &'a str,
url: &'a str,
) -> Option<Login<'a>> {
Login::new(Some(username), Some(password), Some(url))
}
}
pub async fn log_in(user: &GooseUser, login: Option<&Login<'_>>) -> Result<String, GooseTaskError> {
let default_password = "username";
let username = env::var("USER").unwrap_or_else(|_| match login {
Some(l) => l.username.unwrap_or(default_password).to_string(),
None => default_password.to_string(),
});
let default_password = "password";
let password = env::var("PASS").unwrap_or_else(|_| match login {
Some(l) => l.password.unwrap_or(default_password).to_string(),
None => default_password.to_string(),
});
let default_login = "/user/login";
let login_url = match login {
Some(l) => l.url.unwrap_or(default_login),
None => default_login,
};
let goose = user.get(login_url).await?;
let mut login_request = goose.request.clone();
let login_page = crate::validate_and_load_static_assets(
user,
goose,
&crate::Validate::text(r#"<form class="user-login-form""#),
)
.await?;
let login_form = match get_form(&login_page, "user-login-form") {
Some(form) => form,
None => {
user.set_failure(
&format!("{}: no user-login-form on page", login_url),
&mut login_request,
None,
Some(&login_page),
)?;
return Ok("".to_string());
}
};
let form_build_id = match get_form_value(&login_form, "form_build_id") {
Some(build_id) => build_id,
None => {
user.set_failure(
&format!("{}: no form_build_id on page", login_url),
&mut login_request,
None,
Some(&login_form),
)?;
return Ok("".to_string());
}
};
let params = [
("name", &username),
("pass", &password),
("form_build_id", &form_build_id),
("form_id", &"user_login_form".to_string()),
("op", &"Log+in".to_string()),
];
let request_builder = user.goose_post("/user/login").await?;
let mut logged_in_user = user.goose_send(request_builder.form(¶ms), None).await?;
if !logged_in_user.request.redirected {
let headers;
let html = match logged_in_user.response {
Ok(r) => {
headers = Some(r.headers().clone());
r.text().await.unwrap_or_else(|e| e.to_string())
}
Err(e) => {
headers = None;
e.to_string()
}
};
user.set_failure(
&format!(
"{}: login failed (check USER and PASS)",
logged_in_user.request.final_url
),
&mut logged_in_user.request,
headers.as_ref(),
Some(&html),
)?;
return Ok(html);
}
let logged_in_page = crate::validate_and_load_static_assets(
user,
logged_in_user,
&crate::Validate::title(&username),
)
.await?;
Ok(logged_in_page)
}