161 lines
5.8 KiB
Python
161 lines
5.8 KiB
Python
from smb.SMBConnection import SMBConnection
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class SambaManager:
|
|
def __init__(self, server_ip, share_name, username, password, client_machine_name="smbclient"):
|
|
self.server_ip = server_ip
|
|
self.share_name = share_name
|
|
self.username = username
|
|
self.password = password
|
|
self.client_machine_name = client_machine_name
|
|
self.conn = SMBConnection(username, password, client_machine_name, "server_name", use_ntlm_v2=True)
|
|
self.conn.connect(server_ip, 139)
|
|
|
|
def clone(self):
|
|
"""Creates a new independent connection with the same credentials."""
|
|
return SambaManager(self.server_ip, self.share_name, self.username, self.password, self.client_machine_name)
|
|
|
|
def list_path(self, current_dir_path="/"):
|
|
try:
|
|
files = self.conn.listPath(self.share_name, current_dir_path)
|
|
items = []
|
|
for f in files:
|
|
if f.filename in ['.', '..']:
|
|
continue
|
|
|
|
normalized_current_path = current_dir_path
|
|
if normalized_current_path != '/' and not normalized_current_path.endswith('/'):
|
|
normalized_current_path += '/'
|
|
|
|
full_item_path = f"{normalized_current_path}{f.filename}".replace("\\\\", "/")
|
|
|
|
items.append({
|
|
"name": f.filename,
|
|
"is_directory": f.isDirectory,
|
|
"size": f.file_size,
|
|
"path": full_item_path,
|
|
"last_modified": f.last_write_time
|
|
})
|
|
return items
|
|
except Exception as e:
|
|
logger.error(f"Error listing path '{current_dir_path}' on share '{self.share_name}': {e}", exc_info=True)
|
|
return []
|
|
|
|
def search_files(self, query, start_path="/"):
|
|
"""
|
|
Recursively searches for files matching query (partial, case-insensitive).
|
|
"""
|
|
matches = []
|
|
try:
|
|
stack = [start_path]
|
|
while stack:
|
|
current_path = stack.pop()
|
|
try:
|
|
files = self.conn.listPath(self.share_name, current_path)
|
|
except Exception:
|
|
continue # Skip unreadable dirs
|
|
|
|
for f in files:
|
|
if f.filename in ['.', '..']: continue
|
|
|
|
normalized_current_path = current_path
|
|
if normalized_current_path != '/' and not normalized_current_path.endswith('/'):
|
|
normalized_current_path += '/'
|
|
|
|
full_item_path = f"{normalized_current_path}{f.filename}".replace("\\", "/")
|
|
|
|
if query.lower() in f.filename.lower():
|
|
matches.append({
|
|
"name": f.filename,
|
|
"is_directory": f.isDirectory,
|
|
"size": f.file_size,
|
|
"path": full_item_path,
|
|
"last_modified": f.last_write_time
|
|
})
|
|
|
|
if f.isDirectory:
|
|
stack.append(full_item_path)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error searching path '{start_path}': {e}")
|
|
|
|
return matches
|
|
|
|
|
|
def delete_file(self, path):
|
|
try:
|
|
self.conn.deleteFiles(self.share_name, path)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def upload_file(self, path, file_obj):
|
|
try:
|
|
logger.info(f"Uploading file to share '{self.share_name}' at path: '{path}'")
|
|
self.conn.storeFile(self.share_name, path, file_obj)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def download_file(self, path, file_obj):
|
|
try:
|
|
self.conn.retrieveFile(self.share_name, path, file_obj)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
if "Scan canceled by user" in str(e):
|
|
raise e
|
|
return {"error": str(e)}
|
|
|
|
def download_file_range(self, path, file_obj, offset, max_length):
|
|
try:
|
|
self.conn.retrieveFileFromOffset(self.share_name, path, file_obj, offset, max_length)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def get_attributes(self, path):
|
|
try:
|
|
return self.conn.getAttributes(self.share_name, path)
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def rename_file(self, old_path, new_path):
|
|
try:
|
|
self.conn.rename(self.share_name, old_path, new_path)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def create_directory(self, path):
|
|
try:
|
|
self.conn.createDirectory(self.share_name, path)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def delete_directory(self, path):
|
|
try:
|
|
self.conn.deleteDirectory(self.share_name, path)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def delete_directory_recursive(self, path):
|
|
try:
|
|
items = self.list_path(path)
|
|
for item in items:
|
|
item_path = item["path"]
|
|
if item["is_directory"]:
|
|
self.delete_directory_recursive(item_path)
|
|
else:
|
|
self.delete_file(item_path)
|
|
self.delete_directory(path)
|
|
return {"success": True}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def close(self):
|
|
self.conn.close()
|