diff --git a/backend/src/db/mod.rs b/backend/src/db/mod.rs index cc948e8c8..219fa2520 100644 --- a/backend/src/db/mod.rs +++ b/backend/src/db/mod.rs @@ -1,4 +1,5 @@ pub mod model; +pub mod package; pub mod util; use std::borrow::Cow; diff --git a/backend/src/db/model.rs b/backend/src/db/model.rs index bba5a4df1..1a64b31e2 100644 --- a/backend/src/db/model.rs +++ b/backend/src/db/model.rs @@ -223,6 +223,15 @@ impl PackageDataEntry { Self::Updating { installed, .. } | Self::Installed { installed, .. } => Some(installed), } } + pub fn manifest(self) -> Manifest { + match self { + PackageDataEntry::Installing { manifest, .. } => manifest, + PackageDataEntry::Updating { manifest, .. } => manifest, + PackageDataEntry::Restoring { manifest, .. } => manifest, + PackageDataEntry::Removing { manifest, .. } => manifest, + PackageDataEntry::Installed { manifest, .. } => manifest, + } + } } impl PackageDataEntryModel { pub fn installed(self) -> OptionModel { diff --git a/backend/src/db/package.rs b/backend/src/db/package.rs new file mode 100644 index 000000000..89ce8a543 --- /dev/null +++ b/backend/src/db/package.rs @@ -0,0 +1,25 @@ +use patch_db::DbHandle; + +use crate::s9pk::manifest::{Manifest, PackageId}; +use crate::Error; + +pub async fn get_packages(db: &mut Db) -> Result, Error> { + let packages = crate::db::DatabaseModel::new() + .package_data() + .get(db, false) + .await?; + Ok(packages.0.keys().cloned().collect()) +} + +pub async fn get_manifest( + db: &mut Db, + pkg: &PackageId, +) -> Result, Error> { + let mpde = crate::db::DatabaseModel::new() + .package_data() + .idx_model(pkg) + .get(db, false) + .await? + .into_owned(); + Ok(mpde.map(|pde| pde.manifest())) +} diff --git a/backend/src/error.rs b/backend/src/error.rs index 6410333c3..a675ab278 100644 --- a/backend/src/error.rs +++ b/backend/src/error.rs @@ -63,6 +63,7 @@ pub enum ErrorKind { Incoherent = 55, InvalidBackupTargetId = 56, ProductKeyMismatch = 57, + LanPortConflict = 58, } impl ErrorKind { pub fn as_str(&self) -> &'static str { @@ -125,6 +126,7 @@ impl ErrorKind { Incoherent => "Incoherent", InvalidBackupTargetId => "Invalid Backup Target ID", ProductKeyMismatch => "Incompatible Product Keys", + LanPortConflict => "Incompatible LAN port configuration", } } } diff --git a/backend/src/install/mod.rs b/backend/src/install/mod.rs index 77104fa4e..0f753de8c 100644 --- a/backend/src/install/mod.rs +++ b/backend/src/install/mod.rs @@ -9,8 +9,8 @@ use std::time::{Duration, Instant}; use color_eyre::eyre::eyre; use emver::VersionRange; -use futures::future::BoxFuture; -use futures::{FutureExt, StreamExt, TryStreamExt}; +use futures::future::{self, BoxFuture}; +use futures::{stream, FutureExt, StreamExt, TryStreamExt}; use http::header::CONTENT_LENGTH; use http::{Request, Response, StatusCode}; use hyper::Body; @@ -43,7 +43,7 @@ use crate::s9pk::manifest::{Manifest, PackageId}; use crate::s9pk::reader::S9pkReader; use crate::status::{MainStatus, Status}; use crate::util::io::{copy_and_shutdown, response_to_reader}; -use crate::util::serde::{display_serializable, IoFormat}; +use crate::util::serde::{display_serializable, IoFormat, Port}; use crate::util::{display_none, AsyncFileExt, Version}; use crate::version::{Current, VersionT}; use crate::volume::asset_dir; @@ -124,6 +124,7 @@ pub async fn install( .json() .await .with_kind(crate::ErrorKind::Registry)?; + let s9pk = s9pk .error_for_status() .with_kind(crate::ErrorKind::Registry)?; @@ -651,6 +652,51 @@ pub async fn download_install_s9pk( let version = &temp_manifest.version; if let Err(e) = async { + let mut db_handle = ctx.db.handle(); + let mut tx = db_handle.begin().await?; + // Build set of existing manifests + let mut manifests = Vec::new(); + for pkg in crate::db::package::get_packages(&mut tx).await? { + match crate::db::package::get_manifest(&mut tx, &pkg).await? { + Some(m) => { + manifests.push(m); + } + None => {} + } + } + // Build map of current port -> ssl mappings + let port_map = ssl_port_status(&manifests); + tracing::info!("SSL Port Map: {:?}", &port_map); + + // if any of the requested interface lan configs conflict with current state, fail the install + for (_id, iface) in &temp_manifest.interfaces.0 { + if let Some(cfg) = &iface.lan_config { + for (p, lan) in cfg { + if p.0 == 80 && lan.ssl || p.0 == 443 && !lan.ssl { + return Err(Error::new( + eyre!("SSL Conflict with EmbassyOS"), + ErrorKind::LanPortConflict, + )); + } + match port_map.get(&p) { + Some((ssl, pkg)) => { + if *ssl != lan.ssl { + return Err(Error::new( + eyre!("SSL Conflict with package: {}", pkg), + ErrorKind::LanPortConflict, + )); + } + } + None => { + continue; + } + } + } + } + } + tx.save().await?; + drop(db_handle); + let pkg_archive_dir = ctx .datadir .join(PKG_ARCHIVE_DIR) @@ -1345,3 +1391,20 @@ pub fn load_images<'a, P: AsRef + 'a + Send + Sync>( } .boxed() } + +fn ssl_port_status(manifests: &Vec) -> BTreeMap { + let mut ret = BTreeMap::new(); + for m in manifests { + for (_id, iface) in &m.interfaces.0 { + match &iface.lan_config { + None => {} + Some(cfg) => { + for (p, lan) in cfg { + ret.insert(p.clone(), (lan.ssl, m.id.clone())); + } + } + } + } + } + ret +}