diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index e5332c986..ecc8b28ef 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -23,7 +23,9 @@ #include "fwupd-common-private.h" +#include #include +#include /** * fwupd_checksum_guess_kind: @@ -133,3 +135,115 @@ fwupd_checksum_get_best (GPtrArray *checksums) } return NULL; } + +static gchar * +fwupd_build_user_agent_os_release (void) +{ + const gchar *keys[] = { "NAME=", "VERSION_ID=", "VARIANT=", NULL }; + const gchar *values[] = { NULL, NULL, NULL, NULL }; + g_autofree gchar *os_release = NULL; + g_auto(GStrv) lines = NULL; + g_autoptr(GPtrArray) ids_os = g_ptr_array_new (); + + /* get raw data then parse each line */ + if (!g_file_get_contents ("/etc/os-release", &os_release, NULL, NULL)) { + if (!g_file_get_contents ("/usr/lib/os-release", &os_release, NULL, NULL)) + return NULL; + } + lines = g_strsplit (os_release, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + for (guint j = 0; keys[j] != NULL; j++) { + if (g_str_has_prefix (lines[i], keys[j])) { + values[j] = lines[i] + strlen (keys[j]); + break; + } + } + } + + /* create an array of the keys that exist */ + for (guint j = 0; values[j] != NULL; j++) { + if (values[j] != NULL) + g_ptr_array_add (ids_os, (gpointer) values[j]); + } + if (ids_os->len == 0) + return NULL; + g_ptr_array_add (ids_os, NULL); + return g_strjoinv (" ", (gchar **) ids_os->pdata); +} + +static gchar * +fwupd_build_user_agent_system (void) +{ + struct utsname name_tmp = { 0 }; + g_autofree gchar *locale = NULL; + g_autofree gchar *os_release = NULL; + g_autoptr(GPtrArray) ids = g_ptr_array_new_with_free_func (g_free); + + /* system, architecture and kernel, e.g. "Linux i686 4.14.5" */ + if (uname (&name_tmp) >= 0) { + g_ptr_array_add (ids, g_strdup_printf ("%s %s %s", + name_tmp.sysname, + name_tmp.machine, + name_tmp.release)); + } + + /* current locale, e.g. "en-gb" */ + locale = g_strdup (setlocale (LC_MESSAGES, NULL)); + if (locale != NULL) { + g_strdelimit (locale, ".", '\0'); + g_strdelimit (locale, "_", '-'); + g_ptr_array_add (ids, g_steal_pointer (&locale)); + } + + /* OS release, e.g. "Fedora 27 Workstation" */ + os_release = fwupd_build_user_agent_os_release (); + if (os_release != NULL) + g_ptr_array_add (ids, g_steal_pointer (&os_release)); + + /* convert to string */ + if (ids->len == 0) + return NULL; + g_ptr_array_add (ids, NULL); + return g_strjoinv ("; ", (gchar **) ids->pdata); +} + +/** + * fwupd_build_user_agent: + * @package_name: client program name, e.g. "gnome-software" + * @package_version: client program version, e.g. "3.28.1" + * + * Builds a user-agent to use for the download. + * + * Supplying harmless details to the server means it knows more about each + * client. This allows the web service to respond in a different way, for + * instance sending a different metadata file for old versions of fwupd, or + * returning an error for Solaris machines. + * + * Before freaking out about theoretical privacy implications, much more data + * than this is sent to each and every website you visit. + * + * Returns: a string, e.g. `foo/0.1 (Linux i386 4.14.5; en; Fedora 27) fwupd/1.0.3` + * + * Since: 1.0.3 + **/ +gchar * +fwupd_build_user_agent (const gchar *package_name, const gchar *package_version) +{ + GString *str = g_string_new (NULL); + g_autofree gchar *system = NULL; + + /* application name and version */ + g_string_append_printf (str, "%s/%s", package_name, package_version); + + /* system information */ + system = fwupd_build_user_agent_system (); + if (system != NULL) + g_string_append_printf (str, " (%s)", system); + + /* platform, which in our case is just fwupd */ + if (g_strcmp0 (package_name, "fwupd") != 0) + g_string_append_printf (str, " fwupd/%s", PACKAGE_VERSION); + + /* success */ + return g_string_free (str, FALSE); +} diff --git a/libfwupd/fwupd-common.h b/libfwupd/fwupd-common.h index 9eae28cb0..b1f4e3282 100644 --- a/libfwupd/fwupd-common.h +++ b/libfwupd/fwupd-common.h @@ -34,5 +34,7 @@ const gchar *fwupd_checksum_get_best (GPtrArray *checksums); const gchar *fwupd_checksum_get_by_kind (GPtrArray *checksums, GChecksumType kind); GChecksumType fwupd_checksum_guess_kind (const gchar *checksum); +gchar *fwupd_build_user_agent (const gchar *package_name, + const gchar *package_version); #endif /* __FWUPD_COMMON_H */ diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index e89476fd0..23bb7af32 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -215,3 +215,9 @@ LIBFWUPD_1.0.0 { fwupd_remote_to_variant; local: *; } LIBFWUPD_0.9.8; + +LIBFWUPD_1.0.3 { + global: + fwupd_build_user_agent; + local: *; +} LIBFWUPD_1.0.0; diff --git a/src/fu-util.c b/src/fu-util.c index ed29644b4..1aaf3ab85 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -652,7 +652,7 @@ fu_util_download_file (FuUtilPrivate *priv, } /* create the soup session */ - user_agent = g_strdup_printf ("%s/%s", PACKAGE_NAME, PACKAGE_VERSION); + user_agent = fwupd_build_user_agent (PACKAGE_NAME, PACKAGE_VERSION); session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, SOUP_SESSION_TIMEOUT, 60, NULL);