mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 06:26:16 +01:00
Use storage provider for anisette and provide fs-storage implementation
This commit is contained in:
@@ -9,7 +9,6 @@ use isideload::{
|
|||||||
teams::DeveloperTeam,
|
teams::DeveloperTeam,
|
||||||
},
|
},
|
||||||
sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior},
|
sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior},
|
||||||
util::keyring_storage::KeyringStorage,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
@@ -120,9 +119,6 @@ async fn main() {
|
|||||||
let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string())
|
let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string())
|
||||||
.team_selection(TeamSelection::Prompt(team_selection_prompt))
|
.team_selection(TeamSelection::Prompt(team_selection_prompt))
|
||||||
.max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt))
|
.max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt))
|
||||||
.storage(Box::new(KeyringStorage::new(
|
|
||||||
"isideload-minimal".to_string(),
|
|
||||||
)))
|
|
||||||
.machine_name("isideload-minimal".to_string())
|
.machine_name("isideload-minimal".to_string())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ readme = "../README.md"
|
|||||||
default = ["install", "keyring-storage"]
|
default = ["install", "keyring-storage"]
|
||||||
install = ["dep:idevice"]
|
install = ["dep:idevice"]
|
||||||
keyring-storage = ["keyring"]
|
keyring-storage = ["keyring"]
|
||||||
|
fs-storage = []
|
||||||
|
|
||||||
# Unfortunately, dependencies are kinda a mess rn, since this requires a beta version of the srp crate.
|
# Unfortunately, dependencies are kinda a mess rn, since this requires a beta version of the srp crate.
|
||||||
# Once that becomes stable, hopefuly duplicate dependencies should clean up.\
|
# Once that becomes stable, hopefuly duplicate dependencies should clean up.\
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
@@ -11,13 +9,14 @@ use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
|||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
use crate::SideloadError;
|
use crate::SideloadError;
|
||||||
use crate::anisette::remote_v3::state::AnisetteState;
|
use crate::anisette::remote_v3::state::AnisetteState;
|
||||||
use crate::anisette::{AnisetteClientInfo, AnisetteData, AnisetteProvider};
|
use crate::anisette::{AnisetteClientInfo, AnisetteData, AnisetteProvider};
|
||||||
use crate::auth::grandslam::GrandSlam;
|
use crate::auth::grandslam::GrandSlam;
|
||||||
use crate::util::plist::PlistDataExtract;
|
use crate::util::plist::PlistDataExtract;
|
||||||
|
use crate::util::storage::{SideloadingStorage, new_storage};
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
|
||||||
pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app";
|
pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app";
|
||||||
@@ -25,7 +24,7 @@ pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app";
|
|||||||
pub struct RemoteV3AnisetteProvider {
|
pub struct RemoteV3AnisetteProvider {
|
||||||
pub state: Option<AnisetteState>,
|
pub state: Option<AnisetteState>,
|
||||||
url: String,
|
url: String,
|
||||||
config_path: PathBuf,
|
storage: Box<dyn SideloadingStorage>,
|
||||||
serial_number: String,
|
serial_number: String,
|
||||||
client_info: Option<AnisetteClientInfo>,
|
client_info: Option<AnisetteClientInfo>,
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
@@ -36,14 +35,14 @@ impl RemoteV3AnisetteProvider {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// - `url`: The URL of the remote anisette service
|
/// - `url`: The URL of the remote anisette service
|
||||||
/// - `config_path`: The path to the config file
|
/// - `storage`: The storage backend for anisette data
|
||||||
/// - `serial_number`: The serial number of the device
|
/// - `serial_number`: The serial number of the device
|
||||||
///
|
///
|
||||||
pub fn new(url: &str, config_path: PathBuf, serial_number: String) -> Self {
|
pub fn new(url: &str, storage: Box<dyn SideloadingStorage>, serial_number: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: None,
|
state: None,
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
config_path,
|
storage,
|
||||||
serial_number,
|
serial_number,
|
||||||
client_info: None,
|
client_info: None,
|
||||||
client: reqwest::ClientBuilder::new()
|
client: reqwest::ClientBuilder::new()
|
||||||
@@ -58,8 +57,8 @@ impl RemoteV3AnisetteProvider {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config_path(mut self, config_path: PathBuf) -> RemoteV3AnisetteProvider {
|
pub fn set_storage(mut self, storage: Box<dyn SideloadingStorage>) -> RemoteV3AnisetteProvider {
|
||||||
self.config_path = config_path;
|
self.storage = storage;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +70,11 @@ impl RemoteV3AnisetteProvider {
|
|||||||
|
|
||||||
impl Default for RemoteV3AnisetteProvider {
|
impl Default for RemoteV3AnisetteProvider {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(DEFAULT_ANISETTE_V3_URL, PathBuf::new(), "0".to_string())
|
Self::new(
|
||||||
|
DEFAULT_ANISETTE_V3_URL,
|
||||||
|
Box::new(new_storage()),
|
||||||
|
"0".to_string(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +169,15 @@ impl AnisetteProvider for RemoteV3AnisetteProvider {
|
|||||||
|
|
||||||
impl RemoteV3AnisetteProvider {
|
impl RemoteV3AnisetteProvider {
|
||||||
async fn get_state(&mut self, gs: Arc<GrandSlam>) -> Result<&mut AnisetteState, Report> {
|
async fn get_state(&mut self, gs: Arc<GrandSlam>) -> Result<&mut AnisetteState, Report> {
|
||||||
let state_path = self.config_path.join("state.plist");
|
|
||||||
fs::create_dir_all(&self.config_path)?;
|
|
||||||
if self.state.is_none() {
|
if self.state.is_none() {
|
||||||
if let Ok(state) = plist::from_file(&state_path) {
|
if let Ok(Some(state)) = &self.storage.retrieve_data("anisette_state") {
|
||||||
info!("Loaded existing anisette state from {:?}", state_path);
|
if let Ok(state) = plist::from_bytes(state) {
|
||||||
|
info!("Loaded existing anisette state");
|
||||||
self.state = Some(state);
|
self.state = Some(state);
|
||||||
|
} else {
|
||||||
|
warn!("Failed to parse existing anisette state, starting fresh");
|
||||||
|
self.state = Some(AnisetteState::new());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
info!("No existing anisette state found");
|
info!("No existing anisette state found");
|
||||||
self.state = Some(AnisetteState::new());
|
self.state = Some(AnisetteState::new());
|
||||||
@@ -185,7 +191,11 @@ impl RemoteV3AnisetteProvider {
|
|||||||
.await
|
.await
|
||||||
.context("Failed to provision")?;
|
.context("Failed to provision")?;
|
||||||
}
|
}
|
||||||
plist::to_file_xml(&state_path, &state)?;
|
let buf = Vec::new();
|
||||||
|
let mut writer = std::io::BufWriter::new(buf);
|
||||||
|
plist::to_writer_xml(&mut writer, &state).unwrap();
|
||||||
|
self.storage
|
||||||
|
.store_data("anisette_state", &writer.into_inner()?)?;
|
||||||
|
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|||||||
53
isideload/src/util/fs_storage.rs
Normal file
53
isideload/src/util/fs_storage.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use rootcause::prelude::*;
|
||||||
|
|
||||||
|
use crate::util::storage::SideloadingStorage;
|
||||||
|
|
||||||
|
pub struct FsStorage {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FsStorage {
|
||||||
|
pub fn new(path: PathBuf) -> Self {
|
||||||
|
FsStorage { path }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FsStorage {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(PathBuf::from("."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SideloadingStorage for FsStorage {
|
||||||
|
fn store_data(&self, key: &str, data: &[u8]) -> Result<(), Report> {
|
||||||
|
let path = self.path.join(key);
|
||||||
|
let parent = path.parent().unwrap_or(Path::new("."));
|
||||||
|
std::fs::create_dir_all(parent).context("Failed to create storage directory")?;
|
||||||
|
std::fs::write(&path, data).context("Failed to write data to file")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_data(&self, key: &str) -> Result<Option<Vec<u8>>, Report> {
|
||||||
|
let path = self.path.join(key);
|
||||||
|
match std::fs::read(&path) {
|
||||||
|
Ok(data) => Ok(Some(data)),
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(e) => Err(report!(e).context("Failed to read data from file").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(&self, key: &str, value: &str) -> Result<(), Report> {
|
||||||
|
self.store_data(key, value.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve(&self, key: &str) -> Result<Option<String>, Report> {
|
||||||
|
match self.retrieve_data(key) {
|
||||||
|
Ok(Some(data)) => Ok(Some(String::from_utf8_lossy(&data).into_owned())),
|
||||||
|
Ok(None) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
pub mod device;
|
pub mod device;
|
||||||
|
#[cfg(feature = "fs-storage")]
|
||||||
|
pub mod fs_storage;
|
||||||
#[cfg(feature = "keyring-storage")]
|
#[cfg(feature = "keyring-storage")]
|
||||||
pub mod keyring_storage;
|
pub mod keyring_storage;
|
||||||
pub mod plist;
|
pub mod plist;
|
||||||
|
|||||||
@@ -27,11 +27,18 @@ pub trait SideloadingStorage: Send + Sync {
|
|||||||
pub fn new_storage() -> impl SideloadingStorage {
|
pub fn new_storage() -> impl SideloadingStorage {
|
||||||
#[cfg(feature = "keyring-storage")]
|
#[cfg(feature = "keyring-storage")]
|
||||||
{
|
{
|
||||||
crate::util::keyring_storage::KeyringStorage::default()
|
return crate::util::keyring_storage::KeyringStorage::default();
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "keyring-storage"))]
|
#[cfg(feature = "fs-storage")]
|
||||||
{
|
{
|
||||||
InMemoryStorage::new()
|
return crate::util::fs_storage::FsStorage::default();
|
||||||
|
}
|
||||||
|
#[cfg(not(any(feature = "keyring-storage", feature = "fs-storage")))]
|
||||||
|
{
|
||||||
|
tracing::warn!(
|
||||||
|
"Keyring storage not enabled, falling back to in-memory storage. This means that the anisette state and certificates will not be saved across runs. Enable the 'keyring-storage' or 'fs-storage' feature for persistance."
|
||||||
|
);
|
||||||
|
return InMemoryStorage::new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user