2013-06-16 08:17:46 -05:00
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2017-01-04 07:33:32 -06:00
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
2013-06-16 08:17:46 -05:00
// This software is released under the PostgreSQL Licence
//
// pgAdmin4.cpp - Main application entry point
//
//////////////////////////////////////////////////////////////////////////
2013-06-21 17:21:11 -05:00
# include "pgAdmin4.h"
2013-06-16 08:17:46 -05:00
// Must be before QT
# include <Python.h>
# if QT_VERSION >= 0x050000
# include <QtWidgets>
2017-04-03 06:58:08 -05:00
# include <QNetworkProxyFactory>
2017-03-26 20:02:15 -05:00
# include <QNetworkRequest>
# include <QNetworkReply>
2013-06-16 08:17:46 -05:00
# else
# include <QApplication>
2013-06-21 17:32:32 -05:00
# include <QDebug>
2016-01-18 08:33:28 -06:00
# include <QtNetwork>
# include <QLineEdit>
# include <QInputDialog>
2016-09-05 10:45:01 -05:00
# include <QSplashScreen>
2017-03-06 08:53:49 -06:00
# include <QUuid>
2017-03-29 11:17:16 -05:00
# include <QNetworkProxyFactory>
# include <QSslConfiguration>
2013-06-16 08:17:46 -05:00
# endif
2013-06-21 17:21:11 -05:00
// App headers
2013-06-16 08:17:46 -05:00
# include "BrowserWindow.h"
2016-01-19 04:26:01 -06:00
# include "ConfigWindow.h"
2013-06-21 17:21:11 -05:00
# include "Server.h"
2013-06-16 08:17:46 -05:00
2016-02-23 03:10:49 -06:00
# include <QTime>
2017-03-29 11:17:16 -05:00
// Implement support for system proxies for Qt 4.x on Linux
# if defined (Q_OS_LINUX) && QT_VERSION < 0x050000
# include "qnetworkproxy.h"
# include <QtCore/QByteArray>
# include <QtCore/QUrl>
# ifndef QT_NO_NETWORKPROXY
QT_BEGIN_NAMESPACE
static bool ignoreProxyFor ( const QNetworkProxyQuery & query )
{
const QByteArray noProxy = qgetenv ( " no_proxy " ) . trimmed ( ) ;
if ( noProxy . isEmpty ( ) )
return false ;
const QList < QByteArray > noProxyTokens = noProxy . split ( ' , ' ) ;
foreach ( const QByteArray & rawToken , noProxyTokens ) {
QByteArray token = rawToken . trimmed ( ) ;
QString peerHostName = query . peerHostName ( ) ;
// Since we use suffix matching, "*" is our 'default' behaviour
if ( token . startsWith ( " * " ) )
token = token . mid ( 1 ) ;
// Harmonize trailing dot notation
if ( token . endsWith ( ' . ' ) & & ! peerHostName . endsWith ( ' . ' ) )
token = token . left ( token . length ( ) - 1 ) ;
// We prepend a dot to both values, so that when we do a suffix match,
// we don't match "donotmatch.com" with "match.com"
if ( ! token . startsWith ( ' . ' ) )
token . prepend ( ' . ' ) ;
if ( ! peerHostName . startsWith ( ' . ' ) )
peerHostName . prepend ( ' . ' ) ;
if ( peerHostName . endsWith ( QString : : fromLatin1 ( token ) ) )
return true ;
}
return false ;
}
static QList < QNetworkProxy > pgAdminSystemProxyForQuery ( const QNetworkProxyQuery & query )
{
QList < QNetworkProxy > proxyList ;
if ( ignoreProxyFor ( query ) )
return proxyList < < QNetworkProxy : : NoProxy ;
// No need to care about casing here, QUrl lowercases values already
const QString queryProtocol = query . protocolTag ( ) ;
QByteArray proxy_env ;
if ( queryProtocol = = QLatin1String ( " http " ) )
proxy_env = qgetenv ( " http_proxy " ) ;
else if ( queryProtocol = = QLatin1String ( " https " ) )
proxy_env = qgetenv ( " https_proxy " ) ;
else if ( queryProtocol = = QLatin1String ( " ftp " ) )
proxy_env = qgetenv ( " ftp_proxy " ) ;
else
proxy_env = qgetenv ( " all_proxy " ) ;
// Fallback to http_proxy is no protocol specific proxy was found
if ( proxy_env . isEmpty ( ) )
proxy_env = qgetenv ( " http_proxy " ) ;
if ( ! proxy_env . isEmpty ( ) )
{
QUrl url = QUrl ( QString : : fromLocal8Bit ( proxy_env ) ) ;
if ( url . scheme ( ) = = QLatin1String ( " socks5 " ) )
{
QNetworkProxy proxy ( QNetworkProxy : : Socks5Proxy , url . host ( ) ,
url . port ( ) ? url . port ( ) : 1080 , url . userName ( ) , url . password ( ) ) ;
proxyList < < proxy ;
} else if ( url . scheme ( ) = = QLatin1String ( " socks5h " ) )
{
QNetworkProxy proxy ( QNetworkProxy : : Socks5Proxy , url . host ( ) ,
url . port ( ) ? url . port ( ) : 1080 , url . userName ( ) , url . password ( ) ) ;
proxy . setCapabilities ( QNetworkProxy : : HostNameLookupCapability ) ;
proxyList < < proxy ;
} else if ( ( url . scheme ( ) = = QLatin1String ( " http " ) | | url . scheme ( ) = = QLatin1String ( " https " ) | | url . scheme ( ) . isEmpty ( ) )
& & query . queryType ( ) ! = QNetworkProxyQuery : : UdpSocket
& & query . queryType ( ) ! = QNetworkProxyQuery : : TcpServer )
{
QNetworkProxy proxy ( QNetworkProxy : : HttpProxy , url . host ( ) ,
url . port ( ) ? url . port ( ) : 8080 , url . userName ( ) , url . password ( ) ) ;
proxyList < < proxy ;
}
}
if ( proxyList . isEmpty ( ) )
proxyList < < QNetworkProxy : : NoProxy ;
return proxyList ;
}
class pgAdminSystemConfigurationProxyFactory : public QNetworkProxyFactory
{
public :
pgAdminSystemConfigurationProxyFactory ( ) : QNetworkProxyFactory ( ) { }
virtual QList < QNetworkProxy > queryProxy ( const QNetworkProxyQuery & query )
{
QList < QNetworkProxy > proxies = pgAdminSystemProxyForQuery ( query ) ;
// Make sure NoProxy is in the list, so that QTcpServer can work:
// it searches for the first proxy that can has the ListeningCapability capability
// if none have (as is the case with HTTP proxies), it fails to bind.
// NoProxy allows it to fallback to the 'no proxy' case and bind.
proxies . append ( QNetworkProxy : : NoProxy ) ;
return proxies ;
}
} ;
QT_END_NAMESPACE
# endif // QT_NO_NETWORKINTERFACE
# endif
2016-02-23 03:10:49 -06:00
void delay ( int milliseconds )
{
QTime endTime = QTime : : currentTime ( ) . addMSecs ( milliseconds ) ;
while ( QTime : : currentTime ( ) < endTime )
{
QCoreApplication : : processEvents ( QEventLoop : : AllEvents , 100 ) ;
}
}
2013-06-16 08:17:46 -05:00
int main ( int argc , char * argv [ ] )
{
2017-08-09 05:41:44 -05:00
/*
* Before starting main application , need to set ' QT_X11_NO_MITSHM = 1 '
* to make the runtime work with IBM PPC machine .
*/
# if defined (Q_OS_LINUX)
QByteArray val ( " 1 " ) ;
qputenv ( " QT_X11_NO_MITSHM " , val ) ;
# endif
2013-06-16 08:17:46 -05:00
// Create the QT application
QApplication app ( argc , argv ) ;
2013-10-04 09:08:18 -05:00
// Setup the settings management
2016-12-01 20:24:32 -06:00
QCoreApplication : : setOrganizationName ( " pgadmin " ) ;
2013-10-04 09:08:18 -05:00
QCoreApplication : : setOrganizationDomain ( " pgadmin.org " ) ;
2016-12-01 20:24:32 -06:00
QCoreApplication : : setApplicationName ( PGA_APP_NAME . toLower ( ) . replace ( " " , " " ) ) ;
2013-10-04 09:08:18 -05:00
2017-06-16 05:26:31 -05:00
# if QT_VERSION >= 0x050000
2017-06-16 03:57:19 -05:00
// Set high DPI pixmap to display icons clear on Qt widget.
QApplication : : setAttribute ( Qt : : AA_UseHighDpiPixmaps ) ;
2017-06-16 05:26:31 -05:00
# endif
2017-06-16 03:57:19 -05:00
2017-11-15 10:33:24 -06:00
# ifdef _WIN32
// Set registry "HKEY_CLASSES_ROOT\.css\Content Type" to value "text/css" to avoid rendering issue in windows OS.
QString infoMsgStr ( " " ) ;
QSettings css_keys ( " HKEY_CLASSES_ROOT " , QSettings : : NativeFormat ) ;
// If key already exists then check for existing value and it differs then only change it.
if ( css_keys . childGroups ( ) . contains ( " .css " , Qt : : CaseInsensitive ) )
{
QSettings set ( " HKEY_CLASSES_ROOT \\ .css " , QSettings : : NativeFormat ) ;
if ( set . value ( " Content Type " ) . toString ( ) ! = " text/css " )
{
set . setValue ( " Content Type " , " text/css " ) ;
// If error while setting registry then it should be issue with permissions.
if ( set . status ( ) = = QSettings : : NoError )
infoMsgStr = " pgAdmin 4 application has reset the registry key 'HKEY_CLASSES_ROOT \\ css \\ Content Type' to 'text/css' to fix a system misconfiguration that can lead to rendering problems. " ;
else
infoMsgStr = " Failed to reset the registry key 'HKEY_CLASSES_ROOT \\ css \\ Content Type' to 'text/css'. Try to run with Administrator privileges. " ;
}
}
# endif
2017-05-30 07:17:07 -05:00
/* In windows and linux, it is required to set application level proxy
* becuase socket bind logic to find free port gives socket creation error
* when system proxy is configured . We are also setting
* " setUseSystemConfiguration " = true to use the system proxy which will
* override this application level proxy . As this bug is fixed in Qt 5.9 so
* need to set application proxy for Qt version < 5.9 .
*/
# ifndef PGADMIN4_USE_WEBENGINE
# if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
// Give dummy URL required to find proxy server configured in windows.
QNetworkProxyQuery proxyQuery ( QUrl ( " https://www.pgadmin.org " ) ) ;
QNetworkProxy l_proxy ;
QList < QNetworkProxy > listOfProxies = QNetworkProxyFactory : : systemProxyForQuery ( proxyQuery ) ;
if ( listOfProxies . size ( ) )
{
l_proxy = listOfProxies [ 0 ] ;
// If host name is not empty means proxy server is configured.
if ( ! l_proxy . hostName ( ) . isEmpty ( ) ) {
QNetworkProxy : : setApplicationProxy ( QNetworkProxy ( ) ) ;
}
}
# endif
# endif
# ifndef PGADMIN4_USE_WEBENGINE
# if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
QByteArray proxy_env ;
proxy_env = qgetenv ( " http_proxy " ) ;
// If http_proxy environment is defined in linux then proxy server is configured.
if ( ! proxy_env . isEmpty ( ) ) {
QNetworkProxy : : setApplicationProxy ( QNetworkProxy ( ) ) ;
}
# endif
# endif
2016-09-05 10:45:01 -05:00
// Display the spash screen
QSplashScreen * splash = new QSplashScreen ( ) ;
splash - > setPixmap ( QPixmap ( " :/splash.png " ) ) ;
splash - > show ( ) ;
app . processEvents ( QEventLoop : : AllEvents ) ;
2016-01-18 08:33:28 -06:00
quint16 port = 0L ;
2013-10-04 12:16:31 -05:00
// Find an unused port number. Essentially, we're just reserving one
2014-12-16 06:53:09 -06:00
// here that Flask will use when we start up the server.
2016-01-18 08:33:28 -06:00
// In order to use the socket, we need to free this socket ASAP.
// Hence - putting this code in a code block so the scope of the socket
// variable vanishes to make that socket available.
{
2017-03-29 10:33:55 -05:00
# if QT_VERSION >= 0x050000
2017-03-26 20:02:15 -05:00
QTcpSocket socket ;
2017-07-11 05:48:13 -05:00
# if QT_VERSION >= 0x050900
socket . setProxy ( QNetworkProxy : : NoProxy ) ;
# endif
2017-03-26 20:02:15 -05:00
socket . bind ( 0 , QTcpSocket : : ShareAddress ) ;
2017-03-29 10:33:55 -05:00
# else
QUdpSocket socket ;
socket . bind ( 0 , QUdpSocket : : ShareAddress ) ;
# endif
2016-01-18 08:33:28 -06:00
port = socket . localPort ( ) ;
}
2013-10-04 11:12:10 -05:00
2017-03-06 08:53:49 -06:00
// Generate a random key to authenticate the client to the server
QString key = QUuid : : createUuid ( ) . toString ( ) ;
key = key . mid ( 1 , key . length ( ) - 2 ) ;
2017-03-29 11:17:16 -05:00
# if defined (Q_OS_LINUX) && QT_VERSION < 0x050000
QNetworkProxyFactory : : setApplicationProxyFactory ( new pgAdminSystemConfigurationProxyFactory ) ;
QSslConfiguration sslCfg = QSslConfiguration : : defaultConfiguration ( ) ;
QList < QSslCertificate > ca_list = sslCfg . caCertificates ( ) ;
QList < QSslCertificate > ca_new = QSslCertificate : : fromData ( " CaCertificates " ) ;
ca_list + = ca_new ;
sslCfg . setCaCertificates ( ca_list ) ;
sslCfg . setProtocol ( QSsl : : AnyProtocol ) ;
QSslConfiguration : : setDefaultConfiguration ( sslCfg ) ;
# else
QNetworkProxyFactory : : setUseSystemConfiguration ( true ) ;
# endif
2013-06-21 17:21:11 -05:00
// Fire up the webserver
2016-11-02 03:12:08 -05:00
Server * server ;
2013-06-21 17:21:11 -05:00
2016-11-02 03:12:08 -05:00
bool done = false ;
while ( done ! = true )
2013-06-21 17:21:11 -05:00
{
2017-03-06 08:53:49 -06:00
server = new Server ( port , key ) ;
2013-06-21 17:21:11 -05:00
2016-11-02 03:12:08 -05:00
if ( ! server - > Init ( ) )
{
2017-02-27 04:49:47 -06:00
splash - > finish ( NULL ) ;
2016-11-02 03:12:08 -05:00
qDebug ( ) < < server - > getError ( ) ;
2013-06-21 17:21:11 -05:00
2016-11-02 03:12:08 -05:00
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 ) ;
2013-06-16 08:17:46 -05:00
2016-11-02 03:12:08 -05:00
exit ( 1 ) ;
}
2014-12-16 06:53:09 -06:00
2016-11-02 03:12:08 -05:00
server - > start ( ) ;
2014-12-16 06:53:09 -06:00
2016-11-02 03:12:08 -05:00
// Any errors?
if ( server - > isFinished ( ) | | server - > getError ( ) . length ( ) > 0 )
{
splash - > finish ( NULL ) ;
2014-12-16 06:53:09 -06:00
2016-11-02 03:12:08 -05:00
qDebug ( ) < < server - > getError ( ) ;
2016-01-19 04:26:01 -06:00
2016-11-02 03:12:08 -05:00
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 ) ;
2014-12-16 06:53:09 -06:00
2016-11-02 03:12:08 -05:00
// 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 ;
2016-01-19 04:26:01 -06:00
}
2016-11-02 03:12:08 -05:00
else
done = true ;
2014-12-16 06:53:09 -06:00
}
2016-11-02 03:12:08 -05:00
2013-10-04 12:16:31 -05:00
// Generate the app server URL
2017-06-11 07:56:49 -05:00
QString appServerUrl = QString ( " http://127.0.0.1:%1/?key=%2 " ) . arg ( port ) . arg ( key ) ;
2013-10-04 12:16:31 -05:00
2017-07-17 09:17:58 -05:00
// Read the server connection timeout from the registry or set the default timeout.
QSettings settings ;
int timeout = settings . value ( " ConnectionTimeout " , 30 ) . toInt ( ) ;
2013-10-04 12:16:31 -05:00
// Now the server should be up, we'll attempt to connect and get a response.
2017-02-27 04:49:47 -06:00
// We'll retry in a loop a few time before aborting if necessary.
2017-07-17 09:17:58 -05:00
QTime endTime = QTime : : currentTime ( ) . addSecs ( timeout ) ;
bool alive = false ;
while ( QTime : : currentTime ( ) < = endTime )
2013-10-04 12:16:31 -05:00
{
2017-07-17 09:17:58 -05:00
alive = PingServer ( QUrl ( appServerUrl ) ) ;
2013-10-04 12:16:31 -05:00
if ( alive )
{
break ;
}
2017-07-17 09:17:58 -05:00
delay ( 200 ) ;
}
2013-10-04 12:16:31 -05:00
2017-07-17 09:17:58 -05:00
// Attempt to connect one more time in case of a long network timeout while looping
if ( ! alive & & ! PingServer ( QUrl ( appServerUrl ) ) )
{
splash - > finish ( NULL ) ;
QString error ( QWidget : : tr ( " The application server could not be contacted. " ) ) ;
QMessageBox : : critical ( NULL , QString ( QWidget : : tr ( " Fatal Error " ) ) , error ) ;
2017-06-11 08:38:22 -05:00
2017-07-17 09:17:58 -05:00
exit ( 1 ) ;
2013-10-04 12:16:31 -05:00
}
2013-06-16 08:17:46 -05:00
// Create & show the main window
2013-10-04 12:16:31 -05:00
BrowserWindow browserWindow ( appServerUrl ) ;
2016-01-22 08:23:47 -06:00
browserWindow . setWindowTitle ( PGA_APP_NAME ) ;
2016-01-25 11:21:00 -06:00
browserWindow . setWindowIcon ( QIcon ( " :/pgAdmin4.ico " ) ) ;
2017-11-15 10:33:24 -06:00
# ifdef _WIN32
browserWindow . setRegistryMessage ( infoMsgStr ) ;
# endif
2013-06-16 08:17:46 -05:00
browserWindow . show ( ) ;
// Go!
2016-09-05 10:45:01 -05:00
splash - > finish ( NULL ) ;
2017-06-16 03:57:19 -05:00
// Set global application stylesheet.
QFile file ( " :/qss/pgadmin4.qss " ) ;
if ( file . open ( QFile : : ReadOnly ) )
{
QString StyleSheet = QLatin1String ( file . readAll ( ) ) ;
qApp - > setStyleSheet ( StyleSheet ) ;
file . close ( ) ;
}
2013-06-16 08:17:46 -05:00
return app . exec ( ) ;
}
2014-03-09 09:39:12 -05:00
// Ping the application server to see if it's alive
2013-10-04 12:16:31 -05:00
bool PingServer ( QUrl url )
{
QNetworkAccessManager manager ;
QEventLoop loop ;
QNetworkReply * reply ;
QVariant redirectUrl ;
2017-07-17 09:19:30 -05:00
url . setPath ( " /misc/ping " ) ;
2013-10-04 12:16:31 -05:00
do
{
reply = manager . get ( QNetworkRequest ( url ) ) ;
QObject : : connect ( reply , SIGNAL ( finished ( ) ) , & loop , SLOT ( quit ( ) ) ) ;
loop . exec ( ) ;
redirectUrl = reply - > attribute ( QNetworkRequest : : RedirectionTargetAttribute ) ;
url = redirectUrl . toUrl ( ) ;
if ( ! redirectUrl . isNull ( ) )
delete reply ;
} while ( ! redirectUrl . isNull ( ) ) ;
if ( reply - > error ( ) ! = QNetworkReply : : NoError )
{
return false ;
}
QString response = reply - > readAll ( ) ;
if ( response ! = " PING " )
{
qDebug ( ) < < " Failed to connect, server response: " < < response ;
return false ;
}
return true ;
}
2014-03-09 09:39:12 -05:00