blob: 6f80b984113f54e019d0e0b6f764937d49f7d730 [file] [log] [blame]
/*
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 */