2023-03-29 03:51:56 +00:00
|
|
|
use crate::error::{Error, ErrorKind};
|
2023-01-06 14:09:58 +00:00
|
|
|
use crate::globals::GLOBALS;
|
2024-04-22 21:01:34 +00:00
|
|
|
use crate::misc::Private;
|
2023-12-03 22:02:44 +00:00
|
|
|
use crate::people::{Person, PersonList};
|
2023-07-24 21:46:56 +00:00
|
|
|
use nostr_types::{Metadata, Nip05, PublicKey, RelayUrl, Unixtime};
|
2023-02-14 21:34:11 +00:00
|
|
|
use std::sync::atomic::Ordering;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
// This updates the people map and the database with the result
|
2023-07-24 21:37:27 +00:00
|
|
|
pub async fn validate_nip05(person: Person) -> Result<(), Error> {
|
2023-08-17 21:55:51 +00:00
|
|
|
if !GLOBALS.storage.read_setting_check_nip05() {
|
2023-01-23 01:05:20 +00:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2023-01-06 14:09:58 +00:00
|
|
|
let now = Unixtime::now().unwrap();
|
|
|
|
|
|
|
|
// invalid if their nip-05 is not set
|
2024-05-20 20:34:14 +00:00
|
|
|
if person.metadata().is_none()
|
|
|
|
|| matches!(person.metadata(), Some(Metadata { nip05: None, .. }))
|
|
|
|
{
|
2023-01-06 14:09:58 +00:00
|
|
|
GLOBALS
|
|
|
|
.people
|
2023-01-19 21:29:02 +00:00
|
|
|
.upsert_nip05_validity(&person.pubkey, None, false, now.0 as u64)
|
2023-01-06 14:09:58 +00:00
|
|
|
.await?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2024-05-20 20:34:14 +00:00
|
|
|
let metadata = person.metadata().as_ref().unwrap().to_owned();
|
2023-01-19 21:29:02 +00:00
|
|
|
let nip05 = metadata.nip05.as_ref().unwrap().to_owned();
|
|
|
|
|
2023-01-06 14:09:58 +00:00
|
|
|
// Split their DNS ID
|
2023-01-19 21:29:02 +00:00
|
|
|
let (user, domain) = match parse_nip05(&nip05) {
|
2023-01-06 14:09:58 +00:00
|
|
|
Ok(pair) => pair,
|
|
|
|
Err(_) => {
|
|
|
|
GLOBALS
|
|
|
|
.people
|
2023-01-19 21:29:02 +00:00
|
|
|
.upsert_nip05_validity(&person.pubkey, Some(nip05), false, now.0 as u64)
|
2023-01-06 14:09:58 +00:00
|
|
|
.await?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Fetch NIP-05
|
2023-01-19 21:29:02 +00:00
|
|
|
let nip05file = match fetch_nip05(&user, &domain).await {
|
2023-01-11 05:16:58 +00:00
|
|
|
Ok(content) => content,
|
|
|
|
Err(e) => {
|
2024-04-11 19:03:29 +00:00
|
|
|
tracing::warn!("NIP-05 fetch issue with {}@{}", user, domain);
|
2023-01-11 05:16:58 +00:00
|
|
|
return Err(e);
|
|
|
|
}
|
|
|
|
};
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
// Check if the response matches their public key
|
2023-01-22 01:12:04 +00:00
|
|
|
let mut valid = false;
|
2023-01-19 21:29:02 +00:00
|
|
|
match nip05file.names.get(&user) {
|
2023-01-06 14:09:58 +00:00
|
|
|
Some(pk) => {
|
2023-08-02 03:21:43 +00:00
|
|
|
if let Ok(pubkey) = PublicKey::try_from_hex_string(pk, true) {
|
2023-07-24 21:46:56 +00:00
|
|
|
if pubkey == person.pubkey {
|
|
|
|
// Validated
|
|
|
|
GLOBALS
|
|
|
|
.people
|
|
|
|
.upsert_nip05_validity(
|
|
|
|
&person.pubkey,
|
|
|
|
Some(nip05.clone()),
|
|
|
|
true,
|
|
|
|
now.0 as u64,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
valid = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Failed
|
2023-01-06 14:09:58 +00:00
|
|
|
GLOBALS
|
|
|
|
.people
|
2023-07-24 21:46:56 +00:00
|
|
|
.upsert_nip05_validity(&person.pubkey, Some(nip05.clone()), false, now.0 as u64)
|
2023-01-06 14:09:58 +00:00
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
// Failed
|
|
|
|
GLOBALS
|
|
|
|
.people
|
2023-01-22 01:12:04 +00:00
|
|
|
.upsert_nip05_validity(&person.pubkey, Some(nip05.clone()), false, now.0 as u64)
|
2023-01-06 14:09:58 +00:00
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-18 22:22:26 +00:00
|
|
|
// UI cache invalidation (so notes of the person get rerendered)
|
2023-07-24 21:46:56 +00:00
|
|
|
GLOBALS.ui_people_to_invalidate.write().push(person.pubkey);
|
2023-06-18 22:22:26 +00:00
|
|
|
|
2023-01-22 01:12:04 +00:00
|
|
|
if valid {
|
2023-12-21 22:23:10 +00:00
|
|
|
update_relays(&nip05, nip05file, &person.pubkey).await?;
|
2023-01-22 01:12:04 +00:00
|
|
|
}
|
|
|
|
|
2023-01-06 14:09:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-12-03 22:02:44 +00:00
|
|
|
pub async fn get_and_follow_nip05(
|
|
|
|
nip05: String,
|
|
|
|
list: PersonList,
|
2024-04-22 21:01:34 +00:00
|
|
|
private: Private,
|
2023-12-03 22:02:44 +00:00
|
|
|
) -> Result<(), Error> {
|
2023-01-06 14:09:58 +00:00
|
|
|
// Split their DNS ID
|
2023-01-19 21:29:02 +00:00
|
|
|
let (user, domain) = parse_nip05(&nip05)?;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
// Fetch NIP-05
|
2023-01-19 21:29:02 +00:00
|
|
|
let nip05file = fetch_nip05(&user, &domain).await?;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
// Get their pubkey
|
2023-01-19 21:29:02 +00:00
|
|
|
let pubkey = match nip05file.names.get(&user) {
|
2023-08-02 03:21:43 +00:00
|
|
|
Some(pk) => PublicKey::try_from_hex_string(pk, true)?,
|
2023-03-29 03:51:56 +00:00
|
|
|
None => return Err((ErrorKind::Nip05KeyNotFound, file!(), line!()).into()),
|
2023-01-06 14:09:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Save person
|
|
|
|
GLOBALS
|
|
|
|
.people
|
|
|
|
.upsert_nip05_validity(
|
2023-01-22 01:12:04 +00:00
|
|
|
&pubkey,
|
2023-01-19 21:29:02 +00:00
|
|
|
Some(nip05.clone()),
|
2023-01-06 14:09:58 +00:00
|
|
|
true,
|
|
|
|
Unixtime::now().unwrap().0 as u64,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
2023-12-21 22:23:10 +00:00
|
|
|
update_relays(&nip05, nip05file, &pubkey).await?;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
2023-12-21 22:23:10 +00:00
|
|
|
// Follow
|
2024-04-22 21:01:34 +00:00
|
|
|
GLOBALS.people.follow(&pubkey, true, list, private)?;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
2023-12-21 22:23:10 +00:00
|
|
|
tracing::info!("Followed {}", &nip05);
|
2023-01-22 01:12:04 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-12-21 22:23:10 +00:00
|
|
|
async fn update_relays(nip05: &str, nip05file: Nip05, pubkey: &PublicKey) -> Result<(), Error> {
|
2023-01-06 14:09:58 +00:00
|
|
|
// Set their relays
|
2023-07-24 21:46:56 +00:00
|
|
|
let relays = match nip05file.relays.get(&(*pubkey).into()) {
|
2023-01-06 14:09:58 +00:00
|
|
|
Some(relays) => relays,
|
2023-02-01 19:58:54 +00:00
|
|
|
None => return Ok(()),
|
2023-01-06 14:09:58 +00:00
|
|
|
};
|
|
|
|
for relay in relays.iter() {
|
|
|
|
// Save relay
|
2023-01-30 00:16:25 +00:00
|
|
|
if let Ok(relay_url) = RelayUrl::try_from_unchecked_url(relay) {
|
2023-08-03 23:55:08 +00:00
|
|
|
GLOBALS.storage.write_relay_if_missing(&relay_url, None)?;
|
2023-01-06 14:09:58 +00:00
|
|
|
|
2024-05-17 20:53:20 +00:00
|
|
|
// Update person_relay
|
|
|
|
GLOBALS.storage.modify_person_relay(
|
|
|
|
*pubkey,
|
|
|
|
&relay_url,
|
2024-05-16 23:51:19 +00:00
|
|
|
|pr| {
|
|
|
|
pr.read = true;
|
|
|
|
pr.write = true;
|
|
|
|
},
|
2024-05-17 20:53:20 +00:00
|
|
|
None,
|
|
|
|
)?;
|
2023-01-06 14:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 22:23:10 +00:00
|
|
|
tracing::info!("Setup {} relays for {}", relays.len(), nip05);
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns user and domain
|
2023-09-24 03:45:19 +00:00
|
|
|
pub fn parse_nip05(nip05: &str) -> Result<(String, String), Error> {
|
2023-01-19 21:29:02 +00:00
|
|
|
let mut parts: Vec<&str> = nip05.split('@').collect();
|
2023-01-06 14:09:58 +00:00
|
|
|
|
|
|
|
// Add the underscore as a username if they just specified a domain name.
|
|
|
|
if parts.len() == 1 {
|
|
|
|
parts = Vec::from(["_", parts.first().unwrap()])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Require two parts
|
|
|
|
if parts.len() != 2 {
|
2023-03-29 03:51:56 +00:00
|
|
|
Err((ErrorKind::InvalidDnsId, file!(), line!()).into())
|
2023-01-06 14:09:58 +00:00
|
|
|
} else {
|
|
|
|
let domain = parts.pop().unwrap();
|
|
|
|
let user = parts.pop().unwrap();
|
2023-01-13 22:01:13 +00:00
|
|
|
if domain.len() < 4 {
|
|
|
|
// smallest non-TLD domain is like 't.co'
|
2023-03-29 03:51:56 +00:00
|
|
|
return Err((ErrorKind::InvalidDnsId, file!(), line!()).into());
|
2023-01-13 22:01:13 +00:00
|
|
|
}
|
2023-01-06 14:09:58 +00:00
|
|
|
Ok((user.to_string(), domain.to_string()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn fetch_nip05(user: &str, domain: &str) -> Result<Nip05, Error> {
|
2023-03-04 04:23:26 +00:00
|
|
|
// FIXME add user-agent if configured
|
|
|
|
|
2023-01-07 08:05:53 +00:00
|
|
|
let nip05_future = reqwest::Client::builder()
|
|
|
|
.timeout(std::time::Duration::new(60, 0))
|
|
|
|
.redirect(reqwest::redirect::Policy::none()) // see NIP-05
|
2023-01-25 18:43:33 +00:00
|
|
|
.gzip(true)
|
|
|
|
.brotli(true)
|
|
|
|
.deflate(true)
|
2023-01-07 08:05:53 +00:00
|
|
|
.build()?
|
2023-01-06 14:09:58 +00:00
|
|
|
.get(format!(
|
|
|
|
"https://{}/.well-known/nostr.json?name={}",
|
|
|
|
domain, user
|
|
|
|
))
|
|
|
|
.send();
|
2023-01-07 08:05:53 +00:00
|
|
|
let response = nip05_future.await?;
|
2023-02-14 21:34:11 +00:00
|
|
|
let bytes = response.bytes().await?;
|
|
|
|
GLOBALS.bytes_read.fetch_add(bytes.len(), Ordering::Relaxed);
|
|
|
|
Ok(serde_json::from_slice(&bytes)?)
|
2023-01-06 14:09:58 +00:00
|
|
|
}
|