gtk3-listdomains.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import gi
  2. gi.require_version('Gtk', '3.0')
  3. from gi.repository import GLib, Gtk, GObject
  4. import json
  5. from nginxparser import loads
  6. import os
  7. import re
  8. import spur
  9. import threading
  10. import time
  11. DIR_PATH = os.path.dirname(os.path.realpath(__file__))
  12. VHOSTS_PATH = DIR_PATH + '/nginx-vhosts'
  13. if not os.path.isdir(VHOSTS_PATH):
  14. os.mkdir(VHOSTS_PATH)
  15. fp = open('creds.json', 'r')
  16. creds = json.load(fp)
  17. class Error(Exception):
  18. """Base class for exceptions in this module."""
  19. pass
  20. class InvalidDomainError(Error):
  21. """Exception raised for invalid domains.
  22. Attributes:
  23. domain -- domain name in which the error occurred
  24. """
  25. def __init__(self, domain):
  26. self.message = 'Invalid domain name: ' + domain
  27. def is_valid_domain(domain):
  28. p = re.compile('^[0-9a-z-.]+$')
  29. return p.match(domain)
  30. def is_domain_root(domain):
  31. return len(domain.split('.')) == 2
  32. def get_domain_root(subdomain):
  33. bits = subdomain.split('.')
  34. return '.'.join(bits[-2:])
  35. def ssh_command(command, sudo=False):
  36. shell = spur.SshShell(
  37. hostname=creds['host'],
  38. username=creds['user'],
  39. password=creds['passphrase'],
  40. private_key_file=creds['ssh_key_path'])
  41. with shell:
  42. command_bits = command.split(" ")
  43. if sudo:
  44. command_bits.insert(0, "sudo")
  45. process = shell.spawn(command_bits)
  46. if sudo:
  47. process.stdin_write(creds['password'])
  48. result = process.wait_for_result()
  49. return result.output.decode()
  50. #
  51. # def get_check_domain_command(domain):
  52. # return "sudo openssl x509 -text -in /etc/letsencrypt/live/" + domain + "/fullchain.pem"
  53. #
  54. # def join_commands(commands):
  55. # return " && ".join(commands)
  56. class EntryWindow(Gtk.Window):
  57. def __init__(self):
  58. Gtk.Window.__init__(self, title="Entry Demo")
  59. self.set_size_request(200, 100)
  60. self.timeout_id = None
  61. vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
  62. self.add(vbox)
  63. self.progressbar = Gtk.ProgressBar(show_text=True)
  64. # self.progressbar.set_fraction(0.0)
  65. vbox.pack_start(self.progressbar, True, True, 0)
  66. hbox = Gtk.Box(spacing=6)
  67. vbox.add(hbox)
  68. self.entry_passphrase = Gtk.Entry()
  69. # https://developer.gnome.org/gtk3/stable/GtkEntry.html#gtk-entry-set-invisible-char
  70. self.entry_passphrase.set_visibility(False)
  71. # self.entry_passphrase.set_text("Enter SSH key passphrase")
  72. hbox.pack_start(self.entry_passphrase, True, True, 0)
  73. self.entry_password = Gtk.Entry()
  74. self.entry_password.set_visibility(False)
  75. # self.entry_password.set_text("Enter sudo user password")
  76. hbox.pack_start(self.entry_password, True, True, 0)
  77. self.button = Gtk.Button(label="Click Here")
  78. self.button.connect("clicked", self.on_button_clicked)
  79. hbox.pack_start(self.button, True, True, 0)
  80. #Setting up the self.grid in which the elements are to be positionned
  81. self.grid = Gtk.Grid()
  82. self.grid.set_column_homogeneous(True)
  83. self.grid.set_row_homogeneous(True)
  84. vbox.add(self.grid)
  85. #Creating the TreeStore model
  86. self.vhost_treestore = Gtk.TreeStore(int, str, str)
  87. vhost_list = []
  88. # for vhost in vhost_list:
  89. # self.vhost_treestore.append(list(vhost))
  90. self.language_filter = self.vhost_treestore.filter_new()
  91. self.language_filter.set_visible_func(self.filter_func)
  92. #creating the treeview, making it use the filter as a model, and adding the columns
  93. self.treeview = Gtk.TreeView.new_with_model(self.language_filter)
  94. for i, column_title in enumerate(["Port", "File path", "Root domain"]):
  95. renderer = Gtk.CellRendererText()
  96. column = Gtk.TreeViewColumn(column_title, renderer, text=i)
  97. self.treeview.append_column(column)
  98. self.scrollable_treelist = Gtk.ScrolledWindow()
  99. self.scrollable_treelist.set_vexpand(True)
  100. self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
  101. self.scrollable_treelist.add(self.treeview)
  102. def filter_func(self, model, iter, data):
  103. return True
  104. def update_progess(self, domain):
  105. self.progressbar.pulse()
  106. self.progressbar.set_text('Done: ' + domain)
  107. return False
  108. def get_https_domains(self):
  109. for d in self.domains:
  110. GLib.idle_add(self.get_https_subdomains_for_domain, d)
  111. time.sleep(0.4)
  112. def get_nginx_vhosts(self):
  113. for v in self.vhosts:
  114. GLib.idle_add(self.get_nginx_vhost, v)
  115. time.sleep(0.1)
  116. def start_thread(self, func):
  117. thread = threading.Thread(target=func)
  118. thread.daemon = True
  119. thread.start()
  120. def on_button_clicked(self, widget):
  121. decoded = ssh_command("ls /etc/nginx/sites-enabled")
  122. vhosts = decoded.split("\n")
  123. vhosts.pop()
  124. self.vhosts = vhosts
  125. self.num_vhosts = len(vhosts)
  126. self.vhosts_done = 0
  127. decoded = ssh_command("ls /etc/letsencrypt/live", True)
  128. domains = decoded.split("\n")
  129. domains.pop()
  130. self.domains = domains
  131. self.num_domains = len(domains)
  132. self.domains_done = 0
  133. self.start_thread(self.get_nginx_vhosts)
  134. # self.start_thread(self.get_https_domains)
  135. # subdomains = [self.get_https_subdomains_for_domain(d) for d in domains]
  136. # subdomains_dict = dict(zip(domains, subdomains))
  137. # print(subdomains_dict)
  138. def get_nginx_vhost(self, vhost):
  139. print(vhost)
  140. vhost_file = VHOSTS_PATH + '/' + vhost
  141. if not os.path.isfile(vhost_file):
  142. print("not found locally: " + vhost_file)
  143. vhost_content = ssh_command("cat /etc/nginx/sites-enabled/" + vhost)
  144. vhost_fp = open(vhost_file, "w")
  145. vhost_fp.write(vhost_content)
  146. vhost_fp.close()
  147. else:
  148. print("found locally: " + vhost_file)
  149. vhost_fp = open(vhost_file, "r")
  150. vhost_content = vhost_fp.read()
  151. vhost_fp.close()
  152. parsed = loads(vhost_content)
  153. port_subdmomains = {}
  154. for server in parsed:
  155. server_inner = server[1]
  156. port = 0
  157. subdomains = []
  158. for directive in server_inner:
  159. if not port and "listen" in directive:
  160. p = re.compile('(\d+)')
  161. # print('listen')
  162. ports = p.findall(directive[1])
  163. port = int(ports[0])
  164. if "server_name" in directive:
  165. # print('server_name')
  166. # print(directive)
  167. subd_trimmed = directive[1].strip()
  168. subdomains = subd_trimmed.split(' ')
  169. port_subdmomains[port] = subdomains
  170. parents = {}
  171. for subd in subdomains:
  172. # subdomain = '*' + subd if is_domain_root(subd) else subd
  173. # print(subdomain)
  174. # self.vhost_treestore.append([port, vhost, subd])
  175. root = get_domain_root(subd)
  176. print('processing', root, '=>', subd)
  177. if not is_valid_domain(subd):
  178. raise InvalidDomainError(subd)
  179. if not root in parents.keys():
  180. parents[root] = self.vhost_treestore.append(None, (port, vhost, subd))
  181. if root != subd:
  182. parent = parents[root]
  183. self.vhost_treestore.append(parent, (port, '', subd))
  184. # print(parents)
  185. # print(port_subdmomains)
  186. self.vhosts_done += 1
  187. percent_done = self.vhosts_done * 1.0 / self.num_vhosts
  188. self.progressbar.set_fraction(percent_done)
  189. return False
  190. def get_https_subdomains_for_domain(self, domain):
  191. # print(domain)
  192. p = re.compile('DNS:([0-9a-z-.]+)')
  193. cert_data = ssh_command("sudo openssl x509 -text -in /etc/letsencrypt/live/" + domain + "/fullchain.pem", True)
  194. self.domains_done += 1
  195. percent_done = self.domains_done * 1.0 / self.num_domains
  196. # print(percent_done)
  197. self.progressbar.set_fraction(percent_done)
  198. # self.progressbar.set_text('Done: ' + domain)
  199. # print(cert_data)
  200. # return p.findall (cert_data)
  201. return False
  202. def app_main():
  203. win = EntryWindow()
  204. win.connect("delete-event", Gtk.main_quit)
  205. win.show_all()
  206. if __name__ == '__main__':
  207. import signal
  208. signal.signal(signal.SIGINT, signal.SIG_DFL)
  209. # Calling GObject.threads_init() is not needed for PyGObject 3.10.2+
  210. GObject.threads_init()
  211. app_main()
  212. Gtk.main()