OpenCPN Partial API docs
Loading...
Searching...
No Matches
catalog_handler.cpp
1/******************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Copyright (C) 2019 Alec Leamas *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************
23 */
24
25#include "config.h"
26
27#include <fstream>
28#include <sstream>
29
30#include <wx/filename.h>
31#include <wx/jsonreader.h>
32#include <wx/log.h>
33
34#include "catalog_handler.h"
35#include "catalog_parser.h"
36#include "downloader.h"
37#include "observable_evtvar.h"
38#include "observable_globvar.h"
39#include "ocpn_utils.h"
40#include "base_platform.h"
41#include "plugin_handler.h"
42
43#ifdef _WIN32
44static const std::string SEP("\\");
45#else
46static const std::string SEP("/");
47#endif
48
49extern wxString g_catalog_custom_url;
50extern wxString g_catalog_channel;
51extern BasePlatform* g_BasePlatform;
52
53static const char* const DOWNLOAD_REPO =
54 "https://raw.githubusercontent.com/OpenCPN/plugins";
55
56static const char* const DOWNLOAD_PATH = "/@branch@/ocpn-plugins.xml";
57
58static const char* const API_ENDPOINT = "https://api.github.com/repos";
59// static const char* const API_PATH = "/leamas/plugins/branches";
60static const char* const API_PATH = "/OpenCPN/plugins/branches";
61
62CatalogHandler::CatalogHandler() : status(ServerStatus::UNKNOWN) {
63 if (g_catalog_channel == "") {
64 g_catalog_channel = DEFAULT_CHANNEL;
65 }
66}
67
68CatalogHandler* CatalogHandler::getInstance() {
69 static CatalogHandler* instance = 0;
70 if (!instance) {
71 instance = new (CatalogHandler);
72 }
73 return instance;
74}
75
77 std::string url = std::string(DOWNLOAD_REPO) + DOWNLOAD_PATH;
78 ocpn::replace(url, "@branch@", g_catalog_channel.ToStdString());
79 return url;
80}
81
82catalog_status CatalogHandler::DownloadCatalog(std::ostream* stream) {
83 std::string path(g_catalog_custom_url.ToStdString());
84 if (path == "") {
85 path = std::string(DOWNLOAD_REPO) + DOWNLOAD_PATH;
86 ocpn::replace(path, "@branch@", g_catalog_channel.ToStdString());
87 wxLogMessage("Effective catalog path: %s", path.c_str());
88 }
89 Downloader downloader(path);
90 bool ok = downloader.download(stream);
91 if (ok) {
92 return ServerStatus::OK;
93 }
94 error_msg = downloader.last_error();
95 return ServerStatus::CURL_ERROR;
96}
97
98catalog_status CatalogHandler::DownloadCatalog(std::ostream* stream,
99 std::string url) {
100 Downloader downloader(url);
101 bool ok = downloader.download(stream);
102 if (ok) {
103 return ServerStatus::OK;
104 }
105 error_msg = downloader.last_error();
106 return ServerStatus::CURL_ERROR;
107}
108
109catalog_status CatalogHandler::DownloadCatalog(std::string& filePath) {
110 if (filePath == "") {
111 filePath = wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
112 }
113 std::ofstream stream;
114 stream.open(filePath.c_str(), std::ios::out | std::ios::trunc);
115 if (!stream.is_open()) {
116 wxLogMessage("CatalogHandler: Cannot open %s for write", filePath);
117 error_msg = strerror(errno);
118 return ServerStatus::OS_ERROR;
119 }
120 auto status = DownloadCatalog(&stream);
121 stream.close();
122 return status;
123}
124
125catalog_status CatalogHandler::DownloadCatalog(std::string& filePath,
126 std::string url) {
127 if (filePath == "") {
128 filePath = wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
129 }
130 std::ofstream stream;
131 stream.open(filePath.c_str(), std::ios::out | std::ios::trunc);
132 if (!stream.is_open()) {
133 wxLogMessage("CatalogHandler: Cannot open %s for write", filePath);
134 error_msg = strerror(errno);
135 return ServerStatus::OS_ERROR;
136 }
137 auto status = DownloadCatalog(&stream, url);
138 stream.close();
139 return status;
140}
141
142catalog_status CatalogHandler::DoParseCatalog(const std::string xml,
143 CatalogCtx* ctx) {
144 std::string url;
145
146 bool ok = ::ParseCatalog(xml, ctx);
147 while (ctx->meta_urls.size() > 0) {
148 std::ostringstream xml;
149 url = ctx->meta_urls.back();
150 ctx->meta_urls.pop_back();
151
152 // already parsed this meta file?
153 bool bdone = false;
154 for (std::vector<std::string>::iterator it = ctx->parsed_metas.begin();
155 it != ctx->parsed_metas.end(); it++) {
156 if (*it == url) {
157 bdone = true;
158 break;
159 }
160 }
161
162 if (!bdone) {
163 ctx->parsed_metas.push_back(url);
164 if (DownloadCatalog(&xml, url) != ServerStatus::OK) {
165 wxLogMessage("CatalogHandler: Cannot download meta-url: %s",
166 url.c_str());
167 } else {
168 ok = DoParseCatalog(xml.str(), ctx) == ServerStatus::OK;
169 if (!ok) break;
170 }
171 }
172 }
173 if (!ok) {
174 wxLogWarning("Cannot parse xml starting with: %s",
175 xml.substr(0, 60).c_str());
176 }
177 return ok ? ServerStatus::OK : ServerStatus::XML_ERROR;
178}
179
180catalog_status CatalogHandler::ParseCatalog(const std::string xml,
181 bool latest) {
182 CatalogCtx ctx;
183 auto status = DoParseCatalog(xml, &ctx);
184 if (status == ServerStatus::OK && latest) {
185 this->latest_data.version = ctx.version;
186 this->latest_data.date = ctx.date;
187 this->latest_data.undef = false;
188 }
189 return status;
190}
191
192std::vector<std::string> CatalogHandler::GetChannels() { return channels; }
193
194bool CatalogHandler::SetActiveChannel(const char* channel) {
195 for (auto c : channels) {
196 if (c == channel) {
197 GlobalVar<wxString> catalog_channel(&g_catalog_channel);
198 catalog_channel.Set(channel);
199 return true;
200 }
201 }
202 wxLogMessage("Attempt to set illegal active channel: %s", channel);
203 return false;
204}
205
207 return g_catalog_channel.ToStdString();
208}
209
210void CatalogHandler::SetCustomUrl(const char* url) {
211 g_catalog_custom_url = url;
212}
213
215 if (latest_data.undef) {
216 std::ostringstream os;
217 if (DownloadCatalog(&os) == ServerStatus::OK) {
218 ParseCatalog(os.str());
219 }
220 }
221 return latest_data;
222}
223
224void CatalogHandler::LoadCatalogData(const std::string& path,
225 CatalogData& data) {
226 if (!ocpn::exists(path)) {
227 data.version = "?";
228 data.date = "?";
229 data.undef = false;
230 return;
231 }
232 std::ifstream file;
233 file.open(path, std::ios::in);
234 if (file.is_open()) {
235 std::string xml((std::istreambuf_iterator<char>(file)),
236 std::istreambuf_iterator<char>());
237 file.close();
238 CatalogCtx ctx;
239 auto status = DoParseCatalog(xml, &ctx);
240 if (status == ServerStatus::OK) {
241 data.version = ctx.version;
242 data.date = ctx.date;
243 data.undef = false;
244 }
245 }
246}
247
249 if (user_data.undef) {
250 auto plugin_handler = PluginHandler::getInstance();
251 std::string path = g_BasePlatform->GetPrivateDataDir().ToStdString();
252 path += SEP;
253 path += "ocpn-plugins.xml";
254 LoadCatalogData(path, user_data);
255 }
256 return user_data;
257}
258
260 if (default_data.undef) {
261 auto plugin_handler = PluginHandler::getInstance();
262 std::string path = g_BasePlatform->GetSharedDataDir().ToStdString();
263 path += SEP;
264 path += "ocpn-plugins.xml";
265 LoadCatalogData(path, default_data);
266 }
267 return default_data;
268}
269
271 default_data.undef = true;
272 user_data.undef = true;
273 latest_data.undef = true;
274}
275
277 return g_catalog_custom_url.ToStdString();
278}
279
280std::string CatalogHandler::LastErrorMsg() { return error_msg; }
281
282catalog_status CatalogHandler::LoadChannels(std::ostream* stream) {
283 Downloader downloader(std::string(API_ENDPOINT) + API_PATH);
284 bool ok = downloader.download(stream);
285 if (ok) {
286 return ServerStatus::OK;
287 }
288 error_msg = downloader.last_error();
289 return ServerStatus::CURL_ERROR;
290}
291
292catalog_status CatalogHandler::LoadChannels(const std::string& json) {
293 wxJSONValue node;
294 wxJSONReader parser;
295 parser.Parse(json.c_str(), &node);
296 if (!node.IsArray()) {
297 wxLogMessage("Cannot parse json (toplevel)");
298 error_msg = parser.GetErrors().Item(0).ToStdString();
299 return ServerStatus::JSON_ERROR;
300 }
301 auto branches = node.AsArray();
302 wxLogMessage("Got %d branches", branches->Count());
303 channels.clear();
304 for (size_t i = 0; i < branches->Count(); i += 1) {
305 auto branch = branches->Item(i);
306 channels.push_back(branch["name"].AsString().ToStdString());
307 }
308 if (branches->Count() > 0) {
309 wxLogMessage("First branch: %s", channels[0].c_str());
310 }
311 return ServerStatus::OK;
312}
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.
std::string LastErrorMsg()
Last error message, free format.
CatalogData UserCatalogData()
Data for user catalog which overrides the default one.
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.
bool SetActiveChannel(const char *channel)
Set the active channel used when downloading catalog.
Handle downloading of files from remote urls.
Definition: downloader.h:34
bool download(std::ostream *stream)
Download url into stream, return false on errors.
Definition: downloader.cpp:55
std::string last_error()
Last Curl error message.
Definition: downloader.cpp:49
Wrapper for global variable, supports notification events when value changes.
void Set(const T &arg)
Set variable value and notify all listeners.
The result from parsing the xml catalog i.
Datatypes and a single method to parse ocpn-plugins.xml XML data.