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 {

View File

@@ -9,7 +9,8 @@ use suppaftp::types::Mode;
use serde::Deserialize;
use crate::component::ftp_client::FtpClient;
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, MutexGuard};
use std::time::Duration;
use socks::{Socks4Stream, Socks5Stream};
// МЕТОДЫ КОМПОНЕНТЫ -------------------------------------------------------------------------------
@@ -19,7 +20,8 @@ pub const METHODS: &[&[u16]] = &[
name!("Connect"),
name!("UpdateSettings"),
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))
},
4 => {
Box::new(match obj.get_client(){
Ok(c) => c.get_welcome_msg(),
Err(e) => process_error(e.as_str())
})
}
_ => Box::new(false), // Неверный номер команды
}
@@ -71,8 +79,8 @@ pub const PROPS: &[&[u16]] = &[];
#[derive(Deserialize, Debug)]
struct FtpProxySettings {
server: String,
port: u16,
server: Option<String>,
port: Option<u16>,
login: Option<String>,
password: Option<String>,
proxy_type: Option<String>, // "http", "socks4", "socks5"
@@ -85,7 +93,8 @@ struct FtpSettings {
login: Option<String>,
password: Option<String>,
passive: Option<bool>,
timeout: Option<u64>,
read_timeout: Option<u64>,
write_timeout: Option<u64>,
}
pub struct AddIn {
@@ -94,7 +103,8 @@ pub struct AddIn {
login: Option<String>,
password: Option<String>,
passive: bool,
timeout: u64,
read_timeout: u64,
write_timeout: u64,
// TLS
use_tls: bool,
accept_invalid_certs: bool,
@@ -118,11 +128,11 @@ impl AddIn {
login: None,
password: None,
passive: true,
timeout: 120,
read_timeout: 120,
write_timeout: 120,
use_tls: false,
accept_invalid_certs: false,
ca_cert_path: String::new(),
// Инициализация полей прокси
proxy_server: None,
proxy_port: None,
proxy_login: None,
@@ -135,7 +145,7 @@ impl AddIn {
let json_struct: FtpSettings = match serde_json::from_str(json_data){
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 {
@@ -150,12 +160,16 @@ impl AddIn {
self.passive = passive;
}
if let Some(timeout) = json_struct.timeout {
self.timeout = timeout;
if let Some(read_timeout) = json_struct.read_timeout {
self.read_timeout = read_timeout;
}
self.login = json_struct.login.or(self.login.clone());
self.password = json_struct.password.or(self.password.clone());
if let Some(write_timeout) = json_struct.write_timeout {
self.write_timeout = write_timeout;
}
self.login = json_struct.login;
self.password = json_struct.password;
json!({"result": true}).to_string()
@@ -165,11 +179,12 @@ impl AddIn {
let json_struct: FtpProxySettings = match serde_json::from_str(json_data){
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_password = json_struct.password;
self.proxy_type = json_struct.proxy_type;
@@ -181,7 +196,7 @@ impl AddIn {
pub fn initialize(&mut self) -> String {
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() {
@@ -189,6 +204,19 @@ impl AddIn {
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) {
Ok(client) => client,
Err(e) => return e,
@@ -199,7 +227,7 @@ impl AddIn {
self.client = match client.login(login, password) {
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()
@@ -217,11 +245,11 @@ impl AddIn {
let tls_connector = self.get_tls_connector()?;
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
.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_passive_nat_workaround(true);
@@ -230,7 +258,7 @@ impl AddIn {
} else {
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_passive_nat_workaround(true);
@@ -250,7 +278,7 @@ impl AddIn {
match proxy_type.to_lowercase().as_str() {
"socks5" => self.connect_via_socks5(&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 {
self.connect_direct()
@@ -266,7 +294,7 @@ impl AddIn {
};
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> {
@@ -278,12 +306,12 @@ impl AddIn {
};
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> {
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 {
@@ -295,7 +323,7 @@ impl AddIn {
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()
@@ -304,7 +332,7 @@ impl AddIn {
pub fn set_tls(&mut self, use_tls: bool, accept_invalid_certs: bool, ca_cert_path: &str) -> String {
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;
@@ -323,25 +351,25 @@ impl AddIn {
if !self.ca_cert_path.is_empty() {
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)
.map_err(|e| Self::process_error(&e.to_string()))?;
.map_err(|e| process_error(&e.to_string()))?;
tls_builder.add_root_certificate(cert);
}
match tls_builder.build(){
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{
json!({
"result": false,
"error": e
}).to_string()
fn get_client(&self) -> Result<MutexGuard<'_, FtpClient>, String> {
self.client
.as_ref()
.ok_or_else(|| process_error("FTP client is not initialized"))
.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 {
@@ -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 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 должны содержать только сериализуемые значения");
КонецПопытки;
Возврат Результат;
КонецФункции
#КонецОбласти