]> Nutra Git (v2) - gamesguru/ffpass.git/commitdiff
Windows: Fix profile discovery and CSV printing.
authorShane Jaroch <chown_tee@proton.me>
Fri, 26 Dec 2025 07:46:21 +0000 (02:46 -0500)
committerShane Jaroch <chown_tee@proton.me>
Fri, 26 Dec 2025 09:55:23 +0000 (04:55 -0500)
ffpass/__init__.py
tests/test_run.py

index e531a77b8e60441d2cff5e44cf52ad9213e6bef7..b837655cd4a398be23dbf843e2ca458a4026ab81 100755 (executable)
@@ -374,6 +374,11 @@ def readCSV(csv_file):
 
     logins = []
     for row in reader:
+        logging.error(f"row: {row}")
+
+        if not row:
+            continue  # The Windows CSV parser can give extra empty rows
+
         if isinstance(reader, csv.DictReader):
             logging.error("DictReader it is!")
             u = row.get("url", "")
@@ -431,20 +436,22 @@ def addNewLogins(key, jsonLogins, logins):
 PROFILE_GUESS_DIRS = {
     "darwin": "~/Library/Application Support/Firefox/Profiles",
     "linux": "~/.mozilla/firefox",
-    "win32": os.path.expandvars(r"%LOCALAPPDATA%\Mozilla\Firefox\Profiles"),
-    "cygwin": os.path.expandvars(r"%LOCALAPPDATA%\Mozilla\Firefox\Profiles"),
+    "win32": os.path.expandvars("%APPDATA%\\Mozilla\\Firefox\\Profiles"),
+    "cygwin": os.path.expandvars("%APPDATA%\\Mozilla\\Firefox\\Profiles"),
 }
 
 
-def getProfiles():
+def getProfiles() -> list[Path]:
     paths = Path(PROFILE_GUESS_DIRS[sys.platform]).expanduser()
     logging.debug(f"Paths: {paths}")
-    profiles = [path.parent for path in paths.glob(os.path.join("*", "logins.json"))]
+    _potential_profile_paths = paths.glob(os.path.join("*", "logins.json"))
+    logging.debug(f"_potential_profile_paths: {_potential_profile_paths}")
+    profiles = [path.parent for path in _potential_profile_paths]
     logging.debug(f"Profiles: {profiles}")
     return profiles
 
 
-def guessDir():
+def guessDir() -> Path:
     if sys.platform not in PROFILE_GUESS_DIRS:
         logging.error(f"Automatic profile selection is not supported for {sys.platform}")
         logging.error("Please specify a profile to parse (-d path/to/profile)")
@@ -501,7 +508,8 @@ def main_export(args):
 
     jsonLogins = getJsonLogins(args.directory)
     logins = exportLogins(key, jsonLogins)
-    writer = csv.writer(args.file)
+    # Hard-code to "\n" to fix Windows bug with every other row empty: [a, "", b, "", ...].
+    writer = csv.writer(args.file, lineterminator="\n")
     writer.writerow(["url", "username", "password"])
     writer.writerows(logins)
 
@@ -606,12 +614,11 @@ def main():
     # Try to obtain profile directory
     if args.directory is None:
         try:
-            args.directory = guessDir()
+            args.directory = guessDir().expanduser()
         except NoProfile:
             logging.error("No Firefox profile selected.")
             parser.print_help()
             parser.exit()
-    args.directory = args.directory.expanduser()
 
     # Run arg parser
     try:
index 18dcc372e241e0f0cf86c4d5be6500bc4eb91756..b6aea287b197f0c1fc342e6c0aa4c50a4d16013e 100644 (file)
@@ -30,7 +30,7 @@ def clean_profile(tmp_path):
 
 
 def run_ffpass(mode, path):
-    command = ["ffpass", mode, "-d", str(path)]
+    command = ["python", "./ffpass/__init__.py", mode, "-d", str(path)]
 
     if mode == 'import':
         ffpass_input = OS_NEWLINE.join([HEADER, IMPORT_CREDENTIAL])
@@ -41,7 +41,7 @@ def run_ffpass(mode, path):
 
 
 def stdout_splitter(input_text):
-    return [x for x in input_text.splitlines() if x != ""]
+    return [x for x in input_text.splitlines()]
 
 
 def test_legacy_firefox_export(clean_profile):