@@ -153,6 +153,7 @@ import urllib.error
153153import shutil
154154import subprocess
155155import time
156+ import platform
156157from datetime import datetime
157158
158159sys .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
229323VERSION_URL = "https://raw.githubusercontent.com/coffeegrind123/pydoll-mcp/refs/heads/master/version"
230324BINARY_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
233335def check_for_updates ():
234336 """Check for updates using conditional requests to minimize bandwidth"""
@@ -291,10 +393,13 @@ def check_for_updates():
291393def 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
422523def 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