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