Esempio n. 1
0
/* Returns 0 on success */
static CURLcode http_post_to_kerneloops_site(const char *url, const char *oopsdata)
{
    CURLcode ret;
    CURL *handle;
    struct curl_httppost *post = NULL;
    struct curl_httppost *last = NULL;
    struct curl_slist *headers = NULL;

    handle = curl_easy_init();
    if (!handle)
        error_msg_and_die("Can't create curl handle");

    headers = curl_slist_append(headers, "Accept: */*");
    headers = curl_slist_append(headers, "Expect:");

    curl_easy_setopt(handle, CURLOPT_URL, url);
    curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1L);
    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);

    curl_formadd(&post, &last,
            CURLFORM_COPYNAME, "oopsdata",
            CURLFORM_COPYCONTENTS, oopsdata,
            CURLFORM_END);
    curl_formadd(&post, &last,
            CURLFORM_COPYNAME, "pass_on_allowed",
            CURLFORM_COPYCONTENTS, "yes",
            CURLFORM_END);

    curl_easy_setopt(handle, CURLOPT_HTTPPOST, post);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writefunction);

    ret = curl_easy_perform_with_proxy(handle, url);

    curl_formfree(post);
    curl_slist_free_all(headers);
    curl_easy_cleanup(handle);

    return ret;
}
Esempio n. 2
0
int
post(post_state_t *state,
                const char *url,
                const char *content_type,
                const char **additional_headers,
                const char *data,
                off_t data_size)
{
    CURLcode curl_err;
    long response_code;
    post_state_t localstate;

    VERB3 log("%s('%s','%s')", __func__, url, data);

    if (!state)
    {
        memset(&localstate, 0, sizeof(localstate));
        state = &localstate;
    }

    state->curl_result = 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);
    // Shut off the built-in progress meter completely
    xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1);

    if (g_verbose >= 2)
    {
        // "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);
        xcurl_easy_setopt_ptr(handle, CURLOPT_DEBUGFUNCTION, curl_debug);
    }

    // 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 : ""));
    }

    if (data_size != POST_DATA_FROMFILE_PUT)
    {
        // Do a 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);
    }
    // else (only POST_DATA_FROMFILE_PUT): do HTTP PUT.

    struct curl_httppost *post = NULL;
    struct curl_httppost *last = NULL;
    FILE *data_file = NULL;
    FILE *body_stream = NULL;
    struct curl_slist *httpheader_list = NULL;

    // Supply data...
    if (data_size == POST_DATA_FROMFILE
     || data_size == POST_DATA_FROMFILE_PUT
    ) {
        // ...from a file
        data_file = fopen(data, "r");
        if (!data_file)
        {
            perror_msg("Can't open '%s'", data);
            goto ret; // return -1
        }

        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);
        fseeko(data_file, 0, SEEK_END);
        off_t sz = ftello(data_file);
        fseeko(data_file, 0, SEEK_SET);
        if (data_size == POST_DATA_FROMFILE)
        {
            // Without this, curl would send "Content-Length: -1"
            // servers don't like that: "413 Request Entity Too Large"
            xcurl_easy_setopt_off_t(handle, CURLOPT_POSTFIELDSIZE_LARGE, sz);
        }
        else
        {
            xcurl_easy_setopt_long(handle, CURLOPT_UPLOAD, 1);
            xcurl_easy_setopt_off_t(handle, CURLOPT_INFILESIZE_LARGE, sz);
        }
    }
    else if (data_size == 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)
        {
            perror_msg("Can't open '%s'", data);
            goto ret; // return -1
        }
        // 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
//FIXME: what if file size doesn't fit in long?
                        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 if (data_size == POST_DATA_STRING_AS_FORM_DATA)
    {
        CURLFORMcode curlform_err = curl_formadd(&post, &last,
                        CURLFORM_PTRNAME, "file", // element name
                        // curl bug - missing filename
                        // http://curl.haxx.se/mail/lib-2011-07/0176.html
                        // https://github.com/bagder/curl/commit/45d883d
                        // fixed in curl-7.22.0~144
                        // tested with curl-7.24.0-3
                        // should be working on F17
                        CURLFORM_BUFFER, "*buffer*", // provides filename
                        CURLFORM_BUFFERPTR, data,
                        CURLFORM_BUFFERLENGTH, (long)strlen(data),
//FIXME: what if file size doesn't fit in long?
                        CURLFORM_CONTENTTYPE, content_type,
                        CURLFORM_END);
        if (curlform_err != 0)
            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 == POST_DATA_STRING == -1, curl will use strlen(data)
        xcurl_easy_setopt_long(handle, CURLOPT_POSTFIELDSIZE, data_size);
        // We don't use CURLOPT_POSTFIELDSIZE_LARGE because
        // I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1.
        // Not a big problem: memory blobs >4GB are very unlikely.
    }

    // Override "Content-Type:"
    if (data_size != POST_DATA_FROMFILE_AS_FORM_DATA
        && data_size != POST_DATA_STRING_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);
    }

    for (; additional_headers && *additional_headers; additional_headers++)
    {
        httpheader_list = curl_slist_append(httpheader_list, *additional_headers);
        if (!httpheader_list)
            error_msg_and_die("out of memory");
    }

    // Add User-Agent: ABRT/N.M
    httpheader_list = curl_slist_append(httpheader_list, "User-Agent: ABRT/"VERSION);
    if (!httpheader_list)
        error_msg_and_die("out of memory");

    if (httpheader_list)
        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 & POST_WANT_HEADERS)
    {
        xcurl_easy_setopt_ptr(handle, CURLOPT_HEADERFUNCTION, (void*)save_headers);
        xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEHEADER, state);
    }
    if (state->flags & 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 & 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.
    state->curl_result = curl_err = curl_easy_perform_with_proxy(handle, url);
    if (curl_err)
    {
        VERB2 log("curl_easy_perform: error %d", (int)curl_err);
        if (state->flags & 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;
    }

    // curl-7.20.1 doesn't do it, we get NULL body in the log message below
    // unless we fflush the body memstream ourself
    if (body_stream)
        fflush(body_stream);

    // 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: response_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;
}