diff mbox series

[07/17] channel_hawkbit: move to corelib for reusing

Message ID 1511176210-28928-7-git-send-email-sbabic@denx.de
State Accepted
Headers show
Series [01/17] parser: added function to get net child in tree | expand

Commit Message

Stefano Babic Nov. 20, 2017, 11:10 a.m. UTC
Open a channel via curl can be reused outside the Hawkbit server.
Rename the file dropping the hawkbit suffix and move the code outside
of suricatta.

The code can be generalised to be used by any code that
uses libcurl and JSON to communicate with a server.

Signed-off-by: Stefano Babic <sbabic@denx.de>
---
 corelib/Makefile            |   1 +
 corelib/channel_curl.c      | 941 ++++++++++++++++++++++++++++++++++++++++++++
 include/channel_curl.h      |  63 +++
 suricatta/Makefile          |   2 +-
 suricatta/channel_hawkbit.c | 941 --------------------------------------------
 suricatta/channel_hawkbit.h |  63 ---
 suricatta/server_hawkbit.c  |   2 +-
 7 files changed, 1007 insertions(+), 1006 deletions(-)
 create mode 100644 corelib/channel_curl.c
 create mode 100644 include/channel_curl.h
 delete mode 100644 suricatta/channel_hawkbit.c
 delete mode 100644 suricatta/channel_hawkbit.h
diff mbox series

Patch

diff --git a/corelib/Makefile b/corelib/Makefile
index 16767b2..693ad47 100644
--- a/corelib/Makefile
+++ b/corelib/Makefile
@@ -13,3 +13,4 @@  lib-$(CONFIG_ENCRYPTED_IMAGES)	+= swupdate_decrypt.o
 lib-$(CONFIG_LIBCONFIG)		+= swupdate_settings.o \
 				   parsing_library_libconfig.o
 lib-$(CONFIG_JSON)		+= parsing_library_libjson.o
+lib-$(CONFIG_CURL)		+= channel_curl.o
diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
new file mode 100644
index 0000000..6c16592
--- /dev/null
+++ b/corelib/channel_curl.c
@@ -0,0 +1,941 @@ 
+/*
+ * Author: Christian Storm
+ * Copyright (C) 2016, Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <curl/curl.h>
+#include <json-c/json.h>
+#include <generated/autoconf.h>
+#include <unistd.h>
+#include <network_ipc.h>
+#include <util.h>
+#ifdef CONFIG_SURICATTA_SSL
+#include <openssl/sha.h>
+#endif
+#include "suricatta/channel.h"
+#include "channel_curl.h"
+#include "suricatta/suricatta.h"
+
+#define SPEED_LOW_BYTES_SEC 8
+#define SPEED_LOW_TIME_SEC 300
+#define KEEPALIVE_DELAY 204L
+#define KEEPALIVE_INTERVAL 120L
+
+typedef struct {
+	char *memory;
+	size_t size;
+} output_data_t;
+
+typedef struct {
+	char *proxy;
+	char *effective_url;
+	CURL *handle;
+	struct curl_slist *header;
+} channel_curl_t;
+
+typedef struct {
+	channel_data_t *channel_data;
+	int output;
+	output_data_t *outdata;
+} write_callback_t;
+
+#ifdef CONFIG_SURICATTA_SSL
+static SHA_CTX checksum_ctx;
+#endif
+
+/* Prototypes for "internal" functions */
+/* Note that they're not `static` so that they're callable from unit tests. */
+size_t channel_callback_write_file(void *streamdata, size_t size, size_t nmemb,
+				   write_callback_t *data);
+size_t channel_callback_membuffer(void *streamdata, size_t size, size_t nmemb,
+				   write_callback_t *data);
+channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code);
+channel_op_res_t channel_map_curl_error(CURLcode res);
+channel_op_res_t channel_set_options(channel_t *this, channel_data_t *channel_data,
+				     channel_method_t method);
+static void channel_log_effective_url(channel_t *this);
+
+/* Prototypes for "public" functions */
+channel_op_res_t channel_hawkbit_init(void);
+channel_op_res_t channel_close(channel_t *this);
+channel_op_res_t channel_open(channel_t *this, void *cfg);
+channel_op_res_t channel_get(channel_t *this, void *data);
+channel_op_res_t channel_get_file(channel_t *this, void *data, int file_handle);
+channel_op_res_t channel_put(channel_t *this, void *data);
+channel_t *channel_new(void);
+
+
+channel_op_res_t channel_hawkbit_init(void)
+{
+#ifdef CONFIG_SURICATTA_SSL
+#define CURL_FLAGS CURL_GLOBAL_SSL
+#else
+#define CURL_FLAGS CURL_GLOBAL_NOTHING
+#endif
+	CURLcode curlrc = curl_global_init(CURL_FLAGS);
+	if (curlrc != CURLE_OK) {
+		ERROR("Initialization of channel failed (%d): '%s'\n", curlrc,
+		      curl_easy_strerror(curlrc));
+		return CHANNEL_EINIT;
+	}
+#undef CURL_FLAGS
+	return CHANNEL_OK;
+}
+
+channel_t *channel_new(void)
+{
+	channel_t *newchan = (channel_t *)calloc(1, sizeof(*newchan) +
+				sizeof(channel_curl_t));
+
+	if (newchan) {
+		newchan->priv = (void *)newchan +  sizeof(*newchan);
+		newchan->open = &channel_open;
+		newchan->close = &channel_close;
+		newchan->get = &channel_get;
+		newchan->get_file = &channel_get_file;
+		newchan->put = &channel_put;
+	}
+
+	return newchan;
+}
+
+channel_op_res_t channel_close(channel_t *this)
+{
+	channel_curl_t *channel_curl = this->priv;
+
+	if ((channel_curl->proxy != NULL) &&
+	    (channel_curl->proxy != USE_PROXY_ENV)) {
+		free(channel_curl->proxy);
+	}
+	if (channel_curl->handle == NULL) {
+		return CHANNEL_OK;
+	}
+	curl_easy_cleanup(channel_curl->handle);
+	channel_curl->handle = NULL;
+
+	return CHANNEL_OK;
+}
+
+channel_op_res_t channel_open(channel_t *this, void *cfg)
+{
+	assert(this != NULL);
+	channel_curl_t *channel_curl = this->priv;
+	assert(channel_curl->handle == NULL);
+
+	channel_data_t *channel_cfg = (channel_data_t *)cfg;
+
+	if ((channel_cfg != NULL) && (channel_cfg->proxy != NULL)) {
+		channel_curl->proxy = channel_cfg->proxy == USE_PROXY_ENV
+					 ? USE_PROXY_ENV
+					 : strdup(channel_cfg->proxy);
+		if (channel_curl->proxy == NULL) {
+			return CHANNEL_EINIT;
+		}
+	}
+
+	if ((channel_curl->handle = curl_easy_init()) == NULL) {
+		ERROR("Initialization of channel failed.\n");
+		return CHANNEL_EINIT;
+	}
+
+	return CHANNEL_OK;
+}
+
+static channel_op_res_t result_channel_callback_write_file;
+size_t channel_callback_write_file(void *streamdata, size_t size, size_t nmemb,
+				   write_callback_t *data)
+{
+	if (!nmemb) {
+		return 0;
+	}
+	if (!data)
+		return 0;
+	result_channel_callback_write_file = CHANNEL_OK;
+#ifdef CONFIG_SURICATTA_SSL
+	if (SHA1_Update(&checksum_ctx, streamdata, size * nmemb) != 1) {
+		ERROR("Updating checksum of chunk failed.\n");
+		result_channel_callback_write_file = CHANNEL_EIO;
+		return 0;
+	}
+#endif
+	if (ipc_send_data(data->output, streamdata, (int)(size * nmemb)) <
+	    0) {
+		ERROR("Writing into SWUpdate IPC stream failed.\n");
+		result_channel_callback_write_file = CHANNEL_EIO;
+		return 0;
+	}
+
+	if (data->channel_data->checkdwl && data->channel_data->checkdwl())
+		return 0;
+	/*
+	 * Now check if there is a callback from the server
+	 * during the download
+	 */
+
+	return size * nmemb;
+}
+
+size_t channel_callback_membuffer(void *streamdata, size_t size, size_t nmemb,
+				  write_callback_t *data)
+{
+	if (!nmemb) {
+		return 0;
+	}
+	if (!data) {
+		return 0;
+	}
+
+	size_t realsize = size * nmemb;
+	output_data_t *mem = data->outdata;
+
+	mem->memory = realloc(mem->memory, mem->size + realsize + 1);
+	if (mem->memory == NULL) {
+		ERROR("Channel get operation failed with OOM\n");
+		return 0;
+	}
+	memcpy(&(mem->memory[mem->size]), streamdata, realsize);
+	mem->size += realsize;
+	mem->memory[mem->size] = 0;
+	return realsize;
+}
+
+static void channel_log_effective_url(channel_t *this)
+{
+	channel_curl_t *channel_curl = this->priv;
+
+	CURLcode curlrc =
+	    curl_easy_getinfo(channel_curl->handle, CURLINFO_EFFECTIVE_URL,
+			      &channel_curl->effective_url);
+	if (curlrc != CURLE_OK && curlrc == CURLE_UNKNOWN_OPTION) {
+		ERROR("Get channel's effective URL response unsupported by "
+		      "libcURL %s.\n",
+		      LIBCURL_VERSION);
+		return;
+	}
+	TRACE("Channel's effective URL resolved to %s\n",
+	      channel_curl->effective_url);
+}
+
+channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code)
+{
+	channel_curl_t *channel_curl = this->priv;
+	CURLcode curlrc =
+	    curl_easy_getinfo(channel_curl->handle, CURLINFO_RESPONSE_CODE,
+			      http_response_code);
+	if (curlrc != CURLE_OK && curlrc == CURLE_UNKNOWN_OPTION) {
+		ERROR("Get channel HTTP response code unsupported by libcURL "
+		      "%s.\n",
+		      LIBCURL_VERSION);
+		return CHANNEL_EINIT;
+	}
+	switch (*http_response_code) {
+	case 0:   /* libcURL: no server response code has been received yet */
+		DEBUG("No HTTP response code has been received yet!\n");
+		return CHANNEL_EBADMSG;
+	case 401: /* Unauthorized. The request requires user authentication. */
+	case 403: /* Forbidden. */
+	case 405: /* Method not Allowed. */
+	case 407: /* Proxy Authentication Required */
+	case 503: /* Service Unavailable */
+		return CHANNEL_EACCES;
+	case 400: /* Bad Request, e.g., invalid parameters */
+	case 404: /* Wrong URL */
+	case 406: /* Not acceptable. Accept header is not response compliant */
+	case 443: /* Connection refused */
+		return CHANNEL_EBADMSG;
+	case 429: /* Bad Request, i.e., too many requests. Try again later. */
+		return CHANNEL_EAGAIN;
+	case 200:
+	case 206:
+		return CHANNEL_OK;
+	case 500:
+		return CHANNEL_EBADMSG;
+	default:
+		ERROR("Channel operation returned unhandled HTTP error code "
+		      "%ld\n",
+		      *http_response_code);
+		return CHANNEL_EBADMSG;
+	}
+}
+
+channel_op_res_t channel_map_curl_error(CURLcode res)
+{
+	switch (res) {
+	case CURLE_NOT_BUILT_IN:
+	case CURLE_BAD_FUNCTION_ARGUMENT:
+	case CURLE_UNKNOWN_OPTION:
+	case CURLE_SSL_ENGINE_NOTFOUND:
+	case CURLE_SSL_ENGINE_SETFAILED:
+	case CURLE_SSL_CERTPROBLEM:
+	case CURLE_SSL_CIPHER:
+	case CURLE_SSL_ENGINE_INITFAILED:
+	case CURLE_SSL_CACERT_BADFILE:
+	case CURLE_SSL_CRL_BADFILE:
+	case CURLE_SSL_ISSUER_ERROR:
+#if LIBCURL_VERSION_NUM >= 0x072900
+	case CURLE_SSL_INVALIDCERTSTATUS:
+#endif
+#if LIBCURL_VERSION_NUM >= 0x072700
+	case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+#endif
+		return CHANNEL_EINIT;
+	case CURLE_COULDNT_RESOLVE_PROXY:
+	case CURLE_COULDNT_RESOLVE_HOST:
+	case CURLE_COULDNT_CONNECT:
+	case CURLE_INTERFACE_FAILED:
+	case CURLE_SSL_CONNECT_ERROR:
+	case CURLE_PEER_FAILED_VERIFICATION:
+	case CURLE_SSL_CACERT:
+	case CURLE_USE_SSL_FAILED:
+		return CHANNEL_ENONET;
+	case CURLE_OPERATION_TIMEDOUT:
+	case CURLE_SEND_ERROR:
+	case CURLE_RECV_ERROR:
+	case CURLE_GOT_NOTHING:
+	case CURLE_HTTP_POST_ERROR:
+	case CURLE_PARTIAL_FILE:
+		return CHANNEL_EAGAIN;
+	case CURLE_OUT_OF_MEMORY:
+		return CHANNEL_ENOMEM;
+	case CURLE_REMOTE_FILE_NOT_FOUND:
+		return CHANNEL_ENOENT;
+	case CURLE_FILESIZE_EXCEEDED:
+	case CURLE_ABORTED_BY_CALLBACK:
+	case CURLE_WRITE_ERROR:
+	case CURLE_CHUNK_FAILED:
+	case CURLE_SSL_SHUTDOWN_FAILED:
+		return CHANNEL_EIO;
+	case CURLE_TOO_MANY_REDIRECTS:
+		return CHANNEL_ELOOP;
+	case CURLE_BAD_CONTENT_ENCODING:
+	case CURLE_CONV_FAILED:
+	case CURLE_CONV_REQD:
+		return CHANNEL_EILSEQ;
+	case CURLE_REMOTE_ACCESS_DENIED:
+	case CURLE_LOGIN_DENIED:
+		return CHANNEL_EACCES;
+	case CURLE_OK:
+		return CHANNEL_OK;
+	default:
+		return CHANNEL_EINIT;
+	}
+}
+
+channel_op_res_t channel_set_options(channel_t *this,
+					channel_data_t *channel_data,
+					channel_method_t method)
+{
+	channel_curl_t *channel_curl = this->priv;
+	channel_op_res_t result = CHANNEL_OK;
+	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL,
+			      channel_data->url) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT,
+			      "libcurl-agent/1.0") != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_LOW_SPEED_LIMIT,
+			      SPEED_LOW_BYTES_SEC) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_LOW_SPEED_TIME,
+			      SPEED_LOW_TIME_SEC) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_HTTPHEADER,
+			      channel_curl->header) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_MAXREDIRS, -1) !=
+	     CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_FOLLOWLOCATION, 1) !=
+	     CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_REDIR_PROTOCOLS,
+			      CURLPROTO_HTTP | CURLPROTO_HTTPS) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle,
+			      CURLOPT_CAINFO,
+			      channel_data->cafile) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle,
+			      CURLOPT_SSLKEY,
+			      channel_data->sslkey) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle,
+			      CURLOPT_SSLCERT,
+			      channel_data->sslcert) != CURLE_OK)) {
+		result = CHANNEL_EINIT;
+		goto cleanup;
+	}
+
+	if (channel_data->strictssl == true) {
+		if ((curl_easy_setopt(channel_curl->handle,
+				      CURLOPT_SSL_VERIFYHOST,
+				      2L) != CURLE_OK) ||
+		    (curl_easy_setopt(channel_curl->handle,
+				      CURLOPT_SSL_VERIFYPEER,
+				      1L) != CURLE_OK)) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+	}
+	else {
+		if ((curl_easy_setopt(channel_curl->handle,
+				      CURLOPT_SSL_VERIFYHOST,
+				      0L) != CURLE_OK) ||
+		    (curl_easy_setopt(channel_curl->handle,
+				      CURLOPT_SSL_VERIFYPEER,
+				      0L) != CURLE_OK)) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+	}
+
+	switch (method) {
+	case CHANNEL_GET:
+		if (curl_easy_setopt(channel_curl->handle, CURLOPT_CUSTOMREQUEST,
+				     "GET") != CURLE_OK) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+		break;
+	case CHANNEL_PUT:
+		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_PUT, 1L) !=
+		     CURLE_OK) ||
+		     (curl_easy_setopt(channel_curl->handle, CURLOPT_UPLOAD, 1L) !=
+		      CURLE_OK)) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+		break;
+	case CHANNEL_POST:
+		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_POST, 1L) !=
+		     CURLE_OK) ||
+		    (curl_easy_setopt(channel_curl->handle, CURLOPT_POSTFIELDS,
+				      channel_data->json_string) != CURLE_OK)) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+		if (channel_data->debug) {
+			TRACE("Post JSON: %s\n", channel_data->json_string);
+		}
+		break;
+	}
+
+	if (channel_curl->proxy != NULL) {
+		if (channel_curl->proxy != USE_PROXY_ENV) {
+			if (curl_easy_setopt(
+				channel_curl->handle, CURLOPT_PROXY,
+				channel_curl->proxy) != CURLE_OK) {
+				result = CHANNEL_EINIT;
+				goto cleanup;
+			}
+		}
+		if (curl_easy_setopt(channel_curl->handle, CURLOPT_NETRC,
+				     CURL_NETRC_OPTIONAL) != CURLE_OK) {
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+	}
+
+	CURLcode curlrc =
+	    curl_easy_setopt(channel_curl->handle, CURLOPT_TCP_KEEPALIVE, 1L);
+	if (curlrc == CURLE_OK) {
+		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_TCP_KEEPIDLE,
+				      KEEPALIVE_DELAY) != CURLE_OK) ||
+		    (curl_easy_setopt(channel_curl->handle,
+				      CURLOPT_TCP_KEEPINTVL,
+				      KEEPALIVE_INTERVAL) != CURLE_OK)) {
+			ERROR("TCP Keep-alive interval and delay could not be "
+			      "configured.\n");
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+	} else {
+		if (curlrc != CURLE_UNKNOWN_OPTION) {
+			ERROR("Channel could not be configured to sent "
+			      "keep-alive probes.\n");
+			result = CHANNEL_EINIT;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	return result;
+}
+
+static size_t put_read_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+	channel_data_t *channel_data = (channel_data_t *)data;
+	unsigned int bytes;
+	size_t n;
+
+	/* Check data to be sent */
+	bytes = strlen(channel_data->json_string) - channel_data->offs;
+
+	if (!bytes)
+		return 0;
+
+	n = min(bytes, size * nmemb);
+
+	memcpy(ptr, &channel_data->json_string[channel_data->offs], n);
+	channel_data->offs += n;
+
+	return n;
+}
+
+static channel_op_res_t channel_post_method(channel_t *this, void *data)
+{
+	channel_curl_t *channel_curl = this->priv;
+	assert(data != NULL);
+	assert(channel_curl->handle != NULL);
+
+	channel_op_res_t result = CHANNEL_OK;
+	channel_data_t *channel_data = (channel_data_t *)data;
+
+	if (channel_data->debug) {
+		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
+	}
+
+	if (((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Content-Type: application/json")) ==
+	     NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Accept: application/json")) == NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "charsets: utf-8")) == NULL)) {
+		ERROR("Set channel header failed.\n");
+		result = CHANNEL_EINIT;
+		goto cleanup_header;
+	}
+
+	if ((result = channel_set_options(this, channel_data, CHANNEL_POST)) !=
+	    CHANNEL_OK) {
+		ERROR("Set channel option failed.\n");
+		goto cleanup_header;
+	}
+
+	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
+	if (curlrc != CURLE_OK) {
+		ERROR("Channel put operation failed (%d): '%s'\n", curlrc,
+		      curl_easy_strerror(curlrc));
+		result = channel_map_curl_error(curlrc);
+		goto cleanup_header;
+	}
+
+	channel_log_effective_url(this);
+
+	long http_response_code;
+	if ((result = channel_map_http_code(this, &http_response_code)) !=
+	    CHANNEL_OK) {
+		ERROR("Channel operation returned HTTP error code %ld.\n",
+		      http_response_code);
+		goto cleanup_header;
+	}
+	TRACE("Channel put operation returned HTTP status code %ld.\n",
+	      http_response_code);
+
+cleanup_header:
+	curl_easy_reset(channel_curl->handle);
+	curl_slist_free_all(channel_curl->header);
+	channel_curl->header = NULL;
+
+	return result;
+}
+
+static channel_op_res_t channel_put_method(channel_t *this, void *data)
+{
+	channel_curl_t *channel_curl = this->priv;
+	assert(data != NULL);
+	assert(channel_curl->handle != NULL);
+
+	channel_op_res_t result = CHANNEL_OK;
+	channel_data_t *channel_data = (channel_data_t *)data;
+	channel_data->offs = 0;
+
+	if (channel_data->debug) {
+		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
+	}
+
+	if (((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Content-Type: application/json")) ==
+	     NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Accept: application/json")) == NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "charsets: utf-8")) == NULL)) {
+		ERROR("Set channel header failed.\n");
+		result = CHANNEL_EINIT;
+		goto cleanup_header;
+	}
+
+	if ((result = channel_set_options(this, channel_data, CHANNEL_PUT)) !=
+	    CHANNEL_OK) {
+		ERROR("Set channel option failed.\n");
+		goto cleanup_header;
+	}
+
+	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_READFUNCTION, put_read_callback) !=
+		CURLE_OK) ||
+	   (curl_easy_setopt(channel_curl->handle, CURLOPT_INFILESIZE_LARGE,
+			     (curl_off_t)strlen(channel_data->json_string)) != CURLE_OK) ||
+	   (curl_easy_setopt(channel_curl->handle, CURLOPT_READDATA, channel_data) !=
+			CURLE_OK)) {
+		ERROR("Set channel option failed.\n");
+		goto cleanup_header;
+	}
+
+	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
+	if (curlrc != CURLE_OK) {
+		ERROR("Channel put operation failed (%d): '%s'\n", curlrc,
+		      curl_easy_strerror(curlrc));
+		result = channel_map_curl_error(curlrc);
+		goto cleanup_header;
+	}
+
+	channel_log_effective_url(this);
+
+	long http_response_code;
+	if ((result = channel_map_http_code(this, &http_response_code)) != CHANNEL_OK) {
+		ERROR("Channel operation returned HTTP error code %ld.\n",
+		      http_response_code);
+		goto cleanup_header;
+	}
+	TRACE("Channel put operation returned HTTP error code %ld.\n",
+	      http_response_code);
+
+cleanup_header:
+	curl_easy_reset(channel_curl->handle);
+	curl_slist_free_all(channel_curl->header);
+	channel_curl->header = NULL;
+
+	return result;
+}
+
+channel_op_res_t channel_put(channel_t *this, void *data)
+{
+	assert(data != NULL);
+
+	channel_data_t *channel_data = (channel_data_t *)data;
+
+	switch (channel_data->method) {
+	case CHANNEL_PUT:
+		return channel_put_method(this, data);
+	case CHANNEL_POST:
+		return channel_post_method(this, data);
+	default:
+		TRACE("Channel method (POST, PUT) is not set !\n");
+		return CHANNEL_EINIT;
+	}
+}
+
+channel_op_res_t channel_get_file(channel_t *this, void *data, int file_handle)
+{
+	channel_curl_t *channel_curl = this->priv;
+	assert(data != NULL);
+	assert(channel_curl->handle != NULL);
+
+	channel_op_res_t result = CHANNEL_OK;
+	channel_data_t *channel_data = (channel_data_t *)data;
+
+#ifdef CONFIG_SURICATTA_SSL
+	memset(channel_data->sha1hash, 0x0, SHA_DIGEST_LENGTH * 2 + 1);
+	if (SHA1_Init(&checksum_ctx) != 1) {
+		result = CHANNEL_EINIT;
+		ERROR("Cannot initialize sha1 checksum context.\n");
+		goto cleanup;
+	}
+#endif
+
+	if (channel_data->debug) {
+		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
+	}
+
+	if (((channel_curl->header = curl_slist_append(
+		  channel_curl->header,
+		  "Content-Type: application/octet-stream")) == NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Accept: application/octet-stream")) ==
+	     NULL)) {
+		result = CHANNEL_EINIT;
+		ERROR("Set channel header failed.\n");
+		goto cleanup_header;
+	}
+
+	if ((result = channel_set_options(this, channel_data, CHANNEL_GET)) !=
+	    CHANNEL_OK) {
+		ERROR("Set channel option failed.\n");
+		goto cleanup_header;
+	}
+
+	if (file_handle == FD_USE_IPC) {
+		for (int retries = 3; retries >= 0; retries--) {
+			file_handle = ipc_inst_start_ext(SOURCE_SURICATTA,
+				channel_data->info == NULL ? 0 : strlen(channel_data->info),
+				channel_data->info);
+			if (file_handle > 0)
+				break;
+			sleep(1);
+		}
+		if (file_handle < 0) {
+			ERROR("Cannot open SWUpdate IPC stream: %s\n", strerror(errno));
+			result = CHANNEL_EIO;
+			goto cleanup_header;
+		}
+	} else {
+		assert(file_handle > 0);
+	}
+
+	write_callback_t wrdata;
+	wrdata.channel_data = channel_data;
+	wrdata.output = file_handle;
+	result_channel_callback_write_file = CHANNEL_OK;
+	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEFUNCTION,
+			      channel_callback_write_file) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEDATA,
+			      &wrdata) != CURLE_OK)) {
+		ERROR("Cannot setup file writer callback function.\n");
+		result = CHANNEL_EINIT;
+		goto cleanup_file;
+	}
+
+	/* Depending on the architecture and libcurl, this is at
+	 * least 2^32 - 1 bytes, i.e., enough to account for a 4GiB
+	 * download. Although 32-Bit curl defines `curl_off_t` as
+	 * (signed) long long, use unsigned here, but it wastes some
+	 * bits on 64-Bit.
+	 */
+	unsigned long long int total_bytes_downloaded = 0;
+	unsigned char try_count = 0;
+	CURLcode curlrc = CURLE_OK;
+	do {
+		if (try_count > 0) {
+			if (channel_data->retries == 0) {
+				ERROR(
+				    "Channel get operation failed (%d): '%s'\n",
+				    curlrc, curl_easy_strerror(curlrc));
+				result = channel_map_curl_error(curlrc);
+				goto cleanup_file;
+			}
+
+			if (try_count > channel_data->retries) {
+				ERROR("Channel get operation aborted because "
+				      "of too many failed download attempts "
+				      "(%d).\n",
+				      channel_data->retries);
+				result = CHANNEL_ELOOP;
+				goto cleanup_file;
+			}
+
+			DEBUG("Channel connection interrupted, trying resume "
+			      "after %llu bytes.",
+			      total_bytes_downloaded);
+			if (curl_easy_setopt(
+				channel_curl->handle, CURLOPT_RESUME_FROM_LARGE,
+				total_bytes_downloaded) != CURLE_OK) {
+				ERROR("Could not set Channel resume seek (%d): "
+				      "'%s'\n",
+				      curlrc, curl_easy_strerror(curlrc));
+				result = channel_map_curl_error(curlrc);
+				goto cleanup_file;
+			}
+			TRACE("Channel sleeps for %d seconds now.",
+			      channel_data->retry_sleep);
+			if (sleep(channel_data->retry_sleep) > 0) {
+				TRACE("Channel's sleep got interrupted, "
+				      "retrying nonetheless now.");
+			}
+			TRACE("Channel awakened from sleep.");
+		}
+
+		curlrc = curl_easy_perform(channel_curl->handle);
+		result = channel_map_curl_error(curlrc);
+		if ((result != CHANNEL_OK) && (result != CHANNEL_EAGAIN)) {
+			ERROR("Channel operation returned error (%d): '%s'\n",
+			      curlrc, curl_easy_strerror(curlrc));
+			goto cleanup_file;
+		}
+
+		double bytes_downloaded;
+		CURLcode resdlprogress = curl_easy_getinfo(
+		    channel_curl->handle, CURLINFO_SIZE_DOWNLOAD,
+		    &bytes_downloaded);
+		if (resdlprogress != CURLE_OK) {
+			ERROR("Channel does not report bytes downloaded (%d): "
+			      "'%s'\n",
+			      resdlprogress, curl_easy_strerror(resdlprogress));
+			result = channel_map_curl_error(resdlprogress);
+			goto cleanup_file;
+		}
+		total_bytes_downloaded += bytes_downloaded;
+
+	} while (++try_count && (result != CHANNEL_OK));
+
+	channel_log_effective_url(this);
+
+	DEBUG("Channel downloaded %llu bytes ~ %llu MiB.\n",
+	      total_bytes_downloaded, total_bytes_downloaded / 1024 / 1024);
+
+	long http_response_code;
+	if ((result = channel_map_http_code(this, &http_response_code)) !=
+	    CHANNEL_OK) {
+		ERROR("Channel operation returned HTTP error code %ld.\n",
+		      http_response_code);
+		goto cleanup_file;
+	}
+	TRACE("Channel operation returned HTTP status code %ld.\n",
+	      http_response_code);
+
+	if (result_channel_callback_write_file != CHANNEL_OK) {
+		result = CHANNEL_EIO;
+		goto cleanup_file;
+	}
+
+#ifdef CONFIG_SURICATTA_SSL
+	unsigned char sha1hash[SHA_DIGEST_LENGTH];
+	if (SHA1_Final(sha1hash, &checksum_ctx) != 1) {
+		ERROR("Cannot compute checksum.\n");
+		goto cleanup_file;
+	}
+	char sha1hexchar[3];
+	for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
+		sprintf(sha1hexchar, "%02x", sha1hash[i]);
+		strcat(channel_data->sha1hash, sha1hexchar);
+	}
+#endif
+
+cleanup_file:
+	/* NOTE ipc_end() calls close() but does not return its error code,
+	 *      so use close() here directly to issue an error in case.
+	 *      Also, for a given file handle, calling ipc_end() would make
+	 *      no semantic sense. */
+	if (close(file_handle) != 0) {
+		ERROR("Channel error while closing download target handle: '%s'\n",
+		      strerror(errno));
+	}
+cleanup_header:
+	curl_easy_reset(channel_curl->handle);
+	curl_slist_free_all(channel_curl->header);
+	channel_curl->header = NULL;
+
+#ifdef CONFIG_SURICATTA_SSL
+cleanup:
+#endif
+	return result;
+}
+
+channel_op_res_t channel_get(channel_t *this, void *data)
+{
+	channel_curl_t *channel_curl = this->priv;
+	assert(data != NULL);
+	assert(channel_curl.handle != NULL);
+
+	channel_op_res_t result = CHANNEL_OK;
+	channel_data_t *channel_data = (channel_data_t *)data;
+
+	if (channel_data->debug) {
+		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
+	}
+
+	if (((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Content-Type: application/json")) ==
+	     NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "Accept: application/json")) == NULL) ||
+	    ((channel_curl->header = curl_slist_append(
+		  channel_curl->header, "charsets: utf-8")) == NULL)) {
+		result = CHANNEL_EINIT;
+		ERROR("Set channel header failed.\n");
+		goto cleanup_header;
+	}
+
+	if ((result = channel_set_options(this, channel_data, CHANNEL_GET)) !=
+	    CHANNEL_OK) {
+		ERROR("Set channel option failed.\n");
+		goto cleanup_header;
+	}
+
+	output_data_t chunk = {.memory = NULL, .size = 0};
+	if ((chunk.memory = malloc(1)) == NULL) {
+		ERROR("Channel buffer reservation failed with OOM.\n");
+		result = CHANNEL_ENOMEM;
+		goto cleanup_header;
+	}
+
+	write_callback_t wrdata;
+	wrdata.channel_data = channel_data;
+	wrdata.outdata = &chunk;
+
+	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEFUNCTION,
+			      channel_callback_membuffer) != CURLE_OK) ||
+	    (curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEDATA,
+			      (void *)&wrdata) != CURLE_OK)) {
+		ERROR("Cannot setup memory buffer writer callback function.\n");
+		result = CHANNEL_EINIT;
+		goto cleanup_chunk;
+	}
+
+	DEBUG("Trying to GET %s", channel_data->url);
+	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
+	if (curlrc != CURLE_OK) {
+		ERROR("Channel get operation failed (%d): '%s'\n", curlrc,
+		      curl_easy_strerror(curlrc));
+		result = channel_map_curl_error(curlrc);
+		goto cleanup_chunk;
+	}
+
+	channel_log_effective_url(this);
+
+	long http_response_code;
+	if ((result = channel_map_http_code(this, &http_response_code)) !=
+	    CHANNEL_OK) {
+		ERROR("Channel operation returned HTTP error code %ld.\n",
+		      http_response_code);
+		if (http_response_code == 500) {
+			DEBUG("The error's message is: '%s'\n", chunk.memory);
+		}
+		if (http_response_code == 404) {
+			DEBUG("The error's message is: '%s'\n", chunk.memory);
+		}
+		goto cleanup_chunk;
+	}
+	TRACE("Channel operation returned HTTP status code %ld.\n",
+	      http_response_code);
+
+	assert(channel_data->json_reply == NULL);
+	enum json_tokener_error json_res;
+	struct json_tokener *json_tokenizer = json_tokener_new();
+	do {
+		channel_data->json_reply = json_tokener_parse_ex(
+		    json_tokenizer, chunk.memory, (int)chunk.size);
+	} while ((json_res = json_tokener_get_error(json_tokenizer)) ==
+		 json_tokener_continue);
+	if (json_res != json_tokener_success) {
+		ERROR("Error while parsing channel's returned JSON data: %s\n",
+		      json_tokener_error_desc(json_res));
+		result = CHANNEL_EBADMSG;
+		goto cleanup_json_tokenizer;
+	}
+	if (channel_data->debug) {
+		TRACE("Get JSON: %s\n", chunk.memory);
+	}
+
+cleanup_json_tokenizer:
+	json_tokener_free(json_tokenizer);
+cleanup_chunk:
+	chunk.memory != NULL ? free(chunk.memory) : (void)0;
+cleanup_header:
+	curl_easy_reset(channel_curl->handle);
+	curl_slist_free_all(channel_curl->header);
+	channel_curl->header = NULL;
+
+	return result;
+}
diff --git a/include/channel_curl.h b/include/channel_curl.h
new file mode 100644
index 0000000..ed262fc
--- /dev/null
+++ b/include/channel_curl.h
@@ -0,0 +1,63 @@ 
+/*
+ * Author: Christian Storm
+ * Copyright (C) 2016, Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#pragma once
+#include <json-c/json.h>
+#include <stdio.h>
+#ifdef CONFIG_SURICATTA_SSL
+#include <openssl/sha.h>
+#endif
+
+/* hawkBit Channel Implementation Private Header File.
+ *
+ * This is a "private" header for testability, i.e., the declarations and
+ * definitions herein should be used by `server_hawkbit.c`, `channel_hawkbit.c`,
+ * and unit tests only.
+ */
+
+typedef enum {
+	CHANNEL_GET,
+	CHANNEL_POST,
+	CHANNEL_PUT,
+} channel_method_t;
+
+#define FD_USE_IPC -2
+
+#define USE_PROXY_ENV (char *)0x11
+
+typedef struct {
+	char *url;
+	char *json_string;
+	json_object *json_reply;
+	char *cafile;
+	char *sslkey;
+	char *sslcert;
+	char *proxy;
+	char *info;
+	unsigned int retry_sleep;
+	unsigned int offs;
+	unsigned int method;
+	unsigned int retries;
+	bool debug;
+	bool strictssl;
+	int (*checkdwl)(void);
+#ifdef CONFIG_SURICATTA_SSL
+	char sha1hash[SHA_DIGEST_LENGTH * 2 + 1];
+#endif
+} channel_data_t;
diff --git a/suricatta/Makefile b/suricatta/Makefile
index 72b3e76..fabe926 100644
--- a/suricatta/Makefile
+++ b/suricatta/Makefile
@@ -1,4 +1,4 @@ 
 lib-$(CONFIG_SURICATTA) += state.o suricatta.o
 ifneq ($(CONFIG_SURICATTA_HAWKBIT),)
-lib-$(CONFIG_SURICATTA) += channel_hawkbit.o server_hawkbit.o
+lib-$(CONFIG_SURICATTA) += server_hawkbit.o
 endif
diff --git a/suricatta/channel_hawkbit.c b/suricatta/channel_hawkbit.c
deleted file mode 100644
index f3d00a0..0000000
--- a/suricatta/channel_hawkbit.c
+++ /dev/null
@@ -1,941 +0,0 @@ 
-/*
- * Author: Christian Storm
- * Copyright (C) 2016, Siemens AG
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc.
- */
-
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <curl/curl.h>
-#include <json-c/json.h>
-#include <generated/autoconf.h>
-#include <unistd.h>
-#include <network_ipc.h>
-#include <util.h>
-#ifdef CONFIG_SURICATTA_SSL
-#include <openssl/sha.h>
-#endif
-#include "suricatta/channel.h"
-#include "channel_hawkbit.h"
-#include "suricatta/suricatta.h"
-
-#define SPEED_LOW_BYTES_SEC 8
-#define SPEED_LOW_TIME_SEC 300
-#define KEEPALIVE_DELAY 204L
-#define KEEPALIVE_INTERVAL 120L
-
-typedef struct {
-	char *memory;
-	size_t size;
-} output_data_t;
-
-typedef struct {
-	char *proxy;
-	char *effective_url;
-	CURL *handle;
-	struct curl_slist *header;
-} channel_curl_t;
-
-typedef struct {
-	channel_data_t *channel_data;
-	int output;
-	output_data_t *outdata;
-} write_callback_t;
-
-#ifdef CONFIG_SURICATTA_SSL
-static SHA_CTX checksum_ctx;
-#endif
-
-/* Prototypes for "internal" functions */
-/* Note that they're not `static` so that they're callable from unit tests. */
-size_t channel_callback_write_file(void *streamdata, size_t size, size_t nmemb,
-				   write_callback_t *data);
-size_t channel_callback_membuffer(void *streamdata, size_t size, size_t nmemb,
-				   write_callback_t *data);
-channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code);
-channel_op_res_t channel_map_curl_error(CURLcode res);
-channel_op_res_t channel_set_options(channel_t *this, channel_data_t *channel_data,
-				     channel_method_t method);
-static void channel_log_effective_url(channel_t *this);
-
-/* Prototypes for "public" functions */
-channel_op_res_t channel_hawkbit_init(void);
-channel_op_res_t channel_close(channel_t *this);
-channel_op_res_t channel_open(channel_t *this, void *cfg);
-channel_op_res_t channel_get(channel_t *this, void *data);
-channel_op_res_t channel_get_file(channel_t *this, void *data, int file_handle);
-channel_op_res_t channel_put(channel_t *this, void *data);
-channel_t *channel_new(void);
-
-
-channel_op_res_t channel_hawkbit_init(void)
-{
-#ifdef CONFIG_SURICATTA_SSL
-#define CURL_FLAGS CURL_GLOBAL_SSL
-#else
-#define CURL_FLAGS CURL_GLOBAL_NOTHING
-#endif
-	CURLcode curlrc = curl_global_init(CURL_FLAGS);
-	if (curlrc != CURLE_OK) {
-		ERROR("Initialization of channel failed (%d): '%s'\n", curlrc,
-		      curl_easy_strerror(curlrc));
-		return CHANNEL_EINIT;
-	}
-#undef CURL_FLAGS
-	return CHANNEL_OK;
-}
-
-channel_t *channel_new(void)
-{
-	channel_t *newchan = (channel_t *)calloc(1, sizeof(*newchan) +
-				sizeof(channel_curl_t));
-
-	if (newchan) {
-		newchan->priv = (void *)newchan +  sizeof(*newchan);
-		newchan->open = &channel_open;
-		newchan->close = &channel_close;
-		newchan->get = &channel_get;
-		newchan->get_file = &channel_get_file;
-		newchan->put = &channel_put;
-	}
-
-	return newchan;
-}
-
-channel_op_res_t channel_close(channel_t *this)
-{
-	channel_curl_t *channel_curl = this->priv;
-
-	if ((channel_curl->proxy != NULL) &&
-	    (channel_curl->proxy != USE_PROXY_ENV)) {
-		free(channel_curl->proxy);
-	}
-	if (channel_curl->handle == NULL) {
-		return CHANNEL_OK;
-	}
-	curl_easy_cleanup(channel_curl->handle);
-	channel_curl->handle = NULL;
-
-	return CHANNEL_OK;
-}
-
-channel_op_res_t channel_open(channel_t *this, void *cfg)
-{
-	assert(this != NULL);
-	channel_curl_t *channel_curl = this->priv;
-	assert(channel_curl->handle == NULL);
-
-	channel_data_t *channel_cfg = (channel_data_t *)cfg;
-
-	if ((channel_cfg != NULL) && (channel_cfg->proxy != NULL)) {
-		channel_curl->proxy = channel_cfg->proxy == USE_PROXY_ENV
-					 ? USE_PROXY_ENV
-					 : strdup(channel_cfg->proxy);
-		if (channel_curl->proxy == NULL) {
-			return CHANNEL_EINIT;
-		}
-	}
-
-	if ((channel_curl->handle = curl_easy_init()) == NULL) {
-		ERROR("Initialization of channel failed.\n");
-		return CHANNEL_EINIT;
-	}
-
-	return CHANNEL_OK;
-}
-
-static channel_op_res_t result_channel_callback_write_file;
-size_t channel_callback_write_file(void *streamdata, size_t size, size_t nmemb,
-				   write_callback_t *data)
-{
-	if (!nmemb) {
-		return 0;
-	}
-	if (!data)
-		return 0;
-	result_channel_callback_write_file = CHANNEL_OK;
-#ifdef CONFIG_SURICATTA_SSL
-	if (SHA1_Update(&checksum_ctx, streamdata, size * nmemb) != 1) {
-		ERROR("Updating checksum of chunk failed.\n");
-		result_channel_callback_write_file = CHANNEL_EIO;
-		return 0;
-	}
-#endif
-	if (ipc_send_data(data->output, streamdata, (int)(size * nmemb)) <
-	    0) {
-		ERROR("Writing into SWUpdate IPC stream failed.\n");
-		result_channel_callback_write_file = CHANNEL_EIO;
-		return 0;
-	}
-
-	if (data->channel_data->checkdwl && data->channel_data->checkdwl())
-		return 0;
-	/*
-	 * Now check if there is a callback from the server
-	 * during the download
-	 */
-
-	return size * nmemb;
-}
-
-size_t channel_callback_membuffer(void *streamdata, size_t size, size_t nmemb,
-				  write_callback_t *data)
-{
-	if (!nmemb) {
-		return 0;
-	}
-	if (!data) {
-		return 0;
-	}
-
-	size_t realsize = size * nmemb;
-	output_data_t *mem = data->outdata;
-
-	mem->memory = realloc(mem->memory, mem->size + realsize + 1);
-	if (mem->memory == NULL) {
-		ERROR("Channel get operation failed with OOM\n");
-		return 0;
-	}
-	memcpy(&(mem->memory[mem->size]), streamdata, realsize);
-	mem->size += realsize;
-	mem->memory[mem->size] = 0;
-	return realsize;
-}
-
-static void channel_log_effective_url(channel_t *this)
-{
-	channel_curl_t *channel_curl = this->priv;
-
-	CURLcode curlrc =
-	    curl_easy_getinfo(channel_curl->handle, CURLINFO_EFFECTIVE_URL,
-			      &channel_curl->effective_url);
-	if (curlrc != CURLE_OK && curlrc == CURLE_UNKNOWN_OPTION) {
-		ERROR("Get channel's effective URL response unsupported by "
-		      "libcURL %s.\n",
-		      LIBCURL_VERSION);
-		return;
-	}
-	TRACE("Channel's effective URL resolved to %s\n",
-	      channel_curl->effective_url);
-}
-
-channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code)
-{
-	channel_curl_t *channel_curl = this->priv;
-	CURLcode curlrc =
-	    curl_easy_getinfo(channel_curl->handle, CURLINFO_RESPONSE_CODE,
-			      http_response_code);
-	if (curlrc != CURLE_OK && curlrc == CURLE_UNKNOWN_OPTION) {
-		ERROR("Get channel HTTP response code unsupported by libcURL "
-		      "%s.\n",
-		      LIBCURL_VERSION);
-		return CHANNEL_EINIT;
-	}
-	switch (*http_response_code) {
-	case 0:   /* libcURL: no server response code has been received yet */
-		DEBUG("No HTTP response code has been received yet!\n");
-		return CHANNEL_EBADMSG;
-	case 401: /* Unauthorized. The request requires user authentication. */
-	case 403: /* Forbidden. */
-	case 405: /* Method not Allowed. */
-	case 407: /* Proxy Authentication Required */
-	case 503: /* Service Unavailable */
-		return CHANNEL_EACCES;
-	case 400: /* Bad Request, e.g., invalid parameters */
-	case 404: /* Wrong URL */
-	case 406: /* Not acceptable. Accept header is not response compliant */
-	case 443: /* Connection refused */
-		return CHANNEL_EBADMSG;
-	case 429: /* Bad Request, i.e., too many requests. Try again later. */
-		return CHANNEL_EAGAIN;
-	case 200:
-	case 206:
-		return CHANNEL_OK;
-	case 500:
-		return CHANNEL_EBADMSG;
-	default:
-		ERROR("Channel operation returned unhandled HTTP error code "
-		      "%ld\n",
-		      *http_response_code);
-		return CHANNEL_EBADMSG;
-	}
-}
-
-channel_op_res_t channel_map_curl_error(CURLcode res)
-{
-	switch (res) {
-	case CURLE_NOT_BUILT_IN:
-	case CURLE_BAD_FUNCTION_ARGUMENT:
-	case CURLE_UNKNOWN_OPTION:
-	case CURLE_SSL_ENGINE_NOTFOUND:
-	case CURLE_SSL_ENGINE_SETFAILED:
-	case CURLE_SSL_CERTPROBLEM:
-	case CURLE_SSL_CIPHER:
-	case CURLE_SSL_ENGINE_INITFAILED:
-	case CURLE_SSL_CACERT_BADFILE:
-	case CURLE_SSL_CRL_BADFILE:
-	case CURLE_SSL_ISSUER_ERROR:
-#if LIBCURL_VERSION_NUM >= 0x072900
-	case CURLE_SSL_INVALIDCERTSTATUS:
-#endif
-#if LIBCURL_VERSION_NUM >= 0x072700
-	case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
-#endif
-		return CHANNEL_EINIT;
-	case CURLE_COULDNT_RESOLVE_PROXY:
-	case CURLE_COULDNT_RESOLVE_HOST:
-	case CURLE_COULDNT_CONNECT:
-	case CURLE_INTERFACE_FAILED:
-	case CURLE_SSL_CONNECT_ERROR:
-	case CURLE_PEER_FAILED_VERIFICATION:
-	case CURLE_SSL_CACERT:
-	case CURLE_USE_SSL_FAILED:
-		return CHANNEL_ENONET;
-	case CURLE_OPERATION_TIMEDOUT:
-	case CURLE_SEND_ERROR:
-	case CURLE_RECV_ERROR:
-	case CURLE_GOT_NOTHING:
-	case CURLE_HTTP_POST_ERROR:
-	case CURLE_PARTIAL_FILE:
-		return CHANNEL_EAGAIN;
-	case CURLE_OUT_OF_MEMORY:
-		return CHANNEL_ENOMEM;
-	case CURLE_REMOTE_FILE_NOT_FOUND:
-		return CHANNEL_ENOENT;
-	case CURLE_FILESIZE_EXCEEDED:
-	case CURLE_ABORTED_BY_CALLBACK:
-	case CURLE_WRITE_ERROR:
-	case CURLE_CHUNK_FAILED:
-	case CURLE_SSL_SHUTDOWN_FAILED:
-		return CHANNEL_EIO;
-	case CURLE_TOO_MANY_REDIRECTS:
-		return CHANNEL_ELOOP;
-	case CURLE_BAD_CONTENT_ENCODING:
-	case CURLE_CONV_FAILED:
-	case CURLE_CONV_REQD:
-		return CHANNEL_EILSEQ;
-	case CURLE_REMOTE_ACCESS_DENIED:
-	case CURLE_LOGIN_DENIED:
-		return CHANNEL_EACCES;
-	case CURLE_OK:
-		return CHANNEL_OK;
-	default:
-		return CHANNEL_EINIT;
-	}
-}
-
-channel_op_res_t channel_set_options(channel_t *this,
-					channel_data_t *channel_data,
-					channel_method_t method)
-{
-	channel_curl_t *channel_curl = this->priv;
-	channel_op_res_t result = CHANNEL_OK;
-	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL,
-			      channel_data->url) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT,
-			      "libcurl-agent/1.0") != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_LOW_SPEED_LIMIT,
-			      SPEED_LOW_BYTES_SEC) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_LOW_SPEED_TIME,
-			      SPEED_LOW_TIME_SEC) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_HTTPHEADER,
-			      channel_curl->header) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_MAXREDIRS, -1) !=
-	     CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_FOLLOWLOCATION, 1) !=
-	     CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_REDIR_PROTOCOLS,
-			      CURLPROTO_HTTP | CURLPROTO_HTTPS) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle,
-			      CURLOPT_CAINFO,
-			      channel_data->cafile) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle,
-			      CURLOPT_SSLKEY,
-			      channel_data->sslkey) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle,
-			      CURLOPT_SSLCERT,
-			      channel_data->sslcert) != CURLE_OK)) {
-		result = CHANNEL_EINIT;
-		goto cleanup;
-	}
-
-	if (channel_data->strictssl == true) {
-		if ((curl_easy_setopt(channel_curl->handle,
-				      CURLOPT_SSL_VERIFYHOST,
-				      2L) != CURLE_OK) ||
-		    (curl_easy_setopt(channel_curl->handle,
-				      CURLOPT_SSL_VERIFYPEER,
-				      1L) != CURLE_OK)) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-	}
-	else {
-		if ((curl_easy_setopt(channel_curl->handle,
-				      CURLOPT_SSL_VERIFYHOST,
-				      0L) != CURLE_OK) ||
-		    (curl_easy_setopt(channel_curl->handle,
-				      CURLOPT_SSL_VERIFYPEER,
-				      0L) != CURLE_OK)) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-	}
-
-	switch (method) {
-	case CHANNEL_GET:
-		if (curl_easy_setopt(channel_curl->handle, CURLOPT_CUSTOMREQUEST,
-				     "GET") != CURLE_OK) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-		break;
-	case CHANNEL_PUT:
-		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_PUT, 1L) !=
-		     CURLE_OK) ||
-		     (curl_easy_setopt(channel_curl->handle, CURLOPT_UPLOAD, 1L) !=
-		      CURLE_OK)) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-		break;
-	case CHANNEL_POST:
-		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_POST, 1L) !=
-		     CURLE_OK) ||
-		    (curl_easy_setopt(channel_curl->handle, CURLOPT_POSTFIELDS,
-				      channel_data->json_string) != CURLE_OK)) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-		if (channel_data->debug) {
-			TRACE("Post JSON: %s\n", channel_data->json_string);
-		}
-		break;
-	}
-
-	if (channel_curl->proxy != NULL) {
-		if (channel_curl->proxy != USE_PROXY_ENV) {
-			if (curl_easy_setopt(
-				channel_curl->handle, CURLOPT_PROXY,
-				channel_curl->proxy) != CURLE_OK) {
-				result = CHANNEL_EINIT;
-				goto cleanup;
-			}
-		}
-		if (curl_easy_setopt(channel_curl->handle, CURLOPT_NETRC,
-				     CURL_NETRC_OPTIONAL) != CURLE_OK) {
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-	}
-
-	CURLcode curlrc =
-	    curl_easy_setopt(channel_curl->handle, CURLOPT_TCP_KEEPALIVE, 1L);
-	if (curlrc == CURLE_OK) {
-		if ((curl_easy_setopt(channel_curl->handle, CURLOPT_TCP_KEEPIDLE,
-				      KEEPALIVE_DELAY) != CURLE_OK) ||
-		    (curl_easy_setopt(channel_curl->handle,
-				      CURLOPT_TCP_KEEPINTVL,
-				      KEEPALIVE_INTERVAL) != CURLE_OK)) {
-			ERROR("TCP Keep-alive interval and delay could not be "
-			      "configured.\n");
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-	} else {
-		if (curlrc != CURLE_UNKNOWN_OPTION) {
-			ERROR("Channel could not be configured to sent "
-			      "keep-alive probes.\n");
-			result = CHANNEL_EINIT;
-			goto cleanup;
-		}
-	}
-
-cleanup:
-	return result;
-}
-
-static size_t put_read_callback(void *ptr, size_t size, size_t nmemb, void *data)
-{
-	channel_data_t *channel_data = (channel_data_t *)data;
-	unsigned int bytes;
-	size_t n;
-
-	/* Check data to be sent */
-	bytes = strlen(channel_data->json_string) - channel_data->offs;
-
-	if (!bytes)
-		return 0;
-
-	n = min(bytes, size * nmemb);
-
-	memcpy(ptr, &channel_data->json_string[channel_data->offs], n);
-	channel_data->offs += n;
-
-	return n;
-}
-
-static channel_op_res_t channel_post_method(channel_t *this, void *data)
-{
-	channel_curl_t *channel_curl = this->priv;
-	assert(data != NULL);
-	assert(channel_curl->handle != NULL);
-
-	channel_op_res_t result = CHANNEL_OK;
-	channel_data_t *channel_data = (channel_data_t *)data;
-
-	if (channel_data->debug) {
-		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
-	}
-
-	if (((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Content-Type: application/json")) ==
-	     NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Accept: application/json")) == NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "charsets: utf-8")) == NULL)) {
-		ERROR("Set channel header failed.\n");
-		result = CHANNEL_EINIT;
-		goto cleanup_header;
-	}
-
-	if ((result = channel_set_options(this, channel_data, CHANNEL_POST)) !=
-	    CHANNEL_OK) {
-		ERROR("Set channel option failed.\n");
-		goto cleanup_header;
-	}
-
-	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
-	if (curlrc != CURLE_OK) {
-		ERROR("Channel put operation failed (%d): '%s'\n", curlrc,
-		      curl_easy_strerror(curlrc));
-		result = channel_map_curl_error(curlrc);
-		goto cleanup_header;
-	}
-
-	channel_log_effective_url(this);
-
-	long http_response_code;
-	if ((result = channel_map_http_code(this, &http_response_code)) !=
-	    CHANNEL_OK) {
-		ERROR("Channel operation returned HTTP error code %ld.\n",
-		      http_response_code);
-		goto cleanup_header;
-	}
-	TRACE("Channel put operation returned HTTP status code %ld.\n",
-	      http_response_code);
-
-cleanup_header:
-	curl_easy_reset(channel_curl->handle);
-	curl_slist_free_all(channel_curl->header);
-	channel_curl->header = NULL;
-
-	return result;
-}
-
-static channel_op_res_t channel_put_method(channel_t *this, void *data)
-{
-	channel_curl_t *channel_curl = this->priv;
-	assert(data != NULL);
-	assert(channel_curl->handle != NULL);
-
-	channel_op_res_t result = CHANNEL_OK;
-	channel_data_t *channel_data = (channel_data_t *)data;
-	channel_data->offs = 0;
-
-	if (channel_data->debug) {
-		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
-	}
-
-	if (((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Content-Type: application/json")) ==
-	     NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Accept: application/json")) == NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "charsets: utf-8")) == NULL)) {
-		ERROR("Set channel header failed.\n");
-		result = CHANNEL_EINIT;
-		goto cleanup_header;
-	}
-
-	if ((result = channel_set_options(this, channel_data, CHANNEL_PUT)) !=
-	    CHANNEL_OK) {
-		ERROR("Set channel option failed.\n");
-		goto cleanup_header;
-	}
-
-	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_READFUNCTION, put_read_callback) !=
-		CURLE_OK) ||
-	   (curl_easy_setopt(channel_curl->handle, CURLOPT_INFILESIZE_LARGE,
-			     (curl_off_t)strlen(channel_data->json_string)) != CURLE_OK) ||
-	   (curl_easy_setopt(channel_curl->handle, CURLOPT_READDATA, channel_data) !=
-			CURLE_OK)) {
-		ERROR("Set channel option failed.\n");
-		goto cleanup_header;
-	}
-
-	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
-	if (curlrc != CURLE_OK) {
-		ERROR("Channel put operation failed (%d): '%s'\n", curlrc,
-		      curl_easy_strerror(curlrc));
-		result = channel_map_curl_error(curlrc);
-		goto cleanup_header;
-	}
-
-	channel_log_effective_url(this);
-
-	long http_response_code;
-	if ((result = channel_map_http_code(this, &http_response_code)) != CHANNEL_OK) {
-		ERROR("Channel operation returned HTTP error code %ld.\n",
-		      http_response_code);
-		goto cleanup_header;
-	}
-	TRACE("Channel put operation returned HTTP error code %ld.\n",
-	      http_response_code);
-
-cleanup_header:
-	curl_easy_reset(channel_curl->handle);
-	curl_slist_free_all(channel_curl->header);
-	channel_curl->header = NULL;
-
-	return result;
-}
-
-channel_op_res_t channel_put(channel_t *this, void *data)
-{
-	assert(data != NULL);
-
-	channel_data_t *channel_data = (channel_data_t *)data;
-
-	switch (channel_data->method) {
-	case CHANNEL_PUT:
-		return channel_put_method(this, data);
-	case CHANNEL_POST:
-		return channel_post_method(this, data);
-	default:
-		TRACE("Channel method (POST, PUT) is not set !\n");
-		return CHANNEL_EINIT;
-	}
-}
-
-channel_op_res_t channel_get_file(channel_t *this, void *data, int file_handle)
-{
-	channel_curl_t *channel_curl = this->priv;
-	assert(data != NULL);
-	assert(channel_curl->handle != NULL);
-
-	channel_op_res_t result = CHANNEL_OK;
-	channel_data_t *channel_data = (channel_data_t *)data;
-
-#ifdef CONFIG_SURICATTA_SSL
-	memset(channel_data->sha1hash, 0x0, SHA_DIGEST_LENGTH * 2 + 1);
-	if (SHA1_Init(&checksum_ctx) != 1) {
-		result = CHANNEL_EINIT;
-		ERROR("Cannot initialize sha1 checksum context.\n");
-		goto cleanup;
-	}
-#endif
-
-	if (channel_data->debug) {
-		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
-	}
-
-	if (((channel_curl->header = curl_slist_append(
-		  channel_curl->header,
-		  "Content-Type: application/octet-stream")) == NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Accept: application/octet-stream")) ==
-	     NULL)) {
-		result = CHANNEL_EINIT;
-		ERROR("Set channel header failed.\n");
-		goto cleanup_header;
-	}
-
-	if ((result = channel_set_options(this, channel_data, CHANNEL_GET)) !=
-	    CHANNEL_OK) {
-		ERROR("Set channel option failed.\n");
-		goto cleanup_header;
-	}
-
-	if (file_handle == FD_USE_IPC) {
-		for (int retries = 3; retries >= 0; retries--) {
-			file_handle = ipc_inst_start_ext(SOURCE_SURICATTA,
-				channel_data->info == NULL ? 0 : strlen(channel_data->info),
-				channel_data->info);
-			if (file_handle > 0)
-				break;
-			sleep(1);
-		}
-		if (file_handle < 0) {
-			ERROR("Cannot open SWUpdate IPC stream: %s\n", strerror(errno));
-			result = CHANNEL_EIO;
-			goto cleanup_header;
-		}
-	} else {
-		assert(file_handle > 0);
-	}
-
-	write_callback_t wrdata;
-	wrdata.channel_data = channel_data;
-	wrdata.output = file_handle;
-	result_channel_callback_write_file = CHANNEL_OK;
-	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEFUNCTION,
-			      channel_callback_write_file) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEDATA,
-			      &wrdata) != CURLE_OK)) {
-		ERROR("Cannot setup file writer callback function.\n");
-		result = CHANNEL_EINIT;
-		goto cleanup_file;
-	}
-
-	/* Depending on the architecture and libcurl, this is at
-	 * least 2^32 - 1 bytes, i.e., enough to account for a 4GiB
-	 * download. Although 32-Bit curl defines `curl_off_t` as
-	 * (signed) long long, use unsigned here, but it wastes some
-	 * bits on 64-Bit.
-	 */
-	unsigned long long int total_bytes_downloaded = 0;
-	unsigned char try_count = 0;
-	CURLcode curlrc = CURLE_OK;
-	do {
-		if (try_count > 0) {
-			if (channel_data->retries == 0) {
-				ERROR(
-				    "Channel get operation failed (%d): '%s'\n",
-				    curlrc, curl_easy_strerror(curlrc));
-				result = channel_map_curl_error(curlrc);
-				goto cleanup_file;
-			}
-
-			if (try_count > channel_data->retries) {
-				ERROR("Channel get operation aborted because "
-				      "of too many failed download attempts "
-				      "(%d).\n",
-				      channel_data->retries);
-				result = CHANNEL_ELOOP;
-				goto cleanup_file;
-			}
-
-			DEBUG("Channel connection interrupted, trying resume "
-			      "after %llu bytes.",
-			      total_bytes_downloaded);
-			if (curl_easy_setopt(
-				channel_curl->handle, CURLOPT_RESUME_FROM_LARGE,
-				total_bytes_downloaded) != CURLE_OK) {
-				ERROR("Could not set Channel resume seek (%d): "
-				      "'%s'\n",
-				      curlrc, curl_easy_strerror(curlrc));
-				result = channel_map_curl_error(curlrc);
-				goto cleanup_file;
-			}
-			TRACE("Channel sleeps for %d seconds now.",
-			      channel_data->retry_sleep);
-			if (sleep(channel_data->retry_sleep) > 0) {
-				TRACE("Channel's sleep got interrupted, "
-				      "retrying nonetheless now.");
-			}
-			TRACE("Channel awakened from sleep.");
-		}
-
-		curlrc = curl_easy_perform(channel_curl->handle);
-		result = channel_map_curl_error(curlrc);
-		if ((result != CHANNEL_OK) && (result != CHANNEL_EAGAIN)) {
-			ERROR("Channel operation returned error (%d): '%s'\n",
-			      curlrc, curl_easy_strerror(curlrc));
-			goto cleanup_file;
-		}
-
-		double bytes_downloaded;
-		CURLcode resdlprogress = curl_easy_getinfo(
-		    channel_curl->handle, CURLINFO_SIZE_DOWNLOAD,
-		    &bytes_downloaded);
-		if (resdlprogress != CURLE_OK) {
-			ERROR("Channel does not report bytes downloaded (%d): "
-			      "'%s'\n",
-			      resdlprogress, curl_easy_strerror(resdlprogress));
-			result = channel_map_curl_error(resdlprogress);
-			goto cleanup_file;
-		}
-		total_bytes_downloaded += bytes_downloaded;
-
-	} while (++try_count && (result != CHANNEL_OK));
-
-	channel_log_effective_url(this);
-
-	DEBUG("Channel downloaded %llu bytes ~ %llu MiB.\n",
-	      total_bytes_downloaded, total_bytes_downloaded / 1024 / 1024);
-
-	long http_response_code;
-	if ((result = channel_map_http_code(this, &http_response_code)) !=
-	    CHANNEL_OK) {
-		ERROR("Channel operation returned HTTP error code %ld.\n",
-		      http_response_code);
-		goto cleanup_file;
-	}
-	TRACE("Channel operation returned HTTP status code %ld.\n",
-	      http_response_code);
-
-	if (result_channel_callback_write_file != CHANNEL_OK) {
-		result = CHANNEL_EIO;
-		goto cleanup_file;
-	}
-
-#ifdef CONFIG_SURICATTA_SSL
-	unsigned char sha1hash[SHA_DIGEST_LENGTH];
-	if (SHA1_Final(sha1hash, &checksum_ctx) != 1) {
-		ERROR("Cannot compute checksum.\n");
-		goto cleanup_file;
-	}
-	char sha1hexchar[3];
-	for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
-		sprintf(sha1hexchar, "%02x", sha1hash[i]);
-		strcat(channel_data->sha1hash, sha1hexchar);
-	}
-#endif
-
-cleanup_file:
-	/* NOTE ipc_end() calls close() but does not return its error code,
-	 *      so use close() here directly to issue an error in case.
-	 *      Also, for a given file handle, calling ipc_end() would make
-	 *      no semantic sense. */
-	if (close(file_handle) != 0) {
-		ERROR("Channel error while closing download target handle: '%s'\n",
-		      strerror(errno));
-	}
-cleanup_header:
-	curl_easy_reset(channel_curl->handle);
-	curl_slist_free_all(channel_curl->header);
-	channel_curl->header = NULL;
-
-#ifdef CONFIG_SURICATTA_SSL
-cleanup:
-#endif
-	return result;
-}
-
-channel_op_res_t channel_get(channel_t *this, void *data)
-{
-	channel_curl_t *channel_curl = this->priv;
-	assert(data != NULL);
-	assert(channel_curl.handle != NULL);
-
-	channel_op_res_t result = CHANNEL_OK;
-	channel_data_t *channel_data = (channel_data_t *)data;
-
-	if (channel_data->debug) {
-		curl_easy_setopt(channel_curl->handle, CURLOPT_VERBOSE, 1L);
-	}
-
-	if (((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Content-Type: application/json")) ==
-	     NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "Accept: application/json")) == NULL) ||
-	    ((channel_curl->header = curl_slist_append(
-		  channel_curl->header, "charsets: utf-8")) == NULL)) {
-		result = CHANNEL_EINIT;
-		ERROR("Set channel header failed.\n");
-		goto cleanup_header;
-	}
-
-	if ((result = channel_set_options(this, channel_data, CHANNEL_GET)) !=
-	    CHANNEL_OK) {
-		ERROR("Set channel option failed.\n");
-		goto cleanup_header;
-	}
-
-	output_data_t chunk = {.memory = NULL, .size = 0};
-	if ((chunk.memory = malloc(1)) == NULL) {
-		ERROR("Channel buffer reservation failed with OOM.\n");
-		result = CHANNEL_ENOMEM;
-		goto cleanup_header;
-	}
-
-	write_callback_t wrdata;
-	wrdata.channel_data = channel_data;
-	wrdata.outdata = &chunk;
-
-	if ((curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEFUNCTION,
-			      channel_callback_membuffer) != CURLE_OK) ||
-	    (curl_easy_setopt(channel_curl->handle, CURLOPT_WRITEDATA,
-			      (void *)&wrdata) != CURLE_OK)) {
-		ERROR("Cannot setup memory buffer writer callback function.\n");
-		result = CHANNEL_EINIT;
-		goto cleanup_chunk;
-	}
-
-	DEBUG("Trying to GET %s", channel_data->url);
-	CURLcode curlrc = curl_easy_perform(channel_curl->handle);
-	if (curlrc != CURLE_OK) {
-		ERROR("Channel get operation failed (%d): '%s'\n", curlrc,
-		      curl_easy_strerror(curlrc));
-		result = channel_map_curl_error(curlrc);
-		goto cleanup_chunk;
-	}
-
-	channel_log_effective_url(this);
-
-	long http_response_code;
-	if ((result = channel_map_http_code(this, &http_response_code)) !=
-	    CHANNEL_OK) {
-		ERROR("Channel operation returned HTTP error code %ld.\n",
-		      http_response_code);
-		if (http_response_code == 500) {
-			DEBUG("The error's message is: '%s'\n", chunk.memory);
-		}
-		if (http_response_code == 404) {
-			DEBUG("The error's message is: '%s'\n", chunk.memory);
-		}
-		goto cleanup_chunk;
-	}
-	TRACE("Channel operation returned HTTP status code %ld.\n",
-	      http_response_code);
-
-	assert(channel_data->json_reply == NULL);
-	enum json_tokener_error json_res;
-	struct json_tokener *json_tokenizer = json_tokener_new();
-	do {
-		channel_data->json_reply = json_tokener_parse_ex(
-		    json_tokenizer, chunk.memory, (int)chunk.size);
-	} while ((json_res = json_tokener_get_error(json_tokenizer)) ==
-		 json_tokener_continue);
-	if (json_res != json_tokener_success) {
-		ERROR("Error while parsing channel's returned JSON data: %s\n",
-		      json_tokener_error_desc(json_res));
-		result = CHANNEL_EBADMSG;
-		goto cleanup_json_tokenizer;
-	}
-	if (channel_data->debug) {
-		TRACE("Get JSON: %s\n", chunk.memory);
-	}
-
-cleanup_json_tokenizer:
-	json_tokener_free(json_tokenizer);
-cleanup_chunk:
-	chunk.memory != NULL ? free(chunk.memory) : (void)0;
-cleanup_header:
-	curl_easy_reset(channel_curl->handle);
-	curl_slist_free_all(channel_curl->header);
-	channel_curl->header = NULL;
-
-	return result;
-}
diff --git a/suricatta/channel_hawkbit.h b/suricatta/channel_hawkbit.h
deleted file mode 100644
index ed262fc..0000000
--- a/suricatta/channel_hawkbit.h
+++ /dev/null
@@ -1,63 +0,0 @@ 
-/*
- * Author: Christian Storm
- * Copyright (C) 2016, Siemens AG
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc.
- */
-
-#pragma once
-#include <json-c/json.h>
-#include <stdio.h>
-#ifdef CONFIG_SURICATTA_SSL
-#include <openssl/sha.h>
-#endif
-
-/* hawkBit Channel Implementation Private Header File.
- *
- * This is a "private" header for testability, i.e., the declarations and
- * definitions herein should be used by `server_hawkbit.c`, `channel_hawkbit.c`,
- * and unit tests only.
- */
-
-typedef enum {
-	CHANNEL_GET,
-	CHANNEL_POST,
-	CHANNEL_PUT,
-} channel_method_t;
-
-#define FD_USE_IPC -2
-
-#define USE_PROXY_ENV (char *)0x11
-
-typedef struct {
-	char *url;
-	char *json_string;
-	json_object *json_reply;
-	char *cafile;
-	char *sslkey;
-	char *sslcert;
-	char *proxy;
-	char *info;
-	unsigned int retry_sleep;
-	unsigned int offs;
-	unsigned int method;
-	unsigned int retries;
-	bool debug;
-	bool strictssl;
-	int (*checkdwl)(void);
-#ifdef CONFIG_SURICATTA_SSL
-	char sha1hash[SHA_DIGEST_LENGTH * 2 + 1];
-#endif
-} channel_data_t;
diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index 1b0e600..186e9f0 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -33,7 +33,7 @@ 
 #include <swupdate_status.h>
 #include "suricatta/suricatta.h"
 #include "suricatta/channel.h"
-#include "channel_hawkbit.h"
+#include "channel_curl.h"
 #include "suricatta/state.h"
 #include "server_hawkbit.h"
 #include "parselib.h"