diff --git a/contrac.pro b/contrac.pro index 51a0a30..fc00ecc 100644 --- a/contrac.pro +++ b/contrac.pro @@ -49,15 +49,12 @@ HEADERS += \ src/download.h \ src/downloadconfig.h \ src/imageprovider.h \ - src/s3access.h \ - src/s3/s3.h \ - src/s3/s3internal.h \ - src/s3/s3xml.h \ contracd/src/exposuresummary.h \ contracd/src/exposureinformation.h \ contracd/src/temporaryexposurekey.h \ contracd/src/exposureconfiguration.h \ contracd/src/zipistreambuffer.h \ + src/serveraccess.h \ src/settings.h \ src/upload.h @@ -70,17 +67,12 @@ SOURCES += \ src/contactmodel.cpp \ src/download.cpp \ src/imageprovider.cpp \ - src/s3access.cpp \ - src/s3/s3bucket.c \ - src/s3/s3digest.c \ - src/s3/s3ops.c \ - src/s3/s3string.c \ - src/s3/s3xml.c \ contracd/src/exposuresummary.cpp \ contracd/src/exposureinformation.cpp \ contracd/src/temporaryexposurekey.cpp \ contracd/src/exposureconfiguration.cpp \ contracd/src/zipistreambuffer.cpp \ + src/serveraccess.cpp \ src/settings.cpp \ src/upload.cpp diff --git a/contracd/src/settings.cpp b/contracd/src/settings.cpp index 91cf5ea..80a900c 100644 --- a/contracd/src/settings.cpp +++ b/contracd/src/settings.cpp @@ -2,29 +2,33 @@ #include "settings.h" +#define SETTINGS_MAX_VERSION (0) + Settings * Settings::instance = nullptr; Settings::Settings(QObject *parent) : QObject(parent), - settings(this) + m_settings(this) { - m_tracingKey = settings.value(QStringLiteral("keys/tracingKey"), QVariant(QByteArray())).toByteArray(); - m_enabled = settings.value(QStringLiteral("state/enabled"), false).toBool(); - m_sent = settings.value(QStringLiteral("state/sent"), 0).toUInt(); - m_received = settings.value(QStringLiteral("state/received"), 0).toUInt(); - m_txPower = static_cast(settings.value(QStringLiteral("configuration/txPower"), -30).toInt()); - m_rssiCorrection = static_cast(settings.value(QStringLiteral("configuration/rssiCorretion"), 5).toInt()); + m_tracingKey = m_settings.value(QStringLiteral("keys/tracingKey"), QVariant(QByteArray())).toByteArray(); + m_enabled = m_settings.value(QStringLiteral("state/enabled"), false).toBool(); + m_sent = m_settings.value(QStringLiteral("state/sent"), 0).toUInt(); + m_received = m_settings.value(QStringLiteral("state/received"), 0).toUInt(); + m_txPower = static_cast(m_settings.value(QStringLiteral("configuration/txPower"), -30).toInt()); + m_rssiCorrection = static_cast(m_settings.value(QStringLiteral("configuration/rssiCorretion"), 5).toInt()); + + qDebug() << "Settings created: " << m_settings.fileName(); - qDebug() << "Settings created: " << settings.fileName(); + upgrade(); } Settings::~Settings() { - settings.setValue(QStringLiteral("keys/tracingKey"), m_tracingKey); - settings.setValue(QStringLiteral("state/enabled"), m_enabled); - settings.setValue(QStringLiteral("state/sent"), m_sent); - settings.setValue(QStringLiteral("state/received"), m_received); - settings.setValue(QStringLiteral("configuration/txPower"), m_txPower); - settings.setValue(QStringLiteral("configuration/rssiCorrection"), m_rssiCorrection); + m_settings.setValue(QStringLiteral("keys/tracingKey"), m_tracingKey); + m_settings.setValue(QStringLiteral("state/enabled"), m_enabled); + m_settings.setValue(QStringLiteral("state/sent"), m_sent); + m_settings.setValue(QStringLiteral("state/received"), m_received); + m_settings.setValue(QStringLiteral("configuration/txPower"), m_txPower); + m_settings.setValue(QStringLiteral("configuration/rssiCorrection"), m_rssiCorrection); instance = nullptr; qDebug() << "Deleted settings"; @@ -129,3 +133,34 @@ void Settings::setRssiCorrection(qint8 rssiCorrection) emit rssiCorrectionChanged(); } } + +bool Settings::upgrade() +{ + quint32 version; + bool success = true; + + if (m_settings.allKeys().size() == 0) { + version = SETTINGS_MAX_VERSION; + qDebug() << "Creating new settings file with version: " << SETTINGS_MAX_VERSION; + } + else { + version = m_settings.value(QStringLiteral("application/settingsVersion"), 0).toUInt(); + qDebug() << "Existing settings file version: " << version; + } + + switch (version) { + default: + case SETTINGS_MAX_VERSION: + // File upgraded + // Do nothing + break; + } + + if (success) { + m_settings.setValue(QStringLiteral("application/version"), VERSION); + m_settings.setValue(QStringLiteral("application/settingsVersion"), SETTINGS_MAX_VERSION); + } + + return success; +} + diff --git a/contracd/src/settings.h b/contracd/src/settings.h index 86cc619..b8bc835 100644 --- a/contracd/src/settings.h +++ b/contracd/src/settings.h @@ -46,9 +46,12 @@ public slots: void setTxPower(qint8 txPower); void setRssiCorrection(qint8 rssiCorrection); +private: + bool upgrade(); + private: static Settings * instance; - QSettings settings; + QSettings m_settings; QByteArray m_tracingKey; bool m_enabled; diff --git a/qml/pages/Main.qml b/qml/pages/Main.qml index e2c39a0..bdf526a 100644 --- a/qml/pages/Main.qml +++ b/qml/pages/Main.qml @@ -95,15 +95,6 @@ Page { title: qsTrId("contrac-main_title") } - Label { - width: parent.width - 2 * Theme.horizontalPageMargin - x: Theme.horizontalPageMargin - //% "Using the Google/Apple API and a Corona Warn App test server. Uploads/downloads are only for testing." - text: qsTrId("contrac-main_info") - color: Theme.highlightColor - wrapMode: Text.Wrap - } - SectionHeader { //% "Status" text: qsTrId("contrac-main_he_status") @@ -187,6 +178,14 @@ Page { } } color: Theme.highlightColor + + MouseArea { + anchors.fill: parent + onClicked: { + download.clearError() + upload.clearError() + } + } } } diff --git a/src/download.cpp b/src/download.cpp index 582bb60..62e393e 100644 --- a/src/download.cpp +++ b/src/download.cpp @@ -4,13 +4,13 @@ #include #include "settings.h" -#include "s3access.h" +#include "serveraccess.h" #include "downloadconfig.h" #include "download.h" Download::Download(QObject *parent) : QObject(parent) - , m_s3Access(new S3Access(this)) + , m_serverAccess(new ServerAccess(this)) , m_fileQueue() , m_latest() , m_downloading(false) @@ -19,10 +19,10 @@ Download::Download(QObject *parent) : QObject(parent) , m_status(StatusIdle) , m_downloadConfig(new DownloadConfig(this)) { - m_s3Access->setId("accessKey1"); - m_s3Access->setSecret("verySecretKey1"); - m_s3Access->setBaseUrl(Settings::getInstance().downloadServer()); - m_s3Access->setBucket("cwa"); + m_serverAccess->setId("accessKey1"); + m_serverAccess->setSecret("verySecretKey1"); + m_serverAccess->setBaseUrl(Settings::getInstance().downloadServer()); + m_serverAccess->setBucket("cwa"); m_latest = Settings::getInstance().summaryUpdated().date(); @@ -55,14 +55,13 @@ Q_INVOKABLE void Download::downloadLatest() } } - void Download::configDownloadComplete(QString const &) { switch (m_downloadConfig->error()) { case DownloadConfig::ErrorNone: qDebug() << "Requesting keys"; setStatus(StatusDownloadingKeys); - m_s3Access->setBaseUrl(Settings::getInstance().downloadServer()); + m_serverAccess->setBaseUrl(Settings::getInstance().downloadServer()); startNextDateDownload(); break; default: @@ -133,8 +132,8 @@ void Download::startNextFileDownload() { QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/download/" + file; qDebug() << "Saving to:" << filename; - S3GetFileResult *result = m_s3Access->getFile(key, filename); - connect(result, &S3Result::finished, this, [this, result, date, filename]() { + ServerGetFileResult *result = m_serverAccess->getFile(key, filename); + connect(result, &ServerResult::finished, this, [this, result, date, filename]() { qDebug() << "Finished downloading:" << m_fileQueue[date].first(); ++m_filesReceived; @@ -187,9 +186,9 @@ void Download::startDateDownload(QDate const &date) { qDebug() << "Starting date download:" << date; - QString url = "version/v1/diagnosis-keys/country/DE/date/" + date.toString("yyyy-MM-dd") + "/hour/"; - S3ListResult *result = m_s3Access->list(url); - connect(result, &S3ListResult::finished, this, [this, result, date]() { + QString url = "version/v1/diagnosis-keys/country/DE/date/" + date.toString("yyyy-MM-dd") + "/hour"; + ServerListResult *result = m_serverAccess->list(url); + connect(result, &ServerListResult::finished, this, [this, result, date]() { QStringList keys; switch (result->error()) { @@ -333,3 +332,16 @@ QStringList Download::fileList() const return result; } +Q_INVOKABLE void Download::clearError() +{ + if (m_error != ErrorNone) { + qDebug() << "Clearing download error status"; + m_error = ErrorNone; + + if (m_status == StatusError) { + m_status = StatusIdle; + emit statusChanged(); + } + emit errorChanged(); + } +} diff --git a/src/download.h b/src/download.h index ae1e6f2..b645b4d 100644 --- a/src/download.h +++ b/src/download.h @@ -7,7 +7,7 @@ #include "../contracd/src/exposureconfiguration.h" -class S3Access; +class ServerAccess; class DownloadConfig; class Download : public QObject @@ -45,6 +45,7 @@ class Download : public QObject float progress() const; Status status() const; ErrorType error() const; + Q_INVOKABLE void clearError(); ExposureConfiguration const *config() const; signals: @@ -73,7 +74,7 @@ private slots: void setStatusError(ErrorType error); private: - S3Access *m_s3Access; + ServerAccess *m_serverAccess; QMap m_fileQueue; QDate m_latest; bool m_downloading; diff --git a/src/downloadconfig.cpp b/src/downloadconfig.cpp index f609dcd..486e423 100644 --- a/src/downloadconfig.cpp +++ b/src/downloadconfig.cpp @@ -1,26 +1,28 @@ #include +#include #include "../contracd/src/exposureconfiguration.h" #include "../contracd/src/zipistreambuffer.h" #include "settings.h" -#include "s3access.h" +#include "serveraccess.h" #include "proto/applicationConfiguration.pb.h" #include "downloadconfig.h" -#define CONFIG_FILENAME QStringLiteral("/download/file.config") +#define CONFIG_DIRECTORY QStringLiteral("/download/") +#define CONFIG_LEAFNAME QStringLiteral("file.config") DownloadConfig::DownloadConfig(QObject *parent) : QObject(parent) - , m_s3Access(new S3Access(this)) + , m_serverAccess(new ServerAccess(this)) , m_downloading(false) , m_status(StatusIdle) , m_configuration(new ExposureConfiguration(this)) { - m_s3Access->setId("accessKey1"); - m_s3Access->setSecret("verySecretKey1"); - m_s3Access->setBaseUrl(Settings::getInstance().downloadServer()); - m_s3Access->setBucket("cwa"); + m_serverAccess->setId("accessKey1"); + m_serverAccess->setSecret("verySecretKey1"); + m_serverAccess->setBaseUrl(Settings::getInstance().downloadServer()); + m_serverAccess->setBucket("cwa"); connect(m_configuration, &ExposureConfiguration::minimumRiskScoreChanged, this, &DownloadConfig::configChanged); connect(m_configuration, &ExposureConfiguration::attenuationScoresChanged, this, &DownloadConfig::configChanged); @@ -41,14 +43,19 @@ Q_INVOKABLE void DownloadConfig::downloadLatest() qDebug() << "Requesting configuration"; if (!m_downloading) { - m_s3Access->setBaseUrl(Settings::getInstance().downloadServer()); + m_serverAccess->setBaseUrl(Settings::getInstance().downloadServer()); setStatus(StatusDownloading); QString key = "version/v1/configuration/country/DE/app_config"; - QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + CONFIG_FILENAME; + QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + CONFIG_DIRECTORY; + QDir dir; + qDebug() << "Creating directory:" << filename; + dir.mkpath(filename); + filename += CONFIG_LEAFNAME; qDebug() << "Saving to:" << filename; - S3GetFileResult *result = m_s3Access->getFile(key, filename); - connect(result, &S3Result::finished, this, [this, result, filename]() { + + ServerGetFileResult *result = m_serverAccess->getFile(key, filename); + connect(result, &ServerResult::finished, this, [this, result, filename]() { qDebug() << "Finished downloading configuration"; finalise(); switch (result->error()) { @@ -127,7 +134,7 @@ bool DownloadConfig::downloading() const bool DownloadConfig::loadConfig() { - QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + CONFIG_FILENAME; + QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + CONFIG_DIRECTORY + CONFIG_LEAFNAME; QuaZip quazip(filename); bool result = true; diff --git a/src/downloadconfig.h b/src/downloadconfig.h index 8b0cb52..f95a811 100644 --- a/src/downloadconfig.h +++ b/src/downloadconfig.h @@ -3,7 +3,7 @@ #include -class S3Access; +class ServerAccess; class ExposureConfiguration; namespace diagnosis { @@ -56,7 +56,7 @@ private slots: void applyConfiguration(diagnosis::ApplicationConfiguration const &appConfig); private: - S3Access *m_s3Access; + ServerAccess *m_serverAccess; bool m_downloading; Status m_status; ErrorType m_error; diff --git a/src/harbour-contrac.cpp b/src/harbour-contrac.cpp index 00d24c0..c421b9a 100644 --- a/src/harbour-contrac.cpp +++ b/src/harbour-contrac.cpp @@ -40,6 +40,10 @@ int main(int argc, char *argv[]) qDebug() << "VERSION_MINOR:" << VERSION_MINOR; qDebug() << "VERSION_BUILD:" << VERSION_BUILD; + // Needed for Settings save/load + qRegisterMetaType(); + qRegisterMetaTypeStreamOperators("ExposureSummary"); + Settings::instantiate(app); qmlRegisterType("uk.co.flypig.contrac", 1, 0, "DBusProxy"); @@ -53,10 +57,6 @@ int main(int argc, char *argv[]) qmlRegisterSingletonType("uk.co.flypig.contrac", 1, 0, "Settings", Settings::provider); - // Needed for Settings save/load - qRegisterMetaType(); - qRegisterMetaTypeStreamOperators("ExposureSummary"); - QQuickView *view = SailfishApp::createView(); // The engine takes ownership of the ImageProvider view->engine()->addImageProvider(QLatin1String("contrac"), new ImageProvider(Settings::getInstance())); diff --git a/src/s3/s3.h b/src/s3/s3.h deleted file mode 100644 index faff647..0000000 --- a/src/s3/s3.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _S3_H -#define _S3_H - -#include -#include -#include "s3xml.h" - -#ifdef LINUX -size_t strlcpy(char *dst, const char *src, size_t size); -int asprintf(char **strp, const char *format, ...); -#endif - -#define S3_SECRET_LENGTH 128 -#define S3_ID_LENGTH 128 - -struct s3_string { - char *ptr; - size_t len; - size_t uploaded; -}; - - -struct S3 { - char *secret; - char *id; - char *base_url; - char *proxy; -}; - -struct s3_bucket_entry { - char *key; - char *lastmod; /* time_t */ - size_t size; - char *etag; - TAILQ_ENTRY(s3_bucket_entry) list; -}; - -TAILQ_HEAD(s3_bucket_entry_head, s3_bucket_entry); - -struct S3 * s3_init(const char *id, const char *secret, const char *base_url); -void s3_free(struct S3 *s3); - -struct s3_string * s3_string_init(void); -size_t s3_string_curl_writefunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s); -size_t s3_string_curl_readfunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s); -void s3_string_free(struct s3_string *str); - -char * s3_hmac_sign(const char *key, const char *str, size_t len); -char * s3_md5_sum(const char *content, size_t len); - -void s3_get(struct S3 *s3, const char *bucket, const char *key, struct s3_string *out); -void s3_delete(struct S3 *s3, const char *bucket, const char *key); -void s3_put(struct S3 *s3, const char *bucket, const char *key, const char *content_type, const char *data, size_t len); - -void s3_bucket_entry_free(struct s3_bucket_entry *entry); -void s3_bucket_entries_free(struct s3_bucket_entry_head *entries); - -struct s3_bucket_entry_head * s3_list_bucket(struct S3 *s3, const char *bucket, const char *prefix); - -#endif /* _S3_H */ diff --git a/src/s3/s3bucket.c b/src/s3/s3bucket.c deleted file mode 100644 index 8331d0a..0000000 --- a/src/s3/s3bucket.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2014 Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "s3.h" -#include "s3internal.h" - -void -s3_bucket_entry_free(struct s3_bucket_entry *entry) { - if (entry->key) - free(entry->key); - if (entry->lastmod) - free(entry->lastmod); - if (entry->etag) - free(entry->etag); - - free(entry); -} - -void -s3_bucket_entries_free(struct s3_bucket_entry_head *entries) { - struct s3_bucket_entry *e; - - while ((e = TAILQ_FIRST(entries)) != NULL) { - TAILQ_REMOVE(entries, e, list); - s3_bucket_entry_free(e); - } - free(entries); -} - - -static struct s3_bucket_entry * -s3_bucket_entry_from_node(xmlNode *root) { - struct s3_bucket_entry *entry = malloc(sizeof(struct s3_bucket_entry)); - xmlNode *node = NULL; - for (node = root; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE) { - xmlChar *value = xmlNodeGetContent(node->children); - size_t len = strlen((const char *) value); - - if (strcasecmp("key", (const char *)node->name) == 0) { - entry->key = malloc(len + 2); - strlcpy(entry->key, (const char *)value, len + 1); - } else if (strcasecmp("lastmodified", (const char *)node->name) == 0) { - entry->lastmod = malloc(len + 2); - strlcpy(entry->lastmod, (const char *)value, len + 1); - } else if (strcasecmp("etag", (const char *)node->name) == 0) { - entry->etag = malloc(len + 2); - strlcpy(entry->etag, (const char *)value, len + 1); - } else { -#ifdef DEBUG - printf("node type: Element, name: %s\n", node->name); - printf("node xmlNodeGetContent: %s\n",value); -#endif - } - xmlFree(value); - } - } - return entry; -} - -#ifdef WALK_CONTENT_PREFIXES -static void -s3_parse_bucket_prefixes(xmlNode *root) { - xmlNode *node = NULL; - for (node = root; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE) { - xmlChar *value = xmlNodeGetContent(node->children); - - printf("Prefix node type: Element, name: %s\n", node->name); - printf("Prefix node xmlsNodeGetContent: %s\n",value); - xmlFree(value); - } - } -} - -static void -s3_walk_content_prefixes(xmlNodeSetPtr nodes, void *data) { - xmlNodePtr cur; - int size; - int i; - - size = (nodes) ? nodes->nodeNr : 0; -#ifdef DEBUG - printf("prefix size is %d nodes\n", size); -#endif - for (i = 0; i < size ; i++) { - if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) { - cur = nodes->nodeTab[i]; - s3_parse_bucket_prefixes(cur->children); - } - } -} -#endif - -static void -s3_walk_content_nodes(xmlNodeSetPtr nodes, void *data) { - struct s3_bucket_entry_head *head; - xmlNodePtr cur; - int size; - int i; - - head = (struct s3_bucket_entry_head *) data; - - size = (nodes) ? nodes->nodeNr : 0; -#ifdef DEBUG - printf("size is %d nodes\n", size); -#endif - for (i = 0; i < size ; i++) { - if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) { - cur = nodes->nodeTab[i]; - - struct s3_bucket_entry *entry = s3_bucket_entry_from_node(cur->children); - TAILQ_INSERT_TAIL(head, entry, list); - } - } -} - -struct s3_bucket_entry_head * -s3_parse_bucket_response(char *xml) { - struct s3_bucket_entry_head *entries; - xmlDocPtr doc; - - doc = xmlReadMemory(xml, strlen(xml), "noname.xml", NULL, 0); - if (doc == NULL) - return NULL; - - entries = malloc(sizeof (*entries)); - TAILQ_INIT(entries); - - /* - * Since Amazon uses an XML Namespace, we need to declare it - * and use it as a prefix in Xpath queries, even though it's - * not written out in the tag names - libxml2 follows the - * standard where others don't - */ - s3_execute_xpath_expr(doc, (const xmlChar *)"//amzn:Contents", s3_walk_content_nodes, entries); -#ifdef WALK_CONTENT_PREFIXES - s3_execute_xpath_expr(doc, (const xmlChar *)"//amzn:CommonPrefixes", s3_walk_content_prefixes, NULL); -#endif - xmlFreeDoc(doc); - xmlCleanupParser(); - - return entries; -} - -struct s3_bucket_entry_head * -s3_list_bucket(struct S3 *s3, const char *bucket, const char *prefix) { - char *date; - char *sign_data; - char *url; - struct s3_string *str; - const char *method = "GET"; - struct s3_bucket_entry_head *entries; - - str = s3_string_init(); - date = s3_make_date(); - - asprintf(&sign_data, "%s\n\n\n%s\n/%s/", method, date, bucket); - asprintf(&url, "http://%s.%s/?delimiter=/%s%s", bucket, s3->base_url, prefix ? "&prefix=" : "", prefix ? prefix : ""); - - s3_perform_op(s3, method, url, sign_data, date, str, NULL, NULL, NULL); - - entries = s3_parse_bucket_response(str->ptr); - - s3_string_free(str); - free(url); - free(sign_data); - free(date); - - return entries; -} diff --git a/src/s3/s3digest.c b/src/s3/s3digest.c deleted file mode 100644 index babd1a3..0000000 --- a/src/s3/s3digest.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "s3.h" - -#include -#include -#include -#include - -char * -s3_hmac_sign(const char *key, const char *str, size_t len) { - unsigned char *digest; - char *buf; - unsigned int digest_len = EVP_MAX_MD_SIZE; /* HMAC_Final needs at most EVP_MAX_MD_SIZE bytes */ - HMAC_CTX ctx; - BIO *bmem, *b64; - BUF_MEM *bufptr; - - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - /* Setup HMAC context, init with sha1 and our key*/ - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, key, strlen((char *)key), EVP_sha1(), NULL); - - /* Create Base64 BIO filter that outputs to memory */ - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - - /* Give us a buffer to write result into */ - digest = malloc(digest_len); - - /* Push data into HMAC */ - HMAC_Update(&ctx, (unsigned char *)str, (unsigned int)len); - - /* Flush HMAC data into buffer */ - HMAC_Final(&ctx, digest, &digest_len); - - /* Write data into BIO, flush and fetch the data */ - BIO_write(b64, digest, digest_len); - (void) BIO_flush(b64); - BIO_get_mem_ptr(b64, &bufptr); - - buf = malloc(bufptr->length); - memcpy(buf, bufptr->data, bufptr->length - 1); - buf[bufptr->length - 1] = '\0'; - - BIO_free_all(b64); - HMAC_CTX_cleanup(&ctx); - - ENGINE_cleanup(); - free(digest); - return buf; -} - -char * -s3_md5_sum(const char *content, size_t len) { - - const EVP_MD *md = EVP_md5(); - unsigned char *digest; - EVP_MD_CTX *ctx; - unsigned int digest_len; - BIO *bmem, *b64; - BUF_MEM *bufptr; - char *buf; - - digest = malloc(EVP_MAX_MD_SIZE); - - ctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(ctx, md, NULL); - EVP_DigestUpdate(ctx, content, len); - EVP_DigestFinal_ex(ctx, digest, &digest_len); - - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - - BIO_write(b64, digest, digest_len); - (void) BIO_flush(b64); - BIO_get_mem_ptr(b64, &bufptr); - - buf = malloc(bufptr->length); - memcpy(buf, bufptr->data, bufptr->length - 1); - buf[bufptr->length - 1] = '\0'; - - BIO_free_all(b64); - free(digest); - EVP_MD_CTX_destroy(ctx); - - return buf; -} diff --git a/src/s3/s3internal.h b/src/s3/s3internal.h deleted file mode 100644 index aef76f8..0000000 --- a/src/s3/s3internal.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2014, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _S3_INTERNAL_H -#define _S3_INTERNAL_H - -char * s3_make_date(void); -void s3_perform_op(struct S3 *s3, const char *method, const char *url, const char *sign_data, const char *date, struct s3_string *out, struct s3_string *in, const char *content_md5, const char *content_type); -struct s3_bucket_entry_head * s3_parse_bucket_response(char *xml); -#endif //_S3_INTERNAL_H diff --git a/src/s3/s3ops.c b/src/s3/s3ops.c deleted file mode 100644 index 4172c9a..0000000 --- a/src/s3/s3ops.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include - -#include "s3.h" -#include "s3internal.h" -#include - -struct S3 * -s3_init(const char *id, const char *secret, const char *base_url) { - struct S3 *s3 = malloc(sizeof (struct S3)); - - s3->id = malloc(S3_ID_LENGTH); - s3->secret = malloc(S3_SECRET_LENGTH); - /* XXX better length */ - s3->base_url = malloc(256); - - strlcpy(s3->id, id, S3_ID_LENGTH); - strlcpy(s3->secret, secret, S3_SECRET_LENGTH); - strlcpy(s3->base_url, base_url, 255); - - s3->proxy = NULL; - - return s3; -} - -void -s3_free(struct S3 *s3) { - free(s3->id); - free(s3->secret); - free(s3->base_url); - - free(s3); -} - -char * -s3_make_date(void) { - char *date; - time_t now; - struct tm tm; - - date = malloc(128); - now = time(0); - tm = *gmtime(&now); - strftime(date, 128, "%a, %d %b %Y %H:%M:%S %Z", &tm); - - return date; - -} - -/* Add return values later */ -void -s3_perform_op(struct S3 *s3, const char *method, const char *url, const char *sign_data, const char *date, struct s3_string *out, struct s3_string *in, const char *content_md5, const char *content_type) { - char *digest; - char *hdr; - - CURL *curl; - struct curl_slist *headers = NULL; - - - - digest = s3_hmac_sign(s3->secret, sign_data, strlen(sign_data)); -#ifdef DEBUG - fprintf(stderr, "DEBUG: data to sign:%s\n", sign_data); - fprintf(stderr, "DEBUG: Authentication: AWS %s:%s\n", s3->id, digest); -#endif - curl = curl_easy_init(); - - hdr = malloc(1024); - - if (strcmp(method, "DELETE") == 0) { - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); - } else if (strcmp(method, "PUT") == 0) { - if (content_type) { - snprintf(hdr, 1023, "Content-Type: %s", content_type); - headers = curl_slist_append(headers, hdr); - } - - if (content_md5) { - snprintf(hdr, 1023, "Content-MD5: %s", content_md5); - headers = curl_slist_append(headers, hdr); - } - - curl_easy_setopt(curl, CURLOPT_READFUNCTION, s3_string_curl_readfunc); - curl_easy_setopt(curl, CURLOPT_READDATA, in); - curl_easy_setopt(curl, CURLOPT_INFILESIZE, in->len); - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); - } - - snprintf(hdr, 1023, "Date: %s", date); - headers = curl_slist_append(headers, hdr); - - snprintf(hdr, 1023, "Authorization: AWS %s:%s", s3->id, digest); - headers = curl_slist_append(headers, hdr); - free(hdr); - -#ifdef DEBUG - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - curl_easy_setopt(curl, CURLOPT_HEADER, 1); -#endif - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, s3_string_curl_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, out); - - curl_easy_setopt(curl, CURLOPT_URL, url); - - if (s3->proxy) { - curl_easy_setopt(curl, CURLOPT_PROXY, s3->proxy); - } - - curl_easy_perform(curl); - - curl_easy_cleanup(curl); - curl_slist_free_all(headers); - - free(digest); -} - - -void -s3_get(struct S3 *s3, const char *bucket, const char *key, struct s3_string *out) { - const char *method = "GET"; - char *sign_data; - char *date; - char *url; - - date = s3_make_date(); - - asprintf(&sign_data, "%s\n\n\n%s\n/%s/%s", method, date, bucket, key); - asprintf(&url, "http://%s.%s/%s", bucket, s3->base_url, key); - - s3_perform_op(s3, method, url, sign_data, date, out, NULL, NULL, NULL); - - free(sign_data); - free(date); - free(url); -} - - -void -s3_delete(struct S3 *s3, const char *bucket, const char *key) { - char *sign_data; - char *url; - char *date; - const char *method = "DELETE"; - struct s3_string *out; - - out = s3_string_init(); - date = s3_make_date(); - - asprintf(&sign_data, "%s\n\n\n%s\n/%s/%s", method, date, bucket, key); - asprintf(&url, "http://%s.%s/%s", bucket, s3->base_url, key); - - s3_perform_op(s3, method, url, sign_data, date, out, NULL, NULL, NULL); - - s3_string_free(out); - free(date); - free(url); - free(sign_data); - -} - -void -s3_put(struct S3 *s3, const char *bucket, const char *key, const char *content_type, const char *data, size_t len) { - const char *method = "PUT"; - char *sign_data; - char *date; - char *url; - char *md5; - struct s3_string *in, *out; - - in = s3_string_init(); - out = s3_string_init(); - - in->ptr = realloc(in->ptr, len + 1); - memcpy(in->ptr, data, len); - in->len = len; - - md5 = s3_md5_sum(data, len); - - date = s3_make_date(); - asprintf(&sign_data, "%s\n%s\n%s\n%s\n/%s/%s", method, md5, content_type ? content_type : "", date, bucket, key); - - - asprintf(&url, "http://%s.%s/%s", bucket, s3->base_url, key); - - s3_perform_op(s3, method, url, sign_data, date, out, in, md5, content_type); - s3_string_free(in); - s3_string_free(out); - - free(url); - free(md5); - free(date); - free(sign_data); -} diff --git a/src/s3/s3string.c b/src/s3/s3string.c deleted file mode 100644 index 6905914..0000000 --- a/src/s3/s3string.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "s3.h" -#include "s3internal.h" - -#ifdef LINUX -size_t strlcpy(char *dst, const char *src, size_t size) { - size_t i; - for (i = 0; (i < (size - 1)) && (src[i] != 0); ++i) { - dst[i] = src[i]; - } - dst[i] = 0; - - return i; -} -#endif - -size_t -s3_string_curl_writefunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s) { - size_t new_len = s->len + len *nmemb; - s->ptr = realloc(s->ptr, new_len + 1); - if (s->ptr == NULL) { - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); - } - memcpy(s->ptr+s->len, ptr, len*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - - return len*nmemb; -} - -size_t -s3_string_curl_readfunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s) { - size_t left = s->len - s->uploaded; - size_t max_chunk = len * nmemb; - size_t retcode = left < max_chunk ? left : max_chunk; - - memcpy(ptr, s->ptr + s->uploaded, retcode); - - s->uploaded += retcode; - return retcode; -} - -struct s3_string * -s3_string_init(void) { - struct s3_string *s; - s = malloc(sizeof (struct s3_string)); - s->len = 0; - s->uploaded = 0; - s->ptr = malloc(s->len+1); - if (s->ptr == NULL) { - fprintf(stderr, "malloc() failed\n"); - exit(EXIT_FAILURE); - } - s->ptr[0] = '\0'; - return s; -} - -void -s3_string_free(struct s3_string *str) { - free(str->ptr); - free(str); -} - -int asprintf(char **strp, const char *format, ...) { - va_list args; - int size; - char *buffer; - - va_start(args, format); - size = vsnprintf(NULL, 0, format, args); - va_end(args); - - buffer = malloc((unsigned int)size + 1); - - if (buffer != NULL) { - va_start(args, format); - vsnprintf(buffer, (unsigned int)size + 1, format, args); - va_end(args); - *strp = buffer; - } - else { - size = -1; - } - - - return size; -} diff --git a/src/s3/s3test.c b/src/s3/s3test.c deleted file mode 100644 index 3c8479f..0000000 --- a/src/s3/s3test.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2013,2014 Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "s3.h" - -#include -#include -#include - -#include - - - -int main (int argc, char **argv) { - struct S3 *s3; - struct s3_string *out; - struct s3_bucket_entry_head *bkt_entries; - struct s3_bucket_entry *e; - char *file_contents = "foo bar gazonk"; - - - char *s3_key_id = "accessKey1"; - char *s3_secret = "verySecretKey1"; - - if (argc < 2) { - fprintf(stderr, "Usage: s3test \n"); - return 1; - } - - if (s3_key_id == NULL || s3_secret == NULL) { - fprintf(stderr, "Error: Environment variable AWS_ACCESS_KEY_ID or AWS_SECRET_KEY not set\n"); - return 1; - } - - char *bucket = argv[1]; - - s3 = s3_init(s3_key_id, s3_secret, "localhost:8003"); - - // aws s3 --profile cwa --endpoint-url=http://localhost:8003 sync s3://cwa . - - printf("Listing bucket root\n"); - bkt_entries = s3_list_bucket(s3, bucket, NULL); - if (bkt_entries != NULL) { - TAILQ_FOREACH(e, bkt_entries, list) { - printf("\tKey %s\n", e->key); - } - s3_bucket_entries_free(bkt_entries); - } - - - printf("Listing /version/\n"); - bkt_entries = s3_list_bucket(s3, bucket, "version/"); - if (bkt_entries != NULL) { - TAILQ_FOREACH(e, bkt_entries, list) { - printf("\tKey %s\n", e->key); - } - s3_bucket_entries_free(bkt_entries); - } - - printf("Uploading foo.txt\n"); - s3_put(s3, bucket, "foo.txt", "text/plain", file_contents, strlen(file_contents)); - - printf("Downloading foo.txt\n"); - out = s3_string_init(); - s3_get(s3, bucket, "foo.txt", out); - - printf("Contents:\n%.*s\n", (int)out->len, out->ptr); - - s3_string_free(out); - - printf("Deleting foo.txt\n"); - s3_delete(s3, bucket, "foo.txt"); - s3_free(s3); - - return 0; -} diff --git a/src/s3/s3xml.c b/src/s3/s3xml.c deleted file mode 100644 index 375979c..0000000 --- a/src/s3/s3xml.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "s3xml.h" - -static void -s3_register_namespaces(xmlXPathContextPtr ctx, const xmlChar *nsList) { - xmlChar* nsListDup; - xmlChar* prefix; - xmlChar* href; - xmlChar* next; - - nsListDup = xmlStrdup(nsList); - if(nsListDup == NULL) { - fprintf(stderr, "Error: unable to strdup namespaces list\n"); - return; - } - - next = nsListDup; - while(next != NULL) { - /* skip spaces */ - while((*next) == ' ') next++; - if((*next) == '\0') break; - - /* find prefix */ - prefix = next; - next = (xmlChar*)xmlStrchr(next, '='); - if(next == NULL) { - fprintf(stderr,"Error: invalid namespaces list format\n"); - xmlFree(nsListDup); - return; - } - *(next++) = '\0'; - - /* find href */ - href = next; - next = (xmlChar*)xmlStrchr(next, ' '); - if(next != NULL) { - *(next++) = '\0'; - } - - /* do register namespace */ - if(xmlXPathRegisterNs(ctx, prefix, href) != 0) { - fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href); - xmlFree(nsListDup); - return; - } - } - - xmlFree(nsListDup); -} - -void -s3_execute_xpath_expr(const xmlDocPtr doc, const xmlChar *xpath_expr, void (*nodeset_cb)(xmlNodeSetPtr, void *), void *cb_data) { - xmlXPathContextPtr xpath_ctx; - xmlXPathObjectPtr xpath_obj; - const xmlChar *ns_list = (const xmlChar *)"amzn=http://s3.amazonaws.com/doc/2006-03-01/"; - - xpath_ctx = xmlXPathNewContext(doc); - if (xpath_ctx == NULL) { - fprintf(stderr,"Error: unable to create new XPath context\n"); - return; - } - - s3_register_namespaces(xpath_ctx, ns_list); - - xpath_obj = xmlXPathEvalExpression(xpath_expr, xpath_ctx); - if(xpath_obj == NULL) { - fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpath_expr); - } else { - - nodeset_cb(xpath_obj->nodesetval, cb_data); - xmlXPathFreeObject(xpath_obj); - } - - xmlXPathFreeContext(xpath_ctx); -} diff --git a/src/s3/s3xml.h b/src/s3/s3xml.h deleted file mode 100644 index 704e3fa..0000000 --- a/src/s3/s3xml.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2013, Ian Delahorne - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - - -void s3_execute_xpath_expr(const xmlDocPtr doc, const xmlChar *xpath_expr, void (*nodeset_cb)(xmlNodeSetPtr, void *), void *cb_data); diff --git a/src/s3access.cpp b/src/s3access.cpp deleted file mode 100644 index b584b49..0000000 --- a/src/s3access.cpp +++ /dev/null @@ -1,279 +0,0 @@ -extern "C" { -#include "s3/s3internal.h" -} - -#include - -#include "s3access.h" - -S3Result::S3Result(QNetworkReply *reply, QObject *parent) : QObject(parent) - , m_reply(reply) -{ - connect(reply, &QNetworkReply::finished, this, &S3Result::onFinished); -} - -S3ListResult::S3ListResult(QNetworkReply *reply, QObject *parent) : S3Result (reply, parent) -{ -} - -S3GetResult::S3GetResult(QNetworkReply *reply, QObject *parent) : S3Result (reply, parent) -{ -} - -S3GetFileResult::S3GetFileResult(QNetworkReply *reply, QString const &filename, QObject *parent) : S3Result (reply, parent) - , m_filename(filename) -{ -} - -S3Result::~S3Result() -{ - m_reply->deleteLater(); -} - -QNetworkReply::NetworkError S3Result::error() const -{ - return m_reply->error(); -} - -S3Access::S3Access(QObject *parent) : QObject(parent) - , m_s3(nullptr) - , m_manager(new QNetworkAccessManager(this)) -{ -} - -S3Access::~S3Access() -{ - if (m_s3) { - s3_free(m_s3); - m_s3 = nullptr; - } -} - -void S3Access::initialise() -{ - if (m_s3) { - s3_free(m_s3); - m_s3 = nullptr; - } - m_s3 = s3_init(m_id.toLatin1().data(), m_baseUrl.toLatin1().data(), m_baseUrl.toLatin1().data()); -} - -void S3Access::setId(QString const &id) -{ - if (m_id != id) { - m_id = id; - initialise(); - emit idChanged(); - } -} - -void S3Access::setSecret(QString const &secret) -{ - if (m_secret != secret) { - m_secret = secret; - initialise(); - emit secretChanged(); - } -} - -void S3Access::setBaseUrl(QString const &baseUrl) -{ - if (m_baseUrl != baseUrl) { - m_baseUrl = baseUrl; - initialise(); - emit baseUrlChanged(); - } -} - -void S3Access::setBucket(QString const &bucket) -{ - if (m_bucket != bucket) { - m_bucket = bucket; - emit bucketChanged(); - } -} - -QString S3Access::id() const -{ - return m_id; -} - -QString S3Access::secret() const -{ - return m_secret; -} - -QString S3Access::baseUrl() const -{ - return m_baseUrl; -} - -QString S3Access::bucket() const -{ - return m_bucket; -} - -S3ListResult *S3Access::list(QString const &prefix) -{ - char *date; - QString signData; - QString url; - QNetworkReply *reply; - S3ListResult *result; - - date = s3_make_date(); - signData = "GET\n\n\n" + QString(date) + "\n/" + m_bucket + "/"; - url = "http://" + m_baseUrl + "/" + m_bucket + "/?delimiter=/"; - if (!prefix.isEmpty()) { - url += "&prefix=" + prefix; - } - - reply = performOp(GET, url, signData, date, nullptr, nullptr, nullptr); - result = new S3ListResult(reply, this); - free(date); - - return result; -} - -QNetworkReply *S3Access::performOp(Method method, QString const &url, QString const &sign_data, const char *date, QIODevice *in, const char *content_md5, const char *content_type) -{ - QNetworkRequest request; - char *digest; - QNetworkReply *reply; - - request.setUrl(QUrl(QString(url))); - digest = s3_hmac_sign(m_secret.toLatin1().data(), sign_data.toLatin1().data(), sign_data.toLatin1().size()); - - request.setRawHeader("Date", date); - request.setRawHeader("Authorization", QString(QStringLiteral("AWS %1:%2")).arg(m_id).arg(digest).toLocal8Bit()); - request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - - qDebug() << "Request to: " << url; - - switch (method) { - case DELETE: - qDebug() << "DELETE request"; - reply = m_manager->deleteResource(request); - break; - case PUT: - qDebug() << "PUT request"; - if (content_type) { - request.setHeader(QNetworkRequest::ContentTypeHeader, content_type); - } - - if (content_md5) { - request.setRawHeader("Content-MD5", content_md5); - } - - reply = m_manager->put(request, in); - break; - default: // GET - qDebug() << "GET request"; - reply = m_manager->get(request); - break; - } - - free(digest); - - return reply; -} - -void S3ListResult::onFinished() -{ - qDebug() << "S3ListResult::onFinished"; - - QByteArray data; - struct s3_bucket_entry_head *entries; - struct s3_bucket_entry *e; - - data = m_reply->readAll(); - if (m_reply->error() == QNetworkReply::NoError) { - entries = s3_parse_bucket_response(data.data()); - - m_keys.clear(); - if (entries != nullptr) { - TAILQ_FOREACH(e, entries, list) { - m_keys.append(QString(e->key)); - } - s3_bucket_entries_free(entries); - } - emit keysChanged(); - } - else { - qDebug() << "Error: " << m_reply->error(); - } - - emit finished(); -} - -QStringList S3ListResult::keys() const -{ - return m_keys; -} - -void S3GetResult::onFinished() -{ - m_data = m_reply->readAll(); - - emit dataChanged(); -} - -QByteArray S3GetResult::data() const -{ - return m_data; -} - -S3GetFileResult *S3Access::getFile(QString const &key, QString const &filename) -{ - char *date; - QString signData; - QString url; - QNetworkReply *reply; - S3GetFileResult *result; - - date = s3_make_date(); - signData = "GET\n\n\n" + QString(date) + "\n/" + m_bucket + "/" + key; - url = "http://" + m_baseUrl + "/" + m_bucket + "/" + key; - - reply = performOp(GET, url, signData, date, nullptr, nullptr, nullptr); - result = new S3GetFileResult(reply, filename, this); - free(date); - - return result; -} - -void S3Result::onFinished() -{ - qDebug() << "S3Result::onFinished"; - - if (m_reply->error() == QNetworkReply::NoError) { - qDebug() << "Download complete"; - } - else { - qDebug() << "Error: " << m_reply->error(); - } - - emit finished(); -} - -void S3GetFileResult::onFinished() -{ - qDebug() << "S3Result::onFinished"; - - if (m_reply->error() == QNetworkReply::NoError) { - QFile file(m_filename); - if (file.open(QIODevice::WriteOnly)) { - file.write(m_reply->readAll()); - file.close(); - qDebug() << "Data written to file: " << m_filename; - } - else { - qDebug() << "Error writing to file: " << m_filename; - } - } - else { - qDebug() << "Error: " << m_reply->error(); - } - - emit finished(); -} diff --git a/src/serveraccess.cpp b/src/serveraccess.cpp new file mode 100644 index 0000000..fce8875 --- /dev/null +++ b/src/serveraccess.cpp @@ -0,0 +1,322 @@ +#include + +#include "serveraccess.h" +#include +#include +#include + +namespace { + +// Guaranteed to end with a / +QString constructUrl(QString const &baseUrl, QString const & /* bucket */) +{ + QString url; + + if (!baseUrl.contains(QLatin1String("://"))) { + url += QStringLiteral("https://"); + } + + url += baseUrl; + if (!url.endsWith('/')) { + url += '/'; + } + + /* + url += bucket; + + if (!url.endsWith('/')) { + url += '/'; + } + */ + + return url; +} + +QString constructListUrl(QString const &baseUrl, QString const &bucket, QString const &prefix) +{ + QString url = constructUrl(baseUrl, bucket); + url += prefix; + + return url; +} + +QString constructFileUrl(QString const &baseUrl, QString const &bucket, QString const &key) +{ + QString url = constructUrl(baseUrl, bucket); + url += key; + + return url; +} + +} // Empty namespace + +ServerResult::ServerResult(QNetworkReply *reply, QObject *parent) : QObject(parent) + , m_reply(reply) +{ + connect(reply, &QNetworkReply::finished, this, &ServerResult::onFinished); +} + +ServerListResult::ServerListResult(QNetworkReply *reply, QString const &prefix, QObject *parent) + : ServerResult (reply, parent) + , m_prefix(prefix) +{ +} + +ServerGetResult::ServerGetResult(QNetworkReply *reply, QObject *parent) : ServerResult (reply, parent) +{ +} + +ServerGetFileResult::ServerGetFileResult(QNetworkReply *reply, QString const &filename, QObject *parent) : ServerResult (reply, parent) + , m_filename(filename) +{ +} + +ServerResult::~ServerResult() +{ + m_reply->deleteLater(); +} + +QNetworkReply::NetworkError ServerResult::error() const +{ + return m_reply->error(); +} + +ServerAccess::ServerAccess(QObject *parent) : QObject(parent) + , m_manager(new QNetworkAccessManager(this)) +{ +} + +ServerAccess::~ServerAccess() +{ +} + +void ServerAccess::initialise() +{ +} + +void ServerAccess::setId(QString const &id) +{ + if (m_id != id) { + m_id = id; + initialise(); + emit idChanged(); + } +} + +void ServerAccess::setSecret(QString const &secret) +{ + if (m_secret != secret) { + m_secret = secret; + initialise(); + emit secretChanged(); + } +} + +void ServerAccess::setBaseUrl(QString const &baseUrl) +{ + if (m_baseUrl != baseUrl) { + m_baseUrl = baseUrl; + initialise(); + emit baseUrlChanged(); + } +} + +void ServerAccess::setBucket(QString const &bucket) +{ + if (m_bucket != bucket) { + m_bucket = bucket; + emit bucketChanged(); + } +} + +QString ServerAccess::id() const +{ + return m_id; +} + +QString ServerAccess::secret() const +{ + return m_secret; +} + +QString ServerAccess::baseUrl() const +{ + return m_baseUrl; +} + +QString ServerAccess::bucket() const +{ + return m_bucket; +} + +ServerListResult *ServerAccess::list(QString const &prefix) +{ + QString url; + QNetworkReply *reply; + ServerListResult *result; + + url = constructListUrl(m_baseUrl, m_bucket, prefix); + + reply = performOp(GET, url, nullptr, nullptr); + result = new ServerListResult(reply, prefix, this); + + return result; +} + +QNetworkReply *ServerAccess::performOp(Method method, QString const &url, QIODevice *in, const char *content_type) +{ + QNetworkRequest request; + QNetworkReply *reply; + + request.setUrl(QUrl(QString(url))); + + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + qDebug() << "Request to: " << url; + + switch (method) { + case DELETE: + qDebug() << "DELETE request"; + reply = m_manager->deleteResource(request); + break; + case PUT: + qDebug() << "PUT request"; + if (content_type) { + request.setHeader(QNetworkRequest::ContentTypeHeader, content_type); + } + reply = m_manager->put(request, in); + break; + default: // GET + qDebug() << "GET request"; + reply = m_manager->get(request); + break; + } + + return reply; +} + +void ServerListResult::onFinished() +{ + qDebug() << "ServerListResult::onFinished"; + + QByteArray data; + QJsonParseError error; + QJsonDocument doc; + bool result = true; + + if (m_reply->error() == QNetworkReply::NoError) { + // Should return a json array + data = m_reply->readAll(); + qDebug() << "Received list: " << data; + } + else { + qDebug() << "Error: " << m_reply->error(); + } + + if (result) { + doc = QJsonDocument::fromJson(data, &error); + if (doc.isNull()) { + qDebug() << "JSON error: " << error.errorString(); + result = false; + } + } + + if (result && !doc.isArray()) { + qDebug() << "JSON error: expected an array"; + result = false; + } + + if (result) { + m_keys.clear(); + for (QJsonValue value : doc.array()) { + QString converted; + switch (value.type()) { + case QJsonValue::Double: + converted = QString::number(value.toInt()); + break; + case QJsonValue::String: + converted = value.toString(); + break; + default: + // Don't append anything + qDebug() << "JSON error: converting type " << value.type() << " not supported"; + break; + } + if (!converted.isEmpty()) { + m_keys.append(m_prefix + "/" + converted); + } + } + emit keysChanged(); + qDebug() << "Successfullly parsed list: " << m_keys; + } + + emit finished(); +} + +QStringList ServerListResult::keys() const +{ + return m_keys; +} + +void ServerGetResult::onFinished() +{ + m_data = m_reply->readAll(); + + emit dataChanged(); +} + +QByteArray ServerGetResult::data() const +{ + return m_data; +} + +ServerGetFileResult *ServerAccess::getFile(QString const &key, QString const &filename) +{ + QString url; + QNetworkReply *reply; + ServerGetFileResult *result; + + url = constructFileUrl(m_baseUrl, m_bucket, key); + + reply = performOp(GET, url, nullptr, nullptr); + result = new ServerGetFileResult(reply, filename, this); + + return result; +} + +void ServerResult::onFinished() +{ + qDebug() << "ServerResult::onFinished"; + + if (m_reply->error() == QNetworkReply::NoError) { + qDebug() << "Download complete"; + } + else { + qDebug() << "Error: " << m_reply->error(); + } + + emit finished(); +} + +void ServerGetFileResult::onFinished() +{ + qDebug() << "ServerResult::onFinished"; + + if (m_reply->error() == QNetworkReply::NoError) { + QFile file(m_filename); + if (file.open(QIODevice::WriteOnly)) { + file.write(m_reply->readAll()); + qDebug() << "Data written to file: " << m_filename; + qDebug() << "Data size: " << file.pos(); + file.close(); + } + else { + qDebug() << "Error writing to file: " << m_filename; + } + } + else { + qDebug() << "Error: " << m_reply->error(); + } + + emit finished(); +} diff --git a/src/s3access.h b/src/serveraccess.h similarity index 60% rename from src/s3access.h rename to src/serveraccess.h index 39e0e21..1e7dea9 100644 --- a/src/s3access.h +++ b/src/serveraccess.h @@ -1,20 +1,16 @@ -#ifndef S3ACCESS_H -#define S3ACCESS_H +#ifndef SERVERACCESS_H +#define SERVERACCESS_H #include #include -extern "C" { -#include "s3/s3.h" -} - -class S3Result : public QObject +class ServerResult : public QObject { Q_OBJECT public: - explicit S3Result(QNetworkReply *reply, QObject *parent = nullptr); - ~S3Result(); + explicit ServerResult(QNetworkReply *reply, QObject *parent = nullptr); + ~ServerResult(); virtual QNetworkReply::NetworkError error() const; @@ -28,13 +24,13 @@ protected slots: QNetworkReply *m_reply; }; -class S3ListResult : public S3Result +class ServerListResult : public ServerResult { Q_OBJECT Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged) public: - explicit S3ListResult(QNetworkReply *reply, QObject *parent = nullptr); + explicit ServerListResult(QNetworkReply *reply, QString const &prefix, QObject *parent = nullptr); QStringList keys() const; @@ -45,16 +41,17 @@ private slots: virtual void onFinished(); private: + QString m_prefix; QStringList m_keys; }; -class S3GetResult : public S3Result +class ServerGetResult : public ServerResult { Q_OBJECT Q_PROPERTY(QByteArray data READ data NOTIFY dataChanged) public: - explicit S3GetResult(QNetworkReply *reply, QObject *parent = nullptr); + explicit ServerGetResult(QNetworkReply *reply, QObject *parent = nullptr); QByteArray data() const; @@ -68,12 +65,12 @@ private slots: QByteArray m_data; }; -class S3GetFileResult : public S3Result +class ServerGetFileResult : public ServerResult { Q_OBJECT public: - explicit S3GetFileResult(QNetworkReply *reply, QString const &filename, QObject *parent = nullptr); + explicit ServerGetFileResult(QNetworkReply *reply, QString const &filename, QObject *parent = nullptr); private slots: virtual void onFinished(); @@ -82,7 +79,7 @@ private slots: QString m_filename; }; -class S3Access : public QObject +class ServerAccess : public QObject { Q_OBJECT @@ -92,15 +89,15 @@ class S3Access : public QObject Q_PROPERTY(QString bucket READ bucket WRITE setBucket NOTIFY bucketChanged) public: - explicit S3Access(QObject *parent = nullptr); - ~S3Access(); + explicit ServerAccess(QObject *parent = nullptr); + ~ServerAccess(); - S3ListResult *list(QString const &prefix); - S3GetResult *get(QString const &key); - S3GetFileResult *getFile(QString const &key, QString const &filename); - S3Result *put(QString const &key, QString const &contentType, QByteArray const &data); - S3Result *putFile(QString const &key, QString const &contentType, QString const &filename); - S3Result *remove(QString const &key); + ServerListResult *list(QString const &prefix); + ServerGetResult *get(QString const &key); + ServerGetFileResult *getFile(QString const &key, QString const &filename); + ServerResult *put(QString const &key, QString const &contentType, QByteArray const &data); + ServerResult *putFile(QString const &key, QString const &contentType, QString const &filename); + ServerResult *remove(QString const &key); QString id() const; QString secret() const; @@ -127,7 +124,7 @@ public slots: DELETE }; void initialise(); - QNetworkReply *performOp(Method method, QString const &url, QString const &sign_data, const char *date, QIODevice *in, const char *content_md5, const char *content_type); + QNetworkReply *performOp(Method method, QString const &url, QIODevice *in, const char *content_type); private: QString m_id; @@ -135,8 +132,7 @@ public slots: QString m_baseUrl; QString m_bucket; - struct S3 *m_s3; QNetworkAccessManager *m_manager; }; -#endif // S3ACCESS_H +#endif // SERVERACCESS_H diff --git a/src/settings.cpp b/src/settings.cpp index d6d3777..fd21726 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -6,16 +6,18 @@ #include "settings.h" +#define SETTINGS_MAX_VERSION (1) + Settings * Settings::instance = nullptr; Settings::Settings(QObject *parent) : QObject(parent), - settings(this) + m_settings(this) { - m_downloadServer = settings.value(QStringLiteral("servers/downloadServer"), QStringLiteral("127.0.0.1:8003")).toString(); - m_uploadServer = settings.value(QStringLiteral("servers/uploadServer"), QStringLiteral("127.0.0.1:8000")).toString(); - m_verificationServer = settings.value(QStringLiteral("servers/verificationServer"), QStringLiteral("127.0.0.1:8004")).toString(); - m_latestSummary = settings.value(QStringLiteral("update/latestSummary"), QVariant::fromValue(ExposureSummary())).value(); - m_summaryUpdated = settings.value(QStringLiteral("update/date"), QDateTime()).toDateTime(); + m_downloadServer = m_settings.value(QStringLiteral("servers/downloadServer"), QStringLiteral("https://svc90.main.px.t-online.de")).toString(); + m_uploadServer = m_settings.value(QStringLiteral("servers/uploadServer"), QStringLiteral("https://submission.coronawarn.app")).toString(); + m_verificationServer = m_settings.value(QStringLiteral("servers/verificationServer"), QStringLiteral("https://verification.coronawarn.app")).toString(); + m_latestSummary = m_settings.value(QStringLiteral("update/latestSummary"), QVariant::fromValue(ExposureSummary())).value(); + m_summaryUpdated = m_settings.value(QStringLiteral("update/date"), QDateTime()).toDateTime(); // Figure out where we're going to find our images QScopedPointer ratioItem(new MGConfItem("/desktop/sailfish/silica/theme_pixel_ratio")); @@ -35,16 +37,18 @@ Settings::Settings(QObject *parent) : QObject(parent), m_imageDir = SailfishApp::pathTo("qml/images/z" + dir[pos]).toString(QUrl::RemoveScheme) + "/"; qDebug() << "Image folder: " << m_imageDir; - qDebug() << "Settings created: " << settings.fileName(); + qDebug() << "Settings created: " << m_settings.fileName(); + + upgrade(); } Settings::~Settings() { - settings.setValue(QStringLiteral("servers/downloadServer"), m_downloadServer); - settings.setValue(QStringLiteral("servers/uploadServer"), m_uploadServer); - settings.setValue(QStringLiteral("servers/verificationServer"), m_verificationServer); - settings.setValue(QStringLiteral("update/latestSummary"), QVariant::fromValue(m_latestSummary)); - settings.setValue(QStringLiteral("update/date"), m_summaryUpdated); + m_settings.setValue(QStringLiteral("servers/downloadServer"), m_downloadServer); + m_settings.setValue(QStringLiteral("servers/uploadServer"), m_uploadServer); + m_settings.setValue(QStringLiteral("servers/verificationServer"), m_verificationServer); + m_settings.setValue(QStringLiteral("update/latestSummary"), QVariant::fromValue(m_latestSummary)); + m_settings.setValue(QStringLiteral("update/date"), m_summaryUpdated); qDebug() << "Deleted settings"; } @@ -139,3 +143,59 @@ QString Settings::getImageUrl(QString const &id) const { return m_imageDir + id + ".png"; } +bool Settings::upgrade() +{ + quint32 version; + bool success = true; + + if (m_settings.allKeys().size() == 0) { + version = SETTINGS_MAX_VERSION; + qDebug() << "Creating new settings file with version: " << SETTINGS_MAX_VERSION; + } + else { + version = m_settings.value(QStringLiteral("application/settingsVersion"), 0).toUInt(); + qDebug() << "Existing settings file version: " << version; + } + + switch (version) { + case 0: + if (success) { + success = upgradeToVersion1(); + } + // Fallthrough + [[clang::fallthrough]]; + default: + case SETTINGS_MAX_VERSION: + // File upgraded + // Do nothing + break; + } + + if (success) { + m_settings.setValue(QStringLiteral("application/version"), VERSION); + m_settings.setValue(QStringLiteral("application/settingsVersion"), SETTINGS_MAX_VERSION); + } + + return success; +} + +bool Settings::upgradeToVersion1() +{ + bool success = true; + + qDebug() << "Upgrading settings to version 1"; + + // Update from the test servers to the real Corona-Warn-App servers + m_downloadServer = QStringLiteral("https://svc90.main.px.t-online.de"); + m_uploadServer = QStringLiteral("https://submission.coronawarn.app"); + m_verificationServer = QStringLiteral("https://verification.coronawarn.app"); + + m_settings.setValue(QStringLiteral("servers/downloadServer"), m_downloadServer); + m_settings.setValue(QStringLiteral("servers/uploadServer"), m_uploadServer); + m_settings.setValue(QStringLiteral("servers/verificationServer"), m_verificationServer); + + m_settings.sync(); + success = (m_settings.status() == QSettings::NoError); + + return success; +} diff --git a/src/settings.h b/src/settings.h index b1914bf..3b01723 100644 --- a/src/settings.h +++ b/src/settings.h @@ -49,9 +49,13 @@ public slots: void setLatestSummary(ExposureSummary const *latestSummary); void setSummaryUpdated(QDateTime summaryUpdated); +private: + bool upgrade(); + bool upgradeToVersion1(); + private: static Settings * instance; - QSettings settings; + QSettings m_settings; QString m_downloadServer; QString m_uploadServer; diff --git a/src/upload.cpp b/src/upload.cpp index adab807..f01587b 100644 --- a/src/upload.cpp +++ b/src/upload.cpp @@ -46,7 +46,7 @@ void Upload::submitTeleTAN(QString const &teleTAN) if ((m_status == StatusSubmitTeleTAN) && (m_reply == nullptr)) { QNetworkRequest request; - request.setUrl(QUrl("http://" + Settings::getInstance().verificationServer() + "/version/v1/registrationToken")); + request.setUrl(QUrl(Settings::getInstance().verificationServer() + "/version/v1/registrationToken")); request.setRawHeader("accept", "*/*"); request.setRawHeader("Content-Type", "application/json"); request.setRawHeader("cwa-fake", "0"); @@ -114,7 +114,7 @@ void Upload::submitRegToken(QString const ®Token) if ((m_status == StatusSubmitRegToken) && (m_reply == nullptr)) { QNetworkRequest request; - request.setUrl(QUrl("http://" + Settings::getInstance().verificationServer() + "/version/v1/tan")); + request.setUrl(QUrl(Settings::getInstance().verificationServer() + "/version/v1/tan")); request.setRawHeader("accept", "*/*"); request.setRawHeader("Content-Type", "application/json"); request.setRawHeader("cwa-fake", "0"); @@ -212,7 +212,7 @@ void Upload::submitDiagnosisKeys(QString const &tan) QByteArray data = QByteArray::fromStdString(payload.SerializeAsString()); qDebug() << "Upload size: " << data.length(); - request.setUrl(QUrl("http://" + Settings::getInstance().uploadServer() + "/version/v1/diagnosis-keys")); + request.setUrl(QUrl(Settings::getInstance().uploadServer() + "/version/v1/diagnosis-keys")); request.setRawHeader("accept", "*/*"); request.setRawHeader("Content-Type", "application/x-protobuf"); request.setRawHeader("cwa-authorization", tan.toLatin1()); @@ -403,3 +403,17 @@ bool Upload::validateTeleTANCharacters(QString const &teleTAN) const return result; } + +Q_INVOKABLE void Upload::clearError() +{ + if (m_error != ErrorNone) { + qDebug() << "Clearing upload error status"; + m_error = ErrorNone; + + if (m_status == StatusError) { + m_status = StatusIdle; + emit statusChanged(); + } + emit errorChanged(); + } +} diff --git a/src/upload.h b/src/upload.h index a81e10f..ce37b59 100644 --- a/src/upload.h +++ b/src/upload.h @@ -49,6 +49,7 @@ class Upload : public QObject QDate latest() const; Status status() const; ErrorType error() const; + Q_INVOKABLE void clearError(); signals: void uploadComplete(); diff --git a/translations/harbour-contrac-en.ts b/translations/harbour-contrac-en.ts index fcf9f42..66c32bc 100644 --- a/translations/harbour-contrac-en.ts +++ b/translations/harbour-contrac-en.ts @@ -78,11 +78,6 @@ Verification server - - Using the Google/Apple API and a Corona Warn App test server. Uploads/downloads are only for testing. - Using the Google/Apple API and a Corona Warn App test server. Uploads/downloads are only for testing - - TAN entry diff --git a/translations/harbour-contrac.ts b/translations/harbour-contrac.ts index fcf9f42..66c32bc 100644 --- a/translations/harbour-contrac.ts +++ b/translations/harbour-contrac.ts @@ -78,11 +78,6 @@ Verification server - - Using the Google/Apple API and a Corona Warn App test server. Uploads/downloads are only for testing. - Using the Google/Apple API and a Corona Warn App test server. Uploads/downloads are only for testing - - TAN entry