From c106ebe3158e411062f622ea74691803e4bb2f11 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Fri, 18 Sep 2026 13:09:46 +0200 Subject: [PATCH] apt: query the Launchpad PPA API through the shared HTTP client --- src/apt/keyring.rs | 48 +++++++++++++++++++++++++++------------------- src/launchpad.rs | 5 +++-- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/apt/keyring.rs b/src/apt/keyring.rs index 935a49d..c6cfa1f 100644 --- a/src/apt/keyring.rs +++ b/src/apt/keyring.rs @@ -198,7 +198,12 @@ fn validate_keyring_dir(dir_uid: u32, mode: u32, euid: u32) -> Result<(), String Ok(()) } -/// Download and import a PPA key using Launchpad API +/// Download and import a PPA key using the Launchpad API +/// +/// The signing key fingerprint is looked up through the shared HTTP client; +/// the key itself is fetched from the keyserver with curl through the +/// context, because the key file must land in the context's filesystem +/// (which may be remote). /// /// # Arguments /// * `ctx` - Optional context to use @@ -229,33 +234,36 @@ pub async fn download_trust_ppa_key( ppa_name ); - // Get PPA information from Launchpad API to get signing key fingerprint - // Use the correct devel API endpoint - let api_url = format!( - "https://api.launchpad.net/1.0/~{}/+archive/ubuntu/{}", - ppa_owner, ppa_name - ); + // Get PPA information from the Launchpad API to get the signing key + // fingerprint. The query is context-independent metadata, so it goes + // through the shared HTTP client (timeouts, retries) rather than + // shelling out to curl. + let api_url = crate::launchpad::archive_url(ppa_owner, ppa_name); log::debug!("Querying Launchpad API: {}", api_url); - let api_response = ctx - .command("curl") - .arg("-s") - .arg("-f") - .arg("-H") - .arg("Accept: application/json") - .arg(&api_url) - .output()?; - - if !api_response.status.success() { + let response = distro_info::http_get_retried(&api_url).await.map_err(|e| { + format!( + "Failed to query Launchpad API for PPA {}/{}: {}", + ppa_owner, ppa_name, e + ) + })?; + if !response.status().is_success() { return Err(format!( - "Failed to query Launchpad API for PPA {}/{}", - ppa_owner, ppa_name + "Failed to query Launchpad API for PPA {}/{}: HTTP {}", + ppa_owner, + ppa_name, + response.status() ) .into()); } // Parse the JSON response to extract the signing key fingerprint - let api_response_str = String::from_utf8_lossy(&api_response.stdout); + let api_response_str = response.text().await.map_err(|e| { + format!( + "Failed to read the Launchpad API response for PPA {}/{}: {}", + ppa_owner, ppa_name, e + ) + })?; let ppa_response: LaunchpadPpaResponse = serde_json::from_str(&api_response_str).map_err(|e| { format!( diff --git a/src/launchpad.rs b/src/launchpad.rs index 07a9646..d5af061 100644 --- a/src/launchpad.rs +++ b/src/launchpad.rs @@ -92,8 +92,9 @@ fn person_url(user: &str) -> String { } /// URL of the Launchpad API resource of a PPA (`~user/+archive/ubuntu/name` -/// covers the default `ppa` archive and named archives alike) -fn archive_url(user: &str, ppa: &str) -> String { +/// covers the default `ppa` archive and named archives alike); shared by the +/// put-side pre-flight checks and the apt keyring's fingerprint lookup +pub(crate) fn archive_url(user: &str, ppa: &str) -> String { format!("{API_BASE}/~{user}/+archive/ubuntu/{ppa}") }