diff --git a/src/profile/tofu.rs b/src/profile/tofu.rs index 4d5cb9e4..ac7c1803 100644 --- a/src/profile/tofu.rs +++ b/src/profile/tofu.rs @@ -4,7 +4,10 @@ mod database; use anyhow::Result; use certificate::Certificate; use database::Database; -use gtk::{gio::TlsCertificate, glib::Uri}; +use gtk::{ + gio::TlsCertificate, + glib::{GString, Uri}, +}; use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; use sqlite::Transaction; @@ -15,7 +18,7 @@ use std::{cell::RefCell, collections::HashMap}; /// https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation pub struct Tofu { database: Database, - memory: RefCell>, + memory: RefCell>, } impl Tofu { @@ -31,8 +34,11 @@ impl Tofu { // build in-memory index... let mut m = memory.borrow_mut(); for r in records { - if m.insert(r.address, Certificate::from_db(Some(r.id), &r.pem, r.time)?) - .is_some() + if m.insert( + (r.host.into(), r.port), + Certificate::from_db(Some(r.id), &r.pem, r.time)?, + ) + .is_some() { panic!() // expect unique address } @@ -50,31 +56,34 @@ impl Tofu { default_port: i32, tls_certificate: TlsCertificate, ) -> Result { - match address(uri, default_port) { - Some(k) => Ok(self + match uri.host() { + Some(host) => Ok(self .memory .borrow_mut() - .insert(k, Certificate::from_tls_certificate(tls_certificate)?) + .insert( + (host, port(uri.port(), default_port)), + Certificate::from_tls_certificate(tls_certificate)?, + ) .is_none()), None => Ok(false), } } pub fn server_certificate(&self, uri: &Uri, default_port: i32) -> Option { - address(uri, default_port).and_then(|k| { + uri.host().and_then(|host| { self.memory .borrow() - .get(&k) + .get(&(host, port(uri.port(), default_port))) .map(|c| c.tls_certificate().clone()) }) } /// Save in-memory index to the permanent database (on app close) pub fn save(&self) -> Result<()> { - for (address, certificate) in self.memory.borrow_mut().drain() { + for ((host, port), certificate) in self.memory.borrow_mut().drain() { if certificate.id().is_none() { self.database - .add(address, certificate.time(), &certificate.pem())?; + .add(host.into(), port, certificate.time(), &certificate.pem())?; } } Ok(()) @@ -94,17 +103,10 @@ pub fn migrate(tx: &Transaction) -> Result<()> { Ok(()) } -fn address(uri: &Uri, default_port: i32) -> Option { - uri.host().map(|host| { - let port = uri.port(); - format!( - "{}:{}", - host, - if port.is_positive() { - port - } else { - default_port - } - ) - }) +fn port(port: i32, default_port: i32) -> i32 { + if port.is_positive() { + port + } else { + default_port + } } diff --git a/src/profile/tofu/database.rs b/src/profile/tofu/database.rs index d8205de0..261d9a66 100644 --- a/src/profile/tofu/database.rs +++ b/src/profile/tofu/database.rs @@ -34,10 +34,10 @@ impl Database { /// Create new record in database /// * return last insert ID on success - pub fn add(&self, address: String, time: &DateTime, pem: &str) -> Result { + pub fn add(&self, host: String, port: i32, time: &DateTime, pem: &str) -> Result { let mut connection = self.pool.get()?; let tx = connection.transaction()?; - let id = insert(&tx, self.profile_id, address, time, pem)?; + let id = insert(&tx, self.profile_id, host, port, time, pem)?; tx.commit()?; Ok(id) } @@ -52,11 +52,12 @@ pub fn init(tx: &Transaction) -> Result { `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_id` INTEGER NOT NULL, `time` INTEGER NOT NULL, - `address` VARCHAR(255) NOT NULL, + `port` INTEGER NOT NULL, + `host` VARCHAR(1024) NOT NULL, `pem` TEXT NOT NULL, FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`), - UNIQUE (`address`) + UNIQUE (`host`, `port`) )", [], )?) @@ -65,7 +66,8 @@ pub fn init(tx: &Transaction) -> Result { pub fn insert( tx: &Transaction, profile_id: i64, - address: String, + host: String, + port: i32, time: &DateTime, pem: &str, ) -> Result { @@ -73,28 +75,35 @@ pub fn insert( "INSERT INTO `profile_tofu` ( `profile_id`, `time`, - `address`, + `host`, + `port`, `pem` - ) VALUES (?, ?, ?, ?) ON CONFLICT (`address`) - DO UPDATE SET `time` = `excluded`.`time`, - `pem` = `excluded`.`pem`", - (profile_id, time.to_unix(), address, pem), + ) VALUES (?, ?, ?, ?, ?) ON CONFLICT (`host`, `port`) + DO UPDATE SET `time` = `excluded`.`time`, + `pem` = `excluded`.`pem`", + (profile_id, time.to_unix(), host, port, pem), )?; Ok(tx.last_insert_rowid()) } pub fn select(tx: &Transaction, profile_id: i64) -> Result> { let mut stmt = tx.prepare( - "SELECT `id`, `profile_id`, `address`, `time`, `pem` FROM `profile_tofu` WHERE `profile_id` = ?", + "SELECT `id`, + `profile_id`, + `host`, + `port`, + `time`, + `pem` FROM `profile_tofu` WHERE `profile_id` = ?", )?; let result = stmt.query_map([profile_id], |row| { Ok(Row { id: row.get(0)?, //profile_id: row.get(1)?, - address: row.get(2)?, - time: DateTime::from_unix_local(row.get(3)?).unwrap(), - pem: row.get(4)?, + host: row.get(2)?, + port: row.get(3)?, + time: DateTime::from_unix_local(row.get(4)?).unwrap(), + pem: row.get(5)?, }) })?; diff --git a/src/profile/tofu/database/row.rs b/src/profile/tofu/database/row.rs index fa24b800..91fe12d2 100644 --- a/src/profile/tofu/database/row.rs +++ b/src/profile/tofu/database/row.rs @@ -1,6 +1,7 @@ pub struct Row { - pub address: String, + pub host: String, pub id: i64, pub pem: String, + pub port: i32, pub time: gtk::glib::DateTime, }