Use Url instead of String

pull/722/head
Felix Ableitner 2020-04-08 14:37:05 +02:00
parent edd0ef5991
commit 6962b9c433
6 changed files with 42 additions and 37 deletions

1
server/Cargo.lock generated vendored
View File

@ -2743,6 +2743,7 @@ dependencies = [
"idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

2
server/Cargo.toml vendored
View File

@ -34,7 +34,7 @@ rss = "1.9.0"
htmlescape = "0.3.1" htmlescape = "0.3.1"
config = "0.10.1" config = "0.10.1"
hjson = "0.8.2" hjson = "0.8.2"
url = "2.1.1" url = { version = "2.1.1", features = ["serde"] }
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
isahc = "0.9" isahc = "0.9"
comrak = "0.7" comrak = "0.7"

View File

@ -22,6 +22,7 @@ use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection; use diesel::PgConnection;
use failure::Error; use failure::Error;
use serde::Deserialize; use serde::Deserialize;
use url::Url;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct CommunityQuery { pub struct CommunityQuery {
@ -87,16 +88,14 @@ impl Community {
impl CommunityForm { impl CommunityForm {
pub fn from_group(group: &GroupExt, conn: &PgConnection) -> Result<Self, Error> { pub fn from_group(group: &GroupExt, conn: &PgConnection) -> Result<Self, Error> {
let followers_uri = &group.extension.get_followers().unwrap().to_string(); let followers_uri = Url::parse(&group.extension.get_followers().unwrap().to_string())?;
let outbox_uri = &group.extension.get_outbox().to_string(); let outbox_uri = Url::parse(&group.extension.get_outbox().to_string())?;
let _outbox = fetch_remote_object::<OrderedCollection>(outbox_uri)?; let _outbox = fetch_remote_object::<OrderedCollection>(&outbox_uri)?;
let _followers = fetch_remote_object::<UnorderedCollection>(followers_uri)?; let _followers = fetch_remote_object::<UnorderedCollection>(&followers_uri)?;
let oprops = &group.base.object_props; let oprops = &group.base.object_props;
let aprops = &group.extension; let aprops = &group.extension;
let creator = fetch_remote_user( let apub_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string(), let creator = fetch_remote_user(&apub_id, conn)?;
conn,
)?;
Ok(CommunityForm { Ok(CommunityForm {
name: oprops.get_name_xsd_string().unwrap().to_string(), name: oprops.get_name_xsd_string().unwrap().to_string(),
title: aprops.get_preferred_username().unwrap().to_string(), title: aprops.get_preferred_username().unwrap().to_string(),

View File

@ -12,6 +12,7 @@ use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection; use diesel::PgConnection;
use failure::Error; use failure::Error;
use serde::Deserialize; use serde::Deserialize;
use url::Url;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct PostQuery { pub struct PostQuery {
@ -64,10 +65,8 @@ impl Post {
impl PostForm { impl PostForm {
pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> { pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> {
let oprops = &page.object_props; let oprops = &page.object_props;
let creator = fetch_remote_user( let apub_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string(), let creator = fetch_remote_user(&apub_id, conn)?;
conn,
)?;
Ok(PostForm { Ok(PostForm {
name: oprops.get_name_xsd_string().unwrap().to_string(), name: oprops.get_name_xsd_string().unwrap().to_string(),
url: oprops.get_url_xsd_any_uri().map(|u| u.to_string()), url: oprops.get_url_xsd_any_uri().map(|u| u.to_string()),

View File

@ -15,22 +15,23 @@ use isahc::prelude::*;
use log::warn; use log::warn;
use serde::Deserialize; use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use url::Url;
fn fetch_node_info(domain: &str) -> Result<NodeInfo, Error> { fn fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
let well_known_uri = format!( let well_known_uri = Url::parse(&format!(
"{}://{}/.well-known/nodeinfo", "{}://{}/.well-known/nodeinfo",
get_apub_protocol_string(), get_apub_protocol_string(),
domain domain
); ))?;
let well_known = fetch_remote_object::<NodeInfoWellKnown>(&well_known_uri)?; let well_known = fetch_remote_object::<NodeInfoWellKnown>(&well_known_uri)?;
Ok(fetch_remote_object::<NodeInfo>(&well_known.links.href)?) Ok(fetch_remote_object::<NodeInfo>(&well_known.links.href)?)
} }
fn fetch_communities_from_instance( fn fetch_communities_from_instance(
community_list_url: &str, community_list: &Url,
conn: &PgConnection, conn: &PgConnection,
) -> Result<Vec<Community>, Error> { ) -> Result<Vec<Community>, Error> {
fetch_remote_object::<UnorderedCollection>(community_list_url)? fetch_remote_object::<UnorderedCollection>(community_list)?
.collection_props .collection_props
.get_many_items_base_boxes() .get_many_items_base_boxes()
.unwrap() .unwrap()
@ -53,16 +54,16 @@ fn fetch_communities_from_instance(
} }
// TODO: add an optional param last_updated and only fetch if its too old // TODO: add an optional param last_updated and only fetch if its too old
pub fn fetch_remote_object<Response>(uri: &str) -> Result<Response, Error> pub fn fetch_remote_object<Response>(url: &Url) -> Result<Response, Error>
where where
Response: for<'de> Deserialize<'de>, Response: for<'de> Deserialize<'de>,
{ {
if Settings::get().federation.tls_enabled && !uri.starts_with("https://") { if Settings::get().federation.tls_enabled && url.scheme() != "https" {
return Err(format_err!("Activitypub uri is insecure: {}", uri)); return Err(format_err!("Activitypub uri is insecure: {}", url));
} }
// TODO: this function should return a future // TODO: this function should return a future
let timeout = Duration::from_secs(60); let timeout = Duration::from_secs(60);
let text = Request::get(uri) let text = Request::get(url.as_str())
.header("Accept", APUB_JSON_CONTENT_TYPE) .header("Accept", APUB_JSON_CONTENT_TYPE)
.connect_timeout(timeout) .connect_timeout(timeout)
.timeout(timeout) .timeout(timeout)
@ -78,11 +79,14 @@ fn fetch_remote_community_posts(
community: &Community, community: &Community,
conn: &PgConnection, conn: &PgConnection,
) -> Result<Vec<Post>, Error> { ) -> Result<Vec<Post>, Error> {
let endpoint = format!("http://{}/federation/c/{}", instance, community.name); let endpoint = Url::parse(&format!(
"http://{}/federation/c/{}",
instance, community.name
))?;
let group = fetch_remote_object::<GroupExt>(&endpoint)?; let group = fetch_remote_object::<GroupExt>(&endpoint)?;
let outbox_uri = &group.extension.get_outbox().to_string(); let outbox_uri = Url::parse(&group.extension.get_outbox().to_string())?;
// TODO: outbox url etc should be stored in local db // TODO: outbox url etc should be stored in local db
let outbox = fetch_remote_object::<OrderedCollection>(outbox_uri)?; let outbox = fetch_remote_object::<OrderedCollection>(&outbox_uri)?;
let items = outbox.collection_props.get_many_items_base_boxes(); let items = outbox.collection_props.get_many_items_base_boxes();
Ok( Ok(
@ -106,7 +110,7 @@ fn fetch_remote_community_posts(
) )
} }
pub fn fetch_remote_user(apub_id: &str, conn: &PgConnection) -> Result<User_, Error> { pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Error> {
let person = fetch_remote_object::<PersonExt>(apub_id)?; let person = fetch_remote_object::<PersonExt>(apub_id)?;
let uf = UserForm::from_person(&person)?; let uf = UserForm::from_person(&person)?;
let existing = User_::read_from_apub_id(conn, &uf.actor_id); let existing = User_::read_from_apub_id(conn, &uf.actor_id);
@ -122,8 +126,8 @@ pub fn fetch_remote_user(apub_id: &str, conn: &PgConnection) -> Result<User_, Er
pub fn fetch_all(conn: &PgConnection) -> Result<(), Error> { pub fn fetch_all(conn: &PgConnection) -> Result<(), Error> {
for instance in &get_following_instances() { for instance in &get_following_instances() {
let node_info = fetch_node_info(instance)?; let node_info = fetch_node_info(instance)?;
if let Some(community_list_url) = node_info.metadata.community_list_url { if let Some(community_list) = node_info.metadata.community_list_url {
let communities = fetch_communities_from_instance(&community_list_url, conn)?; let communities = fetch_communities_from_instance(&community_list, conn)?;
for c in communities { for c in communities {
fetch_remote_community_posts(instance, &c, conn)?; fetch_remote_community_posts(instance, &c, conn)?;
} }

View File

@ -7,8 +7,10 @@ use actix_web::web;
use actix_web::HttpResponse; use actix_web::HttpResponse;
use diesel::r2d2::{ConnectionManager, Pool}; use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection; use diesel::PgConnection;
use failure::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use url::Url;
pub fn config(cfg: &mut web::ServiceConfig) { pub fn config(cfg: &mut web::ServiceConfig) {
cfg cfg
@ -16,18 +18,18 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known)); .route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
} }
async fn node_info_well_known() -> HttpResponse<Body> { async fn node_info_well_known() -> Result<HttpResponse<Body>, Error> {
let node_info = NodeInfoWellKnown { let node_info = NodeInfoWellKnown {
links: NodeInfoWellKnownLinks { links: NodeInfoWellKnownLinks {
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".to_string(), rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
href: format!( href: Url::parse(&format!(
"{}://{}/nodeinfo/2.0.json", "{}://{}/nodeinfo/2.0.json",
get_apub_protocol_string(), get_apub_protocol_string(),
Settings::get().hostname Settings::get().hostname
), ))?,
}, },
}; };
HttpResponse::Ok().json(node_info) Ok(HttpResponse::Ok().json(node_info))
} }
async fn node_info( async fn node_info(
@ -60,11 +62,11 @@ async fn node_info(
open_registrations: site_view.open_registration, open_registrations: site_view.open_registration,
}, },
metadata: NodeInfoMetadata { metadata: NodeInfoMetadata {
community_list_url: Some(format!( community_list_url: Some(Url::parse(&format!(
"{}://{}/federation/communities", "{}://{}/federation/communities",
get_apub_protocol_string(), get_apub_protocol_string(),
Settings::get().hostname Settings::get().hostname
)), ))?),
}, },
}) })
}) })
@ -81,8 +83,8 @@ pub struct NodeInfoWellKnown {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct NodeInfoWellKnownLinks { pub struct NodeInfoWellKnownLinks {
pub rel: String, pub rel: Url,
pub href: String, pub href: Url,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -116,5 +118,5 @@ pub struct NodeInfoUsers {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct NodeInfoMetadata { pub struct NodeInfoMetadata {
pub community_list_url: Option<String>, pub community_list_url: Option<Url>,
} }