Merge pull request 'Update crates' (#961) from update-crates into main
Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/961
This commit is contained in:
		
						commit
						0f7094a70e
					
				| @ -11,7 +11,7 @@ RUN apt update &&\ | ||||
|     rm -rf /var/lib/apt/lists/* | ||||
| 
 | ||||
| #install and configure rust | ||||
| RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2021-01-15 -y &&\ | ||||
| RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2021-11-27 -y &&\ | ||||
|     rustup component add rustfmt clippy &&\ | ||||
|     rustup component add rust-std --target wasm32-unknown-unknown | ||||
| 
 | ||||
|  | ||||
| @ -16,7 +16,7 @@ | ||||
| 
 | ||||
| - Upgrade some dependent crates (#858) | ||||
| - Use tracing crate (#868) | ||||
| - Update Rust version to nightly-2021-01-15 (#878) | ||||
| - Update Rust version to nightly-2021-11-27 (#961) | ||||
| - Upgrade Tantivy to 0.13.3 and lindera-tantivy to 0.7.1 (#878) | ||||
| - Run searcher on actor system (#870) | ||||
| - Use article title as its slug instead of capitalizing and inserting hyphens (#920) | ||||
|  | ||||
							
								
								
									
										435
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										435
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -18,13 +18,13 @@ guid-create = "0.1" | ||||
| lettre = "0.9.2" | ||||
| lettre_email = "0.9.2" | ||||
| num_cpus = "1.10" | ||||
| rocket = "=0.4.6" | ||||
| rocket_contrib = { version = "=0.4.5", features = ["json"] } | ||||
| rocket = "0.4.6" | ||||
| rocket_contrib = { version = "0.4.5", features = ["json"] } | ||||
| rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" } | ||||
| rpassword = "4.0" | ||||
| scheduled-thread-pool = "0.2.2" | ||||
| serde = "1.0" | ||||
| serde_json = "< 1.0.70" | ||||
| serde_json = "1.0.70" | ||||
| shrinkwraprs = "0.2.1" | ||||
| validator = "0.8" | ||||
| validator_derive = "0.8" | ||||
|  | ||||
							
								
								
									
										10
									
								
								build.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								build.rs
									
									
									
									
									
								
							| @ -41,9 +41,9 @@ fn main() { | ||||
|         .expect("compile templates"); | ||||
| 
 | ||||
|     compile_themes().expect("Theme compilation error"); | ||||
|     recursive_copy(&Path::new("assets").join("icons"), &Path::new("static")) | ||||
|     recursive_copy(&Path::new("assets").join("icons"), Path::new("static")) | ||||
|         .expect("Couldn't copy icons"); | ||||
|     recursive_copy(&Path::new("assets").join("images"), &Path::new("static")) | ||||
|     recursive_copy(&Path::new("assets").join("images"), Path::new("static")) | ||||
|         .expect("Couldn't copy images"); | ||||
|     create_dir_all(&Path::new("static").join("media")).expect("Couldn't init media directory"); | ||||
| 
 | ||||
| @ -97,12 +97,12 @@ fn compile_theme(path: &Path, out_dir: &Path) -> std::io::Result<()> { | ||||
|         .components() | ||||
|         .skip_while(|c| *c != Component::Normal(OsStr::new("themes"))) | ||||
|         .skip(1) | ||||
|         .filter_map(|c| { | ||||
|         .map(|c| { | ||||
|             c.as_os_str() | ||||
|                 .to_str() | ||||
|                 .unwrap_or_default() | ||||
|                 .splitn(2, '.') | ||||
|                 .next() | ||||
|                 .split_once('.') | ||||
|                 .map_or(c.as_os_str().to_str().unwrap_or_default(), |x| x.0) | ||||
|         }) | ||||
|         .collect::<Vec<_>>() | ||||
|         .join("-"); | ||||
|  | ||||
| @ -14,11 +14,11 @@ heck = "0.3.0" | ||||
| hex = "0.3" | ||||
| hyper = "0.12.33" | ||||
| openssl = "0.10.22" | ||||
| rocket = "=0.4.6" | ||||
| rocket = "0.4.6" | ||||
| reqwest = { version = "0.9", features = ["socks"] } | ||||
| serde = "1.0" | ||||
| serde_derive = "1.0" | ||||
| serde_json = "< 1.0.70" | ||||
| serde_json = "1.0.70" | ||||
| shrinkwraprs = "0.3.0" | ||||
| syntect = "4.5.0" | ||||
| tokio = "0.1.22" | ||||
|  | ||||
| @ -207,7 +207,7 @@ where | ||||
|                 }; | ||||
| 
 | ||||
|                 // Handle the activity
 | ||||
|                 match obj.activity(ctx, actor, &act_id) { | ||||
|                 match obj.activity(ctx, actor, act_id) { | ||||
|                     Ok(res) => Inbox::Handled(res.into()), | ||||
|                     Err(e) => Inbox::Failed(e), | ||||
|                 } | ||||
|  | ||||
| @ -145,7 +145,7 @@ where | ||||
|             warn!("Inbox doesn't have host: {:?}", &inbox); | ||||
|             continue; | ||||
|         }; | ||||
|         let host_header_value = HeaderValue::from_str(&url.host_str().expect("Unreachable")); | ||||
|         let host_header_value = HeaderValue::from_str(url.host_str().expect("Unreachable")); | ||||
|         if host_header_value.is_err() { | ||||
|             warn!("Header value is invalid: {:?}", url.host_str()); | ||||
|             continue; | ||||
|  | ||||
| @ -182,7 +182,7 @@ pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>( | ||||
|     } | ||||
|     let digest = all_headers.get_one("digest").unwrap_or(""); | ||||
|     let digest = request::Digest::from_header(digest); | ||||
|     if !digest.map(|d| d.verify_header(&data)).unwrap_or(false) { | ||||
|     if !digest.map(|d| d.verify_header(data)).unwrap_or(false) { | ||||
|         // signature was valid, but body content does not match its digest
 | ||||
|         return SignatureValidity::Invalid; | ||||
|     } | ||||
|  | ||||
| @ -141,13 +141,13 @@ fn highlight_code<'a>( | ||||
|                     unreachable!(); | ||||
|                 }; | ||||
|                 let syntax_set = SyntaxSet::load_defaults_newlines(); | ||||
|                 let syntax = syntax_set.find_syntax_by_token(&lang).unwrap_or_else(|| { | ||||
|                 let syntax = syntax_set.find_syntax_by_token(lang).unwrap_or_else(|| { | ||||
|                     syntax_set | ||||
|                         .find_syntax_by_name(&lang) | ||||
|                         .find_syntax_by_name(lang) | ||||
|                         .unwrap_or_else(|| syntax_set.find_syntax_plain_text()) | ||||
|                 }); | ||||
|                 let mut html = ClassedHTMLGenerator::new_with_class_style( | ||||
|                     &syntax, | ||||
|                     syntax, | ||||
|                     &syntax_set, | ||||
|                     ClassStyle::Spaced, | ||||
|                 ); | ||||
| @ -334,16 +334,15 @@ pub fn md_to_html<'a>( | ||||
|                                         text_acc.push(c) | ||||
|                                     } | ||||
|                                     let mention = text_acc; | ||||
|                                     let short_mention = mention.splitn(1, '@').next().unwrap_or(""); | ||||
|                                     let link = Tag::Link( | ||||
|                                         LinkType::Inline, | ||||
|                                         format!("{}@/{}/", base_url, &mention).into(), | ||||
|                                         short_mention.to_owned().into(), | ||||
|                                         mention.clone().into(), | ||||
|                                     ); | ||||
| 
 | ||||
|                                     mentions.push(mention.clone()); | ||||
|                                     events.push(Event::Start(link.clone())); | ||||
|                                     events.push(Event::Text(format!("@{}", &short_mention).into())); | ||||
|                                     events.push(Event::Text(format!("@{}", &mention).into())); | ||||
|                                     events.push(Event::End(link)); | ||||
| 
 | ||||
|                                     ( | ||||
|  | ||||
| @ -54,11 +54,6 @@ pub enum EditorError { | ||||
|     DOMError, | ||||
| } | ||||
| 
 | ||||
| impl From<std::option::NoneError> for EditorError { | ||||
|     fn from(_: std::option::NoneError) -> Self { | ||||
|         EditorError::NoneError | ||||
|     } | ||||
| } | ||||
| const AUTOSAVE_DEBOUNCE_TIME: i32 = 5000; | ||||
| #[derive(Serialize, Deserialize)] | ||||
| struct AutosaveInformation { | ||||
| @ -198,7 +193,7 @@ fn clear_autosave() { | ||||
|         .unwrap() | ||||
|         .remove_item(&get_autosave_id()) | ||||
|         .unwrap(); | ||||
|     console::log_1(&&format!("Saved to {}", &get_autosave_id()).into()); | ||||
|     console::log_1(&format!("Saved to {}", &get_autosave_id()).into()); | ||||
| } | ||||
| type TimeoutHandle = i32; | ||||
| lazy_static! { | ||||
| @ -366,7 +361,9 @@ fn init_editor() -> Result<(), EditorError> { | ||||
|             return Ok(()); | ||||
|         } | ||||
|         let old_ed = old_ed.unwrap(); | ||||
|         let old_title = document().get_element_by_id("plume-editor-title")?; | ||||
|         let old_title = document() | ||||
|             .get_element_by_id("plume-editor-title") | ||||
|             .ok_or(EditorError::NoneError)?; | ||||
|         old_ed | ||||
|             .dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
| @ -434,7 +431,8 @@ fn init_editor() -> Result<(), EditorError> { | ||||
|             bg.class_list().add_1("show").unwrap(); | ||||
|         })) as Box<dyn FnMut(MouseEvent)>); | ||||
|         document() | ||||
|             .get_element_by_id("publish")? | ||||
|             .get_element_by_id("publish") | ||||
|             .ok_or(EditorError::NoneError)? | ||||
|             .add_event_listener_with_callback("click", show_popup.as_ref().unchecked_ref()) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         show_popup.forget(); | ||||
| @ -528,8 +526,14 @@ fn init_popup( | ||||
|     cover_label | ||||
|         .set_attribute("for", "cover") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     let cover = document.get_element_by_id("cover")?; | ||||
|     cover.parent_element()?.remove_child(&cover).ok(); | ||||
|     let cover = document | ||||
|         .get_element_by_id("cover") | ||||
|         .ok_or(EditorError::NoneError)?; | ||||
|     cover | ||||
|         .parent_element() | ||||
|         .ok_or(EditorError::NoneError)? | ||||
|         .remove_child(&cover) | ||||
|         .ok(); | ||||
|     popup | ||||
|         .append_child(&cover_label) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| @ -554,7 +558,7 @@ fn init_popup( | ||||
|         draft.set_checked(draft_checkbox.checked()); | ||||
| 
 | ||||
|         draft_label | ||||
|             .append_child(&draft) | ||||
|             .append_child(draft) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         draft_label | ||||
|             .append_child(&document.create_text_node(&i18n!(CATALOG, "This is a draft"))) | ||||
| @ -620,11 +624,12 @@ fn init_popup( | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     callback.forget(); | ||||
|     popup | ||||
|         .append_child(&button) | ||||
|         .append_child(button) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     document | ||||
|         .body()? | ||||
|         .body() | ||||
|         .ok_or(EditorError::NoneError)? | ||||
|         .append_child(&popup) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     Ok(popup) | ||||
| @ -641,7 +646,8 @@ fn init_popup_bg() -> Result<Element, EditorError> { | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     document() | ||||
|         .body()? | ||||
|         .body() | ||||
|         .ok_or(EditorError::NoneError)? | ||||
|         .append_child(&bg) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     let callback = Closure::wrap(Box::new(|_| close_popup()) as Box<dyn FnMut(MouseEvent)>); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #![recursion_limit = "128"] | ||||
| #![feature(decl_macro, proc_macro_hygiene, try_trait)] | ||||
| #![feature(decl_macro, proc_macro_hygiene)] | ||||
| 
 | ||||
| #[macro_use] | ||||
| extern crate gettext_macros; | ||||
| @ -61,7 +61,7 @@ lazy_static! { | ||||
|     static ref CATALOG: gettext::Catalog = { | ||||
|         let catalogs = include_i18n!(); | ||||
|         let lang = window().unwrap().navigator().language().unwrap(); | ||||
|         let lang = lang.splitn(2, '-').next().unwrap_or("en"); | ||||
|         let lang = lang.split_once('-').map_or("en", |x| x.0); | ||||
| 
 | ||||
|         let english_position = catalogs | ||||
|             .iter() | ||||
| @ -85,7 +85,7 @@ pub fn main() -> Result<(), JsValue> { | ||||
|     menu(); | ||||
|     search(); | ||||
|     editor::init() | ||||
|         .map_err(|e| console::error_1(&&format!("Editor error: {:?}", e).into())) | ||||
|         .map_err(|e| console::error_1(&format!("Editor error: {:?}", e).into())) | ||||
|         .ok(); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -58,7 +58,7 @@ pub fn import_migrations(input: TokenStream) -> TokenStream { | ||||
|             (name, up_sql, down_sql) | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|     let migrations_name = migrations.iter().map(|m| &m.0).collect::<Vec<_>>(); | ||||
|     let migrations_name = migrations.iter().map(|m| &m.0); | ||||
|     let migrations_up = migrations | ||||
|         .iter() | ||||
|         .map(|m| m.1.as_str()) | ||||
| @ -103,7 +103,7 @@ fn file_to_migration(file: &str) -> TokenStream2 { | ||||
|                 acc.push('\n'); | ||||
|             } | ||||
|         } else if let Some(acc_str) = line.strip_prefix("--#!") { | ||||
|             acc.push_str(&acc_str); | ||||
|             acc.push_str(acc_str); | ||||
|             acc.push('\n'); | ||||
|         } else if line.starts_with("--") { | ||||
|             continue; | ||||
|  | ||||
| @ -15,13 +15,13 @@ lazy_static = "1.0" | ||||
| ldap3 = "0.7.1" | ||||
| migrations_internals= "1.4.0" | ||||
| openssl = "0.10.22" | ||||
| rocket = "=0.4.6" | ||||
| rocket = "0.4.6" | ||||
| rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" } | ||||
| reqwest = "0.9" | ||||
| scheduled-thread-pool = "0.2.2" | ||||
| serde = "1.0" | ||||
| serde_derive = "1.0" | ||||
| serde_json = "< 1.0.70" | ||||
| serde_json = "1.0.70" | ||||
| tantivy = "0.13.3" | ||||
| url = "2.1" | ||||
| walkdir = "2.2" | ||||
|  | ||||
| @ -86,14 +86,18 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApiToken { | ||||
|         } | ||||
| 
 | ||||
|         let mut parsed_header = headers[0].split(' '); | ||||
|         let auth_type = parsed_header.next().map_or_else( | ||||
|             || Outcome::Failure((Status::BadRequest, TokenError::NoType)), | ||||
|             Outcome::Success, | ||||
|         )?; | ||||
|         let val = parsed_header.next().map_or_else( | ||||
|             || Outcome::Failure((Status::BadRequest, TokenError::NoValue)), | ||||
|             Outcome::Success, | ||||
|         )?; | ||||
|         let auth_type = parsed_header | ||||
|             .next() | ||||
|             .map_or_else::<rocket::Outcome<&str, _, ()>, _, _>( | ||||
|                 || Outcome::Failure((Status::BadRequest, TokenError::NoType)), | ||||
|                 Outcome::Success, | ||||
|             )?; | ||||
|         let val = parsed_header | ||||
|             .next() | ||||
|             .map_or_else::<rocket::Outcome<&str, _, ()>, _, _>( | ||||
|                 || Outcome::Failure((Status::BadRequest, TokenError::NoValue)), | ||||
|                 Outcome::Success, | ||||
|             )?; | ||||
| 
 | ||||
|         if auth_type == "Bearer" { | ||||
|             let conn = request | ||||
|  | ||||
| @ -28,7 +28,7 @@ impl BlocklistedEmail { | ||||
|     pub fn delete_entries(conn: &Connection, ids: Vec<i32>) -> Result<bool> { | ||||
|         use diesel::delete; | ||||
|         for i in ids { | ||||
|             let be: BlocklistedEmail = BlocklistedEmail::find_by_id(&conn, i)?; | ||||
|             let be: BlocklistedEmail = BlocklistedEmail::find_by_id(conn, i)?; | ||||
|             delete(&be).execute(conn)?; | ||||
|         } | ||||
|         Ok(true) | ||||
|  | ||||
| @ -149,7 +149,15 @@ impl Blog { | ||||
|             .into_iter() | ||||
|             .find(|l| l.mime_type == Some(String::from("application/activity+json"))) | ||||
|             .ok_or(Error::Webfinger) | ||||
|             .and_then(|l| Blog::from_id(conn, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e)) | ||||
|             .and_then(|l| { | ||||
|                 Blog::from_id( | ||||
|                     conn, | ||||
|                     &l.href.ok_or(Error::MissingApProperty)?, | ||||
|                     None, | ||||
|                     CONFIG.proxy(), | ||||
|                 ) | ||||
|                 .map_err(|(_, e)| e) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> { | ||||
| @ -236,7 +244,7 @@ impl Blog { | ||||
|         (min, max): (i32, i32), | ||||
|     ) -> Result<ActivityStream<OrderedCollectionPage>> { | ||||
|         let mut coll = OrderedCollectionPage::default(); | ||||
|         let acts = self.get_activity_page(&conn, (min, max)); | ||||
|         let acts = self.get_activity_page(conn, (min, max)); | ||||
|         //This still doesn't do anything because the outbox
 | ||||
|         //doesn't do anything yet
 | ||||
|         coll.collection_page_props.set_next_link(Id::new(&format!( | ||||
| @ -265,7 +273,10 @@ impl Blog { | ||||
| 
 | ||||
|     pub fn get_keypair(&self) -> Result<PKey<Private>> { | ||||
|         PKey::from_rsa(Rsa::private_key_from_pem( | ||||
|             self.private_key.clone()?.as_ref(), | ||||
|             self.private_key | ||||
|                 .clone() | ||||
|                 .ok_or(Error::MissingApProperty)? | ||||
|                 .as_ref(), | ||||
|         )?) | ||||
|         .map_err(Error::from) | ||||
|     } | ||||
| @ -318,7 +329,7 @@ impl Blog { | ||||
|     } | ||||
| 
 | ||||
|     pub fn delete(&self, conn: &Connection) -> Result<()> { | ||||
|         for post in Post::get_for_blog(conn, &self)? { | ||||
|         for post in Post::get_for_blog(conn, self)? { | ||||
|             post.delete(conn)?; | ||||
|         } | ||||
|         diesel::delete(self) | ||||
| @ -339,12 +350,12 @@ impl FromId<DbConn> for Blog { | ||||
|     type Object = CustomGroup; | ||||
| 
 | ||||
|     fn from_db(conn: &DbConn, id: &str) -> Result<Self> { | ||||
|         Self::find_by_ap_url(&conn, id) | ||||
|         Self::find_by_ap_url(conn, id) | ||||
|     } | ||||
| 
 | ||||
|     fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> { | ||||
|         let url = Url::parse(&acct.object.object_props.id_string()?)?; | ||||
|         let inst = url.host_str()?; | ||||
|         let inst = url.host_str().ok_or(Error::Url)?; | ||||
|         let instance = Instance::find_by_domain(conn, inst).or_else(|_| { | ||||
|             Instance::insert( | ||||
|                 conn, | ||||
| @ -468,7 +479,7 @@ impl sign::Signer for Blog { | ||||
|         let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?; | ||||
|         let mut verifier = Verifier::new(MessageDigest::sha256(), &key)?; | ||||
|         verifier.update(data.as_bytes())?; | ||||
|         verifier.verify(&signature).map_err(Error::from) | ||||
|         verifier.verify(signature).map_err(Error::from) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -141,18 +141,20 @@ impl Comment { | ||||
|     } | ||||
| 
 | ||||
|     pub fn create_activity(&self, conn: &DbConn) -> Result<Create> { | ||||
|         let author = User::get(&conn, self.author_id)?; | ||||
|         let author = User::get(conn, self.author_id)?; | ||||
| 
 | ||||
|         let note = self.to_activity(conn)?; | ||||
|         let mut act = Create::default(); | ||||
|         act.create_props.set_actor_link(author.into_id())?; | ||||
|         act.create_props.set_object_object(note.clone())?; | ||||
|         act.object_props | ||||
|             .set_id_string(format!("{}/activity", self.ap_url.clone()?,))?; | ||||
|         act.object_props.set_id_string(format!( | ||||
|             "{}/activity", | ||||
|             self.ap_url.clone().ok_or(Error::MissingApProperty)?, | ||||
|         ))?; | ||||
|         act.object_props | ||||
|             .set_to_link_vec(note.object_props.to_link_vec::<Id>()?)?; | ||||
|         act.object_props | ||||
|             .set_cc_link_vec(vec![Id::new(self.get_author(&conn)?.followers_endpoint)])?; | ||||
|             .set_cc_link_vec(vec![Id::new(self.get_author(conn)?.followers_endpoint)])?; | ||||
|         Ok(act) | ||||
|     } | ||||
| 
 | ||||
| @ -182,7 +184,9 @@ impl Comment { | ||||
|             .set_actor_link(self.get_author(conn)?.into_id())?; | ||||
| 
 | ||||
|         let mut tombstone = Tombstone::default(); | ||||
|         tombstone.object_props.set_id_string(self.ap_url.clone()?)?; | ||||
|         tombstone | ||||
|             .object_props | ||||
|             .set_id_string(self.ap_url.clone().ok_or(Error::MissingApProperty)?)?; | ||||
|         act.delete_props.set_object_object(tombstone)?; | ||||
| 
 | ||||
|         act.object_props | ||||
| @ -204,7 +208,13 @@ impl FromId<DbConn> for Comment { | ||||
| 
 | ||||
|     fn from_activity(conn: &DbConn, note: Note) -> Result<Self> { | ||||
|         let comm = { | ||||
|             let previous_url = note.object_props.in_reply_to.as_ref()?.as_str()?; | ||||
|             let previous_url = note | ||||
|                 .object_props | ||||
|                 .in_reply_to | ||||
|                 .as_ref() | ||||
|                 .ok_or(Error::MissingApProperty)? | ||||
|                 .as_str() | ||||
|                 .ok_or(Error::MissingApProperty)?; | ||||
|             let previous_comment = Comment::find_by_ap_url(conn, previous_url); | ||||
| 
 | ||||
|             let is_public = |v: &Option<serde_json::Value>| match v | ||||
| @ -346,7 +356,7 @@ impl AsObject<User, Delete, &DbConn> for Comment { | ||||
|             m.delete(conn)?; | ||||
|         } | ||||
| 
 | ||||
|         for n in Notification::find_for_comment(&conn, &self)? { | ||||
|         for n in Notification::find_for_comment(conn, &self)? { | ||||
|             n.delete(&**conn)?; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -17,7 +17,7 @@ pub struct Config { | ||||
|     pub db_min_idle: Option<u32>, | ||||
|     pub search_index: String, | ||||
|     pub search_tokenizers: SearchTokenizerConfig, | ||||
|     pub rocket: Result<RocketConfig, RocketError>, | ||||
|     pub rocket: Result<RocketConfig, InvalidRocketConfig>, | ||||
|     pub logo: LogoConfig, | ||||
|     pub default_theme: String, | ||||
|     pub media_directory: String, | ||||
| @ -31,21 +31,21 @@ impl Config { | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum RocketError { | ||||
|     InvalidEnv, | ||||
|     InvalidAddress, | ||||
|     InvalidSecretKey, | ||||
| pub enum InvalidRocketConfig { | ||||
|     Env, | ||||
|     Address, | ||||
|     SecretKey, | ||||
| } | ||||
| 
 | ||||
| fn get_rocket_config() -> Result<RocketConfig, RocketError> { | ||||
|     let mut c = RocketConfig::active().map_err(|_| RocketError::InvalidEnv)?; | ||||
| fn get_rocket_config() -> Result<RocketConfig, InvalidRocketConfig> { | ||||
|     let mut c = RocketConfig::active().map_err(|_| InvalidRocketConfig::Env)?; | ||||
| 
 | ||||
|     let address = var("ROCKET_ADDRESS").unwrap_or_else(|_| "localhost".to_owned()); | ||||
|     let port = var("ROCKET_PORT") | ||||
|         .ok() | ||||
|         .map(|s| s.parse::<u16>().unwrap()) | ||||
|         .unwrap_or(7878); | ||||
|     let secret_key = var("ROCKET_SECRET_KEY").map_err(|_| RocketError::InvalidSecretKey)?; | ||||
|     let secret_key = var("ROCKET_SECRET_KEY").map_err(|_| InvalidRocketConfig::SecretKey)?; | ||||
|     let form_size = var("FORM_SIZE") | ||||
|         .unwrap_or_else(|_| "128".to_owned()) | ||||
|         .parse::<u64>() | ||||
| @ -56,10 +56,10 @@ fn get_rocket_config() -> Result<RocketConfig, RocketError> { | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     c.set_address(address) | ||||
|         .map_err(|_| RocketError::InvalidAddress)?; | ||||
|         .map_err(|_| InvalidRocketConfig::Address)?; | ||||
|     c.set_port(port); | ||||
|     c.set_secret_key(secret_key) | ||||
|         .map_err(|_| RocketError::InvalidSecretKey)?; | ||||
|         .map_err(|_| InvalidRocketConfig::SecretKey)?; | ||||
| 
 | ||||
|     c.set_limits( | ||||
|         Limits::new() | ||||
| @ -155,7 +155,7 @@ impl Default for LogoConfig { | ||||
|             .ok() | ||||
|             .or_else(|| custom_main.clone()); | ||||
|         let other = if let Some(main) = custom_main.clone() { | ||||
|             let ext = |path: &str| match path.rsplitn(2, '.').next() { | ||||
|             let ext = |path: &str| match path.rsplit_once('.').map(|x| x.1) { | ||||
|                 Some("png") => Some("image/png".to_owned()), | ||||
|                 Some("jpg") | Some("jpeg") => Some("image/jpeg".to_owned()), | ||||
|                 Some("svg") => Some("image/svg+xml".to_owned()), | ||||
|  | ||||
| @ -195,7 +195,7 @@ impl AsObject<User, Undo, &DbConn> for Follow { | ||||
|             diesel::delete(&self).execute(&**conn)?; | ||||
| 
 | ||||
|             // delete associated notification if any
 | ||||
|             if let Ok(notif) = Notification::find(&conn, notification_kind::FOLLOW, self.id) { | ||||
|             if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) { | ||||
|                 diesel::delete(¬if).execute(&**conn)?; | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| #![feature(try_trait)] | ||||
| #![feature(never_type)] | ||||
| #![feature(proc_macro_hygiene)] | ||||
| #![feature(box_patterns)] | ||||
| @ -86,12 +85,6 @@ impl From<diesel::result::Error> for Error { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<std::option::NoneError> for Error { | ||||
|     fn from(_: std::option::NoneError) -> Self { | ||||
|         Error::NotFound | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<url::ParseError> for Error { | ||||
|     fn from(_: url::ParseError) -> Self { | ||||
|         Error::Url | ||||
|  | ||||
| @ -148,7 +148,7 @@ impl AsObject<User, activity::Undo, &DbConn> for Like { | ||||
|             diesel::delete(&self).execute(&**conn)?; | ||||
| 
 | ||||
|             // delete associated notification if any
 | ||||
|             if let Ok(notif) = Notification::find(&conn, notification_kind::LIKE, self.id) { | ||||
|             if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) { | ||||
|                 diesel::delete(¬if).execute(&**conn)?; | ||||
|             } | ||||
|             Ok(()) | ||||
|  | ||||
| @ -143,6 +143,7 @@ macro_rules! func { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[allow(dead_code)] | ||||
| #[derive(Clone, Queryable, Identifiable)] | ||||
| struct ListElem { | ||||
|     pub id: i32, | ||||
|  | ||||
| @ -104,8 +104,8 @@ impl Media { | ||||
|     pub fn category(&self) -> MediaCategory { | ||||
|         match &*self | ||||
|             .file_path | ||||
|             .rsplitn(2, '.') | ||||
|             .next() | ||||
|             .rsplit_once('.') | ||||
|             .map(|x| x.1) | ||||
|             .expect("Media::category: extension error") | ||||
|             .to_lowercase() | ||||
|         { | ||||
| @ -208,14 +208,17 @@ impl Media { | ||||
| 
 | ||||
|     // TODO: merge with save_remote?
 | ||||
|     pub fn from_activity(conn: &DbConn, image: &Image) -> Result<Media> { | ||||
|         let remote_url = image.object_props.url_string().ok()?; | ||||
|         let remote_url = image | ||||
|             .object_props | ||||
|             .url_string() | ||||
|             .or(Err(Error::MissingApProperty))?; | ||||
|         let path = determine_mirror_file_path(&remote_url); | ||||
|         let parent = path.parent()?; | ||||
|         let parent = path.parent().ok_or(Error::InvalidValue)?; | ||||
|         if !parent.is_dir() { | ||||
|             DirBuilder::new().recursive(true).create(parent)?; | ||||
|         } | ||||
| 
 | ||||
|         let mut dest = fs::File::create(path.clone()).ok()?; | ||||
|         let mut dest = fs::File::create(path.clone())?; | ||||
|         // TODO: conditional GET
 | ||||
|         if let Some(proxy) = CONFIG.proxy() { | ||||
|             reqwest::ClientBuilder::new().proxy(proxy.clone()).build()? | ||||
| @ -223,16 +226,17 @@ impl Media { | ||||
|             reqwest::Client::new() | ||||
|         } | ||||
|         .get(remote_url.as_str()) | ||||
|         .send() | ||||
|         .ok()? | ||||
|         .copy_to(&mut dest) | ||||
|         .ok()?; | ||||
|         .send()? | ||||
|         .copy_to(&mut dest)?; | ||||
| 
 | ||||
|         Media::find_by_file_path(conn, &path.to_str()?) | ||||
|         Media::find_by_file_path(conn, path.to_str().ok_or(Error::InvalidValue)?) | ||||
|             .and_then(|mut media| { | ||||
|                 let mut updated = false; | ||||
| 
 | ||||
|                 let alt_text = image.object_props.content_string().ok()?; | ||||
|                 let alt_text = image | ||||
|                     .object_props | ||||
|                     .content_string() | ||||
|                     .or(Err(Error::NotFound))?; | ||||
|                 let sensitive = image.object_props.summary_string().is_ok(); | ||||
|                 let content_warning = image.object_props.summary_string().ok(); | ||||
|                 if media.alt_text != alt_text { | ||||
| @ -264,8 +268,11 @@ impl Media { | ||||
|                 Media::insert( | ||||
|                     conn, | ||||
|                     NewMedia { | ||||
|                         file_path: path.to_str()?.to_string(), | ||||
|                         alt_text: image.object_props.content_string().ok()?, | ||||
|                         file_path: path.to_str().ok_or(Error::InvalidValue)?.to_string(), | ||||
|                         alt_text: image | ||||
|                             .object_props | ||||
|                             .content_string() | ||||
|                             .or(Err(Error::NotFound))?, | ||||
|                         is_remote: false, | ||||
|                         remote_url: None, | ||||
|                         sensitive: image.object_props.summary_string().is_ok(), | ||||
| @ -275,9 +282,10 @@ impl Media { | ||||
|                             image | ||||
|                                 .object_props | ||||
|                                 .attributed_to_link_vec::<Id>() | ||||
|                                 .ok()? | ||||
|                                 .or(Err(Error::NotFound))? | ||||
|                                 .into_iter() | ||||
|                                 .next()? | ||||
|                                 .next() | ||||
|                                 .ok_or(Error::NotFound)? | ||||
|                                 .as_ref(), | ||||
|                             None, | ||||
|                             CONFIG.proxy(), | ||||
| @ -325,7 +333,7 @@ fn determine_mirror_file_path(url: &str) -> PathBuf { | ||||
|                 .next() | ||||
|                 .map(ToOwned::to_owned) | ||||
|                 .unwrap_or_else(|| String::from("png")); | ||||
|             file_path.push(format!("{}.{}", GUID::rand().to_string(), ext)); | ||||
|             file_path.push(format!("{}.{}", GUID::rand(), ext)); | ||||
|         }); | ||||
|     file_path | ||||
| } | ||||
|  | ||||
| @ -47,7 +47,11 @@ impl Mention { | ||||
| 
 | ||||
|     pub fn get_user(&self, conn: &Connection) -> Result<User> { | ||||
|         match self.get_post(conn) { | ||||
|             Ok(p) => Ok(p.get_authors(conn)?.into_iter().next()?), | ||||
|             Ok(p) => Ok(p | ||||
|                 .get_authors(conn)? | ||||
|                 .into_iter() | ||||
|                 .next() | ||||
|                 .ok_or(Error::NotFound)?), | ||||
|             Err(_) => self.get_comment(conn).and_then(|c| c.get_author(conn)), | ||||
|         } | ||||
|     } | ||||
| @ -77,7 +81,7 @@ impl Mention { | ||||
|         in_post: bool, | ||||
|         notify: bool, | ||||
|     ) -> Result<Self> { | ||||
|         let ap_url = ment.link_props.href_string().ok()?; | ||||
|         let ap_url = ment.link_props.href_string().or(Err(Error::NotFound))?; | ||||
|         let mentioned = User::find_by_ap_url(conn, &ap_url)?; | ||||
| 
 | ||||
|         if in_post { | ||||
|  | ||||
| @ -105,7 +105,8 @@ impl ImportedMigrations { | ||||
|     pub fn rerun_last_migration(&self, conn: &Connection, path: &Path) -> Result<()> { | ||||
|         let latest_migration = conn.latest_run_migration_version()?; | ||||
|         let id = latest_migration | ||||
|             .and_then(|m| self.0.binary_search_by_key(&m.as_str(), |m| m.name).ok())?; | ||||
|             .and_then(|m| self.0.binary_search_by_key(&m.as_str(), |m| m.name).ok()) | ||||
|             .ok_or(Error::NotFound)?; | ||||
|         let migration = &self.0[id]; | ||||
|         conn.transaction(|| { | ||||
|             migration.revert(conn, path)?; | ||||
|  | ||||
| @ -61,7 +61,7 @@ impl PasswordResetRequest { | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_and_delete_by_token(conn: &Connection, token: &str) -> Result<Self> { | ||||
|         let request = Self::find_by_token(&conn, &token)?; | ||||
|         let request = Self::find_by_token(conn, token)?; | ||||
| 
 | ||||
|         let filter = | ||||
|             password_reset_requests::table.filter(password_reset_requests::id.eq(request.id)); | ||||
|  | ||||
| @ -97,7 +97,7 @@ impl Post { | ||||
|     } | ||||
| 
 | ||||
|     pub fn delete(&self, conn: &Connection) -> Result<()> { | ||||
|         for m in Mention::list_for_post(&conn, self.id)? { | ||||
|         for m in Mention::list_for_post(conn, self.id)? { | ||||
|             m.delete(conn)?; | ||||
|         } | ||||
|         diesel::delete(self).execute(conn)?; | ||||
| @ -457,14 +457,14 @@ impl Post { | ||||
|             .filter_map(|(id, m)| id.map(|id| (m, id))) | ||||
|             .collect::<Vec<_>>(); | ||||
| 
 | ||||
|         let old_mentions = Mention::list_for_post(&conn, self.id)?; | ||||
|         let old_mentions = Mention::list_for_post(conn, self.id)?; | ||||
|         let old_user_mentioned = old_mentions | ||||
|             .iter() | ||||
|             .map(|m| m.mentioned_id) | ||||
|             .collect::<HashSet<_>>(); | ||||
|         for (m, id) in &mentions { | ||||
|             if !old_user_mentioned.contains(&id) { | ||||
|                 Mention::from_activity(&*conn, &m, self.id, true, true)?; | ||||
|             if !old_user_mentioned.contains(id) { | ||||
|                 Mention::from_activity(&*conn, m, self.id, true, true)?; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -476,7 +476,7 @@ impl Post { | ||||
|             .iter() | ||||
|             .filter(|m| !new_mentions.contains(&m.mentioned_id)) | ||||
|         { | ||||
|             m.delete(&conn)?; | ||||
|             m.delete(conn)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| @ -700,7 +700,7 @@ impl FromId<DbConn> for Post { | ||||
|                 Post::insert( | ||||
|                     conn, | ||||
|                     NewPost { | ||||
|                         blog_id: blog?.id, | ||||
|                         blog_id: blog.ok_or(Error::NotFound)?.id, | ||||
|                         slug: Self::slug(&title).to_string(), | ||||
|                         title, | ||||
|                         content: SafeString::new(&article.object_props.content_string()?), | ||||
|  | ||||
| @ -173,7 +173,7 @@ impl AsObject<User, Undo, &DbConn> for Reshare { | ||||
|             diesel::delete(&self).execute(&**conn)?; | ||||
| 
 | ||||
|             // delete associated notification if any
 | ||||
|             if let Ok(notif) = Notification::find(&conn, notification_kind::RESHARE, self.id) { | ||||
|             if let Ok(notif) = Notification::find(conn, notification_kind::RESHARE, self.id) { | ||||
|                 diesel::delete(¬if).execute(&**conn)?; | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -102,7 +102,7 @@ pub struct SafeString { | ||||
| impl SafeString { | ||||
|     pub fn new(value: &str) -> Self { | ||||
|         SafeString { | ||||
|             value: CLEAN.clean(&value).to_string(), | ||||
|             value: CLEAN.clean(value).to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -148,7 +148,7 @@ impl PlumeQuery { | ||||
| 
 | ||||
|     /// Parse a query string into this Query
 | ||||
|     pub fn parse_query(&mut self, query: &str) -> &mut Self { | ||||
|         self.from_str_req(&query.trim()) | ||||
|         self.from_str_req(query.trim()) | ||||
|     } | ||||
| 
 | ||||
|     /// Convert this Query to a Tantivy Query
 | ||||
| @ -360,7 +360,7 @@ impl std::str::FromStr for PlumeQuery { | ||||
|     fn from_str(query: &str) -> Result<PlumeQuery, !> { | ||||
|         let mut res: PlumeQuery = Default::default(); | ||||
| 
 | ||||
|         res.from_str_req(&query.trim()); | ||||
|         res.from_str_req(query.trim()); | ||||
|         Ok(res) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -18,12 +18,6 @@ pub enum QueryError { | ||||
|     RuntimeError(String), | ||||
| } | ||||
| 
 | ||||
| impl From<std::option::NoneError> for QueryError { | ||||
|     fn from(_: std::option::NoneError) -> Self { | ||||
|         QueryError::UnexpectedEndOfQuery | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub type QueryResult<T> = std::result::Result<T, QueryError>; | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
| @ -239,7 +233,7 @@ impl WithList { | ||||
|     ) -> Result<bool> { | ||||
|         match list { | ||||
|             List::List(name) => { | ||||
|                 let list = lists::List::find_for_user_by_name(conn, timeline.user_id, &name)?; | ||||
|                 let list = lists::List::find_for_user_by_name(conn, timeline.user_id, name)?; | ||||
|                 match (self, list.kind()) { | ||||
|                     (WithList::Blog, ListType::Blog) => list.contains_blog(conn, post.blog_id), | ||||
|                     (WithList::Author { boosts, likes }, ListType::User) => match kind { | ||||
| @ -414,7 +408,7 @@ enum List<'a> { | ||||
| 
 | ||||
| fn parse_s<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> { | ||||
|     let mut res = Vec::new(); | ||||
|     let (left, token) = parse_a(&stream)?; | ||||
|     let (left, token) = parse_a(stream)?; | ||||
|     res.push(token); | ||||
|     stream = left; | ||||
|     while !stream.is_empty() { | ||||
| @ -436,7 +430,7 @@ fn parse_s<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], | ||||
| 
 | ||||
| fn parse_a<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> { | ||||
|     let mut res = Vec::new(); | ||||
|     let (left, token) = parse_b(&stream)?; | ||||
|     let (left, token) = parse_b(stream)?; | ||||
|     res.push(token); | ||||
|     stream = left; | ||||
|     while !stream.is_empty() { | ||||
| @ -463,7 +457,7 @@ fn parse_b<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ< | ||||
|             match left.get(0) { | ||||
|                 Some(Token::RParent(_)) => Ok((&left[1..], token)), | ||||
|                 Some(t) => t.get_error(Token::RParent(0)), | ||||
|                 None => None?, | ||||
|                 None => Err(QueryError::UnexpectedEndOfQuery), | ||||
|             } | ||||
|         } | ||||
|         _ => parse_c(stream), | ||||
| @ -484,9 +478,13 @@ fn parse_c<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ< | ||||
| } | ||||
| 
 | ||||
| fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Arg<'a>)> { | ||||
|     match stream.get(0).map(Token::get_text)? { | ||||
|     match stream | ||||
|         .get(0) | ||||
|         .map(Token::get_text) | ||||
|         .ok_or(QueryError::UnexpectedEndOfQuery)? | ||||
|     { | ||||
|         s @ "blog" | s @ "author" | s @ "license" | s @ "tags" | s @ "lang" => { | ||||
|             match stream.get(1)? { | ||||
|             match stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)? { | ||||
|                 Token::Word(_, _, r#in) if r#in == &"in" => { | ||||
|                     let (mut left, list) = parse_l(&stream[2..])?; | ||||
|                     let kind = match s { | ||||
| @ -498,7 +496,12 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], | ||||
|                                 if *clude != "include" && *clude != "exclude" { | ||||
|                                     break; | ||||
|                                 } | ||||
|                                 match (*clude, left.get(1).map(Token::get_text)?) { | ||||
|                                 match ( | ||||
|                                     *clude, | ||||
|                                     left.get(1) | ||||
|                                         .map(Token::get_text) | ||||
|                                         .ok_or(QueryError::UnexpectedEndOfQuery)?, | ||||
|                                 ) { | ||||
|                                     ("include", "reshares") | ("include", "reshare") => { | ||||
|                                         boosts = true | ||||
|                                     } | ||||
| @ -529,7 +532,10 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], | ||||
|                 t => t.get_error(Token::Word(0, 0, "'in'")), | ||||
|             } | ||||
|         } | ||||
|         s @ "title" | s @ "subtitle" | s @ "content" => match (stream.get(1)?, stream.get(2)?) { | ||||
|         s @ "title" | s @ "subtitle" | s @ "content" => match ( | ||||
|             stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)?, | ||||
|             stream.get(2).ok_or(QueryError::UnexpectedEndOfQuery)?, | ||||
|         ) { | ||||
|             (Token::Word(_, _, contains), Token::Word(_, _, w)) if contains == &"contains" => Ok(( | ||||
|                 &stream[3..], | ||||
|                 Arg::Contains( | ||||
| @ -555,7 +561,13 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], | ||||
|                     if *clude != "include" && *clude != "exclude" { | ||||
|                         break; | ||||
|                     } | ||||
|                     match (*clude, stream.get(2).map(Token::get_text)?) { | ||||
|                     match ( | ||||
|                         *clude, | ||||
|                         stream | ||||
|                             .get(2) | ||||
|                             .map(Token::get_text) | ||||
|                             .ok_or(QueryError::UnexpectedEndOfQuery)?, | ||||
|                     ) { | ||||
|                         ("include", "reshares") | ("include", "reshare") => boosts = true, | ||||
|                         ("exclude", "reshares") | ("exclude", "reshare") => boosts = false, | ||||
|                         ("include", "likes") | ("include", "like") => likes = true, | ||||
| @ -577,20 +589,23 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], | ||||
|             "all" => Ok((&stream[1..], Arg::Boolean(Bool::All))), | ||||
|             _ => unreachable!(), | ||||
|         }, | ||||
|         _ => stream.get(0)?.get_error(Token::Word( | ||||
|             0, | ||||
|             0, | ||||
|             "one of 'blog', 'author', 'license', 'tags', 'lang', \ | ||||
|         _ => stream | ||||
|             .get(0) | ||||
|             .ok_or(QueryError::UnexpectedEndOfQuery)? | ||||
|             .get_error(Token::Word( | ||||
|                 0, | ||||
|                 0, | ||||
|                 "one of 'blog', 'author', 'license', 'tags', 'lang', \ | ||||
|              'title', 'subtitle', 'content', 'followed', 'has_cover', 'local' or 'all'",
 | ||||
|         )), | ||||
|             )), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn parse_l<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], List<'a>)> { | ||||
|     match stream.get(0)? { | ||||
|     match stream.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? { | ||||
|         Token::LBracket(_) => { | ||||
|             let (left, list) = parse_m(&stream[1..])?; | ||||
|             match left.get(0)? { | ||||
|             match left.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? { | ||||
|                 Token::RBracket(_) => Ok((&left[1..], List::Array(list))), | ||||
|                 t => t.get_error(Token::Word(0, 0, "one of ']' or ','")), | ||||
|             } | ||||
| @ -601,16 +616,20 @@ fn parse_l<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Lis | ||||
| } | ||||
| 
 | ||||
| fn parse_m<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Vec<&'a str>)> { | ||||
|     let mut res: Vec<&str> = vec![match stream.get(0)? { | ||||
|         Token::Word(_, _, w) => w, | ||||
|         t => return t.get_error(Token::Word(0, 0, "any word")), | ||||
|     }]; | ||||
|     stream = &stream[1..]; | ||||
|     while let Token::Comma(_) = stream[0] { | ||||
|         res.push(match stream.get(1)? { | ||||
|     let mut res: Vec<&str> = vec![ | ||||
|         match stream.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? { | ||||
|             Token::Word(_, _, w) => w, | ||||
|             t => return t.get_error(Token::Word(0, 0, "any word")), | ||||
|         }); | ||||
|         }, | ||||
|     ]; | ||||
|     stream = &stream[1..]; | ||||
|     while let Token::Comma(_) = stream[0] { | ||||
|         res.push( | ||||
|             match stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)? { | ||||
|                 Token::Word(_, _, w) => w, | ||||
|                 t => return t.get_error(Token::Word(0, 0, "any word")), | ||||
|             }, | ||||
|         ); | ||||
|         stream = &stream[2..]; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -210,7 +210,13 @@ impl User { | ||||
|             .into_iter() | ||||
|             .find(|l| l.mime_type == Some(String::from("application/activity+json"))) | ||||
|             .ok_or(Error::Webfinger)?; | ||||
|         User::from_id(conn, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e) | ||||
|         User::from_id( | ||||
|             conn, | ||||
|             link.href.as_ref().ok_or(Error::Webfinger)?, | ||||
|             None, | ||||
|             CONFIG.proxy(), | ||||
|         ) | ||||
|         .map_err(|(_, e)| e) | ||||
|     } | ||||
| 
 | ||||
|     pub fn fetch_remote_interact_uri(acct: &str) -> Result<String> { | ||||
| @ -258,7 +264,7 @@ impl User { | ||||
|                     .icon_image()? | ||||
|                     .object_props | ||||
|                     .url_string()?, | ||||
|                 &self, | ||||
|                 self, | ||||
|             ) | ||||
|             .ok(); | ||||
| 
 | ||||
| @ -427,12 +433,12 @@ impl User { | ||||
|         let last = &format!( | ||||
|             "{}?page={}", | ||||
|             &self.outbox_url, | ||||
|             self.get_activities_count(&conn) / i64::from(ITEMS_PER_PAGE) + 1 | ||||
|             self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1 | ||||
|         ); | ||||
|         coll.collection_props.set_first_link(Id::new(first))?; | ||||
|         coll.collection_props.set_last_link(Id::new(last))?; | ||||
|         coll.collection_props | ||||
|             .set_total_items_u64(self.get_activities_count(&conn) as u64)?; | ||||
|             .set_total_items_u64(self.get_activities_count(conn) as u64)?; | ||||
|         Ok(ActivityStream::new(coll)) | ||||
|     } | ||||
|     pub fn outbox_page( | ||||
| @ -441,7 +447,7 @@ impl User { | ||||
|         (min, max): (i32, i32), | ||||
|     ) -> Result<ActivityStream<OrderedCollectionPage>> { | ||||
|         let acts = self.get_activities_page(conn, (min, max))?; | ||||
|         let n_acts = self.get_activities_count(&conn); | ||||
|         let n_acts = self.get_activities_count(conn); | ||||
|         let mut coll = OrderedCollectionPage::default(); | ||||
|         if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) { | ||||
|             coll.collection_page_props.set_next_link(Id::new(&format!( | ||||
| @ -513,7 +519,7 @@ impl User { | ||||
|                 if page.is_empty() { | ||||
|                     break; | ||||
|                 } | ||||
|                 items.extend(page.drain(..)); | ||||
|                 items.append(&mut page); | ||||
|                 if let Some(n) = nxt { | ||||
|                     if n == next { | ||||
|                         break; | ||||
| @ -720,7 +726,7 @@ impl User { | ||||
| 
 | ||||
|     pub fn get_keypair(&self) -> Result<PKey<Private>> { | ||||
|         PKey::from_rsa(Rsa::private_key_from_pem( | ||||
|             self.private_key.clone()?.as_ref(), | ||||
|             self.private_key.clone().ok_or(Error::Signature)?.as_ref(), | ||||
|         )?) | ||||
|         .map_err(Error::from) | ||||
|     } | ||||
| @ -943,7 +949,7 @@ impl FromId<DbConn> for User { | ||||
| 
 | ||||
|     fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> { | ||||
|         let url = Url::parse(&acct.object.object_props.id_string()?)?; | ||||
|         let inst = url.host_str()?; | ||||
|         let inst = url.host_str().ok_or(Error::Url)?; | ||||
|         let instance = Instance::find_by_domain(conn, inst).or_else(|_| { | ||||
|             Instance::insert( | ||||
|                 conn, | ||||
| @ -1080,7 +1086,7 @@ impl Signer for User { | ||||
|         let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?; | ||||
|         let mut verifier = sign::Verifier::new(MessageDigest::sha256(), &key)?; | ||||
|         verifier.update(data.as_bytes())?; | ||||
|         verifier.verify(&signature).map_err(Error::from) | ||||
|         verifier.verify(signature).map_err(Error::from) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1121,7 +1127,7 @@ impl NewUser { | ||||
|                 display_name, | ||||
|                 role: role as i32, | ||||
|                 summary: summary.to_owned(), | ||||
|                 summary_html: SafeString::new(&utils::md_to_html(&summary, None, false, None).0), | ||||
|                 summary_html: SafeString::new(&utils::md_to_html(summary, None, false, None).0), | ||||
|                 email: Some(email), | ||||
|                 hashed_password: password, | ||||
|                 instance_id: instance.id, | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| nightly-2021-03-25 | ||||
| nightly-2021-11-27 | ||||
|  | ||||
| @ -25,7 +25,7 @@ parts: | ||||
|   plume: | ||||
|     plugin: rust | ||||
|     source: . | ||||
|     rust-revision: nightly-2021-03-25 | ||||
|     rust-revision: nightly-2021-11-27 | ||||
|     build-packages: | ||||
|       - libssl-dev | ||||
|       - pkg-config | ||||
|  | ||||
| @ -19,12 +19,6 @@ impl From<Error> for ApiError { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<std::option::NoneError> for ApiError { | ||||
|     fn from(err: std::option::NoneError) -> ApiError { | ||||
|         ApiError(err.into()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'r> Responder<'r> for ApiError { | ||||
|     fn respond_to(self, req: &Request<'_>) -> response::Result<'r> { | ||||
|         match self.0 { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use chrono::NaiveDateTime; | ||||
| use rocket_contrib::json::Json; | ||||
| 
 | ||||
| use crate::api::{authorization::*, Api}; | ||||
| use crate::api::{authorization::*, Api, ApiError}; | ||||
| use plume_api::posts::*; | ||||
| use plume_common::{activity_pub::broadcast, utils::md_to_html}; | ||||
| use plume_models::{ | ||||
| @ -121,14 +121,17 @@ pub fn create( | ||||
|         Some(Media::get_media_processor(&conn, vec![&author])), | ||||
|     ); | ||||
| 
 | ||||
|     let blog = payload.blog_id.or_else(|| { | ||||
|         let blogs = Blog::find_for_author(&conn, &author).ok()?; | ||||
|         if blogs.len() == 1 { | ||||
|             Some(blogs[0].id) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     })?; | ||||
|     let blog = payload | ||||
|         .blog_id | ||||
|         .or_else(|| { | ||||
|             let blogs = Blog::find_for_author(&conn, &author).ok()?; | ||||
|             if blogs.len() == 1 { | ||||
|                 Some(blogs[0].id) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }) | ||||
|         .ok_or(ApiError(Error::NotFound))?; | ||||
| 
 | ||||
|     if Post::find_by_slug(&conn, slug, blog).is_ok() { | ||||
|         return Err(Error::InvalidValue.into()); | ||||
|  | ||||
| @ -90,8 +90,8 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for SignedJson<T> { | ||||
|         o: Transformed<'a, Self>, | ||||
|     ) -> rocket::data::Outcome<Self, Self::Error> { | ||||
|         let string = o.borrowed()?; | ||||
|         match serde_json::from_str(&string) { | ||||
|             Ok(v) => Success(SignedJson(Digest::from_body(&string), Json(v))), | ||||
|         match serde_json::from_str(string) { | ||||
|             Ok(v) => Success(SignedJson(Digest::from_body(string), Json(v))), | ||||
|             Err(e) => { | ||||
|                 if e.is_data() { | ||||
|                     Failure((Status::UnprocessableEntity, JsonError::Parse(string, e))) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #![allow(clippy::too_many_arguments)] | ||||
| #![feature(decl_macro, proc_macro_hygiene, try_trait)] | ||||
| #![feature(decl_macro, proc_macro_hygiene)] | ||||
| 
 | ||||
| #[macro_use] | ||||
| extern crate gettext_macros; | ||||
|  | ||||
| @ -372,7 +372,7 @@ fn ban(id: i32, conn: &Connection, worker: &ScheduledThreadPool) -> Result<(), E | ||||
|         .unwrap_or(false) | ||||
|     { | ||||
|         BlocklistedEmail::insert( | ||||
|             &conn, | ||||
|             conn, | ||||
|             NewBlocklistedEmail { | ||||
|                 email_address: u.email.clone().unwrap(), | ||||
|                 note: "Banned".to_string(), | ||||
|  | ||||
| @ -77,12 +77,7 @@ pub fn upload( | ||||
|                     .map(|ext| format!(".{}", ext)) | ||||
|             }) | ||||
|             .unwrap_or_default(); | ||||
|         let dest = format!( | ||||
|             "{}/{}{}", | ||||
|             CONFIG.media_directory, | ||||
|             GUID::rand().to_string(), | ||||
|             ext | ||||
|         ); | ||||
|         let dest = format!("{}/{}{}", CONFIG.media_directory, GUID::rand(), ext); | ||||
| 
 | ||||
|         match fields["file"][0].data { | ||||
|             SavedData::Bytes(ref bytes) => fs::write(&dest, bytes) | ||||
|  | ||||
| @ -425,7 +425,7 @@ pub fn create( | ||||
|         Ok(_) => ValidationErrors::new(), | ||||
|         Err(e) => e, | ||||
|     }; | ||||
|     if Post::find_by_slug(&conn, &slug, blog.id).is_ok() { | ||||
|     if Post::find_by_slug(&conn, slug, blog.id).is_ok() { | ||||
|         errors.add( | ||||
|             "title", | ||||
|             ValidationError { | ||||
|  | ||||
| @ -54,7 +54,7 @@ pub fn search(query: Option<Form<SearchQuery>>, conn: DbConn, rockets: PlumeRock | ||||
|     let query = query.map(Form::into_inner).unwrap_or_default(); | ||||
|     let page = query.page.unwrap_or_default(); | ||||
|     let mut parsed_query = | ||||
|         Query::from_str(&query.q.as_deref().unwrap_or_default()).unwrap_or_default(); | ||||
|         Query::from_str(query.q.as_deref().unwrap_or_default()).unwrap_or_default(); | ||||
| 
 | ||||
|     param_to_query!(query, parsed_query; normal: title, subtitle, content, tag, | ||||
|               instance, author, blog, lang, license; | ||||
|  | ||||
| @ -553,14 +553,14 @@ pub fn ap_followers( | ||||
| #[get("/@/<name>/atom.xml")] | ||||
| pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> { | ||||
|     let conn = &conn; | ||||
|     let author = User::find_by_fqn(&conn, &name).ok()?; | ||||
|     let entries = Post::get_recents_for_author(&conn, &author, 15).ok()?; | ||||
|     let author = User::find_by_fqn(conn, &name).ok()?; | ||||
|     let entries = Post::get_recents_for_author(conn, &author, 15).ok()?; | ||||
|     let uri = Instance::get_local() | ||||
|         .ok()? | ||||
|         .compute_box("@", &name, "atom.xml"); | ||||
|     let title = &author.display_name; | ||||
|     let default_updated = &author.creation_date; | ||||
|     let feed = super::build_atom_feed(entries, &uri, title, default_updated, &conn); | ||||
|     let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn); | ||||
|     Some(Content( | ||||
|         ContentType::new("application", "atom+xml"), | ||||
|         feed.to_string(), | ||||
|  | ||||
| @ -41,7 +41,7 @@ impl IntoContext for (&DbConn, &PlumeRocket) { | ||||
|         Option<(String, String)>, | ||||
|     ) { | ||||
|         ( | ||||
|             &self.0, | ||||
|             self.0, | ||||
|             &self.1.intl.catalog, | ||||
|             self.1.user.clone(), | ||||
|             self.1.flash_msg.clone(), | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| 
 | ||||
| @(ctx: BaseContext, comment_tree: &CommentTree, in_reply_to: Option<&str>, blog: &str, slug: &str) | ||||
| 
 | ||||
| @if let Some(ref comm) = Some(&comment_tree.comment) { | ||||
| @if let Some(comm) = Some(&comment_tree.comment) { | ||||
| @if let Ok(author) = comm.get_author(ctx.0) { | ||||
| <div class="comment u-comment h-cite" id="comment-@comm.id"> | ||||
|     <main class="content"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user