use crate::model::{Lookup, NewLookup};
use crate::schema::lookups;
use chrono::{NaiveDateTime, TimeDelta, Utc};
use diesel::prelude::*;
use rpgpie::key::Certificate;
const STALE: TimeDelta = TimeDelta::days(1);
#[derive(Debug, Clone, Copy)]
enum Sources {
Koo,
Ubuntu,
#[allow(dead_code)]
Sks, }
impl From<Sources> for i32 {
fn from(value: Sources) -> Self {
match value {
Sources::Koo => 1,
Sources::Ubuntu => 2,
Sources::Sks => 3,
}
}
}
const KOO_SEARCH: &str = "https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=";
const UBUNTU_SEARCH: &str = "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x";
pub(crate) fn get_keyservers(
identifiers: &[String],
conn: &mut SqliteConnection,
) -> Vec<Certificate> {
let mut certs = vec![];
for identifier in identifiers {
let mut ubuntu = get_keyserver_ubuntu_single(identifier, conn);
if !ubuntu.is_empty() {
certs.append(&mut ubuntu);
} else {
let mut koo = get_keyserver_koo_single(identifier, conn);
certs.append(&mut koo);
}
}
certs
}
pub(crate) fn get_keyserver_koo_single(
identifier: &str,
conn: &mut SqliteConnection,
) -> Vec<Certificate> {
get_keyserver_single(identifier, KOO_SEARCH, Sources::Koo, conn)
}
pub(crate) fn get_keyserver_ubuntu_single(
identifier: &str,
conn: &mut SqliteConnection,
) -> Vec<Certificate> {
get_keyserver_single(identifier, UBUNTU_SEARCH, Sources::Ubuntu, conn)
}
fn get_keyserver_single(
identifier: &str,
url: &str,
source: Sources,
conn: &mut SqliteConnection,
) -> Vec<Certificate> {
let search = format!("{url}{identifier}");
log::info!("get_keyserver_single {:?}: {search}", source);
if let Some(date) = recent_lookup(source, identifier, conn) {
log::info!(" skipping due to recent lookup at {}", date);
return vec![];
}
if let Ok(response) = reqwest::blocking::get(search) {
touch(source, identifier, conn);
if let Ok(text) = response.text() {
if let Ok(loaded_certs) = Certificate::load(&mut std::io::Cursor::new(text.as_bytes()))
{
return loaded_certs;
}
}
}
vec![]
}
fn touch(source: Sources, identifier: &str, conn: &mut SqliteConnection) {
let insert = NewLookup {
source: source.into(),
identity: identifier,
last_poll: Utc::now().naive_utc(),
};
let inserted_row_count = diesel::insert_into(lookups::table)
.values(&insert)
.on_conflict((lookups::source, lookups::identity))
.do_update()
.set(lookups::last_poll.eq(diesel::dsl::now))
.execute(conn);
if inserted_row_count != Ok(1) {
log::warn!("touch failed");
}
}
fn recent_lookup(
source: Sources,
identifier: &str,
conn: &mut SqliteConnection,
) -> Option<NaiveDateTime> {
if let Ok(lookup) = lookups::table
.filter(
lookups::source
.eq(i32::from(source))
.and(lookups::identity.eq(identifier)),
)
.first::<Lookup>(conn)
{
let now = Utc::now();
let delta = now.signed_duration_since(lookup.last_poll.and_utc());
if delta >= STALE {
None
} else {
Some(lookup.last_poll)
}
} else {
None
}
}