def __on_signal(self, connection, sender, path, interface, signal, params): """ Add video to download manager @param connection as Gio.DBusConnection @param sender as str @param path as str @param interface as str @param signal as str @param parameters as GLib.Variant """ if signal == "VideoInPage": uri = params[0] title = params[1] page_id = params[2] El().download_manager.add_video(uri, title, page_id) elif signal == "UnsecureFormFocused": self._window.toolbar.title.show_input_warning(self) elif signal == "InputMouseDown": if self.__last_click_event: model = FormMenu(params[0], self.get_page_id()) popover = Gtk.Popover.new_from_model(self, model) popover.set_modal(False) self._window.register(popover) rect = Gdk.Rectangle() rect.x = self.__last_click_event["x"] rect.y = self.__last_click_event["y"] - 10 rect.width = rect.height = 1 popover.set_pointing_to(rect) helper = PasswordsHelper() helper.get(self.get_uri(), self.__on_password, popover, model)
def __init__(self, extension, page, form_extension, jsblock_extension): """ Init server @param extension as WebKit2WebExtension.WebExtension @param page as WebKit2WebExtension.WebPage @param form_extension as FormsExtension @param jsblock_extension as JSBlockExtension """ self.__extension = extension self.__page = page self.__form_extension = form_extension self.__jsblock_extension = jsblock_extension self.__focused = None self.__on_input_timeout_id = None self.__elements_history = {} self.__send_requests = [] self.__helper = PasswordsHelper() self.__proxy_bus = PROXY_BUS % self.__page.get_id() addr = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION, None) self.__bus = None Gio.DBusConnection.new_for_address( addr, Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None, self.__on_bus_new_for_address) form_extension.connect("submit-form", self.__on_submit_form) page.connect("send-request", self.__on_send_request) page.connect("context-menu", self.__on_context_menu) page.connect("notify::uri", self.__on_notify_uri) page.connect("form-controls-associated", self.__on_form_control_associated)
def __init__(self): """ Init worker """ self.__syncing = False self.__sync_cancellable = Gio.Cancellable() self.__username = "" self.__password = "" self.__token = "" self.__uid = "" self.__keyB = b"" self.__mtimes = {"bookmarks": 0.1, "history": 0.1, "passwords": 0.1} # We do not create this here because it's slow down Eolie startup # See __firefox_sync property self.__mz = None self.__state_lock = True self.__session = None try: self.__pending_records = load( open(EOLIE_DATA_PATH + "/firefox_sync_pendings.bin", "rb")) except: self.__pending_records = { "history": [], "passwords": [], "bookmarks": [] } self.__helper = PasswordsHelper() self.set_credentials()
def __init__(self, username, userform, password, passform, uri, window): """ Init popover @param username as str @param password as str @param netloc as str @param window as Window """ Gtk.Popover.__init__(self) self.set_modal(False) window.register(self) self.__helper = PasswordsHelper() self.__username = username self.__userform = userform self.__password = password self.__passform = passform self.__uri = uri self.__uuid = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverCredentials.ui') builder.connect_signals(self) self.__label = builder.get_object('label') parsed = urlparse(uri) builder.get_object('uri').set_text(parsed.netloc) builder.get_object('username').set_text(username) builder.get_object('password').set_text(password) self.add(builder.get_object('widget'))
def _on_sync_button_clicked(self, button): """ Start sync @param button as Gtk.Button """ helper = PasswordsHelper() helper.get_sync(self.__on_get_sync) button.hide()
def __init__(self, extension, settings): """ Connect wanted signal @param extension as WebKit2WebExtension @param settings as Settings """ self.__helper = PasswordsHelper() self.__settings = settings extension.connect("page-created", self.__on_page_created)
def __init__(self, extension): """ Connect wanted signal @param extension as WebKit2WebExtension """ GObject.Object.__init__(self) self.__helper = PasswordsHelper() self.__extension = extension self.__elements_uri = None self.__pending_credentials = None self.__page_id = None extension.connect("page-created", self.__on_page_created)
def __init__(self): """ Init worker """ GObject.GObject.__init__(self) self.__stop = True self.__username = "" self.__password = "" self.__mtimes = {"bookmarks": 0.1, "history": 0.1} self.__mozilla_sync = MozillaSync() self.__session = None self.__helper = PasswordsHelper()
def __on_save_credentials(self, source, result, form_uri, user_form_name, pass_form_name): """ Get password and push credential to sync @param source as GObject.Object @param result as Gio.AsyncResult @param form_uri as str @param user_form_name as str @param pass_form_name as str """ helper = PasswordsHelper() helper.get(form_uri, user_form_name, pass_form_name, self.__on_get_password)
def __init__(self): """ Init worker """ self.__syncing = False self.__sync_cancellable = Gio.Cancellable() self.__username = "" self.__password = "" self.__mtimes = {"bookmarks": 0.1, "history": 0.1} self.__mozilla_sync = None self.__state_lock = True self.__session = None self.__helper = PasswordsHelper() self.__helper.get_sync(self.__set_credentials)
def __init__(self): """ Init popover """ Gtk.Popover.__init__(self) self.__filter = "" self.__helper = PasswordsHelper() self.set_position(Gtk.PositionType.BOTTOM) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverPasswords.ui') builder.connect_signals(self) self.__listbox = builder.get_object("listbox") self.__listbox.set_filter_func(self.__filter_func) self.add(builder.get_object('widget')) self.set_size_request(300, 300)
def SetAuthForms(self, username, page_id): """ Get password forms for page id @param username as str @param page id as int """ try: page = self.__extension.get_page(page_id) if page is None: return helper = PasswordsHelper() helper.get(page.get_uri(), self.__forms.set_input_forms, page, username) except Exception as e: print("ProxyExtension::SetAuthForms():", e)
def __init__(self, uuid, user_form_name, user_form_value, pass_form_name, uri, form_uri, page_id, window): """ Init popover @param uuid as str @param user_form_name as str @param user_form_value as str @param pass_form_name as str @param uri as str @param form_uri as str @param page_id as int @param window as Window """ Gtk.Popover.__init__(self) self.set_modal(False) window.register(self) self.__helper = PasswordsHelper() self.__user_form_name = user_form_name self.__user_form_value = user_form_value self.__pass_form_name = pass_form_name self.__uri = uri self.__form_uri = form_uri self.__uuid = uuid self.__page_id = page_id builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverCredentials.ui') builder.connect_signals(self) self.__label = builder.get_object('label') parsed = urlparse(uri) builder.get_object('uri').set_text(parsed.netloc) if uuid: self.__label.set_text(_("Do you want to modify this password?")) self.add(builder.get_object('widget'))
def __init__(self): """ Init worker """ self.__syncing = False self.__sync_cancellable = Gio.Cancellable() self.__username = "" self.__password = "" self.__token = "" self.__uid = "" self.__keyB = b"" self.__mtimes = {"bookmarks": 0.1, "history": 0.1} # We do not create this here because it's slow down Eolie startup # See __mozilla_sync property self.__mz = None self.__state_lock = True self.__session = None self.__helper = PasswordsHelper() self.set_credentials()
def __on_signal(self, signal, params): """ Handle proxy signals @param signal as str @params as [] """ if signal == "UnsecureFormFocused": self._window.toolbar.title.show_input_warning(self) elif signal == "AskSaveCredentials": (uuid, user_form_name, user_form_value, pass_form_name, uri, form_uri) = params[0] self._window.close_popovers() self._window.toolbar.title.show_password(uuid, user_form_name, user_form_value, pass_form_name, uri, form_uri, self.get_page_id()) elif signal == "ShowCredentials": (userform, form_uri) = params model = FormMenu(self.get_page_id(), self._window) helper = PasswordsHelper() helper.get(form_uri, userform, None, self.__on_password, model)
class FormsExtension: """ Handle forms prefill """ def __init__(self, extension, settings): """ Connect wanted signal @param extension as WebKit2WebExtension @param settings as Settings """ self.__helper = PasswordsHelper() self.__settings = settings extension.connect("page-created", self.__on_page_created) def has_password(self, webpage): """ True if webpage has a password input """ dom_document = webpage.get_dom_document() inputs = dom_document.get_elements_by_tag_name("input") i = 0 while i < inputs.get_length(): if inputs.item(i).get_input_type() == "password": return True i += 1 return False def get_input_forms(self, webpage): """ Return forms for webpage @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ forms = [] dom_document = webpage.get_dom_document() inputs = dom_document.get_elements_by_tag_name("input") i = 0 while i < inputs.get_length(): if inputs.item(i).get_input_type() in ["text", "search"]: forms.append(inputs.item(i)) i += 1 return forms def get_textarea_forms(self, webpage): """ Return forms for webpage @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ forms = [] dom_document = webpage.get_dom_document() textareas = dom_document.get_elements_by_tag_name("textarea") i = 0 while i < textareas.get_length(): forms.append(textareas.item(i)) i += 1 return forms def get_password_forms(self, name, webpage): """ Return forms for webpage @param name as str @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ forms = [] dom_document = webpage.get_dom_document() inputs = dom_document.get_elements_by_tag_name("input") i = 0 while i < inputs.get_length(): if inputs.item(i).get_input_type() == "password" and\ (not name or name == inputs.item(i).get_attribute("name")): forms.append(inputs.item(i)) i += 1 return forms def get_login_forms(self, name, webpage): """ Return auth forms for webpage @param name as str @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ dom_document = webpage.get_dom_document() inputs = dom_document.get_elements_by_tag_name("input") i = 0 forms = [] while i < inputs.get_length(): if inputs.item(i).get_input_type() == "text": input_name = inputs.item(i).get_attribute("name") input_id = inputs.item(i).get_attribute("id") # We search for wanted name if name and (name == input_name or name == input_id): forms.append(inputs.item(i)) else: if self.is_login_form(inputs.item(i)): forms.append(inputs.item(i)) i += 1 return forms def set_input_forms(self, attributes, password, uri, index, count, webpage, username=None): """ Set username/password input @param attributes as {} @param password as str @param uri as str @param index as int @param count as int @param webpage as WebKit2WebExtension.WebPage @param username as str/None """ # We only set first available password if index != 0 and username is None: return parsed = urlparse(uri) # Allow unsecure completion if wanted by user if parsed.scheme != "https" and username is None: return submit_uri = "%s://%s" % (parsed.scheme, parsed.netloc) # Do not set anything if no attributes or # If we have already text in input and we are not a perfect completion if attributes is None or\ (username is not None and attributes["login"] != username) or\ attributes["formSubmitURL"] != submit_uri: return try: usernames = self.get_login_forms(attributes["userform"], webpage) passwords = self.get_password_forms(attributes["passform"], webpage) # Passwords form may have changed, take first if not passwords: passwords = self.get_password_forms("", webpage) if usernames and passwords: usernames[0].set_value(attributes["login"]) passwords[0].set_value(password) except Exception as e: print("FormsExtension::set_input_forms()", e) def is_login_form(self, form): """ Return True if form is a login form @param form as WebKit2WebExtension.DOMHTMLInputElement @return bool """ input_name = form.get_attribute("name") input_id = form.get_attribute("id") # We search for common name for search in LOGINS: if (input_name is not None and input_name.lower().find(search) != -1) or\ (input_id is not None and input_id.lower().find(search) != -1): return True return False ####################### # PRIVATE # ####################### def __on_page_created(self, extension, webpage): """ Connect to send request @param extension as WebKit2WebExtension @param webpage as WebKit2WebExtension.WebPage """ webpage.connect("document-loaded", self.__on_document_loaded) def __on_document_loaded(self, webpage): """ Restore forms @param webpage as WebKit2WebExtension.WebPage """ if not self.__settings.get_value("remember-passwords"): return if self.has_password(webpage): self.__helper.get(webpage.get_uri(), self.set_input_forms, webpage)
class FormsExtension(GObject.Object): """ Handle forms prefill """ __gsignals__ = { 'submit-form': (GObject.SignalFlags.RUN_FIRST, None, (GLib.Variant, )) } def __init__(self, extension): """ Connect wanted signal @param extension as WebKit2WebExtension """ GObject.Object.__init__(self) self.__helper = PasswordsHelper() self.__extension = extension self.__elements_uri = None self.__pending_credentials = None self.__page_id = None extension.connect("page-created", self.__on_page_created) def set_credentials(self, form, webpage): """ Set credentials on page @param form as { "element":WebKit2WebExtension.DOMHTMLFormElement, "username": WebKit2WebExtension.DOMHTMLInputElement, "password": WebKit2WebExtension.DOMHTMLInputElement} @param webpage as WebKit2WebExtension.WebPage """ if App().settings.get_value("remember-passwords"): form_input_username = form["username"].get_name() form_input_password = form["password"].get_name() if form_input_username is not None and\ form_input_password is not None: self.__helper.get(self.get_form_uri(form["element"]), form_input_username, form_input_password, self.set_input_forms, webpage, form) def set_input_forms(self, attributes, password, uri, index, count, webpage, form, username=None): """ Set login/password input @param attributes as {} @param password as str @param uri as str @param index as int @param count as int @param webpage as WebKit2WebExtension.WebPage @param form as { "element":WebKit2WebExtension.DOMHTMLFormElement, "username": WebKit2WebExtension.DOMHTMLInputElement, "password": WebKit2WebExtension.DOMHTMLInputElement} @param username as str """ if attributes is None: return # We only set first available password if (index != 0 or count > 1) and username is None: return parsed = urlparse(self.get_form_uri(form["element"])) # Allow unsecure completion if wanted by user if parsed.scheme != "https" and username is None: return # We want a user, check if it wanted password if username is not None and username != attributes["login"]: return try: form["username"].set_value(attributes["login"]) form["password"].set_value(password) except Exception as e: Logger.error("FormsExtension::set_input_forms(): %s", e) def get_elements(self, elements): """ Get forms as dict and textareas for elements @param elements as [WebKit2WebExtension.DOMElement] @return elements as ([{"element":WebKit2WebExtension.DOMHTMLFormElement, "username": WebKit2WebExtension.DOMHTMLInputElement, "password": WebKit2WebExtension.DOMHTMLInputElement}], [WebKit2WebExtension.DOMHTMLTextAreaElement]) """ forms = [] textareas = [] for element in elements: if isinstance(element, WebKit2WebExtension.DOMHTMLTextAreaElement): textareas.append(element) elif isinstance(element, WebKit2WebExtension.DOMHTMLFormElement): form = {"element": element} elements_collection = element.get_elements() h = 0 while h < elements_collection.get_length(): element = elements_collection.item(h) # Ignore hidden elements if element.get_client_top() == 0: h += 1 continue if isinstance(element, WebKit2WebExtension.DOMHTMLInputElement): if element.get_input_type() == "password" and\ element.get_name() is not None: form["password"] = element elif element.get_input_type() in ["text", "email", "search"] and\ element.get_name() is not None: form["username"] = element elif isinstance( element, WebKit2WebExtension.DOMHTMLTextAreaElement): textareas.append(element) h += 1 keys = form.keys() if "username" in keys and "password" in keys: forms.append(form) return (forms, textareas) def get_hostname_uri(self, page): """ Get form uri for page @param page as WebKit2WebExtension.WebPage @return str @raise Exception """ page = self.__extension.get_page(self.__page_id) if page is None: raise Exception("Can't find page!") uri = page.get_uri() parsed = urlparse(uri) return "%s://%s" % (parsed.scheme, parsed.netloc) def get_form_uri(self, form): """ Get form uri for form @param form as WebKit2WebExtension.DOMHTMLFormElement @return str """ form_uri = form.get_action() if form_uri is None: page = self.__extension.get_page(self.__page_id) return self.get_hostname_uri(page) else: parsed_form_uri = urlparse(form_uri) return "%s://%s" % (parsed_form_uri.scheme, parsed_form_uri.netloc) @property def pending_credentials(self): """ Get credentials @return (str, str, str, str, str, str)/None/Type.NONE """ return self.__pending_credentials ####################### # PRIVATE # ####################### def __on_get_password(self, attributes, password, form_uri, index, count, user_form_name, user_form_value, pass_form_name, pass_form_value, hostname_uri, page_id): """ Ask for credentials through DBus @param attributes as {} @param password as str @param form_uri as str @param index as int @param count as int @param user_form_name as str @param user_form_value as str @param pass_form_name as str @param pass_form_value as str @param hostname_uri as str @param page_id as int """ try: # If credentials not found (!=Type.NONE) and something new # is submitted if self.__pending_credentials != Type.NONE and ( attributes is None or attributes["login"] != user_form_value or password != pass_form_value): if attributes is None or\ attributes["login"] != user_form_value: uuid = "" else: uuid = attributes["uuid"] self.__pending_credentials = (uuid, user_form_name, user_form_value, pass_form_name, pass_form_value, hostname_uri, form_uri) else: # Found, no more lookup self.__pending_credentials = Type.NONE # Last found credentials if count < 1 or index == count - 1: # Reset pending if self.__pending_credentials == Type.NONE: self.__pending_credentials = None # Ask for user input elif self.__pending_credentials not in [None, Type.NONE]: (uuid, user_form_name, user_form_value, pass_form_name, pass_form_value, hostname_uri, form_uri) = self.__pending_credentials args = (uuid, user_form_name, user_form_value, pass_form_name, hostname_uri, form_uri) variant = GLib.Variant.new_tuple( GLib.Variant("(ssssss)", args)) self.emit("submit-form", variant) except Exception as e: Logger.error("FormsExtension::__on_get_password(): %s", e) def __on_will_submit_form(self, webpage, form, step, source, target, names, values): """ @param webpage as WebKit2WebExtension.WebPage @param form as WebKit2WebExtension.DOMHTMLFormElement @param step as WebKit2WebExtension.FormSubmissionStep @param source as WebKit2WebExtension.Frame @param target as WebKit2WebExtension.Frame @param names as [str] @param values as [str] """ if step != WebKit2WebExtension.FormSubmissionStep.SEND_DOM_EVENT or\ not names or not values: return try: # Check elements document = webpage.get_dom_document() username_idx = None password_idx = None idx = 0 for name in names: if username_idx is not None and password_idx is not None: break elements = document.get_elements_by_name(name) for elements_idx in range(0, elements.get_length()): element = elements.item(elements_idx) if isinstance(element, WebKit2WebExtension.DOMHTMLInputElement): if element.get_input_type() == "password" and\ element.get_name() is not None: password_idx = idx break elif element.get_input_type() in ["text", "email", "search"] and\ element.get_name() is not None: username_idx = idx break idx += 1 if username_idx is not None and password_idx is not None: hostname_uri = self.get_hostname_uri(webpage) form_uri = self.get_form_uri(form) user_form_name = names[username_idx] user_form_value = values[username_idx] pass_form_name = names[password_idx] pass_form_value = values[password_idx] self.__helper.get(form_uri, user_form_name, pass_form_name, self.__on_get_password, user_form_name, user_form_value, pass_form_name, pass_form_value, hostname_uri, self.__page_id) except Exception as e: print("FormsExtension::__on_will_submit_form():", e) def __on_page_created(self, extension, webpage): """ Connect to send request @param extension as WebKit2WebExtension @param webpage as WebKit2WebExtension.WebPage """ self.__page_id = webpage.get_id() webpage.connect("will-submit-form", self.__on_will_submit_form)
class PasswordsPopover(Gtk.Popover): """ Show saved passwords """ def __init__(self): """ Init popover """ Gtk.Popover.__init__(self) self.__filter = "" self.__helper = PasswordsHelper() self.set_position(Gtk.PositionType.BOTTOM) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverPasswords.ui') builder.connect_signals(self) self.__listbox = builder.get_object("listbox") self.__listbox.set_filter_func(self.__filter_func) self.__listbox.set_sort_func(self.__sort_func) self.add(builder.get_object('widget')) self.set_size_request(400, 300) def populate(self): """ Populate popover """ self.__helper.get_all(self.__add_password) ####################### # PROTECTED # ####################### def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self.__filter = entry.get_text() self.__listbox.invalidate_filter() def _on_remove_all_clicked(self, button): """ Remove all passwords @param button as Gtk.Button """ for child in self.__listbox.get_children(): child.delete() ####################### # PRIVATE # ####################### def __filter_func(self, row): """ Filter rows @param row as Row """ return self.__filter in row.item.get_property("uri") def __sort_func(self, row1, row2): """ Sort rows @param row1 as Row @param row2 as Row """ return row2.item.get_property("username") <\ row1.item.get_property("username") def __add_password(self, attributes, password, uri, index, count): """ Add password to model @param attributes as {} @param password as str @param uri as None @param index as int @param count as int """ if attributes is None: return try: item = Item() item.set_property("username", attributes["login"]) item.set_property("uri", attributes["formSubmitURL"]) item.set_property("uuid", attributes["uuid"]) child = Row(item, self.__helper) child.show() self.__listbox.add(child) except: # Here because firsts Eolie releases do not # provide formSubmitURL # FIXME Remove later pass
class FormsExtension: """ Handle forms prefill """ def __init__(self, extension, settings): """ Connect wanted signal @param extension as WebKit2WebExtension @param settings as Settings """ self.__helper = PasswordsHelper() self.__settings = settings self.__input_logins = [] self.__input_passwords = [] extension.connect("page-created", self.__on_page_created) def set_credentials(self, webpage): """ Set credentials on page @param webpage as WebKit2WebExtension.WebPage """ forms = self.update_inputs_list(webpage) for form in forms: self.__helper.get(form.get_action(), self.set_input_forms, webpage) def get_input_forms(self, webpage): """ Return forms for webpage @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ forms = [] dom_document = webpage.get_dom_document() inputs = dom_document.get_elements_by_tag_name("input") i = 0 while i < inputs.get_length(): if inputs.item(i).get_input_type() in ["text", "email", "search"]: forms.append(inputs.item(i)) i += 1 return forms def get_textarea_forms(self, webpage): """ Return forms for webpage @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ forms = [] dom_document = webpage.get_dom_document() textareas = dom_document.get_elements_by_tag_name("textarea") i = 0 while i < textareas.get_length(): forms.append(textareas.item(i)) i += 1 return forms def get_password_inputs(self, webpage): """ Return password inputs @param webpage as WebKit2WebExtension.WebPage @return [WebKit2WebExtension.DOMHTMLInputElement] """ return self.__input_passwords def is_password_input(self, name, webpage): """ Return True if a password input @param name as str @param webpage as WebKit2WebExtension.WebPage @return WebKit2WebExtension.DOMHTMLInputElement/None """ for input_password in self.__input_passwords: input_name = input_password.get_attribute("name") input_id = input_password.get_attribute("id") # We search for wanted name if name and (name == input_name or name == input_id): return True return False def set_input_forms(self, attributes, password, uri, index, count, webpage, login=None): """ Set login/password input @param attributes as {} @param password as str @param uri as str @param index as int @param count as int @param webpage as WebKit2WebExtension.WebPage @param login as str/None """ # We only set first available password if (index != 0 or count > 1) and login is None: return parsed = urlparse(uri) # Allow unsecure completion if wanted by user if parsed.scheme != "https" and login is None: return submit_uri = "%s://%s" % (parsed.scheme, parsed.netloc) # Do not set anything if no attributes or # If we have already text in input if attributes is None or\ (login is not None and attributes["login"] != login) or\ attributes["formSubmitURL"] != submit_uri: return try: wanted_input_login = None name = attributes["userform"] for input_login in self.__input_logins: input_name = input_login.get_attribute("name") input_id = input_login.get_attribute("id") # We search for wanted name if name and (name == input_name or name == input_id): wanted_input_login = input_login break if wanted_input_login is None: return wanted_input_password = None name = attributes["passform"] for input_password in self.__input_passwords: input_name = input_password.get_attribute("name") input_id = input_password.get_attribute("id") if not name or name == input_name or name == input_id: wanted_input_password = input_password break if wanted_input_password is None: return wanted_input_login.set_value(attributes["login"]) wanted_input_password.set_value(password) except Exception as e: print("FormsExtension::set_input_forms()", e) def is_login_form(self, form): """ Return True if form is a login form @param form as WebKit2WebExtension.DOMHTMLInputElement @return bool """ input_name = form.get_attribute("name") input_id = form.get_attribute("id") # We search for common name for search in LOGINS: if (input_name is not None and input_name.lower().find(search) != -1) or\ (input_id is not None and input_id.lower().find(search) != -1): return True return False def update_inputs_list(self, webpage): """ Update login and password inputs @param webpage as WebKit2WebExtension.WebPage @return forms with a password input """ self.__input_logins = [] self.__input_passwords = [] forms = [] collection = webpage.get_dom_document().get_forms() i = 0 while i < collection.get_length(): form = collection.item(i) if form.get_method() == "post": elements_collection = form.get_elements() h = 0 while h < elements_collection.get_length(): element = elements_collection.item(h) if not isinstance(element, WebKit2WebExtension.DOMHTMLInputElement): h += 1 continue if element.get_input_type() == "password": self.__input_passwords.append(element) if form not in forms: forms.append(form) elif element.get_input_type() in ["text", "email", "search"]: self.__input_logins.append(element) h += 1 i += 1 return forms ####################### # PRIVATE # ####################### def __on_page_created(self, extension, webpage): """ Connect to send request @param extension as WebKit2WebExtension @param webpage as WebKit2WebExtension.WebPage """ webpage.connect("document-loaded", self.__on_document_loaded) def __on_document_loaded(self, webpage): """ Restore forms @param webpage as WebKit2WebExtension.WebPage """ if not self.__settings.get_value("remember-passwords"): return self.set_credentials(webpage)
class FormsExtension(GObject.Object): """ Handle forms prefill """ __gsignals__ = { 'submit-form': (GObject.SignalFlags.RUN_FIRST, None, (GLib.Variant, )) } def __init__(self, extension, settings): """ Connect wanted signal @param extension as WebKit2WebExtension @param settings as Settings """ GObject.Object.__init__(self) self.__helper = PasswordsHelper() self.__extension = extension self.__settings = settings self.__elements_uri = None self.__pending_credentials = None self.__page_id = None extension.connect("page-created", self.__on_page_created) def set_credentials(self, form, webpage): """ Set credentials on page @param form as {} @param webpage as WebKit2WebExtension.WebPage """ if self.__settings.get_value("remember-passwords"): form_input_username = form["username"].get_name() form_input_password = form["password"].get_name() if form_input_username is not None and\ form_input_password is not None: self.__helper.get(form["element"].get_action(), form_input_username, form_input_password, self.set_input_forms, webpage, form) def set_input_forms(self, attributes, password, uri, index, count, webpage, form, username=None): """ Set login/password input @param attributes as {} @param password as str @param uri as str @param index as int @param count as int @param webpage as WebKit2WebExtension.WebPage @param form as {} @param username as str """ if attributes is None: return # We only set first available password if (index != 0 or count > 1) and username is None: return parsed = urlparse(form["element"].get_action()) # Allow unsecure completion if wanted by user if parsed.scheme != "https" and username is None: return # We want a user, check if it wanted password if username is not None and username != attributes["login"]: return try: form["username"].set_value(attributes["login"]) form["password"].set_value(password) except Exception as e: print("FormsExtension::set_input_forms()", e) def get_elements(self, elements): """ Get forms as dict and textareas for elements @param elements as [WebKit2WebExtension.DOMElement] @return elements as ([{}], [WebKit2WebExtension.DOMHTMLTextAreaElement]) """ forms = [] textareas = [] for element in elements: if isinstance(element, WebKit2WebExtension.DOMHTMLTextAreaElement): textareas.append(element) elif isinstance(element, WebKit2WebExtension.DOMHTMLFormElement): form = {"element": element} elements_collection = element.get_elements() h = 0 while h < elements_collection.get_length(): element = elements_collection.item(h) if isinstance(element, WebKit2WebExtension.DOMHTMLInputElement): if element.get_input_type() == "password" and\ element.get_name() is not None: form["password"] = element elif element.get_input_type() in ["text", "email", "search"] and\ element.get_name() is not None: form["username"] = element elif isinstance( element, WebKit2WebExtension.DOMHTMLTextAreaElement): textareas.append(element) h += 1 keys = form.keys() if "username" in keys and "password" in keys: forms.append(form) return (forms, textareas) def on_form_submit(self, element, event): """ Ask user for saving credentials @param element as WebKit2WebExtension.DOMElement @param event as WebKit2WebExtension.DOMUIEvent """ page = self.__extension.get_page(self.__page_id) if page is None: return (forms, textareas) = self.get_elements([element]) if not forms or not forms[0]["password"].get_value(): return try: form = forms[0] uri = page.get_uri() form_uri = form["element"].get_action() parsed_form_uri = urlparse(form_uri) form_uri = "%s://%s" % (parsed_form_uri.scheme, parsed_form_uri.netloc) user_form_name = form["username"].get_name() user_form_value = form["username"].get_value() pass_form_name = form["password"].get_name() pass_form_value = form["password"].get_value() self.__helper.get(form_uri, user_form_name, pass_form_name, self.__on_get_password, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, self.__page_id) except Exception as e: print("FormsExtension::on_form_submit():", e) @property def pending_credentials(self): """ Get credentials @return (str, str, str, str, str, str)/None/Type.NONE """ return self.__pending_credentials ####################### # PRIVATE # ####################### def __on_get_password(self, attributes, password, form_uri, index, count, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, page_id): """ Ask for credentials through DBus @param attributes as {} @param password as str @param form_uri as str @param index as int @param count as int @param user_form_name as str @param user_form_value as str @param pass_form_name as str @param pass_form_value as str @param uri as str @param page_id as int """ try: # If credentials not found (!=Type.NONE) and something new # is submitted if self.__pending_credentials != Type.NONE and ( attributes is None or attributes["login"] != user_form_value or password != pass_form_value): if attributes is None or\ attributes["login"] != user_form_value: uuid = "" else: uuid = attributes["uuid"] self.__pending_credentials = (uuid, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri) else: # Found, no more lookup self.__pending_credentials = Type.NONE # Last found credentials if count < 1 or index == count - 1: # Reset pending if self.__pending_credentials == Type.NONE: self.__pending_credentials = None # Ask for user input elif self.__pending_credentials not in [None, Type.NONE]: (uuid, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri) = self.__pending_credentials args = (uuid, user_form_name, user_form_value, pass_form_name, uri, form_uri) variant = GLib.Variant.new_tuple( GLib.Variant("(ssssss)", args)) self.emit("submit-form", variant) except Exception as e: print("FormsExtension::__on_get_password()", e) def __on_page_created(self, extension, webpage): """ Connect to send request @param extension as WebKit2WebExtension @param webpage as WebKit2WebExtension.WebPage """ self.__page_id = webpage.get_id()
class CredentialsPopover(Gtk.Popover): """ Tell user to save form credentials """ def __init__(self, username, userform, password, passform, uri, window): """ Init popover @param username as str @param password as str @param netloc as str @param window as Window """ Gtk.Popover.__init__(self) self.set_modal(False) window.register(self) self.__helper = PasswordsHelper() self.__username = username self.__userform = userform self.__password = password self.__passform = passform self.__uri = uri self.__uuid = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverCredentials.ui') builder.connect_signals(self) self.__label = builder.get_object('label') parsed = urlparse(uri) builder.get_object('uri').set_text(parsed.netloc) builder.get_object('username').set_text(username) builder.get_object('password').set_text(password) self.add(builder.get_object('widget')) ####################### # PROTECTED # ####################### def _on_save_clicked(self, button): """ Save username and password @param button as Gtk.Button """ try: parsed = urlparse(self.__uri) uri = "%s://%s" % (parsed.scheme, parsed.netloc) self.__helper.clear(self.__username, uri) if self.__uuid is None: self.__uuid = str(uuid3(NAMESPACE_DNS, parsed.netloc)) self.__helper.store(self.__username, self.__password, uri, self.__uuid, self.__userform, self.__passform, None) if El().sync_worker is not None: El().sync_worker.push_password(self.__username, self.__userform, self.__password, self.__passform, uri, self.__uuid) self.destroy() except Exception as e: print("CredentialsPopover::_on_save_clicked()", e) def popup(self): """ Overwrite popup """ self.__helper.get(self.__uri, self.__on_get_password) ####################### # PRIVATE # ####################### def __on_get_password(self, attributes, password, uri, index, count): """ Set username/password input @param attributes as {} @param password as str @param uri as str @param index as int @param count as int """ try: # No saved password if attributes is None: Gtk.Popover.popup(self) # Password saved and unchanged elif attributes["login"] == self.__username and\ self.__password == password and\ attributes["userform"] == self.__userform and\ attributes["passform"] == self.__passform: self.emit("closed") self.set_relative_to(None) # Prevent popover to be displayed # Password changed elif attributes["login"] == self.__username: Gtk.Popover.popup(self) self.__uuid = attributes["uuid"] self.__label.set_text( _("Do you want to modify this password?")) # Last password, it's a new login/password elif index == count - 1: Gtk.Popover.popup(self) except Exception as e: print("CredentialsPopover::__on_get_password()", e)
class FormsExtension: """ Handle forms prefill """ def __init__(self, extension, settings): """ Connect wanted signal @param extension as WebKit2WebExtension @param settings as Settings """ self.__helper = PasswordsHelper() self.__settings = settings extension.connect("page-created", self.__on_page_created) def get_forms(self, webpage): """ Return forms for webpage @param webpage as WebKit2WebExtension.WebPage @return (WebKit2WebExtension.DOMElement, WebKit2WebExtension.DOMElement) """ inputs = webpage.get_dom_document().get_elements_by_tag_name('input') i = 0 username_input = None password_input = None while i < inputs.get_length(): name = inputs.item(i).get_attribute('name') if name is None: i += 1 continue if password_input is None and\ inputs.item(i).get_input_type() == "password": password_input = inputs.item(i) i += 1 continue if username_input is None and\ inputs.item(i).get_input_type() != "hidden": for search in LOGINS: if name.lower().find(search) != -1: username_input = inputs.item(i) break i += 1 return (username_input, password_input) ####################### # PRIVATE # ####################### def __on_page_created(self, extension, webpage): """ Connect to send request @param extension as WebKit2WebExtension @param webpage as WebKit2WebExtension.WebPage """ webpage.connect("document-loaded", self.__on_document_loaded) def __on_document_loaded(self, webpage): """ Restore forms @param webpage as WebKit2WebExtension.WebPage """ if not self.__settings.get_value("remember-passwords"): return (username_input, password_input) = self.get_forms(webpage) if username_input is None or password_input is None: return self.__helper.get(webpage.get_uri(), self.__set_input, username_input, password_input) def __set_input(self, attributes, password, uri, username_input, password_input): """ Set username/password input @param attributes as {} @param password as str @param uri as str @param username_input as WebKit2WebExtension.DOMElement @param password_input as WebKit2WebExtension.DOMElement """ # Do not set anything if no attributes or # If we have already text in input and we are not a perfect completion if attributes is None or ( password_input.get_value() and uri != attributes["formSubmitURL"]): return try: username_input.set_value(attributes["login"]) password_input.set_value(password) except Exception as e: print("FormsExtension::__set_input()", e)
class SyncWorker: """ Manage sync with mozilla server, will start syncing on init """ def check_modules(): """ True if deps are installed @return bool """ from importlib import util fxa = util.find_spec("fxa") crypto = util.find_spec("Crypto") return fxa is not None and crypto is not None def __init__(self): """ Init worker """ self.__syncing = False self.__sync_cancellable = Gio.Cancellable() self.__username = "" self.__password = "" self.__token = "" self.__uid = "" self.__keyB = b"" self.__mtimes = {"bookmarks": 0.1, "history": 0.1} # We do not create this here because it's slow down Eolie startup # See __mozilla_sync property self.__mz = None self.__state_lock = True self.__session = None self.__helper = PasswordsHelper() self.set_credentials() def login(self, attributes, password): """ Login to service @param attributes as {} @param password as str @raise exceptions """ self.__username = "" self.__password = "" self.__uid = "" self.__token = "" self.__keyB = b"" if attributes is None or not attributes["login"] or not password: print("SyncWorker::login():", attributes) return from base64 import b64encode session = None self.__username = attributes["login"] self.__password = password # Connect to mozilla sync try: session = self.__mozilla_sync.login(self.__username, password) bid_assertion, key = self.__mozilla_sync.\ get_browserid_assertion(session) self.__token = session.token self.__uid = session.uid self.__keyB = session.keys[1] keyB_encoded = b64encode(self.__keyB).decode("utf-8") self.__helper.clear_sync(self.__helper.store_sync, self.__username, password, self.__uid, self.__token, keyB_encoded, self.on_password_stored, True) except Exception as e: self.__helper.clear_sync(self.__helper.store_sync, attributes["login"], password, "", "", "") raise e def new_session(self): """ Start a new session """ # Just reset session, will be set by get_session_bulk_keys() self.__session = None def set_credentials(self): """ Set credentials using Secret """ self.__helper.get_sync(self.__set_credentials) def sync(self, loop=False, first_sync=False): """ Start syncing, you need to check sync_status property @param loop as bool -> for GLib.timeout_add() @param first_sync as bool """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password and not self.syncing: task_helper = TaskHelper() task_helper.run(self.__sync, first_sync) return loop def push_history(self, history_ids): """ Push history ids @param history_ids as [int] """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password: task_helper = TaskHelper() task_helper.run(self.__push_history, history_ids) def push_password(self, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri, uuid): """ Push password @param user_form_name as str @param user_form_value as str @param pass_form_name as str @param pass_form_value as str @param uri as str @param form_uri as str @param uuid as str """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password: task_helper = TaskHelper() task_helper.run(self.__push_password, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri, uuid) def remove_from_history(self, guid): """ Remove history guid from remote history @param guid as str """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password: task_helper = TaskHelper() task_helper.run(self.__remove_from_history, guid) def remove_from_bookmarks(self, guid): """ Remove bookmark guid from remote bookmarks @param guid as str """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password: task_helper = TaskHelper() task_helper.run(self.__remove_from_bookmarks, guid) def remove_from_passwords(self, uuid): """ Remove password from passwords collection @param uuid as str """ if Gio.NetworkMonitor.get_default().get_network_available() and\ self.__username and self.__password: task_helper = TaskHelper() task_helper.run(self.__remove_from_passwords, uuid) def delete_secret(self): """ Delete sync secret """ self.__username = "" self.__password = "" self.__session = None self.__sync_cancellable.reset() self.__helper.clear_sync(None) def stop(self, force=False): """ Stop update, if force, kill session too @param force as bool """ self.__sync_cancellable.cancel() if force: self.__session = None def on_password_stored(self, secret, result, sync): """ Update credentials @param secret as Secret @param result as Gio.AsyncResult @param sync as bool """ self.set_credentials() if sync: # Wait for credentials (server side) GLib.timeout_add(10, self.sync, True) @property def mtimes(self): """ Sync engine modification times @return {} """ return self.__mtimes @property def syncing(self): """ True if sync is running @return bool """ return self.__syncing @property def status(self): """ True if sync is working @return bool """ try: if self.__username: self.__get_session_bulk_keys() self.__mozilla_sync.client.info_collections() return True except Exception as e: print("SyncWorker::status(): %s" % e) @property def username(self): """ Get username @return str """ return self.__username ####################### # PRIVATE # ####################### @property def __mozilla_sync(self): """ Get mozilla sync, create if None """ if self.__mz is None: self.__mz = MozillaSync() return self.__mz def __get_session_bulk_keys(self): """ Get session decrypt keys @return keys as (b"", b"") """ if self.__session is None: from fxa.core import Session as FxASession from fxa.crypto import quick_stretch_password self.__session = FxASession(self.__mozilla_sync.fxa_client, self.__username, quick_stretch_password( self.__username, self.__password), self.__uid, self.__token) self.__session.keys = [b"", self.__keyB] self.__session.check_session_status() bid_assertion, key = self.__mozilla_sync.get_browserid_assertion( self.__session) bulk_keys = self.__mozilla_sync.connect(bid_assertion, key) return bulk_keys def __update_state(self): """ Update state file """ try: # If syncing, state will be written by self.__sync() if not self.syncing: f = open(EOLIE_DATA_PATH + "/mozilla_sync.bin", "wb") # Lock file flock(f, LOCK_EX | LOCK_NB) self.__mtimes = self.__mozilla_sync.client.info_collections() dump(self.__mtimes, f) # Unlock file flock(f, LOCK_UN) except Exception as e: debug("SyncWorker::__update_state(): %s" % e) def __push_history(self, history_ids): """ Push history ids if atime is available, else, ask to remove @param history ids as [int] """ try: bulk_keys = self.__get_session_bulk_keys() for history_id in history_ids: self.__check_worker() sleep(0.01) record = {} atimes = El().history.get_atimes(history_id) guid = El().history.get_guid(history_id) if atimes: record["histUri"] = El().history.get_uri(history_id) record["id"] = guid record["title"] = El().history.get_title(history_id) record["visits"] = [] for atime in atimes: record["visits"].append({"date": atime*1000000, "type": 1}) debug("pushing %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) else: record["id"] = guid record["type"] = "item" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) self.__update_state() except Exception as e: debug("SyncWorker::__push_history(): %s" % e) def __push_password(self, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri, uuid): """ Push password @param user_form_name as str @param user_form_value as str @param pass_form_name as str @param pass_form_value as str @param uri as str @param uuid as str """ try: self.__check_worker() bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = "{%s}" % uuid record["hostname"] = uri record["formSubmitURL"] = form_uri record["httpRealm"] = None record["username"] = user_form_value record["password"] = pass_form_value record["usernameField"] = user_form_name record["passwordField"] = pass_form_name mtime = int(time()*1000) record["timeCreated"] = mtime record["timePasswordChanged"] = mtime debug("pushing %s" % record) self.__mozilla_sync.add(record, "passwords", bulk_keys) self.__update_state() except Exception as e: print("SyncWorker::__push_password():", e) def __remove_from_history(self, guid): """ Remove from history @param guid as str """ try: self.__check_worker() bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = guid record["type"] = "item" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) self.__update_state() except Exception as e: debug("SyncWorker::__remove_from_history(): %s" % e) def __remove_from_bookmarks(self, guid): """ Remove from history @param guid as str """ try: self.__check_worker() bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = guid record["type"] = "bookmark" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "bookmark", bulk_keys) self.__update_state() except Exception as e: debug("SyncWorker::__remove_from_bookmarks(): %s" % e) def __remove_from_passwords(self, uuid): """ Remove password from passwords collection @param uuid as str """ try: self.__check_worker() bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = uuid record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "passwords", bulk_keys) self.__update_state() except Exception as e: debug("SyncWorker::__remove_from_passwords(): %s" % e) def __sync(self, first_sync): """ Sync Eolie objects (bookmarks, history, ...) with Mozilla Sync @param first_sync as bool """ debug("Start syncing") self.__syncing = True self.__sync_cancellable.reset() try: self.__mtimes = load(open(EOLIE_DATA_PATH + "/mozilla_sync.bin", "rb")) except: self.__mtimes = {"bookmarks": 0.1, "history": 0.1, "passwords": 0.1} try: self.__check_worker() bulk_keys = self.__get_session_bulk_keys() new_mtimes = self.__mozilla_sync.client.info_collections() self.__check_worker() ######################## # Passwords Management # ######################## try: debug("local passwords: %s, remote passwords: %s" % ( self.__mtimes["passwords"], new_mtimes["passwords"])) # Only pull if something new available if self.__mtimes["passwords"] != new_mtimes["passwords"]: self.__pull_passwords(bulk_keys) except: pass # No passwords in sync self.__check_worker() ###################### # History Management # ###################### try: debug("local history: %s, remote history: %s" % ( self.__mtimes["history"], new_mtimes["history"])) # Only pull if something new available if self.__mtimes["history"] != new_mtimes["history"]: self.__pull_history(bulk_keys) except: pass # No history in sync self.__check_worker() ######################## # Bookmarks Management # ######################## try: debug("local bookmarks: %s, remote bookmarks: %s" % ( self.__mtimes["bookmarks"], new_mtimes["bookmarks"])) # Push new bookmarks self.__push_bookmarks(bulk_keys) except: pass # No bookmarks in sync self.__check_worker() # Only pull if something new available if self.__mtimes["bookmarks"] != new_mtimes["bookmarks"]: self.__pull_bookmarks(bulk_keys, first_sync) # Update last sync mtime self.__syncing = False self.__update_state() debug("Stop syncing") except Exception as e: debug("SyncWorker::__sync(): %s" % e) if str(e) == "The authentication token could not be found": self.set_credentials() self.__syncing = False def __push_bookmarks(self, bulk_keys): """ Push to bookmarks @param bulk keys as KeyBundle @param start time as float @raise StopIteration """ debug("push bookmarks") parents = [] for bookmark_id in El().bookmarks.get_ids_for_mtime( self.__mtimes["bookmarks"]): self.__check_worker() sleep(0.01) parent_guid = El().bookmarks.get_parent_guid(bookmark_id) # No parent, move it to unfiled if parent_guid is None: parent_guid = "unfiled" parent_id = El().bookmarks.get_id_by_guid(parent_guid) if parent_id not in parents: parents.append(parent_id) record = {} record["bmkUri"] = El().bookmarks.get_uri(bookmark_id) record["id"] = El().bookmarks.get_guid(bookmark_id) record["title"] = El().bookmarks.get_title(bookmark_id) record["tags"] = El().bookmarks.get_tags(bookmark_id) record["parentid"] = parent_guid record["parentName"] = El().bookmarks.get_parent_name(bookmark_id) record["type"] = "bookmark" debug("pushing %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) # Del old bookmarks for bookmark_id in El().bookmarks.get_deleted_ids(): self.__check_worker() sleep(0.01) parent_guid = El().bookmarks.get_parent_guid(bookmark_id) parent_id = El().bookmarks.get_id_by_guid(parent_guid) if parent_id not in parents: parents.append(parent_id) record = {} record["id"] = El().bookmarks.get_guid(bookmark_id) record["type"] = "bookmark" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) El().bookmarks.remove(bookmark_id) # Push parents in this order, parents near root are handled later # Otherwise, order will be broken by new children updates while parents: parent_id = parents.pop(0) parent_guid = El().bookmarks.get_guid(parent_id) parent_name = El().bookmarks.get_title(parent_id) children = El().bookmarks.get_children(parent_guid) # So search if children in parents found = False for child_guid in children: child_id = El().bookmarks.get_id_by_guid(child_guid) if child_id in parents: found = True break # Handle children first if found: parents.append(parent_id) debug("later: %s" % parent_name) continue record = {} record["id"] = parent_guid record["type"] = "folder" # A parent with parent as unfiled needs to be moved to places # Firefox internal grand_parent_guid = El().bookmarks.get_parent_guid(parent_id) if grand_parent_guid == "unfiled": grand_parent_guid = "places" record["parentid"] = grand_parent_guid record["parentName"] = El().bookmarks.get_parent_name(parent_id) record["title"] = parent_name record["children"] = children debug("pushing parent %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) El().bookmarks.clean_tags() def __pull_bookmarks(self, bulk_keys, first_sync): """ Pull from bookmarks @param bulk_keys as KeyBundle @param first_sync as bool @raise StopIteration """ debug("pull bookmarks") SqlCursor.add(El().bookmarks) records = self.__mozilla_sync.get_records("bookmarks", bulk_keys) children_array = [] for record in records: self.__check_worker() if record["modified"] < self.__mtimes["bookmarks"]: continue sleep(0.01) bookmark = record["payload"] bookmark_id = El().bookmarks.get_id_by_guid(bookmark["id"]) # Nothing to apply, continue if El().bookmarks.get_mtime(bookmark_id) >= record["modified"]: continue debug("pulling %s" % record) # Deleted bookmark if "deleted" in bookmark.keys(): El().bookmarks.remove(bookmark_id) # Keep folder only for firefox compatiblity elif "type" in bookmark.keys() and bookmark["type"] == "folder"\ and bookmark["id"] is not None\ and bookmark["title"]: if bookmark_id is None: bookmark_id = El().bookmarks.add(bookmark["title"], bookmark["id"], bookmark["id"], [], 0, False) # Will calculate position later if "children" in bookmark.keys(): children_array.append(bookmark["children"]) # We have a bookmark, add it elif "type" in bookmark.keys() and bookmark["type"] == "bookmark"\ and bookmark["id"] is not None\ and bookmark["title"]: # Add a new bookmark if bookmark_id is None: # Use parent name if no bookmarks tags if "tags" not in bookmark.keys() or\ not bookmark["tags"]: if "parentName" in bookmark.keys() and\ bookmark["parentName"]: bookmark["tags"] = [bookmark["parentName"]] else: bookmark["tags"] = [] bookmark_id = El().bookmarks.add(bookmark["title"], bookmark["bmkUri"], bookmark["id"], bookmark["tags"], 0, False) # Update bookmark else: El().bookmarks.set_title(bookmark_id, bookmark["title"], False) El().bookmarks.set_uri(bookmark_id, bookmark["bmkUri"], False) # Update tags current_tags = El().bookmarks.get_tags(bookmark_id) for tag in El().bookmarks.get_tags(bookmark_id): if "tags" in bookmark.keys() and\ tag not in bookmark["tags"]: tag_id = El().bookmarks.get_tag_id(tag) current_tags.remove(tag) El().bookmarks.del_tag_from(tag_id, bookmark_id, False) if "tags" in bookmark.keys(): for tag in bookmark["tags"]: # Tag already associated if tag in current_tags: continue tag_id = El().bookmarks.get_tag_id(tag) if tag_id is None: tag_id = El().bookmarks.add_tag(tag, False) El().bookmarks.add_tag_to(tag_id, bookmark_id, False) # Update parent name if available if bookmark_id is not None and "parentName" in bookmark.keys(): El().bookmarks.set_parent(bookmark_id, bookmark["parentid"], bookmark["parentName"], False) El().bookmarks.set_mtime(bookmark_id, record["modified"], False) # Update bookmark position for children in children_array: position = 0 for child in children: bid = El().bookmarks.get_id_by_guid(child) El().bookmarks.set_position(bid, position, False) position += 1 El().bookmarks.clean_tags() # Will commit SqlCursor.remove(El().bookmarks) def __pull_passwords(self, bulk_keys): """ Pull from passwords @param bulk_keys as KeyBundle @raise StopIteration """ debug("pull passwords") records = self.__mozilla_sync.get_records("passwords", bulk_keys) for record in records: self.__check_worker() if record["modified"] < self.__mtimes["passwords"]: continue sleep(0.01) debug("pulling %s" % record) password = record["payload"] password_id = password["id"].strip("{}") if "formSubmitURL" in password.keys(): self.__helper.clear(password_id, self.__helper.store, password["usernameField"], password["username"], password["passwordField"], password["password"], password["hostname"], password["formSubmitURL"], password_id, None) elif "deleted" in password.keys(): # We assume True self.__helper.clear(password_id) def __pull_history(self, bulk_keys): """ Pull from history @param bulk_keys as KeyBundle @raise StopIteration """ debug("pull history") records = self.__mozilla_sync.get_records("history", bulk_keys) for record in records: self.__check_worker() if record["modified"] < self.__mtimes["history"]: continue sleep(0.01) El().history.thread_lock.acquire() history = record["payload"] keys = history.keys() history_id = El().history.get_id_by_guid(history["id"]) # Check we have a valid history item if "histUri" in keys and\ "title" in keys and\ history["title"] and\ El().history.get_mtime(history_id) < record["modified"]: # Try to get visit date atimes = [] try: for visit in history["visits"]: atimes.append(round(int(visit["date"]) / 1000000, 2)) except: El().history.thread_lock.release() continue debug("pulling %s" % record) title = history["title"].rstrip().lstrip() history_id = El().history.add(title, history["histUri"], record["modified"], history["id"], atimes, True) elif "deleted" in keys: history_id = El().history.get_id_by_guid(history_id) El().history.remove(history_id) El().history.thread_lock.release() def __set_credentials(self, attributes, password, uri, index, count): """ Set credentials @param attributes as {} @param password as str @param uri as None @param index as int @param count as int """ if attributes is None: return from base64 import b64decode try: self.__username = attributes["login"] self.__password = password self.__token = attributes["token"] self.__uid = attributes["uid"] self.__keyB = b64decode(attributes["keyB"]) # Force login if no token if not self.__token: self.login(attributes, password) except Exception as e: debug("SyncWorker::__set_credentials(): %s" % e) def __check_worker(self): """ Raise an exception if worker should not be syncing: error&cancel """ if self.__sync_cancellable.is_cancelled(): raise StopIteration("SyncWorker: cancelled") elif not self.__username: raise StopIteration("SyncWorker: missing username") elif not self.__password: raise StopIteration("SyncWorker: missing password") elif not self.__token: raise StopIteration("SyncWorker: missing token")
class ProxyExtensionServer(Server): ''' <!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'> <node> <interface name="org.gnome.Eolie.Proxy"> <method name="FormsFilled"> <arg type="b" name="results" direction="out" /> </method> <method name="SetPreviousElementValue"> </method> <method name="SetNextElementValue"> </method> <method name="SaveCredentials"> <arg type="s" name="uuid" direction="in" /> <arg type="s" name="user_form_name" direction="in" /> <arg type="s" name="pass_form_name" direction="in" /> <arg type="s" name="uri" direction="in" /> <arg type="s" name="form_uri" direction="in" /> </method> <method name="SetAuthForms"> <arg type="s" name="username" direction="in" /> <arg type="s" name="username" direction="in" /> </method> <method name="GetScripts"> <arg type="as" name="results" direction="out" /> </method> <method name="GetImages"> <arg type="as" name="results" direction="out" /> </method> <method name="GetVideos"> <arg type="aas" name="results" direction="out" /> </method> <method name="GetImageLinks"> <arg type="as" name="results" direction="out" /> </method> <method name="GetSelection"> <arg type="s" name="selection" direction="out" /> </method> <signal name='UnsecureFormFocused'> <arg type="as" name="forms" direction="out" /> </signal> <signal name='ShowCredentials'> <arg type="as" name="forms" direction="out" /> <arg type="as" name="form_uri" direction="out" /> </signal> <signal name='AskSaveCredentials'> <arg type="s" name="uuid" direction="out" /> <arg type="s" name="user_form_value" direction="out" /> <arg type="s" name="uri" direction="out" /> <arg type="s" name="form_uri" direction="out" /> </signal> </interface> </node> ''' def __init__(self, extension, page, form_extension, jsblock_extension): """ Init server @param extension as WebKit2WebExtension.WebExtension @param page as WebKit2WebExtension.WebPage @param form_extension as FormsExtension @param jsblock_extension as JSBlockExtension """ self.__extension = extension self.__page = page self.__form_extension = form_extension self.__jsblock_extension = jsblock_extension self.__focused = None self.__on_input_timeout_id = None self.__elements_history = {} self.__send_requests = [] self.__helper = PasswordsHelper() self.__proxy_bus = PROXY_BUS % self.__page.get_id() addr = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION, None) self.__bus = None Gio.DBusConnection.new_for_address( addr, Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None, self.__on_bus_new_for_address) form_extension.connect("submit-form", self.__on_submit_form) page.connect("send-request", self.__on_send_request) page.connect("context-menu", self.__on_context_menu) page.connect("notify::uri", self.__on_notify_uri) page.connect("form-controls-associated", self.__on_form_control_associated) def FormsFilled(self): """ True if form contains data @return bool """ dom = self.__page.get_dom_document() collection = dom.get_elements_by_tag_name("textarea") for i in range(0, collection.get_length()): if collection.item(i).is_edited(): return True return False def SaveCredentials(self, uuid, user_form_name, pass_form_name, uri, form_uri): """ Save credentials to org.freedesktop.Secrets @param uuid as str @param user_form_name as str @param pass_form_name as str @param uri as str @param form_uri as str """ if self.__form_extension.pending_credentials in [None, Type.NONE]: return try: (_uuid, _user_form_name, user_form_value, _pass_form_name, pass_form_value, _uri, _form_uri) = self.__form_extension.pending_credentials if user_form_name != _user_form_name or\ pass_form_name != _pass_form_name or\ uri != _uri or\ form_uri != _form_uri: return parsed = urlparse(uri) uri = "%s://%s" % (parsed.scheme, parsed.netloc) if not uuid: uuid = str(uuid4()) self.__helper.store(user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri, uuid, None) else: self.__helper.clear(uuid, self.__helper.store, user_form_name, user_form_value, pass_form_name, pass_form_value, uri, form_uri, uuid, None) except Exception as e: print("ProxyExtension::SaveCredentials():", e) def SetAuthForms(self, userform, username): """ Get password forms @param userform as str """ try: collection = self.__page.get_dom_document().get_forms() elements = [] for i in range(0, collection.get_length()): elements.append(collection.item(i)) (forms, textareas) = self.__form_extension.get_elements(elements) for form in forms: if form["username"].get_name() == userform: self.__helper.get(form["element"].get_action(), userform, form["password"].get_name(), self.__form_extension.set_input_forms, self.__page, form, username) return except Exception as e: print("ProxyExtension::SetAuthForms():", e) def GetScripts(self): """ Get images @return [str] """ try: return self.__jsblock_extension.scripts except Exception as e: print("ProxyExtension::GetScripts():", e) return [] def GetImages(self): """ Get images @return [str] """ try: page = self.__extension.get_page(self.__page.get_id()) if page is None: return [] dom_list = page.get_dom_document().get_elements_by_tag_name("img") uris = [] for i in range(0, dom_list.get_length()): uri = dom_list.item(i).get_src() if uri not in uris: uris.append(uri) return uris except Exception as e: print("ProxyExtension::GetImages():", e) return [] def GetVideos(self): """ Get videos @return [str] """ page = self.__extension.get_page(self.__page.get_id()) if page is None: return [] videos = [] extensions = ["avi", "flv", "mp4", "mpg", "mpeg", "webm"] for uri in self.__send_requests: parsed = urlparse(uri) title = None # Search for video in page if uri.split(".")[-1] in extensions: title = uri elif parsed.netloc.endswith("googlevideo.com") and\ parsed.path == "/videoplayback": title = page.get_dom_document().get_title() if title is None: title = uri # For youtube, we only want one video: return [(title, uri)] if title is not None and (title, uri) not in videos: videos.append((title, uri)) return videos def GetImageLinks(self): """ Get image links @return [str] """ try: page = self.__extension.get_page(self.__page.get_id()) if page is None: return [] dom_list = page.get_dom_document().get_elements_by_tag_name("a") uris = [] for i in range(0, dom_list.get_length()): uri = dom_list.item(i).get_href() if uri is None or uri in uris: continue ext = uri.split(".")[-1] if ext in ["gif", "jpg", "png", "jpeg"]: uris.append(uri) return uris except Exception as e: print("ProxyExtension::GetImagesLinks():", e) return [] def GetSelection(self): """ Get selected text @param page id as int @return str """ webpage = self.__extension.get_page(self.__page.get_id()) document = webpage.get_dom_document() if document is None: return "" window = document.get_default_view() if window is None: return "" selection = window.get_selection() if selection is None: return "" try: dom_range = selection.get_range_at(0) except: dom_range = None value = "" if dom_range is not None: value = dom_range.to_string() if value is None: value = "" return value def SetPreviousElementValue(self): """ Set focused form to previous value """ if self.__focused is None: return try: value = self.__focused.get_value().rstrip(" ") if self.__focused in self.__elements_history.keys(): current = self.__elements_history[self.__focused] if current.prev is not None: self.__elements_history[self.__focused] = current.prev self.__focused.set_value(current.prev.value) elif value: next = LinkedList(value, None, None) current = LinkedList("", next, None) next.set_prev(current) self.__elements_history[self.__focused] = current self.__focused.set_value("") except Exception as e: print("ProxyExtension::SetPreviousForm():", e) def SetNextElementValue(self): """ Set focused form to next value """ if self.__focused is None: return try: if self.__focused in self.__elements_history.keys(): current = self.__elements_history[self.__focused] if current.next is not None: self.__elements_history[self.__focused] = current.next self.__focused.set_value(current.next.value) except Exception as e: print("ProxyExtension::SetNextForm():", e) ####################### # PRIVATE # ####################### def __add_event_listeners(self, forms, textareas, webpage): """ Add event listeners on inputs and textareas @param forms as {} @param textareas as [WebKit2WebExtension.DOMHTMLTextAreaElement] @param webpage as WebKit2WebExtension.WebPage """ self.__focused = None self.__elements_history = {} parsed = urlparse(webpage.get_uri()) # Manage forms events for form in forms: form["username"].add_event_listener("input", self.__on_input, False) form["username"].add_event_listener("focus", self.__on_focus, False) form["username"].add_event_listener("blur", self.__on_blur, False) form["username"].add_event_listener("mousedown", self.__on_mouse_down, False) # Check for unsecure content if parsed.scheme == "http": form["password"].add_event_listener("focus", self.__on_focus, False) # Manage textareas events for textarea in textareas: textarea.add_event_listener("input", self.__on_input, False) textarea.add_event_listener("focus", self.__on_focus, False) def __on_bus_new_for_address(self, source, result): """ Init bus @param source as GObject.Object @param result as Gio.AsyncResult """ self.__bus = source.new_for_address_finish(result) Gio.bus_own_name_on_connection(self.__bus, self.__proxy_bus, Gio.BusNameOwnerFlags.NONE, None, None) Server.__init__(self, self.__bus, PROXY_PATH) def __on_focus(self, element, event): """ Keep last focused form @param element as WebKit2WebExtension.DOMElement @param event as WebKit2WebExtension.DOMUIEvent """ if isinstance(element, WebKit2WebExtension.DOMHTMLInputElement): if element.get_input_type() == "password" and\ self.__bus is not None: self.__bus.emit_signal( None, PROXY_PATH, self.__proxy_bus, "UnsecureFormFocused", None) self.__focused = element def __on_blur(self, element, event): """ Reset focus @param element as WebKit2WebExtension.DOMElement @param event as WebKit2WebExtension.DOMUIEvent """ if self.__focused == element: self.__focused = None def __on_mouse_down(self, element, event): """ Emit Input mouse down signal @param element as WebKit2WebExtension.DOMHTMLInputElement @param event as WebKit2WebExtension.DOMUIEvent """ if element.get_name() is None: return if self.__focused != element and\ self.__bus is not None: form_uri = element.get_form().get_action() parsed_form_uri = urlparse(form_uri) form_uri = "%s://%s" % (parsed_form_uri.scheme, parsed_form_uri.netloc) args = GLib.Variant("(ss)", (element.get_name(), form_uri)) self.__bus.emit_signal( None, PROXY_PATH, self.__proxy_bus, "ShowCredentials", args) def __on_input(self, element, event): """ Run a timeout before saving buffer to history @param element as WebKit2WebExtension.DOMElement @param event as WebKit2WebExtension.DOMUIEvent """ if self.__on_input_timeout_id is not None: GLib.source_remove(self.__on_input_timeout_id) self.__on_input_timeout_id = GLib.timeout_add(500, self.__on_input_timeout, element) def __on_input_timeout(self, element): """ Save buffer to history @param element as WebKit2WebExtension.DOMElement """ self.__on_input_timeout_id = None new_value = element.get_value() item = LinkedList("", None, None) if element in self.__elements_history.keys(): item = self.__elements_history[element] next = LinkedList(new_value.rstrip(" "), None, item) if item is not None: item.set_next(next) self.__elements_history[element] = next def __on_form_control_associated(self, webpage, elements): """ Add elements to forms @param webpage as WebKit2WebExtension.WebPage @param elements as [WebKit2WebExtension.DOMElement] """ (forms, textareas) = self.__form_extension.get_elements(elements) self.__add_event_listeners(forms, textareas, webpage) for form in forms: form["element"].add_event_listener( "submit", self.__form_extension.on_form_submit, False) self.__form_extension.set_credentials(form, webpage) def __on_context_menu(self, webpage, context_menu, hit): """ Add selection to context menu user data @param webpage as WebKit2WebExtension.WebPage @param context_menu as WebKit2WebExtension.ContextMenu @param hit as WebKit2.HitTestResult """ value = self.GetSelection() context_menu.set_user_data(GLib.Variant("s", value)) def __on_submit_form(self, forms, variant): """ Ask for credentials save @param forms as FormsExtension @param variant as GLib.Variant """ if self.__bus is not None: self.__bus.emit_signal( None, PROXY_PATH, self.__proxy_bus, "AskSaveCredentials", variant) def __on_notify_uri(self, webpage, param): """ Reset send requests @param webpage as WebKit2WebExtension.WebPage @param uri as GObject.ParamSpec """ self.__send_requests = [] def __on_send_request(self, webpage, request, redirect): """ Keep send requests @param webpage as WebKit2WebExtension.WebPage @param request as WebKit2.URIRequest @param redirect as WebKit2WebExtension.URIResponse """ self.__send_requests.append(request.get_uri())
class PasswordsPopover(Gtk.Popover): """ Show saved passwords """ def __init__(self): """ Init popover """ Gtk.Popover.__init__(self) self.__filter = "" self.__helper = PasswordsHelper() self.set_position(Gtk.PositionType.BOTTOM) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverPasswords.ui') builder.connect_signals(self) self.__listbox = builder.get_object("listbox") self.__listbox.set_filter_func(self.__filter_func) self.add(builder.get_object('widget')) self.set_size_request(300, 300) def populate(self): """ Populate popover """ self.__helper.get_all(self.__add_password) ####################### # PROTECTED # ####################### def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self.__filter = entry.get_text() self.__listbox.invalidate_filter() def _on_remove_all_clicked(self, button): """ Remove all passwords @param button as Gtk.Button """ for child in self.__listbox.get_children(): child.delete() ####################### # PRIVATE # ####################### def __filter_func(self, row): """ Filter rows @param row as Row """ return self.__filter in row.item.get_property("uri") def __add_password(self, attributes, password, uri): """ Add password to model @param attributes as {} @param password as str @param uri as None """ if attributes is None: return item = Item() item.set_property("uri", attributes["formSubmitURL"]) child = Row(item, self.__helper) child.show() self.__listbox.add(child)
def __init__(self, window): """ Init dialog @param window as Window """ self.__helper = PasswordsHelper() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/SettingsDialog.ui") self.__settings_dialog = builder.get_object("settings_dialog") self.__settings_dialog.set_transient_for(window) # self.__settings_dialog.connect("destroy", self.__on_destroy) if False: self.__settings_dialog.set_title(_("Preferences")) else: headerbar = builder.get_object("header_bar") headerbar.set_title(_("Preferences")) self.__settings_dialog.set_titlebar(headerbar) download_chooser = builder.get_object("download_chooser") dir_uri = El().settings.get_value("download-uri").get_string() if not dir_uri: directory = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD) if directory is not None: dir_uri = GLib.filename_to_uri(directory, None) if dir_uri: download_chooser.set_uri(dir_uri) else: download_chooser.set_uri("file://" + GLib.getenv("HOME")) open_downloads = builder.get_object("open_downloads_check") open_downloads.set_active( El().settings.get_value("open-downloads")) self.__start_page_uri = builder.get_object("start_page_uri") combo_start = builder.get_object("combo_start") start_page = El().settings.get_value("start-page").get_string() if start_page.startswith("http"): combo_start.set_active_id("address") self.__start_page_uri.set_text(start_page) self.__start_page_uri.show() else: combo_start.set_active_id(start_page) remember_session = builder.get_object("remember_sessions_check") remember_session.set_active( El().settings.get_value("remember-session")) enable_plugins = builder.get_object("plugins_check") enable_plugins.set_active( El().settings.get_value("enable-plugins")) self.__fonts_grid = builder.get_object("fonts_grid") use_system_fonts = builder.get_object("system_fonts_check") use_system_fonts.set_active( El().settings.get_value("use-system-fonts")) self.__fonts_grid.set_sensitive( not El().settings.get_value("use-system-fonts")) sans_serif_button = builder.get_object("sans_serif_button") sans_serif_button.set_font_name( El().settings.get_value("font-sans-serif").get_string()) serif_button = builder.get_object("serif_button") serif_button.set_font_name( El().settings.get_value("font-serif").get_string()) monospace_button = builder.get_object("monospace_button") monospace_button.set_font_name( El().settings.get_value("font-monospace").get_string()) min_font_size_spin = builder.get_object("min_font_size_spin") min_font_size_spin.set_value( El().settings.get_value("min-font-size").get_int32()) monitor_model = get_current_monitor_model(window) zoom_levels = El().settings.get_value("default-zoom-level") wanted_zoom_level = 1.0 try: for zoom_level in zoom_levels: zoom_splited = zoom_level.split('@') if zoom_splited[0] == monitor_model: wanted_zoom_level = float(zoom_splited[1]) except: pass default_zoom_level = builder.get_object("default_zoom_level") default_zoom_level.set_value(float(wanted_zoom_level)) cookies_combo = builder.get_object("cookies_combo") storage = El().settings.get_enum("cookie-storage") cookies_combo.set_active_id(str(storage)) history_combo = builder.get_object("history_combo") storage = El().settings.get_enum("history-storage") history_combo.set_active_id(str(storage)) self.__populars_count = builder.get_object("populars_count") if start_page == "popular": self.__populars_count.show() max_popular_items = El().settings.get_value( "max-popular-items").get_int32() builder.get_object("popular_spin_button").set_value(max_popular_items) remember_passwords = builder.get_object("remember_passwords_check") remember_passwords.set_active( El().settings.get_value("remember-passwords")) tracking_check = builder.get_object("tracking_check") tracking_check.set_active( El().settings.get_value("do-not-track")) self.__result_label = builder.get_object("result_label") self.__sync_button = builder.get_object("sync_button") self.__login_entry = builder.get_object("login_entry") self.__password_entry = builder.get_object("password_entry") self.__result_image = builder.get_object("result_image") builder.connect_signals(self) self.__helper.get_sync(self.__on_get_sync) thread = Thread(target=self.__get_sync_status) thread.daemon = True thread.start()
class SettingsDialog: """ Dialog showing eolie options """ def __init__(self, window): """ Init dialog @param window as Window """ self.__helper = PasswordsHelper() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/SettingsDialog.ui") self.__settings_dialog = builder.get_object("settings_dialog") self.__settings_dialog.set_transient_for(window) # self.__settings_dialog.connect("destroy", self.__on_destroy) if False: self.__settings_dialog.set_title(_("Preferences")) else: headerbar = builder.get_object("header_bar") headerbar.set_title(_("Preferences")) self.__settings_dialog.set_titlebar(headerbar) download_chooser = builder.get_object("download_chooser") dir_uri = El().settings.get_value("download-uri").get_string() if not dir_uri: directory = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD) if directory is not None: dir_uri = GLib.filename_to_uri(directory, None) if dir_uri: download_chooser.set_uri(dir_uri) else: download_chooser.set_uri("file://" + GLib.getenv("HOME")) open_downloads = builder.get_object("open_downloads_check") open_downloads.set_active( El().settings.get_value("open-downloads")) self.__start_page_uri = builder.get_object("start_page_uri") combo_start = builder.get_object("combo_start") start_page = El().settings.get_value("start-page").get_string() if start_page.startswith("http"): combo_start.set_active_id("address") self.__start_page_uri.set_text(start_page) self.__start_page_uri.show() else: combo_start.set_active_id(start_page) remember_session = builder.get_object("remember_sessions_check") remember_session.set_active( El().settings.get_value("remember-session")) enable_plugins = builder.get_object("plugins_check") enable_plugins.set_active( El().settings.get_value("enable-plugins")) self.__fonts_grid = builder.get_object("fonts_grid") use_system_fonts = builder.get_object("system_fonts_check") use_system_fonts.set_active( El().settings.get_value("use-system-fonts")) self.__fonts_grid.set_sensitive( not El().settings.get_value("use-system-fonts")) sans_serif_button = builder.get_object("sans_serif_button") sans_serif_button.set_font_name( El().settings.get_value("font-sans-serif").get_string()) serif_button = builder.get_object("serif_button") serif_button.set_font_name( El().settings.get_value("font-serif").get_string()) monospace_button = builder.get_object("monospace_button") monospace_button.set_font_name( El().settings.get_value("font-monospace").get_string()) min_font_size_spin = builder.get_object("min_font_size_spin") min_font_size_spin.set_value( El().settings.get_value("min-font-size").get_int32()) monitor_model = get_current_monitor_model(window) zoom_levels = El().settings.get_value("default-zoom-level") wanted_zoom_level = 1.0 try: for zoom_level in zoom_levels: zoom_splited = zoom_level.split('@') if zoom_splited[0] == monitor_model: wanted_zoom_level = float(zoom_splited[1]) except: pass default_zoom_level = builder.get_object("default_zoom_level") default_zoom_level.set_value(float(wanted_zoom_level)) cookies_combo = builder.get_object("cookies_combo") storage = El().settings.get_enum("cookie-storage") cookies_combo.set_active_id(str(storage)) history_combo = builder.get_object("history_combo") storage = El().settings.get_enum("history-storage") history_combo.set_active_id(str(storage)) self.__populars_count = builder.get_object("populars_count") if start_page == "popular": self.__populars_count.show() max_popular_items = El().settings.get_value( "max-popular-items").get_int32() builder.get_object("popular_spin_button").set_value(max_popular_items) remember_passwords = builder.get_object("remember_passwords_check") remember_passwords.set_active( El().settings.get_value("remember-passwords")) tracking_check = builder.get_object("tracking_check") tracking_check.set_active( El().settings.get_value("do-not-track")) self.__result_label = builder.get_object("result_label") self.__sync_button = builder.get_object("sync_button") self.__login_entry = builder.get_object("login_entry") self.__password_entry = builder.get_object("password_entry") self.__result_image = builder.get_object("result_image") builder.connect_signals(self) self.__helper.get_sync(self.__on_get_sync) thread = Thread(target=self.__get_sync_status) thread.daemon = True thread.start() def show(self): """ Show dialog """ self.__settings_dialog.show() ####################### # PROTECTED # ####################### def _on_popular_spin_value_changed(self, button): """ Save value @param button as Gtk.SpinButton """ value = GLib.Variant("i", button.get_value()) El().settings.set_value("max-popular-items", value) def _on_configure_engines_clicked(self, button): """ Show Web engines configurator @param button as Gtk.Button """ from eolie.dialog_search_engine import SearchEngineDialog dialog = SearchEngineDialog(self.__settings_dialog) dialog.run() def _on_clear_personnal_data_clicked(self, button): """ Show clear personnal data dialog @param button as Gtk.button """ from eolie.dialog_clear_data import ClearDataDialog dialog = ClearDataDialog(self.__settings_dialog) dialog.run() def _on_manage_cookies_clicked(self, button): """ Show cookies popover @param button as Gtk.button """ from eolie.popover_cookies import CookiesPopover popover = CookiesPopover() popover.populate() popover.set_relative_to(button) popover.popup() def _on_manage_passwords_clicked(self, button): """ Launch searhorse @param button as Gtk.Button """ from eolie.popover_passwords import PasswordsPopover popover = PasswordsPopover() popover.populate() popover.set_relative_to(button) popover.popup() def _on_tracking_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ El().settings.set_value("do-not-track", GLib.Variant("b", button.get_active())) def _on_cookies_changed(self, combo): """ Save cookies setting @param combo as Gtk.ComboBoxText """ El().settings.set_enum("cookie-storage", int(combo.get_active_id())) for window in El().windows: for view in window.container.views: context = view.webview.get_context() cookie_manager = context.get_cookie_manager() cookie_manager.set_accept_policy( El().settings.get_enum("cookie-storage")) def _on_history_changed(self, combo): """ Save history keep setting @param combo as Gtk.ComboBoxText """ El().settings.set_enum("history-storage", int(combo.get_active_id())) def _on_default_zoom_changed(self, button): """ Save size @param button as Gtk.SpinButton """ monitor_model = get_current_monitor_model( self.__settings_dialog.get_transient_for()) try: # Add current value less monitor model zoom_levels = [] for zoom_level in El().settings.get_value("default-zoom-level"): zoom_splited = zoom_level.split('@') if zoom_splited[0] == monitor_model: continue else: zoom_levels.append("%s@%s" % (zoom_splited[0], zoom_splited[1])) # Add new zoom value for monitor model zoom_levels.append("%s@%s" % (monitor_model, button.get_value())) El().settings.set_value("default-zoom-level", GLib.Variant("as", zoom_levels)) for window in El().windows: window.update_zoom_level(True) except Exception as e: print("SettingsDialog::_on_default_zoom_changed():", e) def _on_min_font_size_changed(self, button): """ Save size @param button as Gtk.SpinButton """ value = GLib.Variant("i", button.get_value()) El().settings.set_value("min-font-size", value) El().set_setting("minimum-font-size", button.get_value()) def _on_system_fonts_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ self.__fonts_grid.set_sensitive(not button.get_active()) El().settings.set_value("use-system-fonts", GLib.Variant("b", button.get_active())) def _on_font_sans_serif_set(self, fontbutton): """ Save font setting @param fontchooser as Gtk.FontButton """ value = GLib.Variant("s", fontbutton.get_font_name()) El().settings.set_value("font-sans-serif", value) El().set_setting("sans-serif-font-family", fontbutton.get_font_name()) def _on_font_serif_set(self, fontbutton): """ Save font setting @param fontchooser as Gtk.FontButton """ value = GLib.Variant("s", fontbutton.get_font_name()) El().settings.set_value("font-serif", value) El().set_setting("serif-font-family", fontbutton.get_font_name()) def _on_font_monospace_set(self, fontbutton): """ Save font setting @param fontchooser as Gtk.FontButton """ value = GLib.Variant("s", fontbutton.get_font_name()) El().settings.set_value("font-monospace", value) El().set_setting("monospace-font-family", fontbutton.get_font_name()) def _on_plugins_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ value = GLib.Variant("b", button.get_active()) El().settings.set_value("enable-plugins", value) El().set_setting("enable-plugins", button.get_active()) def _on_remember_passwords_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ El().settings.set_value("remember-passwords", GLib.Variant("b", button.get_active())) def _on_remember_sessions_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ El().settings.set_value("remember-session", GLib.Variant("b", button.get_active())) def _on_start_page_uri_changed(self, entry): """ Save startup page @param entry as Gtk.Entry """ El().settings.set_value("start-page", GLib.Variant("s", entry.get_text())) def _on_start_changed(self, combo): """ Save startup page @param combo as Gtk.ComboBoxText """ self.__start_page_uri.hide() self.__populars_count.hide() if combo.get_active_id() == "address": self.__start_page_uri.show() elif combo.get_active_id() == "popular": self.__populars_count.show() El().settings.set_value("start-page", GLib.Variant("s", combo.get_active_id())) def _on_engine_changed(self, combo): """ Save engine @param combo as Gtk.ComboBoxText """ El().settings.set_value("search-engine", GLib.Variant("s", combo.get_active_id())) El().search.update_default_engine() def _on_open_downloads_toggled(self, button): """ Save state @param button as Gtk.ToggleButton """ El().settings.set_value("open-downloads", GLib.Variant("b", button.get_active())) def _on_selection_changed(self, chooser): """ Save uri @chooser as Gtk.FileChooserButton """ uri = chooser.get_uri() if uri is None: uri = "" El().settings.set_value("download-uri", GLib.Variant("s", uri)) def _on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.__settings_dialog.destroy() def _on_sync_button_clicked(self, button): """ Connect to Mozilla Sync to get tokens @param button as Gtk.Button """ icon_name = self.__result_image.get_icon_name()[0] if icon_name == "network-transmit-receive-symbolic": El().sync_worker.stop(True) El().sync_worker.delete_secret() self.__setup_sync_button(False) else: El().sync_worker.delete_secret() self.__result_label.set_text(_("Connecting…")) button.set_sensitive(False) self.__result_image.set_from_icon_name("content-loading-symbolic", Gtk.IconSize.MENU) thread = Thread(target=self.__connect_mozilla_sync, args=(self.__login_entry.get_text(), self.__password_entry.get_text())) thread.daemon = True thread.start() ####################### # PRIVATE # ####################### def __get_sync_status(self): """ Get sync status @thread safe """ if El().sync_worker is not None: status = El().sync_worker.status GLib.idle_add(self.__setup_sync_button, status) else: GLib.idle_add(self.__missing_fxa) def __setup_sync_button(self, status): """ Setup sync button based on current sync status @param status as bool """ self.__sync_button.set_sensitive(True) self.__sync_button.get_style_context().remove_class( "destructive-action") self.__sync_button.get_style_context().remove_class( "suggested-action") if status: self.__result_label.set_text(_("Synchronization is working")) self.__result_image.set_from_icon_name( "network-transmit-receive-symbolic", Gtk.IconSize.MENU) self.__sync_button.get_style_context().add_class( "destructive-action") self.__sync_button.set_label(_("Cancel synchronization")) else: self.__result_label.set_text( _("Synchronization is not working")) self.__result_image.set_from_icon_name( "computer-fail-symbolic", Gtk.IconSize.MENU) self.__sync_button.get_style_context().add_class( "suggested-action") self.__sync_button.set_label(_("Allow synchronization")) def __missing_fxa(self): """ Show a message about missing fxa module """ try: from eolie.mozilla_sync import SyncWorker SyncWorker # Just make PEP8 happy except Exception as e: self.__result_label.set_text( _("Synchronization is not available" " on your computer:\n %s") % e) self.__sync_button.set_sensitive(False) def __connect_mozilla_sync(self, username, password): """ Connect to mozilla sync @param username as str @param password as str @thread safe """ try: El().sync_worker.login({"login": username}, password) GLib.idle_add(self.__setup_sync_button, True) except Exception as e: print("SettingsDialog::__connect_mozilla_sync():", e) GLib.idle_add(self.__sync_button.set_sensitive, True) if str(e) == "Unverified account": GLib.timeout_add(1000, self.__settings_dialog.destroy) # Try to go to webmail split = username.split("@") GLib.idle_add(El().active_window.container.add_webview, "https://%s" % split[1], Gdk.WindowType.CHILD) GLib.idle_add( El().active_window.toolbar.title.show_message, El().active_window.container.current.webview, _("You've received an email" " to validate synchronization")) self.__helper.store_sync(username, password, "", "", "", El().sync_worker.on_password_stored, False) else: GLib.idle_add(self.__result_label.set_text, str(e)) GLib.idle_add(self.__result_image.set_from_icon_name, "computer-fail-symbolic", Gtk.IconSize.MENU) def __on_get_sync(self, attributes, password, uri, index, count): """ Set username and password @param attributes as {} @param password as str @param uri as None @param index as int @param count as int """ if attributes is None: return try: self.__login_entry.set_text(attributes["login"]) self.__password_entry.set_text(password) except Exception as e: print("SettingsDialog::__on_get_sync()", e)
class SyncWorker(GObject.GObject): """ Manage sync with mozilla server, will start syncing on init """ __gsignals__ = {"sync-finished": (GObject.SignalFlags.RUN_FIRST, None, ())} def __init__(self): """ Init worker """ GObject.GObject.__init__(self) self.__stop = True self.__username = "" self.__password = "" self.__mtimes = {"bookmarks": 0.1, "history": 0.1} self.__mozilla_sync = MozillaSync() self.__session = None self.__helper = PasswordsHelper() def login(self, attributes, password, uri): """ Login to service @param attributes as {} @param password as str @param uri as None @raise exceptions """ if attributes is None: return keyB = "" session = None # Connect to mozilla sync session = self.__mozilla_sync.login(attributes["login"], password) bid_assertion, key = self.__mozilla_sync.get_browserid_assertion( session) keyB = base64.b64encode(session.keys[1]).decode("utf-8") # Store credentials if session is None: uid = "" token = "" else: uid = session.uid token = session.token self.__helper.store_sync(attributes["login"], password, uid, token, keyB, lambda x, y: self.sync(True)) def sync(self, first_sync=False): """ Start syncing, you need to check sync_status property @param first_sync as bool """ if self.syncing or\ not Gio.NetworkMonitor.get_default().get_network_available(): return self.__username = "" self.__password = "" self.__stop = False # We force session reset to user last stored token if first_sync: self.__session = None self.__helper.get_sync(self.__start_sync, first_sync) def push_history(self, history_ids): """ Push history ids @param history_ids as [int] """ if Gio.NetworkMonitor.get_default().get_network_available(): thread = Thread(target=self.__push_history, args=(history_ids, )) thread.daemon = True thread.start() def push_password(self, username, userform, password, passform, uri, uuid): """ Push password @param username as str @param userform as str @param password as str @param passform as str @param uri as str @param uuid as str """ if Gio.NetworkMonitor.get_default().get_network_available(): thread = Thread(target=self.__push_password, args=(username, userform, password, passform, uri, uuid)) thread.daemon = True thread.start() def remove_from_history(self, guid): """ Remove history id from remote history A first call to sync() is needed to populate secrets @param guid as str """ if Gio.NetworkMonitor.get_default().get_network_available(): thread = Thread(target=self.__remove_from_history, args=(guid, )) thread.daemon = True thread.start() def remove_from_passwords(self, uri): """ Remove password from passwords collection @param uri as str """ if Gio.NetworkMonitor.get_default().get_network_available(): self.__helper.get(uri, self.__remove_from_passwords_thread) def delete_secret(self): """ Delete sync secret """ self.__username = "" self.__password = "" self.__session = None self.__stop = True self.__helper.clear_sync() def stop(self): """ Stop update """ self.__stop = True @property def mtimes(self): """ Sync engine modification times @return {} """ return self.__mtimes @property def syncing(self): """ True if sync is running @return bool """ return not self.__stop @property def status(self): """ True if sync is working @return bool """ try: self.__mozilla_sync.client.info_collections() return True except: return False @property def username(self): """ Get username @return str """ return self.__username ####################### # PRIVATE # ####################### def __get_session_bulk_keys(self): """ Get session decrypt keys @return keys as (b"", b"") """ if self.__session is None: self.__session = FxASession( self.__mozilla_sync.fxa_client, self.__username, quick_stretch_password(self.__username, self.__password), self.__uid, self.__token) self.__session.keys = [b"", self.__keyB] self.__session.check_session_status() bid_assertion, key = self.__mozilla_sync.get_browserid_assertion( self.__session) bulk_keys = self.__mozilla_sync.connect(bid_assertion, key) return bulk_keys def __push_history(self, history_ids): """ Push history ids if atime is available, else, ask to remove @param history ids as [int] """ if not self.__username or not self.__password: return try: bulk_keys = self.__get_session_bulk_keys() for history_id in history_ids: sleep(0.01) record = {} atimes = El().history.get_atimes(history_id) guid = El().history.get_guid(history_id) if atimes: record["histUri"] = El().history.get_uri(history_id) record["id"] = guid record["title"] = El().history.get_title(history_id) record["visits"] = [] for atime in atimes: record["visits"].append({ "date": atime * 1000000, "type": 1 }) debug("pushing %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) else: record["id"] = guid record["type"] = "item" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) self.__mtimes = self.__mozilla_sync.client.info_collections() dump(self.__mtimes, open(LOCAL_PATH + "/mozilla_sync.bin", "wb")) except Exception as e: print("SyncWorker::__push_history():", e) def __push_password(self, username, userform, password, passform, uri, uuid): """ Push password @param username as str @param userform as str @param password as str @param passform as str @param uri as str @param uuid as str """ if not self.__username or not self.__password: return try: bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = "{%s}" % uuid record["hostname"] = uri record["formSubmitURL"] = uri record["httpRealm"] = None record["username"] = username record["password"] = password record["usernameField"] = userform record["passwordField"] = passform mtime = int(time() * 1000) record["timeCreated"] = mtime record["timePasswordChanged"] = mtime debug("pushing %s" % record) self.__mozilla_sync.add(record, "passwords", bulk_keys) except Exception as e: print("SyncWorker::__push_password():", e) def __remove_from_history(self, guid): """ Remove from history @param guid as str """ if not self.__username or not self.__password: return try: bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = guid record["type"] = "item" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "history", bulk_keys) except Exception as e: print("SyncWorker::__remove_from_history():", e) def __remove_from_passwords(self, attributes, password, uri): """ Remove password from passwords collection @param attributes as {} @param password as str @param uri as str """ if not self.__username or not self.__password: return try: bulk_keys = self.__get_session_bulk_keys() record = {} record["id"] = attributes["uuid"] record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "passwords", bulk_keys) self.__helper.clear(uri) except Exception as e: print("SyncWorker::__remove_from_passwords():", e) def __remove_from_passwords_thread(self, attributes, password, uri): """ Remove password from passwords collection @param attributes as {} @param password as str @param uri as str """ thread = Thread(target=self.__remove_from_passwords, args=(attributes, password, uri)) thread.daemon = True thread.start() def __sync(self, first_sync): """ Sync Eolie objects (bookmarks, history, ...) with Mozilla Sync @param first_sync as bool """ debug("Start syncing") if not self.__username or not self.__password: self.__stop = True return try: self.__mtimes = load(open(LOCAL_PATH + "/mozilla_sync.bin", "rb")) except: self.__mtimes = { "bookmarks": 0.1, "history": 0.1, "passwords": 0.1 } try: bulk_keys = self.__get_session_bulk_keys() new_mtimes = self.__mozilla_sync.client.info_collections() if self.__stop: return ###################### # Passwords Management # ###################### debug("local passwords: %s, remote passwords: %s" % (self.__mtimes["passwords"], new_mtimes["passwords"])) # Only pull if something new available if self.__mtimes["passwords"] != new_mtimes["passwords"]: self.__pull_passwords(bulk_keys) if self.__stop: return ###################### # History Management # ###################### debug("local history: %s, remote history: %s" % (self.__mtimes["history"], new_mtimes["history"])) # Only pull if something new available if self.__mtimes["history"] != new_mtimes["history"]: self.__pull_history(bulk_keys) if self.__stop: return ######################## # Bookmarks Management # ######################## debug("local bookmarks: %s, remote bookmarks: %s" % (self.__mtimes["bookmarks"], new_mtimes["bookmarks"])) # Push new bookmarks self.__push_bookmarks(bulk_keys) if self.__stop: return # Only pull if something new available if self.__mtimes["bookmarks"] != new_mtimes["bookmarks"]: self.__pull_bookmarks(bulk_keys, first_sync) # Update last sync mtime self.__mtimes = self.__mozilla_sync.client.info_collections() dump(self.__mtimes, open(LOCAL_PATH + "/mozilla_sync.bin", "wb")) debug("Stop syncing") GLib.idle_add(self.emit, "sync-finished") except Exception as e: print("SyncWorker::__sync():", e) if str(e) == "The authentication token could not be found": self.__helper.get_sync(self.login) self.__stop = True def __push_bookmarks(self, bulk_keys): """ Push to bookmarks @param bulk keys as KeyBundle @param start time as float @raise StopIteration """ debug("push bookmarks") parents = [] for bookmark_id in El().bookmarks.get_ids_for_mtime( self.__mtimes["bookmarks"]): if self.__stop: raise StopIteration("Cancelled") sleep(0.01) parent_guid = El().bookmarks.get_parent_guid(bookmark_id) # No parent, move it to unfiled if parent_guid is None: parent_guid = "unfiled" parent_id = El().bookmarks.get_id_by_guid(parent_guid) if parent_id not in parents: parents.append(parent_id) record = {} record["bmkUri"] = El().bookmarks.get_uri(bookmark_id) record["id"] = El().bookmarks.get_guid(bookmark_id) record["title"] = El().bookmarks.get_title(bookmark_id) record["tags"] = El().bookmarks.get_tags(bookmark_id) record["parentid"] = parent_guid record["type"] = "bookmark" debug("pushing %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) # Del old bookmarks for bookmark_id in El().bookmarks.get_deleted_ids(): if self.__stop: raise StopIteration("Cancelled") sleep(0.01) parent_guid = El().bookmarks.get_parent_guid(bookmark_id) parent_id = El().bookmarks.get_id_by_guid(parent_guid) if parent_id not in parents: parents.append(parent_id) record = {} record["id"] = El().bookmarks.get_guid(bookmark_id) record["type"] = "item" record["deleted"] = True debug("deleting %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) El().bookmarks.remove(bookmark_id) # Push parents in this order, parents near root are handled later # Otherwise, order will be broken by new children updates while parents: parent_id = parents.pop(0) parent_guid = El().bookmarks.get_guid(parent_id) parent_name = El().bookmarks.get_title(parent_id) children = El().bookmarks.get_children(parent_guid) # So search if children in parents found = False for child_guid in children: child_id = El().bookmarks.get_id_by_guid(child_guid) if child_id in parents: found = True break # Handle children first if found: parents.append(parent_id) debug("later: %s" % parent_name) continue record = {} record["id"] = parent_guid record["type"] = "folder" # A parent with parent as unfiled needs to be moved to places # Firefox internal grand_parent_guid = El().bookmarks.get_parent_guid(parent_id) if grand_parent_guid == "unfiled": grand_parent_guid = "places" record["parentid"] = grand_parent_guid record["parentName"] = El().bookmarks.get_parent_name(parent_id) record["title"] = parent_name record["children"] = children debug("pushing parent %s" % record) self.__mozilla_sync.add(record, "bookmarks", bulk_keys) El().bookmarks.clean_tags() def __pull_bookmarks(self, bulk_keys, first_sync): """ Pull from bookmarks @param bulk_keys as KeyBundle @param first_sync as bool @raise StopIteration """ debug("pull bookmarks") SqlCursor.add(El().bookmarks) records = self.__mozilla_sync.get_records("bookmarks", bulk_keys) # We get all guids here and remove them while sync # At the end, we have deleted records # On fist sync, keep all if first_sync: to_delete = [] else: to_delete = El().bookmarks.get_guids() for record in records: if self.__stop: raise StopIteration("Cancelled") sleep(0.01) bookmark = record["payload"] if "type" not in bookmark.keys() or\ bookmark["type"] not in ["folder", "bookmark"]: continue bookmark_id = El().bookmarks.get_id_by_guid(bookmark["id"]) # This bookmark exists, remove from to delete if bookmark["id"] in to_delete: to_delete.remove(bookmark["id"]) # Nothing to apply, continue if El().bookmarks.get_mtime(bookmark_id) >= record["modified"]: continue debug("pulling %s" % record) if bookmark_id is None: if "bmkUri" in bookmark.keys(): # Use parent name if no bookmarks tags if "tags" not in bookmark.keys() or\ not bookmark["tags"]: if "parentName" in bookmark.keys() and\ bookmark["parentName"]: bookmark["tags"] = [bookmark["parentName"]] else: bookmark["tags"] = [] bookmark_id = El().bookmarks.add(bookmark["title"], bookmark["bmkUri"], bookmark["id"], bookmark["tags"], False) else: bookmark["tags"] = [] bookmark_id = El().bookmarks.add(bookmark["title"], bookmark["id"], bookmark["id"], bookmark["tags"], False) else: El().bookmarks.set_title(bookmark_id, bookmark["title"], False) if "bmkUri" in bookmark.keys(): El().bookmarks.set_uri(bookmark_id, bookmark["bmkUri"], False) elif "children" in bookmark.keys(): position = 0 for child in bookmark["children"]: bid = El().bookmarks.get_id_by_guid(child) El().bookmarks.set_position(bid, position, False) position += 1 # Remove previous tags current_tags = El().bookmarks.get_tags(bookmark_id) for tag in El().bookmarks.get_tags(bookmark_id): if "tags" in bookmark.keys() and\ tag not in bookmark["tags"]: tag_id = El().bookmarks.get_tag_id(tag) current_tags.remove(tag) El().bookmarks.del_tag_from(tag_id, bookmark_id, False) if "tags" in bookmark.keys(): for tag in bookmark["tags"]: # Tag already associated if tag in current_tags: continue tag_id = El().bookmarks.get_tag_id(tag) if tag_id is None: tag_id = El().bookmarks.add_tag(tag, False) El().bookmarks.add_tag_to(tag_id, bookmark_id, False) El().bookmarks.set_mtime(bookmark_id, record["modified"], False) if "parentName" in bookmark.keys(): El().bookmarks.set_parent(bookmark_id, bookmark["parentid"], bookmark["parentName"], False) for guid in to_delete: if self.__stop: raise StopIteration("Cancelled") debug("deleting: %s" % guid) bookmark_id = El().bookmarks.get_id_by_guid(guid) if bookmark_id is not None: El().bookmarks.remove(bookmark_id, False) El().bookmarks.clean_tags() # Will commit SqlCursor.remove(El().bookmarks) def __pull_passwords(self, bulk_keys): """ Pull from passwords @param bulk_keys as KeyBundle @raise StopIteration """ debug("pull passwords") records = self.__mozilla_sync.get_records("passwords", bulk_keys) for record in records: if self.__stop: raise StopIteration("Cancelled") sleep(0.01) debug("pulling %s" % record) password = record["payload"] if "formSubmitURL" in password.keys(): self.__helper.clear(password["formSubmitURL"]) self.__helper.store(password["username"], password["password"], password["formSubmitURL"], password["id"], None) elif "deleted" in password.keys(): # We assume True self.__helper.clear(password["id"]) def __pull_history(self, bulk_keys): """ Pull from history @param bulk_keys as KeyBundle @raise StopIteration """ debug("pull history") records = self.__mozilla_sync.get_records("history", bulk_keys) for record in records: if self.__stop: raise StopIteration("Cancelled") sleep(0.01) El().history.thread_lock.acquire() history = record["payload"] keys = history.keys() # Ignore pages without a title if "title" not in keys or not history["title"]: El().history.thread_lock.release() continue # Ignore pages without an uri (deleted) if "histUri" not in keys: El().history.thread_lock.release() continue history_id = El().history.get_id_by_guid(history["id"]) # Nothing to apply, continue if El().history.get_mtime(history_id) >= record["modified"]: El().history.thread_lock.release() continue # Try to get visit date atimes = [] try: for visit in history["visits"]: atimes.append(round(int(visit["date"]) / 1000000, 2)) except: El().history.thread_lock.release() continue debug("pulling %s" % record) title = history["title"].rstrip().lstrip() history_id = El().history.add(title, history["histUri"], record["modified"], history["id"], atimes, True) El().history.thread_lock.release() def __start_sync(self, attributes, password, uri, first_sync): """ Set params and start sync @param attributes as {} @param password as str @param uri as None @param first_sync as bool """ if attributes is None: return try: self.__username = attributes["login"] self.__password = password self.__token = attributes["token"] self.__uid = attributes["uid"] self.__keyB = base64.b64decode(attributes["keyB"]) if Gio.NetworkMonitor.get_default().get_network_available(): thread = Thread(target=self.__sync, args=(first_sync, )) thread.daemon = True thread.start() except Exception as e: print("SyncWorker::__start_sync()", e)
class CredentialsPopover(Gtk.Popover): """ Tell user to save form credentials """ def __init__(self, username, userform, password, passform, uri): """ Init popover @param username as str @param password as str @param netloc as str """ Gtk.Popover.__init__(self) self.__helper = PasswordsHelper() self.__username = username self.__userform = userform self.__password = password self.__passform = passform self.__uri = uri self.__uuid = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/PopoverCredentials.ui') builder.connect_signals(self) self.__label = builder.get_object('label') parsed = urlparse(uri) builder.get_object('uri').set_text(parsed.netloc) builder.get_object('username').set_text(username) builder.get_object('password').set_text(password) self.add(builder.get_object('widget')) ####################### # PROTECTED # ####################### def _on_save_clicked(self, button): """ Save username and password @param button as Gtk.Button """ try: parsed = urlparse(self.__uri) uri = "%s://%s" % (parsed.scheme, parsed.netloc) if self.__uuid is None: self.__uuid = str(uuid3(NAMESPACE_DNS, parsed.netloc)) else: self.__helper.clear(self.__uuid) self.__helper.store(self.__username, self.__password, uri, self.__uuid, None) if El().sync_worker is not None: El().sync_worker.push_password(self.__username, self.__userform, self.__password, self.__passform, uri, self.__uuid) self.destroy() except Exception as e: print("CredentialsPopover::_on_save_clicked()", e) def show(self): """ Overwrite show """ self.__helper.get(self.__uri, self.__on_get_password) ####################### # PRIVATE # ####################### def __on_get_password(self, attributes, password, uri): """ Set username/password input @param attributes as {} @param password as str @param uri as str """ try: if attributes is None: Gtk.Popover.show(self) elif attributes["login"] == self.__username and\ self.__password == password: self.emit("closed") else: Gtk.Popover.show(self) self.__uuid = attributes["uuid"] self.__label.set_text( _("Do you want to modify this password?")) except Exception as e: print("CredentialsPopover::__on_get_password()", e)