| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /** |
| * @file microhttpd/internal.c |
| * @brief internal shared structures |
| * @author Daniel Pittman |
| * @author Christian Grothoff |
| */ |
| #include "internal.h" |
| #include "mhd_str.h" |
| |
| #ifdef HAVE_MESSAGES |
| #if DEBUG_STATES |
| /** |
| * State to string dictionary. |
| */ |
| const char * |
| MHD_state_to_string (enum MHD_CONNECTION_STATE state) |
| { |
| switch (state) |
| { |
| case MHD_CONNECTION_INIT: |
| return "connection init"; |
| case MHD_CONNECTION_URL_RECEIVED: |
| return "connection url received"; |
| case MHD_CONNECTION_HEADER_PART_RECEIVED: |
| return "header partially received"; |
| case MHD_CONNECTION_HEADERS_RECEIVED: |
| return "headers received"; |
| case MHD_CONNECTION_HEADERS_PROCESSED: |
| return "headers processed"; |
| case MHD_CONNECTION_CONTINUE_SENDING: |
| return "continue sending"; |
| case MHD_CONNECTION_CONTINUE_SENT: |
| return "continue sent"; |
| case MHD_CONNECTION_BODY_RECEIVED: |
| return "body received"; |
| case MHD_CONNECTION_FOOTER_PART_RECEIVED: |
| return "footer partially received"; |
| case MHD_CONNECTION_FOOTERS_RECEIVED: |
| return "footers received"; |
| case MHD_CONNECTION_HEADERS_SENDING: |
| return "headers sending"; |
| case MHD_CONNECTION_HEADERS_SENT: |
| return "headers sent"; |
| case MHD_CONNECTION_NORMAL_BODY_READY: |
| return "normal body ready"; |
| case MHD_CONNECTION_NORMAL_BODY_UNREADY: |
| return "normal body unready"; |
| case MHD_CONNECTION_CHUNKED_BODY_READY: |
| return "chunked body ready"; |
| case MHD_CONNECTION_CHUNKED_BODY_UNREADY: |
| return "chunked body unready"; |
| case MHD_CONNECTION_BODY_SENT: |
| return "body sent"; |
| case MHD_CONNECTION_FOOTERS_SENDING: |
| return "footers sending"; |
| case MHD_CONNECTION_FOOTERS_SENT: |
| return "footers sent"; |
| case MHD_CONNECTION_CLOSED: |
| return "closed"; |
| default: |
| return "unrecognized connection state"; |
| } |
| } |
| |
| |
| #endif |
| #endif |
| |
| |
| #ifdef HAVE_MESSAGES |
| /** |
| * fprintf-like helper function for logging debug |
| * messages. |
| */ |
| void |
| MHD_DLOG (const struct MHD_Daemon *daemon, |
| enum MHD_StatusCode sc, |
| const char *format, |
| ...) |
| { |
| va_list va; |
| |
| if (NULL == daemon->logger) |
| return; |
| va_start (va, |
| format); |
| daemon->logger (daemon->logger_cls, |
| sc, |
| format, |
| va); |
| va_end (va); |
| } |
| |
| |
| #endif |
| |
| |
| /** |
| * Convert all occurrences of '+' to ' '. |
| * |
| * @param arg string that is modified (in place), must be 0-terminated |
| */ |
| void |
| MHD_unescape_plus (char *arg) |
| { |
| char *p; |
| |
| for (p = strchr (arg, '+'); NULL != p; p = strchr (p + 1, '+')) |
| *p = ' '; |
| } |
| |
| |
| /** |
| * Process escape sequences ('%HH') Updates val in place; the |
| * result should be UTF-8 encoded and cannot be larger than the input. |
| * The result must also still be 0-terminated. |
| * |
| * @param val value to unescape (modified in the process) |
| * @return length of the resulting val (strlen(val) maybe |
| * shorter afterwards due to elimination of escape sequences) |
| */ |
| size_t |
| MHD_http_unescape (char *val) |
| { |
| char *rpos = val; |
| char *wpos = val; |
| |
| while ('\0' != *rpos) |
| { |
| uint32_t num; |
| switch (*rpos) |
| { |
| case '%': |
| if (2 == MHD_strx_to_uint32_n_ (rpos + 1, |
| 2, |
| &num)) |
| { |
| *wpos = (char) ((unsigned char) num); |
| wpos++; |
| rpos += 3; |
| break; |
| } |
| /* TODO: add bad sequence handling */ |
| /* intentional fall through! */ |
| default: |
| *wpos = *rpos; |
| wpos++; |
| rpos++; |
| } |
| } |
| *wpos = '\0'; /* add 0-terminator */ |
| return wpos - val; /* = strlen(val) */ |
| } |
| |
| |
| /** |
| * Parse and unescape the arguments given by the client |
| * as part of the HTTP request URI. |
| * |
| * @param request request to add headers to |
| * @param kind header kind to pass to @a cb |
| * @param[in,out] args argument URI string (after "?" in URI), |
| * clobbered in the process! |
| * @param cb function to call on each key-value pair found |
| * @param[out] num_headers set to the number of headers found |
| * @return false on failure (@a cb returned false), |
| * true for success (parsing succeeded, @a cb always |
| * returned true) |
| */ |
| bool |
| MHD_parse_arguments_ (struct MHD_Request *request, |
| enum MHD_ValueKind kind, |
| char *args, |
| MHD_ArgumentIterator_ cb, |
| unsigned int *num_headers) |
| { |
| struct MHD_Daemon *daemon = request->daemon; |
| char *equals; |
| char *amper; |
| |
| *num_headers = 0; |
| while ( (NULL != args) && |
| ('\0' != args[0]) ) |
| { |
| equals = strchr (args, '='); |
| amper = strchr (args, '&'); |
| if (NULL == amper) |
| { |
| /* last argument */ |
| if (NULL == equals) |
| { |
| /* last argument, without '=' */ |
| MHD_unescape_plus (args); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| args); |
| if (! cb (request, |
| args, |
| NULL, |
| kind)) |
| return false; |
| (*num_headers)++; |
| break; |
| } |
| /* got 'foo=bar' */ |
| equals[0] = '\0'; |
| equals++; |
| MHD_unescape_plus (args); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| args); |
| MHD_unescape_plus (equals); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| equals); |
| if (! cb (request, |
| args, |
| equals, |
| kind)) |
| return false; |
| (*num_headers)++; |
| break; |
| } |
| /* amper is non-NULL here */ |
| amper[0] = '\0'; |
| amper++; |
| if ( (NULL == equals) || |
| (equals >= amper) ) |
| { |
| /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */ |
| MHD_unescape_plus (args); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| args); |
| if (! cb (request, |
| args, |
| NULL, |
| kind)) |
| return false; |
| /* continue with 'bar' */ |
| (*num_headers)++; |
| args = amper; |
| continue; |
| } |
| /* equals and amper are non-NULL here, and equals < amper, |
| so we got regular 'foo=value&bar...'-kind of argument */ |
| equals[0] = '\0'; |
| equals++; |
| MHD_unescape_plus (args); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| args); |
| MHD_unescape_plus (equals); |
| daemon->unescape_cb (daemon->unescape_cb_cls, |
| request, |
| equals); |
| if (! cb (request, |
| args, |
| equals, |
| kind)) |
| return false; |
| (*num_headers)++; |
| args = amper; |
| } |
| return true; |
| } |
| |
| |
| /* end of internal.c */ |