31 #include <wx/filename.h>
32 #include <wx/jsonreader.h>
35 #include "model/base_platform.h"
36 #include "model/catalog_handler.h"
37 #include "model/catalog_parser.h"
38 #include "model/config_vars.h"
39 #include "model/downloader.h"
40 #include "model/ocpn_utils.h"
41 #include "model/plugin_handler.h"
42 #include "observable_evtvar.h"
43 #include "observable_globvar.h"
46 static const std::string SEP(
"\\");
48 static const std::string SEP(
"/");
51 static const char*
const DOWNLOAD_REPO =
52 "https://raw.githubusercontent.com/OpenCPN/plugins";
54 static const char*
const DOWNLOAD_PATH =
"/@branch@/ocpn-plugins.xml";
56 static const char*
const API_ENDPOINT =
"https://api.github.com/repos";
58 static const char*
const API_PATH =
"/OpenCPN/plugins/branches";
61 status(ServerStatus::UNKNOWN),
62 m_catalog_status(ServerStatus::UNKNOWN){
63 if (g_catalog_channel ==
"") {
64 g_catalog_channel = DEFAULT_CHANNEL;
77 std::string url = std::string(DOWNLOAD_REPO) + DOWNLOAD_PATH;
78 ocpn::replace(url,
"@branch@", g_catalog_channel.ToStdString());
83 return m_catalog_status;
88 if (m_catalog_status == ServerStatus::OK){
94 if (!ocpn::exists(path)) {
95 m_catalog_status = ServerStatus::FILE_ERROR;
98 file.open(path, std::ios::in);
100 std::string xml((std::istreambuf_iterator<char>(file)),
101 std::istreambuf_iterator<char>());
103 auto status = DoParseCatalog(xml, &m_catalogctx);
104 m_catalog_status = status;
105 return &m_catalogctx;
108 return &m_catalogctx;
112 if (m_catalog_status == ServerStatus::OK) {
113 m_catalogctx.plugins.push_back(metadata);
121 std::string path(g_catalog_custom_url.ToStdString());
123 path = std::string(DOWNLOAD_REPO) + DOWNLOAD_PATH;
124 ocpn::replace(path,
"@branch@", g_catalog_channel.ToStdString());
125 wxLogMessage(
"Effective catalog path: %s", path.c_str());
128 bool ok = downloader.
download(stream);
130 return ServerStatus::OK;
133 return ServerStatus::CURL_ERROR;
139 bool ok = downloader.
download(stream);
141 return ServerStatus::OK;
144 return ServerStatus::CURL_ERROR;
148 if (filePath ==
"") {
149 filePath = wxFileName::CreateTempFileName(
"ocpn_dl").ToStdString();
151 std::ofstream stream;
152 stream.open(filePath.c_str(), std::ios::out | std::ios::trunc);
153 if (!stream.is_open()) {
154 wxLogMessage(
"CatalogHandler: Cannot open %s for write", filePath);
155 error_msg = strerror(errno);
156 return ServerStatus::OS_ERROR;
165 if (filePath ==
"") {
166 filePath = wxFileName::CreateTempFileName(
"ocpn_dl").ToStdString();
168 std::ofstream stream;
169 stream.open(filePath.c_str(), std::ios::out | std::ios::trunc);
170 if (!stream.is_open()) {
171 wxLogMessage(
"CatalogHandler: Cannot open %s for write", filePath);
172 error_msg = strerror(errno);
173 return ServerStatus::OS_ERROR;
180 catalog_status CatalogHandler::DoParseCatalog(
const std::string xml,
185 for (
auto path : PluginHandler::getInstance()->GetImportPaths()) {
186 std::ifstream plugin_xml(path);
187 std::stringstream ss;
188 ss << plugin_xml.rdbuf();
190 if (ss.str().size() == 0) {
193 ::ParsePlugin(ss.str().c_str(), metadata);
194 metadata.is_imported =
true;
195 ctx->plugins.push_back(metadata);
197 while (ctx->meta_urls.size() > 0) {
198 std::ostringstream xml;
199 url = ctx->meta_urls.back();
200 ctx->meta_urls.pop_back();
203 auto match = [url](
const std::string& s) {
return url == s; };
204 const auto& haystack = ctx->parsed_metas;
205 auto found = std::find_if(haystack.begin(), haystack.end(), match);
206 if (found != haystack.end()) {
209 ctx->parsed_metas.push_back(url);
211 wxLogMessage(
"CatalogHandler: Cannot download meta-url: %s",
214 ok = DoParseCatalog(xml.str(), ctx) == ServerStatus::OK;
219 wxLogWarning(
"Cannot parse xml starting with: %s",
220 xml.substr(0, 60).c_str());
222 return ok ? ServerStatus::OK : ServerStatus::XML_ERROR;
228 auto status = DoParseCatalog(xml, &ctx);
229 if (status == ServerStatus::OK && latest) {
230 this->latest_data.version = ctx.version;
231 this->latest_data.date = ctx.date;
232 this->latest_data.undef =
false;
240 for (
auto c : channels) {
243 catalog_channel.Set(channel);
247 wxLogMessage(
"Attempt to set illegal active channel: %s", channel);
252 return g_catalog_channel.ToStdString();
256 g_catalog_custom_url = url;
260 if (latest_data.undef) {
261 std::ostringstream os;
269 void CatalogHandler::LoadCatalogData(
const std::string& path,
271 if (!ocpn::exists(path)) {
278 file.open(path, std::ios::in);
279 if (file.is_open()) {
280 std::string xml((std::istreambuf_iterator<char>(file)),
281 std::istreambuf_iterator<char>());
284 auto status = DoParseCatalog(xml, &ctx);
285 if (status == ServerStatus::OK) {
286 data.version = ctx.version;
287 data.date = ctx.date;
294 if (user_data.undef) {
295 auto plugin_handler = PluginHandler::getInstance();
298 path +=
"ocpn-plugins.xml";
299 LoadCatalogData(path, user_data);
305 if (default_data.undef) {
306 auto plugin_handler = PluginHandler::getInstance();
307 std::string path = g_BasePlatform->GetSharedDataDir().ToStdString();
309 path +=
"ocpn-plugins.xml";
310 LoadCatalogData(path, default_data);
316 default_data.undef =
true;
317 user_data.undef =
true;
318 latest_data.undef =
true;
319 m_catalog_status = ServerStatus::UNKNOWN;
321 m_catalogctx.plugins.clear();
322 m_catalogctx.meta_urls.clear();
323 m_catalogctx.parsed_metas.clear();
324 m_catalogctx.version.clear();
325 m_catalogctx.date.clear();
331 return g_catalog_custom_url.ToStdString();
337 Downloader downloader(std::string(API_ENDPOINT) + API_PATH);
338 bool ok = downloader.
download(stream);
340 return ServerStatus::OK;
343 return ServerStatus::CURL_ERROR;
349 parser.Parse(json.c_str(), &node);
350 if (!node.IsArray()) {
351 wxLogMessage(
"Cannot parse json (toplevel)");
352 error_msg = parser.GetErrors().Item(0).ToStdString();
353 return ServerStatus::JSON_ERROR;
355 auto branches = node.AsArray();
356 wxLogMessage(
"Got %d branches", branches->Count());
358 for (
size_t i = 0; i < branches->Count(); i += 1) {
359 auto branch = branches->Item(i);
360 channels.push_back(branch[
"name"].AsString().ToStdString());
362 if (branches->Count() > 0) {
363 wxLogMessage(
"First branch: %s", channels[0].c_str());
365 return ServerStatus::OK;
Plugin catalog management: Check for available versions and branches, download as required.
std::vector< std::string > GetChannels()
Get the downloaded list of channels, empty on errors.
ServerStatus LoadChannels(std::ostream *json)
Download channel json data, possibly return error code.
CatalogHandler()
Initiate the handler.
CatalogData DefaultCatalogData()
Data for default version, installed with main opencpn.
std::string GetDefaultUrl()
Get the default URL, with actual channel included.
CatalogData LatestCatalogData()
Data for latest parsed data marked as latest.
CatalogCtx * GetActiveCatalogContext()
Return a pointer to the currently active plugin catalog context.
std::string LastErrorMsg()
Last error message, free format.
CatalogData UserCatalogData()
Data for user catalog which overrides the default one.
bool AddMetadataToActiveContext(PluginMetadata metadata)
Add an abritrary stub metadata netry to the active catalog context.
ServerStatus ParseCatalog(const std::string xml, bool latest=false)
Parse XML contents, save as latest data if latest is true.
std::string GetActiveChannel()
Get the branch (a.
void SetCustomUrl(const char *url)
Set a custom url, overrides also channel settings.
ServerStatus DownloadCatalog(std::ostream *stream)
Download the latest catalog to given stream.
void ClearCatalogData()
Invalidate *CatalogData caches.
std::string GetCustomUrl()
Set a custom url, overrides also channel settings.
ServerStatus GetCatalogStatus()
Retrieve status of currently active plugin catalog
bool SetActiveChannel(const char *channel)
Set the active channel used when downloading catalog.
Handle downloading of files from remote urls.
bool download(std::ostream *stream)
Download url into stream, return false on errors.
std::string last_error()
Last Curl error message.
Wrapper for global variable, supports notification events when value changes.
std::string getMetadataPath()
Return path to metadata XML file.
The result from parsing the xml catalog i.
Datatypes and methods to parse ocpn-plugins.xml XML data, either complete catalog or a single plugin.