Date: Friday, July 14, 2017 @ 16:44:00 Author: jleclanche Revision: 245111
archrelease: copy trunk to community-i686, community-x86_64 Added: pgadmin4/repos/community-i686/PKGBUILD (from rev 245110, pgadmin4/trunk/PKGBUILD) pgadmin4/repos/community-i686/config_distro.py (from rev 245110, pgadmin4/trunk/config_distro.py) pgadmin4/repos/community-i686/config_local.py (from rev 245110, pgadmin4/trunk/config_local.py) pgadmin4/repos/community-i686/pgAdmin4.desktop (from rev 245110, pgadmin4/trunk/pgAdmin4.desktop) pgadmin4/repos/community-x86_64/PKGBUILD (from rev 245110, pgadmin4/trunk/PKGBUILD) pgadmin4/repos/community-x86_64/config_distro.py (from rev 245110, pgadmin4/trunk/config_distro.py) pgadmin4/repos/community-x86_64/config_local.py (from rev 245110, pgadmin4/trunk/config_local.py) pgadmin4/repos/community-x86_64/pgAdmin4.desktop (from rev 245110, pgadmin4/trunk/pgAdmin4.desktop) Deleted: pgadmin4/repos/community-i686/0001-Simplify-Server-s-python-setup.patch pgadmin4/repos/community-i686/PKGBUILD pgadmin4/repos/community-i686/config_distro.py pgadmin4/repos/community-i686/config_local.py pgadmin4/repos/community-i686/pgAdmin4.desktop pgadmin4/repos/community-x86_64/0001-Simplify-Server-s-python-setup.patch pgadmin4/repos/community-x86_64/PKGBUILD pgadmin4/repos/community-x86_64/config_distro.py pgadmin4/repos/community-x86_64/config_local.py pgadmin4/repos/community-x86_64/pgAdmin4.desktop ------------------------------------------------------------+ /PKGBUILD | 166 ++ /config_distro.py | 4 /pgAdmin4.desktop | 22 community-i686/0001-Simplify-Server-s-python-setup.patch | 633 ----------- community-i686/PKGBUILD | 86 - community-i686/config_distro.py | 2 community-i686/pgAdmin4.desktop | 11 community-x86_64/0001-Simplify-Server-s-python-setup.patch | 633 ----------- community-x86_64/PKGBUILD | 86 - community-x86_64/config_distro.py | 2 community-x86_64/pgAdmin4.desktop | 11 11 files changed, 192 insertions(+), 1464 deletions(-) Deleted: community-i686/0001-Simplify-Server-s-python-setup.patch =================================================================== --- community-i686/0001-Simplify-Server-s-python-setup.patch 2017-07-14 16:43:51 UTC (rev 245110) +++ community-i686/0001-Simplify-Server-s-python-setup.patch 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,633 +0,0 @@ -From a5a1c332e23a094e0c2a1c9513fb9f6b05cbbf05 Mon Sep 17 00:00:00 2001 -From: "Jan Alexander Steffens (heftig)" <jan.steff...@gmail.com> -Date: Sat, 28 Jan 2017 01:19:06 +0100 -Subject: [PATCH] Simplify Server's python setup ---- - runtime/BrowserWindow.cpp | 6 +- - runtime/ConfigWindow.cpp | 16 ++- - runtime/ConfigWindow.h | 6 +- - runtime/ConfigWindow.ui | 22 +++- - runtime/Server.cpp | 253 +++++++++++----------------------------------- - runtime/Server.h | 11 +- - runtime/pgAdmin4.cpp | 106 ++++++++++--------- - 7 files changed, 149 insertions(+), 271 deletions(-) - -diff --git a/runtime/BrowserWindow.cpp b/runtime/BrowserWindow.cpp -index 516b8689818bddc9..a2ee4895f5351565 100644 ---- a/runtime/BrowserWindow.cpp -+++ b/runtime/BrowserWindow.cpp -@@ -1286,17 +1286,17 @@ void BrowserWindow::preferences() - - ConfigWindow *dlg = new ConfigWindow(); - dlg->setWindowTitle(QWidget::tr("Configuration")); -- dlg->setPythonPath(settings.value("PythonPath").toString()); -+ dlg->setPythonExecutable(settings.value("PythonExecutable").toString()); - dlg->setApplicationPath(settings.value("ApplicationPath").toString()); - dlg->setModal(true); - ok = dlg->exec(); - -- QString pythonpath = dlg->getPythonPath(); -+ QString pythonexecutable = dlg->getPythonExecutable(); - QString applicationpath = dlg->getApplicationPath(); - - if (ok) - { -- settings.setValue("PythonPath", pythonpath); -+ settings.setValue("PythonExecutable", pythonexecutable); - settings.setValue("ApplicationPath", applicationpath); - } - } -diff --git a/runtime/ConfigWindow.cpp b/runtime/ConfigWindow.cpp -index 3fb1a2738eb89ec0..c31345bf08d06d88 100644 ---- a/runtime/ConfigWindow.cpp -+++ b/runtime/ConfigWindow.cpp -@@ -17,37 +17,45 @@ ConfigWindow::ConfigWindow(QWidget *parent) : - ui(new Ui::ConfigWindow) - { - ui->setupUi(this); -+ -+#ifdef PYTHON2 -+ ui->pythonExecutableLineEdit->setPlaceholderText( -+ QString(QWidget::tr("The Python 2 executable to use (may be within a virtual environment)"))); -+#else -+ ui->pythonExecutableLineEdit->setPlaceholderText( -+ QString(QWidget::tr("The Python 3 executable to use (may be within a virtual environment)"))); -+#endif - } - - ConfigWindow::~ConfigWindow() - { - delete ui; - } - - void ConfigWindow::on_buttonBox_accepted() - { - this->close(); - } - - void ConfigWindow::on_buttonBox_rejected() - { - this->close(); - } - --QString ConfigWindow::getPythonPath() -+QString ConfigWindow::getPythonExecutable() - { -- return ui->pythonPathLineEdit->text(); -+ return ui->pythonExecutableLineEdit->text(); - } - - QString ConfigWindow::getApplicationPath() - { - return ui->applicationPathLineEdit->text(); - } - - --void ConfigWindow::setPythonPath(QString path) -+void ConfigWindow::setPythonExecutable(QString path) - { -- ui->pythonPathLineEdit->setText(path); -+ ui->pythonExecutableLineEdit->setText(path); - } - - void ConfigWindow::setApplicationPath(QString path) -diff --git a/runtime/ConfigWindow.h b/runtime/ConfigWindow.h -index 0027a742aca7b762..c4487fcc6e97f354 100644 ---- a/runtime/ConfigWindow.h -+++ b/runtime/ConfigWindow.h -@@ -26,19 +26,19 @@ public: - explicit ConfigWindow(QWidget *parent = 0); - ~ConfigWindow(); - -- QString getPythonPath(); -+ QString getPythonExecutable(); - QString getApplicationPath(); - -- void setPythonPath(QString path); -+ void setPythonExecutable(QString path); - void setApplicationPath(QString path); - - private slots: - void on_buttonBox_accepted(); - void on_buttonBox_rejected(); - - private: - Ui::ConfigWindow *ui; -- QString m_pythonpath, m_applicationpath; -+ QString m_pythonexecutable, m_applicationpath; - }; - - #endif // CONFIGWINDOW_H -diff --git a/runtime/ConfigWindow.ui b/runtime/ConfigWindow.ui -index 40d87be43803204a..8b379052a1da8cd0 100644 ---- a/runtime/ConfigWindow.ui -+++ b/runtime/ConfigWindow.ui -@@ -29,24 +29,38 @@ - <enum>QFormLayout::ExpandingFieldsGrow</enum> - </property> - <item row="0" column="0"> -- <widget class="QLabel" name="pythonPathLabel"> -+ <widget class="QLabel" name="pythonExecutableLabel"> -+ <property name="sizeIncrement"> -+ <size> -+ <width>0</width> -+ <height>1</height> -+ </size> -+ </property> - <property name="text"> -- <string>Python Path</string> -+ <string>Python Executable</string> - </property> - </widget> - </item> - <item row="0" column="1"> -- <widget class="QLineEdit" name="pythonPathLineEdit"/> -+ <widget class="QLineEdit" name="pythonExecutableLineEdit"> -+ <property name="placeholderText"> -+ <string>The python executable to use (may be within a virtual environment)</string> -+ </property> -+ </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="applicationPathLabel"> - <property name="text"> - <string>Application Path</string> - </property> - </widget> - </item> - <item row="1" column="1"> -- <widget class="QLineEdit" name="applicationPathLineEdit"/> -+ <widget class="QLineEdit" name="applicationPathLineEdit"> -+ <property name="placeholderText"> -+ <string>The directory containing pgAdmin4.py (do not include the file name)</string> -+ </property> -+ </widget> - </item> - </layout> - </item> -diff --git a/runtime/Server.cpp b/runtime/Server.cpp -index 33b43583480440b7..83bcdeedb09197c8 100644 ---- a/runtime/Server.cpp -+++ b/runtime/Server.cpp -@@ -22,201 +22,85 @@ - // App headers - #include "Server.h" - --static void add_to_path(QString &python_path, QString path, bool prepend=false) --{ -- if (!python_path.contains(path)) -- { -- if (!prepend) -- { --#if defined(Q_OS_WIN) -- if (!python_path.isEmpty() && !python_path.endsWith(";")) -- python_path.append(";"); --#else -- if (!python_path.isEmpty() && !python_path.endsWith(":")) -- python_path.append(":"); --#endif -- -- python_path.append(path); -- } -- else -- { --#if defined(Q_OS_WIN) -- if (!python_path.isEmpty() && !python_path.startsWith(";")) -- python_path.prepend(";"); --#else -- if (!python_path.isEmpty() && !python_path.startsWith(":")) -- python_path.prepend(":"); --#endif -- -- python_path.prepend(path); -- } -- } --} -- - Server::Server(quint16 port, QString key) - { - // Appserver port etc - m_port = port; - m_key = key; -- m_wcAppName = NULL; -- m_wcPythonHome = NULL; - - // Initialise Python -- Py_NoSiteFlag=1; -- Py_DontWriteBytecodeFlag=1; -+ Py_DontWriteBytecodeFlag = 1; - -- PGA_APP_NAME_UTF8 = PGA_APP_NAME.toUtf8(); -+ // Get the application directory -+ QString appDir = qApp->applicationDirPath(); -+ -+ // Build (and canonicalise) the virtual environment path -+#ifdef Q_OS_MAC -+ QFileInfo venvBinPath(appDir + "/../Resources/venv/bin"); -+#elif defined(Q_OS_WIN) -+ QFileInfo venvBinPath(appDir + "/../venv"); -+#else -+ QFileInfo venvBinPath(appDir + "/../venv/bin"); -+#endif -+ QString binPath = venvBinPath.canonicalFilePath(); -+ -+ if (binPath.isEmpty()) -+ { -+ // Use default Python environment from the config, or lastly, PATH -+ QSettings settings; -+ binPath.append(settings.value("PythonExecutable").toString()); -+ if (binPath.isEmpty()) -+ binPath.append("python"); -+ } -+ else -+ { -+ binPath.append("/python"); -+ } - - // Python3 requires conversion of char * to wchar_t *, so... - #ifdef PYTHON2 -- Py_SetProgramName(PGA_APP_NAME_UTF8.data()); -+ QByteArray appName = binPath.toLocal8Bit(); -+ m_pyAppName = new char[strlen(appName.data()) + 1]; -+ strcpy(m_pyAppName, appName.data()); -+ qDebug() << "Python interpreter: " << QString::fromLocal8Bit(m_pyAppName); - #else -- char *appName = PGA_APP_NAME_UTF8.data(); -- const size_t cSize = strlen(appName)+1; -- m_wcAppName = new wchar_t[cSize]; -- mbstowcs (m_wcAppName, appName, cSize); -- Py_SetProgramName(m_wcAppName); -+ std::wstring appName = binPath.toStdWString(); -+ m_pyAppName = new wchar_t[wcslen(appName.c_str()) + 1]; -+ wcscpy(m_pyAppName, appName.c_str()); -+ qDebug() << "Python interpreter: " << QString::fromWCharArray(m_pyAppName); - #endif -- -- // Setup the search path -- QSettings settings; -- QString python_path = settings.value("PythonPath").toString(); -- -- // Get the application directory -- QString app_dir = qApp->applicationDirPath(), -- path_env = qgetenv("PATH"), -- pythonHome; -- QStringList path_list; -- int i; -- --#ifdef Q_OS_MAC -- // In the case we're running in a release appbundle, we need to ensure the -- // bundled virtual env is included in the Python path. We include it at the -- // end, so expert users can override the path, but we do not save it, because -- // if users move the app bundle, we'll end up with dead entries -- -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../Resources/venv/bin"); -- QFileInfo venvLibPath(app_dir + "/../Resources/venv/lib/python"); -- QFileInfo venvDynLibPath(app_dir + "/../Resources/venv/lib/python/lib-dynload"); -- QFileInfo venvSitePackagesPath(app_dir + "/../Resources/venv/lib/python/site-packages"); -- QFileInfo venvPath(app_dir + "/../Resources/venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append the path, if it's not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDynLibPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#elif defined(Q_OS_WIN) -- -- // In the case we're running in a release application, we need to ensure the -- // bundled virtual env is included in the Python path. We include it at the -- // end, so expert users can override the path, but we do not save it. -- -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../venv"); -- QFileInfo venvLibPath(app_dir + "/../venv/Lib"); -- QFileInfo venvDLLsPath(app_dir + "/../venv/DLLs"); -- QFileInfo venvSitePackagesPath(app_dir + "/../venv/Lib/site-packages"); -- QFileInfo venvPath(app_dir + "/../venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append paths, if they're not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDLLsPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#else -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../venv/bin"); -- QFileInfo venvLibPath(app_dir + "/../venv/lib/python"); -- QFileInfo venvDynLibPath(app_dir + "/../venv/lib/python/lib-dynload"); -- QFileInfo venvSitePackagesPath(app_dir + "/../venv/lib/python/site-packages"); -- QFileInfo venvPath(app_dir + "/../venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append the path, if it's not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDynLibPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#endif -- -- qputenv("PATH", path_env.toUtf8().data()); -- -- if (python_path.length() > 0) -- { -- // Split the path setting into individual entries -- path_list = python_path.split(";", QString::SkipEmptyParts); -- python_path = QString(); -- -- // Add new additional path elements -- for (i = path_list.size() - 1; i >= 0 ; --i) -- { -- python_path.append(path_list.at(i)); -- if (i > 0) -- { --#if defined(Q_OS_WIN) -- python_path.append(";"); --#else -- python_path.append(":"); --#endif -- } -- } -- qputenv("PYTHONPATH", python_path.toUtf8().data()); -- } -- -- qDebug() << "Python path: " << python_path -- << "\nPython Home: " << pythonHome; -- if (!pythonHome.isEmpty()) -- { -- pythonHome_utf8 = pythonHome.toUtf8(); --#ifdef PYTHON2 -- Py_SetPythonHome(pythonHome_utf8.data()); --#else -- char *python_home = pythonHome_utf8.data(); -- const size_t cSize = strlen(python_home) + 1; -- m_wcPythonHome = new wchar_t[cSize]; -- mbstowcs (m_wcPythonHome, python_home, cSize); -- -- Py_SetPythonHome(m_wcPythonHome); --#endif -- } -+ Py_SetProgramName(m_pyAppName); - - Py_Initialize(); - -+ /* -+ * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -+ * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -+ * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -+ * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -+ */ -+ PySys_SetArgvEx(1, &m_pyAppName, 0); -+ - // Get the current path - PyObject* sysPath = PySys_GetObject((char*)"path"); -- -- // Add new additional path elements -- for (i = path_list.size() - 1; i >= 0 ; --i) -- { -+ PyObject* sysPathRepr = PyObject_Repr(sysPath); - #ifdef PYTHON2 -- PyList_Append(sysPath, PyString_FromString(path_list.at(i).toUtf8().data())); -+ qDebug() << "Python path: " << QString::fromLocal8Bit(PyString_AsString(sysPathRepr)); - #else --#if PY_MINOR_VERSION > 2 -- PyList_Append(sysPath, PyUnicode_DecodeFSDefault(path_list.at(i).toUtf8().data())); --#else -- PyList_Append(sysPath, PyBytes_FromString(path_list.at(i).toUtf8().data())); -+ PyObject* sysPathBytes = PyUnicode_AsEncodedString(sysPathRepr, "utf8", "replace"); -+ qDebug() << "Python path: " << QString::fromUtf8(PyBytes_AsString(sysPathBytes)); -+ Py_DECREF(sysPathBytes); - #endif --#endif -- } -+ Py_DECREF(sysPathRepr); - } - - Server::~Server() - { -- if (m_wcAppName) -- delete m_wcAppName; -- -- if (m_wcPythonHome) -- delete m_wcPythonHome; -- - // Shutdown Python - Py_Finalize(); -+ -+ if (m_pyAppName) -+ delete [] m_pyAppName; - } - - bool Server::Init() -@@ -264,52 +148,29 @@ bool Server::Init() - void Server::run() - { - // Open the application code and run it. -- FILE *cp = fopen(m_appfile.toUtf8().data(), "r"); -+ QByteArray m_appfile_utf8 = m_appfile.toUtf8(); -+ FILE *cp = fopen(m_appfile_utf8.data(), "r"); - if (!cp) - { - setError(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile)); - return; - } - - // Set the port number - PyRun_SimpleString(QString("PGADMIN_PORT = %1").arg(m_port).toLatin1()); - PyRun_SimpleString(QString("PGADMIN_KEY = '%1'").arg(m_key).toLatin1()); - - // Run the app! -- QByteArray m_appfile_utf8 = m_appfile.toUtf8(); - #ifdef PYTHON2 -- /* -- * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -- * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -- * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -- * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -- */ -- char* n_argv[] = { m_appfile_utf8.data() }; -- PySys_SetArgv(1, n_argv); -- - PyObject* PyFileObject = PyFile_FromString(m_appfile_utf8.data(), (char *)"r"); - int ret = PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile_utf8.data()); -- if (ret != 0) -- setError(tr("Failed to launch the application server, server thread exiting.")); - #else -- /* -- * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -- * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -- * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -- * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -- */ -- char *appName = m_appfile_utf8.data(); -- const size_t cSize = strlen(appName)+1; -- wchar_t* wcAppName = new wchar_t[cSize]; -- mbstowcs (wcAppName, appName, cSize); -- wchar_t* n_argv[] = { wcAppName }; -- PySys_SetArgv(1, n_argv); -- - int fd = fileno(cp); - PyObject* PyFileObject = PyFile_FromFd(fd, m_appfile_utf8.data(), (char *)"r", -1, NULL, NULL,NULL,1); -- if (PyRun_SimpleFile(fdopen(PyObject_AsFileDescriptor(PyFileObject),"r"), m_appfile_utf8.data()) != 0) -- setError(tr("Failed to launch the application server, server thread exiting.")); -+ int ret = PyRun_SimpleFile(fdopen(PyObject_AsFileDescriptor(PyFileObject),"r"), m_appfile_utf8.data()); - #endif -+ if (ret != 0) -+ setError(tr("Failed to launch the application server, server thread exiting.")); - - fclose(cp); - } -diff --git a/runtime/Server.h b/runtime/Server.h -index 488f9fcedc6d7dc7..207e2f157db7d7a5 100644 ---- a/runtime/Server.h -+++ b/runtime/Server.h -@@ -40,12 +40,11 @@ private: - - quint16 m_port; - QString m_key; -- // Application name in UTF-8 for Python -- wchar_t *m_wcAppName; -- QByteArray PGA_APP_NAME_UTF8; -- // PythonHome for Python -- wchar_t *m_wcPythonHome; -- QByteArray pythonHome_utf8; -+#ifdef PYTHON2 -+ char *m_pyAppName; -+#else -+ wchar_t *m_pyAppName; -+#endif - }; - - #endif // SERVER_H -diff --git a/runtime/pgAdmin4.cpp b/runtime/pgAdmin4.cpp -index a2dbe7451557befc..ebcb4ff4d4899aaf 100644 ---- a/runtime/pgAdmin4.cpp -+++ b/runtime/pgAdmin4.cpp -@@ -228,69 +228,65 @@ int main(int argc, char * argv[]) - // Fire up the webserver - Server *server; - -- bool done = false; -+ server = new Server(port, key); - -- while (done != true) -+ if (!server->Init()) - { -- server = new Server(port, key); -+ splash->finish(NULL); - -- if (!server->Init()) -+ qDebug() << server->getError(); -+ -+ QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -+ QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -+ -+ exit(1); -+ } -+ -+ server->start(); -+ -+ // This is a hack. Wait a second and then check to see if the server thread -+ // is still running. If it's not, we probably had a startup error -+ delay(1000); -+ -+ // Any errors? -+ if (server->isFinished() || server->getError().length() > 0) -+ { -+ splash->finish(NULL); -+ -+ qDebug() << server->getError(); -+ -+ QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -+ QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -+ -+ // Allow the user to tweak the Python Path if needed -+ QSettings settings; -+ bool ok; -+ -+ ConfigWindow *dlg = new ConfigWindow(); -+ dlg->setWindowTitle(QWidget::tr("Configuration")); -+ dlg->setPythonExecutable(settings.value("PythonExecutable").toString()); -+ dlg->setApplicationPath(settings.value("ApplicationPath").toString()); -+ dlg->setModal(true); -+ ok = dlg->exec(); -+ -+ QString pythonexecutable = dlg->getPythonExecutable(); -+ QString applicationpath = dlg->getApplicationPath(); -+ -+ if (ok) - { -- splash->finish(NULL); -+ settings.setValue("PythonExecutable", pythonexecutable); -+ settings.setValue("ApplicationPath", applicationpath); -+ settings.sync(); - -- qDebug() << server->getError(); -+ QString msg = QString(QWidget::tr("Please restart the application to allow the changes to take effect.")); -+ QMessageBox::information(NULL, QString(QWidget::tr("Restart")), msg); - -- QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -- QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -- -- exit(1); -- } -- -- server->start(); -- -- // This is a hack. Wait a second and then check to see if the server thread -- // is still running. If it's not, we probably had a startup error -- delay(1000); -- -- // Any errors? -- if (server->isFinished() || server->getError().length() > 0) -- { -- splash->finish(NULL); -- -- qDebug() << server->getError(); -- -- QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -- QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -- -- // Allow the user to tweak the Python Path if needed -- QSettings settings; -- bool ok; -- -- ConfigWindow *dlg = new ConfigWindow(); -- dlg->setWindowTitle(QWidget::tr("Configuration")); -- dlg->setPythonPath(settings.value("PythonPath").toString()); -- dlg->setApplicationPath(settings.value("ApplicationPath").toString()); -- dlg->setModal(true); -- ok = dlg->exec(); -- -- QString pythonpath = dlg->getPythonPath(); -- QString applicationpath = dlg->getApplicationPath(); -- -- if (ok) -- { -- settings.setValue("PythonPath", pythonpath); -- settings.setValue("ApplicationPath", applicationpath); -- settings.sync(); -- } -- else -- { -- exit(1); -- } -- -- delete server; -+ exit(0); - } - else -- done = true; -+ { -+ exit(1); -+ } - } - - --- -2.13.0 - Deleted: community-i686/PKGBUILD =================================================================== --- community-i686/PKGBUILD 2017-07-14 16:43:51 UTC (rev 245110) +++ community-i686/PKGBUILD 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,86 +0,0 @@ -# $Id$ -# Maintainer: Jerome Leclanche <jer...@leclan.ch> - -pkgname=pgadmin4 -pkgver=1.5 -pkgrel=3 -pkgdesc="Comprehensive design and management interface for PostgreSQL" -arch=("i686" "x86_64") -url="https://www.pgadmin.org/" -license=("custom") -depends=("qt5-webengine" "python" "postgresql-libs" "hicolor-icon-theme") -makedepends=("python-setuptools" "imagemagick") -source=( - "https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v$pkgver/source/$pkgname-$pkgver.tar.gz"{,.sig} - "pgAdmin4.desktop" - "config_distro.py" - "config_local.py" - # https://www.postgresql.org/message-id/CAMQ-g0dHUN2SFioNg93hCTJkRR3LsVZ-ZvhUh6in%2BO4UAU2Qow%40mail.gmail.com - "0001-Simplify-Server-s-python-setup.patch" -) -sha256sums=( - "a814d926e9af046ff8ebaf968ec37da8f26ba012de2bff655630703ed2c6bf5c" - "SKIP" - "9a39a321fa5a7fcf4f7f7abf6872de6de537800212e9f6f4902ed37b806765b7" - "510c11bf26fa335548ec4c582bc9f5a5242b8e03f3cb0b7c19e6bfd12b6d663c" - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - "f6a3268c0a4b88d6c06d6c92e307dece1f2e3d2ab9d80339395f525cbf23c93e" -) -validpgpkeys=( - "E0C4CEEB826B1FDA4FB468E024ADFAAF698F1519" -) - - -prepare() { - cd "$pkgname-$pkgver" - patch -Np1 -i "../0001-Simplify-Server-s-python-setup.patch" - convert runtime/pgAdmin4.{ico,png} - - /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv" - "$srcdir/$pkgname-$pkgver/venv/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" - - /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv-docs" - "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" - "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install sphinx -} - - -build() { - export LANG=en_US.UTF-8 - export LC_ALL=en_US.UTF-8 - - cd "$pkgname-$pkgver/runtime" - qmake CONFIG+=release - make - - PATH="$srcdir/$pkgname-$pkgver/venv-docs/bin:$PATH" make -C "$srcdir/$pkgname-$pkgver" docs -} - - -package() { - cd "$pkgname-$pkgver" - - install -D runtime/pgAdmin4 "$pkgdir/usr/lib/pgadmin4/runtime/pgAdmin4" - - cp -a docs web "$pkgdir/usr/lib/pgadmin4" - cp -a venv "$pkgdir/usr/lib/pgadmin4/venv" - - install -Dm644 "$srcdir/config_distro.py" "$pkgdir/usr/lib/pgadmin4/web/config_distro.py" - install -Dm644 "$srcdir/config_local.py" "$pkgdir/usr/lib/pgadmin4/web/config_local.py" - - ### Launcher - - install -Dm644 runtime/pgAdmin4-0.png "$pkgdir/usr/share/icons/hicolor/256x256/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-1.png "$pkgdir/usr/share/icons/hicolor/48x48/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-2.png "$pkgdir/usr/share/icons/hicolor/32x32/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-3.png "$pkgdir/usr/share/icons/hicolor/16x16/apps/pgAdmin4.png" - - install -Dm644 "$srcdir/pgAdmin4.desktop" "$pkgdir/usr/share/applications/pgAdmin4.desktop" - - install -D /dev/stdin "$pkgdir/usr/bin/pgadmin4" <<END -#!/bin/sh -exec /usr/lib/pgadmin4/runtime/pgAdmin4 "\$@" -END - - install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" -} Copied: pgadmin4/repos/community-i686/PKGBUILD (from rev 245110, pgadmin4/trunk/PKGBUILD) =================================================================== --- community-i686/PKGBUILD (rev 0) +++ community-i686/PKGBUILD 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,83 @@ +# $Id$ +# Maintainer: Jerome Leclanche <jer...@leclan.ch> + +pkgname=pgadmin4 +pkgver=1.6 +pkgrel=1 +pkgdesc="Comprehensive design and management interface for PostgreSQL" +arch=("i686" "x86_64") +url="https://www.pgadmin.org/" +license=("custom") +depends=("qt5-webengine" "python" "postgresql-libs" "hicolor-icon-theme") +makedepends=("python-setuptools" "imagemagick") +source=( + "https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v$pkgver/source/$pkgname-$pkgver.tar.gz"{,.sig} + "pgAdmin4.desktop" + "config_distro.py" + "config_local.py" + # https://www.postgresql.org/message-id/CAMQ-g0dHUN2SFioNg93hCTJkRR3LsVZ-ZvhUh6in%2BO4UAU2Qow%40mail.gmail.com +) +sha256sums=( + "514c021994c91eb9adb4a42edcec12974b473175289d48a5824b8c0f4e15e0c8" + "SKIP" + "9a39a321fa5a7fcf4f7f7abf6872de6de537800212e9f6f4902ed37b806765b7" + "510c11bf26fa335548ec4c582bc9f5a5242b8e03f3cb0b7c19e6bfd12b6d663c" + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +) +validpgpkeys=( + "E0C4CEEB826B1FDA4FB468E024ADFAAF698F1519" +) + + +prepare() { + cd "$pkgname-$pkgver" + convert runtime/pgAdmin4.{ico,png} + + /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv" + "$srcdir/$pkgname-$pkgver/venv/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" + + /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv-docs" + "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" + "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install sphinx +} + + +build() { + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + + cd "$pkgname-$pkgver/runtime" + qmake CONFIG+=release + make + + PATH="$srcdir/$pkgname-$pkgver/venv-docs/bin:$PATH" make -C "$srcdir/$pkgname-$pkgver" docs +} + + +package() { + cd "$pkgname-$pkgver" + + install -D runtime/pgAdmin4 "$pkgdir/usr/lib/pgadmin4/runtime/pgAdmin4" + + cp -a docs web "$pkgdir/usr/lib/pgadmin4" + cp -a venv "$pkgdir/usr/lib/pgadmin4/venv" + + install -Dm644 "$srcdir/config_distro.py" "$pkgdir/usr/lib/pgadmin4/web/config_distro.py" + install -Dm644 "$srcdir/config_local.py" "$pkgdir/usr/lib/pgadmin4/web/config_local.py" + + ### Launcher + + install -Dm644 runtime/pgAdmin4-0.png "$pkgdir/usr/share/icons/hicolor/256x256/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-1.png "$pkgdir/usr/share/icons/hicolor/48x48/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-2.png "$pkgdir/usr/share/icons/hicolor/32x32/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-3.png "$pkgdir/usr/share/icons/hicolor/16x16/apps/pgAdmin4.png" + + install -Dm644 "$srcdir/pgAdmin4.desktop" "$pkgdir/usr/share/applications/pgAdmin4.desktop" + + install -D /dev/stdin "$pkgdir/usr/bin/pgadmin4" <<END +#!/bin/sh +exec /usr/lib/pgadmin4/runtime/pgAdmin4 "\$@" +END + + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} Deleted: community-i686/config_distro.py =================================================================== --- community-i686/config_distro.py 2017-07-14 16:43:51 UTC (rev 245110) +++ community-i686/config_distro.py 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,2 +0,0 @@ -SERVER_MODE = False -HELP_PATH = "/usr/lib/pgadmin4/docs/en_US/_build/html/" Copied: pgadmin4/repos/community-i686/config_distro.py (from rev 245110, pgadmin4/trunk/config_distro.py) =================================================================== --- community-i686/config_distro.py (rev 0) +++ community-i686/config_distro.py 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,2 @@ +SERVER_MODE = False +HELP_PATH = "/usr/lib/pgadmin4/docs/en_US/_build/html/" Deleted: community-i686/config_local.py =================================================================== Copied: pgadmin4/repos/community-i686/config_local.py (from rev 245110, pgadmin4/trunk/config_local.py) =================================================================== Deleted: community-i686/pgAdmin4.desktop =================================================================== --- community-i686/pgAdmin4.desktop 2017-07-14 16:43:51 UTC (rev 245110) +++ community-i686/pgAdmin4.desktop 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,11 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=pgAdmin 4 -Exec=/usr/lib/pgadmin4/runtime/pgAdmin4 -Icon=pgAdmin4 -Type=Application -Categories=Application;Development; -MimeType=text/html -DocPath=/usr/lib/pgadmin4/docs/en_US/_build/html/index.html -Comment=PostgreSQL Tools -Keywords=database;db;sql;query;administration;development; Copied: pgadmin4/repos/community-i686/pgAdmin4.desktop (from rev 245110, pgadmin4/trunk/pgAdmin4.desktop) =================================================================== --- community-i686/pgAdmin4.desktop (rev 0) +++ community-i686/pgAdmin4.desktop 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=pgAdmin 4 +Exec=/usr/lib/pgadmin4/runtime/pgAdmin4 +Icon=pgAdmin4 +Type=Application +Categories=Application;Development; +MimeType=text/html +DocPath=/usr/lib/pgadmin4/docs/en_US/_build/html/index.html +Comment=PostgreSQL Tools +Keywords=database;db;sql;query;administration;development; Deleted: community-x86_64/0001-Simplify-Server-s-python-setup.patch =================================================================== --- community-x86_64/0001-Simplify-Server-s-python-setup.patch 2017-07-14 16:43:51 UTC (rev 245110) +++ community-x86_64/0001-Simplify-Server-s-python-setup.patch 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,633 +0,0 @@ -From a5a1c332e23a094e0c2a1c9513fb9f6b05cbbf05 Mon Sep 17 00:00:00 2001 -From: "Jan Alexander Steffens (heftig)" <jan.steff...@gmail.com> -Date: Sat, 28 Jan 2017 01:19:06 +0100 -Subject: [PATCH] Simplify Server's python setup ---- - runtime/BrowserWindow.cpp | 6 +- - runtime/ConfigWindow.cpp | 16 ++- - runtime/ConfigWindow.h | 6 +- - runtime/ConfigWindow.ui | 22 +++- - runtime/Server.cpp | 253 +++++++++++----------------------------------- - runtime/Server.h | 11 +- - runtime/pgAdmin4.cpp | 106 ++++++++++--------- - 7 files changed, 149 insertions(+), 271 deletions(-) - -diff --git a/runtime/BrowserWindow.cpp b/runtime/BrowserWindow.cpp -index 516b8689818bddc9..a2ee4895f5351565 100644 ---- a/runtime/BrowserWindow.cpp -+++ b/runtime/BrowserWindow.cpp -@@ -1286,17 +1286,17 @@ void BrowserWindow::preferences() - - ConfigWindow *dlg = new ConfigWindow(); - dlg->setWindowTitle(QWidget::tr("Configuration")); -- dlg->setPythonPath(settings.value("PythonPath").toString()); -+ dlg->setPythonExecutable(settings.value("PythonExecutable").toString()); - dlg->setApplicationPath(settings.value("ApplicationPath").toString()); - dlg->setModal(true); - ok = dlg->exec(); - -- QString pythonpath = dlg->getPythonPath(); -+ QString pythonexecutable = dlg->getPythonExecutable(); - QString applicationpath = dlg->getApplicationPath(); - - if (ok) - { -- settings.setValue("PythonPath", pythonpath); -+ settings.setValue("PythonExecutable", pythonexecutable); - settings.setValue("ApplicationPath", applicationpath); - } - } -diff --git a/runtime/ConfigWindow.cpp b/runtime/ConfigWindow.cpp -index 3fb1a2738eb89ec0..c31345bf08d06d88 100644 ---- a/runtime/ConfigWindow.cpp -+++ b/runtime/ConfigWindow.cpp -@@ -17,37 +17,45 @@ ConfigWindow::ConfigWindow(QWidget *parent) : - ui(new Ui::ConfigWindow) - { - ui->setupUi(this); -+ -+#ifdef PYTHON2 -+ ui->pythonExecutableLineEdit->setPlaceholderText( -+ QString(QWidget::tr("The Python 2 executable to use (may be within a virtual environment)"))); -+#else -+ ui->pythonExecutableLineEdit->setPlaceholderText( -+ QString(QWidget::tr("The Python 3 executable to use (may be within a virtual environment)"))); -+#endif - } - - ConfigWindow::~ConfigWindow() - { - delete ui; - } - - void ConfigWindow::on_buttonBox_accepted() - { - this->close(); - } - - void ConfigWindow::on_buttonBox_rejected() - { - this->close(); - } - --QString ConfigWindow::getPythonPath() -+QString ConfigWindow::getPythonExecutable() - { -- return ui->pythonPathLineEdit->text(); -+ return ui->pythonExecutableLineEdit->text(); - } - - QString ConfigWindow::getApplicationPath() - { - return ui->applicationPathLineEdit->text(); - } - - --void ConfigWindow::setPythonPath(QString path) -+void ConfigWindow::setPythonExecutable(QString path) - { -- ui->pythonPathLineEdit->setText(path); -+ ui->pythonExecutableLineEdit->setText(path); - } - - void ConfigWindow::setApplicationPath(QString path) -diff --git a/runtime/ConfigWindow.h b/runtime/ConfigWindow.h -index 0027a742aca7b762..c4487fcc6e97f354 100644 ---- a/runtime/ConfigWindow.h -+++ b/runtime/ConfigWindow.h -@@ -26,19 +26,19 @@ public: - explicit ConfigWindow(QWidget *parent = 0); - ~ConfigWindow(); - -- QString getPythonPath(); -+ QString getPythonExecutable(); - QString getApplicationPath(); - -- void setPythonPath(QString path); -+ void setPythonExecutable(QString path); - void setApplicationPath(QString path); - - private slots: - void on_buttonBox_accepted(); - void on_buttonBox_rejected(); - - private: - Ui::ConfigWindow *ui; -- QString m_pythonpath, m_applicationpath; -+ QString m_pythonexecutable, m_applicationpath; - }; - - #endif // CONFIGWINDOW_H -diff --git a/runtime/ConfigWindow.ui b/runtime/ConfigWindow.ui -index 40d87be43803204a..8b379052a1da8cd0 100644 ---- a/runtime/ConfigWindow.ui -+++ b/runtime/ConfigWindow.ui -@@ -29,24 +29,38 @@ - <enum>QFormLayout::ExpandingFieldsGrow</enum> - </property> - <item row="0" column="0"> -- <widget class="QLabel" name="pythonPathLabel"> -+ <widget class="QLabel" name="pythonExecutableLabel"> -+ <property name="sizeIncrement"> -+ <size> -+ <width>0</width> -+ <height>1</height> -+ </size> -+ </property> - <property name="text"> -- <string>Python Path</string> -+ <string>Python Executable</string> - </property> - </widget> - </item> - <item row="0" column="1"> -- <widget class="QLineEdit" name="pythonPathLineEdit"/> -+ <widget class="QLineEdit" name="pythonExecutableLineEdit"> -+ <property name="placeholderText"> -+ <string>The python executable to use (may be within a virtual environment)</string> -+ </property> -+ </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="applicationPathLabel"> - <property name="text"> - <string>Application Path</string> - </property> - </widget> - </item> - <item row="1" column="1"> -- <widget class="QLineEdit" name="applicationPathLineEdit"/> -+ <widget class="QLineEdit" name="applicationPathLineEdit"> -+ <property name="placeholderText"> -+ <string>The directory containing pgAdmin4.py (do not include the file name)</string> -+ </property> -+ </widget> - </item> - </layout> - </item> -diff --git a/runtime/Server.cpp b/runtime/Server.cpp -index 33b43583480440b7..83bcdeedb09197c8 100644 ---- a/runtime/Server.cpp -+++ b/runtime/Server.cpp -@@ -22,201 +22,85 @@ - // App headers - #include "Server.h" - --static void add_to_path(QString &python_path, QString path, bool prepend=false) --{ -- if (!python_path.contains(path)) -- { -- if (!prepend) -- { --#if defined(Q_OS_WIN) -- if (!python_path.isEmpty() && !python_path.endsWith(";")) -- python_path.append(";"); --#else -- if (!python_path.isEmpty() && !python_path.endsWith(":")) -- python_path.append(":"); --#endif -- -- python_path.append(path); -- } -- else -- { --#if defined(Q_OS_WIN) -- if (!python_path.isEmpty() && !python_path.startsWith(";")) -- python_path.prepend(";"); --#else -- if (!python_path.isEmpty() && !python_path.startsWith(":")) -- python_path.prepend(":"); --#endif -- -- python_path.prepend(path); -- } -- } --} -- - Server::Server(quint16 port, QString key) - { - // Appserver port etc - m_port = port; - m_key = key; -- m_wcAppName = NULL; -- m_wcPythonHome = NULL; - - // Initialise Python -- Py_NoSiteFlag=1; -- Py_DontWriteBytecodeFlag=1; -+ Py_DontWriteBytecodeFlag = 1; - -- PGA_APP_NAME_UTF8 = PGA_APP_NAME.toUtf8(); -+ // Get the application directory -+ QString appDir = qApp->applicationDirPath(); -+ -+ // Build (and canonicalise) the virtual environment path -+#ifdef Q_OS_MAC -+ QFileInfo venvBinPath(appDir + "/../Resources/venv/bin"); -+#elif defined(Q_OS_WIN) -+ QFileInfo venvBinPath(appDir + "/../venv"); -+#else -+ QFileInfo venvBinPath(appDir + "/../venv/bin"); -+#endif -+ QString binPath = venvBinPath.canonicalFilePath(); -+ -+ if (binPath.isEmpty()) -+ { -+ // Use default Python environment from the config, or lastly, PATH -+ QSettings settings; -+ binPath.append(settings.value("PythonExecutable").toString()); -+ if (binPath.isEmpty()) -+ binPath.append("python"); -+ } -+ else -+ { -+ binPath.append("/python"); -+ } - - // Python3 requires conversion of char * to wchar_t *, so... - #ifdef PYTHON2 -- Py_SetProgramName(PGA_APP_NAME_UTF8.data()); -+ QByteArray appName = binPath.toLocal8Bit(); -+ m_pyAppName = new char[strlen(appName.data()) + 1]; -+ strcpy(m_pyAppName, appName.data()); -+ qDebug() << "Python interpreter: " << QString::fromLocal8Bit(m_pyAppName); - #else -- char *appName = PGA_APP_NAME_UTF8.data(); -- const size_t cSize = strlen(appName)+1; -- m_wcAppName = new wchar_t[cSize]; -- mbstowcs (m_wcAppName, appName, cSize); -- Py_SetProgramName(m_wcAppName); -+ std::wstring appName = binPath.toStdWString(); -+ m_pyAppName = new wchar_t[wcslen(appName.c_str()) + 1]; -+ wcscpy(m_pyAppName, appName.c_str()); -+ qDebug() << "Python interpreter: " << QString::fromWCharArray(m_pyAppName); - #endif -- -- // Setup the search path -- QSettings settings; -- QString python_path = settings.value("PythonPath").toString(); -- -- // Get the application directory -- QString app_dir = qApp->applicationDirPath(), -- path_env = qgetenv("PATH"), -- pythonHome; -- QStringList path_list; -- int i; -- --#ifdef Q_OS_MAC -- // In the case we're running in a release appbundle, we need to ensure the -- // bundled virtual env is included in the Python path. We include it at the -- // end, so expert users can override the path, but we do not save it, because -- // if users move the app bundle, we'll end up with dead entries -- -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../Resources/venv/bin"); -- QFileInfo venvLibPath(app_dir + "/../Resources/venv/lib/python"); -- QFileInfo venvDynLibPath(app_dir + "/../Resources/venv/lib/python/lib-dynload"); -- QFileInfo venvSitePackagesPath(app_dir + "/../Resources/venv/lib/python/site-packages"); -- QFileInfo venvPath(app_dir + "/../Resources/venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append the path, if it's not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDynLibPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#elif defined(Q_OS_WIN) -- -- // In the case we're running in a release application, we need to ensure the -- // bundled virtual env is included in the Python path. We include it at the -- // end, so expert users can override the path, but we do not save it. -- -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../venv"); -- QFileInfo venvLibPath(app_dir + "/../venv/Lib"); -- QFileInfo venvDLLsPath(app_dir + "/../venv/DLLs"); -- QFileInfo venvSitePackagesPath(app_dir + "/../venv/Lib/site-packages"); -- QFileInfo venvPath(app_dir + "/../venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append paths, if they're not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDLLsPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#else -- // Build (and canonicalise) the virtual environment path -- QFileInfo venvBinPath(app_dir + "/../venv/bin"); -- QFileInfo venvLibPath(app_dir + "/../venv/lib/python"); -- QFileInfo venvDynLibPath(app_dir + "/../venv/lib/python/lib-dynload"); -- QFileInfo venvSitePackagesPath(app_dir + "/../venv/lib/python/site-packages"); -- QFileInfo venvPath(app_dir + "/../venv"); -- -- // Prepend the bin directory to the path -- add_to_path(path_env, venvBinPath.canonicalFilePath(), true); -- // Append the path, if it's not already there -- add_to_path(python_path, venvLibPath.canonicalFilePath()); -- add_to_path(python_path, venvDynLibPath.canonicalFilePath()); -- add_to_path(python_path, venvSitePackagesPath.canonicalFilePath()); -- add_to_path(pythonHome, venvPath.canonicalFilePath()); --#endif -- -- qputenv("PATH", path_env.toUtf8().data()); -- -- if (python_path.length() > 0) -- { -- // Split the path setting into individual entries -- path_list = python_path.split(";", QString::SkipEmptyParts); -- python_path = QString(); -- -- // Add new additional path elements -- for (i = path_list.size() - 1; i >= 0 ; --i) -- { -- python_path.append(path_list.at(i)); -- if (i > 0) -- { --#if defined(Q_OS_WIN) -- python_path.append(";"); --#else -- python_path.append(":"); --#endif -- } -- } -- qputenv("PYTHONPATH", python_path.toUtf8().data()); -- } -- -- qDebug() << "Python path: " << python_path -- << "\nPython Home: " << pythonHome; -- if (!pythonHome.isEmpty()) -- { -- pythonHome_utf8 = pythonHome.toUtf8(); --#ifdef PYTHON2 -- Py_SetPythonHome(pythonHome_utf8.data()); --#else -- char *python_home = pythonHome_utf8.data(); -- const size_t cSize = strlen(python_home) + 1; -- m_wcPythonHome = new wchar_t[cSize]; -- mbstowcs (m_wcPythonHome, python_home, cSize); -- -- Py_SetPythonHome(m_wcPythonHome); --#endif -- } -+ Py_SetProgramName(m_pyAppName); - - Py_Initialize(); - -+ /* -+ * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -+ * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -+ * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -+ * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -+ */ -+ PySys_SetArgvEx(1, &m_pyAppName, 0); -+ - // Get the current path - PyObject* sysPath = PySys_GetObject((char*)"path"); -- -- // Add new additional path elements -- for (i = path_list.size() - 1; i >= 0 ; --i) -- { -+ PyObject* sysPathRepr = PyObject_Repr(sysPath); - #ifdef PYTHON2 -- PyList_Append(sysPath, PyString_FromString(path_list.at(i).toUtf8().data())); -+ qDebug() << "Python path: " << QString::fromLocal8Bit(PyString_AsString(sysPathRepr)); - #else --#if PY_MINOR_VERSION > 2 -- PyList_Append(sysPath, PyUnicode_DecodeFSDefault(path_list.at(i).toUtf8().data())); --#else -- PyList_Append(sysPath, PyBytes_FromString(path_list.at(i).toUtf8().data())); -+ PyObject* sysPathBytes = PyUnicode_AsEncodedString(sysPathRepr, "utf8", "replace"); -+ qDebug() << "Python path: " << QString::fromUtf8(PyBytes_AsString(sysPathBytes)); -+ Py_DECREF(sysPathBytes); - #endif --#endif -- } -+ Py_DECREF(sysPathRepr); - } - - Server::~Server() - { -- if (m_wcAppName) -- delete m_wcAppName; -- -- if (m_wcPythonHome) -- delete m_wcPythonHome; -- - // Shutdown Python - Py_Finalize(); -+ -+ if (m_pyAppName) -+ delete [] m_pyAppName; - } - - bool Server::Init() -@@ -264,52 +148,29 @@ bool Server::Init() - void Server::run() - { - // Open the application code and run it. -- FILE *cp = fopen(m_appfile.toUtf8().data(), "r"); -+ QByteArray m_appfile_utf8 = m_appfile.toUtf8(); -+ FILE *cp = fopen(m_appfile_utf8.data(), "r"); - if (!cp) - { - setError(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile)); - return; - } - - // Set the port number - PyRun_SimpleString(QString("PGADMIN_PORT = %1").arg(m_port).toLatin1()); - PyRun_SimpleString(QString("PGADMIN_KEY = '%1'").arg(m_key).toLatin1()); - - // Run the app! -- QByteArray m_appfile_utf8 = m_appfile.toUtf8(); - #ifdef PYTHON2 -- /* -- * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -- * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -- * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -- * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -- */ -- char* n_argv[] = { m_appfile_utf8.data() }; -- PySys_SetArgv(1, n_argv); -- - PyObject* PyFileObject = PyFile_FromString(m_appfile_utf8.data(), (char *)"r"); - int ret = PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile_utf8.data()); -- if (ret != 0) -- setError(tr("Failed to launch the application server, server thread exiting.")); - #else -- /* -- * Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later -- * versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator, -- * which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory. -- * Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation. -- */ -- char *appName = m_appfile_utf8.data(); -- const size_t cSize = strlen(appName)+1; -- wchar_t* wcAppName = new wchar_t[cSize]; -- mbstowcs (wcAppName, appName, cSize); -- wchar_t* n_argv[] = { wcAppName }; -- PySys_SetArgv(1, n_argv); -- - int fd = fileno(cp); - PyObject* PyFileObject = PyFile_FromFd(fd, m_appfile_utf8.data(), (char *)"r", -1, NULL, NULL,NULL,1); -- if (PyRun_SimpleFile(fdopen(PyObject_AsFileDescriptor(PyFileObject),"r"), m_appfile_utf8.data()) != 0) -- setError(tr("Failed to launch the application server, server thread exiting.")); -+ int ret = PyRun_SimpleFile(fdopen(PyObject_AsFileDescriptor(PyFileObject),"r"), m_appfile_utf8.data()); - #endif -+ if (ret != 0) -+ setError(tr("Failed to launch the application server, server thread exiting.")); - - fclose(cp); - } -diff --git a/runtime/Server.h b/runtime/Server.h -index 488f9fcedc6d7dc7..207e2f157db7d7a5 100644 ---- a/runtime/Server.h -+++ b/runtime/Server.h -@@ -40,12 +40,11 @@ private: - - quint16 m_port; - QString m_key; -- // Application name in UTF-8 for Python -- wchar_t *m_wcAppName; -- QByteArray PGA_APP_NAME_UTF8; -- // PythonHome for Python -- wchar_t *m_wcPythonHome; -- QByteArray pythonHome_utf8; -+#ifdef PYTHON2 -+ char *m_pyAppName; -+#else -+ wchar_t *m_pyAppName; -+#endif - }; - - #endif // SERVER_H -diff --git a/runtime/pgAdmin4.cpp b/runtime/pgAdmin4.cpp -index a2dbe7451557befc..ebcb4ff4d4899aaf 100644 ---- a/runtime/pgAdmin4.cpp -+++ b/runtime/pgAdmin4.cpp -@@ -228,69 +228,65 @@ int main(int argc, char * argv[]) - // Fire up the webserver - Server *server; - -- bool done = false; -+ server = new Server(port, key); - -- while (done != true) -+ if (!server->Init()) - { -- server = new Server(port, key); -+ splash->finish(NULL); - -- if (!server->Init()) -+ qDebug() << server->getError(); -+ -+ QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -+ QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -+ -+ exit(1); -+ } -+ -+ server->start(); -+ -+ // This is a hack. Wait a second and then check to see if the server thread -+ // is still running. If it's not, we probably had a startup error -+ delay(1000); -+ -+ // Any errors? -+ if (server->isFinished() || server->getError().length() > 0) -+ { -+ splash->finish(NULL); -+ -+ qDebug() << server->getError(); -+ -+ QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -+ QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -+ -+ // Allow the user to tweak the Python Path if needed -+ QSettings settings; -+ bool ok; -+ -+ ConfigWindow *dlg = new ConfigWindow(); -+ dlg->setWindowTitle(QWidget::tr("Configuration")); -+ dlg->setPythonExecutable(settings.value("PythonExecutable").toString()); -+ dlg->setApplicationPath(settings.value("ApplicationPath").toString()); -+ dlg->setModal(true); -+ ok = dlg->exec(); -+ -+ QString pythonexecutable = dlg->getPythonExecutable(); -+ QString applicationpath = dlg->getApplicationPath(); -+ -+ if (ok) - { -- splash->finish(NULL); -+ settings.setValue("PythonExecutable", pythonexecutable); -+ settings.setValue("ApplicationPath", applicationpath); -+ settings.sync(); - -- qDebug() << server->getError(); -+ QString msg = QString(QWidget::tr("Please restart the application to allow the changes to take effect.")); -+ QMessageBox::information(NULL, QString(QWidget::tr("Restart")), msg); - -- QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -- QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -- -- exit(1); -- } -- -- server->start(); -- -- // This is a hack. Wait a second and then check to see if the server thread -- // is still running. If it's not, we probably had a startup error -- delay(1000); -- -- // Any errors? -- if (server->isFinished() || server->getError().length() > 0) -- { -- splash->finish(NULL); -- -- qDebug() << server->getError(); -- -- QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError()); -- QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error); -- -- // Allow the user to tweak the Python Path if needed -- QSettings settings; -- bool ok; -- -- ConfigWindow *dlg = new ConfigWindow(); -- dlg->setWindowTitle(QWidget::tr("Configuration")); -- dlg->setPythonPath(settings.value("PythonPath").toString()); -- dlg->setApplicationPath(settings.value("ApplicationPath").toString()); -- dlg->setModal(true); -- ok = dlg->exec(); -- -- QString pythonpath = dlg->getPythonPath(); -- QString applicationpath = dlg->getApplicationPath(); -- -- if (ok) -- { -- settings.setValue("PythonPath", pythonpath); -- settings.setValue("ApplicationPath", applicationpath); -- settings.sync(); -- } -- else -- { -- exit(1); -- } -- -- delete server; -+ exit(0); - } - else -- done = true; -+ { -+ exit(1); -+ } - } - - --- -2.13.0 - Deleted: community-x86_64/PKGBUILD =================================================================== --- community-x86_64/PKGBUILD 2017-07-14 16:43:51 UTC (rev 245110) +++ community-x86_64/PKGBUILD 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,86 +0,0 @@ -# $Id$ -# Maintainer: Jerome Leclanche <jer...@leclan.ch> - -pkgname=pgadmin4 -pkgver=1.5 -pkgrel=3 -pkgdesc="Comprehensive design and management interface for PostgreSQL" -arch=("i686" "x86_64") -url="https://www.pgadmin.org/" -license=("custom") -depends=("qt5-webengine" "python" "postgresql-libs" "hicolor-icon-theme") -makedepends=("python-setuptools" "imagemagick") -source=( - "https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v$pkgver/source/$pkgname-$pkgver.tar.gz"{,.sig} - "pgAdmin4.desktop" - "config_distro.py" - "config_local.py" - # https://www.postgresql.org/message-id/CAMQ-g0dHUN2SFioNg93hCTJkRR3LsVZ-ZvhUh6in%2BO4UAU2Qow%40mail.gmail.com - "0001-Simplify-Server-s-python-setup.patch" -) -sha256sums=( - "a814d926e9af046ff8ebaf968ec37da8f26ba012de2bff655630703ed2c6bf5c" - "SKIP" - "9a39a321fa5a7fcf4f7f7abf6872de6de537800212e9f6f4902ed37b806765b7" - "510c11bf26fa335548ec4c582bc9f5a5242b8e03f3cb0b7c19e6bfd12b6d663c" - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - "f6a3268c0a4b88d6c06d6c92e307dece1f2e3d2ab9d80339395f525cbf23c93e" -) -validpgpkeys=( - "E0C4CEEB826B1FDA4FB468E024ADFAAF698F1519" -) - - -prepare() { - cd "$pkgname-$pkgver" - patch -Np1 -i "../0001-Simplify-Server-s-python-setup.patch" - convert runtime/pgAdmin4.{ico,png} - - /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv" - "$srcdir/$pkgname-$pkgver/venv/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" - - /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv-docs" - "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" - "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install sphinx -} - - -build() { - export LANG=en_US.UTF-8 - export LC_ALL=en_US.UTF-8 - - cd "$pkgname-$pkgver/runtime" - qmake CONFIG+=release - make - - PATH="$srcdir/$pkgname-$pkgver/venv-docs/bin:$PATH" make -C "$srcdir/$pkgname-$pkgver" docs -} - - -package() { - cd "$pkgname-$pkgver" - - install -D runtime/pgAdmin4 "$pkgdir/usr/lib/pgadmin4/runtime/pgAdmin4" - - cp -a docs web "$pkgdir/usr/lib/pgadmin4" - cp -a venv "$pkgdir/usr/lib/pgadmin4/venv" - - install -Dm644 "$srcdir/config_distro.py" "$pkgdir/usr/lib/pgadmin4/web/config_distro.py" - install -Dm644 "$srcdir/config_local.py" "$pkgdir/usr/lib/pgadmin4/web/config_local.py" - - ### Launcher - - install -Dm644 runtime/pgAdmin4-0.png "$pkgdir/usr/share/icons/hicolor/256x256/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-1.png "$pkgdir/usr/share/icons/hicolor/48x48/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-2.png "$pkgdir/usr/share/icons/hicolor/32x32/apps/pgAdmin4.png" - install -Dm644 runtime/pgAdmin4-3.png "$pkgdir/usr/share/icons/hicolor/16x16/apps/pgAdmin4.png" - - install -Dm644 "$srcdir/pgAdmin4.desktop" "$pkgdir/usr/share/applications/pgAdmin4.desktop" - - install -D /dev/stdin "$pkgdir/usr/bin/pgadmin4" <<END -#!/bin/sh -exec /usr/lib/pgadmin4/runtime/pgAdmin4 "\$@" -END - - install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" -} Copied: pgadmin4/repos/community-x86_64/PKGBUILD (from rev 245110, pgadmin4/trunk/PKGBUILD) =================================================================== --- community-x86_64/PKGBUILD (rev 0) +++ community-x86_64/PKGBUILD 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,83 @@ +# $Id$ +# Maintainer: Jerome Leclanche <jer...@leclan.ch> + +pkgname=pgadmin4 +pkgver=1.6 +pkgrel=1 +pkgdesc="Comprehensive design and management interface for PostgreSQL" +arch=("i686" "x86_64") +url="https://www.pgadmin.org/" +license=("custom") +depends=("qt5-webengine" "python" "postgresql-libs" "hicolor-icon-theme") +makedepends=("python-setuptools" "imagemagick") +source=( + "https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v$pkgver/source/$pkgname-$pkgver.tar.gz"{,.sig} + "pgAdmin4.desktop" + "config_distro.py" + "config_local.py" + # https://www.postgresql.org/message-id/CAMQ-g0dHUN2SFioNg93hCTJkRR3LsVZ-ZvhUh6in%2BO4UAU2Qow%40mail.gmail.com +) +sha256sums=( + "514c021994c91eb9adb4a42edcec12974b473175289d48a5824b8c0f4e15e0c8" + "SKIP" + "9a39a321fa5a7fcf4f7f7abf6872de6de537800212e9f6f4902ed37b806765b7" + "510c11bf26fa335548ec4c582bc9f5a5242b8e03f3cb0b7c19e6bfd12b6d663c" + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +) +validpgpkeys=( + "E0C4CEEB826B1FDA4FB468E024ADFAAF698F1519" +) + + +prepare() { + cd "$pkgname-$pkgver" + convert runtime/pgAdmin4.{ico,png} + + /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv" + "$srcdir/$pkgname-$pkgver/venv/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" + + /usr/bin/python -m venv "$srcdir/$pkgname-$pkgver/venv-docs" + "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install -r "$srcdir/$pkgname-$pkgver/requirements.txt" + "$srcdir/$pkgname-$pkgver/venv-docs/bin/python" -m pip install sphinx +} + + +build() { + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + + cd "$pkgname-$pkgver/runtime" + qmake CONFIG+=release + make + + PATH="$srcdir/$pkgname-$pkgver/venv-docs/bin:$PATH" make -C "$srcdir/$pkgname-$pkgver" docs +} + + +package() { + cd "$pkgname-$pkgver" + + install -D runtime/pgAdmin4 "$pkgdir/usr/lib/pgadmin4/runtime/pgAdmin4" + + cp -a docs web "$pkgdir/usr/lib/pgadmin4" + cp -a venv "$pkgdir/usr/lib/pgadmin4/venv" + + install -Dm644 "$srcdir/config_distro.py" "$pkgdir/usr/lib/pgadmin4/web/config_distro.py" + install -Dm644 "$srcdir/config_local.py" "$pkgdir/usr/lib/pgadmin4/web/config_local.py" + + ### Launcher + + install -Dm644 runtime/pgAdmin4-0.png "$pkgdir/usr/share/icons/hicolor/256x256/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-1.png "$pkgdir/usr/share/icons/hicolor/48x48/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-2.png "$pkgdir/usr/share/icons/hicolor/32x32/apps/pgAdmin4.png" + install -Dm644 runtime/pgAdmin4-3.png "$pkgdir/usr/share/icons/hicolor/16x16/apps/pgAdmin4.png" + + install -Dm644 "$srcdir/pgAdmin4.desktop" "$pkgdir/usr/share/applications/pgAdmin4.desktop" + + install -D /dev/stdin "$pkgdir/usr/bin/pgadmin4" <<END +#!/bin/sh +exec /usr/lib/pgadmin4/runtime/pgAdmin4 "\$@" +END + + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} Deleted: community-x86_64/config_distro.py =================================================================== --- community-x86_64/config_distro.py 2017-07-14 16:43:51 UTC (rev 245110) +++ community-x86_64/config_distro.py 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,2 +0,0 @@ -SERVER_MODE = False -HELP_PATH = "/usr/lib/pgadmin4/docs/en_US/_build/html/" Copied: pgadmin4/repos/community-x86_64/config_distro.py (from rev 245110, pgadmin4/trunk/config_distro.py) =================================================================== --- community-x86_64/config_distro.py (rev 0) +++ community-x86_64/config_distro.py 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,2 @@ +SERVER_MODE = False +HELP_PATH = "/usr/lib/pgadmin4/docs/en_US/_build/html/" Deleted: community-x86_64/config_local.py =================================================================== Copied: pgadmin4/repos/community-x86_64/config_local.py (from rev 245110, pgadmin4/trunk/config_local.py) =================================================================== Deleted: community-x86_64/pgAdmin4.desktop =================================================================== --- community-x86_64/pgAdmin4.desktop 2017-07-14 16:43:51 UTC (rev 245110) +++ community-x86_64/pgAdmin4.desktop 2017-07-14 16:44:00 UTC (rev 245111) @@ -1,11 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=pgAdmin 4 -Exec=/usr/lib/pgadmin4/runtime/pgAdmin4 -Icon=pgAdmin4 -Type=Application -Categories=Application;Development; -MimeType=text/html -DocPath=/usr/lib/pgadmin4/docs/en_US/_build/html/index.html -Comment=PostgreSQL Tools -Keywords=database;db;sql;query;administration;development; Copied: pgadmin4/repos/community-x86_64/pgAdmin4.desktop (from rev 245110, pgadmin4/trunk/pgAdmin4.desktop) =================================================================== --- community-x86_64/pgAdmin4.desktop (rev 0) +++ community-x86_64/pgAdmin4.desktop 2017-07-14 16:44:00 UTC (rev 245111) @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=pgAdmin 4 +Exec=/usr/lib/pgadmin4/runtime/pgAdmin4 +Icon=pgAdmin4 +Type=Application +Categories=Application;Development; +MimeType=text/html +DocPath=/usr/lib/pgadmin4/docs/en_US/_build/html/index.html +Comment=PostgreSQL Tools +Keywords=database;db;sql;query;administration;development;