Exemple #1
0
static void string_decoding_in_place(dAT)
{
    char *s1 = apr_palloc(p,4096);
    char *s2 = apr_palloc(p,4096);
    char *s3;

    strcpy(s1, "bend it like beckham");
    strcpy(s2, "dandy %3Edons");

    AT_str_eq(s1,"bend it like beckham");
    apreq_unescape(s1);
    AT_str_eq(s1, "bend it like beckham");
    s3 = apreq_escape(p, s1, 20);
    AT_str_eq(s3, "bend+it+like+beckham");
    apreq_unescape(s3);
    AT_str_eq(s3,"bend it like beckham");

    AT_str_eq(s2,"dandy %3Edons");
    apreq_unescape(s2);
    AT_str_eq(s2,"dandy >dons");
    s3 = apreq_escape(p, s2, 11);
    AT_str_eq(s3,"dandy+%3Edons");
    apreq_unescape(s3);
    AT_str_eq(s3,"dandy >dons");
}
Exemple #2
0
// Handles a simple parameter value, simply concats the name and value to the output 
// stream
apr_status_t porter_handle_parameter(porter_upload_request_t *ur, apreq_param_t *param)
{
  const char *new_parameter_value;
  apr_status_t rv;

  new_parameter_value = apr_pstrcat(ur->pool, apreq_escape(ur->pool, param->v.name, strlen(param->v.name)), "=",
                                              apreq_escape(ur->pool, param->v.data, strlen(param->v.data)), "&",
                                              NULL);
  PORTER_LOG("Writing plain parameter");
  PORTER_LOG(param->v.name);
  PORTER_LOG(new_parameter_value);

  PORTER_HANDLE_ERROR(apr_brigade_write(ur->bucket_brigade, NULL, NULL,
                                    new_parameter_value, strlen(new_parameter_value)));
  return APR_SUCCESS;
}
Exemple #3
0
// Handles an upload parameter.  If the user didn't select a file, it calls the
// regular porter_handle_parameter function.  For uploads it appends several
// sub-parameters:
//
// * filename:     File name that the user has uploaded.
// * content_type: The content type the browser has provided (sometimes not present).
// * path:         The location of the tempfile where the contents were copied to.
// * signature:    A base64 encoded SHA1 hash of the filename and the PorterSharedSecret.
apr_status_t porter_handle_upload(porter_upload_request_t *ur, apreq_param_t *p)
{
  const char *content_disposition;
  const char *file_name;
  const char *content_type;
  const char *signature;

  apr_size_t size;
  apr_status_t rv;
  apr_finfo_t finfo;

  apr_pool_t *pool  = ur->pool;
  char *escaped_key = apreq_escape(pool, p->v.name, strlen(p->v.name));
  apr_table_t *info = p->info;
  
  PORTER_LOG("Handling Upload");
  PORTER_LOG(p->v.name);

  porter_server_conf *config = (porter_server_conf *)ap_get_module_config(ur->raw_request->server->module_config, &porter_module);

  content_disposition = apr_table_get(info, "content-disposition");
  apreq_header_attribute(content_disposition, "filename", 8, &file_name, &size);

  if (size == 0)
  {
    // There was no file, or at least it had no name, so let's
    // just skip it.
    PORTER_LOG("Appears there was no file, skipping the parameter");
    return APR_SUCCESS;
  }
  PORTER_LOG("Appears there was a file, continuing");
  

  // We know we have a file so push the param name into the array
  // of parameter names so it can be added to the request header.
  *(const char**)apr_array_push(ur->param_names) = p->v.name;

  PORTER_HANDLE_ERROR(porter_append_sub_parameter(pool, ur->bucket_brigade, escaped_key, "filename", file_name, size));

  // content type is optional, and safari doesn't send it if it's unsure.
  content_type = apr_table_get(info, "content-type");
  if (content_type)
  {
    PORTER_HANDLE_ERROR(porter_append_sub_parameter(pool, ur->bucket_brigade, escaped_key, "content_type", content_type, strlen(content_type)));
  }

  // Write the actual upload to disk
  PORTER_HANDLE_ERROR(porter_stream_file_to_disk(pool, p, &finfo, config->directory));

  // Set appropriate tempfile permissions
  PORTER_HANDLE_ERROR(apr_file_perms_set(finfo.fname, config->permission));

  PORTER_HANDLE_ERROR(porter_append_sub_parameter(pool, ur->bucket_brigade, escaped_key, "path", finfo.fname, strlen(finfo.fname)));

  signature = porter_sign_filename(ur, &finfo);

  PORTER_HANDLE_ERROR(porter_append_sub_parameter(pool, ur->bucket_brigade, escaped_key, "signature", signature, strlen(signature)));
  return APR_SUCCESS;
}
// If 'redirect' is foo/bar, then redirect to it.  If it is
// foo/bar/%s, then replace the %s with r->uri.
static void compose_and_set_redirect(request_rec *r, const char* redirect) {
	char* composed_redirect = NULL;
        char* encoded_uri = NULL;
	if (ap_strstr_c(redirect, "%s")) {
                encoded_uri = apreq_escape(r->pool, r->unparsed_uri, strlen(r->unparsed_uri));
		composed_redirect = apr_psprintf(r->pool, redirect, r->unparsed_uri);
 	}
        apr_table_setn(r->headers_out, "Location", composed_redirect ? composed_redirect : redirect);
}
Exemple #5
0
// Appends the sub parameter to the bucket brigade. Sub parameters are given
// 'rails style' with parent_param[sub_param_name]=sub_param_value
apr_status_t porter_append_sub_parameter(apr_pool_t *pool, apr_bucket_brigade *bb,
                                     const char *parent_param,
                                     const char *sub_param,
                                     const char *sub_param_value,
                                     apr_size_t length)
{
  const char *escaped_value = apreq_escape(pool, sub_param_value, length);
  const char *encoded_value = apr_pstrcat(pool, parent_param, "%5B", sub_param, "%5D=", escaped_value, "&", NULL);
  PORTER_LOG("appending sub param");
  PORTER_LOG(sub_param);
  PORTER_LOG(escaped_value);
  return apr_brigade_write(bb, NULL, NULL, encoded_value, strlen(encoded_value));
}
// See here for the structure of request_rec:
// http://ci.apache.org/projects/httpd/trunk/doxygen/structrequest__rec.html
static int hook(request_rec *r)
{
    settings_rec *cfg = ap_get_module_config( r->per_dir_config,
                                              &querystring2cookie_module );

    /* Do not run in subrequests, don't run if not enabled */
    if( !(cfg->enabled || r->main) ) {
        return DECLINED;
    }

    /* No query string? nothing to do here */
    if( !(r->args) || strlen( r->args ) < 1 ) {
        return DECLINED;
    }

    /* skip if dnt headers are present? */
    if( !(cfg->enabled_if_dnt) && apr_table_get( r->headers_in, "DNT" ) ) {
        _DEBUG && fprintf( stderr, "DNT header sent: declined\n" );
        return DECLINED;
    }

    _DEBUG && fprintf( stderr, "Query string: '%s'\n", r->args );

    // ***********************************
    // Calculate expiry time
    // ***********************************

    // The expiry time. We can't use max-age because IE6 - IE8 do not
    // support it :(
    char *expires = "";

    if( cfg->cookie_expires > 0 ) {

        apr_time_exp_t tms;
        apr_time_exp_gmt( &tms, r->request_time
                              + apr_time_from_sec( cfg->cookie_expires ) );

        expires = apr_psprintf( r->pool,
                            "expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
                            apr_day_snames[tms.tm_wday],
                            tms.tm_mday,
                            apr_month_snames[tms.tm_mon],
                            tms.tm_year % 100,
                            tms.tm_hour, tms.tm_min, tms.tm_sec
                        );
    }

    // ***********************************
    // Find key/value pairs
    // ***********************************

    // keep track of how much data we've been writing - there's a limit to how
    // much a browser will store per domain (usually 4k) so we want to make sure
    // it's not getting flooded.
    int total_pair_size = 0;

    // This holds the final cookie we'll send back - make sure to initialize
    // or it can point at garbage!
    char *cookie = "";

    // string to use as the cookie name (together with the prefix) - make sure to
    // initialize or it can point at garbage!
    char *cookie_name = "";

    // Iterate over the key/value pairs
    char *last_pair;
    char *pair = apr_strtok( apr_pstrdup( r->pool, r->args ), "&", &last_pair );

    _DEBUG && fprintf( stderr, "about to parse query string for pairs\n" );

    _DEBUG && fprintf( stderr, "looking for cookie name in %s\n", cfg->cookie_name_from );

    while( pair != NULL ) {

        // length of the substr before the = sign (or index of the = sign)
        int contains_equals_at = strcspn( pair, "=" );

        // Does not contains a =, or starts with a =, meaning it's garbage
        if( !strstr(pair, "=") || contains_equals_at < 1 ) {
            _DEBUG && fprintf( stderr, "invalid pair: %s\n", pair );

            // And get the next pair -- has to be done at every break
            pair = apr_strtok( NULL, "&", &last_pair );
            continue;
        }

        _DEBUG && fprintf( stderr, "pair looks valid: %s - = sign at pos: %i\n",
                            pair, contains_equals_at );

        // So this IS a key value pair. Let's get the key and the value.
        // first, get the key - everything up to the first =
        char *key   = apr_pstrndup( r->pool, pair, contains_equals_at );

        // now get the value, everything AFTER the = sign. We do that by
        // moving the pointer past the = sign.
        char *value = apr_pstrdup( r->pool, pair );
        value += contains_equals_at + 1;

        _DEBUG && fprintf( stderr, "pair=%s, key=%s, value=%s\n", pair, key, value );

        // you want us to use a name from the query string?
        // This might be that name.
        if( cfg->cookie_name_from && !(strlen(cookie_name)) &&
            strcasecmp( key, cfg->cookie_name_from ) == 0
        ) {
            // get everything after the = sign -- that's our name.
            cookie_name = apr_pstrcat( r->pool, cfg->cookie_prefix, value, NULL );

            _DEBUG && fprintf( stderr, "using %s as the cookie name\n", cookie_name );

            // And get the next pair -- has to be done at every break
            pair = apr_strtok( NULL, "&", &last_pair );
            continue;

        // may be on the ignore list
        } else {

            // may have to continue the outer loop, use this as a marker
            int do_continue = 0;

            // you might have blacklisted this key; let's check
            // Following tutorial code here again:
            // http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-19.html
            int i;
            for( i = 0; i < cfg->qs_ignore->nelts; i++ ) {

                char *ignore = ((char **)cfg->qs_ignore->elts)[i];

                _DEBUG && fprintf( stderr, "processing ignore %s against pair %s\n",
                                        ignore, pair );

                // it's indeed on the ignore list; move on
                // do this by comparing the string length first - if the length of
                // the ignore key and the key are identical AND the first N characters
                // of the string are the same
                if( strcasecmp( key, ignore ) == 0 ) {
                    _DEBUG && fprintf( stderr, "pair %s is on the ignore list: %s\n",
                                        pair, ignore );

                    // signal to continue the outer loop; we found an ignore match
                    do_continue = 1;
                    break;
                }
            }

            // ignore match found, move on
            if( do_continue ) {
                // And get the next pair -- has to be done at every break
                pair = apr_strtok( NULL, "&", &last_pair );
                continue;
            }
        }

        // looks like a valid key=value declaration
        _DEBUG && fprintf( stderr, "valid key/value pair: %s\n", pair );

        // Now, the key may contain URL unsafe characters, which are also
        // not allowed in Cookies. See here:
        // http://tools.ietf.org/html/rfc2068, section 2.2 on 'tspecials'
        //
        // So instead, we url encode the key. The size of the key is max
        // 3 times old key size (any char gets encoded into %xx), so allow
        // for that space. See the documentation here:
        // http://httpd.apache.org/apreq/docs/libapreq2/apreq__util_8h.html#785be2ceae273b0a7b2ffda223b2ebae
        char *escaped_key   = apreq_escape( r->pool, key, strlen(key) );
        char *escaped_value = apreq_escape( r->pool, value, strlen(value) );

        _DEBUG && fprintf( stderr, "Original key: %s - Escaped key: %s\n", key, escaped_key );
        _DEBUG && fprintf( stderr, "Original value: %s - Escaped value: %s\n", value, escaped_value );

        // Now, let's do some transposing: The '=' sign needs to be replaced
        // with whatever the separator is. It can't be a '=' sign, as that's
        // illegal in cookies. The string may be larger than a single char,
        // so split the string and do the magix.

        // This makes key[delim]value - redefining pair here is safe, we're
        // just using it for printing now.
        char *key_value = apr_pstrcat( r->pool,
                                       escaped_key,
                                       cfg->cookie_key_value_delimiter,
                                       escaped_value,
                                       NULL
                                    );

        int this_pair_size = strlen( key_value );

        // Make sure the individual pair, as well as the whole thing doesn't
        // get too long

        _DEBUG && fprintf( stderr,
                "this pair size: %i, total pair size: %i, max size: %i\n",
                this_pair_size, total_pair_size, cfg->cookie_max_size  );

        if( (this_pair_size <= cfg->cookie_max_size) &&
            (total_pair_size + this_pair_size <= cfg->cookie_max_size)
        ) {

            cookie = apr_pstrcat( r->pool,
                                  cookie,       // the cookie so far
                                  // If we already have pairs in here, we need the
                                  // delimiter, otherwise we don't.
                                  (strlen(cookie) ? cfg->cookie_pair_delimiter : ""),
                                  key_value,    // the next pair.
                                  NULL
                            );

            // update the book keeping - this is the new size including delims
            total_pair_size = strlen(cookie);

            _DEBUG && fprintf( stderr, "this pair size: %i, total pair size: %i\n",
                                    this_pair_size, total_pair_size );

        } else {
            _DEBUG && fprintf( stderr,
                "Pair size too long to add: %s (this: %i total: %i max: %i)\n",
                key_value, this_pair_size, total_pair_size, cfg->cookie_max_size );
        }

        // and move the pointer
        pair = apr_strtok( NULL, "&", &last_pair );
    }

     // So you told us we should use a cookie name from the query string,
     // but we never found it in there. That's a problem.
     if( cfg->cookie_name_from && !strlen(cookie_name) ) {

         // r->err_headers_out also honors non-2xx responses and
         // internal redirects. See the patch here:
         // http://svn.apache.org/viewvc?view=revision&revision=1154620
         apr_table_addn( r->err_headers_out,
             "X-QS2Cookie",
             apr_pstrcat( r->pool,
                 "ERROR: Did not detect cookie name - missing QS argument: ",
                 cfg->cookie_name_from,
                 NULL
             )
         );

     // Let's return the output
     } else {

         // we got here without a cookie name? We can use the default.
         if( !strlen(cookie_name) ) {
             _DEBUG && fprintf( stderr, "explicitly setting cookie name to: %s\n",
                                         cfg->cookie_name );

             cookie_name = apr_pstrcat( r->pool,
                                        cfg->cookie_prefix, cfg->cookie_name,
                                        NULL );
         }

         _DEBUG && fprintf( stderr, "cookie name: %s\n", cookie_name );

        // XXX use a sprintf format for more flexibility?
        if( cfg->encode_in_key ) {
            _DEBUG && fprintf( stderr, "%s: encoding in the key\n", cookie_name );

            cookie = apr_pstrcat( r->pool,
                            // cookie data
                            cookie_name, cfg->cookie_pair_delimiter, cookie, "=",
                            // The format is different on 32 (%ld) vs 64bit (%lld), so
                            // use the constant for it instead. You can find this in apr.h
                            apr_psprintf( r->pool, "%" APR_OFF_T_FMT, apr_time_sec(apr_time_now()) ),
                            NULL
                         );
        } else {
            _DEBUG && fprintf( stderr, "%s: encoding in the value\n", cookie_name );
            cookie = apr_pstrcat( r->pool, cookie_name, "=", cookie, NULL );

        }

        // And now add the meta data to the cookie
        cookie = apr_pstrcat( r->pool, cookie, "; ",
                                "path=/; ", cfg->cookie_domain, expires,
                                NULL );

        _DEBUG && fprintf( stderr, "cookie: %s\n", cookie );

        // r->err_headers_out also honors non-2xx responses and
        // internal redirects. See the patch here:
        // http://svn.apache.org/viewvc?view=revision&revision=1154620
        apr_table_addn( r->err_headers_out, "Set-Cookie", cookie );
    }

    return OK;
}