KLone APIs | Modules | Data Structures | File List | Data Fields | Globals

request.c

Go to the documentation of this file.
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.66 2009/05/29 10:26:00 tho 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 #include <klone/supplier.h>
00028 
00029 struct request_s
00030 {
00031     http_t *http;               /* http server handle                       */
00032     header_t *header;           /* input header                             */
00033     io_t *io;                   /* input io stream                          */
00034     int method;                 /* get,post,etc.                            */
00035     char *cli_rq;               /* verbatim client request line             */
00036     char *uri;                  /* verbatim uri asked by the client         */
00037     char *protocol;             /* proto/ver                                */
00038     char *path_info;            /* extra info at the end of the path        */
00039     char *query;                /* query string (data after '?')            */
00040     char *filename;             /* path of the req resource                 */
00041     char *resolved_path_info;   /* resolved path_info                       */
00042     char *resolved_filename;    /* unaliased filename                       */
00043     vars_t *args;               /* mixed get/post args                      */
00044     vars_t *args_get;           /* get variables                            */
00045     vars_t *args_post;          /* post variables                           */
00046     vars_t *cookies;            /* cookies                                  */
00047     vars_t *uploads;            /* uploaded file list                       */
00048     char *content_type;         /* type/subtype                             */
00049     char *content_encoding;     /* 7bit/8bit/base64/qp, etc                 */
00050         size_t content_length;      /* content-length http header field         */
00051     time_t if_modified_since;   /* time_t IMS header                        */
00052     kaddr_t local_addr, peer_addr; /* local and perr address                 */
00053     int cgi;                    /* if running in cgi mode                   */
00054     size_t idle_timeout;        /* max # of secs to wait for the request    */
00055     size_t post_timeout;        /* max # of secs for reading POSTed data    */
00056     size_t post_maxsize;        /* max # of POSTed bytes to accepts         */
00057     const char *temp_dir;       /* where temp files go                      */
00058     vhost_t *vhost;             /* cached vhost pointer                     */
00059     size_t padding;
00060     /* cached supplier info data */
00061     supplier_t *si_sup;
00062     void *si_handle;
00063     time_t si_mtime;
00064 };
00065 
00066 typedef struct upload_info_s    /* uploaded file info struct         */
00067 {
00068     char mime_type[MIME_TYPE_BUFSZ];
00069     char filename[U_FILENAME_MAX];
00070     size_t size;
00071 } upload_info_t;
00072 
00073 enum { 
00074     REQUEST_DEFAULT_IDLE_TIMEOUT = 10,         /* 10 secs */
00075     REQUEST_DEFAULT_POST_TIMEOUT = 600,        /* 10 mins */
00076     REQUEST_DEFAULT_POST_MAXSIZE = 5*1024000,  /* 5 MB    */
00077 };
00078 
00079 
00080 #define REQUEST_SET_STRING_FIELD(lval, rval)        \
00081     do {                                            \
00082         U_FREE(lval);                               \
00083         if(rval)                                    \
00084         {                                           \
00085             lval = u_strdup(rval);                  \
00086             dbg_err_if(lval == NULL);               \
00087         }                                           \
00088     } while(0)
00089 
00090 
00091 int request_is_encoding_accepted(request_t *rq, const char *encoding)
00092 {
00093     char *pp, *tok, *src, *buf = NULL;
00094     const char *accept_encoding;
00095     int rc = 0;
00096 
00097     dbg_err_if (rq == NULL);
00098     dbg_err_if (encoding == NULL);
00099     
00100     accept_encoding = header_get_field_value(rq->header, "Accept-Encoding");
00101     if(accept_encoding)
00102     {
00103         /* get a copy to work on */
00104         buf = u_strdup(accept_encoding);
00105         dbg_err_if(buf == NULL);
00106 
00107         /* foreach encoding pair... */
00108         for(src = buf; (tok = strtok_r(src, " ,", &pp)) != NULL; src = NULL)
00109         {
00110             if(strcasecmp(tok, encoding) == 0)
00111             {
00112                 rc++; /* found */
00113                 break;
00114             }
00115         }
00116 
00117         U_FREE(buf);
00118     }
00119 
00120     return rc;
00121 err:
00122     U_FREE(buf);
00123     return 0;
00124 }
00125 
00141 io_t *request_io(request_t *rq)
00142 {
00143     dbg_return_if (rq == NULL, NULL);
00144 
00145     return rq->io;
00146 }
00147 
00159 vars_t *request_get_cookies(request_t *rq)
00160 {
00161     dbg_return_if (rq == NULL, NULL);
00162 
00163     return rq->cookies;
00164 }
00165 
00177 const char *request_get_cookie(request_t *rq, const char *name)
00178 {
00179     var_t *v;
00180 
00181     dbg_return_if (rq == NULL, NULL);
00182     dbg_return_if (name == NULL, NULL);
00183 
00184     v = vars_get(rq->cookies, name);
00185 
00186     return v ? var_get_value(v): NULL;
00187 }
00188 
00199 vars_t *request_get_args(request_t *rq)
00200 {
00201     dbg_return_if (rq == NULL, NULL);
00202 
00203     return rq->args;
00204 }
00205 
00216 vars_t *request_get_getargs(request_t *rq)
00217 {
00218     dbg_return_if (rq == NULL, NULL);
00219 
00220     return rq->args_get;
00221 }
00222 
00233 vars_t *request_get_postargs(request_t *rq)
00234 {
00235     dbg_return_if (rq == NULL, NULL);
00236 
00237     return rq->args_post;
00238 }
00239 
00253 const char *request_get_arg(request_t *rq, const char *name)
00254 {
00255     var_t *v;
00256 
00257     dbg_return_if (rq == NULL, NULL);
00258     dbg_return_if (name == NULL, NULL);
00259 
00260     v = vars_get(rq->args, name);
00261 
00262     return v ? var_get_value(v): NULL;
00263 }
00264 
00278 const char *request_get_getarg(request_t *rq, const char *name)
00279 {
00280     var_t *v;
00281 
00282     dbg_return_if (rq == NULL, NULL);
00283     dbg_return_if (name == NULL, NULL);
00284 
00285     v = vars_get(rq->args_get, name);
00286 
00287     return v ? var_get_value(v): NULL;
00288 }
00289 
00303 const char *request_get_postarg(request_t *rq, const char *name)
00304 {
00305     var_t *v;
00306 
00307     dbg_return_if (rq == NULL, NULL);
00308     dbg_return_if (name == NULL, NULL);
00309 
00310     v = vars_get(rq->args_post, name);
00311 
00312     return v ? var_get_value(v): NULL;
00313 }
00314 
00315 int request_set_field(request_t *rq, const char *name, const char *value)
00316 {
00317     dbg_return_if (rq == NULL, ~0);
00318     dbg_return_if (name == NULL, ~0);
00319     dbg_return_if (value == NULL, ~0);
00320 
00321     return header_set_field(rq->header, name, value);
00322 }
00323 
00334 const char *request_get_uri(request_t *rq)
00335 {
00336     dbg_return_if (rq == NULL, NULL);
00337 
00338     return rq->uri;
00339 }
00340 
00351 const char *request_get_filename(request_t *rq)
00352 {
00353     dbg_return_if (rq == NULL, NULL);
00354 
00355     return rq->filename;
00356 }
00357 
00358 /*
00359  * \ingroup request
00360  * \brief   Set the filename field of a request
00361  *
00362  * Set the filename field of request \p rq to \p filename.
00363  *
00364  * \param rq        request object
00365  * \param filename  filename string
00366  *
00367  * \return \c 0 if successful, non-zero on error
00368  */
00369 int request_set_filename(request_t *rq, const char *filename)
00370 {
00371     dbg_err_if (rq == NULL);
00372     dbg_err_if (filename == NULL);
00373     
00374     REQUEST_SET_STRING_FIELD(rq->filename, filename);
00375 
00376     return 0;
00377 err:
00378     return ~0;
00379 }
00380 
00391 const char *request_get_query_string(request_t *rq)
00392 {
00393     dbg_return_if (rq == NULL, NULL);
00394 
00395     return rq->query;
00396 }
00397 
00408 const char *request_get_path_info(request_t *rq)
00409 {
00410     dbg_return_if (rq == NULL, NULL);
00411 
00412     return rq->path_info;
00413 }
00414 
00415 /* parse and set if-modified-since value */
00416 static int request_parse_ims(request_t *rq)
00417 {
00418     const char *ims;
00419 
00420     dbg_err_if (rq == NULL);
00421     
00422     rq->if_modified_since = 0;
00423 
00424     ims = header_get_field_value(rq->header, "If-Modified-Since");
00425     if(ims)
00426         dbg_err_if(u_httpdate_to_tt(ims, &rq->if_modified_since));
00427 
00428 err: /* ignore if it's not formatted properly */
00429     return 0;
00430 }
00431 
00442 time_t request_get_if_modified_since(request_t *rq)
00443 {
00444     dbg_return_if (rq == NULL, (time_t) -1);
00445 
00446     return rq->if_modified_since;
00447 }
00448 
00449 /*
00450  * \ingroup request
00451  * \brief   Set the resolved filename field of a request
00452  *
00453  * Set the resolved filename field of request \p rq to \p resolved_fn
00454  *
00455  * \param rq          request object
00456  * \param resolved_fn resolved filename
00457  *
00458  * \return \c 0 if successful, non-zero on error
00459  */
00460 int request_set_resolved_filename(request_t *rq, const char *resolved_fn)
00461 {
00462     dbg_err_if (rq == NULL);
00463     dbg_err_if (resolved_fn == NULL);
00464 
00465     REQUEST_SET_STRING_FIELD(rq->resolved_filename, resolved_fn);
00466 
00467     return 0;
00468 err:
00469     return ~0;
00470 }
00471 
00482 http_t* request_get_http(request_t *rq)
00483 {
00484     dbg_return_if (rq == NULL, NULL);
00485 
00486     return rq->http;
00487 }
00488 
00489 /*
00490  * \ingroup request
00491  * \brief   Bind request I/O to a given I/O. 
00492  *  
00493  * Bind the I/O of request \p rq to \p in.
00494  *
00495  * \param rq    request object
00496  * \param in    input I/O object
00497  *
00498  * \return \c 0 if successful, non-zero on error
00499  */
00500 int request_bind(request_t *rq, io_t *in)
00501 {
00502     dbg_return_if (rq == NULL, ~0);
00503     dbg_return_if (in == NULL, ~0);
00504 
00505     rq->io = in;
00506 
00507     return 0;
00508 }
00509 
00510 /*
00511  * \ingroup request
00512  * \brief   Set the query string of a request
00513  *
00514  * Parse \p query string and build the \p rq->args list.
00515  *
00516  * \param rq     request object
00517  * \param query  query string 
00518  *
00519  * \return \c 0 if successful, non-zero on error
00520  */
00521 int request_set_query_string(request_t *rq, const char *query)
00522 {
00523     dbg_err_if (rq == NULL);
00524     dbg_err_if (query == NULL);
00525     
00526     REQUEST_SET_STRING_FIELD(rq->query, query);
00527 
00528     return 0;
00529 err:
00530     return ~0;
00531 }
00532 
00533 void request_clear_uri(request_t *rq)
00534 {
00535     U_FREE(rq->uri);
00536     U_FREE(rq->protocol);
00537     U_FREE(rq->path_info);
00538     U_FREE(rq->query);
00539     U_FREE(rq->filename);
00540     U_FREE(rq->resolved_path_info);
00541     U_FREE(rq->resolved_filename);
00542     U_FREE(rq->content_type);
00543     U_FREE(rq->content_encoding);
00544 }
00545 
00546 /*
00547  * \ingroup request
00548  * \brief   Set the path info field of a request
00549  *
00550  * Set the path info field of request \p rq to \p path_info.
00551  *
00552  * \param rq         request object
00553  * \param path_info  path info
00554  *
00555  * \return \c 0 if successful, non-zero on error
00556  */
00557 int request_set_path_info(request_t *rq, const char *path_info)
00558 {
00559     dbg_err_if (rq == NULL);
00560     dbg_err_if (path_info == NULL);
00561 
00562     REQUEST_SET_STRING_FIELD(rq->path_info, path_info);
00563 
00564     return 0;
00565 err:
00566     return ~0;
00567 }
00568 
00569 /*
00570  * \ingroup request
00571  * \brief   Set the resolved path info field of a request
00572  *
00573  * Set the resolved path info field of request \p rq to \p resolved_pi.
00574  *
00575  * \param rq           request object
00576  * \param resolved_pi  resolved path info
00577  *
00578  * \return \c 0 if successful, non-zero on error
00579  */
00580 int request_set_resolved_path_info(request_t *rq, const char *resolved_pi)
00581 {
00582     dbg_err_if (rq == NULL);
00583     dbg_err_if (resolved_pi == NULL);
00584 
00585     REQUEST_SET_STRING_FIELD(rq->resolved_path_info, resolved_pi);
00586 
00587     return 0;
00588 err:
00589     return ~0;
00590 }
00591 
00592 /*
00593  * \ingroup request
00594  * \brief   Set the URI field of a request
00595  *
00596  * Set the URI field of request \p rq to \p uri given 
00597  *
00598  * \param rq           request object
00599  * \param uri          URI string
00600  * \param is_valid_uri URI validation function 
00601  * \param arg          argument to is_valid_uri
00602  *
00603  * \return \c 0 if successful, non-zero on error
00604  */
00605 int request_set_uri(request_t *rq, const char *uri,
00606         int (*is_valid_uri)(void*, const char *, size_t),
00607         void* arg)
00608 {
00609     char *p, *fn, *pi;
00610     size_t uri_len = strlen(uri);
00611     char cp[4096];
00612 
00613     dbg_err_if (rq == NULL);
00614     dbg_err_if (uri == NULL);
00615     /* is_valid_uri may be NULL */
00616     /* arg may be NULL */
00617  
00618     request_clear_uri(rq);
00619 
00620     /* this is just to avoid recursive infinite redirect loops for pages that 
00621        appends something to the URI and redirects to the same page */
00622     warn_err_ifm(uri_len >= sizeof(cp), "Request URI too long");
00623 
00624     REQUEST_SET_STRING_FIELD(rq->uri, uri);
00625 
00626     /* save (undecoded) query string i.e. everything after '?' */
00627     if((p = strchr(uri, '?')) != NULL)
00628         dbg_err_if(request_set_query_string(rq, ++p));
00629 
00630     /* copy decoded url */
00631     dbg_err_if(u_urlncpy(cp, rq->uri, uri_len, URLCPY_DECODE) <= 0);
00632 
00633     if((p = strchr(cp, '?')) != NULL)
00634         *p++ = 0; /* remove query string from the uri copy */
00635 
00636     /* normalize the URI (remove /../, /./, ecc) */
00637     dbg_err_if(u_uri_normalize(cp));
00638 
00639     /* set filename is case there's not path_info and/or file does not exists */
00640     dbg_err_if(request_set_filename(rq, cp));
00641 
00642     /* look for path_info */
00643     fn = cp;                    /* filename     */
00644     pi = fn + strlen(fn);       /* path_info    */
00645     for(;;)
00646     {
00647         if(is_valid_uri == NULL || is_valid_uri(arg, fn, pi - fn))
00648         {
00649             dbg_err_if(request_set_filename(rq, fn));
00650             rq->filename[pi-fn] = 0; /* trunc */
00651             if(strlen(pi))
00652                 dbg_err_if(request_set_path_info(rq, pi));
00653             break;
00654         } else {
00655             if((p = u_strnrchr(fn, '/', pi - fn)) == NULL)
00656                 break; /* file pointed by this uri does not exists */
00657             pi = p; /* try again */
00658         }
00659     }
00660 
00661     return 0;
00662 err:
00663     return ~0;
00664 }
00665 
00666 static int request_set_proto(request_t *rq, const char *proto)
00667 {
00668     dbg_err_if (rq == NULL);
00669     dbg_err_if (proto == NULL);
00670 
00671     /* be sure that the requested protocol is http */
00672     if(strncasecmp(proto, "http", 4))
00673         return ~0; /* unknown or unsupported protocol */
00674 
00675     REQUEST_SET_STRING_FIELD(rq->protocol, proto);
00676 
00677     return 0;
00678 err:
00679     return ~0;
00680 }
00681 
00682 /*
00683  * \ingroup request
00684  * \brief   Save client request
00685  *
00686  * Save client request line
00687  *
00688  * \param rq     request object
00689  * \param ln     the request line
00690  *
00691  * \return \c 0 if successful, non-zero on error
00692  */
00693 int request_set_client_request(request_t *rq, const char *ln)
00694 {
00695     char *p;
00696     dbg_err_if(rq == NULL);
00697     dbg_err_if(ln == NULL);
00698 
00699     rq->cli_rq = u_strdup(ln);
00700     dbg_err_if(rq->cli_rq == NULL);
00701 
00702     /* cut the trailing newline if any */
00703     for(p = rq->cli_rq; *p && (*p != '\r' && *p != '\n'); ++p)
00704         continue;
00705     *p = 0;
00706 
00707     return 0;
00708 err:
00709     return ~0;
00710 }
00711 
00722 const char *request_get_client_request(request_t *rq)
00723 {
00724     return rq->cli_rq;
00725 }
00726 
00727 /*
00728  * Set the \p method of request \p rq.  Refer to http.h for possible methods.
00729  *
00730  * \param rq     request object
00731  * \param method the HTTP method 
00732  *
00733  * \return \c 0 if successful, non-zero on error
00734  */
00735 int request_set_method(request_t *rq, const char *method)
00736 {
00737     dbg_return_if (rq == NULL, ~0);
00738     dbg_return_if (method == NULL, ~0);
00739 
00740     if(!strcasecmp(method, "get"))
00741         rq->method = HM_GET;
00742     else if(!strcasecmp(method, "head"))
00743         rq->method = HM_HEAD;
00744     else if(!strcasecmp(method, "post"))
00745         rq->method = HM_POST;
00746     else if(!strcasecmp(method, "put"))
00747         rq->method = HM_PUT;
00748     else if(!strcasecmp(method, "delete"))
00749         rq->method = HM_DELETE;
00750     else {
00751         /* put, delete, * */
00752         rq->method = HM_UNKNOWN;
00753         return ~0; /* unknown or unsupported method */
00754     }
00755     
00756     return 0;
00757 }
00758 
00759 static int request_set_content_length(request_t *rq)
00760 {
00761     const char *clen;
00762     size_t len;
00763 
00764     dbg_err_if (rq == NULL);
00765 
00766     clen = header_get_field_value(rq->header, "Content-Length");
00767     dbg_err_if(clen == NULL || (len = atoi(clen)) < 0);
00768 
00769     rq->content_length = len;
00770 
00771     return 0;
00772 err:
00773     return ~0;
00774 }
00775 
00776 static int request_parse_cookie(request_t *rq, field_t *field)
00777 {
00778     enum { BUFSZ = 4096 }; /* cookie size limit */
00779     char *pp, *tok, *src, buf[BUFSZ];
00780 
00781     dbg_err_if (rq == NULL);
00782     dbg_err_if (field == NULL);
00783     
00784     dbg_err_if(field_get_value(field) == NULL);
00785 
00786     /* save a copy to tokenize it */
00787     strncpy(buf, field_get_value(field), BUFSZ);
00788 
00789     /* foreach name=value pair... */
00790     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00791         dbg_if(vars_add_urlvar(rq->cookies, tok, NULL));
00792 
00793     return 0;
00794 err:
00795     return ~0;
00796 }
00797 
00798 static int request_parse_cookies(request_t *rq)
00799 {
00800     field_t *f;
00801     size_t i, count;
00802 
00803     dbg_err_if (rq == NULL);
00804     
00805     count = header_field_count(rq->header);
00806     for(i = 0; i < count; ++i)
00807     {
00808         f = header_get_fieldn(rq->header, i);
00809         dbg_err_if(f == NULL); /* shouldn't happen */
00810         if(strcasecmp(field_get_name(f), "cookie") == 0)
00811             dbg_err_if(request_parse_cookie(rq, f));
00812     }
00813 
00814     return 0;
00815 err:
00816     return ~0;
00817 }
00818 
00819 static int request_cb_add_var(vars_t *args, vars_t *also_to, const char *tok)
00820 {
00821     var_t *v = NULL;
00822 
00823     dbg_err_if(args == NULL);
00824     dbg_err_if(also_to == NULL);
00825     dbg_err_if(tok == NULL);
00826 
00827     /* may fail with bad var encoding (url?a=b&v); bad vars are ignored */
00828     dbg_if(vars_add_urlvar(args, tok, &v));
00829 
00830     if(v)
00831     {
00832         /* add the ptr also to the other vars list */
00833         dbg_err_if(vars_add(also_to, v));
00834     }
00835 
00836     return 0;
00837 err:
00838     return ~0;
00839 }
00840 
00841 
00842 static int request_cb_add_post_var(void *arg, const char *tok)
00843 {
00844     request_t *rq = (request_t*)arg;
00845 
00846     return request_cb_add_var(rq->args, rq->args_post, tok);
00847 }
00848 
00849 static int request_cb_add_get_var(void *arg, const char *tok)
00850 {
00851     request_t *rq = (request_t*)arg;
00852 
00853     return request_cb_add_var(rq->args, rq->args_get, tok);
00854 }
00855 
00856 static int foreach_query_var(const char *urlquery, int offset, 
00857         int(*cb)(void*,const char*), void *arg)
00858 {
00859     char *pp, *tok, *src, *query = NULL;
00860 
00861     dbg_err_if(offset < 0);
00862     dbg_err_if(cb == NULL);
00863 
00864     if(!urlquery)
00865         return 0; /* no args */
00866 
00867     /* dup to tokenize it */
00868     query = u_strdup(urlquery + offset);
00869     dbg_err_if(query == NULL);
00870 
00871     /* foreach name=value pair... */
00872     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00873     {
00874         /* call the callback that will save this var */
00875         dbg_err_if(cb(arg, tok));
00876     }
00877 
00878     U_FREE(query);
00879 
00880     return 0;
00881 err:
00882     U_FREE(query);
00883     return ~0;
00884 }
00885 
00886 static int request_parse_query_args(request_t *rq)
00887 {
00888     dbg_err_if(rq == NULL);
00889 
00890     return foreach_query_var(rq->query, 0, request_cb_add_get_var, (void*)rq); 
00891 err:
00892     return ~0;
00893 }
00894 
00895 /* set is-cgi flag */
00896 void request_set_cgi(request_t *rq, int cgi)
00897 {
00898     rq->cgi = cgi;
00899     return;
00900 }
00901 
00913 ssize_t request_get_content_length(request_t *rq)
00914 {
00915     dbg_return_if (rq == NULL, -1);
00916 
00917     return (ssize_t) rq->content_length;
00918 }
00919 
00920 static int match_content_type(header_t *h, const char *mime_type)
00921 {
00922     const char *ct;
00923 
00924     dbg_return_if (h == NULL, 0);
00925     dbg_return_if (mime_type == NULL, 0);
00926 
00927     ct = header_get_field_value(h, "Content-Type");
00928     if(ct == NULL || strncasecmp(ct, mime_type, strlen(mime_type)))
00929         return 0;
00930 
00931     return 1;
00932 }
00933 
00934 static int request_is_content_type(request_t *rq, const char *ct)
00935 {
00936     return match_content_type(rq->header, ct);
00937 }
00938 
00939 static int request_is_multipart_formdata(request_t *rq)
00940 {
00941     return request_is_content_type(rq, "multipart/form-data");
00942 }
00943 
00944 static int request_is_urlencoded(request_t *rq)
00945 {
00946     if(header_get_field_value(rq->header, "Content-Type") == NULL)
00947         return 1; /* yes; no content-type field provided */
00948 
00949     if(request_is_content_type(rq, "application/x-www-form-urlencoded"))
00950         return 1; /* yes */
00951 
00952     return 0; /* no */
00953 }
00954 
00955 static int request_parse_urlencoded_data(request_t *rq)
00956 {
00957     ssize_t qsz, len;
00958 
00959     dbg_err_if (rq == NULL);
00960 
00961     len = rq->content_length; /* shortcut */
00962 
00963     qsz = (rq->query ? strlen(rq->query) : 0);
00964 
00965     /* alloc or enlarge the query string buffer */
00966     rq->query = u_realloc(rq->query, len + qsz + 2);
00967     dbg_err_if(rq->query == NULL);
00968 
00969     /* dbg("rq->query %x  size %u", rq->query, len+qsz+2); */
00970 
00971     rq->query[qsz] = 0; /* must be zero-term for strcat to work */
00972     if(qsz)
00973     {   /* append a '&' */
00974         strcat(rq->query, "&");
00975         ++qsz;
00976     }
00977 
00978     /* append to current query string */
00979     dbg_err_if(io_read(rq->io, rq->query + qsz, len) != len);
00980 
00981     /* zero terminate it */
00982     rq->query[qsz + len] = 0;
00983 
00984     /* parse and add post vars to the rq->args and rq->args_post array */
00985     dbg_err_if(foreach_query_var(rq->query, qsz, 
00986                 request_cb_add_post_var, (void*)rq));
00987 
00988     return 0;
00989 err:
00990     return ~0;
00991 }
00992 
00993 /* return the value of the param named 'param_name' of the field 'field_name'
00994    and save it to 'buffer' */
00995 static int request_get_fieldparam(request_t *rq, const char *field_name, 
00996     const char *param_name, char *buf, size_t size)
00997 {
00998     const char *param_value, *field_value, *p;
00999     size_t pv_len;
01000 
01001     dbg_err_if (rq == NULL);
01002     dbg_err_if (field_name == NULL);
01003     dbg_err_if (param_name == NULL);
01004     dbg_err_if (buf == NULL);
01005     dbg_err_if (size == 0);
01006 
01007     field_value = header_get_field_value(rq->header, field_name);
01008     dbg_err_if(field_value == NULL);
01009 
01010     /* look for param name=value pair */
01011     param_value = u_stristr(field_value, param_name);
01012     dbg_err_if(param_value == NULL);
01013 
01014     /* skip param name */
01015     param_value += strlen(param_name);
01016 
01017     /* first char must be an equal sign */
01018     dbg_err_if(*param_value++ != '=');
01019 
01020     /* a param value ends on the first ';', space or at the end of string */
01021     for(p = param_value; ;++p)
01022         if(*p == '\0' || *p == ';' || isspace(*p))
01023             break; /* end of param value */
01024 
01025     /* param value len */
01026     pv_len = p - param_value;
01027 
01028     /* boundary check */
01029     dbg_err_if(pv_len > size - 1); 
01030 
01031     /* copy out the param value */
01032     strncpy(buf, param_value, pv_len);
01033     buf[MIN(pv_len, size - 1)] = 0;
01034 
01035     return 0;
01036 err:
01037     return ~0;
01038 }
01039 
01040 static int is_multipart_mixed(header_t *h)
01041 {
01042     return match_content_type(h, "multipart/mixed");
01043 }
01044 
01045 static int is_encoded(header_t *h)
01046 {
01047     const char *cte;
01048 
01049     dbg_return_if (h == NULL, 0);
01050 
01051     if((cte = header_get_field_value(h, "Content-Transfer-Encoding")) == NULL)
01052         return 0; /* not encoded */
01053 
01054     if(strcasecmp(cte, "binary") == 0)
01055         return 0; /* not encoded */
01056 
01057     return 1; /* encoded */
01058 }
01059 
01060 static inline int is_nl(char c)
01061 {
01062     return (c == '\n' || c == '\r' ? c : 0);
01063 }
01064 
01065 static inline int is_quote(char c)
01066 {
01067     return (c == '"' || c == '\'' ? c : 0);
01068 }
01069 
01070 static int parse_content_disposition(header_t *h, char *name, char *filename,
01071     size_t prmsz)
01072 {
01073     enum { BUFSZ = 512 };
01074     char *pp, *tok, *src, buf[BUFSZ];
01075     size_t n_len, fn_len;
01076     const char *cd;
01077     int q;
01078 
01079     dbg_err_if (h == NULL);
01080     dbg_err_if (name == NULL);
01081     dbg_err_if (filename == NULL);
01082     dbg_err_if (prmsz == 0);
01083     
01084     cd = header_get_field_value(h, "Content-Disposition");
01085     dbg_err_if(cd == NULL);
01086 
01087     dbg_err_if(strlen(cd) >= BUFSZ);
01088 
01089     /* must start with form-data */
01090     dbg_err_if(strncmp(cd, "form-data", strlen("form-data")));
01091 
01092     name[0] = filename[0] = 0;
01093 
01094     /* save a copy to tokenize it */
01095     strncpy(buf, cd, BUFSZ);
01096 
01097     /* shortcut */
01098     n_len = strlen("name=");
01099     fn_len = strlen("filename=");
01100 
01101     /* foreach name=value pair... */
01102     for(src = buf; (tok = strtok_r(src, ";", &pp)) != NULL; src = NULL)
01103     {
01104         /* skip trailing blanks */
01105         while(isspace(*tok))
01106             ++tok;
01107 
01108         if(strncmp(tok, "form-data", strlen("form-data")) == 0)
01109             continue;   /* skip */
01110         else if(strncmp(tok, "name=", n_len) == 0) {
01111             /* skip the name part */
01112             tok += n_len;
01113 
01114             /* remove single or double quotes */
01115             if((q = is_quote(tok[0])) != 0)
01116                 ++tok;
01117             if(strlen(tok) && tok[strlen(tok) - 1] == q)
01118                 tok[strlen(tok) - 1] = 0;
01119 
01120             strncpy(name, tok, prmsz);
01121         } else if(strncmp(tok, "filename=", fn_len) == 0) {
01122             /* skip the filename part */
01123             tok += fn_len;
01124 
01125             /* remove single or double quotes */
01126             if((q = is_quote(tok[0])) != 0)
01127                 ++tok;
01128             if(strlen(tok) && tok[strlen(tok) - 1] == q)
01129                 tok[strlen(tok) - 1] = 0;
01130 
01131             strncpy(filename, tok, prmsz);
01132         } 
01133         /* else ignore unknown fields */
01134     }
01135             
01136     return 0;
01137 err:
01138     return ~0;
01139 }
01140 
01141 /* 
01142  * Read from io until obuf is full or until stop_at string is found.
01143  *
01144  * Boyer-Moore algorithm is used for efficiency. 
01145  *
01146  * Returns the number of bytes written to obuf 
01147  */
01148 static ssize_t read_until(io_t *io, const char *stop_at, char *obuf, 
01149     size_t size, int *found)
01150 {
01151     /* use this macro before accessing obuf[idx] elem. the macro will load from
01152        the given io enough bytes to access the required byte. if the buffer
01153        is too small (i.e. less then idx bytes long) the function will return */
01154     #define SETUP_BUF_ACCESS_AT(idx)                                        \
01155         if(idx >= wtot) {                                                   \
01156             if(idx >= size)                                                 \
01157                 return wtot; /* the output buffer is full */                \
01158                                                                             \
01159             /* we need to fetch some more bytes to access obuf[i] */        \
01160             dbg_err_if((rc = io_read(io, wbuf, idx + 1 - wtot)) < 0);       \
01161             if(rc == 0 || rc < idx + 1 - wtot)                              \
01162                 return wtot + rc; /* eof or short count */                  \
01163             wbuf += rc;                                                     \
01164             wtot += rc;                                                     \
01165         }
01166 
01167     int sa_len = strlen(stop_at);
01168     int i, t, shift[256], rc;
01169     unsigned char c;
01170     size_t wtot = 0;
01171     char *wbuf = obuf;
01172 
01173     dbg_err_if (io == NULL);
01174     dbg_err_if (stop_at == NULL);
01175     dbg_err_if (obuf == NULL);
01176     /* size may be 0 */
01177     dbg_err_if (found == NULL);
01178 
01179     for(i = 0; i < 256; ++i)  
01180         shift[i] = sa_len;
01181 
01182     for(i = 0; i < sa_len; ++i)
01183         shift[ (int)stop_at[i] ] = sa_len - i - 1;
01184 
01185     *found = 0;
01186 
01187     for(i = t = sa_len-1; t >= 0; --i, --t)
01188     {
01189         SETUP_BUF_ACCESS_AT(i);
01190 
01191         while((c = obuf[i]) != stop_at[t]) 
01192         {
01193             i += MAX(sa_len - t, shift[c]);
01194 
01195             SETUP_BUF_ACCESS_AT(i);
01196 
01197             t = sa_len - 1;
01198         }
01199     }
01200 
01201     *found = 1;
01202 
01203     /* found; obuf[i] is where the matching string is */
01204     return wtot;
01205 err:
01206     return -1;
01207 }
01208 
01209 
01228 vars_t *request_get_uploads(request_t *rq)
01229 {
01230     return rq->uploads;
01231 }
01232 
01233 /*
01234  * name:         form "name" <input> tag attribute value
01235  * filename:     name of the uploaded file provided by the client
01236  * tmp_filename: name on the temp file when the uploaded data has been saved
01237  *               on the local disk
01238  * mime_type:    uploaded MIME type as stated by the browser (may be NULL)
01239  */
01240 static int request_add_uploaded_file(request_t *rq, const char *name, 
01241     const char *filename, const char *tmp_filename, const char *mime_type)
01242 {
01243     struct stat st;
01244     var_t *v = NULL;
01245     upload_info_t *info = NULL;
01246 
01247     dbg_err_if (rq == NULL);
01248     dbg_err_if (name == NULL);
01249     /* filename may be NULL */
01250     dbg_err_if (tmp_filename == NULL);
01251     /* mime_type may be NULL */
01252 
01253     dbg_err_sif (stat(tmp_filename, &st) < 0);
01254 
01255     /* create a new var obj */
01256     dbg_err_if(var_create(name, tmp_filename, &v));
01257 
01258     /* alloc an info struct to attach to the var_t obj */
01259     dbg_err_if((info = u_zalloc(sizeof(upload_info_t))) == NULL);
01260 
01261     /* set info data */
01262     info->size = st.st_size;
01263     if(mime_type)
01264         snprintf(info->mime_type, MIME_TYPE_BUFSZ, "%s", mime_type);
01265     else
01266         info->mime_type[0] = 0;
01267 
01268     if(filename)
01269         snprintf(info->filename, U_FILENAME_MAX, "%s", filename);
01270 
01271     /* attach info to v */
01272     var_set_opaque(v, info);
01273     info = NULL;
01274 
01275     /* push into the cookie list */
01276     dbg_err_if(vars_add(rq->uploads, v));
01277 
01278     return 0;
01279 err:
01280     if(info)
01281         U_FREE(info);
01282     if(v)
01283         var_free(v);
01284     return ~0;
01285 }
01286 
01287 static int request_get_uploaded_filev(request_t *rq, var_t *v,
01288     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01289     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01290 {           
01291     upload_info_t *info;
01292     const char *tmp_fqn;
01293 
01294     dbg_err_if (rq == NULL);
01295     dbg_err_if (v == NULL);
01296     dbg_err_if (local_filename == NULL);
01297     dbg_err_if (client_filename == NULL);
01298     dbg_err_if (mime_type == NULL);
01299     dbg_err_if (file_size == NULL);
01300 
01301     info = var_get_opaque(v);
01302     dbg_err_if(info == NULL);
01303 
01304     tmp_fqn = var_get_value(v);
01305     dbg_err_if(tmp_fqn == NULL);
01306 
01307     /* copy out return values */
01308     strncpy(local_filename, tmp_fqn, U_FILENAME_MAX);
01309     strncpy(mime_type, info->mime_type, MIME_TYPE_BUFSZ);
01310     strncpy(client_filename, info->filename, U_FILENAME_MAX);
01311     *file_size = info->size;
01312 
01313     return 0;
01314 err:
01315     return ~0;
01316 }
01317 
01340 int request_get_uploaded_file(request_t *rq, const char *name, size_t idx,
01341     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01342     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01343 {
01344     var_t *v;
01345 
01346     dbg_err_if (rq == NULL);
01347     dbg_err_if (name == NULL);
01348     dbg_err_if (idx >= vars_count(rq->uploads));
01349     dbg_err_if (local_filename == NULL);
01350     dbg_err_if (client_filename == NULL);
01351     dbg_err_if (mime_type == NULL);
01352     dbg_err_if (file_size == NULL);
01353 
01354     v = vars_geti(rq->uploads, name, idx);
01355     dbg_err_if(v == NULL);
01356 
01357     return request_get_uploaded_filev(rq, v, local_filename, client_filename,
01358         mime_type, file_size);
01359 err:
01360     return ~0;
01361 }
01362 
01363 static ssize_t request_read_until_boundary(request_t *rq, io_t *io, 
01364         const char *boundary, char *buf, size_t size, u_buf_t **pubuf)
01365 {
01366     u_buf_t *ubuf = NULL;
01367     int found;
01368     size_t bound_len, trb;
01369     ssize_t rc;
01370 
01371     /* shortcut */
01372     bound_len = strlen(boundary);
01373 
01374     trb = 0;
01375 
01376     for(found = 0; !found; /* nothing */)
01377     {
01378         rc = read_until(io, boundary, buf, size, &found);
01379         dbg_err_if(rc <= 0); /* on error or eof exit */
01380 
01381         /* write all but the last bound_len + 2 (\r\n) bytes */
01382         if(found)
01383         {
01384             rc -= (bound_len + 2);
01385             dbg_err_if(rc < 0);
01386 
01387             /* zero-term the buffer (removing the boundary) */
01388             buf[rc] = 0;
01389 
01390         } else {
01391 
01392             /* buffer too small, alloc (and use) a dynamic buffer */
01393             if(ubuf == NULL)
01394             {
01395                 dbg_err_if(u_buf_create(&ubuf));
01396                 dbg_err_if(u_buf_reserve(ubuf, 2 * size));
01397             }
01398         }
01399 
01400         /* total read bytes */
01401         trb += rc;
01402 
01403         warn_err_ifm(trb > rq->post_maxsize, "POST data exceed post_maxsize");
01404 
01405         /* if we're using the buf on the heap then append read data */
01406         if(ubuf)
01407             dbg_err_if(u_buf_append(ubuf, buf, rc));
01408     }
01409 
01410     *pubuf = ubuf;
01411 
01412     return (ubuf ? u_buf_len(ubuf) : rc);
01413 err:
01414     if(ubuf)
01415         u_buf_free(ubuf);
01416     return -1;
01417 
01418 }
01419 
01420 static int request_parse_multipart_chunk(request_t *rq, io_t *io, 
01421     const char *boundary, int *eof)
01422 {
01423     enum { PRMSZ = 512, BUFSZ = 4096 };
01424     header_t *h = NULL;
01425     io_t *tmpio = NULL;
01426     var_t *v = NULL;
01427     u_buf_t *ubuf = NULL;
01428     char name[PRMSZ], filename[PRMSZ], buf[BUFSZ];
01429     size_t bound_len;
01430     int found;
01431     ssize_t rc;
01432 
01433     /* create an header object to parse MIME part headers */
01434     dbg_err_if(header_create(&h));
01435 
01436     /* read header lines until the first blank line */
01437     dbg_err_if(header_load(h, io));
01438 
01439     warn_err_ifm(is_multipart_mixed(h), 
01440         "multipart/mixed content is not supported yet");
01441 
01442     /* HTTP should never use cte */
01443     warn_err_ifm(is_encoded(h), 
01444         "encoded file upload is not supported");
01445 
01446     dbg_err_if(parse_content_disposition(h, name, filename, PRMSZ));
01447 
01448     /* shortcut */
01449     bound_len = strlen(boundary);
01450 
01451     if(filename[0] != '\0')
01452     {
01453         dbg_err_if(BUFSZ <= bound_len);
01454 
01455         /* open a temporary file to dump uploaded data */
01456         dbg_err_if(u_tmpfile_open(rq->temp_dir, &tmpio));
01457 
01458         for(found = 0; !found; /* nothing */)
01459         {
01460             rc = read_until(io, boundary, buf, BUFSZ, &found);
01461             dbg_err_if(rc <= 0); /* on error or eof exit */
01462 
01463             /* write all but the last bound_len + 2 (\r\n) bytes */
01464             if(found)
01465             {
01466                 rc -= (bound_len + 2);
01467                 dbg_err_if(rc < 0);
01468             }
01469 
01470             /* write to the temp file */
01471             dbg_err_if(io_write(tmpio, buf, rc) < 0);
01472         }
01473 
01474         /* save the path/name of the tmp file to buf */
01475         dbg_err_if(io_name_get(tmpio, buf, BUFSZ));
01476 
01477         /* flush and free */
01478         io_free(tmpio); tmpio = NULL;
01479 
01480         /* add this file to the uploaded file list */
01481         dbg_err_if(request_add_uploaded_file(rq, name, filename, buf, 
01482             header_get_field_value(h, "Content-Type")));
01483 
01484         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01485         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01486 
01487         if(strncmp(buf, "--", 2) == 0)
01488             *eof = 1; /* end of MIME stuff */
01489 
01490     } else {
01491         /* read data before the boundary into the buffer. if the buffer is too 
01492            small the function will return all data in a new ubuf that must be 
01493            freed by the caller */
01494         rc = request_read_until_boundary(rq, io, boundary, buf, BUFSZ, &ubuf);
01495         dbg_err_if(rc < 0);
01496 
01497         /* add a new binary var to request arguments list */
01498         dbg_err_if(var_bin_create(name, 
01499                     (ubuf ? u_buf_ptr(ubuf) : buf), 
01500                     (ubuf ? u_buf_len(ubuf) : rc), 
01501                     &v));
01502 
01503         dbg_if(vars_add(rq->args, v));
01504 
01505         /* also add it to the post array */
01506         dbg_if(vars_add(rq->args_post, v));
01507 
01508         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01509         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01510 
01511         if(strncmp(buf, "--", 2) == 0)
01512             *eof = 1; /* end of MIME stuff */
01513     }
01514 
01515     if(ubuf)
01516         u_buf_free(ubuf);
01517 
01518     header_free(h);
01519 
01520     return 0;
01521 err:
01522     if(ubuf)
01523         u_buf_free(ubuf);
01524     if(tmpio)
01525         io_free(tmpio);
01526     if(h)
01527         header_free(h);
01528     return ~0;
01529 }
01530 
01531 static int request_parse_multipart_data(request_t *rq)
01532 {
01533     enum { BOUNDARY_BUFSZ = 128, BUFSZ = 1024 }; 
01534     char boundary[BOUNDARY_BUFSZ], buf[BUFSZ];
01535     int eof;
01536 
01537     /* boundaries always start with -- */
01538     strcpy(boundary, "--");
01539 
01540     dbg_err_if(request_get_fieldparam(rq, "Content-Type", "boundary",
01541         boundary + 2, BOUNDARY_BUFSZ - 2));
01542 
01543     dbg_err_if(strlen(boundary) == 0);
01544 
01545     /* skip the MIME preamble (usually not used in HTTP) */
01546     for(;;)
01547     {
01548         dbg_err_if(io_gets(rq->io, buf, BUFSZ) <= 0);
01549         if(!strncmp(buf, boundary, strlen(boundary)))
01550             break; /* boundary found */
01551     }
01552 
01553     /* cycle on each MIME part */
01554     for(eof = 0; eof == 0; )
01555         dbg_err_if(request_parse_multipart_chunk(rq, rq->io, boundary, &eof));
01556 
01557     return 0;
01558 err:
01559     return ~0;
01560 }
01561 
01562 static int request_cb_close_socket(talarm_t *al, void *arg)
01563 {
01564     io_t *io = (io_t*)arg;
01565 
01566     u_unused_args(al);
01567 
01568     warn("[%x] connection timed out, closing", io);
01569     
01570     /* close the stream (but not free it) */
01571     io_close(io);
01572 
01573     return 0;
01574 }
01575 
01576 int request_parse_data(request_t *rq)
01577 {
01578     talarm_t *al = NULL;
01579     int rc = HTTP_STATUS_BAD_REQUEST;
01580 
01581     if(rq->method == HM_POST)
01582     {
01583         /* some vars may be urlencoded */
01584         dbg_err_if(request_parse_query_args(rq));
01585 
01586         /* Content-Length is required when using POST */
01587         dbg_err_if(request_set_content_length(rq) && 
01588             (rc = HTTP_STATUS_LENGTH_REQUIRED));
01589 
01590         if(rq->content_length == 0)
01591             return 0; /* no data posted */
01592 
01593         /* set a timeout to abort POST if it takes too long ... */
01594         dbg_err_if(timerm_add(rq->post_timeout, request_cb_close_socket, 
01595             (void*)rq->io, &al));
01596 
01597         /* abort if the client is pushing too much data */
01598         dbg_err_if(rq->content_length > rq->post_maxsize &&
01599             (rc = HTTP_STATUS_REQUEST_TOO_LARGE));
01600 
01601         if(request_is_multipart_formdata(rq))
01602         { 
01603             /* <form enctype="multipart/form-data" ...> */
01604             dbg_err_if(request_parse_multipart_data(rq));
01605         } else if(request_is_urlencoded(rq)) {
01606             /* if there's no content-type field or
01607                <form [enctype="application/x-www-form-urlencoded"] ...> */
01608             dbg_err_if(request_parse_urlencoded_data(rq));
01609         } else {
01610             /* nothing to do, the user will handle incoming data in his .kl1 */
01611         }
01612 
01613         /* post timeout not expired, clear it */
01614         dbg_if(timerm_del(al)); al = NULL;
01615     } else {
01616         /* parse urlencoded variables and set var_t* array */
01617         dbg_err_if(request_parse_query_args(rq));
01618     }
01619 
01620     return 0;
01621 err:
01622     if(al)
01623         dbg_if(timerm_del(al));
01624     return rc;
01625 }
01626 
01627 /* cache data found in broker_is_valid_uri so we don't have to look for it 
01628    again in broker_serve */
01629 void request_set_sup_info(request_t *rq, supplier_t *sup, void *handle, 
01630         time_t mtime)
01631 {
01632     rq->si_sup = sup;
01633     rq->si_handle = handle;
01634     rq->si_mtime = mtime;
01635 
01636     return;
01637 }
01638 
01639 void request_get_sup_info(request_t *rq, supplier_t **psup, void **phandle, 
01640         time_t *pmtime)
01641 {
01642     if(psup)
01643         *psup = rq->si_sup;
01644 
01645     if(phandle)
01646         *phandle = rq->si_handle;
01647 
01648     if(pmtime)
01649         *pmtime = rq->si_mtime;
01650 
01651     return;
01652 }
01653 
01654 /*
01655  * Parse request object \p rq.
01656  *
01657  * \param rq            request object
01658  * \param is_valid_uri  URI validation function
01659  * \param arg           argument to is_valid_uri
01660  *
01661  * \return \c 0 if successful, non-zero on error
01662  */
01663 int request_parse_header(request_t *rq, 
01664         int (*is_valid_uri)(void*, const char *, size_t),
01665         void* arg)
01666 {
01667     enum { BUFSZ = 4096 };
01668     const char WP[] = " \t\r\n";
01669     char ln[BUFSZ], *pp, *method, *uri, *proto;
01670     talarm_t *al = NULL;
01671     
01672     dbg_err_if (rq == NULL);
01673     dbg_err_if (rq->io == NULL); /* must call rq_bind before rq_parse */
01674 
01675     /* wait at most N seconds to receive the request */
01676     dbg_err_if(timerm_add(rq->idle_timeout, request_cb_close_socket, 
01677         (void*)rq->io, &al));
01678 
01679     if(!rq->cgi)
01680     {
01681         /* cp the first line */
01682         dbg_err_if(io_gets(rq->io, ln, BUFSZ) <= 0);
01683 
01684         /* save the verbatim request line */
01685         dbg_err_if(request_set_client_request(rq, ln));
01686 
01687         method = strtok_r(ln, WP, &pp); 
01688         dbg_err_if(!method || request_set_method(rq, method));
01689 
01690         uri = strtok_r(NULL, WP, &pp);
01691         dbg_err_if(!uri || request_set_uri(rq, uri, is_valid_uri, arg));
01692 
01693         /* HTTP/0.9 not supported yet */ 
01694         proto = strtok_r(NULL, WP, &pp);
01695         dbg_err_if(!proto || request_set_proto(rq, proto)); 
01696 
01697         dbg_err_if(header_load(rq->header, rq->io));
01698     } else {
01699         dbg_err_if(header_load_from_cgienv(rq->header));
01700     }
01701 
01702     /* set if-modified-since time_t value */
01703     dbg_err_if(request_parse_ims(rq));
01704 
01705     /* parse "Cookie:" fields and set the cookies vars_t */
01706     dbg_err_if(request_parse_cookies(rq));
01707 
01708     /* Content-Length is required when using POST */
01709     if(request_get_method(rq) == HM_POST)
01710         dbg_err_if(request_set_content_length(rq));
01711 
01712     /* idle timeout not expired, clear it */
01713     dbg_if(timerm_del(al)); al = NULL;
01714 
01715     return 0;
01716 err:
01717     if(al)
01718         timerm_del(al);
01719     return ~0;
01720 }
01721 
01733 int request_get_method(request_t *rq)
01734 {
01735     dbg_return_if (rq == NULL, HM_UNKNOWN);
01736 
01737     return rq->method;
01738 }
01739 
01751 const char* request_get_protocol(request_t *rq)
01752 {
01753     dbg_return_if (rq == NULL, "unknown");
01754 
01755     return rq->protocol;
01756 }
01757 
01768 const char *request_get_resolved_filename(request_t *rq)
01769 {
01770     dbg_return_if (rq == NULL, NULL);
01771 
01772     return rq->resolved_filename;
01773 }
01774 
01785 const char *request_get_resolved_path_info(request_t *rq)
01786 {
01787     dbg_return_if (rq == NULL, NULL);
01788 
01789     return rq->resolved_path_info;
01790 }
01791 
01792 int request_print(request_t *rq)
01793 {
01794     dbg_return_if (rq == NULL, ~0);
01795 
01796     dbg("method: %u", rq->method);
01797     dbg("uri: %s", rq->uri);
01798     dbg("proto: %s", rq->protocol);
01799     dbg("filename: %s", rq->filename);
01800     dbg("resolved filename: %s", rq->resolved_filename);
01801     dbg("path_info: %s", rq->path_info);
01802     dbg("resolved path_info: %s", rq->resolved_path_info);
01803     dbg("query: %s", rq->query);
01804 
01805     return 0;
01806 }
01807 
01808 static int request_load_config(request_t *rq)
01809 {
01810     u_config_t *c;
01811     vhost_t *vhost;
01812     const char *v;
01813 
01814     dbg_err_if (rq == NULL);
01815     dbg_err_if (rq->http == NULL);
01816 
01817     dbg_err_if((vhost = http_get_vhost(rq->http, rq)) == NULL);
01818 
01819     /* vhost can override those fields */
01820     dbg_err_if((c = vhost->config) == NULL);
01821     
01822     /* defaults */
01823     rq->idle_timeout = REQUEST_DEFAULT_IDLE_TIMEOUT;
01824     rq->post_timeout = REQUEST_DEFAULT_POST_TIMEOUT;
01825     rq->post_maxsize = REQUEST_DEFAULT_POST_MAXSIZE;
01826     rq->temp_dir = NULL;    /* use system default */
01827 
01828     /* idle timeout */
01829     if((v = u_config_get_subkey_value(c, "idle_timeout")) != NULL)
01830         rq->idle_timeout = MAX(1, atoi(v));
01831 
01832     /* post timeout */
01833     if((v = u_config_get_subkey_value(c, "post_timeout")) != NULL)
01834         rq->post_timeout = MAX(5, atoi(v));
01835 
01836     /* post maxsize */
01837     if((v = u_config_get_subkey_value(c, "post_maxsize")) != NULL)
01838         rq->post_maxsize = MAX(1024, atoi(v));
01839 
01840     /* temp dir to use on file upload.  when 'NULL' use system default */
01841     rq->temp_dir = u_config_get_subkey_value(c, "temp_dir");
01842 
01843     return 0;
01844 err:
01845     return ~0;
01846 }
01847 
01848 int request_create(http_t *http, request_t **prq)
01849 {
01850     request_t *rq = NULL;
01851 
01852     dbg_return_if (prq == NULL, ~0);
01853     dbg_return_if (http == NULL, ~0);
01854 
01855     rq = u_zalloc(sizeof(request_t));
01856     dbg_err_if(rq == NULL);
01857 
01858     dbg_err_if(header_create(&rq->header));
01859 
01860     dbg_err_if(vars_create(&rq->args));
01861     dbg_err_if(vars_create(&rq->cookies));
01862     dbg_err_if(vars_create(&rq->uploads));
01863 
01864     dbg_err_if(vars_create(&rq->args_get));
01865     dbg_err_if(vars_create(&rq->args_post));
01866 
01867     /* args_get and args_post link to var_t owned by the rq->args list */
01868     dbg_err_if(vars_set_flags(rq->args_get, VARS_FLAG_FOREIGN));
01869     dbg_err_if(vars_set_flags(rq->args_post, VARS_FLAG_FOREIGN));
01870 
01871     rq->http = http;
01872 
01873     dbg_err_if(request_load_config(rq));
01874 
01875     *prq = rq;
01876 
01877     return 0;
01878 err:
01879     if(rq)
01880         request_free(rq);
01881     return ~0;
01882 }
01883 
01884 static int request_unlink_uploads(var_t *v, void * arg)
01885 {
01886     dbg_err_if (v == NULL);
01887 
01888     u_unused_args(arg);
01889 
01890     if(var_get_opaque(v) && var_get_value(v))
01891     {   /* it's a file var, unlink v->value */
01892         u_remove(var_get_value(v));
01893     }
01894 
01895 err:
01896     return 0;
01897 }
01898 
01899 int request_free(request_t *rq)
01900 {
01901     if (rq)
01902     {
01903         /* free internal stuff */
01904         request_clear_uri(rq);
01905 
01906         /* verbatim client request line */
01907         U_FREE(rq->cli_rq);
01908 
01909         if(rq->header)
01910             header_free(rq->header);
01911 
01912         if(rq->io)
01913             io_free(rq->io);
01914 
01915         if(rq->uploads)
01916         {
01917             /* unlink uploaded files (if any) */
01918             vars_foreach(rq->uploads, request_unlink_uploads, NULL);
01919             vars_free(rq->uploads);
01920         }
01921 
01922         if(rq->cookies)
01923             vars_free(rq->cookies);
01924 
01925         if(rq->args_get)
01926             vars_free(rq->args_get);
01927 
01928         if(rq->args_post)
01929             vars_free(rq->args_post);
01930 
01931         if(rq->args)
01932             vars_free(rq->args);
01933 
01934         U_FREE(rq);
01935     }
01936 
01937     return 0;
01938 }
01939 
01940 /* save the local address struct (ip and port) in the request obj */
01941 int request_set_addr(request_t *rq, kaddr_t *addr)
01942 {
01943     dbg_return_if (rq == NULL, ~0);
01944     dbg_return_if (addr == NULL, ~0);
01945 
01946     memcpy(&rq->local_addr, addr, sizeof(kaddr_t));
01947 
01948     return 0;
01949 }
01950 
01951 /* save the peer address struct (ip and port) in the request obj */
01952 int request_set_peer_addr(request_t *rq, kaddr_t *addr)
01953 {
01954     dbg_return_if (rq == NULL, ~0);
01955     dbg_return_if (addr == NULL, ~0);
01956 
01957     memcpy(&rq->peer_addr, addr, sizeof(kaddr_t));
01958 
01959     return 0;
01960 }
01961 
01972 kaddr_t* request_get_addr(request_t *rq)
01973 {
01974     dbg_return_if (rq == NULL, NULL);
01975 
01976     return &rq->local_addr;
01977 }
01978 
01989 kaddr_t* request_get_peer_addr(request_t *rq)
01990 {
01991     dbg_return_if (rq == NULL, NULL);
01992 
01993     return &rq->peer_addr;
01994 }
01995 
02007 header_t* request_get_header(request_t *rq)
02008 {
02009     dbg_return_if (rq == NULL, NULL);
02010 
02011     return rq->header;
02012 }
02013 
02025 field_t* request_get_field(request_t *rq, const char *name)
02026 {
02027     dbg_return_if (rq == NULL, NULL);
02028     dbg_return_if (name == NULL, NULL);
02029 
02030     return header_get_field(rq->header, name);
02031 }
02032 
02044 const char* request_get_field_value(request_t *rq, const char *name)
02045 {
02046     dbg_return_if (rq == NULL, NULL);
02047     dbg_return_if (name == NULL, NULL);
02048 
02049     return header_get_field_value(rq->header, name);
02050 }
02051 
02052 vhost_t* request_get_vhost(request_t *rq)
02053 {
02054     dbg_return_if (rq == NULL, NULL);
02055 
02056     return rq->vhost; /* may be NULL */
02057 }
02058 
02059 int request_set_vhost(request_t *rq, vhost_t *vhost)
02060 {
02061     dbg_return_if (rq == NULL, ~0);
02062 
02063     rq->vhost = vhost;
02064 
02065     return 0;
02066 }
02067