use std::error::Error; use std::fs::File; use std::io::Read; use colored::Colorize; use pandoc::{PandocError, PandocOutput}; use reqwest::blocking::Client; use serde::Deserialize; use crate::config_spec::{CfgField, CfgGroup}; pub const CONFIG: CfgGroup<'static> = CfgGroup { name: "wiki", description: "API Settings for Mediawiki", fields: &[ CfgField::Default { key: "server-url", default: "https://wiki.berlin.ccc.de", description: "Server running the wiki.", }, CfgField::Default { key: "http-user", default: "cccb-wiki", description: "HTTP basic auth user name.", }, CfgField::Password { key: "http-password", description: "HTTP basic auth password." }, CfgField::Default { key: "api-user", default: "PlenumBot@PlenumBot-PW2", description: "API Username associated with the bot account used for edits.", }, CfgField::Password { key: "api-secret", description: "API secret / \"password\" used for authenticating as the bot.", }, ], }; pub struct Mediawiki { server_url: String, http_user: String, http_password: String, api_user: String, api_secret: String, is_dry_run: bool, login_token: String, csrf_token: String, client: Client, } impl std::fmt::Debug for Mediawiki { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Mediawiki") .field("server_url", &self.server_url) .field("http_user", &self.http_user) .field("http_password", &"*****") .field("is_dry_run", &self.is_dry_run) .field("client", &self.client) .finish() } } impl Mediawiki { pub fn new( server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, api_secret: &str, is_dry_run: bool, ) -> Self { Self { server_url: server_url.to_string(), http_user: http_auth_user.to_string(), http_password: http_auth_password.to_string(), api_user: api_user.to_string(), api_secret: api_secret.to_string(), is_dry_run, login_token: String::new(), csrf_token: String::new(), client: Client::builder().cookie_store(true).build().unwrap(), } } pub fn get_login_token(&self) -> Result> { let url = format!("{}/api.php?action=query&meta=tokens&type=login&format=json", self.server_url); let resp = self .client .get(url) //.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT .send()? .text()?; let response_deserialized: QueryResponse = serde_json::from_str(&resp)?; Ok(response_deserialized.query.tokens.logintoken) } pub fn login (&self) -> Result> { let url = format!("{}/api.php?action=login", self.server_url); let params = [ ("lgname", &self.api_user), ("lgpassword", &self.api_secret), ("lgtoken", &self.login_token) ]; let resp = self .client .post(url) .form(¶ms) .send()? .text()?; Ok(resp) } pub fn get_csrf_token(&self) -> Result> { // HAS TO BE FIXED // action=query&meta=tokens let url = format!("{}/api.php?", self.server_url); let params = [ ("format", "json"), ("meta", "tokens"), ("formatversion", "2"), ("lgtoken", &self.login_token) ]; let resp = self .client .get(url) //.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT .query(¶ms) .send()? .text()?; println!(" --- \n{}\n ---", resp.green()); let response_deserialized: QueryResponse = serde_json::from_str(&resp)?; Ok(response_deserialized.query.tokens.logintoken) } } #[derive(Deserialize)] struct QueryResponse { batchcomplete: String, query: QueryTokens, } #[derive(Deserialize)] struct QueryTokens { tokens: Tokens, } #[derive(Deserialize)] struct Tokens { logintoken: String, } pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box> { convert_md_to_mediawiki(old_pad_content); let auth_result = wiki.get_login_token().unwrap(); wiki.login_token = auth_result.clone(); println!("AUTH Success"); let login_result = wiki.login()?; println!("LOGIN Success"); let csrf_token = wiki.get_csrf_token(); println!("CSRF Success"); //wiki.csrf_token.clone_from(&csrf_token); HAS TO BE FIXED println!("---AUTH RESULT:---\n{}\n---LOGIN RESULT:---\n{:?}\n---CSRF RESULT:---\n\n-----------", auth_result, login_result,/* csrf_token*/); // Textdatei wieder einlesen // Passwörter aus Datenbank lesen (ToBeDone) /* let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1"); let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**"); let login_token = login_to_mediawiki(plenum_bot_user.clone(), plenum_bot_pw.clone()) .expect("Fehler beim Einloggen!"); println!("plenum_bot_user: {plenum_bot_user}, plenum_bot_pw: {plenum_bot_pw}, login_token: {login_token}") */ Ok(()) } /// Converts one file type into another using pandoc and saves the result as a txt file fn pandoc_convert( old_pad_content: String, output_filepath: &str, input_format: pandoc::InputFormat, output_format: pandoc::OutputFormat, ) -> Result { //Convert Markdown into Mediawiki // Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight let mut p = pandoc::new(); p.set_input(pandoc::InputKind::Pipe(old_pad_content)); p.set_input_format(input_format, vec![]); p.set_output(pandoc::OutputKind::File(output_filepath.parse().unwrap())); p.set_output_format(output_format, vec![]); p.execute() } /// Reads a text file from a specified path and returns it as a String fn read_txt_file(filepath: &str) -> String { let mut file = File::open(filepath) .expect(&*format!("Fehler beim öffnen der Textdatei mit Pfad {filepath}!")); let mut contents = String::new(); file.read_to_string(&mut contents) .expect("Fehler beim auslesen der MediaWiki-Textdatei!") .to_string() } /// Takes a Sting in the Markdown format and returns a String in the mediawiki Format fn convert_md_to_mediawiki(old_pad_content: String) -> String { // TODO: use tempfile="3.3", make it a NamedTempFile::new()?; // or alternatively use piped stdout to avoid files entirely let output_filepath: &str = "./pandoc_mediawiki.txt"; pandoc_convert( old_pad_content, output_filepath, pandoc::InputFormat::Markdown, pandoc::OutputFormat::MediaWiki, ) .expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); read_txt_file(output_filepath) }