OpenCPN Partial API docs
Loading...
Searching...
No Matches
plugin_blacklist.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2022 by Alec Leamas *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
25#include <algorithm>
26#include <cctype>
27#include <regex>
28#include <unordered_map>
29#include <vector>
30
31#include <wx/translation.h>
32#include <wx/log.h>
33
35#include "model/logger.h"
36
37// Work around gnu's major() and minor() macros
38#ifdef major
39#undef major
40#undef minor
41#endif
42
43#ifdef _WIN32
44static const char SEP = '\\';
45#else
46static const char SEP = '/';
47#endif
48
50typedef struct config_block {
51 const char* name;
53 int version_minor;
54 bool hard;
56 const char* message;
58
59static const char* const STD_HARD_MSG = _(R"(
60PlugIn %s, version %i.%i was detected.
61This version is known to be unstable and will not be loaded.
62Please update this PlugIn using the PlugIn manager master catalog.
63)");
64
65static const char* const STD_SOFT_MSG = _(R"(
66PlugIn %s, version %i.%i was detected.
67This version is known to be unstable.
68Please update this PlugIn using the PlugIn manager master catalog.
69)");
70
71static const char* const OCHART_OBSOLETED_MSG = _(R"(
72PlugIn %s, version %i.%i was detected.
73This plugin is obsolete, the o-charts plugin should be used
74instead. Please uninstall this plugin and install o-charts
75using the PlugIn manager master catalog.
76)");
77
78static const config_block plugin_blacklist[] = {
79 {"Radar", 0, 95, true, STD_HARD_MSG},
80 {"Watchdog", 1, 0, true, STD_HARD_MSG},
81 {"squiddio", 0, 2, true, STD_HARD_MSG},
82 {"ObjSearch", 0, 3, true, STD_HARD_MSG},
83#ifdef __WXOSX__
84 {"S63", 0, 6, true, STD_HARD_MSG},
85#endif
86 {"oeSENC", 99, 99, true, OCHART_OBSOLETED_MSG},
87 {"oernc_pi", 99, 99, true, OCHART_OBSOLETED_MSG},
88 {"oesenc_pi", 99, 99, true, OCHART_OBSOLETED_MSG}};
89
91typedef struct block {
92 int major;
93 int minor;
94 plug_status status;
95 const char* message;
96
97 block() : major(0), minor(0), status(plug_status::unblocked), message("") {};
98
99 block(int _major, int _minor)
100 : major(_major),
101 minor(_minor),
102 status(plug_status::unblocked),
103 message("") {};
104
105 block(const struct config_block& cb)
106 : major(cb.version_major),
107 minor(cb.version_minor),
108 status(cb.hard ? plug_status::hard : plug_status::soft),
109 message(cb.message) {};
110
112 bool is_matching(int _major, int _minor) const {
113 if (major == -1) return true;
114 if (_major == -1) return false;
115 if (_major > major) return false;
116 if (_minor > minor) return false;
117 return true;
118 };
119
120 plug_data to_plug_data(std::string name) {
121 return plug_data(name, major, minor);
122 }
123
125
127static inline std::string normalize_lib(const std::string& name) {
128 auto libname(name);
129 auto slashpos = libname.rfind(SEP);
130 if (slashpos != std::string::npos) libname = libname.substr(slashpos + 1);
131#if defined(__WXGTK__) || defined(__WXOSX__)
132 if (libname.find("lib") == 0) libname = libname.substr(3);
133#endif
134 auto dotpos = libname.rfind('.');
135 if (dotpos != std::string::npos) libname = libname.substr(0, dotpos);
136 return libname;
137}
138
139static std::string to_lower(const std::string& arg) {
140 std::string s(arg);
141 std::transform(s.begin(), s.end(), s.begin(),
142 [](unsigned char c) { return std::tolower(c); });
143 return s;
144}
145
147 friend std::unique_ptr<AbstractBlacklist> blacklist_factory();
148
149 typedef std::unordered_map<std::string, block> block_map;
150
151private:
152 PlugBlacklist() {
153 constexpr int list_len = sizeof(plugin_blacklist) / sizeof(config_block);
154 for (int i = 0; i < list_len; i += 1) {
155 m_blocks[plugin_blacklist[i].name] = block(plugin_blacklist[i]);
156 }
157 }
158
159 block_map m_blocks;
160
161 block_map::iterator find_block(const std::string& name) {
162 const auto s = to_lower(name);
163 for (auto it = m_blocks.begin(); it != m_blocks.end(); it++) {
164 if (to_lower(it->first) == s) return it;
165 }
166 return m_blocks.end();
167 }
168
169 bool update_block(const std::string& name, int major, int minor) {
170 bool new_block = false;
171 if (m_blocks.find(name) == m_blocks.end()) {
172 m_blocks[name] = block(major, minor);
173 new_block = true;
174 }
175 m_blocks[name].status = plug_status::unloadable;
176 m_blocks[name].major = major;
177 m_blocks[name].minor = minor;
178 return new_block;
179 }
180
182 std::string format_message(const std::string msg, const plug_data& data) {
183 int size = std::snprintf(nullptr, 0, msg.c_str(), data.name.c_str(),
184 data.major, data.minor);
185 if (size < 0) {
186 wxLogWarning("Cannot format message for %s", data.name.c_str());
187 return "Internal error: Cannot format message(!)";
188 }
189 std::unique_ptr<char[]> buf(new char[size]);
190 std::snprintf(buf.get(), size, msg.c_str(), data.name.c_str(), data.major,
191 data.minor);
192 return std::string(buf.get(), buf.get() + size - 1);
193 }
194
195public:
196 virtual plug_data get_library_data(const std::string& library_file) {
197 std::string filename(normalize_lib(library_file));
198 auto found = find_block(filename);
199 if (found == m_blocks.end()) return plug_data("", -1, -1);
200 return plug_data(found->first, found->second.major, found->second.minor);
201 }
202
203 plug_status get_status(const std::string& name, int major, int minor) {
204 if (m_blocks.find(name) == m_blocks.end()) return plug_status::unblocked;
205 const auto& b = m_blocks[name];
206 return b.is_matching(major, minor) ? b.status : plug_status::unblocked;
207 }
208
210 return get_status(pd.name, pd.major, pd.minor);
211 }
212
213 virtual bool mark_unloadable(const std::string& name, int major, int minor) {
214 return update_block(name, major, minor);
215 }
216
218 bool mark_unloadable(const std::string& path) {
219 auto filename(path);
220 auto slashpos = filename.rfind(SEP);
221 if (slashpos != std::string::npos) filename = filename.substr(slashpos + 1);
222 return update_block(filename, -1, -1);
223 }
224
225 bool is_loadable(const std::string path) {
226 auto filename(path);
227 auto slashpos = filename.rfind(SEP);
228 if (slashpos != std::string::npos) filename = filename.substr(slashpos + 1);
229
230 if (m_blocks.find(filename) == m_blocks.end()) return true;
231 return m_blocks[filename].status != plug_status::unloadable;
232 }
233
234// gcc 12 bogus regex warning
235#pragma GCC diagnostic push
236#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
237 std::string get_message(plug_status status, const plug_data& data) {
238 if (status == plug_status::unloadable) {
239 std::string msg(_("Plugin library %s can not be loaded"));
240 msg = std::regex_replace(msg, std::regex("%s"), data.name);
241 return msg;
242 }
243 if (status == plug_status::unblocked) {
244 wxLogMessage("Attempt to get message for unblocked plugin %s",
245 data.name.c_str());
246 return "No applicable message";
247 }
248 auto found = find_block(data.name);
249 if (found == m_blocks.end())
250 return format_message("No known message for %s version %d.%d", data);
251 else
252 return format_message(found->second.message, data);
253 }
254#pragma GCC diagnostic pop
255};
256
257std::unique_ptr<AbstractBlacklist> blacklist_factory() {
258 return std::unique_ptr<AbstractBlacklist>(new PlugBlacklist());
259};
260
261#ifdef BLACKLIST_TEST
262// $ export CPPFLAGS="-g -I../include -DBLACKLIST_TEST -D__WXGTK__"
263// $ make plugin_blacklist
264// $ ./plugin_blacklist aisradar_pi 0 96
265// unblocked
266
267#include <iostream>
268int main(int argc, char** argv) {
269 const std::string name(argv[1]);
270 int major = atoi(argv[2]);
271 int minor = atoi(argv[3]);
272 auto blacklist = blacklist_factory();
273 blacklist->mark_unloadable("foo");
274 auto s = blacklist->get_status(name, major, minor);
275 switch (s) {
276 case plug_status::unloadable:
277 std::cout << "unloadable\n";
278 break;
279 case plug_status::unblocked:
280 std::cout << "unblocked\n";
281 break;
282 case plug_status::hard:
283 std::cout << "hard\n";
284 break;
285 case plug_status::soft:
286 std::cout << "soft\n";
287 break;
288 }
289 auto lib = blacklist->plugin_by_libname(name);
290 std::cout << "found plugin: \"" << lib.name << "\" version: " << lib.major
291 << "." << lib.minor << "\n";
292 exit(0);
293}
294
295#endif // BLACKLIST_TEST
Plugins could be blacklisted in runtime if they are unloadable or in hardcoded, compile-time list.
plug_status get_status(const plug_data pd)
Return status for given official plugin name and version.
bool mark_unloadable(const std::string &path)
Given a path, mark filename as unloadable.
virtual plug_data get_library_data(const std::string &library_file)
Best effort attempt to get data for a library file.
bool is_loadable(const std::string path)
Return true iff plugin (a path) is loadable.
virtual bool mark_unloadable(const std::string &name, int major, int minor)
Given plugin name and version mark it as unloadable.
std::string get_message(plug_status status, const plug_data &data)
Return plugin-specific message, possibly "".
plug_status get_status(const std::string &name, int major, int minor)
Return status for given official plugin name and version.
Enhanced logging interface on top of wx/log.h.
Plugin blacklist for plugins which can or should not be loaded.
plug_status
Runtime representation of a plugin block.
bool is_matching(int _major, int _minor) const
Return true if _major/_minor matches the blocked plugin.
Hardcoded representation of a blocked plugin.
const char * message
If true, unconditional hard block; else load plugin with a warning.
int version_major
Official plugin name as of GetCommonName().