OpenCPN Partial API docs
Loading...
Searching...
No Matches
plugin_loader.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: PlugIn Manager Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26#include "config.h"
27
28#include <algorithm>
29#include <set>
30#include <sstream>
31#include <typeinfo>
32#include <unordered_map>
33
34#ifdef USE_LIBELF
35#include <elf.h>
36#include <libelf.h>
37#include <gelf.h>
38#endif
39
40#if defined(__linux__) && !defined(__ANDROID__)
41#include <wordexp.h>
42#endif
43
44#ifndef WIN32
45#include <cxxabi.h>
46#endif
47
48#include <wx/wx.h>
49#include <wx/bitmap.h>
50#include <wx/config.h>
51#include <wx/dir.h>
52#include <wx/event.h>
53#include <wx/hashset.h>
54#include <wx/filename.h>
55#include <wx/string.h>
56#include <wx/tokenzr.h>
57#include <wx/window.h>
58
59#include "plugin_loader.h"
60
61#include "base_platform.h"
62#include "config_vars.h"
63#include "ocpn_utils.h"
64#include "logger.h"
65#include "observable_confvar.h"
66#include "observable_evtvar.h"
67#include "plugin_blacklist.h"
68#include "plugin_cache.h"
69#include "plugin_handler.h"
70#include "plugin_paths.h"
71#include "safe_mode.h"
72#include "chartdb.h"
73
74#ifdef __ANDROID__
75#include "androidUTIL.h"
76#include <dlfcn.h>
77#endif
78
79extern BasePlatform* g_BasePlatform;
80extern wxWindow* gFrame;
81extern ChartDB* ChartData;
82
83const char* const LINUX_LOAD_PATH = "~/.local/lib:/usr/local/lib:/usr/lib";
84const char* const FLATPAK_LOAD_PATH = "~/.var/app/org.opencpn.OpenCPN/lib";
85
86static const std::vector<std::string> SYSTEM_PLUGINS = {
87 "chartdownloader", "wmm", "dashboard", "grib"};
88// Helper and interface classes
89
90PlugInContainer::PlugInContainer() {
91 m_pplugin = NULL;
92 m_bEnabled = false;
93 m_bInitState = false;
94 m_bToolboxPanel = false;
95 m_bitmap = NULL;
96 m_pluginStatus = PluginStatus::Unknown;
97 m_api_version = 0;
98}
99
101 if (m_ManagedMetadata.version.size()) {
102 return SemanticVersion::parse(m_ManagedMetadata.version);
103 }
104
105 if (!m_pplugin) {
106 return SemanticVersion(0, 0, 0);
107 }
108 auto plugin_117 = dynamic_cast<opencpn_plugin_117*>(m_pplugin);
109 if (plugin_117) {
110 return SemanticVersion(
111 plugin_117->GetPlugInVersionMajor(),
112 plugin_117->GetPlugInVersionMinor(),
113 plugin_117->GetPlugInVersionPatch(), plugin_117->GetPlugInVersionPost(),
114 plugin_117->GetPlugInVersionPre(), plugin_117->GetPlugInVersionBuild());
115 } else {
116 return SemanticVersion(m_pplugin->GetPlugInVersionMajor(),
117 m_pplugin->GetPlugInVersionMinor(),
118 -1); // Don't print .patch. It's unknown
119 }
120}
121
122//-----------------------------------------------------------------------------------------------------
123//
124// Plugin Loader Implementation
125//
126//-----------------------------------------------------------------------------------------------------
127
137static void setLoadPath() {
138 using namespace std;
139
140 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
141 vector<string> dirs = PluginPaths::getInstance()->Libdirs();
142 if (osSystemId & wxOS_UNIX_LINUX) {
143 string path = ocpn::join(dirs, ':');
144 wxString envPath;
145 if (wxGetEnv("LD_LIBRARY_PATH", &envPath)) {
146 path = path + ":" + envPath.ToStdString();
147 }
148 wxLogMessage("Using LD_LIBRARY_PATH: %s", path.c_str());
149 wxSetEnv("LD_LIBRARY_PATH", path.c_str());
150 } else if (osSystemId & wxOS_WINDOWS) {
151 // On windows, Libdirs() and Bindirs() are the same.
152 string path = ocpn::join(dirs, ';');
153 wxString envPath;
154 if (wxGetEnv("PATH", &envPath)) {
155 path = path + ";" + envPath.ToStdString();
156 }
157 wxLogMessage("Using PATH: %s", path);
158 wxSetEnv("PATH", path);
159 } else if (osSystemId & wxOS_MAC) {
160 string path = ocpn::join(dirs, ':');
161 wxString envPath;
162 if (wxGetEnv("DYLD_LIBRARY_PATH", &envPath)) {
163 path = path + ":" + envPath.ToStdString();
164 }
165 wxLogMessage("Using DYLD_LIBRARY_PATH: %s", path.c_str());
166 wxSetEnv("DYLD_LIBRARY_PATH", path.c_str());
167 } else {
168 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
169 if (os_name.Contains("wxQT")) {
170 wxLogMessage("setLoadPath() using Android library path");
171 } else
172 wxLogWarning("SetLoadPath: Unsupported platform.");
173 }
174 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
175 vector<string> dirs = PluginPaths::getInstance()->Bindirs();
176 string path = ocpn::join(dirs, ':');
177 wxString envPath;
178 wxGetEnv("PATH", &envPath);
179 path = path + ":" + envPath.ToStdString();
180 wxLogMessage("Using PATH: %s", path);
181 wxSetEnv("PATH", path);
182 }
183}
184
185PluginLoader* PluginLoader::getInstance() {
186 static PluginLoader* instance = 0;
187
188 if (!instance) instance = new PluginLoader();
189 return instance;
190}
191
192PluginLoader::PluginLoader()
193 : m_blacklist(blacklist_factory()), m_default_plugin_icon(0) {}
194
195bool PluginLoader::IsPlugInAvailable(wxString commonName) {
196 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
197 PlugInContainer* pic = plugin_array[i];
198 if (pic && pic->m_bEnabled && (pic->m_common_name == commonName))
199 return true;
200 }
201 return false;
202}
203
204static void ProcessLateInit(PlugInContainer* pic) {
205 if (pic->m_cap_flag & WANTS_LATE_INIT) {
206 wxString msg("PlugInManager: Calling LateInit PlugIn: ");
207 msg += pic->m_plugin_file;
208 wxLogMessage(msg);
209
210 opencpn_plugin_110* ppi =
211 dynamic_cast<opencpn_plugin_110*>(pic->m_pplugin);
212 if (ppi) ppi->LateInit();
213 }
214}
215
216const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
217 if (!m_default_plugin_icon)
218 m_default_plugin_icon = new wxBitmap(32, 32);
219 return m_default_plugin_icon;
220}
221
222void PluginLoader::SetPluginDefaultIcon(const wxBitmap* bitmap) {
223 delete m_default_plugin_icon;
224 m_default_plugin_icon = bitmap;
225}
226
227
228bool PluginLoader::LoadAllPlugIns(bool load_enabled) {
229 using namespace std;
230
231 static const wxString sep = wxFileName::GetPathSeparator();
232 vector<string> dirs = PluginPaths::getInstance()->Libdirs();
233 wxLogMessage("PlugInManager: loading plugins from %s", ocpn::join(dirs, ';'));
234 setLoadPath();
235 bool any_dir_loaded = false;
236 for (auto dir : dirs) {
237 wxString wxdir(dir);
238 wxLogMessage("Loading plugins from dir: %s", wxdir.mb_str().data());
239 if (LoadPlugInDirectory(wxdir, load_enabled))
240 any_dir_loaded = true;
241 }
242
243 // Read the default ocpn-plugins.xml, and update/merge the plugin array
244 // This only needs to happen when the entire universe (enabled and disabled)
245 // of plugins are loaded for management.
246 if (!load_enabled) UpdateManagedPlugins();
247
248 // Some additional actions needed after all plugins are loaded.
249 evt_update_chart_types.Notify();
250 evt_plugin_loadall_finalize.Notify();
251
252 return any_dir_loaded;
253}
254
255bool PluginLoader::LoadPluginCandidate(wxString file_name, bool load_enabled) {
256
257 wxString plugin_file = wxFileName(file_name).GetFullName();
258 wxLogMessage("Checking plugin candidate: %s", file_name.mb_str().data());
259 wxDateTime plugin_modification =
260 wxFileName(file_name).GetModificationTime();
261
262 // this gets called every time we switch to the plugins tab.
263 // this allows plugins to be installed and enabled without restarting
264 // opencpn. For this reason we must check that we didn't already load this
265 // plugin
266 bool loaded = false;
267 PlugInContainer* loaded_pic = 0;
268 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
269 PlugInContainer* pic_test = plugin_array[i];
270
271 // Checking for dynamically updated plugins
272 if (pic_test->m_plugin_filename == plugin_file) {
273 // Do not re-load same-name plugins from different directories. Certain
274 // to crash...
275 if (pic_test->m_plugin_file == file_name) {
276 if (pic_test->m_plugin_modification != plugin_modification) {
277 // modification times don't match, reload plugin
278 plugin_array.Remove(pic_test);
279 i--;
280
281 DeactivatePlugIn(pic_test);
282 pic_test->m_destroy_fn(pic_test->m_pplugin);
283
284 delete pic_test;
285 } else {
286 loaded = true;
287 loaded_pic = pic_test;
288 break;
289 }
290 } else {
291 loaded = true;
292 loaded_pic = pic_test;
293 break;
294 }
295 }
296 }
297
298 if (loaded) return true;
299
300 // Check the config file to see if this PlugIn is user-enabled
301
302 const auto path = std::string("/PlugIns/") + plugin_file.ToStdString();
303 ConfigVar<bool> enabled(path, "bEnabled", TheBaseConfig());
304
305 // only loading enabled plugins? check that it is enabled
306 if (load_enabled && !enabled.Get(true)) {
307 wxLogMessage("Skipping not enabled candidate.");
308 return true;
309 }
310
311 bool b_compat = CheckPluginCompatibility(file_name);
312
313 if (!b_compat) {
314 auto msg = std::string(_("Incompatible plugin detected: ")
315 + file_name.ToStdString());
316 wxLogMessage(msg.c_str());
317 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
318 evt_incompatible_plugin.Notify(msg);
319 }
320 }
321
322 PlugInContainer* pic = NULL;
323 if (b_compat) pic = LoadPlugIn(file_name);
324
325 wxLog::FlushActive();
326
327 if (pic) {
328 if (pic->m_pplugin) {
329 plugin_array.Add(pic);
330
331 // The common name is available without initialization and startup of
332 // the PlugIn
333 pic->m_common_name = pic->m_pplugin->GetCommonName();
334 pic->m_plugin_filename = plugin_file;
335 pic->m_plugin_modification = plugin_modification;
336 pic->m_bEnabled = enabled.Get(false);
337
338 if (safe_mode::get_mode()) {
339 pic->m_bEnabled = false;
340 enabled.Set(false);
341 }
342#ifndef CLIAPP
343 // The CLI has no graphics context, but plugins assumes there is.
344 if (pic->m_bEnabled) {
345 pic->m_cap_flag = pic->m_pplugin->Init();
346 pic->m_bInitState = true;
347 evt_load_plugin.Notify(pic);
348 }
349#endif
350 wxLog::FlushActive();
351
352 std::string found_version;
353 for (auto p : PluginHandler::getInstance()->getInstalled()) {
354 if (p.name == pic->m_common_name.Lower()) {
355 found_version = p.readonly ? "" : p.version;
356 break;
357 }
358 }
359 pic->m_version_str = found_version;
360 pic->m_short_description = pic->m_pplugin->GetShortDescription();
361 pic->m_long_description = pic->m_pplugin->GetLongDescription();
362 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
363 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
364 pic->m_bitmap = pic->m_pplugin->GetPlugInBitmap();
365
366 wxBitmap* pbm = new wxBitmap(pic->m_bitmap->GetSubBitmap(wxRect(
367 0, 0, pic->m_bitmap->GetWidth(), pic->m_bitmap->GetHeight())));
368 pic->m_bitmap = pbm;
369
370 if (!pic->m_bEnabled && pic->m_destroy_fn) {
371 wxBitmap* pbm = new wxBitmap(pic->m_bitmap->GetSubBitmap(wxRect(
372 0, 0, pic->m_bitmap->GetWidth(), pic->m_bitmap->GetHeight())));
373 pic->m_bitmap = pbm;
374 pic->m_destroy_fn(pic->m_pplugin);
375 pic->m_destroy_fn = NULL;
376 pic->m_pplugin = NULL;
377 pic->m_bInitState = false;
378 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
379 }
380
381 } else // not loaded
382 {
383 wxString msg;
384 msg.Printf(
385 " PlugInManager: Unloading invalid PlugIn, API version %d ",
386 pic->m_api_version);
387 wxLogMessage(msg);
388
389 pic->m_destroy_fn(pic->m_pplugin);
390
391 delete pic;
392 }
393 }
394 return true;
395}
396
397// Helper function: loads all plugins from a single directory
398bool PluginLoader::LoadPlugInDirectory(const wxString& plugin_dir,
399 bool load_enabled) {
400 evt_load_directory.Notify();
401 m_plugin_location = plugin_dir;
402
403 wxString msg("PlugInManager searching for PlugIns in location ");
404 msg += m_plugin_location;
405 wxLogMessage(msg);
406
407#ifdef __WXMSW__
408 wxString pispec = "*_pi.dll";
409#elif defined(__WXOSX__)
410 wxString pispec = "*_pi.dylib";
411#else
412 wxString pispec = "*_pi.so";
413#endif
414
415 if (!::wxDirExists(m_plugin_location)) {
416 msg = m_plugin_location;
417 msg.Prepend(" Directory ");
418 msg.Append(" does not exist.");
419 wxLogMessage(msg);
420 return false;
421 }
422
423 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS)) return false;
424
425 wxArrayString file_list;
426
427 int get_flags = wxDIR_FILES | wxDIR_DIRS;
428#ifdef __WXMSW__
429#ifdef _DEBUG
430 get_flags = wxDIR_FILES;
431#endif
432#endif
433
434#ifdef __ANDROID__
435 get_flags = wxDIR_FILES; // No subdirs, especially "/files" where PlugIns are
436 // initially placed in APK
437#endif
438
439 bool ret =
440 false; // return true if at least one new plugins gets loaded/unloaded
441 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
442
443 wxLogMessage("Found %d candidates", (int)file_list.GetCount());
444 for (unsigned int i = 0; i < file_list.GetCount(); i++) {
445 wxLog::FlushActive();
446
447 wxString file_name = file_list[i];
448
449
450 LoadPluginCandidate(file_name, load_enabled);
451 }
452
453 // Scrub the plugin array...
454 // Here, looking for duplicates caused by new installation of a plugin
455 // We want to remove the previous entry representing the uninstalled packaged
456 // plugin metadata
457 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
458 PlugInContainer* pic = plugin_array[i];
459 for (unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
460 PlugInContainer* pict = plugin_array[j];
461
462 if (pic->m_common_name == pict->m_common_name) {
463 if (pic->m_plugin_file.IsEmpty())
464 plugin_array.Item(i)->m_pluginStatus =
465 PluginStatus::PendingListRemoval;
466 else
467 plugin_array.Item(j)->m_pluginStatus =
468 PluginStatus::PendingListRemoval;
469 }
470 }
471 }
472
473 // Remove any list items marked
474 size_t i = 0;
475 while ((i >= 0) && (i < plugin_array.GetCount())) {
476 PlugInContainer* pict = plugin_array.Item(i);
477 if (pict->m_pluginStatus == PluginStatus::PendingListRemoval) {
478 plugin_array.RemoveAt(i);
479 i = 0;
480 } else
481 i++;
482 }
483
484 return ret;
485}
486
487bool PluginLoader::UpdatePlugIns() {
488 bool bret = false;
489
490 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
491 PlugInContainer* pic = plugin_array[i];
492
493 // Try to confirm that the m_pplugin member points to a valid plugin
494 // image...
495 if (pic->m_pplugin) {
496 opencpn_plugin* ppl = dynamic_cast<opencpn_plugin*>(pic->m_pplugin);
497 if (!ppl) {
498 pic->m_pplugin = NULL;
499 pic->m_bInitState = false;
500 }
501 }
502
503 // Installed and loaded?
504 if (!pic->m_pplugin) { // Needs a reload?
505 if (pic->m_bEnabled) {
506 PluginStatus stat = pic->m_pluginStatus;
507 PlugInContainer* newpic = LoadPlugIn(pic->m_plugin_file, pic);
508 if (newpic) {
509 pic->m_pluginStatus = stat;
510 pic->m_bEnabled = true;
511 }
512 } else
513 continue;
514 }
515
516 if (pic->m_bEnabled && !pic->m_bInitState && pic->m_pplugin) {
517 wxString msg("PlugInManager: Initializing PlugIn: ");
518 msg += pic->m_plugin_file;
519 wxLogMessage(msg);
520
521 pic->m_cap_flag = pic->m_pplugin->Init();
522 pic->m_pplugin->SetDefaults();
523 pic->m_bInitState = true;
524 ProcessLateInit(pic);
525 pic->m_short_description = pic->m_pplugin->GetShortDescription();
526 pic->m_long_description = pic->m_pplugin->GetLongDescription();
527 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
528 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
529 pic->m_bitmap = pic->m_pplugin->GetPlugInBitmap();
530 bret = true;
531 } else if (!pic->m_bEnabled && pic->m_bInitState) {
532 // Save a local copy of the plugin icon before unloading
533 wxBitmap* pbm = new wxBitmap(pic->m_bitmap->GetSubBitmap(
534 wxRect(0, 0, pic->m_bitmap->GetWidth(), pic->m_bitmap->GetHeight())));
535 pic->m_bitmap = pbm;
536
537 bret = DeactivatePlugIn(pic);
538 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
539 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
540 pic->m_pplugin = NULL;
541 pic->m_bInitState = false;
542 }
543 }
544 evt_update_chart_types.Notify();
545 return bret;
546}
547
548bool PluginLoader::DeactivatePlugIn(PlugInContainer* pic) {
549
550 if (!pic) return false;
551 if (pic->m_bInitState) {
552 wxString msg("PlugInManager: Deactivating PlugIn: ");
553 wxLogMessage(msg + pic->m_plugin_file);
554
555#ifndef CLIAPP
556 // if this plugin is responsible for any charts, then unload chart cache
557 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
558 (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL)) {
559 ChartData->PurgeCachePlugins();
560 }
561#endif
562
563 pic->m_bInitState = false;
564 pic->m_pplugin->DeInit();
565 // pic is doomed and will be deleted. Make a copy to handler which
566 // can be used to look up items in toolbar etc.
567 // FIXME: Correct solution is to use a shared_ptr instead, expanding
568 // to a large patch covering many areas.
569 auto pic_copy =
570 static_cast<PlugInContainer*>(malloc(sizeof(PlugInContainer)));
571 memcpy(pic_copy, pic, sizeof(PlugInContainer));
573 }
574 return true;
575}
576
577
582// FIXME: Move to PluginHandler.
583static std::vector<PluginMetadata> getCompatiblePlugins() {
585 struct metadata_compare {
586 bool operator()(const PluginMetadata& lhs,
587 const PluginMetadata& rhs) const {
588 return lhs.key() < rhs.key();
589 }
590 };
591
592 std::vector<PluginMetadata> returnArray;
593
594 std::set<PluginMetadata, metadata_compare> unique_plugins;
595 for (auto plugin : PluginHandler::getInstance()->getAvailable()) {
596 unique_plugins.insert(plugin);
597 }
598 for (auto plugin : unique_plugins) {
599 if (PluginHandler::isCompatible(plugin)) {
600 returnArray.push_back(plugin);
601 }
602 }
603 return returnArray;
604}
605
607 if (ix >= plugin_array.GetCount()) {
608 wxLogWarning("Attempt to remove non-existing plugin %d", ix);
609 return false;
610 }
611 PlugInContainer* pic = plugin_array[ix];
612 if (!DeactivatePlugIn(pic)) {
613 return false;
614 }
615 if (pic->m_pplugin) {
616 pic->m_destroy_fn(pic->m_pplugin);
617 }
618
619 delete pic; // This will unload the PlugIn via DTOR of pic->m_library
620 plugin_array.RemoveAt(ix);
621 return true;
622}
623
624void PluginLoader::UpdateManagedPlugins() {
625 PlugInContainer* pict;
626 // Clear the status (to "unmanaged") on all plugins
627 for (size_t i = 0; i < plugin_array.GetCount(); i++) {
628 pict = plugin_array.Item(i);
629 plugin_array.Item(i)->m_pluginStatus = PluginStatus::Unmanaged;
630
631 // Pre-mark the default "system" plugins
632 auto r =
633 std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
634 plugin_array.Item(i)->m_common_name.Lower().ToStdString());
635 if (r != SYSTEM_PLUGINS.end())
636 plugin_array.Item(i)->m_pluginStatus = PluginStatus::System;
637 }
638
639 std::vector<PluginMetadata> available = getCompatiblePlugins();
640
641 // Traverse the list again
642 // Remove any inactive/uninstalled managed plugins that are no longer
643 // available in the current catalog Usually due to reverting from Alpha/Beta
644 // catalog back to master
645 for (size_t i = 0; i < plugin_array.GetCount(); i++) {
646 pict = plugin_array.Item(i);
647 if (pict->m_ManagedMetadata.name
648 .size()) { // If metadata is good, must be a managed plugin
649 bool bfound = false;
650 for (auto plugin : available) {
651 if (pict->m_common_name.IsSameAs(wxString(plugin.name.c_str()))) {
652 bfound = true;
653 break;
654 }
655 }
656 if (!bfound) {
657 if (!pict->m_pplugin) { // Only remove inactive plugins
658 plugin_array.Item(i)->m_pluginStatus =
659 PluginStatus::PendingListRemoval;
660 }
661 }
662 }
663 }
664
665 // Remove any list items marked
666 size_t i = 0;
667 while ((i >= 0) && (i < plugin_array.GetCount())) {
668 pict = plugin_array.Item(i);
669 if (pict->m_pluginStatus == PluginStatus::PendingListRemoval) {
670 plugin_array.RemoveAt(i);
671 i = 0;
672 } else
673 i++;
674 }
675
676 for (size_t i = 0; i < plugin_array.GetCount(); i++) {
677 pict = plugin_array.Item(i);
678 int yyp = 4;
679 }
680
681 // Now merge and update from the catalog
682 for (auto plugin : available) {
683 PlugInContainer* pic = NULL;
684 // Search for an exact name match in the existing plugin array
685 bool bfound = false;
686 for (size_t i = 0; i < plugin_array.GetCount(); i++) {
687 pic = plugin_array.Item(i);
688 if (plugin_array.Item(i)->m_common_name.IsSameAs(
689 wxString(plugin.name.c_str()))) {
690 bfound = true;
691 break;
692 }
693 }
694
695 // No match found, so add a container, and populate it
696 if (!bfound) {
697 PlugInContainer* new_pic = new PlugInContainer;
698 new_pic->m_common_name = wxString(plugin.name.c_str());
699 new_pic->m_pluginStatus = PluginStatus::ManagedInstallAvailable;
700 new_pic->m_ManagedMetadata = plugin;
701 new_pic->m_version_major = 0;
702 new_pic->m_version_minor = 0;
703
704 // In safe mode, check to see if the plugin appears to be installed
705 // If so, set the status to "ManagedInstalledCurrentVersion", thus
706 // enabling the "uninstall" button.
707 if (safe_mode::get_mode()) {
708 std::string installed;
709 if (isRegularFile(PluginHandler::fileListPath(plugin.name).c_str())) {
710 // Get the installed version from the manifest
711 std::string path = PluginHandler::versionPath(plugin.name);
712 if (path != "" && wxFileName::IsFileReadable(path)) {
713 std::ifstream stream;
714 stream.open(path, std::ifstream::in);
715 stream >> installed;
716 }
717 }
718 if (!installed.empty())
719 new_pic->m_pluginStatus =
720 PluginStatus::ManagedInstalledCurrentVersion;
721 else
722 new_pic->m_pluginStatus = PluginStatus::Unknown;
723 }
724
725 plugin_array.Add(new_pic);
726
727 }
728 // Match found, so merge the info and determine the plugin status
729 else {
730 // If the managed plugin is installed, the fileList (manifest) will be
731 // present
732 if (isRegularFile(PluginHandler::fileListPath(plugin.name).c_str())) {
733 // Get the installed version from the manifest
734 std::string installed;
735 std::string path = PluginHandler::versionPath(plugin.name);
736 if (path != "" && wxFileName::IsFileReadable(path)) {
737 std::ifstream stream;
738 stream.open(path, std::ifstream::in);
739 stream >> installed;
740 }
741 pic->m_InstalledManagedVersion = installed;
742 auto installedVersion = SemanticVersion::parse(installed);
743
744 // Compare to the version reported in metadata
745 auto metaVersion = SemanticVersion::parse(plugin.version);
746 if (installedVersion < metaVersion)
747 pic->m_pluginStatus = PluginStatus::ManagedInstalledUpdateAvailable;
748 else if (installedVersion == metaVersion)
749 pic->m_pluginStatus = PluginStatus::ManagedInstalledCurrentVersion;
750 else if (installedVersion > metaVersion)
751 pic->m_pluginStatus =
752 PluginStatus::ManagedInstalledDowngradeAvailable;
753
754 pic->m_ManagedMetadata = plugin;
755 }
756
757 // If the new plugin is not installed....
758 else {
759 // If the plugin is actually loaded, but the new plugin is known not to
760 // be installed,
761 // then there must be a legacy plugin loaded.
762 // and the new status must be "PluginStatus::LegacyUpdateAvailable"
763 if (pic->m_api_version) {
764 pic->m_pluginStatus = PluginStatus::LegacyUpdateAvailable;
765 pic->m_ManagedMetadata = plugin;
766 }
767 // Otherwise, this is an uninstalled managed plugin.
768 else {
769 pic->m_pluginStatus = PluginStatus::ManagedInstallAvailable;
770 }
771 }
772 }
773 }
774
775 // Sort the list
776
777 // Detach and hold the uninstalled, managed plugins
778 std::map<std::string, PlugInContainer*> sortmap;
779 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
780 PlugInContainer* pic = plugin_array[i];
781 if (pic->m_pluginStatus == PluginStatus::ManagedInstallAvailable) {
782 plugin_array.Remove(pic);
783
784 // Sort by name, lower cased.
785 std::string name = pic->m_ManagedMetadata.name;
786 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
787 sortmap[name] = pic;
788 i = 0; // Restart the list
789 }
790 }
791
792 // Add the detached plugins back at the top of the list.
793 // Later, the list will be populated in reverse order...Why??
794 for (std::map<std::string, PlugInContainer*>::iterator i = sortmap.begin();
795 i != sortmap.end(); i++) {
796 PlugInContainer* pic = i->second;
797 plugin_array.Insert(pic, 0);
798 }
799 evt_pluglist_change.Notify();
800}
801
802bool PluginLoader::UnLoadAllPlugIns() {
803 bool rv = true;
804 while (plugin_array.GetCount()) {
805 if (!UnLoadPlugIn(0)) {
806 rv = false;;
807 }
808 }
809 return rv;
810}
811
812bool PluginLoader::DeactivateAllPlugIns() {
813 for (unsigned int i = 0; i < plugin_array.GetCount(); i++) {
814 PlugInContainer* pic = plugin_array[i];
815 if (pic && pic->m_bEnabled && pic->m_bInitState) DeactivatePlugIn(pic);
816 }
817 return true;
818}
819
820#ifdef __WXMSW__
821/*Convert Virtual Address to File Offset */
822DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
823 size_t i = 0;
824 PIMAGE_SECTION_HEADER pSeh;
825 if (rva == 0) {
826 return (rva);
827 }
828 pSeh = psh;
829 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
830 if (rva >= pSeh->VirtualAddress &&
831 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
832 break;
833 }
834 pSeh++;
835 }
836 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
837}
838#endif
839
841public:
842 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
843 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
844 DependencyMap);
845
846 uint64_t type_magic;
847 DependencyMap dependencies;
848};
849
850#ifdef USE_LIBELF
851bool ReadModuleInfoFromELF(const wxString& file,
852 const ModuleInfo::DependencySet& dependencies,
853 ModuleInfo& info) {
854 static bool b_libelf_initialized = false;
855 static bool b_libelf_usable = false;
856
857 if (b_libelf_usable) {
858 // Nothing to do.
859 } else if (b_libelf_initialized) {
860 return false;
861 } else if (elf_version(EV_CURRENT) == EV_NONE) {
862 b_libelf_initialized = true;
863 b_libelf_usable = false;
864 wxLogError("LibELF is outdated.");
865 return false;
866 } else {
867 b_libelf_initialized = true;
868 b_libelf_usable = true;
869 }
870
871 int file_handle;
872 Elf* elf_handle = NULL;
873 GElf_Ehdr elf_file_header;
874 Elf_Scn* elf_section_handle = NULL;
875
876 file_handle = open(file, O_RDONLY);
877 if (file_handle == -1) {
878 wxLogError("Could not open file \"%s\" for reading: %s", file,
879 strerror(errno));
880 goto FailureEpilogue;
881 }
882
883 elf_handle = elf_begin(file_handle, ELF_C_READ, NULL);
884 if (elf_handle == NULL) {
885 wxLogError("Could not get ELF structures from \"%s\".", file);
886 goto FailureEpilogue;
887 }
888
889 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
890 wxLogError("Could not get ELF file header from \"%s\".", file);
891 goto FailureEpilogue;
892 }
893
894 switch (elf_file_header.e_type) {
895 case ET_EXEC:
896 case ET_DYN:
897 break;
898 default:
899 wxLogError(wxString::Format(
900 "Module \"%s\" is not an executable or shared library.", file));
901 goto FailureEpilogue;
902 }
903
904 info.type_magic =
905 (static_cast<uint64_t>(elf_file_header.e_ident[EI_CLASS])
906 << 0) | // ELF class (32/64).
907 (static_cast<uint64_t>(elf_file_header.e_ident[EI_DATA])
908 << 8) | // Endianness.
909 (static_cast<uint64_t>(elf_file_header.e_ident[EI_OSABI])
910 << 16) | // OS ABI (Linux, FreeBSD, etc.).
911 (static_cast<uint64_t>(elf_file_header.e_ident[EI_ABIVERSION])
912 << 24) | // OS ABI version.
913 (static_cast<uint64_t>(elf_file_header.e_machine)
914 << 32) | // Instruction set.
915 0;
916
917 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
918 NULL) {
919 GElf_Shdr elf_section_header;
920 Elf_Data* elf_section_data = NULL;
921 size_t elf_section_entry_count = 0;
922
923 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
924 &elf_section_header) {
925 wxLogError("Could not get ELF section header from \"%s\".", file);
926 goto FailureEpilogue;
927 } else if (elf_section_header.sh_type != SHT_DYNAMIC) {
928 continue;
929 }
930
931 elf_section_data = elf_getdata(elf_section_handle, NULL);
932 if (elf_section_data == NULL) {
933 wxLogError("Could not get ELF section data from \"%s\".", file);
934 goto FailureEpilogue;
935 }
936
937 if ((elf_section_data->d_size == 0) ||
938 (elf_section_header.sh_entsize == 0)) {
939 wxLogError("Got malformed ELF section metadata from \"%s\".", file);
940 goto FailureEpilogue;
941 }
942
943 elf_section_entry_count =
944 elf_section_data->d_size / elf_section_header.sh_entsize;
945 for (size_t elf_section_entry_index = 0;
946 elf_section_entry_index < elf_section_entry_count;
947 ++elf_section_entry_index) {
948 GElf_Dyn elf_dynamic_entry;
949 const char* elf_dynamic_entry_name = NULL;
950 if (gelf_getdyn(elf_section_data, elf_section_entry_index,
951 &elf_dynamic_entry) != &elf_dynamic_entry) {
952 wxLogError("Could not get ELF dynamic_section entry from \"%s\".",
953 file);
954 goto FailureEpilogue;
955 } else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
956 continue;
957 }
958 elf_dynamic_entry_name = elf_strptr(
959 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
960 if (elf_dynamic_entry_name == NULL) {
961 wxLogError(wxString::Format("Could not get %s %s from \"%s\".", "ELF",
962 "string entry", file));
963 goto FailureEpilogue;
964 }
965 wxString name_full(elf_dynamic_entry_name);
966 wxString name_part(elf_dynamic_entry_name,
967 strcspn(elf_dynamic_entry_name, "-."));
968 if (dependencies.find(name_part) != dependencies.end()) {
969 info.dependencies.insert(
970 ModuleInfo::DependencyMap::value_type(name_part, name_full));
971 }
972 }
973 };
974
975 goto SuccessEpilogue;
976
977SuccessEpilogue:
978 elf_end(elf_handle);
979 close(file_handle);
980 return true;
981
982FailureEpilogue:
983 if (elf_handle != NULL) elf_end(elf_handle);
984 if (file_handle >= 0) close(file_handle);
985 return false;
986}
987#endif // USE_LIBELF
988
989bool PluginLoader::CheckPluginCompatibility(wxString plugin_file) {
990 bool b_compat = true;
991
992#ifdef __WXMSW__
993 char strver[22]; // Enough space even for very big integers...
994 sprintf(strver, "%i%i", wxMAJOR_VERSION, wxMINOR_VERSION);
995 LPCWSTR fNmae = plugin_file.wc_str();
996 HANDLE handle = CreateFile(fNmae, GENERIC_READ, 0, 0, OPEN_EXISTING,
997 FILE_ATTRIBUTE_NORMAL, 0);
998 DWORD byteread, size = GetFileSize(handle, NULL);
999 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1000 ReadFile(handle, virtualpointer, size, &byteread, NULL);
1001 CloseHandle(handle);
1002 // Get pointer to NT header
1003 PIMAGE_NT_HEADERS ntheaders =
1004 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1005 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1006 PIMAGE_SECTION_HEADER pSech =
1007 IMAGE_FIRST_SECTION(ntheaders); // Pointer to first section header
1008 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; // Pointer to import descriptor
1009 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1010 .Size !=
1011 0) /*if size of the table is 0 - Import Table does not exist */
1012 {
1013 pImportDescriptor =
1014 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1015 Rva2Offset(
1016 ntheaders->OptionalHeader
1017 .DataDirectory
1018 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1019 .VirtualAddress,
1020 pSech, ntheaders));
1021 LPSTR libname[256];
1022 size_t i = 0;
1023 // Walk until you reached an empty IMAGE_IMPORT_DESCRIPTOR
1024 while (pImportDescriptor->Name != 0) {
1025 // Get the name of each DLL
1026 libname[i] =
1027 (PCHAR)((DWORD_PTR)virtualpointer +
1028 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1029 if (strstr(libname[i], "wx") != NULL) {
1030 if (strstr(libname[i], strver) == NULL) b_compat = false;
1031 break;
1032 }
1033 pImportDescriptor++; // advance to next IMAGE_IMPORT_DESCRIPTOR
1034 i++;
1035 }
1036 } else {
1037 wxLogMessage(
1038 wxString::Format("No Import Table! in %s", plugin_file.c_str()));
1039 }
1040 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1041#endif
1042#if defined(__WXGTK__) || defined(__WXQT__)
1043#if defined(USE_LIBELF)
1044
1045 static bool b_own_info_queried = false;
1046 static bool b_own_info_usable = false;
1047 static ModuleInfo own_info;
1048 static ModuleInfo::DependencySet dependencies;
1049
1050 if (!b_own_info_queried) {
1051 dependencies.insert("libwx_baseu");
1052 const wxApp& app = *wxTheApp;
1053 if (app.argc && !app.argv[0].IsEmpty()) {
1054 wxString app_path(app.argv[0]);
1055 b_own_info_usable =
1056 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1057 } else {
1058 wxLogError("Cannot get own executable path.");
1059 }
1060 b_own_info_queried = true;
1061 }
1062
1063 if (b_own_info_usable) {
1064 bool b_pi_info_usable = false;
1065 ModuleInfo pi_info;
1066 b_pi_info_usable =
1067 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1068 if (b_pi_info_usable) {
1069 b_compat = (pi_info.type_magic == own_info.type_magic);
1070 if (1 /*g_BasePlatform->isFlatpacked()*/) { // Ignore specific difference in
1071 // OSABI field on flatpak builds
1072 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000)
1073 b_compat = true;
1074 }
1075 if (!b_compat) {
1076 pi_info.dependencies.clear();
1077 wxLogError(
1078 wxString::Format(" Plugin \"%s\" is of another binary "
1079 "flavor than the main module.",
1080 plugin_file));
1081 wxLogDebug("host magic: %.8x, plugin magic: %.8x", own_info.type_magic,
1082 pi_info.type_magic);
1083 }
1084 for (ModuleInfo::DependencyMap::const_iterator own_dependency =
1085 own_info.dependencies.begin();
1086 own_dependency != own_info.dependencies.end(); ++own_dependency) {
1087 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1088 pi_info.dependencies.find(own_dependency->first);
1089 if ((pi_dependency != pi_info.dependencies.end()) &&
1090 (pi_dependency->second != own_dependency->second)) {
1091 b_compat = false;
1092 wxLogError(
1093 " Plugin \"%s\" depends on library \"%s\", but the main "
1094 "module was built for \"%s\".",
1095 plugin_file, pi_dependency->second, own_dependency->second);
1096 break;
1097 }
1098 }
1099 } else {
1100 b_compat = false;
1101 wxLogMessage(
1102 wxString::Format(" Plugin \"%s\" could not be reliably "
1103 "checked for compatibility.",
1104 plugin_file));
1105 }
1106 } else {
1107 // Allow any plugin when own info is not available.
1108 b_compat = true;
1109 }
1110
1111 wxLogMessage("Plugin is compatible by elf library scan: %s",
1112 b_compat ? "true" : "false");
1113 return b_compat;
1114
1115#endif // LIBELF
1116
1117 // But Android Plugins do not include the wxlib specification in their ELF
1118 // file. So we assume Android Plugins are compatible....
1119#ifdef __ANDROID__
1120 return true;
1121#endif
1122
1123 // If libelf is not available, then we must use a simplistic file scan method.
1124 // This is easily fooled if the wxWidgets version in use is not exactly
1125 // recognized. File scan is 3x faster than the ELF scan method
1126
1127 FILE* f = fopen(plugin_file, "r");
1128 char strver[26]; // Enough space even for very big integers...
1129
1130 sprintf(strver,
1131#if defined(__WXGTK3__)
1132 "libwx_gtk3u_core-%i.%i"
1133#elif defined(__WXGTK20__)
1134 "libwx_gtk2u_core-%i.%i"
1135#elif defined(__WXQT__)
1136 "libwx_qtu_core-%i.%i"
1137#else
1138#error undefined plugin platform
1139#endif
1140 ,
1141 wxMAJOR_VERSION, wxMINOR_VERSION);
1142 b_compat = false;
1143
1144 int pos = 0, len = strlen(strver), c;
1145 while ((c = fgetc(f)) != EOF) {
1146 if (c == strver[pos]) {
1147 if (++pos == len) {
1148 b_compat = true;
1149 break;
1150 }
1151 } else
1152 pos = 0;
1153 }
1154 fclose(f);
1155#endif // __WXGTK__ or __WXQT__
1156
1157 wxLogMessage("Plugin is compatible: %s", b_compat ? "true" : "false");
1158 return b_compat;
1159}
1160
1161PlugInContainer* PluginLoader::LoadPlugIn(wxString plugin_file) {
1163 if (!LoadPlugIn(plugin_file, pic)) {
1164 delete pic;
1165 return 0;
1166 } else
1167 return pic;
1168}
1169
1170PlugInContainer* PluginLoader::LoadPlugIn(wxString plugin_file,
1171 PlugInContainer* pic) {
1172 wxLogMessage(wxString("PlugInManager: Loading PlugIn: ") + plugin_file);
1173
1174 // Check if blacklisted, exit if so.
1175 auto sts = m_blacklist->get_status(pic->m_common_name.ToStdString(),
1176 pic->m_version_major,
1177 pic->m_version_minor);
1178 if (sts != plug_status::unblocked) {
1179 wxLogDebug("Refusing to load blacklisted plugin: %s",
1180 pic->m_common_name.ToStdString().c_str());
1181 return 0;
1182 }
1183 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1184 if (data.name != "") {
1185 wxLogDebug("Refusing to load blacklisted library: %s",
1186 plugin_file.ToStdString().c_str());
1187 return 0;
1188 }
1189 pic->m_plugin_file = plugin_file;
1190 pic->m_pluginStatus =
1191 PluginStatus::Unmanaged; // Status is updated later, if necessary
1192
1193 // load the library
1194
1195 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1196
1197 if (!wxIsReadable(plugin_file)) {
1198 evt_unreadable_plugin.Notify(plugin_file.ToStdString());
1199 return 0;
1200 }
1201
1202 pic->m_library.Load(plugin_file);
1203
1204 if (!pic->m_library.IsLoaded()) {
1205
1206 // Look in the Blacklist, try to match a filename, to give some kind of
1207 // message extract the probable plugin name
1208 wxFileName fn(plugin_file);
1209 std::string name = fn.GetName().ToStdString();
1210 wxString msg(wxString::Format("%s:\n%s\n\n",
1211 _("Incompatible plugin detected"),
1212 name.c_str()));
1213 auto found = m_blacklist->get_library_data(name);
1214 if (found.name != "") {
1215 auto msg1 = wxString::Format(_("PlugIn [ %s ] version %i.%i"),
1216 found.name.c_str(), found.major,
1217 found.minor);
1218 msg += msg1;
1219 msg += _(" is incompatible with this version of OpenCPN.");
1220 }
1221 else {
1222 msg = wxString(" PlugInManager: Cannot load library:") + plugin_file;
1223 }
1224 if (m_blacklist->mark_unloadable(plugin_file.ToStdString()))
1225 evt_incompatible_plugin.Notify(msg.ToStdString());
1226 wxLogMessage(wxString(" PlugInManager: Cannot load library: ")
1227 + plugin_file);
1228 return NULL;
1229 }
1230
1231 // load the factory symbols
1232 const char* const FIX_LOADING =
1233 _("\n Install/uninstall plugin or remove file to mute message");
1234 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol("create_pi");
1235 if (NULL == create_plugin) {
1236 std::string msg(_(" PlugInManager: Cannot load symbol create_pi: "));
1237 msg += plugin_file;
1238 wxLogMessage(msg.c_str());
1239 if (m_blacklist->mark_unloadable(plugin_file.ToStdString()))
1240 evt_incompatible_plugin.Notify(msg + FIX_LOADING);
1241 return 0;
1242 }
1243
1244 destroy_t* destroy_plugin =
1245 (destroy_t*)pic->m_library.GetSymbol("destroy_pi");
1246 pic->m_destroy_fn = destroy_plugin;
1247 if (NULL == destroy_plugin) {
1248 std::string msg(_(" PlugInManager: Cannot load symbol destroy_pi: "));
1249 msg += plugin_file;
1250 wxLogMessage(msg.c_str());
1251 if (m_blacklist->mark_unloadable(plugin_file.ToStdString()))
1252 evt_incompatible_plugin.Notify(msg + FIX_LOADING);
1253 return 0;
1254 }
1255
1256 // create an instance of the plugin class
1257 opencpn_plugin* plug_in = create_plugin(this);
1258
1259 int api_major = plug_in->GetAPIVersionMajor();
1260 int api_minor = plug_in->GetAPIVersionMinor();
1261 int api_ver = (api_major * 100) + api_minor;
1262 pic->m_api_version = api_ver;
1263
1264 int pi_major = plug_in->GetPlugInVersionMajor();
1265 int pi_minor = plug_in->GetPlugInVersionMinor();
1266 SemanticVersion pi_ver(pi_major, pi_minor, -1);
1267
1268 wxString pi_name = plug_in->GetCommonName();
1269
1270 wxLogDebug("blacklist: Get status for %s %d %d",
1271 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1272 const auto status = m_blacklist->get_status(pi_name.ToStdString(),
1273 pi_major, pi_minor);
1274 if (status != plug_status::unblocked) {
1275 wxLogDebug("Ignoring blacklisted plugin.");
1276 if (status != plug_status::unloadable) {
1277 plug_data data(pi_name.ToStdString(), pi_major, pi_minor);
1278 auto msg = m_blacklist->get_message(status, data);
1279 evt_incompatible_plugin.Notify(msg);
1280 }
1281 return NULL;
1282 }
1283
1284 switch (api_ver) {
1285 case 105:
1286 pic->m_pplugin = dynamic_cast<opencpn_plugin*>(plug_in);
1287 break;
1288
1289 case 106:
1290 pic->m_pplugin = dynamic_cast<opencpn_plugin_16*>(plug_in);
1291 break;
1292
1293 case 107:
1294 pic->m_pplugin = dynamic_cast<opencpn_plugin_17*>(plug_in);
1295 break;
1296
1297 case 108:
1298 pic->m_pplugin = dynamic_cast<opencpn_plugin_18*>(plug_in);
1299 break;
1300
1301 case 109:
1302 pic->m_pplugin = dynamic_cast<opencpn_plugin_19*>(plug_in);
1303 break;
1304
1305 case 110:
1306 pic->m_pplugin = dynamic_cast<opencpn_plugin_110*>(plug_in);
1307 break;
1308
1309 case 111:
1310 pic->m_pplugin = dynamic_cast<opencpn_plugin_111*>(plug_in);
1311 break;
1312
1313 case 112:
1314 pic->m_pplugin = dynamic_cast<opencpn_plugin_112*>(plug_in);
1315 break;
1316
1317 case 113:
1318 pic->m_pplugin = dynamic_cast<opencpn_plugin_113*>(plug_in);
1319 break;
1320
1321 case 114:
1322 pic->m_pplugin = dynamic_cast<opencpn_plugin_114*>(plug_in);
1323 break;
1324 case 115:
1325 pic->m_pplugin = dynamic_cast<opencpn_plugin_115*>(plug_in);
1326 break;
1327
1328 case 116:
1329 pic->m_pplugin = dynamic_cast<opencpn_plugin_116*>(plug_in);
1330 break;
1331
1332 case 117:
1333 pic->m_pplugin = dynamic_cast<opencpn_plugin_117*>(plug_in);
1334 do /* force a local scope */ {
1335 auto p = dynamic_cast<opencpn_plugin_117*>(plug_in);
1336 pi_ver =
1337 SemanticVersion(pi_major, pi_minor, p->GetPlugInVersionPatch(),
1338 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1339 p->GetPlugInVersionBuild());
1340 } while (false);
1341 break;
1342 case 118:
1343 pic->m_pplugin = dynamic_cast<opencpn_plugin_118*>(plug_in);
1344 do /* force a local scope */ {
1345 auto p = dynamic_cast<opencpn_plugin_118*>(plug_in);
1346 pi_ver =
1347 SemanticVersion(pi_major, pi_minor, p->GetPlugInVersionPatch(),
1348 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1349 p->GetPlugInVersionBuild());
1350 } while (false);
1351 break;
1352
1353 default:
1354 break;
1355 }
1356
1357 std::stringstream ss;
1358 if (!pic->m_pplugin) {
1359 ss << _("Incompatible plugin detected: ") << plugin_file << "\n";
1360 ss << _(" API Version detected: ");
1361 ss << api_major << "." << api_minor << "\n";
1362 ss << _(" PlugIn Version detected: ") << pi_ver << "\n";
1363 INFO_LOG << ss.str();
1364 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1365 pi_ver.minor))
1366 {
1367 evt_incompatible_plugin.Notify(ss.str());
1368 }
1369 return 0;
1370 }
1371
1372 return pic;
1373}
Wrapper for configuration variables which lives in a wxBaseConfig object.
const void Notify()
Notify all listeners, no data supplied.
SemanticVersion GetVersion()
Return version from plugin API.
static std::string fileListPath(std::string name)
Return path to installation manifest for given plugin.
static std::string versionPath(std::string name)
Return path to file containing version for given plugin.
static bool isCompatible(const PluginMetadata &metadata, const char *os=PKG_TARGET, const char *os_version=PKG_TARGET_VERSION)
Return true if given plugin is loadable on given os/version.
PluginLoader is a backend module without any direct GUI functionality.
bool UnLoadPlugIn(size_t ix)
Unload, delete and remove item ix in GetPlugInArray().
EventVar evt_deactivate_plugin
Receives a malloc'ed copy of a PlugInContainer owned by listener.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
Definition: plugin_paths.h:26
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
Definition: plugin_paths.h:29
static PluginPaths * getInstance()
Return the singleton instance.
virtual wxBitmap * GetPlugInBitmap()
FIXME static wxBitmap* LoadSVG(const wxString filename, unsigned int width, ...
Definition: ocpn_plugin.cpp:78
Plugin metadata, reflects the xml format directly.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
Definition: semantic_vers.h:51
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.