Skip to content

Commit

Permalink
feat: allow updating AppImages from www.appimagehub.com and www.pling…
Browse files Browse the repository at this point in the history
  • Loading branch information
azubieta committed Oct 26, 2021
1 parent 9f9073c commit 7554bb9
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ add_definitions("-DBUILD_NUMBER=\"${BUILD_NUMBER}\"")
add_library(
libappimageupdate
SHARED
${PROJECT_SOURCE_DIR}/include/appimage/update.h updater.cpp
${PROJECT_SOURCE_DIR}/include/appimage/update.h updater.cpp update_methods/pling_v1_zsync.cpp
util.h
)
# since the target is called libsomething, one doesn't need CMake's additional lib prefix
Expand Down
71 changes: 71 additions & 0 deletions src/update_methods/pling_v1_zsync.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// system
#include <fnmatch.h>
#include <regex>

//libraries
#include <cpr/cpr.h>

//local
#include "pling_v1_zsync.h"

const char* appimage::update::methods::PlingV1Zsync::plingContentEndpointUrl = "https://api.pling.com/ocs/v1/content/data/";

appimage::update::methods::PlingV1Zsync::PlingV1Zsync(std::vector<std::string> updateStringParts) :
productId(updateStringParts[1]), fileMatchingPattern(updateStringParts[2]) {

}

std::vector<std::string> appimage::update::methods::PlingV1Zsync::getAvailableDownloads() {
std::vector<std::string> downloads;

cpr::Url productDetailsUrl = plingContentEndpointUrl + productId;
auto response = cpr::Get(productDetailsUrl);
if (response.status_code >= 200 && response.status_code < 300) {
std::regex urlRegex(R"((?:\<downloadlink\d+\>)(.*?)(?:<\/downloadlink\d+\>))");

std::string text = response.text;
std::smatch match;

// match download link
while (std::regex_search(text, match, urlRegex)) {
// extract second matched group which contains the actual url
std::string url = match[1].str();

// apply file matching patter to the file name
auto fileName = url.substr(url.rfind('/') + 1);
if (fnmatch(fileMatchingPattern.data(), fileName.data(), 0) == 0)
downloads.push_back(url);

text = match.suffix();
}
}

return downloads;
}

std::string appimage::update::methods::PlingV1Zsync::findLatestRelease(const std::vector<std::string>& downloads) {
std::string latestReleaseUrl;
std::string latestReleaseFileName;

for (const auto& url: downloads) {
auto file_name = url.substr(url.rfind('/') + 1);

// keep only the latest release
if (file_name > latestReleaseFileName) {
latestReleaseUrl = std::string(url);
latestReleaseFileName = std::string(file_name);
}
}

return latestReleaseUrl;
}

std::string appimage::update::methods::PlingV1Zsync::resolveZsyncUrl(const std::string& downloadUrl) {
// pling.com creates zsync files for every uploaded file, we just need to append .zsync
return downloadUrl + ".zsync";
}

bool appimage::update::methods::PlingV1Zsync::isUpdateStringAccepted(std::vector<std::string> updateStringParts) {
return updateStringParts.size() == 3 && updateStringParts[0] == "pling-v1-zsync";

}
41 changes: 41 additions & 0 deletions src/update_methods/pling_v1_zsync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once
// system
#include <string>
#include <vector>

//libraries

//local



namespace appimage {
namespace update {
namespace methods {
/**
* Pling is a family of services which include an AppImage store among many other things. AppImage files are
* served from the www.appimagehub.com and www.pling.com urls. They are also available through an xml API
* `https://api.pling.com/ocs/v1/`
*
* format: pling-v1-zsync|<content id>|<file name matching pattern>
*/
class PlingV1Zsync {
public:
explicit PlingV1Zsync(std::vector<std::string> updateStringParts);

static bool isUpdateStringAccepted(std::vector<std::string> updateStringParts);

std::vector<std::string> getAvailableDownloads();

std::string findLatestRelease(const std::vector<std::string>& downloads);

std::string resolveZsyncUrl(const std::string& downloadUrl);

private:
static const char* plingContentEndpointUrl;
std::string productId;
std::string fileMatchingPattern;
};
}
}
}
14 changes: 13 additions & 1 deletion src/updater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// local headers
#include "appimage/update.h"
#include "util.h"
#include "update_methods/pling_v1_zsync.h"

// convenience declaration
typedef std::lock_guard<std::mutex> lock_guard;
Expand Down Expand Up @@ -66,6 +67,7 @@ namespace appimage {
ZSYNC_GENERIC = 0,
ZSYNC_GITHUB_RELEASES,
ZSYNC_BINTRAY,
ZSYNC_PLING_V1,
};

struct AppImage {
Expand Down Expand Up @@ -452,6 +454,12 @@ namespace appimage {
<< "information.";
issueStatusMessage(oss.str());
}
} else if (methods::PlingV1Zsync::isUpdateStringAccepted(uiParts)) {
uiType = ZSYNC_PLING_V1;
auto updateMethod = methods::PlingV1Zsync(uiParts);
auto availableDownloads = updateMethod.getAvailableDownloads();
auto latestReleaseUrl = updateMethod.findLatestRelease(availableDownloads);
zsyncUrl = updateMethod.resolveZsyncUrl(latestReleaseUrl);
} else {
// unknown type
}
Expand Down Expand Up @@ -553,6 +561,7 @@ namespace appimage {

if (appImage->updateInformationType == ZSYNC_GITHUB_RELEASES ||
appImage->updateInformationType == ZSYNC_BINTRAY ||
appImage->updateInformationType == ZSYNC_PLING_V1 ||
appImage->updateInformationType == ZSYNC_GENERIC) {
// doesn't matter which type it is exactly, they all work like the same
zSyncClient = new zsync2::ZSyncClient(appImage->zsyncUrl, pathToAppImage, overwrite);
Expand Down Expand Up @@ -623,7 +632,8 @@ namespace appimage {

if (appImage->updateInformationType == ZSYNC_GITHUB_RELEASES ||
appImage->updateInformationType == ZSYNC_BINTRAY ||
appImage->updateInformationType == ZSYNC_GENERIC) {
appImage->updateInformationType == ZSYNC_PLING_V1 ||
appImage->updateInformationType == ZSYNC_GENERIC ) {
zSyncClient = new zsync2::ZSyncClient(appImage->zsyncUrl, pathToAppImage);
return zSyncClient->checkForChanges(updateAvailable, method);
}
Expand Down Expand Up @@ -772,6 +782,8 @@ namespace appimage {
oss << "ZSync via Bintray";
else if (appImage->updateInformationType == d->ZSYNC_GITHUB_RELEASES)
oss << "ZSync via GitHub Releases";
else if (appImage->updateInformationType == d->ZSYNC_PLING_V1)
oss << "ZSync via OCS";
else if (appImage->updateInformationType == d->INVALID)
oss << "Invalid (parsing failed/no update information available)";
else
Expand Down

0 comments on commit 7554bb9

Please sign in to comment.