Switch from libsoup to libcurl

The former drags on glib-networking and then gsettings-desktop-schemas, which
add over 5Mb to the minimal IoT and CoreOS composes. Everything already uses
libcurl (even NetworkManager!) and so this is an easy way to reduce image size.
This commit is contained in:
Richard Hughes 2020-11-13 13:25:22 +00:00
parent a6e6085365
commit 3a73c342ba
18 changed files with 338 additions and 386 deletions

View File

@ -32,14 +32,16 @@ jobs:
gcc gcc
gcab gcab
mingw32-nsis mingw32-nsis
mingw64-brotli
mingw64-gcc mingw64-gcc
mingw64-pkg-config mingw64-pkg-config
mingw64-glib2 mingw64-glib2
mingw64-gnutls
mingw64-libgusb mingw64-libgusb
mingw64-sqlite mingw64-sqlite
mingw64-libarchive mingw64-libarchive
mingw64-json-glib mingw64-json-glib
mingw64-libsoup mingw64-curl
wine wine
- checkout - checkout
- run: - run:

View File

@ -11,7 +11,7 @@ license=('GPL2')
depends=('libgusb' 'modemmanager' 'tpm2-tss') depends=('libgusb' 'modemmanager' 'tpm2-tss')
makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow' 'git' makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow' 'git'
'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala'
'libsoup' 'polkit' 'gcab') 'curl' 'polkit' 'gcab')
pkgver() { pkgver() {
cd ${pkgname} cd ${pkgname}

View File

@ -817,20 +817,20 @@
<package variant="x86_64" /> <package variant="x86_64" />
</distro> </distro>
</dependency> </dependency>
<dependency type="build" id="libsoup2.4-dev"> <dependency type="build" id="libcurl4-gnutls-dev">
<distro id="arch"> <distro id="arch">
<package>libsoup</package> <package>curl</package>
</distro> </distro>
<distro id="centos"> <distro id="centos">
<package>libsoup-devel</package> <package>libcurl-devel</package>
</distro> </distro>
<distro id="fedora"> <distro id="fedora">
<package>libsoup-devel</package> <package>libcurl-devel</package>
</distro> </distro>
<distro id="debian"> <distro id="debian">
<control /> <control />
<package variant="x86_64" /> <package variant="x86_64" />
<package variant="s390x">libsoup2.4-dev:s390x</package> <package variant="s390x">libcurl4-gnutls-dev:s390x</package>
<package variant="i386" /> <package variant="i386" />
</distro> </distro>
<distro id="ubuntu"> <distro id="ubuntu">

View File

@ -1,7 +1,7 @@
%global glib2_version 2.45.8 %global glib2_version 2.45.8
%global libxmlb_version 0.1.3 %global libxmlb_version 0.1.3
%global libgusb_version 0.3.5 %global libgusb_version 0.3.5
%global libsoup_version 2.51.92 %global libcurl_version 7.62.0
%global libjcat_version 0.1.0 %global libjcat_version 0.1.0
%global systemd_version 231 %global systemd_version 231
%global json_glib_version 1.1.1 %global json_glib_version 1.1.1
@ -55,7 +55,7 @@ BuildRequires: libxmlb-devel >= %{libxmlb_version}
BuildRequires: libgcab1-devel BuildRequires: libgcab1-devel
BuildRequires: libgudev1-devel BuildRequires: libgudev1-devel
BuildRequires: libgusb-devel >= %{libgusb_version} BuildRequires: libgusb-devel >= %{libgusb_version}
BuildRequires: libsoup-devel >= %{libsoup_version} BuildRequires: libcurl-devel >= %{libcurl_version}
BuildRequires: libjcat-devel >= %{libjcat_version} BuildRequires: libjcat-devel >= %{libjcat_version}
BuildRequires: polkit-devel >= 0.103 BuildRequires: polkit-devel >= 0.103
BuildRequires: sqlite-devel BuildRequires: sqlite-devel
@ -112,7 +112,6 @@ Requires(postun): systemd
Requires: glib2%{?_isa} >= %{glib2_version} Requires: glib2%{?_isa} >= %{glib2_version}
Requires: libxmlb%{?_isa} >= %{libxmlb_version} Requires: libxmlb%{?_isa} >= %{libxmlb_version}
Requires: libgusb%{?_isa} >= %{libgusb_version} Requires: libgusb%{?_isa} >= %{libgusb_version}
Requires: libsoup%{?_isa} >= %{libsoup_version}
Requires: bubblewrap Requires: bubblewrap
Requires: shared-mime-info Requires: shared-mime-info

View File

@ -48,7 +48,7 @@ Section "fwupd"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libnettle-7.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libnettle-7.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libp11-kit-0.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libp11-kit-0.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libpcre-1.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libpcre-1.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libsoup-2.4-1.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libcurl-4.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libsqlite3-0.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libsqlite3-0.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libtasn1-6.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libtasn1-6.dll"
File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libusb-1.0.dll" File "/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libusb-1.0.dll"

View File

@ -8,7 +8,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <gio/gio.h> #include <gio/gio.h>
#include <libsoup/soup.h> #include <curl/curl.h>
#ifdef HAVE_GIO_UNIX #ifdef HAVE_GIO_UNIX
#include <gio/gunixfdlist.h> #include <gio/gunixfdlist.h>
#endif #endif
@ -52,10 +52,14 @@ typedef struct {
gchar *host_security_id; gchar *host_security_id;
GDBusConnection *conn; GDBusConnection *conn;
GDBusProxy *proxy; GDBusProxy *proxy;
SoupSession *soup_session;
gchar *user_agent; gchar *user_agent;
} FwupdClientPrivate; } FwupdClientPrivate;
typedef struct {
CURL *curl;
curl_mime *mime;
} FwupdCurlHelper;
enum { enum {
SIGNAL_CHANGED, SIGNAL_CHANGED,
SIGNAL_STATUS_CHANGED, SIGNAL_STATUS_CHANGED,
@ -71,7 +75,7 @@ enum {
PROP_PERCENTAGE, PROP_PERCENTAGE,
PROP_DAEMON_VERSION, PROP_DAEMON_VERSION,
PROP_TAINTED, PROP_TAINTED,
PROP_SOUP_SESSION, PROP_SOUP_SESSION, /* unused ABI */
PROP_HOST_PRODUCT, PROP_HOST_PRODUCT,
PROP_HOST_MACHINE_ID, PROP_HOST_MACHINE_ID,
PROP_HOST_SECURITY_ID, PROP_HOST_SECURITY_ID,
@ -84,6 +88,20 @@ static guint signals [SIGNAL_LAST] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE (FwupdClient, fwupd_client, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (FwupdClient, fwupd_client, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fwupd_client_get_instance_private (o)) #define GET_PRIVATE(o) (fwupd_client_get_instance_private (o))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
static void
fwupd_client_curl_helper_free (FwupdCurlHelper *helper)
{
if (helper->curl != NULL)
curl_easy_cleanup (helper->curl);
if (helper->mime != NULL)
curl_mime_free (helper->mime);
g_free (helper);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdCurlHelper, fwupd_client_curl_helper_free)
static void static void
fwupd_client_set_host_product (FwupdClient *self, const gchar *host_product) fwupd_client_set_host_product (FwupdClient *self, const gchar *host_product)
{ {
@ -262,12 +280,6 @@ gboolean
fwupd_client_ensure_networking (FwupdClient *self, GError **error) fwupd_client_ensure_networking (FwupdClient *self, GError **error)
{ {
FwupdClientPrivate *priv = GET_PRIVATE (self); FwupdClientPrivate *priv = GET_PRIVATE (self);
const gchar *http_proxy;
g_autoptr(SoupSession) session = NULL;
/* already exists */
if (priv->soup_session != NULL)
return TRUE;
/* check the user agent is sane */ /* check the user agent is sane */
if (priv->user_agent == NULL) { if (priv->user_agent == NULL) {
@ -284,22 +296,62 @@ fwupd_client_ensure_networking (FwupdClient *self, GError **error)
"user agent unsuitable; fwupd version required"); "user agent unsuitable; fwupd version required");
return FALSE; return FALSE;
} }
return TRUE;
}
/* create the soup session */ static int
session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, priv->user_agent, fwupd_client_progress_callback_cb (void *clientp,
SOUP_SESSION_TIMEOUT, 60, curl_off_t dltotal,
NULL); curl_off_t dlnow,
if (session == NULL) { curl_off_t ultotal,
curl_off_t ulnow)
{
FwupdClient *self = FWUPD_CLIENT (clientp);
/* calculate percentage */
if (dltotal > 0 && dlnow >= 0 && dlnow <= dltotal) {
guint percentage = (guint) ((100 * dlnow) / dltotal);
g_debug ("download progress: %u%%", percentage);
fwupd_client_set_percentage (self, percentage);
} else if (ultotal > 0 && ulnow >= 0 && ulnow <= ultotal) {
guint percentage = (guint) ((100 * ulnow) / ultotal);
g_debug ("upload progress: %u%%", percentage);
fwupd_client_set_percentage (self, percentage);
}
return 0;
}
static FwupdCurlHelper *
fwupd_client_curl_new (FwupdClient *self, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
const gchar *http_proxy;
g_autoptr(FwupdCurlHelper) helper = g_new0 (FwupdCurlHelper, 1);
/* check the user agent is sane */
if (!fwupd_client_ensure_networking (self, error))
return NULL;
/* create the session */
helper->curl = curl_easy_init ();
if (helper->curl == NULL) {
g_set_error_literal (error, g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INTERNAL, FWUPD_ERROR_INTERNAL,
"failed to setup networking"); "failed to setup networking");
return FALSE; return NULL;
} }
if (g_getenv ("FWUPD_CURL_VERBOSE") != NULL)
curl_easy_setopt (helper->curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt (helper->curl, CURLOPT_XFERINFOFUNCTION, fwupd_client_progress_callback_cb);
curl_easy_setopt (helper->curl, CURLOPT_XFERINFODATA, self);
curl_easy_setopt (helper->curl, CURLOPT_USERAGENT, priv->user_agent);
curl_easy_setopt (helper->curl, CURLOPT_CONNECTTIMEOUT, 60L);
/* relax the SSL checks for broken corporate proxies */ /* relax the SSL checks for broken corporate proxies */
if (g_getenv ("DISABLE_SSL_STRICT") != NULL) if (g_getenv ("DISABLE_SSL_STRICT") != NULL)
g_object_set (session, SOUP_SESSION_SSL_STRICT, FALSE, NULL); curl_easy_setopt (helper->curl, CURLOPT_SSL_VERIFYPEER, 0L);
/* set the proxy */ /* set the proxy */
http_proxy = g_getenv ("https_proxy"); http_proxy = g_getenv ("https_proxy");
@ -309,22 +361,12 @@ fwupd_client_ensure_networking (FwupdClient *self, GError **error)
http_proxy = g_getenv ("http_proxy"); http_proxy = g_getenv ("http_proxy");
if (http_proxy == NULL) if (http_proxy == NULL)
http_proxy = g_getenv ("HTTP_PROXY"); http_proxy = g_getenv ("HTTP_PROXY");
if (http_proxy != NULL && strlen (http_proxy) > 0) { if (http_proxy != NULL && strlen (http_proxy) > 0)
g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy); curl_easy_setopt (helper->curl, CURLOPT_PROXY, http_proxy);
if (proxy_uri == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"invalid proxy URI: %s", http_proxy);
return FALSE;
}
g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
}
/* this disables the double-compression of the firmware.xml.gz file */ /* this disables the double-compression of the firmware.xml.gz file */
soup_session_remove_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER); curl_easy_setopt (helper->curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
priv->soup_session = g_steal_pointer (&session); return g_steal_pointer (&helper);
return TRUE;
} }
static void static void
@ -2234,6 +2276,13 @@ fwupd_client_install_release_download_cb (GObject *source, GAsyncResult *res, gp
g_steal_pointer (&task)); g_steal_pointer (&task));
} }
static gboolean
fwupd_client_is_url (const gchar *perhaps_url)
{
g_autoptr(CURLU) h = curl_url ();
return curl_url_set (h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK;
}
static void static void
fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data) fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{ {
@ -2242,7 +2291,6 @@ fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpoi
g_autoptr(FwupdRemote) remote = NULL; g_autoptr(FwupdRemote) remote = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data); g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(SoupURI) uri = NULL;
FwupdClientInstallReleaseData *data = g_task_get_task_data (task); FwupdClientInstallReleaseData *data = g_task_get_task_data (task);
GCancellable *cancellable = g_task_get_cancellable (task); GCancellable *cancellable = g_task_get_cancellable (task);
@ -2254,8 +2302,8 @@ fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpoi
} }
/* local and directory remotes may have the firmware already */ /* local and directory remotes may have the firmware already */
uri = soup_uri_new (fwupd_release_get_uri (data->release)); if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) { !fwupd_client_is_url (fwupd_release_get_uri (data->release))) {
const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
g_autofree gchar *path = g_path_get_dirname (fn_cache); g_autofree gchar *path = g_path_get_dirname (fn_cache);
fn = g_build_filename (path, fwupd_release_get_uri (data->release), NULL); fn = g_build_filename (path, fwupd_release_get_uri (data->release), NULL);
@ -3929,97 +3977,60 @@ fwupd_client_set_user_agent_for_package (FwupdClient *self,
priv->user_agent = g_string_free (str, FALSE); priv->user_agent = g_string_free (str, FALSE);
} }
static void static size_t
fwupd_client_download_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) fwupd_client_download_write_callback_cb (char *ptr, size_t size, size_t nmemb, void *userdata)
{ {
guint percentage; GByteArray *buf = (GByteArray *) userdata;
goffset header_size; gsize realsize = size * nmemb;
goffset body_length; g_byte_array_append (buf, (const guint8 *) ptr, realsize);
FwupdClient *self = FWUPD_CLIENT (user_data); return realsize;
/* if it's returning "Found" or an error, ignore the percentage */
if (msg->status_code != SOUP_STATUS_OK) {
g_debug ("ignoring status code %u (%s)",
msg->status_code, msg->reason_phrase);
return;
}
/* get data */
body_length = msg->response_body->length;
header_size = soup_message_headers_get_content_length (msg->response_headers);
if (header_size < body_length)
return;
/* calculate percentage */
percentage = (guint) ((100 * body_length) / header_size);
g_debug ("progress: %u%%", percentage);
fwupd_client_set_percentage (self, percentage);
} }
static void static void
fwupd_client_read_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) fwupd_client_download_bytes_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{ {
g_autoptr(GBytes) bytes = NULL; FwupdClient *self = FWUPD_CLIENT (source_object);
g_autoptr(GError) error = NULL; FwupdCurlHelper *helper = g_task_get_task_data (task);
g_autoptr(GTask) task = G_TASK (user_data); CURLcode res;
gchar errbuf[CURL_ERROR_SIZE] = { '\0' };
g_autoptr(GByteArray) buf = g_byte_array_new ();
bytes = fwupd_input_stream_read_bytes_finish (G_INPUT_STREAM (source), res, &error); curl_easy_setopt (helper->curl, CURLOPT_ERRORBUFFER, errbuf);
if (bytes == NULL) { curl_easy_setopt (helper->curl, CURLOPT_WRITEFUNCTION, fwupd_client_download_write_callback_cb);
g_task_return_error (task, g_steal_pointer (&error)); curl_easy_setopt (helper->curl, CURLOPT_WRITEDATA, buf);
return; res = curl_easy_perform (helper->curl);
}
/* success */
g_task_return_pointer (task,
g_steal_pointer (&bytes),
(GDestroyNotify) g_bytes_unref);
}
static void
fwupd_client_download_bytes_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
FwupdClient *self = g_task_get_source_object (task);
FwupdClientPrivate *priv = GET_PRIVATE (self);
GCancellable *cancellable = g_task_get_cancellable (task);
g_autoptr(GError) error = NULL;
g_autoptr(GInputStream) istr = NULL;
guint status_code = 0;
SoupMessage *msg = g_task_get_task_data (task);
/* get the result */
fwupd_client_set_status (self, FWUPD_STATUS_IDLE); fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
istr = soup_session_send_finish (priv->soup_session, res, &error); if (res != CURLE_OK) {
if (istr == NULL) { glong status_code = 0;
g_task_return_error (task, g_steal_pointer (&error)); curl_easy_getinfo (helper->curl, CURLINFO_RESPONSE_CODE, &status_code);
return; g_debug ("status-code was %ld", status_code);
} if (status_code == 429) {
g_task_return_new_error (task,
/* check the input stream before reading the data */ FWUPD_ERROR,
g_object_get (msg, "status-code", &status_code, NULL); FWUPD_ERROR_INVALID_FILE,
g_debug ("status-code was %u", status_code); "Failed to download due to server limit");
if (status_code == 429) { return;
}
if (errbuf[0] != '\0') {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"%s", errbuf);
return;
}
g_task_return_new_error (task, g_task_return_new_error (task,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"Failed to download due to server limit"); "%s", curl_easy_strerror (res));
return;
}
if (status_code != SOUP_STATUS_OK) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to download: %s",
soup_status_get_phrase (status_code));
return;
}
/* read the input stream into a GBytes, async */ return;
fwupd_input_stream_read_bytes_async (istr, cancellable, }
fwupd_client_read_bytes_cb, g_task_return_pointer (task,
g_steal_pointer (&task)); g_byte_array_free_to_bytes (g_steal_pointer (&buf)),
(GDestroyNotify) g_bytes_unref);
} }
/** /**
@ -4049,9 +4060,8 @@ fwupd_client_download_bytes_async (FwupdClient *self,
{ {
FwupdClientPrivate *priv = GET_PRIVATE (self); FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL; g_autoptr(GTask) task = NULL;
g_autoptr(SoupMessage) msg = NULL;
g_autoptr(SoupURI) uri = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(FwupdCurlHelper) helper = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self)); g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (url != NULL); g_return_if_fail (url != NULL);
@ -4060,31 +4070,18 @@ fwupd_client_download_bytes_async (FwupdClient *self,
/* ensure networking set up */ /* ensure networking set up */
task = g_task_new (self, cancellable, callback, callback_data); task = g_task_new (self, cancellable, callback, callback_data);
if (!fwupd_client_ensure_networking (self, &error)) { helper = fwupd_client_curl_new (self, &error);
if (helper == NULL) {
g_task_return_error (task, g_steal_pointer (&error)); g_task_return_error (task, g_steal_pointer (&error));
return; return;
} }
curl_easy_setopt (helper->curl, CURLOPT_URL, url);
g_task_set_task_data (task, g_steal_pointer (&helper), (GDestroyNotify) fwupd_client_curl_helper_free);
/* download data */ /* download data */
g_debug ("downloading %s", url); g_debug ("downloading %s", url);
uri = soup_uri_new (url);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
if (msg == NULL) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to parse URI %s", url);
return;
}
g_signal_connect (msg, "got-chunk",
G_CALLBACK (fwupd_client_download_chunk_cb),
self);
g_task_set_task_data (task, g_object_ref (msg), (GDestroyNotify) g_object_unref);
fwupd_client_set_status (self, FWUPD_STATUS_DOWNLOADING); fwupd_client_set_status (self, FWUPD_STATUS_DOWNLOADING);
soup_session_send_async (priv->soup_session, msg, g_task_run_in_thread (task, fwupd_client_download_bytes_thread_cb);
cancellable,
fwupd_client_download_bytes_cb,
g_steal_pointer (&task));
} }
/** /**
@ -4109,42 +4106,43 @@ fwupd_client_download_bytes_finish (FwupdClient *self, GAsyncResult *res, GError
} }
static void static void
fwupd_client_upload_bytes_cb (GObject *source, fwupd_client_upload_bytes_thread_cb (GTask *task,
GAsyncResult *res, gpointer source_object,
gpointer user_data) gpointer task_data,
GCancellable *cancellable)
{ {
g_autoptr(GTask) task = G_TASK (user_data); FwupdClient *self = FWUPD_CLIENT (source_object);
FwupdClient *self = g_task_get_source_object (task); FwupdCurlHelper *helper = g_task_get_task_data (task);
FwupdClientPrivate *priv = GET_PRIVATE (self); CURLcode res;
GCancellable *cancellable = g_task_get_cancellable (task); gchar errbuf[CURL_ERROR_SIZE] = { '\0' };
g_autoptr(GError) error = NULL; g_autoptr(GByteArray) buf = g_byte_array_new ();
g_autoptr(GInputStream) istr = NULL;
guint status_code;
SoupMessage *msg = g_task_get_task_data (task);
/* get the result */ curl_easy_setopt (helper->curl, CURLOPT_ERRORBUFFER, errbuf);
istr = soup_session_send_finish (priv->soup_session, res, &error); curl_easy_setopt (helper->curl, CURLOPT_WRITEFUNCTION, fwupd_client_download_write_callback_cb);
if (istr == NULL) { curl_easy_setopt (helper->curl, CURLOPT_WRITEDATA, buf);
g_task_return_error (task, g_steal_pointer (&error)); res = curl_easy_perform (helper->curl);
return; fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
} if (res != CURLE_OK) {
glong status_code = 0;
/* check the input stream before reading the data */ curl_easy_getinfo (helper->curl, CURLINFO_RESPONSE_CODE, &status_code);
g_object_get (msg, "status-code", &status_code, NULL); g_debug ("status-code was %ld", status_code);
g_debug ("status-code was %u", status_code); if (errbuf[0] != '\0') {
if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) { g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"%s", errbuf);
return;
}
g_task_return_new_error (task, g_task_return_new_error (task,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"Failed to download: %s", "%s", curl_easy_strerror (res));
soup_status_get_phrase (status_code));
return; return;
} }
g_task_return_pointer (task,
/* read the input stream into a GBytes, async */ g_byte_array_free_to_bytes (g_steal_pointer (&buf)),
fwupd_input_stream_read_bytes_async (istr, cancellable, (GDestroyNotify) g_bytes_unref);
fwupd_client_read_bytes_cb,
g_steal_pointer (&task));
} }
/** /**
@ -4178,8 +4176,7 @@ fwupd_client_upload_bytes_async (FwupdClient *self,
{ {
FwupdClientPrivate *priv = GET_PRIVATE (self); FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL; g_autoptr(GTask) task = NULL;
g_autoptr(SoupMessage) msg = NULL; g_autoptr(FwupdCurlHelper) helper = NULL;
g_autoptr(SoupURI) uri = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self)); g_return_if_fail (FWUPD_IS_CLIENT (self));
@ -4189,48 +4186,37 @@ fwupd_client_upload_bytes_async (FwupdClient *self,
/* ensure networking set up */ /* ensure networking set up */
task = g_task_new (self, cancellable, callback, callback_data); task = g_task_new (self, cancellable, callback, callback_data);
if (!fwupd_client_ensure_networking (self, &error)) { helper = fwupd_client_curl_new (self, &error);
if (helper == NULL) {
g_task_return_error (task, g_steal_pointer (&error)); g_task_return_error (task, g_steal_pointer (&error));
return; return;
} }
/* download data */
g_debug ("downloading %s", url);
uri = soup_uri_new (url);
/* build message */ /* build message */
if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 || if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 ||
signature != NULL) { signature != NULL) {
g_autoptr(SoupMultipart) mp = NULL; curl_mimepart *part;
mp = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); helper->mime = curl_mime_init (helper->curl);
soup_multipart_append_form_string (mp, "payload", payload); curl_easy_setopt (helper->curl, CURLOPT_MIMEPOST, helper->mime);
if (signature != NULL) part = curl_mime_addpart (helper->mime);
soup_multipart_append_form_string (mp, "signature", signature); curl_mime_data (part, payload, CURL_ZERO_TERMINATED);
msg = soup_form_request_new_from_multipart (url, mp); curl_mime_name (part, "payload");
if (signature != NULL) {
part = curl_mime_addpart (helper->mime);
curl_mime_data (part, signature, CURL_ZERO_TERMINATED);
curl_mime_name (part, "signature");
}
} else { } else {
msg = soup_message_new (SOUP_METHOD_POST, url); curl_easy_setopt (helper->mime, CURLOPT_POST, 1L);
soup_message_set_request (msg, "application/json; charset=utf-8", curl_easy_setopt (helper->curl, CURLOPT_POSTFIELDSIZE, strlen (payload));
SOUP_MEMORY_COPY, payload, strlen (payload)); curl_easy_setopt (helper->curl, CURLOPT_COPYPOSTFIELDS, payload);
} }
/* POST request */
if (msg == NULL) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to parse URI %s", url);
return;
}
g_signal_connect (msg, "got-chunk",
G_CALLBACK (fwupd_client_download_chunk_cb),
self);
g_task_set_task_data (task, g_object_ref (msg), (GDestroyNotify) g_object_unref);
fwupd_client_set_status (self, FWUPD_STATUS_IDLE); fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
g_debug ("uploading to %s", url); g_debug ("uploading to %s", url);
soup_session_send_async (priv->soup_session, msg, curl_easy_setopt (helper->curl, CURLOPT_URL, url);
cancellable, g_task_set_task_data (task, g_steal_pointer (&helper), (GDestroyNotify) fwupd_client_curl_helper_free);
fwupd_client_upload_bytes_cb, g_task_run_in_thread (task, fwupd_client_upload_bytes_thread_cb);
g_steal_pointer (&task));
} }
/** /**
@ -4269,7 +4255,7 @@ fwupd_client_get_property (GObject *object, guint prop_id,
g_value_set_boolean (value, priv->tainted); g_value_set_boolean (value, priv->tainted);
break; break;
case PROP_SOUP_SESSION: case PROP_SOUP_SESSION:
g_value_set_object (value, priv->soup_session); g_value_set_object (value, NULL);
break; break;
case PROP_PERCENTAGE: case PROP_PERCENTAGE:
g_value_set_uint (value, priv->percentage); g_value_set_uint (value, priv->percentage);
@ -4309,9 +4295,6 @@ fwupd_client_set_property (GObject *object, guint prop_id,
case PROP_PERCENTAGE: case PROP_PERCENTAGE:
priv->percentage = g_value_get_uint (value); priv->percentage = g_value_get_uint (value);
break; break;
case PROP_SOUP_SESSION:
g_set_object (&priv->soup_session, g_value_get_object (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -4471,12 +4454,12 @@ fwupd_client_class_init (FwupdClientClass *klass)
/** /**
* FwupdClient:soup-session: * FwupdClient:soup-session:
* *
* The libsoup session. * The libsoup session, now unused.
* *
* Since: 1.4.5 * Since: 1.4.5
*/ */
pspec = g_param_spec_object ("soup-session", NULL, NULL, SOUP_TYPE_SESSION, pspec = g_param_spec_object ("soup-session", NULL, NULL, G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME); G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_SOUP_SESSION, pspec); g_object_class_install_property (object_class, PROP_SOUP_SESSION, pspec);
/** /**
@ -4533,8 +4516,6 @@ fwupd_client_finalize (GObject *object)
g_object_unref (priv->conn); g_object_unref (priv->conn);
if (priv->proxy != NULL) if (priv->proxy != NULL)
g_object_unref (priv->proxy); g_object_unref (priv->proxy);
if (priv->soup_session != NULL)
g_object_unref (priv->soup_session);
G_OBJECT_CLASS (fwupd_client_parent_class)->finalize (object); G_OBJECT_CLASS (fwupd_client_parent_class)->finalize (object);
} }

View File

@ -6,7 +6,7 @@
#include "config.h" #include "config.h"
#include <libsoup/soup.h> #include <curl/curl.h>
#include <jcat.h> #include <jcat.h>
#include "fwupd-deprecated.h" #include "fwupd-deprecated.h"
@ -67,6 +67,10 @@ enum {
G_DEFINE_TYPE_WITH_PRIVATE (FwupdRemote, fwupd_remote, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (FwupdRemote, fwupd_remote, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fwupd_remote_get_instance_private (o)) #define GET_PRIVATE(o) (fwupd_remote_get_instance_private (o))
typedef gchar curlptr;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(curlptr, curl_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
static void static void
fwupd_remote_set_username (FwupdRemote *self, const gchar *username) fwupd_remote_set_username (FwupdRemote *self, const gchar *username)
{ {
@ -150,11 +154,11 @@ fwupd_remote_set_filename_source (FwupdRemote *self, const gchar *filename_sourc
priv->filename_source = g_strdup (filename_source); priv->filename_source = g_strdup (filename_source);
} }
static SoupURI * static CURLU *
fwupd_remote_build_uri (FwupdRemote *self, const gchar *url, GError **error) fwupd_remote_build_uri (FwupdRemote *self, const gchar *url, GError **error)
{ {
FwupdRemotePrivate *priv = GET_PRIVATE (self); FwupdRemotePrivate *priv = GET_PRIVATE (self);
SoupURI *uri; g_autoptr(CURLU) uri = curl_url ();
g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL); g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL);
g_return_val_if_fail (url != NULL, NULL); g_return_val_if_fail (url != NULL, NULL);
@ -162,48 +166,43 @@ fwupd_remote_build_uri (FwupdRemote *self, const gchar *url, GError **error)
/* create URI, substituting if required */ /* create URI, substituting if required */
if (priv->firmware_base_uri != NULL) { if (priv->firmware_base_uri != NULL) {
g_autoptr(SoupURI) uri_tmp = NULL;
g_autofree gchar *basename = NULL; g_autofree gchar *basename = NULL;
g_autofree gchar *url2 = NULL; g_autofree gchar *path_new = NULL;
uri_tmp = soup_uri_new (url); g_autoptr(curlptr) path = NULL;
if (uri_tmp == NULL) { g_autoptr(CURLU) uri_tmp = curl_url ();
if (curl_url_set (uri_tmp, CURLUPART_URL, url, 0) != CURLUE_OK) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"Failed to parse URI '%s'", url); "Failed to parse url '%s'", url);
return NULL;
}
basename = g_path_get_basename (soup_uri_get_path (uri_tmp));
url2 = g_build_filename (priv->firmware_base_uri, basename, NULL);
uri = soup_uri_new (url2);
if (uri == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to parse URI '%s'", url2);
return NULL; return NULL;
} }
curl_url_get (uri_tmp, CURLUPART_PATH, &path, 0);
basename = g_path_get_basename (path);
path_new = g_build_filename (priv->firmware_base_uri, basename, NULL);
curl_url_set (uri, CURLUPART_URL, path_new, 0);
/* use the base URI of the metadata to build the full path */ /* use the base URI of the metadata to build the full path */
} else if (g_strstr_len (url, -1, "/") == NULL) { } else if (g_strstr_len (url, -1, "/") == NULL) {
g_autofree gchar *basename = NULL; g_autofree gchar *basename = NULL;
g_autofree gchar *path = NULL; g_autofree gchar *path_new = NULL;
uri = soup_uri_new (priv->metadata_uri); g_autoptr(curlptr) path = NULL;
if (uri == NULL) { if (curl_url_set (uri, CURLUPART_URL, priv->metadata_uri, 0) != CURLUE_OK) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"Failed to parse metadata URI '%s'", url); "Failed to parse url '%s'",
priv->metadata_uri);
return NULL; return NULL;
} }
basename = g_path_get_dirname (soup_uri_get_path (uri)); curl_url_get (uri, CURLUPART_PATH, &path, 0);
path = g_build_filename (basename, url, NULL); basename = g_path_get_dirname (path);
soup_uri_set_path (uri, path); path_new = g_build_filename (basename, url, NULL);
curl_url_set (uri, CURLUPART_URL, path_new, 0);
/* a normal URI */ /* a normal URI */
} else { } else {
uri = soup_uri_new (url); if (curl_url_set (uri, CURLUPART_URL, url, 0) != CURLUE_OK) {
if (uri == NULL) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
@ -214,10 +213,10 @@ fwupd_remote_build_uri (FwupdRemote *self, const gchar *url, GError **error)
/* set the username and password */ /* set the username and password */
if (priv->username != NULL) if (priv->username != NULL)
soup_uri_set_user (uri, priv->username); curl_url_set (uri, CURLUPART_USER, priv->username, 0);
if (priv->password != NULL) if (priv->password != NULL)
soup_uri_set_password (uri, priv->password); curl_url_set (uri, CURLUPART_PASSWORD, priv->password, 0);
return uri; return g_steal_pointer (&uri);
} }
/* note, this has to be set before username and password */ /* note, this has to be set before username and password */
@ -225,12 +224,6 @@ static void
fwupd_remote_set_metadata_uri (FwupdRemote *self, const gchar *metadata_uri) fwupd_remote_set_metadata_uri (FwupdRemote *self, const gchar *metadata_uri)
{ {
FwupdRemotePrivate *priv = GET_PRIVATE (self); FwupdRemotePrivate *priv = GET_PRIVATE (self);
g_autoptr(SoupURI) uri = NULL;
/* build the URI */
uri = soup_uri_new (metadata_uri);
if (uri == NULL)
return;
/* save this so we can export the object as a GVariant */ /* save this so we can export the object as a GVariant */
priv->metadata_uri = g_strdup (metadata_uri); priv->metadata_uri = g_strdup (metadata_uri);
@ -872,10 +865,12 @@ fwupd_remote_get_checksum (FwupdRemote *self)
gchar * gchar *
fwupd_remote_build_firmware_uri (FwupdRemote *self, const gchar *url, GError **error) fwupd_remote_build_firmware_uri (FwupdRemote *self, const gchar *url, GError **error)
{ {
g_autoptr(SoupURI) uri = fwupd_remote_build_uri (self, url, error); g_autoptr(curlptr) tmp = NULL;
g_autoptr(CURLU) uri = fwupd_remote_build_uri (self, url, error);
if (uri == NULL) if (uri == NULL)
return NULL; return NULL;
return soup_uri_to_string (uri, FALSE); curl_url_get (uri, CURLUPART_URL, &tmp, 0);
return g_strdup (tmp);
} }
/** /**

View File

@ -32,7 +32,7 @@ install_headers([
libfwupd_deps = [ libfwupd_deps = [
giounix, giounix,
soup, libcurl,
libjcat, libjcat,
libjsonglib, libjsonglib,
] ]
@ -122,19 +122,18 @@ if get_option('introspection')
header : 'fwupd.h', header : 'fwupd.h',
dependencies : [ dependencies : [
giounix, giounix,
soup, libcurl,
], ],
includes : [ includes : [
'Gio-2.0', 'Gio-2.0',
'GObject-2.0', 'GObject-2.0',
'Soup-2.4',
], ],
install : true install : true
) )
gnome.generate_vapi('fwupd', gnome.generate_vapi('fwupd',
sources : fwupd_gir[0], sources : fwupd_gir[0],
packages : ['gio-2.0', 'libsoup-2.4'], packages : ['gio-2.0'],
install : true, install : true,
) )
@ -178,7 +177,7 @@ if get_option('tests')
], ],
dependencies : [ dependencies : [
gio, gio,
soup, libcurl,
libjsonglib, libjsonglib,
], ],
link_with : fwupd, link_with : fwupd,

View File

@ -143,7 +143,6 @@ fwupdplugin_pkgg.generate(
'json-glib-1.0', 'json-glib-1.0',
'libarchive', 'libarchive',
'libgcab-1.0', 'libgcab-1.0',
'libsoup-2.4',
'xmlb', 'xmlb',
'jcat', 'jcat',
], ],

View File

@ -208,7 +208,7 @@ endif
libjcat = dependency('jcat', version : '>= 0.1.0', fallback : ['libjcat', 'libjcat_dep']) libjcat = dependency('jcat', version : '>= 0.1.0', fallback : ['libjcat', 'libjcat_dep'])
libjsonglib = dependency('json-glib-1.0', version : '>= 1.1.1') libjsonglib = dependency('json-glib-1.0', version : '>= 1.1.1')
valgrind = dependency('valgrind', required: false) valgrind = dependency('valgrind', required: false)
soup = dependency('libsoup-2.4', version : '>= 2.51.92') libcurl = dependency('libcurl', version : '>= 7.62.0')
if build_daemon if build_daemon
if get_option('polkit') if get_option('polkit')
polkit = dependency('polkit-gobject-1', version : '>= 0.103') polkit = dependency('polkit-gobject-1', version : '>= 0.103')
@ -448,7 +448,6 @@ if build_standalone
plugin_deps += giounix plugin_deps += giounix
plugin_deps += gmodule plugin_deps += gmodule
plugin_deps += gusb plugin_deps += gusb
plugin_deps += soup
plugin_deps += libarchive plugin_deps += libarchive
plugin_deps += gudev plugin_deps += gudev
plugin_deps += libjsonglib plugin_deps += libjsonglib

View File

@ -7,7 +7,7 @@
#include "config.h" #include "config.h"
#include <json-glib/json-glib.h> #include <json-glib/json-glib.h>
#include <libsoup/soup.h> #include <curl/curl.h>
#include <string.h> #include <string.h>
#include "fwupd-error.h" #include "fwupd-error.h"
@ -22,14 +22,11 @@
struct _FuRedfishClient struct _FuRedfishClient
{ {
GObject parent_instance; GObject parent_instance;
SoupSession *session; CURL *curl;
gchar *hostname; gchar *hostname;
guint port; guint port;
gchar *username;
gchar *password;
gchar *update_uri_path; gchar *update_uri_path;
gchar *push_uri_path; gchar *push_uri_path;
gboolean auth_created;
gboolean use_https; gboolean use_https;
gboolean cacheck; gboolean cacheck;
GPtrArray *devices; GPtrArray *devices;
@ -37,62 +34,57 @@ struct _FuRedfishClient
G_DEFINE_TYPE (FuRedfishClient, fu_redfish_client, G_TYPE_OBJECT) G_DEFINE_TYPE (FuRedfishClient, fu_redfish_client, G_TYPE_OBJECT)
static void typedef gchar curlptr;
fu_redfish_client_set_auth (FuRedfishClient *self, SoupURI *uri, G_DEFINE_AUTOPTR_CLEANUP_FUNC(curlptr, curl_free)
SoupMessage *msg) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
static size_t
fu_redfish_client_fetch_data_cb (char *ptr, size_t size, size_t nmemb, void *userdata)
{ {
if ((self->username != NULL && self->password != NULL) && GByteArray *buf = (GByteArray *) userdata;
self->auth_created == FALSE) { gsize realsize = size * nmemb;
/* g_byte_array_append (buf, (const guint8 *) ptr, realsize);
* Some redfish implementations miss WWW-Authenticate return realsize;
* header for a 401 response, and SoupAuthManager couldn't
* generate SoupAuth accordingly. Since DSP0266 makes
* Basic Authorization a requirement for redfish, it shall be
* safe to use Basic Auth for all redfish implementations.
*/
SoupAuthManager *manager = SOUP_AUTH_MANAGER (soup_session_get_feature (self->session, SOUP_TYPE_AUTH_MANAGER));
g_autoptr(SoupAuth) auth = soup_auth_new (SOUP_TYPE_AUTH_BASIC,
msg, "Basic");
soup_auth_authenticate (auth, self->username, self->password);
soup_auth_manager_use_auth (manager, uri, auth);
self->auth_created = TRUE;
}
} }
static GBytes * static GBytes *
fu_redfish_client_fetch_data (FuRedfishClient *self, const gchar *uri_path, GError **error) fu_redfish_client_fetch_data (FuRedfishClient *self, const gchar *uri_path, GError **error)
{ {
guint status_code; CURLcode res;
g_autoptr(SoupMessage) msg = NULL; g_autofree gchar *port = g_strdup_printf ("%u", self->port);
g_autoptr(SoupURI) uri = NULL; g_autoptr(CURLU) uri = NULL;
g_autoptr(GByteArray) buf = g_byte_array_new ();
/* create URI */ /* create URI */
uri = soup_uri_new (NULL); uri = curl_url ();
soup_uri_set_scheme (uri, self->use_https ? "https" : "http"); curl_url_set (uri, CURLU_DEFAULT_SCHEME, self->use_https ? "https" : "http", 0);
soup_uri_set_path (uri, uri_path); curl_url_set (uri, CURLUPART_PATH, uri_path, 0);
soup_uri_set_host (uri, self->hostname); curl_url_set (uri, CURLUPART_HOST, self->hostname, 0);
soup_uri_set_port (uri, self->port); curl_url_set (uri, CURLUPART_PORT, port, 0);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); if (curl_easy_setopt (self->curl, CURLOPT_CURLU, uri) != CURLE_OK) {
if (msg == NULL) { g_set_error_literal (error,
g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE); FWUPD_ERROR,
g_set_error (error, FWUPD_ERROR_INVALID_FILE,
FWUPD_ERROR, "failed to create message for URI");
FWUPD_ERROR_INVALID_FILE,
"failed to create message for URI %s", tmp);
return NULL; return NULL;
} }
fu_redfish_client_set_auth (self, uri, msg); curl_easy_setopt (self->curl, CURLOPT_WRITEFUNCTION, fu_redfish_client_fetch_data_cb);
status_code = soup_session_send_message (self->session, msg); curl_easy_setopt (self->curl, CURLOPT_WRITEDATA, buf);
if (status_code != SOUP_STATUS_OK) { res = curl_easy_perform (self->curl);
g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE); if (res != CURLE_OK) {
glong status_code = 0;
g_autoptr(curlptr) uri_str = NULL;
curl_easy_getinfo (self->curl, CURLINFO_RESPONSE_CODE, &status_code);
curl_url_get (uri, CURLUPART_URL, &uri_str, 0);
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"failed to download %s: %s", "failed to download %s: %s",
tmp, soup_status_get_phrase (status_code)); uri_str, curl_easy_strerror (res));
return NULL; return NULL;
} }
return g_bytes_new (msg->response_body->data, msg->response_body->length);
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
} }
static gboolean static gboolean
@ -569,19 +561,19 @@ fu_redfish_client_set_smbios_interfaces (FuRedfishClient *self,
return TRUE; return TRUE;
} }
G_DEFINE_AUTOPTR_CLEANUP_FUNC(curl_mime, curl_mime_free)
gboolean gboolean
fu_redfish_client_update (FuRedfishClient *self, FuDevice *device, GBytes *blob_fw, fu_redfish_client_update (FuRedfishClient *self, FuDevice *device, GBytes *blob_fw,
GError **error) GError **error)
{ {
CURLcode res;
FwupdRelease *release; FwupdRelease *release;
curl_mimepart *part;
g_autofree gchar *filename = NULL; g_autofree gchar *filename = NULL;
g_autofree gchar *port = g_strdup_printf ("%u", self->port);
guint status_code; g_autoptr(CURLU) uri = curl_url ();
g_autoptr(SoupMessage) msg = NULL; g_autoptr(curl_mime) mime = curl_mime_init (self->curl);
g_autoptr(SoupURI) uri = NULL;
g_autoptr(SoupMultipart) multipart = NULL;
g_autoptr(SoupBuffer) buffer = NULL;
g_autofree gchar *uri_str = NULL;
/* Get the update version */ /* Get the update version */
release = fwupd_device_get_release_default (FWUPD_DEVICE (device)); release = fwupd_device_get_release_default (FWUPD_DEVICE (device));
@ -595,38 +587,35 @@ fu_redfish_client_update (FuRedfishClient *self, FuDevice *device, GBytes *blob_
} }
/* create URI */ /* create URI */
uri = soup_uri_new (NULL); curl_url_set (uri, CURLU_DEFAULT_SCHEME, self->use_https ? "https" : "http", 0);
soup_uri_set_scheme (uri, self->use_https ? "https" : "http"); curl_url_set (uri, CURLUPART_PATH, self->push_uri_path, 0);
soup_uri_set_path (uri, self->push_uri_path); curl_url_set (uri, CURLUPART_HOST, self->hostname, 0);
soup_uri_set_host (uri, self->hostname); curl_url_set (uri, CURLUPART_PORT, port, 0);
soup_uri_set_port (uri, self->port); if (curl_easy_setopt (self->curl, CURLOPT_CURLU, uri) != CURLE_OK) {
uri_str = soup_uri_to_string (uri, FALSE); g_set_error_literal (error,
FWUPD_ERROR,
/* Create the multipart request */ FWUPD_ERROR_INVALID_FILE,
multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); "failed to create message for URI");
buffer = soup_buffer_new (SOUP_MEMORY_COPY,
g_bytes_get_data (blob_fw, NULL),
g_bytes_get_size (blob_fw));
soup_multipart_append_form_file (multipart, filename, filename,
"application/octet-stream",
buffer);
msg = soup_form_request_new_from_multipart (uri_str, multipart);
if (msg == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to create message for URI %s", uri_str);
return FALSE; return FALSE;
} }
fu_redfish_client_set_auth (self, uri, msg);
status_code = soup_session_send_message (self->session, msg); /* Create the multipart request */
if (status_code != SOUP_STATUS_OK) { curl_easy_setopt (self->curl, CURLOPT_MIMEPOST, mime);
part = curl_mime_addpart (mime);
curl_mime_data (part, g_bytes_get_data (blob_fw, NULL), g_bytes_get_size (blob_fw));
curl_mime_type (part, "application/octet-stream");
res = curl_easy_perform (self->curl);
if (res != CURLE_OK) {
glong status_code = 0;
g_autoptr(curlptr) uri_str = NULL;
curl_easy_getinfo (self->curl, CURLINFO_RESPONSE_CODE, &status_code);
curl_url_get (uri, CURLUPART_URL, &uri_str, 0);
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"failed to upload %s to %s: %s", "failed to upload %s to %s: %s",
filename, uri_str, filename, uri_str,
soup_status_get_phrase (status_code)); curl_easy_strerror (res));
return FALSE; return FALSE;
} }
@ -662,24 +651,12 @@ fu_redfish_client_setup (FuRedfishClient *self, GBytes *smbios_table, GError **e
return FALSE; return FALSE;
} }
/* create the soup session */ /* setup networking */
user_agent = g_strdup_printf ("%s/%s", PACKAGE_NAME, PACKAGE_VERSION); user_agent = g_strdup_printf ("%s/%s", PACKAGE_NAME, PACKAGE_VERSION);
self->session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, curl_easy_setopt (self->curl, CURLOPT_USERAGENT , user_agent);
SOUP_SESSION_TIMEOUT, 60, curl_easy_setopt (self->curl, CURLOPT_CONNECTTIMEOUT, 60L);
NULL); if (self->cacheck == FALSE)
if (self->session == NULL) { curl_easy_setopt (self->curl, CURLOPT_SSL_VERIFYPEER , 0L);
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to setup networking");
return FALSE;
}
if (self->cacheck == FALSE) {
g_object_set (G_OBJECT (self->session),
SOUP_SESSION_SSL_STRICT, FALSE,
NULL);
}
/* this is optional */ /* this is optional */
if (smbios_table != NULL) { if (smbios_table != NULL) {
@ -700,10 +677,6 @@ fu_redfish_client_setup (FuRedfishClient *self, GBytes *smbios_table, GError **e
g_debug ("Hostname: %s", self->hostname); g_debug ("Hostname: %s", self->hostname);
if (self->port != 0) if (self->port != 0)
g_debug ("Port: %u", self->port); g_debug ("Port: %u", self->port);
if (self->username != NULL)
g_debug ("Username: %s", self->username);
if (self->password != NULL)
g_debug ("Password: %s", self->password);
/* try to connect */ /* try to connect */
blob = fu_redfish_client_fetch_data (self, "/redfish/v1/", error); blob = fu_redfish_client_fetch_data (self, "/redfish/v1/", error);
@ -800,28 +773,24 @@ fu_redfish_client_set_cacheck (FuRedfishClient *self, gboolean cacheck)
void void
fu_redfish_client_set_username (FuRedfishClient *self, const gchar *username) fu_redfish_client_set_username (FuRedfishClient *self, const gchar *username)
{ {
g_free (self->username); curl_easy_setopt (self->curl, CURLOPT_USERNAME, username);
self->username = g_strdup (username);
} }
void void
fu_redfish_client_set_password (FuRedfishClient *self, const gchar *password) fu_redfish_client_set_password (FuRedfishClient *self, const gchar *password)
{ {
g_free (self->password); curl_easy_setopt (self->curl, CURLOPT_PASSWORD, password);
self->password = g_strdup (password);
} }
static void static void
fu_redfish_client_finalize (GObject *object) fu_redfish_client_finalize (GObject *object)
{ {
FuRedfishClient *self = FU_REDFISH_CLIENT (object); FuRedfishClient *self = FU_REDFISH_CLIENT (object);
if (self->session != NULL) if (self->curl != NULL)
g_object_unref (self->session); curl_easy_cleanup (self->curl);
g_free (self->update_uri_path); g_free (self->update_uri_path);
g_free (self->push_uri_path); g_free (self->push_uri_path);
g_free (self->hostname); g_free (self->hostname);
g_free (self->username);
g_free (self->password);
g_ptr_array_unref (self->devices); g_ptr_array_unref (self->devices);
G_OBJECT_CLASS (fu_redfish_client_parent_class)->finalize (object); G_OBJECT_CLASS (fu_redfish_client_parent_class)->finalize (object);
} }
@ -837,6 +806,11 @@ static void
fu_redfish_client_init (FuRedfishClient *self) fu_redfish_client_init (FuRedfishClient *self)
{ {
self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
self->curl = curl_easy_init ();
/* since DSP0266 makes Basic Authorization a requirement,
* it is safe to use Basic Auth for all implementations */
curl_easy_setopt (self->curl, CURLOPT_HTTPAUTH, (glong) CURLAUTH_BASIC);
} }
FuRedfishClient * FuRedfishClient *

View File

@ -21,6 +21,7 @@ shared_module('fu_plugin_redfish',
c_args : cargs, c_args : cargs,
dependencies : [ dependencies : [
plugin_deps, plugin_deps,
libcurl,
libjsonglib, libjsonglib,
], ],
) )
@ -45,6 +46,7 @@ if get_option('tests')
], ],
dependencies : [ dependencies : [
plugin_deps, plugin_deps,
libcurl,
libjsonglib, libjsonglib,
], ],
link_with : [ link_with : [

View File

@ -35,6 +35,8 @@ apps:
command: fwupdagent.wrapper command: fwupdagent.wrapper
parts: parts:
curl:
source: https://curl.se/download/curl-7.73.0.tar.bz2
tpm2-tss: tpm2-tss:
plugin: autotools plugin: autotools
source: https://github.com/tpm2-software/tpm2-tss/releases/download/2.3.0/tpm2-tss-2.3.0.tar.gz source: https://github.com/tpm2-software/tpm2-tss/releases/download/2.3.0/tpm2-tss-2.3.0.tar.gz
@ -211,7 +213,6 @@ parts:
- libpci-dev - libpci-dev
- libpolkit-gobject-1-dev - libpolkit-gobject-1-dev
- libsmbios-dev - libsmbios-dev
- libsoup2.4-dev
- libsqlite3-dev - libsqlite3-dev
- libsystemd-dev - libsystemd-dev
- locales - locales
@ -232,7 +233,6 @@ parts:
- libjson-glib-1.0-0 - libjson-glib-1.0-0
- libpolkit-gobject-1-0 - libpolkit-gobject-1-0
- libsmbios-c2 - libsmbios-c2
- libsoup2.4-1
- glib-networking - glib-networking
- libglib2.0-bin - libglib2.0-bin
prime: prime:
@ -272,7 +272,7 @@ parts:
- -usr/lib/*/pkgconfig - -usr/lib/*/pkgconfig
# we don't want system gnutls leaking in # we don't want system gnutls leaking in
- -usr/lib/*/libgnutls* - -usr/lib/*/libgnutls*
after: [meson, build-introspection, modemmanager, libmbim, libqmi, tpm2-tss, gnutls] after: [meson, build-introspection, modemmanager, libmbim, libqmi, tpm2-tss, gnutls, curl]
fix-bash-completion: fix-bash-completion:
plugin: make plugin: make
source: contrib/snap/fix-bash-completion source: contrib/snap/fix-bash-completion

View File

@ -18,7 +18,6 @@
#include <locale.h> #include <locale.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <libsoup/soup.h>
#include <jcat.h> #include <jcat.h>
#include "fu-device-private.h" #include "fu-device-private.h"
@ -912,13 +911,11 @@ static gchar *
fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
{ {
g_autofree gchar *filename = NULL; g_autofree gchar *filename = NULL;
g_autoptr(SoupURI) uri = NULL;
/* a local file */ /* a local file */
uri = soup_uri_new (perhapsfn);
if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS)) if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
return g_strdup (perhapsfn); return g_strdup (perhapsfn);
if (uri == NULL) if (!fu_util_is_url (perhapsfn))
return g_strdup (perhapsfn); return g_strdup (perhapsfn);
/* download the firmware to a cachedir */ /* download the firmware to a cachedir */
@ -1079,7 +1076,6 @@ fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
const gchar *remote_id; const gchar *remote_id;
const gchar *uri_tmp; const gchar *uri_tmp;
g_auto(GStrv) argv = NULL; g_auto(GStrv) argv = NULL;
g_autoptr(SoupURI) uri = NULL;
uri_tmp = fwupd_release_get_uri (rel); uri_tmp = fwupd_release_get_uri (rel);
if (uri_tmp == NULL) { if (uri_tmp == NULL) {
@ -1107,8 +1103,8 @@ fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
argv = g_new0 (gchar *, 2); argv = g_new0 (gchar *, 2);
/* local remotes may have the firmware already */ /* local remotes may have the firmware already */
uri = soup_uri_new (uri_tmp); if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) { !fu_util_is_url (uri_tmp)) {
const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
g_autofree gchar *path = g_path_get_dirname (fn_cache); g_autofree gchar *path = g_path_get_dirname (fn_cache);
argv[0] = g_build_filename (path, uri_tmp, NULL); argv[0] = g_build_filename (path, uri_tmp, NULL);

View File

@ -13,6 +13,7 @@
#include <gusb.h> #include <gusb.h>
#include <xmlb.h> #include <xmlb.h>
#include <fwupd.h> #include <fwupd.h>
#include <curl/curl.h>
#include "fu-common.h" #include "fu-common.h"
#include "fu-device-private.h" #include "fu-device-private.h"
@ -2007,3 +2008,12 @@ fu_util_show_unsupported_warn (void)
g_printerr ("%s %s\n", fmt, _("This package has not been validated, it may not work properly.")); g_printerr ("%s %s\n", fmt, _("This package has not been validated, it may not work properly."));
#endif #endif
} }
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
gboolean
fu_util_is_url (const gchar *perhaps_url)
{
g_autoptr(CURLU) h = curl_url ();
return curl_url_set (h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK;
}

View File

@ -8,7 +8,6 @@
#include <glib.h> #include <glib.h>
#include <fwupd.h> #include <fwupd.h>
#include <libsoup/soup.h>
/* this is only valid for tools */ /* this is only valid for tools */
#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) #define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1)
@ -119,3 +118,4 @@ gboolean fu_util_switch_branch_warning (FwupdDevice *dev,
gboolean assume_yes, gboolean assume_yes,
GError **error); GError **error);
void fu_util_show_unsupported_warn (void); void fu_util_show_unsupported_warn (void);
gboolean fu_util_is_url (const gchar *perhaps_url);

View File

@ -584,13 +584,11 @@ fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GErro
{ {
g_autofree gchar *filename = NULL; g_autofree gchar *filename = NULL;
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
g_autoptr(SoupURI) uri = NULL;
/* a local file */ /* a local file */
if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS)) if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
return g_strdup (perhapsfn); return g_strdup (perhapsfn);
uri = soup_uri_new (perhapsfn); if (!fu_util_is_url (perhapsfn))
if (uri == NULL)
return g_strdup (perhapsfn); return g_strdup (perhapsfn);
/* download the firmware to a cachedir */ /* download the firmware to a cachedir */

View File

@ -34,11 +34,11 @@ fwupdmgr = executable(
fwupdplugin_incdir, fwupdplugin_incdir,
], ],
dependencies : [ dependencies : [
libcurl,
libxmlb, libxmlb,
giounix, giounix,
gudev, gudev,
gusb, gusb,
soup,
sqlite, sqlite,
libarchive, libarchive,
libjsonglib, libjsonglib,
@ -67,11 +67,11 @@ fwupdagent = executable(
fwupdplugin_incdir, fwupdplugin_incdir,
], ],
dependencies : [ dependencies : [
libcurl,
libxmlb, libxmlb,
giounix, giounix,
gudev, gudev,
gusb, gusb,
soup,
libjsonglib, libjsonglib,
], ],
link_with : [ link_with : [
@ -102,9 +102,9 @@ fwupdoffline = executable(
giounix, giounix,
gudev, gudev,
gusb, gusb,
libcurl,
libjsonglib, libjsonglib,
libxmlb, libxmlb,
soup,
sqlite, sqlite,
], ],
link_with : [ link_with : [
@ -153,6 +153,7 @@ fwupdtool = executable(
fwupdplugin_incdir, fwupdplugin_incdir,
], ],
dependencies : [ dependencies : [
libcurl,
libjcat, libjcat,
libxmlb, libxmlb,
libgcab, libgcab,
@ -160,7 +161,6 @@ fwupdtool = executable(
gmodule, gmodule,
gudev, gudev,
gusb, gusb,
soup,
sqlite, sqlite,
valgrind, valgrind,
libarchive, libarchive,
@ -263,7 +263,6 @@ executable(
gmodule, gmodule,
gudev, gudev,
gusb, gusb,
soup,
sqlite, sqlite,
valgrind, valgrind,
libarchive, libarchive,
@ -323,7 +322,6 @@ if get_option('tests')
gmodule, gmodule,
gudev, gudev,
gusb, gusb,
soup,
sqlite, sqlite,
valgrind, valgrind,
libarchive, libarchive,