/** * 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[out] num_headers set to the number of headers found * @return #MHD_NO on failure (@a cb returned #MHD_NO), * #MHD_YES for success (parsing succeeded, @a cb always * returned #MHD_YES) */ int MHD_parse_arguments_ (struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers) { struct MHD_Daemon *daemon = connection->daemon; char *equals; char *amper; *num_headers = 0; 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_YES != cb (connection, args, key_len, NULL, 0, kind)) return MHD_NO; (*num_headers)++; 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_YES != cb (connection, args, key_len, equals, value_len, kind)) return MHD_NO; (*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); key_len = daemon->unescape_callback (daemon->unescape_callback_cls, connection, args); if (MHD_YES != cb (connection, args, key_len, NULL, 0, kind)) return MHD_NO; /* 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); 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_YES != cb (connection, args, key_len, equals, value_len, kind)) return MHD_NO; (*num_headers)++; args = amper; } return MHD_YES; }
/** * Process url-encoded POST data. * * @param pp post processor context * @param post_data upload data * @param post_data_len number of bytes in @a post_data * @return #MHD_YES on success, #MHD_NO if there was an error processing the data */ static int post_process_urlencoded (struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len) { size_t equals; size_t amper; size_t poff; size_t xoff; size_t delta; int end_of_value_found; char *buf; char xbuf[XBUF_SIZE + 1]; buf = (char *) &pp[1]; poff = 0; while (poff < post_data_len) { switch (pp->state) { case PP_Error: return MHD_NO; case PP_Done: /* did not expect to receive more data */ pp->state = PP_Error; return MHD_NO; case PP_Init: equals = 0; while ((equals + poff < post_data_len) && (post_data[equals + poff] != '=')) equals++; if (equals + pp->buffer_pos > pp->buffer_size) { pp->state = PP_Error; /* out of memory */ return MHD_NO; } memcpy (&buf[pp->buffer_pos], &post_data[poff], equals); pp->buffer_pos += equals; if (equals + poff == post_data_len) return MHD_YES; /* no '=' yet */ buf[pp->buffer_pos] = '\0'; /* 0-terminate key */ pp->buffer_pos = 0; /* reset for next key */ MHD_unescape_plus (buf); MHD_http_unescape (buf); poff += equals + 1; pp->state = PP_ProcessValue; pp->value_offset = 0; break; case PP_ProcessValue: /* obtain rest of value from previous iteration */ memcpy (xbuf, pp->xbuf, pp->xbuf_pos); xoff = pp->xbuf_pos; pp->xbuf_pos = 0; /* find last position in input buffer that is part of the value */ amper = 0; while ((amper + poff < post_data_len) && (amper < XBUF_SIZE) && (post_data[amper + poff] != '&') && (post_data[amper + poff] != '\n') && (post_data[amper + poff] != '\r')) amper++; end_of_value_found = ((amper + poff < post_data_len) && ((post_data[amper + poff] == '&') || (post_data[amper + poff] == '\n') || (post_data[amper + poff] == '\r'))); /* compute delta, the maximum number of bytes that we will be able to process right now (either amper-limited of xbuf-size limited) */ delta = amper; if (delta > XBUF_SIZE - xoff) delta = XBUF_SIZE - xoff; /* move input into processing buffer */ memcpy (&xbuf[xoff], &post_data[poff], delta); xoff += delta; poff += delta; /* find if escape sequence is at the end of the processing buffer; if so, exclude those from processing (reduce delta to point at end of processed region) */ delta = xoff; if ((delta > 0) && (xbuf[delta - 1] == '%')) delta--; else if ((delta > 1) && (xbuf[delta - 2] == '%')) delta -= 2; /* if we have an incomplete escape sequence, save it to pp->xbuf for later */ if (delta < xoff) { memcpy (pp->xbuf, &xbuf[delta], xoff - delta); pp->xbuf_pos = xoff - delta; xoff = delta; } /* If we have nothing to do (delta == 0) and not just because the value is empty (are waiting for more data), go for next iteration */ if ((xoff == 0) && (poff == post_data_len)) continue; /* unescape */ xbuf[xoff] = '\0'; /* 0-terminate in preparation */ MHD_unescape_plus (xbuf); xoff = MHD_http_unescape (xbuf); /* finally: call application! */ pp->must_ikvi = MHD_NO; if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1], /* key */ NULL, NULL, NULL, xbuf, pp->value_offset, xoff)) { pp->state = PP_Error; return MHD_NO; } pp->value_offset += xoff; /* are we done with the value? */ if (end_of_value_found) { /* we found the end of the value! */ if ((post_data[poff] == '\n') || (post_data[poff] == '\r')) { pp->state = PP_ExpectNewLine; } else if (post_data[poff] == '&') { poff++; /* skip '&' */ pp->state = PP_Init; } } break; case PP_ExpectNewLine: if ((post_data[poff] == '\n') || (post_data[poff] == '\r')) { poff++; /* we are done, report error if we receive any more... */ pp->state = PP_Done; return MHD_YES; } return MHD_NO; default: mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /* should never happen! */ } } return MHD_YES; }