use crate::{
errors::NetworkError,
mion::{
cgis::{do_simple_request, encode_url_parameters},
proto::cgis::MionCGIErrors,
},
};
use bytes::{BufMut, Bytes, BytesMut};
use reqwest::{Client, Method};
use std::{fmt::Display, net::Ipv4Addr, ops::Deref, time::Duration};
use tracing::debug;
const EEPROM_MAX_ADDRESS: usize = 0x1E00;
const TABLE_START_SIGIL: &str = "<table border=0 cellspacing=3 cellpadding=3>";
const TABLE_END_SIGIL: &str = "</table>";
pub async fn dump_eeprom(mion_ip: Ipv4Addr) -> Result<Bytes, NetworkError> {
dump_eeprom_with_raw_client(&Client::default(), mion_ip).await
}
pub async fn dump_eeprom_with_raw_client(
client: &Client,
mion_ip: Ipv4Addr,
) -> Result<Bytes, NetworkError> {
let mut memory_buffer = BytesMut::with_capacity(0x2000);
while memory_buffer.len() <= EEPROM_MAX_ADDRESS {
debug!(
bridge.ip = %mion_ip,
address = %format!("{:04X}", memory_buffer.len()),
"Dumping eeprom memory area",
);
let body_as_string = do_raw_eeprom_request(
client,
mion_ip,
&[("start_addr", format!("{:04X}", memory_buffer.len()))],
)
.await?;
let table = extract_memory_table_body(&body_as_string)?;
for table_row in table.split("<tr>").skip(3) {
for table_column in table_row
.trim()
.trim_end_matches("</tbody>")
.trim_end()
.trim_end_matches("</tr>")
.trim_end()
.replace("</td>", "")
.split("<td>")
.skip(3)
{
if table_column.trim().len() != 2 {
return Err(MionCGIErrors::HtmlResponseBadByte(table_column.to_owned()).into());
}
memory_buffer
.put_u8(u8::from_str_radix(table_column.trim(), 16).map_err(|_| {
MionCGIErrors::HtmlResponseBadByte(table_column.to_owned())
})?);
}
}
}
Ok(memory_buffer.freeze())
}
fn extract_memory_table_body(body: &str) -> Result<String, MionCGIErrors> {
let start = body
.find(TABLE_START_SIGIL)
.ok_or_else(|| MionCGIErrors::HtmlResponseMissingMemoryDumpSigil(body.to_owned()))?;
let body_minus_start = &body[start + TABLE_START_SIGIL.len()..];
let end = body_minus_start
.find(TABLE_END_SIGIL)
.ok_or_else(|| MionCGIErrors::HtmlResponseMissingMemoryDumpSigil(body.to_owned()))?;
Ok(body_minus_start[..end].to_owned())
}
pub async fn do_raw_eeprom_request(
client: &Client,
mion_ip: Ipv4Addr,
url_parameters: &[(impl Deref<Target = str>, impl Display)],
) -> Result<String, NetworkError> {
do_simple_request::<String>(
client,
Method::POST,
format!("http://{mion_ip}/dbg/eeprom_dump.cgi"),
Some(encode_url_parameters(url_parameters)),
Some(Duration::from_mins(1)),
)
.await
}