Skip to content
Permalink
Browse files

[FEATURE][Plugin installer] Support for encrypted zips when installin…

…g plugins from local files
  • Loading branch information
borysiasty committed Sep 13, 2018
1 parent 23f1e43 commit 0f7a09947cea7cc660ec42639cf8193b0f865133
Showing with 61 additions and 34 deletions.
  1. +38 −25 python/pyplugin_installer/installer.py
  2. +23 −9 python/pyplugin_installer/unzip.py
@@ -28,7 +28,7 @@
import zipfile

from qgis.PyQt.QtCore import Qt, QObject, QDir, QUrl, QFileInfo, QFile
from qgis.PyQt.QtWidgets import QMessageBox, QLabel, QFrame, QApplication, QFileDialog
from qgis.PyQt.QtWidgets import QMessageBox, QLabel, QFrame, QApplication, QFileDialog, QInputDialog, QLineEdit
from qgis.PyQt.QtNetwork import QNetworkRequest

import qgis
@@ -539,9 +539,6 @@ def installFromZipFile(self, filePath):
settings.setValue(settingsGroup + '/lastZipDirectory',
QFileInfo(filePath).absoluteDir().absolutePath())

error = False
infoString = None

with zipfile.ZipFile(filePath, 'r') as zf:
pluginName = os.path.split(zf.namelist()[0])[0]

@@ -551,24 +548,41 @@ def installFromZipFile(self, filePath):
if not QDir(pluginsDirectory).exists():
QDir().mkpath(pluginsDirectory)

pluginDirectory = QDir.cleanPath(os.path.join(pluginsDirectory, pluginName))

# If the target directory already exists as a link,
# remove the link without resolving
QFile(os.path.join(pluginsDirectory, pluginFileName)).remove()
QFile(pluginDirectory).remove()

try:
# Test extraction. If fails, then exception will be raised
# and no removing occurs
unzip(str(filePath), str(pluginsDirectory))
# Removing old plugin files if exist
removeDir(QDir.cleanPath(os.path.join(pluginsDirectory, pluginFileName)))
# Extract new files
unzip(str(filePath), str(pluginsDirectory))
except:
error = True
infoString = (self.tr("Plugin installation failed"),
self.tr("Failed to unzip the plugin package\n{}.\nProbably it is broken".format(filePath)))
password = None
infoString = None
success = False
keepTrying = True

while keepTrying:
try:
# Test extraction. If fails, then exception will be raised and no removing occurs
unzip(filePath, pluginsDirectory, password)
# Removing old plugin files if exist
removeDir(pluginDirectory)
# Extract new files
unzip(filePath, pluginsDirectory, password)
keepTrying = False
success = True
except Exception as e:
success = False
if 'password' in str(e):
infoString = self.tr('Aborted by user')
if 'Bad password' in str(e):
msg = self.tr('Wrong password. Please enter a correct password to the zip file.')
else:
msg = self.tr('The zip file is encrypted. Please enter password.')
password, keepTrying = QInputDialog.getText(iface.mainWindow(), self.tr('Enter password'), msg, QLineEdit.Password)
else:
infoString = self.tr("Failed to unzip the plugin package\n{}.\nProbably it is broken".format(filePath))
keepTrying = False

if infoString is None:
if success:
updateAvailablePlugins()
loadPlugin(pluginName)
plugins.getAllInstalled()
@@ -585,11 +599,10 @@ def installFromZipFile(self, filePath):
else:
if startPlugin(pluginName):
settings.setValue('/PythonPlugins/' + pluginName, True)
infoString = (self.tr("Plugin installed successfully"), "")

if infoString[0]:
level = error and Qgis.Critical or Qgis.Info
msg = "<b>%s</b>" % infoString[0]
if infoString[1]:
msg += "<b>:</b> %s" % infoString[1]
iface.pluginManagerInterface().pushMessage(msg, level)
msg = "<b>%s</b>" % self.tr("Plugin installed successfully")
else:
msg = "<b>%s:</b> %s" % (self.tr("Plugin installation failed"), infoString)

level = Qgis.Info if success else Qgis.Critical
iface.pluginManagerInterface().pushMessage(msg, level)
@@ -24,27 +24,41 @@
import os


def unzip(file, targetDir):
def unzip(file, targetDir, password=None):
""" Creates directory structure and extracts the zip contents to it.
file - the zip file to extract
targetDir - target location
file (file object) - the zip file to extract
targetDir (str) - target location
password (str; optional) - password to decrypt the zip file (if encrypted)
"""

# convert password to bytes
if isinstance(password, str):
password = bytes(password, 'utf8')

# create destination directory if doesn't exist
if not targetDir.endswith(':') and not os.path.exists(targetDir):
os.makedirs(targetDir)

zf = zipfile.ZipFile(file)
for name in zf.namelist():
# Skip directories - they will be created when necessary by os.makedirs
if name.endswith('/'):
continue

# Read the source file before creating any output,
# so no directories are created if user doesn't know the password
memberContent = zf.read(name, password)

# create directory if doesn't exist
localDir = os.path.split(name)[0]
fullDir = os.path.normpath(os.path.join(targetDir, localDir))
if not os.path.exists(fullDir):
os.makedirs(fullDir)
# extract file
if not name.endswith('/'):
fullPath = os.path.normpath(os.path.join(targetDir, name))
outfile = open(fullPath, 'wb')
outfile.write(zf.read(name))
outfile.flush()
outfile.close()
fullPath = os.path.normpath(os.path.join(targetDir, name))
outfile = open(fullPath, 'wb')
outfile.write(memberContent)
outfile.flush()
outfile.close()

zf.close()

0 comments on commit 0f7a099

Please sign in to comment.
You can’t perform that action at this time.