[plugin_installer] Port recent plugin_installer

This inlcudes slight refactoring and switch to https instead of ftps
This commit is contained in:
Philipp Hörist
2017-02-20 16:30:19 +01:00
parent 3347951de9
commit c0b3f36b24
2 changed files with 74 additions and 100 deletions

View File

@@ -394,24 +394,6 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkCheckButton" id="check_update_periodically">
<property name="label" translatable="yes">Check update every 24 hours</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="focus_on_click">False</property>
<property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_check_update_periodically_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
</child> </child>
</object> </object>

View File

@@ -25,7 +25,6 @@ from gi.repository import Pango
from gi.repository import GLib from gi.repository import GLib
from gi.repository import GObject from gi.repository import GObject
import ftplib
import io import io
import threading import threading
import configparser import configparser
@@ -33,9 +32,11 @@ import os
import fnmatch import fnmatch
import sys import sys
import zipfile import zipfile
import ssl
import logging import logging
import posixpath
from urllib.request import urlopen
from urllib.parse import urlparse, urljoin
from common import gajim from common import gajim
from plugins import GajimPlugin from plugins import GajimPlugin
from plugins.helpers import log_calls, log from plugins.helpers import log_calls, log
@@ -68,11 +69,11 @@ class PluginInstaller(GajimPlugin):
@log_calls('PluginInstallerPlugin') @log_calls('PluginInstallerPlugin')
def init(self): def init(self):
self.description = _('Install and upgrade plugins from ftp') self.description = _('Install and Upgrade Plugins')
self.config_dialog = PluginInstallerPluginConfigDialog(self) self.config_dialog = PluginInstallerPluginConfigDialog(self)
self.config_default_values = {'ftp_server': ('ftp.gajim.org', ''), self.config_default_values = {'http_server': ('https://ftp.gajim.org', ''),
'check_update': (True, ''), 'check_update': (True, ''),
'check_update_periodically': (True, '')} }
self.window = None self.window = None
self.progressbar = None self.progressbar = None
self.available_plugins_model = None self.available_plugins_model = None
@@ -82,20 +83,10 @@ class PluginInstaller(GajimPlugin):
icon = Gtk.Image() icon = Gtk.Image()
self.def_icon = icon.render_icon(Gtk.STOCK_PREFERENCES, self.def_icon = icon.render_icon(Gtk.STOCK_PREFERENCES,
Gtk.IconSize.MENU) Gtk.IconSize.MENU)
if gajim.version.startswith('0.15'): self.server_folder = 'plugins_1'
self.server_folder = 'plugins_0.15'
elif gajim.version.startswith('0.16.10'):
self.server_folder = 'plugins_1'
else:
self.server_folder = 'plugins_0.16'
@log_calls('PluginInstallerPlugin') @log_calls('PluginInstallerPlugin')
def activate(self): def activate(self):
self.pl_menuitem = gajim.interface.roster.xml.get_object(
'plugins_menuitem')
self.id_ = self.pl_menuitem.connect_after('activate', self.on_activate)
if 'plugins' in gajim.interface.instances:
self.on_activate(None)
if self.config['check_update']: if self.config['check_update']:
self.timeout_id = GLib.timeout_add_seconds(30, self.check_update) self.timeout_id = GLib.timeout_add_seconds(30, self.check_update)
@@ -113,34 +104,53 @@ class PluginInstaller(GajimPlugin):
' your installer plugins. Do you want to update those plugins:' ' your installer plugins. Do you want to update those plugins:'
'\n%s') % plugins_str, on_response_yes=open_update) '\n%s') % plugins_str, on_response_yes=open_update)
def ftp_connect(self): def parse_manifest(self, buf):
if os.name == 'nt': '''
ctx = ssl.create_default_context() given the buffer of the zipfile, returns the list of plugin manifests
con = ftplib.FTP_TLS(self.config['ftp_server'], context=ctx) '''
else: zip_file = zipfile.ZipFile(buf)
con = ftplib.FTP_TLS(self.config['ftp_server']) manifest_list = zip_file.namelist()
plugins = []
for filename in manifest_list:
config = configparser.ConfigParser()
conf_file = zip_file.open(filename)
config.read_file(io.TextIOWrapper(conf_file, encoding='utf-8'))
conf_file.close()
if not config.has_section('info'):
continue
plugins.append(config)
return plugins
con.login() def retrieve_path(self, directory, fname):
con.prot_p() print('retrive path')
return con server = self.config['http_server']
if not server:
server = self.config_default_values['http_server'][0]
if not urlparse(server).scheme:
server = 'https://' + server
if urlparse(server).scheme != 'https':
log.warn('Warning: not using HTTPS is a '
'very serious security issue!')
location = posixpath.join(directory, fname)
uri = urljoin(server, location)
log.debug('Fetching {}'.format(uri))
request = urlopen(uri)
manifest_buffer = io.BytesIO(request.read())
return manifest_buffer
def retrieve_manifest(self):
return self.retrieve_path(self.server_folder, 'manifests.zip')
@log_calls('PluginInstallerPlugin') @log_calls('PluginInstallerPlugin')
def check_update(self): def check_update(self):
def _run(): def _run():
try: try:
to_update = [] to_update = []
con = self.ftp_connect() zipbuf = self.retrieve_manifest()
con.cwd(self.server_folder) plugin_manifests = self.parse_manifest(zipbuf)
con.retrbinary('RETR manifests.zip', ftp.handleDownload) for config in plugin_manifests:
zip_file = zipfile.ZipFile(ftp.buffer_)
manifest_list = zip_file.namelist()
for filename in manifest_list:
config = configparser.ConfigParser()
conf_file = zip_file.open(filename)
config.read_file(io.TextIOWrapper(conf_file, encoding='utf-8'))
conf_file.close()
if not config.has_section('info'):
continue
opts = config.options('info') opts = config.options('info')
if 'name' not in opts or 'version' not in opts or \ if 'name' not in opts or 'version' not in opts or \
'description' not in opts or 'authors' not in opts or \ 'description' not in opts or 'authors' not in opts or \
@@ -154,13 +164,9 @@ class PluginInstaller(GajimPlugin):
'version')) 'version'))
if remote > local: if remote > local:
to_update.append(config.get('info', 'name')) to_update.append(config.get('info', 'name'))
con.quit()
GLib.idle_add(self.warn_update, to_update) GLib.idle_add(self.warn_update, to_update)
# check for updates at least once every 24 hours
if self.config['check_update_periodically']:
self.timeout_id = GLib.timeout_add_seconds(24*3600, self.check_update)
except Exception as e: except Exception as e:
log.debug('Ftp error when check updates: %s' % str(e)) log.error('Ftp error when check updates: %s' % str(e), exc_info=True)
ftp = Ftp(self) ftp = Ftp(self)
ftp.run = _run ftp.run = _run
ftp.start() ftp.start()
@@ -181,18 +187,15 @@ class PluginInstaller(GajimPlugin):
GLib.source_remove(self.timeout_id) GLib.source_remove(self.timeout_id)
self.timeout_id = 0 self.timeout_id = 0
def on_activate(self, widget): def on_activate(self, plugin_win):
if 'plugins' not in gajim.interface.instances:
return
if hasattr(self, 'page_num'): if hasattr(self, 'page_num'):
# 'Available' tab exists # 'Available' tab exists
return return
self.installed_plugins_model = gajim.interface.instances[ self.installed_plugins_model = plugin_win.installed_plugins_model
'plugins'].installed_plugins_model self.notebook = plugin_win.plugins_notebook
self.notebook = gajim.interface.instances['plugins'].plugins_notebook
id_ = self.notebook.connect('switch-page', self.on_notebook_switch_page) id_ = self.notebook.connect('switch-page', self.on_notebook_switch_page)
self.connected_ids[id_] = self.notebook self.connected_ids[id_] = self.notebook
self.window = gajim.interface.instances['plugins'].window self.window = plugin_win.window
id_ = self.window.connect('destroy', self.on_win_destroy) id_ = self.window.connect('destroy', self.on_win_destroy)
self.connected_ids[id_] = self.window self.connected_ids[id_] = self.window
self.Gtk_BUILDER_FILE_PATH = self.local_file_path('config_dialog.ui') self.Gtk_BUILDER_FILE_PATH = self.local_file_path('config_dialog.ui')
@@ -461,11 +464,10 @@ class PluginInstaller(GajimPlugin):
file_path)) file_path))
# read metadata from manifest.ini # read metadata from manifest.ini
with open(manifest_path) as _file: conf.readfp(open(manifest_path, 'r'))
conf.read_file(_file)
for option in fields: for option in fields:
if conf.get('info', option) is '': if conf.get('info', option) is '':
raise configparser.NoOptionError('field empty') raise configparser.NoOptionError
setattr(module_attr, option, conf.get('info', option)) setattr(module_attr, option, conf.get('info', option))
conf.remove_section('info') conf.remove_section('info')
plugins_found.append(module_attr) plugins_found.append(module_attr)
@@ -496,13 +498,12 @@ class Ftp(threading.Thread):
self.window = plugin.window self.window = plugin.window
self.progressbar = plugin.progressbar self.progressbar = plugin.progressbar
self.model = plugin.available_plugins_model self.model = plugin.available_plugins_model
self.buffer_ = io.BytesIO()
self.remote_dirs = None self.remote_dirs = None
self.append_to_model = True self.append_to_model = True
self.upgrading = False self.upgrading = False
icon = Gtk.Image() icon = Gtk.Image()
self.def_icon = icon.render_icon(Gtk.STOCK_PREFERENCES, self.def_icon = icon.render_icon(
Gtk.IconSize.MENU) Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU)
def model_append(self, row): def model_append(self, row):
self.model.append(row) self.model.append(row)
@@ -521,14 +522,15 @@ class Ftp(threading.Thread):
try: try:
GLib.idle_add(self.progressbar.set_text, GLib.idle_add(self.progressbar.set_text,
_('Connecting to server')) _('Connecting to server'))
self.ftp = self.plugin.ftp_connect()
self.ftp.cwd(self.plugin.server_folder)
self.progressbar.set_show_text(True)
if not self.remote_dirs: if not self.remote_dirs:
GLib.idle_add(self.progressbar.set_text, GLib.idle_add(self.progressbar.set_text,
_('Scan files on the server')) _('Scan files on the server'))
self.ftp.retrbinary('RETR manifests_images.zip', self.handleDownload) try:
zip_file = zipfile.ZipFile(self.buffer_) buf = self.plugin.retrieve_path(self.plugin.server_folder, 'manifests_images.zip')
except:
log.exception("Error fetching plugin list")
return
zip_file = zipfile.ZipFile(buf)
manifest_list = zip_file.namelist() manifest_list = zip_file.namelist()
progress_step = 1.0 / len(manifest_list) progress_step = 1.0 / len(manifest_list)
for filename in manifest_list: for filename in manifest_list:
@@ -593,9 +595,6 @@ class Ftp(threading.Thread):
except Exception as e: except Exception as e:
self.window.emit('error_signal', str(e)) self.window.emit('error_signal', str(e))
def handleDownload(self, block):
self.buffer_.write(block)
def download_plugin(self): def download_plugin(self):
GLib.idle_add(self.progressbar.show) GLib.idle_add(self.progressbar.show)
self.pulse = GLib.timeout_add(150, self.progressbar_pulse) self.pulse = GLib.timeout_add(150, self.progressbar_pulse)
@@ -605,24 +604,22 @@ class Ftp(threading.Thread):
base_dir, user_dir = gajim.PLUGINS_DIRS base_dir, user_dir = gajim.PLUGINS_DIRS
if not os.path.isdir(user_dir): if not os.path.isdir(user_dir):
os.mkdir(user_dir) os.mkdir(user_dir)
local_dir = ld = os.path.join(user_dir, remote_dir) local_dir = os.path.join(user_dir, remote_dir)
if not os.path.isdir(local_dir): if not os.path.isdir(local_dir):
os.mkdir(local_dir) os.mkdir(local_dir)
local_dir = os.path.split(user_dir)[0] local_dir = os.path.split(user_dir)[0]
# downloading zip file # downloading zip file
GLib.idle_add(self.progressbar.set_text, GLib.idle_add(self.progressbar.set_text,
_('Downloading "%s"') % filename) _('Downloading "%s"') % filename)
full_filename = os.path.join(user_dir, filename)
self.buffer_ = io.BytesIO()
try: try:
self.ftp.retrbinary('RETR %s' % filename, self.handleDownload) buf = self.plugin.retrieve_path(self.plugin.server_folder,
except ftplib.all_errors as e: filename)
print (str(e)) except:
log.exception("Error downloading plugin %s" % filename)
with zipfile.ZipFile(self.buffer_) as zip_file: continue
zip_file.extractall(os.path.join(user_dir)) with zipfile.ZipFile(buf) as zip_file:
zip_file.extractall(os.path.join(local_dir, 'plugins'))
self.ftp.quit() self.ftp.quit()
GLib.idle_add(self.window.emit, 'plugin_downloaded', self.remote_dirs) GLib.idle_add(self.window.emit, 'plugin_downloaded', self.remote_dirs)
GLib.source_remove(self.pulse) GLib.source_remove(self.pulse)
@@ -642,12 +639,10 @@ class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog):
self.connect('hide', self.on_hide) self.connect('hide', self.on_hide)
def on_run(self): def on_run(self):
widget = self.xml.get_object('ftp_server') widget = self.xml.get_object('http_server')
widget.set_text(str(self.plugin.config['ftp_server'])) widget.set_text(str(self.plugin.config['http_server']))
self.xml.get_object('check_update').set_active( self.xml.get_object('check_update').set_active(
self.plugin.config['check_update']) self.plugin.config['check_update'])
self.xml.get_object('check_update_periodically').set_active(
self.plugin.config['check_update_periodically'])
def on_hide(self, widget): def on_hide(self, widget):
widget = self.xml.get_object('ftp_server') widget = self.xml.get_object('ftp_server')
@@ -655,6 +650,3 @@ class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog):
def on_check_update_toggled(self, widget): def on_check_update_toggled(self, widget):
self.plugin.config['check_update'] = widget.get_active() self.plugin.config['check_update'] = widget.get_active()
def on_check_update_periodically_toggled(self, widget):
self.plugin.config['check_update_periodically'] = widget.get_active()