1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11354 $
6  * $Id: pluginmanager.cpp 11354 2018-03-31 21:50:05Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/pluginmanager.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include <wx/dir.h>
14     #include <wx/filesys.h>
15     #include <wx/intl.h>
16     #include <wx/menu.h>
17     #include <wx/string.h>
18 
19     #include "pluginmanager.h"
20     #include "cbexception.h"
21     #include "cbplugin.h"
22     #include "infowindow.h"
23     #include "logmanager.h"
24     #include "macrosmanager.h"
25     #include "manager.h"
26     #include "editormanager.h"
27     #include "configmanager.h"
28     #include "personalitymanager.h"
29     #include "scriptingmanager.h"
30     #include "globals.h"
31     #include "sdk_events.h"
32 #endif
33 
34 #include <algorithm>
35 
36 #include <wx/dynlib.h>
37 #include <wx/filesys.h>
38 #include <wx/progdlg.h>
39 #include <wx/utils.h>
40 #include <wx/filename.h>
41 
42 #include <wx/wfstream.h>
43 #include <wx/zipstrm.h>
44 #include <wx/txtstrm.h>
45 
46 #include "filefilters.h"
47 #include <tinyxml.h>
48 
49 #include "annoyingdialog.h"
50 #include "pluginsconfigurationdlg.h"
51 
52 #include "scripting/bindings/sc_plugin.h"
53 
54 template<> PluginManager* Mgr<PluginManager>::instance = nullptr;
55 template<> bool  Mgr<PluginManager>::isShutdown = false;
56 
VersionStringToNumbers(const wxString & version,long * major,long * minor,long * release)57 inline void VersionStringToNumbers(const wxString& version, long* major, long* minor, long* release)
58 {
59     wxString majorS = version.BeforeFirst(_T('.')); // 6.3.2 -> 6
60     wxString minorS = version.AfterFirst(_T('.')); // 6.3.2 -> 3.2
61     wxString releaseS = version.AfterLast(_T('.')); // 6.3.2 -> 2
62     minorS = minorS.BeforeFirst(_T('.')); // 3.2 -> 3
63     if (major)
64         majorS.ToLong(major);
65     if (minor)
66         minorS.ToLong(minor);
67     if (release)
68         releaseS.ToLong(release);
69 }
70 
71 // returns -1 if new is less then old, 0 if equal and 1 if new is greater than old
CompareVersions(const wxString & new_version,const wxString & old_version)72 inline int CompareVersions(const wxString& new_version, const wxString& old_version)
73 {
74     long new_major, new_minor, new_release;
75     long old_major, old_minor, old_release;
76 
77     VersionStringToNumbers(new_version, &new_major, &new_minor, &new_release);
78     VersionStringToNumbers(old_version, &old_major, &old_minor, &old_release);
79 
80 #define SIGN(a) (a>0?1:(a<0?-1:0))
81     int result = 0;
82     result += SIGN(new_major - old_major) << 2;
83     result += SIGN(new_minor - old_minor) << 1;
84     result += SIGN(new_release - old_release) << 0;
85 #undef SIGN
86 
87     if (result < 0) return -1;
88     else if (result > 0) return 1;
89     return 0;
90 }
91 
92 namespace LibLoader
93 {
94     struct RefCountedLib
95     {
RefCountedLibLibLoader::RefCountedLib96         RefCountedLib() : lib(nullptr), ref(0) {}
97         wxDynamicLibrary* lib;
98         int ref;
99     };
100     typedef std::map<wxString, RefCountedLib> Libs;
101     Libs s_Libs;
102 
LoadLibrary(const wxString & filename)103     inline wxDynamicLibrary* LoadLibrary(const wxString& filename)
104     {
105         Libs::iterator it = s_Libs.find(filename);
106         if (it != s_Libs.end())
107         {
108             // existing lib./codeblocks
109             it->second.ref++;
110             return it->second.lib;
111         }
112         // new lib
113         it = s_Libs.insert(s_Libs.end(), std::make_pair(filename, RefCountedLib()));
114         it->second.lib = new wxDynamicLibrary;
115         it->second.ref = 1;
116         it->second.lib->Load(filename);
117         return it->second.lib;
118     }
119 
RemoveLibrary(wxDynamicLibrary * lib)120     inline void RemoveLibrary(wxDynamicLibrary* lib)
121     {
122         Libs::iterator it;
123         for (it = s_Libs.begin(); it != s_Libs.end(); ++it)
124         {
125             RefCountedLib& rcl = it->second;
126             if (rcl.lib == lib)
127             {
128                 // found
129                 rcl.ref--;
130                 if (rcl.ref == 0)
131                 {
132                     // only delete the lib if not shutting down
133                     // if we are shutting down, it will be deleted automatically
134                     if (!Manager::IsAppShuttingDown())
135                         delete rcl.lib;
136                     s_Libs.erase(it);
137                 }
138                 return;
139             }
140         }
141         // if we reached here, it's a lib that was not handled by us
142         // (or had wrong refcounting)
143     }
144 
Cleanup()145     inline void Cleanup()
146     {
147         Libs::iterator it;
148         for (it = s_Libs.begin(); it != s_Libs.end(); ++it)
149         {
150             RefCountedLib& rcl = it->second;
151             // only delete the lib if not shutting down
152             // if we are shutting down, it will be deleted automatically
153             if (!Manager::IsAppShuttingDown())
154                 delete rcl.lib;
155         }
156         s_Libs.clear();
157     }
158 };
159 
160 //static
161 bool PluginManager::s_SafeMode = false;
162 
BEGIN_EVENT_TABLE(PluginManager,wxEvtHandler)163 BEGIN_EVENT_TABLE(PluginManager, wxEvtHandler)
164 //
165 END_EVENT_TABLE()
166 
167 // class constructor
168 PluginManager::PluginManager()
169     : m_pCurrentlyLoadingLib(nullptr),
170     m_pCurrentlyLoadingManifestDoc(nullptr)
171 {
172     Manager::Get()->GetAppWindow()->PushEventHandler(this);
173 }
174 
175 // class destructor
~PluginManager()176 PluginManager::~PluginManager()
177 {
178     UnloadAllPlugins();
179 }
180 
CreateMenu(cb_unused wxMenuBar * menuBar)181 void PluginManager::CreateMenu(cb_unused wxMenuBar* menuBar)
182 {
183 }
184 
ReleaseMenu(cb_unused wxMenuBar * menuBar)185 void PluginManager::ReleaseMenu(cb_unused wxMenuBar* menuBar)
186 {
187 }
188 
AttachPlugin(cbPlugin * plugin,bool ignoreSafeMode)189 bool PluginManager::AttachPlugin(cbPlugin* plugin, bool ignoreSafeMode)
190 {
191     if (!plugin)
192         return false;
193     if (plugin->IsAttached())
194         return true;
195 
196     if (!s_SafeMode || ignoreSafeMode)
197         plugin->Attach();
198     return true;
199 }
200 
DetachPlugin(cbPlugin * plugin)201 bool PluginManager::DetachPlugin(cbPlugin* plugin)
202 {
203     if (!plugin)
204         return false;
205     if (!plugin->IsAttached())
206         return true;
207 
208     Manager::Get()->RemoveAllEventSinksFor(plugin);
209     plugin->Release(Manager::IsAppShuttingDown());
210     return true;
211 }
212 
InstallPlugin(const wxString & pluginName,bool forAllUsers,bool askForConfirmation)213 bool PluginManager::InstallPlugin(const wxString& pluginName, bool forAllUsers, bool askForConfirmation)
214 {
215     if (pluginName.IsEmpty())
216         return false;
217 
218     wxString actualName = pluginName;
219     Manager::Get()->GetMacrosManager()->ReplaceMacros(actualName);
220 
221     // base name
222     wxString basename = wxFileName(actualName).GetName();
223     wxString version;
224     if (basename.Contains(_T('-')))
225     {
226         version = basename.AfterFirst(_T('-'));
227         basename = basename.BeforeFirst(_T('-'));
228     }
229 
230 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("InstallPlugin: basename='%s', version=%s"), basename.c_str(), version.c_str()));
231 
232     // if plugin with the same name exists, ask to uninstall first
233     cbPlugin* existingPlugin = FindPluginByName(basename);
234     if (existingPlugin)
235     {
236         if (askForConfirmation)
237         {
238             wxString msg = _("A plugin with the same name is already installed.\n");
239             if (!version.IsEmpty())
240             {
241                 const PluginInfo* existingInfo = GetPluginInfo(existingPlugin);
242                 if (CompareVersions(version, existingInfo->version) < 0)
243                 {
244                     msg = _("The plugin you are trying to install, is older "
245                             "than the one currently installed.");
246                 }
247             }
248 
249             if (cbMessageBox(msg + _T('\n') +
250                             _("If you want to proceed, the installed plugin will be "
251                             "uninstalled first.\n"
252                             "Do you want to proceed?"),
253                             _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxID_NO)
254             {
255                 return false;
256             }
257         }
258         if (!UninstallPlugin(existingPlugin))
259             return false;
260     }
261 
262     wxString pluginDir;
263     wxString resourceDir;
264     if (forAllUsers)
265     {
266         pluginDir = ConfigManager::GetFolder(sdPluginsGlobal);
267         resourceDir = ConfigManager::GetFolder(sdDataGlobal);
268     }
269     else
270     {
271         pluginDir = ConfigManager::GetFolder(sdPluginsUser);
272         resourceDir = ConfigManager::GetFolder(sdDataUser);
273     }
274 
275     wxProgressDialog pd(_("Installing: ") + basename, _T("A description wide enough for the dialog ;)"), 5);
276 
277     wxString localName = basename + FileFilters::DYNAMICLIB_DOT_EXT;
278     wxString resourceName = basename + _T(".zip");
279     wxString settingsOnName = basename + _T(".png");
280     wxString settingsOffName = basename + _T("-off.png");
281     if (!platform::windows && resourceName.StartsWith(_T("lib")))
282         resourceName.Remove(0, 3);
283     if (!platform::windows && settingsOnName.StartsWith(_T("lib")))
284         settingsOnName.Remove(0, 3);
285     if (!platform::windows && settingsOffName.StartsWith(_T("lib")))
286         settingsOffName.Remove(0, 3);
287     wxString pluginFilename = UnixFilename(pluginDir + _T('/') + localName);
288 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + pluginFilename));
289 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin resources: ") + ConfigManager::GetDataFolder() + _T('/') + resourceName));
290 
291     pd.Update(1, _("Extracting plugin"));
292 
293     // extract plugin from bundle
294     if (!ExtractFile(actualName,
295                     localName,
296                     pluginFilename))
297         return false;
298 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted plugin")));
299 
300     pd.Update(2, _("Extracting plugin resources"));
301 
302     // extract resources from bundle
303     if (!ExtractFile(actualName,
304                     resourceName,
305                     resourceDir + _T('/') + resourceName))
306         return false;
307 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
308 
309     pd.Update(3, _("Extracting plugin icons for \"Settings\" dialog"));
310 
311     // extract resources from bundle
312     ExtractFile(actualName,
313                 settingsOnName,
314                 resourceDir + _T("/images/settings/") + settingsOnName,
315                 false);
316 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
317 
318     // extract resources from bundle
319     ExtractFile(actualName,
320                 settingsOffName,
321                 resourceDir + _T("/images/settings/") + settingsOffName,
322                 false);
323 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
324 
325     // extract extra files
326     wxArrayString extraFiles;
327     ReadExtraFilesFromManifestFile(localName, extraFiles);
328     for (size_t i = 0; i < extraFiles.GetCount(); ++i)
329     {
330         ExtractFile(actualName,
331                     extraFiles[i],
332                     resourceDir + _T("/") + extraFiles[i],
333                     false);
334     }
335 
336     pd.Update(4, _("Loading plugin"));
337 
338     // bundle extracted; now load the plugin on-the-fly
339 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Loading plugin...")));
340     ScanForPlugins(pluginDir);
341     LoadAllPlugins();
342     cbPlugin* plugin = FindPluginByFileName(pluginFilename);
343     const PluginInfo* info = GetPluginInfo(plugin);
344     if (!plugin || !info)
345     {
346         Manager::Get()->GetLogManager()->DebugLog(_T("Failed"));
347         return false;
348     }
349 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Succeeded")));
350 
351     // inform app to update menus and toolbars
352     pd.Update(5, _("Updating menus and toolbars"));
353     CodeBlocksEvent evt(cbEVT_PLUGIN_INSTALLED);
354     evt.SetPlugin(plugin);
355     Manager::Get()->ProcessEvent(evt);
356 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Menus updated")));
357 
358     return true;
359 }
360 
UninstallPlugin(cbPlugin * plugin,bool removeFiles)361 bool PluginManager::UninstallPlugin(cbPlugin* plugin, bool removeFiles)
362 {
363     if (!plugin)
364         return false;
365 
366     wxString title;
367     wxString pluginFilename;
368     wxString resourceFilename;
369     wxString settingsOnFilename;
370     wxString settingsOffFilename;
371     wxArrayString extrafiles;
372 
373     // find the plugin element
374     for (size_t i = 0; i < m_Plugins.GetCount(); ++i)
375     {
376         PluginElement* elem = m_Plugins[i];
377         if (elem && elem->plugin == plugin)
378         {
379             // got it
380             title = elem->info.title;
381             pluginFilename = elem->fileName;
382             // now get the resource name
383             wxFileName fname(pluginFilename);
384             resourceFilename = fname.GetName() + _T(".zip");
385             settingsOnFilename = fname.GetName() + _T(".png");
386             settingsOffFilename = fname.GetName() + _T("-off.png");
387             if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
388                 resourceFilename.Remove(0, 3);
389             if (!platform::windows && settingsOnFilename.StartsWith(_T("lib")))
390                 settingsOnFilename.Remove(0, 3);
391             if (!platform::windows && settingsOffFilename.StartsWith(_T("lib")))
392                 settingsOffFilename.Remove(0, 3);
393             resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
394             settingsOnFilename = ConfigManager::LocateDataFile(_T("images/settings/") + settingsOnFilename, sdDataGlobal | sdDataUser);
395             settingsOffFilename = ConfigManager::LocateDataFile(_T("images/settings/") + settingsOffFilename, sdDataGlobal | sdDataUser);
396 
397             ReadExtraFilesFromManifestFile(resourceFilename, extrafiles);
398             for (size_t n = 0; n < extrafiles.GetCount(); ++n)
399             {
400                 extrafiles[n] = ConfigManager::LocateDataFile(extrafiles[n], sdDataGlobal | sdDataUser);
401             }
402             break;
403         }
404     }
405 
406     if (wxFileExists(pluginFilename) && !wxFile::Access(pluginFilename, wxFile::write))
407     {
408         // no write-access; abort
409         cbMessageBox(_("You don't have the needed privileges to uninstall this plugin.\n"
410                         "Ask your administrator to uninstall this plugin for you..."),
411                         _("Warning"), wxICON_WARNING);
412         return false;
413     }
414 
415 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("UninstallPlugin:")));
416 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + pluginFilename));
417 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin resources: ") + resourceFilename));
418 
419     wxProgressDialog pd(wxString::Format(_("Uninstalling %s"), title.c_str()),
420                         _T("A description wide enough for the dialog ;)"), 3);
421 
422     pd.Update(1, _("Detaching plugin"));
423     DetachPlugin(plugin);
424 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin released")));
425 
426     pd.Update(2, _("Updating menus and toolbars"));
427     CodeBlocksEvent event(cbEVT_PLUGIN_UNINSTALLED);
428     event.SetPlugin(plugin);
429     Manager::Get()->ProcessEvent(event);
430 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Menus updated")));
431 
432     pd.Update(3, _("Unloading plugin"));
433     UnloadPlugin(plugin);
434 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin unloaded")));
435 
436     if (!removeFiles)
437         return true;
438 
439     // under linux, if the progress dialog is still visible and updated
440     // causes a crash because it re-enters gtk_main_iteration() calling
441     // eventually OnUpdateUI() in the config dialog, which in turn references
442     // an invalid plugin...
443 //    pd.Update(4, _("Removing files"));
444 
445     if (!pluginFilename.IsEmpty())
446     {
447         if (wxRemoveFile(pluginFilename))
448         {
449 //            Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin file removed")));
450             if (!resourceFilename.IsEmpty())
451             {
452                 if (!wxRemoveFile(resourceFilename))
453                     Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove plugin resources: ") + resourceFilename);
454             }
455             if (!settingsOnFilename.IsEmpty() && wxFileExists(settingsOnFilename))
456             {
457                 if (!wxRemoveFile(settingsOnFilename))
458                     Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove icon for \"Settings\" dialog: ") + settingsOnFilename);
459             }
460             if (!settingsOffFilename.IsEmpty() && wxFileExists(settingsOffFilename))
461             {
462                 if (!wxRemoveFile(settingsOffFilename))
463                     Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove icon for \"Settings\" dialog: ") + settingsOffFilename);
464             }
465             for (size_t i = 0; i < extrafiles.GetCount(); ++i)
466             {
467                 if (!extrafiles[i].IsEmpty() && wxFileExists(extrafiles[i]))
468                 {
469                     if (!wxRemoveFile(extrafiles[i]))
470                         Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove extra file: ") + extrafiles[i]);
471                 }
472             }
473             return true;
474         }
475         else
476         {
477             Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove plugin file: ") + pluginFilename);
478             cbMessageBox(_("Plugin could not be completely uninstalled because its files could not be removed.\n\n"
479                             "This can happen if the plugin's file is in-use like, for "
480                             "example, when the same plugin file provides more than one "
481                             "plugin.\n"
482                             "In this case either uninstall all other plugins "
483                             "which are provided by the same file, or remove it yourself "
484                             "(manually) when you shut down Code::Blocks.\n"
485                             "The files that could not be deleted are:\n\n") +
486                             pluginFilename + _T('\n') +
487                             resourceFilename + _T('\n') +
488                             settingsOnFilename + _T('\n') +
489                             settingsOffFilename,
490                             _("Warning"), wxICON_WARNING);
491             return false;
492         }
493     }
494     return false;
495 }
496 
ExportPlugin(cbPlugin * plugin,const wxString & filename)497 bool PluginManager::ExportPlugin(cbPlugin* plugin, const wxString& filename)
498 {
499     if (!plugin)
500         return false;
501 
502     wxArrayString sourcefiles;
503     wxArrayString extrafiles;
504     wxArrayString extrafilesdest;
505     wxFileName fname;
506     wxString resourceFilename;
507 
508     // find the plugin element
509     for (size_t i = 0; i < m_Plugins.GetCount(); ++i)
510     {
511         PluginElement* elem = m_Plugins[i];
512         if (elem && elem->plugin == plugin)
513         {
514             // got it
515 
516             // plugin file
517             sourcefiles.Add(elem->fileName);
518             fname.Assign(elem->fileName);
519 
520             // now get the resource zip filename
521             resourceFilename = fname.GetName() + _T(".zip");
522             if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
523                 resourceFilename.Remove(0, 3);
524             resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
525             sourcefiles.Add(resourceFilename);
526 
527             // the highlighted icon the plugin may have for its "settings" page
528             resourceFilename = fname.GetName() + _T(".png");
529             if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
530                 resourceFilename.Remove(0, 3);
531             resourceFilename.Prepend(_T("images/settings/"));
532             resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
533             if (!resourceFilename.IsEmpty())
534                 sourcefiles.Add(resourceFilename);
535 
536             // the non-highlighted icon the plugin may have for its "settings" page
537             resourceFilename = fname.GetName() + _T("-off.png");
538             if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
539                 resourceFilename.Remove(0, 3);
540             resourceFilename.Prepend(_T("images/settings/"));
541             resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
542             if (!resourceFilename.IsEmpty())
543                 sourcefiles.Add(resourceFilename);
544 
545             // export extra files
546             resourceFilename = fname.GetName() + _T(".zip");
547             if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
548                 resourceFilename.Remove(0, 3);
549             ReadExtraFilesFromManifestFile(resourceFilename, extrafilesdest);
550             for (size_t n = 0; n < extrafilesdest.GetCount(); ++n)
551             {
552                 extrafiles.Add(ConfigManager::LocateDataFile(extrafilesdest[n], sdDataGlobal | sdDataUser));
553             }
554 
555             break;
556         }
557     }
558 
559     if (wxFileExists(filename))
560     {
561         if (!wxFile::Access(filename, wxFile::write))
562         {
563             cbMessageBox(wxString::Format(_("%s is in use.\nAborting..."), filename.c_str()),
564                         _("Warning"), wxICON_WARNING);
565             return false;
566         }
567     }
568 
569 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Creating archive: ") + filename));
570     wxFileOutputStream out(filename);
571     wxZipOutputStream zip(out, 9); // max compression
572     for (size_t i = 0; i < sourcefiles.GetCount(); ++i)
573     {
574         if (sourcefiles[i].IsEmpty())
575             continue;
576 
577         wxFileInputStream in(sourcefiles[i]);
578         zip.PutNextEntry(wxFileName(sourcefiles[i]).GetFullName());
579         zip << in;
580     }
581     for (size_t i = 0; i < extrafiles.GetCount(); ++i)
582     {
583         if (extrafiles[i].IsEmpty() || extrafilesdest[i].IsEmpty())
584             continue;
585 
586         wxFileInputStream in(extrafiles[i]);
587 
588         zip.PutNextEntry(extrafilesdest[i]);
589         zip << in;
590     }
591     zip.SetComment(_T("This is a redistributable plugin for the Code::Blocks IDE.\n"
592                         "See http://www.codeblocks.org for details..."));
593 
594     return true;
595 }
596 
ExtractFile(const wxString & bundlename,const wxString & src_filename,const wxString & dst_filename,bool isMandatory)597 bool PluginManager::ExtractFile(const wxString& bundlename,
598                                 const wxString& src_filename,
599                                 const wxString& dst_filename,
600                                 bool isMandatory)
601 {
602 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("ExtractFile:")));
603 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + bundlename));
604 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Source filename: ") + src_filename));
605 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Destination filename: ") + dst_filename));
606 
607     // check if the destination file already exists
608     if (wxFileExists(dst_filename) && !wxFile::Access(dst_filename, wxFile::write))
609     {
610 //        Manager::Get()->GetLogManager()->DebugLog(F(_T("Destination file in use")));
611         cbMessageBox(_("The destination file is in use.\nAborting..."), _("Warning"), wxICON_WARNING);
612         return false;
613     }
614 
615     // make sure destination dir exists
616     CreateDirRecursively(wxFileName(dst_filename).GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
617 
618     // actually extract file
619 //    Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracting...")));
620     wxFileSystem* fs = new wxFileSystem;
621     wxFSFile* f = fs->OpenFile(bundlename + _T("#zip:") + src_filename);
622     if (f)
623     {
624         // open output file for writing
625         wxFile output(dst_filename, wxFile::write);
626         if (!output.IsOpened())
627         {
628 //            Manager::Get()->GetLogManager()->DebugLog(F(_T("Can't open destination file for writing")));
629             wxString msg = wxString::Format(_("Can't open destination file '%s' for writing..."),
630                                             dst_filename.c_str());
631             cbMessageBox(msg, _("Error"), wxICON_ERROR);
632             delete f;
633             delete fs;
634             return false;
635         }
636 
637         // copy file
638         wxInputStream* is = f->GetStream();
639         char tmp[1025] = {};
640         while (!is->Eof() && is->CanRead())
641         {
642             memset(tmp, 0, sizeof(tmp));
643             is->Read(tmp, sizeof(tmp) - 1);
644             output.Write(tmp, is->LastRead());
645         }
646         delete f;
647 //        Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted")));
648     }
649     else
650     {
651 //        Manager::Get()->GetLogManager()->DebugLog(F(_T("File not found in plugin")));
652         if (isMandatory)
653         {
654             wxString msg = wxString::Format(_("File '%s' not found in plugin '%s'"),
655                                             src_filename.c_str(), bundlename.c_str());
656             cbMessageBox(msg, _("Error"), wxICON_ERROR);
657             delete fs;
658             return false;
659         }
660     }
661     delete fs;
662     return true;
663 }
664 
RegisterPlugin(const wxString & name,CreatePluginProc createProc,FreePluginProc freeProc,PluginSDKVersionProc versionProc)665 void PluginManager::RegisterPlugin(const wxString& name,
666                                     CreatePluginProc createProc,
667                                     FreePluginProc freeProc,
668                                     PluginSDKVersionProc versionProc)
669 {
670     // sanity checks
671     if (name.IsEmpty() || !createProc || !freeProc || !versionProc)
672         return;
673 
674     // first check to see it's not already loaded
675     if (FindPluginByName(name))
676         return; // yes, already loaded
677 
678     // read manifest file for plugin
679     PluginInfo info;
680     if (!ReadManifestFile(m_CurrentlyLoadingFilename, name, &info) ||
681         info.name.IsEmpty())
682     {
683         Manager::Get()->GetLogManager()->LogError(_T("Invalid manifest file for: ") + name);
684         return;
685     }
686 
687     // now get the SDK version number (extra check)
688     int major;
689     int minor;
690     int release;
691     versionProc(&major, &minor, &release);
692     if (major != PLUGIN_SDK_VERSION_MAJOR ||
693         minor != PLUGIN_SDK_VERSION_MINOR ||
694         release != PLUGIN_SDK_VERSION_RELEASE)
695     {
696         // wrong version: in this case, inform the user...
697         wxString fmt;
698         fmt.Printf(_("SDK version mismatch for %s (%d.%d.%d). Expecting %d.%d.%d"),
699                     name.c_str(),
700                     major,
701                     minor,
702                     release,
703                     PLUGIN_SDK_VERSION_MAJOR,
704                     PLUGIN_SDK_VERSION_MINOR,
705                     PLUGIN_SDK_VERSION_RELEASE);
706         Manager::Get()->GetLogManager()->LogError(fmt);
707         return;
708     }
709 
710     // all done
711     // add this plugin in the temporary registration vector to be loaded
712     // by LoadPlugin() (which triggered the call to this function).
713     PluginRegistration pr;
714     pr.name = name;
715     pr.createProc = createProc;
716     pr.freeProc = freeProc;
717     pr.versionProc = versionProc;
718     pr.info = info;
719     m_RegisteredPlugins.push_back(pr);
720 }
721 
ReadManifestFile(const wxString & pluginFilename,const wxString & pluginName,PluginInfo * infoOut)722 bool PluginManager::ReadManifestFile(const wxString& pluginFilename,
723                                     const wxString& pluginName,
724                                     PluginInfo* infoOut)
725 {
726     if (!m_pCurrentlyLoadingManifestDoc)
727     {
728         // find and load plugin's resource file
729         // (pluginFilename contains no path info)
730         wxFileName fname(pluginFilename);
731         fname.SetExt(_T("zip"));
732         wxString actual = fname.GetFullName();
733 
734         // remove 'lib' prefix from plugin name (if any)
735         if (!platform::windows && actual.StartsWith(_T("lib")))
736             actual.Remove(0, 3);
737 
738         actual = ConfigManager::LocateDataFile(actual, sdPluginsUser | sdDataUser | sdPluginsGlobal | sdDataGlobal);
739         if (actual.IsEmpty())
740         {
741             Manager::Get()->GetLogManager()->LogError(_T("Plugin resource not found: ") + fname.GetFullName());
742             return false; // not found
743         }
744 
745         // load XML from ZIP
746         wxString contents;
747         wxFileSystem* fs = new wxFileSystem;
748         wxFSFile* f = fs->OpenFile(actual + _T("#zip:manifest.xml"));
749         if (f)
750         {
751             wxInputStream* is = f->GetStream();
752             char tmp[1024] = {};
753             while (!is->Eof() && is->CanRead())
754             {
755                 memset(tmp, 0, sizeof(tmp));
756                 is->Read(tmp, sizeof(tmp) - 1);
757                 contents << cbC2U((const char*)tmp);
758             }
759             delete f;
760         }
761         else
762         {
763             Manager::Get()->GetLogManager()->LogError(_T("No plugin manifest file in resource: ") + actual);
764             delete fs;
765             return false;
766         }
767         delete fs;
768 
769         // actually load XML document
770         m_pCurrentlyLoadingManifestDoc = new TiXmlDocument;
771         if (!m_pCurrentlyLoadingManifestDoc->Parse(cbU2C(contents)))
772         {
773             Manager::Get()->GetLogManager()->LogError(_T("Plugin manifest could not be parsed: ") + actual);
774             return false;
775         }
776     }
777 
778     TiXmlElement* root = m_pCurrentlyLoadingManifestDoc->FirstChildElement("CodeBlocks_plugin_manifest_file");
779     if (!root)
780     {
781         Manager::Get()->GetLogManager()->LogError(_T("Plugin resource file not valid (no root element found) for: ") + pluginFilename);
782         return false;
783     }
784 
785     TiXmlElement* version = root->FirstChildElement("SdkVersion");
786     if (!version)
787     {
788         Manager::Get()->GetLogManager()->LogError(_T("Plugin resource file not valid (no SdkVersion element found) for: ") + pluginFilename);
789         return false;
790     }
791 
792     // check version
793 //    int major;
794 //    int minor;
795 //    int release;
796 //    if (version->QueryIntAttribute("major", &major) != TIXML_SUCCESS)
797 //        major = 0;
798 //    if (version->QueryIntAttribute("minor", &minor) != TIXML_SUCCESS)
799 //        minor = 0;
800 //    if (version->QueryIntAttribute("release", &release) != TIXML_SUCCESS)
801 //        release = 0;
802 //
803 //    if (major != PLUGIN_SDK_VERSION_MAJOR ||
804 //        minor != PLUGIN_SDK_VERSION_MINOR ||
805 //        release != PLUGIN_SDK_VERSION_RELEASE)
806 //    {
807 //        // wrong version: in this case, inform the user...
808 //        wxString fmt;
809 //        fmt.Printf(_("SDK version mismatch for %s (%d.%d.%d). Expecting %d.%d.%d"),
810 //                    pluginName.c_str(),
811 //                    major,
812 //                    minor,
813 //                    release,
814 //                    PLUGIN_SDK_VERSION_MAJOR,
815 //                    PLUGIN_SDK_VERSION_MINOR,
816 //                    PLUGIN_SDK_VERSION_RELEASE);
817 //        Manager::Get()->GetLogManager()->LogError(fmt);
818 //        return false;
819 //    }
820 
821     // if no plugin name specified, we 're done here (successfully)
822     if (pluginName.IsEmpty() || !infoOut)
823         return true;
824 
825     TiXmlElement* plugin = root->FirstChildElement("Plugin");
826     while (plugin)
827     {
828         const char* name = plugin->Attribute("name");
829         if (name && cbC2U(name) == pluginName)
830         {
831             infoOut->name = pluginName;
832             TiXmlElement* value = plugin->FirstChildElement("Value");
833             while (value)
834             {
835                 if (value->Attribute("title"))
836                     infoOut->title = cbC2U(value->Attribute("title"));
837                 if (value->Attribute("version"))
838                     infoOut->version = cbC2U(value->Attribute("version"));
839                 if (value->Attribute("description"))
840                     infoOut->description = cbC2U(value->Attribute("description"));
841                 if (value->Attribute("author"))
842                     infoOut->author = cbC2U(value->Attribute("author"));
843                 if (value->Attribute("authorEmail"))
844                     infoOut->authorEmail = cbC2U(value->Attribute("authorEmail"));
845                 if (value->Attribute("authorWebsite"))
846                     infoOut->authorWebsite = cbC2U(value->Attribute("authorWebsite"));
847                 if (value->Attribute("thanksTo"))
848                     infoOut->thanksTo = cbC2U(value->Attribute("thanksTo"));
849                 if (value->Attribute("license"))
850                     infoOut->license = cbC2U(value->Attribute("license"));
851 
852                 value = value->NextSiblingElement("Value");
853             }
854             break;
855         }
856 
857         plugin = plugin->NextSiblingElement("Plugin");
858     }
859 
860     return true;
861 }
862 
ReadExtraFilesFromManifestFile(const wxString & pluginFilename,wxArrayString & extraFiles)863 void PluginManager::ReadExtraFilesFromManifestFile(const wxString& pluginFilename,
864                                                     wxArrayString& extraFiles)
865 {
866     extraFiles.Clear();
867 
868     // find and load plugin's resource file
869     // (pluginFilename contains no path info)
870     wxFileName fname(pluginFilename);
871     fname.SetExt(_T("zip"));
872     wxString actual = fname.GetFullName();
873 
874     // remove 'lib' prefix from plugin name (if any)
875     if (!platform::windows && actual.StartsWith(_T("lib")))
876         actual.Remove(0, 3);
877 
878     actual = ConfigManager::LocateDataFile(actual, sdPluginsUser | sdDataUser | sdPluginsGlobal | sdDataGlobal);
879     if (actual.IsEmpty())
880     {
881         Manager::Get()->GetLogManager()->LogError(_T("Plugin resource not found: ") + fname.GetFullName());
882         return; // not found
883     }
884 
885     // load XML from ZIP
886     wxString contents;
887     wxFileSystem* fs = new wxFileSystem;
888     wxFSFile* f = fs->OpenFile(actual + _T("#zip:manifest.xml"));
889     if (f)
890     {
891         wxInputStream* is = f->GetStream();
892         char tmp[1024] = {};
893         while (!is->Eof() && is->CanRead())
894         {
895             memset(tmp, 0, sizeof(tmp));
896             is->Read(tmp, sizeof(tmp) - 1);
897             contents << cbC2U((const char*)tmp);
898         }
899         delete f;
900     }
901     else
902     {
903         Manager::Get()->GetLogManager()->LogError(_T("No plugin manifest file in resource: ") + actual);
904         delete fs;
905         return;
906     }
907     delete fs;
908 
909     // actually load XML document
910     TiXmlDocument doc;
911     if (!doc.Parse(cbU2C(contents)))
912         return;
913 
914     TiXmlElement* root = doc.FirstChildElement("CodeBlocks_plugin_manifest_file");
915     if (!root)
916         return;
917 
918     TiXmlElement* extra = root->FirstChildElement("Extra");
919     while (extra)
920     {
921         const char* file = extra->Attribute("file");
922         if (file && *file)
923         {
924             extraFiles.Add(cbC2U(file));
925         }
926 
927         extra = extra->NextSiblingElement("Extra");
928     }
929 }
930 
ScanForPlugins(const wxString & path)931 int PluginManager::ScanForPlugins(const wxString& path)
932 {
933     static const wxString PluginsMask = platform::windows                      ? _T("*.dll")
934                                       : (platform::darwin || platform::macosx) ? _T("*.dylib")
935                                       :                                          _T("*.so");
936     int count = 0;
937     if (!wxDirExists(path))
938         return count;
939     wxDir dir(path);
940 
941     if (!dir.IsOpened())
942         return count;
943 
944     bool batch = Manager::IsBatchBuild();
945     wxArrayString bbplugins;
946     if (batch)
947         bbplugins = cbReadBatchBuildPlugins();
948 
949     wxString filename;
950     wxString failed;
951     bool ok = dir.GetFirst(&filename, PluginsMask, wxDIR_FILES);
952     while (ok)
953     {
954         if (batch)
955         {
956             // for batch builds, we will load only those plugins that the
957             // user has set (default only compiler.dll)
958             bool matched = false;
959             for (size_t i = 0; i < bbplugins.GetCount(); ++i)
960             {
961                 if (bbplugins[i] == filename)
962                 {
963                     matched = true;
964                     break;
965                 }
966             }
967             if (!matched)
968             {
969                 ok = dir.GetNext(&filename);
970                 continue;
971             }
972         }
973 
974         // load manifest
975         m_pCurrentlyLoadingManifestDoc = nullptr;
976         if (ReadManifestFile(filename))
977         {
978             if (LoadPlugin(path + wxFILE_SEP_PATH + filename))
979                 ++count;
980             else
981                 failed << _T('\n') << filename;
982         }
983         if (m_pCurrentlyLoadingManifestDoc)
984         {
985             delete m_pCurrentlyLoadingManifestDoc;
986             m_pCurrentlyLoadingManifestDoc = nullptr;
987         }
988         ok = dir.GetNext(&filename);
989     }
990     Manager::Get()->GetLogManager()->Log(F(_("Loaded %d plugins"), count));
991     if (!failed.IsEmpty())
992     {
993         InfoWindow::Display(_("Warning"),
994                             _("One or more plugins were not loaded.\n"
995                             "This usually happens when a plugin is built for\n"
996                             "a different version of the Code::Blocks SDK.\n"
997                             "Check the application log for more info.\n\n"
998                             "List of failed plugins:\n") + failed,
999                             15000, 3000);
1000     }
1001     return count;
1002 }
1003 
LoadPlugin(const wxString & pluginName)1004 bool PluginManager::LoadPlugin(const wxString& pluginName)
1005 {
1006     // clear registration temporary vector
1007     m_RegisteredPlugins.clear();
1008 
1009     // load library
1010     m_CurrentlyLoadingFilename = pluginName;
1011     m_pCurrentlyLoadingLib = LibLoader::LoadLibrary(pluginName);
1012     if (!m_pCurrentlyLoadingLib->IsLoaded())
1013     {
1014         Manager::Get()->GetLogManager()->LogError(F(_T("%s: not loaded (missing symbols?)"), pluginName.wx_str()));
1015         LibLoader::RemoveLibrary(m_pCurrentlyLoadingLib);
1016         m_pCurrentlyLoadingLib = nullptr;
1017         m_CurrentlyLoadingFilename.Clear();
1018         return false;
1019     }
1020 
1021     // by now, the library has loaded and its global variables are initialized.
1022     // this means it has already called RegisterPlugin()
1023     // now we can actually create the plugin(s) instance(s) :)
1024 
1025     // try to load the plugin(s)
1026     std::vector<PluginRegistration>::iterator it;
1027     for (it = m_RegisteredPlugins.begin(); it != m_RegisteredPlugins.end(); ++it)
1028     {
1029         PluginRegistration& pr = *it;
1030         cbPlugin* plug = nullptr;
1031         try
1032         {
1033             plug = pr.createProc();
1034         }
1035         catch (cbException& exception)
1036         {
1037             exception.ShowErrorMessage(false);
1038             continue;
1039         }
1040 
1041         // all done; add it to our list
1042         PluginElement* plugElem = new PluginElement;
1043         plugElem->fileName = m_CurrentlyLoadingFilename;
1044         plugElem->info = pr.info;
1045         plugElem->library = m_pCurrentlyLoadingLib;
1046         plugElem->freeProc = pr.freeProc;
1047         plugElem->plugin = plug;
1048         m_Plugins.Add(plugElem);
1049         if (plug->GetType() == ptCompiler)
1050             m_CompilerPlugins.push_back(static_cast<cbCompilerPlugin*>(plug));
1051 
1052         SetupLocaleDomain(pr.name);
1053 
1054         Manager::Get()->GetLogManager()->DebugLog(F(_T("%s: loaded"), pr.name.wx_str()));
1055     }
1056 
1057     if (m_RegisteredPlugins.empty())
1058     {
1059         // no plugins loaded from this library, but it's not an error
1060         LibLoader::RemoveLibrary(m_pCurrentlyLoadingLib);
1061     }
1062     m_pCurrentlyLoadingLib = nullptr;
1063     m_CurrentlyLoadingFilename.Clear();
1064     return true;
1065 }
1066 
LoadAllPlugins()1067 void PluginManager::LoadAllPlugins()
1068 {
1069     // check if a plugin crashed the app last time
1070     wxString probPlugin = Manager::Get()->GetConfigManager(_T("plugins"))->Read(_T("/try_to_activate"), wxEmptyString);
1071     if (!probPlugin.IsEmpty())
1072     {
1073         wxString msg;
1074         msg.Printf(_("Plugin \"%s\" failed to load last time Code::Blocks was executed.\n"
1075                     "Do you want to disable this plugin from loading?"), probPlugin.c_str());
1076         if (cbMessageBox(msg, _("Warning"), wxICON_WARNING | wxYES_NO) == wxID_NO)
1077             probPlugin = _T("");
1078     }
1079 
1080     PluginElement* elem = nullptr;
1081     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1082     {
1083         elem = m_Plugins[i];
1084         cbPlugin* plug = elem->plugin;
1085         if (!plug || plug->IsAttached())
1086             continue;
1087 
1088         // do not load it if the user has explicitly asked not to...
1089         wxString baseKey;
1090         baseKey << _T("/") << elem->info.name;
1091         bool loadIt = Manager::Get()->GetConfigManager(_T("plugins"))->ReadBool(baseKey, true);
1092 
1093         // if we have a problematic plugin, check if this is it
1094         if (loadIt && !probPlugin.IsEmpty())
1095         {
1096             loadIt = elem->info.title != probPlugin;
1097             // if this is the problematic plugin, don't load it
1098             if (!loadIt)
1099                 Manager::Get()->GetConfigManager(_T("plugins"))->Write(baseKey, false);
1100         }
1101 
1102         if (loadIt)
1103         {
1104             Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), elem->info.title);
1105             Manager::Get()->GetLogManager()->Log(elem->info.name);
1106             try
1107             {
1108                 AttachPlugin(plug);
1109                 Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), wxEmptyString, false);
1110             }
1111             catch (cbException& exception)
1112             {
1113                 Manager::Get()->GetLogManager()->Log(_T("[failed]"));
1114                 exception.ShowErrorMessage(false);
1115 
1116                 wxString msg;
1117                 msg.Printf(_("Plugin \"%s\" failed to load...\n"
1118                             "Do you want to disable this plugin from loading next time?"), elem->info.title.c_str());
1119                 if (cbMessageBox(msg, _("Warning"), wxICON_WARNING | wxYES_NO) == wxID_YES)
1120                     Manager::Get()->GetConfigManager(_T("plugins"))->Write(baseKey, false);
1121             }
1122         }
1123     }
1124     Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), wxEmptyString, false);
1125 }
1126 
UnloadAllPlugins()1127 void PluginManager::UnloadAllPlugins()
1128 {
1129 //    Manager::Get()->GetLogManager()->DebugLog("Count %d", m_Plugins.GetCount());
1130 
1131     while (m_Plugins.GetCount())
1132     {
1133         UnloadPlugin(m_Plugins[0]->plugin);
1134     }
1135     m_CompilerPlugins.clear();
1136     m_Plugins.Clear();
1137     LibLoader::Cleanup();
1138 }
1139 
UnloadPlugin(cbPlugin * plugin)1140 void PluginManager::UnloadPlugin(cbPlugin* plugin)
1141 {
1142     if (!plugin)
1143         return;
1144 
1145     // detach plugin if needed
1146     DetachPlugin(plugin);
1147 
1148     // find plugin element
1149     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1150     {
1151         PluginElement* plugElem = m_Plugins[i];
1152         if (plugElem->plugin == plugin)
1153         {
1154             if (plugin->GetType() == ptCompiler)
1155             {
1156                 auto removeIter = std::remove(m_CompilerPlugins.begin(), m_CompilerPlugins.end(), plugin);
1157                 if (removeIter != m_CompilerPlugins.end())
1158                     m_CompilerPlugins.erase(removeIter);
1159             }
1160 
1161             // found
1162             // free plugin
1163             if (plugElem->freeProc)
1164                 plugElem->freeProc(plugin);
1165             else
1166                 delete plugin; // try to delete it ourselves...
1167             // remove lib
1168             LibLoader::RemoveLibrary(plugElem->library);
1169             // and delete plugin element
1170             delete plugElem;
1171             m_Plugins.RemoveAt(i);
1172 
1173             break;
1174         }
1175     }
1176 }
1177 
FindElementByName(const wxString & pluginName)1178 PluginElement* PluginManager::FindElementByName(const wxString& pluginName)
1179 {
1180     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1181     {
1182         PluginElement* plugElem = m_Plugins[i];
1183         if (plugElem->info.name == pluginName)
1184             return plugElem;
1185     }
1186 
1187     return nullptr;
1188 }
1189 
FindPluginByName(const wxString & pluginName)1190 cbPlugin* PluginManager::FindPluginByName(const wxString& pluginName)
1191 {
1192     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1193     {
1194         PluginElement* plugElem = m_Plugins[i];
1195         if (plugElem->info.name == pluginName)
1196             return plugElem->plugin;
1197     }
1198 
1199     return nullptr;
1200 }
1201 
FindPluginByFileName(const wxString & pluginFileName)1202 cbPlugin* PluginManager::FindPluginByFileName(const wxString& pluginFileName)
1203 {
1204     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1205     {
1206         PluginElement* plugElem = m_Plugins[i];
1207         if (plugElem->fileName == pluginFileName)
1208             return plugElem->plugin;
1209     }
1210 
1211     return nullptr;
1212 }
1213 
GetPluginInfo(const wxString & pluginName)1214 const PluginInfo* PluginManager::GetPluginInfo(const wxString& pluginName)
1215 {
1216     PluginElement* plugElem = FindElementByName(pluginName);
1217     if (plugElem && plugElem->info.name == pluginName)
1218         return &plugElem->info;
1219 
1220     return nullptr;
1221 }
1222 
GetPluginInfo(cbPlugin * plugin)1223 const PluginInfo* PluginManager::GetPluginInfo(cbPlugin* plugin)
1224 {
1225     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1226     {
1227         PluginElement* plugElem = m_Plugins[i];
1228         if (plugElem->plugin == plugin)
1229             return &plugElem->info;
1230     }
1231 
1232     return nullptr;
1233 }
1234 
ExecutePlugin(const wxString & pluginName)1235 int PluginManager::ExecutePlugin(const wxString& pluginName)
1236 {
1237     PluginElement* elem = FindElementByName(pluginName);
1238     cbPlugin* plug = elem ? elem->plugin : nullptr;
1239     if (plug)
1240     {
1241         if (plug->GetType() != ptTool)
1242         {
1243             Manager::Get()->GetLogManager()->LogError(F(_T("Plugin %s is not a tool to have Execute() method!"), elem->info.name.wx_str()));
1244         }
1245         else
1246         {
1247             try
1248             {
1249                 return ((cbToolPlugin*)plug)->Execute();
1250             }
1251             catch (cbException& exception)
1252             {
1253                 exception.ShowErrorMessage(false);
1254             }
1255         }
1256     }
1257     else
1258     {
1259         Manager::Get()->GetLogManager()->LogError(F(_T("No plugin registered by this name: %s"), pluginName.wx_str()));
1260     }
1261     return 0;
1262 }
1263 
SortByConfigurationPriority(cbPlugin ** first,cbPlugin ** second)1264 inline int SortByConfigurationPriority(cbPlugin** first, cbPlugin** second)
1265 {
1266     return (*first)->GetConfigurationPriority() - (*second)->GetConfigurationPriority();
1267 }
1268 
GetConfigurationPanels(int group,wxWindow * parent,ConfigurationPanelsArray & arrayToFill)1269 void PluginManager::GetConfigurationPanels(int group, wxWindow* parent, ConfigurationPanelsArray& arrayToFill)
1270 {
1271     // build an array of Plugins* because we need to order it by configuration priority
1272     PluginsArray arr;
1273     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1274     {
1275         cbPlugin* plug = m_Plugins[i]->plugin;
1276         // all check are done here
1277         if (plug && plug->IsAttached() && (plug->GetConfigurationGroup() & group))
1278             arr.Add(plug);
1279     }
1280 
1281     // sort the array
1282     arr.Sort(SortByConfigurationPriority);
1283 
1284     // now enumerate the array and fill the supplied configurations panel array
1285     arrayToFill.Clear();
1286     for (unsigned int i = 0; i < arr.GetCount(); ++i)
1287     {
1288         cbPlugin* plug = arr[i];
1289         cbConfigurationPanel* pnl = plug->GetConfigurationPanel(parent);
1290         if (pnl)
1291             arrayToFill.Add(pnl);
1292     }
1293 }
1294 
GetProjectConfigurationPanels(wxWindow * parent,cbProject * project,ConfigurationPanelsArray & arrayToFill)1295 void PluginManager::GetProjectConfigurationPanels(wxWindow* parent, cbProject* project, ConfigurationPanelsArray& arrayToFill)
1296 {
1297     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1298     {
1299         cbPlugin* plug = m_Plugins[i]->plugin;
1300         if (plug && plug->IsAttached())
1301         {
1302             cbConfigurationPanel* pnl = plug->GetProjectConfigurationPanel(parent, project);
1303             if (pnl)
1304                 arrayToFill.Add(pnl);
1305         }
1306     }
1307 }
1308 
GetFirstCompiler() const1309 cbCompilerPlugin* PluginManager::GetFirstCompiler() const
1310 {
1311     if (m_CompilerPlugins.empty())
1312         return nullptr;
1313     return m_CompilerPlugins.front();
1314 }
1315 
GetToolOffers()1316 PluginsArray PluginManager::GetToolOffers()
1317 {
1318     return GetOffersFor(ptTool);
1319 }
1320 
GetMimeOffers()1321 PluginsArray PluginManager::GetMimeOffers()
1322 {
1323     return GetOffersFor(ptMime);
1324 }
1325 
GetDebuggerOffers()1326 PluginsArray PluginManager::GetDebuggerOffers()
1327 {
1328     return GetOffersFor(ptDebugger);
1329 }
1330 
GetCodeCompletionOffers()1331 PluginsArray PluginManager::GetCodeCompletionOffers()
1332 {
1333     return GetOffersFor(ptCodeCompletion);
1334 }
1335 
GetSmartIndentOffers()1336 PluginsArray PluginManager::GetSmartIndentOffers()
1337 {
1338     return GetOffersFor(ptSmartIndent);
1339 }
1340 
GetOffersFor(PluginType type)1341 PluginsArray PluginManager::GetOffersFor(PluginType type)
1342 {
1343     PluginsArray arr;
1344 
1345     // special case for MIME plugins
1346     // we 'll add the default MIME handler, last in the returned array
1347     cbPlugin* dflt = nullptr;
1348 
1349     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1350     {
1351         cbPlugin* plug = m_Plugins[i]->plugin;
1352         if (plug && plug->IsAttached() && plug->GetType() == type)
1353         {
1354             if (type == ptMime)
1355             {
1356                 // default MIME handler?
1357                 if (((cbMimePlugin*)plug)->HandlesEverything())
1358                     dflt = plug;
1359                 else
1360                     arr.Add(plug);
1361             }
1362             else
1363                 arr.Add(plug);
1364         }
1365     }
1366 
1367     // add default MIME handler last
1368     if (dflt)
1369         arr.Add(dflt);
1370 
1371     return arr;
1372 }
1373 
AskPluginsForModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)1374 void PluginManager::AskPluginsForModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
1375 {
1376     std::map<wxString, cbPlugin*> sortedPlugins;
1377     for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1378     {
1379         cbPlugin* plug = m_Plugins[i]->plugin;
1380         if (plug && plug->IsAttached())
1381             sortedPlugins[m_Plugins[i]->info.name] = plug;
1382     }
1383 
1384     // We want the order of iteration to be more stable, so there are fewer surprises on different machines.
1385     for (auto &pair : sortedPlugins)
1386     {
1387         try
1388         {
1389             pair.second->BuildModuleMenu(type, menu, data);
1390         }
1391         catch (cbException& exception)
1392         {
1393             exception.ShowErrorMessage(false);
1394         }
1395     }
1396 
1397     // script plugins now
1398     wxArrayInt ids = ScriptBindings::ScriptPluginWrapper::CreateModuleMenu(type, menu, data);
1399     for (size_t i = 0; i < ids.GetCount(); ++i)
1400     {
1401         Connect(ids[i], -1, wxEVT_COMMAND_MENU_SELECTED,
1402                 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
1403                 &PluginManager::OnScriptModuleMenu);
1404     }
1405 }
1406 
ResetModuleMenu()1407 void PluginManager::ResetModuleMenu()
1408 {
1409     m_FindMenuItemCount = 0;
1410     m_FindMenuItemFirst = 0;
1411     m_LastNonPluginMenuId = 0;
1412 }
1413 
RegisterFindMenuItems(bool before,int count)1414 void PluginManager::RegisterFindMenuItems(bool before, int count)
1415 {
1416     if (before)
1417         m_FindMenuItemFirst += count;
1418     else
1419         m_FindMenuItemCount += count;
1420 }
1421 
GetFindMenuItemCount() const1422 int PluginManager::GetFindMenuItemCount() const
1423 {
1424     return m_FindMenuItemCount;
1425 }
1426 
GetFindMenuItemFirst() const1427 int PluginManager::GetFindMenuItemFirst() const
1428 {
1429     return m_FindMenuItemFirst;
1430 }
1431 
RegisterLastNonPluginMenuItem(int id)1432 void PluginManager::RegisterLastNonPluginMenuItem(int id)
1433 {
1434     m_LastNonPluginMenuId = id;
1435 }
1436 
FindSortedMenuItemPosition(wxMenu & popup,const wxString & label) const1437 int PluginManager::FindSortedMenuItemPosition(wxMenu &popup, const wxString& label) const
1438 {
1439     wxString labelNoAmpersands = label;
1440     // Remove ampersands, because they are not visible but affect the sorting if they are at the
1441     // start of the label.
1442     labelNoAmpersands.erase(std::remove(labelNoAmpersands.begin(), labelNoAmpersands.end(), '&'),
1443                             labelNoAmpersands.end());
1444 
1445     int position = -1;
1446     const wxMenuItemList &items = popup.GetMenuItems();
1447     const int count = int(items.size());
1448     for (int ii = 0; ii < count; ++ii)
1449     {
1450         if (items[ii]->GetId() == m_LastNonPluginMenuId)
1451         {
1452             position = ii + 1;
1453             break;
1454         }
1455     }
1456 
1457     if (position == -1 || (position >= count))
1458         return count;
1459     if (items[position]->GetKind() == wxITEM_SEPARATOR)
1460         position++;
1461 
1462     // Linear search for now. The number of items isn't large, so it shouldn't be a performance
1463     // problem.
1464     for (int ii = position; ii < count; ++ii)
1465     {
1466         const wxString &itemLabel = items[ii]->GetItemLabelText();
1467         if (labelNoAmpersands.CmpNoCase(itemLabel) <= 0)
1468             return ii;
1469     }
1470     return count;
1471 }
1472 
OnScriptMenu(wxCommandEvent & event)1473 void PluginManager::OnScriptMenu(wxCommandEvent& event)
1474 {
1475     ScriptBindings::ScriptPluginWrapper::OnScriptMenu(event.GetId());
1476 }
1477 
OnScriptModuleMenu(wxCommandEvent & event)1478 void PluginManager::OnScriptModuleMenu(wxCommandEvent& event)
1479 {
1480     ScriptBindings::ScriptPluginWrapper::OnScriptModuleMenu(event.GetId());
1481 }
1482 
GetMIMEHandlerForFile(const wxString & filename)1483 cbMimePlugin* PluginManager::GetMIMEHandlerForFile(const wxString& filename)
1484 {
1485     PluginsArray mimes = GetMimeOffers();
1486     for (unsigned int i = 0; i < mimes.GetCount(); ++i)
1487     {
1488         cbMimePlugin* plugin = (cbMimePlugin*)mimes[i];
1489         if (plugin && plugin->CanHandleFile(filename))
1490             return plugin;
1491     }
1492     return nullptr;
1493 }
1494 
Configure()1495 int PluginManager::Configure()
1496 {
1497     PluginsConfigurationDlg dlg(Manager::Get()->GetAppWindow());
1498     PlaceWindow(&dlg);
1499     return dlg.ShowModal();
1500 }
1501 
SetupLocaleDomain(const wxString & DomainName)1502 void PluginManager::SetupLocaleDomain(const wxString& DomainName)
1503 {
1504     int catalogNum=Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/locale/catalogNum"),(int)0);
1505     int i = 1;
1506     for (; i <= catalogNum; ++i)
1507     {
1508         wxString catalogName=Manager::Get()->GetConfigManager(_T("app"))->Read(wxString::Format(_T("/locale/Domain%d"), i), wxEmptyString);
1509         if (catalogName.Cmp(DomainName) == 0)
1510             break;
1511     }
1512     if (i > catalogNum)
1513     {
1514         ++catalogNum;
1515         Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/locale/catalogNum"), (int)catalogNum);
1516         Manager::Get()->GetConfigManager(_T("app"))->Write(wxString::Format(_T("/locale/Domain%d"), i), DomainName);
1517     }
1518 }
1519 
NotifyPlugins(CodeBlocksEvent & event)1520 void PluginManager::NotifyPlugins(CodeBlocksEvent& event)
1521 {
1522     Manager::Get()->ProcessEvent(event);
1523 }
1524 
NotifyPlugins(CodeBlocksDockEvent & event)1525 void PluginManager::NotifyPlugins(CodeBlocksDockEvent& event)
1526 {
1527     Manager::Get()->ProcessEvent(event);
1528 }
1529 
NotifyPlugins(CodeBlocksLayoutEvent & event)1530 void PluginManager::NotifyPlugins(CodeBlocksLayoutEvent& event)
1531 {
1532     Manager::Get()->ProcessEvent(event);
1533 }
1534 
cbHasRunningCompilers(const PluginManager * manager)1535 bool cbHasRunningCompilers(const PluginManager *manager)
1536 {
1537     for (const cbCompilerPlugin *p : manager->GetCompilerPlugins())
1538     {
1539         if (p && p->IsRunning())
1540             return true;
1541     }
1542     return false;
1543 }
1544 
cbStopRunningCompilers(PluginManager * manager)1545 void cbStopRunningCompilers(PluginManager *manager)
1546 {
1547     for (cbCompilerPlugin *compiler : manager->GetCompilerPlugins())
1548     {
1549         if (!compiler || !compiler->IsRunning())
1550             continue;
1551         compiler->KillProcess();
1552         while (compiler->IsRunning())
1553         {
1554             wxMilliSleep(100);
1555             Manager::Yield();
1556         }
1557     }
1558 }
1559