main.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import os
  2. import configparser
  3. import shutil
  4. from typing import Optional
  5. from time import sleep
  6. from argparse import ArgumentParser
  7. from subprocess import run, Popen
  8. from sys import stderr, stdout
  9. from yaml import load, SafeLoader
  10. from marionette_driver.addons import Addons
  11. from marionette_driver.marionette import Marionette
  12. from marionette_driver.geckoinstance import DesktopInstance
  13. from mozprofile import FirefoxProfile
  14. def shell_execute(cmd: str, env: Optional[dict] = None):
  15. if env is None:
  16. env = {}
  17. run(cmd, env=env, stdout=stdout, stderr=stderr, shell=True)
  18. def profile_list() -> list:
  19. result = []
  20. ini = configparser.ConfigParser()
  21. ini.read(os.path.expanduser("~/.mozilla/firefox/profiles.ini"))
  22. for _, values in ini.items():
  23. for key, value in values.items():
  24. if key != "path":
  25. continue
  26. result.append(value)
  27. return list(set(result))
  28. def profile_create(name_1: str):
  29. shell_execute(f"/usr/bin/firefox --createprofile {name_1} -headless")
  30. def profile_configure(name_1: str, name_2: str):
  31. cwd = os.path.dirname(os.path.realpath(__file__))
  32. # Read config
  33. config = {}
  34. with open(f"{cwd}/profiles/{name_1}.yaml", "rt", encoding="utf-8") as handle:
  35. config = load(handle, Loader=SafeLoader)
  36. # Install signed addons first
  37. # Start firefox
  38. profile = FirefoxProfile(name_1)
  39. firefox = DesktopInstance(
  40. host="127.0.0.1",
  41. port=2828,
  42. bin="/usr/bin/firefox",
  43. app_args=["-remote-allow-system-access"],
  44. profile=profile,
  45. headless=1,
  46. )
  47. firefox.start()
  48. # Init marionette
  49. marionette = Marionette()
  50. marionette.start_session()
  51. # Install addons
  52. addons = Addons(marionette)
  53. for addon in config["addons"]:
  54. addons.install(f"{cwd}/addons/{addon['id']}-{addon['version']}.xpi")
  55. firefox.close()
  56. # Set prefs
  57. with open(f"{cwd}/{name_1}/user.js", "wt", encoding="utf-8") as handle:
  58. for key, value in config["prefs"].items():
  59. if type(value) == str:
  60. value = f'"{value}"'
  61. elif type(value) == bool:
  62. value = 'true' if value else 'false'
  63. else:
  64. value = value
  65. handle.write(f'user_pref("{key}", {value});\n')
  66. # Install profile
  67. from_path = f"{cwd}/{name_1}"
  68. to_path = os.path.expanduser(f"~/.mozilla/firefox/{name_2}")
  69. if os.path.exists(to_path):
  70. shutil.rmtree(to_path)
  71. shutil.copytree(from_path, to_path, dirs_exist_ok=True)
  72. shutil.rmtree(from_path)
  73. def profile_init(name_1: str):
  74. proc = Popen(f"/usr/bin/firefox -p {name_1} -headless", shell=True)
  75. sleep(5) # TODO: investigate why firefox take a time to apply extensions
  76. proc.kill()
  77. def profile_patch(name_2: str):
  78. cwd = os.path.dirname(os.path.realpath(__file__))
  79. # Patch profile
  80. from_path = f"{cwd}/patches/search.json.mozlz4"
  81. to_path = os.path.expanduser(f"~/.mozilla/firefox/{name_2}/search.json.mozlz4")
  82. shutil.copy(from_path, to_path)
  83. def main():
  84. parser = ArgumentParser()
  85. parser.add_argument("--configs", type=str, nargs="*", required=True)
  86. args = parser.parse_args()
  87. try:
  88. shutil.rmtree(os.path.expanduser("~/.mozilla"))
  89. except:
  90. pass
  91. names_1 = []
  92. for config in args.configs:
  93. names_1.append(config)
  94. for name_1 in names_1:
  95. profile_create(name_1)
  96. sleep(1) # Prevent profile hash collision
  97. names_2 = profile_list()
  98. mapping = {}
  99. for name_1 in names_1:
  100. for name_2 in names_2:
  101. if name_1 in name_2:
  102. mapping[name_1] = name_2
  103. break
  104. for name_1, name_2 in mapping.items():
  105. profile_configure(name_1, name_2)
  106. profile_init(name_1) # TODO: remove this workaround
  107. # profile_patch(name_2) # TODO: patching not working for now, dynamic patching needed
  108. if __name__ == "__main__":
  109. try:
  110. main()
  111. except Exception as e:
  112. print(e)