Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

_request.c

00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: request.c,v 1.43 2007/10/25 20:26:56 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 *cli_rq;               /* verbatim client request line             */
00034     char *uri;                  /* verbatim uri asked by the client         */
00035     char *protocol;             /* proto/ver                                */
00036     char *path_info;            /* extra info at the end of the path        */
00037     char *query;                /* query string (data after '?')            */
00038     char *filename;             /* path of the req resource                 */
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 
00236 const char *request_get_uri(request_t *rq)
00237 {
00238     dbg_return_if (rq == NULL, NULL);
00239 
00240     return rq->uri;
00241 }
00242 
00252 const char *request_get_filename(request_t *rq)
00253 {
00254     dbg_return_if (rq == NULL, NULL);
00255 
00256     return rq->filename;
00257 }
00258 
00268 const char *request_get_query_string(request_t *rq)
00269 {
00270     dbg_return_if (rq == NULL, NULL);
00271 
00272     return rq->query;
00273 }
00274 
00284 const char *request_get_path_info(request_t *rq)
00285 {
00286     dbg_return_if (rq == NULL, NULL);
00287 
00288     return rq->path_info;
00289 }
00290 
00291 /* parse and set if-modified-since value */
00292 static int request_parse_ims(request_t *rq)
00293 {
00294     const char *ims;
00295 
00296     dbg_err_if (rq == NULL);
00297     
00298     rq->if_modified_since = 0;
00299 
00300     ims = header_get_field_value(rq->header, "If-Modified-Since");
00301     if(ims)
00302         dbg_err_if(u_httpdate_to_tt(ims, &rq->if_modified_since));
00303 
00304 err: /* ignore if it's not formatted properly */
00305     return 0;
00306 }
00307 
00317 time_t request_get_if_modified_since(request_t *rq)
00318 {
00319     dbg_return_if (rq == NULL, (time_t) -1);
00320 
00321     return rq->if_modified_since;
00322 }
00323 
00324 
00334 http_t* request_get_http(request_t *rq)
00335 {
00336     dbg_return_if (rq == NULL, NULL);
00337 
00338     return rq->http;
00339 }
00340 
00341 /*
00342  * \brief   Bind request I/O to a given I/O. 
00343  *  
00344  * Bind the I/O of request \p rq to \p in.
00345  *
00346  * \param rq    request object
00347  * \param in    input I/O object
00348  *
00349  * \return \c 0 if successful, non-zero on error
00350  */
00351 int request_bind(request_t *rq, io_t *in)
00352 {
00353     dbg_return_if (rq == NULL, ~0);
00354     dbg_return_if (in == NULL, ~0);
00355 
00356     rq->io = in;
00357 
00358     return 0;
00359 }
00360 
00361 /*
00362  * \brief   Clear the URI field of a request
00363  *
00364  * Clear the URI field of request \p rq.
00365  *
00366  * \param rq  request object
00367  *
00368  * \return nothing
00369  */
00370 void request_clear_uri(request_t *rq)
00371 {
00372     U_FREE(rq->uri);
00373     U_FREE(rq->protocol);
00374     U_FREE(rq->path_info);
00375     U_FREE(rq->query);
00376     U_FREE(rq->filename);
00377     U_FREE(rq->resolved_path_info);
00378     U_FREE(rq->resolved_filename);
00379     U_FREE(rq->content_type);
00380     U_FREE(rq->content_encoding);
00381 }
00382 
00383 
00384 /*
00385  * \brief   Set the URI field of a request
00386  *
00387  * Set the URI field of request \p rq to \p uri given 
00388  *
00389  * \param rq           request object
00390  * \param uri          URI string
00391  * \param is_valid_uri URI validation function 
00392  * \param arg          argument to is_valid_uri
00393  *
00394  * \return \c 0 if successful, non-zero on error
00395  */
00396 int request_set_uri(request_t *rq, const char *uri,
00397         int (*is_valid_uri)(void*, const char *, size_t),
00398         void* arg)
00399 {
00400     enum { REQUEST_URI_MAX_LENGTH = 4095 };
00401     char *p, *fn, *pi, *cp = NULL;
00402     size_t uri_len = strlen(uri);
00403 
00404     dbg_err_if (rq == NULL);
00405     dbg_err_if (uri == NULL);
00406     /* is_valid_uri may be NULL */
00407     /* arg may be NULL */
00408  
00409     request_clear_uri(rq);
00410 
00411     /* this is just to avoid recursive infinite redirect loops for pages that 
00412        appends something to the URI and redirects to the same page */
00413     warn_err_ifm(uri_len > REQUEST_URI_MAX_LENGTH, "Request URI too long");
00414 
00415     REQUEST_SET_STRING_FIELD(rq->uri, uri);
00416 
00417     /* save (undecoded) query string i.e. everything after '?' */
00418     if((p = strchr(uri, '?')) != NULL)
00419         dbg_err_if(request_set_query_string(rq, ++p));
00420 
00421     cp = (char*)u_malloc(uri_len + 1);
00422     dbg_err_if(cp == NULL);
00423 
00424     /* copy decoded url */
00425     dbg_err_if(u_urlncpy(cp, rq->uri, uri_len, URLCPY_DECODE) <= 0);
00426 
00427     if((p = strchr(cp, '?')) != NULL)
00428         *p++ = 0; /* remove query string from the uri copy */
00429 
00430     /* normalize the URI (remove /../, /./, ecc) */
00431     dbg_err_if(u_path_normalize(cp));
00432 
00433     /* set filename is case there's not path_info and/or file does not exists */
00434     dbg_err_if(request_set_filename(rq, cp));
00435 
00436     /* look for path_info */
00437     fn = cp;                    /* filename     */
00438     pi = fn + strlen(fn);       /* path_info    */
00439     for(;;)
00440     {
00441         if(is_valid_uri == NULL || is_valid_uri(arg, fn, pi - fn))
00442         {
00443             dbg_err_if(request_set_filename(rq, fn));
00444             rq->filename[pi-fn] = 0; /* trunc */
00445             if(strlen(pi))
00446                 dbg_err_if(request_set_path_info(rq, pi));
00447             break;
00448         } else {
00449             if((p = u_strnrchr(fn, '/', pi - fn)) == NULL)
00450                 break; /* file pointed by this uri does not exists */
00451             pi = p; /* try again */
00452         }
00453     }
00454 
00455     U_FREE(cp);
00456 
00457     return 0;
00458 err:
00459     U_FREE(cp);
00460     return ~0;
00461 }
00462 
00463 static int request_set_proto(request_t *rq, const char *proto)
00464 {
00465     dbg_err_if (rq == NULL);
00466     dbg_err_if (proto == NULL);
00467 
00468     /* be sure that the requested protocol is http */
00469     if(strncasecmp(proto, "http", 4))
00470         return ~0; /* unknown or unsupported protocol */
00471 
00472     REQUEST_SET_STRING_FIELD(rq->protocol, proto);
00473 
00474     return 0;
00475 err:
00476     return ~0;
00477 }
00478 
00479 /*
00480  * \brief   Save client request
00481  *
00482  * Save client request line
00483  *
00484  * \param rq     request object
00485  * \param ln     the request line
00486  *
00487  * \return \c 0 if successful, non-zero on error
00488  */
00489 int request_set_client_request(request_t *rq, const char *ln)
00490 {
00491     char *p;
00492     dbg_err_if(rq == NULL);
00493     dbg_err_if(ln == NULL);
00494 
00495     rq->cli_rq = u_strdup(ln);
00496     dbg_err_if(rq->cli_rq == NULL);
00497 
00498     /* cut the trailing newline if any */
00499     for(p = rq->cli_rq; *p && (*p != '\r' && *p != '\n'); ++p)
00500         continue;
00501     *p = 0;
00502 
00503     return 0;
00504 err:
00505     return ~0;
00506 }
00507 
00517 const char *request_get_client_request(request_t *rq)
00518 {
00519     return rq->cli_rq;
00520 }
00521 
00522 /*
00523  * Set the \p method of request \p rq.  Refer to http.h for possible methods.
00524  *
00525  * \param rq     request object
00526  * \param method the HTTP method 
00527  *
00528  * \return \c 0 if successful, non-zero on error
00529  */
00530 int request_set_method(request_t *rq, const char *method)
00531 {
00532     dbg_return_if (rq == NULL, ~0);
00533     dbg_return_if (method == NULL, ~0);
00534 
00535     if(!strcasecmp(method, "get"))
00536         rq->method = HM_GET;
00537     else if(!strcasecmp(method, "head"))
00538         rq->method = HM_HEAD;
00539     else if(!strcasecmp(method, "post"))
00540         rq->method = HM_POST;
00541     else {
00542         /* put, delete, * */
00543         rq->method = HM_UNKNOWN;
00544         return ~0; /* unknown or unsupported method */
00545     }
00546     
00547     return 0;
00548 }
00549 
00550 static int request_set_content_length(request_t *rq)
00551 {
00552     const char *clen;
00553     size_t len;
00554 
00555     dbg_err_if (rq == NULL);
00556 
00557     clen = header_get_field_value(rq->header, "Content-Length");
00558     dbg_err_if(clen == NULL || (len = atoi(clen)) < 0);
00559 
00560     rq->content_length = len;
00561 
00562     return 0;
00563 err:
00564     return ~0;
00565 }
00566 
00567 static int request_parse_cookie(request_t *rq, field_t *field)
00568 {
00569     enum { BUFSZ = 4096 }; /* cookie size limit */
00570     char *pp, *tok, *src, buf[BUFSZ];
00571 
00572     dbg_err_if (rq == NULL);
00573     dbg_err_if (field == NULL);
00574     
00575     dbg_err_if(field_get_value(field) == NULL);
00576 
00577     /* save a copy to tokenize it */
00578     strncpy(buf, field_get_value(field), BUFSZ);
00579 
00580     /* foreach name=value pair... */
00581     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00582         dbg_if(vars_add_urlvar(rq->cookies, tok, NULL));
00583 
00584     return 0;
00585 err:
00586     return ~0;
00587 }
00588 
00589 static int request_parse_cookies(request_t *rq)
00590 {
00591     field_t *f;
00592     size_t i, count;
00593 
00594     dbg_err_if (rq == NULL);
00595     
00596     count = header_field_count(rq->header);
00597     for(i = 0; i < count; ++i)
00598     {
00599         f = header_get_fieldn(rq->header, i);
00600         dbg_err_if(f == NULL); /* shouldn't happen */
00601         if(strcasecmp(field_get_name(f), "cookie") == 0)
00602             dbg_err_if(request_parse_cookie(rq, f));
00603     }
00604 
00605     return 0;
00606 err:
00607     return ~0;
00608 }
00609 
00610 static int request_parse_query_args(request_t *rq)
00611 {
00612     char *pp, *tok, *src, *query = NULL;
00613 
00614     dbg_err_if (rq == NULL);
00615 
00616     if(!rq->query)
00617         return 0; /* no args */
00618 
00619     /* dup to tokenize it */
00620     query = u_strdup(rq->query);
00621     dbg_err_if(query == NULL);
00622 
00623     /* foreach name=value pair... */
00624     for(src = query; (tok = strtok_r(src, "&", &pp)) != NULL; src = NULL)
00625     {
00626         /* create a new var_t obj and push it into the args vars-list */
00627         dbg_if(vars_add_urlvar(rq->args, tok, NULL));
00628     }
00629 
00630     U_FREE(query);
00631 
00632     return 0;
00633 err:
00634     U_FREE(query);
00635     return ~0;
00636 }
00637 
00638 /* set is-cgi flag */
00639 void request_set_cgi(request_t *rq, int cgi)
00640 {
00641     rq->cgi = cgi;
00642     return;
00643 }
00644 
00655 ssize_t request_get_content_length(request_t *rq)
00656 {
00657     dbg_return_if (rq == NULL, -1);
00658 
00659     return (ssize_t) rq->content_length;
00660 }
00661 
00662 static int match_content_type(header_t *h, const char *mime_type)
00663 {
00664     const char *ct;
00665 
00666     dbg_return_if (h == NULL, 0);
00667     dbg_return_if (mime_type == NULL, 0);
00668 
00669     ct = header_get_field_value(h, "Content-Type");
00670     if(ct == NULL || strncasecmp(ct, mime_type, strlen(mime_type)))
00671         return 0;
00672 
00673     return 1;
00674 }
00675 
00676 static int request_is_multipart_formdata(request_t *rq)
00677 {
00678     return match_content_type(rq->header, "multipart/form-data");
00679 }
00680 
00681 static int request_parse_urlencoded_data(request_t *rq)
00682 {
00683     ssize_t qsz, len;
00684 
00685     dbg_err_if (rq == NULL);
00686 
00687     len = rq->content_length; /* shortcut */
00688 
00689     qsz = (rq->query ? strlen(rq->query) : 0);
00690 
00691     /* alloc or enlarge the query string buffer */
00692     rq->query = u_realloc(rq->query, len + qsz + 2);
00693     dbg_err_if(rq->query == NULL);
00694 
00695     /* dbg("rq->query %x  size %u", rq->query, len+qsz+2); */
00696 
00697     rq->query[qsz] = 0; /* must be zero-term for strcat to work */
00698     if(qsz)
00699     {   /* append a '&' */
00700         strcat(rq->query, "&");
00701         ++qsz;
00702     }
00703 
00704     /* append to current query string */
00705     dbg_err_if(io_read(rq->io, rq->query + qsz, len) != len);
00706 
00707     /* zero terminate it */
00708     rq->query[qsz + len] = 0;
00709 
00710     /* parse rq->query and build the args var_t* array */
00711     dbg_err_if(request_parse_query_args(rq));
00712 
00713     return 0;
00714 err:
00715     return ~0;
00716 }
00717 
00718 /* return the value of the param named 'param_name' of the field 'field_name'
00719    and save it to 'buffer' */
00720 static int request_get_fieldparam(request_t *rq, const char *field_name, 
00721     const char *param_name, char *buf, size_t size)
00722 {
00723     const char *param_value, *field_value, *p;
00724     size_t pv_len;
00725 
00726     dbg_err_if (rq == NULL);
00727     dbg_err_if (field_name == NULL);
00728     dbg_err_if (param_name == NULL);
00729     dbg_err_if (buf == NULL);
00730     dbg_err_if (size == 0);
00731 
00732     field_value = header_get_field_value(rq->header, field_name);
00733     dbg_err_if(field_value == NULL);
00734 
00735     /* look for param name=value pair */
00736     param_value = u_stristr(field_value, param_name);
00737     dbg_err_if(param_value == NULL);
00738 
00739     /* skip param name */
00740     param_value += strlen(param_name);
00741 
00742     /* first char must be an equal sign */
00743     dbg_err_if(*param_value++ != '=');
00744 
00745     /* a param value ends on the first ';', space or at the end of string */
00746     for(p = param_value; ;++p)
00747         if(*p == '\0' || *p == ';' || isspace(*p))
00748             break; /* end of param value */
00749 
00750     /* param value len */
00751     pv_len = p - param_value;
00752 
00753     /* boundary check */
00754     dbg_err_if(pv_len > size - 1); 
00755 
00756     /* copy out the param value */
00757     strncpy(buf, param_value, pv_len);
00758     buf[MIN(pv_len, size - 1)] = 0;
00759 
00760     return 0;
00761 err:
00762     return ~0;
00763 }
00764 
00765 static int is_multipart_mixed(header_t *h)
00766 {
00767     return match_content_type(h, "multipart/mixed");
00768 }
00769 
00770 static int is_encoded(header_t *h)
00771 {
00772     const char *cte;
00773 
00774     dbg_return_if (h == NULL, 0);
00775 
00776     if((cte = header_get_field_value(h, "Content-Transfer-Encoding")) == NULL)
00777         return 0; /* not encoded */
00778 
00779     if(strcasecmp(cte, "binary") == 0)
00780         return 0; /* not encoded */
00781 
00782     return 1; /* encoded */
00783 }
00784 
00785 static inline int is_nl(char c)
00786 {
00787     return (c == '\n' || c == '\r' ? c : 0);
00788 }
00789 
00790 static inline int is_quote(char c)
00791 {
00792     return (c == '"' || c == '\'' ? c : 0);
00793 }
00794 
00795 static int parse_content_disposition(header_t *h, char *name, char *filename,
00796     size_t prmsz)
00797 {
00798     enum { BUFSZ = 512 };
00799     char *pp, *tok, *src, buf[BUFSZ];
00800     size_t n_len, fn_len;
00801     const char *cd;
00802     int q;
00803 
00804     dbg_err_if (h == NULL);
00805     dbg_err_if (name == NULL);
00806     dbg_err_if (filename == NULL);
00807     dbg_err_if (prmsz == 0);
00808     
00809     cd = header_get_field_value(h, "Content-Disposition");
00810     dbg_err_if(cd == NULL);
00811 
00812     dbg_err_if(strlen(cd) >= BUFSZ);
00813 
00814     /* must start with form-data */
00815     dbg_err_if(strncmp(cd, "form-data", strlen("form-data")));
00816 
00817     name[0] = filename[0] = 0;
00818 
00819     /* save a copy to tokenize it */
00820     strncpy(buf, cd, BUFSZ);
00821 
00822     /* shortcut */
00823     n_len = strlen("name=");
00824     fn_len = strlen("filename=");
00825 
00826     /* foreach name=value pair... */
00827     for(src = buf; (tok = strtok_r(src, " ;", &pp)) != NULL; src = NULL)
00828     {
00829         if(strncmp(tok, "form-data", strlen("form-data")) == 0)
00830             continue;   /* skip */
00831         else if(strncmp(tok, "name=", n_len) == 0) {
00832             /* skip the name part */
00833             tok += n_len;
00834 
00835             /* remove single or double quotes */
00836             if((q = is_quote(tok[0])) != 0)
00837                 ++tok;
00838             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00839                 tok[strlen(tok) - 1] = 0;
00840 
00841             strncpy(name, tok, prmsz);
00842         } else if(strncmp(tok, "filename=", fn_len) == 0) {
00843             /* skip the filename part */
00844             tok += fn_len;
00845 
00846             /* remove single or double quotes */
00847             if((q = is_quote(tok[0])) != 0)
00848                 ++tok;
00849             if(strlen(tok) && tok[strlen(tok) - 1] == q)
00850                 tok[strlen(tok) - 1] = 0;
00851 
00852             strncpy(filename, tok, prmsz);
00853         } 
00854         /* else ignore unknown fields */
00855     }
00856             
00857     return 0;
00858 err:
00859     return ~0;
00860 }
00861 
00862 /* 
00863  * Read from io until obuf is full or until stop_at string is found.
00864  *
00865  * Boyer-Moore algorithm is used for efficiency. 
00866  *
00867  * Returns the number of bytes written to obuf 
00868  */
00869 static ssize_t read_until(io_t *io, const char *stop_at, char *obuf, 
00870     size_t size, int *found)
00871 {
00872     /* use this macro before accessing obuf[idx] elem. the macro will load from
00873        the given io enough bytes to access the required byte. if the buffer
00874        is too small (i.e. less then idx bytes long) the function will return */
00875     #define SETUP_BUF_ACCESS_AT(idx)                                        \
00876         if(idx >= wtot) {                                                   \
00877             if(idx >= size)                                                 \
00878                 return wtot; /* the output buffer is full */                \
00879                                                                             \
00880             /* we need to fetch some more bytes to access obuf[i] */        \
00881             dbg_err_if((rc = io_read(io, wbuf, idx + 1 - wtot)) < 0);       \
00882             if(rc == 0 || rc < idx + 1 - wtot)                              \
00883                 return wtot + rc; /* eof or short count */                  \
00884             wbuf += rc;                                                     \
00885             wtot += rc;                                                     \
00886         }
00887 
00888     int sa_len = strlen(stop_at);
00889     int i, t, shift[256], rc;
00890     unsigned char c;
00891     size_t wtot = 0;
00892     char *wbuf = obuf;
00893 
00894     dbg_err_if (io == NULL);
00895     dbg_err_if (stop_at == NULL);
00896     dbg_err_if (obuf == NULL);
00897     /* size may be 0 */
00898     dbg_err_if (found == NULL);
00899 
00900     for(i = 0; i < 256; ++i)  
00901         shift[i] = sa_len;
00902 
00903     for(i = 0; i < sa_len; ++i)
00904         shift[ stop_at[i] ] = sa_len - i - 1;
00905 
00906     *found = 0;
00907 
00908     for(i = t = sa_len-1; t >= 0; --i, --t)
00909     {
00910         SETUP_BUF_ACCESS_AT(i);
00911 
00912         while((c = obuf[i]) != stop_at[t]) 
00913         {
00914             i += MAX(sa_len - t, shift[c]);
00915 
00916             SETUP_BUF_ACCESS_AT(i);
00917 
00918             t = sa_len - 1;
00919         }
00920     }
00921 
00922     *found = 1;
00923 
00924     /* found; obuf[i] is where the matching string is */
00925     return wtot;
00926 err:
00927     return -1;
00928 }
00929 
00930 
00948 vars_t *request_get_uploads(request_t *rq)
00949 {
00950     return rq->uploads;
00951 }
00952 
00953 /*
00954  * name:         form "name" <input> tag attribute value
00955  * filename:     name of the uploaded file provided by the client
00956  * tmp_filename: name on the temp file when the uploaded data has been saved
00957  *               on the local disk
00958  * mime_type:    uploaded MIME type as stated by the browser (may be NULL)
00959  */
00960 static int request_add_uploaded_file(request_t *rq, const char *name, 
00961     const char *filename, const char *tmp_filename, const char *mime_type)
00962 {
00963     struct stat st;
00964     var_t *v = NULL;
00965     upload_info_t *info = NULL;
00966 
00967     dbg_err_if (rq == NULL);
00968     dbg_err_if (name == NULL);
00969     /* filename may be NULL */
00970     dbg_err_if (tmp_filename == NULL);
00971     /* mime_type may be NULL */
00972 
00973     dbg_err_sif (stat(tmp_filename, &st) < 0);
00974 
00975     /* create a new var obj */
00976     dbg_err_if(var_create(name, tmp_filename, &v));
00977 
00978     /* alloc an info struct to attach to the var_t obj */
00979     dbg_err_if((info = u_zalloc(sizeof(upload_info_t))) == NULL);
00980 
00981     /* set info data */
00982     info->size = st.st_size;
00983     if(mime_type)
00984         snprintf(info->mime_type, MIME_TYPE_BUFSZ, "%s", mime_type);
00985     else
00986         info->mime_type[0] = 0;
00987 
00988     if(filename)
00989         snprintf(info->filename, U_FILENAME_MAX, "%s", filename);
00990 
00991     /* attach info to v */
00992     var_set_opaque(v, info);
00993     info = NULL;
00994 
00995     /* push into the cookie list */
00996     dbg_err_if(vars_add(rq->uploads, v));
00997 
00998     return 0;
00999 err:
01000     if(info)
01001         U_FREE(info);
01002     if(v)
01003         var_free(v);
01004     return ~0;
01005 }
01006 
01007 static int request_get_uploaded_filev(request_t *rq, var_t *v,
01008     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01009     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01010 {           
01011     upload_info_t *info;
01012     const char *tmp_fqn;
01013 
01014     dbg_err_if (rq == NULL);
01015     dbg_err_if (v == NULL);
01016     dbg_err_if (local_filename == NULL);
01017     dbg_err_if (client_filename == NULL);
01018     dbg_err_if (mime_type == NULL);
01019     dbg_err_if (file_size == NULL);
01020 
01021     info = var_get_opaque(v);
01022     dbg_err_if(info == NULL);
01023 
01024     tmp_fqn = var_get_value(v);
01025     dbg_err_if(tmp_fqn == NULL);
01026 
01027     /* copy out return values */
01028     strncpy(local_filename, tmp_fqn, U_FILENAME_MAX);
01029     strncpy(mime_type, info->mime_type, MIME_TYPE_BUFSZ);
01030     strncpy(client_filename, info->filename, U_FILENAME_MAX);
01031     *file_size = info->size;
01032 
01033     return 0;
01034 err:
01035     return ~0;
01036 }
01037 
01059 int request_get_uploaded_file(request_t *rq, const char *name, size_t idx,
01060     char local_filename[U_FILENAME_MAX], char client_filename[U_FILENAME_MAX],
01061     char mime_type[MIME_TYPE_BUFSZ], size_t *file_size)
01062 {
01063     var_t *v;
01064     upload_info_t *info;
01065     const char *tmp_fqn;
01066 
01067     dbg_err_if (rq == NULL);
01068     dbg_err_if (name == NULL);
01069     dbg_err_if (idx >= vars_count(rq->uploads));
01070     dbg_err_if (local_filename == NULL);
01071     dbg_err_if (client_filename == NULL);
01072     dbg_err_if (mime_type == NULL);
01073     dbg_err_if (file_size == NULL);
01074 
01075     v = vars_geti(rq->uploads, name, idx);
01076     dbg_err_if(v == NULL);
01077 
01078     return request_get_uploaded_filev(rq, v, local_filename, client_filename,
01079         mime_type, file_size);
01080 err:
01081     return ~0;
01082 }
01083 
01084 static int request_parse_multipart_chunk(request_t *rq, io_t *io, 
01085     const char *boundary, int *eof)
01086 {
01087     enum { PRMSZ = 512, BUFSZ = 4096 };
01088     header_t *h = NULL;
01089     io_t *tmpio = NULL;
01090     var_t *v = NULL;
01091     char name[PRMSZ], filename[PRMSZ], buf[BUFSZ];
01092     size_t bound_len, len;
01093     int found;
01094     ssize_t rc;
01095 
01096     /* create an header object to parse MIME part headers */
01097     dbg_err_if(header_create(&h));
01098 
01099     /* read header lines until the first blank line */
01100     dbg_err_if(header_load(h, io));
01101 
01102     warn_err_ifm(is_multipart_mixed(h), 
01103         "multipart/mixed content is not supported yet");
01104 
01105     /* HTTP should never use cte */
01106     warn_err_ifm(is_encoded(h), 
01107         "encoded file upload is not supported");
01108 
01109     dbg_err_if(parse_content_disposition(h, name, filename, PRMSZ));
01110 
01111     /* shortcut */
01112     bound_len = strlen(boundary);
01113 
01114     if(filename[0] != '\0')
01115     {
01116         dbg_err_if(BUFSZ <= bound_len);
01117 
01118         /* open a temporary file to dump uploaded data */
01119         dbg_err_if(u_tmpfile_open(&tmpio));
01120 
01121         for(found = 0; !found; /* nothing */)
01122         {
01123             rc = read_until(io, boundary, buf, BUFSZ, &found);
01124             dbg_err_if(rc <= 0); /* on error or eof exit */
01125 
01126             /* write all but the last bound_len + 2 (\r\n) bytes */
01127             if(found)
01128             {
01129                 rc -= (bound_len + 2);
01130                 dbg_err_if(rc < 0);
01131             }
01132 
01133             /* write to the temp file */
01134             dbg_err_if(io_write(tmpio, buf, rc) < 0);
01135         }
01136 
01137         /* save the path/name of the tmp file to buf */
01138         dbg_err_if(io_name_get(tmpio, buf, BUFSZ));
01139 
01140         /* flush and free */
01141         io_free(tmpio); tmpio = NULL;
01142 
01143         /* add this file to the uploaded file list */
01144         dbg_err_if(request_add_uploaded_file(rq, name, filename, buf, 
01145             header_get_field_value(h, "Content-Type")));
01146 
01147         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01148         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01149 
01150         if(strncmp(buf, "--", 2) == 0)
01151             *eof = 1; /* end of MIME stuff */
01152 
01153     } else {
01154         /* read the value of the variable (all until the next boundary) */
01155         rc = read_until(io, boundary, buf, BUFSZ, &found);
01156         dbg_err_if(rc <= 0); /* on error or eof exit */
01157 
01158         /* write all but the last bound_len + 2 (\r\n) bytes */
01159         warn_err_ifm(!found, "malformed request or BUFSZ too small");
01160 
01161         rc -= (bound_len + 2);
01162         dbg_err_if(rc < 0);
01163 
01164         /* zero-term the buffer (removing the boundary) */
01165         buf[rc] = 0;
01166 
01167         /* add a new binary var to request arguments list */
01168         dbg_err_if(var_bin_create(name, buf, rc, &v));
01169         dbg_if(vars_add(rq->args, v));
01170 
01171         /* could be "\r\n" for not-ending boundaries or "--\r\n" */
01172         dbg_err_if(io_gets(io, buf, BUFSZ) <= 0);
01173 
01174         if(strncmp(buf, "--", 2) == 0)
01175             *eof = 1; /* end of MIME stuff */
01176     }
01177 
01178     header_free(h);
01179 
01180     return 0;
01181 err:
01182     if(tmpio)
01183         io_free(tmpio);
01184     if(h)
01185         header_free(h);
01186     return ~0;
01187 }
01188 
01189 static int request_parse_multipart_data(request_t *rq)
01190 {
01191     enum { BOUNDARY_BUFSZ = 128, BUFSZ = 1024 }; 
01192     char boundary[BOUNDARY_BUFSZ], buf[BUFSZ];
01193     int eof;
01194 
01195     /* boundaries always start with -- */
01196     strcpy(boundary, "--");
01197 
01198     dbg_err_if(request_get_fieldparam(rq, "Content-Type", "boundary",
01199         boundary + 2, BOUNDARY_BUFSZ - 2));
01200 
01201     dbg_err_if(strlen(boundary) == 0);
01202 
01203     /* skip the MIME preamble (usually not used in HTTP) */
01204     for(;;)
01205     {
01206         dbg_err_if(io_gets(rq->io, buf, BUFSZ) <= 0);
01207         if(!strncmp(buf, boundary, strlen(boundary)))
01208             break; /* boundary found */
01209     }
01210 
01211     /* cycle on each MIME part */
01212     for(eof = 0; eof == 0; )
01213         dbg_err_if(request_parse_multipart_chunk(rq, rq->io, boundary, &eof));
01214 
01215     return 0;
01216 err:
01217     return ~0;
01218 }
01219 
01220 static int request_cb_close_socket(talarm_t *al, void *arg)
01221 {
01222     io_t *io = (io_t*)arg;
01223 
01224     u_unused_args(al);
01225 
01226     warn("[%x] connection timed out, closing", io);
01227     
01228     /* close the stream (but not free it) */
01229     io_close(io);
01230 
01231     return 0;
01232 }
01233 
01234 int request_parse_data(request_t *rq)
01235 {
01236     talarm_t *al = NULL;
01237     int rc = HTTP_STATUS_BAD_REQUEST;
01238 
01239     if(rq->method == HM_POST)
01240     {
01241         /* set a timeout to abort POST if it takes too much... */
01242         dbg_err_if(timerm_add(rq->post_timeout, request_cb_close_socket, 
01243             (void*)rq->io, &al));
01244 
01245         /* Content-Length is required when using POST */
01246         dbg_err_if(request_set_content_length(rq) && 
01247             (rc = HTTP_STATUS_LENGTH_REQUIRED));
01248 
01249         if(rq->content_length == 0)
01250             return 0; /* no data posted */
01251 
01252         /* abort if the client is pushing too much data */
01253         dbg_err_if(rq->content_length > rq->post_maxsize &&
01254             (rc = HTTP_STATUS_REQUEST_TOO_LARGE));
01255 
01256         if(request_is_multipart_formdata(rq))
01257         { 
01258             /* some vars may be urlencoded */
01259             dbg_err_if(request_parse_query_args(rq));
01260 
01261             /* <form enctype="multipart/form-data" ...> */
01262             dbg_err_if(request_parse_multipart_data(rq));
01263         } else {
01264             /* <form [enctype="application/x-www-form-urlencoded"] ...> */
01265             dbg_err_if(request_parse_urlencoded_data(rq));
01266         }
01267 
01268         /* post timeout not expired, clear it */
01269         dbg_if(timerm_del(al)); al = NULL;
01270     } else {
01271         /* parse urlencoded variables and set var_t* array */
01272         dbg_err_if(request_parse_query_args(rq));
01273     }
01274 
01275     return 0;
01276 err:
01277     return rc;
01278 }
01279 
01280 /*
01281  * Parse request object \p rq.
01282  *
01283  * \param rq            request object
01284  * \param is_valid_uri  URI validation function
01285  * \param arg           argument to is_valid_uri
01286  *
01287  * \return \c 0 if successful, non-zero on error
01288  */
01289 int request_parse_header(request_t *rq, 
01290         int (*is_valid_uri)(void*, const char *, size_t),
01291         void* arg)
01292 {
01293     enum { BUFSZ = 4096 };
01294     const char WP[] = " \t\r\n";
01295     char ln[BUFSZ], *pp, *method, *uri, *proto;
01296     talarm_t *al = NULL;
01297     int rc = HTTP_STATUS_BAD_REQUEST;
01298     
01299     dbg_err_if (rq == NULL);
01300     dbg_err_if (rq->io == NULL); /* must call rq_bind before rq_parse */
01301 
01302     /* wait at most N seconds to receive the request */
01303     dbg_err_if(timerm_add(rq->idle_timeout, request_cb_close_socket, 
01304         (void*)rq->io, &al));
01305 
01306     if(!rq->cgi)
01307     {
01308         /* cp the first line */
01309         dbg_err_if(io_gets(rq->io, ln, BUFSZ) <= 0);
01310 
01311         /* save the verbatim request line */
01312         dbg_err_if(request_set_client_request(rq, ln));
01313 
01314         method = strtok_r(ln, WP, &pp); 
01315         dbg_err_if(!method || request_set_method(rq, method));
01316 
01317         uri = strtok_r(NULL, WP, &pp);
01318         dbg_err_if(!uri || request_set_uri(rq, uri, is_valid_uri, arg));
01319 
01320         /* HTTP/0.9 not supported yet */ 
01321         proto = strtok_r(NULL, WP, &pp);
01322         dbg_err_if(!proto || request_set_proto(rq, proto)); 
01323 
01324         dbg_err_if(header_load(rq->header, rq->io));
01325     } else
01326         dbg_err_if(header_load_from_cgienv(rq->header));
01327 
01328     /* set if-modified-since time_t value */
01329     dbg_err_if(request_parse_ims(rq));
01330 
01331     /* parse "Cookie:" fields and set the cookies vars_t */
01332     dbg_err_if(request_parse_cookies(rq));
01333 
01334     /* Content-Length is required when using POST */
01335     if(request_get_method(rq) == HM_POST)
01336         dbg_err_if(request_set_content_length(rq));
01337 
01338     /* idle timeout not expired, clear it */
01339     dbg_if(timerm_del(al)); al = NULL;
01340 
01341     /* parse URL encoded or POSTed data */
01342     /* dbg_err_if((rc = request_parse_data(rq))); */
01343 
01344     return 0;
01345 err:
01346     if(al)
01347         timerm_del(al);
01348     return rc;
01349 }
01350 
01361 int request_get_method(request_t *rq)
01362 {
01363     dbg_return_if (rq == NULL, HM_UNKNOWN);
01364 
01365     return rq->method;
01366 }
01367 
01378 const char* request_get_protocol(request_t *rq)
01379 {
01380     dbg_return_if (rq == NULL, "unknown");
01381 
01382     return rq->protocol;
01383 }
01384 
01394 const char *request_get_resolved_filename(request_t *rq)
01395 {
01396     dbg_return_if (rq == NULL, NULL);
01397 
01398     return rq->resolved_filename;
01399 }
01400 
01410 const char *request_get_resolved_path_info(request_t *rq)
01411 {
01412     dbg_return_if (rq == NULL, NULL);
01413 
01414     return rq->resolved_path_info;
01415 }
01416 
01417 int request_print(request_t *rq)
01418 {
01419     dbg_return_if (rq == NULL, ~0);
01420 
01421     dbg("method: %u", rq->method);
01422     dbg("uri: %s", rq->uri);
01423     dbg("proto: %s", rq->protocol);
01424     dbg("filename: %s", rq->filename);
01425     dbg("resolved filename: %s", rq->resolved_filename);
01426     dbg("path_info: %s", rq->path_info);
01427     dbg("resolved path_info: %s", rq->resolved_path_info);
01428     dbg("query: %s", rq->query);
01429 
01430     return 0;
01431 }
01432 
01433 static int request_load_config(request_t *rq)
01434 {
01435     u_config_t *c;
01436     const char *v;
01437 
01438     dbg_err_if (rq == NULL);
01439     dbg_err_if (rq->http == NULL);
01440     dbg_err_if (http_get_config(rq->http) == NULL);
01441 
01442     c = http_get_config(rq->http);
01443     
01444     /* defaults */
01445     rq->idle_timeout = REQUEST_DEFAULT_IDLE_TIMEOUT;
01446     rq->post_timeout = REQUEST_DEFAULT_POST_TIMEOUT;
01447     rq->post_maxsize = REQUEST_DEFAULT_POST_MAXSIZE;
01448 
01449     /* idle timeout */
01450     if((v = u_config_get_subkey_value(c, "idle_timeout")) != NULL)
01451         rq->idle_timeout = MAX(1, atoi(v));
01452 
01453     /* post timeout */
01454     if((v = u_config_get_subkey_value(c, "post_timeout")) != NULL)
01455         rq->post_timeout = MAX(5, atoi(v));
01456 
01457     /* post maxsize */
01458     if((v = u_config_get_subkey_value(c, "post_maxsize")) != NULL)
01459         rq->post_maxsize = MAX(1024, atoi(v));
01460 
01461     return 0;
01462 err:
01463     return ~0;
01464 }
01465 
01466 int request_create(http_t *http, request_t **prq)
01467 {
01468     request_t *rq = NULL;
01469 
01470     dbg_return_if (prq == NULL, ~0);
01471     dbg_return_if (http == NULL, ~0);
01472 
01473     rq = u_zalloc(sizeof(request_t));
01474     dbg_err_if(rq == NULL);
01475 
01476     dbg_err_if(header_create(&rq->header));
01477 
01478     dbg_err_if(vars_create(&rq->args));
01479     dbg_err_if(vars_create(&rq->cookies));
01480     dbg_err_if(vars_create(&rq->uploads));
01481 
01482     rq->http = http;
01483 
01484     dbg_err_if(request_load_config(rq));
01485 
01486     *prq = rq;
01487 
01488     return 0;
01489 err:
01490     if(rq)
01491         request_free(rq);
01492     return ~0;
01493 }
01494 
01495 static int request_unlink_uploads(var_t *v, void * arg)
01496 {
01497     dbg_err_if (v == NULL);
01498 
01499     u_unused_args(arg);
01500 
01501     if(var_get_opaque(v) && var_get_value(v))
01502     {   /* it's a file var, unlink v->value */
01503         unlink(var_get_value(v));
01504     }
01505 
01506 err:
01507     return 0;
01508 }
01509 
01510 int request_free(request_t *rq)
01511 {
01512     if (rq)
01513     {
01514         /* free internal stuff */
01515         request_clear_uri(rq);
01516 
01517         if(rq->header)
01518             header_free(rq->header);
01519 
01520         if(rq->io)
01521             io_free(rq->io);
01522 
01523         if(rq->uploads)
01524         {
01525             /* unlink uploaded files (if any) */
01526             vars_foreach(rq->uploads, request_unlink_uploads, NULL);
01527             vars_free(rq->uploads);
01528         }
01529         
01530         if(rq->cookies)
01531             vars_free(rq->cookies);
01532 
01533         if(rq->args)
01534             vars_free(rq->args);
01535 
01536         U_FREE(rq);
01537     }
01538 
01539     return 0;
01540 }
01541 
01542 /* save the local address struct (ip and port) in the request obj */
01543 int request_set_addr(request_t *rq, addr_t *addr)
01544 {
01545     dbg_return_if (rq == NULL, ~0);
01546     dbg_return_if (addr == NULL, ~0);
01547 
01548     memcpy(&rq->local_addr, addr, sizeof(addr_t));
01549 
01550     return 0;
01551 }
01552 
01553 /* save the peer address struct (ip and port) in the request obj */
01554 int request_set_peer_addr(request_t *rq, addr_t *addr)
01555 {
01556     dbg_return_if (rq == NULL, ~0);
01557     dbg_return_if (addr == NULL, ~0);
01558 
01559     memcpy(&rq->peer_addr, addr, sizeof(addr_t));
01560 
01561     return 0;
01562 }
01563 
01573 addr_t* request_get_addr(request_t *rq)
01574 {
01575     dbg_return_if (rq == NULL, NULL);
01576 
01577     return &rq->local_addr;
01578 }
01579 
01589 addr_t* request_get_peer_addr(request_t *rq)
01590 {
01591     dbg_return_if (rq == NULL, NULL);
01592 
01593     return &rq->peer_addr;
01594 }
01595 
01606 header_t* request_get_header(request_t *rq)
01607 {
01608     dbg_return_if (rq == NULL, NULL);
01609 
01610     return rq->header;
01611 }
01612 
01623 field_t* request_get_field(request_t *rq, const char *name)
01624 {
01625     dbg_return_if (rq == NULL, NULL);
01626     dbg_return_if (name == NULL, NULL);
01627 
01628     return header_get_field(rq->header, name);
01629 }
01630 
01641 const char* request_get_field_value(request_t *rq, const char *name)
01642 {
01643     dbg_return_if (rq == NULL, NULL);
01644     dbg_return_if (name == NULL, NULL);
01645 
01646     return header_get_field_value(rq->header, name);
01647 }
01648 
01649 
01655 int request_set_field(request_t *rq, const char *name, const char *value)
01656 {
01657     dbg_return_if (rq == NULL, ~0);
01658     dbg_return_if (name == NULL, ~0);
01659     dbg_return_if (value == NULL, ~0);
01660 
01661     return header_set_field(rq->header, name, value);
01662 }
01663 
01664 /*
01665  * \brief   Set the filename field of a request
01666  *
01667  * Set the filename field of request \p rq to \p filename.
01668  *
01669  * \param rq        request object
01670  * \param filename  filename string
01671  *
01672  * \return \c 0 if successful, non-zero on error
01673  */
01674 int request_set_filename(request_t *rq, const char *filename)
01675 {
01676     dbg_err_if (rq == NULL);
01677     dbg_err_if (filename == NULL);
01678     
01679     REQUEST_SET_STRING_FIELD(rq->filename, filename);
01680 
01681     return 0;
01682 err:
01683     return ~0;
01684 }
01685 
01686 /*
01687  * \brief   Set the resolved filename field of a request
01688  *
01689  * Set the resolved filename field of request \p rq to \p resolved_fn
01690  *
01691  * \param rq          request object
01692  * \param resolved_fn resolved filename
01693  *
01694  * \return \c 0 if successful, non-zero on error
01695  */
01696 int request_set_resolved_filename(request_t *rq, const char *resolved_fn)
01697 {
01698     dbg_err_if (rq == NULL);
01699     dbg_err_if (resolved_fn == NULL);
01700 
01701     REQUEST_SET_STRING_FIELD(rq->resolved_filename, resolved_fn);
01702 
01703     return 0;
01704 err:
01705     return ~0;
01706 }
01707 /*
01708  * \brief   Set the query string of a request
01709  *
01710  * Parse \p query string and build the \p rq->args list.
01711  *
01712  * \param rq     request object
01713  * \param query  query string 
01714  *
01715  * \return \c 0 if successful, non-zero on error
01716  */
01717 int request_set_query_string(request_t *rq, const char *query)
01718 {
01719     dbg_err_if (rq == NULL);
01720     dbg_err_if (query == NULL);
01721     
01722     REQUEST_SET_STRING_FIELD(rq->query, query);
01723 
01724     return 0;
01725 err:
01726     return ~0;
01727 }
01728 
01729 /*
01730  * \brief   Set the path info field of a request
01731  *
01732  * Set the path info field of request \p rq to \p path_info.
01733  *
01734  * \param rq         request object
01735  * \param path_info  path info
01736  *
01737  * \return \c 0 if successful, non-zero on error
01738  */
01739 int request_set_path_info(request_t *rq, const char *path_info)
01740 {
01741     dbg_err_if (rq == NULL);
01742     dbg_err_if (path_info == NULL);
01743 
01744     REQUEST_SET_STRING_FIELD(rq->path_info, path_info);
01745 
01746     return 0;
01747 err:
01748     return ~0;
01749 }
01750 
01751 /*
01752  * \brief   Set the resolved path info field of a request
01753  *
01754  * Set the resolved path info field of request \p rq to \p resolved_pi.
01755  *
01756  * \param rq           request object
01757  * \param resolved_pi  resolved path info
01758  *
01759  * \return \c 0 if successful, non-zero on error
01760  */
01761 int request_set_resolved_path_info(request_t *rq, const char *resolved_pi)
01762 {
01763     dbg_err_if (rq == NULL);
01764     dbg_err_if (resolved_pi == NULL);
01765 
01766     REQUEST_SET_STRING_FIELD(rq->resolved_path_info, resolved_pi);
01767 
01768     return 0;
01769 err:
01770     return ~0;
01771 }
01772 
01773