Logo Search packages:      
Sourcecode: rapple version File versions  Download package

http.c

Go to the documentation of this file.
/*
   Name: $RCSfile: http.c,v $
   Author: Alan Moran
   $Date: 2005/07/21 21:14:10 $
   $Revision: 1.9 $
   $Id: http.c,v 1.9 2005/07/21 21:14:10 nloyola Exp $

   Legal Notice:

   This program is free software; you can redistribute it and/or
   modify it under the terms of the license contained in the
   COPYING file that comes with this distribution.

 */

/**
   @file

   @brief Functions for parsing HTTP reponses.

   The http module supports the use of rapple as a web based tool by assisting
   in MIME parsing (e.g., to support the processing of uploaded files) and
   other related functions (e.g., the discovery of remote user names in
   authenticated web environments.)

*/

#include "globals.h"
#include "http.h"

rpl_http_form_data   data;
rpl_str_t    filename;
rpl_str_t    content_type;
rpl_str_t    tmp_filename;

static void http_parse_form_enc_query(size_t cnt_length);
static void http_parse_multipart_query(size_t cnt_length, rpl_c_str_t boundary);
static void http_parse_get_query(void);
static void http_parse_query_string(rpl_str_t query_string);
static int  http_parse_multipart_string(rpl_str_t post_cnt, rpl_c_str_t bdy_param);
static void http_create_parameter(rpl_c_str_t key, rpl_c_str_t value);

/*

   converts MIME content from "application/x-www-form-encoded" to "text/plain".
static rpl_str_t
decode(rpl_str_t field)
{
    size_t i=-1,j=0,k,length;
    rpl_str_t decoded_field;

    assert(field != NULL);

    length = strlen(field) + 1;
    decoded_field = (rpl_str_t)rpl_me_malloc(length);
    // now do the rest......
    while(++i <= length)
    {
        if(field[i] == '%')
        {
            sscanf(&field[i+1],"%02X", &k);
            field[j++] = 0xff & k;
            i+=2;
        } else {
            field[j++] = (field[i]=='+') ? ' ' : field[i];
        }
    }
}
 */

/**
   Returns the HTML FORM parameter value associated with key.

   @param key

   @return  the HTML FORM parameter value associated with key.
 */
rpl_str_t
00079 rpl_http_get_parameter(rpl_c_str_t key) {
    size_t length;
    rpl_str_t value = RPL_STR_NUL;
    rpl_http_form_data *tr;

    assert(key != NULL);

    tr = data.next;

    do {
        if(strcmp(key, tr->key) == 0) {
            length = strlen(tr->value) + 1;
            value = (rpl_str_t)rpl_me_malloc(length);
            snprintf(value, length, "%s", tr->value);
            break;
        }
        tr = tr->next;
    } while(tr);

    return value;
}

/**
   Returns the filename in a HTTP file upload.

   @return the filename in a HTTP file upload.
 */
rpl_str_t
00107 rpl_http_get_filename() {
    rpl_str_t fname = RPL_STR_NUL;
    size_t length;

    if(filename != NULL) {
        length = strlen(filename) + 1;
        fname = (rpl_str_t)rpl_me_malloc(length);
        snprintf(fname, length, "%s", filename);
    }
    return fname;
}

/**
   Returns local cache of downloaded file.

   @return local cache of downloaded file.
 */
rpl_str_t
00125 rpl_http_get_local_filename() {
    return (tmp_filename != NULL) ? tmp_filename : RPL_STR_NUL;
}

/**
   Returns the filename in a HTTP file upload.

   @return the filename in a HTTP file upload.
 */
rpl_str_t
00135 rpl_http_get_upload_filename() {
    rpl_str_t up_fname = RPL_STR_NUL;

    up_fname = (rpl_str_t)rpl_me_malloc(L_tmpnam);
    sprintf(up_fname, "%s", tmp_filename);

    return up_fname;
}

/**
   Returns the content_type in a HTTP file upload.

   @return the content_type in a HTTP file upload.
 */
rpl_str_t
00150 rpl_http_get_content_type() {
    rpl_str_t cnt_type = RPL_STR_NUL;
    size_t length;

    if(content_type != NULL) {
        length = strlen(content_type) + 1;
        cnt_type = (rpl_str_t)rpl_me_malloc(length);
        snprintf(cnt_type, length, "%s", content_type);
    }
    return cnt_type;
}

/**
   Returns the remote user (e.g., the login name used in HTTP Basic Authentication).

   @return the remote user.
 */
rpl_str_t
00168 rpl_http_get_remote_user() {
    rpl_str_t env_usr, r_usr = RPL_STR_NUL;
    size_t length;
    static rpl_str_t unknown = "Unknown";

    /* determine the allocation required to store r_usr value */
    env_usr = getenv("REMOTE_USER");
    length = (env_usr == NULL) ? strlen(unknown) + 1 : strlen(env_usr) + 1;
    r_usr = (rpl_str_t)rpl_me_malloc(length);

    /* populate the r_usr string */
    if(env_usr == NULL) {
        rpl_str_t msg = rpl_message_get("UNIDENTIFIED_USER",getenv("REMOTE_ADDR"), RPL_EOM);
        rpl_log_warn(msg);
        free(msg);
        snprintf(r_usr, length, "%s", unknown);
    } else {
        snprintf(r_usr, length, "%s", env_usr);
    }
    return r_usr;
}

/**
   Parses the query string and populates the form data structure.

   @param query_string
 */
/* use create_paramater etc. for the form data population. */
static void
00197 http_parse_query_string(rpl_str_t query_string) {
    rpl_str_t key_value_pair;
    /* initialise the head of the list */
    data.key = NULL;
    data.value = NULL;
    data.next = NULL;

    assert(query_string != NULL);

    /* tokenize the query string */
    key_value_pair = strtok(query_string, "&");
    while(key_value_pair != NULL) {
        /* insert form data into list */
        rpl_http_form_data *temp;
        temp = (rpl_http_form_data *)rpl_me_malloc(sizeof(rpl_http_form_data));
        rpl_str_split(key_value_pair, '=', &(temp->key), &(temp->value));
        temp->next = data.next;
        data.next = temp;

        /* acquire the next key/pair */
        key_value_pair = strtok(NULL, "&");
    }
}

/**
   Parses a multipart string and populates the form data structure.

   @param post_cnt
   @param bdy_param

   @return -1 on error, 0 on success.
 */
static int
00230 http_parse_multipart_string(rpl_str_t post_cnt, rpl_c_str_t bdy_param) {
    rpl_str_t fp, sp, ep;
    rpl_str_t bdy;
    size_t length;
    int filesize;
    int tmp_fd;
    FILE *tmp_fp;
    /* Temporary filename for mkstemp */
    char tmp_templ[] = "rapple_XXXXXX";

    assert((post_cnt != NULL) && (bdy_param != NULL));

    /* see RFC2046 for exact details of how multipart boundaries are defined */
    length = sizeof(RPL_HTTP_MTP_FORM_BDY_PREFIX) + sizeof(bdy_param) + 1;
    bdy = rpl_str_concat(RPL_HTTP_MTP_FORM_BDY_PREFIX, bdy_param, RPL_STR_EOC);

    /* initialise the head of the list */
    data.key = NULL;
    data.value = NULL;
    data.next = NULL;

    /* position the form pointer */
    fp=strstr(post_cnt, bdy_param);
    fp += strlen(RPL_HTTP_MTP_FORM_BDY_PREFIX) + strlen(bdy_param);

    do {
        rpl_str_t key, value;

        /* acquire the key attribute */
        if((sp = strstr(fp, RPL_HTTP_MTP_FORM_NAME)) == NULL) {
            return -1; /* handle error : no name ? */
        }
        sp += strlen(RPL_HTTP_MTP_FORM_NAME) + 1;
        if((ep = strchr(sp, '"')) == NULL) {
            return -1; /* no matching " ! */
        }
        *ep = '\0';
        key = sp;
        fp = ep + 1;
        /* capture the filename if required */
        if(strcmp(RPL_HTTP_FILE_UPLOAD_CONTROL_NAME, sp) == 0) {
            if((sp=strstr(fp, RPL_HTTP_MTP_FORM_FILENAME)) == NULL) {
                return -1; /* handle error */
            }
            sp += strlen(RPL_HTTP_MTP_FORM_FILENAME) + 1;
            if((ep = strchr(sp, '"')) == NULL) {
                return -1; /* no matching " ! */
            }
            *ep = '\0';
            fp = ep + 1;
            filename = sp;

            /* now capture the content type */
            if((sp=strstr(fp,RPL_HTTP_MTP_CONTENT_TYPE)) == NULL) {
                /* check spec - is the content type mandatory ? */
                return -1;
            }
            sp += strlen(RPL_HTTP_MTP_CONTENT_TYPE);
            ep = strchr(sp,'\n');
            ep--;
            *ep = '\0';
            fp = ep + 2;
            content_type = (rpl_str_t) rpl_me_malloc(strlen(sp) + 1);
            sprintf(content_type, "%s", sp);

            /* now capture the file itself and make a local copy */
            sp = fp;
            while((ep = strstr(sp, RPL_HTTP_HDR_CRLF)) == NULL)
                sp++;
            sp = ep + strlen(RPL_HTTP_HDR_CRLF);
            fp = sp;

            while((ep = strstr(fp, bdy)) == NULL)
                fp++;
            ep--;

            filesize = ep - sp;
            tmp_fd = mkstemp(tmp_templ);
            if (tmp_fd == -1) {
                rpl_log_fatal(rpl_message_get("FS_TEMP_FILE_FAILED", tmp_filename, RPL_EOM));
            }
            if((tmp_fp = fdopen(tmp_fd, "wb")) == NULL) {
                rpl_log_fatal(rpl_message_get("FS_TEMP_FILE_FAILED", tmp_filename, RPL_EOM));
            }
            fwrite(sp, filesize - 1, 1, tmp_fp);
            fclose(tmp_fp);

            /* record some sensible key ? */
            continue;
        }

        /* acquire the value attribute */
        if((sp = strstr(fp, RPL_HTTP_HDR_SEP)) == NULL) {
            return -1; /* unable to locate separators */
        }
        sp += strlen(RPL_HTTP_HDR_SEP);
        if((ep = strstr(sp, bdy)) == NULL) {
            return -1; /* no matching closing boundary */
        }
        *(ep--) = '\0';
        value = sp;
        fp = ep + 2;

        http_create_parameter(key, value);

    } while(fp != NULL);

    return 0;
}

/**
   Populates the form data structure with the key/value pair.

   @param key
   @param value
 */
static void
00347 http_create_parameter(rpl_c_str_t key, rpl_c_str_t value) {
    size_t klen, vlen;
    rpl_http_form_data *temp;

    assert((key != NULL) && (value != NULL));

    temp = (rpl_http_form_data *)rpl_me_malloc(sizeof(rpl_http_form_data));

    /* copy the key and value data */
    klen = strlen(key) + 1;
    temp->key = (rpl_str_t)rpl_me_malloc(klen);
    snprintf(temp->key, klen, "%s", key);
    vlen = strlen(value) + 1;
    temp->value= (rpl_str_t)rpl_me_malloc(vlen);
    snprintf(temp->value, vlen, "%s", value);

    /* TODO: has the protocol been incorrectly implemented ? */
    if((temp->value)[vlen - 3] == '\r')
        temp->value[vlen - 3] = '\0';

    temp->next = data.next;
    data.next = temp;
}

/**
   Controls which parsing function is to be called (GET,POST +/- MULTIPART).
 */
void
00375 rpl_http_parse_query() {
    long int cnt_length;
    rpl_str_t cnt_length_str, cnt_type;

    /* check the request method and delegate as appropriate */
    if(strcmp(getenv("REQUEST_METHOD"),"POST") == 0) {
        cnt_length_str = getenv("CONTENT_LENGTH");
        if((cnt_length_str == NULL) || (strlen(cnt_length_str) == 0) || (sscanf(cnt_length_str, "%ld", &cnt_length) !=
                1))
            rpl_log_fatal(rpl_message_get("PARSE_FORM_FAILED", RPL_EOM));

        cnt_type = getenv("CONTENT_TYPE");
        if(strstr(cnt_type, RPL_HTTP_MTP_FORM_CONTENT_TYPE) != NULL) {
            http_parse_multipart_query(cnt_length, strstr(cnt_type, RPL_HTTP_MTP_FORM_BDY) + strlen(RPL_HTTP_MTP_FORM_BDY));
        } else {
            http_parse_form_enc_query(cnt_length);
        }
    } else if(strcmp(getenv("REQUEST_METHOD"),"GET") == 0) {
        http_parse_get_query();
    } else {
        rpl_log_fatal(rpl_message_get("UNSUPPORTED_METHOD", getenv("REQUEST_METHOD"), RPL_EOM));
    }
}

/*
   http_parse_form_enc_query:
   parses a default (non-MULTIPART) HTTP POST request.
 */
static void
http_parse_form_enc_query(size_t cnt_length) {
    rpl_str_t post_cnt;
    size_t l_pos;

    assert(cnt_length > 0); /* was >=0 but default type is unsigned so always true */

    /* +2 is required for the terminating character and the newline that fgets() sometimes stores */
    post_cnt = (rpl_str_t)rpl_me_malloc(cnt_length + 2);
    /* note: CGI does not stipulate use of the feof facility so cnt_length bytes must be read */
    if(fgets(post_cnt, cnt_length + 1, stdin) == NULL)
        rpl_log_fatal(rpl_message_get("PARSE_FORM_FAILED", RPL_EOM));

    /* if fgets() stores the terminating newline in the buffer then it is overwritten */
    l_pos = strlen(post_cnt) -1;
    if(post_cnt[l_pos] == '\n')
        post_cnt[l_pos] = '\0';

    http_parse_query_string(post_cnt);
}

/**
   Parses HTTP POST (MULTIPART) request (used for file uploads).

   @param cnt_length
   @param boundary
 */
static void
00431 http_parse_multipart_query(size_t cnt_length, rpl_c_str_t boundary) {
    rpl_str_t post_cnt;
    int l_pos;

    /* was >=0 but default type is unsigned so always true */
    assert((cnt_length > 0) && (boundary != NULL));

    /* +2 is required for the terminating character and the newline that fgets() sometimes stores */
    post_cnt = (rpl_str_t)rpl_me_malloc(cnt_length + 2);
    /* note: CGI does not stipulate use of the feof facility so cnt_length bytes must be read */
    if(fread(post_cnt, 1, cnt_length + 1, stdin) != cnt_length)
        rpl_log_fatal(rpl_message_get("PARSE_FORM_FAILED", RPL_EOM));


    /* if fgets() stores the terminating newline in the buffer then it is overwritten */
    l_pos = strlen(post_cnt) -1;
    if(post_cnt[l_pos] == '\n')
        post_cnt[l_pos] = '\0';

    http_parse_multipart_string(post_cnt, boundary);

}

/**
   Parses HTTP GET query.
 */
static void
00458 http_parse_get_query() {
    rpl_str_t query_str;

    if((query_str = getenv("QUERY_STRING")) != NULL)
        http_parse_query_string(query_str);
}


Generated by  Doxygen 1.6.0   Back to index