Blame auth_mellon_httpclient.c

Packit Service d6b4c9
/*
Packit Service d6b4c9
 *
Packit Service d6b4c9
 *   mod_auth_mellon.c: an authentication apache module
Packit Service d6b4c9
 *   Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
Packit Service d6b4c9
 *
Packit Service d6b4c9
 *   This program is free software; you can redistribute it and/or modify
Packit Service d6b4c9
 *   it under the terms of the GNU General Public License as published by
Packit Service d6b4c9
 *   the Free Software Foundation; either version 2 of the License, or
Packit Service d6b4c9
 *   (at your option) any later version.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 *   This program is distributed in the hope that it will be useful,
Packit Service d6b4c9
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d6b4c9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d6b4c9
 *   GNU General Public License for more details.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 *   You should have received a copy of the GNU General Public License
Packit Service d6b4c9
 *   along with this program; if not, write to the Free Software
Packit Service d6b4c9
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit Service d6b4c9
 *
Packit Service d6b4c9
 */
Packit Service d6b4c9
Packit Service d6b4c9
#include "auth_mellon.h"
Packit Service d6b4c9
Packit Service d6b4c9
#include <curl/curl.h>
Packit Service d6b4c9
Packit Service d6b4c9
/* The size of the blocks we will allocate. */
Packit Service d6b4c9
#define AM_HC_BLOCK_SIZE 1000
Packit Service d6b4c9
Packit Service d6b4c9
#ifdef APLOG_USE_MODULE
Packit Service d6b4c9
APLOG_USE_MODULE(auth_mellon);
Packit Service d6b4c9
#endif
Packit Service d6b4c9
Packit Service d6b4c9
/* This structure describes a single-linked list of downloaded blocks. */
Packit Service d6b4c9
typedef struct am_hc_block_s {
Packit Service d6b4c9
    /* The next block we have allocated. */
Packit Service d6b4c9
    struct am_hc_block_s *next;
Packit Service d6b4c9
Packit Service d6b4c9
    /* The number of bytes written to this block. */
Packit Service d6b4c9
    apr_size_t used;
Packit Service d6b4c9
Packit Service d6b4c9
    /* The data stored in this block. */
Packit Service d6b4c9
    uint8_t data[AM_HC_BLOCK_SIZE];
Packit Service d6b4c9
} am_hc_block_t;
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This structure describes a header for the block list. */
Packit Service d6b4c9
typedef struct {
Packit Service d6b4c9
    /* The pool we will allocate memory for new blocks from. */
Packit Service d6b4c9
    apr_pool_t *pool;
Packit Service d6b4c9
Packit Service d6b4c9
    /* The first block in the linked list of blocks. */
Packit Service d6b4c9
    am_hc_block_t *first;
Packit Service d6b4c9
Packit Service d6b4c9
    /* The last block in the linked list of blocks. */
Packit Service d6b4c9
    am_hc_block_t *last;
Packit Service d6b4c9
} am_hc_block_header_t;
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function allocates and initializes a block for data copying.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  apr_pool_t *pool     The pool we should allocate the block from.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  The new block we allocated.
Packit Service d6b4c9
 */
Packit Service d6b4c9
static am_hc_block_t *am_hc_block_alloc(apr_pool_t *pool)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_hc_block_t *blk;
Packit Service d6b4c9
Packit Service d6b4c9
    blk = (am_hc_block_t *)apr_palloc(pool, sizeof(am_hc_block_t));
Packit Service d6b4c9
Packit Service d6b4c9
    blk->next = NULL;
Packit Service d6b4c9
    blk->used = 0;
Packit Service d6b4c9
Packit Service d6b4c9
    return blk;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function adds data to the end of a block, and allocates new blocks
Packit Service d6b4c9
 * if the data doesn't fit in one block.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  am_hc_block_t *block   The block we should begin by appending data to.
Packit Service d6b4c9
 *  apr_pool_t *pool       The pool we should allocate memory for new blocks
Packit Service d6b4c9
 *                         from.
Packit Service d6b4c9
 *  const uint8_t *data    The data we should append to the blocks.
Packit Service d6b4c9
 *  apr_size_t size        The length of the data we should append.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  The last block written to (i.e. the next block we should write to).
Packit Service d6b4c9
 */
Packit Service d6b4c9
static am_hc_block_t *am_hc_block_write(
Packit Service d6b4c9
    am_hc_block_t *block,
Packit Service d6b4c9
    apr_pool_t *pool,
Packit Service d6b4c9
    const uint8_t *data, apr_size_t size
Packit Service d6b4c9
    )
Packit Service d6b4c9
{
Packit Service d6b4c9
    apr_size_t num_cpy;
Packit Service d6b4c9
Packit Service d6b4c9
    while(size > 0) {
Packit Service d6b4c9
        /* Find the number of bytes we should write to this block. */
Packit Service d6b4c9
        num_cpy = AM_HC_BLOCK_SIZE - block->used;
Packit Service d6b4c9
        if(num_cpy == 0) {
Packit Service d6b4c9
            /* This block is full -- allocate a new block. */
Packit Service d6b4c9
            block->next = am_hc_block_alloc(pool);
Packit Service d6b4c9
            block = block->next;
Packit Service d6b4c9
            num_cpy = AM_HC_BLOCK_SIZE;
Packit Service d6b4c9
        }
Packit Service d6b4c9
        if(num_cpy > size) {
Packit Service d6b4c9
            num_cpy = size;
Packit Service d6b4c9
        }
Packit Service d6b4c9
Packit Service d6b4c9
        /* Copy data to this block. */
Packit Service d6b4c9
        memcpy(&block->data[block->used], data, num_cpy);
Packit Service d6b4c9
        block->used += num_cpy;
Packit Service d6b4c9
Packit Service d6b4c9
        size -= num_cpy;
Packit Service d6b4c9
        data += num_cpy;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* The next write should be to this block. */
Packit Service d6b4c9
    return block;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function initializes a am_hc_block_header_t structure, which
Packit Service d6b4c9
 * contains information about the linked list of data blocks.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  am_hc_block_header_t *bh   Pointer to the data header whcih we
Packit Service d6b4c9
 *                             should initialize.
Packit Service d6b4c9
 *  apr_pool_t *pool           The pool we should allocate data from.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  Nothing.
Packit Service d6b4c9
 */
Packit Service d6b4c9
static void am_hc_block_header_init(am_hc_block_header_t *bh,
Packit Service d6b4c9
                                    apr_pool_t *pool)
Packit Service d6b4c9
{
Packit Service d6b4c9
    bh->pool = pool;
Packit Service d6b4c9
Packit Service d6b4c9
    bh->first = am_hc_block_alloc(pool);
Packit Service d6b4c9
    bh->last = bh->first;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function writes data to the linked list of blocks identified by
Packit Service d6b4c9
 * the stream-parameter. It matches the prototype required by curl.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  void *data           The data that should be written. It is size*nmemb
Packit Service d6b4c9
 *                       bytes long.
Packit Service d6b4c9
 *  size_t size          The size of each block of data that should
Packit Service d6b4c9
 *                       be written.
Packit Service d6b4c9
 *  size_t nmemb         The number of blocks of data that should be written.
Packit Service d6b4c9
 *  void *block_header   A pointer to a am_hc_block_header_t structure which
Packit Service d6b4c9
 *                       identifies the linked list we should store data in.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  The number of bytes that have been written.
Packit Service d6b4c9
 */
Packit Service d6b4c9
static size_t am_hc_data_write(void *data, size_t size, size_t nmemb,
Packit Service d6b4c9
                               void *data_header)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_hc_block_header_t *bh;
Packit Service d6b4c9
Packit Service d6b4c9
    bh = (am_hc_block_header_t *)data_header;
Packit Service d6b4c9
Packit Service d6b4c9
    bh->last = am_hc_block_write(bh->last, bh->pool, (const uint8_t *)data,
Packit Service d6b4c9
                                 size * nmemb);
Packit Service d6b4c9
Packit Service d6b4c9
    return size * nmemb;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function fetches the data which was written to the databuffers
Packit Service d6b4c9
 * in the linked list which the am_hc_data_t structure keeps track of.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  am_hc_block_header_t *bh   The header telling us which data buffers
Packit Service d6b4c9
 *                             we should extract data from.
Packit Service d6b4c9
 *  apr_pool_t *pool           The pool we should allocate the data
Packit Service d6b4c9
 *                             buffer from.
Packit Service d6b4c9
 *  void **buffer              A pointer to where we should store a pointer
Packit Service d6b4c9
 *                             to the data buffer we allocate. We will
Packit Service d6b4c9
 *                             always add a null-terminator to the end of
Packit Service d6b4c9
 *                             data buffer. This parameter can't be NULL.
Packit Service d6b4c9
 *  apr_size_t *size           This is a pointer to where we will store the
Packit Service d6b4c9
 *                             length of the data, not including the
Packit Service d6b4c9
 *                             null-terminator we add. This parameter can
Packit Service d6b4c9
 *                             be NULL.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  Nothing.
Packit Service d6b4c9
 */
Packit Service d6b4c9
static void am_hc_data_extract(am_hc_block_header_t *bh, apr_pool_t *pool,
Packit Service d6b4c9
                               void **buffer, apr_size_t *size)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_hc_block_t *blk;
Packit Service d6b4c9
    apr_size_t length;
Packit Service d6b4c9
    uint8_t *buf;
Packit Service d6b4c9
    apr_size_t pos;
Packit Service d6b4c9
Packit Service d6b4c9
    /* First we find the length of the data. */
Packit Service d6b4c9
    length = 0;
Packit Service d6b4c9
    for(blk = bh->first; blk != NULL; blk = blk->next) {
Packit Service d6b4c9
        length += blk->used;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Allocate memory for the data. Add one to the size in order to
Packit Service d6b4c9
     *  have space for the null-terminator.
Packit Service d6b4c9
     */
Packit Service d6b4c9
    buf = (uint8_t *)apr_palloc(pool, length + 1);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Copy the data into the buffer. */
Packit Service d6b4c9
    pos = 0;
Packit Service d6b4c9
    for(blk = bh->first; blk != NULL; blk = blk->next) {
Packit Service d6b4c9
        memcpy(&buf[pos], blk->data, blk->used);
Packit Service d6b4c9
        pos += blk->used;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Add the null-terminator. */
Packit Service d6b4c9
    buf[length] = 0;
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set up the return values. */
Packit Service d6b4c9
    *buffer = (void *)buf;
Packit Service d6b4c9
    if(size != NULL) {
Packit Service d6b4c9
        *size = length;
Packit Service d6b4c9
    }
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function creates a curl object and performs generic initialization
Packit Service d6b4c9
 * of it.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  request_rec *r             The request we should log errors against.
Packit Service d6b4c9
 *  const char *uri            The URI we should request.
Packit Service d6b4c9
 *  am_hc_block_header_t *bh   The buffer curl will write response data to.
Packit Service d6b4c9
 *  char *curl_error           A buffer of size CURL_ERROR_SIZE where curl
Packit Service d6b4c9
 *                             will store error messages.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  A initialized curl object on succcess, or NULL on error.
Packit Service d6b4c9
 */
Packit Service d6b4c9
static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
Packit Service d6b4c9
                                     am_hc_block_header_t *bh,
Packit Service d6b4c9
                                     char *curl_error)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
Packit Service d6b4c9
    CURL *curl;
Packit Service d6b4c9
    CURLcode res;
Packit Service d6b4c9
Packit Service d6b4c9
    /* Initialize the curl object. */
Packit Service d6b4c9
    curl = curl_easy_init();
Packit Service d6b4c9
    if(curl == NULL) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to initialize a curl object.");
Packit Service d6b4c9
        return NULL;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set up error reporting. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set curl error buffer: [%u]\n", res);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Disable progress reporting. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to disable curl progress reporting: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Disable use of signals. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to disable signals in curl: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set the timeout of the transfer. It is currently set to two minutes. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set the timeout of the curl download:"
Packit Service d6b4c9
                      " [%u] %s", res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* If we have a CA configured, try to use it */
Packit Service d6b4c9
    if (cfg->idp_ca_file != NULL) {
Packit Service d6b4c9
        res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file->path);
Packit Service d6b4c9
        if(res != CURLE_OK) {
Packit Service d6b4c9
            AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                          "Failed to set SSL CA info %s:"
Packit Service d6b4c9
                          " [%u] %s", cfg->idp_ca_file->path, res, curl_error);
Packit Service d6b4c9
            goto cleanup_fail;
Packit Service d6b4c9
        }
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Enable fail on http error. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to enable failure on http error: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Select which uri we should download. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_URL, uri);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set curl download uri to \"%s\": [%u] %s",
Packit Service d6b4c9
                      uri, res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set up data writing. */
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set curl write function. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, am_hc_data_write);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set the curl write function: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set the curl write function parameter. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, bh);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set the curl write function data: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    return curl;
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
 cleanup_fail:
Packit Service d6b4c9
    curl_easy_cleanup(curl);
Packit Service d6b4c9
    return NULL;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function downloads data from a specified URI, with specified timeout
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  request_rec *r       The apache request this download is associated
Packit Service d6b4c9
 *                       with. It is used for memory allocation and logging.
Packit Service d6b4c9
 *  const char *uri      The URI we should download.
Packit Service d6b4c9
 *  void **buffer        A pointer to where we should store a pointer to the
Packit Service d6b4c9
 *                       downloaded data. We will always add a null-terminator
Packit Service d6b4c9
 *                       to the data. This parameter can't be NULL.
Packit Service d6b4c9
 *  apr_size_t *size     This is a pointer to where we will store the length
Packit Service d6b4c9
 *                       of the downloaded data, not including the
Packit Service d6b4c9
 *                       null-terminator we add. This parameter can be NULL.
Packit Service d6b4c9
 *  int timeout          Timeout in seconds, 0 for no timeout.
Packit Service d6b4c9
 *  long *status         Pointer to HTTP status code. 
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  OK on success, or HTTP_INTERNAL_SERVER_ERROR on failure. On failure we
Packit Service d6b4c9
 *  will write a log message describing the error.
Packit Service d6b4c9
 */
Packit Service d6b4c9
int am_httpclient_get(request_rec *r, const char *uri,
Packit Service d6b4c9
                      void **buffer, apr_size_t *size,
Packit Service d6b4c9
                      int timeout, long *status)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_hc_block_header_t bh;
Packit Service d6b4c9
    CURL *curl;
Packit Service d6b4c9
    char curl_error[CURL_ERROR_SIZE];
Packit Service d6b4c9
    CURLcode res;
Packit Service d6b4c9
Packit Service d6b4c9
    /* Initialize the data storage. */
Packit Service d6b4c9
    am_hc_block_header_init(&bh, r->pool);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Initialize the curl object. */
Packit Service d6b4c9
    curl = am_httpclient_init_curl(r, uri, &bh, curl_error);
Packit Service d6b4c9
    if(curl == NULL) {
Packit Service d6b4c9
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to download data from the uri \"%s\", "
Packit Service d6b4c9
                      "cannot set timeout to %ld: [%u] %s",
Packit Service d6b4c9
                      uri, (long)timeout, res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
    
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, (long)timeout);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to download data from the uri \"%s\", "
Packit Service d6b4c9
                      "cannot set connect timeout to %ld: [%u] %s",
Packit Service d6b4c9
                      uri, (long)timeout,  res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Do the download. */
Packit Service d6b4c9
    res = curl_easy_perform(curl);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to download data from the uri \"%s\", "
Packit Service d6b4c9
                      "transaction aborted: [%u] %s",
Packit Service d6b4c9
                      uri, res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    if (status != NULL) {
Packit Service d6b4c9
        res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
Packit Service d6b4c9
        if(res != CURLE_OK) {
Packit Service d6b4c9
            AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                          "Failed to download data from the uri \"%s\", "
Packit Service d6b4c9
                          "no status report: [%u] %s",
Packit Service d6b4c9
                          uri, res, curl_error);
Packit Service d6b4c9
            goto cleanup_fail;
Packit Service d6b4c9
       }
Packit Service d6b4c9
    }
Packit Service d6b4c9
    
Packit Service d6b4c9
    /* Free the curl object. */
Packit Service d6b4c9
    curl_easy_cleanup(curl);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Copy the data. */
Packit Service d6b4c9
    am_hc_data_extract(&bh, r->pool, buffer, size);
Packit Service d6b4c9
Packit Service d6b4c9
    return OK;
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
 cleanup_fail:
Packit Service d6b4c9
    curl_easy_cleanup(curl);
Packit Service d6b4c9
    return HTTP_INTERNAL_SERVER_ERROR;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function downloads data from a specified URI by issuing a POST
Packit Service d6b4c9
 * request.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  request_rec *r             The apache request this download is
Packit Service d6b4c9
 *                             associated with. It is used for memory
Packit Service d6b4c9
 *                             allocation and logging.
Packit Service d6b4c9
 *  const char *uri            The URI we should post data to.
Packit Service d6b4c9
 *  const void *post_data      The POST data we should send.
Packit Service d6b4c9
 *  apr_size_t post_length     The length of the POST data.
Packit Service d6b4c9
 *  const char *content_type   The content type of the POST data. This
Packit Service d6b4c9
 *                             parameter can be NULL, in which case the
Packit Service d6b4c9
 *                             content type will be
Packit Service d6b4c9
 *                             "application/x-www-form-urlencoded".
Packit Service d6b4c9
 *  void **buffer              A pointer to where we should store a pointer
Packit Service d6b4c9
 *                             to the downloaded data. We will always add a
Packit Service d6b4c9
 *                             null-terminator to the data. This parameter
Packit Service d6b4c9
 *                             can't be NULL.
Packit Service d6b4c9
 *  apr_size_t *size           This is a pointer to where we will store the
Packit Service d6b4c9
 *                             length of the downloaded data, not including
Packit Service d6b4c9
 *                             the null-terminator we add. This parameter
Packit Service d6b4c9
 *                             can be NULL.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  OK on success. On failure we will write a log message describing the
Packit Service d6b4c9
 *  error, and return HTTP_INTERNAL_SERVER_ERROR.
Packit Service d6b4c9
 */
Packit Service d6b4c9
int am_httpclient_post(request_rec *r, const char *uri,
Packit Service d6b4c9
                       const void *post_data, apr_size_t post_length,
Packit Service d6b4c9
                       const char *content_type,
Packit Service d6b4c9
                       void **buffer, apr_size_t *size)
Packit Service d6b4c9
{
Packit Service d6b4c9
    am_hc_block_header_t bh;
Packit Service d6b4c9
    CURL *curl;
Packit Service d6b4c9
    char curl_error[CURL_ERROR_SIZE];
Packit Service d6b4c9
    CURLcode res;
Packit Service d6b4c9
    struct curl_slist *ctheader;
Packit Service d6b4c9
Packit Service d6b4c9
    /* Initialize the data storage. */
Packit Service d6b4c9
    am_hc_block_header_init(&bh, r->pool);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Initialize the curl object. */
Packit Service d6b4c9
    curl = am_httpclient_init_curl(r, uri, &bh, curl_error);
Packit Service d6b4c9
    if(curl == NULL) {
Packit Service d6b4c9
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Enable POST request. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_POST, 1L);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to enable POST request: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set POST data size. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_length);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set the POST data length: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set POST data. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set the POST data: [%u] %s",
Packit Service d6b4c9
                      res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set the content-type header. */
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set default content type if content_type is NULL. */
Packit Service d6b4c9
    if(content_type == NULL) {
Packit Service d6b4c9
        content_type = "application/x-www-form-urlencoded";
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Create header list. */
Packit Service d6b4c9
    ctheader = NULL;
Packit Service d6b4c9
    ctheader = curl_slist_append(ctheader, apr_pstrcat(
Packit Service d6b4c9
                                     r->pool,
Packit Service d6b4c9
                                     "Content-Type: ",
Packit Service d6b4c9
                                     content_type,
Packit Service d6b4c9
                                     NULL
Packit Service d6b4c9
                                     ));
Packit Service d6b4c9
Packit Service d6b4c9
    /* Set headers. */
Packit Service d6b4c9
    res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, ctheader);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to set content-type header to \"%s\": [%u] %s",
Packit Service d6b4c9
                      content_type, res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
    /* Do the download. */
Packit Service d6b4c9
    res = curl_easy_perform(curl);
Packit Service d6b4c9
    if(res != CURLE_OK) {
Packit Service d6b4c9
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
Packit Service d6b4c9
                      "Failed to download data from the uri \"%s\": [%u] %s",
Packit Service d6b4c9
                      uri, res, curl_error);
Packit Service d6b4c9
        goto cleanup_fail;
Packit Service d6b4c9
    }
Packit Service d6b4c9
Packit Service d6b4c9
    /* Free the curl object. */
Packit Service d6b4c9
    curl_easy_cleanup(curl);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Free the content-type header. */
Packit Service d6b4c9
    curl_slist_free_all(ctheader);
Packit Service d6b4c9
Packit Service d6b4c9
    /* Copy the data. */
Packit Service d6b4c9
    am_hc_data_extract(&bh, r->pool, buffer, size);
Packit Service d6b4c9
Packit Service d6b4c9
    return OK;
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
 cleanup_fail:
Packit Service d6b4c9
    curl_easy_cleanup(curl);
Packit Service d6b4c9
    return HTTP_INTERNAL_SERVER_ERROR;
Packit Service d6b4c9
}
Packit Service d6b4c9
Packit Service d6b4c9
Packit Service d6b4c9
/* This function downloads data from a specified URI by issuing a POST
Packit Service d6b4c9
 * request.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Parameters:
Packit Service d6b4c9
 *  request_rec *r             The apache request this download is
Packit Service d6b4c9
 *                             associated with. It is used for memory
Packit Service d6b4c9
 *                             allocation and logging.
Packit Service d6b4c9
 *  const char *uri            The URI we should post data to.
Packit Service d6b4c9
 *  const char *post_data      The POST data we should send.
Packit Service d6b4c9
 *  const char *content_type   The content type of the POST data. This
Packit Service d6b4c9
 *                             parameter can be NULL, in which case the
Packit Service d6b4c9
 *                             content type will be
Packit Service d6b4c9
 *                             "application/x-www-form-urlencoded".
Packit Service d6b4c9
 *  void **buffer              A pointer to where we should store a pointer
Packit Service d6b4c9
 *                             to the downloaded data. We will always add a
Packit Service d6b4c9
 *                             null-terminator to the data. This parameter
Packit Service d6b4c9
 *                             can't be NULL.
Packit Service d6b4c9
 *  apr_size_t *size           This is a pointer to where we will store the
Packit Service d6b4c9
 *                             length of the downloaded data, not including
Packit Service d6b4c9
 *                             the null-terminator we add. This parameter
Packit Service d6b4c9
 *                             can be NULL.
Packit Service d6b4c9
 *
Packit Service d6b4c9
 * Returns:
Packit Service d6b4c9
 *  OK on success. On failure we will write a log message describing the
Packit Service d6b4c9
 *  error, and return HTTP_INTERNAL_SERVER_ERROR.
Packit Service d6b4c9
 */
Packit Service d6b4c9
int am_httpclient_post_str(request_rec *r, const char *uri,
Packit Service d6b4c9
                           const char *post_data,
Packit Service d6b4c9
                           const char *content_type,
Packit Service d6b4c9
                           void **buffer, apr_size_t *size)
Packit Service d6b4c9
{
Packit Service d6b4c9
    return am_httpclient_post(r, uri, post_data, strlen(post_data),
Packit Service d6b4c9
                              content_type, buffer, size);
Packit Service d6b4c9
}