forked from kevin.dorner/iserv-ipad-helper
Initial commit - intermediate / not fully working state
This commit is contained in:
commit
a6f4357ded
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "modify-device"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
clap = { version = "3.1.6", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1.5"
|
|
@ -0,0 +1,354 @@
|
|||
use std::borrow::Cow;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[clap(short, long)]
|
||||
hostname: String,
|
||||
|
||||
//example: "IServSAT=mvuvFvCZSlpvuwhk0mmNwF1NKEQypM4d; IServSession=xEPqyJO1a5nsVd34HEpbPqQGMMQKapvw; nav-show-additional-modules=true; PHPSESSID=mchbrl11epjnnp851q0i4rt8rc"
|
||||
#[clap(short, long)]
|
||||
session_cookie: String,
|
||||
|
||||
#[clap(long)]
|
||||
target_ip: String,
|
||||
|
||||
#[clap(long)]
|
||||
target_room: u16,
|
||||
|
||||
#[clap(long)]
|
||||
target_compilation: u16,
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Args::parse();
|
||||
loop {
|
||||
let params = [
|
||||
("filter[controllable]", ""),
|
||||
("filter[room][]", "__none"),
|
||||
("filter[group]", ""),
|
||||
("filter[search]", "iPad"),
|
||||
];
|
||||
let header = ("Cookie", args.session_cookie.to_owned());
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
let resp = client
|
||||
.get(format!("https://{}/iserv/admin/hosts", args.hostname))
|
||||
.header(header.0, header.to_owned().1)
|
||||
.query(¶ms)
|
||||
.send()?;
|
||||
|
||||
let status = &resp.status();
|
||||
let plain = &resp.text()?;
|
||||
|
||||
let content = squeeze(
|
||||
&plain,
|
||||
"<script id=\"crud-data\" type=\"application/json\">",
|
||||
"</script>",
|
||||
);
|
||||
|
||||
let json: serde_json::Value = serde_json::from_str(content).expect("Bad Jason!");
|
||||
|
||||
let json_part = &json["data"][0]["name"]["rendered"]
|
||||
.to_string()
|
||||
.replace("\\", "");
|
||||
|
||||
// skip rest of loop when no entry appeared
|
||||
if json_part == "null" {
|
||||
println!("###################################");
|
||||
println!("No new host: skipping (Retry in 5s)");
|
||||
println!("###################################");
|
||||
//sleep for 5 seconds
|
||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||
continue;
|
||||
}
|
||||
|
||||
let host_id = squeeze(json_part, "data-host-id=\"", "\"");
|
||||
let device_name = squeeze(json_part, "</span>", "</a>");
|
||||
println!("-----------------------------------");
|
||||
println!("1/7 Found host");
|
||||
println!("Host id: {}", host_id);
|
||||
println!("Device name: {}", device_name);
|
||||
println!("Received response status: {:?}", status);
|
||||
|
||||
let dest = format!("https://{}/iserv/admin/host/edit/{}", args.hostname, &host_id);
|
||||
let resp = client.get(&dest).header(header.0, header.to_owned().1).send()?;
|
||||
//TODO Check for non 200 response
|
||||
|
||||
let status = &resp.status();
|
||||
let plain = &resp.text()?;
|
||||
|
||||
let mut form_ip: &str = &args.target_ip; //default
|
||||
|
||||
let form_name = squeeze_html_form(plain, "name=\"host[name]\"", "value=\"", "\"");
|
||||
let form_tags = squeeze_html_form(plain, "name=\"host[tags][]\"", "value=\"", "\"");
|
||||
let form_mac = squeeze_html_form(plain, "name=\"host[mac]\"", "value=\"", "\"");
|
||||
let form_description = squeeze_html_form(plain, "name=\"host[description]\"", ">", "<");
|
||||
let form_token = squeeze_html_form(plain, "name=\"host[_token]\"", "value=\"", "\"");
|
||||
|
||||
let mut form_data = [
|
||||
("host[name]", form_name),
|
||||
("host[tags][]", form_tags),
|
||||
("host[room]", &args.target_room.to_string()),
|
||||
("host[ip]", &form_ip),
|
||||
("host[mac]", form_mac),
|
||||
("host[internet]", "1"),
|
||||
("host[proxyEnforce]", "0"),
|
||||
("host[owner]", ""),
|
||||
("host[controllable]", "0"),
|
||||
("host[inventoryNumber]", ""),
|
||||
("host[shutdown]", ""),
|
||||
("host[description]", form_description),
|
||||
("host[image][id]", ""),
|
||||
("host[image][x]", ""),
|
||||
("host[image][y]", ""),
|
||||
("host[actions][submit]", ""),
|
||||
("host[_token]", form_token),
|
||||
];
|
||||
println!("-----------------------------------");
|
||||
println!("2/7 Received current host properties");
|
||||
println!("Received response status: {:?}", status);
|
||||
|
||||
let resp = client
|
||||
.post(&dest)
|
||||
.header(header.0, header.to_owned().1)
|
||||
.form(&form_data)
|
||||
.send()?;
|
||||
|
||||
let status = &resp.status();
|
||||
let plain = &resp.text()?;
|
||||
|
||||
form_ip = squeeze_html_form(plain, "name=\"host[ip]\"", "value=\"", "\"");
|
||||
form_data[3] = ("host[ip]", form_ip);
|
||||
|
||||
println!("-----------------------------------");
|
||||
println!("3/7 Received recommended ip: {}", form_ip);
|
||||
println!("Received response status: {:?}", status);
|
||||
|
||||
let resp = client
|
||||
.post(&dest)
|
||||
.header(header.0, header.to_owned().1)
|
||||
.form(&form_data)
|
||||
.send()?;
|
||||
|
||||
let status = &resp.status();
|
||||
|
||||
println!("-----------------------------------");
|
||||
println!("4/7 Sucessfully updated host");
|
||||
println!("Received response status: {:?}", status);
|
||||
println!("-----------------------------------");
|
||||
|
||||
|
||||
let resp = client
|
||||
.get(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/1", args.hostname))
|
||||
.header(header.0, header.to_owned().1)
|
||||
.send()?;
|
||||
//TODO Check for non 200 response
|
||||
|
||||
let status = &resp.status();
|
||||
let plain = &resp.text()?;
|
||||
|
||||
println!("-----------------------------------");
|
||||
println!("5/7 Received MDM compilation");
|
||||
println!("Received response status: {:?}", status);
|
||||
println!("-----------------------------------");
|
||||
|
||||
|
||||
let mut form_data = Vec::from([
|
||||
(
|
||||
Cow::Borrowed("ioscompilation[name]"),
|
||||
Cow::Borrowed(squeeze_html_form(plain, "name=\"ioscompilation[name]\"", "value=\"", "\"")),
|
||||
),
|
||||
(
|
||||
Cow::Borrowed("ioscompilation[description]"),
|
||||
Cow::Borrowed(squeeze_html_form(plain, "name=\"ioscompilation[description]\"", "value=\"", "\"")),
|
||||
),
|
||||
]);
|
||||
|
||||
// push apps (<option value="52" selected="selected">)
|
||||
let plain_inner_applications = squeeze_html_form(plain, "name=\"ioscompilation[applications][]\"", ">", "</select>");
|
||||
form_data.append(&mut squeeze_loop(plain_inner_applications, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[applications][]"));
|
||||
|
||||
// push profiles
|
||||
let plain_inner_profiles = squeeze_html_form(plain, "name=\"ioscompilation[profiles][]\"", ">", "</select>");
|
||||
form_data.append(&mut squeeze_loop(plain_inner_profiles, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[profiles][]"));
|
||||
|
||||
// push existing devices ([\s\n]* seems not needed)
|
||||
let plain_inner_devices = squeeze_html_form(plain, "name=\"ioscompilation[devices][]\"", ">", "</select>");
|
||||
form_data.append(&mut squeeze_loop(plain_inner_devices, "<option value=\"", "\"", " selected=\"selected\"", 3, "ioscompilation[devices][]"));
|
||||
|
||||
/*
|
||||
let re =
|
||||
regex::Regex::new(r#"<option value="(\d+)" selected="selected">(iPad-\w+)</option>"#)
|
||||
.unwrap();
|
||||
form_data.extend(
|
||||
re.captures_iter(plain)
|
||||
.map(|cap| ("ioscompilation[devices][]".into(), cap[1].to_owned().into())),
|
||||
); */
|
||||
|
||||
// push new device
|
||||
if plain_inner_devices.contains(form_name) {
|
||||
form_data.push(("ioscompilation[devices][]".into(), squeeze_right(plain_inner_devices, "<option value=\"", &format!("\">{}", form_name)).into()));
|
||||
} else {
|
||||
eprintln!("Device not found in compilation edit.")
|
||||
}
|
||||
|
||||
/*
|
||||
let re = regex::Regex::new(&format!(
|
||||
"<option value=\"(\\d+)\">({})</option>",
|
||||
form_name
|
||||
))
|
||||
.unwrap();
|
||||
let mut iter = re
|
||||
.captures_iter(plain)
|
||||
.map(|cap| ("ioscompilation[devices][]".into(), cap[1].to_owned().into()));
|
||||
form_data.push(iter.next().expect("Device not found in compilation edit"));
|
||||
if iter.next().is_none() {
|
||||
eprintln!("Expected single device match, found multiple");
|
||||
eprintln!("Perhaps the item already was added to the compilation");
|
||||
};*/
|
||||
|
||||
// push submit action
|
||||
form_data.push(("ioscompilation[actions][submit]".into(), "".into()));
|
||||
|
||||
//push token
|
||||
let form_token = squeeze_html_form(plain, "name=\"ioscompilation[_token]\"", "value=\"", "\"");
|
||||
form_data.push(("ioscompilation[_token]".into(), form_token.into()));
|
||||
|
||||
println!("{:#?}", form_data);
|
||||
|
||||
//remove me
|
||||
//std::fs::write("plain.out", &plain).unwrap();
|
||||
continue;
|
||||
|
||||
let resp = client
|
||||
.post(format!("https://{}/iserv/admin/mdm/ios/compilation/edit/1", args.hostname))
|
||||
.header(header.0, header.to_owned().1)
|
||||
.form(&form_data)
|
||||
.send()?;
|
||||
|
||||
let status = &resp.status();
|
||||
//let plain = &resp.text()?;
|
||||
println!("-----------------------------------");
|
||||
println!("6/7 Added device to MDM Compilation");
|
||||
println!("Received response status: {:?}", status);
|
||||
println!("-----------------------------------");
|
||||
|
||||
|
||||
let form_data = [
|
||||
("iserv_crud_multi_select[actions][apply-all-changes]", ""),
|
||||
("iserv_crud_multi_select[grouped_actions]", ""),
|
||||
// seems to work with and without token :o (but without it throws a csrf error) // example token: rqB8um_Y6sJ1hCFMeVlg9r3L0v__tH3GS5KcV4Lj_ug
|
||||
("iserv_crud_multi_select[_token]", ""),
|
||||
];
|
||||
|
||||
let resp = client
|
||||
.post(format!("https://{}/iserv/admin/mdm/ios/device/batch/confirm", args.hostname))
|
||||
.header(header.0, header.to_owned().1)
|
||||
.form(&form_data)
|
||||
.send()?;
|
||||
|
||||
let status = &resp.status();
|
||||
//let plain = &resp.text()?;
|
||||
|
||||
//println!("{}", plain);
|
||||
|
||||
|
||||
/*{
|
||||
"iserv_crud_multi_select[actions][apply-all-changes]": "",
|
||||
"iserv_crud_multi_select[grouped_actions]": "",
|
||||
"iserv_crud_multi_select[_token]": "rqB8um_Y6sJ1hCFMeVlg9r3L0v__tH3GS5KcV4Lj_ug"
|
||||
}*/
|
||||
|
||||
println!("-----------------------------------");
|
||||
println!("7/7 Confirmed actions");
|
||||
println!("Received response status: {:?}", status);
|
||||
println!("-----------------------------------");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// helper functions
|
||||
fn squeeze<'input>(input: &'input str, before: &str, after: &str) -> &'input str {
|
||||
//find before occurence (excluded from output)
|
||||
if let Some(before_bytes) = input.find(before) {
|
||||
let adjusted_before_bytes = before_bytes + before.len();
|
||||
let leftover = &input[adjusted_before_bytes..];
|
||||
//find after occurence (excluded from output)
|
||||
if let Some(after_bytes) = leftover.find(after) {
|
||||
let adjusted_after_bytes = after_bytes + adjusted_before_bytes;
|
||||
// ensure bytes are not equal
|
||||
if adjusted_before_bytes < adjusted_after_bytes {
|
||||
return &input[adjusted_before_bytes..adjusted_after_bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fn squeeze_right<'input>(input: &'input str, before: &str, after: &str) -> &'input str {
|
||||
//find before occurence (excluded from output)
|
||||
if let Some(after_bytes) = input.find (after) {
|
||||
// let adjusted_before_bytes = after_bytes + after.len();
|
||||
let leftover = &input[..after_bytes];
|
||||
//find after occurence (excluded from output)
|
||||
if let Some(before_bytes) = leftover.rfind(before) {
|
||||
let adjusted_before_bytes = before_bytes + before.len();
|
||||
// ensure bytes are not equal
|
||||
if adjusted_before_bytes < after_bytes {
|
||||
return &input[adjusted_before_bytes..after_bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fn squeeze_html_form<'input>(
|
||||
input: &'input str,
|
||||
first: &str,
|
||||
second: &str,
|
||||
last: &str,
|
||||
) -> &'input str {
|
||||
//find first occurence (broad search)
|
||||
if let Some(from_first_bytes) = input.find(first) {
|
||||
let after_first_bytes = from_first_bytes + first.len();
|
||||
let after_first = &input[after_first_bytes..];
|
||||
//find second occurence (field search)
|
||||
if let Some(rel_from_second_bytes) = after_first.find(second) {
|
||||
let after_second_bytes = rel_from_second_bytes + after_first_bytes + second.len();
|
||||
let after_second = &input[after_second_bytes..];
|
||||
//find closing last match (excluded from output)
|
||||
if let Some(rel_from_last_byte) = after_second.find(last) {
|
||||
let from_last_byte = rel_from_last_byte + after_second_bytes;
|
||||
return &input[after_second_bytes..from_last_byte];
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fn squeeze_loop<'input, 'vec> (input: &'input str, before: &str, after: &str, must_include_after: &str, include_after_distance: usize, index_name: &'input str) -> Vec<(Cow<'input, str>, Cow<'input, str>)> {
|
||||
let mut collector: Vec<(Cow<str>, Cow<str>)> = Vec::new();
|
||||
let mut leftover = input;
|
||||
//find before occurence (excluded from output)
|
||||
while let Some(before_bytes) = leftover.find(before) {
|
||||
let input = leftover;
|
||||
let adjusted_before_bytes = before_bytes + before.len();
|
||||
leftover = &leftover[adjusted_before_bytes..];
|
||||
//find after occurence (excluded from output)
|
||||
if let Some(after_bytes) = leftover.find(after) {
|
||||
let absolute_after_bytes = after_bytes + adjusted_before_bytes;
|
||||
leftover = &leftover[after_bytes + after.len()..];
|
||||
// ensure bytes are not equal
|
||||
if let Some(must_include_distance) = leftover.find(must_include_after) {
|
||||
if adjusted_before_bytes < absolute_after_bytes && must_include_distance < include_after_distance {
|
||||
collector.push((index_name.into(), input[adjusted_before_bytes..absolute_after_bytes].into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
collector
|
||||
}
|
Loading…
Reference in New Issue