fn update_db()

in rust-query-crlite/src/main.rs [117:196]


fn update_db(
    db_dir: &Path,
    attachment_url: &str,
    base_url: &str,
    channel: &CRLiteFilterChannel,
) -> Result<(), CRLiteDBError> {
    info!(
        "Fetching cert-revocations records from remote settings {}",
        base_url
    );
    let cert_rev_records: CertRevCollection =
        reqwest::blocking::get(base_url.to_owned() + "cert-revocations/records")
            .map_err(|_| CRLiteDBError::from("could not fetch remote settings collection"))?
            .json()
            .map_err(|_| CRLiteDBError::from("could not read remote settings data"))?;

    let filters: Vec<&CertRevRecord> = cert_rev_records
        .data
        .iter()
        .filter(|x| x.channel.unwrap_or_default() == *channel)
        .collect();

    if filters.iter().filter(|x| !x.incremental).count() != 1 {
        return Err(CRLiteDBError::from(
            "number of full filters found in remote settings is not 1",
        ));
    }

    let expected_filenames: HashSet<OsString> = filters
        .iter()
        .map(|x| x.attachment.filename.clone().into())
        .collect();

    // Remove any filter or delta files that are not listed in the collection
    for dir_entry in std::fs::read_dir(db_dir)? {
        let Ok(dir_entry) = dir_entry else { continue };
        let dir_entry_path = dir_entry.path();
        let extension = dir_entry_path
            .extension()
            .and_then(|os_str| os_str.to_str());
        if (extension == Some("delta") || extension == Some("filter"))
            && !expected_filenames.contains(&dir_entry.file_name())
        {
            info!("Removing {:?}", dir_entry.file_name());
            let _ = std::fs::remove_file(dir_entry_path);
        }
    }

    for filter in filters {
        let expected_digest = hex::decode(&filter.attachment.hash)
            .map_err(|_| CRLiteDBError::from("filter digest corrupted"))?;
        let path = db_dir.join(filter.attachment.filename.clone());
        if path.exists() {
            let digest = Sha256::digest(std::fs::read(&path)?);
            if expected_digest == digest.as_slice() {
                info!("Found existing copy of {}", filter.attachment.filename);
                continue;
            }
        }

        let filter_url = format!("{}{}", attachment_url, filter.attachment.location);
        info!(
            "Fetching {} from {}",
            filter.attachment.filename, filter_url
        );
        let filter_bytes = &reqwest::blocking::get(filter_url)
            .map_err(|_| CRLiteDBError::from("could not fetch filter"))?
            .bytes()
            .map_err(|_| CRLiteDBError::from("could not read filter"))?;

        let digest = Sha256::digest(filter_bytes);
        if expected_digest != digest.as_slice() {
            return Err(CRLiteDBError::from("filter digest mismatch"));
        }

        std::fs::write(&path, filter_bytes)?;
    }

    Ok(())
}