| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2007 Daniel Pittman and Christian Grothoff |
| Copyright (C) 2015-2021 Evgeny Grin (Karlson2k) |
| |
| 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 |
| * @author Karlson2k (Evgeny Grin) |
| */ |
| |
| #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_REQ_LINE_RECEIVING: |
| return "receiving request line"; |
| 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_BODY_RECEIVING: |
| return "body receiving"; |
| 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_FULL_REQ_RECEIVED: |
| return "full request received"; |
| case MHD_CONNECTION_START_REPLY: |
| return "start sending reply"; |
| case MHD_CONNECTION_HEADERS_SENDING: |
| return "headers sending"; |
| case MHD_CONNECTION_HEADERS_SENT: |
| return "headers sent"; |
| case MHD_CONNECTION_NORMAL_BODY_UNREADY: |
| return "normal body unready"; |
| case MHD_CONNECTION_NORMAL_BODY_READY: |
| return "normal body ready"; |
| case MHD_CONNECTION_CHUNKED_BODY_UNREADY: |
| return "chunked body unready"; |
| case MHD_CONNECTION_CHUNKED_BODY_READY: |
| return "chunked body ready"; |
| case MHD_CONNECTION_CHUNKED_BODY_SENT: |
| return "chunked body sent"; |
| case MHD_CONNECTION_FOOTERS_SENDING: |
| return "footers sending"; |
| case MHD_CONNECTION_FULL_REPLY_SENT: |
| return "reply sent completely"; |
| 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, |
| const char *format, |
| ...) |
| { |
| va_list va; |
| |
| if (0 == (daemon->options & MHD_USE_ERROR_LOG)) |
| return; |
| va_start (va, format); |
| daemon->custom_error_log (daemon->custom_error_log_cls, |
| 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 cannot be larger than the input. |
| * The result is still be 0-terminated. |
| * |
| * @param val value to unescape (modified in the process) |
| * @return length of the resulting val (`strlen(val)` may be |
| * shorter afterwards due to elimination of escape sequences) |
| */ |
| _MHD_EXTERN size_t |
| MHD_http_unescape (char *val) |
| { |
| return MHD_str_pct_decode_in_place_lenient_ (val, NULL); |
| } |
| |
| |
| /** |
| * Parse and unescape the arguments given by the client |
| * as part of the HTTP request URI. |
| * |
| * @param kind header kind to pass to @a cb |
| * @param connection connection to add headers to |
| * @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 cls the iterator context |
| * @return #MHD_NO on failure (@a cb returned #MHD_NO), |
| * #MHD_YES for success (parsing succeeded, @a cb always |
| * returned #MHD_YES) |
| */ |
| enum MHD_Result |
| MHD_parse_arguments_ (struct MHD_Connection *connection, |
| enum MHD_ValueKind kind, |
| char *args, |
| MHD_ArgumentIterator_ cb, |
| void *cls) |
| { |
| struct MHD_Daemon *daemon = connection->daemon; |
| char *equals; |
| char *amper; |
| |
| while ( (NULL != args) && |
| ('\0' != args[0]) ) |
| { |
| size_t key_len; |
| size_t value_len; |
| equals = strchr (args, '='); |
| amper = strchr (args, '&'); |
| if (NULL == amper) |
| { |
| /* last argument */ |
| if (NULL == equals) |
| { |
| /* last argument, without '=' */ |
| MHD_unescape_plus (args); |
| key_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| args); |
| if (MHD_NO == cb (cls, |
| args, |
| key_len, |
| NULL, |
| 0, |
| kind)) |
| return MHD_NO; |
| break; |
| } |
| /* got 'foo=bar' */ |
| equals[0] = '\0'; |
| equals++; |
| MHD_unescape_plus (args); |
| key_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| args); |
| MHD_unescape_plus (equals); |
| value_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| equals); |
| if (MHD_NO == cb (cls, |
| args, |
| key_len, |
| equals, |
| value_len, |
| kind)) |
| return MHD_NO; |
| 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); |
| key_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| args); |
| if (MHD_NO == cb (cls, |
| args, |
| key_len, |
| NULL, |
| 0, |
| kind)) |
| return MHD_NO; |
| /* continue with 'bar' */ |
| 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); |
| key_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| args); |
| MHD_unescape_plus (equals); |
| value_len = daemon->unescape_callback (daemon->unescape_callback_cls, |
| connection, |
| equals); |
| if (MHD_NO == cb (cls, |
| args, |
| key_len, |
| equals, |
| value_len, |
| kind)) |
| return MHD_NO; |
| args = amper; |
| } |
| return MHD_YES; |
| } |
| |
| |
| /* end of internal.c */ |