def error(self, page, title, message, display_bugreport_link): """ Called from pages on error. Removes everything from page and creates error message. """ for c in [] + page.get_children() : page.remove(c) # Title l_title = WrappedLabel("<b>%s</b>" % (title,)) l_title.props.margin_bottom = 15 page.attach(l_title, 0, 0, 2, 1) # Message l_message = WrappedLabel(message) l_message.props.margin_bottom = 15 page.attach(l_message, 0, 1, 2, 1) # Bugreport link if display_bugreport_link: github_link = '<a href="https://github.com/syncthing/syncthing-gtk/issues">GitHub</a>' l_bugreport = WrappedLabel( _("Please, check error log and fill bug report on %s.") % (github_link,) ) page.attach(l_bugreport, 0, 2, 2, 1) # 'Display error log' button button = Gtk.Button(_("Display error log")) button.props.margin_top = 25 page.attach(button, 1, 3, 2, 1) button.connect("clicked", lambda *a : self.show_output()) page.show_all() return page
def display(self): if len(self.updated) == 1 and len(self.deleted) == 0: # One updated file f_path = list(self.updated)[0] filename = os.path.split(f_path)[-1] self.info(_("The file '%s' was updated on remote device.") % (filename,)) elif len(self.updated) == 0 and len(self.deleted) == 1: # One deleted file f_path = list(self.deleted)[0] filename = os.path.split(f_path)[-1] self.info(_("The file '%s' was deleted on remote device.") % (filename,)) elif len(self.deleted) == 0 and len(self.updated) > 0: # Multiple updated, nothing deleted self.info(_("%s files were updated on remote device.") % (len(self.updated),)) elif len(self.updated) == 0 and len(self.deleted) > 0: # Multiple deleted, no updated self.info(_("%s files were deleted on remote device.") % (len(self.deleted),)) elif len(self.deleted) > 0 and len(self.updated) > 0: # Multiple deleted, multiple updated self.info( _("%(updated)s files were updated and %(deleted)s deleted on remote device.") % { 'updated' : len(self.updated), 'deleted' : len(self.deleted) } ) self.updated = set([]) self.deleted = set([])
def init_page(self): """ Displayed while Syncthing binary is being searched for """ self.label = WrappedLabel( "<b>%s</b>\n\n%s" % (_("Syncthing is generating RSA key and certificate."), _("This may take a while..."))) self.attach(self.label, 0, 0, 1, 1)
def cb_process_exit(self, process, *a): """ Called after daemon binary outputs version and exits """ from syncthing_gtk.app import MIN_ST_VERSION bin_path = process.get_commandline()[0] if compare_version(self.version_string, MIN_ST_VERSION): # Daemon binary exists, is executable and meets # version requirements. That's good, btw. self.parent.config["syncthing_binary"] = bin_path if not can_upgrade_binary(bin_path): # Don't try enable auto-update if binary is in # non-writable location (auto-update is enabled # by default on Windows only) self.parent.config["st_autoupdate"] = False self.parent.set_page_complete(self, True) self.label.set_markup( "<b>" + _("Syncthing daemon binary found.") + "</b>" + "\n\n" + _("Binary path:") + " " + bin_path + "\n" + _("Version:") + " " + self.version_string ) else: # Found daemon binary too old to be ussable. # Just ignore it and try to find better one. log.info("Binary in %s is too old", bin_path) self.ignored_version = self.version_string GLib.idle_add(self.search)
def init_page(self): """ Displayed while settings are being saved """ self.label = WrappedLabel("<b>" + _("Saving settings...") + "</b>" + "\n\n") self.status = Gtk.Label(_("Checking for available port...")) self.attach(self.label, 0, 0, 1, 1) self.attach(self.status, 0, 1, 1, 1)
def rejected(self, nid): device = self.app.devices[nid].get_title() label_fb = self.label or self.id actions = [ (self.ACT_DEFAULT, _('Accept folder "%s"') % label_fb, self.cb_accept, nid), (self.ACT_ACCEPT, _('Accept folder "%s"') % label_fb, self.cb_accept, nid), (self.ACT_IGNORE, _('Ignore folder "%s"') % label_fb, self.cb_ignore, nid), ] markup_dev = self.supports("body-markup", "<b>%s</b>" % device, device) markup_fol = self.supports("body-markup", "<b>%s</b>" % label_fb, label_fb) summary = _("Folder rejected") body = _( 'Unexpected folder "%(folder)s" sent from device "%(device)s".' ) % { 'device': markup_dev, 'folder': markup_fol } self.push(summary, body, actions=actions, urg=Notify.Urgency.CRITICAL)
def prepare(self): # Determine which Syncthing to use suffix, tag = StDownloader.determine_platform() # Report error on unsupported platforms if suffix is None or tag is None: pd = "%s %s %s" % ( platform.uname()[0], platform.uname()[2], # OS, version platform.uname()[4]) # architecture self.parent.error(self, _("Cannot download Syncthing daemon."), _("This platform (%s) is not supported") % (pd,), False) return # Determine target file & directory self.target = os.path.join( os.path.expanduser(StDownloader.get_target_folder()), "syncthing%s" % (suffix,) ) # Create downloader and connect events self.sd = StDownloader(self.target, tag) self.sd.connect("error", self.on_download_error) self.sd.connect("version", self.on_version) self.sd.connect("download-progress", self.on_progress) self.sd.connect("download-finished", self.on_extract_start) self.sd.connect("extraction-progress", self.on_progress) self.sd.connect("extraction-finished", self.on_extract_finished) # Start downloading self.sd.get_version()
def error(self, page, title, message, display_bugreport_link): """ Called from pages on error. Removes everything from page and creates error message. """ for c in [] + page.get_children(): page.remove(c) # Title l_title = WrappedLabel("<b>%s</b>" % (title, )) l_title.props.margin_bottom = 15 page.attach(l_title, 0, 0, 2, 1) # Message l_message = WrappedLabel(message) l_message.props.margin_bottom = 15 page.attach(l_message, 0, 1, 2, 1) # Bugreport link if display_bugreport_link: github_link = '<a href="https://github.com/syncthing/syncthing-gtk/issues">GitHub</a>' l_bugreport = WrappedLabel( _("Please, check error log and fill bug report on %s.") % (github_link, )) page.attach(l_bugreport, 0, 2, 2, 1) # 'Display error log' button button = Gtk.Button(_("Display error log")) button.props.margin_top = 25 page.attach(button, 1, 3, 2, 1) button.connect("clicked", lambda *a: self.show_output()) page.show_all() return page
def _open_achive(self, archive_name): try: # Determine archive format archive = None if tarfile.is_tarfile(archive_name): # Open TAR archive = tarfile.open(archive_name, "r", bufsize=CHUNK_SIZE * 2) elif zipfile.is_zipfile(archive_name): # Open ZIP archive = ZipThatPretendsToBeTar(archive_name, "r") else: # Unrecognized format self.emit("error", None, _("Downloaded file is corrupted.")) # Find binary inside for pathname in archive.getnames(): filename = pathname.replace("\\", "/").split("/")[-1] if filename.startswith("syncthing"): # Last sanity check, then just open files # and start extracting tinfo = archive.getmember(pathname) if tinfo.isfile(): compressed = archive.extractfile(pathname) try: os.makedirs(os.path.split(self.target)[0]) except Exception: pass output = file(self.target, "wb") GLib.idle_add(self._extract, (archive, compressed, output, 0, tinfo.size)) return except Exception, e: log.exception(e) self.emit("error", e, _("Failed to determine latest Syncthing version.")) return
def prepare(self): # Determine which Syncthing to use suffix, tag = StDownloader.determine_platform() # Report error on unsupported platforms if suffix is None or tag is None: pd = "%s %s %s" % ( platform.uname()[0], platform.uname()[2], # OS, version platform.uname()[4]) # architecture self.parent.error( self, _("Cannot download Syncthing daemon."), _("This platform (%s) is not supported") % (pd, ), False) return # Determine target file & directory self.target = os.path.join( os.path.expanduser(StDownloader.get_target_folder()), "syncthing%s" % (suffix, )) # Create downloader and connect events self.sd = StDownloader(self.target, tag) self.sd.connect("error", self.on_download_error) self.sd.connect("version", self.on_version) self.sd.connect("download-progress", self.on_progress) self.sd.connect("download-finished", self.on_extract_start) self.sd.connect("extraction-progress", self.on_progress) self.sd.connect("extraction-finished", self.on_extract_finished) # Start downloading self.sd.get_version()
def cb_format_value_s_or_disabed(self, spinner): """ Formats spinner value """ val = int(spinner.get_adjustment().get_value()) if val < 1: spinner.get_buffer().set_text(_("disabled"), -1) else: spinner.get_buffer().set_text(_("%ss") % (val, ), -1) return True
def on_extract_finished(self, *a): """ Called after extraction is finished """ # Everything done. Praise supernatural entities... self.label.set_markup("<b>" + _("Download finished.") + "</b>") self.parent.config["syncthing_binary"] = self.target self.version.set_markup(_("Binary path:") + " " + self.target) self.pb.set_visible(False) self.parent.set_page_complete(self, True)
def cb_format_value_kibps_or_no_limit(self, spinner): """ Formats spinner value """ val = int(spinner.get_adjustment().get_value()) if val < 1: spinner.get_buffer().set_text(_("no limit"), -1) else: spinner.get_buffer().set_text(_("%s KiB/s") % (val,), -1) return True
def cb_format_value_s_or_disabed(self, spinner): """ Formats spinner value """ val = int(spinner.get_adjustment().get_value()) if val < 1: spinner.get_buffer().set_text(_("disabled"), -1) else: spinner.get_buffer().set_text(_("%ss") % (val,), -1); return True
def cb_format_value_kibps_or_no_limit(self, spinner): """ Formats spinner value """ val = int(spinner.get_adjustment().get_value()) if val < 1: spinner.get_buffer().set_text(_("no limit"), -1) else: spinner.get_buffer().set_text(_("%s KiB/s") % (val, ), -1) return True
def init_page(self): """ Displayed while Syncthing binary is being searched for """ self.label = WrappedLabel( "<b>%s</b>\n\n%s" % ( _("Syncthing is generating RSA key and certificate."), _("This may take a while...") ) ) self.attach(self.label, 0, 0, 1, 1)
def init_page(self): """ Well, it's last page. """ label = WrappedLabel( "<b>" + _("Done.") + "</b>" + "\n\n" + _("Syncthing has been successfully configured.") + "\n" + _("You can configure more details later, in " "<b>UI Settings</b> and <b>Daemon Settings</b> menus " "in main window of application.")) self.attach(label, 0, 0, 1, 1)
def init_page(self): """ Displayed while Syncthing binary is being searched for """ self.label = WrappedLabel("<b>" + _("Searching for Syncthing daemon.") + "</b>" + "\n\n" + _("Please wait...")) self.paths = [] self.version_string = "v0.0" self.ignored_version = None self.attach(self.label, 0, 0, 1, 1)
def init_page(self): """ Displayed while wizard downloads and extracts daemon """ self.label = WrappedLabel("<b>" + _("Downloading Syncthing daemon.") + "</b>") self.version = WrappedLabel(_("Please wait...")) self.pb = Gtk.ProgressBar() self.label.props.margin_bottom = 15 self.target = None self.attach(self.label, 0, 0, 1, 1) self.attach(self.version, 0, 1, 1, 1) self.attach(self.pb, 0, 2, 1, 1)
def cb_format_value_days(self, spinner): """ Formats spinner value """ v = int(spinner.get_adjustment().get_value()) if v == 0: spinner.get_buffer().set_text(_("never delete"), -1) elif v == 1: spinner.get_buffer().set_text(_("%s day") % (v, ), -1) else: spinner.get_buffer().set_text(_("%s days") % (v, ), -1) return True
def cb_format_value_days(self, spinner): """ Formats spinner value """ v = int(spinner.get_adjustment().get_value()) if v == 0: spinner.get_buffer().set_text(_("never delete"), -1) elif v == 1: spinner.get_buffer().set_text(_("%s day") % (v,), -1); else: spinner.get_buffer().set_text(_("%s days") % (v,), -1); return True
def rejected(self): label_fb = self.label or self.id actions = [ (self.ACT_DEFAULT, _('Accept device "%s"') % label_fb, self.cb_accept, None), (self.ACT_ACCEPT, _('Accept device "%s"') % label_fb, self.cb_accept, None), (self.ACT_IGNORE, _('Ignore device "%s"') % label_fb, self.cb_ignore, None), ] summary = _("Unknown Device") body = _('Device "%s" is trying to connect to syncthing daemon.' % self.label) self.push(summary, body, actions=actions, urg=Notify.Urgency.CRITICAL)
def init_page(self): """ Permits user to set WebUI settings """ # Wall of text label = WrappedLabel( "<b>" + _("WebUI setup") + "</b>" + "\n\n" + _("Syncthing can be managed remotely using WebUI and " "even if you are going to use Syncthing-GTK, WebUI needs " "to be enabled, as Syncthing-GTK uses it to communicate " "with the Syncthing daemon.") + "\n\n" + _("If you prefer to be able to manage Syncthing remotely, " "over the internet or on your local network, select <b>listen " "on all interfaces</b> and set username and password to " "protect Syncthing from unauthorized access.") + "\n" + _("Otherwise, select <b>listen on localhost</b>, so only " "users and programs on this computer will be able to " "interact with Syncthing.") + "\n" ) # Radiobuttons lbl_radios = WrappedLabel("<b>" + _("WebUI Listen Addresses") + "</b>") self.rb_localhost = Gtk.RadioButton(label=_("Listen on _localhost")) self.rb_all_intfs = Gtk.RadioButton.new_from_widget(self.rb_localhost) self.rb_all_intfs.set_label(_("Listen on _all interfaces")) for x in (self.rb_localhost, self.rb_all_intfs): x.set_use_underline(True) x.set_property('margin-left', 15) # Username & password input boxes self.tx_username = Gtk.Entry() self.tx_password = Gtk.Entry() self.lbl_username = WrappedLabel(_("_Username")) self.lbl_password = WrappedLabel(_("_Password")) self.lbl_username.set_mnemonic_widget(self.tx_username) self.lbl_password.set_mnemonic_widget(self.tx_password) self.tx_password.set_visibility(False) self.tx_password.props.caps_lock_warning = True for x in (self.lbl_username, self.lbl_password): x.set_use_underline(True) x.set_property('margin-left', 45) x.set_property('margin-bottom', 5) for x in (self.tx_username, self.tx_password): x.set_property('margin-bottom', 5) # Connect signals for x in (self.rb_localhost, self.rb_all_intfs): x.connect("toggled", self.cb_stuff_changed) for x in (self.tx_username, self.tx_password): x.connect("changed", self.cb_stuff_changed) x.connect("delete-text", self.cb_stuff_changed) x.connect("insert-text", self.cb_stuff_changed) # Attach everything self.attach(label, 0, 0, 3, 1) self.attach(lbl_radios, 0, 1, 3, 1) self.attach(self.rb_localhost, 0, 2, 2, 1) self.attach(self.rb_all_intfs, 0, 3, 2, 1) self.attach(self.lbl_username, 0, 4, 1, 1) self.attach(self.lbl_password, 0, 5, 1, 1) self.attach(self.tx_username, 1, 4, 2, 1) self.attach(self.tx_password, 1, 5, 2, 1)
def init_page(self): """ Displayed while Syncthing binary is being searched for """ self.label = WrappedLabel( "<b>" + _("Searching for Syncthing daemon.") + "</b>" + "\n\n" + _("Please wait...") ) self.paths = [] self.version_string = "v0.0" self.ignored_version = None self.attach(self.label, 0, 0, 1, 1)
def cb_daemon_exit(self, dproc, exit_code): """ Called when Syncthing finishes """ if exit_code == 0: # Finished without problem, advance to next page self.parent.set_page_complete(self, True) self.parent.next_page() else: self.parent.error(self, _("Failed to generate keys"), _("Syncthing daemon failed to generate RSA key or certificate."), True)
def cb_daemon_exit(self, dproc, exit_code): """ Called when Syncthing finishes """ if exit_code == 0: # Finished without problem, advance to next page self.parent.set_page_complete(self, True) self.parent.next_page() else: self.parent.error( self, _("Failed to generate keys"), _("Syncthing daemon failed to generate RSA key or certificate." ), True)
def init_page(self): """ Well, it's last page. """ label = WrappedLabel( "<b>" + _("Done.") + "</b>" + "\n\n" + _("Syncthing has been successfully configured.") + "\n" + _("You can configure more details later, in " "<b>UI Settings</b> and <b>Daemon Settings</b> menus " "in main window of application.") ) self.attach(label, 0, 0, 1, 1)
def sync_conflict(self, path): path_full = os.path.join(self.app.folders[self.id]["norm_path"], path) summary = _('Conflicting file in "%s"') % (self.label or self.id) text = _('Conflict in path "%s" detected.') % path_full n = Notify.Notification.new(summary, text, ICON_ERR) n.set_urgency(Notify.Urgency.CRITICAL) n.add_action(self.ACT_DEFAULT, _("Open Conflicting file in filemanager"), self.cb_open_conflict, path_full) n.connect("closed", self.cb_sync_conflict_closed), self.conflict.add(n) self.show(n)
def display(self): if len(self.updated) == 1 and len(self.deleted) == 0: # One updated file f_path = list(self.updated)[0] filename = os.path.split(f_path)[-1] self.info( _("%(hostname)s: Downloaded '%(filename)s' to reflect remote changes." ) % { 'hostname': self.app.get_local_name(), 'filename': "<a href='file://%s'>%s</a>" % (f_path.encode('unicode-escape'), filename) }) elif len(self.updated) == 0 and len(self.deleted) == 1: # One deleted file f_path = list(self.deleted)[0] filename = os.path.split(f_path)[-1] self.info( _("%(hostname)s: Deleted '%(filename)s' to reflect remote changes." ) % { 'hostname': self.app.get_local_name(), 'filename': filename }) elif len(self.deleted) == 0 and len(self.updated) > 0: # Multiple updated, nothing deleted self.info( _("%(hostname)s: Downloaded %(updated)s files to reflect remote changes." ) % { 'hostname': self.app.get_local_name(), 'updated': len(self.updated) }) elif len(self.updated) == 0 and len(self.deleted) > 0: # Multiple deleted, no updated self.info( _("%(hostname)s: Deleted %(deleted)s files to reflect remote changes." ) % { 'hostname': self.app.get_local_name(), 'deleted': len(self.deleted) }) elif len(self.deleted) > 0 and len(self.updated) > 0: # Multiple deleted, multiple updated self.info( _("%(hostname)s: downloaded %(updated)s files and deleted %(deleted)s files to reflect remote changes." ) % { 'hostname': self.app.get_local_name(), 'updated': len(self.updated), 'deleted': len(self.deleted) }) self.updated = set([]) self.deleted = set([])
def init_page(self): """ First, intro page. Just static text that explains what's going on """ config_folder = "~/.config/syncthing" config_folder_link = '<a href="file://%s">%s</a>' % ( os.path.expanduser(config_folder), config_folder) self.attach(WrappedLabel( "<b>" + _("Welcome to Syncthing-GTK first run wizard!") + "</b>" + "\n\n" + _("It looks like you never have used Syncthing.") + " " + _("Initial configuration should be created.") + " " + _("Please click <b>Next</b> to create a Syncthing configuration file or <b>Quit</b> to exit") + "\n\n" + (_("If you already had Syncthing daemon configured, please, " "exit this wizard and check your %s folder") % config_folder_link ) ), 0, 0, 1, 1)
def cb_data_failed(self, exception, *a): """ Failed to load configuration. This shouldn't happen unless daemon dies exactly when user clicks to edit menu. Handled by simple error message. """ # All other errors are fatal for now. Error dialog is displayed and program exits. d = Gtk.MessageDialog( self["editor"], Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "%s %s\n\n%s %s" % (_("Failed to load configuration from daemon."), _("Try again."), _("Error message:"), str(exception))) d.run() self.close()
def __init__(self, app, title, icon): # Variables self.app = app self.child = None self.header = None self.str_title = None self.str_status = None self.header_inverted = False self.values = {} self.icons = {} self.value_widgets = {} self.hilight = False self.hilight_factor = 0.0 self.timer_enabled = False self.icon = icon self.color = (1, 0, 1, 1) # rgba self.background = (1, 1, 1, 1) # rgba self.dark_color = None # Overrides background if set self.text_color = (0, 0, 0, 1) # rgba (text color) self.real_color = self.color # set color + hilight self.border_width = 2 self.children = [self.header, self.child] # Initialization Gtk.Container.__init__(self) self.init_header() self.init_grid() # Settings self.set_title(title) self.set_status(_("Disconnected"))
def add_page(self, page): """ Adds page derived from custom Page class """ index = self.append_page(page) page.parent = self self.set_page_type(page, page.TYPE) self.set_page_title(page, _(page.TITLE) + " ") return index
def init_page(self): """ First, intro page. Just static text that explains what's going on """ config_folder = "~/.config/syncthing" config_folder_link = '<a href="file://%s">%s</a>' % ( os.path.expanduser(config_folder), config_folder) self.attach( WrappedLabel( "<b>" + _("Welcome to Syncthing-GTK first run wizard!") + "</b>" + "\n\n" + _("It looks like you never have used Syncthing.") + " " + _("Initial configuration should be created.") + " " + _("Please click <b>Next</b> to create a Syncthing configuration file or <b>Quit</b> to exit" ) + "\n\n" + (_("If you already had Syncthing daemon configured, please, " "exit this wizard and check your %s folder") % config_folder_link)), 0, 0, 1, 1)
def on_btBrowse_clicked(self, *a): """ Display folder browser dialog to browse for folder... folder. Oh god, this new terminology sucks... """ if not self.is_new: return # Prepare dialog d = Gtk.FileChooserDialog( _("Select Folder for new Folder"), # f**k me... self["editor"], Gtk.FileChooserAction.SELECT_FOLDER, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) # Set default path to home directory d.set_current_folder(os.path.expanduser("~")) # Get response if d.run() == Gtk.ResponseType.OK: self["vpath"].set_text(d.get_filename()) if len(self["vid"].get_text().strip()) == 0: # ID is empty, fill it with last path element try: lpl = os.path.split(d.get_filename())[-1] id = RE_GEN_ID.search(lpl).group(0).lower() self["vid"].set_text(id) except AttributeError: # Can't regexp anything pass d.destroy()
def _cb_read_latest(self, f, result, buffer, *a): # Extract release version from response from syncthing_gtk.app import MIN_ST_VERSION latest_ver = None try: success, data, etag = f.load_contents_finish(result) if not success: raise Exception("Gio download failed") # Go over all available versions until compatibile one # is found data = json.loads(data) for release in data: version = release["tag_name"] if latest_ver is None: latest_ver = version if compare_version(self.latest_compat, version) and (self.forced or compare_version(version, MIN_ST_VERSION)): # Compatibile log.verbose("STDownloader: Found compatibile Syncthing version: %s", version) self.version = version for asset in release["assets"]: if self.platform in asset["name"]: self.dll_url = asset["browser_download_url"] self.dll_size = int(asset["size"]) log.debug("STDownloader: URL: %s", self.dll_url) break break else: log.verbose("STDownloader: Ignoring too new Syncthing version: %s", version) del f if self.dll_url is None: raise Exception("No release to download") except Exception, e: log.exception(e) self.emit("error", e, _("Failed to determine latest Syncthing version.")) return
def cb_syncthing_folder_finished(self, daemon, folder_id): if folder_id in self.syncing: self.syncing.remove(folder_id) folder_label = self.app.folders[folder_id]["label"] markup = _("Synchronization of folder '%s' is completed.") % ( (folder_label or folder_id), ) self.info(markup)
def syncthing_cb_post_error(self, exception, *a): # TODO: Unified error message if isinstance(exception, ConnectionRestarted): # Should be ok, this restart is triggered # by App handler for 'config-saved' event. return self.syncthing_cb_post_config() message = "%s\n%s" % (_("Failed to save configuration."), str(exception)) if hasattr(exception, "full_response"): try: fr = unicode(exception.full_response)[0:1024] except UnicodeError: # ... localized error strings on windows are usually # in anything but unicode :( fr = str(repr(exception.full_response))[0:1024] message += "\n\n" + fr d = Gtk.MessageDialog( self["editor"], Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, message) d.run() d.hide() d.destroy() self["editor"].set_sensitive(True)
def _extract(self, data): (archive, compressed, output, extracted, ex_size) = data try: buffer = compressed.read(CHUNK_SIZE) read_size = len(buffer) if read_size == CHUNK_SIZE: # Need some more output.write(buffer) extracted += read_size GLib.idle_add(self._extract, (archive, compressed, output, extracted, ex_size)) self.emit("extraction-progress", float(extracted) / float(ex_size)) else: # End of file # Write rest, if any if read_size > 0: output.write(buffer) # Change file mode to 0755 if hasattr(os, "fchmod"): # ... on Unix os.fchmod(output.fileno(), 0o755) output.close() archive.close() compressed.close() self.emit("extraction-progress", 1.0) self.emit("extraction-finished") except Exception as e: log.exception(e) self.emit("error", e, _("Failed to determine latest Syncthing version.")) return return False
def _cb_download(self, stream, result, data): (tmpfile, downloaded) = data try: # Get response from async call response = stream.read_bytes_finish(result) if response == None: raise Exception("No data received") # 0b of data read indicates end of file if response.get_size() > 0: # Not EOF. Write buffer to disk and download some more downloaded += response.get_size() tmpfile.write(response.get_data()) stream.read_bytes_async(CHUNK_SIZE, GLib.PRIORITY_DEFAULT, None, self._cb_download, (tmpfile, downloaded)) self.emit("download-progress", float(downloaded) / float(self.dll_size)) else: # EOF. Re-open tmpfile as tar and prepare to extract # binary self.emit("download-finished") stream.close(None) tmpfile.close() GLib.idle_add(self._open_archive, tmpfile.name) except Exception as e: log.exception(e) self.emit("error", e, _("Download failed.")) return
def _extract(self, data): (archive, compressed, output, extracted, ex_size) = data try: buffer = compressed.read(CHUNK_SIZE) read_size = len(buffer) if read_size == CHUNK_SIZE: # Need some more output.write(buffer) extracted += read_size GLib.idle_add( self._extract, (archive, compressed, output, extracted, ex_size)) self.emit("extraction-progress", float(extracted) / float(ex_size)) else: # End of file # Write rest, if any if read_size > 0: output.write(buffer) # Change file mode to 0755 if hasattr(os, "fchmod"): # ... on Unix os.fchmod(output.fileno(), 0o755) output.close() archive.close() compressed.close() self.emit("extraction-progress", 1.0) self.emit("extraction-finished") except Exception as e: log.exception(e) self.emit("error", e, _("Failed to determine latest Syncthing version.")) return return False
def syncthing_cb_post_error(self, exception, *a): # TODO: Unified error message if isinstance(exception, ConnectionRestarted): # Should be ok, this restart is triggered # by App handler for 'config-saved' event. return self.syncthing_cb_post_config() message = "%s\n%s" % ( _("Failed to save configuration."), str(exception) ) if hasattr(exception, "full_response"): try: fr = unicode(exception.full_response)[0:1024] except UnicodeError: # ... localized error strings on windows are usually # in anything but unicode :( fr = str(repr(exception.full_response))[0:1024] message += "\n\n" + fr d = Gtk.MessageDialog( self["editor"], Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, message ) d.run() d.hide() d.destroy() self["editor"].set_sensitive(True)
def cb_extract_finished(self, downloader, *a): """ Called after extraction is finished """ self["vsyncthing_binary"].set_sensitive(True) self["btBrowse"].set_sensitive(True) self["vsyncthing_binary"].set_text(downloader.get_target()) self["lblDownloadProgress"].set_markup("<b>" + _("Download finished.") + "</b>") self["pbDownload"].set_visible(False) self["btSave"].set_sensitive(True)
def cb_data_failed(self, exception, *a): """ Failed to load configuration. This shouldn't happen unless daemon dies exactly when user clicks to edit menu. Handled by simple error message. """ # All other errors are fatal for now. Error dialog is displayed and program exits. d = Gtk.MessageDialog( self["editor"], Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "%s %s\n\n%s %s" % (_("Failed to load configuration from daemon."), _("Try again."), _("Error message:"), str(exception)), ) d.run() self.close()
def __init__(self, gladepath="/usr/share/syncthing-gtk", iconpath="/usr/share/syncthing-gtk/icons", config=None): # Init Gtk.Assistant.__init__(self) if not config is None: self.config = config else: self.config = Configuration() self.gladepath = gladepath self.iconpath = iconpath self.syncthing_options = {} self.lines = [] # Daemon and wizard output, # maybe for error reports self.finished = False self.connect("prepare", self.prepare_page) # Find syncthing configuration directory self.st_configdir = os.path.join(get_config_dir(), "syncthing") self.st_configfile = os.path.join(get_config_dir(), "syncthing", "config.xml") # Window setup self.set_position(Gtk.WindowPosition.CENTER) self.set_size_request(720, -1) self.set_default_size(720, 300) self.set_deletable(True) if IS_WINDOWS: self.set_icon_list([ GdkPixbuf.Pixbuf.new_from_file( "icons/32x32/apps/syncthing-gtk.png") ]) else: self.set_icon_name("syncthing-gtk") self.set_title("%s %s" % (_("Syncthing-GTK"), _("First run wizard"))) # Add "Quit" button self.quit_button = Gtk.Button.new_from_stock("gtk-quit") self.add_action_widget(self.quit_button) self.quit_button.set_visible(True) self.quit_button.connect("clicked", lambda *a: self.emit("cancel")) # Pages self.add_page(IntroPage(self)) self.add_page(FindDaemonPage()) self.add_page(GenerateKeysPage()) self.add_page(HttpSettingsPage()) self.add_page(SaveSettingsPage()) self.add_page(LastPage())
def check_port(self, port): """ Tries to open TCP port to check it availability. It this fails, checks next ports, until MAX_PORT is reached. When MAX_PORT is reached, it's safe to assume that something completely wrong is happening and an error should be displayed. """ if port >= MAX_PORT: # Remove config.xml that I just created try: os.unlink(self.parent.st_configfile) except Exception, e: self.parent.output_line("syncthing-gtk: %s" % (str(e),)) self.parent.error(self, _("Failed to find unused port for listening."), _("Please, check your firewall settings and try again."), False) return
def cb_syncthing_error(self, daemon, message): summary = _('An error occurred in Syncthing!') n = Notify.Notification.new(summary, None, ICON_ERR) n.set_urgency(Notify.Urgency.CRITICAL) try: n.show() except Exception: pass
def insert(self, page): """ Inserts new page after currently displayed. """ index = self.get_current_page() index = self.insert_page(page, index + 1) page.parent = self self.set_page_type(page, page.TYPE) self.set_page_title(page, _(page.TITLE) + " ") return index