request.c

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

←Products
Copyright © 2005-2012 - KoanLogic S.r.l. - All rights reserved