import tkinter as tk from tkinter import ttk, filedialog import os import socket import threading import time class FTPClient: def __init__(self): self.server_dir = "" self.root = tk.Tk() self.root.title("Cliente de Archivos") self.root.geometry("950x600") # Valores predeterminados para la dirección IP y el puerto default_ip = "127.0.0.1" default_port = "12345" # Frame para la conexión self.connection_frame = tk.Frame(self.root) self.connection_frame.pack(pady=10) self.con_frame = tk.LabelFrame(self.connection_frame, text="Conexion") self.con_frame.grid(row=0, column=0, padx=10, pady=5) self.label_ip = tk.Label(self.con_frame, text="IP del Servidor:") self.label_ip.grid(row=0, column=0, padx=5) self.entry_ip = tk.Entry(self.con_frame, width=20) self.entry_ip.insert(0, default_ip) self.entry_ip.grid(row=0, column=1, padx=5) self.label_port = tk.Label(self.con_frame, text="Puerto:") self.label_port.grid(row=0, column=2, padx=5) self.entry_port = tk.Entry(self.con_frame, width=10) self.entry_port.insert(0, default_port) self.entry_port.grid(row=0, column=3, padx=5) self.button_connect = tk.Button(self.con_frame, text="Conectar", width=10, command=self.connect_to_server) self.button_connect.grid(row=0, column=4, padx=5) self.button_disconnect = tk.Button(self.con_frame, text="Desconectar", width=10, state=tk.DISABLED, command=self.disconnect_from_server) self.button_disconnect.grid(row=0, column=5, padx=5) # Frame principal para los archivos locales y directorios del servidor self.main_frame = tk.Frame(self.root) self.main_frame.pack(fill="both", expand=True, padx=20, pady=5) # Frame para la configuración de la carpeta local del cliente self.config_frame = tk.LabelFrame(self.main_frame, text="Configuración de Carpeta Local") self.config_frame.pack(fill="x", expand=True, padx=20, pady=5, anchor="center") # Alinear al centro self.label_local_folder = tk.Label(self.config_frame, text="Carpeta Local:") self.label_local_folder.grid(row=0, column=0, padx=20, sticky="e") self.entry_local_folder = tk.Entry(self.config_frame, width=50) self.entry_local_folder.grid(row=0, column=1, padx=20, sticky="w") self.button_browse = tk.Button(self.config_frame, text="Explorar", width=10, command=self.browse_local_folder) self.button_browse.grid(row=0, column=2, padx=20, pady=10, sticky="e") self.button_apply = tk.Button(self.config_frame, text="Aplicar", width=10, command=self.apply_local_folder) self.button_apply.grid(row=0, column=3, padx=5, pady=10, sticky="w") # Frame para los botones de navegación de archivos locales self.local_nav_frame = tk.Frame(self.main_frame) self.local_nav_frame.pack(side="left", padx=5) self.button_up = tk.Button(self.local_nav_frame, text="Subir", command=self.go_up) self.button_up.pack(side="top", pady=5) self.button_open = tk.Button(self.local_nav_frame, text="Abrir", command=self.open_directory) self.button_open.pack(side="top", pady=5) # Frame para los botones de navegación de archivos del servidor self.server_nav_frame = tk.Frame(self.main_frame) self.server_nav_frame.pack(side="right", padx=5) self.server_button_up = tk.Button(self.server_nav_frame, text="Subir", command=self.go_up_server) self.server_button_up.pack(side="top", pady=5) self.server_button_open = tk.Button(self.server_nav_frame, text="Abrir", command=self.open_directory_server) self.server_button_open.pack(side="top", pady=5) # Frame para todos los archivos self.files_frame = tk.Frame(self.main_frame) self.files_frame.pack(fill="both", expand=True, padx=20, pady=5) # Frame para los archivos locales self.local_files_frame = tk.LabelFrame(self.files_frame, text="Archivos Locales") self.local_files_frame.pack(side="left", fill="both", expand=True) self.local_files_list = tk.Listbox(self.local_files_frame, width=50, height=20) self.local_files_list.pack(side="left", fill="both", expand=True) self.local_files_scrollbar = ttk.Scrollbar(self.local_files_frame, orient="vertical") self.local_files_scrollbar.config(command=self.local_files_list.yview) self.local_files_scrollbar.pack(side="right", fill="y") self.local_files_list.config(yscrollcommand=self.local_files_scrollbar.set) # Frame para los archivos del servidor self.server_files_frame = tk.LabelFrame(self.files_frame, text="Archivos del Servidor") self.server_files_frame.pack(side="right", fill="both", expand=True) self.server_files_list = tk.Listbox(self.server_files_frame, width=50, height=20) self.server_files_list.pack(side="left", fill="both", expand=True) self.server_files_scrollbar = ttk.Scrollbar(self.server_files_frame, orient="vertical") self.server_files_scrollbar.config(command=self.server_files_list.yview) self.server_files_scrollbar.pack(side="right", fill="y") self.server_files_list.config(yscrollcommand=self.server_files_scrollbar.set) # Frame para el botón y la ProgressBar self.upload_frame = tk.Frame(self.main_frame) self.upload_frame.pack(side="left", fill="both", pady=10) self.button_send_file = tk.Button(self.upload_frame, text="Enviar Archivo", command=self.send_selected_file) self.button_send_file.pack(side="left", padx=10) self.entry_server_path = tk.Entry(self.upload_frame, width=50) self.entry_server_path.pack(side="left", pady=5) self.progress_bar = ttk.Progressbar(self.upload_frame, orient="horizontal", length=200, mode="determinate") self.progress_bar.pack(side="right", padx=10) self.timer_label = tk.Label(self.upload_frame, text="") self.timer_label.pack() self.client_socket = None self.monitor_thread = None # Hilo para monitorear el buzón de entrada def connect_to_server(self): server = self.entry_ip.get() port = int(self.entry_port.get()) self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: print("Conectando al servidor...") self.client_socket.settimeout(10) # Establecer un tiempo de espera de 10 segundos para la respuesta del servidor self.client_socket.connect((server, port)) credentials = "user:password" self.client_socket.send(credentials.encode()) print(f"Conectado al servidor {server}:{port}") # Esperar y recibir el mensaje de bienvenida del servidor response = self.receive_response(self.client_socket) print("Mensaje de bienvenida del servidor:", response.decode("utf-8")) # Si el mensaje indica que el servidor está listo, continuar con la configuración if response.startswith(b"pyftpdlib"): # Enviar comando para obtener el directorio del servidor self.send_data(b"PWD", self.client_socket) server_dir_response = self.receive_response(self.client_socket) print("Respuesta del servidor al solicitar el directorio:", server_dir_response.decode("utf-8")) default_server_dir = server_dir_response.decode("utf-8").split('"')[1] # Extraer el directorio del servidor de la respuesta print(f"Directorio predeterminado del servidor: {default_server_dir}") self.update_default_server_dir(default_server_dir) # Actualizar la interfaz con el directorio predeterminado del servidor # Restaurar el tiempo de espera del socket a 5 segundos self.client_socket.settimeout(5) self.button_connect.config(state=tk.DISABLED) self.button_disconnect.config(state=tk.NORMAL) self.monitor_thread = threading.Thread(target=self.monitor_inbox) self.monitor_thread.start() # Iniciar el hilo de monitoreo del buzón de entrada else: print("Respuesta inesperada del servidor:", response.decode("utf-8")) except socket.timeout: print("Conexión agotada. Por favor, verifique la IP y el puerto del servidor.") except Exception as e: print(f"Error al conectar al servidor: {e}") def disconnect_from_server(self): if self.client_socket: self.client_socket.close() print("Disconnected from server.") self.button_connect.config(state=tk.NORMAL) self.button_disconnect.config(state=tk.DISABLED) def send_selected_file(self): selected_file = self.local_files_list.get(tk.ACTIVE) full_path = os.path.join(self.entry_local_folder.get(), selected_file) if os.path.isfile(full_path): recipient = "" # Introduce el destinatario si es necesario threading.Thread(target=self.upload_file, args=(full_path, recipient)).start() # Ejecutar la función de carga en un hilo separado else: print("Please select a file to send.") def upload_file(self, filename, recipient): try: with open(filename, 'rb') as file: self.send_data(b'SEND_FILE', self.client_socket) self.send_data(recipient.encode('utf-8'), self.client_socket) self.send_file(filename, self.client_socket) print(f"File '{filename}' uploaded successfully to {recipient}") except Exception as e: print(f"Error uploading file: {e}") def send_file(self, filename, client_socket): with open(filename, 'rb') as file: filename_bytes = os.path.basename(filename).encode('utf-8') self.send_data(filename_bytes, client_socket) file_size = os.path.getsize(filename) self.send_data(str(file_size).encode('utf-8'), client_socket) sent_bytes = 0 while True: chunk = file.read(1024) if not chunk: break client_socket.sendall(chunk) sent_bytes += len(chunk) # Actualizar la ProgressBar progress = min(int(sent_bytes / file_size * 100), 100) self.progress_bar['value'] = progress self.root.update_idletasks() # Actualizar la interfaz gráfica def monitor_inbox(self): while True: self.send_data(b'CHECK_INBOX', self.client_socket) data = self.receive_response(self.client_socket) if data: filename = data.decode('utf-8') self.receive_file(filename) time.sleep(300) # Check every 5 minutes def receive_file(self, filename): try: with open(filename, 'wb') as file: filename_bytes = filename.encode('utf-8') self.send_data(b'RETRIEVE_FILE', self.client_socket) self.send_data(filename_bytes, self.client_socket) file_size = int(self.receive_response(self.client_socket).decode('utf-8')) received_bytes = 0 while received_bytes < file_size: chunk = self.client_socket.recv(1024) if not chunk: break file.write(chunk) received_bytes += len(chunk) print(f"File '{filename}' downloaded successfully") except Exception as e: print(f"Error receiving file: {e}") def receive_response(self, client_socket): try: print("Esperando respuesta del servidor...") response_data = b'' # Inicializar un buffer para almacenar los datos recibidos # Recibir los datos hasta que se encuentre el delimitador especial while True: chunk = client_socket.recv(1024) if not chunk: break response_data += chunk if b'\n' in chunk: break response_str = response_data # Devolver los datos recibidos tal cual print(f"Respuesta del servidor recibida: {response_str}") return response_str except Exception as e: print(f"Error al recibir respuesta del servidor: {e}") return None def send_data(self, data, client_socket): data_size = len(data) data_size_bytes = data_size.to_bytes(4, byteorder='big') client_socket.sendall(data_size_bytes) total_sent = 0 while total_sent < data_size: sent = client_socket.send(data[total_sent:]) if sent == 0: raise RuntimeError("Connection closed unexpectedly.") total_sent += sent def browse_local_folder(self): folder_path = filedialog.askdirectory() self.entry_local_folder.delete(0, tk.END) self.entry_local_folder.insert(0, folder_path) self.update_local_files_list() def apply_local_folder(self): folder_path = self.entry_local_folder.get() if os.path.isdir(folder_path): self.update_local_files_list() else: print("Invalid directory path.") def go_up(self): current_path = self.entry_local_folder.get() parent_path = os.path.dirname(current_path) if os.path.isdir(parent_path): self.entry_local_folder.delete(0, tk.END) self.entry_local_folder.insert(0, parent_path) self.update_local_files_list() def open_directory(self): selected_path = self.local_files_list.get(tk.ACTIVE) full_path = os.path.join(self.entry_local_folder.get(), selected_path) if os.path.isdir(full_path): self.entry_local_folder.delete(0, tk.END) self.entry_local_folder.insert(0, full_path) self.update_local_files_list() def go_up_server(self): current_path = self.entry_server_folder.get() parent_path = os.path.dirname(current_path) if parent_path: self.entry_server_folder.delete(0, tk.END) self.entry_server_folder.insert(0, parent_path) self.update_server_files_list() def open_directory_server(self): selected_path = self.server_files_list.get(tk.ACTIVE) full_path = os.path.join(self.entry_server_folder.get(), selected_path) if os.path.isdir(full_path): self.entry_server_folder.delete(0, tk.END) self.entry_server_folder.insert(0, full_path) self.update_server_files_list() def update_local_files_list(self): folder_path = self.entry_local_folder.get() self.local_files_list.delete(0, tk.END) if os.path.isdir(folder_path): files = os.listdir(folder_path) for file in files: self.local_files_list.insert(tk.END, file) def update_default_server_dir(self, directory): # Actualizar el atributo del directorio del servidor self.server_dir = directory # Limpiar la lista de archivos del servidor self.server_files_list.delete(0, tk.END) # Obtener la lista de archivos en el nuevo directorio del servidor self.send_data(b'LIST_FILES', self.client_socket) self.send_data(directory.encode('utf-8'), self.client_socket) files_data = self.receive_response(self.client_socket) # Decodificar los datos recibidos y agregar cada archivo a la lista files = files_data.decode('utf-8').split('\n') for file in files: self.server_files_list.insert(tk.END, file) def run(self): self.root.mainloop() def main(): client = FTPClient() client.run() if __name__ == "__main__": main()