1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2025-08-10 22:41:43 +02:00

FTP: Доработка компоненты, первые методы

This commit is contained in:
Anton Titovets
2025-07-16 22:23:50 +03:00
parent 2038842749
commit fb484d478e
3 changed files with 196 additions and 35 deletions

View File

@@ -35,6 +35,15 @@ impl FtpClient {
} }
} }
pub fn get_welcome_msg(&self) -> String {
let msg = match self {
FtpClient::Secure(stream) => stream.get_welcome_msg().unwrap_or(""),
FtpClient::Insecure(stream) => stream.get_welcome_msg().unwrap_or(""),
};
json!({"result": true, "data": msg}).to_string()
}
} }
fn format_json_error<E: ToString>(error: E) -> String { fn format_json_error<E: ToString>(error: E) -> String {

View File

@@ -9,7 +9,8 @@ use suppaftp::types::Mode;
use serde::Deserialize; use serde::Deserialize;
use crate::component::ftp_client::FtpClient; use crate::component::ftp_client::FtpClient;
use std::net::TcpStream; use std::net::TcpStream;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, MutexGuard};
use std::time::Duration;
use socks::{Socks4Stream, Socks5Stream}; use socks::{Socks4Stream, Socks5Stream};
// МЕТОДЫ КОМПОНЕНТЫ ------------------------------------------------------------------------------- // МЕТОДЫ КОМПОНЕНТЫ -------------------------------------------------------------------------------
@@ -19,7 +20,8 @@ pub const METHODS: &[&[u16]] = &[
name!("Connect"), name!("Connect"),
name!("UpdateSettings"), name!("UpdateSettings"),
name!("UpdateProxy"), name!("UpdateProxy"),
name!("SetTLS") name!("SetTLS"),
name!("GetWelcomeMsg")
]; ];
// Число параметров функций компоненты // Число параметров функций компоненты
@@ -56,6 +58,12 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box<dyn
Box::new(obj.set_tls(use_tls, accept_invalid_certs, &ca_cert_path)) Box::new(obj.set_tls(use_tls, accept_invalid_certs, &ca_cert_path))
},
4 => {
Box::new(match obj.get_client(){
Ok(c) => c.get_welcome_msg(),
Err(e) => process_error(e.as_str())
})
} }
_ => Box::new(false), // Неверный номер команды _ => Box::new(false), // Неверный номер команды
} }
@@ -71,8 +79,8 @@ pub const PROPS: &[&[u16]] = &[];
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct FtpProxySettings { struct FtpProxySettings {
server: String, server: Option<String>,
port: u16, port: Option<u16>,
login: Option<String>, login: Option<String>,
password: Option<String>, password: Option<String>,
proxy_type: Option<String>, // "http", "socks4", "socks5" proxy_type: Option<String>, // "http", "socks4", "socks5"
@@ -85,7 +93,8 @@ struct FtpSettings {
login: Option<String>, login: Option<String>,
password: Option<String>, password: Option<String>,
passive: Option<bool>, passive: Option<bool>,
timeout: Option<u64>, read_timeout: Option<u64>,
write_timeout: Option<u64>,
} }
pub struct AddIn { pub struct AddIn {
@@ -94,7 +103,8 @@ pub struct AddIn {
login: Option<String>, login: Option<String>,
password: Option<String>, password: Option<String>,
passive: bool, passive: bool,
timeout: u64, read_timeout: u64,
write_timeout: u64,
// TLS // TLS
use_tls: bool, use_tls: bool,
accept_invalid_certs: bool, accept_invalid_certs: bool,
@@ -118,11 +128,11 @@ impl AddIn {
login: None, login: None,
password: None, password: None,
passive: true, passive: true,
timeout: 120, read_timeout: 120,
write_timeout: 120,
use_tls: false, use_tls: false,
accept_invalid_certs: false, accept_invalid_certs: false,
ca_cert_path: String::new(), ca_cert_path: String::new(),
// Инициализация полей прокси
proxy_server: None, proxy_server: None,
proxy_port: None, proxy_port: None,
proxy_login: None, proxy_login: None,
@@ -135,7 +145,7 @@ impl AddIn {
let json_struct: FtpSettings = match serde_json::from_str(json_data){ let json_struct: FtpSettings = match serde_json::from_str(json_data){
Ok(s) => s, Ok(s) => s,
Err(e) => return Self::process_error(&e.to_string()), Err(e) => return process_error(&e.to_string()),
}; };
if let Some(domain) = json_struct.domain { if let Some(domain) = json_struct.domain {
@@ -150,12 +160,16 @@ impl AddIn {
self.passive = passive; self.passive = passive;
} }
if let Some(timeout) = json_struct.timeout { if let Some(read_timeout) = json_struct.read_timeout {
self.timeout = timeout; self.read_timeout = read_timeout;
} }
self.login = json_struct.login.or(self.login.clone()); if let Some(write_timeout) = json_struct.write_timeout {
self.password = json_struct.password.or(self.password.clone()); self.write_timeout = write_timeout;
}
self.login = json_struct.login;
self.password = json_struct.password;
json!({"result": true}).to_string() json!({"result": true}).to_string()
@@ -165,11 +179,12 @@ impl AddIn {
let json_struct: FtpProxySettings = match serde_json::from_str(json_data){ let json_struct: FtpProxySettings = match serde_json::from_str(json_data){
Ok(s) => s, Ok(s) => s,
Err(e) => return Self::process_error(&e.to_string()), Err(e) => return process_error(&e.to_string()),
}; };
self.proxy_server = Some(json_struct.server);
self.proxy_port = Some(json_struct.port); self.proxy_server = json_struct.server;
self.proxy_port = json_struct.port;
self.proxy_login = json_struct.login; self.proxy_login = json_struct.login;
self.proxy_password = json_struct.password; self.proxy_password = json_struct.password;
self.proxy_type = json_struct.proxy_type; self.proxy_type = json_struct.proxy_type;
@@ -181,7 +196,7 @@ impl AddIn {
pub fn initialize(&mut self) -> String { pub fn initialize(&mut self) -> String {
if self.domain.is_empty() { if self.domain.is_empty() {
return Self::process_error("Address must be initialized"); return process_error("Address must be initialized");
} }
let tcp_stream = match self.create_tcp_connection() { let tcp_stream = match self.create_tcp_connection() {
@@ -189,6 +204,19 @@ impl AddIn {
Err(e) => return e, Err(e) => return e,
}; };
let w_timeout = Some(Duration::from_secs(self.write_timeout));
let r_timeout = Some(Duration::from_secs(self.read_timeout));
match tcp_stream.set_write_timeout(w_timeout){
Ok(_) => (),
Err(e) => return process_error(&e.to_string()),
}
match tcp_stream.set_read_timeout(r_timeout) {
Ok(_) => (),
Err(e) => return process_error(&e.to_string()),
}
let client = match self.configure_ftp_client(tcp_stream) { let client = match self.configure_ftp_client(tcp_stream) {
Ok(client) => client, Ok(client) => client,
Err(e) => return e, Err(e) => return e,
@@ -199,7 +227,7 @@ impl AddIn {
self.client = match client.login(login, password) { self.client = match client.login(login, password) {
Ok(auth) => Some(Arc::new(Mutex::new(auth))), Ok(auth) => Some(Arc::new(Mutex::new(auth))),
Err(e) => return Self::process_error(&e.to_string()), Err(e) => return process_error(&e.to_string()),
}; };
json!({"result": true}).to_string() json!({"result": true}).to_string()
@@ -217,11 +245,11 @@ impl AddIn {
let tls_connector = self.get_tls_connector()?; let tls_connector = self.get_tls_connector()?;
let ftp_stream = NativeTlsFtpStream::connect_with_stream(tcp_stream) let ftp_stream = NativeTlsFtpStream::connect_with_stream(tcp_stream)
.map_err(|e| Self::process_error(&e.to_string()))?; .map_err(|e| process_error(&e.to_string()))?;
let mut secure_stream = ftp_stream let mut secure_stream = ftp_stream
.into_secure(NativeTlsConnector::from(tls_connector), &self.domain) .into_secure(NativeTlsConnector::from(tls_connector), &self.domain)
.map_err(|e| Self::process_error(&e.to_string()))?; .map_err(|e| process_error(&e.to_string()))?;
secure_stream.set_mode(mode); secure_stream.set_mode(mode);
secure_stream.set_passive_nat_workaround(true); secure_stream.set_passive_nat_workaround(true);
@@ -230,7 +258,7 @@ impl AddIn {
} else { } else {
let mut ftp_stream = FtpStream::connect_with_stream(tcp_stream) let mut ftp_stream = FtpStream::connect_with_stream(tcp_stream)
.map_err(|e| Self::process_error(&e.to_string()))?; .map_err(|e| process_error(&e.to_string()))?;
ftp_stream.set_mode(mode); ftp_stream.set_mode(mode);
ftp_stream.set_passive_nat_workaround(true); ftp_stream.set_passive_nat_workaround(true);
@@ -250,7 +278,7 @@ impl AddIn {
match proxy_type.to_lowercase().as_str() { match proxy_type.to_lowercase().as_str() {
"socks5" => self.connect_via_socks5(&proxy_addr, target_addr), "socks5" => self.connect_via_socks5(&proxy_addr, target_addr),
"socks4" => self.connect_via_socks4(&proxy_addr, target_addr), "socks4" => self.connect_via_socks4(&proxy_addr, target_addr),
_ => Err(Self::process_error("Unsupported proxy type")), _ => Err(process_error("Unsupported proxy type")),
} }
} else { } else {
self.connect_direct() self.connect_direct()
@@ -266,7 +294,7 @@ impl AddIn {
}; };
stream.map(|s| s.into_inner()) stream.map(|s| s.into_inner())
.map_err(|e| Self::process_error(&format!("SOCKS5 error: {}", e))) .map_err(|e| process_error(&format!("SOCKS5 error: {}", e)))
} }
fn connect_via_socks4(&self, proxy_addr: &str, target_addr: (&str, u16)) -> Result<TcpStream, String> { fn connect_via_socks4(&self, proxy_addr: &str, target_addr: (&str, u16)) -> Result<TcpStream, String> {
@@ -278,12 +306,12 @@ impl AddIn {
}; };
stream.map(|s| s.into_inner()) stream.map(|s| s.into_inner())
.map_err(|e| Self::process_error(&format!("SOCKS4 error: {}", e))) .map_err(|e| process_error(&format!("SOCKS4 error: {}", e)))
} }
fn connect_direct(&self) -> Result<TcpStream, String> { fn connect_direct(&self) -> Result<TcpStream, String> {
let addr = format!("{}:{}", &self.domain, &self.port); let addr = format!("{}:{}", &self.domain, &self.port);
TcpStream::connect(&addr).map_err(|e| Self::process_error(&format!("Direct connection error: {}", e))) TcpStream::connect(&addr).map_err(|e| process_error(&format!("Direct connection error: {}", e)))
} }
pub fn close_connection(&mut self) -> String { pub fn close_connection(&mut self) -> String {
@@ -295,7 +323,7 @@ impl AddIn {
FtpClient::Insecure(stream) => _ = stream.quit(), FtpClient::Insecure(stream) => _ = stream.quit(),
} }
} }
Err(e) => return Self::process_error(&format!("Failed to lock client: {}", e)), Err(e) => return process_error(&format!("Failed to lock client: {}", e)),
} }
} }
json!({"result": true}).to_string() json!({"result": true}).to_string()
@@ -304,7 +332,7 @@ impl AddIn {
pub fn set_tls(&mut self, use_tls: bool, accept_invalid_certs: bool, ca_cert_path: &str) -> String { pub fn set_tls(&mut self, use_tls: bool, accept_invalid_certs: bool, ca_cert_path: &str) -> String {
if self.client.is_some(){ if self.client.is_some(){
return Self::process_error("TLS settings can only be set before the connection is established"); return process_error("TLS settings can only be set before the connection is established");
}; };
self.accept_invalid_certs = accept_invalid_certs; self.accept_invalid_certs = accept_invalid_certs;
@@ -323,25 +351,25 @@ impl AddIn {
if !self.ca_cert_path.is_empty() { if !self.ca_cert_path.is_empty() {
let cert_data = std::fs::read(&self.ca_cert_path) let cert_data = std::fs::read(&self.ca_cert_path)
.map_err(|e| Self::process_error(&e.to_string()))?; .map_err(|e| process_error(&e.to_string()))?;
let cert = native_tls::Certificate::from_pem(&cert_data) let cert = native_tls::Certificate::from_pem(&cert_data)
.map_err(|e| Self::process_error(&e.to_string()))?; .map_err(|e| process_error(&e.to_string()))?;
tls_builder.add_root_certificate(cert); tls_builder.add_root_certificate(cert);
} }
match tls_builder.build(){ match tls_builder.build(){
Ok(connector) => Ok(connector), Ok(connector) => Ok(connector),
Err(e) => Err(Self::process_error(&e.to_string())), Err(e) => Err(process_error(&e.to_string())),
} }
} }
fn process_error(e: &str) -> String{ fn get_client(&self) -> Result<MutexGuard<'_, FtpClient>, String> {
json!({ self.client
"result": false, .as_ref()
"error": e .ok_or_else(|| process_error("FTP client is not initialized"))
}).to_string() .and_then(|arc| arc.lock().map_err(|_| process_error("Failed to lock FTP client mutex")))
} }
pub fn get_field_ptr(&self, index: usize) -> *const dyn getset::ValueType { pub fn get_field_ptr(&self, index: usize) -> *const dyn getset::ValueType {
@@ -352,6 +380,13 @@ impl AddIn {
pub fn get_field_ptr_mut(&mut self, index: usize) -> *mut dyn getset::ValueType { self.get_field_ptr(index) as *mut _ } pub fn get_field_ptr_mut(&mut self, index: usize) -> *mut dyn getset::ValueType { self.get_field_ptr(index) as *mut _ }
} }
pub fn process_error(e: &str) -> String{
json!({
"result": false,
"error": e
}).to_string()
}
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------

View File

@@ -45,6 +45,123 @@
#Область ОсновныеМетоды #Область ОсновныеМетоды
// Открыть соединение !NOCLI
// Открывает FTP соединение с указанными настройками
//
// Параметры:
// НастройкиFTP - Структура Из КлючИЗначение - Настройки FTP. См. ПолучитьНастройкиFTP - set
// Прокси - Структура Из КлючИЗначение - Настройки прокси, если необходимо. См ПолучитьНастройкиПрокси - proxy
// Tls - Структура Из КлючИЗначение - Настройки TLS, если необходимо. См. ПолучитьНастройкиTls - tls
//
// Возвращаемое значение:
// Произвольный - Объект коннектора или соответствие с информацией об ошибке
Функция ОткрытьСоединение(Знач НастройкиFTP, Знач Прокси = Неопределено, Знач Tls = Неопределено) Экспорт
Если ЭтоКоннектор(НастройкиFTP) Тогда
Возврат НастройкиFTP;
КонецЕсли;
Коннектор = OPI_Компоненты.ПолучитьКомпоненту("FTP");
УстановкаНастроек = УстановитьНастройкиFtp(Коннектор, НастройкиFTP);
Если Не OPI_Инструменты.ПолучитьИли(УстановкаНастроек, "result", Ложь) Тогда
Возврат УстановкаНастроек;
КонецЕсли;
Tls = OPI_Компоненты.УстановитьTls(Коннектор, Tls);
Если Не OPI_Инструменты.ПолучитьИли(Tls, "result", Ложь) Тогда
Возврат Tls;
КонецЕсли;
УстановитьПрокси = УстановитьНастройкиПрокси(Коннектор, Прокси);
Если Не OPI_Инструменты.ПолучитьИли(УстановитьПрокси, "result", Ложь) Тогда
Возврат УстановитьПрокси;
КонецЕсли;
Результат = Коннектор.Connect();
Результат = OPI_Инструменты.JSONВСтруктуру(Результат);
Возврат Результат;
КонецФункции
// Это коннектор !NOCLI
// Проверяет, что значение является объектом внешней компоненты для работы с FTP
//
// Параметры:
// Значение - Произвольный - Значение для проверки - value
//
// Возвращаемое значение:
// Булево - Это коннектор
Функция ЭтоКоннектор(Знач Значение) Экспорт
Возврат Строка(ТипЗнч(Значение)) = "AddIn.OPI_FTP.Main";
КонецФункции
#КонецОбласти #КонецОбласти
#КонецОбласти #КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция УстановитьНастройкиFtp(Знач Коннектор, Знач НастройкиFTP)
ТекстОшибки = "Настройки FTP не являются валидной структурой ключ-значение";
Настройки = НастройкиВJson(НастройкиFTP, ТекстОшибки);
Если ТипЗнч(Настройки) = Тип("Соответствие") Тогда
Возврат Настройки;
КонецЕсли;
Результат = Коннектор.UpdateSettings(Настройки);
Результат = OPI_Инструменты.JsonВСтруктуру(Результат);
Возврат Результат;
КонецФункции
Функция УстановитьНастройкиПрокси(Знач Коннектор, Знач НастройкиПрокси)
Если НастройкиПрокси = Неопределено Тогда
Результат = Новый Соответствие;
Результат.Вставить("result", Истина);
Возврат Результат;
КонецЕсли;
ТекстОшибки = "Настройки прокси не являются валидной структурой ключ-значение";
Настройки = НастройкиВJson(НастройкиПрокси, ТекстОшибки);
Если ТипЗнч(Настройки) = Тип("Соответствие") Тогда
Возврат Настройки;
КонецЕсли;
Результат = Коннектор.UpdateProxy(Настройки);
Результат = OPI_Инструменты.JsonВСтруктуру(Результат);
Возврат Результат;
КонецФункции
Функция НастройкиВJson(Знач Коллекция, Знач ТекстОшибки)
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Коллекция);
Попытка
Результат = OPI_Инструменты.JSONСтрокой(Коллекция);
Исключение
Результат = Новый Соответствие;
Результат.Вставить("result", Ложь);
Результат.Вставить("error" , "Настройки FTP должны содержать только сериализуемые значения");
КонецПопытки;
Возврат Результат;
КонецФункции
#КонецОбласти