Main Page | Modules | File List | Globals

request.c

00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: request.c,v 1.55 2008/04/25 19:27:55 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <ctype.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <u/libu.h>
00018 #include <klone/request.h>
00019 #include <klone/utils.h>
00020 #include <klone/io.h>
00021 #include <klone/ioprv.h>
00022 #include <klone/http.h>
00023 #include <klone/addr.h>
00024 #include <klone/vars.h>
00025 #include <klone/timer.h>
00026 #include <klone/vhost.h>
00027 
00028 struct request_s
00029 {
00030     http_t *http;               /* http server handle                       */
00031     header_t *header;           /* input header                             */
00032     io_t *io;                   /* input io stream                          */
00033     int method;                 /* get,post,etc.                            */
00034     char *cli_rq;               /* verbatim client request line             */
00035     char *uri;                  /* verbatim uri asked by the client         */
00036     char *protocol;             /* proto/ver                                */
00037     char *path_info;            /* extra info at the end of the path        */
00038     char *query;                /* query string (data after '?')            */
00039     char *filename;             /* path of the req resource                 */
00040     char *resolved_path_info;   /* resolved path_info                       */
00041     char *resolved_filename;    /* unaliased filename                       */
00042     vars_t *args;               /* mixed get/post args                      */
00043     vars_t *args_get;           /* get variables                            */
00044     vars_t *args_post;          /* post variables                           */
00045     vars_t *cookies;            /* cookies                                  */
00046     vars_t *uploads;            /* uploaded file list                       */
00047     char *content_type;         /* type/subtype                             */
00048     char *content_encoding;     /* 7bit/8bit/base64/qp, etc                 */
00049         size_t content_length;      /* content-length http header field         */
00050     time_t if_modified_since;   /* time_t IMS header                        */
00051     addr_t local_addr, peer_addr; /* local and perr address                 */
00052     int cgi;                    /* if running in cgi mode                   */
00053     size_t idle_timeout;        /* max # of secs to wait for the request    */
00054     size_t post_timeout;        /* max # of secs for reading POSTed data    */
00055     size_t post_maxsize;        /* max # of POSTed bytes to accepts         */
00056     vhost_t *vhost;             /* cached vhost pointer                     */
00057     size_t padding;
00058 };
00059 
00060 typedef struct upload_info_s    /* uploaded file info struct         */
00061 {
00062     char mime_type[MIME_TYPE_BUFSZ];
00063     char filename[U_FILENAME_MAX];
00064     size_t size;
00065 } upload_info_t;
00066 
00067 enum { 
00068     REQUEST_DEFAULT_IDLE_TIMEOUT = 10,         /* 10 secs */
00069     REQUEST_DEFAULT_POST_TIMEOUT = 600,        /* 10 mins */
00070     REQUEST_DEFAULT_POST_MAXSIZE = 5*1024000,  /* 5 MB    */
00071 };
00072 
00073 
00074 #define REQUEST_SET_STRING_FIELD(lval, rval)        \
00075     do {                                            \
00076         U_FREE(lval);                               \
00077         if(rval)                                    \
00078         {                                           \
00079             lval = u_strdup(rval);                  \
00080             dbg_err_if(lval == NULL);               \
00081         }                                           \
00082     } while(0)
00083 
00084 
00085 int request_is_encoding_accepted(request_t *rq, const char *encoding)
00086 {
00087     char *pp, *tok, *src, *buf = NULL;
00088     const char *accept_encoding;
00089     int rc = 0;
00090 
00091     dbg_err_if (rq == NULL);
00092     dbg_err_if (encoding == NULL);
00093     
00094     accept_encoding = header_get_field_value(rq->header, "Accept-Encoding");
00095     if(accept_encoding)
00096     {
00097         /* get a copy to work on */
00098         buf = u_strdup(accept_encoding);
00099         dbg_err_if(buf == NULL);
00100 
00101         /* foreach encoding pair... */
00102         for(src = buf; (tok = strtok_r(src, " ,", &pp)) != NULL; src = NULL)
00103         {
00104             if(strcasecmp(tok, encoding) == 0)
00105             {
00106                 rc++; /* found */
00107                 break;
00108             }
00109         }
00110 
00111         U_FREE(buf);
00112     }
00113 
00114     return rc;
00115 err:
00116     U_FREE(buf);
00117     return 0;
00118 }
00119 
00135 io_t *request_io(request_t *rq)
00136 {
00137     dbg_return_if (rq == NULL, NULL);
00138 
00139     return rq->io;
00140 }
00141 
00153 vars_t *request_get_cookies(request_t *rq)
00154 {
00155     dbg_return_if (rq == NULL, NULL);
00156 
00157     return rq->cookies;
00158 }
00159 
00171 const char *request_get_cookie(request_t *rq, const char *name)
00172 {
00173     var_t *v;
00174 
00175     dbg_return_if (rq == NULL, NULL);
00176     dbg_return_if (name == NULL, NULL);
00177 
00178     v = vars_get(rq->cookies, name);
00179 
00180     return v ? var_get_value(v): NULL;
00181 }
00182 
00193 vars_t *request_get_args(request_t *rq)
00194 {
00195     dbg_return_if (rq == NULL, NULL);
00196 
00197     return rq->args;
00198 }
00199 
00210 vars_t *request_get_getargs(request_t *rq)
00211 {
00212     dbg_return_if (rq == NULL, NULL);
00213 
00214     return rq->args_get;
00215 }
00216 
00227 vars_t *request_get_postargs(request_t *rq)
00228 {
00229     dbg_return_if (rq == NULL, NULL);
00230 
00231     return rq->args_post;
00232 }
00233 
00247 const char *request_get_arg(request_t *rq, const char *name)
00248 {
00249     var_t *v;
00250 
00251     dbg_return_if (rq == NULL, NULL);
00252     dbg_return_if (name == NULL, NULL);
00253 
00254     v = vars_get(rq->args, name);
00255 
00256     return v ? var_get_value(v): NULL;
00257 }
00258 
00272 const char *request_get_getarg(request_t *rq, const char *name)
00273 {
00274     var_t *v;
00275 
00276     dbg_return_if (rq == NULL, NULL);
00277     dbg_return_if (name == NULL, NULL);
00278 
00279     v = vars_get(rq->args_get, name);
00280 
00281     return v ? var_get_value(v): NULL;
00282 }
00283 
00297 const char *request_get_postarg(request_t *rq, const char *name)
00298 {
00299     var_t *v;
00300 
00301     dbg_return_if (rq == NULL, NULL);
00302     dbg_return_if (name == NULL, NULL);
00303 
00304     v = vars_get(rq->args_post, name);
00305 
00306     return v ? var_get_value(v): NULL;
00307 }
00308 
00309 int request_set_field(request_t *rq, const char *name, const char *value)
00310 {
00311     dbg_return_if (rq == NULL, ~0);
00312     dbg_return_if (name == NULL, ~0);
00313     dbg_return_if (value == NULL, ~0);
00314 
00315     return header_set_field(rq->header, name, value);
00316 }
00317 
00328 const char *request_get_uri(request_t *rq)
00329 {
00330     dbg_return_if (rq == NULL, NULL);
00331 
00332     return rq->uri;
00333 }
00334 
00345 const char *request_get_filename(request_t *rq)
00346 {
00347     dbg_return_if (rq == NULL, NULL);
00348 
00349     return rq->filename;
00350 }
00351 
00352 /*
00353  * \ingroup request
00354  * \brief   Set the filename field of a request
00355  *
00356  * Set the filename field of request \p rq to \p filename.
00357  *
00358  * \param rq        request object
00359  * \param filename  filename string
00360  *
00361  * \return \c 0 if successful, non-zero on error
00362  */
00363 int request_set_filename(request_t *rq, const char *filename)
00364 {
00365     dbg_err_if (rq == NULL);
00366     dbg_err_if (filename == NULL);
00367     
00368     REQUEST_SET_STRING_FIELD(rq->filename, filename);
00369 
00370     return 0;
00371 err:
00372     return ~0;
00373 }
00374 
00385 const char *request_get_query_string(request_t *rq)
00386 {
00387     dbg_return_if (rq == NULL, NULL);
00388 
00389     return rq->query;
00390 }
00391 
00402 const char *request_get_path_info(request_t *rq)
00403 {
00404     dbg_return_if (rq == NULL, NULL);
00405 
00406     return rq->path_info;
00407 }
00408 
00409 /* parse and set if-modified-since value */
00410 static int request_parse_ims(request_t *rq)
00411 {
00412     const char *ims;
00413 
00414     dbg_err_if (rq == NULL);
00415     
00416     rq->if_modified_since = 0;
00417 
00418     ims = header_get_field_value(rq->header, "If-Modified-Since");
00419     if(ims)
00420         dbg_err_if(u_httpdate_to_tt(ims, &rq->if_modified_since));
00421 
00422 err: /* ignore if it's not formatted properly */
00423     return 0;
00424 }
00425 
00436 time_t request_get_if_modified_since(request_t *rq)
00437 {
00438     dbg_return_if (rq == NULL, (time_t) -1);
00439 
00440     return rq->if_modified_since;
00441 }
00442 
00443 /*
00444  * \ingroup request
00445  * \brief   Set the resolved filename field of a request
00446  *
00447  * Set the resolved filename field of request \p rq to \p resolved_fn
00448  *
00449  * \param rq          request object
00450  * \param resolved_fn resolved filename
00451  *
00452  * \return \c 0 if successful, non-zero on error
00453  */
00454 int request_set_resolved_filename(request_t *rq, const char *resolved_fn)
00455 {
00456     dbg_err_if (rq == NULL);
00457     dbg_err_if (resolved_fn == NULL);
00458 
00459     REQUEST_SET_STRING_FIELD(rq->resolved_filename, resolved_fn);
00460 
00461     return 0;
00462 err:
00463     return ~0;
00464 }
00465 
00476 http_t* request_get_http(request_t *rq)
00477 {
00478     dbg_return_if (rq == NULL, NULL);
00479 
00480     return rq->http;
00481 }
00482 
00483 /*
00484  * \ingroup request
00485  * \brief   Bind request I/O to a given I/O. 
00486  *  
00487  * Bind the I/O of request \p rq to \p in.
00488  *
00489  * \param rq    request object
00490  * \param in    input I/O object
00491  *
00492  * \return \c 0 if successful, non-zero on error
00493  */
00494 int request_bind(request_t *rq, io_t *in)
00495 {
00496     dbg_return_if (rq == NULL, ~0);
00497     dbg_return_if (in == NULL, ~0);
00498 
00499     rq->io = in;
00500 
00501     return 0;
00502 }
00503 
00504 /*
00505  * \ingroup request
00506  * \brief   Set the query string of a request
00507  *
00508  * Parse \p query string and build the \p rq->args list.
00509  *
00510  * \param rq     request object
00511  * \param query  query string 
00512  *
00513  * \return \c 0 if successful, non-zero on error
00514  */
00515 int request_set_query_string(request_t *rq, const char *query)
00516 {
00517     dbg_err_if (rq == NULL);
00518     dbg_err_if (query == NULL);
00519     
00520     REQUEST_SET_STRING_FIELD(rq->query, query);
00521 
00522     return 0;
00523 err:
00524     return ~0;
00525 }
00526 
00527 void request_clear_uri(request_t *rq)
00528 {
00529     U_FREE(rq->uri);
00530     U_FREE(rq->protocol);
00531     U_FREE(rq->path_info);
00532     U_FREE(rq->query);
00533     U_FREE(rq->filename);
00534     U_FREE(rq->resolved_path_info);
00535     U_FREE(rq->resolved_filename);
00536     U_FREE(rq->content_type);
00537     U_FREE(rq->content_encoding);
00538     U_FREE(rq->cli_rq);
00539 }
00540 
00541 /*
00542  * \ingroup request
00543  * \brief   Set the path info field of a request
00544  *
00545  * Set the path info field of request \p rq to \p path_info.
00546  *
00547  * \param rq         request object
00548  * \param path_info  path info
00549  *
00550  * \return \c 0 if successful, non-zero on error
00551  */
00552 int request_set_path_info(request_t *rq, const char *path_info)
00553 {
00554     dbg_err_if (rq == NULL);
00555     dbg_err_if (path_info == NULL);
00556 
00557     REQUEST_SET_STRING_FIELD(rq->path_info, path_info);
00558 
00559     return 0;
00560 err:
00561     return ~0;
00562 }
00563 
00564 /*
00565  * \ingroup request
00566  * \brief   Set the resolved path info field of a request
00567  *
00568  * Set the resolved path info field of request \p rq to \p resolved_pi.
00569  *
00570  * \param rq           request object
00571  * \param resolved_pi  resolved path info
00572  *
00573  * \return \c 0 if successful, non-zero on error
00574  */
00575 int request_set_resolved_path_info(request_t *rq, const char *resolved_pi)
00576 {
00577     dbg_err_if (rq == NULL);
00578     dbg_err_if (resolved_pi == NULL);
00579 
00580     REQUEST_SET_STRING_FIELD(rq->resolved_path_info, resolved_pi);
00581 
00582     return 0;
00583 err:
00584     return ~0;
00585 }
00586 
00587 /*
00588  * \ingroup request
00589  * \brief   Set the URI field of a request
00590  *
00591  * Set the URI field of request \p rq to \p uri given 
00592  *
00593  * \param rq           request object
00594  * \param uri          URI string
00595  * \param is_valid_uri URI validation function 
00596  * \param arg          argument to is_valid_uri
00597  *
00598  * \return \c 0 if successful, non-zero on error
00599  */
00600 int request_set_uri(request_t *rq, const char *uri,
00601         int (*is_valid_uri)(void*, const char *, size_t),
00602         void* arg)
00603 {
00604     char *p, *fn, *pi;
00605     size_t uri_len = strlen(uri);
00606     char cp[4096];
00607 
00608     dbg_err_if (rq == NULL);
00609     dbg_err_if (uri == NULL);
00610     /* is_valid_uri may be NULL */
00611     /* arg may be NULL */
00612  
00613     request_clear_uri(rq);
00614 
00615     /* this is just to avoid recursive infinite redirect loops for pages that 
00616        appends something to the URI and redirects to the same page */
00617     warn_err_ifm(uri_len >= sizeof(cp), "Request URI too long");
00618 
00619     REQUEST_SET_STRING_FIELD(rq->uri, uri);
00620 
00621     /* save (undecoded) query string i.e. everything after '?' */
00622     if((p = strchr(uri, '?')) != NULL)
00623         dbg_err_if(request_set_query_string(rq, ++p));
00624 
00625     /* copy decoded url */
00626     dbg_err_if(u_urlncpy(cp, rq->uri, uri_len, URLCPY_DECODE) <= 0);
00627 
00628     if((p = strchr(cp, '?')) != NULL)
00629         *p++ = 0; /* remove query string from the uri copy */
00630 
00631     /* normalize the URI (remove /../, /./, ecc) */
00632     dbg_err_if(u_uri_normalize(cp));
00633 
00634     /* set filename is case there's not path_info and/or file does not exists */
00635     dbg_err_if(request_set_filename(rq, cp));
00636 
00637     /* look for path_info */
00638     fn = cp;                    /* filename     */
00639     pi = fn + strlen(fn);       /* path_info    */
00640     for(;;)
00641     {
00642         if(is_valid_uri == NULL || is_valid_uri(arg, fn, pi - fn))
00643         {
00644             dbg_err_if(request_set_filename(rq, fn));
00645             rq->filename[pi-fn] = 0; /* trunc */
00646             if(strlen(pi))
00647                 dbg_err_if(request_set_path_info(rq, pi));
00648             break;
00649         } else {
00650             if((p = u_strnrchr(fn, '/', pi - fn)) == NULL)
00651                 break; /* file pointed by this uri does not exists */
00652             pi = p; /* try again */
00653         }
00654     }
00655 
00656     return 0;
00657 err:
00658     return ~0;
00659 }
00660 
00661 static int request_set_proto(request_t *rq, const char *proto)
00662 {
00663     dbg_err_if (rq == NULL);
00664     dbg_err_if (proto == NULL);
00665 
00666     /* be sure that the requested protocol is http */
00667     if(strncasecmp(proto, "http", 4))
00668         return ~0; /* unknown or unsupported protocol */
00669 
00670     REQUEST_SET_STRING_FIELD(rq->protocol, proto);
00671 
00672     return 0;
00673 err:
00674     return ~0;
00675 }
00676 
00677 /*
00678  * \ingroup request
00679  * \brief   Save client request
00680  *
00681  * Save client request line
00682  *
00683  * \param rq     request object
00684  * \param ln     the request line
00685  *
00686  * \return \c 0 if successful, non-zero on error
00687  */
00688 int request_set_client_request(request_t *rq, const char *ln)
00689 {
00690     char *p;
00691     dbg_err_if(rq == NULL);
00692     dbg_err_if(ln == NULL);
00693 
00694     rq->cli_rq = u_strdup(ln);
00695     dbg_err_if(rq->cli_rq == NULL);
00696 
00697     /* cut the trailing newline if any */
00698     for(p = rq->cli_rq; *p && (*p != '\r' && *p != '\n'); ++p)
00699         continue;
00700     *p = 0;
00701 
00702     return 0;
00703 err:
00704     return ~0;
00705 }
00706 
00717 const char *request_get_client_request(request_t *rq)
00718 {
00719     return rq->cli_rq;
00720 }
00721 
00722 /*
00723  * Set the \p method of request \p rq.  Refer to http.h for possible methods.
00724  *
00725  * \param rq     request object
00726  * \param method the HTTP method 
00727  *
00728  * \return \c 0 if successful, non-zero on error
00729  */
00730 int request_set_method(request_t *rq, const char *method)
00731 {
00732     dbg_return_if (rq == NULL, ~0);
00733     dbg_return_if (method == NULL, ~0);
00734 
00735     if(!strcasecmp(method, "get"))
00736         rq->method = HM_GET;
00737     else if(!strcasecmp(method, "head"))
00738         rq->method = HM_HEAD;
00739     else if(!strcasecmp(method, "post"))
00740         rq->method = HM_POST;
00741     else {
00742         /* put, delete, * */
00743         rq->method = HM_UNKNOWN;
00744         return ~0; /* unknown or unsupported method */
00745     }
00746     
00747     return 0;
00748 }
00749 
00750 static int request_set_content_length(request_t *rq)
00751 {
00752     const char *clen;
00753     size_t len;
00754 
00755     dbg_err_if (rq == NULL);
00756 
00757     clen = header_get_field_value(rq->header, "Content-Length");
00758     dbg_err_if(clen == NULL || (len = atoi(clen)) < 0);
00759 
00760     rq->content_length = len;
00761 
00762     return 0;
00763 err:
00764     return ~0;
00765 }
00766 
00767 static int request_parse_cookie(request_t *rq, field_t *field)
00768 {
00769     enum { BUFSZ = 4096 }; /* cookie size limit */
00770     char *pp, *tok, *src, buf[BUFSZ];
00771 
00772     dbg_err_if (rq == NULL);
00773     dbg_err_if (field == NULL);
00774     
00775     dbg_err_if(field_get_value(field) == NULL);
00776 
00777     /* save a copy to tokenize it */
00778     strncpy(buf, field_get_value(field), BUFSZ);
00779 
00780     /* foreach name=value pair... */
00781     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00782         dbg_if(vars_add_urlvar(rq->cookies, tok, NULL));
00783 
00784     return 0;
00785 err:
00786     return ~0;
00787 }
00788 
00789 static int request_parse_cookies(request_t *rq)
00790 {
00791     field_t *f;
00792     size_t i, count;
00793 
00794     dbg_err_if (rq == NULL);
00795     
00796     count = header_field_count(rq->header);
00797     for(i = 0; i < count; ++i)
00798     {
00799         f = header_get_fieldn(rq->header, i);
00800         dbg_err_if(f == NULL); /* shouldn't happen */
00801         if(strcasecmp(field_get_name(f), "cookie") == 0)
00802             dbg_err_if(request_parse_cookie(rq, f));
00803     }
00804 
00805     return 0;
00806 err:
00807     return ~0;
00808 }
00809 
00810 /* parse the query string from 'offset' and all variables to rq->args; if 'vs'
00811    is set also add all vars into it */
00812 static int request_parse_query_args_from(request_t *rq, int offset, 
00813         vars_t *vs)
00814 {
00815     char *pp, *tok, *src, *query = NULL;
00816     var_t *v;
00817 
00818     dbg_err_if (rq == NULL);
00819 
00820     if(!rq->query)
00821         return 0; /* no args */
00822 
00823     /* dup to tokenize it */
00824     query = u_strdup(rq->query + offset);
00825     dbg_err_if(query == NULL);
00826 
00827     /* foreach name=value pair... */
00828     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00829     {
00830         /* create a new var_t obj and push it into the args vars-list */
00831         dbg_if(vars_add_urlvar(rq->args, tok, &v));
00832 
00833         /* add the ptr also into the given vars list */
00834         if(vs)
00835             dbg_err_if(vars_add(vs, v));
00836     }
00837 
00838     U_FREE(query);
00839 
00840     return 0;
00841 err:
00842     U_FREE(query);
00843     return ~0;
00844 }
00845 
00846 static int request_cb_add_post_var(void *arg, const char *tok)
00847 {
00848     request_t *rq = (request_t*)arg;
00849     var_t *v;
00850 
00851     dbg_err_if(rq == NULL);
00852 
00853     /* create a new var_t obj and push it into the args vars-list */
00854     dbg_if(vars_add_urlvar(rq->args, tok, &v));
00855 
00856     /* add the ptr also to the POST vars list */
00857     dbg_err_if(vars_add(rq->args_post, v));
00858 
00859     return 0;
00860 err:
00861     return ~0;
00862 }
00863 
00864 static int request_cb_add_get_var(void *arg, const char *tok)
00865 {
00866     request_t *rq = (request_t*)arg;
00867     var_t *v;
00868 
00869     dbg_err_if(rq == NULL);
00870 
00871     /* create a new var_t obj and push it into the args vars-list */
00872     dbg_if(vars_add_urlvar(rq->args, tok, &v));
00873 
00874     /* add the ptr also to the GET vars list */
00875     dbg_err_if(vars_add(rq->args_get, v));
00876 
00877     return 0;
00878 err:
00879     return ~0;
00880 }
00881 
00882 static int foreach_query_var(const char *urlquery, int offset, 
00883         int(*cb)(void*,const char*), void *arg)
00884 {
00885     char *pp, *tok, *src, *query = NULL;
00886 
00887     dbg_err_if(offset < 0);
00888     dbg_err_if(cb == NULL);
00889 
00890     if(!urlquery)
00891         return 0; /* no args */
00892 
00893     /* dup to tokenize it */
00894     query = u_strdup(urlquery + offset);
00895     dbg_err_if(query == NULL);
00896 
00897     /* foreach name=value pair... */
00898     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00899     {
00900         /* call the callback that will save this var */
00901         dbg_err_if(cb(arg, tok));
00902     }
00903 
00904     U_FREE(query);
00905 
00906     return 0;
00907 err:
00908     U_FREE(query);
00909     return ~0;
00910 }
00911 
00912 static int request_parse_query_args(request_t *rq)
00913 {
00914     dbg_err_if(rq == NULL);
00915 
00916     return foreach_query_var(rq->query, 0, request_cb_add_get_var, (void*)rq); 
00917 err:
00918     return ~0;
00919 }
00920 
00921 /* set is-cgi flag */
00922 void request_set_cgi(request_t *rq, int cgi)
00923 {
00924     rq->cgi = cgi;
00925     return;
00926 }
00927 
00939 ssize_t request_get_content_length(request_t *rq)
00940 {
00941     dbg_return_if (rq == NULL, -1);
00942 
00943     return (ssize_t) rq->content_length;
00944 }
00945 
00946 static int match_content_type(header_t *h, const char *mime_type)
00947 {
00948     const char *ct;
00949 
00950     dbg_return_if (h == NULL, 0);
00951     dbg_return_if (mime_type == NULL, 0);
00952 
00953     ct = header_get_field_value(h, "Content-Type");
00954     if(ct == NULL || strncasecmp(ct, mime_type, strlen(mime_type)))
00955         return 0;
00956 
00957     return 1;
00958 }
00959 
00960 static int request_is_multipart_formdata(request_t *rq)
00961 {
00962     return match_content_type(rq->header, "multipart/form-data");
00963 }
00964 
00965 static int request_parse_urlencoded_data(request_t *rq)
00966 {
00967     ssize_t qsz, len;
00968 
00969     dbg_err_if (rq == NULL);
00970 
00971     len = rq->content_length; /* shortcut */
00972 
00973     qsz = (rq->query ? strlen(rq->query) : 0);
00974 
00975     /* alloc or enlarge the query string buffer */
00976     rq->query = u_realloc(rq->query, len + qsz + 2);
00977     dbg_err_if(rq->query == NULL);
00978 
00979     /* dbg("rq->query %x  size %u", rq->query, len+qsz+2); */
00980 
00981     rq->query[qsz] = 0; /* must be zero-term for strcat to work */
00982     if(qsz)
00983     {   /* append a '&' */
00984         strcat(rq->query, "&");
00985         ++qsz;
00986     }
00987 
00988     /* append to current query string */
00989     dbg_err_if(io_read(rq->io, rq->query + qsz, len) != len);
00990 
00991     /* zero terminate it */
00992     rq->query[qsz + len] = 0;
00993 
00994     /* parse and add post vars to the rq->args and rq->args_post array */
00995     dbg_err_if(foreach_query_var(rq->query, qsz, 
00996                 request_cb_add_post_var, (void*)rq));
00997 
00998     return 0;
00999 err:
01000     return ~0;
01001 }
01002 
01003 /* return the value of the param named 'param_name' of the field 'field_name'
01004    and save it to 'buffer' */
01005 static int request_get_fieldparam(request_t *rq, const char *field_name, 
01006     const char *param_name, char *buf, size_t size)
01007 {
01008     const char *param_value, *field_value, *p;
01009     size_t pv_len;
01010 
01011     dbg_err_if (rq == NULL);
01012     dbg_err_if (field_name == NULL);
01013     dbg_err_if (param_name == NULL);
01014     dbg_err_if (buf == NULL);
01015     dbg_err_if (size == 0);
01016 
01017     field_value = header_get_field_value(rq->header, field_name);
01018     dbg_err_if(field_value == NULL);
01019 
01020     /* look for param name=value pair */
01021     param_value = u_stristr(field_value, param_name);
01022     dbg_err_if(param_value == NULL);
01023 
01024     /* skip param name */
01025     param_value += strlen(param_name);
01026 
01027     /* first char must be an equal sign */
01028     dbg_err_if(*param_value++ != '=');
01029 
01030     /* a param value ends on the first ';', space or at the end of string */
01031     for(p = param_value; ;++p)
01032         if(*p == '\0' || *p == ';' || isspace(*p))
01033             break; /* end of param value */
01034 
01035     /* param value len */
01036     pv_len = p - param_value;
01037 
01038     /* boundary check */
01039     dbg_err_if(pv_len > size - 1); 
01040 
01041     /* copy out the param value */
01042     strncpy(buf, param_value, pv_len);
01043     buf[MIN(pv_len, size - 1)] = 0;
01044 
01045     return 0;
01046 err:
01047     return ~0;
01048 }
01049 
01050 static int is_multipart_mixed(header_t *h)
01051 {
01052     return match_content_type(h, "multipart/mixed");
01053 }
01054 
01055 static int is_encoded(header_t *h)
01056 {
01057     const char *cte;
01058 
01059     dbg_return_if (h == NULL, 0);
01060 
01061     if((cte = header_get_field_value(h, "Content-Transfer-Encoding")) == NULL)
01062         return 0; /* not encoded */
01063 
01064     if(strcasecmp(cte, "binary") == 0)
01065         return 0; /* not encoded */
01066 
01067     return 1; /* encoded */
01068 }
01069 
01070 static inline int is_nl(char c)
01071 {
01072     return (c == '\n' || c == '\r' ? c : 0);
01073 }
01074 
01075 static inline int is_quote(char c)
01076 {
01077     return (c == '"' || c == '\'' ? c : 0);
01078 }
01079 
01080 static int parse_content_disposition(header_t *h, char *name, char *filename,
01081     size_t prmsz)
01082 {
01083     enum { BUFSZ = 512 };
01084     char *pp, *tok, *src, buf[BUFSZ];
01085     size_t n_len, fn_len;
01086     const char *cd;
01087     int q;
01088 
01089     dbg_err_if (h == NULL);
01090     dbg_err_if (name == NULL);
01091     dbg_err_if (filename == NULL);
01092     dbg_err_if (prmsz == 0);
01093     
01094     cd = header_get_field_value(h, "Content-Disposition");
01095     dbg_err_if(cd == NULL);
01096 
01097     dbg_err_if(strlen(cd) >= BUFSZ);
01098 
01099     /* must start with form-data */
01100     dbg_err_if(strncmp(cd, "form-data", strlen("form-data")));
01101 
01102     name[0] = filename[0] = 0;
01103 
01104     /* save a copy to tokenize it */
01105     strncpy(buf, cd, BUFSZ);
01106 
01107     /* shortcut */
01108     n_len = strlen("name=");
01109     fn_len = strlen("filename=");
01110 
01111     /* foreach name=value pair... */
01112     for(src = buf; (tok = strtok_r(src, ";", &pp)) != NULL; src = NULL)
01113     {
01114         /* skip trailing blanks */
01115         while(isspace(*tok))
01116             ++tok;
01117 
01118         if(strncmp(tok, "form-data", strlen("form-data")) == 0)
01119             continue;   /* skip */
01120         else if(strncmp(tok, "name=", n_len) == 0) {
01121             /* skip the name part */
01122             tok += n_len;
01123 
01124             /* remove single or double quotes */
01125             if((q = is_quote(tok[0])) != 0)
01126                 ++tok;
01127             if(strlen(tok) && tok[strlen(tok) - 1] == q)
01128                 tok[strlen(tok) - 1] = 0;
01129 
01130             strncpy(name, tok, prmsz);
01131         } else if(strncmp(tok, "filename=", fn_len) == 0) {
01132             /* skip the filename part */
01133             tok += fn_len;
01134 
01135             /* remove single or double quotes */
01136             if((q = is_quote(tok[0])) != 0)
01137                 ++tok;
01138             if(strlen(tok) && tok[strlen(tok) - 1] == q)
01139                 tok[strlen(tok) - 1] = 0;
01140 
01141             strncpy(filename, tok, prmsz);
01142         } 
01143         /* else ignore unknown fields */
01144     }
01145             
01146     return 0;
01147 err:
01148     return ~0;
01149 }
01150 
01151 /* 
01152  * Read from io until obuf is full or until stop_at string is found.
01153  *
01154  * Boyer-Moore algorithm is used for efficiency. 
01155  *
01156  * Returns the number of bytes written to obuf 
01157  */
01158 static ssize_t read_until(io_t *io, const char *stop_at, char *obuf, 
01159     size_t size, int *found)
01160 {
01161     /* use this macro before accessing obuf[idx] elem. the macro will load from
01162        the given io enough bytes to access the required byte. if the buffer
01163        is too small (i.e. less then idx bytes long) the function will return */
01164     #define SETUP_BUF_ACCESS_AT(idx)                                        \
01165         if(idx >= wtot) {                                                   \
01166             if(idx >= size)                                                 \
01167                 return wtot; /* the output buffer is full */                \
01168                                                                             \
01169             /* we need to fetch some more bytes to access obuf[i] */        \
01170             dbg_err_if((rc = io_read(io, wbuf, idx + 1 - wtot)) < 0);       \
01171             if(rc == 0 || rc < idx + 1 - wtot)                              \
01172                 return wtot + rc; /* eof or short count */                  \
01173             wbuf += rc;                                                     \
01174             wtot += rc;                                                     \
01175         }
01176 
01177     int sa_len = strlen(stop_at);
01178     int i, t, shift[256], rc;
01179     unsigned char c;
01180     size_t wtot = 0;
01181     char *wbuf = obuf;
01182 
01183     dbg_err_if (io == NULL);
01184     dbg_err_if (stop_at == NULL);
01185     dbg_err_if (obuf == NULL);
01186     /* size may be 0 */
01187     dbg_err_if (found == NULL);
01188 
01189     for(i = 0; i < 256; ++i)  
01190         shift[i] = sa_len;
01191 
01192     for(i = 0; i < sa_len; ++i)
01193         shift[ stop_at[i] ] = sa_len - i - 1;
01194 
01195     *found = 0;
01196 
01197     for(i = t = sa_len-1; t >= 0; --i, --t)
01198     {
01199         SETUP_BUF_ACCESS_AT(i);
01200 
01201         while((c = obuf[i]) != stop_at[t]) 
01202         {
01203             i += MAX(sa_len - t, shift[c]);
01204 
01205             SETUP_BUF_ACCESS_AT(i);
01206 
01207             t = sa_len - 1;
01208         }
01209     }
01210 
01211     *found = 1;
01212 
01213     /* found; obuf[i] is where the matching string is */
01214     return wtot;
01215 err:
01216     return -1;
01217 }
01218 
01219 
01238 vars_t *request_get_uploads(request_t *rq)
01239 {
01240     return rq->uploads;
01241 }
01242 
01243 /*
01244  * name:         form "name" <input> tag attribute value
01245  * filename:     name of the uploaded file provided by the client
01246  * tmp_filename: name on the temp file when the uploaded data has been saved
01247  *               on the local disk
01248  * mime_type:    uploaded MIME type as stated by the browser (may be NULL)
01249  */
01250 static int request_add_uploaded_file(request_t *rq, const char *name, 
01251     const char *filename, const char *tmp_filename, const char *mime_type)
01252 {
01253     struct stat st;
01254     var_t *v = NULL;
01255     upload_info_t *info = NULL;
01256 
01257     dbg_err_if (rq == NULL);
01258     dbg_err_if (name == NULL);
01259     /* filename may be NULL */
01260     dbg_err_if (tmp_filename == NULL);
01261     /* mime_type may be NULL */
01262 
01263     dbg_err_sif (stat(tmp_filename, &st) < 0);
01264 
01265     /* create a new var obj */
01266     dbg_err_if(var_create(name, tmp_filename, &v));
01267 
01268     /* alloc an info struct to attach to the var_t obj */
01269     dbg_err_if((info = u_zalloc(sizeof(upload_info_t))) == NULL);
01270 
01271     /* set info data */
01272     info->size = st.st_size;
01273     if(mime_type)
01274         snprintf(info->mime_type, MIME_TYPE_BUFSZ, "%s", mime_type);
01275     else
01276         info->mime_type[0] = 0;
01277 
01278     if(filename)
01279         snprintf(info->filename, U_FILENAME_MAX, "%s", filename);
01280 
01281     /* attach info to v */
01282     var_set_opaque(v, info);
01283     info = NULL;
01284 
01285     /* push into the cookie list */
01286     dbg_err_if(vars_add(rq->uploads, v));
01287 
01288     return 0;
01289 err:
01290     if(info)
01291         U_FREE(info);
01292     if(v)
01293         var_free(v);
01294     return ~0;
01295 }
01296 
01297 static int request_get_uploaded_filev(request_t *rq, var_t *v,
01298     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01299     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01300 {           
01301     upload_info_t *info;
01302     const char *tmp_fqn;
01303 
01304     dbg_err_if (rq == NULL);
01305     dbg_err_if (v == NULL);
01306     dbg_err_if (local_filename == NULL);
01307     dbg_err_if (client_filename == NULL);
01308     dbg_err_if (mime_type == NULL);
01309     dbg_err_if (file_size == NULL);
01310 
01311     info = var_get_opaque(v);
01312     dbg_err_if(info == NULL);
01313 
01314     tmp_fqn = var_get_value(v);
01315     dbg_err_if(tmp_fqn == NULL);
01316 
01317     /* copy out return values */
01318     strncpy(local_filename, tmp_fqn, U_FILENAME_MAX);
01319     strncpy(mime_type, info->mime_type, MIME_TYPE_BUFSZ);
01320     strncpy(client_filename, info->filename, U_FILENAME_MAX);
01321     *file_size = info->size;
01322 
01323     return 0;
01324 err:
01325     return ~0;
01326 }
01327 
01350 int request_get_uploaded_file(request_t *rq, const char *name, size_t idx,
01351     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01352     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01353 {
01354     var_t *v;
01355 
01356     dbg_err_if (rq == NULL);
01357     dbg_err_if (name == NULL);
01358     dbg_err_if (idx >= vars_count(rq->uploads));
01359     dbg_err_if (local_filename == NULL);
01360     dbg_err_if (client_filename == NULL);
01361     dbg_err_if (mime_type == NULL);
01362     dbg_err_if (file_size == NULL);
01363 
01364     v = vars_geti(rq->uploads, name, idx);
01365     dbg_err_if(v == NULL);
01366 
01367     return request_get_uploaded_filev(rq, v, local_filename, client_filename,
01368         mime_type, file_size);
01369 err:
01370     return ~0;
01371 }
01372 
01373 static int request_parse_multipart_chunk(request_t *rq, io_t *io, 
01374     const char *boundary, int *eof)
01375 {
01376     enum { PRMSZ = 512, BUFSZ = 4096 };
01377     header_t *h = NULL;
01378     io_t *tmpio = NULL;
01379     var_t *v = NULL;
01380     char name[PRMSZ], filename[PRMSZ], buf[BUFSZ];
01381     size_t bound_len;
01382     int found;
01383     ssize_t rc;
01384 
01385     /* create an header object to parse MIME part headers */
01386     dbg_err_if(header_create(&h));
01387 
01388     /* read header lines until the first blank line */
01389     dbg_err_if(header_load(h, io));
01390 
01391     warn_err_ifm(is_multipart_mixed(h), 
01392         "multipart/mixed content is not supported yet");
01393 
01394     /* HTTP should never use cte */
01395     warn_err_ifm(is_encoded(h), 
01396         "encoded file upload is not supported");
01397 
01398     dbg_err_if(parse_content_disposition(h, name, filename, PRMSZ));
01399 
01400     /* shortcut */
01401     bound_len = strlen(boundary);
01402 
01403     if(filename[0] != '\0')
01404     {
01405         dbg_err_if(BUFSZ <= bound_len);
01406 
01407         /* open a temporary file to dump uploaded data */
01408         dbg_err_if(u_tmpfile_open(&tmpio));
01409 
01410         for(found = 0; !found; /* nothing */)
01411         {
01412             rc = read_until(io, boundary, buf, BUFSZ, &found);
01413             dbg_err_if(rc <= 0); /* on error or eof exit */
01414 
01415             /* write all but the last bound_len + 2 (\r\n) bytes */
01416             if(found)
01417             {
01418                 rc -= (bound_len + 2);
01419                 dbg_err_if(rc < 0);
01420             }
01421 
01422             /* write to the temp file */
01423             dbg_err_if(io_write(tmpio, buf, rc) < 0);
01424         }
01425 
01426         /* save the path/name of the tmp file to buf */
01427         dbg_err_if(io_name_get(tmpio, buf, BUFSZ));
01428 
01429         /* flush and free */
01430         io_free(tmpio); tmpio = NULL;
01431 
01432         /* add this file to the uploaded file list */
01433         dbg_err_if(request_add_uploaded_file(rq, name, filename, buf, 
01434             header_get_field_value(h, "Content-Type")));
01435 
01436         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01437         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01438 
01439         if(strncmp(buf, "--", 2) == 0)
01440             *eof = 1; /* end of MIME stuff */
01441 
01442     } else {
01443         /* read the value of the variable (all until the next boundary) */
01444         rc = read_until(io, boundary, buf, BUFSZ, &found);
01445         dbg_err_if(rc <= 0); /* on error or eof exit */
01446 
01447         /* write all but the last bound_len + 2 (\r\n) bytes */
01448         warn_err_ifm(!found, "malformed request or BUFSZ too small");
01449 
01450         rc -= (bound_len + 2);
01451         dbg_err_if(rc < 0);
01452 
01453         /* zero-term the buffer (removing the boundary) */
01454         buf[rc] = 0;
01455 
01456         /* add a new binary var to request arguments list */
01457         dbg_err_if(var_bin_create(name, buf, rc, &v));
01458         dbg_if(vars_add(rq->args, v));
01459 
01460         /* also add it to the post array */
01461         dbg_if(vars_add(rq->args_post, v));
01462 
01463         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01464         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01465 
01466         if(strncmp(buf, "--", 2) == 0)
01467             *eof = 1; /* end of MIME stuff */
01468     }
01469 
01470     header_free(h);
01471 
01472     return 0;
01473 err:
01474     if(tmpio)
01475         io_free(tmpio);
01476     if(h)
01477         header_free(h);
01478     return ~0;
01479 }
01480 
01481 static int request_parse_multipart_data(request_t *rq)
01482 {
01483     enum { BOUNDARY_BUFSZ = 128, BUFSZ = 1024 }; 
01484     char boundary[BOUNDARY_BUFSZ], buf[BUFSZ];
01485     int eof;
01486 
01487     /* boundaries always start with -- */
01488     strcpy(boundary, "--");
01489 
01490     dbg_err_if(request_get_fieldparam(rq, "Content-Type", "boundary",
01491         boundary + 2, BOUNDARY_BUFSZ - 2));
01492 
01493     dbg_err_if(strlen(boundary) == 0);
01494 
01495     /* skip the MIME preamble (usually not used in HTTP) */
01496     for(;;)
01497     {
01498         dbg_err_if(io_gets(rq->io, buf, BUFSZ) <= 0);
01499         if(!strncmp(buf, boundary, strlen(boundary)))
01500             break; /* boundary found */
01501     }
01502 
01503     /* cycle on each MIME part */
01504     for(eof = 0; eof == 0; )
01505         dbg_err_if(request_parse_multipart_chunk(rq, rq->io, boundary, &eof));
01506 
01507     return 0;
01508 err:
01509     return ~0;
01510 }
01511 
01512 static int request_cb_close_socket(talarm_t *al, void *arg)
01513 {
01514     io_t *io = (io_t*)arg;
01515 
01516     u_unused_args(al);
01517 
01518     warn("[%x] connection timed out, closing", io);
01519     
01520     /* close the stream (but not free it) */
01521     io_close(io);
01522 
01523     return 0;
01524 }
01525 
01526 int request_parse_data(request_t *rq)
01527 {
01528     talarm_t *al = NULL;
01529     int rc = HTTP_STATUS_BAD_REQUEST;
01530 
01531     if(rq->method == HM_POST)
01532     {
01533         /* set a timeout to abort POST if it takes too much... */
01534         dbg_err_if(timerm_add(rq->post_timeout, request_cb_close_socket, 
01535             (void*)rq->io, &al));
01536 
01537         /* Content-Length is required when using POST */
01538         dbg_err_if(request_set_content_length(rq) && 
01539             (rc = HTTP_STATUS_LENGTH_REQUIRED));
01540 
01541         if(rq->content_length == 0)
01542             return 0; /* no data posted */
01543 
01544         /* abort if the client is pushing too much data */
01545         dbg_err_if(rq->content_length > rq->post_maxsize &&
01546             (rc = HTTP_STATUS_REQUEST_TOO_LARGE));
01547 
01548         /* some vars may be urlencoded */
01549         dbg_err_if(request_parse_query_args(rq));
01550 
01551         if(request_is_multipart_formdata(rq))
01552         { 
01553             /* <form enctype="multipart/form-data" ...> */
01554             dbg_err_if(request_parse_multipart_data(rq));
01555         } else {
01556             /* <form [enctype="application/x-www-form-urlencoded"] ...> */
01557             dbg_err_if(request_parse_urlencoded_data(rq));
01558         }
01559 
01560         /* post timeout not expired, clear it */
01561         dbg_if(timerm_del(al)); al = NULL;
01562     } else {
01563         /* parse urlencoded variables and set var_t* array */
01564         dbg_err_if(request_parse_query_args(rq));
01565     }
01566 
01567     return 0;
01568 err:
01569     return rc;
01570 }
01571 
01572 /*
01573  * Parse request object \p rq.
01574  *
01575  * \param rq            request object
01576  * \param is_valid_uri  URI validation function
01577  * \param arg           argument to is_valid_uri
01578  *
01579  * \return \c 0 if successful, non-zero on error
01580  */
01581 int request_parse_header(request_t *rq, 
01582         int (*is_valid_uri)(void*, const char *, size_t),
01583         void* arg)
01584 {
01585     enum { BUFSZ = 4096 };
01586     const char WP[] = " \t\r\n";
01587     char ln[BUFSZ], *pp, *method, *uri, *proto;
01588     talarm_t *al = NULL;
01589     
01590     dbg_err_if (rq == NULL);
01591     dbg_err_if (rq->io == NULL); /* must call rq_bind before rq_parse */
01592 
01593     /* wait at most N seconds to receive the request */
01594     dbg_err_if(timerm_add(rq->idle_timeout, request_cb_close_socket, 
01595         (void*)rq->io, &al));
01596 
01597     if(!rq->cgi)
01598     {
01599         /* cp the first line */
01600         dbg_err_if(io_gets(rq->io, ln, BUFSZ) <= 0);
01601 
01602         /* save the verbatim request line */
01603         dbg_err_if(request_set_client_request(rq, ln));
01604 
01605         method = strtok_r(ln, WP, &pp); 
01606         dbg_err_if(!method || request_set_method(rq, method));
01607 
01608         uri = strtok_r(NULL, WP, &pp);
01609         dbg_err_if(!uri || request_set_uri(rq, uri, is_valid_uri, arg));
01610 
01611         /* HTTP/0.9 not supported yet */ 
01612         proto = strtok_r(NULL, WP, &pp);
01613         dbg_err_if(!proto || request_set_proto(rq, proto)); 
01614 
01615         dbg_err_if(header_load(rq->header, rq->io));
01616     } else {
01617         dbg_err_if(header_load_from_cgienv(rq->header));
01618     }
01619 
01620     /* set if-modified-since time_t value */
01621     dbg_err_if(request_parse_ims(rq));
01622 
01623     /* parse "Cookie:" fields and set the cookies vars_t */
01624     dbg_err_if(request_parse_cookies(rq));
01625 
01626     /* Content-Length is required when using POST */
01627     if(request_get_method(rq) == HM_POST)
01628         dbg_err_if(request_set_content_length(rq));
01629 
01630     /* idle timeout not expired, clear it */
01631     dbg_if(timerm_del(al)); al = NULL;
01632 
01633     return 0;
01634 err:
01635     if(al)
01636         timerm_del(al);
01637     return ~0;
01638 }
01639 
01651 int request_get_method(request_t *rq)
01652 {
01653     dbg_return_if (rq == NULL, HM_UNKNOWN);
01654 
01655     return rq->method;
01656 }
01657 
01669 const char* request_get_protocol(request_t *rq)
01670 {
01671     dbg_return_if (rq == NULL, "unknown");
01672 
01673     return rq->protocol;
01674 }
01675 
01686 const char *request_get_resolved_filename(request_t *rq)
01687 {
01688     dbg_return_if (rq == NULL, NULL);
01689 
01690     return rq->resolved_filename;
01691 }
01692 
01703 const char *request_get_resolved_path_info(request_t *rq)
01704 {
01705     dbg_return_if (rq == NULL, NULL);
01706 
01707     return rq->resolved_path_info;
01708 }
01709 
01710 int request_print(request_t *rq)
01711 {
01712     dbg_return_if (rq == NULL, ~0);
01713 
01714     dbg("method: %u", rq->method);
01715     dbg("uri: %s", rq->uri);
01716     dbg("proto: %s", rq->protocol);
01717     dbg("filename: %s", rq->filename);
01718     dbg("resolved filename: %s", rq->resolved_filename);
01719     dbg("path_info: %s", rq->path_info);
01720     dbg("resolved path_info: %s", rq->resolved_path_info);
01721     dbg("query: %s", rq->query);
01722 
01723     return 0;
01724 }
01725 
01726 static int request_load_config(request_t *rq)
01727 {
01728     u_config_t *c;
01729     const char *v;
01730 
01731     dbg_err_if (rq == NULL);
01732     dbg_err_if (rq->http == NULL);
01733     dbg_err_if (http_get_config(rq->http) == NULL);
01734 
01735     c = http_get_config(rq->http);
01736     
01737     /* defaults */
01738     rq->idle_timeout = REQUEST_DEFAULT_IDLE_TIMEOUT;
01739     rq->post_timeout = REQUEST_DEFAULT_POST_TIMEOUT;
01740     rq->post_maxsize = REQUEST_DEFAULT_POST_MAXSIZE;
01741 
01742     /* idle timeout */
01743     if((v = u_config_get_subkey_value(c, "idle_timeout")) != NULL)
01744         rq->idle_timeout = MAX(1, atoi(v));
01745 
01746     /* post timeout */
01747     if((v = u_config_get_subkey_value(c, "post_timeout")) != NULL)
01748         rq->post_timeout = MAX(5, atoi(v));
01749 
01750     /* post maxsize */
01751     if((v = u_config_get_subkey_value(c, "post_maxsize")) != NULL)
01752         rq->post_maxsize = MAX(1024, atoi(v));
01753 
01754     return 0;
01755 err:
01756     return ~0;
01757 }
01758 
01759 int request_create(http_t *http, request_t **prq)
01760 {
01761     request_t *rq = NULL;
01762 
01763     dbg_return_if (prq == NULL, ~0);
01764     dbg_return_if (http == NULL, ~0);
01765 
01766     rq = u_zalloc(sizeof(request_t));
01767     dbg_err_if(rq == NULL);
01768 
01769     dbg_err_if(header_create(&rq->header));
01770 
01771     dbg_err_if(vars_create(&rq->args));
01772     dbg_err_if(vars_create(&rq->cookies));
01773     dbg_err_if(vars_create(&rq->uploads));
01774 
01775     dbg_err_if(vars_create(&rq->args_get));
01776     dbg_err_if(vars_create(&rq->args_post));
01777 
01778     /* args_get and args_post link to var_t owned by the rq->args list */
01779     dbg_err_if(vars_set_flags(rq->args_get, VARS_FLAG_FOREIGN));
01780     dbg_err_if(vars_set_flags(rq->args_post, VARS_FLAG_FOREIGN));
01781 
01782     rq->http = http;
01783 
01784     dbg_err_if(request_load_config(rq));
01785 
01786     *prq = rq;
01787 
01788     return 0;
01789 err:
01790     if(rq)
01791         request_free(rq);
01792     return ~0;
01793 }
01794 
01795 static int request_unlink_uploads(var_t *v, void * arg)
01796 {
01797     dbg_err_if (v == NULL);
01798 
01799     u_unused_args(arg);
01800 
01801     if(var_get_opaque(v) && var_get_value(v))
01802     {   /* it's a file var, unlink v->value */
01803         u_remove(var_get_value(v));
01804     }
01805 
01806 err:
01807     return 0;
01808 }
01809 
01810 int request_free(request_t *rq)
01811 {
01812     if (rq)
01813     {
01814         /* free internal stuff */
01815         request_clear_uri(rq);
01816 
01817         if(rq->header)
01818             header_free(rq->header);
01819 
01820         if(rq->io)
01821             io_free(rq->io);
01822 
01823         if(rq->uploads)
01824         {
01825             /* unlink uploaded files (if any) */
01826             vars_foreach(rq->uploads, request_unlink_uploads, NULL);
01827             vars_free(rq->uploads);
01828         }
01829 
01830         if(rq->cookies)
01831             vars_free(rq->cookies);
01832 
01833         if(rq->args_get)
01834             vars_free(rq->args_get);
01835 
01836         if(rq->args_post)
01837             vars_free(rq->args_post);
01838 
01839         if(rq->args)
01840             vars_free(rq->args);
01841 
01842         U_FREE(rq);
01843     }
01844 
01845     return 0;
01846 }
01847 
01848 /* save the local address struct (ip and port) in the request obj */
01849 int request_set_addr(request_t *rq, addr_t *addr)
01850 {
01851     dbg_return_if (rq == NULL, ~0);
01852     dbg_return_if (addr == NULL, ~0);
01853 
01854     memcpy(&rq->local_addr, addr, sizeof(addr_t));
01855 
01856     return 0;
01857 }
01858 
01859 /* save the peer address struct (ip and port) in the request obj */
01860 int request_set_peer_addr(request_t *rq, addr_t *addr)
01861 {
01862     dbg_return_if (rq == NULL, ~0);
01863     dbg_return_if (addr == NULL, ~0);
01864 
01865     memcpy(&rq->peer_addr, addr, sizeof(addr_t));
01866 
01867     return 0;
01868 }
01869 
01880 addr_t* request_get_addr(request_t *rq)
01881 {
01882     dbg_return_if (rq == NULL, NULL);
01883 
01884     return &rq->local_addr;
01885 }
01886 
01897 addr_t* request_get_peer_addr(request_t *rq)
01898 {
01899     dbg_return_if (rq == NULL, NULL);
01900 
01901     return &rq->peer_addr;
01902 }
01903 
01915 header_t* request_get_header(request_t *rq)
01916 {
01917     dbg_return_if (rq == NULL, NULL);
01918 
01919     return rq->header;
01920 }
01921 
01933 field_t* request_get_field(request_t *rq, const char *name)
01934 {
01935     dbg_return_if (rq == NULL, NULL);
01936     dbg_return_if (name == NULL, NULL);
01937 
01938     return header_get_field(rq->header, name);
01939 }
01940 
01952 const char* request_get_field_value(request_t *rq, const char *name)
01953 {
01954     dbg_return_if (rq == NULL, NULL);
01955     dbg_return_if (name == NULL, NULL);
01956 
01957     return header_get_field_value(rq->header, name);
01958 }
01959 
01960 vhost_t* request_get_vhost(request_t *rq)
01961 {
01962     dbg_return_if (rq == NULL, NULL);
01963 
01964     return rq->vhost; /* may be NULL */
01965 }
01966 
01967 int request_set_vhost(request_t *rq, vhost_t *vhost)
01968 {
01969     dbg_return_if (rq == NULL, ~0);
01970 
01971     rq->vhost = vhost;
01972 
01973     return 0;
01974 }
01975