Compare commits
5 Commits
current
...
feature/im
Author | SHA1 | Date | |
---|---|---|---|
2fbb100514 | |||
3dbed70748 | |||
d05e1f842f | |||
8c03e6420c | |||
a40ca99a3b |
@ -9,3 +9,5 @@ include = [ "lib/**/*.php" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "1.1.9"
|
regex = "1.1.9"
|
||||||
|
uuid = { version = "0.8.1", features = ["v4"] }
|
||||||
|
serde_json = "1.0.59"
|
||||||
|
@ -25,4 +25,44 @@ pub const SSH_FXP_NAME: u8 = 104;
|
|||||||
pub const SSH_FXP_ATTRS: u8 = 105;
|
pub const SSH_FXP_ATTRS: u8 = 105;
|
||||||
pub const SSH_FXP_EXTENDED: u8 = 200;
|
pub const SSH_FXP_EXTENDED: u8 = 200;
|
||||||
pub const SSH_FXP_EXTENDED_REPLY: u8 = 201;
|
pub const SSH_FXP_EXTENDED_REPLY: u8 = 201;
|
||||||
pub const NEXTCLOUD_PATH: &str = "/var/www/nextcloud.sergiotarxz.freemyip.com/htdocs/";
|
// Status codes.
|
||||||
|
pub const SSH_FX_OK: u32 = 0;
|
||||||
|
pub const SSH_FX_EOF: u32 = 1;
|
||||||
|
pub const SSH_FX_NO_SUCH_FILE: u32 = 2;
|
||||||
|
pub const SSH_FX_PERMISSION_DENIED: u32 = 3;
|
||||||
|
pub const SSH_FX_FAILURE: u32 = 4;
|
||||||
|
pub const SSH_FX_BAD_MESSAGE: u32 = 5;
|
||||||
|
pub const SSH_FX_NO_CONNECTION: u32 = 6;
|
||||||
|
pub const SSH_FX_CONNECTION_LOST: u32 = 7;
|
||||||
|
pub const SSH_FX_OP_UNSUPPORTED: u32 = 8;
|
||||||
|
// Attrs.
|
||||||
|
pub const SSH_FILEXFER_ATTR_SIZE: u32 = 0x1;
|
||||||
|
pub const SSH_FILEXFER_ATTR_UIDGID: u32 = 0x2;
|
||||||
|
pub const SSH_FILEXFER_ATTR_PERMISSIONS: u32 = 0x4;
|
||||||
|
pub const SSH_FILEXFER_ATTR_ACMODTIME: u32 = 0x8;
|
||||||
|
pub const SSH_FILEXFER_ATTR_EXTENDED: u32 = 0x80000000;
|
||||||
|
// Mode interpretation.
|
||||||
|
pub const S_IFIFO: u32 = 0o10000; /* named pipe (fifo) */
|
||||||
|
pub const S_IFCHR: u32 = 0o20000; /* character special */
|
||||||
|
pub const S_IFDIR: u32 = 0o40000; /* directory */
|
||||||
|
pub const S_IFBLK: u32 = 0o60000; /* block special */
|
||||||
|
pub const S_IFREG: u32 = 0o100000; /* regular */
|
||||||
|
pub const S_IFLNK: u32 = 0o120000; /* symbolic link */
|
||||||
|
pub const S_IFSOCK: u32 = 0o140000; /* socket */
|
||||||
|
// Permission flags.
|
||||||
|
pub const S_ISUID: u32 = 0o4000; /* set user id on execution */
|
||||||
|
pub const S_ISGID: u32 = 0o2000; /* set group id on execution */
|
||||||
|
pub const S_ISTXT: u32 = 0o1000; /* sticky bit */
|
||||||
|
pub const S_IRWXU: u32 = 0o700; /* RWX mask for owner */
|
||||||
|
pub const S_IRUSR: u32 = 0o400; /* R for owner */
|
||||||
|
pub const S_IWUSR: u32 = 0o200; /* W for owner */
|
||||||
|
pub const S_IXUSR: u32 = 0o100; /* X for owner */
|
||||||
|
pub const S_IRWXG: u32 = 0o70; /* RWX mask for group */
|
||||||
|
pub const S_IRGRP: u32 = 0o40; /* R for group */
|
||||||
|
pub const S_IWGRP: u32 = 0o20; /* W for group */
|
||||||
|
pub const S_IXGRP: u32 = 0o10; /* X for group */
|
||||||
|
pub const S_IRWXO: u32 = 0o7; /* RWX mask for other */
|
||||||
|
pub const S_IROTH: u32 = 0o4; /* R for other */
|
||||||
|
pub const S_IWOTH: u32 = 0o2; /* W for other */
|
||||||
|
pub const S_IXOTH: u32 = 0o1; /* X for other */
|
||||||
|
pub const S_ISVTX: u32 = 0o1000; /* save swapped text even after use */
|
||||||
|
20
src/handle.rs
Normal file
20
src/handle.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
pub struct DirectoryHandle {
|
||||||
|
child_paths: Vec<String>,
|
||||||
|
finished: bool,
|
||||||
|
}
|
||||||
|
impl DirectoryHandle {
|
||||||
|
pub fn get_files(&mut self) -> Result<Vec<String>, String> {
|
||||||
|
if self.finished {
|
||||||
|
Err("Could not find more files.".to_string())
|
||||||
|
} else {
|
||||||
|
self.finished = true;
|
||||||
|
Ok(self.child_paths.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(childs: Vec<String>) -> DirectoryHandle {
|
||||||
|
DirectoryHandle {
|
||||||
|
child_paths: childs,
|
||||||
|
finished: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod sftp;
|
pub mod sftp;
|
||||||
|
pub mod php;
|
||||||
|
pub mod handle;
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -1,23 +1,10 @@
|
|||||||
use fake_sftp::packet::dispatch_packet;
|
|
||||||
|
|
||||||
use std::io::{BufWriter, Write};
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
eprintln!("{}", execute_php("echo \"hola rusty mundo\";"));
|
let arguments: Vec<String> = std::env::args().into_iter().collect();
|
||||||
loop {
|
if arguments.len() < 3 {
|
||||||
let packet = fake_sftp::packet::Packet::read_packet();
|
eprintln!("The nextcloud_folder and user arguments are required.");
|
||||||
dispatch_packet(packet)
|
std::process::exit(-1);
|
||||||
}
|
}
|
||||||
}
|
let nextcloud_folder = &arguments[1];
|
||||||
|
let user = &arguments[2];
|
||||||
fn execute_php(command: &str) -> String {
|
fake_sftp::packet::dispatch::loop_of_dispatch(nextcloud_folder, user);
|
||||||
let output = Command::new("php")
|
|
||||||
.args(&["-r", command])
|
|
||||||
.output()
|
|
||||||
.expect("");
|
|
||||||
let mut buff_stderr = BufWriter::new(std::io::stderr());
|
|
||||||
buff_stderr.write(&output.stderr).expect("Unable to write to stderr.");
|
|
||||||
buff_stderr.flush().expect("Unable to flush stderr.");
|
|
||||||
return String::from_utf8(output.stdout).expect("Unable to parse php response");
|
|
||||||
}
|
}
|
||||||
|
@ -1,68 +1,11 @@
|
|||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod dispatch;
|
||||||
|
|
||||||
use crate::constants::SSH_FXP_INIT;
|
use std::io::{BufReader, Read};
|
||||||
use crate::constants::SSH_FXP_REALPATH;
|
|
||||||
use crate::constants::SSH_FXP_VERSION;
|
|
||||||
use crate::constants::SSH_FXP_NAME;
|
|
||||||
|
|
||||||
use std::io::{BufReader, BufWriter, Read, Write};
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
pub fn dispatch_packet(packet: Packet) {
|
|
||||||
let mut buff_stdout = BufWriter::new(std::io::stdout());
|
|
||||||
let mut response: Packet = match packet.packet_header.type_packet {
|
|
||||||
SSH_FXP_INIT => dispatch_version_request(packet),
|
|
||||||
SSH_FXP_REALPATH => dispatch_realpath_request(packet),
|
|
||||||
_ => panic!("{} not implemented.", packet.packet_header.type_packet),
|
|
||||||
};
|
|
||||||
let serialized_response = response.serialize();
|
|
||||||
eprintln!("Writing response.");
|
|
||||||
eprintln!("{:#?}", &serialized_response);
|
|
||||||
buff_stdout
|
|
||||||
.write(&serialized_response)
|
|
||||||
.expect("Unable to write to stdout.");
|
|
||||||
buff_stdout.flush().expect("Unable to flush stdout.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_realpath_request(packet: Packet) -> Packet {
|
|
||||||
let realpath_packet_data: data::realpath::RealpathData =
|
|
||||||
data::realpath::RealpathData::deserialize(&packet.data);
|
|
||||||
let path = crate::sftp::realpath(&realpath_packet_data.path);
|
|
||||||
let id = realpath_packet_data.id;
|
|
||||||
Packet {
|
|
||||||
packet_header: header::PacketHeader {
|
|
||||||
length: 0,
|
|
||||||
type_packet: SSH_FXP_NAME,
|
|
||||||
},
|
|
||||||
data: crate::packet::data::name::NameData::new(id, &[path]).serialize(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_version_request(packet: Packet) -> Packet {
|
|
||||||
let init_packet_data: data::version::VersionData =
|
|
||||||
match data::version::VersionData::deserialize(&packet.data) {
|
|
||||||
Ok(ok) => ok,
|
|
||||||
Err(err) => panic!(err),
|
|
||||||
};
|
|
||||||
eprintln!("The client version is {}.", init_packet_data.version);
|
|
||||||
if init_packet_data.version < 3 {
|
|
||||||
panic!("Unsupported version, minimal client version 3.");
|
|
||||||
}
|
|
||||||
let version_packet_data = data::version::VersionData {
|
|
||||||
version: 3,
|
|
||||||
extension_data: Vec::new(),
|
|
||||||
};
|
|
||||||
Packet {
|
|
||||||
packet_header: header::PacketHeader {
|
|
||||||
length: 0,
|
|
||||||
type_packet: SSH_FXP_VERSION,
|
|
||||||
},
|
|
||||||
data: version_packet_data.serialize(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Packet {
|
pub struct Packet {
|
||||||
pub packet_header: header::PacketHeader,
|
pub packet_header: header::PacketHeader,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
pub mod version;
|
pub mod version;
|
||||||
pub mod realpath;
|
pub mod realpath;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
|
pub mod opendir;
|
||||||
|
pub mod status;
|
||||||
|
pub mod handle;
|
||||||
|
pub mod attrs;
|
||||||
|
pub mod stat;
|
||||||
|
23
src/packet/data/attrs.rs
Normal file
23
src/packet/data/attrs.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pub struct AttrsData {
|
||||||
|
id: u32,
|
||||||
|
attrs: u32,
|
||||||
|
permission: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrsData {
|
||||||
|
pub fn new(id: u32, attrs: u32, permission: u32) -> AttrsData {
|
||||||
|
AttrsData {
|
||||||
|
id: id,
|
||||||
|
attrs: attrs,
|
||||||
|
permission: permission,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut serialized_data: Vec<u8> = Vec::new();
|
||||||
|
serialized_data.extend(&self.id.to_be_bytes());
|
||||||
|
serialized_data.extend(&self.attrs.to_be_bytes());
|
||||||
|
serialized_data.extend(&self.permission.to_be_bytes());
|
||||||
|
serialized_data
|
||||||
|
}
|
||||||
|
}
|
22
src/packet/data/handle.rs
Normal file
22
src/packet/data/handle.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
pub struct HandleData {
|
||||||
|
pub id: u32,
|
||||||
|
pub handle: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HandleData {
|
||||||
|
pub fn new(id: u32, handle: String) -> HandleData {
|
||||||
|
HandleData {
|
||||||
|
id: id,
|
||||||
|
handle: handle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut serialized_data: Vec<u8> = Vec::new();
|
||||||
|
serialized_data.extend(&self.id.to_be_bytes());
|
||||||
|
let handle = crate::packet::types::string::String {
|
||||||
|
content: self.handle.clone(),
|
||||||
|
};
|
||||||
|
serialized_data.extend(handle.serialize());
|
||||||
|
serialized_data
|
||||||
|
}
|
||||||
|
}
|
20
src/packet/data/opendir.rs
Normal file
20
src/packet/data/opendir.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
pub struct OpendirData {
|
||||||
|
pub id: u32,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
impl OpendirData {
|
||||||
|
pub fn deserialize(mut u8_array_data: &[u8]) -> OpendirData {
|
||||||
|
if u8_array_data.len() < 9 {
|
||||||
|
panic!("Opendir data is expected to be bigger.");
|
||||||
|
}
|
||||||
|
let mut id: [u8; 4] = [0; 4];
|
||||||
|
u8_array_data
|
||||||
|
.read(&mut id)
|
||||||
|
.expect("Failed to read id from opendir packet.");
|
||||||
|
let id = u32::from_be_bytes(id);
|
||||||
|
let path = crate::packet::types::string::String::deserialize(u8_array_data).content;
|
||||||
|
OpendirData { id: id, path: path }
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ pub struct RealpathData {
|
|||||||
|
|
||||||
impl RealpathData {
|
impl RealpathData {
|
||||||
pub fn deserialize(mut u8_array_data: &[u8]) -> RealpathData {
|
pub fn deserialize(mut u8_array_data: &[u8]) -> RealpathData {
|
||||||
if u8_array_data.len() < 5 {
|
if u8_array_data.len() < 9 {
|
||||||
panic!("Realpath data is expected to be bigger.");
|
panic!("Realpath data is expected to be bigger.");
|
||||||
}
|
}
|
||||||
let mut id: [u8; 4] = [0; 4];
|
let mut id: [u8; 4] = [0; 4];
|
||||||
|
17
src/packet/data/stat.rs
Normal file
17
src/packet/data/stat.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
pub struct StatData {
|
||||||
|
pub id: u32,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatData {
|
||||||
|
pub fn deserialize(mut data_to_serialize: &[u8]) -> StatData {
|
||||||
|
let mut id: [u8; 4] = [0; 4];
|
||||||
|
data_to_serialize.read(&mut id).expect("Failed to read id.");
|
||||||
|
let id: u32 = u32::from_be_bytes(id);
|
||||||
|
let path: String =
|
||||||
|
crate::packet::types::string::String::deserialize(data_to_serialize).content;
|
||||||
|
StatData { id: id, path: path }
|
||||||
|
}
|
||||||
|
}
|
20
src/packet/data/status.rs
Normal file
20
src/packet/data/status.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
pub struct StatusData {
|
||||||
|
pub id: u32,
|
||||||
|
pub status_code: u32,
|
||||||
|
pub error_message: String,
|
||||||
|
pub lang: String,
|
||||||
|
}
|
||||||
|
impl StatusData {
|
||||||
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut serialized_data: Vec<u8> = Vec::new();
|
||||||
|
serialized_data.extend(&self.id.to_be_bytes());
|
||||||
|
serialized_data.extend(&self.status_code.to_be_bytes());
|
||||||
|
let error_message = crate::packet::types::string::String {
|
||||||
|
content: self.error_message.clone(),
|
||||||
|
};
|
||||||
|
let lang = crate::packet::types::string::String { content: self.lang.clone() };
|
||||||
|
serialized_data.extend(error_message.serialize());
|
||||||
|
serialized_data.extend(lang.serialize());
|
||||||
|
serialized_data
|
||||||
|
}
|
||||||
|
}
|
220
src/packet/dispatch.rs
Normal file
220
src/packet/dispatch.rs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::constants::SSH_FILEXFER_ATTR_PERMISSIONS;
|
||||||
|
use crate::constants::SSH_FXP_ATTRS;
|
||||||
|
use crate::constants::SSH_FXP_HANDLE;
|
||||||
|
use crate::constants::SSH_FXP_INIT;
|
||||||
|
use crate::constants::SSH_FXP_LSTAT;
|
||||||
|
use crate::constants::SSH_FXP_NAME;
|
||||||
|
use crate::constants::SSH_FXP_OPENDIR;
|
||||||
|
use crate::constants::SSH_FXP_REALPATH;
|
||||||
|
use crate::constants::SSH_FXP_STAT;
|
||||||
|
use crate::constants::SSH_FXP_STATUS;
|
||||||
|
use crate::constants::SSH_FXP_VERSION;
|
||||||
|
use crate::constants::SSH_FX_FAILURE;
|
||||||
|
use crate::constants::SSH_FX_NO_SUCH_FILE;
|
||||||
|
use crate::constants::SSH_FX_OP_UNSUPPORTED;
|
||||||
|
use crate::constants::S_IFDIR;
|
||||||
|
use crate::constants::S_IFREG;
|
||||||
|
use crate::constants::S_IRWXU;
|
||||||
|
|
||||||
|
use crate::handle::DirectoryHandle;
|
||||||
|
use crate::packet::data::attrs::AttrsData;
|
||||||
|
use crate::packet::data::handle::HandleData;
|
||||||
|
use crate::packet::data::name::NameData;
|
||||||
|
use crate::packet::data::opendir::OpendirData;
|
||||||
|
use crate::packet::data::realpath::RealpathData;
|
||||||
|
use crate::packet::data::stat::StatData;
|
||||||
|
use crate::packet::data::status::StatusData;
|
||||||
|
use crate::packet::data::version::VersionData;
|
||||||
|
use crate::packet::header::PacketHeader;
|
||||||
|
use crate::packet::Packet;
|
||||||
|
use crate::php;
|
||||||
|
use crate::sftp::realpath;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
|
||||||
|
pub fn loop_of_dispatch(nextcloud_folder: &str, user: &str) {
|
||||||
|
let mut directory_handles: HashMap<String, DirectoryHandle> = HashMap::new();
|
||||||
|
loop {
|
||||||
|
let packet = Packet::read_packet();
|
||||||
|
dispatch_packet(packet, &mut directory_handles, nextcloud_folder, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_packet(
|
||||||
|
packet: Packet,
|
||||||
|
directory_handles: &mut HashMap<String, DirectoryHandle>,
|
||||||
|
nextcloud_folder: &str,
|
||||||
|
user: &str,
|
||||||
|
) {
|
||||||
|
let mut buff_stdout = BufWriter::new(std::io::stdout());
|
||||||
|
let mut response: Packet = match packet.packet_header.type_packet {
|
||||||
|
SSH_FXP_INIT => dispatch_version_request(packet),
|
||||||
|
SSH_FXP_REALPATH => dispatch_realpath_request(packet),
|
||||||
|
SSH_FXP_OPENDIR => {
|
||||||
|
dispatch_opendir_request(packet, directory_handles, nextcloud_folder, user)
|
||||||
|
}
|
||||||
|
SSH_FXP_STAT => dispatch_stat_request(packet, nextcloud_folder, user),
|
||||||
|
SSH_FXP_LSTAT => dispatch_stat_request(packet, nextcloud_folder, user),
|
||||||
|
_ => {
|
||||||
|
eprintln!("{} not implemented.", packet.packet_header.type_packet);
|
||||||
|
let mut id: [u8; 4] = [0; 4];
|
||||||
|
let mut packet_data: &[u8] = &packet.data;
|
||||||
|
packet_data
|
||||||
|
.read(&mut id)
|
||||||
|
.expect("Id for the last request was not provided.");
|
||||||
|
let id: u32 = u32::from_be_bytes(id);
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_STATUS,
|
||||||
|
},
|
||||||
|
data: StatusData {
|
||||||
|
id: id,
|
||||||
|
status_code: SSH_FX_OP_UNSUPPORTED,
|
||||||
|
error_message: "The operation {} is not supported yet.".to_string(),
|
||||||
|
lang: "en_US".to_string(),
|
||||||
|
}
|
||||||
|
.serialize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let serialized_response = response.serialize();
|
||||||
|
eprintln!("Writing response.");
|
||||||
|
eprintln!("{:#?}", &serialized_response);
|
||||||
|
buff_stdout
|
||||||
|
.write(&serialized_response)
|
||||||
|
.expect("Unable to write to stdout.");
|
||||||
|
buff_stdout.flush().expect("Unable to flush stdout.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_stat_request(packet: Packet, nextcloud_folder: &str, user: &str) -> Packet {
|
||||||
|
let stat_data: StatData = StatData::deserialize(&packet.data);
|
||||||
|
if !php::check_if_exists(&stat_data.path, &user, &nextcloud_folder) {
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_STATUS,
|
||||||
|
},
|
||||||
|
data: StatusData {
|
||||||
|
id: stat_data.id,
|
||||||
|
status_code: SSH_FX_NO_SUCH_FILE,
|
||||||
|
error_message: "No such file or directory.".to_string(),
|
||||||
|
lang: "en_US".to_string(),
|
||||||
|
}
|
||||||
|
.serialize(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut permission: u32 = 0;
|
||||||
|
let mut attrs: u32 = 0;
|
||||||
|
attrs |= SSH_FILEXFER_ATTR_PERMISSIONS;
|
||||||
|
permission |= S_IRWXU;
|
||||||
|
if php::check_if_is_dir(&stat_data.path, &user, &nextcloud_folder) {
|
||||||
|
permission |= S_IFDIR;
|
||||||
|
} else {
|
||||||
|
permission |= S_IFREG;
|
||||||
|
}
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_ATTRS,
|
||||||
|
},
|
||||||
|
data: AttrsData::new(stat_data.id, attrs, permission).serialize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_opendir_request(
|
||||||
|
packet: Packet,
|
||||||
|
directory_handles: &mut HashMap<String, DirectoryHandle>,
|
||||||
|
nextcloud_folder: &str,
|
||||||
|
user: &str,
|
||||||
|
) -> Packet {
|
||||||
|
let opendir_packet_data = OpendirData::deserialize(&packet.data);
|
||||||
|
let path = opendir_packet_data.path;
|
||||||
|
if !php::check_if_exists(&path, &user, &nextcloud_folder) {
|
||||||
|
return Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_STATUS,
|
||||||
|
},
|
||||||
|
data: StatusData {
|
||||||
|
id: opendir_packet_data.id,
|
||||||
|
status_code: SSH_FX_NO_SUCH_FILE,
|
||||||
|
error_message: "No such file or directory.".to_string(),
|
||||||
|
lang: "en_US".to_string(),
|
||||||
|
}
|
||||||
|
.serialize(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if !php::check_if_is_dir(&path, &user, &nextcloud_folder) {
|
||||||
|
return Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_STATUS,
|
||||||
|
},
|
||||||
|
data: StatusData {
|
||||||
|
id: opendir_packet_data.id,
|
||||||
|
status_code: SSH_FX_FAILURE,
|
||||||
|
error_message: "The file is not a directory.".to_string(),
|
||||||
|
lang: "en_US".to_string(),
|
||||||
|
}
|
||||||
|
.serialize(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let str_handle: String = Uuid::new_v4().to_string();
|
||||||
|
eprintln!("{}", str_handle);
|
||||||
|
directory_handles.insert(
|
||||||
|
str_handle.clone(),
|
||||||
|
DirectoryHandle::new(php::ls(&path, &user, &nextcloud_folder)),
|
||||||
|
);
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_HANDLE,
|
||||||
|
},
|
||||||
|
data: HandleData {
|
||||||
|
id: opendir_packet_data.id,
|
||||||
|
handle: str_handle,
|
||||||
|
}
|
||||||
|
.serialize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_realpath_request(packet: Packet) -> Packet {
|
||||||
|
let realpath_packet_data: RealpathData = RealpathData::deserialize(&packet.data);
|
||||||
|
let path = realpath(&realpath_packet_data.path);
|
||||||
|
let id = realpath_packet_data.id;
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_NAME,
|
||||||
|
},
|
||||||
|
data: NameData::new(id, &[path]).serialize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_version_request(packet: Packet) -> Packet {
|
||||||
|
let init_packet_data: VersionData = match VersionData::deserialize(&packet.data) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => panic!(err),
|
||||||
|
};
|
||||||
|
eprintln!("The client version is {}.", init_packet_data.version);
|
||||||
|
if init_packet_data.version < 3 {
|
||||||
|
panic!("Unsupported version, minimal client version 3.");
|
||||||
|
}
|
||||||
|
let version_packet_data = VersionData {
|
||||||
|
version: 3,
|
||||||
|
extension_data: Vec::new(),
|
||||||
|
};
|
||||||
|
Packet {
|
||||||
|
packet_header: PacketHeader {
|
||||||
|
length: 0,
|
||||||
|
type_packet: SSH_FXP_VERSION,
|
||||||
|
},
|
||||||
|
data: version_packet_data.serialize(),
|
||||||
|
}
|
||||||
|
}
|
78
src/php.rs
Normal file
78
src/php.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use serde_json::Value::Bool;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn execute_php(command: &str, arguments: &[&str]) -> String {
|
||||||
|
let mut execute_array: Vec<&str> = Vec::new();
|
||||||
|
execute_array.extend(&["-r", &command, "--"]);
|
||||||
|
execute_array.extend(arguments);
|
||||||
|
let output = std::process::Command::new("php")
|
||||||
|
.args(execute_array)
|
||||||
|
.output()
|
||||||
|
.expect("");
|
||||||
|
let mut buff_stderr = std::io::BufWriter::new(std::io::stderr());
|
||||||
|
buff_stderr
|
||||||
|
.write(&output.stderr)
|
||||||
|
.expect("Unable to write to stderr.");
|
||||||
|
buff_stderr.flush().expect("Unable to flush stderr.");
|
||||||
|
return String::from_utf8(output.stdout).expect("Unable to parse php response");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ls(path: &str, user: &str, nextcloud_location: &str) -> Vec<String> {
|
||||||
|
let php_script: String = [
|
||||||
|
&start_script(),
|
||||||
|
"$path = $argv[3];",
|
||||||
|
"function get_name($node) { return $node->getName(); }",
|
||||||
|
"echo(json_encode(array_map('get_name', $view->getDirectoryContent($path))));",
|
||||||
|
]
|
||||||
|
.join("\n");
|
||||||
|
let result = execute_php(&php_script, &[user, nextcloud_location, path]);
|
||||||
|
let result: Vec<String> = serde_json::from_str(&result).expect("Invalid json reading ls.");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_if_exists(path: &str, user: &str, nextcloud_location: &str) -> bool {
|
||||||
|
let php_script: String = [
|
||||||
|
&start_script(),
|
||||||
|
"$path = $argv[3];",
|
||||||
|
"echo json_encode($view->file_exists($path));",
|
||||||
|
]
|
||||||
|
.join("\n");
|
||||||
|
json_get_boolean(&execute_php(&php_script, &[user, nextcloud_location, path]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_if_is_dir(path: &str, user: &str, nextcloud_location: &str) -> bool {
|
||||||
|
let php_script: String = [
|
||||||
|
&start_script(),
|
||||||
|
"$path = $argv[3];",
|
||||||
|
"echo json_encode($view->is_dir($path));",
|
||||||
|
]
|
||||||
|
.join("\n");
|
||||||
|
json_get_boolean(&execute_php(&php_script, &[user, nextcloud_location, path]))
|
||||||
|
}
|
||||||
|
fn json_get_boolean(json: &str) -> bool {
|
||||||
|
let json_value: Value = serde_json::from_str(json).expect("Unable to parse json at json_get_boolean.");
|
||||||
|
match json_value {
|
||||||
|
Bool(x) => x,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_script() -> String {
|
||||||
|
[
|
||||||
|
"define('OC_CONSOLE', 1);",
|
||||||
|
"$user = $argv[1];",
|
||||||
|
"$nextcloud_location = $argv[2];",
|
||||||
|
"include $nextcloud_location . '/lib/base.php';",
|
||||||
|
"use Symfony\\Component\\Console\\Application as SymfonyApplication;",
|
||||||
|
"use \\OC\\Files\\View;",
|
||||||
|
"$application = new SymfonyApplication(
|
||||||
|
\\OC::$server->getThemingDefaults()->getName(),
|
||||||
|
\\OC_Util::getVersionString()
|
||||||
|
);",
|
||||||
|
"\\OC_Util::setupFS($user);",
|
||||||
|
"$view = new View('/'. $user . '/files');",
|
||||||
|
]
|
||||||
|
.join("\n")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user