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