internal.c (8073B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Daniel Pittman and Christian Grothoff 4 Copyright (C) 2015-2023 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /** 22 * @file microhttpd/internal.c 23 * @brief internal shared structures 24 * @author Daniel Pittman 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "internal.h" 30 #include "mhd_str.h" 31 32 #ifdef HAVE_MESSAGES 33 #if DEBUG_STATES 34 /** 35 * State to string dictionary. 36 */ 37 const char * 38 MHD_state_to_string (enum MHD_CONNECTION_STATE state) 39 { 40 switch (state) 41 { 42 case MHD_CONNECTION_INIT: 43 return "connection init"; 44 case MHD_CONNECTION_REQ_LINE_RECEIVING: 45 return "receiving request line"; 46 case MHD_CONNECTION_REQ_LINE_RECEIVED: 47 return "request line received"; 48 case MHD_CONNECTION_REQ_HEADERS_RECEIVING: 49 return "headers receiving"; 50 case MHD_CONNECTION_HEADERS_RECEIVED: 51 return "headers received"; 52 case MHD_CONNECTION_HEADERS_PROCESSED: 53 return "headers processed"; 54 case MHD_CONNECTION_CONTINUE_SENDING: 55 return "continue sending"; 56 case MHD_CONNECTION_BODY_RECEIVING: 57 return "body receiving"; 58 case MHD_CONNECTION_BODY_RECEIVED: 59 return "body received"; 60 case MHD_CONNECTION_FOOTERS_RECEIVING: 61 return "footers receiving"; 62 case MHD_CONNECTION_FOOTERS_RECEIVED: 63 return "footers received"; 64 case MHD_CONNECTION_FULL_REQ_RECEIVED: 65 return "full request received"; 66 case MHD_CONNECTION_START_REPLY: 67 return "start sending reply"; 68 case MHD_CONNECTION_HEADERS_SENDING: 69 return "headers sending"; 70 case MHD_CONNECTION_HEADERS_SENT: 71 return "headers sent"; 72 case MHD_CONNECTION_NORMAL_BODY_UNREADY: 73 return "normal body unready"; 74 case MHD_CONNECTION_NORMAL_BODY_READY: 75 return "normal body ready"; 76 case MHD_CONNECTION_CHUNKED_BODY_UNREADY: 77 return "chunked body unready"; 78 case MHD_CONNECTION_CHUNKED_BODY_READY: 79 return "chunked body ready"; 80 case MHD_CONNECTION_CHUNKED_BODY_SENT: 81 return "chunked body sent"; 82 case MHD_CONNECTION_FOOTERS_SENDING: 83 return "footers sending"; 84 case MHD_CONNECTION_FULL_REPLY_SENT: 85 return "reply sent completely"; 86 case MHD_CONNECTION_CLOSED: 87 return "closed"; 88 default: 89 return "unrecognized connection state"; 90 } 91 } 92 93 94 #endif 95 #endif 96 97 98 #ifdef HAVE_MESSAGES 99 /** 100 * fprintf-like helper function for logging debug 101 * messages. 102 */ 103 void 104 MHD_DLOG (const struct MHD_Daemon *daemon, 105 const char *format, 106 ...) 107 { 108 va_list va; 109 110 if (0 == (daemon->options & MHD_USE_ERROR_LOG)) 111 return; 112 va_start (va, format); 113 daemon->custom_error_log (daemon->custom_error_log_cls, 114 format, 115 va); 116 va_end (va); 117 } 118 119 120 #endif 121 122 123 /** 124 * Convert all occurrences of '+' to ' '. 125 * 126 * @param arg string that is modified (in place), must be 0-terminated 127 */ 128 void 129 MHD_unescape_plus (char *arg) 130 { 131 char *p; 132 133 for (p = strchr (arg, '+'); NULL != p; p = strchr (p + 1, '+')) 134 *p = ' '; 135 } 136 137 138 /** 139 * Process escape sequences ('%HH') Updates val in place; the 140 * result cannot be larger than the input. 141 * The result is still be 0-terminated. 142 * 143 * @param val value to unescape (modified in the process) 144 * @return length of the resulting val (`strlen(val)` may be 145 * shorter afterwards due to elimination of escape sequences) 146 */ 147 _MHD_EXTERN size_t 148 MHD_http_unescape (char *val) 149 { 150 return MHD_str_pct_decode_in_place_lenient_ (val, NULL); 151 } 152 153 154 /** 155 * Parse and unescape the arguments given by the client 156 * as part of the HTTP request URI. 157 * 158 * @param kind header kind to pass to @a cb 159 * @param connection connection to add headers to 160 * @param[in,out] args argument URI string (after "?" in URI), 161 * clobbered in the process! 162 * @param cb function to call on each key-value pair found 163 * @param cls the iterator context 164 * @return #MHD_NO on failure (@a cb returned #MHD_NO), 165 * #MHD_YES for success (parsing succeeded, @a cb always 166 * returned #MHD_YES) 167 */ 168 enum MHD_Result 169 MHD_parse_arguments_ (struct MHD_Connection *connection, 170 enum MHD_ValueKind kind, 171 char *args, 172 MHD_ArgumentIterator_ cb, 173 void *cls) 174 { 175 struct MHD_Daemon *daemon = connection->daemon; 176 char *equals; 177 char *amper; 178 179 while ( (NULL != args) && 180 ('\0' != args[0]) ) 181 { 182 size_t key_len; 183 size_t value_len; 184 equals = strchr (args, '='); 185 amper = strchr (args, '&'); 186 if (NULL == amper) 187 { 188 /* last argument */ 189 if (NULL == equals) 190 { 191 /* last argument, without '=' */ 192 MHD_unescape_plus (args); 193 key_len = daemon->unescape_callback (daemon->unescape_callback_cls, 194 connection, 195 args); 196 if (MHD_NO == cb (cls, 197 args, 198 key_len, 199 NULL, 200 0, 201 kind)) 202 return MHD_NO; 203 break; 204 } 205 /* got 'foo=bar' */ 206 equals[0] = '\0'; 207 equals++; 208 MHD_unescape_plus (args); 209 key_len = daemon->unescape_callback (daemon->unescape_callback_cls, 210 connection, 211 args); 212 MHD_unescape_plus (equals); 213 value_len = daemon->unescape_callback (daemon->unescape_callback_cls, 214 connection, 215 equals); 216 if (MHD_NO == cb (cls, 217 args, 218 key_len, 219 equals, 220 value_len, 221 kind)) 222 return MHD_NO; 223 break; 224 } 225 /* amper is non-NULL here */ 226 amper[0] = '\0'; 227 amper++; 228 if ( (NULL == equals) || 229 (equals >= amper) ) 230 { 231 /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */ 232 MHD_unescape_plus (args); 233 key_len = daemon->unescape_callback (daemon->unescape_callback_cls, 234 connection, 235 args); 236 if (MHD_NO == cb (cls, 237 args, 238 key_len, 239 NULL, 240 0, 241 kind)) 242 return MHD_NO; 243 /* continue with 'bar' */ 244 args = amper; 245 continue; 246 } 247 /* equals and amper are non-NULL here, and equals < amper, 248 so we got regular 'foo=value&bar...'-kind of argument */ 249 equals[0] = '\0'; 250 equals++; 251 MHD_unescape_plus (args); 252 key_len = daemon->unescape_callback (daemon->unescape_callback_cls, 253 connection, 254 args); 255 MHD_unescape_plus (equals); 256 value_len = daemon->unescape_callback (daemon->unescape_callback_cls, 257 connection, 258 equals); 259 if (MHD_NO == cb (cls, 260 args, 261 key_len, 262 equals, 263 value_len, 264 kind)) 265 return MHD_NO; 266 args = amper; 267 } 268 return MHD_YES; 269 } 270 271 272 /* end of internal.c */