Main Page | Modules | Data Structures | Directories | 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.32 2006/05/21 18:22:16 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <ctype.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <u/libu.h>
00018 #include <klone/request.h>
00019 #include <klone/utils.h>
00020 #include <klone/io.h>
00021 #include <klone/ioprv.h>
00022 #include <klone/http.h>
00023 #include <klone/addr.h>
00024 #include <klone/vars.h>
00025 #include <klone/timer.h>
00026 
00027 struct request_s
00028 {
00029     http_t *http;               /* http server handle                       */
00030     header_t *header;           /* input header                             */
00031     io_t *io;                   /* input io stream                          */
00032     int method;                 /* get,post,etc.                            */
00033     char *uri;                  /* verbatim uri asked by the client         */
00034     char *protocol;             /* proto/ver                                */
00035     char *path_info;            /* extra info at the end of the path        */
00036     char *query;                /* query string (data after '?')            */
00037     char *filename;             /* path of the req resource                 */
00038     char *host;                 /* Host: field                              */
00039     char *resolved_path_info;   /* resolved path_info                       */
00040     char *resolved_filename;    /* unaliased filename                       */
00041     vars_t *args;               /* get/post args                            */
00042     vars_t *cookies;            /* cookies                                  */
00043     vars_t *uploads;            /* uploaded file list                       */
00044     char *content_type;         /* type/subtype                             */
00045     char *content_encoding;     /* 7bit/8bit/base64/qp, etc                 */
00046         size_t content_length;      /* content-length http header field         */
00047     time_t if_modified_since;   /* time_t IMS header                        */
00048     addr_t local_addr, peer_addr; /* local and perr address                 */
00049     int cgi;                    /* if running in cgi mode                   */
00050     size_t idle_timeout;        /* max # of secs to wait for the request    */
00051     size_t post_timeout;        /* max # of secs for reading POSTed data    */
00052     size_t post_maxsize;        /* max # of POSTed bytes to accepts         */
00053 };
00054 
00055 typedef struct upload_info_s    /* uploaded file info struct         */
00056 {
00057     char mime_type[MIME_TYPE_BUFSZ];
00058     char filename[U_FILENAME_MAX];
00059     size_t size;
00060 } upload_info_t;
00061 
00062 enum { 
00063     REQUEST_DEFAULT_IDLE_TIMEOUT = 10,         /* 10 secs */
00064     REQUEST_DEFAULT_POST_TIMEOUT = 600,        /* 10 mins */
00065     REQUEST_DEFAULT_POST_MAXSIZE = 5*1024000,  /* 5 MB    */
00066 };
00067 
00068 
00069 #define REQUEST_SET_STRING_FIELD(lval, rval)        \
00070     do {                                            \
00071         U_FREE(lval);                               \
00072         if(rval)                                    \
00073         {                                           \
00074             lval = u_strdup(rval);                  \
00075             dbg_err_if(lval == NULL);               \
00076         }                                           \
00077     } while(0)
00078 
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 
00141 io_t *request_io(request_t *rq)
00142 {
00143     dbg_return_if (rq == NULL, NULL);
00144 
00145     return rq->io;
00146 }
00147 
00158 vars_t *request_get_cookies(request_t *rq)
00159 {
00160     dbg_return_if (rq == NULL, NULL);
00161 
00162     return rq->cookies;
00163 }
00164 
00175 const char *request_get_cookie(request_t *rq, const char *name)
00176 {
00177     var_t *v;
00178 
00179     dbg_return_if (rq == NULL, NULL);
00180     dbg_return_if (name == NULL, NULL);
00181 
00182     v = vars_get(rq->cookies, name);
00183 
00184     return v ? var_get_value(v): NULL;
00185 }
00186 
00196 vars_t *request_get_args(request_t *rq)
00197 {
00198     dbg_return_if (rq == NULL, NULL);
00199 
00200     return rq->args;
00201 }
00202 
00215 const char *request_get_arg(request_t *rq, const char *name)
00216 {
00217     var_t *v;
00218 
00219     dbg_return_if (rq == NULL, NULL);
00220     dbg_return_if (name == NULL, NULL);
00221 
00222     v = vars_get(rq->args, name);
00223 
00224     return v ? var_get_value(v): NULL;
00225 }
00226 
00238 int request_set_field(request_t *rq, const char *name, const char *value)
00239 {
00240     dbg_return_if (rq == NULL, ~0);
00241     dbg_return_if (name == NULL, ~0);
00242     dbg_return_if (value == NULL, ~0);
00243 
00244     return header_set_field(rq->header, name, value);
00245 }
00246 
00256 const char *request_get_uri(request_t *rq)
00257 {
00258     dbg_return_if (rq == NULL, NULL);
00259 
00260     return rq->uri;
00261 }
00262 
00272 const char *request_get_filename(request_t *rq)
00273 {
00274     dbg_return_if (rq == NULL, NULL);
00275 
00276     return rq->filename;
00277 }
00278 
00289 int request_set_filename(request_t *rq, const char *filename)
00290 {
00291     dbg_err_if (rq == NULL);
00292     dbg_err_if (filename == NULL);
00293     
00294     REQUEST_SET_STRING_FIELD(rq->filename, filename);
00295 
00296     return 0;
00297 err:
00298     return ~0;
00299 }
00300 
00310 const char *request_get_query_string(request_t *rq)
00311 {
00312     dbg_return_if (rq == NULL, NULL);
00313 
00314     return rq->query;
00315 }
00316 
00326 const char *request_get_path_info(request_t *rq)
00327 {
00328     dbg_return_if (rq == NULL, NULL);
00329 
00330     return rq->path_info;
00331 }
00332 
00333 /* parse and set if-modified-since value */
00334 static int request_parse_ims(request_t *rq)
00335 {
00336     const char *ims;
00337 
00338     dbg_err_if (rq == NULL);
00339     
00340     rq->if_modified_since = 0;
00341 
00342     ims = header_get_field_value(rq->header, "If-Modified-Since");
00343     if(ims)
00344         dbg_err_if(u_httpdate_to_tt(ims, &rq->if_modified_since));
00345 
00346 err: /* ignore if it's not formatted properly */
00347     return 0;
00348 }
00349 
00359 time_t request_get_if_modified_since(request_t *rq)
00360 {
00361     dbg_return_if (rq == NULL, (time_t) -1);
00362 
00363     return rq->if_modified_since;
00364 }
00365 
00376 int request_set_resolved_filename(request_t *rq, const char *resolved_fn)
00377 {
00378     dbg_err_if (rq == NULL);
00379     dbg_err_if (resolved_fn == NULL);
00380 
00381     REQUEST_SET_STRING_FIELD(rq->resolved_filename, resolved_fn);
00382 
00383     return 0;
00384 err:
00385     return ~0;
00386 }
00387 
00397 http_t* request_get_http(request_t *rq)
00398 {
00399     dbg_return_if (rq == NULL, NULL);
00400 
00401     return rq->http;
00402 }
00403 
00414 int request_bind(request_t *rq, io_t *in)
00415 {
00416     dbg_return_if (rq == NULL, ~0);
00417     dbg_return_if (in == NULL, ~0);
00418 
00419     rq->io = in;
00420 
00421     return 0;
00422 }
00423 
00434 int request_set_query_string(request_t *rq, const char *query)
00435 {
00436     dbg_err_if (rq == NULL);
00437     dbg_err_if (query == NULL);
00438     
00439     REQUEST_SET_STRING_FIELD(rq->query, query);
00440 
00441     return 0;
00442 err:
00443     return ~0;
00444 }
00445 
00455 void request_clear_uri(request_t *rq)
00456 {
00457     U_FREE(rq->uri);
00458     U_FREE(rq->protocol);
00459     U_FREE(rq->path_info);
00460     U_FREE(rq->query);
00461     U_FREE(rq->filename);
00462     U_FREE(rq->host);
00463     U_FREE(rq->resolved_path_info);
00464     U_FREE(rq->resolved_filename);
00465     U_FREE(rq->content_type);
00466     U_FREE(rq->content_encoding);
00467 }
00468 
00479 int request_set_path_info(request_t *rq, const char *path_info)
00480 {
00481     dbg_err_if (rq == NULL);
00482     dbg_err_if (path_info == NULL);
00483 
00484     REQUEST_SET_STRING_FIELD(rq->path_info, path_info);
00485 
00486     return 0;
00487 err:
00488     return ~0;
00489 }
00490 
00501 int request_set_resolved_path_info(request_t *rq, const char *resolved_pi)
00502 {
00503     dbg_err_if (rq == NULL);
00504     dbg_err_if (resolved_pi == NULL);
00505 
00506     REQUEST_SET_STRING_FIELD(rq->resolved_path_info, resolved_pi);
00507 
00508     return 0;
00509 err:
00510     return ~0;
00511 }
00512 
00513 /*
00514  * \brief   Set the URI field of a request
00515  *
00516  * Set the URI field of request \p rq to \p uri given 
00517  *
00518  * \param rq           request object
00519  * \param uri          URI string
00520  * \param is_valid_uri URI validation function 
00521  * \param arg          argument to is_valid_uri
00522  *
00523  * \return \c 0 if successful, non-zero on error
00524  */
00525 int request_set_uri(request_t *rq, const char *uri,
00526         int (*is_valid_uri)(void*, const char *, size_t),
00527         void* arg)
00528 {
00529     enum { REQUEST_URI_MAX_LENGTH = 4095 };
00530     char *p, *fn, *pi, *cp = NULL;
00531     size_t uri_len = strlen(uri);
00532 
00533     dbg_err_if (rq == NULL);
00534     dbg_err_if (uri == NULL);
00535     /* is_valid_uri may be NULL */
00536     /* arg may be NULL */
00537  
00538     request_clear_uri(rq);
00539 
00540     /* this is just to avoid recursive infinite redirect loops for pages that 
00541        appends something to the URI and redirects to the same page */
00542     warn_err_ifm(uri_len > REQUEST_URI_MAX_LENGTH, "Request URI too long");
00543 
00544     REQUEST_SET_STRING_FIELD(rq->uri, uri);
00545 
00546     /* save (undecoded) query string i.e. everything after '?' */
00547     if((p = strchr(uri, '?')) != NULL)
00548         dbg_err_if(request_set_query_string(rq, ++p));
00549 
00550     cp = (char*)u_malloc(uri_len + 1);
00551     dbg_err_if(cp == NULL);
00552 
00553     /* copy decoded url */
00554     dbg_err_if(u_urlncpy(cp, rq->uri, uri_len, URLCPY_DECODE) <= 0);
00555 
00556     if((p = strchr(cp, '?')) != NULL)
00557         *p++ = 0; /* remove query string from the uri copy */
00558 
00559     /* set filename is case there's not path_info and/or file does not exists */
00560     dbg_err_if(request_set_filename(rq, cp));
00561 
00562     /* look for path_info */
00563     fn = cp;                    /* filename     */
00564     pi = fn + strlen(fn);       /* path_info    */
00565     for(;;)
00566     {
00567         if(is_valid_uri == NULL || is_valid_uri(arg, fn, pi - fn))
00568         {
00569             dbg_err_if(request_set_filename(rq, fn));
00570             rq->filename[pi-fn] = 0; /* trunc */
00571             if(strlen(pi))
00572                 dbg_err_if(request_set_path_info(rq, pi));
00573             break;
00574         } else {
00575             if((p = u_strnrchr(fn, '/', pi - fn)) == NULL)
00576                 break; /* file pointed by this uri does not exists */
00577             pi = p; /* try again */
00578         }
00579     }
00580 
00581     U_FREE(cp);
00582 
00583     return 0;
00584 err:
00585     U_FREE(cp);
00586     return ~0;
00587 }
00588 
00589 static int request_set_proto(request_t *rq, const char *proto)
00590 {
00591     dbg_err_if (rq == NULL);
00592     dbg_err_if (proto == NULL);
00593 
00594     /* be sure that the requested protocol is http */
00595     if(strncasecmp(proto, "http", 4))
00596         return ~0; /* unknown or unsupported protocol */
00597 
00598     REQUEST_SET_STRING_FIELD(rq->protocol, proto);
00599 
00600     return 0;
00601 err:
00602     return ~0;
00603 }
00604 
00615 int request_set_method(request_t *rq, const char *method)
00616 {
00617     dbg_return_if (rq == NULL, ~0);
00618     dbg_return_if (method == NULL, ~0);
00619 
00620     if(!strcasecmp(method, "get"))
00621         rq->method = HM_GET;
00622     else if(!strcasecmp(method, "head"))
00623         rq->method = HM_HEAD;
00624     else if(!strcasecmp(method, "post"))
00625         rq->method = HM_POST;
00626     else {
00627         /* put, delete, * */
00628         rq->method = HM_UNKNOWN;
00629         return ~0; /* unknown or unsupported method */
00630     }
00631     
00632     return 0;
00633 }
00634 
00635 static int request_set_content_length(request_t *rq)
00636 {
00637     const char *clen;
00638     size_t len;
00639 
00640     dbg_err_if (rq == NULL);
00641 
00642     clen = header_get_field_value(rq->header, "Content-Length");
00643     dbg_err_if(clen == NULL || (len = atoi(clen)) <= 0);
00644 
00645     rq->content_length = len;
00646 
00647     return 0;
00648 err:
00649     return ~0;
00650 }
00651 
00652 static int request_parse_cookie(request_t *rq, field_t *field)
00653 {
00654     enum { BUFSZ = 4096 }; /* cookie size limit */
00655     char *pp, *tok, *src, buf[BUFSZ];
00656 
00657     dbg_err_if (rq == NULL);
00658     dbg_err_if (field == NULL);
00659     
00660     dbg_err_if(field_get_value(field) == NULL);
00661 
00662     /* save a copy to tokenize it */
00663     strncpy(buf, field_get_value(field), BUFSZ);
00664 
00665     /* foreach name=value pair... */
00666     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00667         dbg_if(vars_add_urlvar(rq->cookies, tok, NULL));
00668 
00669     return 0;
00670 err:
00671     return ~0;
00672 }
00673 
00674 static int request_parse_cookies(request_t *rq)
00675 {
00676     field_t *f;
00677     size_t i, count;
00678 
00679     dbg_err_if (rq == NULL);
00680     
00681     count = header_field_count(rq->header);
00682     for(i = 0; i < count; ++i)
00683     {
00684         f = header_get_fieldn(rq->header, i);
00685         dbg_err_if(f == NULL); /* shouldn't happen */
00686         if(strcasecmp(field_get_name(f), "cookie") == 0)
00687             dbg_err_if(request_parse_cookie(rq, f));
00688     }
00689 
00690     return 0;
00691 err:
00692     return ~0;
00693 }
00694 
00695 static int request_parse_query_args(request_t *rq)
00696 {
00697     char *pp, *tok, *src, *query = NULL;
00698 
00699     dbg_err_if (rq == NULL);
00700 
00701     if(!rq->query)
00702         return 0; /* no args */
00703 
00704     /* dup to tokenize it */
00705     query = u_strdup(rq->query);
00706     dbg_err_if(query == NULL);
00707 
00708     /* foreach name=value pair... */
00709     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00710     {
00711         /* create a new var_t obj and push it into the args vars-list */
00712         dbg_if(vars_add_urlvar(rq->args, tok, NULL));
00713     }
00714 
00715     U_FREE(query);
00716 
00717     return 0;
00718 err:
00719     U_FREE(query);
00720     return ~0;
00721 }
00722 
00723 /* set is-cgi flag */
00724 void request_set_cgi(request_t *rq, int cgi)
00725 {
00726     rq->cgi = cgi;
00727     return;
00728 }
00729 
00740 ssize_t request_get_content_length(request_t *rq)
00741 {
00742     dbg_return_if (rq == NULL, -1);
00743 
00744     return (ssize_t) rq->content_length;
00745 }
00746 
00747 static int match_content_type(header_t *h, const char *mime_type)
00748 {
00749     const char *ct;
00750 
00751     dbg_return_if (h == NULL, 0);
00752     dbg_return_if (mime_type == NULL, 0);
00753 
00754     ct = header_get_field_value(h, "Content-Type");
00755     if(ct == NULL || strncasecmp(ct, mime_type, strlen(mime_type)))
00756         return 0;
00757 
00758     return 1;
00759 }
00760 
00761 static int request_is_multipart_formdata(request_t *rq)
00762 {
00763     return match_content_type(rq->header, "multipart/form-data");
00764 }
00765 
00766 static int request_parse_urlencoded_data(request_t *rq)
00767 {
00768     ssize_t qsz, len;
00769 
00770     dbg_err_if (rq == NULL);
00771 
00772     len = rq->content_length; /* shortcut */
00773 
00774     qsz = (rq->query ? strlen(rq->query) : 0);
00775 
00776     /* alloc or enlarge the query string buffer */
00777     rq->query = u_realloc(rq->query, len + qsz + 2);
00778     dbg_err_if(rq->query == NULL);
00779 
00780     /* dbg("rq->query %x  size %u", rq->query, len+qsz+2); */
00781 
00782     rq->query[qsz] = 0; /* must be zero-term for strcat to work */
00783     if(qsz)
00784     {   /* append a '&' */
00785         strcat(rq->query, "&");
00786         ++qsz;
00787     }
00788 
00789     /* append to current query string */
00790     dbg_err_if(io_read(rq->io, rq->query + qsz, len) != len);
00791 
00792     /* zero terminate it */
00793     rq->query[qsz + len] = 0;
00794 
00795     /* parse rq->query and build the args var_t* array */
00796     dbg_err_if(request_parse_query_args(rq));
00797 
00798     return 0;
00799 err:
00800     return ~0;
00801 }
00802 
00803 /* return the value of the param named 'param_name' of the field 'field_name'
00804    and save it to 'buffer' */
00805 static int request_get_fieldparam(request_t *rq, const char *field_name, 
00806     const char *param_name, char *buf, size_t size)
00807 {
00808     const char *param_value, *field_value, *p;
00809     size_t pv_len;
00810 
00811     dbg_err_if (rq == NULL);
00812     dbg_err_if (field_name == NULL);
00813     dbg_err_if (param_name == NULL);
00814     dbg_err_if (buf == NULL);
00815     dbg_err_if (size == 0);
00816 
00817     field_value = header_get_field_value(rq->header, field_name);
00818     dbg_err_if(field_value == NULL);
00819 
00820     /* look for param name=value pair */
00821     param_value = u_stristr(field_value, param_name);
00822     dbg_err_if(param_value == NULL);
00823 
00824     /* skip param name */
00825     param_value += strlen(param_name);
00826 
00827     /* first char must be an equal sign */
00828     dbg_err_if(*param_value++ != '=');
00829 
00830     /* a param value ends on the first ';', space or at the end of string */
00831     for(p = param_value; ;++p)
00832         if(*p == '\0' || *p == ';' || isspace(*p))
00833             break; /* end of param value */
00834 
00835     /* param value len */
00836     pv_len = p - param_value;
00837 
00838     /* boundary check */
00839     dbg_err_if(pv_len > size - 1); 
00840 
00841     /* copy out the param value */
00842     strncpy(buf, param_value, pv_len);
00843     buf[MIN(pv_len, size - 1)] = 0;
00844 
00845     return 0;
00846 err:
00847     return ~0;
00848 }
00849 
00850 static int is_multipart_mixed(header_t *h)
00851 {
00852     return match_content_type(h, "multipart/mixed");
00853 }
00854 
00855 static int is_encoded(header_t *h)
00856 {
00857     const char *cte;
00858 
00859     dbg_return_if (h == NULL, 0);
00860 
00861     if((cte = header_get_field_value(h, "Content-Transfer-Encoding")) == NULL)
00862         return 0; /* not encoded */
00863 
00864     if(strcasecmp(cte, "binary") == 0)
00865         return 0; /* not encoded */
00866 
00867     return 1; /* encoded */
00868 }
00869 
00870 static inline int is_nl(char c)
00871 {
00872     return (c == '\n' || c == '\r' ? c : 0);
00873 }
00874 
00875 static inline int is_quote(char c)
00876 {
00877     return (c == '"' || c == '\'' ? c : 0);
00878 }
00879 
00880 static int parse_content_disposition(header_t *h, char *name, char *filename,
00881     size_t prmsz)
00882 {
00883     enum { BUFSZ = 512 };
00884     char *pp, *tok, *src, buf[BUFSZ];
00885     size_t n_len, fn_len;
00886     const char *cd;
00887     int q;
00888 
00889     dbg_err_if (h == NULL);
00890     dbg_err_if (name == NULL);
00891     dbg_err_if (filename == NULL);
00892     dbg_err_if (prmsz == 0);
00893     
00894     cd = header_get_field_value(h, "Content-Disposition");
00895     dbg_err_if(cd == NULL);
00896 
00897     dbg_err_if(strlen(cd) >= BUFSZ);
00898 
00899     /* must start with form-data */
00900     dbg_err_if(strncmp(cd, "form-data", strlen("form-data")));
00901 
00902     name[0] = filename[0] = 0;
00903 
00904     /* save a copy to tokenize it */
00905     strncpy(buf, cd, BUFSZ);
00906 
00907     /* shortcut */
00908     n_len = strlen("name=");
00909     fn_len = strlen("filename=");
00910 
00911     /* foreach name=value pair... */
00912     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00913     {
00914         if(strncmp(tok, "form-data", strlen("form-data")) == 0)
00915             continue;   /* skip */
00916         else if(strncmp(tok, "name=", n_len) == 0) {
00917             /* skip the name part */
00918             tok += n_len;
00919 
00920             /* remove single or double quotes */
00921             if((q = is_quote(tok[0])) != 0)
00922                 ++tok;
00923             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00924                 tok[strlen(tok) - 1] = 0;
00925 
00926             strncpy(name, tok, prmsz);
00927         } else if(strncmp(tok, "filename=", fn_len) == 0) {
00928             /* skip the filename part */
00929             tok += fn_len;
00930 
00931             /* remove single or double quotes */
00932             if((q = is_quote(tok[0])) != 0)
00933                 ++tok;
00934             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00935                 tok[strlen(tok) - 1] = 0;
00936 
00937             strncpy(filename, tok, prmsz);
00938         } 
00939         /* else ignore unknown fields */
00940     }
00941             
00942     return 0;
00943 err:
00944     return ~0;
00945 }
00946 
00947 /* 
00948  * Read from io until obuf is full or until stop_at string is found.
00949  *
00950  * Boyer-Moore algorithm is used for efficiency. 
00951  *
00952  * Returns the number of bytes written to obuf 
00953  */
00954 static ssize_t read_until(io_t *io, const char *stop_at, char *obuf, 
00955     size_t size, int *found)
00956 {
00957     /* use this macro before accessing obuf[idx] elem. the macro will load from
00958        the given io enough bytes to access the required byte. if the buffer
00959        is too small (i.e. less then idx bytes long) the function will return */
00960     #define SETUP_BUF_ACCESS_AT(idx)                                        \
00961         if(idx >= wtot) {                                                   \
00962             if(idx >= size)                                                 \
00963                 return wtot; /* the output buffer is full */                \
00964                                                                             \
00965             /* we need to fetch some more bytes to access obuf[i] */        \
00966             dbg_err_if((rc = io_read(io, wbuf, idx + 1 - wtot)) < 0);       \
00967             if(rc == 0 || rc < idx + 1 - wtot)                              \
00968                 return wtot + rc; /* eof or short count */                  \
00969             wbuf += rc;                                                     \
00970             wtot += rc;                                                     \
00971         }
00972 
00973     int sa_len = strlen(stop_at);
00974     int i, t, shift[256], rc;
00975     unsigned char c;
00976     size_t wtot = 0;
00977     char *wbuf = obuf;
00978 
00979     dbg_err_if (io == NULL);
00980     dbg_err_if (stop_at == NULL);
00981     dbg_err_if (obuf == NULL);
00982     /* size may be 0 */
00983     dbg_err_if (found == NULL);
00984 
00985     for(i = 0; i < 256; ++i)  
00986         shift[i] = sa_len;
00987 
00988     for(i = 0; i < sa_len; ++i)
00989         shift[ stop_at[i] ] = sa_len - i - 1;
00990 
00991     *found = 0;
00992 
00993     for(i = t = sa_len-1; t >= 0; --i, --t)
00994     {
00995         SETUP_BUF_ACCESS_AT(i);
00996 
00997         while((c = obuf[i]) != stop_at[t]) 
00998         {
00999             i += MAX(sa_len - t, shift[c]);
01000 
01001             SETUP_BUF_ACCESS_AT(i);
01002 
01003             t = sa_len - 1;
01004         }
01005     }
01006 
01007     *found = 1;
01008 
01009     /* found; obuf[i] is where the matching string is */
01010     return wtot;
01011 err:
01012     return -1;
01013 }
01014 
01015 
01033 vars_t *request_get_uploads(request_t *rq)
01034 {
01035     return rq->uploads;
01036 }
01037 
01038 /*
01039  * name:         form "name" <input> tag attribute value
01040  * filename:     name of the uploaded file provided by the client
01041  * tmp_filename: name on the temp file when the uploaded data has been saved
01042  *               on the local disk
01043  * mime_type:    uploaded MIME type as stated by the browser (may be NULL)
01044  */
01045 static int request_add_uploaded_file(request_t *rq, const char *name, 
01046     const char *filename, const char *tmp_filename, const char *mime_type)
01047 {
01048     struct stat st;
01049     var_t *v = NULL;
01050     upload_info_t *info = NULL;
01051 
01052     dbg_err_if (rq == NULL);
01053     dbg_err_if (name == NULL);
01054     /* filename may be NULL */
01055     dbg_err_if (tmp_filename == NULL);
01056     /* mime_type may be NULL */
01057 
01058     dbg_err_sif (stat(tmp_filename, &st) < 0);
01059 
01060     /* create a new var obj */
01061     dbg_err_if(var_create(name, tmp_filename, &v));
01062 
01063     /* alloc an info struct to attach to the var_t obj */
01064     dbg_err_if((info = u_zalloc(sizeof(upload_info_t))) == NULL);
01065 
01066     /* set info data */
01067     info->size = st.st_size;
01068     if(mime_type)
01069         snprintf(info->mime_type, MIME_TYPE_BUFSZ, "%s", mime_type);
01070     else
01071         info->mime_type[0] = 0;
01072 
01073     if(filename)
01074         snprintf(info->filename, U_FILENAME_MAX, "%s", filename);
01075 
01076     /* attach info to v */
01077     var_set_opaque(v, info);
01078     info = NULL;
01079 
01080     /* push into the cookie list */
01081     dbg_err_if(vars_add(rq->uploads, v));
01082 
01083     return 0;
01084 err:
01085     if(info)
01086         U_FREE(info);
01087     if(v)
01088         var_free(v);
01089     return ~0;
01090 }
01091 
01092       
01093 /* internal */
01094 int request_get_uploaded_filev(request_t *rq, var_t *v,
01095     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01096     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01097 {           
01098     upload_info_t *info;
01099     const char *tmp_fqn;
01100 
01101     dbg_err_if (rq == NULL);
01102     dbg_err_if (v == NULL);
01103     dbg_err_if (local_filename == NULL);
01104     dbg_err_if (client_filename == NULL);
01105     dbg_err_if (mime_type == NULL);
01106     dbg_err_if (file_size == NULL);
01107 
01108     info = var_get_opaque(v);
01109     dbg_err_if(info == NULL);
01110 
01111     tmp_fqn = var_get_value(v);
01112     dbg_err_if(tmp_fqn == NULL);
01113 
01114     /* copy out return values */
01115     strncpy(local_filename, tmp_fqn, U_FILENAME_MAX);
01116     strncpy(mime_type, info->mime_type, MIME_TYPE_BUFSZ);
01117     strncpy(client_filename, info->filename, U_FILENAME_MAX);
01118     *file_size = info->size;
01119 
01120     return 0;
01121 err:
01122     return ~0;
01123 }
01124 
01146 int request_get_uploaded_file(request_t *rq, const char *name, size_t idx,
01147     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01148     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01149 {
01150     var_t *v;
01151     upload_info_t *info;
01152     const char *tmp_fqn;
01153 
01154     dbg_err_if (rq == NULL);
01155     dbg_err_if (name == NULL);
01156     dbg_err_if (idx >= vars_count(rq->uploads));
01157     dbg_err_if (local_filename == NULL);
01158     dbg_err_if (client_filename == NULL);
01159     dbg_err_if (mime_type == NULL);
01160     dbg_err_if (file_size == NULL);
01161 
01162     v = vars_geti(rq->uploads, name, idx);
01163     dbg_err_if(v == NULL);
01164 
01165     return request_get_uploaded_filev(rq, v, local_filename, client_filename,
01166         mime_type, file_size);
01167 err:
01168     return ~0;
01169 }
01170 
01171 static int request_parse_multipart_chunk(request_t *rq, io_t *io, 
01172     const char *boundary, int *eof)
01173 {
01174     enum { PRMSZ = 512, BUFSZ = 4096 };
01175     header_t *h = NULL;
01176     io_t *tmpio = NULL;
01177     var_t *v = NULL;
01178     char name[PRMSZ], filename[PRMSZ], buf[BUFSZ];
01179     size_t bound_len, len;
01180     int found;
01181     ssize_t rc;
01182 
01183     /* create an header object to parse MIME part headers */
01184     dbg_err_if(header_create(&h));
01185 
01186     /* read header lines until the first blank line */
01187     dbg_err_if(header_load(h, io));
01188 
01189     warn_err_ifm(is_multipart_mixed(h), 
01190         "multipart/mixed content is not supported yet");
01191 
01192     /* HTTP should never use cte */
01193     warn_err_ifm(is_encoded(h), 
01194         "encoded file upload is not supported");
01195 
01196     dbg_err_if(parse_content_disposition(h, name, filename, PRMSZ));
01197 
01198     /* shortcut */
01199     bound_len = strlen(boundary);
01200 
01201     if(filename[0] != '\0')
01202     {
01203         dbg_err_if(BUFSZ <= bound_len);
01204 
01205         /* open a temporary file to dump uploaded data */
01206         dbg_err_if(u_tmpfile_open(&tmpio));
01207 
01208         for(found = 0; !found; /* nothing */)
01209         {
01210             rc = read_until(io, boundary, buf, BUFSZ, &found);
01211             dbg_err_if(rc <= 0); /* on error or eof exit */
01212 
01213             /* write all but the last bound_len + 2 (\r\n) bytes */
01214             if(found)
01215             {
01216                 rc -= (bound_len + 2);
01217                 dbg_err_if(rc < 0);
01218             }
01219 
01220             /* write to the temp file */
01221             dbg_err_if(io_write(tmpio, buf, rc) < 0);
01222         }
01223 
01224         /* save the path/name of the tmp file to buf */
01225         dbg_err_if(io_name_get(tmpio, buf, BUFSZ));
01226 
01227         /* flush and free */
01228         io_free(tmpio); tmpio = NULL;
01229 
01230         /* add this file to the uploaded file list */
01231         dbg_err_if(request_add_uploaded_file(rq, name, filename, buf, 
01232             header_get_field_value(h, "Content-Type")));
01233 
01234         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01235         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01236 
01237         if(strncmp(buf, "--", 2) == 0)
01238             *eof = 1; /* end of MIME stuff */
01239 
01240     } else {
01241         /* read the value of the variable (all until the next boundary) */
01242         rc = read_until(io, boundary, buf, BUFSZ, &found);
01243         dbg_err_if(rc <= 0); /* on error or eof exit */
01244 
01245         /* write all but the last bound_len + 2 (\r\n) bytes */
01246         warn_err_ifm(!found, "malformed request or BUFSZ too small");
01247 
01248         rc -= (bound_len + 2);
01249         dbg_err_if(rc < 0);
01250 
01251         /* zero-term the buffer (removing the boundary) */
01252         buf[rc] = 0;
01253 
01254         /* add a new binary var to request arguments list */
01255         dbg_err_if(var_bin_create(name, buf, rc, &v));
01256         dbg_if(vars_add(rq->args, v));
01257 
01258         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01259         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01260 
01261         if(strncmp(buf, "--", 2) == 0)
01262             *eof = 1; /* end of MIME stuff */
01263     }
01264 
01265     header_free(h);
01266 
01267     return 0;
01268 err:
01269     if(tmpio)
01270         io_free(tmpio);
01271     if(h)
01272         header_free(h);
01273     return ~0;
01274 }
01275 
01276 static int request_parse_multipart_data(request_t *rq)
01277 {
01278     enum { BOUNDARY_BUFSZ = 128, BUFSZ = 1024 }; 
01279     char boundary[BOUNDARY_BUFSZ], buf[BUFSZ];
01280     int eof;
01281 
01282     /* boundaries always start with -- */
01283     strcpy(boundary, "--");
01284 
01285     dbg_err_if(request_get_fieldparam(rq, "Content-Type", "boundary",
01286         boundary + 2, BOUNDARY_BUFSZ - 2));
01287 
01288     dbg_err_if(strlen(boundary) == 0);
01289 
01290     /* skip the MIME preamble (usually not used in HTTP) */
01291     for(;;)
01292     {
01293         dbg_err_if(io_gets(rq->io, buf, BUFSZ) <= 0);
01294         if(!strncmp(buf, boundary, strlen(boundary)))
01295             break; /* boundary found */
01296     }
01297 
01298     /* cycle on each MIME part */
01299     for(eof = 0; eof == 0; )
01300         dbg_err_if(request_parse_multipart_chunk(rq, rq->io, boundary, &eof));
01301 
01302     return 0;
01303 err:
01304     return ~0;
01305 }
01306 
01307 static int request_cb_close_socket(talarm_t *al, void *arg)
01308 {
01309     io_t *io = (io_t*)arg;
01310 
01311     u_unused_args(al);
01312 
01313     warn("[%x] connection timed out, closing", io);
01314 
01315     /* hack: this will call io_fd_term that will close the fd unblocking
01316        pending I/O calls */
01317     io->term(io);
01318 
01319     return 0;
01320 }
01321 
01322 int request_parse_data(request_t *rq)
01323 {
01324     talarm_t *al = NULL;
01325     int rc = HTTP_STATUS_BAD_REQUEST;
01326 
01327     if(rq->method == HM_POST)
01328     {
01329         /* set a timeout to abort POST if it takes too much... */
01330         dbg_err_if(timerm_add(rq->post_timeout, request_cb_close_socket, 
01331             (void*)rq->io, &al));
01332 
01333         /* Content-Length is required when using POST */
01334         dbg_err_if(request_set_content_length(rq) && 
01335             (rc = HTTP_STATUS_LENGTH_REQUIRED));
01336 
01337         /* abort if the client is pushing too much data */
01338         dbg_err_if(rq->content_length > rq->post_maxsize &&
01339             (rc = HTTP_STATUS_REQUEST_TOO_LARGE));
01340 
01341         if(request_is_multipart_formdata(rq))
01342         { 
01343             /* some vars may be urlencoded */
01344             dbg_err_if(request_parse_query_args(rq));
01345 
01346             /* <form enctype="multipart/form-data" ...> */
01347             dbg_err_if(request_parse_multipart_data(rq));
01348         } else {
01349             /* <form [enctype="application/x-www-form-urlencoded"] ...> */
01350             dbg_err_if(request_parse_urlencoded_data(rq));
01351         }
01352 
01353         /* post timeout not expired, clear it */
01354         dbg_if(timerm_del(al)); al = NULL;
01355     } else {
01356         /* parse urlencoded variables and set var_t* array */
01357         dbg_err_if(request_parse_query_args(rq));
01358     }
01359 
01360     return 0;
01361 err:
01362     return rc;
01363 }
01364 
01365 /*
01366  * \brief   Parse a request object
01367  *  
01368  * Parse request object \p rq.
01369  *
01370  * \param rq            request object
01371  * \param is_valid_uri  URI validation function
01372  * \param arg           argument to is_valid_uri
01373  *
01374  * \return \c 0 if successful, non-zero on error
01375  */
01376 int request_parse_header(request_t *rq, 
01377         int (*is_valid_uri)(void*, const char *, size_t),
01378         void* arg)
01379 {
01380     enum { BUFSZ = 4096 };
01381     const char WP[] = " \t\r\n";
01382     char ln[BUFSZ], *pp, *method, *uri, *proto;
01383     talarm_t *al = NULL;
01384     int rc = HTTP_STATUS_BAD_REQUEST;
01385     
01386     dbg_err_if (rq == NULL);
01387     dbg_err_if (rq->io == NULL); /* must call rq_bind before rq_parse */
01388 
01389     /* wait at most N seconds to receive the request */
01390     dbg_err_if(timerm_add(rq->idle_timeout, request_cb_close_socket, 
01391         (void*)rq->io, &al));
01392 
01393     if(!rq->cgi)
01394     {
01395         /* cp the first line */
01396         dbg_err_if(io_gets(rq->io, ln, BUFSZ) <= 0);
01397 
01398         method = strtok_r(ln, WP, &pp); 
01399         dbg_err_if(!method || request_set_method(rq, method));
01400 
01401         uri = strtok_r(NULL, WP, &pp);
01402         dbg_err_if(!uri || request_set_uri(rq, uri, is_valid_uri, arg));
01403 
01404         /* HTTP/0.9 not supported yet */ 
01405         proto = strtok_r(NULL, WP, &pp);
01406         dbg_err_if(!proto || request_set_proto(rq, proto)); 
01407 
01408         dbg_err_if(header_load(rq->header, rq->io));
01409     } else
01410         dbg_err_if(header_load_from_cgienv(rq->header));
01411 
01412     /* set if-modified-since time_t value */
01413     dbg_err_if(request_parse_ims(rq));
01414 
01415     /* parse "Cookie:" fields and set the cookies vars_t */
01416     dbg_err_if(request_parse_cookies(rq));
01417 
01418     /* idle timeout not expired, clear it */
01419     dbg_if(timerm_del(al)); al = NULL;
01420 
01421     /* parse URL encoded or POSTed data */
01422     dbg_err_if((rc = request_parse_data(rq)));
01423 
01424     return 0;
01425 err:
01426     return rc;
01427 }
01428 
01439 int request_get_method(request_t *rq)
01440 {
01441     dbg_return_if (rq == NULL, HM_UNKNOWN);
01442 
01443     return rq->method;
01444 }
01445 
01455 const char *request_get_resolved_filename(request_t *rq)
01456 {
01457     dbg_return_if (rq == NULL, NULL);
01458 
01459     return rq->resolved_filename;
01460 }
01461 
01471 const char *request_get_resolved_path_info(request_t *rq)
01472 {
01473     dbg_return_if (rq == NULL, NULL);
01474 
01475     return rq->resolved_path_info;
01476 }
01477 
01478 int request_print(request_t *rq)
01479 {
01480     dbg_return_if (rq == NULL, ~0);
01481 
01482     dbg("method: %u", rq->method);
01483     dbg("uri: %s", rq->uri);
01484     dbg("proto: %s", rq->protocol);
01485     dbg("filename: %s", rq->filename);
01486     dbg("resolved filename: %s", rq->resolved_filename);
01487     dbg("path_info: %s", rq->path_info);
01488     dbg("resolved path_info: %s", rq->resolved_path_info);
01489     dbg("query: %s", rq->query);
01490 
01491     return 0;
01492 }
01493 
01494 static int request_load_config(request_t *rq)
01495 {
01496     u_config_t *c;
01497     const char *v;
01498 
01499     dbg_err_if (rq == NULL);
01500     dbg_err_if (rq->http == NULL);
01501     dbg_err_if (http_get_config(rq->http) == NULL);
01502 
01503     c = http_get_config(rq->http);
01504     
01505     /* defaults */
01506     rq->idle_timeout = REQUEST_DEFAULT_IDLE_TIMEOUT;
01507     rq->post_timeout = REQUEST_DEFAULT_POST_TIMEOUT;
01508     rq->post_maxsize = REQUEST_DEFAULT_POST_MAXSIZE;
01509 
01510     /* idle timeout */
01511     if((v = u_config_get_subkey_value(c, "idle_timeout")) != NULL)
01512         rq->idle_timeout = MAX(1, atoi(v));
01513 
01514     /* post timeout */
01515     if((v = u_config_get_subkey_value(c, "post_timeout")) != NULL)
01516         rq->post_timeout = MAX(5, atoi(v));
01517 
01518     /* post maxsize */
01519     if((v = u_config_get_subkey_value(c, "post_maxsize")) != NULL)
01520         rq->post_maxsize = MAX(1024, atoi(v));
01521 
01522     return 0;
01523 err:
01524     return ~0;
01525 }
01526 
01527 int request_create(http_t *http, request_t **prq)
01528 {
01529     request_t *rq = NULL;
01530 
01531     dbg_return_if (prq == NULL, ~0);
01532     dbg_return_if (http == NULL, ~0);
01533 
01534     rq = u_zalloc(sizeof(request_t));
01535     dbg_err_if(rq == NULL);
01536 
01537     dbg_err_if(header_create(&rq->header));
01538 
01539     dbg_err_if(vars_create(&rq->args));
01540     dbg_err_if(vars_create(&rq->cookies));
01541     dbg_err_if(vars_create(&rq->uploads));
01542 
01543     rq->http = http;
01544 
01545     dbg_err_if(request_load_config(rq));
01546 
01547     *prq = rq;
01548 
01549     return 0;
01550 err:
01551     if(rq)
01552         request_free(rq);
01553     return ~0;
01554 }
01555 
01556 static int request_unlink_uploads(var_t *v, void * arg)
01557 {
01558     dbg_err_if (v == NULL);
01559 
01560     u_unused_args(arg);
01561 
01562     if(var_get_opaque(v) && var_get_value(v))
01563     {   /* it's a file var, unlink v->value */
01564         unlink(var_get_value(v));
01565     }
01566 
01567 err:
01568     return 0;
01569 }
01570 
01571 int request_free(request_t *rq)
01572 {
01573     if (rq)
01574     {
01575         /* free internal stuff */
01576         request_clear_uri(rq);
01577 
01578         if(rq->header)
01579             header_free(rq->header);
01580 
01581         if(rq->io)
01582             io_free(rq->io);
01583 
01584         if(rq->uploads)
01585         {
01586             /* unlink uploaded files (if any) */
01587             vars_foreach(rq->uploads, request_unlink_uploads, NULL);
01588             vars_free(rq->uploads);
01589         }
01590         
01591         if(rq->cookies)
01592             vars_free(rq->cookies);
01593 
01594         if(rq->args)
01595             vars_free(rq->args);
01596 
01597         U_FREE(rq);
01598     }
01599 
01600     return 0;
01601 }
01602 
01603 /* save the local address struct (ip and port) in the request obj */
01604 int request_set_addr(request_t *rq, addr_t *addr)
01605 {
01606     dbg_return_if (rq == NULL, ~0);
01607     dbg_return_if (addr == NULL, ~0);
01608 
01609     memcpy(&rq->local_addr, addr, sizeof(addr_t));
01610 
01611     return 0;
01612 }
01613 
01614 /* save the peer address struct (ip and port) in the request obj */
01615 int request_set_peer_addr(request_t *rq, addr_t *addr)
01616 {
01617     dbg_return_if (rq == NULL, ~0);
01618     dbg_return_if (addr == NULL, ~0);
01619 
01620     memcpy(&rq->peer_addr, addr, sizeof(addr_t));
01621 
01622     return 0;
01623 }
01624 
01625 /* return the local socket address */
01626 addr_t* request_get_addr(request_t *rq)
01627 {
01628     dbg_return_if (rq == NULL, NULL);
01629 
01630     return &rq->local_addr;
01631 }
01632 
01633 /* return the peer address */
01634 addr_t* request_get_peer_addr(request_t *rq)
01635 {
01636     dbg_return_if (rq == NULL, NULL);
01637 
01638     return &rq->peer_addr;
01639 }
01640 
01641 /* return the header obj */
01642 header_t* request_get_header(request_t *rq)
01643 {
01644     dbg_return_if (rq == NULL, NULL);
01645 
01646     return rq->header;
01647 }
01648 
01649 /* return a field obj of the field named 'name' or NULL if the field does not 
01650    exist */
01651 field_t* request_get_field(request_t *rq, const char *name)
01652 {
01653     dbg_return_if (rq == NULL, NULL);
01654     dbg_return_if (name == NULL, NULL);
01655 
01656     return header_get_field(rq->header, name);
01657 }
01658 
01659 /* return the string value of the field named 'name' or NULL if the field does
01660    not exist */
01661 const char* request_get_field_value(request_t *rq, const char *name)
01662 {
01663     dbg_return_if (rq == NULL, NULL);
01664     dbg_return_if (name == NULL, NULL);
01665 
01666     return header_get_field_value(rq->header, name);
01667 }
01668 
01669 

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