[PATCH] track#552 : reporter-ureport : allows to enable/disable SSL certificate checking
by Jakub Filak
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/include/internal_libreport.h | 3 --
src/lib/Makefile.am | 3 +-
src/lib/json.c | 15 +++++++-----
src/lib/ureport.h | 46 +++++++++++++++++++++++++++++++++++++
src/plugins/report_uReport.xml.in | 5 ++++
src/plugins/ureport.c | 23 ++++++++----------
6 files changed, 72 insertions(+), 23 deletions(-)
create mode 100644 src/lib/ureport.h
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index c57bd5f..6a7b53a 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -795,9 +795,6 @@ void show_usage_and_die(const char *usage, const struct options *opt) NORETURN;
*/
struct abrt_post_state;
-#define post_ureport libreport_post_ureport
-struct abrt_post_state *post_ureport(problem_data_t *pd, const char *ureport_url);
-
#ifdef __cplusplus
}
#endif
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index b7af8c1..c29dd6b 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -93,7 +93,8 @@ libabrt_dbus_la_LIBADD = \
libabrt_web_la_SOURCES = \
abrt_curl.h abrt_curl.c \
abrt_xmlrpc.h abrt_xmlrpc.c \
- proxies.h proxies.c json.c
+ proxies.h proxies.c \
+ ureport.h json.c
libabrt_web_la_CPPFLAGS = \
-Wall -Wwrite-strings -Werror \
-I$(srcdir)/../include \
diff --git a/src/lib/json.c b/src/lib/json.c
index 6d4d72a..53d9bcb 100644
--- a/src/lib/json.c
+++ b/src/lib/json.c
@@ -22,6 +22,7 @@
#include <btparser/core-backtrace.h>
#include "internal_libreport.h"
+#include "ureport.h"
#include "abrt_curl.h"
@@ -250,12 +251,14 @@ char *new_json_ureport(problem_data_t *pd)
return j;
}
-abrt_post_state_t *post_ureport(problem_data_t *pd, const char *ureport_url)
+struct abrt_post_state *post_ureport(problem_data_t *pd, struct ureport_server_config *config)
{
- abrt_post_state_t *post_state;
- post_state = new_abrt_post_state(ABRT_POST_WANT_BODY
- | ABRT_POST_WANT_SSL_VERIFY
- | ABRT_POST_WANT_ERROR_MSG);
+ int flags = ABRT_POST_WANT_BODY | ABRT_POST_WANT_ERROR_MSG;
+
+ if (config->b_ssl_verify)
+ flags |= ABRT_POST_WANT_SSL_VERIFY;
+
+ abrt_post_state_t *post_state = new_abrt_post_state(flags);
static const char *headers[] = {
"Accept: application/json",
@@ -265,7 +268,7 @@ abrt_post_state_t *post_ureport(problem_data_t *pd, const char *ureport_url)
char *json_ureport = new_json_ureport(pd);
- abrt_post_string_as_form_data(post_state, ureport_url, "application/json",
+ abrt_post_string_as_form_data(post_state, config->s_url, "application/json",
headers, json_ureport);
free(json_ureport);
diff --git a/src/lib/ureport.h b/src/lib/ureport.h
new file mode 100644
index 0000000..e2d2638
--- /dev/null
+++ b/src/lib/ureport.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2012 ABRT team
+ Copyright (C) 2012 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef UREPORT_H_
+#define UREPORT_H_
+
+#include "problem_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * uReport server configuration
+ */
+struct ureport_server_config
+{
+ const char *s_url; ///< Web service URL
+ bool b_ssl_verify; ///< Verify HOST and PEER certificates
+};
+
+struct abrt_post_state;
+
+#define post_ureport libreport_post_ureport
+struct abrt_post_state *post_ureport(problem_data_t *pd, struct ureport_server_config *config);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/report_uReport.xml.in b/src/plugins/report_uReport.xml.in
index 77b5880..988db55 100644
--- a/src/plugins/report_uReport.xml.in
+++ b/src/plugins/report_uReport.xml.in
@@ -13,5 +13,10 @@
<_description>Address of uReport webservice</_description>
<default-value>https://retrace.fedoraproject.org/faf/reports/new/</default-value>
</option>
+ <option type="bool" name="uReport_SSLVerify">
+ <_label>Verify SSL</_label>
+ <_description>Check SSL key validity</_description>
+ <default-value>yes</default-value>
+ </option>
</options>
</event>
diff --git a/src/plugins/ureport.c b/src/plugins/ureport.c
index 21dc58d..8ba4bd3 100644
--- a/src/plugins/ureport.c
+++ b/src/plugins/ureport.c
@@ -19,17 +19,10 @@
#include <json/json.h>
#include "internal_libreport.h"
+#include "ureport.h"
#include "abrt_curl.h"
/*
- * uReport server configuration
- */
-struct ureport_server_config
-{
- const char *url; ///< Web service URL
-};
-
-/*
* Loads uReport configuration from various sources.
*
* Replaces a value of an already configured option only if the
@@ -42,7 +35,10 @@ static void load_ureport_server_config(struct ureport_server_config *config)
const char *environ;
environ = getenv("uReport_URL");
- config->url = environ ? environ : config->url;
+ config->s_url = environ ? environ : config->s_url;
+
+ environ = getenv("uReport_SSLVerify");
+ config->b_ssl_verify = environ ? string_to_bool(environ) : config->b_ssl_verify;
}
@@ -93,14 +89,15 @@ int main(int argc, char **argv)
abrt_init(argv);
struct ureport_server_config config = {
- .url = "https://retrace.fedoraproject.org/faf/reports/new/",
+ .s_url = "https://retrace.fedoraproject.org/faf/reports/new/",
+ .b_ssl_verify = true,
};
const char *dump_dir_path = ".";
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT__DUMP_DIR(&dump_dir_path),
- OPT_STRING('u', "url", &config.url, "URL", _("Specify url")),
+ OPT_STRING('u', "url", &config.s_url, "URL", _("Specify url")),
OPT_END(),
};
@@ -123,7 +120,7 @@ int main(int argc, char **argv)
xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
abrt_post_state_t *post_state = NULL;
- post_state = post_ureport(pd, config.url);
+ post_state = post_ureport(pd, &config);
free_problem_data(pd);
if (post_state->http_resp_code != 200)
@@ -131,7 +128,7 @@ int main(int argc, char **argv)
char *errmsg = post_state->curl_error_msg;
if (errmsg && *errmsg)
{
- error_msg("%s '%s'", errmsg, config.url);
+ error_msg("%s '%s'", errmsg, config.s_url);
free_abrt_post_state(post_state);
return 1;
}
--
1.7.7.6
11 years, 11 months
[PATCH] trac#522 : abrt vmcore plugin verbosity correction
by Jakub Filak
---
src/plugins/abrt-action-analyze-vmcore.in | 7 +++++--
src/plugins/abrt-action-install-debuginfo.in | 2 ++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/plugins/abrt-action-analyze-vmcore.in b/src/plugins/abrt-action-analyze-vmcore.in
index af606c4..c3f2e2e 100644
--- a/src/plugins/abrt-action-analyze-vmcore.in
+++ b/src/plugins/abrt-action-analyze-vmcore.in
@@ -25,7 +25,7 @@ if __name__ == "__main__":
vmlinux_di_path = ""
tmpdir = ""
vmcore = ""
- global verbose
+
help_text = _("Usage: {0} [-v[v]] [--core=VMCORE] [--tmpdir=TMPDIR] [--cache=CACHEDIR]").format(PROGNAME)
try:
opts, args = getopt.getopt(sys.argv[1:], "hvd", ["help", "tmpdir=", "cache=", "core="])
@@ -114,7 +114,10 @@ if __name__ == "__main__":
error_msg_and_die(_("Can't download required debuginfo"))
vmlinux_di_path = "{0}/usr/lib/debug/lib/modules/{1}/vmlinux".format(vmlinux_di_cachedir, ver)
else: # we need to use the suid wrapper, if we use the default cache
- downloader = Popen(["/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache", "--size_mb=4096", "--exact={0}".format(vmlinux_di_path)], bufsize = -1)
+ downloader_args = ["/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache", "--size_mb=4096", "--exact={0}".format(vmlinux_di_path)]
+ if verbose > 0:
+ downloader_args.append("-{0}".format("v"*verbose))
+ downloader = Popen(downloader_args, bufsize = -1)
res = downloader.wait()
if res != 0:
error_msg_and_die(_("Can't download required debuginfo"))
diff --git a/src/plugins/abrt-action-install-debuginfo.in b/src/plugins/abrt-action-install-debuginfo.in
index a6c39f3..a061fa6 100644
--- a/src/plugins/abrt-action-install-debuginfo.in
+++ b/src/plugins/abrt-action-install-debuginfo.in
@@ -134,6 +134,8 @@ if __name__ == "__main__":
missing=arg.split(':')
exact_fls = True
+ set_verbosity(verbose)
+
if not cachedirs:
try:
fp = open("@sysconfdir@/@PACKAGE_NAME(a)/plugins/CCpp.conf", "r")
--
1.7.7.6
11 years, 11 months
[PATCH v3] rhbz#747410 : bugzillas are created according to rh bugzilla default template
by Jakub Filak
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/include/internal_libreport.h | 4 +
src/lib/make_descr.c | 43 ++++++---
src/plugins/reporter-bugzilla.c | 2 +-
src/plugins/rhbz.c | 200 ++++++++++++++++++++++++++++++++++----
4 files changed, 215 insertions(+), 34 deletions(-)
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index c57bd5f..23a74f9 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -581,6 +581,8 @@ enum {
};
#define make_description libreport_make_description
char *make_description(problem_data_t *problem_data, char **names_to_skip, unsigned max_text_size, unsigned desc_flags);
+#define make_description_item_multiline libreport_make_description_item_multiline
+char *make_description_item_multiline(const char *name, const char* content);
#define make_description_bz libreport_make_description_bz
char* make_description_bz(problem_data_t *problem_data, unsigned max_text_size);
#define make_description_logger libreport_make_description_logger
@@ -716,6 +718,8 @@ struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name
#define FILENAME_PKG_VERSION "pkg_version"
#define FILENAME_PKG_RELEASE "pkg_release"
#define FILENAME_PKG_ARCH "pkg_arch"
+#define FILENAME_USERNAME "username"
+#define FILENAME_ABRT_VERSION "abrt_version"
// Not stored as files, added "on the fly":
#define CD_DUMPDIR "Directory"
diff --git a/src/lib/make_descr.c b/src/lib/make_descr.c
index 68133a2..21fb83c 100644
--- a/src/lib/make_descr.c
+++ b/src/lib/make_descr.c
@@ -26,6 +26,27 @@ static bool rejected_name(const char *name, char **v, int flags)
return r;
}
+char *make_description_item_multiline(const char *name, const char *content)
+{
+ char *eol = strchr(content, '\n');
+ if (!eol)
+ return NULL;
+
+ struct strbuf *buf = strbuf_new();
+ strbuf_append_str(buf, name);
+ strbuf_append_str(buf, ":\n");
+ for (;;)
+ {
+ eol = strchrnul(content, '\n');
+ strbuf_append_strf(buf, ":%.*s\n", (int)(eol - content), content);
+ if (*eol == '\0' || eol[1] == '\0')
+ break;
+ content = eol + 1;
+ }
+
+ return strbuf_free_nobuf(buf);
+}
+
char *make_description(problem_data_t *problem_data, char **names_to_skip,
unsigned max_text_size, unsigned desc_flags)
{
@@ -171,24 +192,18 @@ char *make_description(problem_data_t *problem_data, char **names_to_skip,
|| (!strcmp(analyzer, "Kerneloops") && !strcmp(key, FILENAME_BACKTRACE))))
{
char *formatted = format_problem_item(item);
- char *output = formatted ? formatted : item->content;
- char *eol = strchr(output, '\n');
- if (eol)
+ char *output = make_description_item_multiline(key, formatted ? formatted : item->content);
+
+ if (output)
{
if (!empty)
- strbuf_append_char(buf_dsc, '\n');
- strbuf_append_str(buf_dsc, key);
- strbuf_append_str(buf_dsc, ":\n");
- for (;;)
- {
- eol = strchrnul(output, '\n');
- strbuf_append_strf(buf_dsc, ":%.*s\n", (int)(eol - output), output);
- if (*eol == '\0' || eol[1] == '\0')
- break;
- output = eol + 1;
- }
+ strbuf_append_str(buf_dsc, "\n");
+
+ strbuf_append_str(buf_dsc, output);
empty = false;
+ free(output);
}
+
free(formatted);
}
}
diff --git a/src/plugins/reporter-bugzilla.c b/src/plugins/reporter-bugzilla.c
index b96549d..bb59ac7 100644
--- a/src/plugins/reporter-bugzilla.c
+++ b/src/plugins/reporter-bugzilla.c
@@ -349,7 +349,7 @@ int main(int argc, char **argv)
int flags = RHBZ_NOMAIL_NOTIFY;
if (opts & OPT_b)
flags |= RHBZ_ATTACH_BINARY_FILES;
- rhbz_attach_big_files(client, bug_id_str, problem_data, flags);
+ rhbz_attach_files(client, bug_id_str, problem_data, flags);
bz = new_bug_info();
bz->bi_status = xstrdup("NEW");
diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c
index 53662e1..1eca5d4 100644
--- a/src/plugins/rhbz.c
+++ b/src/plugins/rhbz.c
@@ -19,6 +19,11 @@
#include "internal_libreport.h"
#include "rhbz.h"
+#include <btparser/location.h>
+#include <btparser/backtrace.h>
+#include <btparser/thread.h>
+#include <btparser/frame.h>
+#include <btparser/strbuf.h>
#define MAX_HOPS 5
@@ -32,6 +37,78 @@
#define func_entry_str(x)
#endif
+/******************************************************************************/
+/* Global lists used in process of creation a new Bugzilla bug
+ * New bug logic:
+ * 1. 'Description of problem' is FILENAME_COMMENT
+ * 2. 'Version-Release number of selected component' is FILENAME_PACKAGE
+ * 3. 'Additional info' is a concatenation of files listed in ADDITIONAL_INFO_FILES_LIST
+ * 3.1 backtrace is also present in 'Additional info'
+ * - if has size lower than CD_TEXT_ATT_SIZE_BZ then the bactrace is inlined
+ * - otherwise preview of crashed thread stack trace is created
+ * 4. Attached files are the ONLY files which ARE NOT present in g_not_attached_files
+ * As you can see g_not_attached_files consist from some hardcoded files and
+ * files from ADDITIONAL_INFO_FILES_LIST.
+ * Black list for attached files allows us to attach all of files added to
+ * a problem directory by a user.
+ */
+#define INLINED_FILES_LIST\
+ FILENAME_ABRT_VERSION,\
+ FILENAME_RATING,\
+ FILENAME_KERNEL,\
+ FILENAME_CRASH_FUNCTION,\
+ FILENAME_CMDLINE,\
+ FILENAME_TAINTED_LONG
+
+/* Items we want to use in additional info */
+static const char *const g_additional_info_files[] = {
+ /* ! a bunch of file names */
+ INLINED_FILES_LIST,
+ NULL
+};
+
+/* Items we don't want to attach to bz */
+static const char *const g_not_attached_files[] = {
+ CD_DUMPDIR,
+ FILENAME_ANALYZER,
+ FILENAME_PID,
+ FILENAME_PWD,
+ FILENAME_ROOTDIR,
+ FILENAME_BINARY,
+ FILENAME_COREDUMP,
+ FILENAME_DUPHASH,
+ FILENAME_ARCHITECTURE,
+ FILENAME_OS_RELEASE,
+ FILENAME_OS_RELEASE_IN_ROOTDIR,
+ FILENAME_PACKAGE,
+ FILENAME_COMPONENT,
+ FILENAME_COMMENT,
+ FILENAME_HOSTNAME,
+ FILENAME_REASON,
+ FILENAME_UID,
+ FILENAME_REMOTE,
+ FILENAME_TAINTED,
+ FILENAME_TAINTED_SHORT,
+ FILENAME_VMCORE,
+ FILENAME_UUID,
+ FILENAME_COUNT,
+ FILENAME_REPORTED_TO,
+ FILENAME_NOT_REPORTABLE,
+ FILENAME_PKG_EPOCH,
+ FILENAME_PKG_NAME,
+ FILENAME_PKG_VERSION,
+ FILENAME_PKG_RELEASE,
+ FILENAME_PKG_ARCH,
+ FILENAME_REMOTE_RESULT,
+ FILENAME_USERNAME,
+ FILENAME_TIME,
+ FILENAME_EXECUTABLE,
+ /* ! a bunch of file names */
+ INLINED_FILES_LIST,
+ NULL
+};
+/******************************************************************************/
+
struct bug_info *new_bug_info()
{
func_entry();
@@ -461,6 +538,61 @@ struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id)
return bz;
}
+/*
+ * Produces an optimal backtrace for bugzilla comment
+ */
+char *rhbz_get_backtrace_info(problem_data_t *problem_data, size_t max_text_size)
+{
+ const problem_item *item = get_problem_data_item_or_NULL(problem_data,
+ FILENAME_BACKTRACE);
+
+ if (!item)
+ return NULL;
+
+ char *truncated = NULL;
+
+ if (strlen(item->content) >= max_text_size)
+ {
+ struct btp_location location;
+ btp_location_init(&location);
+
+ /* btp_backtrace_parse modifies the input parameter */
+ char *content = item->content;
+ struct btp_backtrace *backtrace = btp_backtrace_parse((const char **)&content, &location);
+
+ if (!backtrace)
+ {
+ log(_("Can't parse backtrace"));
+ return NULL;
+ }
+
+ /* Get optimized thread stack trace for 10 top most frames */
+ struct btp_thread *thread = btp_backtrace_get_optimized_thread(backtrace, 10);
+ if (!thread)
+ {
+ btp_backtrace_free(backtrace);
+ log(_("Can't find crash thread"));
+ return NULL;
+ }
+
+ /* Cannot be NULL, it dies on memory error */
+ struct btp_strbuf *bt = btp_strbuf_new();
+
+ for (struct btp_frame *frame = thread->frames; frame; frame=frame->next)
+ btp_frame_append_to_str(frame, bt, true);
+
+ btp_backtrace_free(backtrace);
+
+ truncated = btp_strbuf_free_nobuf(bt);
+ }
+
+ char *bt = make_description_item_multiline(truncated ? "truncated backtrace" : FILENAME_BACKTRACE,
+ truncated ? truncated : item->content);
+ free(truncated);
+
+ return bt;
+}
+
/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
const char *release)
@@ -520,14 +652,39 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
}
char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
- char *bz_dsc;
+ char *full_dsc = NULL;
if (analyzer && !strcmp(analyzer, "Kerneloops"))
- bz_dsc = make_description_koops(problem_data, CD_TEXT_ATT_SIZE_BZ);
+ {
+ char *bz_dsc = make_description_koops(problem_data, CD_TEXT_ATT_SIZE_BZ);
+ full_dsc = xasprintf("libreport version: "VERSION"\n%s", bz_dsc);
+ free(bz_dsc);
+ }
else
- bz_dsc = make_description_bz(problem_data, CD_TEXT_ATT_SIZE_BZ);
-
- char *full_dsc = xasprintf("libreport version: "VERSION"\n%s", bz_dsc);
- free(bz_dsc);
+ {
+ const char *comment = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_COMMENT);
+
+ char *bz_dsc = make_description(problem_data, (char**)g_additional_info_files,
+ CD_TEXT_ATT_SIZE_BZ, MAKEDESC_SHOW_MULTILINE | MAKEDESC_WHITELIST);
+
+ char *backtrace = rhbz_get_backtrace_info(problem_data, CD_TEXT_ATT_SIZE_BZ);
+
+ /* Generating of a description according to the RH bugzilla default format:
+ * https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora
+ */
+ full_dsc = xasprintf("Description of problem:\n%s\n\n"
+ "Version-Release number of selected component:\n%s\n\n"
+ "Additional info:\n"
+ "libreport version: "VERSION"\n%s\n"
+ "%s",
+ comment ? comment : "",
+ package,
+ bz_dsc,
+ backtrace ? backtrace : "");
+
+ free(backtrace);
+ free(bz_dsc);
+ }
char *product = NULL;
char *version = NULL;
@@ -631,7 +788,7 @@ int rhbz_attach_fd(struct abrt_xmlrpc *ax, const char *filename,
}
/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
-int rhbz_attach_big_files(struct abrt_xmlrpc *ax, const char *bug_id,
+int rhbz_attach_files(struct abrt_xmlrpc *ax, const char *bug_id,
problem_data_t *problem_data, int flags)
{
func_entry();
@@ -643,26 +800,31 @@ int rhbz_attach_big_files(struct abrt_xmlrpc *ax, const char *bug_id,
const char *analyzer = get_problem_item_content_or_NULL(problem_data,
FILENAME_ANALYZER);
+ const bool kerneloops = !strcmp(analyzer, "Kerneloops");
+
g_hash_table_iter_init(&iter, problem_data);
while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
+ /* The not attached list is used only if analyzer is not Kerneloops.
+ *
+ * For standard bugs:
+ * The not attached list contains files that are used in the description or
+ * in the bug fields and the files which nobody is interested in.
+ */
+ if (!kerneloops && is_in_string_list(name, (char**)g_not_attached_files))
+ continue;
+
if (value->flags & CD_FLAG_TXT)
{
const char *content = value->content;
- if (!strcmp(analyzer, "Kerneloops") && !strcmp(name, FILENAME_BACKTRACE))
- continue;
+ const unsigned len = strlen(content);
- unsigned len = strlen(content);
+ /* Do not attach backtrace in case of Kerneloops */
+ /* For standard bugs, do not attach backtrace shorter than CD_TEXT_ATT_SIZE_BZ */
+ if (!strcmp(name, FILENAME_BACKTRACE) && (kerneloops || len < CD_TEXT_ATT_SIZE_BZ))
+ continue;
- // We were special-casing FILENAME_BACKTRACE here, but karel says
- // he can retrieve it in inlined form from comments too.
- if (len > CD_TEXT_ATT_SIZE_BZ /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/)
- {
- /* This text item wasn't added in comments, it is too big
- * for that. Attach it as a file.
- */
- rhbz_attach_blob(ax, name, bug_id, content, len, flags);
- }
+ rhbz_attach_blob(ax, name, bug_id, content, len, flags);
}
if ((flags & RHBZ_ATTACH_BINARY_FILES) && (value->flags & CD_FLAG_BIN))
{
--
1.7.7.6
11 years, 11 months
[PATCH v2] rhbz#747410 : bugzillas are created according to rh bugzilla default template
by Jakub Filak
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/include/internal_libreport.h | 2 +
src/lib/make_descr.c | 19 ------
src/plugins/reporter-bugzilla.c | 2 +-
src/plugins/rhbz.c | 122 +++++++++++++++++++++++++++++++------
4 files changed, 105 insertions(+), 40 deletions(-)
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index c57bd5f..5e117d7 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -716,6 +716,8 @@ struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name
#define FILENAME_PKG_VERSION "pkg_version"
#define FILENAME_PKG_RELEASE "pkg_release"
#define FILENAME_PKG_ARCH "pkg_arch"
+#define FILENAME_USERNAME "username"
+#define FILENAME_ABRT_VERSION "abrt_version"
// Not stored as files, added "on the fly":
#define CD_DUMPDIR "Directory"
diff --git a/src/lib/make_descr.c b/src/lib/make_descr.c
index 68133a2..01251ab 100644
--- a/src/lib/make_descr.c
+++ b/src/lib/make_descr.c
@@ -282,22 +282,3 @@ char* make_description_logger(problem_data_t *problem_data, unsigned max_text_si
MAKEDESC_SHOW_FILES | MAKEDESC_SHOW_MULTILINE
);
}
-
-/* Items we want to include to bz */
-static const char *const whitelisted_items[] = {
- FILENAME_CMDLINE,
- FILENAME_BACKTRACE,
- NULL
-};
-
-char* make_description_koops(problem_data_t *problem_data, unsigned max_text_size)
-{
- return make_description(
- problem_data,
- (char**)whitelisted_items,
- max_text_size,
- MAKEDESC_SHOW_FILES
- | MAKEDESC_SHOW_MULTILINE
- | MAKEDESC_WHITELIST
- );
-}
diff --git a/src/plugins/reporter-bugzilla.c b/src/plugins/reporter-bugzilla.c
index b96549d..bb59ac7 100644
--- a/src/plugins/reporter-bugzilla.c
+++ b/src/plugins/reporter-bugzilla.c
@@ -349,7 +349,7 @@ int main(int argc, char **argv)
int flags = RHBZ_NOMAIL_NOTIFY;
if (opts & OPT_b)
flags |= RHBZ_ATTACH_BINARY_FILES;
- rhbz_attach_big_files(client, bug_id_str, problem_data, flags);
+ rhbz_attach_files(client, bug_id_str, problem_data, flags);
bz = new_bug_info();
bz->bi_status = xstrdup("NEW");
diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c
index 39bde75..f73e85f 100644
--- a/src/plugins/rhbz.c
+++ b/src/plugins/rhbz.c
@@ -32,6 +32,76 @@
#define func_entry_str(x)
#endif
+/******************************************************************************/
+/* Global lists used in process of creation a new Bugzilla bug
+ * New bug logic:
+ * 1. 'Description of problem' is FILENAME_COMMENT
+ * 2. 'Version-Release number of selected component' is FILENAME_PACKAGE
+ * 3. 'Additional info' is a concatenation of files listed in ADDITIONAL_INFO_FILES_LIST
+ * 4. Attached files are the ONLY files which ARE NOT present in g_not_attached_files
+ * As you can see g_not_attached_files consist from some hardcoded files and
+ * files from ADDITIONAL_INFO_FILES_LIST.
+ * Black list for attached files allows us to attach all of files added to
+ * a problem directory by a user.
+ */
+#define ADDITIONAL_INFO_FILES_LIST\
+ FILENAME_ABRT_VERSION,\
+ FILENAME_RATING,\
+ FILENAME_TIME,\
+ FILENAME_KERNEL,\
+ FILENAME_CRASH_FUNCTION,\
+ FILENAME_EXECUTABLE,\
+ FILENAME_CMDLINE,\
+ FILENAME_TAINTED_LONG,\
+ FILENAME_BACKTRACE
+
+/* Items we want to use in additional info */
+static const char *const g_additional_info_files[] = {
+ /* ! a bunch of file names */
+ ADDITIONAL_INFO_FILES_LIST,
+ NULL
+};
+
+/* Items we don't want to attach to bz */
+static const char *const g_not_attached_files[] = {
+ CD_DUMPDIR,
+ FILENAME_ANALYZER,
+ FILENAME_PID,
+ FILENAME_PWD,
+ FILENAME_ROOTDIR,
+ FILENAME_BINARY,
+ FILENAME_COREDUMP,
+ FILENAME_DUPHASH,
+ FILENAME_ARCHITECTURE,
+ FILENAME_OS_RELEASE,
+ FILENAME_OS_RELEASE_IN_ROOTDIR,
+ FILENAME_PACKAGE,
+ FILENAME_COMPONENT,
+ FILENAME_COMMENT,
+ FILENAME_HOSTNAME,
+ FILENAME_REASON,
+ FILENAME_UID,
+ FILENAME_REMOTE,
+ FILENAME_TAINTED,
+ FILENAME_TAINTED_SHORT,
+ FILENAME_VMCORE,
+ FILENAME_UUID,
+ FILENAME_COUNT,
+ FILENAME_REPORTED_TO,
+ FILENAME_NOT_REPORTABLE,
+ FILENAME_PKG_EPOCH,
+ FILENAME_PKG_NAME,
+ FILENAME_PKG_VERSION,
+ FILENAME_PKG_RELEASE,
+ FILENAME_PKG_ARCH,
+ FILENAME_REMOTE_RESULT,
+ FILENAME_USERNAME,
+ /* ! a bunch of file names */
+ ADDITIONAL_INFO_FILES_LIST,
+ NULL
+};
+/******************************************************************************/
+
struct bug_info *new_bug_info()
{
func_entry();
@@ -495,6 +565,8 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
FILENAME_ANALYZER);
const char *tainted_short = get_problem_item_content_or_NULL(problem_data,
FILENAME_TAINTED_SHORT);
+ const char *comment = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_COMMENT);
struct strbuf *buf_summary = strbuf_new();
if (analyzer && strcmp(analyzer, "libreport") == 0)
@@ -522,13 +594,26 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
}
char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
- char *bz_dsc;
- if (analyzer && !strcmp(analyzer, "Kerneloops"))
- bz_dsc = make_description_koops(problem_data, CD_TEXT_ATT_SIZE_BZ);
- else
- bz_dsc = make_description_bz(problem_data, CD_TEXT_ATT_SIZE_BZ);
+ /* A special handling of Kerneloops was removed because we met their
+ * requirements with default format
+ */
+
+ /* CD_MAX_TEXT_SIZE is used because we want to attach all text files
+ * listed in g_additional_info_files
+ */
+ char *bz_dsc = make_description(problem_data, (char**)g_additional_info_files,
+ CD_MAX_TEXT_SIZE,
+ MAKEDESC_SHOW_MULTILINE | MAKEDESC_WHITELIST);
+
+ /* Generating of a description according to the RH bugzilla default format:
+ * https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora
+ */
+ char *full_dsc = xasprintf("Description of problem:\n%s\n\n"
+ "Version-Release number of selected component:\n%s\n\n"
+ "Additional info:\n"
+ "libreport version: "VERSION"\n%s\n",
+ comment, package, bz_dsc);
- char *full_dsc = xasprintf("libreport version: "VERSION"\n%s", bz_dsc);
free(bz_dsc);
char *product = NULL;
@@ -633,7 +718,7 @@ int rhbz_attach_fd(struct abrt_xmlrpc *ax, const char *filename,
}
/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
-int rhbz_attach_big_files(struct abrt_xmlrpc *ax, const char *bug_id,
+int rhbz_attach_files(struct abrt_xmlrpc *ax, const char *bug_id,
problem_data_t *problem_data, int flags)
{
func_entry();
@@ -648,23 +733,20 @@ int rhbz_attach_big_files(struct abrt_xmlrpc *ax, const char *bug_id,
g_hash_table_iter_init(&iter, problem_data);
while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
+ /* The not attached list should contain files used in the description,
+ * the bug fields and the files which nobody is interested in.
+ */
+ if (is_in_string_list(name, g_not_attached_files))
+ continue;
+
+ /* All other files are to be attached.
+ */
if (value->flags & CD_FLAG_TXT)
{
const char *content = value->content;
- if (!strcmp(analyzer, "Kerneloops") && !strcmp(name, FILENAME_BACKTRACE))
- continue;
+ const unsigned len = strlen(content);
- unsigned len = strlen(content);
-
- // We were special-casing FILENAME_BACKTRACE here, but karel says
- // he can retrieve it in inlined form from comments too.
- if (len > CD_TEXT_ATT_SIZE_BZ /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/)
- {
- /* This text item wasn't added in comments, it is too big
- * for that. Attach it as a file.
- */
- rhbz_attach_blob(ax, name, bug_id, content, len, flags);
- }
+ rhbz_attach_blob(ax, name, bug_id, content, len, flags);
}
if ((flags & RHBZ_ATTACH_BINARY_FILES) && (value->flags & CD_FLAG_BIN))
{
--
1.7.7.6
11 years, 11 months
[PATCH v4] track#549 : gnome-keyring replaced by Secret Service API
by Jakub Filak
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
configure.ac | 1 +
libreport.spec.in | 7 -
src/gtk-helpers/Makefile.am | 6 +-
src/gtk-helpers/abrt-keyring.c | 130 ----
src/gtk-helpers/event_config_dialog.c | 71 +--
src/gtk-helpers/internal_libreport_gtk.h | 15 +-
src/gtk-helpers/secrets.c | 1053 ++++++++++++++++++++++++++++++
src/gui-wizard-gtk/main.c | 2 +-
8 files changed, 1075 insertions(+), 210 deletions(-)
delete mode 100644 src/gtk-helpers/abrt-keyring.c
create mode 100644 src/gtk-helpers/secrets.c
diff --git a/configure.ac b/configure.ac
index 25d44e5..06896a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,7 @@ AC_SUBST(XMLRPC_CLIENT_LIBS)
PKG_CHECK_MODULES([GTK], [gtk+-3.0])
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.21])
+PKG_CHECK_MODULES([GIO], [gio-2.0])
PKG_CHECK_MODULES([DBUS], [dbus-1])
PKG_CHECK_MODULES([LIBXML], [libxml-2.0])
PKG_CHECK_MODULES([NSS], [nss])
diff --git a/libreport.spec.in b/libreport.spec.in
index f65c87d..ebe247a 100644
--- a/libreport.spec.in
+++ b/libreport.spec.in
@@ -35,13 +35,6 @@ Requires: libreport-filesystem
# FIXME: can be removed when F15 will EOLed, needs to stay in rhel6!
Requires: libreport-python
-# for rhel6
-%if 0%{?rhel} >= 6
-BuildRequires: gnome-keyring-devel
-%else
-BuildRequires: libgnome-keyring-devel
-%endif
-
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%description
diff --git a/src/gtk-helpers/Makefile.am b/src/gtk-helpers/Makefile.am
index 188e48f..68ec928 100644
--- a/src/gtk-helpers/Makefile.am
+++ b/src/gtk-helpers/Makefile.am
@@ -10,7 +10,7 @@ lib_LTLIBRARIES = \
libreport_gtk_la_SOURCES = \
event_config_dialog.c \
- abrt-keyring.c \
+ secrets.c \
hyperlinks.c \
autowrapped_label.c
@@ -20,14 +20,14 @@ libreport_gtk_la_CPPFLAGS = \
-I$(srcdir)/../lib \
$(GTK_CFLAGS) \
$(GLIB_CFLAGS) \
- $(GNOME_KEYRING_CFLAGS) \
+ $(GIO_CFLAGS) \
-D_GNU_SOURCE
libreport_gtk_la_LDFLAGS = \
-version-info 0:1:0
libreport_gtk_la_LIBADD = \
$(GTK_LIBS) \
$(GLIB_LIBS) \
- $(GNOME_KEYRING_LIBS) \
+ $(GIO_LIBS) \
../lib/libreport.la
EXTRA_DIST = libreport-gtk.pc.in
diff --git a/src/gtk-helpers/abrt-keyring.c b/src/gtk-helpers/abrt-keyring.c
deleted file mode 100644
index 4e830ca..0000000
--- a/src/gtk-helpers/abrt-keyring.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- Copyright (C) 2011 ABRT Team
- Copyright (C) 2011 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <gnome-keyring.h>
-#include "internal_libreport.h"
-#include "internal_libreport_gtk.h"
-
-static char *keyring_name;
-static bool got_keyring = 0;
-bool g_keyring_available = 1; //by default we assume that keyring is available
-
-guint32 find_keyring_item_id_for_event(const char *event_name)
-{
- GnomeKeyringAttributeList *attrs = gnome_keyring_attribute_list_new();
- GList *found = NULL;
- gnome_keyring_attribute_list_append_string(attrs, "libreportEventConfig", event_name);
- GnomeKeyringResult result = gnome_keyring_find_items_sync(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- attrs,
- &found);
- gnome_keyring_attribute_list_free(attrs);
-
- //let's hope 0 is not valid item_id
- guint32 item_id = 0;
- if (result != GNOME_KEYRING_RESULT_OK)
- goto ret;
- if (found)
- item_id = ((GnomeKeyringFound *)found->data)->item_id;
- ret:
- if (found)
- gnome_keyring_found_list_free(found);
- VERB2 log("keyring has %sconfiguration for event '%s'", (item_id != 0) ? "" : "no ", event_name);
- return item_id;
-}
-
-static void abrt_keyring_load_settings(const char *event_name, event_config_t *ec)
-{
- //don't bother when we already know that keyring is not available
- if (!g_keyring_available)
- return;
-
- guint item_id = find_keyring_item_id_for_event(event_name);
- if (!item_id)
- return;
- GnomeKeyringAttributeList *attrs = NULL;
- GnomeKeyringResult result = gnome_keyring_item_get_attributes_sync(
- keyring_name,
- item_id,
- &attrs);
- if (result == GNOME_KEYRING_RESULT_OK && attrs)
- {
- VERB3 log("num attrs %i", attrs->len);
- guint index;
- for (index = 0; index < attrs->len; index++)
- {
- char *name = g_array_index(attrs, GnomeKeyringAttribute, index).name;
- VERB2 log("keyring has name '%s'", name);
- event_option_t *option = get_event_option_from_list(name, ec->options);
- if (option)
- {
- free(option->eo_value);
- option->eo_value = xstrdup(g_array_index(attrs, GnomeKeyringAttribute, index).value.string);
- VERB2 log("added or replaced in event config:'%s=%s'", name, option->eo_value);
- }
- }
- }
- if (attrs)
- gnome_keyring_attribute_list_free(attrs);
-}
-
-static void init_keyring()
-{
- /* Called again? */
- if (got_keyring || !g_keyring_available)
- return;
-
- if (!gnome_keyring_is_available())
- {
- g_keyring_available = 0;
- VERB1 error_msg("Cannot connect to Gnome keyring daemon");
- return;
- }
-
- GnomeKeyringResult result = gnome_keyring_get_default_keyring_sync(&keyring_name);
- if (result != GNOME_KEYRING_RESULT_OK)
- {
- error_msg("Can't get default keyring (result:%d)", result);
- return;
- }
-
- got_keyring = 1;
- /*
- * Note: The default keyring name can be NULL. It is a valid name.
- */
- VERB2 log("keyring:'%s'", keyring_name);
-}
-
-static void load_event_config(gpointer key, gpointer value, gpointer user_data)
-{
- char* event_name = (char*)key;
- event_config_t *ec = (event_config_t *)value;
- VERB2 log("loading event '%s' configuration from keyring", event_name);
- abrt_keyring_load_settings(event_name, ec);
-}
-
-/*
- * Tries to load settings for all events in g_event_config_list
-*/
-void load_event_config_data_from_keyring(void)
-{
- init_keyring();
- if (!got_keyring)
- return;
- g_hash_table_foreach(g_event_config_list, &load_event_config, NULL);
-}
diff --git a/src/gtk-helpers/event_config_dialog.c b/src/gtk-helpers/event_config_dialog.c
index 5a564fe..190dad1 100644
--- a/src/gtk-helpers/event_config_dialog.c
+++ b/src/gtk-helpers/event_config_dialog.c
@@ -17,7 +17,6 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <gtk/gtk.h>
-#include <gnome-keyring.h>
#include "internal_libreport_gtk.h"
static GtkWindow *g_event_list_window;
@@ -298,63 +297,6 @@ static void dehydrate_config_dialog()
g_list_foreach(option_widget_list, &save_value_from_widget, NULL);
}
-static void save_settings_to_keyring(const char *event_name)
-{
- //don't bother when we already know that keyring is not available
- if (!g_keyring_available)
- return;
- char *keyring_name = NULL;
- GnomeKeyringResult result = gnome_keyring_get_default_keyring_sync(&keyring_name);
- if (result != GNOME_KEYRING_RESULT_OK)
- {
- error_msg("Can't get default keyring (result:%d)", result);
- return;
- }
-
- const char *store_passwords_s = get_user_setting("store_passwords");
- bool store_passwords = !(store_passwords_s && !strcmp(store_passwords_s, "no"));
-
- GnomeKeyringAttributeList *attrs = gnome_keyring_attribute_list_new();
- event_config_t *ec = get_event_config(event_name);
- /* add string id which we use to search for items */
- gnome_keyring_attribute_list_append_string(attrs, "libreportEventConfig", event_name);
- GList *l;
- for (l = g_list_first(ec->options); l != NULL; l = g_list_next(l))
- {
- event_option_t *op = (event_option_t *)l->data;
- gnome_keyring_attribute_list_append_string(attrs, op->eo_name,
- (!store_passwords && op->eo_type == OPTION_TYPE_PASSWORD) ? "" : op->eo_value);
- }
-
- guint32 item_id = find_keyring_item_id_for_event(event_name);
- if (item_id)
- {
- /* found existing item, so just update the values */
- result = gnome_keyring_item_set_attributes_sync(keyring_name, item_id, attrs);
- VERB2 log("updated item with id: %i", item_id);
- }
- else
- {
- /* did't find existing item, so create a new one */
- result = gnome_keyring_item_create_sync(keyring_name,
- GNOME_KEYRING_ITEM_GENERIC_SECRET, /* type */
- event_name, /* display name */
- attrs, /* attributes */
- "", /* secret - no special handling for password it's stored in attrs */
- 1, /* update if exist */
- &item_id);
- VERB2 log("created new item with id: %i", item_id);
- }
- gnome_keyring_attribute_list_free(attrs);
-
- if (result != GNOME_KEYRING_RESULT_OK)
- {
- error_msg("Error saving event '%s' configuration to keyring", event_name);
- return;
- }
- VERB2 log("saved event '%s' configuration to keyring", event_name);
-}
-
int show_event_config_dialog(const char *event_name, GtkWindow *parent)
{
if (option_widget_list != NULL)
@@ -425,14 +367,14 @@ int show_event_config_dialog(const char *event_name, GtkWindow *parent)
if (g_list_length(gtk_container_get_children(GTK_CONTAINER(g_adv_option_table))) > 0)
gtk_box_pack_start(GTK_BOX(content), adv_expander, false, false, 0);
- /* add warning if keyring is not available showing the nagging dialog
+ /* add warning if secrets service is not available showing the nagging dialog
* is considered "too heavy UI" be designers
*/
- if (!g_keyring_available)
+ if (!is_event_config_user_storage_available())
{
GtkWidget *keyring_warn_lbl =
gtk_label_new(
- _("Gnome Keyring is not available, your settings won't be saved!"));
+ _("Secret Service is not available, your settings won't be saved!"));
static const GdkColor red = { .red = 0xffff };
gtk_widget_modify_fg(keyring_warn_lbl, GTK_STATE_NORMAL, &red);
gtk_box_pack_start(GTK_BOX(content), keyring_warn_lbl, false, false, 0);
@@ -444,7 +386,10 @@ int show_event_config_dialog(const char *event_name, GtkWindow *parent)
if (result == GTK_RESPONSE_APPLY)
{
dehydrate_config_dialog();
- save_settings_to_keyring(event_name);
+ const char *const store_passwords_s = get_user_setting("store_passwords");
+ save_event_config_data_to_user_storage(event_name,
+ get_event_config(event_name),
+ !(store_passwords_s && !strcmp(store_passwords_s, "no")));
}
//else if (result == GTK_RESPONSE_CANCEL)
// log("log");
@@ -460,7 +405,7 @@ void show_events_list_dialog(GtkWindow *parent)
if (g_event_config_list == NULL)
{
load_event_config_data();
- load_event_config_data_from_keyring();
+ load_event_config_data_from_user_storage(g_event_config_list);
}
GtkWidget *event_list_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
diff --git a/src/gtk-helpers/internal_libreport_gtk.h b/src/gtk-helpers/internal_libreport_gtk.h
index 092bfad..dd9ff87 100644
--- a/src/gtk-helpers/internal_libreport_gtk.h
+++ b/src/gtk-helpers/internal_libreport_gtk.h
@@ -27,19 +27,22 @@
extern "C" {
#endif
-extern bool g_keyring_available;
-
#define make_label_autowrap_on_resize libreport_make_label_autowrap_on_resize
void make_label_autowrap_on_resize(GtkLabel *label);
#define show_events_list_dialog libreport_show_events_list_dialog
void show_events_list_dialog(GtkWindow *parent);
-#define load_event_config_data_from_keyring libreport_load_event_config_data_from_keyring
-void load_event_config_data_from_keyring(void);
+#define is_event_config_user_storage_available libreport_is_event_config_user_storage_available
+bool is_event_config_user_storage_available();
+
+#define load_event_config_data_from_user_storage libreport_load_event_config_data_from_user_storage
+void load_event_config_data_from_user_storage(GHashTable *event_config_list);
-#define find_keyring_item_id_for_event libreport_find_keyring_item_id_for_event
-guint32 find_keyring_item_id_for_event(const char *event_name);
+#define save_event_config_data_to_user_storage libreport_save_event_config_data_to_user_storage
+void save_event_config_data_to_user_storage(const char *event_name,
+ const event_config_t *event_config,
+ bool store_password);
#define show_event_config_dialog libreport_show_event_config_dialog
int show_event_config_dialog(const char *event_name, GtkWindow *parent);
diff --git a/src/gtk-helpers/secrets.c b/src/gtk-helpers/secrets.c
new file mode 100644
index 0000000..db96bed
--- /dev/null
+++ b/src/gtk-helpers/secrets.c
@@ -0,0 +1,1053 @@
+/*
+ Copyright (C) 2012 ABRT Team
+ Copyright (C) 2012 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "internal_libreport.h"
+#include "internal_libreport_gtk.h"
+
+#include <gio/gio.h>
+
+#define secrets_api "org.freedesktop.secrets"
+#define SECRETS_NAME_SPACE(interface) "org.freedesktop.Secret."interface
+
+/* label and alias have to be same because of ksecrets */
+#define SECRETS_COLLECTION_LABEL SECRETS_COLLECTION_ALIAS
+#define SECRETS_COLLECTION_ALIAS "default"
+
+#define SECRETS_SEARCH_ATTRIBUTE "libreportEventConfig"
+
+/* 5s timeout*/
+#define SECRETS_CALL_DEFAULT_TIMEOUT 5000
+
+/* Well known errors for which we have workarounds */
+#define NOT_IMPLEMENTED_READ_ALLIAS_ERROR_MESSAGE "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method 'ReadAlias' in interface 'org.freedesktop.Secret.Service' at object path '/org/freedesktop/secrets' (signature 's')"
+#define INVALID_PROPERTIES_ARGUMENTS "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Invalid properties argument"
+
+
+enum secrets_api_state
+{
+ /* intial state */
+ SBS_INITIAL = 0,
+ /* after opening a secrets service session (different than D-Bus session) */
+ SBS_CONNECTED,
+ /* secrets service is not available do not bother user anymore */
+ SBS_UNAVAILABLE,
+};
+
+struct secrets_object
+{
+ GDBusProxy *proxy;
+ /* http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfac... */
+ GDBusProxy *property_proxy;
+ const gchar *interface_name;
+};
+
+/* Glib DBus object org.freedesktop.secrets */
+GDBusConnection *g_connection;
+
+/* State of the service */
+enum secrets_api_state g_state;
+
+/* http://standards.freedesktop.org/secret-service/re01.html */
+/* xorg.freedesktop.Secret.Service interface object */
+struct secrets_object *g_service;
+
+/* http://standards.freedesktop.org/secret-service/ch06.html */
+/* xorg.freedesktop.Secret.Session interface object */
+GDBusProxy *g_session;
+
+/*******************************************************************************/
+/* struct secrets_object */
+/*******************************************************************************/
+
+static struct secrets_object *secrets_object_new_from_proxy(GDBusProxy *proxy)
+{
+ struct secrets_object *obj = xmalloc(sizeof(*obj));
+ obj->proxy = proxy;
+ obj->property_proxy = NULL;
+ obj->interface_name = g_dbus_proxy_get_interface_name(proxy);
+
+ return obj;
+}
+
+static struct secrets_object *secrets_object_new(const gchar *path,
+ const gchar *interface,
+ GError **error)
+{
+ GDBusProxy *const proxy =
+ g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ secrets_api, path, interface, NULL, error);
+
+ if (*error)
+ return NULL;
+
+ return secrets_object_new_from_proxy(proxy);
+}
+
+static void secrets_object_change_path(struct secrets_object *obj,
+ const gchar *path,
+ GError **error)
+{
+ GDBusProxy *const proxy =
+ g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ secrets_api, path, obj->interface_name, NULL, error);
+
+ if (!*error)
+ {
+ if (obj->proxy)
+ g_object_unref(obj->proxy);
+
+ obj->proxy = proxy;
+ }
+}
+
+static void secrets_object_delete(struct secrets_object *obj)
+{
+ if (obj->proxy)
+ g_object_unref(obj->proxy);
+
+ if (obj->property_proxy)
+ g_object_unref(obj->property_proxy);
+
+ free(obj);
+}
+
+static GDBusProxy *secrets_object_get_properties_proxy(struct secrets_object *obj, GError **error)
+{
+ if (!obj->property_proxy)
+ {
+ obj->property_proxy
+ = g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ secrets_api, g_dbus_proxy_get_object_path(obj->proxy),
+ "org.freedesktop.DBus.Properties", NULL, error);
+
+ if (*error)
+ return NULL;
+ }
+
+ return obj->property_proxy;
+}
+
+
+static void secrets_object_set_dbus_property(struct secrets_object *obj,
+ const char *property,
+ GVariant *value,
+ GError **error)
+{
+ GDBusProxy *properties = secrets_object_get_properties_proxy(obj, error);
+
+ if (!*error)
+ {
+ GVariant *const ret
+ = g_dbus_proxy_call_sync(properties, "Set",
+ g_variant_new("(ssv)", obj->interface_name, property, value),
+ G_DBUS_PROXY_FLAGS_NONE, SECRETS_CALL_DEFAULT_TIMEOUT, NULL,
+ error);
+
+ if (ret)
+ g_variant_unref(ret);
+ }
+}
+
+/*******************************************************************************/
+/* struct secrets_api */
+/*******************************************************************************/
+
+static void secrets_api_set_unavailable();
+
+static void secrets_api_error(GError* error, const char *comment)
+{
+ if (comment)
+ {
+ log("%s : %s\n", comment, error->message);
+ }
+ else
+ {
+ log("%s\n", error->message);
+ }
+
+ g_error_free(error);
+ secrets_api_set_unavailable();
+}
+
+/*
+ * Initialize all global variables
+ */
+static enum secrets_api_state secrets_api_connect()
+{
+ GError *error = NULL;
+
+ g_connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (g_connection == NULL)
+ {
+ secrets_api_error(error, "failed to open connection to D-Bus session");
+ return SBS_UNAVAILABLE;
+ }
+
+ g_service = secrets_object_new("/org/freedesktop/secrets", SECRETS_NAME_SPACE("Service"), &error);
+ if (error)
+ {
+ secrets_api_error(error, "failed to create service");
+ return SBS_UNAVAILABLE;
+ }
+
+ GVariant *const alg_in = g_variant_new_string("");
+ GVariant *const args = g_variant_new("(sv)", "plain", alg_in);
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(g_service->proxy, "OpenSession", args, G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT, NULL, &error);
+
+ g_variant_unref(alg_in);
+
+ if (error)
+ {
+ secrets_api_error(error, "cannot open session Secrets session");
+ return SBS_UNAVAILABLE;
+ }
+
+ GVariant *const var = g_variant_get_child_value(ret, 1);
+ g_variant_unref(ret);
+
+ const gchar *const session_path = g_variant_get_string(var, NULL);
+ g_session = g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL, secrets_api,
+ session_path, SECRETS_NAME_SPACE("Session"), NULL, &error);
+
+ g_variant_unref(var);
+
+ if (error)
+ {
+ secrets_api_error(error, "cannot create Proxy for Secrets session");
+ return SBS_UNAVAILABLE;
+ }
+
+ g_state = SBS_CONNECTED;
+
+ return g_state;
+}
+
+static void secrets_api_init()
+{
+ if (g_state == SBS_INITIAL)
+ secrets_api_connect();
+}
+
+static void secrets_api_close()
+{
+ if (g_state == SBS_INITIAL || g_state == SBS_UNAVAILABLE)
+ return;
+
+ GError *error = NULL;
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(g_session, "Close", g_variant_new("()"), G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT, NULL, &error);
+
+ if (error)
+ {
+ VERB1 log("error on closing Secrets session : %s\n", error->message);
+ g_error_free(error);
+ }
+ else
+ g_variant_unref(ret);
+
+ g_object_unref(g_session);
+
+ secrets_object_delete(g_service);
+
+ g_object_unref(g_connection);
+
+ g_connection = NULL;
+ g_service = NULL;
+ g_session = NULL;
+ g_state = SBS_INITIAL;
+}
+
+static void secrets_api_set_unavailable()
+{
+ secrets_api_close();
+
+ g_state = SBS_UNAVAILABLE;
+}
+
+static int is_secrets_api_available()
+{
+ return g_state != SBS_UNAVAILABLE;
+}
+
+/*******************************************************************************/
+/* org.freedesktop.Secret.Prompt */
+/* http://standards.freedesktop.org/secret-service/re05.html */
+/*******************************************************************************/
+
+struct prompt_source
+{
+ GSource source;
+ GDBusProxy *prompt_proxy;
+ GError *error;
+ GMainLoop *loop;
+};
+
+struct prompt_call_back_args
+{
+ gboolean dismissed;
+ GVariant *result;
+ GMainLoop *loop;
+};
+
+static void prompt_quit_loop(GMainLoop* loop)
+{
+ if (g_main_loop_is_running(loop))
+ g_main_loop_quit(loop);
+}
+
+static gboolean prompt_g_idle_prepare(GSource *source_data, gint *timeout)
+{
+ return TRUE;
+}
+
+static gboolean prompt_g_idle_check(GSource *source)
+{
+ return TRUE;
+}
+
+static gboolean prompt_g_idle_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
+{
+ struct prompt_source *const prompt_source = (struct prompt_source *)source;
+ GDBusProxy *const prompt_proxy = prompt_source->prompt_proxy;
+ GVariant *const args = g_variant_new("(s)","");
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(prompt_proxy, "Prompt", args, G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT, NULL, &prompt_source->error);
+
+ if (prompt_source->error)
+ prompt_quit_loop(prompt_source->loop);
+ else
+ g_variant_unref(ret);
+
+ /* always return FALSE, it means that this dispatch is to be removed from idle loop */
+ return FALSE;
+}
+
+static void prompt_call_back(GDBusConnection *connection, const gchar *sender_name,
+ const gchar *object_path, const gchar *interface_name,
+ const gchar *signal_name, GVariant *parameters,
+ gpointer user_data)
+{
+ struct prompt_call_back_args *const args = (struct prompt_call_back_args *)user_data;
+ GVariant *const result = NULL;
+
+ g_variant_get(parameters, "(bv)", &args->dismissed, &result);
+
+ if (!args->dismissed)
+ args->result = result;
+ else
+ g_variant_unref(result);
+
+ prompt_quit_loop(args->loop);
+}
+
+static bool is_prompt_required(const char *prompt_path)
+{
+ return prompt_path && !(prompt_path[0] == '/' && prompt_path[1] == '\0');
+}
+
+static bool secrets_prompt(const char *prompt_path,
+ GVariant **result,
+ GError **error)
+{
+ static GSourceFuncs idle_funcs = {
+ .prepare = prompt_g_idle_prepare,
+ .check = prompt_g_idle_check,
+ .dispatch = prompt_g_idle_dispatch,
+ .finalize = NULL,
+ .closure_callback = NULL,
+ .closure_marshal = NULL,
+ };
+
+ GDBusProxy *const prompt_proxy =
+ g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL, secrets_api,
+ prompt_path, SECRETS_NAME_SPACE("Prompt"), NULL, error);
+
+ if (*error)
+ return true;
+
+ GMainContext *context = g_main_context_get_thread_default();
+ GMainLoop *loop = g_main_loop_new(context, FALSE);
+
+ struct prompt_call_back_args args = {
+ .result=NULL,
+ .dismissed=FALSE,
+ .loop=loop
+ };
+
+ const guint signal_ret =
+ g_dbus_connection_signal_subscribe(g_connection, secrets_api, SECRETS_NAME_SPACE("Prompt"),
+ "Completed", prompt_path, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+ prompt_call_back, &args, NULL);
+
+ /* prepare prompt source */
+ struct prompt_source *const prompt_source =
+ (struct prompt_source*)g_source_new(&idle_funcs, sizeof(*prompt_source));
+
+ prompt_source->prompt_proxy = prompt_proxy;
+ prompt_source->error = NULL;
+
+ g_source_attach((GSource*)prompt_source, context);
+
+ g_main_loop_run(loop);
+
+ /* save error */
+ *error = prompt_source->error;
+ if (args.result)
+ {
+ if (result)
+ *result = args.result;
+ else
+ g_variant_unref(args.result);
+ }
+
+ /* destroy prompt source */
+ g_object_unref(prompt_proxy);
+ g_source_destroy((GSource*)prompt_source);
+
+ g_dbus_connection_signal_unsubscribe(g_connection, signal_ret);
+ g_main_loop_unref(loop);
+
+ return args.dismissed;
+}
+
+/*******************************************************************************/
+/* org.freedesktop.Secret.Service */
+/* http://standards.freedesktop.org/secret-service/re01.html */
+/*******************************************************************************/
+
+static bool secrets_service_unlock(const gchar *const *objects,
+ GVariant **unlocked,
+ GError **error)
+{
+ bool dismissed = false;
+ GVariant *const args = g_variant_new("(^ao)", objects);
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(g_service->proxy, "Unlock", args, G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT, NULL, error);
+
+ if (*error)
+ return dismissed;
+
+ gchar *prompt;
+ GVariant *result;
+ g_variant_get(ret, "(@aoo)", &result, &prompt);
+
+ if (is_prompt_required(prompt))
+ {
+ g_variant_unref(result);
+ result = NULL;
+ dismissed = secrets_prompt(prompt, &result, error);
+ }
+
+ if (result)
+ { /* in case of error or dismissed the result variable has NULL value */
+ if (unlocked)
+ *unlocked = result;
+ else
+ g_variant_unref(result);
+ }
+
+ g_variant_unref(ret);
+
+ return dismissed;
+}
+
+static bool secrets_service_unlock_object(struct secrets_object *obj,
+ GError **error)
+{
+ const gchar *const locked[] = {
+ g_dbus_proxy_get_object_path(obj->proxy),
+ NULL
+ };
+
+ GVariant *result = NULL;
+ bool dismissed = secrets_service_unlock(locked, &result, error);
+
+ if (!*error && !dismissed && result)
+ {
+ gsize length;
+ const gchar *const *unlocked = g_variant_get_objv(result, &length);
+
+ /* ksecrets doesn't set dismissed correctly */
+ if (length)
+ {
+ secrets_object_change_path(obj, *unlocked, error);
+ g_free((gpointer)unlocked);
+ }
+ else
+ dismissed = true;
+
+ g_variant_unref(result);
+ }
+
+ return dismissed;
+}
+
+/*
+ * KSecretsService doesn't implement ReadAlias
+ */
+static struct secrets_object *secrets_service_find_collection(const char *alias, GError **error)
+{
+ /* do not unref this proxy, the proxy is owned by secrets object */
+ GDBusProxy *const properties = secrets_object_get_properties_proxy(g_service, error);
+
+ if (*error)
+ return NULL;
+
+ /* cannot use g_dbus_proxy_get_cached_property() because of */
+ /* errors in case when secrets service was restarted during session */
+ GVariant *const prop =
+ g_dbus_proxy_call_sync(properties, "Get",
+ g_variant_new("(ss)", SECRETS_NAME_SPACE("Service"), "Collections"),
+ G_DBUS_PROXY_FLAGS_NONE, SECRETS_CALL_DEFAULT_TIMEOUT, NULL, error);
+
+ if (*error)
+ return NULL;
+
+ struct secrets_object *collection = NULL;
+
+ /* Returned value */
+ GVariant *const value = g_variant_get_child_value(prop,0);
+ /* Contentet of returned value */
+ GVariant *const list = g_variant_get_child_value(value,0);
+
+ GVariantIter *iter;
+ char *path;
+ g_variant_get(g_variant_get_child_value(value,0), "ao", &iter);
+
+ while (g_variant_iter_loop(iter, "o", &path))
+ {
+ GDBusProxy *const collection_proxy =
+ g_dbus_proxy_new_sync(g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL, secrets_api,
+ path, SECRETS_NAME_SPACE("Collection"), NULL, error);
+
+ if (*error)
+ break;
+
+ GVariant *const lbl_var = g_dbus_proxy_get_cached_property(collection_proxy,
+ "Label");
+
+ const gchar *const label = g_variant_get_string(lbl_var, NULL);
+ const int found = label && (strcmp(label, alias) == 0);
+
+ /* found is introduced because of break if collecion is found */
+ /* and I want to write the following statement just once */
+ /* the label variable is invalid after the following statement */
+ g_variant_unref(lbl_var);
+
+ if (found)
+ {
+ collection = secrets_object_new_from_proxy(collection_proxy);
+ break;
+ }
+
+ g_object_unref(collection_proxy);
+ }
+
+ g_variant_iter_free(iter);
+ g_variant_unref(list);
+ g_variant_unref(value);
+ g_variant_unref(prop);
+
+ return collection;
+}
+
+static struct secrets_object *secrets_service_read_allias(const char *alias, GError **error)
+{
+ struct secrets_object *collection = NULL;
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(g_service->proxy, "ReadAlias", g_variant_new("(s)", alias),
+ G_DBUS_PROXY_FLAGS_NONE, SECRETS_CALL_DEFAULT_TIMEOUT,
+ NULL, error);
+
+ if (!*error)
+ {
+ GVariant *const obj_path = g_variant_get_child_value(ret, 0);
+ const gchar* const coll_path = g_variant_get_string(obj_path, NULL);
+
+ if (strcmp(coll_path,"/"))
+ collection = secrets_object_new(coll_path,
+ SECRETS_NAME_SPACE("Collection"),
+ error);
+
+ g_variant_unref(obj_path);
+ g_variant_unref(ret);
+ }
+ else if (!strcmp((*error)->message, NOT_IMPLEMENTED_READ_ALLIAS_ERROR_MESSAGE))
+ {
+ /* ksecrets doesn't implement ReadAlias, thus search by label is performed */
+ VERB1 log("ReadAlias failed, going to search on the client side : %s", (*error)->message);
+ g_error_free(*error);
+ *error = NULL;
+ collection = secrets_service_find_collection(alias, error);
+ }
+
+ return collection;
+}
+
+static struct secrets_object *secrets_service_create_collection(const gchar *label,
+ const gchar *alias,
+ bool *dismissed,
+ GError **error)
+{
+ GVariantBuilder *const prop_builder = g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
+
+ g_variant_builder_add(prop_builder, "{sv}", "org.freedesktop.Secret.Collection.Label",
+ g_variant_new_string(label) );
+
+ GVariant *const call_args =
+ g_variant_new("(@a{sv}s)", g_variant_builder_end(prop_builder), alias);
+
+ GVariant *const ret =
+ g_dbus_proxy_call_sync(g_service->proxy, "CreateCollection", call_args, G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT, NULL, error);
+
+ *dismissed = false;
+
+ if (*error)
+ return NULL;
+
+ gchar *prompt;
+ GVariant *collection_var;
+ g_variant_get(ret, "(@oo)", &collection_var, &prompt);
+
+ if (is_prompt_required(prompt))
+ {
+ g_variant_unref(collection_var);
+ collection_var = NULL;
+
+ *dismissed = secrets_prompt(prompt, &collection_var, error);
+ }
+
+ struct secrets_object *collection = NULL;
+ if (collection_var)
+ {
+ gsize length;
+ const gchar *const coll_path = g_variant_get_string(collection_var, &length);
+
+ /* ksecrets doens't properly set dismissed */
+ /* if dismissmised is false and result is empty */
+ /* then ksecrets didn't properly set dismissed */
+ if (length)
+ collection = secrets_object_new(coll_path, SECRETS_NAME_SPACE("Collection"), error);
+ else
+ *dismissed = true;
+
+ g_variant_unref(collection_var);
+ }
+
+ g_variant_unref(ret);
+
+ return collection;
+}
+
+/*******************************************************************************/
+/* org.freedesktop.Secret.Collection */
+/* http://standards.freedesktop.org/secret-service/re02.html */
+/*******************************************************************************/
+
+static GVariant *create_item_call_args(const char *event_name,
+ GVariant *attributes,
+ GDBusProxy *session,
+ bool full_property_name)
+{
+ /* ksecrets accepts properties without namespace */
+ /* gnome-keyring accepts only with namespace */
+ const char *const lbl_name = full_property_name ? SECRETS_NAME_SPACE("Item.Label")
+ : "Label";
+ const char *const att_name = full_property_name ? SECRETS_NAME_SPACE("Item.Attributes")
+ : "Attributes";
+
+ GVariantBuilder *const prop_builder = g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
+
+ g_variant_builder_add(prop_builder, "{sv}", lbl_name, g_variant_new_string(event_name));
+ g_variant_builder_add(prop_builder, "{sv}", att_name, attributes);
+
+ GVariantBuilder *const param_builder = g_variant_builder_new(G_VARIANT_TYPE("ay"));
+ GVariantBuilder *const value_builder = g_variant_builder_new(G_VARIANT_TYPE("ay"));
+
+ GVariant *const secret = g_variant_new("(o@ay@ays)",
+ g_dbus_proxy_get_object_path(session),
+ g_variant_builder_end(param_builder),
+ g_variant_builder_end(value_builder),
+ "text/plain, charset=utf8");
+
+ return g_variant_new("(@a{sv}@(oayays)b)", g_variant_builder_end(prop_builder), secret, FALSE);
+}
+
+static void secrets_collection_create_text_item(struct secrets_object *collection,
+ const char *event_name,
+ GVariant *attributes,
+ GError **error)
+{
+ GVariant *call_args, *ret;
+
+ /* ksecrets silently drops properties with full names */
+ /* gnome-keyring returns an error if a property key is not full property name */
+ /* the first iteration sends property names without namespace */
+ /* if NO error was returned service is ksecrets and everything is ok */
+ /* if error was returned service is gnome-keyring and and the second iteration */
+ /* must be performed */
+ /* the second iteration sends property names with namespace */
+ static bool const options[] = {FALSE, TRUE};
+ for (size_t choice = 0; choice < sizeof(options)/sizeof(*options); ++choice)
+ {
+ if (*error)
+ { /* this code is here because I want to return an error */
+ /* it cannot be on the end of the loop because I need unfreed error after the last iteration */
+ g_error_free(*error);
+ *error = NULL;
+ }
+
+ call_args = create_item_call_args(event_name, g_variant_ref(attributes),
+ g_session, options[choice]);
+
+ ret = g_dbus_proxy_call_sync(collection->proxy, "CreateItem", call_args,
+ G_DBUS_PROXY_FLAGS_NONE, SECRETS_CALL_DEFAULT_TIMEOUT,
+ NULL, error);
+
+ if (*error)
+ {
+ if (!strcmp(INVALID_PROPERTIES_ARGUMENTS, (*error)->message))
+ {
+ VERB2 log("CreateItem failed, going to use other properties names : %s", (*error)->message);
+ continue;
+ }
+ else
+ /* if the error wasn't about invalid properties we have an another problem */
+ break;
+ }
+
+ gchar *prompt, *item;
+ g_variant_get(ret, "(oo)", &item, &prompt);
+
+ if (is_prompt_required(prompt))
+ {
+ const bool dismissed = secrets_prompt(prompt, NULL, error);
+ if (*error || dismissed)
+ secrets_api_set_unavailable();
+
+ g_variant_unref(ret);
+ break;
+ }
+
+ g_variant_unref(ret);
+ }
+
+ g_variant_unref(attributes);
+}
+
+/*
+ * Performs org.freedesktop.Secret.Collection.SearchItems and returns the first
+ * item from result. A found item can be possibly locked.
+ */
+static struct secrets_object *secrets_collection_search_one_item(
+ const struct secrets_object *collection,
+ GVariant *search_attrs,
+ GError **error)
+{
+ struct secrets_object *item = NULL;
+ GVariant *const value =
+ g_dbus_proxy_call_sync(collection->proxy, "SearchItems", g_variant_new("(@a{ss})", search_attrs),
+ G_DBUS_PROXY_FLAGS_NONE, SECRETS_CALL_DEFAULT_TIMEOUT, NULL, error);
+
+ if (*error)
+ return NULL;
+
+ /* gnome-keyring returns (unlocked,locked) */
+ /* ksecretsservice returns (unlocked) */
+ /* all childs has to be taken into account */
+ const gsize n_results = g_variant_n_children(value);
+ for(gsize child = 0; !item && child < n_results; ++child)
+ {
+ if (*error)
+ {
+ VERB1 log("search items result error : %s", (*error)->message);
+ free(*error);
+ *error = NULL;
+ }
+
+ GVariant *const paths = g_variant_get_child_value(value, 0);
+ const gchar *const *item_paths = g_variant_get_objv(paths, NULL);
+
+ /* the first found object path is to be returned */
+ if (*item_paths)
+ item = secrets_object_new(*item_paths, SECRETS_NAME_SPACE("Item"), error);
+
+ g_free((gpointer)item_paths);
+ g_variant_unref(paths);
+ }
+
+ g_variant_unref(value);
+
+ return item;
+}
+
+/*******************************************************************************/
+/* utility functions */
+/*******************************************************************************/
+
+static struct secrets_object *get_default_collection(GError **error)
+{
+ bool dismissed = false;
+ struct secrets_object *collection =
+ secrets_service_read_allias(SECRETS_COLLECTION_ALIAS, error);
+
+ if (!*error && collection)
+ {
+ dismissed = secrets_service_unlock_object(collection, error);
+
+ if (*error || dismissed)
+ {
+ secrets_object_delete(collection);
+ collection = NULL;
+ }
+ }
+
+ if (dismissed)
+ /* A user canceled an unlock prompt */
+ /* Secrets BUS is set to unvailable in order to not bother a user */
+ /* with an another unlock prompt */
+ secrets_api_set_unavailable();
+
+ /* no matter if error occured or prompt was dismissed */
+ /* in these cases the collection holds NULL */
+ return collection;
+}
+
+static void load_event_options_from_item(GList *options,
+ struct secrets_object *item)
+{
+ GVariant *const attributes =
+ g_dbus_proxy_get_cached_property(item->proxy, "Attributes");
+
+ GVariantIter *iter;
+ g_variant_get(attributes, "a{ss}", &iter);
+
+ char *name, *value;
+ while (g_variant_iter_loop(iter, "{ss}", &name, &value))
+ {
+ event_option_t *const option = get_event_option_from_list(name, options);
+ if (option)
+ {
+ free(option->eo_value);
+ option->eo_value = xstrdup(value);
+ VERB2 log("loaded event option : '%s => %s'", name, option->eo_value);
+ }
+ }
+
+ g_variant_unref(attributes);
+}
+
+static GVariant *create_attributes_from_options(GList *options,
+ const char *event_name,
+ bool store_passwords)
+{
+ GVariantBuilder *const attr_builder = g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
+
+ for (GList *iter = g_list_first(options); iter; iter = g_list_next(iter))
+ { /* we store data in the search attributes */
+ event_option_t *const op = (event_option_t *)iter->data;
+ if (op->eo_type != OPTION_TYPE_PASSWORD || store_passwords)
+ g_variant_builder_add(attr_builder, "{ss}", op->eo_name, op->eo_value);
+ }
+
+ /* and finaly, add extra attribute used for searching */
+ g_variant_builder_add(attr_builder, "{ss}", SECRETS_SEARCH_ATTRIBUTE, event_name);
+
+ return g_variant_builder_end(attr_builder);
+}
+
+static struct secrets_object *find_item_by_event_name(const struct secrets_object *collection,
+ const char* event_name,
+ GError **error)
+{
+ GVariantBuilder *const attrs = g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
+
+ g_variant_builder_add(attrs, "{ss}", SECRETS_SEARCH_ATTRIBUTE, event_name);
+
+ return secrets_collection_search_one_item(collection, g_variant_builder_end(attrs), error);
+}
+
+static void load_settings(struct secrets_object *collection, const char *event_name, event_config_t *ec)
+{
+ GError *error = NULL;
+ GVariant *const locked = g_dbus_proxy_get_cached_property(collection->proxy, "Locked");
+
+ if (g_variant_get_boolean(locked))
+ {
+ /* a collection can be closed by the service for some reason */
+ /* set api unavailable if unlock attempt was dismissed */
+ if (secrets_service_unlock_object(collection, &error))
+ secrets_api_set_unavailable();
+ }
+
+ g_variant_unref(locked);
+
+ if (!error)
+ {
+ struct secrets_object *item = find_item_by_event_name(collection, event_name, &error);
+
+ if (!error && item)
+ {
+ VERB2 log("loading event config : '%s'", event_name);
+ load_event_options_from_item(ec->options, item);
+ secrets_object_delete(item);
+ }
+ }
+
+ if (error)
+ secrets_api_error(error, NULL);
+}
+
+static void load_event_config(gpointer key, gpointer value, gpointer user_data)
+{
+ char *const event_name = (char*)key;
+ event_config_t *const ec = (event_config_t *)value;
+ struct secrets_object *collection = (struct secrets_object *)user_data;
+
+ if (is_secrets_api_available())
+ load_settings(collection, event_name, ec);
+}
+
+static void save_options(struct secrets_object *collection,
+ const char* event_name,
+ GList *options,
+ bool store_passwords,
+ GError **error)
+{
+ struct secrets_object *item;
+ item = find_item_by_event_name(collection, event_name, error);
+
+ if (*error)
+ return;
+
+ GVariant *const attributes = create_attributes_from_options(options,
+ event_name,
+ store_passwords);
+
+ if (item)
+ { /* item exists */
+ VERB2 log("updating event config : '%s'", event_name);
+ secrets_object_set_dbus_property(item, "Attributes", attributes, error);
+ secrets_object_delete(item);
+ }
+ else /* create a new item */
+ {
+ VERB2 log("creating event config : '%s'", event_name);
+ secrets_collection_create_text_item(collection, event_name, attributes, error);
+ }
+}
+
+static void save_event_config(const char *event_name,
+ GList *options,
+ bool store_passwords)
+{
+ bool dismissed = false;
+ GError *error = NULL;
+ struct secrets_object *collection = get_default_collection(&error);
+
+ if (error)
+ goto save_event_config_end;
+
+ if (!collection)
+ {
+ VERB2 log("going to create a new default collection '"SECRETS_COLLECTION_LABEL"'"
+ " with alias '"SECRETS_COLLECTION_ALIAS"'");
+
+ collection =
+ secrets_service_create_collection(SECRETS_COLLECTION_LABEL, SECRETS_COLLECTION_ALIAS,
+ &dismissed, &error);
+
+ if (error || dismissed)
+ goto save_event_config_end;
+ }
+
+ save_options(collection, event_name, options, store_passwords, &error);
+
+save_event_config_end:
+ if (dismissed)
+ secrets_api_set_unavailable();
+
+ if (error)
+ secrets_api_error(error, NULL);
+}
+
+/******************************************************************************/
+/* Public interface */
+/******************************************************************************/
+
+bool is_event_config_user_storage_available()
+{
+ return is_secrets_api_available();
+}
+
+/*
+ * Loads event config options for passed events
+ *
+ * @param event_config_list Events configs
+ */
+void load_event_config_data_from_user_storage(GHashTable *event_config_list)
+{
+ secrets_api_init();
+
+ if (is_secrets_api_available())
+ {
+ GError *error = NULL;
+ struct secrets_object *collection = get_default_collection(&error);
+
+ if (collection)
+ {
+ g_hash_table_foreach(event_config_list, &load_event_config, collection);
+ secrets_object_delete(collection);
+ }
+
+ if (error)
+ secrets_api_error(error, "D-Bus secrets API cannot provide default collection");
+ }
+ else
+ {
+ VERB1 log("cannot load user's configuration due to unavailability of D-Bus secrets API");
+ }
+}
+
+/*
+ * Saves an event_config options to some kind of session storage.
+ *
+ * @param event_name Lookup key
+ * @param event_config Event data
+ * @param store_passwords If TRUE stores options with passwords, otherwise skips them
+ */
+void save_event_config_data_to_user_storage(const char *event_name,
+ const event_config_t *event_config,
+ bool store_passwords)
+{
+ secrets_api_init();
+
+ if (is_secrets_api_available())
+ save_event_config(event_name, event_config->options, store_passwords);
+ else
+ {
+ VERB1 log("cannot save user's configuration due to unavailability of D-Bus secrets API");
+ }
+}
diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c
index 05d01e9..e9632c3 100644
--- a/src/gui-wizard-gtk/main.c
+++ b/src/gui-wizard-gtk/main.c
@@ -140,7 +140,7 @@ int main(int argc, char **argv)
/* load /etc/abrt/events/foo.{conf,xml} stuff
and $XDG_CACHE_HOME/abrt/events/foo.conf */
load_event_config_data();
- load_event_config_data_from_keyring();
+ load_event_config_data_from_user_storage(g_event_config_list);
load_user_settings("report-gtk");
reload_problem_data_from_dump_dir();
--
1.7.7.6
11 years, 11 months