sharing\natives\windows/
windows.rs

1/*
2 * Authors: Jorge.A Duran & Mario Gónzalez
3 * Company: pispas Technologies SL
4 * Date: April 23, 2023
5 * Description: windows definition and implementation for singleton.
6 */
7use std::ffi::CString;
8
9#[allow(unused)]
10use winapi::um::synchapi::{CreateMutexA, ReleaseMutex, WaitForSingleObject};
11use winapi::um::winnt::HANDLE;
12use std::io::Write;
13use std::net::TcpStream;
14pub type MutexHandle = HANDLE;
15pub enum ResultCode {
16    Ok,
17    Abandoned = winapi::um::winbase::WAIT_ABANDONED as isize,
18}
19
20use std::process::{Command, Stdio};
21use std::thread::sleep;
22
23pub fn powershell_message_box_non_blocking(message: &str) {
24    let title = "UNPISPAS SERVICIO IMPRESION"; 
25    let command = format!(
26        "[reflection.assembly]::LoadWithPartialName('System.Windows.Forms')|out-null;[windows.forms.messagebox]::Show('{}', '{}')",
27        message, title
28    );
29
30    let result = Command::new("powershell")        
31        .arg("-WindowStyle")
32        .arg("Hidden")  
33        .arg("-Command")
34        .arg(command)
35        .stdout(Stdio::null()) 
36        .stderr(Stdio::piped())
37        .spawn(); 
38    
39    if let Err(e) = result {
40        tracing::error!("Error al ejecutar el comando PowerShell: {}", e);
41    }
42}
43
44pub fn create_mutex(name: &str) -> MutexHandle {
45    unsafe {
46        let mutex_name = CString::new(name).unwrap();
47        CreateMutexA(std::ptr::null_mut(), 1, mutex_name.as_ptr())
48    }
49}
50
51pub unsafe fn close_handle(handle: MutexHandle) {
52    unsafe {
53        winapi::um::handleapi::CloseHandle(handle);
54    }
55}
56
57pub fn send_messagebox_to_tray(message: String) -> crate::PisPasResult<()> {
58    match TcpStream::connect(crate::CHANNEL_NAME) {
59        Ok(mut stream) => {
60            // Aquí, pasamos el mensaje real
61            let action = crate::service::Action::MessageBox(message);
62            match stream.write_all(action.to_string().as_bytes()) {
63                Ok(_) => {
64                    tracing::info!("Message sent: {}", action.to_string());
65                    return Ok(());
66                }
67                Err(e) => {
68                    tracing::error!("Failed to write to socket in dedicated thread {}", e);
69                    return Err(anyhow::anyhow!("Failed to write to socket in dedicated thread {}", e));
70                }
71            }
72        }
73        Err(e) => {
74            tracing::error!("Error connecting to pispas-channel: {}", e);
75        }
76    }
77    Ok(())
78}
79
80pub fn stop_pispas_modules() -> crate::PisPasResult<()> {
81    match TcpStream::connect(crate::CHANNEL_NAME) {
82        Ok(mut stream) => {
83            match stream.write_all(crate::service::Action::Stop.to_string().as_bytes()) {
84                Ok(_) => {
85                    tracing::info!("Message sent: {}", crate::service::Action::Stop.to_string());
86                    return Ok(());
87                }
88                Err(e) => {
89                    tracing::error!("Failed to write to socket in dedicated thread {}", e);
90                    return Err(anyhow::anyhow!("Failed to write to socket in dedicated thread {}", e));
91                }
92            }
93        }
94        Err(e) => {
95            tracing::error!("Error connecting to pispas-channel: {}", e);            
96        }
97    }
98    sleep(std::time::Duration::from_secs(1));
99    Ok(())
100}
101
102pub unsafe fn wait_for_single_object(handle: MutexHandle, timeout: u32) -> ResultCode {
103    let code = unsafe { WaitForSingleObject(handle, timeout) };
104    if !(code == winapi::um::winbase::WAIT_ABANDONED || code == winapi::um::winbase::WAIT_OBJECT_0) {
105        ResultCode::Abandoned
106    } else {
107        ResultCode::Ok
108    }
109}
110
111pub unsafe fn release_mutex(handle: MutexHandle) {
112    unsafe { ReleaseMutex(handle) };
113}
114
115#[allow(non_camel_case_types, non_snake_case)]
116#[cfg(target_os = "windows")]
117pub mod native {
118    use std::{
119        ffi::CString,
120        ptr::null_mut,
121        thread::sleep,
122        time::Duration,
123    };
124    use widestring::WideCString;
125    use winapi::{
126        shared::minwindef::{BOOL, DWORD},
127        um::{
128            handleapi::CloseHandle,
129            processthreadsapi::{CreateProcessAsUserW, OpenProcess},
130            winbase::{CREATE_UNICODE_ENVIRONMENT},
131            winnt::{HANDLE, PROCESS_ALL_ACCESS, PROCESS_SUSPEND_RESUME},
132        },
133    };
134    use windows_sys::Win32::System::Environment::{
135        CreateEnvironmentBlock, DestroyEnvironmentBlock,
136    };
137    use windows_sys::Win32::Foundation::GetLastError;
138    use std::ptr;
139    use std::ffi::OsStr;
140    use std::os::windows::ffi::OsStrExt;
141    use winapi::um::winbase::WTSGetActiveConsoleSessionId;
142    use std::ffi::c_void; // Asegúrate de usar el tipo de `std::ffi`
143
144    const WTS_CURRENT_SERVER_HANDLE: HANDLE = null_mut();
145    const WTS_USER_NAME: DWORD = 5;
146
147    #[repr(C)]
148    struct WTS_SESSION_INFO {
149        SessionId: DWORD,
150        pWinStationName: *mut u16,
151        State: DWORD,
152    }
153
154    enum WTSSessionState {
155        WTSActive = 0,
156        WTSConnected = 1,
157        WTSDisconnected = 4,
158    }
159
160    #[link(name = "wtsapi32")]
161    extern "system" {
162        fn LoadLibraryA(lpFileName: *const i8) -> HANDLE;
163        fn GetProcAddress(hModule: HANDLE, lpProcName: *const i8) -> *mut c_void;
164        fn FreeLibrary(hLibModule: HANDLE) -> BOOL;
165        fn WTSQueryUserToken(SessionId: DWORD, phToken: *mut HANDLE) -> BOOL;
166        // fn WTSEnumerateSessionsW(hServer: HANDLE, Reserved: DWORD, Version: DWORD, ppSessionInfo: *mut *mut WTS_SESSION_INFO, pCount: *mut DWORD) -> BOOL;
167        fn WTSQuerySessionInformationW(hServer: HANDLE, SessionId: DWORD, WTSInfoClass: DWORD, ppBuffer: *mut *mut u16, pBytesReturned: *mut DWORD) -> BOOL;
168        fn WTSFreeMemory(pMemory: *mut c_void);
169    }
170
171    fn load_wtsapi32_module() -> Result<HANDLE, &'static str> {
172        let library_name = CString::new("wtsapi32.dll").expect("CString::new failed");
173        let h_module = unsafe { LoadLibraryA(library_name.as_ptr()) };
174        if h_module.is_null() {
175            Err("Failed to load wtsapi32.dll")
176        } else {
177            Ok(h_module)
178        }
179    }
180
181    fn get_function_addresses(h_module: HANDLE) -> Result<
182        (
183            extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL,
184            extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL
185        ),
186        &'static str
187    > {
188        let function_name = CString::new("WTSEnumerateSessionsW").unwrap();
189        let enum_sessions: Option<extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL> = unsafe {
190            std::mem::transmute(GetProcAddress(h_module, function_name.as_ptr()))
191        };
192
193        let function_name_cstr = CString::new("WTSQuerySessionInformationW").unwrap();
194        let query_session: Option<extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL> = unsafe {
195            std::mem::transmute(GetProcAddress(h_module, function_name_cstr.as_ptr()))
196        };
197
198        match (enum_sessions, query_session) {
199            (Some(enumerate_sessions_func), Some(query_session_func)) => Ok((enumerate_sessions_func, query_session_func)),
200            _ => Err("Failed to get procedure addresses.")
201        }
202    }
203
204    fn to_wide_cstring(value: &str) -> WideCString {
205        WideCString::from_str(value).unwrap()
206    }
207
208    fn create_command_line_wide(executable_path: &str, script_path: &str) -> WideCString {
209        let cmd = format!("\"{}\" \"{}\"", executable_path.trim_end_matches('\\'), script_path);
210        WideCString::from_str(&cmd).unwrap()
211    }
212
213    fn wait_for_user_session(session_id: DWORD) -> anyhow::Result<()> {
214        loop {
215            let state = query_session_state(session_id)?;
216            if state == WTSSessionState::WTSActive as DWORD {
217                return Ok(()); // WTSActive
218            }
219            sleep(Duration::from_millis(500));
220        }
221    }
222
223    fn query_session_state(session_id: DWORD) -> anyhow::Result<DWORD> {
224        let mut p_buffer: *mut u16 = null_mut();
225        let mut bytes_returned: DWORD = 0;
226
227        unsafe {
228            let success = WTSQuerySessionInformationW(
229                WTS_CURRENT_SERVER_HANDLE,
230                session_id,
231                0, // WTSConnectState
232                &mut p_buffer,
233                &mut bytes_returned,
234            );
235
236            if success == 0 {
237                return Err(anyhow::anyhow!("Failed to query session information"));
238            }
239
240            let state = *p_buffer as DWORD;
241            WTSFreeMemory(p_buffer as *mut c_void);
242            Ok(state)
243        }
244    }
245
246    fn wait_for_any_active_session(enumerate_sessions_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL) -> anyhow::Result<()> {
247        loop {
248            let mut session_info_ptr: *mut WTS_SESSION_INFO = null_mut();
249            let mut session_count: DWORD = 0;
250
251            let success = enumerate_sessions_func(
252                WTS_CURRENT_SERVER_HANDLE,
253                0,
254                1,
255                &mut session_info_ptr,
256                &mut session_count,
257            );
258
259            if success != 0 {
260                unsafe {
261                    for i in 0..session_count {
262                        let session = session_info_ptr.offset(i as isize);
263
264                        if (*session).State == WTSSessionState::WTSActive as u32 {
265                            return Ok(()); // Found an active session
266                        }
267                    }
268                }
269            }
270            sleep(Duration::from_secs(1));
271        }
272    }
273
274    fn launch_process_for_user(executable_path: &str, command_line: &str, session_id: DWORD) -> Option<HANDLE> {
275        unsafe {
276            if let Err(e) = wait_for_user_session(session_id) {
277                tracing::error!("Failed to wait for user session: {}", e);
278                return None;
279            }
280
281            // Añadimos un sleep adicional para asegurar que la sesión esté completamente inicializada
282            sleep(Duration::from_secs(1));
283
284            let mut user_token: HANDLE = null_mut();
285            if WTSQueryUserToken(session_id, &mut user_token) != 0 {
286                let exe_path_wide = to_wide_cstring(executable_path);
287                let cmd_line_wide = create_command_line_wide(executable_path, command_line);
288                let path_current_dir_wide = to_wide_cstring(&crate::paths::bin_dir().display().to_string());
289
290                tracing::info!("Executable path: {}", executable_path);
291                tracing::info!("Command line: {:?}", cmd_line_wide);
292
293                let mut startup_info = std::mem::zeroed::<winapi::um::processthreadsapi::STARTUPINFOW>();
294                startup_info.cb = std::mem::size_of::<winapi::um::processthreadsapi::STARTUPINFOW>() as u32;
295
296                let desktop_name_wide: Box<Vec<u16>> = Box::new(to_wide_string("WinSta0\\Default"));
297                startup_info.lpDesktop = desktop_name_wide.as_ptr() as *mut u16;
298                                
299                let mut environment_block: *mut c_void = std::ptr::null_mut();
300                // create the environment block for the user
301                if CreateEnvironmentBlock(&mut environment_block, user_token as *mut c_void, 1)  == 0 {
302                    let error_code = GetLastError();
303                    tracing::error!("Failed to create environment block for user. Error code: {}", error_code);
304                    CloseHandle(user_token);
305                    return None;
306                }
307                
308                let _keep_alive = desktop_name_wide; // Esto mantiene la memoria durante el uso
309
310                let mut process_info = std::mem::zeroed::<winapi::um::processthreadsapi::PROCESS_INFORMATION>();
311                tracing::info!("exe_path: {:?}, command_line: {:?}, path_current_dir: {:?}", cmd_line_wide, cmd_line_wide, path_current_dir_wide);
312                
313            
314                let success = CreateProcessAsUserW(
315                    user_token,
316                    exe_path_wide.as_ptr() as *mut u16,
317                    cmd_line_wide.as_ptr() as *mut u16,
318                    null_mut(),
319                    null_mut(),
320                    0,
321                    CREATE_UNICODE_ENVIRONMENT,
322                    environment_block as *mut winapi::ctypes::c_void,
323                    path_current_dir_wide.as_ptr() as *mut u16,
324                    &mut startup_info,
325                    &mut process_info,
326                );
327
328                if success != 0 {
329                    CloseHandle(user_token);
330                    DestroyEnvironmentBlock(environment_block);
331                    return Some(process_info.hProcess);
332                } else {
333                    let error_code = GetLastError();
334                    tracing::error!("CreateProcessAsUserW failed: Error code = {}", error_code);
335                }
336                DestroyEnvironmentBlock(environment_block);
337                CloseHandle(user_token);
338            } else {
339                let error_code = GetLastError();
340                tracing::error!("WTSQueryUserToken failed for session {}: Error code = {}", session_id, error_code);
341            }
342        }
343
344        None
345    }
346
347    fn enumerate_and_launch(
348        executable_path: &str,
349        command_line: &str,
350        all_users: bool,
351        first_execution: bool,
352        enumerate_sessions_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL,
353        query_session_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL
354    ) -> crate::PisPasResult<Option<HANDLE>> {
355        wait_for_any_active_session(enumerate_sessions_func)?;
356
357        let mut session_info_ptr: *mut WTS_SESSION_INFO = null_mut();
358        let mut session_count: DWORD = 0;
359
360        let success = enumerate_sessions_func(
361            WTS_CURRENT_SERVER_HANDLE,
362            0,
363            1,
364            &mut session_info_ptr,
365            &mut session_count,
366        );
367
368        if success != 0 {
369            tracing::info!("Found {} sessions.", session_count);
370            unsafe {
371                for i in 0..session_count {
372                    if session_info_ptr.is_null() {
373                        return Err(anyhow::anyhow!("Session info pointer is null"));
374                    }
375                    let session = session_info_ptr.offset(i as isize);
376
377                    if (*session).State == WTSSessionState::WTSActive as u32 ||
378                        (*session).State == WTSSessionState::WTSConnected as u32 ||
379                        (*session).State == WTSSessionState::WTSDisconnected as u32 {
380                        let mut data: *mut c_void = null_mut();
381                        let mut bytes: DWORD = 0;
382
383                        if query_session_func(WTS_CURRENT_SERVER_HANDLE, (*session).SessionId, WTS_USER_NAME, &mut data, &mut bytes) != 0 {
384                            let user_name: *const u16 = data as *const u16;
385                            let user_slice = std::slice::from_raw_parts(user_name, bytes as usize / 2);
386                            match String::from_utf16(user_slice) {
387                                Ok(mut user) => {
388                                    user = user.trim_end_matches('\0').to_string(); // Remove null characters
389                                    if !user.trim().is_empty() && (*session).SessionId != 0 {
390                                        tracing::info!("Session {}: User = {}", (*session).SessionId, user);
391                                        let bin_dir = crate::paths::bin_dir().display().to_string();
392                                        tracing::info!("bin_dir: {}", bin_dir);
393                                        if first_execution {
394                                            if let Err(e) = set_permissions_robust(&bin_dir, &user) {
395                                                tracing::error!("Failed to set permissions for user {}: {}", user, e);
396                                            } else {
397                                                tracing::info!("Permissions set for user {}", user);
398                                                sleep(Duration::from_secs(1));
399                                            }
400                                        }
401
402                                        if let Some(process_handle) = launch_process_for_user(executable_path, command_line, (*session).SessionId) {
403                                            tracing::info!("Process launched for user {}", user);
404                                            if !all_users {
405                                                return Ok(Some(process_handle));
406                                            }
407                                        } else {
408                                            tracing::error!("Failed to launch process for user {}", user);
409                                        }
410                                    } else {
411                                        tracing::warn!("Ignoring session {}: User name is empty or session ID is 0", (*session).SessionId);
412                                    }
413                                }
414                                Err(e) => {
415                                    tracing::info!("Failed to convert UTF-16 to string: {}", e);
416                                    return Err(anyhow::anyhow!("Error converting UTF-16 to string"));
417                                }
418                            }
419                        }
420                    }
421                }
422            }
423        } else {
424            return Err(anyhow::anyhow!("Error enumerate_sessions_func"));
425        }
426        Ok(None)
427    }
428    // Function to convert &str to Vec<u16> for Windows compatibility
429    fn to_wide_string(s: &str) -> Vec<u16> {
430        OsStr::new(s).encode_wide().chain(Some(0)).collect()
431    }
432    struct WideString {
433        inner: Vec<u16>,
434    }
435
436    impl WideString {
437        pub fn new(s: &str) -> Self {
438            Self {
439                inner: to_wide_string(s),
440            }
441        }
442
443        pub fn as_ptr(&self) -> *const u16 {
444            self.inner.as_ptr()
445        }
446    }
447    // other way to launch poss as logged in user
448    pub fn launch_process_as_logged_in_user(executable_path: &str, command_line: &str, first_launch: bool) -> crate::PisPasResult<Option<HANDLE>> {    
449        let mut attempts = 0;
450        let user = loop {
451            if attempts >= 20 {
452                return Err(anyhow::anyhow!("No user found logged in after multiple attempts."));
453            }
454
455            let output = std::process::Command::new("powershell")
456                .args(&["-Command", "(Get-WmiObject -Class Win32_ComputerSystem).UserName"])
457                .output()
458                .map_err(|e| anyhow::anyhow!("Failed to execute PowerShell command to get logged in user: {}", e))?;
459
460            if output.status.success() {
461                let user = String::from_utf8_lossy(&output.stdout).trim().to_string();
462                if !user.is_empty() {
463                    break user;
464                } else {
465                    tracing::error!("No user found logged in, retrying...");
466                }
467            } else {
468                let stderr = String::from_utf8_lossy(&output.stderr);
469                tracing::error!("Error getting logged in user: {}, retrying...", stderr);
470            }
471
472            attempts += 1;
473            sleep(Duration::from_secs(1));
474        };
475
476
477        tracing::info!("Logged in user: {}", user);
478        let username = user.split('\\').last().unwrap_or(&user);
479        tracing::info!("Extracted username: {}", username);
480        
481        //adjust permissions for the user on the specified executable
482        if first_launch{
483            if let Err(e) = set_permissions_robust(executable_path, &username) {
484                tracing::error!("Failed to set permissions for user {}: {}", user, e);
485            } else {
486                tracing::info!("Permissions set for user {}", user);
487                sleep(Duration::from_secs(1));
488            }
489        }        
490        
491        // obtain the active session ID and user token
492        let session_id = unsafe { WTSGetActiveConsoleSessionId() };
493        if session_id == u32::MAX {
494            let error_code = unsafe { GetLastError() };
495            tracing::error!("Failed to get active session ID: {}", error_code);
496            return Err(anyhow::anyhow!("Failed to get active session ID: {}", error_code));
497        }
498
499        let mut user_token: HANDLE = ptr::null_mut();
500        if unsafe { WTSQueryUserToken(session_id, &mut user_token) } == 0 {
501            let error_code = unsafe { GetLastError() };
502            tracing::error!("Failed to get user token: {}", error_code);
503            return Err(anyhow::anyhow!("Failed to get user token: {}", error_code));
504        }
505        
506        let full_command = format!("\"{}\" \"{}\"", executable_path, command_line);
507        let command_utf16 = OsStr::new(&full_command).encode_wide().chain(Some(0)).collect::<Vec<u16>>();
508
509        if first_launch {
510            sleep(Duration::from_secs(4));    
511        }        
512        let mut startup_info = unsafe { std::mem::zeroed::<winapi::um::processthreadsapi::STARTUPINFOW>() };
513        startup_info.cb = std::mem::size_of::<winapi::um::processthreadsapi::STARTUPINFOW>() as u32;
514        let desktop_name = WideString::new("WinSta0\\Default");
515        startup_info.lpDesktop = desktop_name.as_ptr() as *mut u16;
516
517        // Structure to receive process information
518        let mut process_info = unsafe { std::mem::zeroed::<winapi::um::processthreadsapi::PROCESS_INFORMATION>() };
519
520        // Lanza el proceso en el contexto del usuario logeado
521        let success = unsafe {
522            CreateProcessAsUserW(
523                user_token,
524                ptr::null(),
525                command_utf16.as_ptr() as *mut _,
526                ptr::null_mut(),
527                ptr::null_mut(),
528                0,
529                CREATE_UNICODE_ENVIRONMENT,
530                ptr::null_mut(),
531                ptr::null(),
532                &mut startup_info,
533                &mut process_info,
534            )
535        };
536        
537        unsafe { CloseHandle(user_token) };
538
539        if success == 0 {
540            let error_code = unsafe { GetLastError() };
541            tracing::error!("Failed to launch process: {}", error_code);
542            Err(anyhow::anyhow!("Failed to launch process: {}", error_code))
543        } else {
544            tracing::info!("Process launched successfully in the context of the logged-in user.");
545            Ok(Some(process_info.hProcess))
546        }
547    }
548    
549    pub fn run_in_all_sessions(executable_path: &str, command_line: &str, all_user: bool, first_execution: bool) -> crate::PisPasResult<Option<HANDLE>> {
550        match load_wtsapi32_module() {
551            Ok(h_module) => {
552                match get_function_addresses(h_module) {
553                    Ok((enumerate_sessions_func, query_session_func)) => {
554                        tracing::info!("Enumerating sessions...");
555                        let result = enumerate_and_launch(executable_path, command_line, all_user, first_execution, enumerate_sessions_func, query_session_func);
556                        unsafe { FreeLibrary(h_module); }
557                        result
558                    }
559                    Err(e) => {
560                        unsafe { FreeLibrary(h_module); }
561                        tracing::error!("{}", e);
562                        Err(anyhow::anyhow!("Error getting function addresses"))
563                    }
564                }
565            }
566            Err(e) => {
567                tracing::error!("error {}", e);
568                Err(anyhow::anyhow!("Error loading wtsapi32 module"))
569            }
570        }
571    }
572
573    pub fn open_process(pid: u32) -> Option<HANDLE> {
574        let desired_access = PROCESS_ALL_ACCESS | PROCESS_SUSPEND_RESUME;
575        let process_handle = unsafe { OpenProcess(desired_access, 0, pid) };
576
577        if process_handle.is_null() {
578            unsafe {
579                tracing::info!("Error obtaining process handle. Error: {}", GetLastError());
580            }
581            None
582        } else {
583            tracing::info!("Process handle: {:?}", process_handle);
584            Some(process_handle)
585        }
586    }
587
588    /// Hybrid approach - use PowerShell with better error handling
589    pub fn set_permissions_robust(path: &str, user: &str) -> Result<(), String> {
590        let user = user.trim_end_matches('\0');
591        let path = path.trim_end_matches('\0');
592
593        let powershell_script = format!(
594            r#"
595        try {{
596            $path = '{}'
597            $user = '{}'
598            $successCount = 0
599            $errorCount = 0
600            
601            # Set permissions on root folder first
602            try {{
603                $acl = Get-Acl $path
604                $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','ContainerInherit,ObjectInherit','None','Allow')
605                $acl.SetAccessRule($rule)
606                Set-Acl $path $acl
607                $successCount++
608                Write-Output "Root permissions set: $path"
609            }} catch {{
610                $errorCount++
611                Write-Warning "Failed to set root permissions: $($_.Exception.Message)"
612            }}
613            
614            # Process all child items
615            Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object {{
616                try {{
617                    $acl = Get-Acl $_.FullName
618                    $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','ContainerInherit,ObjectInherit','None','Allow')
619                    $acl.SetAccessRule($rule)
620                    Set-Acl $_.FullName $acl
621                    $successCount++
622                }} catch {{
623                    $errorCount++
624                    Write-Warning "Failed: $($_.FullName) - $($_.Exception.Message)"
625                }}
626            }}
627            
628            Write-Output "Completed: $successCount successful, $errorCount errors"
629        }} catch {{
630            Write-Error $_.Exception.Message
631            exit 1
632        }}
633        "#,
634            path, user
635        );
636
637        tracing::info!("Executing robust recursive permissions for: {}", path);
638
639        let output = std::process::Command::new("powershell")
640            .args(&["-ExecutionPolicy", "Bypass", "-Command", &powershell_script])
641            .output()
642            .expect("Failed to execute process");
643
644        let stdout = String::from_utf8_lossy(&output.stdout).to_string();
645        let stderr = String::from_utf8_lossy(&output.stderr).to_string();
646
647        tracing::info!("robust_permissions stdout: {}", stdout);
648        if !stderr.is_empty() {
649            tracing::warn!("robust_permissions stderr: {}", stderr);
650        }
651
652        if !output.status.success() {
653            return Err(format!("Failed to set robust permissions: {}", stderr));
654        }
655
656        Ok(())
657    }
658    pub fn set_permissions_for_user(path: &str, user: &str) -> Result<(), String> {
659        let user = user.trim_end_matches('\0');
660        let path = path.trim_end_matches('\0');
661        let powershell_script = format!(
662            r#"
663        try {{
664            $path = '{}'
665            $user = '{}'
666            $acl = Get-Acl $path
667            $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','None','None','Allow')
668            $acl.SetAccessRule($rule)
669            Set-Acl $path $acl
670            Write-Output "Permissions set successfully"
671        }} catch {{
672            Write-Error $_.Exception.Message
673        }}
674        "#,
675            path, user
676        );
677
678        tracing::info!("powershell_script: {:?}", powershell_script);
679
680        let output = std::process::Command::new("powershell")
681            .args(&["-Command", &powershell_script])
682            .output()
683            .expect("Failed to execute process");
684
685        let stdout = String::from_utf8_lossy(&output.stdout).to_string();
686        let stderr = String::from_utf8_lossy(&output.stderr).to_string();
687
688        tracing::info!("set_permissions_for_user stdout: {}", stdout);
689        tracing::info!("set_permissions_for_user stderr: {}", stderr);
690
691        if !output.status.success() {
692            return Err(format!("Failed to set permissions: {}", stderr));
693        }
694
695        Ok(())
696    }
697}
698
699pub fn get_name_service() -> String {
700    let path = match std::env::current_exe() {
701        Ok(path) => path,
702        Err(e) => {
703            println!("Error al obtener el nombre del archivo ejecutable: {:?}", e);
704            return "ServiceDefault".to_string();
705        }
706    };
707    let name = match path.file_name() {
708        Some(name_without_extension) => name_without_extension.to_str().unwrap(),
709        None => {
710            println!("Error al obtener el nombre del archivo ejecutable");
711            return "ServiceDefault".to_string();
712        }
713    };
714    let _name_without_extension = name.replace(".exe", "");
715    _name_without_extension.to_string()
716}