|
|
@@ -8,6 +8,7 @@ |
|
|
|
#include <sys/param.h> // for MAXPATHLEN |
|
|
|
#include <fcntl.h> |
|
|
|
#include <thread> |
|
|
|
#include <stdexcept> |
|
|
|
|
|
|
|
#include <zip.h> |
|
|
|
#include <jansson.h> |
|
|
@@ -190,14 +191,31 @@ static int extractZip(const char *filename, const char *dir) { |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
static void syncPlugin(json_t *pluginJ) { |
|
|
|
static bool syncPlugin(json_t *pluginJ, bool dryRun) { |
|
|
|
json_t *slugJ = json_object_get(pluginJ, "slug"); |
|
|
|
if (!slugJ) return; |
|
|
|
if (!slugJ) |
|
|
|
return false; |
|
|
|
std::string slug = json_string_value(slugJ); |
|
|
|
info("Syncing plugin %s", slug.c_str()); |
|
|
|
|
|
|
|
// Get community version |
|
|
|
std::string version; |
|
|
|
json_t *versionJ = json_object_get(pluginJ, "version"); |
|
|
|
if (versionJ) { |
|
|
|
version = json_string_value(versionJ); |
|
|
|
} |
|
|
|
|
|
|
|
// Check whether we already have a plugin with the same slug and version |
|
|
|
for (Plugin *plugin : gPlugins) { |
|
|
|
if (plugin->slug == slug) { |
|
|
|
// plugin->version might be blank, so adding a version of the manifest will update the plugin |
|
|
|
if (plugin->version == version) |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
json_t *nameJ = json_object_get(pluginJ, "name"); |
|
|
|
if (!nameJ) return; |
|
|
|
if (!nameJ) |
|
|
|
return false; |
|
|
|
std::string name = json_string_value(nameJ); |
|
|
|
|
|
|
|
std::string download; |
|
|
@@ -237,7 +255,7 @@ static void syncPlugin(json_t *pluginJ) { |
|
|
|
|
|
|
|
if (download.empty()) { |
|
|
|
warn("Could not get download URL for plugin %s", slug.c_str()); |
|
|
|
return; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// If plugin is not loaded, download the zip file to /plugins |
|
|
@@ -249,66 +267,30 @@ static void syncPlugin(json_t *pluginJ) { |
|
|
|
std::string pluginPath = pluginsDir + "/" + slug; |
|
|
|
std::string zipPath = pluginPath + ".zip"; |
|
|
|
bool success = requestDownload(download, zipPath, &downloadProgress); |
|
|
|
if (success) { |
|
|
|
if (!sha256.empty()) { |
|
|
|
// Check SHA256 hash |
|
|
|
std::string actualSha256 = requestSHA256File(zipPath); |
|
|
|
if (actualSha256 != sha256) { |
|
|
|
warn("Plugin %s does not match expected SHA256 checksum", slug.c_str()); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Unzip file |
|
|
|
int err = extractZip(zipPath.c_str(), pluginsDir.c_str()); |
|
|
|
if (!err) { |
|
|
|
// Delete zip |
|
|
|
remove(zipPath.c_str()); |
|
|
|
// Load plugin |
|
|
|
// loadPlugin(pluginPath); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
downloadName = ""; |
|
|
|
} |
|
|
|
|
|
|
|
static bool trySyncPlugin(json_t *pluginJ, json_t *communityPluginsJ, bool dryRun) { |
|
|
|
std::string slug = json_string_value(pluginJ); |
|
|
|
|
|
|
|
// Find community plugin |
|
|
|
size_t communityIndex; |
|
|
|
json_t *communityPluginJ = NULL; |
|
|
|
json_array_foreach(communityPluginsJ, communityIndex, communityPluginJ) { |
|
|
|
json_t *communitySlugJ = json_object_get(communityPluginJ, "slug"); |
|
|
|
if (communitySlugJ) { |
|
|
|
std::string communitySlug = json_string_value(communitySlugJ); |
|
|
|
if (slug == communitySlug) |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (communityIndex == json_array_size(communityPluginsJ)) { |
|
|
|
warn("Plugin sync error: %s not found in community", slug.c_str()); |
|
|
|
if (!success) { |
|
|
|
warn("Plugin %s download was unsuccessful"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// Get community version |
|
|
|
std::string version; |
|
|
|
json_t *versionJ = json_object_get(communityPluginJ, "version"); |
|
|
|
if (versionJ) { |
|
|
|
version = json_string_value(versionJ); |
|
|
|
if (!sha256.empty()) { |
|
|
|
// Check SHA256 hash |
|
|
|
std::string actualSha256 = requestSHA256File(zipPath); |
|
|
|
if (actualSha256 != sha256) { |
|
|
|
warn("Plugin %s does not match expected SHA256 checksum", slug.c_str()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Check whether we already have a plugin with the same slug and version |
|
|
|
for (Plugin *plugin : gPlugins) { |
|
|
|
if (plugin->slug == slug) { |
|
|
|
// plugin->version might be blank, so adding a version of the manifest will update the plugin |
|
|
|
if (plugin->version == version) |
|
|
|
return false; |
|
|
|
} |
|
|
|
// Unzip file |
|
|
|
int err = extractZip(zipPath.c_str(), pluginsDir.c_str()); |
|
|
|
if (!err) { |
|
|
|
// Delete zip |
|
|
|
remove(zipPath.c_str()); |
|
|
|
// Load plugin |
|
|
|
// loadPlugin(pluginPath); |
|
|
|
} |
|
|
|
|
|
|
|
if (!dryRun) |
|
|
|
syncPlugin(communityPluginJ); |
|
|
|
downloadName = ""; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
@@ -318,50 +300,77 @@ bool pluginSync(bool dryRun) { |
|
|
|
|
|
|
|
bool available = false; |
|
|
|
|
|
|
|
// Download my plugins |
|
|
|
json_t *reqJ = json_object(); |
|
|
|
json_object_set(reqJ, "version", json_string(gApplicationVersion.c_str())); |
|
|
|
json_object_set(reqJ, "token", json_string(gToken.c_str())); |
|
|
|
json_t *resJ = requestJson(METHOD_GET, gApiHost + "/plugins", reqJ); |
|
|
|
json_decref(reqJ); |
|
|
|
|
|
|
|
// Download community plugins |
|
|
|
json_t *communityResJ = requestJson(METHOD_GET, gApiHost + "/community/plugins", NULL); |
|
|
|
|
|
|
|
if (!dryRun) { |
|
|
|
isDownloading = true; |
|
|
|
downloadProgress = 0.0; |
|
|
|
downloadName = ""; |
|
|
|
downloadName = "Updating plugins..."; |
|
|
|
} |
|
|
|
|
|
|
|
if (resJ && communityResJ) { |
|
|
|
json_t *resJ = NULL; |
|
|
|
json_t *communityResJ = NULL; |
|
|
|
|
|
|
|
try { |
|
|
|
// Download plugin slugs |
|
|
|
json_t *reqJ = json_object(); |
|
|
|
json_object_set(reqJ, "version", json_string(gApplicationVersion.c_str())); |
|
|
|
json_object_set(reqJ, "token", json_string(gToken.c_str())); |
|
|
|
resJ = requestJson(METHOD_GET, gApiHost + "/plugins", reqJ); |
|
|
|
json_decref(reqJ); |
|
|
|
if (!resJ) |
|
|
|
throw std::runtime_error("No response from server"); |
|
|
|
|
|
|
|
json_t *errorJ = json_object_get(resJ, "error"); |
|
|
|
json_t *communityErrorJ = json_object_get(resJ, "error"); |
|
|
|
if (errorJ) { |
|
|
|
warn("Plugin sync error: %s", json_string_value(errorJ)); |
|
|
|
} |
|
|
|
else if (communityErrorJ) { |
|
|
|
warn("Plugin sync error: %s", json_string_value(communityErrorJ)); |
|
|
|
} |
|
|
|
else { |
|
|
|
// Check each plugin in list of my plugins |
|
|
|
json_t *pluginsJ = json_object_get(resJ, "plugins"); |
|
|
|
json_t *communityPluginsJ = json_object_get(communityResJ, "plugins"); |
|
|
|
size_t index; |
|
|
|
json_t *pluginJ; |
|
|
|
json_array_foreach(pluginsJ, index, pluginJ) { |
|
|
|
if (trySyncPlugin(pluginJ, communityPluginsJ, dryRun)) |
|
|
|
available = true; |
|
|
|
if (errorJ) |
|
|
|
throw std::runtime_error(json_string_value(errorJ)); |
|
|
|
|
|
|
|
// Download community plugins |
|
|
|
communityResJ = requestJson(METHOD_GET, gApiHost + "/community/plugins", NULL); |
|
|
|
if (!communityResJ) |
|
|
|
throw std::runtime_error("No response from server"); |
|
|
|
|
|
|
|
json_t *communityErrorJ = json_object_get(communityResJ, "error"); |
|
|
|
if (communityErrorJ) |
|
|
|
throw std::runtime_error(json_string_value(communityErrorJ)); |
|
|
|
|
|
|
|
// Check each plugin in list of plugin slugs |
|
|
|
json_t *pluginSlugsJ = json_object_get(resJ, "plugins"); |
|
|
|
json_t *communityPluginsJ = json_object_get(communityResJ, "plugins"); |
|
|
|
|
|
|
|
size_t index; |
|
|
|
json_t *pluginSlugJ; |
|
|
|
json_array_foreach(pluginSlugsJ, index, pluginSlugJ) { |
|
|
|
std::string slug = json_string_value(pluginSlugJ); |
|
|
|
// Search for plugin slug in community |
|
|
|
size_t communityIndex; |
|
|
|
json_t *communityPluginJ = NULL; |
|
|
|
json_array_foreach(communityPluginsJ, communityIndex, communityPluginJ) { |
|
|
|
json_t *communitySlugJ = json_object_get(communityPluginJ, "slug"); |
|
|
|
if (!communitySlugJ) |
|
|
|
continue; |
|
|
|
std::string communitySlug = json_string_value(communitySlugJ); |
|
|
|
if (slug == communitySlug) |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
// Sync plugin |
|
|
|
if (syncPlugin(communityPluginJ, dryRun)) { |
|
|
|
available = true; |
|
|
|
} |
|
|
|
else { |
|
|
|
warn("Plugin %s not found in community", slug.c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (resJ) |
|
|
|
json_decref(resJ); |
|
|
|
catch (std::runtime_error &e) { |
|
|
|
warn("Plugin sync error: %s", e.what()); |
|
|
|
} |
|
|
|
|
|
|
|
if (communityResJ) |
|
|
|
json_decref(communityResJ); |
|
|
|
|
|
|
|
if (resJ) |
|
|
|
json_decref(resJ); |
|
|
|
|
|
|
|
if (!dryRun) { |
|
|
|
isDownloading = false; |
|
|
|
} |
|
|
|