فهرست منبع

API function mg_split_form_urlencoded (#856)

Add parameter for form_fields array size.
Add API documentation
Add/fix unit tests.
bel2125 5 سال پیش
والد
کامیت
229c964c02
5فایلهای تغییر یافته به همراه82 افزوده شده و 24 حذف شده
  1. 36 0
      docs/api/mg_split_form_urlencoded.md
  2. 8 8
      include/civetweb.h
  3. 23 5
      src/civetweb.c
  4. 4 2
      src/mod_lua.inl
  5. 11 9
      unittest/public_func.c

+ 36 - 0
docs/api/mg_split_form_urlencoded.md

@@ -0,0 +1,36 @@
+# Civetweb API Reference
+
+### `mg_split_form_urlencoded( data, form_fields, num_form_fields );`
+
+### Parameters
+
+| Parameter | Type | Description |
+| :--- | :--- | :--- |
+|**`data`**|`char *`|Form encoded data to be split. This buffer will be modified by `mg_split_form_urlencoded`.|
+|**`form_fields`**|`struct mg_header []`|Output buffer for name-value-pairs.|
+|**`num_form_fields`**|`unsigned`|Number of elements in form_fields buffer.|
+
+### Return Value
+
+| Type | Description |
+| :--- | :--- |
+|`int`|Number of encoded form fields, or an error code|
+
+### Description
+
+The function `mg_split_form_urlencoded()` can be used to split an x-www-form-urlencoded data field into name-value-pairs. It can split the POST body data of an x-www-form-urlencoded html form, or a query string. The parameter `data` is modified by this function, so the `query_string` member of `struct mg_request_info` must not be passed directly to this function, since `query_string` is a `const char *`. Use a copy (e.g, created by `strdup`) as input for `mg_split_form_urlencoded()`. 
+The caller has to provide all required buffers, since the function does not do any memory handling itself. Percent-encoded elements will be decoded.
+
+Example for an x-www-form-urlencoded format encoded input:
+`data = "keyName1=value1&keyName2=value2&keyName3=value3"`
+The function will return 3 and set `form_fields[0].name = "keyName1"`, `form_fields[0].value = "value1"`, `form_fields[1].name = "keyName2"`, `form_fields[1].value = "value2"`, `form_fields[2].name = "keyName3"`, `form_fields[2].value = "value3"`, and modify `data` to `"keyName1\x00value1\x00keyName2\x00value2\x00keyName3\x00value3"`.
+
+
+### See Also
+
+* [`struct mg_header`](mg_header.md)
+* [`struct mg_request_info`](mg_request_info.md)
+* [`mg_get_cookie();`](mg_get_cookie.md)
+* [`mg_get_var();`](mg_get_var.md)
+* [`mg_get_var2();`](mg_get_var2.md)
+* [`mg_handle_form_request();`](mg_handle_form_request.md)

+ 8 - 8
include/civetweb.h

@@ -141,10 +141,6 @@ struct mg_header {
 };
 
 
-/* Maximum number of form fields for mg_spit_form_encoded. */
-#define MG_MAX_FORM_FIELDS (64)
-
-
 /* This structure contains information about the HTTP request. */
 struct mg_request_info {
 	const char *request_method;  /* "GET", "POST", etc */
@@ -1166,15 +1162,19 @@ CIVETWEB_API int mg_get_var2(const char *data,
 
    Parameters:
      data: form encoded iput string. Will be modified by this function.
-     form_fields: output list of name/value-pairs.
+     form_fields: output list of name/value-pairs. A buffer with a size
+                  specified by num_form_fields must be provided by the
+                  caller.
+     num_form_fields: Size of provided form_fields buffer in number of
+                      "struct mg_header" elements.
 
    Return:
      On success: number of form_fields filled
      On error:
         -1 (parameter error). */
-CIVETWEB_API int
-mg_split_form_urlencoded(char *data,
-                         struct mg_header form_fields[MG_MAX_FORM_FIELDS]);
+CIVETWEB_API int mg_split_form_urlencoded(char *data,
+                                          struct mg_header *form_fields,
+                                          unsigned num_form_fields);
 
 
 /* Fetch value of certain cookie variable into the destination buffer.

+ 23 - 5
src/civetweb.c

@@ -7344,18 +7344,36 @@ mg_get_var2(const char *data,
 /* split a string "key1=val1&key2=val2" into key/value pairs */
 int
 mg_split_form_urlencoded(char *data,
-                         struct mg_header form_fields[MG_MAX_FORM_FIELDS])
+                         struct mg_header *form_fields,
+                         unsigned num_form_fields)
 {
 	char *b;
 	int i;
 	int num = 0;
 
-	if ((data == NULL) || (form_fields == NULL)) {
+	if ((form_fields == NULL) && (num_form_fields == 0)) {
+		/* determine the number of expected fields */
+		if (data[0] == 0) {
+			return 0;
+		}
+		/* count number of & to return the number of key-value-pairs */
+		num = 1;
+		while (*data) {
+			if (*data == '&') {
+				num++;
+			}
+			data++;
+		}
+		return num;
+	}
+
+	if ((data == NULL) || (form_fields == NULL)
+	    || ((int)num_form_fields <= 0)) {
 		/* parameter error */
 		return -1;
 	}
 
-	for (i = 0; i < MG_MAX_FORM_FIELDS; i++) {
+	for (i = 0; i < (int)num_form_fields; i++) {
 		/* extract key-value pairs from input data */
 		while ((*data == ' ') || (*data == '\t')) {
 			/* skip initial spaces */
@@ -18422,7 +18440,7 @@ process_new_connection(struct mg_connection *conn)
 #if defined(USE_HTTP2)
 		if (is_http2) {
 			if (!is_valid_http2_primer(conn)) {
-				/* Primer does not match expectation from RFC. 
+				/* Primer does not match expectation from RFC.
 				 * See https://tools.ietf.org/html/rfc7540#section-3.5 */
 				mg_snprintf(conn,
 				            NULL, /* No truncation check for ebuf */
@@ -18436,7 +18454,7 @@ process_new_connection(struct mg_connection *conn)
 			}
 		} else
 #endif
-		if (ebuf[0] == '\0') {
+		    if (ebuf[0] == '\0') {
 			if (conn->request_info.local_uri) {
 
 /* handle request to local server */

+ 4 - 2
src/mod_lua.inl

@@ -1151,6 +1151,8 @@ lsp_get_var(lua_State *L)
 }
 
 
+#define MG_MAX_FORM_FIELDS (64)
+
 /* mg.split_form_data */
 static int
 lsp_split_form_urlencoded(lua_State *L)
@@ -1182,8 +1184,8 @@ lsp_split_form_urlencoded(lua_State *L)
 	}
 	memcpy(buf, in, len + 1);
 
-	/* mg_split_form_encoded does the real work */
-	ret = mg_split_form_urlencoded(buf, form_fields);
+	/* mg_split_form_urlencoded does the real work */
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 
 	if (ret < 0) {
 		return luaL_error(L, "error in invalid split_form_data() call");

+ 11 - 9
unittest/public_func.c

@@ -528,33 +528,35 @@ START_TEST(test_mg_url_decode)
 END_TEST
 
 
-START_TEST(test_mg_split_form_encoded)
+#define MG_MAX_FORM_FIELDS (64)
+
+START_TEST(test_mg_split_form_urlencoded)
 {
 	char buf[256] = {0};
 	struct mg_header form_fields[MG_MAX_FORM_FIELDS] = {0};
 	int ret;
 
-	ret = mg_split_form_encoded(NULL, form_fields);
+	ret = mg_split_form_urlencoded(NULL, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, -1);
 
 	strcpy(buf, "");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 0);
 
 	strcpy(buf, "test");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 1);
 	ck_assert_str_eq(form_fields[0].name, "test");
 	ck_assert_ptr_eq(form_fields[0].value, NULL);
 
 	strcpy(buf, "key=val");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 1);
 	ck_assert_str_eq(form_fields[0].name, "key");
 	ck_assert_str_eq(form_fields[0].value, "val");
 
 	strcpy(buf, "key=val&key2=val2");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 2);
 	ck_assert_str_eq(form_fields[0].name, "key");
 	ck_assert_str_eq(form_fields[0].value, "val");
@@ -562,7 +564,7 @@ START_TEST(test_mg_split_form_encoded)
 	ck_assert_str_eq(form_fields[1].value, "val2");
 
 	strcpy(buf, "k1=v1&k2=v2&k3=&k4&k5=v5");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 5);
 	ck_assert_str_eq(form_fields[0].name, "k1");
 	ck_assert_str_eq(form_fields[1].name, "k2");
@@ -576,7 +578,7 @@ START_TEST(test_mg_split_form_encoded)
 	ck_assert_str_eq(form_fields[4].value, "v5");
 
 	strcpy(buf, "key=v+l1&key2=v%20l2");
-	ret = mg_split_form_encoded(buf, form_fields);
+	ret = mg_split_form_urlencoded(buf, form_fields, MG_MAX_FORM_FIELDS);
 	ck_assert_int_eq(ret, 2);
 	ck_assert_str_eq(form_fields[0].name, "key");
 	ck_assert_str_eq(form_fields[0].value, "v l1");
@@ -653,7 +655,7 @@ make_public_func_suite(void)
 
 	tcase_add_test(tcase_urlencodingdecoding, test_mg_url_encode);
 	tcase_add_test(tcase_urlencodingdecoding, test_mg_url_decode);
-	tcase_add_test(tcase_urlencodingdecoding, test_mg_split_form_encoded);
+	tcase_add_test(tcase_urlencodingdecoding, test_mg_split_form_urlencoded);
 	tcase_set_timeout(tcase_urlencodingdecoding, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_urlencodingdecoding);