Blame SOURCES/0080-ureport-aggressive-refactorization-of-uReport-source.patch

4b6aa8
From 85b9ada575f47a5f25f1ffeaccf9c4e1142fd689 Mon Sep 17 00:00:00 2001
4b6aa8
From: Jakub Filak <jfilak@redhat.com>
4b6aa8
Date: Sat, 13 Sep 2014 22:26:53 +0200
4b6aa8
Subject: [LIBREPORT PATCH 80/93] ureport: aggressive refactorization of
4b6aa8
 uReport source
4b6aa8
4b6aa8
Simplify/unify the uReport API and provide a development documentation
4b6aa8
in form of Doxygen comments.
4b6aa8
4b6aa8
Related to rhbz1139987
4b6aa8
4b6aa8
Signed-off-by: Jakub Filak <jfilak@redhat.com>
4b6aa8
---
4b6aa8
 po/POTFILES.in                 |   4 +-
4b6aa8
 src/include/ureport.h          | 185 +++++++++++-
4b6aa8
 src/lib/ureport.c              | 633 ++++++++++++++++++++++++++++++++++++++---
4b6aa8
 src/plugins/reporter-ureport.c | 581 ++-----------------------------------
4b6aa8
 4 files changed, 793 insertions(+), 610 deletions(-)
4b6aa8
4b6aa8
diff --git a/po/POTFILES.in b/po/POTFILES.in
4b6aa8
index 7ba080c..00046e2 100644
4b6aa8
--- a/po/POTFILES.in
4b6aa8
+++ b/po/POTFILES.in
4b6aa8
@@ -19,7 +19,7 @@ src/lib/client.c
4b6aa8
 src/lib/create_dump_dir.c
4b6aa8
 src/lib/curl.c
4b6aa8
 src/lib/event_config.c
4b6aa8
-src/lib/json.c
4b6aa8
+src/lib/ureport.c
4b6aa8
 src/lib/make_descr.c
4b6aa8
 src/lib/parse_options.c
4b6aa8
 src/lib/problem_data.c
4b6aa8
@@ -42,7 +42,7 @@ src/plugins/report_Uploader.xml.in
4b6aa8
 src/plugins/report_uReport.xml.in
4b6aa8
 src/plugins/report_EmergencyAnalysis.xml.in
4b6aa8
 src/plugins/rhbz.c
4b6aa8
-src/plugins/ureport.c
4b6aa8
+src/plugins/reporter-ureport.c
4b6aa8
 src/report-newt/report-newt.c
4b6aa8
 src/workflows/workflow_AnacondaFedora.xml.in
4b6aa8
 src/workflows/workflow_AnacondaRHEL.xml.in
4b6aa8
diff --git a/src/include/ureport.h b/src/include/ureport.h
4b6aa8
index d341f6e..3fffed7 100644
4b6aa8
--- a/src/include/ureport.h
4b6aa8
+++ b/src/include/ureport.h
4b6aa8
@@ -19,12 +19,19 @@
4b6aa8
 #ifndef UREPORT_H_
4b6aa8
 #define UREPORT_H_
4b6aa8
 
4b6aa8
-#include "problem_data.h"
4b6aa8
-
4b6aa8
 #ifdef __cplusplus
4b6aa8
 extern "C" {
4b6aa8
 #endif
4b6aa8
 
4b6aa8
+#define UREPORT_CONF_FILE_PATH PLUGINS_CONF_DIR"/ureport.conf"
4b6aa8
+
4b6aa8
+#define UREPORT_OPTION_VALUE_FROM_CONF(settings, opt, var, tr) do { const char *value = getenv("uReport_"opt); \
4b6aa8
+        if (!value) { value = get_map_string_item_or_NULL(settings, opt); } if (value) { var = tr(value); } \
4b6aa8
+    } while(0)
4b6aa8
+
4b6aa8
+#define UREPORT_SUBMIT_ACTION "reports/new/"
4b6aa8
+#define UREPORT_ATTACH_ACTION "reports/attach/"
4b6aa8
+
4b6aa8
 /*
4b6aa8
  * uReport generation configuration
4b6aa8
  */
4b6aa8
@@ -48,22 +55,176 @@ struct ureport_server_config
4b6aa8
     struct ureport_preferences ur_prefs; ///< configuration for uReport generation
4b6aa8
 };
4b6aa8
 
4b6aa8
-struct abrt_post_state;
4b6aa8
+/*
4b6aa8
+ * Initialize structure members
4b6aa8
+ *
4b6aa8
+ * @param config Initialized structure
4b6aa8
+ */
4b6aa8
+#define ureport_server_config_init libreport_ureport_server_config_init
4b6aa8
+void
4b6aa8
+ureport_server_config_init(struct ureport_server_config *config);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Release all allocated resources
4b6aa8
+ *
4b6aa8
+ * @param config Released structure
4b6aa8
+ */
4b6aa8
+#define ureport_server_config_destroy libreport_ureport_server_config_destroy
4b6aa8
+void
4b6aa8
+ureport_server_config_destroy(struct ureport_server_config *config);
4b6aa8
 
4b6aa8
-#define ureport_post libreport_ureport_post
4b6aa8
-struct post_state *ureport_post(const char *json_ureport,
4b6aa8
-                                struct ureport_server_config *config);
4b6aa8
+/*
4b6aa8
+ * Loads uReport configuration from various sources.
4b6aa8
+ *
4b6aa8
+ * Replaces a value of an already configured option only if the
4b6aa8
+ * option was found in a configuration source.
4b6aa8
+ *
4b6aa8
+ * @param config a server configuration to be populated
4b6aa8
+ */
4b6aa8
+#define ureport_server_config_load libreport_ureport_server_config_load
4b6aa8
+void
4b6aa8
+ureport_server_config_load(struct ureport_server_config *config,
4b6aa8
+                           map_string_t *settings);
4b6aa8
 
4b6aa8
-#define ureport_attach_rhbz libreport_ureport_attach_rhbz
4b6aa8
-struct post_state *ureport_attach_rhbz(const char *bthash, int rhbz_bug_id,
4b6aa8
+/*
4b6aa8
+ * Configure client certificate paths
4b6aa8
+ *
4b6aa8
+ * @param config Where the paths are stored
4b6aa8
+ * @param client_path Path in form of cert_full_path:key_full_path or one of
4b6aa8
+ *        the following string: 'rhsm', 'puppet'.
4b6aa8
+ */
4b6aa8
+#define ureport_server_config_set_client_auth libreport_ureport_server_config_set_client_auth
4b6aa8
+void
4b6aa8
+ureport_server_config_set_client_auth(struct ureport_server_config *config,
4b6aa8
+                                      const char *client_auth);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * uReport server response
4b6aa8
+ */
4b6aa8
+struct ureport_server_response
4b6aa8
+{
4b6aa8
+    bool urr_is_error;  ///< True if server replied with error response
4b6aa8
+    char *urr_value;    ///< Value of the response
4b6aa8
+    char *urr_message;  ///< Additional message
4b6aa8
+    char *urr_bthash;   ///< uReport's server side identifier
4b6aa8
+    GList *urr_reported_to_list; ///< Known external reports for uReport
4b6aa8
+                                 ///< in *reported_to* format
4b6aa8
+    char *urr_solution; ///< URL pointing to solution for uReport
4b6aa8
+};
4b6aa8
+
4b6aa8
+/* Can't include "abrt_curl.h", it's not a public API.
4b6aa8
+ * Resorting to just forward-declaring the struct we need.
4b6aa8
+ */
4b6aa8
+struct post_state;
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Parse server reply
4b6aa8
+ *
4b6aa8
+ * @param post_state Server reply
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return Pointer to malloced memory or NULL in case of error in communication
4b6aa8
+ */
4b6aa8
+#define ureport_server_response_from_reply libreport_ureport_server_response_from_reply
4b6aa8
+struct ureport_server_response *
4b6aa8
+ureport_server_response_from_reply(struct post_state *post_state,
4b6aa8
+                                   struct ureport_server_config *config);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Save response in dump dir files
4b6aa8
+ *
4b6aa8
+ * @param resp Parsed server response
4b6aa8
+ * @param dump_dir_pat Path to dump directory
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return False in case of any error; otherwise True.
4b6aa8
+ */
4b6aa8
+#define ureport_server_response_save_in_dump_dir libreport_ureport_server_response_save_in_dump_dir
4b6aa8
+bool
4b6aa8
+ureport_server_response_save_in_dump_dir(struct ureport_server_response *resp,
4b6aa8
+                                         const char *dump_dir_path,
4b6aa8
+                                         struct ureport_server_config *config);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Build URL to submitted uReport
4b6aa8
+ *
4b6aa8
+ * @param resp Parsed server response
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return Malloced zero-terminated string
4b6aa8
+ */
4b6aa8
+#define ureport_server_response_get_report_url libreport_ureport_server_response_get_report_url
4b6aa8
+char *
4b6aa8
+ureport_server_response_get_report_url(struct ureport_server_response *resp,
4b6aa8
                                        struct ureport_server_config *config);
4b6aa8
 
4b6aa8
-#define ureport_attach_email libreport_ureport_attach_email
4b6aa8
-struct post_state *ureport_attach_email(const char *bthash, const char *email,
4b6aa8
-                                        struct ureport_server_config *config);
4b6aa8
+/*
4b6aa8
+ * Release allocated resources
4b6aa8
+ *
4b6aa8
+ * @param resp Released structured
4b6aa8
+ */
4b6aa8
+#define ureport_server_response_free libreport_ureport_server_response_free
4b6aa8
+void
4b6aa8
+ureport_server_response_free(struct ureport_server_response *resp);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Send JSON to server and obtain reply
4b6aa8
+ *
4b6aa8
+ * @param json Sent data
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @param url_sfx Local part of the upload URL
4b6aa8
+ * @return Malloced server reply or NULL in case of communication errors
4b6aa8
+ */
4b6aa8
+#define ureport_do_post libreport_ureport_do_post
4b6aa8
+struct post_state *
4b6aa8
+ureport_do_post(const char *json, struct ureport_server_config *config,
4b6aa8
+                const char *url_sfx);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Submit uReport on server
4b6aa8
+ *
4b6aa8
+ * @param json Sent data
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return Malloced, parsed server response
4b6aa8
+ */
4b6aa8
+#define ureport_submit libreport_ureport_submit
4b6aa8
+struct ureport_server_response *
4b6aa8
+ureport_submit(const char *json_ureport, struct ureport_server_config *config);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Attach given string to uReport
4b6aa8
+ *
4b6aa8
+ * @param bthash uReport identifier
4b6aa8
+ * @param type Type of attachment
4b6aa8
+ * @param data Attached data
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return False in case of any error; otherwise True
4b6aa8
+ */
4b6aa8
+#define ureport_attach_string libreport_ureport_attach_string
4b6aa8
+bool
4b6aa8
+ureport_attach_string(const char *bthash, const char *type, const char *data,
4b6aa8
+               struct ureport_server_config *config);
4b6aa8
 
4b6aa8
+/*
4b6aa8
+ * Attach given integer to uReport
4b6aa8
+ *
4b6aa8
+ * @param bthash uReport identifier
4b6aa8
+ * @param type Type of attachment
4b6aa8
+ * @param data Attached data
4b6aa8
+ * @param config Configuration used in communication
4b6aa8
+ * @return False in case of any error; otherwise True
4b6aa8
+ */
4b6aa8
+#define ureport_attach_int libreport_ureport_attach_int
4b6aa8
+bool
4b6aa8
+ureport_attach_int(const char *bthash, const char *type, int data,
4b6aa8
+                   struct ureport_server_config *config);
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Build uReport from dump dir
4b6aa8
+ *
4b6aa8
+ * @param dump_dir_path FS path to dump dir
4b6aa8
+ * @return Malloced JSON string
4b6aa8
+ */
4b6aa8
 #define ureport_from_dump_dir libreport_ureport_from_dump_dir
4b6aa8
-char *ureport_from_dump_dir(const char *dump_dir_path);
4b6aa8
+char *
4b6aa8
+ureport_from_dump_dir(const char *dump_dir_path);
4b6aa8
 
4b6aa8
 #define ureport_from_dump_dir_ext libreport_ureport_from_dump_dir_ext
4b6aa8
 char *ureport_from_dump_dir_ext(const char *dump_dir_path,
4b6aa8
diff --git a/src/lib/ureport.c b/src/lib/ureport.c
4b6aa8
index 761fe62..e1816ef 100644
4b6aa8
--- a/src/lib/ureport.c
4b6aa8
+++ b/src/lib/ureport.c
4b6aa8
@@ -26,9 +26,528 @@
4b6aa8
 #include "ureport.h"
4b6aa8
 #include "libreport_curl.h"
4b6aa8
 
4b6aa8
+#define DESTROYED_POINTER (void *)0xdeadbeef
4b6aa8
 
4b6aa8
-static void ureport_add_str(struct json_object *ur, const char *key,
4b6aa8
-                            const char *s)
4b6aa8
+#define BTHASH_URL_SFX "reports/bthash/"
4b6aa8
+
4b6aa8
+#define RHSM_CERT_PATH "/etc/pki/consumer/cert.pem"
4b6aa8
+#define RHSM_KEY_PATH "/etc/pki/consumer/key.pem"
4b6aa8
+
4b6aa8
+#define RHSMENT_PEM_DIR_PATH "/etc/pki/entitlement"
4b6aa8
+#define RHSMENT_ENT_DATA_BEGIN_TAG "-----BEGIN ENTITLEMENT DATA-----"
4b6aa8
+#define RHSMENT_ENT_DATA_END_TAG "-----END ENTITLEMENT DATA-----"
4b6aa8
+#define RHSMENT_SIG_DATA_BEGIN_TAG "-----BEGIN RSA SIGNATURE-----"
4b6aa8
+#define RHSMENT_SIG_DATA_END_TAG "-----END RSA SIGNATURE-----"
4b6aa8
+
4b6aa8
+static char *
4b6aa8
+puppet_config_print(const char *key)
4b6aa8
+{
4b6aa8
+    char *command = xasprintf("puppet config print %s", key);
4b6aa8
+    char *result = run_in_shell_and_save_output(0, command, NULL, NULL);
4b6aa8
+    free(command);
4b6aa8
+
4b6aa8
+    /* run_in_shell_and_save_output always returns non-NULL */
4b6aa8
+    if (result[0] != '/')
4b6aa8
+        goto error;
4b6aa8
+
4b6aa8
+    char *newline = strchrnul(result, '\n');
4b6aa8
+    if (!newline)
4b6aa8
+        goto error;
4b6aa8
+
4b6aa8
+    *newline = '\0';
4b6aa8
+    return result;
4b6aa8
+error:
4b6aa8
+    free(result);
4b6aa8
+    error_msg_and_die("Unable to determine puppet %s path (puppet not installed?)", key);
4b6aa8
+}
4b6aa8
+
4b6aa8
+void
4b6aa8
+ureport_server_config_set_client_auth(struct ureport_server_config *config,
4b6aa8
+                                      const char *client_auth)
4b6aa8
+{
4b6aa8
+    if (client_auth == NULL)
4b6aa8
+        return;
4b6aa8
+
4b6aa8
+    if (strcmp(client_auth, "") == 0)
4b6aa8
+    {
4b6aa8
+        config->ur_client_cert = NULL;
4b6aa8
+        config->ur_client_key = NULL;
4b6aa8
+        log_notice("Not using client authentication");
4b6aa8
+    }
4b6aa8
+    else if (strcmp(client_auth, "rhsm") == 0)
4b6aa8
+    {
4b6aa8
+        config->ur_client_cert = xstrdup(RHSM_CERT_PATH);
4b6aa8
+        config->ur_client_key = xstrdup(RHSM_KEY_PATH);
4b6aa8
+    }
4b6aa8
+    else if (strcmp(client_auth, "rhsm-entitlement") == 0)
4b6aa8
+    {
4b6aa8
+        GList *certs = get_file_list(RHSMENT_PEM_DIR_PATH, "pem");
4b6aa8
+        if (g_list_length(certs) != 2)
4b6aa8
+        {
4b6aa8
+            log_notice(RHSMENT_PEM_DIR_PATH" does not contain unique cert-key files pair");
4b6aa8
+            log_notice("Not using client authentication");
4b6aa8
+            return;
4b6aa8
+        }
4b6aa8
+
4b6aa8
+        const char *cert = NULL;
4b6aa8
+        const char *key = NULL;
4b6aa8
+
4b6aa8
+        file_obj_t *fst = (file_obj_t *)certs->data;
4b6aa8
+        file_obj_t *scn = (file_obj_t *)certs->next->data;
4b6aa8
+
4b6aa8
+        if (strlen(fo_get_filename(fst)) < strlen(fo_get_filename(scn)))
4b6aa8
+        {
4b6aa8
+            cert = fo_get_filename(fst);
4b6aa8
+            key = fo_get_filename(scn);
4b6aa8
+
4b6aa8
+            config->ur_client_cert = xstrdup(fo_get_fullpath(fst));
4b6aa8
+            config->ur_client_key = xstrdup(fo_get_fullpath(scn));
4b6aa8
+        }
4b6aa8
+        else
4b6aa8
+        {
4b6aa8
+            cert = fo_get_filename(scn);
4b6aa8
+            key = fo_get_filename(fst);
4b6aa8
+
4b6aa8
+            config->ur_client_cert = xstrdup(fo_get_fullpath(scn));
4b6aa8
+            config->ur_client_key = xstrdup(fo_get_fullpath(fst));
4b6aa8
+        }
4b6aa8
+
4b6aa8
+        const bool iscomplement = prefixcmp(key, cert) != 0 || strcmp("-key", key + strlen(cert)) != 0;
4b6aa8
+        g_list_free_full(certs, (GDestroyNotify)free_file_obj);
4b6aa8
+
4b6aa8
+        if (iscomplement)
4b6aa8
+        {
4b6aa8
+            log_notice("Key file '%s' isn't complement to cert file '%s'",
4b6aa8
+                    config->ur_client_key, config->ur_client_cert);
4b6aa8
+            log_notice("Not using client authentication");
4b6aa8
+
4b6aa8
+            free(config->ur_client_cert);
4b6aa8
+            free(config->ur_client_key);
4b6aa8
+            config->ur_client_cert = NULL;
4b6aa8
+            config->ur_client_key = NULL;
4b6aa8
+
4b6aa8
+            return;
4b6aa8
+        }
4b6aa8
+
4b6aa8
+        char *certdata = xmalloc_open_read_close(config->ur_client_cert, /*no size limit*/NULL);
4b6aa8
+        if (certdata != NULL)
4b6aa8
+        {
4b6aa8
+            char *ent_data = xstrdup_between(certdata,
4b6aa8
+                    RHSMENT_ENT_DATA_BEGIN_TAG, RHSMENT_ENT_DATA_END_TAG);
4b6aa8
+
4b6aa8
+            char *sig_data = xstrdup_between(certdata,
4b6aa8
+                    RHSMENT_SIG_DATA_BEGIN_TAG, RHSMENT_SIG_DATA_END_TAG);
4b6aa8
+
4b6aa8
+            if (ent_data != NULL && sig_data != NULL)
4b6aa8
+            {
4b6aa8
+                ent_data = strremovech(ent_data, '\n');
4b6aa8
+                insert_map_string(config->ur_http_headers,
4b6aa8
+                        xstrdup("X-RH-Entitlement-Data"),
4b6aa8
+                        xasprintf(RHSMENT_ENT_DATA_BEGIN_TAG"%s"RHSMENT_ENT_DATA_END_TAG, ent_data));
4b6aa8
+
4b6aa8
+                sig_data = strremovech(sig_data, '\n');
4b6aa8
+                insert_map_string(config->ur_http_headers,
4b6aa8
+                        xstrdup("X-RH-Entitlement-Sig"),
4b6aa8
+                        xasprintf(RHSMENT_SIG_DATA_BEGIN_TAG"%s"RHSMENT_SIG_DATA_END_TAG, sig_data));
4b6aa8
+            }
4b6aa8
+            else
4b6aa8
+            {
4b6aa8
+                log_notice("Cert file '%s' doesn't contain Entitlement and RSA Signature sections", config->ur_client_cert);
4b6aa8
+                log_notice("Not using HTTP authentication headers");
4b6aa8
+            }
4b6aa8
+
4b6aa8
+            free(sig_data);
4b6aa8
+            free(ent_data);
4b6aa8
+            free(certdata);
4b6aa8
+        }
4b6aa8
+    }
4b6aa8
+    else if (strcmp(client_auth, "puppet") == 0)
4b6aa8
+    {
4b6aa8
+        config->ur_client_cert = puppet_config_print("hostcert");
4b6aa8
+        config->ur_client_key = puppet_config_print("hostprivkey");
4b6aa8
+    }
4b6aa8
+    else
4b6aa8
+    {
4b6aa8
+        char *scratch = xstrdup(client_auth);
4b6aa8
+        config->ur_client_cert = xstrdup(strtok(scratch, ":"));
4b6aa8
+        config->ur_client_key = xstrdup(strtok(NULL, ":"));
4b6aa8
+        free(scratch);
4b6aa8
+
4b6aa8
+        if (config->ur_client_cert == NULL || config->ur_client_key == NULL)
4b6aa8
+            error_msg_and_die("Invalid client authentication specification");
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (config->ur_client_cert && config->ur_client_key)
4b6aa8
+    {
4b6aa8
+        log_notice("Using client certificate: %s", config->ur_client_cert);
4b6aa8
+        log_notice("Using client private key: %s", config->ur_client_key);
4b6aa8
+    }
4b6aa8
+}
4b6aa8
+
4b6aa8
+void
4b6aa8
+ureport_server_config_load(struct ureport_server_config *config,
4b6aa8
+                           map_string_t *settings)
4b6aa8
+{
4b6aa8
+    UREPORT_OPTION_VALUE_FROM_CONF(settings, "URL", config->ur_url, (const char *));
4b6aa8
+    UREPORT_OPTION_VALUE_FROM_CONF(settings, "SSLVerify", config->ur_ssl_verify, string_to_bool);
4b6aa8
+
4b6aa8
+    bool include_auth = false;
4b6aa8
+    UREPORT_OPTION_VALUE_FROM_CONF(settings, "IncludeAuthData", include_auth, string_to_bool);
4b6aa8
+
4b6aa8
+    if (include_auth)
4b6aa8
+    {
4b6aa8
+        const char *auth_items = NULL;
4b6aa8
+        UREPORT_OPTION_VALUE_FROM_CONF(settings, "AuthDataItems", auth_items, (const char *));
4b6aa8
+        config->ur_prefs.urp_auth_items = parse_list(auth_items);
4b6aa8
+
4b6aa8
+        if (config->ur_prefs.urp_auth_items == NULL)
4b6aa8
+            log_warning("IncludeAuthData set to 'yes' but AuthDataItems is empty.");
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    const char *client_auth = NULL;
4b6aa8
+    UREPORT_OPTION_VALUE_FROM_CONF(settings, "SSLClientAuth", client_auth, (const char *));
4b6aa8
+    ureport_server_config_set_client_auth(config, client_auth);
4b6aa8
+}
4b6aa8
+
4b6aa8
+void
4b6aa8
+ureport_server_config_init(struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    config->ur_url = NULL;
4b6aa8
+    config->ur_ssl_verify = true;
4b6aa8
+    config->ur_client_cert = NULL;
4b6aa8
+    config->ur_client_key = NULL;
4b6aa8
+    config->ur_http_headers = new_map_string();
4b6aa8
+    config->ur_prefs.urp_auth_items = NULL;
4b6aa8
+}
4b6aa8
+
4b6aa8
+void
4b6aa8
+ureport_server_config_destroy(struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    free(config->ur_client_cert);
4b6aa8
+    config->ur_client_cert = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free(config->ur_client_key);
4b6aa8
+    config->ur_client_key = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    g_list_free_full(config->ur_prefs.urp_auth_items, free);
4b6aa8
+    config->ur_prefs.urp_auth_items = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free_map_string(config->ur_http_headers);
4b6aa8
+    config->ur_http_headers = DESTROYED_POINTER;
4b6aa8
+}
4b6aa8
+
4b6aa8
+void
4b6aa8
+ureport_server_response_free(struct ureport_server_response *resp)
4b6aa8
+{
4b6aa8
+    if (!resp)
4b6aa8
+        return;
4b6aa8
+
4b6aa8
+    free(resp->urr_solution);
4b6aa8
+    resp->urr_solution = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    g_list_free_full(resp->urr_reported_to_list, g_free);
4b6aa8
+    resp->urr_reported_to_list = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free(resp->urr_bthash);
4b6aa8
+    resp->urr_bthash = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free(resp->urr_message);
4b6aa8
+    resp->urr_message = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free(resp->urr_value);
4b6aa8
+    resp->urr_value = DESTROYED_POINTER;
4b6aa8
+
4b6aa8
+    free(resp);
4b6aa8
+}
4b6aa8
+
4b6aa8
+static char *
4b6aa8
+parse_solution_from_json_list(struct json_object *list,
4b6aa8
+                              GList **reported_to)
4b6aa8
+{
4b6aa8
+    json_object *list_elem, *struct_elem;
4b6aa8
+    const char *cause, *note, *url;
4b6aa8
+    struct strbuf *solution_buf = strbuf_new();
4b6aa8
+
4b6aa8
+    const unsigned length = json_object_array_length(list);
4b6aa8
+
4b6aa8
+    const char *one_format = _("Your problem seems to be caused by %s\n\n%s\n");
4b6aa8
+    if (length > 1)
4b6aa8
+    {
4b6aa8
+        strbuf_append_str(solution_buf, _("Your problem seems to be caused by one of the following:\n"));
4b6aa8
+        one_format = "\n* %s\n\n%s\n";
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    bool empty = true;
4b6aa8
+    for (unsigned i = 0; i < length; ++i)
4b6aa8
+    {
4b6aa8
+        list_elem = json_object_array_get_idx(list, i);
4b6aa8
+        if (!list_elem)
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "cause", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        cause = json_object_get_string(struct_elem);
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "note", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        note = json_object_get_string(struct_elem);
4b6aa8
+        if (!note)
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        empty = false;
4b6aa8
+        strbuf_append_strf(solution_buf, one_format, cause, note);
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "url", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        url = json_object_get_string(struct_elem);
4b6aa8
+        if (url)
4b6aa8
+        {
4b6aa8
+            char *reported_to_line = xasprintf("%s: URL=%s", cause, url);
4b6aa8
+            *reported_to = g_list_append(*reported_to, reported_to_line);
4b6aa8
+        }
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (empty)
4b6aa8
+    {
4b6aa8
+        strbuf_free(solution_buf);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    return strbuf_free_nobuf(solution_buf);
4b6aa8
+}
4b6aa8
+
4b6aa8
+/* reported_to json element should be a list of structures
4b6aa8
+   {
4b6aa8
+     "reporter": "Bugzilla",
4b6aa8
+     "type": "url",
4b6aa8
+     "value": "https://bugzilla.redhat.com/show_bug.cgi?id=XYZ"
4b6aa8
+   }
4b6aa8
+ */
4b6aa8
+static GList *
4b6aa8
+parse_reported_to_from_json_list(struct json_object *list)
4b6aa8
+{
4b6aa8
+    int i;
4b6aa8
+    json_object *list_elem, *struct_elem;
4b6aa8
+    const char *reporter, *value, *type;
4b6aa8
+    char *reported_to_line, *prefix;
4b6aa8
+    GList *result = NULL;
4b6aa8
+
4b6aa8
+    for (i = 0; i < json_object_array_length(list); ++i)
4b6aa8
+    {
4b6aa8
+        prefix = NULL;
4b6aa8
+        list_elem = json_object_array_get_idx(list, i);
4b6aa8
+        if (!list_elem)
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "reporter", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        reporter = json_object_get_string(struct_elem);
4b6aa8
+        if (!reporter)
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "value", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        value = json_object_get_string(struct_elem);
4b6aa8
+        if (!value)
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        if (!json_object_object_get_ex(list_elem, "type", &struct_elem))
4b6aa8
+            continue;
4b6aa8
+
4b6aa8
+        type = json_object_get_string(struct_elem);
4b6aa8
+        if (type)
4b6aa8
+        {
4b6aa8
+            if (strcasecmp("url", type) == 0)
4b6aa8
+                prefix = xstrdup("URL=");
4b6aa8
+            else if (strcasecmp("bthash", type) == 0)
4b6aa8
+                prefix = xstrdup("BTHASH=");
4b6aa8
+        }
4b6aa8
+
4b6aa8
+        if (!prefix)
4b6aa8
+            prefix = xstrdup("");
4b6aa8
+
4b6aa8
+        reported_to_line = xasprintf("%s: %s%s", reporter, prefix, value);
4b6aa8
+        free(prefix);
4b6aa8
+
4b6aa8
+        result = g_list_append(result, reported_to_line);
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    return result;
4b6aa8
+}
4b6aa8
+
4b6aa8
+/*
4b6aa8
+ * Reponse samples:
4b6aa8
+ * {"error":"field 'foo' is required"}
4b6aa8
+ * {"response":"true"}
4b6aa8
+ * {"response":"false"}
4b6aa8
+ */
4b6aa8
+static struct ureport_server_response *
4b6aa8
+ureport_server_parse_json(json_object *json)
4b6aa8
+{
4b6aa8
+    json_object *obj = NULL;
4b6aa8
+    if (json_object_object_get_ex(json, "error", &obj))
4b6aa8
+    {
4b6aa8
+        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
4b6aa8
+        out_response->urr_is_error = true;
4b6aa8
+        /*
4b6aa8
+         * Used to use json_object_to_json_string(obj), but it returns
4b6aa8
+         * the string in quote marks (") - IOW, json-formatted string.
4b6aa8
+         */
4b6aa8
+        out_response->urr_value = xstrdup(json_object_get_string(obj));
4b6aa8
+        return out_response;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (json_object_object_get_ex(json, "result", &obj))
4b6aa8
+    {
4b6aa8
+        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
4b6aa8
+        out_response->urr_value = xstrdup(json_object_get_string(obj));
4b6aa8
+
4b6aa8
+        json_object *message = NULL;
4b6aa8
+        if (json_object_object_get_ex(json, "message", &message))
4b6aa8
+            out_response->urr_message = xstrdup(json_object_get_string(message));
4b6aa8
+
4b6aa8
+        json_object *bthash = NULL;
4b6aa8
+        if (json_object_object_get_ex(json, "bthash", &bthash))
4b6aa8
+            out_response->urr_bthash = xstrdup(json_object_get_string(bthash));
4b6aa8
+
4b6aa8
+        json_object *reported_to_list = NULL;
4b6aa8
+        if (json_object_object_get_ex(json, "reported_to", &reported_to_list))
4b6aa8
+            out_response->urr_reported_to_list =
4b6aa8
+                parse_reported_to_from_json_list(reported_to_list);
4b6aa8
+
4b6aa8
+        json_object *solutions = NULL;
4b6aa8
+        if (json_object_object_get_ex(json, "solutions", &solutions))
4b6aa8
+            out_response->urr_solution =
4b6aa8
+                parse_solution_from_json_list(solutions, &(out_response->urr_reported_to_list));
4b6aa8
+
4b6aa8
+        return out_response;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    return NULL;
4b6aa8
+}
4b6aa8
+
4b6aa8
+struct ureport_server_response *
4b6aa8
+ureport_server_response_from_reply(post_state_t *post_state,
4b6aa8
+                                   struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    /* Previously, the condition here was (post_state->errmsg[0] != '\0')
4b6aa8
+     * however when the server asks for optional client authentication and we do not have the certificates,
4b6aa8
+     * then post_state->errmsg contains "NSS: client certificate not found (nickname not specified)" even though
4b6aa8
+     * the request succeeded.
4b6aa8
+     */
4b6aa8
+    if (post_state->curl_result != CURLE_OK)
4b6aa8
+    {
4b6aa8
+        error_msg(_("Failed to upload uReport to the server '%s' with curl: %s"), config->ur_url, post_state->errmsg);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (post_state->http_resp_code == 404)
4b6aa8
+    {
4b6aa8
+        error_msg(_("The URL '%s' does not exist (got error 404 from server)"), config->ur_url);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (post_state->http_resp_code == 500)
4b6aa8
+    {
4b6aa8
+        error_msg(_("The server at '%s' encountered an internal error (got error 500)"), config->ur_url);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (post_state->http_resp_code == 503)
4b6aa8
+    {
4b6aa8
+        error_msg(_("The server at '%s' currently can't handle the request (got error 503)"), config->ur_url);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (post_state->http_resp_code != 202
4b6aa8
+            && post_state->http_resp_code != 400
4b6aa8
+            && post_state->http_resp_code != 413)
4b6aa8
+    {
4b6aa8
+        /* can't print better error message */
4b6aa8
+        error_msg(_("Unexpected HTTP response from '%s': %d"), config->ur_url, post_state->http_resp_code);
4b6aa8
+        log_notice("%s", post_state->body);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    json_object *const json = json_tokener_parse(post_state->body);
4b6aa8
+
4b6aa8
+    if (is_error(json))
4b6aa8
+    {
4b6aa8
+        error_msg(_("Unable to parse response from ureport server at '%s'"), config->ur_url);
4b6aa8
+        log_notice("%s", post_state->body);
4b6aa8
+        json_object_put(json);
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    struct ureport_server_response *response = ureport_server_parse_json(json);
4b6aa8
+    json_object_put(json);
4b6aa8
+
4b6aa8
+    if (!response)
4b6aa8
+        error_msg(_("The response from '%s' has invalid format"), config->ur_url);
4b6aa8
+    else if ((post_state->http_resp_code == 202 && response->urr_is_error)
4b6aa8
+                || (post_state->http_resp_code != 202 && !response->urr_is_error))
4b6aa8
+    {
4b6aa8
+        /* HTTP CODE 202 means that call was successful but the response */
4b6aa8
+        /* has an error message */
4b6aa8
+        error_msg(_("Type mismatch has been detected in the response from '%s'"), config->ur_url);
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    return response;
4b6aa8
+}
4b6aa8
+
4b6aa8
+bool
4b6aa8
+ureport_server_response_save_in_dump_dir(struct ureport_server_response *resp,
4b6aa8
+                                         const char *dump_dir_path,
4b6aa8
+                                         struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    struct dump_dir *dd = dd_opendir(dump_dir_path, /* flags */ 0);
4b6aa8
+    if (!dd)
4b6aa8
+        return false;
4b6aa8
+
4b6aa8
+    if (resp->urr_bthash)
4b6aa8
+    {
4b6aa8
+        char *msg = xasprintf("uReport: BTHASH=%s", resp->urr_bthash);
4b6aa8
+        add_reported_to(dd, msg);
4b6aa8
+        free(msg);
4b6aa8
+
4b6aa8
+        char *report_url = ureport_server_response_get_report_url(resp, config);
4b6aa8
+        msg = xasprintf("ABRT Server: URL=%s", report_url);
4b6aa8
+        add_reported_to(dd, msg);
4b6aa8
+        free(msg);
4b6aa8
+        free(report_url);
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (resp->urr_reported_to_list)
4b6aa8
+    {
4b6aa8
+        for (GList *e = resp->urr_reported_to_list; e; e = g_list_next(e))
4b6aa8
+            add_reported_to(dd, e->data);
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    if (resp->urr_solution)
4b6aa8
+        dd_save_text(dd, FILENAME_NOT_REPORTABLE, resp->urr_solution);
4b6aa8
+
4b6aa8
+    dd_close(dd);
4b6aa8
+    return true;
4b6aa8
+}
4b6aa8
+
4b6aa8
+char *
4b6aa8
+ureport_server_response_get_report_url(struct ureport_server_response *resp,
4b6aa8
+                                       struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    char *bthash_url = concat_path_file(config->ur_url, BTHASH_URL_SFX);
4b6aa8
+    char *report_url = concat_path_file(bthash_url, resp->urr_bthash);
4b6aa8
+    free(bthash_url);
4b6aa8
+    return report_url;
4b6aa8
+}
4b6aa8
+
4b6aa8
+static void
4b6aa8
+ureport_add_str(struct json_object *ur, const char *key, const char *s)
4b6aa8
 {
4b6aa8
     struct json_object *jstring = json_object_new_string(s);
4b6aa8
     if (!jstring)
4b6aa8
@@ -37,7 +556,8 @@ static void ureport_add_str(struct json_object *ur, const char *key,
4b6aa8
     json_object_object_add(ur, key, jstring);
4b6aa8
 }
4b6aa8
 
4b6aa8
-char *ureport_from_dump_dir_ext(const char *dump_dir_path, const struct ureport_preferences *preferences)
4b6aa8
+char *
4b6aa8
+ureport_from_dump_dir_ext(const char *dump_dir_path, const struct ureport_preferences *preferences)
4b6aa8
 {
4b6aa8
     char *error_message;
4b6aa8
     struct sr_report *report = sr_abrt_report_from_dir(dump_dir_path,
4b6aa8
@@ -77,28 +597,15 @@ char *ureport_from_dump_dir_ext(const char *dump_dir_path, const struct ureport_
4b6aa8
     return json_ureport;
4b6aa8
 }
4b6aa8
 
4b6aa8
-char *ureport_from_dump_dir(const char *dump_dir_path)
4b6aa8
+char *
4b6aa8
+ureport_from_dump_dir(const char *dump_dir_path)
4b6aa8
 {
4b6aa8
     return ureport_from_dump_dir_ext(dump_dir_path, /*no preferences*/NULL);
4b6aa8
 }
4b6aa8
 
4b6aa8
-static char *new_json_attachment(const char *bthash, const char *type, const char *data)
4b6aa8
-{
4b6aa8
-    struct json_object *attachment = json_object_new_object();
4b6aa8
-    if (!attachment)
4b6aa8
-        die_out_of_memory();
4b6aa8
-
4b6aa8
-    ureport_add_str(attachment, "bthash", bthash);
4b6aa8
-    ureport_add_str(attachment, "type", type);
4b6aa8
-    ureport_add_str(attachment, "data", data);
4b6aa8
-
4b6aa8
-    char *result = xstrdup(json_object_to_json_string(attachment));
4b6aa8
-    json_object_put(attachment);
4b6aa8
-
4b6aa8
-    return result;
4b6aa8
-}
4b6aa8
-
4b6aa8
-struct post_state *ureport_post(const char *json, struct ureport_server_config *config)
4b6aa8
+struct post_state *
4b6aa8
+ureport_do_post(const char *json, struct ureport_server_config *config,
4b6aa8
+                const char *url_sfx)
4b6aa8
 {
4b6aa8
     int flags = POST_WANT_BODY | POST_WANT_ERROR_MSG;
4b6aa8
 
4b6aa8
@@ -130,8 +637,10 @@ struct post_state *ureport_post(const char *json, struct ureport_server_config *
4b6aa8
         headers[i] = NULL;
4b6aa8
     }
4b6aa8
 
4b6aa8
-    post_string_as_form_data(post_state, config->ur_url, "application/json",
4b6aa8
-                    (const char **)headers, json);
4b6aa8
+    char *dest_url = concat_path_file(config->ur_url, url_sfx);
4b6aa8
+
4b6aa8
+    post_string_as_form_data(post_state, dest_url, "application/json",
4b6aa8
+                     (const char **)headers, json);
4b6aa8
 
4b6aa8
     /* Client authentication failed. Try again without client auth.
4b6aa8
      * CURLE_SSL_CONNECT_ERROR - cert not found/server doesnt trust the CA
4b6aa8
@@ -145,11 +654,13 @@ struct post_state *ureport_post(const char *json, struct ureport_server_config *
4b6aa8
         free_post_state(post_state);
4b6aa8
         post_state = new_post_state(flags);
4b6aa8
 
4b6aa8
-        post_string_as_form_data(post_state, config->ur_url, "application/json",
4b6aa8
+        post_string_as_form_data(post_state, dest_url, "application/json",
4b6aa8
                          (const char **)headers, json);
4b6aa8
 
4b6aa8
     }
4b6aa8
 
4b6aa8
+    free(dest_url);
4b6aa8
+
4b6aa8
     for (unsigned i = size_map_string(config->ur_http_headers); i != 0; --i)
4b6aa8
         free(headers[i + 1]);
4b6aa8
     free(headers);
4b6aa8
@@ -157,24 +668,70 @@ struct post_state *ureport_post(const char *json, struct ureport_server_config *
4b6aa8
     return post_state;
4b6aa8
 }
4b6aa8
 
4b6aa8
-struct post_state *ureport_attach_rhbz(const char *bthash, int rhbz_bug_id,
4b6aa8
-                                       struct ureport_server_config *config)
4b6aa8
+struct ureport_server_response *
4b6aa8
+ureport_submit(const char *json, struct ureport_server_config *config)
4b6aa8
 {
4b6aa8
-    char *str_bug_id = xasprintf("%d", rhbz_bug_id);
4b6aa8
-    char *json_attachment = new_json_attachment(bthash, "RHBZ", str_bug_id);
4b6aa8
-    struct post_state *post_state = ureport_post(json_attachment, config);
4b6aa8
-    free(str_bug_id);
4b6aa8
-    free(json_attachment);
4b6aa8
+    struct post_state *post_state = ureport_do_post(json, config, UREPORT_SUBMIT_ACTION);
4b6aa8
 
4b6aa8
-    return post_state;
4b6aa8
+    if (post_state == NULL)
4b6aa8
+    {
4b6aa8
+        error_msg(_("Failed on submitting the problem"));
4b6aa8
+        return NULL;
4b6aa8
+    }
4b6aa8
+
4b6aa8
+    struct ureport_server_response *resp = ureport_server_response_from_reply(post_state, config);
4b6aa8
+    free(post_state);
4b6aa8
+
4b6aa8
+    return resp;
4b6aa8
 }
4b6aa8
 
4b6aa8
-struct post_state *ureport_attach_email(const char *bthash, const char *email,
4b6aa8
-                                       struct ureport_server_config *config)
4b6aa8
+static char *
4b6aa8
+ureport_json_attachment_new(const char *bthash, const char *type, const char *data)
4b6aa8
 {
4b6aa8
-    char *json_attachment = new_json_attachment(bthash, "email", email);
4b6aa8
-    struct post_state *post_state = ureport_post(json_attachment, config);
4b6aa8
-    free(json_attachment);
4b6aa8
+    struct json_object *attachment = json_object_new_object();
4b6aa8
+    if (!attachment)
4b6aa8
+        die_out_of_memory();
4b6aa8
 
4b6aa8
-    return post_state;
4b6aa8
+    ureport_add_str(attachment, "bthash", bthash);
4b6aa8
+    ureport_add_str(attachment, "type", type);
4b6aa8
+    ureport_add_str(attachment, "data", data);
4b6aa8
+
4b6aa8
+    char *result = xstrdup(json_object_to_json_string(attachment));
4b6aa8
+    json_object_put(attachment);
4b6aa8
+
4b6aa8
+    return result;
4b6aa8
+}
4b6aa8
+
4b6aa8
+bool
4b6aa8
+ureport_attach_string(const char *bthash, const char *type, const char *data,
4b6aa8
+               struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    char *json = ureport_json_attachment_new(bthash, type, data);
4b6aa8
+    post_state_t *post_state = ureport_do_post(json, config, UREPORT_ATTACH_ACTION);
4b6aa8
+    free(json);
4b6aa8
+
4b6aa8
+    struct ureport_server_response *resp =
4b6aa8
+        ureport_server_response_from_reply(post_state, config);
4b6aa8
+    free_post_state(post_state);
4b6aa8
+    /* don't use str_bo_bool() because we require "true" string */
4b6aa8
+    const int result = !resp || resp->urr_is_error || strcmp(resp->urr_value, "true") != 0;
4b6aa8
+
4b6aa8
+    if (resp && resp->urr_is_error)
4b6aa8
+        error_msg(_("The server at '%s' responded with an error: '%s'"),
4b6aa8
+                config->ur_url, resp->urr_value);
4b6aa8
+
4b6aa8
+    ureport_server_response_free(resp);
4b6aa8
+
4b6aa8
+    return result;
4b6aa8
+}
4b6aa8
+
4b6aa8
+bool
4b6aa8
+ureport_attach_int(const char *bthash, const char *type, int data,
4b6aa8
+                    struct ureport_server_config *config)
4b6aa8
+{
4b6aa8
+    char *data_str = xasprintf("%d", data);
4b6aa8
+    const bool result = ureport_attach_string(bthash, type, data_str, config);
4b6aa8
+    free(data_str);
4b6aa8
+
4b6aa8
+    return result;
4b6aa8
 }
4b6aa8
diff --git a/src/plugins/reporter-ureport.c b/src/plugins/reporter-ureport.c
4b6aa8
index d827c7d..7bd3fb3 100644
4b6aa8
--- a/src/plugins/reporter-ureport.c
4b6aa8
+++ b/src/plugins/reporter-ureport.c
4b6aa8
@@ -22,482 +22,6 @@
4b6aa8
 #include "ureport.h"
4b6aa8
 #include "libreport_curl.h"
4b6aa8
 
4b6aa8
-#define CONF_FILE_PATH PLUGINS_CONF_DIR"/ureport.conf"
4b6aa8
-
4b6aa8
-#define REPORT_URL_SFX "reports/new/"
4b6aa8
-#define ATTACH_URL_SFX "reports/attach/"
4b6aa8
-#define BTHASH_URL_SFX "reports/bthash/"
4b6aa8
-
4b6aa8
-#define RHSM_CERT_PATH "/etc/pki/consumer/cert.pem"
4b6aa8
-#define RHSM_KEY_PATH "/etc/pki/consumer/key.pem"
4b6aa8
-
4b6aa8
-#define RHAP_PEM_DIR_PATH "/etc/pki/entitlement"
4b6aa8
-#define RHAP_ENT_DATA_BEGIN_TAG "-----BEGIN ENTITLEMENT DATA-----"
4b6aa8
-#define RHAP_ENT_DATA_END_TAG "-----END ENTITLEMENT DATA-----"
4b6aa8
-#define RHAP_SIG_DATA_BEGIN_TAG "-----BEGIN RSA SIGNATURE-----"
4b6aa8
-#define RHAP_SIG_DATA_END_TAG "-----END RSA SIGNATURE-----"
4b6aa8
-
4b6aa8
-#define VALUE_FROM_CONF(opt, var, tr) do { const char *value = getenv("uReport_"opt); \
4b6aa8
-        if (!value) { value = get_map_string_item_or_NULL(settings, opt); } if (value) { var = tr(value); } \
4b6aa8
-    } while(0)
4b6aa8
-
4b6aa8
-static char *puppet_config_print(const char *key)
4b6aa8
-{
4b6aa8
-    char *command = xasprintf("puppet config print %s", key);
4b6aa8
-    char *result = run_in_shell_and_save_output(0, command, NULL, NULL);
4b6aa8
-    free(command);
4b6aa8
-
4b6aa8
-    /* run_in_shell_and_save_output always returns non-NULL */
4b6aa8
-    if (result[0] != '/')
4b6aa8
-        goto error;
4b6aa8
-
4b6aa8
-    char *newline = strchrnul(result, '\n');
4b6aa8
-    if (!newline)
4b6aa8
-        goto error;
4b6aa8
-
4b6aa8
-    *newline = '\0';
4b6aa8
-    return result;
4b6aa8
-error:
4b6aa8
-    free(result);
4b6aa8
-    error_msg_and_die("Unable to determine puppet %s path (puppet not installed?)", key);
4b6aa8
-}
4b6aa8
-
4b6aa8
-static void parse_client_auth_paths(struct ureport_server_config *config, const char *client_auth)
4b6aa8
-{
4b6aa8
-    if (client_auth == NULL)
4b6aa8
-        return;
4b6aa8
-
4b6aa8
-    if (strcmp(client_auth, "") == 0)
4b6aa8
-    {
4b6aa8
-        config->ur_client_cert = NULL;
4b6aa8
-        config->ur_client_key = NULL;
4b6aa8
-        log_notice("Not using client authentication");
4b6aa8
-    }
4b6aa8
-    else if (strcmp(client_auth, "rhsm") == 0)
4b6aa8
-    {
4b6aa8
-        config->ur_client_cert = xstrdup(RHSM_CERT_PATH);
4b6aa8
-        config->ur_client_key = xstrdup(RHSM_KEY_PATH);
4b6aa8
-    }
4b6aa8
-    else if (strcmp(client_auth, "rhsm-entitlement") == 0)
4b6aa8
-    {
4b6aa8
-        GList *certs = get_file_list(RHAP_PEM_DIR_PATH, "pem");
4b6aa8
-        if (g_list_length(certs) != 2)
4b6aa8
-        {
4b6aa8
-            log_notice(RHAP_PEM_DIR_PATH" does not contain unique cert-key files pair");
4b6aa8
-            log_notice("Not using client authentication");
4b6aa8
-            return;
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        const char *cert = NULL;
4b6aa8
-        const char *key = NULL;
4b6aa8
-
4b6aa8
-        file_obj_t *fst = (file_obj_t *)certs->data;
4b6aa8
-        file_obj_t *scn = (file_obj_t *)certs->next->data;
4b6aa8
-
4b6aa8
-        if (strlen(fo_get_filename(fst)) < strlen(fo_get_filename(scn)))
4b6aa8
-        {
4b6aa8
-            cert = fo_get_filename(fst);
4b6aa8
-            key = fo_get_filename(scn);
4b6aa8
-
4b6aa8
-            config->ur_client_cert = xstrdup(fo_get_fullpath(fst));
4b6aa8
-            config->ur_client_key = xstrdup(fo_get_fullpath(scn));
4b6aa8
-        }
4b6aa8
-        else
4b6aa8
-        {
4b6aa8
-            cert = fo_get_filename(scn);
4b6aa8
-            key = fo_get_filename(fst);
4b6aa8
-
4b6aa8
-            config->ur_client_cert = xstrdup(fo_get_fullpath(scn));
4b6aa8
-            config->ur_client_key = xstrdup(fo_get_fullpath(fst));
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        const bool iscomplement = prefixcmp(key, cert) != 0 || strcmp("-key", key + strlen(cert)) != 0;
4b6aa8
-        g_list_free_full(certs, (GDestroyNotify)free_file_obj);
4b6aa8
-
4b6aa8
-        if (iscomplement)
4b6aa8
-        {
4b6aa8
-            log_notice("Key file '%s' isn't complement to cert file '%s'",
4b6aa8
-                    config->ur_client_key, config->ur_client_cert);
4b6aa8
-            log_notice("Not using client authentication");
4b6aa8
-
4b6aa8
-            free(config->ur_client_cert);
4b6aa8
-            free(config->ur_client_key);
4b6aa8
-            config->ur_client_cert = NULL;
4b6aa8
-            config->ur_client_key = NULL;
4b6aa8
-
4b6aa8
-            return;
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        char *certdata = xmalloc_open_read_close(config->ur_client_cert, /*no size limit*/NULL);
4b6aa8
-        if (certdata != NULL)
4b6aa8
-        {
4b6aa8
-            char *ent_data = xstrdup_between(certdata,
4b6aa8
-                    RHAP_ENT_DATA_BEGIN_TAG, RHAP_ENT_DATA_END_TAG);
4b6aa8
-
4b6aa8
-            char *sig_data = xstrdup_between(certdata,
4b6aa8
-                    RHAP_SIG_DATA_BEGIN_TAG, RHAP_SIG_DATA_END_TAG);
4b6aa8
-
4b6aa8
-            if (ent_data != NULL && sig_data != NULL)
4b6aa8
-            {
4b6aa8
-                ent_data = strremovech(ent_data, '\n');
4b6aa8
-                insert_map_string(config->ur_http_headers,
4b6aa8
-                        xstrdup("X-RH-Entitlement-Data"),
4b6aa8
-                        xasprintf(RHAP_ENT_DATA_BEGIN_TAG"%s"RHAP_ENT_DATA_END_TAG, ent_data));
4b6aa8
-
4b6aa8
-                sig_data = strremovech(sig_data, '\n');
4b6aa8
-                insert_map_string(config->ur_http_headers,
4b6aa8
-                        xstrdup("X-RH-Entitlement-Sig"),
4b6aa8
-                        xasprintf(RHAP_SIG_DATA_BEGIN_TAG"%s"RHAP_SIG_DATA_END_TAG, sig_data));
4b6aa8
-            }
4b6aa8
-            else
4b6aa8
-            {
4b6aa8
-                log_notice("Cert file '%s' doesn't contain Entitlement and RSA Signature sections", config->ur_client_cert);
4b6aa8
-                log_notice("Not using HTTP authentication headers");
4b6aa8
-            }
4b6aa8
-
4b6aa8
-            free(sig_data);
4b6aa8
-            free(ent_data);
4b6aa8
-            free(certdata);
4b6aa8
-        }
4b6aa8
-    }
4b6aa8
-    else if (strcmp(client_auth, "puppet") == 0)
4b6aa8
-    {
4b6aa8
-        config->ur_client_cert = puppet_config_print("hostcert");
4b6aa8
-        config->ur_client_key = puppet_config_print("hostprivkey");
4b6aa8
-    }
4b6aa8
-    else
4b6aa8
-    {
4b6aa8
-        char *scratch = xstrdup(client_auth);
4b6aa8
-        config->ur_client_cert = xstrdup(strtok(scratch, ":"));
4b6aa8
-        config->ur_client_key = xstrdup(strtok(NULL, ":"));
4b6aa8
-        free(scratch);
4b6aa8
-        if (config->ur_client_cert == NULL || config->ur_client_key == NULL)
4b6aa8
-            error_msg_and_die("Invalid client authentication specification");
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (config->ur_client_cert && config->ur_client_key)
4b6aa8
-    {
4b6aa8
-        log_notice("Using client certificate: %s", config->ur_client_cert);
4b6aa8
-        log_notice("Using client private key: %s", config->ur_client_key);
4b6aa8
-    }
4b6aa8
-}
4b6aa8
-
4b6aa8
-/*
4b6aa8
- * Loads uReport configuration from various sources.
4b6aa8
- *
4b6aa8
- * Replaces a value of an already configured option only if the
4b6aa8
- * option was found in a configuration source.
4b6aa8
- *
4b6aa8
- * @param config a server configuration to be populated
4b6aa8
- */
4b6aa8
-static void load_ureport_server_config(struct ureport_server_config *config, map_string_t *settings)
4b6aa8
-{
4b6aa8
-    VALUE_FROM_CONF("URL", config->ur_url, (const char *));
4b6aa8
-    VALUE_FROM_CONF("SSLVerify", config->ur_ssl_verify, string_to_bool);
4b6aa8
-
4b6aa8
-    bool include_auth = false;
4b6aa8
-    VALUE_FROM_CONF("IncludeAuthData", include_auth, string_to_bool);
4b6aa8
-
4b6aa8
-    if (include_auth)
4b6aa8
-    {
4b6aa8
-        const char *auth_items = NULL;
4b6aa8
-        VALUE_FROM_CONF("AuthDataItems", auth_items, (const char *));
4b6aa8
-        config->ur_prefs.urp_auth_items = parse_list(auth_items);
4b6aa8
-
4b6aa8
-        if (config->ur_prefs.urp_auth_items == NULL)
4b6aa8
-            log_warning("IncludeAuthData set to 'yes' but AuthDataItems is empty.");
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    const char *client_auth = NULL;
4b6aa8
-    VALUE_FROM_CONF("SSLClientAuth", client_auth, (const char *));
4b6aa8
-    parse_client_auth_paths(config, client_auth);
4b6aa8
-}
4b6aa8
-
4b6aa8
-struct ureport_server_response {
4b6aa8
-    bool is_error;
4b6aa8
-    char *value;
4b6aa8
-    char *message;
4b6aa8
-    char *bthash;
4b6aa8
-    GList *reported_to_list;
4b6aa8
-    char *solution;
4b6aa8
-};
4b6aa8
-
4b6aa8
-void free_ureport_server_response(struct ureport_server_response *resp)
4b6aa8
-{
4b6aa8
-    if (!resp)
4b6aa8
-        return;
4b6aa8
-
4b6aa8
-    free(resp->solution);
4b6aa8
-    g_list_free_full(resp->reported_to_list, g_free);
4b6aa8
-    free(resp->bthash);
4b6aa8
-    free(resp->message);
4b6aa8
-    free(resp->value);
4b6aa8
-    free(resp);
4b6aa8
-}
4b6aa8
-
4b6aa8
-static char *parse_solution_from_json_list(struct json_object *list, GList **reported_to)
4b6aa8
-{
4b6aa8
-    json_object *list_elem, *struct_elem;
4b6aa8
-    const char *cause, *note, *url;
4b6aa8
-    struct strbuf *solution_buf = strbuf_new();
4b6aa8
-
4b6aa8
-    const unsigned length = json_object_array_length(list);
4b6aa8
-
4b6aa8
-    const char *one_format = _("Your problem seems to be caused by %s\n\n%s\n");
4b6aa8
-    if (length > 1)
4b6aa8
-    {
4b6aa8
-        strbuf_append_str(solution_buf, _("Your problem seems to be caused by one of the following:\n"));
4b6aa8
-        one_format = "\n* %s\n\n%s\n";
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    bool empty = true;
4b6aa8
-    for (unsigned i = 0; i < length; ++i)
4b6aa8
-    {
4b6aa8
-        list_elem = json_object_array_get_idx(list, i);
4b6aa8
-        if (!list_elem)
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "cause", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        cause = json_object_get_string(struct_elem);
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "note", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        note = json_object_get_string(struct_elem);
4b6aa8
-        if (!note)
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        empty = false;
4b6aa8
-        strbuf_append_strf(solution_buf, one_format, cause, note);
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "url", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        url = json_object_get_string(struct_elem);
4b6aa8
-        if (url)
4b6aa8
-        {
4b6aa8
-            char *reported_to_line = xasprintf("%s: URL=%s", cause, url);
4b6aa8
-            *reported_to = g_list_append(*reported_to, reported_to_line);
4b6aa8
-        }
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (empty)
4b6aa8
-    {
4b6aa8
-        strbuf_free(solution_buf);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    return strbuf_free_nobuf(solution_buf);
4b6aa8
-}
4b6aa8
-
4b6aa8
-/* reported_to json element should be a list of structures
4b6aa8
-{ "reporter": "Bugzilla",
4b6aa8
-  "type": "url",
4b6aa8
-  "value": "https://bugzilla.redhat.com/show_bug.cgi?id=XYZ" } */
4b6aa8
-static GList *parse_reported_to_from_json_list(struct json_object *list)
4b6aa8
-{
4b6aa8
-    int i;
4b6aa8
-    json_object *list_elem, *struct_elem;
4b6aa8
-    const char *reporter, *value, *type;
4b6aa8
-    char *reported_to_line, *prefix;
4b6aa8
-    GList *result = NULL;
4b6aa8
-
4b6aa8
-    for (i = 0; i < json_object_array_length(list); ++i)
4b6aa8
-    {
4b6aa8
-        prefix = NULL;
4b6aa8
-        list_elem = json_object_array_get_idx(list, i);
4b6aa8
-        if (!list_elem)
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "reporter", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        reporter = json_object_get_string(struct_elem);
4b6aa8
-        if (!reporter)
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "value", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        value = json_object_get_string(struct_elem);
4b6aa8
-        if (!value)
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        if (!json_object_object_get_ex(list_elem, "type", &struct_elem))
4b6aa8
-            continue;
4b6aa8
-
4b6aa8
-        type = json_object_get_string(struct_elem);
4b6aa8
-        if (type)
4b6aa8
-        {
4b6aa8
-            if (strcasecmp("url", type) == 0)
4b6aa8
-                prefix = xstrdup("URL=");
4b6aa8
-            else if (strcasecmp("bthash", type) == 0)
4b6aa8
-                prefix = xstrdup("BTHASH=");
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        if (!prefix)
4b6aa8
-            prefix = xstrdup("");
4b6aa8
-
4b6aa8
-        reported_to_line = xasprintf("%s: %s%s", reporter, prefix, value);
4b6aa8
-        free(prefix);
4b6aa8
-
4b6aa8
-        result = g_list_append(result, reported_to_line);
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    return result;
4b6aa8
-}
4b6aa8
-
4b6aa8
-/*
4b6aa8
- * Reponse samples:
4b6aa8
- * {"error":"field 'foo' is required"}
4b6aa8
- * {"response":"true"}
4b6aa8
- * {"response":"false"}
4b6aa8
- */
4b6aa8
-static struct ureport_server_response *ureport_server_parse_json(json_object *json)
4b6aa8
-{
4b6aa8
-    json_object *obj = NULL;
4b6aa8
-    if (json_object_object_get_ex(json, "error", &obj))
4b6aa8
-    {
4b6aa8
-        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
4b6aa8
-        out_response->is_error = true;
4b6aa8
-        /*
4b6aa8
-         * Used to use json_object_to_json_string(obj), but it returns
4b6aa8
-         * the string in quote marks (") - IOW, json-formatted string.
4b6aa8
-         */
4b6aa8
-        out_response->value = xstrdup(json_object_get_string(obj));
4b6aa8
-        return out_response;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (json_object_object_get_ex(json, "result", &obj))
4b6aa8
-    {
4b6aa8
-        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
4b6aa8
-        out_response->value = xstrdup(json_object_get_string(obj));
4b6aa8
-
4b6aa8
-        json_object *message = NULL;
4b6aa8
-        if (json_object_object_get_ex(json, "message", &message))
4b6aa8
-            out_response->message = xstrdup(json_object_get_string(message));
4b6aa8
-
4b6aa8
-        json_object *bthash = NULL;
4b6aa8
-        if (json_object_object_get_ex(json, "bthash", &bthash))
4b6aa8
-            out_response->bthash = xstrdup(json_object_get_string(bthash));
4b6aa8
-
4b6aa8
-        json_object *reported_to_list = NULL;
4b6aa8
-        if (json_object_object_get_ex(json, "reported_to", &reported_to_list))
4b6aa8
-            out_response->reported_to_list = parse_reported_to_from_json_list(reported_to_list);
4b6aa8
-
4b6aa8
-        json_object *solutions = NULL;
4b6aa8
-        if (json_object_object_get_ex(json, "solutions", &solutions))
4b6aa8
-            out_response->solution = parse_solution_from_json_list(solutions, &(out_response->reported_to_list));
4b6aa8
-
4b6aa8
-        return out_response;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    return NULL;
4b6aa8
-}
4b6aa8
-
4b6aa8
-static struct ureport_server_response *get_server_response(post_state_t *post_state, struct ureport_server_config *config)
4b6aa8
-{
4b6aa8
-    /* Previously, the condition here was (post_state->errmsg[0] != '\0')
4b6aa8
-     * however when the server asks for optional client authentication and we do not have the certificates,
4b6aa8
-     * then post_state->errmsg contains "NSS: client certificate not found (nickname not specified)" even though
4b6aa8
-     * the request succeeded.
4b6aa8
-     */
4b6aa8
-    if (post_state->curl_result != CURLE_OK)
4b6aa8
-    {
4b6aa8
-        error_msg(_("Failed to upload uReport to the server '%s' with curl: %s"), config->ur_url, post_state->errmsg);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (post_state->http_resp_code == 404)
4b6aa8
-    {
4b6aa8
-        error_msg(_("The URL '%s' does not exist (got error 404 from server)"), config->ur_url);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (post_state->http_resp_code == 500)
4b6aa8
-    {
4b6aa8
-        error_msg(_("The server at '%s' encountered an internal error (got error 500)"), config->ur_url);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (post_state->http_resp_code == 503)
4b6aa8
-    {
4b6aa8
-        error_msg(_("The server at '%s' currently can't handle the request (got error 503)"), config->ur_url);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    if (post_state->http_resp_code != 202
4b6aa8
-            && post_state->http_resp_code != 400
4b6aa8
-            && post_state->http_resp_code != 413)
4b6aa8
-    {
4b6aa8
-        /* can't print better error message */
4b6aa8
-        error_msg(_("Unexpected HTTP response from '%s': %d"), config->ur_url, post_state->http_resp_code);
4b6aa8
-        log_notice("%s", post_state->body);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    json_object *const json = json_tokener_parse(post_state->body);
4b6aa8
-
4b6aa8
-    if (is_error(json))
4b6aa8
-    {
4b6aa8
-        error_msg(_("Unable to parse response from ureport server at '%s'"), config->ur_url);
4b6aa8
-        log_notice("%s", post_state->body);
4b6aa8
-        json_object_put(json);
4b6aa8
-        return NULL;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    struct ureport_server_response *response = ureport_server_parse_json(json);
4b6aa8
-    json_object_put(json);
4b6aa8
-
4b6aa8
-    if (!response)
4b6aa8
-        error_msg(_("The response from '%s' has invalid format"), config->ur_url);
4b6aa8
-    else if ((post_state->http_resp_code == 202 && response->is_error)
4b6aa8
-                || (post_state->http_resp_code != 202 && !response->is_error))
4b6aa8
-    {
4b6aa8
-        /* HTTP CODE 202 means that call was successful but the response */
4b6aa8
-        /* has an error message */
4b6aa8
-        error_msg(_("Type mismatch has been detected in the response from '%s'"), config->ur_url);
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    return response;
4b6aa8
-}
4b6aa8
-
4b6aa8
-typedef post_state_t *(*attach_handler)(const char *, void *, struct ureport_server_config *);
4b6aa8
-
4b6aa8
-static post_state_t *wrp_ureport_attach_rhbz(const char *ureport_hash, int *rhbz_bug,
4b6aa8
-        struct ureport_server_config *config)
4b6aa8
-{
4b6aa8
-    return ureport_attach_rhbz(ureport_hash, *rhbz_bug, config);
4b6aa8
-}
4b6aa8
-
4b6aa8
-static bool perform_attach(struct ureport_server_config *config, const char *ureport_hash,
4b6aa8
-        attach_handler handler, void *args)
4b6aa8
-{
4b6aa8
-    char *dest_url = concat_path_file(config->ur_url, ATTACH_URL_SFX);
4b6aa8
-    const char *old_url = config->ur_url;
4b6aa8
-    config->ur_url = dest_url;
4b6aa8
-    post_state_t *post_state = handler(ureport_hash, args, config);
4b6aa8
-    config->ur_url = old_url;
4b6aa8
-    free(dest_url);
4b6aa8
-
4b6aa8
-    struct ureport_server_response *resp = get_server_response(post_state, config);
4b6aa8
-    free_post_state(post_state);
4b6aa8
-    /* don't use str_bo_bool() because we require "true" string */
4b6aa8
-    const int result = !resp || resp->is_error || strcmp(resp->value,"true") != 0;
4b6aa8
-
4b6aa8
-    if (resp && resp->is_error)
4b6aa8
-    {
4b6aa8
-        error_msg(_("The server at '%s' responded with an error: '%s'"), config->ur_url, resp->value);
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    free_ureport_server_response(resp);
4b6aa8
-
4b6aa8
-    return result;
4b6aa8
-}
4b6aa8
-
4b6aa8
 int main(int argc, char **argv)
4b6aa8
 {
4b6aa8
     setlocale(LC_ALL, "");
4b6aa8
@@ -508,18 +32,8 @@ int main(int argc, char **argv)
4b6aa8
 
4b6aa8
     abrt_init(argv);
4b6aa8
 
4b6aa8
-    struct ureport_server_config config = {
4b6aa8
-        .ur_url = NULL,
4b6aa8
-        .ur_ssl_verify = true,
4b6aa8
-        .ur_client_cert = NULL,
4b6aa8
-        .ur_client_key = NULL,
4b6aa8
-        .ur_http_headers = NULL,
4b6aa8
-        {
4b6aa8
-            .urp_auth_items = NULL,
4b6aa8
-        },
4b6aa8
-    };
4b6aa8
-
4b6aa8
-    config.ur_http_headers = new_map_string();
4b6aa8
+    struct ureport_server_config config;
4b6aa8
+    ureport_server_config_init(&config);
4b6aa8
 
4b6aa8
     enum {
4b6aa8
         OPT_v = 1 << 0,
4b6aa8
@@ -532,7 +46,7 @@ int main(int argc, char **argv)
4b6aa8
 
4b6aa8
     int ret = 1; /* "failure" (for now) */
4b6aa8
     bool insecure = !config.ur_ssl_verify;
4b6aa8
-    const char *conf_file = CONF_FILE_PATH;
4b6aa8
+    const char *conf_file = UREPORT_CONF_FILE_PATH;
4b6aa8
     const char *arg_server_url = NULL;
4b6aa8
     const char *client_auth = NULL;
4b6aa8
     GList *auth_items = NULL;
4b6aa8
@@ -575,7 +89,7 @@ int main(int argc, char **argv)
4b6aa8
         "\n"
4b6aa8
         "Upload micro report or add an attachment to a micro report\n"
4b6aa8
         "\n"
4b6aa8
-        "Reads the default configuration from "CONF_FILE_PATH
4b6aa8
+        "Reads the default configuration from "UREPORT_CONF_FILE_PATH
4b6aa8
     );
4b6aa8
 
4b6aa8
     unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
4b6aa8
@@ -583,14 +97,14 @@ int main(int argc, char **argv)
4b6aa8
     map_string_t *settings = new_map_string();
4b6aa8
     load_conf_file(conf_file, settings, /*skip key w/o values:*/ false);
4b6aa8
 
4b6aa8
-    load_ureport_server_config(&config, settings);
4b6aa8
+    ureport_server_config_load(&config, settings);
4b6aa8
 
4b6aa8
     if (opts & OPT_u)
4b6aa8
         config.ur_url = arg_server_url;
4b6aa8
     if (opts & OPT_k)
4b6aa8
         config.ur_ssl_verify = !insecure;
4b6aa8
     if (opts & OPT_t)
4b6aa8
-        parse_client_auth_paths(&config, client_auth);
4b6aa8
+        ureport_server_config_set_client_auth(&config, client_auth);
4b6aa8
     if (opts & OPT_i)
4b6aa8
     {
4b6aa8
         g_list_free_full(config.ur_prefs.urp_auth_items, free);
4b6aa8
@@ -600,8 +114,6 @@ int main(int argc, char **argv)
4b6aa8
     if (!config.ur_url)
4b6aa8
         error_msg_and_die("You need to specify server URL");
4b6aa8
 
4b6aa8
-    post_state_t *post_state = NULL;
4b6aa8
-
4b6aa8
     if (ureport_hash && ureport_hash_from_rt)
4b6aa8
         error_msg_and_die("You need to pass either -a bthash or -A");
4b6aa8
 
4b6aa8
@@ -654,7 +166,7 @@ int main(int argc, char **argv)
4b6aa8
 
4b6aa8
     if (email_address_from_env)
4b6aa8
     {
4b6aa8
-        VALUE_FROM_CONF("ContactEmail", email_address, (const char *));
4b6aa8
+        UREPORT_OPTION_VALUE_FROM_CONF(settings, "ContactEmail", email_address, (const char *));
4b6aa8
 
4b6aa8
         if (!email_address)
4b6aa8
             error_msg_and_die(_("Neither environment variable 'uReport_ContactEmail' nor configuration option 'ContactEmail' is set"));
4b6aa8
@@ -667,13 +179,13 @@ int main(int argc, char **argv)
4b6aa8
 
4b6aa8
         if (rhbz_bug >= 0)
4b6aa8
         {
4b6aa8
-            if (perform_attach(&config, ureport_hash, (attach_handler)wrp_ureport_attach_rhbz, (void *)&rhbz_bug))
4b6aa8
+            if (ureport_attach_int(ureport_hash, "RHBZ", rhbz_bug, &config))
4b6aa8
                 goto finalize;
4b6aa8
         }
4b6aa8
 
4b6aa8
         if (email_address)
4b6aa8
         {
4b6aa8
-            if (perform_attach(&config, ureport_hash, (attach_handler)ureport_attach_email, (void *)email_address))
4b6aa8
+            if (ureport_attach_string(ureport_hash, "email", email_address, &config))
4b6aa8
                 goto finalize;
4b6aa8
         }
4b6aa8
 
4b6aa8
@@ -683,95 +195,48 @@ int main(int argc, char **argv)
4b6aa8
     if (!ureport_hash && (rhbz_bug >= 0 || email_address))
4b6aa8
         error_msg_and_die(_("You need to specify bthash of the uReport to attach."));
4b6aa8
 
4b6aa8
-    /* -b, -a nor -r were specified - upload uReport from dump_dir */
4b6aa8
-    const char *server_url = config.ur_url;
4b6aa8
-    char *dest_url = concat_path_file(config.ur_url, REPORT_URL_SFX);
4b6aa8
-    config.ur_url = dest_url;
4b6aa8
-
4b6aa8
     char *json_ureport = ureport_from_dump_dir_ext(dump_dir_path, &(config.ur_prefs));
4b6aa8
     if (!json_ureport)
4b6aa8
     {
4b6aa8
         error_msg(_("Not uploading an empty uReport"));
4b6aa8
-        goto format_err;
4b6aa8
+        goto finalize;
4b6aa8
     }
4b6aa8
 
4b6aa8
-    post_state = ureport_post(json_ureport, &config);
4b6aa8
+    struct ureport_server_response *response = ureport_submit(json_ureport, &config);
4b6aa8
     free(json_ureport);
4b6aa8
 
4b6aa8
-    if (!post_state)
4b6aa8
-    {
4b6aa8
-        error_msg(_("Failed on submitting the problem"));
4b6aa8
-        goto format_err;
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    struct ureport_server_response *response = get_server_response(post_state, &config);
4b6aa8
-
4b6aa8
     if (!response)
4b6aa8
-        goto format_err;
4b6aa8
+        goto finalize;
4b6aa8
 
4b6aa8
-    if (!response->is_error)
4b6aa8
+    if (!response->urr_is_error)
4b6aa8
     {
4b6aa8
-        log_notice("is known: %s", response->value);
4b6aa8
+        log_notice("is known: %s", response->urr_value);
4b6aa8
         ret = 0; /* "success" */
4b6aa8
 
4b6aa8
-        dd = dd_opendir(dump_dir_path, /* flags */ 0);
4b6aa8
-        if (!dd)
4b6aa8
+        if (!ureport_server_response_save_in_dump_dir(response, dump_dir_path, &config))
4b6aa8
             xfunc_die();
4b6aa8
 
4b6aa8
-        if (response->bthash)
4b6aa8
-        {
4b6aa8
-            char *msg = xasprintf("uReport: BTHASH=%s", response->bthash);
4b6aa8
-            add_reported_to(dd, msg);
4b6aa8
-            free(msg);
4b6aa8
-
4b6aa8
-            char *bthash_url = concat_path_file(server_url, BTHASH_URL_SFX);
4b6aa8
-            msg = xasprintf("ABRT Server: URL=%s%s", bthash_url, response->bthash);
4b6aa8
-            add_reported_to(dd, msg);
4b6aa8
-            free(msg);
4b6aa8
-            free(bthash_url);
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        if (response->reported_to_list)
4b6aa8
-        {
4b6aa8
-            for (GList *e = response->reported_to_list; e; e = g_list_next(e))
4b6aa8
-                add_reported_to(dd, e->data);
4b6aa8
-        }
4b6aa8
-
4b6aa8
-        if (response->solution)
4b6aa8
-            dd_save_text(dd, FILENAME_NOT_REPORTABLE, response->solution);
4b6aa8
-
4b6aa8
-        dd_close(dd);
4b6aa8
-
4b6aa8
         /* If a reported problem is not known then emit NEEDMORE */
4b6aa8
-        if (strcmp("true", response->value) == 0)
4b6aa8
+        if (strcmp("true", response->urr_value) == 0)
4b6aa8
         {
4b6aa8
             log(_("This problem has already been reported."));
4b6aa8
-            if (response->message)
4b6aa8
-                log(response->message);
4b6aa8
+            if (response->urr_message)
4b6aa8
+                log(response->urr_message);
4b6aa8
 
4b6aa8
             ret = EXIT_STOP_EVENT_RUN;
4b6aa8
         }
4b6aa8
     }
4b6aa8
     else
4b6aa8
-    {
4b6aa8
-        error_msg(_("Server responded with an error: '%s'"), response->value);
4b6aa8
-    }
4b6aa8
-
4b6aa8
-    free_ureport_server_response(response);
4b6aa8
+        error_msg(_("Server responded with an error: '%s'"), response->urr_value);
4b6aa8
 
4b6aa8
-format_err:
4b6aa8
-    free_post_state(post_state);
4b6aa8
-    free(dest_url);
4b6aa8
+    ureport_server_response_free(response);
4b6aa8
 
4b6aa8
 finalize:
4b6aa8
-    if (config.ur_prefs.urp_auth_items != auth_items)
4b6aa8
-        g_list_free_full(config.ur_prefs.urp_auth_items, free);
4b6aa8
-
4b6aa8
-    free_map_string(config.ur_http_headers);
4b6aa8
+    if (config.ur_prefs.urp_auth_items == auth_items)
4b6aa8
+        config.ur_prefs.urp_auth_items = NULL;
4b6aa8
 
4b6aa8
     free_map_string(settings);
4b6aa8
-    free(config.ur_client_cert);
4b6aa8
-    free(config.ur_client_key);
4b6aa8
+    ureport_server_config_destroy(&config);
4b6aa8
 
4b6aa8
     return ret;
4b6aa8
 }
4b6aa8
-- 
4b6aa8
1.8.3.1
4b6aa8