Skip to content
This repository was archived by the owner on Mar 17, 2026. It is now read-only.

Commit d94e94a

Browse files
Add files via upload
1 parent 84e1425 commit d94e94a

File tree

1 file changed

+120
-18
lines changed

1 file changed

+120
-18
lines changed

pydoll-mcp

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ import urllib.error
153153
import shutil
154154
import subprocess
155155
import time
156+
import platform
156157
from datetime import datetime
157158

158159
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1)
@@ -225,10 +226,111 @@ logger.info("Applied Shadow DOM monkey patch to PyDoll WebElement")
225226
# Server version
226227
__version__ = "1.0.1"
227228

229+
# Cross-platform utility functions
230+
def get_platform_temp_dir():
231+
"""Get platform-appropriate temporary directory"""
232+
if platform.system() == "Windows":
233+
return os.environ.get('TEMP', os.environ.get('TMP', 'C:\\Windows\\Temp'))
234+
else:
235+
return '/tmp'
236+
237+
def get_platform_cache_dir():
238+
"""Get platform-appropriate cache directory"""
239+
system = platform.system()
240+
if system == "Windows":
241+
cache_dir = os.environ.get('LOCALAPPDATA', os.path.expanduser('~\\AppData\\Local'))
242+
return os.path.join(cache_dir, 'pydoll-mcp')
243+
elif system == "Darwin": # macOS
244+
return os.path.expanduser('~/Library/Caches/pydoll-mcp')
245+
else: # Linux and others
246+
xdg_cache = os.environ.get('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
247+
return os.path.join(xdg_cache, 'pydoll-mcp')
248+
249+
def get_platform_allowed_paths():
250+
"""Get platform-appropriate allowed installation paths"""
251+
system = platform.system()
252+
if system == "Windows":
253+
return [
254+
'C:\\Program Files\\',
255+
'C:\\Program Files (x86)\\',
256+
os.environ.get('PROGRAMFILES', 'C:\\Program Files\\'),
257+
os.environ.get('PROGRAMFILES(X86)', 'C:\\Program Files (x86)\\'),
258+
os.path.expanduser('~\\AppData\\Local\\'),
259+
os.path.expanduser('~\\AppData\\Roaming\\'),
260+
os.path.expanduser('~\\')
261+
]
262+
elif system == "Darwin": # macOS
263+
return [
264+
'/usr/local/bin/',
265+
'/usr/bin/',
266+
'/opt/',
267+
'/Applications/',
268+
os.path.expanduser('~/Applications/'),
269+
os.path.expanduser('~/bin/'),
270+
os.path.expanduser('~/'),
271+
]
272+
else: # Linux and others
273+
return [
274+
'/usr/local/bin/',
275+
'/usr/bin/',
276+
'/opt/',
277+
os.path.expanduser('~/bin/'),
278+
os.path.expanduser('~/')
279+
]
280+
281+
def make_file_executable(file_path):
282+
"""Make file executable in a cross-platform way"""
283+
system = platform.system()
284+
if system == "Windows":
285+
# On Windows, executable permission is handled differently
286+
# We don't need to change permissions for Python scripts
287+
pass
288+
else:
289+
# Unix-like systems (Linux, macOS)
290+
os.chmod(file_path, 0o755)
291+
292+
def safe_remove_file(file_path):
293+
"""Safely remove a file in a cross-platform way"""
294+
try:
295+
if os.path.exists(file_path):
296+
# On Windows, sometimes we need to handle file locks
297+
if platform.system() == "Windows":
298+
# Try to remove read-only attribute if it exists
299+
try:
300+
import stat
301+
os.chmod(file_path, stat.S_IWRITE)
302+
except:
303+
pass
304+
os.unlink(file_path)
305+
return True
306+
except Exception as e:
307+
print(f"Warning: Could not remove file {file_path}: {e}", file=sys.stderr)
308+
return False
309+
310+
def get_platform_info():
311+
"""Get detailed platform information for debugging"""
312+
return {
313+
'system': platform.system(),
314+
'machine': platform.machine(),
315+
'platform': platform.platform(),
316+
'python_version': platform.python_version(),
317+
'temp_dir': get_platform_temp_dir(),
318+
'cache_dir': get_platform_cache_dir(),
319+
'allowed_paths': get_platform_allowed_paths()
320+
}
321+
228322
# Auto-Update System URLs and Configuration
229323
VERSION_URL = "https://raw.githubusercontent.com/coffeegrind123/pydoll-mcp/refs/heads/master/version"
230324
BINARY_URL = "https://raw.githubusercontent.com/coffeegrind123/pydoll-mcp/refs/heads/master/pydoll-mcp"
231-
UPDATE_CACHE_FILE = "/tmp/pydoll-mcp-update-cache"
325+
326+
# Create cache directory if it doesn't exist
327+
try:
328+
CACHE_DIR = get_platform_cache_dir()
329+
os.makedirs(CACHE_DIR, exist_ok=True)
330+
UPDATE_CACHE_FILE = os.path.join(CACHE_DIR, "pydoll-mcp-update-cache")
331+
except:
332+
# Fallback to temp directory
333+
UPDATE_CACHE_FILE = os.path.join(get_platform_temp_dir(), "pydoll-mcp-update-cache")
232334

233335
def check_for_updates():
234336
"""Check for updates using conditional requests to minimize bandwidth"""
@@ -291,10 +393,13 @@ def check_for_updates():
291393
def perform_update():
292394
"""Download and install the updated version"""
293395
try:
294-
print("Downloading updated pydoll-mcp...", file=sys.stderr)
396+
platform_info = get_platform_info()
397+
print(f"Downloading updated pydoll-mcp on {platform_info['system']}...", file=sys.stderr)
295398

296399
# Download to temporary file
297-
temp_file = f"/tmp/pydoll-mcp-update-{int(time.time())}"
400+
temp_dir = get_platform_temp_dir()
401+
temp_file = os.path.join(temp_dir, f"pydoll-mcp-update-{int(time.time())}")
402+
print(f"Using temporary file: {temp_file}", file=sys.stderr)
298403

299404
request = urllib.request.Request(BINARY_URL, headers={'User-Agent': 'pydoll-mcp-updater/1.0'})
300405
with urllib.request.urlopen(request, timeout=30) as response:
@@ -339,21 +444,23 @@ def perform_update():
339444
raise Exception(f"Downloaded file has Python syntax errors: {e}")
340445

341446
except Exception as e:
342-
os.unlink(temp_file)
447+
safe_remove_file(temp_file)
343448
raise Exception(f"Downloaded file validation failed: {e}")
344449

345450
# Make executable
346-
os.chmod(temp_file, 0o755)
451+
make_file_executable(temp_file)
347452

348453
# Get current script path with additional security checks
349454
current_script = os.path.abspath(__file__)
350455

351456
# Security: Ensure we're only updating files we should be updating
352-
if not current_script.endswith('pydoll-mcp'):
457+
script_name = os.path.basename(current_script)
458+
valid_names = ['pydoll-mcp', 'pydoll-mcp.exe', 'pydoll-mcp.py']
459+
if not any(script_name == name or script_name.endswith(name) for name in valid_names):
353460
raise Exception(f"Refusing to update non-pydoll-mcp file: {current_script}")
354461

355462
# Security: Ensure the current script path is reasonable
356-
allowed_paths = ['/usr/local/bin/', '/usr/bin/', '/opt/', os.path.expanduser('~')]
463+
allowed_paths = get_platform_allowed_paths()
357464
if not any(current_script.startswith(path) for path in allowed_paths):
358465
raise Exception(f"Refusing to update file in suspicious location: {current_script}")
359466

@@ -397,32 +504,27 @@ def perform_update():
397504
backups = sorted(glob.glob(backup_pattern))
398505
if len(backups) > 3:
399506
for old_backup in backups[:-3]:
400-
os.unlink(old_backup)
401-
print(f"Removed old backup: {old_backup}", file=sys.stderr)
507+
if safe_remove_file(old_backup):
508+
print(f"Removed old backup: {old_backup}", file=sys.stderr)
402509
except:
403510
pass # Backup cleanup is non-critical
404511

405512
# Clear update cache so next run will check again
406-
try:
407-
os.unlink(UPDATE_CACHE_FILE)
408-
except:
409-
pass
513+
safe_remove_file(UPDATE_CACHE_FILE)
410514

411515
return True
412516

413517
except Exception as e:
414518
print(f"Update failed: {e}", file=sys.stderr)
415519
# Clean up temp file if it exists
416-
try:
417-
os.unlink(temp_file)
418-
except:
419-
pass
520+
safe_remove_file(temp_file)
420521
return False
421522

422523
def auto_update_check():
423524
"""Perform automatic update check and update if needed"""
424525
try:
425-
print(f"Checking for updates (current version: {__version__})...", file=sys.stderr)
526+
platform_info = get_platform_info()
527+
print(f"Checking for updates on {platform_info['system']} (current version: {__version__})...", file=sys.stderr)
426528
remote_version = check_for_updates()
427529

428530
if remote_version is None:

0 commit comments

Comments
 (0)