diff --git a/wrapper.py b/wrapper.py index 3cb71c6..1fb3b7e 100755 --- a/wrapper.py +++ b/wrapper.py @@ -13,7 +13,11 @@ import hashlib import os import shutil import configparser -import keyring +try: + import keyring + HAS_KEYRING = True +except ImportError: + HAS_KEYRING = False import getpass import urllib.parse @@ -172,42 +176,51 @@ def detect_launch_command(): def get_secret(config, config_path): """ Retrieves the OTP secret in the following priority: - 1. System Keyring (SERVICE_NAME, USERNAME) - 2. config.json (Legacy) - if found, migrates to Keyring and removes from config. - 3. User Input - prompts user and saves to Keyring. + 1. System Keyring (SERVICE_NAME, USERNAME) - if available + 2. config.json - if Keyring unavailable or secret found there (and Keyring unavailable for migration) + 3. User Input - prompts user and saves to Keyring (if available) or config.json. """ SERVICE_NAME = "XIVLauncherWrapper" USERNAME = "OTP_SECRET" # 1. Try Keyring - try: - secret = keyring.get_password(SERVICE_NAME, USERNAME) - if secret: - return secret - except Exception as e: - print(f"Warning: Failed to access keyring: {e}") + if HAS_KEYRING: + try: + secret = keyring.get_password(SERVICE_NAME, USERNAME) + if secret: + return secret + except Exception as e: + print(f"Warning: Failed to access keyring: {e}") - # 2. Try Config (Legacy/Migration) + # 2. Try Config if config and "secret" in config: secret = config["secret"] if secret and secret != "YOUR_BASE32_SECRET_HERE": - print("Migrating secret from config.json to system keyring...") - try: - keyring.set_password(SERVICE_NAME, USERNAME, secret) - - # Remove from config - del config["secret"] - with open(config_path, 'w') as f: - json.dump(config, f, indent=4) - print("Secret migrated successfully and removed from config.json.") - return secret - except Exception as e: - print(f"Error migrating to keyring: {e}") - print("Continuing with secret from config...") - return secret + if HAS_KEYRING: + print("Migrating secret from config.json to system keyring...") + try: + keyring.set_password(SERVICE_NAME, USERNAME, secret) + + # Remove from config + del config["secret"] + with open(config_path, 'w') as f: + json.dump(config, f, indent=4) + print("Secret migrated successfully and removed from config.json.") + return secret + except Exception as e: + print(f"Error migrating to keyring: {e}") + print("Continuing with secret from config...") + return secret + else: + # No keyring, just use config + return secret # 3. Prompt User - print("\nOTP Secret not found in keyring.") + if HAS_KEYRING: + print("\nOTP Secret not found in keyring.") + else: + print("\nOTP Secret not found in config.json and keyring module is missing.") + print("Please enter your TOTP Secret (base32) or otpauth:// URL.") while True: try: @@ -222,16 +235,26 @@ def get_secret(config, config_path): print("Invalid secret format. Please try again.") continue - try: - keyring.set_password(SERVICE_NAME, USERNAME, parsed) - print("Secret saved to system keyring.") - return parsed - except Exception as e: - print(f"Error saving to keyring: {e}") - # If we can't save to keyring, we might just return it in memory for this session - # But typically we want persistence. - print("Proceeding with in-memory secret for this session.") - return parsed + if HAS_KEYRING: + try: + keyring.set_password(SERVICE_NAME, USERNAME, parsed) + print("Secret saved to system keyring.") + return parsed + except Exception as e: + print(f"Error saving to keyring: {e}") + print("Proceeding with in-memory secret for this session.") + return parsed + else: + # Save to config.json + try: + config['secret'] = parsed + with open(config_path, 'w') as f: + json.dump(config, f, indent=4) + print(f"Secret saved to {config_path}") + return parsed + except Exception as e: + print(f"Error saving to config.json: {e}") + return parsed except KeyboardInterrupt: print("\nOperation cancelled.")