OpenCPN Partial API docs
Loading...
Searching...
No Matches
plugin_blacklist.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Plugin blacklist for plugins which can or should not be loaded
5 * Author: Alec Leamas
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 by Alec Leamas *
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#include "model/plugin_blacklist.h"
26
27#include <algorithm>
28#include <cctype>
29#include <regex>
30#include <unordered_map>
31#include <vector>
32
33#include <wx/translation.h>
34#include <wx/log.h>
35
36#include "model/logger.h"
37
38// Work around gnu's major() and minor() macros
39#ifdef major
40#undef major
41#undef minor
42#endif
43
44#ifdef _WIN32
45static const char SEP = '\\';
46#else
47static const char SEP = '/';
48#endif
49
51typedef struct config_block {
52 const char* name;
54 int version_minor;
55 bool hard;
57 const char* message;
59
60static const char* const STD_HARD_MSG = _(R"(
61PlugIn %s, version %i.%i was detected.
62This version is known to be unstable and will not be loaded.
63Please update this PlugIn using the PlugIn manager master catalog.
64)");
65
66static const char* const STD_SOFT_MSG = _(R"(
67PlugIn %s, version %i.%i was detected.
68This version is known to be unstable.
69Please update this PlugIn using the PlugIn manager master catalog.
70)");
71
72static const char* const OCHART_OBSOLETED_MSG = _(R"(
73PlugIn %s, version %i.%i was detected.
74This plugin is obsolete, the o-charts plugin should be used
75instead. Please uninstall this plugin and install o-charts
76using the PlugIn manager master catalog.
77)");
78
79static const config_block plugin_blacklist[] = {
80 {"Radar", 0, 95, true, STD_HARD_MSG},
81 {"Watchdog", 1, 0, true, STD_HARD_MSG},
82 {"squiddio", 0, 2, true, STD_HARD_MSG},
83 {"ObjSearch", 0, 3, true, STD_HARD_MSG},
84#ifdef __WXOSX__
85 {"S63", 0, 6, true, STD_HARD_MSG},
86#endif
87 {"oeSENC", 99, 99, true, OCHART_OBSOLETED_MSG},
88 {"oernc_pi", 99, 99, true, OCHART_OBSOLETED_MSG},
89 {"oesenc_pi", 99, 99, true, OCHART_OBSOLETED_MSG}};
90
92typedef struct block {
93 int major;
94 int minor;
95 plug_status status;
96 const char* message;
97
98 block() : major(0), minor(0), status(plug_status::unblocked), message("") {};
99
100 block(int _major, int _minor)
101 : major(_major),
102 minor(_minor),
103 status(plug_status::unblocked),
104 message("") {};
105
106 block(const struct config_block& cb)
107 : major(cb.version_major),
108 minor(cb.version_minor),
109 status(cb.hard ? plug_status::hard : plug_status::soft),
110 message(cb.message) {};
111
113 bool is_matching(int _major, int _minor) const {
114 if (major == -1) return true;
115 if (_major == -1) return false;
116 if (_major > major) return false;
117 if (_minor > minor) return false;
118 return true;
119 };
120
121 plug_data to_plug_data(std::string name) {
122 return plug_data(name, major, minor);
123 }
124
125} block;
126
128static inline std::string normalize_lib(const std::string& name) {
129 auto libname(name);
130 auto slashpos = libname.rfind(SEP);
131 if (slashpos != std::string::npos) libname = libname.substr(slashpos + 1);
132#if defined(__WXGTK__) || defined(__WXOSX__)
133 if (libname.find("lib") == 0) libname = libname.substr(3);
134#endif
135 auto dotpos = libname.rfind('.');
136 if (dotpos != std::string::npos) libname = libname.substr(0, dotpos);
137 return libname;
138}
139
140static std::string to_lower(const std::string& arg) {
141 std::string s(arg);
142 std::transform(s.begin(), s.end(), s.begin(),
143 [](unsigned char c) { return std::tolower(c); });
144 return s;
145}
146
148 friend std::unique_ptr<AbstractBlacklist> blacklist_factory();
149
150 typedef std::unordered_map<std::string, block> block_map;
151
152private:
153 PlugBlacklist() {
154 constexpr int list_len = sizeof(plugin_blacklist) / sizeof(config_block);
155 for (int i = 0; i < list_len; i += 1) {
156 m_blocks[plugin_blacklist[i].name] = block(plugin_blacklist[i]);
157 }
158 }
159
160 block_map m_blocks;
161
162 block_map::iterator find_block(const std::string& name) {
163 const auto s = to_lower(name);
164 for (auto it = m_blocks.begin(); it != m_blocks.end(); it++) {
165 if (to_lower(it->first) == s) return it;
166 }
167 return m_blocks.end();
168 }
169
170 bool update_block(const std::string& name, int major, int minor) {
171 bool new_block = false;
172 if (m_blocks.find(name) == m_blocks.end()) {
173 m_blocks[name] = block(major, minor);
174 new_block = true;
175 }
176 m_blocks[name].status = plug_status::unloadable;
177 m_blocks[name].major = major;
178 m_blocks[name].minor = minor;
179 return new_block;
180 }
181
183 std::string format_message(const std::string msg, const plug_data& data) {
184 int size = std::snprintf(nullptr, 0, msg.c_str(), data.name.c_str(),
185 data.major, data.minor);
186 if (size < 0) {
187 wxLogWarning("Cannot format message for %s", data.name.c_str());
188 return "Internal error: Cannot format message(!)";
189 }
190 std::unique_ptr<char[]> buf(new char[size]);
191 std::snprintf(buf.get(), size, msg.c_str(), data.name.c_str(), data.major,
192 data.minor);
193 return std::string(buf.get(), buf.get() + size - 1);
194 }
195
196public:
197 virtual plug_data get_library_data(const std::string& library_file) {
198 std::string filename(normalize_lib(library_file));
199 auto found = find_block(filename);
200 if (found == m_blocks.end()) return plug_data("", -1, -1);
201 return plug_data(found->first, found->second.major, found->second.minor);
202 }
203
204 plug_status get_status(const std::string& name, int major, int minor) {
205 if (m_blocks.find(name) == m_blocks.end()) return plug_status::unblocked;
206 const auto& b = m_blocks[name];
207 return b.is_matching(major, minor) ? b.status : plug_status::unblocked;
208 }
209
210 plug_status get_status(const plug_data pd) {
211 return get_status(pd.name, pd.major, pd.minor);
212 }
213
214 virtual bool mark_unloadable(const std::string& name, int major, int minor) {
215 return update_block(name, major, minor);
216 }
217
219 bool mark_unloadable(const std::string& path) {
220 auto filename(path);
221 auto slashpos = filename.rfind(SEP);
222 if (slashpos != std::string::npos) filename = filename.substr(slashpos + 1);
223 return update_block(filename, -1, -1);
224 }
225
226 bool is_loadable(const std::string path) {
227 auto filename(path);
228 auto slashpos = filename.rfind(SEP);
229 if (slashpos != std::string::npos) filename = filename.substr(slashpos + 1);
230
231 if (m_blocks.find(filename) == m_blocks.end()) return true;
232 return m_blocks[filename].status != plug_status::unloadable;
233 }
234
235// gcc 12 bogus regex warning
236#pragma GCC diagnostic push
237#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
238 std::string get_message(plug_status status, const plug_data& data) {
239 if (status == plug_status::unloadable) {
240 std::string msg(_("Plugin library %s can not be loaded"));
241 msg = std::regex_replace(msg, std::regex("%s"), data.name);
242 return msg;
243 }
244 if (status == plug_status::unblocked) {
245 wxLogMessage("Attempt to get message for unblocked plugin %s",
246 data.name.c_str());
247 return "No applicable message";
248 }
249 auto found = find_block(data.name);
250 if (found == m_blocks.end())
251 return format_message("No known message for %s version %d.%d", data);
252 else
253 return format_message(found->second.message, data);
254 }
255#pragma GCC diagnostic pop
256};
257
258std::unique_ptr<AbstractBlacklist> blacklist_factory() {
259 return std::unique_ptr<AbstractBlacklist>(new PlugBlacklist());
260};
261
262#ifdef BLACKLIST_TEST
263// $ export CPPFLAGS="-g -I../include -DBLACKLIST_TEST -D__WXGTK__"
264// $ make plugin_blacklist
265// $ ./plugin_blacklist aisradar_pi 0 96
266// unblocked
267
268#include <iostream>
269int main(int argc, char** argv) {
270 const std::string name(argv[1]);
271 int major = atoi(argv[2]);
272 int minor = atoi(argv[3]);
273 auto blacklist = blacklist_factory();
274 blacklist->mark_unloadable("foo");
275 auto s = blacklist->get_status(name, major, minor);
276 switch (s) {
277 case plug_status::unloadable:
278 std::cout << "unloadable\n";
279 break;
280 case plug_status::unblocked:
281 std::cout << "unblocked\n";
282 break;
283 case plug_status::hard:
284 std::cout << "hard\n";
285 break;
286 case plug_status::soft:
287 std::cout << "soft\n";
288 break;
289 }
290 auto lib = blacklist->plugin_by_libname(name);
291 std::cout << "found plugin: \"" << lib.name << "\" version: " << lib.major
292 << "." << lib.minor << "\n";
293 exit(0);
294}
295
296#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.
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().