pgadmin3/schema/pgServer.cpp
2025-10-20 11:58:38 +05:00

2342 lines
No EOL
65 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgServer.cpp - PostgreSQL Server
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/busyinfo.h>
#include <wx/dir.h>
#include <wx/fileconf.h>
#include <wx/wfstream.h>
// App headers
#include "ctl/ctlMenuToolbar.h"
#include "frm/menu.h"
#include "utils/misc.h"
#include "frm/frmMain.h"
#include "frm/frmHint.h"
#include "dlg/dlgConnect.h"
#include "schema/pgDatabase.h"
#include "schema/pgTablespace.h"
#include "schema/pgGroup.h"
#include "schema/pgUser.h"
#include "schema/pgRole.h"
#include "schema/gpResQueue.h"
#include "agent/pgaJob.h"
#include "utils/utffile.h"
#include "utils/pgfeatures.h"
#include "utils/registry.h"
#include "frm/frmReport.h"
#include "dlg/dlgServer.h"
#include "schema/edbResourceGroup.h"
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
#include "utils/sshTunnel.h"
#endif
#define DEFAULT_PG_DATABASE wxT("postgres")
pgServer::pgServer(const wxString &newName, const wxString &newHostAddr, const wxString &newDescription, const wxString &newService,
const wxString &newDatabase, const wxString &newUsername, int newPort, bool _storePwd, const wxString &newRolename, const wxString& newConnStr, bool _restore,
int _ssl, const wxString &_colour, const wxString &_group, bool _sshTunnel, const wxString &newTunnelHost, const wxString &newTunnelUserName,
bool _authModePwd, const wxString &newTunnelPassword, const wxString &newPublicKey, const wxString &newIdentity, const int &sshPort)
: pgObject(serverFactory, newName)
{
description = newDescription;
hostaddr = newHostAddr;
service = newService;
database = newDatabase;
username = newUsername;
port = newPort;
ssl = _ssl;
colour = _colour;
group = _group;
serverIndex = 0;
connected = false;
lastSystemOID = 0;
conn = NULL;
passwordValid = true;
storePwd = _storePwd;
rolename = newRolename;
connstr = newConnStr;
restore = _restore;
superUser = false;
createPrivilege = false;
sshTunnel = _sshTunnel;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
// SSH Tunnel
tunnelObj = NULL;
tunnelHost = newTunnelHost;
tunnelUserName = newTunnelUserName;
authModePwd = _authModePwd;
tunnelPassword = newTunnelPassword;
publicKeyFile = newPublicKey;
identityFile = newIdentity;
tunnelPort = sshPort;
#endif
#ifdef WIN32
scmHandle = 0;
serviceHandle = 0;
#endif
}
pgServer::~pgServer()
{
if (conn)
delete conn;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(tunnelObj)
{
if(tunnelObj->IsAlive())
{
tunnelObj->Cleanup();
}
tunnelObj = NULL;
}
#endif
#ifdef WIN32
if (serviceHandle)
CloseServiceHandle(serviceHandle);
if (scmHandle)
CloseServiceHandle(scmHandle);
#endif
}
wxString pgServer::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on server");
message += wxT(" ") + GetName();
break;
case REFRESHINGDETAILS:
message = _("Refreshing server");
message += wxT(" ") + GetName();
break;
case BACKUPGLOBALS:
message = _("Backup globals of server");
message += wxT(" ") + GetName();
break;
case BACKUPSERVERTITLE:
message = _("Backup server");
message += wxT(" ") + GetName();
break;
case DROPTITLE:
message = _("Drop server?");
break;
case PROPERTIESREPORT:
message = _("Server properties report");
message += wxT(" - ") + GetName();
break;
case PROPERTIES:
message = _("Server properties");
break;
case STATISTICSREPORT:
message = _("Server statistics report");
message += wxT(" - ") + GetName();
break;
case OBJSTATISTICS:
message = _("Server statistics");
break;
}
return message;
}
int pgServer::GetIconId()
{
if (GetConnected())
return serverFactory.GetIconId();
else
return serverFactory.GetClosedIconId();
}
wxMenu *pgServer::GetNewMenu()
{
wxMenu *menu = 0;
if (connected && (GetSuperUser() || GetCreateRole()))
{
menu = new wxMenu();
if (settings->GetDisplayOption(_("Tablespaces")))
tablespaceFactory.AppendMenu(menu);
if (GetConnection()->BackendMinimumVersion(8, 1))
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
groupRoleFactory.AppendMenu(menu);
if (settings->GetDisplayOption(_("Users/login Roles")))
loginRoleFactory.AppendMenu(menu);
if (GetConnection()->GetIsGreenplum())
{
if (settings->GetDisplayOption(_("Resource Queues")))
resQueueFactory.AppendMenu(menu);
}
}
else
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
groupFactory.AppendMenu(menu);
if (settings->GetDisplayOption(_("Users/login Roles")))
userFactory.AppendMenu(menu);
}
// Added Resource Group only for PPAS 9.4 and above
if (conn->GetIsEdb() && conn->EdbMinimumVersion(9, 4))
{
if (settings->GetDisplayOption(_("Resource Groups")))
resourceGroupFactory.AppendMenu(menu);
}
}
return menu;
}
pgServer *pgServer::GetServer() const
{
if (connected)
return (pgServer *)this;
return 0;
}
pgConn *pgServer::CreateConn(wxString dbName, OID oid, wxString applicationname)
{
if (!connected)
return 0;
if (dbName.IsEmpty())
{
dbName = GetDatabaseName();
oid = dbOid;
}
pgConn *conn = NULL;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
conn = new pgConn(local_listenhost, service, hostaddr, dbName, username, password, local_listenport, rolename,connstr, ssl, oid, applicationname, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
}
else
#endif
{
conn = new pgConn(GetName(), service, hostaddr, dbName, username, password, port, rolename,connstr, ssl, oid, applicationname, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
}
if (conn && conn->GetStatus() != PGCONN_OK)
{
wxLogError(wxT("%s"), conn->GetLastError().c_str());
delete conn;
return 0;
}
return conn;
}
wxString pgServer::GetFullName()
{
if (GetDescription().Length() > 0)
return GetDescription() + wxT(" (") + GetIdentifier() + wxT(")");
else
return wxT("(") + GetIdentifier() + wxT(")");
}
wxString pgServer::GetFullIdentifier()
{
return GetFullName();
}
bool pgServer::Disconnect(frmMain *form)
{
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(tunnelObj)
{
if(tunnelObj->IsAlive())
{
tunnelObj->Cleanup();
}
tunnelObj = NULL;
}
#endif
if (conn)
{
delete conn;
conn = 0;
connected = false;
expandedKids = false;
ver = wxT("");
versionNum = wxT("");
lastSystemOID = 0;
}
if (form)
UpdateIcon(form->GetBrowser());
return true;
}
bool pgServer::GetCanHint()
{
return connected && conn->BackendMinimumVersion(8, 1) && !autovacuumRunning;
}
void pgServer::ShowHint(frmMain *form, bool force)
{
wxArrayString hints;
if (!autovacuumRunning)
hints.Add(HINT_AUTOVACUUM);
if (force || !hintShown)
frmHint::ShowHint(form, hints, GetFullIdentifier(), force);
hintShown = true;
}
#define SERVICEBUFSIZE 10000
#define QUERYBUFSIZE 256
#ifdef WIN32
wxArrayString pgServer::GetDependentServices(SC_HANDLE handle)
{
wxArrayString services;
LPENUM_SERVICE_STATUS sbuf = (LPENUM_SERVICE_STATUS) new char[SERVICEBUFSIZE];
DWORD servicesReturned = 0, bytesNeeded;
::EnumDependentServices(handle, SERVICE_STATE_ALL, sbuf, SERVICEBUFSIZE, &bytesNeeded, &servicesReturned);
DWORD i;
for (i = 0 ; i < servicesReturned ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, sbuf[i].lpServiceName, SERVICE_QUERY_CONFIG);
if (h)
{
char buffer[QUERYBUFSIZE];
LPQUERY_SERVICE_CONFIG qsc = (LPQUERY_SERVICE_CONFIG)buffer;
if(::QueryServiceConfig(h, qsc, QUERYBUFSIZE, &bytesNeeded))
{
if (qsc->dwStartType != SERVICE_DISABLED)
services.Add(sbuf[i].lpServiceName);
}
::CloseServiceHandle(h);
}
}
delete[] sbuf;
return services;
}
#endif
bool pgServer::StartService()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
done = (::StartService(serviceHandle, 0, 0) != 0);
if (!done)
{
DWORD rc = ::GetLastError();
if (rc == ERROR_SERVICE_ALREADY_RUNNING)
{
GetServerRunning();
return true;
}
// report error
wxLogError(__("Failed to start server %s: Errcode=%d\nCheck event log for details."),
serviceId.c_str(), rc);
}
else
{
GetServerRunning(); // ignore result, just to wait for startup
wxArrayString services = GetDependentServices(serviceHandle);
if (services.GetCount() > 0)
{
size_t i;
wxString serviceString;
for (i = 0 ; i < services.GetCount() ; i++)
serviceString += wxT(" ") + services.Item(i) + wxT("\n");
wxMessageDialog msg(0, _("There are dependent services configured:\n\n")
+ serviceString + _("\nStart dependent services too?"), _("Dependent services"),
wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT);
if (msg.ShowModal() == wxID_YES)
{
for (i = 0 ; i < services.GetCount() ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, services.Item(i), GENERIC_EXECUTE | GENERIC_READ);
if (h)
{
if (!::StartService(h, 0, 0))
done = false;
CloseServiceHandle(h);
}
else
done = false;
}
if (!done)
{
wxMessageDialog msg(0, _("One or more dependent services didn't start; see the eventlog for details."), _("Service start problem"),
wxICON_EXCLAMATION | wxOK);
msg.ShowModal();
done = true;
}
}
}
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" start"));
done = (res.Find(wxT("tarting")) > 0);
#endif
return done;
}
bool pgServer::StopService()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
SERVICE_STATUS st;
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
if (!done)
{
if (::GetLastError() == ERROR_DEPENDENT_SERVICES_RUNNING)
{
LPENUM_SERVICE_STATUS sbuf = (LPENUM_SERVICE_STATUS) new char[SERVICEBUFSIZE];
DWORD bytesNeeded, servicesReturned = 0;
::EnumDependentServices(serviceHandle, SERVICE_ACTIVE, sbuf, SERVICEBUFSIZE, &bytesNeeded, &servicesReturned);
done = true;
if (servicesReturned)
{
DWORD i;
wxString services;
for (i = 0 ; i < servicesReturned ; i++)
services += wxT(" ") + wxString(sbuf[i].lpDisplayName) + wxT("\n");
wxMessageDialog msg(0, _("There are dependent services running:\n\n")
+ services + _("\nStop dependent services?"), _("Dependent services"),
wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT);
if (msg.ShowModal() != wxID_YES)
return false;
for (i = 0 ; done && i < servicesReturned ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, sbuf[i].lpServiceName, GENERIC_EXECUTE | GENERIC_READ);
if (h)
{
done = (::ControlService(h, SERVICE_CONTROL_STOP, &st) != 0);
CloseServiceHandle(h);
}
else
done = false;
}
if (done)
{
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
int retries = 10;
while (!done && retries > 0)
{
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
retries--;
wxSleep(5);
}
}
}
}
// report error
if (!done)
wxLogError(__("Failed to stop server %s: Errcode=%d\nCheck event log for details."),
serviceId.c_str(), ::GetLastError());
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" stop"));
done = (res.Find(wxT("stopped")) > 0);
#endif
return done;
}
bool pgServer::GetServerRunning()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
SERVICE_STATUS st;
int loops;
for (loops = 0 ; loops < 20 ; loops++)
{
if (::QueryServiceStatus(serviceHandle, &st) == 0)
{
DWORD rc = ::GetLastError();
CloseServiceHandle(serviceHandle);
CloseServiceHandle(scmHandle);
serviceHandle = 0;
scmHandle = 0;
return false;
}
done = (st.dwCurrentState == SERVICE_RUNNING);
if (st.dwCurrentState == SERVICE_START_PENDING)
Sleep(100);
else
break;
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" status"));
done = (res.Find(wxT("PID: ")) > 0);
#endif
return done;
}
void pgServer::iSetServiceID(const wxString &s)
{
serviceId = s;
#ifdef WIN32
if (serviceId.Find('\\') < 0)
scmHandle = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, GENERIC_EXECUTE);
else
scmHandle = OpenSCManager(wxT("\\\\") + serviceId.BeforeFirst('\\'), SERVICES_ACTIVE_DATABASE, GENERIC_EXECUTE | GENERIC_READ);
if (scmHandle)
serviceHandle = OpenService(scmHandle, serviceId.AfterLast('\\'), GENERIC_EXECUTE | GENERIC_READ);
#endif
}
bool pgServer::GetServerControllable()
{
#ifdef WIN32
return serviceHandle != 0;
#else
return !serviceId.IsEmpty();
#endif
}
wxString pgServer::passwordFilename()
{
wxString fname = sysSettings::GetConfigFile(sysSettings::PGPASS);
wxLogInfo(wxT("Using password file %s"), fname.c_str());
return fname;
}
bool pgServer::GetPasswordIsStored()
{
wxString fname = passwordFilename();
if (!wxFile::Exists(fname))
return false;
wxUtfFile file(fname, wxFile::read, wxFONTENCODING_SYSTEM);
if (file.IsOpened())
{
wxString before;
file.Read(before);
wxStringTokenizer lines(before, wxT("\n\r"));
wxString seekStr = GetName() + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ username + wxT(":") ;
wxString seekStr2 = wxString(GetName().mb_str(wxConvUTF8), wxConvLibc) + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ wxString(username.mb_str(wxConvUTF8), wxConvLibc) + wxT(":") ;
while (lines.HasMoreTokens())
{
wxString str = lines.GetNextToken();
if (str.Left(seekStr.Length()) == seekStr)
return true;
if (str.Left(seekStr2.Length()) == seekStr2)
return true;
}
}
return false;
}
void pgServer::StorePassword()
{
wxString fname = passwordFilename();
if (!wxFile::Exists(fname))
{
return;
}
wxUtfFile file;
// Don't try to read and write in one OP - it doesn't work well
wxString before;
file.Open(fname, wxFile::read, wxFONTENCODING_SYSTEM);
file.Read(before);
file.Close();
file.Open(fname, wxFile::write, wxFONTENCODING_SYSTEM);
if (file.IsOpened())
{
wxString after;
wxString passwd;
wxString seekStr;
if (GetConnection()->GetNeedUtfConnectString())
{
passwd = wxString(password.mb_str(wxConvUTF8), wxConvLibc);
seekStr = wxString(GetName().mb_str(wxConvUTF8), wxConvLibc) + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ wxString(username.mb_str(wxConvUTF8), wxConvLibc) + wxT(":") ;
}
else
{
passwd = password;
seekStr = GetName() + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ username + wxT(":") ;
}
// Escape ":" and "\" from the password field
if (!passwd.IsEmpty())
{
passwd.Replace(wxT("\\"), wxT("\\\\"));
passwd.Replace(wxT(":") , wxT("\\:"));
}
file.Read(before);
wxStringTokenizer lines(before, wxT("\n\r"));
file.Seek(0);
bool found = false;
while (lines.HasMoreTokens())
{
wxString str = lines.GetNextToken();
if (str.Left(seekStr.Length()) == seekStr && !passwd.IsEmpty())
{
// entry found
found = true;
if (storePwd)
file.Write(seekStr + passwd + END_OF_LINE);
}
else
file.Write(str + END_OF_LINE);
}
if (!found && storePwd && !passwd.IsEmpty())
file.Write(seekStr + passwd + END_OF_LINE);
file.Close();
}
}
int pgServer::Connect(frmMain *form, bool askPassword, const wxString &pwd, bool forceStorePassword, bool askTunnelPassword)
{
wxLogInfo(wxT("Attempting to create a connection object..."));
bool storePassword = false;
if (!conn || conn->GetStatus() != PGCONN_OK)
{
if (conn)
{
delete conn;
conn = 0;
}
if (askPassword)
{
if ((sshTunnel || !passwordValid || !GetPasswordIsStored() || !GetStorePwd()) && GetSSLCert() == wxEmptyString)
{
wxString txt;
txt.Printf(_("Please enter password for user %s\non server %s (%s)"), username.c_str(), description.c_str(), GetName().c_str());
dlgConnect *dlg = NULL;
// if sshTunnel is true then we have to hide 'Stored Password' option
if(sshTunnel)
dlg = new dlgConnect(NULL, txt, false);
else
dlg = new dlgConnect(form, txt, GetStorePwd());
dlg->SetWindowStyleFlag( dlg->GetWindowStyleFlag() | wxSTAY_ON_TOP);
switch (dlg->Go())
{
case wxID_OK:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
break;
case wxID_CANCEL:
case -1:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
return PGCONN_ABORTED;
default:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
wxLogError(__("Couldn't create a connection dialogue!"));
return PGCONN_BAD;
}
iSetStorePwd(dlg->GetStorePwd());
password = dlg->GetPassword();
storePassword = true;
if(dlg)
{
delete dlg;
dlg = NULL;
}
}
}
else
iSetPassword(pwd);
form->StartMsg(_("Connecting to database"));
wxString host;
int iPort;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
//Ask Tunnel Password
if(askTunnelPassword)
{
wxString txt;
if(GetAuthModePwd())
{
txt.Printf(_("Please enter the SSH tunnel password for user %s\non server %s"), tunnelUserName.c_str(), tunnelHost.c_str());
}
else
{
txt.Printf(_("Please enter the pass phrase for the identity file\n%s"), identityFile.c_str());
}
dlgConnect dlg(NULL, txt, false);
switch (dlg.Go())
{
case wxID_OK:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
tunnelPassword = dlg.GetPassword();
break;
case wxID_CANCEL:
case -1:
default:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
form->EndMsg(false);
return PGCONN_ABORTED;
}
}
// Create SSH Tunnel if required
if(!tunnelObj)
{
if(!createSSHTunnel())
{
form->EndMsg(false);
return PGCONN_SSHTUNNEL_ERROR;
}
}
host = local_listenhost;
iPort = local_listenport;
}
else
#endif
{
host = GetName();
iPort = port;
}
wxString usr=wxEmptyString;
#ifndef __WXMSW__
wxGetEnv(wxT("USER"), &usr);
#else
wxGetEnv(wxT("USERNAME"), &usr);
#endif
if (database.IsEmpty())
{
conn = new pgConn(host, service, hostaddr, DEFAULT_PG_DATABASE, username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() +wxT(" - ")+ usr, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (conn->GetStatus() == PGCONN_OK)
database = DEFAULT_PG_DATABASE;
else if (conn->GetStatus() == PGCONN_BAD && conn->GetLastError().Find(
wxT("database \"") DEFAULT_PG_DATABASE wxT("\" does not exist")) >= 0)
{
delete conn;
conn = new pgConn(host, service, hostaddr, wxT("template1"), username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() + wxT(" - Browser"), sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (conn && conn->GetStatus() == PGCONN_OK)
database = wxT("template1");
}
}
else
{
conn = new pgConn(host, service, hostaddr, database, username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() + " "+usr, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (!conn)
{
form->EndMsg(false);
wxLogError(__("Couldn't create a connection object!"));
return PGCONN_BAD;
}
}
}
int status = conn->GetStatus();
if (status == PGCONN_OK)
{
dbOid = conn->GetDbOid();
// Check the server version
if (conn->GetIsGreenplum())
{
// Greenplum HAWQ (SQL on Hadoop) is not supported by this pgAdmin version
if (conn->GetIsHawq())
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str());
}
else
{
// Check for Greenplum specific version
// Greenplum always shows PG version "8.2.15" for now
// this might change once the merge with recent PG versions makes progress
// therefore also check for the max version
if (!(conn->BackendMinimumVersion(GP_MIN_VERSION_N >> 8, GP_MIN_VERSION_N & 0x00FF)) ||
(conn->BackendMinimumVersion(GP_MAX_VERSION_N >> 8, (GP_MAX_VERSION_N & 0x00FF) + 1)))
{
if (GP_MIN_VERSION_N == GP_MAX_VERSION_N)
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server version is %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(GP_MIN_VERSION_T).c_str());
}
else
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server versions are %s to %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(GP_MIN_VERSION_T).c_str(),
wxString(GP_MAX_VERSION_T).c_str());
}
}
}
}
else
{
if (!(conn->BackendMinimumVersion(SERVER_MIN_VERSION_N >> 8, SERVER_MIN_VERSION_N & 0x00FF)) ||
(conn->BackendMinimumVersion(SERVER_MAX_VERSION_N >> 8, (SERVER_MAX_VERSION_N & 0x00FF) + 1))) {
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server versions are %s to %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(SERVER_MIN_VERSION_T).c_str(),
wxString(SERVER_MAX_VERSION_T).c_str());
}
}
connected = true;
bool hasUptime = false;
wxString sql = wxT("SELECT usecreatedb, usesuper");
if (conn->BackendMinimumVersion(8, 1))
{
hasUptime = true;
sql += wxT(", pg_postmaster_start_time() as upsince");
}
else if (conn->HasFeature(FEATURE_POSTMASTER_STARTTIME))
{
hasUptime = true;
sql += wxT(", CASE WHEN usesuper THEN pg_postmaster_starttime() ELSE NULL END as upsince");
}
if (conn->BackendMinimumVersion(8, 4))
{
sql += wxT(", pg_conf_load_time() as confloadedsince");
}
if (conn->BackendMinimumVersion(8, 5))
{
sql += wxT(", CASE WHEN usesuper THEN pg_is_in_recovery() ELSE NULL END as inrecovery");
if (conn->BackendMinimumVersion(10, 0))
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_wal_receive_lsn() ELSE NULL END as receiveloc");
sql += wxT(", CASE WHEN usesuper THEN pg_last_wal_replay_lsn() ELSE NULL END as replayloc");
}
else
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_xlog_receive_location() ELSE NULL END as receiveloc");
sql += wxT(", CASE WHEN usesuper THEN pg_last_xlog_replay_location() ELSE NULL END as replayloc");
}
}
if (conn->BackendMinimumVersion(9, 1))
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_xact_replay_timestamp() ELSE NULL END as replay_timestamp");
if (conn->BackendMinimumVersion(10, 0))
sql += wxT(", CASE WHEN usesuper AND pg_is_in_recovery() THEN pg_is_wal_replay_paused() ELSE NULL END as isreplaypaused");
else
sql += wxT(", CASE WHEN usesuper AND pg_is_in_recovery() THEN pg_is_xlog_replay_paused() ELSE NULL END as isreplaypaused");
}
pgSet *set = ExecuteSet(sql + wxT("\n FROM pg_user WHERE usename=current_user"));
if (set)
{
iSetCreatePrivilege(set->GetBool(wxT("usecreatedb")));
iSetSuperUser(set->GetBool(wxT("usesuper")));
if (hasUptime)
iSetUpSince(set->GetDateTime(wxT("upsince")));
if (conn->BackendMinimumVersion(8, 4))
iSetConfLoadedSince(set->GetDateTime(wxT("confloadedsince")));
if (conn->BackendMinimumVersion(8, 5))
{
iSetInRecovery(set->GetBool(wxT("inrecovery")));
iSetReplayLoc(set->GetVal(wxT("replayloc")));
iSetReceiveLoc(set->GetVal(wxT("receiveloc")));
}
if (conn->BackendMinimumVersion(9, 1))
{
iSetReplayTimestamp(set->GetVal(wxT("replay_timestamp")));
SetReplayPaused(set->GetBool(wxT("isreplaypaused")));
}
delete set;
}
if (conn->BackendMinimumVersion(8, 1))
{
set = ExecuteSet(wxT("SELECT rolcreaterole, rolcreatedb FROM pg_roles WHERE rolname = current_user;"));
if (set)
{
iSetCreatePrivilege(set->GetBool(wxT("rolcreatedb")));
iSetCreateRole(set->GetBool(wxT("rolcreaterole")));
delete set;
}
}
else
iSetCreateRole(false);
wxString version, allVersions;
version.Printf(wxT("%d.%d"), conn->GetMajorVersion(), conn->GetMinorVersion());
allVersions = settings->Read(wxT("Updates/pgsql-Versions"), wxEmptyString);
if (allVersions.Find(version) < 0)
{
if (!allVersions.IsEmpty())
allVersions += wxT(", ");
allVersions += version;
settings->Write(wxT("Updates/pgsql-Versions"), allVersions);
}
if (conn->IsSSLconnected())
settings->WriteBool(wxT("Updates/UseSSL"), true);
UpdateIcon(form->GetBrowser());
if (storePassword || forceStorePassword)
StorePassword();
}
else
{
connected = false;
}
form->EndMsg(connected && status == PGCONN_OK);
passwordValid = connected;
return status;
}
wxString pgServer::GetIdentifier() const
{
wxString idstr;
if (GetService().IsEmpty())
{
if (GetName().IsEmpty())
idstr.Printf(wxT("local:.s.PGSQL.%d"), port);
else if (GetName().StartsWith(wxT("/")))
idstr.Printf(wxT("local:%s/.s.PGSQL.%d"), GetName().c_str(), port);
else
idstr.Printf(wxT("%s:%d"), GetName().c_str(), port);
}
else
idstr.Printf(_("service %s"), GetService().c_str());
return idstr;
}
wxString pgServer::GetVersionString()
{
if (connected)
{
if (ver.IsEmpty())
ver = wxString(conn->GetVersionString());
return ver;
}
else
return wxEmptyString;
}
wxString pgServer::GetVersionNumber()
{
if (connected)
{
if (versionNum.IsEmpty())
{
int major = 0, minor = 0;
sscanf(GetVersionString().ToAscii(), "%*s %d.%d", &major, &minor);
versionNum.Printf(wxT("%d.%d"), major, minor);
}
}
return versionNum;
}
OID pgServer::GetLastSystemOID()
{
if (connected)
{
if (lastSystemOID == 0)
lastSystemOID = conn->GetLastSystemOID();
return lastSystemOID;
}
else
return 0;
}
bool pgServer::SetPassword(const wxString &newVal)
{
wxString sql;
sql.Printf(wxT("ALTER USER %s WITH ENCRYPTED PASSWORD %s;"), qtIdent(username).c_str(), qtDbString(conn->EncryptPassword(username, newVal)).c_str());
bool executed = conn->ExecuteVoid(sql);
if (executed)
{
password = newVal;
StorePassword();
return true;
}
else
return false;
}
wxString pgServer::GetLastError() const
{
wxString msg;
if (conn)
{
if (error != wxT(""))
{
if (conn->GetLastError() != wxT(""))
{
msg.Printf(wxT("%s\n%s"), error.c_str(), conn->GetLastError().c_str());
}
else
{
msg = error;
}
}
else
{
msg = conn->GetLastError();
}
}
return msg;
}
void pgServer::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
{
// Add child nodes if necessary
if (GetConnected())
{
// Reset password menu option
// form->fileMenu->Enable(MNU_PASSWORD, true);
if (!expandedKids)
{
expandedKids = true;
// Log
wxLogInfo(wxT("Adding child object to server %s"), GetIdentifier().c_str());
if (settings->GetDisplayOption(_("Databases")))
browser->AppendCollection(this, databaseFactory);
if (conn->BackendMinimumVersion(8, 0) && settings->GetDisplayOption(_("Tablespaces")))
browser->AppendCollection(this, tablespaceFactory);
// Jobs
// We only add the Jobs node if the appropriate objects are the initial DB.
if (settings->GetDisplayOption(_("pgAgent Jobs")))
{
wxString exists = conn->ExecuteScalar(
wxT("SELECT cl.oid FROM pg_class cl JOIN pg_namespace ns ON ns.oid=relnamespace\n")
wxT(" WHERE relname='pga_job' AND nspname='pgagent'"));
if (!exists.IsNull())
{
exists = conn->ExecuteScalar(wxT("SELECT has_schema_privilege('pgagent', 'USAGE')"));
if (exists == wxT("t"))
browser->AppendCollection(this, jobFactory);
}
}
if (conn->BackendMinimumVersion(8, 1))
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
browser->AppendCollection(this, groupRoleFactory);
if (settings->GetDisplayOption(_("Users/login Roles")))
browser->AppendCollection(this, loginRoleFactory);
if (GetConnection()->GetIsGreenplum())
{
if (settings->GetDisplayOption(_("Resource Queues")))
browser->AppendCollection(this, resQueueFactory);
}
}
else
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
browser->AppendCollection(this, groupFactory);
if (settings->GetDisplayOption(_("Users/login Roles")))
browser->AppendCollection(this, userFactory);
}
// Added Resource Group only for PPAS 9.4 and above
if (conn->GetIsEdb() && conn->EdbMinimumVersion(9, 4))
{
if (settings->GetDisplayOption(_("Resource Groups")))
browser->AppendCollection(this, resourceGroupFactory);
}
autovacuumRunning = true;
wxString qry;
if (conn->BackendMinimumVersion(8, 3))
qry = wxT("SELECT setting FROM pg_settings WHERE name IN ('autovacuum', 'track_counts')");
else
qry = wxT("SELECT setting FROM pg_settings WHERE name IN ('autovacuum', 'stats_start_collector', 'stats_row_level')");
pgSetIterator set(conn, qry);
while (autovacuumRunning && set.RowsLeft())
autovacuumRunning = set.GetBool(wxT("setting"));
}
}
if (properties)
{
// Add the properties view columns
CreateListColumns(properties);
// Display the Server properties
properties->AppendItem(_("Description"), GetDescription());
properties->AppendItem(_("Service"), GetService());
if (GetName().IsEmpty() || GetName().StartsWith(wxT("/")))
{
if (GetName().IsEmpty() && !GetService().IsEmpty())
properties->AppendItem(_("Hostname"), wxEmptyString);
else
properties->AppendItem(_("Hostname"), wxT("local:") + GetName());
if (GetPort() == 0 && !GetService().IsEmpty())
properties->AppendItem(_("Port"), wxEmptyString);
else
properties->AppendItem(_("Port"), (long)GetPort());
}
else
{
properties->AppendItem(_("Hostname"), GetName());
properties->AppendItem(_("Host Address"), GetHostAddr());
if (GetPort() == 0 && !GetService().IsEmpty())
properties->AppendItem(_("Port"), wxEmptyString);
else
properties->AppendItem(_("Port"), (long)GetPort());
#ifdef PG_SSL
if (GetConnected())
{
properties->AppendItem(_("Encryption"),
conn->IsSSLconnected() ? _("SSL encrypted") : _("not encrypted"));
}
else
{
if (ssl > 0)
{
wxString sslMode;
switch (ssl)
{
case 1:
sslMode = _("require");
break;
case 2:
sslMode = _("prefer");
break;
case 3:
sslMode = _("allow");
break;
case 4:
sslMode = _("disable");
break;
case 5:
sslMode = _("verify-ca");
break;
case 6:
sslMode = _("verify-full");
break;
}
properties->AppendItem(_("SSL Mode"), sslMode);
}
}
properties->AppendItem(_("SSL Certificate File"), GetSSLCert());
properties->AppendItem(_("SSL Key File"), GetSSLKey());
properties->AppendItem(_("SSL Root Certificate File"), GetSSLRootCert());
properties->AppendItem(_("SSL Certificate Revocation List"), GetSSLCrl());
properties->AppendItem(_("SSL Compression?"), (GetSSLCompression() ? _("yes") : _("no")));
#endif
}
if (!serviceId.IsEmpty())
properties->AppendItem(_("Service ID"), serviceId);
properties->AppendItem(_("Maintenance database"), GetDatabaseName());
properties->AppendItem(_("Username"), GetUsername());
if (!GetRolename().IsEmpty())
properties->AppendItem(_("Default role"), GetRolename());
if (!GetConnStr().IsEmpty())
properties->AppendItem(_("Add connect str"), GetConnStr());
properties->AppendYesNoItem(_("Store password?"), GetStorePwd());
properties->AppendYesNoItem(_("Restore environment?"), GetRestore());
if (GetConnected())
{
properties->AppendItem(_("Version string"), GetVersionString());
properties->AppendItem(_("Version number"), GetVersionNumber());
properties->AppendItem(_("Last system OID"), GetLastSystemOID());
}
properties->AppendYesNoItem(_("Connected?"), GetConnected());
if (GetConnected())
{
if (GetUpSince().IsValid())
properties->AppendItem(_("Up since"), GetUpSince());
if (GetConfLoadedSince().IsValid())
properties->AppendItem(_("Configuration loaded since"), GetConfLoadedSince());
if (conn->BackendMinimumVersion(8, 1))
properties->AppendItem(wxT("Autovacuum"), (autovacuumRunning ? _("running") : _("not running")));
if (conn->BackendMinimumVersion(8, 5))
{
properties->AppendItem(_("In recovery"), (GetInRecovery() ? _("yes") : _("no")));
properties->AppendItem(_("Last XLOG receive location"), GetReceiveLoc());
properties->AppendItem(_("Last XLOG replay location"), GetReplayLoc());
}
if (conn->BackendMinimumVersion(9, 1))
{
properties->AppendItem(_("Last XACT replay timestamp"), GetReplayTimestamp());
if (GetInRecovery())
properties->AppendItem(_("Replay paused"), (GetReplayPaused() ? _("paused") : _("running")));
else
properties->AppendItem(_("Replay paused"), wxEmptyString);
}
}
if (GetServerControllable())
properties->AppendYesNoItem(_("Running?"), GetServerRunning());
if (!GetDbRestriction().IsEmpty())
properties->AppendItem(_("DB restriction"), GetDbRestriction());
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
properties->AppendItem(_("SSH tunneling?"), (sshTunnel ? _("Yes") : _("No")));
properties->AppendItem(_("Tunnel host"), GetTunnelHost());
properties->AppendItem(_("Tunnel username"), GetTunnelUserName());
properties->AppendItem(_("Authentication mode"), (GetAuthModePwd() ? _("Password") : _("Identity file")));
if(!GetAuthModePwd())
{
properties->AppendItem(_("Identity file path"), GetIdentityFile());
}
}
#endif
}
if(!GetConnected())
return;
if (form && GetCanHint() && !hintShown)
{
ShowHint(form, false);
}
}
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
bool pgServer::createSSHTunnel()
{
bool retVal = false;
tunnelObj = new CSSHTunnelThread(tunnelHost, GetName(), port, tunnelUserName, tunnelPassword, publicKeyFile,
identityFile, authModePwd ? AUTH_PASSWORD : AUTH_PUBLICKEY, tunnelPort);
if(tunnelObj)
{
if(tunnelObj->Initialize())
{
if ( tunnelObj->Create() != wxTHREAD_NO_ERROR )
{
delete tunnelObj;
tunnelObj = NULL;
wxLogError(_("SSH Error: Unable to create SSH Tunnling Thread"));
}
else
{
if (tunnelObj->Run() != wxTHREAD_NO_ERROR )
{
delete tunnelObj;
tunnelObj = NULL;
wxLogError(_("SSH Error: Unable to start SSH Tunnling Thread"));
}
SetLocalListenHost(tunnelObj->GetLocalListenIP());
SetLocalListenPort(tunnelObj->GetLocalListenPort());
retVal = true;
}
}
else
{
delete tunnelObj;
tunnelObj = NULL;
}
}
return retVal;
}
#endif
void pgServer::ShowStatistics(frmMain *form, ctlListView *statistics)
{
if (conn)
{
wxString pidcol = GetConnection()->BackendMinimumVersion(9, 2) ? wxT("pid") : wxT("procpid");
wxString querycol = GetConnection()->BackendMinimumVersion(9, 2) ? wxT("query") : wxT("current_query");
wxString sql;
wxString replication_query = wxT("state || ' (' || sent_location || ' sent, ' || write_location || ' written, ' || flush_location || ' flushed, ' || replay_location || ' applied)'");
if (GetConnection()->BackendMinimumVersion(10, 0)) {
replication_query = wxT("state || ' (' || sent_lsn || ' sent, ' || write_lsn || ' written, ' || flush_lsn || ' flushed, ' || replay_lsn || ' applied)'");
}
wxLogInfo(wxT("Displaying statistics for server %s"), GetIdentifier().c_str());
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(wxT("PID"), 35);
statistics->AddColumn(_("User"), 70);
statistics->AddColumn(_("Database"), 70);
if (GetConnection()->BackendMinimumVersion(8, 1))
{
statistics->AddColumn(_("Backend start"), 70);
statistics->AddColumn(_("Client"), 70);
}
statistics->AddColumn(_("Current Query"), 300);
sql = wxT("SELECT ") + pidcol + wxT(" AS pid, usename, datname, backend_start, client_addr, ");
if (GetConnection()->BackendMinimumVersion(9, 1))
sql += wxT("client_hostname, ");
sql += wxT("client_port, ") + querycol + wxT(" AS query FROM pg_stat_activity\n");
if (GetConnection()->BackendMinimumVersion(9, 1))
{
sql += wxT("UNION\n")
wxT("SELECT ") + pidcol + wxT(", usename, '' AS datname, backend_start, client_addr, client_hostname, client_port, ")
+ replication_query + wxT(" AS query FROM pg_stat_replication");
}
pgSet *stats = ExecuteSet(sql);
if (stats)
{
int pos = 0;
while (!stats->Eof())
{
statistics->InsertItem(pos, stats->GetVal(wxT("pid")), 0);
int colpos = 1;
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("usename")));
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("datname")));
if (GetConnection()->BackendMinimumVersion(8, 1))
{
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("backend_start")));
wxString client;
if (GetConnection()->BackendMinimumVersion(9, 1) && !stats->GetVal(wxT("client_hostname")).IsEmpty())
client = stats->GetVal(wxT("client_hostname")) + wxT(":") + stats->GetVal(wxT("client_port"));
else
client = stats->GetVal(wxT("client_addr")) + wxT(":") + stats->GetVal(wxT("client_port"));
if (client == wxT(":-1"))
client = _("local pipe");
statistics->SetItem(pos, colpos++, client);
}
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("query")));
stats->MoveNext();
pos++;
}
delete stats;
}
}
}
void pgServer::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString &wh)
{
}
void pgServer::ShowDependents(frmMain *form, ctlListView *referencedBy, const wxString &wh)
{
}
bool pgServer::ReloadConfiguration()
{
wxString sql = wxT("select pg_reload_conf()");
return conn->ExecuteVoid(sql);
}
bool pgServer::PauseReplay()
{
SetReplayPaused(true);
wxString sql = wxT("SELECT pg_xlog_replay_pause()");
if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_pause()");
return conn->ExecuteVoid(sql);
}
bool pgServer::ResumeReplay()
{
SetReplayPaused(false);
wxString sql = wxT("SELECT pg_xlog_replay_resume()");
if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_resume()");
return conn->ExecuteVoid(sql);
}
bool pgServer::AddNamedRestorePoint()
{
wxString namedrestorepoint = wxGetTextFromUser(_("Enter the name of the restore point to add"), _("Restore point name"));
if (!namedrestorepoint.IsEmpty())
{
wxString sql = wxT("select pg_create_restore_point(") + qtDbString(namedrestorepoint) + wxT(")");
return conn->ExecuteVoid(sql);
}
return false;
}
pgServerCollection::pgServerCollection(pgaFactory *factory)
: pgCollection(factory)
{
}
wxString pgServerCollection::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on servers");
break;
case REFRESHINGDETAILS:
message = _("Refreshing servers");
break;
case OBJECTSLISTREPORT:
message = _("Servers list report");
break;
}
return message;
}
pgServerObjCollection::pgServerObjCollection(pgaFactory *factory, pgServer *sv)
: pgCollection(factory)
{
server = sv;
}
bool pgServerObjCollection::CanCreate()
{
// We can't create resource queues on Greenplum yet.
if (IsCollectionForType(GP_RESOURCE_QUEUE))
return false;
// We can't create tablespaces on Greenplum
if (server->GetConnection()->GetIsGreenplum() && IsCollectionForType(PGM_TABLESPACE))
return false;
if (server->GetMetaType() == PGM_DATABASE)
return (GetServer()->GetCreatePrivilege() || GetServer()->GetSuperUser());
else
{
if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_ROLE)
return (server->GetCreateRole() || server->GetSuperUser());
else if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_DATABASE)
return (server->GetCreatePrivilege() || server->GetSuperUser());
else if (GetMetaType() == PGM_JOB)
return true;
else
return server->GetSuperUser();
}
}
pgObject *pgServerFactory::CreateObjects(pgCollection *obj, ctlTree *browser, const wxString &restr)
{
wxTreeItemId groupitem, serveritem;
wxTreeItemIdValue groupcookie;
bool found;
long numServers = settings->Read(wxT("Servers/Count"), 0L);
long loop, port, ssl = 0;
wxString key, servername, hostaddr, description, service, database, username, lastDatabase, lastSchema;
wxString storePwd, rolename, connstr, restore, serviceID, discoveryID, dbRestriction, colour;
wxString group, sslcert, sslkey, sslrootcert, sslcrl, sslcompression;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
wxString sshTunnel, authModePwd, tunnelHost, tunnelUserName, tunnelPassword, publicKeyFile, identityFile;
long tunnelPort;
#endif
pgServer *server = 0;
wxArrayString discoveredServers;
// Get the hostname for later...
char buf[255];
gethostname(buf, 255);
wxString hostname = wxString(buf, wxConvUTF8);
//wxLogError(wxT("Loading previously registered servers"));
wxLogInfo(wxT("Loading previously registered servers"));
for (loop = 1; loop <= numServers; ++loop)
{
key.Printf(wxT("Servers/%d/"), (int)loop);
settings->Read(key + wxT("Server"), &servername, wxEmptyString);
settings->Read(key + wxT("HostAddr"), &hostaddr, wxEmptyString);
settings->Read(key + wxT("Service"), &service, wxEmptyString);
settings->Read(key + wxT("ServiceID"), &serviceID, wxEmptyString);
settings->Read(key + wxT("DiscoveryID"), &discoveryID, serviceID);
settings->Read(key + wxT("Description"), &description, wxEmptyString);
settings->Read(key + wxT("StorePwd"), &storePwd, wxEmptyString);
settings->Read(key + wxT("Rolename"), &rolename, wxEmptyString);
settings->Read(key + wxT("ConnStr"), &connstr, wxEmptyString);
settings->Read(key + wxT("Restore"), &restore, wxT("true"));
settings->Read(key + wxT("Port"), &port, 0);
settings->Read(key + wxT("Database"), &database, wxEmptyString);
settings->Read(key + wxT("Username"), &username, wxEmptyString);
settings->Read(key + wxT("LastDatabase"), &lastDatabase, wxEmptyString);
settings->Read(key + wxT("LastSchema"), &lastSchema, wxEmptyString);
settings->Read(key + wxT("DbRestriction"), &dbRestriction, wxEmptyString);
settings->Read(key + wxT("Colour"), &colour, wxEmptyString);
settings->Read(key + wxT("Group"), &group, wxT("Servers"));
settings->Read(key + wxT("SSLCert"), &sslcert, wxEmptyString);
settings->Read(key + wxT("SSLKey"), &sslkey, wxEmptyString);
settings->Read(key + wxT("SSLRootCert"), &sslrootcert, wxEmptyString);
settings->Read(key + wxT("SSLCrl"), &sslcrl, wxEmptyString);
settings->Read(key + wxT("SSLCompression"), &sslcompression, wxT("true"));
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
settings->Read(key + wxT("SSHTunnel"), &sshTunnel, wxT("false"));
settings->Read(key + wxT("TunnelHost"), &tunnelHost, wxEmptyString);
settings->Read(key + wxT("TunnelUserName"), &tunnelUserName, wxEmptyString);
settings->Read(key + wxT("TunnelModePwd"), &authModePwd, wxT("true"));
settings->Read(key + wxT("PublicKeyFile"), &publicKeyFile, wxEmptyString);
settings->Read(key + wxT("IdentityFile"), &identityFile, wxEmptyString);
settings->Read(key + wxT("TunnelPort"), &tunnelPort, DEFAULT_SSH_PORT);
#endif
// Sanitize the colour
colour = colour.Trim();
if (!colour.IsEmpty())
{
wxColour cColour;
if (cColour.Set(colour))
colour = cColour.GetAsString(wxC2S_HTML_SYNTAX);
else
colour = wxEmptyString;
}
if (colour.IsEmpty())
{
wxColour cColour;
cColour.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW).GetAsString(wxC2S_HTML_SYNTAX));
colour = cColour.GetAsString(wxC2S_HTML_SYNTAX);
}
// SSL mode
#ifdef PG_SSL
settings->Read(key + wxT("SSL"), &ssl, 0);
#endif
// Sanitize the group
if (group.IsEmpty())
{
group = _("Servers");
}
// Add the Server node
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
server = new pgServer(servername, hostaddr, description, service, database, username, port, StrToBool(storePwd), rolename, connstr, StrToBool(restore), ssl,
colour, group, StrToBool(sshTunnel), tunnelHost, tunnelUserName, StrToBool(authModePwd), tunnelPassword, publicKeyFile, identityFile, tunnelPort);
#else
server = new pgServer(servername, hostaddr, description, service, database, username, port, StrToBool(storePwd), rolename, connstr, StrToBool(restore), ssl,
colour, group);
#endif
server->iSetLastDatabase(lastDatabase);
server->iSetLastSchema(lastSchema);
server->iSetService(service);
server->iSetServiceID(serviceID);
server->iSetDiscoveryID(discoveryID);
server->iSetDiscovered(false);
server->iSetDbRestriction(dbRestriction);
server->iSetServerIndex(loop);
server->SetSSLCert(sslcert);
server->SetSSLKey(sslkey);
server->SetSSLRootCert(sslrootcert);
server->SetSSLCrl(sslcrl);
server->iSetSSLCompression(StrToBool(sslcompression));
found = false;
if (browser->ItemHasChildren(obj->GetId()))
{
groupitem = browser->GetFirstChild(obj->GetId(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(obj->GetId(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(obj->GetId(), group, obj->GetIconId());
}
serveritem = browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
if (!server->GetColour().IsEmpty())
browser->SetItemBackgroundColour(serveritem, wxColour(server->GetColour()));
// Note if we're reloading a discovered server
if (!discoveryID.IsEmpty())
discoveredServers.Add(discoveryID);
}
group = _("Servers");
#ifdef __WXMSW__
// Add local servers. Will currently only work on Win32 with >= BETA3
// of the Win32 PostgreSQL installer.
wxLogInfo(wxT("Loading servers registered on the local machine"));
pgRegKey::PGREGWOWMODE wowMode = pgRegKey::PGREG_WOW_DEFAULT;
if (::wxIsPlatform64Bit())
wowMode = pgRegKey::PGREG_WOW32;
pgRegKey *pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
if (pgKey == NULL)
{
wowMode = pgRegKey::PGREG_WOW64;
pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
}
while (pgKey != NULL)
{
pgRegKey *svcKey = NULL;
wxString svcName;
long cookie = 0;
DWORD tmpport = 0;
bool flag = false;
flag = pgKey->GetFirstKey(svcKey, cookie);
while (flag != false)
{
svcName = svcKey->GetKeyName();
// On Windows, the discovery ID is always the service name.
// Only load the server if we didn't load it with all the others.
if (discoveredServers.Index(svcName, false) < 0)
{
servername = wxT("localhost");
database = wxEmptyString;
svcKey->QueryValue(wxT("Display Name"), description);
svcKey->QueryValue(wxT("Database Superuser"), username);
svcKey->QueryValue(wxT("Port"), &tmpport);
// Add the Server node
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, database, username, (long)tmpport, false, wxEmptyString, wxEmptyString, false);
server->iSetDiscoveryID(svcName);
server->iSetDiscovered(true);
server->iSetServiceID(svcName);
server->iSetGroup(group);
found = false;
if (browser->ItemHasChildren(browser->GetRootItem()))
{
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(browser->GetRootItem(), group, obj->GetIconId());
browser->SortChildren(browser->GetRootItem());
}
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
}
// Release the current registry key
delete svcKey;
// Get the next one...
flag = pgKey->GetNextKey(svcKey, cookie);
}
/* Release current registry key */
delete pgKey;
pgKey = NULL;
/*
* If wowMode is equal to WOW32, that means this machine is a 64 bit machine and we need to read now 64 bit registry
*/
if (wowMode == pgRegKey::PGREG_WOW32)
{
wowMode = pgRegKey::PGREG_WOW64;
pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
}
}
#endif // __WXMSW__
// Add local servers on non-Win32 platforms (on Win32, they will be picked up above)
#ifndef WIN32
// On Unix/Mac, the discovery ID can be anything. We use the PostgreSQL
// package config filename if it's present, as that is the only thing vaguely
// discoverable and unique to a given installation. We can do the same for
// other distros in the future if they drop a suitable file someplace.
// Look for any files that match the basic postgres*.ini pattern.
wxLogInfo(wxT("Loading servers registered on the local machine"));
if (wxFile::Exists(REGISTRY_FILE))
{
wxString version, locale;
long cookie;
wxFileInputStream fst(REGISTRY_FILE);
wxFileConfig *cnf = new wxFileConfig(fst);
// PostgreSQL servers
cnf->SetPath(wxT("/PostgreSQL"));
bool flag = cnf->GetFirstGroup(version, cookie);
while (flag)
{
// If there is no Version entry, this is probably an uninstalled server
if (cnf->Read(version + wxT("/Version"), wxEmptyString) != wxEmptyString)
{
// Only load this server if we haven't read it from the pgAdmin config
if (discoveredServers.Index(cnf->GetPath() + wxT("/") + version, false) < 0)
{
// Basic details
servername = wxT("localhost");
cnf->Read(version + wxT("/Description"), &description, wxT("PostgreSQL ") + version);
cnf->Read(version + wxT("/Superuser"), &username, wxEmptyString);
cnf->Read(version + wxT("/Port"), &port, 0);
// Add the item, if it looks sane
if (port != 0 && username != wxEmptyString)
{
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, wxT("postgres"), username, port, false, rolename, connstr, 0);
server->iSetDiscoveryID(cnf->GetPath() + wxT("/") + version);
server->iSetDiscovered(true);
server->iSetGroup(group);
found = false;
if (browser->ItemHasChildren(browser->GetRootItem()))
{
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(browser->GetRootItem(), group, obj->GetIconId());
browser->SortChildren(browser->GetRootItem());
}
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
}
}
}
flag = cnf->GetNextGroup(version, cookie);
}
// EnterpriseDB servers
cnf->SetPath(wxT("/EnterpriseDB"));
flag = cnf->GetFirstGroup(version, cookie);
while (flag)
{
// If there is no Version entry, this is probably an uninstalled server
if (cnf->Read(version + wxT("/Version"), wxEmptyString) != wxEmptyString)
{
// Only load this server if we haven't read it from the pgAdmin config
if (discoveredServers.Index(cnf->GetPath() + wxT("/") + version, false) < 0)
{
// Basic details
servername = wxT("localhost");
cnf->Read(version + wxT("/Description"), &description, wxT("EnterpriseDB ") + version);
cnf->Read(version + wxT("/Superuser"), &username, wxEmptyString);
cnf->Read(version + wxT("/Port"), &port, 0);
// Add the item, if it looks sane
if (port != 0 && username != wxEmptyString)
{
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, wxT("edb"), username, port, false, rolename, connstr, 0);
server->iSetDiscoveryID(cnf->GetPath() + wxT("/") + version);
server->iSetDiscovered(true);
groupitem = browser->GetFirstChild(obj->GetId(), groupcookie);
if (!groupitem.IsOk())
groupitem = browser->AppendItem(obj->GetId(), group, obj->GetIconId());
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
}
}
}
flag = cnf->GetNextGroup(version, cookie);
}
delete cnf;
browser->SortChildren(obj->GetId());
}
#endif // !WIN32
return server;
}
#include "images/servers.pngc"
#include "images/server.pngc"
#include "images/server-sm.pngc"
#include "images/serverbad.pngc"
#include "images/serverbad-sm.pngc"
pgServerFactory::pgServerFactory()
: pgaFactory(__("Server"), __("New Server Registration"), __("Create a new Server registration."), server_png_img, server_sm_png_img)
{
metaType = PGM_SERVER;
closedId = addIcon(serverbad_png_img);
smallClosedId = addIcon(serverbad_sm_png_img);
}
pgCollection *pgServerFactory::CreateCollection(pgObject *obj)
{
return new pgCollection(GetCollectionFactory());
}
pgCollection *pgServerObjFactory::CreateCollection(pgObject *obj)
{
return new pgServerObjCollection(GetCollectionFactory(), (pgServer *)obj);
}
pgServerFactory serverFactory;
static pgaCollectionFactory cf(&serverFactory, __("Servers"), servers_png_img);
#include "images/connect.pngc"
addServerFactory::addServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, _("&Add Server..."), _("Add a connection to a server."));
toolbar->AddTool(id, _("Add Server"), GetBundleSVG(connect_png_bmp, "connect.svg", wxSize(32, 32)), _("Add a connection to a server."), wxITEM_NORMAL);
}
wxWindow *addServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
int rc = PGCONN_BAD;
dlgServer dlg(&serverFactory, form, 0);
dlg.CenterOnParent();
while (rc != PGCONN_OK)
{
if (dlg.GoNew() != wxID_OK)
return 0;
pgServer *server = (pgServer *)dlg.CreateObject(0);
if (dlg.GetTryConnect())
{
wxBusyInfo waiting(wxString::Format(_("Connecting to server %s (%s:%d)"),
server->GetDescription().c_str(), server->GetName().c_str(), server->GetPort()), form);
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
rc = server->Connect(form, false, dlg.GetPassword(), true);
}
else
{
rc = PGCONN_OK;
server->InvalidatePassword();
}
switch (rc)
{
case PGCONN_OK:
{
int icon;
ctlTree *browser = form->GetBrowser();
wxTreeItemId groupitem, parentitem;
wxTreeItemIdValue groupcookie;
int total;
wxString label;
if (server->GetConnected())
icon = serverFactory.GetIconId();
else
icon = serverFactory.GetClosedIconId();
wxLogInfo(wxT("pgServer object initialised as required."));
// Add the new server in its group
wxString group = server->GetGroup();
if (group.Length() == 0)
group = _("Servers");
// Get the parent group
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!parentitem && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
parentitem = groupitem;
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
if (!parentitem)
parentitem = browser->AppendItem(browser->GetRootItem(), group, icon, -1);
browser->AppendItem(parentitem, server->GetFullName(), icon, -1, server);
browser->SortChildren(parentitem);
browser->Expand(parentitem);
total = browser->GetChildrenCount(parentitem, false);
label = group + wxT(" (") + NumToStr((long)total) + wxT(")");
browser->SetItemText(parentitem, label);
form->StoreServers();
return 0;
}
case PGCONN_DNSERR:
{
delete server;
break;
}
case PGCONN_BAD:
case PGCONN_BROKEN:
{
form->ReportConnError(server);
delete server;
break;
}
case PGCONN_SSHTUNNEL_ERROR:
{
delete server;
break;
}
default:
{
wxLogInfo(__("pgServer object didn't initialise because the user aborted."));
delete server;
return 0;
}
}
}
return 0;
}
startServiceFactory::startServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Start Service"), _("Start PostgreSQL Service"));
}
wxWindow *startServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Starting Service"));
bool rc = server->StartService();
if (rc)
form->execSelChange(server->GetId(), true);
form->EndMsg(rc);
return 0;
}
bool startServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetServerControllable() && !server->GetServerRunning();
}
return false;
}
stopServiceFactory::stopServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Stop Service"), _("Stop PostgreSQL Service"));
}
wxWindow *stopServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
wxMessageDialog msg(form, _("Are you sure you wish to shutdown this server?"),
_("Stop Service"), wxYES_NO | wxICON_QUESTION);
if (msg.ShowModal() == wxID_YES)
{
form->StartMsg(_("Stopping service"));
bool done = server->StopService();
if (done)
{
if (server->Disconnect(form))
{
form->GetBrowser()->DeleteChildren(server->GetId());
form->execSelChange(server->GetId(), true);
}
}
form->EndMsg(done);
}
return 0;
}
bool stopServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetServerControllable() && server->GetServerRunning();
}
return false;
}
connectServerFactory::connectServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("&Connect"), _("Connect to the selected server."));
}
wxWindow *connectServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->ReconnectServer(server);
return 0;
}
bool connectServerFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return !((pgServer *)obj)->GetConnected();
return false;
}
disconnectServerFactory::disconnectServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Disconnec&t server"), _("Disconnect from the selected server."));
}
wxWindow *disconnectServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
if (obj->CheckOpenDialogs(form->GetBrowser(), form->GetBrowser()->GetSelection()))
{
wxString msg = _("There are properties dialogues open for one or more objects belonging to a database which will be disconnected. Please close the properties dialogues and try again.");
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
}
else
{
pgServer *server = (pgServer *)obj;
server->Disconnect(form);
server->UpdateIcon(form->GetBrowser());
form->GetBrowser()->DeleteChildren(obj->GetId());
form->execSelChange(obj->GetId(), true);
}
return 0;
}
bool disconnectServerFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return ((pgServer *)obj)->GetConnected();
return false;
}
disconnectServerFactoryAll::disconnectServerFactoryAll(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Disconnec&t all servers"), _("Disconnect from all servers."));
}
wxWindow* disconnectServerFactoryAll::StartDialog(frmMain* form, pgObject* obj)
{
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
folderitem = form->GetBrowser()->GetFirstChild(form->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (form->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = form->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = form->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
if (CheckEnable(object)) {
if (object->CheckOpenDialogs(form->GetBrowser(), serveritem))
{
wxString msg = _("There are properties dialogues open for one or more objects belonging to a database which will be disconnected. Please close the properties dialogues and try again.");
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
}
else
{
server = (pgServer*)object;
server->Disconnect(form);
server->UpdateIcon(form->GetBrowser());
form->GetBrowser()->DeleteChildren(object->GetId());
form->execSelChange(object->GetId(), true);
}
}
}
serveritem = form->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = form->GetBrowser()->GetNextChild(form->GetBrowser()->GetRootItem(), foldercookie);
}
return 0;
}
bool disconnectServerFactoryAll::CheckEnable(pgObject* obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return ((pgServer*)obj)->GetConnected();
return false;
}
reloadconfServiceFactory::reloadconfServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Reload configuration"), _("Reload configuration"));
}
wxWindow *reloadconfServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Reloading configuration"));
bool rc = server->ReloadConfiguration();
form->EndMsg(rc);
return 0;
}
bool reloadconfServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() && server->connection()->BackendMinimumVersion(8, 1);
}
return false;
}
pausereplayServiceFactory::pausereplayServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Pause replay of WAL"), _("Pause replay of WAL"));
}
wxWindow *pausereplayServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Pausing replay of WAL"));
bool rc = server->PauseReplay();
form->EndMsg(rc);
return 0;
}
bool pausereplayServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() &&
server->connection()->BackendMinimumVersion(9, 1) &&
server->GetInRecovery() &&
!server->GetReplayPaused();
}
return false;
}
resumereplayServiceFactory::resumereplayServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Resume replay of WAL"), _("Resume replay of WAL"));
}
wxWindow *resumereplayServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Resuming replay of WAL"));
bool rc = server->ResumeReplay();
form->EndMsg(rc);
return 0;
}
bool resumereplayServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() &&
server->connection()->BackendMinimumVersion(9, 1) &&
server->GetInRecovery() &&
server->GetReplayPaused();
}
return false;
}
addnamedrestorepointServiceFactory::addnamedrestorepointServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Add named restore point"), _("Add named restore point"));
}
wxWindow *addnamedrestorepointServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Add named restore point"));
bool rc = server->AddNamedRestorePoint();
form->EndMsg(rc);
return 0;
}
bool addnamedrestorepointServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() && server->connection()->BackendMinimumVersion(9, 1) && !server->GetInRecovery();
}
return false;
}