From 568f01db9d2e2f540ec855b622b4e93162e0f21d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 26 Aug 2010 16:33:27 +0200 Subject: abrt_curl.cpp -> abrt_curl.c, abrt_rh_support.cpp -> abrt_rh_support.c Signed-off-by: Denys Vlasenko --- lib/utils/Makefile.am | 4 +- lib/utils/abrt_curl.c | 376 ++++++++++++++++++++++++++++++ lib/utils/abrt_curl.cpp | 378 ------------------------------ lib/utils/abrt_curl.h | 8 + lib/utils/abrt_rh_support.c | 519 +++++++++++++++++++++++++++++++++++++++++ lib/utils/abrt_rh_support.cpp | 525 ------------------------------------------ lib/utils/abrt_rh_support.h | 8 + 7 files changed, 913 insertions(+), 905 deletions(-) create mode 100644 lib/utils/abrt_curl.c delete mode 100644 lib/utils/abrt_curl.cpp create mode 100644 lib/utils/abrt_rh_support.c delete mode 100644 lib/utils/abrt_rh_support.cpp (limited to 'lib/utils') diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am index 5bacd991..66a48b06 100644 --- a/lib/utils/Makefile.am +++ b/lib/utils/Makefile.am @@ -56,8 +56,8 @@ libABRTdUtils_la_SOURCES = \ make_descr.cpp \ $(HEADER_DIR)/comm_layer_inner.h CommLayerInner.cpp \ $(HEADER_DIR)/abrt_xmlrpc.h abrt_xmlrpc.cpp \ - abrt_rh_support.h abrt_rh_support.cpp \ - abrt_curl.h abrt_curl.cpp \ + abrt_rh_support.h abrt_rh_support.c \ + abrt_curl.h abrt_curl.c \ $(HEADER_DIR)/plugin.h Plugin.cpp \ Polkit.h Polkit.cpp diff --git a/lib/utils/abrt_curl.c b/lib/utils/abrt_curl.c new file mode 100644 index 00000000..80faa19c --- /dev/null +++ b/lib/utils/abrt_curl.c @@ -0,0 +1,376 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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 "abrtlib.h" +#include "abrt_curl.h" +#include "comm_layer_inner.h" + +/* + * Utility functions + */ +CURL* xcurl_easy_init() +{ + CURL* curl = curl_easy_init(); + if (!curl) + { + error_msg_and_die("Can't create curl handle"); + } + return curl; +} + +static char* +check_curl_error(CURLcode err, const char* msg) +{ + if (err) + return xasprintf("%s: %s", msg, curl_easy_strerror(err)); + return NULL; +} + +static void +die_if_curl_error(CURLcode err) +{ + if (err) { + char *msg = check_curl_error(err, "curl"); + error_msg_and_die("%s", msg); + } +} + +static void +xcurl_easy_setopt_ptr(CURL *handle, CURLoption option, const void *parameter) +{ + CURLcode err = curl_easy_setopt(handle, option, parameter); + if (err) { + char *msg = check_curl_error(err, "curl"); + error_msg_and_die("%s", msg); + } +} +static inline void +xcurl_easy_setopt_long(CURL *handle, CURLoption option, long parameter) +{ + xcurl_easy_setopt_ptr(handle, option, (void*)parameter); +} + +/* + * post_state utility functions + */ + +abrt_post_state_t *new_abrt_post_state(int flags) +{ + abrt_post_state_t *state = (abrt_post_state_t *)xzalloc(sizeof(*state)); + state->flags = flags; + return state; +} + +void free_abrt_post_state(abrt_post_state_t *state) +{ + char **headers = state->headers; + if (headers) + { + while (*headers) + free(*headers++); + free(state->headers); + } + free(state->curl_error_msg); + free(state->body); + free(state); +} + +char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str) +{ + char **headers = state->headers; + if (headers) + { + unsigned len = strlen(str); + while (*headers) + { + if (strncmp(*headers, str, len) == 0) + return skip_whitespace(*headers + len); + headers++; + } + } + return NULL; +} + +/* + * abrt_post: perform HTTP POST transaction + */ + +/* "save headers" callback */ +static size_t +save_headers(void *buffer_pv, size_t count, size_t nmemb, void *ptr) +{ + abrt_post_state_t* state = (abrt_post_state_t*)ptr; + size_t size = count * nmemb; + + char *h = xstrndup((char*)buffer_pv, size); + strchrnul(h, '\r')[0] = '\0'; + strchrnul(h, '\n')[0] = '\0'; + + unsigned cnt = state->header_cnt; + + /* Check for the case when curl follows a redirect: + * header 0: 'HTTP/1.1 301 Moved Permanently' + * header 1: 'Connection: close' + * header 2: 'Location: NEW_URL' + * header 3: '' + * header 0: 'HTTP/1.1 200 OK' <-- we need to forget all hdrs and start anew + */ + if (cnt != 0 + && strncmp(h, "HTTP/", 5) == 0 + && state->headers[cnt-1][0] == '\0' /* prev header is an empty string */ + ) { + char **headers = state->headers; + if (headers) + { + while (*headers) + free(*headers++); + } + cnt = 0; + } + + VERB3 log("save_headers: header %d: '%s'", cnt, h); + state->headers = (char**)xrealloc(state->headers, (cnt+2) * sizeof(state->headers[0])); + state->headers[cnt] = h; + state->header_cnt = ++cnt; + state->headers[cnt] = NULL; + + return size; +} + +/* "read local data from a file" callback */ +static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + static time_t last_t; // hack + + FILE *fp = (FILE*)userdata; + time_t t = time(NULL); + + // Report current file position every 16 seconds + if (!(t & 0xf) && last_t != t) + { + last_t = t; + off_t cur_pos = ftello(fp); + fseeko(fp, 0, SEEK_END); + off_t sz = ftello(fp); + fseeko(fp, cur_pos, SEEK_SET); + error_msg(_("Uploaded: %llu of %llu kbytes"), + (unsigned long long)cur_pos / 1024, + (unsigned long long)sz / 1024); + } + + return fread(ptr, size, nmemb, fp); +} + +int +abrt_post(abrt_post_state_t *state, + const char *url, + const char *content_type, + const char *data, + off_t data_size) +{ + CURLcode curl_err; + long response_code; + abrt_post_state_t localstate; + + VERB3 log("abrt_post('%s','%s')", url, data); + + if (!state) + { + memset(&localstate, 0, sizeof(localstate)); + state = &localstate; + } + + state->http_resp_code = response_code = -1; + + CURL *handle = xcurl_easy_init(); + + // Buffer[CURL_ERROR_SIZE] curl stores human readable error messages in. + // This may be more helpful than just return code from curl_easy_perform. + // curl will need it until curl_easy_cleanup. + state->errmsg[0] = '\0'; + xcurl_easy_setopt_ptr(handle, CURLOPT_ERRORBUFFER, state->errmsg); + // "Display a lot of verbose information about its operations. + // Very useful for libcurl and/or protocol debugging and understanding. + // The verbose information will be sent to stderr, or the stream set + // with CURLOPT_STDERR" + //xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1); + // Shut off the built-in progress meter completely + xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1); + + // TODO: do we need to check for CURLE_URL_MALFORMAT error *here*, + // not in curl_easy_perform? + xcurl_easy_setopt_ptr(handle, CURLOPT_URL, url); + + // Auth if configured + if (state->username) { + // bitmask of allowed auth methods + xcurl_easy_setopt_long(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + xcurl_easy_setopt_ptr(handle, CURLOPT_USERNAME, state->username); + xcurl_easy_setopt_ptr(handle, CURLOPT_PASSWORD, (state->password ? state->password : "")); + } + + // Do a regular HTTP post. This also makes curl use + // a "Content-Type: application/x-www-form-urlencoded" header. + // (This is by far the most commonly used POST method). + xcurl_easy_setopt_long(handle, CURLOPT_POST, 1); + // Supply POST data... + struct curl_httppost* post = NULL; + struct curl_httppost* last = NULL; + FILE* data_file = NULL; + if (data_size == ABRT_POST_DATA_FROMFILE) { + // ...from a file + data_file = fopen(data, "r"); + if (!data_file) +//FIXME: + perror_msg_and_die("can't open '%s'", data); + xcurl_easy_setopt_ptr(handle, CURLOPT_READDATA, data_file); + // Want to use custom read function + xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting); + } else if (data_size == ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) { + // ...from a file, in multipart/formdata format + const char *basename = strrchr(data, '/'); + if (basename) basename++; + else basename = data; +#if 0 + // Simple way, without custom reader function + CURLFORMcode curlform_err = curl_formadd(&post, &last, + CURLFORM_PTRNAME, "file", // element name + CURLFORM_FILE, data, // filename to read from + CURLFORM_CONTENTTYPE, content_type, + CURLFORM_FILENAME, basename, // filename to put in the form + CURLFORM_END); +#else + data_file = fopen(data, "r"); + if (!data_file) +//FIXME: + perror_msg_and_die("can't open '%s'", data); + // Want to use custom read function + xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting); + // Need to know file size + fseeko(data_file, 0, SEEK_END); + off_t sz = ftello(data_file); + fseeko(data_file, 0, SEEK_SET); + // Create formdata + CURLFORMcode curlform_err = curl_formadd(&post, &last, + CURLFORM_PTRNAME, "file", // element name + // use CURLOPT_READFUNCTION for reading, pass data_file as its last param: + CURLFORM_STREAM, data_file, + CURLFORM_CONTENTSLENGTH, (long)sz, // a must if we use CURLFORM_STREAM option + CURLFORM_CONTENTTYPE, content_type, + CURLFORM_FILENAME, basename, // filename to put in the form + CURLFORM_END); +#endif + if (curlform_err != 0) +//FIXME: + error_msg_and_die("out of memory or read error (curl_formadd error code: %d)", (int)curlform_err); + xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPPOST, post); + } else { + // .. from a blob in memory + xcurl_easy_setopt_ptr(handle, CURLOPT_POSTFIELDS, data); + // note1: if data_size == ABRT_POST_DATA_STRING == -1, curl will use strlen(data) + xcurl_easy_setopt_long(handle, CURLOPT_POSTFIELDSIZE, data_size); + // note2: CURLOPT_POSTFIELDSIZE_LARGE can't be used: xcurl_easy_setopt_long() + // truncates data_size on 32-bit arch. Need xcurl_easy_setopt_long_long()? + // Also, I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1. + } + // Override "Content-Type:" + struct curl_slist *httpheader_list = NULL; + if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) + { + char *content_type_header = xasprintf("Content-Type: %s", content_type); + // Note: curl_slist_append() copies content_type_header + httpheader_list = curl_slist_append(httpheader_list, content_type_header); + if (!httpheader_list) + error_msg_and_die("out of memory"); + free(content_type_header); + xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list); + } + +// Disabled: was observed to also handle "305 Use proxy" redirect, +// apparently with POST->GET remapping - which server didn't like at all. +// Attempted to suppress remapping on 305 using CURLOPT_POSTREDIR of -1, +// but it still did not work. +#if 0 + // Please handle 301/302 redirects for me + xcurl_easy_setopt_long(handle, CURLOPT_FOLLOWLOCATION, 1); + xcurl_easy_setopt_long(handle, CURLOPT_MAXREDIRS, 10); + // Bitmask to control how libcurl acts on redirects after POSTs. + // Bit 0 set (value CURL_REDIR_POST_301) makes libcurl + // not convert POST requests into GET requests when following + // a 301 redirection. Bit 1 (value CURL_REDIR_POST_302) makes libcurl + // maintain the request method after a 302 redirect. + // CURL_REDIR_POST_ALL is a convenience define that sets both bits. + // The non-RFC behaviour is ubiquitous in web browsers, so the library + // does the conversion by default to maintain consistency. + // However, a server may require a POST to remain a POST. + xcurl_easy_setopt_long(handle, CURLOPT_POSTREDIR, -1L /*CURL_REDIR_POST_ALL*/ ); +#endif + + // Prepare for saving information + if (state->flags & ABRT_POST_WANT_HEADERS) + { + xcurl_easy_setopt_ptr(handle, CURLOPT_HEADERFUNCTION, (void*)save_headers); + xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEHEADER, state); + } + FILE* body_stream = NULL; + if (state->flags & ABRT_POST_WANT_BODY) + { + body_stream = open_memstream(&state->body, &state->body_size); + if (!body_stream) + error_msg_and_die("out of memory"); + xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEDATA, body_stream); + } + if (!(state->flags & ABRT_POST_WANT_SSL_VERIFY)) + { + xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYPEER, 0); + xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYHOST, 0); + } + + // This is the place where everything happens. + // Here errors are not limited to "out of memory", can't just die. + curl_err = curl_easy_perform(handle); + if (curl_err) + { + VERB2 log("curl_easy_perform: error %d", (int)curl_err); + if (state->flags & ABRT_POST_WANT_ERROR_MSG) + { + state->curl_error_msg = check_curl_error(curl_err, "curl_easy_perform"); + VERB3 log("curl_easy_perform: error_msg: %s", state->curl_error_msg); + } + goto ret; + } + + // Headers/body are already saved (if requested), extract more info + curl_err = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code); + die_if_curl_error(curl_err); + state->http_resp_code = response_code; + VERB3 log("after curl_easy_perform: http code %ld body:'%s'", response_code, state->body); + + ret: + curl_easy_cleanup(handle); + if (httpheader_list) + curl_slist_free_all(httpheader_list); + if (body_stream) + fclose(body_stream); + if (data_file) + fclose(data_file); + if (post) + curl_formfree(post); + + return response_code; +} diff --git a/lib/utils/abrt_curl.cpp b/lib/utils/abrt_curl.cpp deleted file mode 100644 index a836f704..00000000 --- a/lib/utils/abrt_curl.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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 "abrtlib.h" -#include "abrt_curl.h" -#include "comm_layer_inner.h" - -using namespace std; - -/* - * Utility functions - */ -CURL* xcurl_easy_init() -{ - CURL* curl = curl_easy_init(); - if (!curl) - { - error_msg_and_die("Can't create curl handle"); - } - return curl; -} - -static char* -check_curl_error(CURLcode err, const char* msg) -{ - if (err) - return xasprintf("%s: %s", msg, curl_easy_strerror(err)); - return NULL; -} - -static void -die_if_curl_error(CURLcode err) -{ - if (err) { - char *msg = check_curl_error(err, "curl"); - error_msg_and_die("%s", msg); - } -} - -static void -xcurl_easy_setopt_ptr(CURL *handle, CURLoption option, const void *parameter) -{ - CURLcode err = curl_easy_setopt(handle, option, parameter); - if (err) { - char *msg = check_curl_error(err, "curl"); - error_msg_and_die("%s", msg); - } -} -static inline void -xcurl_easy_setopt_long(CURL *handle, CURLoption option, long parameter) -{ - xcurl_easy_setopt_ptr(handle, option, (void*)parameter); -} - -/* - * post_state utility functions - */ - -abrt_post_state_t *new_abrt_post_state(int flags) -{ - abrt_post_state_t *state = (abrt_post_state_t *)xzalloc(sizeof(*state)); - state->flags = flags; - return state; -} - -void free_abrt_post_state(abrt_post_state_t *state) -{ - char **headers = state->headers; - if (headers) - { - while (*headers) - free(*headers++); - free(state->headers); - } - free(state->curl_error_msg); - free(state->body); - free(state); -} - -char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str) -{ - char **headers = state->headers; - if (headers) - { - unsigned len = strlen(str); - while (*headers) - { - if (strncmp(*headers, str, len) == 0) - return skip_whitespace(*headers + len); - headers++; - } - } - return NULL; -} - -/* - * abrt_post: perform HTTP POST transaction - */ - -/* "save headers" callback */ -static size_t -save_headers(void *buffer_pv, size_t count, size_t nmemb, void *ptr) -{ - abrt_post_state_t* state = (abrt_post_state_t*)ptr; - size_t size = count * nmemb; - - char *h = xstrndup((char*)buffer_pv, size); - strchrnul(h, '\r')[0] = '\0'; - strchrnul(h, '\n')[0] = '\0'; - - unsigned cnt = state->header_cnt; - - /* Check for the case when curl follows a redirect: - * header 0: 'HTTP/1.1 301 Moved Permanently' - * header 1: 'Connection: close' - * header 2: 'Location: NEW_URL' - * header 3: '' - * header 0: 'HTTP/1.1 200 OK' <-- we need to forget all hdrs and start anew - */ - if (cnt != 0 - && strncmp(h, "HTTP/", 5) == 0 - && state->headers[cnt-1][0] == '\0' /* prev header is an empty string */ - ) { - char **headers = state->headers; - if (headers) - { - while (*headers) - free(*headers++); - } - cnt = 0; - } - - VERB3 log("save_headers: header %d: '%s'", cnt, h); - state->headers = (char**)xrealloc(state->headers, (cnt+2) * sizeof(state->headers[0])); - state->headers[cnt] = h; - state->header_cnt = ++cnt; - state->headers[cnt] = NULL; - - return size; -} - -/* "read local data from a file" callback */ -static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *userdata) -{ - static time_t last_t; // hack - - FILE *fp = (FILE*)userdata; - time_t t = time(NULL); - - // Report current file position every 16 seconds - if (!(t & 0xf) && last_t != t) - { - last_t = t; - off_t cur_pos = ftello(fp); - fseeko(fp, 0, SEEK_END); - off_t sz = ftello(fp); - fseeko(fp, cur_pos, SEEK_SET); - update_client(_("Uploaded: %llu of %llu kbytes"), - (unsigned long long)cur_pos / 1024, - (unsigned long long)sz / 1024); - } - - return fread(ptr, size, nmemb, fp); -} - -int -abrt_post(abrt_post_state_t *state, - const char *url, - const char *content_type, - const char *data, - off_t data_size) -{ - CURLcode curl_err; - long response_code; - abrt_post_state_t localstate; - - VERB3 log("abrt_post('%s','%s')", url, data); - - if (!state) - { - memset(&localstate, 0, sizeof(localstate)); - state = &localstate; - } - - state->http_resp_code = response_code = -1; - - CURL *handle = xcurl_easy_init(); - - // Buffer[CURL_ERROR_SIZE] curl stores human readable error messages in. - // This may be more helpful than just return code from curl_easy_perform. - // curl will need it until curl_easy_cleanup. - state->errmsg[0] = '\0'; - xcurl_easy_setopt_ptr(handle, CURLOPT_ERRORBUFFER, state->errmsg); - // "Display a lot of verbose information about its operations. - // Very useful for libcurl and/or protocol debugging and understanding. - // The verbose information will be sent to stderr, or the stream set - // with CURLOPT_STDERR" - //xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1); - // Shut off the built-in progress meter completely - xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1); - - // TODO: do we need to check for CURLE_URL_MALFORMAT error *here*, - // not in curl_easy_perform? - xcurl_easy_setopt_ptr(handle, CURLOPT_URL, url); - - // Auth if configured - if (state->username) { - // bitmask of allowed auth methods - xcurl_easy_setopt_long(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - xcurl_easy_setopt_ptr(handle, CURLOPT_USERNAME, state->username); - xcurl_easy_setopt_ptr(handle, CURLOPT_PASSWORD, (state->password ? state->password : "")); - } - - // Do a regular HTTP post. This also makes curl use - // a "Content-Type: application/x-www-form-urlencoded" header. - // (This is by far the most commonly used POST method). - xcurl_easy_setopt_long(handle, CURLOPT_POST, 1); - // Supply POST data... - struct curl_httppost* post = NULL; - struct curl_httppost* last = NULL; - FILE* data_file = NULL; - if (data_size == ABRT_POST_DATA_FROMFILE) { - // ...from a file - data_file = fopen(data, "r"); - if (!data_file) -//FIXME: - perror_msg_and_die("can't open '%s'", data); - xcurl_easy_setopt_ptr(handle, CURLOPT_READDATA, data_file); - // Want to use custom read function - xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting); - } else if (data_size == ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) { - // ...from a file, in multipart/formdata format - const char *basename = strrchr(data, '/'); - if (basename) basename++; - else basename = data; -#if 0 - // Simple way, without custom reader function - CURLFORMcode curlform_err = curl_formadd(&post, &last, - CURLFORM_PTRNAME, "file", // element name - CURLFORM_FILE, data, // filename to read from - CURLFORM_CONTENTTYPE, content_type, - CURLFORM_FILENAME, basename, // filename to put in the form - CURLFORM_END); -#else - data_file = fopen(data, "r"); - if (!data_file) -//FIXME: - perror_msg_and_die("can't open '%s'", data); - // Want to use custom read function - xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting); - // Need to know file size - fseeko(data_file, 0, SEEK_END); - off_t sz = ftello(data_file); - fseeko(data_file, 0, SEEK_SET); - // Create formdata - CURLFORMcode curlform_err = curl_formadd(&post, &last, - CURLFORM_PTRNAME, "file", // element name - // use CURLOPT_READFUNCTION for reading, pass data_file as its last param: - CURLFORM_STREAM, data_file, - CURLFORM_CONTENTSLENGTH, (long)sz, // a must if we use CURLFORM_STREAM option - CURLFORM_CONTENTTYPE, content_type, - CURLFORM_FILENAME, basename, // filename to put in the form - CURLFORM_END); -#endif - if (curlform_err != 0) -//FIXME: - error_msg_and_die("out of memory or read error (curl_formadd error code: %d)", (int)curlform_err); - xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPPOST, post); - } else { - // .. from a blob in memory - xcurl_easy_setopt_ptr(handle, CURLOPT_POSTFIELDS, data); - // note1: if data_size == ABRT_POST_DATA_STRING == -1, curl will use strlen(data) - xcurl_easy_setopt_long(handle, CURLOPT_POSTFIELDSIZE, data_size); - // note2: CURLOPT_POSTFIELDSIZE_LARGE can't be used: xcurl_easy_setopt_long() - // truncates data_size on 32-bit arch. Need xcurl_easy_setopt_long_long()? - // Also, I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1. - } - // Override "Content-Type:" - struct curl_slist *httpheader_list = NULL; - if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) - { - char *content_type_header = xasprintf("Content-Type: %s", content_type); - // Note: curl_slist_append() copies content_type_header - httpheader_list = curl_slist_append(httpheader_list, content_type_header); - if (!httpheader_list) - error_msg_and_die("out of memory"); - free(content_type_header); - xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list); - } - -// Disabled: was observed to also handle "305 Use proxy" redirect, -// apparently with POST->GET remapping - which server didn't like at all. -// Attempted to suppress remapping on 305 using CURLOPT_POSTREDIR of -1, -// but it still did not work. -#if 0 - // Please handle 301/302 redirects for me - xcurl_easy_setopt_long(handle, CURLOPT_FOLLOWLOCATION, 1); - xcurl_easy_setopt_long(handle, CURLOPT_MAXREDIRS, 10); - // Bitmask to control how libcurl acts on redirects after POSTs. - // Bit 0 set (value CURL_REDIR_POST_301) makes libcurl - // not convert POST requests into GET requests when following - // a 301 redirection. Bit 1 (value CURL_REDIR_POST_302) makes libcurl - // maintain the request method after a 302 redirect. - // CURL_REDIR_POST_ALL is a convenience define that sets both bits. - // The non-RFC behaviour is ubiquitous in web browsers, so the library - // does the conversion by default to maintain consistency. - // However, a server may require a POST to remain a POST. - xcurl_easy_setopt_long(handle, CURLOPT_POSTREDIR, -1L /*CURL_REDIR_POST_ALL*/ ); -#endif - - // Prepare for saving information - if (state->flags & ABRT_POST_WANT_HEADERS) - { - xcurl_easy_setopt_ptr(handle, CURLOPT_HEADERFUNCTION, (void*)save_headers); - xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEHEADER, state); - } - FILE* body_stream = NULL; - if (state->flags & ABRT_POST_WANT_BODY) - { - body_stream = open_memstream(&state->body, &state->body_size); - if (!body_stream) - error_msg_and_die("out of memory"); - xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEDATA, body_stream); - } - if (!(state->flags & ABRT_POST_WANT_SSL_VERIFY)) - { - xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYPEER, 0); - xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYHOST, 0); - } - - // This is the place where everything happens. - // Here errors are not limited to "out of memory", can't just die. - curl_err = curl_easy_perform(handle); - if (curl_err) - { - VERB2 log("curl_easy_perform: error %d", (int)curl_err); - if (state->flags & ABRT_POST_WANT_ERROR_MSG) - { - state->curl_error_msg = check_curl_error(curl_err, "curl_easy_perform"); - VERB3 log("curl_easy_perform: error_msg: %s", state->curl_error_msg); - } - goto ret; - } - - // Headers/body are already saved (if requested), extract more info - curl_err = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code); - die_if_curl_error(curl_err); - state->http_resp_code = response_code; - VERB3 log("after curl_easy_perform: http code %ld body:'%s'", response_code, state->body); - - ret: - curl_easy_cleanup(handle); - if (httpheader_list) - curl_slist_free_all(httpheader_list); - if (body_stream) - fclose(body_stream); - if (data_file) - fclose(data_file); - if (post) - curl_formfree(post); - - return response_code; -} diff --git a/lib/utils/abrt_curl.h b/lib/utils/abrt_curl.h index 1f34e7ec..77b4605c 100644 --- a/lib/utils/abrt_curl.h +++ b/lib/utils/abrt_curl.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + CURL* xcurl_easy_init(); typedef struct abrt_post_state { @@ -85,4 +89,8 @@ abrt_post_file_as_form(abrt_post_state_t *state, return abrt_post(state, url, content_type, filename, ABRT_POST_DATA_FROMFILE_AS_FORM_DATA); } +#ifdef __cplusplus +} +#endif + #endif diff --git a/lib/utils/abrt_rh_support.c b/lib/utils/abrt_rh_support.c new file mode 100644 index 00000000..04e2c8ef --- /dev/null +++ b/lib/utils/abrt_rh_support.c @@ -0,0 +1,519 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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 +#include +#include +#include "abrtlib.h" +#include "abrt_curl.h" +#include "abrt_rh_support.h" + +struct reportfile { + xmlTextWriterPtr writer; + xmlBufferPtr buf; +}; + +static void __attribute__((__noreturn__)) +die_xml_oom(void) +{ + error_msg_and_die("can't create XML attribute (out of memory?)"); +} + +static xmlBufferPtr +xxmlBufferCreate(void) +{ + xmlBufferPtr r = xmlBufferCreate(); + if (!r) + die_xml_oom(); + return r; +} + +static xmlTextWriterPtr +xxmlNewTextWriterMemory(xmlBufferPtr buf /*, int compression*/) +{ + xmlTextWriterPtr r = xmlNewTextWriterMemory(buf, /*compression:*/ 0); + if (!r) + die_xml_oom(); + return r; +} + +static void +xxmlTextWriterStartDocument(xmlTextWriterPtr writer, + const char * version, + const char * encoding, + const char * standalone) +{ + if (xmlTextWriterStartDocument(writer, version, encoding, standalone) < 0) + die_xml_oom(); +} + +static void +xxmlTextWriterEndDocument(xmlTextWriterPtr writer) +{ + if (xmlTextWriterEndDocument(writer) < 0) + die_xml_oom(); +} + +static void +xxmlTextWriterStartElement(xmlTextWriterPtr writer, const char *name) +{ + // these bright guys REDEFINED CHAR (!) to unsigned char... + if (xmlTextWriterStartElement(writer, (unsigned char*)name) < 0) + die_xml_oom(); +} + +static void +xxmlTextWriterEndElement(xmlTextWriterPtr writer) +{ + if (xmlTextWriterEndElement(writer) < 0) + die_xml_oom(); +} + +static void +xxmlTextWriterWriteElement(xmlTextWriterPtr writer, const char *name, const char *content) +{ + if (xmlTextWriterWriteElement(writer, (unsigned char*)name, (unsigned char*)content) < 0) + die_xml_oom(); +} + +static void +xxmlTextWriterWriteAttribute(xmlTextWriterPtr writer, const char *name, const char *content) +{ + if (xmlTextWriterWriteAttribute(writer, (unsigned char*)name, (unsigned char*)content) < 0) + die_xml_oom(); +} + +#if 0 //unused +static void +xxmlTextWriterWriteString(xmlTextWriterPtr writer, const char *content) +{ + if (xmlTextWriterWriteString(writer, (unsigned char*)content) < 0) + die_xml_oom(); +} +#endif + +// +// End the reportfile, and prepare it for delivery. +// No more bindings can be added after this. +// +static void +close_writer(reportfile_t* file) +{ + if (!file->writer) + return; + + // close off the end of the xml file + xxmlTextWriterEndDocument(file->writer); + xmlFreeTextWriter(file->writer); + file->writer = NULL; +} + +// +// This allocates a reportfile_t structure and initializes it. +// +reportfile_t* +new_reportfile(void) +{ + // create a new reportfile_t + reportfile_t* file = (reportfile_t*)xmalloc(sizeof(*file)); + + // set up a libxml 'buffer' and 'writer' to that buffer + file->buf = xxmlBufferCreate(); + file->writer = xxmlNewTextWriterMemory(file->buf); + + // start a new xml document: + // ... + xxmlTextWriterStartDocument(file->writer, /*version:*/ NULL, /*encoding:*/ NULL, /*standalone:*/ NULL); + xxmlTextWriterStartElement(file->writer, "report"); + xxmlTextWriterWriteAttribute(file->writer, "xmlns", "http://www.redhat.com/gss/strata"); + + return file; +} + +static void +internal_reportfile_start_binding(reportfile_t* file, const char* name, int isbinary, const char* filename) +{ + // writer, "binding"); + xxmlTextWriterWriteAttribute(file->writer, "name", name); + if (filename) + xxmlTextWriterWriteAttribute(file->writer, "fileName", filename); + if (isbinary) + xxmlTextWriterWriteAttribute(file->writer, "type", "binary"); + else + xxmlTextWriterWriteAttribute(file->writer, "type", "text"); +} + +// +// Add a new text binding +// +void +reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value) +{ + // + internal_reportfile_start_binding(file, name, /*isbinary:*/ 0, /*filename:*/ NULL); + xxmlTextWriterWriteAttribute(file->writer, "value", value); + xxmlTextWriterEndElement(file->writer); +} + +// +// Add a new binding to a report whose value is represented as a file. +// +void +reportfile_add_binding_from_namedfile(reportfile_t* file, + const char* on_disk_filename, /* unused so far */ + const char* binding_name, + const char* recorded_filename, + int isbinary) +{ + // + char *href_name = concat_path_file("content", binding_name); + xxmlTextWriterWriteAttribute(file->writer, "href", href_name); + free(href_name); +} + +// +// Return the contents of the reportfile as a string. +// +const char* +reportfile_as_string(reportfile_t* file) +{ + close_writer(file); + // unsigned char -> char + return (char*)file->buf->content; +} + +void +reportfile_free(reportfile_t* file) +{ + if (!file) + return; + close_writer(file); + xmlBufferFree(file->buf); + free(file); +} + + +// +// post_signature() +// +char* +post_signature(const char* baseURL, bool ssl_verify, const char* signature) +{ + char *URL = concat_path_file(baseURL, "/signatures"); + + abrt_post_state_t *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) + ); + int http_resp_code = abrt_post_string(state, URL, "application/xml", signature); + free(URL); + + char *retval; + const char *strata_msg; + switch (http_resp_code) + { + case 200: + case 201: + if (state->body) + { + retval = state->body; + state->body = NULL; + break; + } + strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:"); + if (strata_msg && strcmp(strata_msg, "CREATED") != 0) { + retval = xstrdup(strata_msg); + break; + } + retval = xstrdup("Signature submitted successfully"); + break; + + default: + strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:"); + if (strata_msg) + { + retval = xasprintf("Error (HTTP response %d): %s", + http_resp_code, + strata_msg); + break; + } + if (state->curl_error_msg) + { + if (http_resp_code >= 0) + retval = xasprintf("Error (HTTP response %d): %s", http_resp_code, state->curl_error_msg); + else + retval = xasprintf("Error in HTTP transaction: %s", state->curl_error_msg); + break; + } + retval = xasprintf("Error (HTTP response %d), body:\n%s", http_resp_code, state->body); + break; + } + + free_abrt_post_state(state); + return retval; +} + + +// +// send_report_to_new_case() +// + +static char* +make_case_data(const char* summary, const char* description, + const char* product, const char* version, + const char* component) +{ + char* retval; + xmlTextWriterPtr writer; + xmlBufferPtr buf; + + buf = xxmlBufferCreate(); + writer = xxmlNewTextWriterMemory(buf); + + xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes"); + xxmlTextWriterStartElement(writer, "case"); + xxmlTextWriterWriteAttribute(writer, "xmlns", + "http://www.redhat.com/gss/strata"); + + xxmlTextWriterWriteElement(writer, "summary", summary); + xxmlTextWriterWriteElement(writer, "description", description); + if (product) { + xxmlTextWriterWriteElement(writer, "product", product); + } + if (version) { + xxmlTextWriterWriteElement(writer, "version", version); + } + if (component) { + xxmlTextWriterWriteElement(writer, "component", component); + } + + xxmlTextWriterEndDocument(writer); + retval = xstrdup((const char*)buf->content); + xmlFreeTextWriter(writer); + xmlBufferFree(buf); + return retval; +} + +#if 0 //unused +static char* +make_response(const char* title, const char* body, + const char* actualURL, const char* displayURL) +{ + char* retval; + xmlTextWriterPtr writer; + xmlBufferPtr buf; + + buf = xxmlBufferCreate(); + writer = xxmlNewTextWriterMemory(buf); + + xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes"); + xxmlTextWriterStartElement(writer, "response"); + if (title) { + xxmlTextWriterWriteElement(writer, "title", title); + } + if (body) { + xxmlTextWriterWriteElement(writer, "body", body); + } + if (actualURL || displayURL) { + xxmlTextWriterStartElement(writer, "URL"); + if (actualURL) { + xxmlTextWriterWriteAttribute(writer, "href", actualURL); + } + if (displayURL) { + xxmlTextWriterWriteString(writer, displayURL); + } + } + + xxmlTextWriterEndDocument(writer); + retval = xstrdup((const char*)buf->content); + xmlFreeTextWriter(writer); + xmlBufferFree(buf); + return retval; +} +//Example: +// +//Case Created and Report AttachedNew Case URL +#endif + +char* +send_report_to_new_case(const char* baseURL, + const char* username, + const char* password, + bool ssl_verify, + const char* summary, + const char* description, + const char* component, + const char* report_file_name) +{ + char *case_url = concat_path_file(baseURL, "/cases"); + + char *case_data = make_case_data(summary, description, + "Red Hat Enterprise Linux", "6.0", + component); + + int redirect_count = 0; + char *errmsg; + char *allocated = NULL; + char* retval = NULL; + abrt_post_state_t *case_state; + + redirect_case: + case_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) + ); + case_state->username = username; + case_state->password = password; + abrt_post_string(case_state, case_url, "application/xml", case_data); + + char *case_location = find_header_in_abrt_post_state(case_state, "Location:"); + switch (case_state->http_resp_code) + { + case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */ + case 302: /* "302 Found" (just in case) */ + case 305: /* "305 Use Proxy" */ + if (++redirect_count < 10 && case_location) + { + free(case_url); + case_url = xstrdup(case_location); + free_abrt_post_state(case_state); + goto redirect_case; + } + goto bad_resp_code; + + case 404: + /* Not strictly necessary, but makes this typical error less cryptic: + * instead of returning html-encoded body, we show short concise message, + * and show offending URL (typos in which is a typical cause) */ + retval = xasprintf("error in case creation, " + "HTTP code: 404 (Not found), URL:'%s'", case_url); + break; + + default: + bad_resp_code: + errmsg = case_state->curl_error_msg; + if (errmsg) + retval = xasprintf("error in case creation: %s", errmsg); + else + { + errmsg = find_header_in_abrt_post_state(case_state, "Strata-Message:"); + if ((!errmsg || !errmsg[0]) && case_state->body && case_state->body[0]) + errmsg = case_state->body; + if (errmsg) + retval = xasprintf("error in case creation, HTTP code: %d, server says: '%s'", + case_state->http_resp_code, errmsg); + else + retval = xasprintf("error in case creation, HTTP code: %d", + case_state->http_resp_code); + } + break; + + case 200: + case 201: { + if (!case_location) { + /* Case Creation returned valid code, but no location */ + retval = xasprintf("error in case creation: no Location URL, HTTP code: %d", + case_state->http_resp_code); + break; + } + + char *atch_url = concat_path_file(case_location, "/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 = username; + atch_state->password = password; + abrt_post_file_as_form(atch_state, atch_url, "application/binary", 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; + } + /* fall through */ + + default: + /* Case Creation Succeeded, attachement FAILED */ + errmsg = find_header_in_abrt_post_state(atch_state, "Strata-Message:"); + if (!errmsg || !errmsg[0]) + 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) */ + allocated = errmsg = xasprintf("%s. %s", + atch_state->body, + errmsg); + else /* only body exists */ + errmsg = atch_state->body; + } + /* Note: to prevent URL misparsing, make sure to delimit + * case_location only using spaces */ + retval = xasprintf("Case created: %s but report attachment failed (HTTP code %d)%s%s", + case_location, + atch_state->http_resp_code, + errmsg ? ": " : "", + errmsg ? errmsg : "" + ); + break; + + case 200: + case 201: + // unused + //char *body = atch_state->body; + //if (case_state->body && case_state->body[0]) + //{ + // body = case_state->body; + // if (atch_state->body && atch_state->body[0]) + // allocated = body = xasprintf("%s\n%s", + // case_state->body, + // atch_state->body); + //} + retval = xasprintf("Case created: %s", /*body,*/ case_location); + } /* switch (attach HTTP code) */ + + free_abrt_post_state(atch_state); + free(atch_url); + } /* case 200/201 */ + + } /* switch (case HTTP code) */ + + free_abrt_post_state(case_state); + free(allocated); + free(case_url); + return retval; +} diff --git a/lib/utils/abrt_rh_support.cpp b/lib/utils/abrt_rh_support.cpp deleted file mode 100644 index fd78a427..00000000 --- a/lib/utils/abrt_rh_support.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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. -*/ -//#define _GNU_SOURCE - -#include -#include -#include -#include "abrtlib.h" -#include "abrt_curl.h" -#include "abrt_xmlrpc.h" -#include "abrt_exception.h" -#include "abrt_rh_support.h" - -using namespace std; - -struct reportfile { - xmlTextWriterPtr writer; - xmlBufferPtr buf; -}; - -static void __attribute__((__noreturn__)) -die_xml_oom(void) -{ - error_msg_and_die("can't create XML attribute (out of memory?)"); -} - -static xmlBufferPtr -xxmlBufferCreate(void) -{ - xmlBufferPtr r = xmlBufferCreate(); - if (!r) - die_xml_oom(); - return r; -} - -static xmlTextWriterPtr -xxmlNewTextWriterMemory(xmlBufferPtr buf /*, int compression*/) -{ - xmlTextWriterPtr r = xmlNewTextWriterMemory(buf, /*compression:*/ 0); - if (!r) - die_xml_oom(); - return r; -} - -static void -xxmlTextWriterStartDocument(xmlTextWriterPtr writer, - const char * version, - const char * encoding, - const char * standalone) -{ - if (xmlTextWriterStartDocument(writer, version, encoding, standalone) < 0) - die_xml_oom(); -} - -static void -xxmlTextWriterEndDocument(xmlTextWriterPtr writer) -{ - if (xmlTextWriterEndDocument(writer) < 0) - die_xml_oom(); -} - -static void -xxmlTextWriterStartElement(xmlTextWriterPtr writer, const char *name) -{ - // these bright guys REDEFINED CHAR (!) to unsigned char... - if (xmlTextWriterStartElement(writer, (unsigned char*)name) < 0) - die_xml_oom(); -} - -static void -xxmlTextWriterEndElement(xmlTextWriterPtr writer) -{ - if (xmlTextWriterEndElement(writer) < 0) - die_xml_oom(); -} - -static void -xxmlTextWriterWriteElement(xmlTextWriterPtr writer, const char *name, const char *content) -{ - if (xmlTextWriterWriteElement(writer, (unsigned char*)name, (unsigned char*)content) < 0) - die_xml_oom(); -} - -static void -xxmlTextWriterWriteAttribute(xmlTextWriterPtr writer, const char *name, const char *content) -{ - if (xmlTextWriterWriteAttribute(writer, (unsigned char*)name, (unsigned char*)content) < 0) - die_xml_oom(); -} - -#if 0 //unused -static void -xxmlTextWriterWriteString(xmlTextWriterPtr writer, const char *content) -{ - if (xmlTextWriterWriteString(writer, (unsigned char*)content) < 0) - die_xml_oom(); -} -#endif - -// -// End the reportfile, and prepare it for delivery. -// No more bindings can be added after this. -// -static void -close_writer(reportfile_t* file) -{ - if (!file->writer) - return; - - // close off the end of the xml file - xxmlTextWriterEndDocument(file->writer); - xmlFreeTextWriter(file->writer); - file->writer = NULL; -} - -// -// This allocates a reportfile_t structure and initializes it. -// -reportfile_t* -new_reportfile(void) -{ - // create a new reportfile_t - reportfile_t* file = (reportfile_t*)xmalloc(sizeof(*file)); - - // set up a libxml 'buffer' and 'writer' to that buffer - file->buf = xxmlBufferCreate(); - file->writer = xxmlNewTextWriterMemory(file->buf); - - // start a new xml document: - // ... - xxmlTextWriterStartDocument(file->writer, /*version:*/ NULL, /*encoding:*/ NULL, /*standalone:*/ NULL); - xxmlTextWriterStartElement(file->writer, "report"); - xxmlTextWriterWriteAttribute(file->writer, "xmlns", "http://www.redhat.com/gss/strata"); - - return file; -} - -static void -internal_reportfile_start_binding(reportfile_t* file, const char* name, int isbinary, const char* filename) -{ - // writer, "binding"); - xxmlTextWriterWriteAttribute(file->writer, "name", name); - if (filename) - xxmlTextWriterWriteAttribute(file->writer, "fileName", filename); - if (isbinary) - xxmlTextWriterWriteAttribute(file->writer, "type", "binary"); - else - xxmlTextWriterWriteAttribute(file->writer, "type", "text"); -} - -// -// Add a new text binding -// -void -reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value) -{ - // - internal_reportfile_start_binding(file, name, /*isbinary:*/ 0, /*filename:*/ NULL); - xxmlTextWriterWriteAttribute(file->writer, "value", value); - xxmlTextWriterEndElement(file->writer); -} - -// -// Add a new binding to a report whose value is represented as a file. -// -void -reportfile_add_binding_from_namedfile(reportfile_t* file, - const char* on_disk_filename, /* unused so far */ - const char* binding_name, - const char* recorded_filename, - int isbinary) -{ - // - char *href_name = concat_path_file("content", binding_name); - xxmlTextWriterWriteAttribute(file->writer, "href", href_name); - free(href_name); -} - -// -// Return the contents of the reportfile as a string. -// -const char* -reportfile_as_string(reportfile_t* file) -{ - close_writer(file); - // unsigned char -> char - return (char*)file->buf->content; -} - -void -reportfile_free(reportfile_t* file) -{ - if (!file) - return; - close_writer(file); - xmlBufferFree(file->buf); - free(file); -} - - -// -// post_signature() -// -char* -post_signature(const char* baseURL, bool ssl_verify, const char* signature) -{ - char *URL = concat_path_file(baseURL, "/signatures"); - - abrt_post_state *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) - ); - int http_resp_code = abrt_post_string(state, URL, "application/xml", signature); - free(URL); - - char *retval; - const char *strata_msg; - switch (http_resp_code) - { - case 200: - case 201: - if (state->body) - { - retval = state->body; - state->body = NULL; - break; - } - strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:"); - if (strata_msg && strcmp(strata_msg, "CREATED") != 0) { - retval = xstrdup(strata_msg); - break; - } - retval = xstrdup("Signature submitted successfully"); - break; - - default: - strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:"); - if (strata_msg) - { - retval = xasprintf("Error (HTTP response %d): %s", - http_resp_code, - strata_msg); - break; - } - if (state->curl_error_msg) - { - if (http_resp_code >= 0) - retval = xasprintf("Error (HTTP response %d): %s", http_resp_code, state->curl_error_msg); - else - retval = xasprintf("Error in HTTP transaction: %s", state->curl_error_msg); - break; - } - retval = xasprintf("Error (HTTP response %d), body:\n%s", http_resp_code, state->body); - break; - } - - free_abrt_post_state(state); - return retval; -} - - -// -// send_report_to_new_case() -// - -static char* -make_case_data(const char* summary, const char* description, - const char* product, const char* version, - const char* component) -{ - char* retval; - xmlTextWriterPtr writer; - xmlBufferPtr buf; - - buf = xxmlBufferCreate(); - writer = xxmlNewTextWriterMemory(buf); - - xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes"); - xxmlTextWriterStartElement(writer, "case"); - xxmlTextWriterWriteAttribute(writer, "xmlns", - "http://www.redhat.com/gss/strata"); - - xxmlTextWriterWriteElement(writer, "summary", summary); - xxmlTextWriterWriteElement(writer, "description", description); - if (product) { - xxmlTextWriterWriteElement(writer, "product", product); - } - if (version) { - xxmlTextWriterWriteElement(writer, "version", version); - } - if (component) { - xxmlTextWriterWriteElement(writer, "component", component); - } - - xxmlTextWriterEndDocument(writer); - retval = xstrdup((const char*)buf->content); - xmlFreeTextWriter(writer); - xmlBufferFree(buf); - return retval; -} - -#if 0 //unused -static char* -make_response(const char* title, const char* body, - const char* actualURL, const char* displayURL) -{ - char* retval; - xmlTextWriterPtr writer; - xmlBufferPtr buf; - - buf = xxmlBufferCreate(); - writer = xxmlNewTextWriterMemory(buf); - - xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes"); - xxmlTextWriterStartElement(writer, "response"); - if (title) { - xxmlTextWriterWriteElement(writer, "title", title); - } - if (body) { - xxmlTextWriterWriteElement(writer, "body", body); - } - if (actualURL || displayURL) { - xxmlTextWriterStartElement(writer, "URL"); - if (actualURL) { - xxmlTextWriterWriteAttribute(writer, "href", actualURL); - } - if (displayURL) { - xxmlTextWriterWriteString(writer, displayURL); - } - } - - xxmlTextWriterEndDocument(writer); - retval = xstrdup((const char*)buf->content); - xmlFreeTextWriter(writer); - xmlBufferFree(buf); - return retval; -} -//Example: -// -//Case Created and Report AttachedNew Case URL -#endif - -char* -send_report_to_new_case(const char* baseURL, - const char* username, - const char* password, - bool ssl_verify, - const char* summary, - const char* description, - const char* component, - const char* report_file_name) -{ - char *case_url = concat_path_file(baseURL, "/cases"); - - char *case_data = make_case_data(summary, description, - "Red Hat Enterprise Linux", "6.0", - component); - - int redirect_count = 0; - char *errmsg; - char *allocated = NULL; - char* retval = NULL; - abrt_post_state *case_state; - - redirect_case: - case_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) - ); - case_state->username = username; - case_state->password = password; - abrt_post_string(case_state, case_url, "application/xml", case_data); - - char *case_location = find_header_in_abrt_post_state(case_state, "Location:"); - switch (case_state->http_resp_code) - { - case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */ - case 302: /* "302 Found" (just in case) */ - case 305: /* "305 Use Proxy" */ - if (++redirect_count < 10 && case_location) - { - free(case_url); - case_url = xstrdup(case_location); - free_abrt_post_state(case_state); - goto redirect_case; - } - goto bad_resp_code; - - case 404: - /* Not strictly necessary, but makes this typical error less cryptic: - * instead of returning html-encoded body, we show short concise message, - * and show offending URL (typos in which is a typical cause) */ - retval = xasprintf("error in case creation, " - "HTTP code: 404 (Not found), URL:'%s'", case_url); - break; - - default: - bad_resp_code: - errmsg = case_state->curl_error_msg; - if (errmsg) - retval = xasprintf("error in case creation: %s", errmsg); - else - { - errmsg = find_header_in_abrt_post_state(case_state, "Strata-Message:"); - if ((!errmsg || !errmsg[0]) && case_state->body && case_state->body[0]) - errmsg = case_state->body; - if (errmsg) - retval = xasprintf("error in case creation, HTTP code: %d, server says: '%s'", - case_state->http_resp_code, errmsg); - else - retval = xasprintf("error in case creation, HTTP code: %d", - case_state->http_resp_code); - } - break; - - case 200: - case 201: { - if (!case_location) { - /* Case Creation returned valid code, but no location */ - retval = xasprintf("error in case creation: no Location URL, HTTP code: %d", - case_state->http_resp_code); - break; - } - - char *atch_url = concat_path_file(case_location, "/attachments"); - abrt_post_state *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 = username; - atch_state->password = password; - abrt_post_file_as_form(atch_state, atch_url, "application/binary", 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; - } - /* fall through */ - - default: - /* Case Creation Succeeded, attachement FAILED */ - errmsg = find_header_in_abrt_post_state(atch_state, "Strata-Message:"); - if (!errmsg || !errmsg[0]) - 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) */ - allocated = errmsg = xasprintf("%s. %s", - atch_state->body, - errmsg); - else /* only body exists */ - errmsg = atch_state->body; - } - /* Note: to prevent URL misparsing, make sure to delimit - * case_location only using spaces */ - retval = xasprintf("Case created: %s but report attachment failed (HTTP code %d)%s%s", - case_location, - atch_state->http_resp_code, - errmsg ? ": " : "", - errmsg ? errmsg : "" - ); - break; - - case 200: - case 201: - // unused - //char *body = atch_state->body; - //if (case_state->body && case_state->body[0]) - //{ - // body = case_state->body; - // if (atch_state->body && atch_state->body[0]) - // allocated = body = xasprintf("%s\n%s", - // case_state->body, - // atch_state->body); - //} - retval = xasprintf("Case created: %s", /*body,*/ case_location); - } /* switch (attach HTTP code) */ - - free_abrt_post_state(atch_state); - free(atch_url); - } /* case 200/201 */ - - } /* switch (case HTTP code) */ - - free_abrt_post_state(case_state); - free(allocated); - free(case_url); - return retval; -} diff --git a/lib/utils/abrt_rh_support.h b/lib/utils/abrt_rh_support.h index 393a1a2c..db6e9cd7 100644 --- a/lib/utils/abrt_rh_support.h +++ b/lib/utils/abrt_rh_support.h @@ -19,6 +19,10 @@ #ifndef ABRT_RH_SUPPORT_H_ #define ABRT_RH_SUPPORT_H_ 1 +#ifdef __cplusplus +extern "C" { +#endif + typedef struct reportfile reportfile_t; reportfile_t *new_reportfile(void); @@ -44,4 +48,8 @@ send_report_to_new_case(const char* baseURL, const char* component, const char* report_file_name); +#ifdef __cplusplus +} +#endif + #endif -- cgit