1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // plugins.cpp - Plugin management for frmMain
9 //
10 //
11 //////////////////////////////////////////////////////////////////////////
12 
13 #include "pgAdmin3.h"
14 
15 // wxWindows headers
16 #include <wx/wx.h>
17 #include <wx/dir.h>
18 #include <wx/file.h>
19 
20 // Application headers
21 #include "frm/frmMain.h"
22 #include "db/pgConn.h"
23 #include "ctl/ctlMenuToolbar.h"
24 #include "schema/pgObject.h"
25 #include "schema/pgDatabase.h"
26 #include "schema/pgSchema.h"
27 #include "schema/pgTable.h"
28 #include "utils/sysSettings.h"
29 
LoadPluginUtilities()30 void frmMain::LoadPluginUtilities()
31 {
32 	if (pluginsDir.IsEmpty())
33 		return;
34 
35 	PluginUtility *util = new PluginUtility;
36 	ClearPluginUtility(util);
37 
38 	// Loop through all the ini files we find in the directory.
39 	wxString iniFile;
40 	wxDir iniDir(pluginsDir);
41 
42 	if (!iniDir.IsOpened())
43 		return;
44 
45 	wxLogInfo(wxT("Loading plugin ini files from %s"), pluginsDir.c_str());
46 
47 	bool cont = iniDir.GetFirst(&iniFile, wxT("*.ini"), wxDIR_FILES);
48 
49 	while(cont)
50 	{
51 		// Load the config file
52 		wxFileName utilIni(pluginsDir + wxT("/") + iniFile);
53 		if (!utilIni.FileExists())
54 		{
55 			cont = iniDir.GetNext(&iniFile);
56 			continue;
57 		}
58 
59 		wxLogInfo(wxT("Loading plugin utilities from %s"), utilIni.GetFullPath().c_str());
60 		wxString brCfg = FileRead(utilIni.GetFullPath());
61 
62 		wxStringTokenizer tkz(brCfg, wxT("\r\n"));
63 
64 		// Loop round the lines in the file. Everytime we find a new 'Title' value
65 		// we create the current plugin and start a new one
66 		while(tkz.HasMoreTokens())
67 		{
68 			wxString token = tkz.GetNextToken();
69 
70 			if (token.Trim() == wxEmptyString || token.StartsWith(wxT(";")))
71 				continue;
72 
73 			// Separator
74 			if (token.Lower().StartsWith(wxT("[separator]")))
75 			{
76 				// Add the previous app if required.
77 				AddPluginUtility(util);
78 				pluginsMenu->AppendSeparator();
79 			}
80 
81 			// Title
82 			if (token.Lower().StartsWith(wxT("title=")))
83 			{
84 				// Add the previous app if required.
85 				AddPluginUtility(util);
86 				util->title = token.AfterFirst('=').Trim();
87 			}
88 
89 			// Command
90 			if (token.Lower().StartsWith(wxT("command=")))
91 				util->command = token.AfterFirst('=').Trim();
92 
93 			// Description
94 			if (token.Lower().StartsWith(wxT("description=")))
95 				util->description = token.AfterFirst('=').Trim();
96 
97 			// KeyFile
98 			if (token.Lower().StartsWith(wxT("keyfile=")))
99 			{
100 				wxString keyfile = token.AfterFirst('=').Trim();
101 
102 				// Substitute path placeholders
103 				keyfile.Replace(wxT("$$BINDIR"), loadPath);
104 				keyfile.Replace(wxT("$$WORKINGDIR"), wxGetCwd());
105 				keyfile.Replace(wxT("$$PGBINDIR"), settings->GetPostgresqlPath());
106 				keyfile.Replace(wxT("$$EDBBINDIR"), settings->GetEnterprisedbPath());
107 				keyfile.Replace(wxT("$$SLONYBINDIR"), settings->GetSlonyPath());
108 
109 				util->keyfile = keyfile;
110 			}
111 
112 			// Platform
113 			if (token.Lower().StartsWith(wxT("platform=")))
114 				util->platform = token.AfterFirst('=').Trim();
115 
116 			// Server types
117 			if (token.Lower().StartsWith(wxT("servertype=")))
118 			{
119 				util->server_types.Clear();
120 
121 				// This is a comma delimited list of values going into an array.
122 				wxStringTokenizer valueTkz(token.AfterFirst('='), wxT(","));
123 
124 				while(valueTkz.HasMoreTokens())
125 					util->server_types.Add(valueTkz.GetNextToken());
126 			}
127 
128 			// Database
129 			if (token.Lower().StartsWith(wxT("database=")))
130 			{
131 				if (token.AfterFirst('=').Trim().Lower() == wxT("yes"))
132 					util->database = true;
133 				else
134 					util->database = false;
135 			}
136 
137 			// Applies to
138 			if (token.Lower().StartsWith(wxT("appliesto=")))
139 			{
140 				util->applies_to.Clear();
141 
142 				// This is a comma delimited list of values going into an array.
143 				wxStringTokenizer valueTkz(token.AfterFirst('='), wxT(","));
144 
145 				while(valueTkz.HasMoreTokens())
146 					util->applies_to.Add(valueTkz.GetNextToken());
147 			}
148 
149 			// Set password
150 			if (token.Lower().StartsWith(wxT("setpassword=")))
151 			{
152 				if (token.AfterFirst('=').Trim().Lower() == wxT("yes"))
153 					util->set_password = true;
154 				else
155 					util->set_password = false;
156 			}
157 			// Environment
158 			if (token.Lower().StartsWith(wxT("environment=")))
159 			{
160 				util->set_env.Clear();
161 
162 				// This is a comma delimited list of values going into an array.
163 				wxStringTokenizer valueTkz(token.AfterFirst('='), wxT(","));
164 
165 				while(valueTkz.HasMoreTokens())
166 					util->set_env.Add(valueTkz.GetNextToken());
167 			}
168 		}
169 
170 		// Add the last app if required.
171 		AddPluginUtility(util);
172 
173 		// Get the next file
174 		cont = iniDir.GetNext(&iniFile);
175 	}
176 
177 	if (util)
178 		delete util;
179 }
180 
181 // Add a new plugin to the collection.
AddPluginUtility(PluginUtility * util)182 void frmMain::AddPluginUtility(PluginUtility *util)
183 {
184 	// Platform name
185 #ifdef __WXMSW__
186 	wxString thisPlatform = wxT("windows");
187 #else
188 #ifdef __WXGTK__
189 	wxString thisPlatform = wxT("unix");
190 #else
191 	wxString thisPlatform = wxT("osx");
192 #endif
193 #endif
194 
195 	// Only add apps targeted to this, or any platform
196 	if (util->platform.Lower() == thisPlatform || util->platform == wxEmptyString)
197 	{
198 		// Only add an app with a title and command
199 		if (!util->title.IsEmpty() && !util->command.IsEmpty())
200 		{
201 			// We're only going to add this if the keyfile exists or isn't specified
202 			if (util->keyfile.IsEmpty() || wxFileExists(util->keyfile))
203 			{
204 				CreatePluginUtility(util);
205 				ClearPluginUtility(util);
206 				pluginUtilityCount++;
207 			}
208 		}
209 	}
210 }
211 
212 // Create a new Plugin utility factory
CreatePluginUtility(PluginUtility * util)213 void frmMain::CreatePluginUtility(PluginUtility *util)
214 {
215 	wxLogInfo(wxT("Adding plugin utility: %s"), util->title.c_str());
216 	wxLogInfo(wxT("              Command: %s"), util->command.c_str());
217 	wxLogInfo(wxT("          Description: %s"), util->description.c_str());
218 	wxLogInfo(wxT("            Database?: %s"), util->database ? wxT("Yes") : wxT("No"));
219 	wxLogInfo(wxT("        Set Password?: %s"), util->set_password ? wxT("Yes") : wxT("No"));
220 
221 	new pluginUtilityFactory(menuFactories, pluginsMenu, util);
222 }
223 
224 // Clear a PluginUtility struct
ClearPluginUtility(PluginUtility * util)225 void frmMain::ClearPluginUtility(PluginUtility *util)
226 {
227 	util->title = wxEmptyString;
228 	util->command = wxEmptyString;
229 	util->description = wxEmptyString;
230 	util->keyfile = wxEmptyString;
231 	util->platform = wxEmptyString;
232 	util->server_types.Clear();
233 	util->database = false;
234 	util->applies_to.Clear();
235 	util->set_password = false;
236 	util->set_env.Clear();
237 }
238 
239 // The actionFactory for the plugin utilities
pluginUtilityFactory(menuFactoryList * list,wxMenu * menu,PluginUtility * util)240 pluginUtilityFactory::pluginUtilityFactory(menuFactoryList *list, wxMenu *menu, PluginUtility *util) : actionFactory(list)
241 {
242 	title = util->title;
243 	command = util->command;
244 	description = util->description;
245 	server_types = util->server_types;
246 	database = util->database;
247 	applies_to = util->applies_to;
248 	set_password = util->set_password;
249 	set_env = util->set_env;
250 
251 	menu->Append(id, title, description);
252 }
253 
254 
StartDialog(frmMain * form,pgObject * obj)255 wxWindow *pluginUtilityFactory::StartDialog(frmMain *form, pgObject *obj)
256 {
257 	wxString execCmd = command;
258 	wxArrayString environment = set_env;
259 
260 	// Remember this as the last plugin used
261 	form->SetLastPluginUtility(this);
262 
263 	// Replace all the place holders with appropriate values
264 	if (HaveDatabase(obj))
265 	{
266 		execCmd.Replace(wxT("$$HOSTNAME"), obj->GetConnection()->GetHostName());
267 		execCmd.Replace(wxT("$$HOSTADDR"), obj->GetConnection()->GetHostName());
268 		execCmd.Replace(wxT("$$PORT"), NumToStr((long)obj->GetConnection()->GetPort()));
269 		execCmd.Replace(wxT("$$SSLMODE"), obj->GetConnection()->GetSslModeName());
270 		execCmd.Replace(wxT("$$DATABASE"), obj->GetConnection()->GetDbname());
271 		execCmd.Replace(wxT("$$USERNAME"), obj->GetConnection()->GetUser());
272 		execCmd.Replace(wxT("$$PASSWORD"), obj->GetConnection()->GetPassword());
273 
274 		// Set the PGPASSWORD variable if required.
275 		if (set_password && !obj->GetConnection()->GetPassword().IsEmpty())
276 			wxSetEnv(wxT("PGPASSWORD"), obj->GetConnection()->GetPassword());
277 
278 		// Pass the SSL settings via the environment
279 		switch (obj->GetConnection()->GetSslMode())
280 		{
281 			case 1:
282 				wxSetEnv(wxT("PGREQUIRESSL"), wxT("1"));
283 				break;
284 			case 2:
285 				wxSetEnv(wxT("PGREQUIRESSL"), wxT("0"));
286 				break;
287 		}
288 
289 		wxSetEnv(wxT("PGSSLMODE"), obj->GetConnection()->GetSslModeName());
290 		wxSetEnv(wxT("PGSSLCERT"), obj->GetConnection()->GetSSLCert());
291 		wxSetEnv(wxT("PGSSLKEY"), obj->GetConnection()->GetSSLKey());
292 		wxSetEnv(wxT("PGSSLROOTCERT"), obj->GetConnection()->GetSSLRootCert());
293 		wxSetEnv(wxT("PGSSLCRL"), obj->GetConnection()->GetSSLCrl());
294 	}
295 	else
296 	{
297 		// Blank the rest
298 		execCmd.Replace(wxT("$$HOSTNAME"), wxEmptyString);
299 		execCmd.Replace(wxT("$$HOSTADDR"), wxEmptyString);
300 		execCmd.Replace(wxT("$$PORT"), wxEmptyString);
301 		execCmd.Replace(wxT("$$SSLMODE"), wxEmptyString);
302 		execCmd.Replace(wxT("$$DATABASE"), wxEmptyString);
303 		execCmd.Replace(wxT("$$USERNAME"), wxEmptyString);
304 		execCmd.Replace(wxT("$$PASSWORD"), wxEmptyString);
305 	}
306 
307 	// Name
308 	if (obj && obj->IsCollection())
309 		execCmd.Replace(wxT("$$OBJECTNAME"), wxT("*"));
310 	else if (obj)
311 		execCmd.Replace(wxT("$$OBJECTNAME"), obj->GetName());
312 	else
313 		execCmd.Replace(wxT("$$OBJECTNAME"), wxEmptyString);
314 
315 	// Object type
316 	if (obj && obj->GetFactory())
317 		execCmd.Replace(wxT("$$OBJECTTYPE"), wxString(obj->GetFactory()->GetTypeName()).Upper());
318 	else
319 		execCmd.Replace(wxT("$$OBJECTTYPE"), wxEmptyString);
320 
321 	// Schema
322 	if (obj)
323 	{
324 		if (obj->GetMetaType() == PGM_SCHEMA)
325 			execCmd.Replace(wxT("$$SCHEMA"), obj->GetName());
326 		else if (obj->GetSchema())
327 			execCmd.Replace(wxT("$$SCHEMA"), obj->GetSchema()->GetName());
328 	}
329 	else
330 		execCmd.Replace(wxT("$$SCHEMA"), wxEmptyString);
331 
332 	// Table
333 	if (obj)
334 	{
335 		if (obj->GetMetaType() == PGM_TABLE || obj->GetMetaType() == GP_PARTITION)
336 			execCmd.Replace(wxT("$$TABLE"), obj->GetName());
337 		else if (obj->GetTable())
338 			execCmd.Replace(wxT("$$TABLE"), obj->GetTable()->GetName());
339 	}
340 	else
341 		execCmd.Replace(wxT("$$TABLE"), wxEmptyString);
342 
343 	// Directory substitutions
344 	execCmd.Replace(wxT("$$BINDIR"), loadPath);
345 	execCmd.Replace(wxT("$$WORKINGDIR"), wxGetCwd());
346 	execCmd.Replace(wxT("$$PGBINDIR"), settings->GetPostgresqlPath());
347 	execCmd.Replace(wxT("$$EDBBINDIR"), settings->GetEnterprisedbPath());
348 	execCmd.Replace(wxT("$$SLONYBINDIR"), settings->GetSlonyPath());
349 
350 	// set Environment variable.
351 	for (size_t i = 0 ; i < environment.GetCount() ; i++)
352 	{
353 		wxString str = environment.Item(i);
354 		wxSetEnv(str.BeforeFirst('='), str.AfterFirst('='));
355 	}
356 
357 	// Let's go!!
358 	if (wxExecute(execCmd) == 0)
359 	{
360 		wxLogError(_("Failed to execute plugin %s (%s)"), title.c_str(), command.c_str());
361 	}
362 
363 	// Reset the environment variables set by us
364 	wxUnsetEnv(wxT("PGPASSWORD"));
365 	wxUnsetEnv(wxT("PGSSLMODE"));
366 	wxUnsetEnv(wxT("PGREQUIRESSL"));
367 	wxUnsetEnv(wxT("PGSSLCERT"));
368 	wxUnsetEnv(wxT("PGSSLKEY"));
369 	wxUnsetEnv(wxT("PGSSLROOTCERT"));
370 	wxUnsetEnv(wxT("PGSSLCRL"));
371 
372 	return 0;
373 }
374 
CheckEnable(pgObject * obj)375 bool pluginUtilityFactory::CheckEnable(pgObject *obj)
376 {
377 	// First check that this is one of the supported server types
378 	// for this plugin. If none are specified, then anything goes
379 	if (database && server_types.Count() > 0)
380 	{
381 		// If we need a specific server type, we can't enable unless
382 		// we have a connection.
383 		if (!obj || !(obj->GetConnection()->GetStatus() == PGCONN_OK))
384 			return false;
385 
386 		// Get the server type.
387 		wxString serverType = wxT("postgresql");
388 		if (obj->GetConnection()->GetIsEdb())
389 			serverType = wxT("enterprisedb");
390 
391 		// Check if it's in the list.
392 		if (server_types.Index(serverType) == wxNOT_FOUND)
393 			return false;
394 	}
395 
396 	// Now check that this is one of the supported object types
397 	// for this plugin. If none are specified, then anything goes
398 	if (obj && applies_to.Count() > 0)
399 	{
400 		if (applies_to.Index(wxString(obj->GetFactory()->GetTypeName()).Lower()) == wxNOT_FOUND)
401 			return false;
402 	}
403 
404 	// If we don't need a database, we're always OK.
405 	if (!database)
406 		return true;
407 
408 	return HaveDatabase(obj);
409 }
410 
HaveDatabase(pgObject * obj)411 bool pluginUtilityFactory::HaveDatabase(pgObject *obj)
412 {
413 	// We need a good connection and database.
414 	if (!obj)
415 		return false;
416 
417 	if (!obj->GetDatabase())
418 		return false;
419 
420 	if (!obj->GetDatabase()->GetConnection())
421 		return false;
422 
423 	if (!(obj->GetDatabase()->GetConnection()->GetStatus() == PGCONN_OK))
424 		return false;
425 
426 	return true;
427 }
428 
429 // The pluginButtonMenuFactory class manages the toolbar menu button
430 // for the plugins.
431 
432 #include "images/plugins.pngc"
pluginButtonMenuFactory(menuFactoryList * list,wxMenu * popupmenu,ctlMenuToolbar * toolbar,int pluginCount)433 pluginButtonMenuFactory::pluginButtonMenuFactory(menuFactoryList *list, wxMenu *popupmenu, ctlMenuToolbar *toolbar, int pluginCount) : actionFactory(list)
434 {
435 	if (pluginCount)
436 		enableButton = true;
437 	else
438 		enableButton = false;
439 
440 	if (toolbar)
441 	{
442 		toolbar->AddTool(id, wxEmptyString, *plugins_png_bmp, _("Execute the last used plugin."));
443 		pulldownButton = toolbar->AddMenuPulldownTool(MNU_PLUGINBUTTONLIST, wxT("Execute Plugin"), wxT("Select a plugin."), popupmenu);
444 	}
445 }
446 
447 // Call the last plugin used, or popup the menu if this is the first time
StartDialog(frmMain * form,pgObject * obj)448 wxWindow *pluginButtonMenuFactory::StartDialog(frmMain *form, pgObject *obj)
449 {
450 	if (form->GetLastPluginUtility() && form->GetLastPluginUtility()->CheckEnable(obj))
451 		return form->GetLastPluginUtility()->StartDialog(form, obj);
452 	else
453 	{
454 		wxMouseEvent evt;
455 		pulldownButton->DoProcessLeftClick(evt);
456 	}
457 
458 	return 0;
459 }
460 
CheckEnable(pgObject * obj)461 bool pluginButtonMenuFactory::CheckEnable(pgObject *obj)
462 {
463 	return enableButton;
464 }
465