Compare commits

...

6 Commits

Author SHA1 Message Date
grimhilt
c6cf8a9730 update readme to show auth 2023-10-21 22:29:00 +02:00
grimhilt
f6db6992a0 working login system 2023-10-21 22:27:34 +02:00
grimhilt
908ead5b11 change name of functions 2023-10-21 21:48:21 +02:00
grimhilt
9ea1d01c27 add trait ApiCall 2023-10-21 21:47:48 +02:00
grimhilt
07f6405b26 test login 2023-10-21 19:54:11 +02:00
grimhilt
dadf00f4a5 add import necessary for test 2023-09-12 15:48:37 +02:00
27 changed files with 413 additions and 185 deletions

22
Cargo.lock generated
View File

@ -641,6 +641,7 @@ dependencies = [
"md5", "md5",
"regex", "regex",
"reqwest", "reqwest",
"rpassword",
"rust-crypto", "rust-crypto",
"textwrap 0.13.4", "textwrap 0.13.4",
"tokio", "tokio",
@ -915,6 +916,27 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "rpassword"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
dependencies = [
"libc",
"rtoolbox",
"winapi",
]
[[package]]
name = "rtoolbox"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "rust-crypto" name = "rust-crypto"
version = "0.2.36" version = "0.2.36"

View File

@ -21,6 +21,7 @@ chrono = "0.4.26"
indicatif = "0.17.5" indicatif = "0.17.5"
md5 = "0.7.0" md5 = "0.7.0"
futures-util = "0.3.28" futures-util = "0.3.28"
rpassword = "7.2"
[profile.release] [profile.release]
debug = true debug = true

View File

@ -13,13 +13,12 @@ This should work pretty much like git with some adaptations to be more debuggabl
- [x] Pushing updates (new, deleted, modified) - [x] Pushing updates (new, deleted, modified)
- [x] Using a .nextsyncignore to ignore files - [x] Using a .nextsyncignore to ignore files
- [ ] Pulling changes - [ ] Pulling changes
- [ ] Auth without using env variables - [x] Auth with a token
- [ ] Remember token
- [ ] Various optimisation - [ ] Various optimisation
## Usage ## Usage
For the authentification, I use env variables (USERNAME and PASSWORD), this is temporary.
``` ```
USAGE: USAGE:
nextsync [SUBCOMMAND] nextsync [SUBCOMMAND]

View File

@ -10,6 +10,7 @@ use crate::utils::path::path_buf_to_string;
use crate::utils::remote::{enumerate_remote, EnumerateOptions}; use crate::utils::remote::{enumerate_remote, EnumerateOptions};
use crate::global::global::{DIR_PATH, set_dir_path}; use crate::global::global::{DIR_PATH, set_dir_path};
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::{ReqProps, ObjProps}; use crate::services::req_props::{ReqProps, ObjProps};
use crate::store::object::{tree, blob::Blob}; use crate::store::object::{tree, blob::Blob};
use crate::commands::config; use crate::commands::config;

View File

@ -2,6 +2,7 @@ use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::r#copy::Copy; use crate::services::r#copy::Copy;
use crate::services::api_call::ApiCall;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::commands::status::LocalObj; use crate::commands::status::LocalObj;
use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState}; use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState};
@ -26,10 +27,10 @@ impl PushChange for Copied {
fn push(&self) -> io::Result<()> { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = Copy::new() let res = Copy::new()
.set_url( .set_url_copy(
&path_buf_to_string(obj.path_from.clone().unwrap()), &path_buf_to_string(obj.path_from.clone().unwrap()),
obj.path.to_str().unwrap()) obj.path.to_str().unwrap())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::delete_path::DeletePath; use crate::services::delete_path::DeletePath;
use crate::store::index; use crate::store::index;
use crate::store::object::blob::Blob; use crate::store::object::blob::Blob;
@ -26,7 +27,7 @@ impl PushChange for Deleted {
let obj = &self.obj; let obj = &self.obj;
let res = DeletePath::new() let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::services::upload_file::UploadFile; use crate::services::upload_file::UploadFile;
use crate::commands::status::LocalObj; use crate::commands::status::LocalObj;
@ -27,7 +28,7 @@ impl PushChange for Modified {
let res = UploadFile::new() let res = UploadFile::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
.set_file(obj.path.clone()) .set_file(obj.path.clone())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::r#move::Move; use crate::services::r#move::Move;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::commands::status::LocalObj; use crate::commands::status::LocalObj;
@ -26,10 +27,10 @@ impl PushChange for Moved {
fn push(&self) -> io::Result<()> { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = Move::new() let res = Move::new()
.set_url( .set_url_move(
&path_buf_to_string(obj.path_from.clone().unwrap()), &path_buf_to_string(obj.path_from.clone().unwrap()),
obj.path.to_str().unwrap()) obj.path.to_str().unwrap())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::services::upload_file::UploadFile; use crate::services::upload_file::UploadFile;
use crate::store::object::blob::Blob; use crate::store::object::blob::Blob;
@ -27,7 +28,7 @@ impl PushChange for New {
let res = UploadFile::new() let res = UploadFile::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
.set_file(obj.path.clone()) .set_file(obj.path.clone())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::services::create_folder::CreateFolder; use crate::services::create_folder::CreateFolder;
use crate::store::index; use crate::store::index;
@ -33,7 +34,7 @@ impl PushChange for NewDir {
let obj = &self.obj; let obj = &self.obj;
let res = CreateFolder::new() let res = CreateFolder::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -2,6 +2,7 @@ use std::path::PathBuf;
use std::io; use std::io;
use crate::commands::status::{State, LocalObj}; use crate::commands::status::{State, LocalObj};
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::commands::push::new::New; use crate::commands::push::new::New;
use crate::commands::push::new_dir::NewDir; use crate::commands::push::new_dir::NewDir;

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io; use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::delete_path::DeletePath; use crate::services::delete_path::DeletePath;
use crate::store::index; use crate::store::index;
use crate::store::object::tree; use crate::store::object::tree;
@ -32,7 +33,7 @@ impl PushChange for RmDir {
let obj = &self.obj; let obj = &self.obj;
let res = DeletePath::new() let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
.send_with_err(); .send();
match res { match res {
Err(ApiError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {

View File

@ -1,4 +1,5 @@
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::{ReqProps, ObjProps}; use crate::services::req_props::{ReqProps, ObjProps};
use crate::store::object::Object; use crate::store::object::Object;
use crate::utils::api::{ApiProps, get_api_props}; use crate::utils::api::{ApiProps, get_api_props};

View File

@ -7,4 +7,8 @@ pub mod delete_path;
pub mod downloader; pub mod downloader;
pub mod r#move; pub mod r#move;
pub mod r#copy; pub mod r#copy;
pub mod login;
pub mod request_manager;
pub mod api_call;
//pub mod auth;
//pub mod bulk_upload; //pub mod bulk_upload;

View File

@ -1,4 +1,7 @@
use std::env; use std::env;
use lazy_static::lazy_static;
use std::sync::Mutex;
use std::path::PathBuf;
use dotenv::dotenv; use dotenv::dotenv;
use reqwest::Client; use reqwest::Client;
use reqwest::RequestBuilder; use reqwest::RequestBuilder;
@ -8,6 +11,11 @@ use reqwest::header::{HeaderValue, CONTENT_TYPE, HeaderMap, IntoHeaderName};
use crate::utils::api::ApiProps; use crate::utils::api::ApiProps;
use crate::commands::config; use crate::commands::config;
use crate::commands::clone::get_url_props; use crate::commands::clone::get_url_props;
use crate::services::request_manager::get_request_manager;
lazy_static! {
static ref HTTP_TOKEN: Mutex<String> = Mutex::new(String::new());
}
#[derive(Debug)] #[derive(Debug)]
pub enum ApiError { pub enum ApiError {
@ -20,7 +28,9 @@ pub enum ApiError {
pub struct ApiBuilder { pub struct ApiBuilder {
client: Client, client: Client,
request: Option<RequestBuilder>, request: Option<RequestBuilder>,
headers: Option<HeaderMap> headers: Option<HeaderMap>,
auth_set: bool,
host: Option<String>,
} }
impl ApiBuilder { impl ApiBuilder {
@ -29,24 +39,14 @@ impl ApiBuilder {
client: Client::new(), client: Client::new(),
request: None, request: None,
headers: None, headers: None,
auth_set: false,
host: None,
} }
} }
pub fn set_url(&mut self, method: Method, url: &str) -> &mut ApiBuilder { pub fn set_url(&mut self, method: Method, url: &str) -> &mut ApiBuilder {
let remote = match config::get("remote") { self.request = Some(self.client.request(method, url));
Some(r) => r,
None => {
eprintln!("fatal: unable to find a remote");
std::process::exit(1);
}
};
let (host, _, _) = get_url_props(&remote);
let mut u = String::from(host);
u.push_str(url);
self.request = Some(self.client.request(method, u));
self self
} }
pub fn build_request(&mut self, method: Method, path: &str) -> &mut ApiBuilder { pub fn build_request(&mut self, method: Method, path: &str) -> &mut ApiBuilder {
@ -58,6 +58,7 @@ impl ApiBuilder {
} }
}; };
let (host, username, root) = get_url_props(&remote); let (host, username, root) = get_url_props(&remote);
self.host = Some(host.clone());
let mut url = String::from(host); let mut url = String::from(host);
url.push_str("/remote.php/dav/files/"); url.push_str("/remote.php/dav/files/");
url.push_str(username.unwrap()); url.push_str(username.unwrap());
@ -71,6 +72,7 @@ impl ApiBuilder {
} }
pub fn set_req(&mut self, meth: Method, p: &str, api_props: &ApiProps) -> &mut ApiBuilder { pub fn set_req(&mut self, meth: Method, p: &str, api_props: &ApiProps) -> &mut ApiBuilder {
self.host = Some(api_props.clone().host.clone());
let mut url = String::from(&api_props.host); let mut url = String::from(&api_props.host);
url.push_str("/remote.php/dav/files/"); url.push_str("/remote.php/dav/files/");
url.push_str("/"); url.push_str("/");
@ -84,23 +86,33 @@ impl ApiBuilder {
self self
} }
fn set_auth(&mut self) -> &mut ApiBuilder { pub fn set_basic_auth(&mut self, login: String, pwd: String) -> &mut ApiBuilder {
// todo if not exist
dotenv().ok();
let password = env::var("PASSWORD").unwrap();
let username = env::var("USERNAME").unwrap();
match self.request.take() { match self.request.take() {
None => { None => {
eprintln!("fatal: incorrect request"); eprintln!("fatal: incorrect request");
std::process::exit(1); std::process::exit(1);
}, },
Some(req) => { Some(req) => {
self.request = Some(req.basic_auth(username, Some(password))); self.request = Some(req.basic_auth(login, Some(pwd)));
} }
} }
self.auth_set = true;
self self
} }
pub fn set_token(&mut self, token: String) {
match self.request.take() {
None => {
eprintln!("fatal: incorrect request");
std::process::exit(1);
},
Some(req) => {
self.request = Some(req.bearer_auth(token));
}
}
self.auth_set = true;
}
pub fn set_xml(&mut self, xml_payload: String) -> &mut ApiBuilder { pub fn set_xml(&mut self, xml_payload: String) -> &mut ApiBuilder {
match self.request.take() { match self.request.take() {
None => { None => {
@ -148,8 +160,71 @@ impl ApiBuilder {
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> { fn set_request_manager(&mut self) {
self.set_auth(); let mut request_manager = get_request_manager().lock().unwrap();
let request_manager = request_manager.as_mut().unwrap();
if !self.host.is_none()
{
request_manager.set_host(self.host.clone().unwrap().replace("https://", ""));
}
if !self.auth_set {
self.set_token(request_manager.get_token());
//self.set_auth();
}
}
pub fn send(&mut self, need_text: bool) -> Result<Option<String>, ApiError> {
if !self.host.is_none() || !self.auth_set {
self.set_request_manager();
}
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
match self.request.take() {
None => {
eprintln!("fatal: incorrect request");
std::process::exit(1);
},
Some(req) => {
if let Some(headers) = &self.headers {
req.headers(headers.clone())
.send().await
} else {
req.send().await
}
},
}
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
if need_text {
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(|err| ApiError::EmptyError(err))?;
Ok(Some(body))
} else {
Ok(None)
}
} else {
Err(ApiError::IncorrectRequest(res))
}
}
pub async fn old_send(&mut self) -> Result<Response, Error> {
let mut request_manager = get_request_manager().lock().unwrap();
let request_manager = request_manager.as_mut().unwrap();
if !self.host.is_none()
{
request_manager.set_host(self.host.clone().unwrap());
}
if !self.auth_set {
//self.set_auth();
self.set_token(request_manager.get_token());
}
match self.request.take() { match self.request.take() {
None => { None => {
eprintln!("fatal: incorrect request"); eprintln!("fatal: incorrect request");

13
src/services/api_call.rs Normal file
View File

@ -0,0 +1,13 @@
use crate::services::api::ApiError;
pub trait ApiCall {
fn new() -> Self where Self: Sized {
unimplemented!()
}
fn set_url(&mut self, url: &str) -> &mut Self {
self
}
fn send(&mut self) -> Result<Option<String>, ApiError> {
unimplemented!()
}
}

View File

@ -1,2 +0,0 @@

View File

@ -1,20 +1,27 @@
use reqwest::{Method, Response, Error, header::HeaderValue}; use reqwest::{Method, header::HeaderValue};
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::clone::get_url_props; use crate::clone::get_url_props;
use crate::commands::config; use crate::commands::config;
use crate::services::api_call::ApiCall;
pub struct Copy { pub struct Copy {
api_builder: ApiBuilder, api_builder: ApiBuilder,
} }
impl Copy { impl ApiCall for Copy {
pub fn new() -> Self { fn new() -> Self {
Copy { Copy {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
} }
} }
pub fn set_url(&mut self, url: &str, destination: &str) -> &mut Copy { fn send(&mut self) -> Result<Option<String>, ApiError> {
self.api_builder.send(true)
}
}
impl Copy {
pub fn set_url_copy(&mut self, url: &str, destination: &str) -> &mut Copy {
self.api_builder.build_request(Method::from_bytes(b"COPY").unwrap(), url); self.api_builder.build_request(Method::from_bytes(b"COPY").unwrap(), url);
let remote = match config::get("remote") { let remote = match config::get("remote") {
@ -37,25 +44,10 @@ impl Copy {
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> {
self.api_builder.send().await
}
pub fn _overwrite(&mut self, overwrite: bool) -> &mut Copy { pub fn _overwrite(&mut self, overwrite: bool) -> &mut Copy {
self.api_builder.set_header("Overwrite", HeaderValue::from_str({ self.api_builder.set_header("Overwrite", HeaderValue::from_str({
if overwrite { "T" } else { "F" } if overwrite { "T" } else { "F" }
}).unwrap()); }).unwrap());
self self
} }
pub fn send_with_err(&mut self) -> Result<(), ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
Ok(())
} else {
Err(ApiError::IncorrectRequest(res))
}
}
} }

View File

@ -1,34 +1,24 @@
use reqwest::{Method, Response, Error}; use reqwest::Method;
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::services::api_call::ApiCall;
pub struct CreateFolder { pub struct CreateFolder {
api_builder: ApiBuilder, api_builder: ApiBuilder,
} }
impl CreateFolder { impl ApiCall for CreateFolder {
pub fn new() -> Self { fn new() -> Self {
CreateFolder { CreateFolder {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
} }
} }
pub fn set_url(&mut self, url: &str) -> &mut CreateFolder { fn set_url(&mut self, url: &str) -> &mut CreateFolder {
self.api_builder.build_request(Method::from_bytes(b"MKCOL").unwrap(), url); self.api_builder.build_request(Method::from_bytes(b"MKCOL").unwrap(), url);
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> { fn send(&mut self) -> Result<Option<String>, ApiError> {
self.api_builder.send().await self.api_builder.send(false)
}
pub fn send_with_err(&mut self) -> Result<(), ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
Ok(())
} else {
Err(ApiError::IncorrectRequest(res))
}
} }
} }

View File

@ -1,38 +1,24 @@
use reqwest::{Method, Response, Error}; use reqwest::Method;
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::services::api_call::ApiCall;
pub struct DeletePath { pub struct DeletePath {
api_builder: ApiBuilder, api_builder: ApiBuilder,
} }
impl DeletePath { impl ApiCall for DeletePath {
pub fn new() -> Self { fn new() -> Self {
DeletePath { DeletePath {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
} }
} }
pub fn set_url(&mut self, url: &str) -> &mut DeletePath { fn set_url(&mut self, url: &str) -> &mut DeletePath {
self.api_builder.build_request(Method::DELETE, url); self.api_builder.build_request(Method::DELETE, url);
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> { fn send(&mut self) -> Result<Option<String>, ApiError> {
self.api_builder.send().await self.api_builder.send(true)
}
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))
}
} }
} }

View File

@ -6,38 +6,32 @@ use std::io::{self, Write};
use reqwest::{Method, Response, Error}; use reqwest::{Method, Response, Error};
use crate::utils::api::ApiProps; use crate::utils::api::ApiProps;
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::services::api_call::ApiCall;
pub struct DownloadFiles { pub struct DownloadFiles {
api_builder: ApiBuilder, api_builder: ApiBuilder,
relative_ps: String, relative_ps: String,
} }
impl DownloadFiles { impl ApiCall for DownloadFiles {
pub fn new() -> Self { fn new() -> Self {
DownloadFiles { DownloadFiles {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
relative_ps: String::new(), relative_ps: String::new(),
} }
} }
}
pub fn set_url(&mut self, relative_ps: &str, api_props: &ApiProps) -> &mut DownloadFiles { impl DownloadFiles {
// todo make it beautiful
pub fn set_url_download(&mut self, relative_ps: &str, api_props: &ApiProps) -> &mut DownloadFiles {
self.relative_ps = relative_ps.to_string(); self.relative_ps = relative_ps.to_string();
self.api_builder.set_req(Method::GET, relative_ps, api_props); self.api_builder.set_req(Method::GET, relative_ps, api_props);
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> { pub async fn send_download(&mut self) -> Result<Response, Error> {
self.api_builder.send().await self.api_builder.old_send().await
}
pub async fn _send_with_err(mut self) -> Result<Vec<u8>, ApiError> {
let res = self.send().await.map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
Ok(body.to_vec())
} else {
Err(ApiError::IncorrectRequest(res))
}
} }
pub fn save_stream(&mut self, ref_p: PathBuf, callback: Option<impl Fn(u64)>) -> Result<(), ApiError> { pub fn save_stream(&mut self, ref_p: PathBuf, callback: Option<impl Fn(u64)>) -> Result<(), ApiError> {
@ -45,7 +39,7 @@ impl DownloadFiles {
let mut file = File::create(abs_p).unwrap(); let mut file = File::create(abs_p).unwrap();
tokio::runtime::Runtime::new().unwrap().block_on(async { tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = self.send().await.map_err(ApiError::RequestError)?; let res = self.send_download().await.map_err(ApiError::RequestError)?;
if res.status().is_success() { if res.status().is_success() {
let mut stream = res.bytes_stream(); let mut stream = res.bytes_stream();
@ -70,7 +64,7 @@ impl DownloadFiles {
pub fn save(&mut self, ref_p: PathBuf) -> Result<(), ApiError> { pub fn save(&mut self, ref_p: PathBuf) -> Result<(), ApiError> {
tokio::runtime::Runtime::new().unwrap().block_on(async { tokio::runtime::Runtime::new().unwrap().block_on(async {
let p = ref_p.join(PathBuf::from(self.relative_ps.clone())); let p = ref_p.join(PathBuf::from(self.relative_ps.clone()));
let res = self.send().await.map_err(ApiError::RequestError)?; let res = self.send_download().await.map_err(ApiError::RequestError)?;
if res.status().is_success() { if res.status().is_success() {
let body = res.bytes().await.map_err(ApiError::EmptyError)?; let body = res.bytes().await.map_err(ApiError::EmptyError)?;
match Self::write_file(p, &body.to_vec()) { match Self::write_file(p, &body.to_vec()) {

View File

@ -2,6 +2,7 @@ use std::path::PathBuf;
use indicatif::{ProgressBar, MultiProgress, ProgressStyle, HumanBytes}; use indicatif::{ProgressBar, MultiProgress, ProgressStyle, HumanBytes};
use crate::utils::api::ApiProps; use crate::utils::api::ApiProps;
use crate::services::api_call::ApiCall;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::download_files::DownloadFiles; use crate::services::download_files::DownloadFiles;
use crate::services::req_props::ObjProps; use crate::services::req_props::ObjProps;
@ -106,7 +107,7 @@ impl Downloader {
for file in self.files.clone() { for file in self.files.clone() {
let relative_s = &file.clone().relative_s.unwrap(); let relative_s = &file.clone().relative_s.unwrap();
let mut download = DownloadFiles::new(); let mut download = DownloadFiles::new();
download.set_url(&relative_s, &self.api_props.clone().unwrap()); download.set_url_download(&relative_s, &self.api_props.clone().unwrap());
let should_use_stream = { let should_use_stream = {
if let Some(size) = file.contentlength { if let Some(size) = file.contentlength {

102
src/services/login.rs Normal file
View File

@ -0,0 +1,102 @@
use std::io;
use std::io::Cursor;
use std::io::prelude::*;
use xml::reader::{EventReader, XmlEvent};
use reqwest::{header::HeaderValue, Method};
use rpassword;
use crate::services::api_call::ApiCall;
use crate::services::api::{ApiBuilder, ApiError};
pub struct Login {
api_builder: ApiBuilder,
login: String,
password: String,
host: Option<String>,
}
impl ApiCall for Login {
fn new() -> Self {
Login {
api_builder: ApiBuilder::new(),
login: String::new(),
password: String::new(),
host: None,
}
}
fn send(&mut self) -> Result<Option<String>, ApiError> {
let url = match self.host.clone() {
Some(h) => {
let mut u = String::from("https://");
u.push_str(&h);
u.push_str("/ocs/v2.php/core/getapppassword");
u
},
None => "/ocs/v2.php/core/getapppassword".to_owned(),
};
dbg!(url.clone());
self.api_builder.set_url(Method::GET, &url);
self.api_builder.set_header("OCS-APIRequest", HeaderValue::from_str("true").unwrap());
self.api_builder.set_basic_auth(self.login.clone(), self.password.clone());
self.api_builder.send(true)
}
}
impl Login {
pub fn ask_auth(&mut self) -> &mut Login {
println!("Please enter your username/email: ");
let stdin = io::stdin();
self.login = stdin.lock().lines().next().unwrap().unwrap();
println!("Please enter your password: ");
self.password = rpassword::read_password().unwrap();
self
}
pub fn set_host(&mut self, host: Option<String>) -> &mut Login {
self.host = host;
self
}
pub fn send_login(&mut self) -> Result<String, ApiError> {
match self.send() {
Ok(Some(body)) => Ok(self.parse(body)),
Ok(None) => Err(ApiError::Unexpected(String::from("Empty after tested"))),
Err(err) => Err(err),
}
}
fn parse(&self, xml: String) -> String {
let cursor = Cursor::new(xml);
let parser = EventReader::new(cursor);
let mut should_get = false;
for event in parser {
match event {
Ok(XmlEvent::StartElement { name, .. }) => {
should_get = {
if &name.local_name == "apppassword" {
true
} else {
false
}
};
}
Ok(XmlEvent::Characters(text)) => {
if !text.trim().is_empty() && should_get {
return text.clone();
}
}
Ok(XmlEvent::EndElement { name, .. }) => {
}
Err(e) => {
eprintln!("err: parsing xml: {}", e);
break;
}
_ => {}
}
}
String::new()
}
}

View File

@ -1,20 +1,28 @@
use reqwest::{Method, Response, Error, header::HeaderValue}; use reqwest::{Method, header::HeaderValue};
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::clone::get_url_props; use crate::clone::get_url_props;
use crate::commands::config; use crate::commands::config;
use crate::services::api_call::ApiCall;
pub struct Move { pub struct Move {
api_builder: ApiBuilder, api_builder: ApiBuilder,
} }
impl Move { impl ApiCall for Move {
pub fn new() -> Self { fn new() -> Self {
Move { Move {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
} }
} }
pub fn set_url(&mut self, url: &str, destination: &str) -> &mut Move {
fn send(&mut self) -> Result<Option<String>, ApiError> {
self.api_builder.send(false)
}
}
impl Move {
pub fn set_url_move(&mut self, url: &str, destination: &str) -> &mut Move {
self.api_builder.build_request(Method::from_bytes(b"MOVE").unwrap(), url); self.api_builder.build_request(Method::from_bytes(b"MOVE").unwrap(), url);
let remote = match config::get("remote") { let remote = match config::get("remote") {
@ -37,25 +45,10 @@ impl Move {
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> {
self.api_builder.send().await
}
pub fn _overwrite(&mut self, overwrite: bool) -> &mut Move { pub fn _overwrite(&mut self, overwrite: bool) -> &mut Move {
self.api_builder.set_header("Overwrite", HeaderValue::from_str({ self.api_builder.set_header("Overwrite", HeaderValue::from_str({
if overwrite { "T" } else { "F" } if overwrite { "T" } else { "F" }
}).unwrap()); }).unwrap());
self self
} }
pub fn send_with_err(&mut self) -> Result<(), ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
Ok(())
} else {
Err(ApiError::IncorrectRequest(res))
}
}
} }

View File

@ -1,6 +1,6 @@
use std::io::Cursor; use std::io::Cursor;
use chrono::{Utc, DateTime}; use chrono::{Utc, DateTime};
use reqwest::{Method, Response, Error}; use reqwest::Method;
use xml::reader::{EventReader, XmlEvent}; use xml::reader::{EventReader, XmlEvent};
use reqwest::header::HeaderValue; use reqwest::header::HeaderValue;
use crate::commands::clone::get_url_props; use crate::commands::clone::get_url_props;
@ -8,6 +8,7 @@ use crate::commands::config;
use crate::utils::time::parse_timestamp; use crate::utils::time::parse_timestamp;
use crate::utils::api::{get_relative_s, ApiProps}; use crate::utils::api::{get_relative_s, ApiProps};
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::services::api_call::ApiCall;
#[derive(Debug)] #[derive(Debug)]
pub struct ObjProps { pub struct ObjProps {
@ -55,8 +56,8 @@ pub struct ReqProps {
api_props: Option<ApiProps> api_props: Option<ApiProps>
} }
impl ReqProps { impl ApiCall for ReqProps {
pub fn new() -> Self { fn new() -> Self {
ReqProps { ReqProps {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
xml_balises: vec![], xml_balises: vec![],
@ -65,7 +66,7 @@ impl ReqProps {
} }
} }
pub fn set_url(&mut self, url: &str) -> &mut ReqProps { fn set_url(&mut self, url: &str) -> &mut ReqProps {
let remote = match config::get("remote") { let remote = match config::get("remote") {
Some(r) => r, Some(r) => r,
None => { None => {
@ -83,6 +84,13 @@ impl ReqProps {
self self
} }
fn send(&mut self) -> Result<Option<String>, ApiError> {
self.validate_xml();
self.api_builder.send(true)
}
}
impl ReqProps {
pub fn set_request(&mut self, p: &str, api_props: &ApiProps) -> &mut ReqProps { pub fn set_request(&mut self, p: &str, api_props: &ApiProps) -> &mut ReqProps {
self.api_props = Some(api_props.clone()); self.api_props = Some(api_props.clone());
self.api_builder.set_req(Method::from_bytes(b"PROPFIND").unwrap(), p, api_props); self.api_builder.set_req(Method::from_bytes(b"PROPFIND").unwrap(), p, api_props);
@ -145,32 +153,10 @@ impl ReqProps {
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> {
self.validate_xml();
self.api_builder.send().await
}
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
tokio::runtime::Runtime::new().unwrap().block_on(async {
match self.send().await {
Err(res) => Err(ApiError::RequestError(res)),
Ok(res) if res.status().is_success() => {
let body = res
.text()
.await
.map_err(|err| ApiError::EmptyError(err))?;
Ok(body)
},
Ok(res) => {
Err(ApiError::IncorrectRequest(res))
}
}
})
}
pub fn send_req_multiple(&mut self) -> Result<Vec<ObjProps>, ApiError> { pub fn send_req_multiple(&mut self) -> Result<Vec<ObjProps>, ApiError> {
match self.send_with_err() { match self.send() {
Ok(body) => Ok(self.parse(body, true)), Ok(Some(body)) => Ok(self.parse(body, true)),
Ok(None) => Err(ApiError::Unexpected(String::from("Empty after tested"))),
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
@ -178,12 +164,13 @@ impl ReqProps {
pub fn send_req_single(&mut self) -> Result<ObjProps, ApiError> { pub fn send_req_single(&mut self) -> Result<ObjProps, ApiError> {
// set depth to 0 as we only need one element // set depth to 0 as we only need one element
self.set_depth("0"); self.set_depth("0");
match self.send_with_err() { match self.send() {
Ok(body) => { Ok(Some(body)) => {
let objs = self.parse(body, false); let objs = self.parse(body, false);
let obj = objs[0].clone(); let obj = objs[0].clone();
Ok(obj) Ok(obj)
}, },
Ok(None) => Err(ApiError::Unexpected(String::from("Empty after tested"))),
Err(err) => Err(err), Err(err) => Err(err),
} }
} }

View File

@ -0,0 +1,73 @@
use lazy_static::lazy_static;
use std::sync::Mutex;
use crate::services::login::Login;
use crate::commands::config;
use crate::commands::clone::get_url_props;
use crate::services::api_call::ApiCall;
lazy_static! {
static ref REQUEST_MANAGER: Mutex<Option<RequestManager>> = Mutex::new(None);
}
pub fn get_request_manager() -> &'static Mutex<Option<RequestManager>> {
if REQUEST_MANAGER.lock().unwrap().is_none() {
*REQUEST_MANAGER.lock().unwrap() = Some(RequestManager::new());
}
&REQUEST_MANAGER
}
pub struct RequestManager {
token: Option<String>,
host: Option<String>,
}
impl RequestManager {
pub fn new() -> Self {
RequestManager {
token: None,
host: None,
}
}
pub fn set_host(&mut self, host: String) {
self.host = Some(host);
}
pub fn get_host(&mut self) -> String
{
if self.host.is_none()
{
let remote = match config::get("remote") {
Some(r) => r,
None => {
// todo ask user instead
eprintln!("fatal: unable to find a remote");
std::process::exit(1);
}
};
let (host, _, _) = get_url_props(&remote);
self.host = Some(host.clone());
// todo ask user
}
self.host.clone().unwrap()
}
pub fn get_token(&mut self) -> String {
if self.token.is_none() {
// todo check in config
let get_token = Login::new()
.ask_auth()
.set_host(Some(self.get_host()))
.send_login();
// todo deal with error cases
self.token = Some(get_token.unwrap());
}
self.token.clone().unwrap()
}
pub fn create_request()
{
}
}

View File

@ -1,25 +1,32 @@
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use reqwest::{Method, Response, Error}; use reqwest::Method;
use crate::services::api::{ApiBuilder, ApiError}; use crate::services::api::{ApiBuilder, ApiError};
use crate::services::api_call::ApiCall;
pub struct UploadFile { pub struct UploadFile {
api_builder: ApiBuilder, api_builder: ApiBuilder,
} }
impl UploadFile { impl ApiCall for UploadFile {
pub fn new() -> Self { fn new() -> Self {
UploadFile { UploadFile {
api_builder: ApiBuilder::new(), api_builder: ApiBuilder::new(),
} }
} }
pub fn set_url(&mut self, url: &str) -> &mut UploadFile { fn set_url(&mut self, url: &str) -> &mut UploadFile {
self.api_builder.build_request(Method::PUT, url); self.api_builder.build_request(Method::PUT, url);
self self
} }
fn send(&mut self) -> Result<Option<String>, ApiError> {
self.api_builder.send(true)
}
}
impl UploadFile {
pub fn set_file(&mut self, path: PathBuf) -> &mut UploadFile { pub fn set_file(&mut self, path: PathBuf) -> &mut UploadFile {
// todo large file // todo large file
// todo small files // todo small files
@ -29,23 +36,4 @@ impl UploadFile {
self.api_builder.set_body(buffer); self.api_builder.set_body(buffer);
self self
} }
pub async fn send(&mut self) -> Result<Response, Error> {
self.api_builder.send().await
}
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))
}
}
} }