35#include <unordered_map>
50#include <archive_entry.h>
51typedef __LA_INT64_T la_int64_t;
53#if defined(__MINGW32__) && defined(Yield)
57#include "base_platform.h"
58#include "catalog_handler.h"
59#include "catalog_parser.h"
61#include "downloader.h"
65#include "ocpn_utils.h"
66#include "plugin_cache.h"
67#include "plugin_handler.h"
68#include "plugin_loader.h"
69#include "plugin_paths.h"
72static std::string SEP(
"\\");
74static std::string SEP(
"/");
82extern wxString g_winPluginDir;
84extern bool g_bportable;
86extern wxString g_compatOS;
87extern wxString g_compatOsVersion;
90static std::vector<std::string> split(
const std::string& s,
91 const std::string& delim) {
92 std::vector<std::string> result;
93 size_t pos = s.find(delim);
94 if (pos == std::string::npos) {
98 result.push_back(s.substr(0, pos));
99 result.push_back(s.substr(pos + delim.length()));
103inline std::string basename(
const std::string path) {
104 wxFileName wxFile(path);
105 return wxFile.GetFullName().ToStdString();
108bool isRegularFile(
const char* path) {
109 wxFileName wxFile(path);
110 return wxFile.FileExists() && !wxFile.IsDir();
113static void mkdir(
const std::string path) {
114#if defined(_WIN32) && !defined(__MINGW32__)
115 _mkdir(path.c_str());
116#elif defined(__MINGW32__)
119 mkdir(path.c_str(), 0755);
127static ssize_t PlugInIxByName(
const std::string name, ArrayOfPlugIns* plugins) {
128 for (
unsigned i = 0; i < plugins->GetCount(); i += 1) {
129 if (name == plugins->Item(i)->m_common_name.ToStdString()) {
136static std::string pluginsConfigDir() {
137 std::string pluginDataDir = g_BasePlatform->GetPrivateDataDir().ToStdString();
138 pluginDataDir += SEP +
"plugins";
139 if (!ocpn::exists(pluginDataDir)) {
140 mkdir(pluginDataDir);
142 pluginDataDir += SEP +
"install_data";
143 if (!ocpn::exists(pluginDataDir)) {
144 mkdir(pluginDataDir);
146 return pluginDataDir;
149static std::string dirListPath(std::string name) {
150 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
151 return pluginsConfigDir() + SEP + name +
".dirs";
158 m_abi = metadata.target;
159 m_abi_version = metadata.target_version;
160 m_major_version = ocpn::split(m_abi_version.c_str(),
".")[0];
161 m_name = metadata.name;
162 wxLogDebug(
"Plugin: setting up, name: %s", m_name);
163 wxLogDebug(
"Plugin: init: abi: %s, abi_version: %s, major ver: %s", m_abi,
164 m_abi_version, m_major_version);
166 const std::string& abi()
const {
return m_abi; }
167 const std::string& abi_version()
const {
return m_abi_version; }
168 const std::string& major_version()
const {
return m_major_version; }
169 const std::string& name()
const {
return m_name; }
173 std::string m_abi_version;
174 std::string m_major_version;
182 m_abi = compatOs->name();
183 m_abi_version = compatOs->version();
184 m_major_version = ocpn::split(m_abi_version.c_str(),
".")[0];
185 wxLogDebug(
"Host: init: abi: %s, abi_version: %s, major ver: %s", m_abi,
186 m_abi_version, m_major_version);
189 bool is_version_compatible(
const Plugin& plugin)
const {
190 if (ocpn::startswith(plugin.abi(),
"ubuntu")) {
191 return plugin.abi_version() == m_abi_version;
193 return plugin.major_version() == m_major_version;
198 bool is_debian_plugin_compatible(
const Plugin& plugin)
const {
199 if (!ocpn::startswith(m_abi,
"ubuntu"))
return false;
200 static const std::vector<std::string> compat_versions = {
202 "debian-x86_64;11;ubuntu-gtk3-x86_64;20.04",
203 "debian-wx32-x86_64;11;ubuntu-wx32-x86_64;22.04",
204 "debian-x86_64;12;ubuntu-x86_64;23.04",
205 "debian-x86_64;12;ubuntu-x86_64;23.10",
206 "debian-x86_64;12;ubuntu-x86_64;24.04",
207 "debian-x86_64;sid;ubuntu-x86_64;24.04"
209 if (ocpn::startswith(plugin.abi(),
"debian")) {
210 wxLogDebug(
"Checking for debian plugin on a ubuntu-x86_64 host");
211 const std::string compat_version =
212 plugin.abi() +
";" + plugin.major_version() +
";" + m_abi +
";" + m_abi_version;
213 for (
auto& cv : compat_versions) {
214 if (compat_version == cv) {
222 const std::string& abi()
const {
return m_abi; }
224 const std::string& abi_version()
const {
return m_abi_version; }
226 const std::string& major_version()
const {
return m_major_version; }
230 std::string m_abi_version;
231 std::string m_major_version;
235 static std::string last_global_os(
"");
238 if (!instance || last_global_os != g_compatOS) {
240 last_global_os = g_compatOS;
245CompatOs::CompatOs() : _name(PKG_TARGET), _version(PKG_TARGET_VERSION) {
251 std::string compatOS(_name);
252 std::string compatOsVersion(_version);
254 if (getenv(
"OPENCPN_COMPAT_TARGET") != 0) {
255 _name = getenv(
"OPENCPN_COMPAT_TARGET");
256 if (_name.find(
':') != std::string::npos) {
257 auto tokens = ocpn::split(_name.c_str(),
":");
259 _version = tokens[1];
261 }
else if (g_compatOS !=
"") {
264 if (g_compatOsVersion !=
"") {
265 _version = g_compatOsVersion;
267 }
else if (ocpn::startswith(_name,
"ubuntu") && (_version ==
"22.04")) {
268 int wxv = wxMAJOR_VERSION * 10 + wxMINOR_VERSION;
270 auto tokens = ocpn::split(_name.c_str(),
"-");
271 _name = std::string(tokens[0]) + std::string(
"-wx32-") + tokens[1];
275 _name = ocpn::tolower(_name);
276 _version = ocpn::tolower(_version);
280 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
281 return pluginsConfigDir() + SEP + name +
".files";
284PluginHandler::PluginHandler() {}
287 const char* os_version) {
289 static const std::vector<std::string> simple_abis = {
"msvc",
"msvc-wx32",
290 "darwin",
"darwin-wx32",
"android-armhf",
"android-arm64"};
292 auto compatOS = CompatOs::getInstance();
296 auto found = std::find(simple_abis.begin(), simple_abis.end(), plugin.abi());
297 if (found != simple_abis.end()) {
298 bool ok = plugin.abi() == host.abi();
299 wxLogDebug(
"Returning %s for %s", (ok ?
"ok" :
"fail"), host.abi());
304 if (host.abi() == plugin.abi() && host.is_version_compatible(plugin)) {
306 wxLogDebug(
"Found matching abi version %s", plugin.abi_version());
307 }
else if (host.is_debian_plugin_compatible(plugin)) {
309 wxLogDebug(
"Found Debian version matching Ubuntu host");
311 DEBUG_LOG <<
"Plugin compatibility check Final: "
312 << (rv ?
"ACCEPTED: " :
"REJECTED: ") << metadata.name;
317 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
318 return pluginsConfigDir() + SEP + name +
".version";
321typedef std::unordered_map<std::string, std::string> pathmap_t;
327static pathmap_t getInstallPaths() {
339static void saveFilelist(std::string filelist, std::string name) {
342 ofstream diskfiles(listpath);
343 if (!diskfiles.is_open()) {
344 wxLogWarning(
"Cannot create installed files list.");
347 diskfiles << filelist;
350static void saveDirlist(std::string name) {
352 string path = dirListPath(name);
354 if (!dirs.is_open()) {
355 wxLogWarning(
"Cannot create installed files list.");
358 pathmap_t pathmap = getInstallPaths();
359 unordered_map<string, string>::iterator it;
360 for (it = pathmap.begin(); it != pathmap.end(); it++) {
361 dirs << it->first <<
": " << it->second << endl;
365static void saveVersion(
const std::string& name,
const std::string& version) {
368 ofstream stream(path);
369 if (!stream.is_open()) {
370 wxLogWarning(
"Cannot create version file.");
373 stream << version << endl;
376static int copy_data(
struct archive* ar,
struct archive* aw) {
383 r = archive_read_data_block(ar, &buff, &size, &offset);
384 if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
385 if (r < ARCHIVE_OK) {
386 std::string s(archive_error_string(ar));
389 r = archive_write_data_block(aw, buff, size, offset);
390 if (r < ARCHIVE_OK) {
391 std::string s(archive_error_string(aw));
392 wxLogWarning(
"Error copying install data: %s", archive_error_string(aw));
398static bool win_entry_set_install_path(
struct archive_entry* entry,
399 pathmap_t installPaths) {
402 string path = archive_entry_pathname(entry);
405 int slashes = count(path.begin(), path.end(),
'/');
407 archive_entry_set_pathname(entry,
"");
410 if (ocpn::startswith(path,
"./")) {
411 path = path.substr(1);
415 int slashpos = path.find_first_of(
'/', 1);
417 archive_entry_set_pathname(entry,
"");
421 string prefix = path.substr(0, slashpos);
422 path = path.substr(prefix.size() + 1);
425 if (ocpn::endswith(path,
".dll") || ocpn::endswith(path,
".exe")) {
426 slashpos = path.find_first_of(
'/');
427 path = path.substr(slashpos + 1);
428 path = installPaths[
"bin"] +
"\\" + path;
429 }
else if (ocpn::startswith(path,
"share")) {
431 wxFileName fn(installPaths[
"share"].c_str(),
434 path = fn.GetFullPath().ToStdString() + path;
435 }
else if (ocpn::startswith(path,
"plugins")) {
436 slashpos = path.find_first_of(
'/');
438 path = path.substr(slashpos + 1);
439 path = installPaths[
"share"] +
"\\" + path;
441 }
else if (archive_entry_filetype(entry) == AE_IFREG) {
442 wxString msg(_T(
"PluginHandler::Invalid install path on file: "));
443 msg += wxString(path.c_str());
448 s.Replace(
"/",
"\\");
449 s.Replace(
"\\\\",
"\\");
450 archive_entry_set_pathname(entry, s.c_str());
454static bool flatpak_entry_set_install_path(
struct archive_entry* entry,
455 pathmap_t installPaths) {
458 string path = archive_entry_pathname(entry);
459 int slashes = count(path.begin(), path.end(),
'/');
461 archive_entry_set_pathname(entry,
"");
464 if (ocpn::startswith(path,
"./")) {
465 path = path.substr(2);
467 int slashpos = path.find_first_of(
'/', 1);
468 string prefix = path.substr(0, slashpos);
469 path = path.substr(prefix.size() + 1);
470 slashpos = path.find_first_of(
'/');
471 string location = path.substr(0, slashpos);
472 string suffix = path.substr(slashpos + 1);
473 if (installPaths.find(location) == installPaths.end() &&
474 archive_entry_filetype(entry) == AE_IFREG) {
475 wxString msg(_T(
"PluginHandler::Invalid install path on file: "));
476 msg += wxString(path.c_str());
480 string dest = installPaths[location] +
"/" + suffix;
481 archive_entry_set_pathname(entry, dest.c_str());
486static bool linux_entry_set_install_path(
struct archive_entry* entry,
487 pathmap_t installPaths) {
490 string path = archive_entry_pathname(entry);
491 int slashes = count(path.begin(), path.end(),
'/');
493 archive_entry_set_pathname(entry,
"");
497 int slashpos = path.find_first_of(
'/', 1);
498 if (ocpn::startswith(path,
"./"))
499 slashpos = path.find_first_of(
'/', 2);
501 string prefix = path.substr(0, slashpos);
502 path = path.substr(prefix.size() + 1);
503 if (ocpn::startswith(path,
"usr/")) {
504 path = path.substr(strlen(
"usr/"));
506 if (ocpn::startswith(path,
"local/")) {
507 path = path.substr(strlen(
"local/"));
509 slashpos = path.find_first_of(
'/');
510 string location = path.substr(0, slashpos);
511 string suffix = path.substr(slashpos + 1);
512 if (installPaths.find(location) == installPaths.end() &&
513 archive_entry_filetype(entry) == AE_IFREG) {
514 wxString msg(_T(
"PluginHandler::Invalid install path on file: "));
515 msg += wxString(path.c_str());
520 string dest = installPaths[location] +
"/" + suffix;
524 if (ocpn::startswith(location,
"share") &&
525 ocpn::startswith(suffix,
"opencpn/plugins/")) {
526 slashpos = suffix.find_first_of(
"opencpn/plugins/");
527 suffix = suffix.substr(16);
530 g_BasePlatform->GetPrivateDataDir().ToStdString() +
"/plugins/" + suffix;
532 if (ocpn::startswith(location,
"lib") &&
533 ocpn::startswith(suffix,
"opencpn/")) {
534 suffix = suffix.substr(8);
536 dest = g_BasePlatform->GetPrivateDataDir().ToStdString() +
"/plugins/lib/" +
541 archive_entry_set_pathname(entry, dest.c_str());
545static bool apple_entry_set_install_path(
struct archive_entry* entry,
546 pathmap_t installPaths) {
550 "/Library/Application Support/OpenCPN";
552 string path = archive_entry_pathname(entry);
553 if (ocpn::startswith(path,
"./")) path = path.substr(2);
556 size_t slashes = count(path.begin(), path.end(),
'/');
558 archive_entry_set_pathname(entry,
"");
561 auto parts = split(path,
"Contents/Resources");
562 if (parts.size() >= 2) {
563 dest = base +
"/Contents/Resources" + parts[1];
566 parts = split(path,
"Contents/SharedSupport");
567 if (parts.size() >= 2) {
568 dest = base +
"/Contents/SharedSupport" + parts[1];
572 parts = split(path,
"Contents/PlugIns");
573 if (parts.size() >= 2) {
574 dest = base +
"/Contents/PlugIns" + parts[1];
577 if (dest ==
"" && archive_entry_filetype(entry) == AE_IFREG) {
578 wxString msg(_T(
"PluginHandler::Invalid install path on file: "));
579 msg += wxString(path.c_str());
583 archive_entry_set_pathname(entry, dest.c_str());
587static bool android_entry_set_install_path(
struct archive_entry* entry,
588 pathmap_t installPaths) {
591 string path = archive_entry_pathname(entry);
592 int slashes = count(path.begin(), path.end(),
'/');
594 archive_entry_set_pathname(entry,
"");
599 int slashpos = path.find_first_of(
'/', 1);
600 if (ocpn::startswith(path,
"./"))
601 slashpos = path.find_first_of(
'/', 2);
603 string prefix = path.substr(0, slashpos);
604 path = path.substr(prefix.size() + 1);
605 if (ocpn::startswith(path,
"usr/")) {
606 path = path.substr(strlen(
"usr/"));
608 if (ocpn::startswith(path,
"local/")) {
609 path = path.substr(strlen(
"local/"));
611 slashpos = path.find_first_of(
'/');
612 string location = path.substr(0, slashpos);
613 string suffix = path.substr(slashpos + 1);
614 if (installPaths.find(location) == installPaths.end() &&
615 archive_entry_filetype(entry) == AE_IFREG) {
616 wxString msg(_T(
"PluginHandler::Invalid install path on file: "));
617 msg += wxString(path.c_str());
622 if ((location ==
"lib") && ocpn::startswith(suffix,
"opencpn")) {
623 auto parts = split(suffix,
"/");
624 if (parts.size() == 2) suffix = parts[1];
627 if ((location ==
"share") && ocpn::startswith(suffix,
"opencpn")) {
628 auto parts = split(suffix,
"opencpn/");
629 if (parts.size() == 2) suffix = parts[1];
633 string dest = installPaths[location] +
"/" + suffix;
635 archive_entry_set_pathname(entry, dest.c_str());
639static bool entry_set_install_path(
struct archive_entry* entry,
640 pathmap_t installPaths) {
641 const std::string src = archive_entry_pathname(entry);
643#ifdef __OCPN__ANDROID__
644 rv = android_entry_set_install_path(entry, installPaths);
646 const auto osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
647 if (g_BasePlatform->isFlatpacked()) {
648 rv = flatpak_entry_set_install_path(entry, installPaths);
649 }
else if (osSystemId & wxOS_UNIX_LINUX) {
650 rv = linux_entry_set_install_path(entry, installPaths);
651 }
else if (osSystemId & wxOS_WINDOWS) {
652 rv = win_entry_set_install_path(entry, installPaths);
653 }
else if (osSystemId & wxOS_MAC) {
654 rv = apple_entry_set_install_path(entry, installPaths);
656 wxLogMessage(
"set_install_path() invoked, unsupported platform %s",
657 wxPlatformInfo::Get().GetOperatingSystemDescription());
661 const std::string dest = archive_entry_pathname(entry);
664 MESSAGE_LOG <<
"Installing " << src <<
" into " << dest << std::endl;
670bool PluginHandler::archive_check(
int r,
const char* msg,
struct archive* a) {
671 if (r < ARCHIVE_OK) {
674 if (archive_error_string(a)) s = s +
": " + archive_error_string(a);
675 wxLogMessage(s.c_str());
678 return r >= ARCHIVE_WARN;
681bool PluginHandler::explodeTarball(
struct archive* src,
struct archive* dest,
682 std::string& filelist,
683 const std::string& metadata_path) {
684 struct archive_entry* entry = 0;
685 pathmap_t pathmap = getInstallPaths();
687 int r = archive_read_next_header(src, &entry);
688 if (r == ARCHIVE_EOF) {
691 if (!archive_check(r,
"archive read header error", src)) {
694 std::string path = archive_entry_pathname(entry);
695 bool is_metadata = std::string::npos != path.find(
"metadata.xml");
697 if (metadata_path ==
"")
continue;
698 archive_entry_set_pathname(entry, metadata_path.c_str());
700 else if (!entry_set_install_path(entry, pathmap))
continue;
701 if (strlen(archive_entry_pathname(entry)) == 0) {
705 filelist.append(std::string(archive_entry_pathname(entry)) +
"\n");
707 r = archive_write_header(dest, entry);
708 archive_check(r,
"archive write install header error", dest);
709 if (r >= ARCHIVE_OK && archive_entry_size(entry) > 0) {
710 r = copy_data(src, dest);
711 if (!archive_check(r,
"archive copy data error", dest)) {
715 r = archive_write_finish_entry(dest);
716 if (!archive_check(r,
"archive finish write error", dest)) {
750bool PluginHandler::extractTarball(
const std::string path,
751 std::string& filelist,
752 const std::string metadata_path) {
753 struct archive* src = archive_read_new();
754 archive_read_support_filter_gzip(src);
755 archive_read_support_format_tar(src);
756 int r = archive_read_open_filename(src, path.c_str(), 10240);
757 if (r != ARCHIVE_OK) {
758 std::ostringstream os;
759 os <<
"Cannot read installation tarball: " << path;
760 wxLogWarning(os.str().c_str());
761 last_error_msg = os.str();
764 struct archive* dest = archive_write_disk_new();
765 archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
766 bool ok = explodeTarball(src, dest, filelist, metadata_path);
767 archive_read_free(src);
768 archive_write_free(dest);
784 auto loader = PluginLoader::getInstance();
785 return PlugInIxByName(name, loader->GetPlugInArray()) == -1;
788static std::string computeMetadataPath(
void) {
789 std::string path = g_BasePlatform->GetPrivateDataDir().ToStdString();
791 path +=
"ocpn-plugins.xml";
792 if (ocpn::exists(path)) {
806 path = g_BasePlatform->GetSharedDataDir();
808 path +=
"ocpn-plugins.xml";
809 if (!ocpn::exists(path)) {
810 wxLogWarning(
"Non-existing plugins file: %s", path);
816 if (metadataPath.size() > 0) {
819 metadataPath = computeMetadataPath();
820 wxLogDebug(
"Using metadata path: %s", metadataPath.c_str());
824static void parseMetadata(
const std::string path,
CatalogCtx& ctx) {
827 wxLogMessage(
"PluginHandler: using metadata path: %s", path);
829 if (!ocpn::exists(path)) {
830 wxLogWarning(
"Non-existing plugins metadata file: %s", path.c_str());
833 ifstream ifpath(path);
834 std::string xml((istreambuf_iterator<char>(ifpath)),
835 istreambuf_iterator<char>());
836 ParseCatalog(xml, &ctx);
842 plugins.insert(plugins.end(), a.begin(), a.end());
843 std::map<std::string, int> count_by_target;
844 for (
const auto& p : plugins) {
845 if (p.target ==
"") {
848 auto key = p.target +
":" + p.target_version;
849 if (count_by_target.find(key) == count_by_target.end()) {
850 count_by_target[key] = 1;
852 count_by_target[key] += 1;
855 return count_by_target;
858void PluginHandler::cleanupFiles(
const std::string& manifestFile,
859 const std::string& plugname) {
860 std::ifstream diskfiles(manifestFile);
861 if (diskfiles.is_open()) {
862 std::stringstream buffer;
863 buffer << diskfiles.rdbuf();
869 const std::string& plugname) {
870 wxLogMessage(
"Cleaning up failed install of %s", plugname.c_str());
872 std::istringstream files(filelist);
873 while (!files.eof()) {
875 files.getline(line,
sizeof(line));
876 if (isRegularFile(line)) {
877 int r = remove(line);
879 wxLogWarning(
"Cannot remove file %s: %s", line, strerror(r));
887 while (!done && (iloop < 6)) {
889 std::istringstream dirs(filelist);
890 while (!dirs.eof()) {
892 dirs.getline(line,
sizeof(line));
894 wxFileName wxFile(line);
895 if (wxFile.IsDir() && wxFile.DirExists()) {
896 wxDir dir(wxFile.GetFullPath());
897 if (dir.IsOpened()) {
898 if (!dir.HasFiles() && !dir.HasSubDirs()) {
899 wxFile.Rmdir(wxPATH_RMDIR_RECURSIVE);
909 if (ocpn::exists(path)) {
910 remove(path.c_str());
912 remove(dirListPath(plugname).c_str());
920 auto catalogHandler = CatalogHandler::getInstance();
926 if (!ocpn::exists(path)) {
930 file.open(path, std::ios::in);
931 if (file.is_open()) {
932 std::string xml((std::istreambuf_iterator<char>(file)),
933 std::istreambuf_iterator<char>());
935 auto status = catalogHandler->DoParseCatalog(xml, &ctx);
936 if (status == CatalogHandler::ServerStatus::OK) {
937 catalogData.undef =
false;
938 catalogData.version = ctx.version;
939 catalogData.date = ctx.date;
948 vector<PluginMetadata> plugins;
950 auto loader = PluginLoader::getInstance();
951 ArrayOfPlugIns* mgr_plugins = loader->GetPlugInArray();
952 for (
unsigned int i = 0; i < mgr_plugins->GetCount(); i += 1) {
955 auto name = string(p->m_common_name);
958 std::stringstream ss;
959 ss << p->m_version_major <<
"." << p->m_version_minor;
960 plugin.version = ss.str();
963 if (path !=
"" && wxFileName::IsFileReadable(path)) {
964 std::ifstream stream;
965 stream.open(path, ifstream::in);
966 stream >> plugin.version;
968 plugins.push_back(plugin);
974 std::string filelist;
975 if (!extractTarball(path, filelist)) {
976 std::ostringstream os;
977 os <<
"Cannot unpack plugin: " << plugin.name <<
" at " << path;
978 last_error_msg = os.str();
983 saveFilelist(filelist, plugin.name);
984 saveDirlist(plugin.name);
985 saveVersion(plugin.name, plugin.version);
994 if (tmpnam(fname) == NULL) {
995 wxLogWarning(
"Cannot create temporary file");
999 path = std::string(fname);
1000 std::ofstream stream;
1001 stream.open(path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
1002 DEBUG_LOG <<
"Downloading: " << plugin.name << std::endl;
1003 auto downloader =
Downloader(plugin.tarball_url);
1004 downloader.download(&stream);
1010 std::string filelist;
1011 std::string temp_path(tmpnam(0));
1012 if (!extractTarball(path, filelist, temp_path)) {
1013 std::ostringstream os;
1014 os <<
"Cannot unpack plugin tarball at : " << path;
1015 if (filelist !=
"")
cleanup(filelist,
"unknown_name");
1016 last_error_msg = os.str();
1020 std::ifstream istream(temp_path);
1021 std::stringstream buff;
1022 buff << istream.rdbuf();
1023 remove(temp_path.c_str());
1025 auto xml = std::string(
"<plugins>") + buff.str() +
"</plugins>";
1027 ParseCatalog(xml, &ctx);
1028 auto name = ctx.plugins[0].name;
1029 auto version = ctx.plugins[0].version;
1031 saveFilelist(filelist, name);
1033 saveVersion(name, version);
1039 using namespace std;
1041 auto loader = PluginLoader::getInstance();
1042 auto ix = PlugInIxByName(plugin_name, loader->GetPlugInArray());
1043 auto pic = loader->GetPlugInArray()->Item(ix);
1045 loader->UnLoadPlugIn(ix);
1047 if (!ocpn::exists(path)) {
1048 wxLogWarning(
"Cannot find installation data for %s (%s)",
1049 plugin_name.c_str(), path);
1052 ifstream files(path);
1053 while (!files.eof()) {
1055 files.getline(line,
sizeof(line));
1056 if (isRegularFile(line)) {
1057 int r = remove(line);
1059 wxLogWarning(
"Cannot remove file %s: %s", line, strerror(r));
1068 while (!done && (iloop < 6)) {
1070 ifstream dirs(path);
1071 while (!dirs.eof()) {
1073 dirs.getline(line,
sizeof(line));
1076 wxFileName wxFile(line);
1077 if (wxFile.IsDir() && wxFile.DirExists()) {
1078 wxDir dir(wxFile.GetFullPath());
1079 if (dir.IsOpened()) {
1080 if (!dir.HasFiles() && !dir.HasSubDirs()) {
1081 wxFile.Rmdir(wxPATH_RMDIR_RECURSIVE);
1092 int r = remove(path.c_str());
1094 wxLogWarning(
"Cannot remove file %s: %s", path.c_str(), strerror(r));
1096 remove(dirListPath(plugin_name).c_str());
1104 wxURI uri(wxString(plugin.tarball_url.c_str()));
1105 wxFileName fn(uri.GetPath());
1106 wxString tarballFile = fn.GetFullName();
1113 if (cacheFile ==
"") {
1115 wxFileName fn1(fn.GetFullName());
1116 if (fn1.GetExt().IsSameAs(
"tar")) {
1117 tarballFile = fn.GetFullName();
1123 if (cacheFile !=
"") {
1124 wxLogMessage(
"Installing %s from local cache", tarballFile.c_str());
1127 wxLogWarning(
"Cannot install tarball file %s", cacheFile.c_str());
1128 evt_download_failed.
Notify(cacheFile);
1131 evt_download_ok.
Notify(plugin.name +
" " + plugin.version);
Handle downloading of files from remote urls.
const void Notify()
Notify all listeners, no data supplied.
Host ABI encapsulation and plugin compatibility checks.
const std::vector< PluginMetadata > getInstalled()
Return list of all installed plugins.
static void cleanup(const std::string &filelist, const std::string &plugname)
Cleanup failed installation attempt using filelist for plugin.
bool installPluginFromCache(PluginMetadata plugin)
Install plugin tarball from local cache.
const std::map< std::string, int > getCountByTarget()
Map of available plugin targets -> number of occurences.
bool isPluginWritable(std::string name)
Check if given plugin can be installed/updated.
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.
bool uninstall(const std::string plugin)
Uninstall an installed plugin.
bool installPlugin(PluginMetadata plugin)
Download and install a new, not installed plugin.
std::string getMetadataPath()
Return path to metadata XML file.
const std::vector< PluginMetadata > getAvailable()
Return list of available, not installed plugins.
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.
std::string UserLibdir()
The single, user-writable directory for installing .dll files.
std::string Homedir() const
home directory, convenience stuff.
std::string UserDatadir()
The single, user-writable common parent for plugin data directories, typically ending in 'plugins'.
std::string UserBindir()
The single, user-writable directory for installing helper binaries.
static PluginPaths * getInstance()
Return the singleton instance.
Plugin ABI encapsulation.
std::string lookup_tarball(const char *uri)
Get path to tarball in cache for given filename.
std::string lookup_metadata(const char *name)
Get metadata path for a given name defaulting to ocpn-plugins.xml)
The result from parsing the xml catalog i.