On Wed, 2011-08-03 at 11:02 +0200, Denys Vlasenko wrote:
I need to parse reported_to for "attach a file to existing RHTS
report"
functionality. (BZ will need the same soon). I need the latest
(most recent) reported_to line for a specific prefix.
Jiri needs to parse reported_to in order to display it in GUI.
He needs all lines parsed.
For this, I also needed to improve RHTS reported_to generation -
"MSG=Case created:
https://api.access.redhat.com/rs/cases/00512471"
is not parseable. So I changed it into
"URL=https://api.access.redhat.com/rs/cases/00512471 MSG=Case created".
...
I propose the following API instead:
struct report_result {
time_t timestamp;
char *url;
char *msg;
char *email;
...
};
/* Find latest "PFX: ...", parse and return it. If not found, return NULL */
report_result_t *find_reported_to(struct dump_dir *dd, const char *prefix);
/* Load, parse and return all lines as list of report_result_t's */
GList *parse_reported_to(struct dump_dir *dd);
find_reported_to() is for my needs, and parse_reported_to() is for Jiri.
Formatting of reported_to lines, IMO, is handled by xasprintf()
well enough already. I tried to imagine useful helper function API
but so far I have nothing.
Here is the proposed patch.
struct report_result is changed, find_in_reported_to() is implemented
and used in rhts plugin (ignore most of changes there, just find
- report_to_rhtsupport(dump_dir_name, settings);
+ if (file_name)
...
part and see how find_in_reported_to() is used there).
Jiri's needs are partially addressed by parse_reported_line().
So far it is a static function which parses one line.
For Jiri, it will be used by a new, rather simple function
which reads reported_to line-by-line, calls parse_reported_line()
on each line, and returns the list of results.
As an immediate next step, I propose nuking src/lib/report_result.c
and moving the lone remaining function, free_report_result(),
to dump_dir.c. Perhaps report_result-related function decls
should be moved to dump_dir.h too.
--
vda
diff -x '*.po' -d -urpN libreport.5/src/include/internal_libreport.h
libreport.6/src/include/internal_libreport.h
--- libreport.5/src/include/internal_libreport.h 2011-08-02 11:00:46.000000000 +0200
+++ libreport.6/src/include/internal_libreport.h 2011-08-03 15:54:00.741079803 +0200
@@ -534,8 +534,6 @@ char* get_environ(pid_t pid);
*/
#define iso_date_string libreport_iso_date_string
char *iso_date_string(const time_t *pt);
-#define string_iso_date libreport_string_iso_date
-time_t string_iso_date(const char *date);
enum {
MAKEDESC_SHOW_FILES = (1 << 0),
@@ -654,29 +652,23 @@ enum {
EVENT_LOG_LOW_WATERMARK = 20 * 1024,
};
-enum report_result_type {
- REPORT_RESULT_TYPE_URL,
- REPORT_RESULT_TYPE_MESSAGE
-};
-
struct report_result {
- char *event;
- char *data;
- enum report_result_type type;
- time_t timestamp;
+ char *url;
+ char *msg;
+ /* char *whole_line; */
+ /* time_t timestamp; */
+ /* ^^^ if you add more fields, don't forget to update free_report_result() */
};
+typedef struct report_result report_result_t;
+
+#define free_report_result libreport_free_report_result
+void free_report_result(struct report_result *result);
#define add_reported_to libreport_add_reported_to
void add_reported_to(struct dump_dir *dd, const char *line);
-#define new_report_result libreport_new_report_result
-struct report_result *new_report_result(enum report_result_type type, char *data);
-#define format_report_result libreport_format_report_result
-char *format_report_result(const struct report_result *result);
-#define parse_report_result libreport_parse_report_result
-struct report_result *parse_report_result(const char *text);
-#define free_report_result libreport_free_report_result
-void free_report_result(struct report_result *result);
+#define find_in_reported_to libreport_find_in_reported_to
+report_result_t *find_in_reported_to(struct dump_dir *dd, const char *prefix);
#define log_problem_data libreport_log_problem_data
void log_problem_data(problem_data_t *problem_data, const char *pfx);
diff -x '*.po' -d -urpN libreport.5/src/lib/dump_dir.c
libreport.6/src/lib/dump_dir.c
--- libreport.5/src/lib/dump_dir.c 2011-07-29 11:08:13.000000000 +0200
+++ libreport.6/src/lib/dump_dir.c 2011-08-03 14:25:45.609075779 +0200
@@ -798,6 +798,72 @@ void add_reported_to(struct dump_dir *dd
free(reported_to);
}
+static report_result_t *parse_reported_line(const char *line)
+{
+ report_result_t *result = xzalloc(sizeof(*result));
+
+ //result->whole_line = xstrdup(line);
+ for (;;)
+ {
+ line = skip_whitespace(line);
+ if (!*line)
+ break;
+ const char *end = skip_non_whitespace(line);
+ if (strncmp(line, "MSG=", 4) == 0)
+ {
+ result->msg = xstrdup(line + 4);
+ /* MSG=... eats entire line: exiting the loop */
+ break;
+ }
+ if (strncmp(line, "URL=", 4) == 0)
+ {
+ free(result->url);
+ result->url = xstrndup(line + 4, end - (line + 4));
+ }
+ //else
+ //if (strncmp(line, "TIME=", 5) == 0)
+ //{
+ // free(result->time);
+ // result->time = foo(line + 5, end - (line + 5));
+ //}
+ //...
+ line = end;
+ continue;
+ }
+
+ return result;
+}
+
+report_result_t *find_in_reported_to(struct dump_dir *dd, const char *prefix)
+{
+ char *reported_to = dd_load_text_ext(dd, FILENAME_REPORTED_TO, DD_FAIL_QUIETLY_ENOENT
| DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
+ if (!reported_to)
+ return NULL;
+
+ /* Find *last* (most recent) line with this prefix */
+ unsigned prefix_len = strlen(prefix);
+ char *found = NULL;
+ char *p = reported_to;
+ while (*p)
+ {
+ if (strncmp(p, prefix, prefix_len) == 0)
+ found = p + prefix_len;
+ p = strchrnul(p, '\n');
+ if (*p)
+ {
+ *p = '\0'; /* EOL marker for parse_reported_line() below */
+ p++;
+ }
+ }
+
+ report_result_t *result = NULL;
+ if (found)
+ result = parse_reported_line(found);
+
+ free(reported_to);
+ return result;
+}
+
DIR *dd_init_next_file(struct dump_dir *dd)
{
// if (!dd->locked)
diff -x '*.po' -d -urpN libreport.5/src/lib/iso_date_string.c
libreport.6/src/lib/iso_date_string.c
--- libreport.5/src/lib/iso_date_string.c 2011-07-25 10:57:18.000000000 +0200
+++ libreport.6/src/lib/iso_date_string.c 2011-08-03 16:01:53.628409453 +0200
@@ -29,11 +29,3 @@ char *iso_date_string(const time_t *pt)
return buf;
}
-
-time_t string_iso_date(const char *date)
-{
- struct tm tm;
- strptime(date, "%Y-%m-%d-%H:%M:%S", &tm);
-
- return mktime(&tm);
-}
diff -x '*.po' -d -urpN libreport.5/src/lib/report_result.c
libreport.6/src/lib/report_result.c
--- libreport.5/src/lib/report_result.c 2011-08-02 15:36:52.144860418 +0200
+++ libreport.6/src/lib/report_result.c 2011-08-03 15:49:37.056299891 +0200
@@ -18,84 +18,11 @@
*/
#include "internal_libreport.h"
-struct report_result *new_report_result(enum report_result_type type, char *data)
-{
- struct report_result *res;
- const char *event;
-
- event = getenv("EVENT");
- if (!event)
- event = "unknown";
-
- res = xmalloc(sizeof (*res));
- res->event = xstrdup(event);
- res->data = data;
- res->type = type;
- res->timestamp = time(NULL);
-
- return res;
-}
-
-char *format_report_result(const struct report_result *result)
-{
- const char *type_string;
-
- switch (result->type)
- {
- case REPORT_RESULT_TYPE_URL:
- type_string = "URL";
- break;
- case REPORT_RESULT_TYPE_MESSAGE:
- type_string = "MSG";
- break;
- default:
- assert(0);
- }
- return xasprintf("%s: TIME=%s %s=%s", result->event,
- iso_date_string(&result->timestamp), type_string, result->data);
-}
-
-struct report_result *parse_report_result(const char *text)
-{
- struct report_result *res;
- enum report_result_type type;
- time_t ts;
- int event_len, event_ts_len;
- char event[256], timestamp[256], *data;
-
- if (sscanf(text, "%s%n %s%n", event, &event_len, timestamp,
&event_ts_len) != 2)
- return NULL;
-
- data = skip_whitespace(text + event_ts_len);
- if (!strncmp(data, "URL=", 4))
- {
- data += 4;
- type = REPORT_RESULT_TYPE_URL;
- }
- else if (!strncmp(data, "MSG=", 4))
- {
- data += 4;
- type = REPORT_RESULT_TYPE_MESSAGE;
- }
- else
- return NULL;
-
- if (strncmp(timestamp, "TIME=", 5))
- return NULL;
- ts = string_iso_date(timestamp + 5);
-
- res = xmalloc(sizeof (*res));
- res->event = xstrndup(event, event_len - 1);
- res->data = xstrdup(data);
- res->type = type;
- res->timestamp = ts;
-
- return res;
-}
-
void free_report_result(struct report_result *result)
{
- free(result->event);
- free(result->data);
+ if (!result)
+ return;
+ free(result->url);
+ free(result->msg);
free(result);
}
diff -x '*.po' -d -urpN libreport.5/src/plugins/abrt_rh_support.c
libreport.6/src/plugins/abrt_rh_support.c
--- libreport.5/src/plugins/abrt_rh_support.c 2011-08-02 16:28:01.750516487 +0200
+++ libreport.6/src/plugins/abrt_rh_support.c 2011-08-03 15:13:01.204392942 +0200
@@ -395,7 +395,7 @@ send_report_to_new_case(const char* base
break;
}
- char *atch_url = concat_path_file(case_location, "/attachments");
+ char *atch_url = concat_path_file(case_location, "attachments");
abrt_post_state_t *atch_state;
redirect_attach:
atch_state = new_abrt_post_state(0
@@ -410,16 +410,18 @@ send_report_to_new_case(const char* base
abrt_post_file_as_form(atch_state, atch_url, "application/binary",
headers,
report_file_name);
- char *atch_location = find_header_in_abrt_post_state(atch_state,
"Location:");
switch (atch_state->http_resp_code)
{
case 305: /* "305 Use Proxy" */
- if (++redirect_count < 10 && atch_location)
{
- free(atch_url);
- atch_url = xstrdup(atch_location);
- free_abrt_post_state(atch_state);
- goto redirect_attach;
+ char *atch_location = find_header_in_abrt_post_state(atch_state,
"Location:");
+ if (++redirect_count < 10 && atch_location)
+ {
+ free(atch_url);
+ atch_url = xstrdup(atch_location);
+ free_abrt_post_state(atch_state);
+ goto redirect_attach;
+ }
}
/* fall through */
diff -x '*.po' -d -urpN libreport.5/src/plugins/reporter-rhtsupport.c
libreport.6/src/plugins/reporter-rhtsupport.c
--- libreport.5/src/plugins/reporter-rhtsupport.c 2011-08-02 16:37:06.398115806 +0200
+++ libreport.6/src/plugins/reporter-rhtsupport.c 2011-08-03 15:47:55.429991436 +0200
@@ -22,9 +22,12 @@
#include "abrt_xmlrpc.h"
#include "abrt_rh_support.h"
-static void report_to_rhtsupport(
- const char *dump_dir_name,
- map_string_h *settings)
+static char *url;
+static char *login;
+static char *password;
+static bool ssl_verify;
+
+static void report_to_rhtsupport(const char *dump_dir_name)
{
problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
if (!problem_data)
@@ -45,24 +48,6 @@ static void report_to_rhtsupport(
const char* reason;
const char* package;
- char* env;
- env = getenv("RHTSupport_URL");
- char *url = xstrdup(env ? env : (get_map_string_item_or_NULL(settings,
"URL") ? : "https://api.access.redhat.com/rs"));
-
- env = getenv("RHTSupport_Login");
- char *login = xstrdup(env ? env : get_map_string_item_or_empty(settings,
"Login"));
-
- env = getenv("RHTSupport_Password");
- char *password = xstrdup(env ? env : get_map_string_item_or_empty(settings,
"Password"));
-
- env = getenv("RHTSupport_SSLVerify");
- bool ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings,
"SSLVerify"));
-
- if (!login[0] || !password[0])
- {
- error_msg_and_die(_("Empty RHTS login or password"));
- }
-
package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE);
reason = get_problem_item_content_or_NULL(problem_data, FILENAME_REASON);
function = get_problem_item_content_or_NULL(problem_data, FILENAME_CRASH_FUNCTION);
@@ -292,41 +277,130 @@ static void report_to_rhtsupport(
free_problem_data(problem_data);
}
+static void attach_to_rhtsupport(const char *file_name)
+{
+ log(_("Attaching '%s' to case '%s'"), file_name, url);
+
+ static const char *headers[] = {
+ "Accept: text/plain",
+ NULL
+ };
+
+ int redirect_count = 0;
+ char *atch_url = concat_path_file(url, "attachments");
+ abrt_post_state_t *atch_state;
+
+ redirect_attach:
+ atch_state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ atch_state->username = login;
+ atch_state->password = password;
+ abrt_post_file_as_form(atch_state,
+ atch_url,
+ "application/binary",
+ headers,
+ file_name
+ );
+ free(atch_url);
+
+ char *atch_location = find_header_in_abrt_post_state(atch_state,
"Location:");
+ switch (atch_state->http_resp_code)
+ {
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && atch_location)
+ {
+ atch_url = xstrdup(atch_location);
+ free_abrt_post_state(atch_state);
+ goto redirect_attach;
+ }
+ /* fall through */
+
+ default:
+ /* Error */
+ {
+ const char *errmsg = atch_state->curl_error_msg;
+ if (atch_state->body && atch_state->body[0])
+ {
+ if (errmsg && errmsg[0]
+ && strcmp(errmsg, atch_state->body) != 0
+ ) /* both strata/curl error and body are present (and aren't the
same) */
+ errmsg = xasprintf("%s. %s",
+ atch_state->body,
+ errmsg);
+ else /* only body exists */
+ errmsg = atch_state->body;
+ }
+ error_msg_and_die("Can't attach. HTTP code %d%s%s",
+ atch_state->http_resp_code,
+ errmsg ? ". " : "",
+ errmsg ? errmsg : ""
+ );
+ }
+ break;
+
+ case 200:
+ case 201:
+ log("Attachment URL:%s", atch_location);
+ log("File attached successfully");
+ } /* switch */
+
+ free_abrt_post_state(atch_state);
+}
+
int main(int argc, char **argv)
{
abrt_init(argv);
- map_string_h *settings = new_map_string();
const char *dump_dir_name = ".";
+ const char *file_name = NULL;
+ const char *ticket_no = NULL;
GList *conf_file = NULL;
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
- "\b [-v] -c CONFFILE -d DIR\n"
+ "\b [-v] -c CONFFILE -d DIR [-f FILE [-t URL_SFX]]\n"
"\n"
"Reports a problem to RHTSupport.\n"
"\n"
"CONFFILE lines should have 'PARAM = VALUE' format.\n"
"Recognized string parameters: URL, Login, Password.\n"
"Recognized boolean parameter (VALUE should be 1/0, yes/no):
SSLVerify.\n"
- "Parameters can be overridden via $RHTSupport_PARAM environment
variables."
+ "Parameters can be overridden via $RHTSupport_PARAM environment
variables.\n"
+ "\n"
+ "-f FILE uploads FILE to the already created case on RHTSupport
site.\n"
+ "The location of report is retrieved from directory specified by -d
DIR.\n"
+ "If problem data in DIR was never reported to RHTSupport, upload will
fail.\n"
+ "\n"
+ "-f FILE -t SFX uploads FILE to an arbitrary case on RHTSupport
site.\n"
+ "In this case -d DIR is ignored. The address of the case to which
FILE\n"
+ "is to be uploaded is URL/SFX."
);
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
OPT_c = 1 << 2,
+ OPT_f = 1 << 3,
+ OPT_t = 1 << 4,
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump
directory")),
OPT_LIST( 'c', NULL, &conf_file , "FILE",
_("Configuration file (may be given many times)")),
+ OPT_STRING('f', NULL, &file_name , "FILE", _("File
to upload (attach to a case)")),
+ OPT_STRING('t', NULL, &ticket_no , "SFX" , _("URL
suffix, usually it has form 'cases/123456'")),
OPT_END()
};
/*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
export_abrt_envvars(0);
+ /* Parse config, extract necessary params */
+ map_string_h *settings = new_map_string();
while (conf_file)
{
char *fn = (char *)conf_file->data;
@@ -335,6 +409,20 @@ int main(int argc, char **argv)
VERB3 log("Loaded '%s'", fn);
conf_file = g_list_remove(conf_file, fn);
}
+ char* envvar;
+ envvar = getenv("RHTSupport_URL");
+ url = xstrdup(envvar ? envvar : (get_map_string_item_or_NULL(settings,
"URL") ? : "https://api.access.redhat.com/rs"));
+ envvar = getenv("RHTSupport_Login");
+ login = xstrdup(envvar ? envvar : get_map_string_item_or_empty(settings,
"Login"));
+ envvar = getenv("RHTSupport_Password");
+ password = xstrdup(envvar ? envvar : get_map_string_item_or_empty(settings,
"Password"));
+ envvar = getenv("RHTSupport_SSLVerify");
+ ssl_verify = string_to_bool(envvar ? envvar : get_map_string_item_or_empty(settings,
"SSLVerify"));
+ if (!login[0] || !password[0])
+ {
+ error_msg_and_die(_("Empty RHTS login or password"));
+ }
+ free_map_string(settings);
VERB1 log("Initializing XML-RPC library");
xmlrpc_env env;
@@ -344,8 +432,38 @@ int main(int argc, char **argv)
error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string,
env.fault_code);
xmlrpc_env_clean(&env);
- report_to_rhtsupport(dump_dir_name, settings);
+ if (file_name)
+ {
+ if (!ticket_no)
+ {
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ xfunc_die();
+ report_result_t *reported_to = find_in_reported_to(dd,
"RHTSupport:");
+ dd_close(dd);
+
+ if (!reported_to || !reported_to->url)
+ error_msg_and_die("Can't attach: problem data in '%s'
"
+ "was not reported to RHTSupport and therefore has no
URL",
+ dump_dir_name);
+
+ //log("URL:'%s'", reported_to->url);
+ //log("MSG:'%s'", reported_to->msg);
+ free(url);
+ url = reported_to->url;
+ reported_to->url = NULL;
+ free_report_result(reported_to);
+ }
+ else
+ {
+ char *url1 = concat_path_file(url, ticket_no);
+ free(url);
+ url = url1;
+ }
+ attach_to_rhtsupport(file_name);
+ }
+ else
+ report_to_rhtsupport(dump_dir_name);
- free_map_string(settings);
return 0;
}