|
@@ -27,31 +27,78 @@
|
|
/**********************/
|
|
/**********************/
|
|
/* proposed interface */
|
|
/* proposed interface */
|
|
|
|
|
|
-enum {
|
|
|
|
- FORM_DISPOSITION_SKIP = 0x0,
|
|
|
|
- FORM_DISPOSITION_GET = 0x1,
|
|
|
|
- FORM_DISPOSITION_STORE = 0x2,
|
|
|
|
- /* FORM_DISPOSITION_READ = 0x3, not in the first step */
|
|
|
|
- FORM_DISPOSITION_ABORT = 0x10
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
+/* This structure contains callback functions for handling form fields.
|
|
|
|
+ It is used as an argument to mg_handle_form_data.
|
|
|
|
+*/
|
|
struct mg_form_data_handler {
|
|
struct mg_form_data_handler {
|
|
|
|
+ /* This callback is called, if a new field is about to be read.
|
|
|
|
+ * Parameters:
|
|
|
|
+ * key: Name of the field ("name" property of the HTML input field).
|
|
|
|
+ * filename: Name of a file to upload, at the client computer.
|
|
|
|
+ * Only set for input fields of type "file", otherwise NULL.
|
|
|
|
+ * path: Output parameter: File name (incl. path) to store the file
|
|
|
|
+ * at the server computer. Only used if FORM_FIELD_STORAGE_STORE
|
|
|
|
+ * is returned by this callback.
|
|
|
|
+ * pathlen: Length of the buffer for path.
|
|
|
|
+ * user_data: Value of the member user_data of mg_form_data_handler
|
|
|
|
+ * Return value:
|
|
|
|
+ * The callback must return the intended storage for this field
|
|
|
|
+ * (See FORM_FIELD_STORAGE_*).
|
|
|
|
+ */
|
|
int (*field_found)(const char *key,
|
|
int (*field_found)(const char *key,
|
|
const char *filename,
|
|
const char *filename,
|
|
char *path,
|
|
char *path,
|
|
size_t pathlen,
|
|
size_t pathlen,
|
|
void *user_data);
|
|
void *user_data);
|
|
|
|
+
|
|
|
|
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
|
|
|
|
+ * this callback will receive the field data.
|
|
|
|
+ * Parameters:
|
|
|
|
+ * key: Name of the field ("name" property of the HTML input field).
|
|
|
|
+ * value: Value of the input field.
|
|
|
|
+ * user_data: Value of the member user_data of mg_form_data_handler
|
|
|
|
+ * Return value:
|
|
|
|
+ * TODO: Needs to be defined.
|
|
|
|
+ */
|
|
int (*field_get)(const char *key,
|
|
int (*field_get)(const char *key,
|
|
- const char *filename,
|
|
|
|
const char *value,
|
|
const char *value,
|
|
size_t valuelen,
|
|
size_t valuelen,
|
|
void *user_data);
|
|
void *user_data);
|
|
|
|
+
|
|
|
|
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
|
|
|
|
+ * the data will be stored into a file. If the file has been written
|
|
|
|
+ * sucessfully, this callback will be called.
|
|
|
|
+ * Parameters:
|
|
|
|
+ * path: Path of the file stored at the server.
|
|
|
|
+ * user_data: Value of the member user_data of mg_form_data_handler
|
|
|
|
+ * Return value:
|
|
|
|
+ * TODO: Needs to be defined.
|
|
|
|
+ */
|
|
int (*field_stored)(const char *path, void *user_data);
|
|
int (*field_stored)(const char *path, void *user_data);
|
|
|
|
+
|
|
|
|
+ /* User supplied argument, passed to all callbacks. */
|
|
void *user_data;
|
|
void *user_data;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
+/* Return values definition for the "field_found" callback in
|
|
|
|
+ * mg_form_data_handler. */
|
|
|
|
+enum {
|
|
|
|
+ /* Skip this field (neither get nor store it). Continue with the
|
|
|
|
+ * next field. */
|
|
|
|
+ FORM_FIELD_STORAGE_SKIP = 0x0,
|
|
|
|
+ /* Get the field value. */
|
|
|
|
+ FORM_FIELD_STORAGE_GET = 0x1,
|
|
|
|
+ /* Store the field value into a file. */
|
|
|
|
+ FORM_FIELD_STORAGE_STORE = 0x2,
|
|
|
|
+ /* Read the filed in chunks using a read function. */
|
|
|
|
+ /* FORM_FIELD_STORAGE_READ = 0x3, not in the first step */
|
|
|
|
+ /* Stop parsing this request. Skip the remaining fields. */
|
|
|
|
+ FORM_FIELD_STORAGE_ABORT = 0x10
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
int mg_handle_form_data(struct mg_connection *conn,
|
|
int mg_handle_form_data(struct mg_connection *conn,
|
|
struct mg_form_data_handler *fdh);
|
|
struct mg_form_data_handler *fdh);
|
|
|
|
|
|
@@ -77,7 +124,7 @@ url_encoded_field_found(const struct mg_connection *conn,
|
|
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
|
|
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
|
|
|
|
|
|
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
|
|
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
|
|
- return FORM_DISPOSITION_SKIP;
|
|
|
|
|
|
+ return FORM_FIELD_STORAGE_SKIP;
|
|
}
|
|
}
|
|
|
|
|
|
if (filename) {
|
|
if (filename) {
|
|
@@ -91,7 +138,7 @@ url_encoded_field_found(const struct mg_connection *conn,
|
|
|| (filename_dec_len < 0)) {
|
|
|| (filename_dec_len < 0)) {
|
|
/* Log error message and skip this field. */
|
|
/* Log error message and skip this field. */
|
|
mg_cry(conn, "%s: Cannot decode filename", __func__);
|
|
mg_cry(conn, "%s: Cannot decode filename", __func__);
|
|
- return FORM_DISPOSITION_SKIP;
|
|
|
|
|
|
+ return FORM_FIELD_STORAGE_SKIP;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
filename_dec[0] = 0;
|
|
filename_dec[0] = 0;
|
|
@@ -106,14 +153,11 @@ static int
|
|
url_encoded_field_get(const struct mg_connection *conn,
|
|
url_encoded_field_get(const struct mg_connection *conn,
|
|
const char *key,
|
|
const char *key,
|
|
size_t key_len,
|
|
size_t key_len,
|
|
- const char *filename,
|
|
|
|
- size_t filename_len,
|
|
|
|
const char *value,
|
|
const char *value,
|
|
size_t value_len,
|
|
size_t value_len,
|
|
struct mg_form_data_handler *fdh)
|
|
struct mg_form_data_handler *fdh)
|
|
{
|
|
{
|
|
char key_dec[1024];
|
|
char key_dec[1024];
|
|
- char filename_dec[1024];
|
|
|
|
|
|
|
|
char *value_dec = mg_malloc(value_len + 1);
|
|
char *value_dec = mg_malloc(value_len + 1);
|
|
int value_dec_len;
|
|
int value_dec_len;
|
|
@@ -124,26 +168,15 @@ url_encoded_field_get(const struct mg_connection *conn,
|
|
"%s: Not enough memory (required: %lu)",
|
|
"%s: Not enough memory (required: %lu)",
|
|
__func__,
|
|
__func__,
|
|
(unsigned long)(value_len + 1));
|
|
(unsigned long)(value_len + 1));
|
|
- return FORM_DISPOSITION_ABORT;
|
|
|
|
|
|
+ return FORM_FIELD_STORAGE_ABORT;
|
|
}
|
|
}
|
|
|
|
|
|
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
|
|
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
|
|
|
|
|
|
- if (filename) {
|
|
|
|
- mg_url_decode(filename,
|
|
|
|
- (int)filename_len,
|
|
|
|
- filename_dec,
|
|
|
|
- (int)sizeof(filename_dec),
|
|
|
|
- 1);
|
|
|
|
- } else {
|
|
|
|
- filename_dec[0] = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
value_dec_len =
|
|
value_dec_len =
|
|
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
|
|
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
|
|
|
|
|
|
return fdh->field_get(key_dec,
|
|
return fdh->field_get(key_dec,
|
|
- filename_dec,
|
|
|
|
value_dec,
|
|
value_dec,
|
|
(size_t)value_dec_len,
|
|
(size_t)value_dec_len,
|
|
fdh->user_data);
|
|
fdh->user_data);
|
|
@@ -202,7 +235,7 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
const char *content_type;
|
|
const char *content_type;
|
|
char path[512];
|
|
char path[512];
|
|
char buf[1024];
|
|
char buf[1024];
|
|
- int disposition;
|
|
|
|
|
|
+ int field_storage;
|
|
int buf_fill = 0;
|
|
int buf_fill = 0;
|
|
int r;
|
|
int r;
|
|
FILE *fstore = NULL;
|
|
FILE *fstore = NULL;
|
|
@@ -252,25 +285,25 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
keylen = val - data;
|
|
keylen = val - data;
|
|
|
|
|
|
/* In every "field_found" callback we ask what to do with the
|
|
/* In every "field_found" callback we ask what to do with the
|
|
- * data ("disposition"). This could be:
|
|
|
|
- * FORM_DISPOSITION_SKIP (0) ... ignore the value of this field
|
|
|
|
- * FORM_DISPOSITION_GET (1) ... read the data and call the get
|
|
|
|
|
|
+ * data ("field_storage"). This could be:
|
|
|
|
+ * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
|
|
|
|
+ * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
|
|
* callback function
|
|
* callback function
|
|
- * FORM_DISPOSITION_STORE (2) ... store the data in a file
|
|
|
|
- * FORM_DISPOSITION_READ (3) ... let the user read the data
|
|
|
|
|
|
+ * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
|
|
|
|
+ * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
|
|
* (for parsing long data on the fly)
|
|
* (for parsing long data on the fly)
|
|
* (currently not implemented)
|
|
* (currently not implemented)
|
|
- * FORM_DISPOSITION_ABORT (flag) ... stop parsing
|
|
|
|
|
|
+ * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
|
|
*/
|
|
*/
|
|
memset(path, 0, sizeof(path));
|
|
memset(path, 0, sizeof(path));
|
|
- disposition = url_encoded_field_found(conn,
|
|
|
|
- data,
|
|
|
|
- (size_t)keylen,
|
|
|
|
- NULL,
|
|
|
|
- 0,
|
|
|
|
- path,
|
|
|
|
- sizeof(path) - 1,
|
|
|
|
- fdh);
|
|
|
|
|
|
+ field_storage = url_encoded_field_found(conn,
|
|
|
|
+ data,
|
|
|
|
+ (size_t)keylen,
|
|
|
|
+ NULL,
|
|
|
|
+ 0,
|
|
|
|
+ path,
|
|
|
|
+ sizeof(path) - 1,
|
|
|
|
+ fdh);
|
|
|
|
|
|
val++;
|
|
val++;
|
|
next = strchr(val, '&');
|
|
next = strchr(val, '&');
|
|
@@ -282,18 +315,12 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
next = val + vallen;
|
|
next = val + vallen;
|
|
}
|
|
}
|
|
|
|
|
|
- if (disposition == FORM_DISPOSITION_GET) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
|
|
/* Call callback */
|
|
/* Call callback */
|
|
- url_encoded_field_get(conn,
|
|
|
|
- data,
|
|
|
|
- (size_t)keylen,
|
|
|
|
- NULL,
|
|
|
|
- 0,
|
|
|
|
- val,
|
|
|
|
- (size_t)vallen,
|
|
|
|
- fdh);
|
|
|
|
|
|
+ url_encoded_field_get(
|
|
|
|
+ conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
|
|
}
|
|
}
|
|
- if (disposition == FORM_DISPOSITION_STORE) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
|
/* Store the content to a file */
|
|
/* Store the content to a file */
|
|
fstore = fopen(path, "wb");
|
|
fstore = fopen(path, "wb");
|
|
if (fstore != NULL) {
|
|
if (fstore != NULL) {
|
|
@@ -328,21 +355,21 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /* if (disposition == FORM_DISPOSITION_READ) { */
|
|
|
|
- /* The idea of "disposition=read" is to let the API user read
|
|
|
|
|
|
+ /* if (field_storage == FORM_FIELD_STORAGE_READ) { */
|
|
|
|
+ /* The idea of "field_storage=read" is to let the API user read
|
|
* data chunk by chunk and to some data processing on the fly.
|
|
* data chunk by chunk and to some data processing on the fly.
|
|
* This should avoid the need to store data in the server:
|
|
* This should avoid the need to store data in the server:
|
|
* It should neither be stored in memory, like
|
|
* It should neither be stored in memory, like
|
|
- * "disposition=get" does, nor in a file like
|
|
|
|
- * "disposition=store".
|
|
|
|
|
|
+ * "field_storage=get" does, nor in a file like
|
|
|
|
+ * "field_storage=store".
|
|
* However, for a "GET" request this does not make any much
|
|
* However, for a "GET" request this does not make any much
|
|
* sense, since the data is already stored in memory, as it is
|
|
* sense, since the data is already stored in memory, as it is
|
|
* part of the query string.
|
|
* part of the query string.
|
|
*/
|
|
*/
|
|
/* } */
|
|
/* } */
|
|
|
|
|
|
- if ((disposition & FORM_DISPOSITION_ABORT)
|
|
|
|
- == FORM_DISPOSITION_ABORT) {
|
|
|
|
|
|
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
|
|
|
|
+ == FORM_FIELD_STORAGE_ABORT) {
|
|
/* Stop parsing the request */
|
|
/* Stop parsing the request */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -406,22 +433,22 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
|
|
|
|
/* Call callback */
|
|
/* Call callback */
|
|
memset(path, 0, sizeof(path));
|
|
memset(path, 0, sizeof(path));
|
|
- disposition = url_encoded_field_found(conn,
|
|
|
|
- buf,
|
|
|
|
- (size_t)keylen,
|
|
|
|
- NULL,
|
|
|
|
- 0,
|
|
|
|
- path,
|
|
|
|
- sizeof(path) - 1,
|
|
|
|
- fdh);
|
|
|
|
-
|
|
|
|
- if ((disposition & FORM_DISPOSITION_ABORT)
|
|
|
|
- == FORM_DISPOSITION_ABORT) {
|
|
|
|
|
|
+ field_storage = url_encoded_field_found(conn,
|
|
|
|
+ buf,
|
|
|
|
+ (size_t)keylen,
|
|
|
|
+ NULL,
|
|
|
|
+ 0,
|
|
|
|
+ path,
|
|
|
|
+ sizeof(path) - 1,
|
|
|
|
+ fdh);
|
|
|
|
+
|
|
|
|
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
|
|
|
|
+ == FORM_FIELD_STORAGE_ABORT) {
|
|
/* Stop parsing the request */
|
|
/* Stop parsing the request */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- if (disposition == FORM_DISPOSITION_STORE) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
|
fstore = fopen(path, "wb");
|
|
fstore = fopen(path, "wb");
|
|
if (!fstore) {
|
|
if (!fstore) {
|
|
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
|
|
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
|
|
@@ -452,7 +479,7 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
remove_bad_file(conn, path);
|
|
remove_bad_file(conn, path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (disposition == FORM_DISPOSITION_GET) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
|
|
if (!end_of_key_value_pair_found && !all_data_read) {
|
|
if (!end_of_key_value_pair_found && !all_data_read) {
|
|
/* TODO: check for an easy way to get longer data */
|
|
/* TODO: check for an easy way to get longer data */
|
|
mg_cry(conn,
|
|
mg_cry(conn,
|
|
@@ -461,14 +488,8 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
/* Call callback */
|
|
/* Call callback */
|
|
- url_encoded_field_get(conn,
|
|
|
|
- buf,
|
|
|
|
- (size_t)keylen,
|
|
|
|
- NULL,
|
|
|
|
- 0,
|
|
|
|
- val,
|
|
|
|
- (size_t)vallen,
|
|
|
|
- fdh);
|
|
|
|
|
|
+ url_encoded_field_get(
|
|
|
|
+ conn, buf, (size_t)keylen, val, (size_t)vallen, fdh);
|
|
}
|
|
}
|
|
|
|
|
|
if (!end_of_key_value_pair_found) {
|
|
if (!end_of_key_value_pair_found) {
|
|
@@ -629,20 +650,23 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
}
|
|
}
|
|
|
|
|
|
memset(path, 0, sizeof(path));
|
|
memset(path, 0, sizeof(path));
|
|
- disposition = url_encoded_field_found(conn,
|
|
|
|
- nbeg,
|
|
|
|
- (size_t)(nend - nbeg),
|
|
|
|
- fbeg,
|
|
|
|
- (size_t)(fend - fbeg),
|
|
|
|
- path,
|
|
|
|
- sizeof(path) - 1,
|
|
|
|
- fdh);
|
|
|
|
|
|
+ field_storage = url_encoded_field_found(conn,
|
|
|
|
+ nbeg,
|
|
|
|
+ (size_t)(nend - nbeg),
|
|
|
|
+ fbeg,
|
|
|
|
+ (size_t)(fend - fbeg),
|
|
|
|
+ path,
|
|
|
|
+ sizeof(path) - 1,
|
|
|
|
+ fdh);
|
|
|
|
|
|
/* If the boundary is already in the buffer, get the address,
|
|
/* If the boundary is already in the buffer, get the address,
|
|
* otherwise next will be NULL. */
|
|
* otherwise next will be NULL. */
|
|
- next = search_boundary(hbuf, buf - hbuf + buf_fill, boundary, bl);
|
|
|
|
|
|
+ next = search_boundary(hbuf,
|
|
|
|
+ (size_t)((buf - hbuf) + buf_fill),
|
|
|
|
+ boundary,
|
|
|
|
+ bl);
|
|
|
|
|
|
- if (disposition == FORM_DISPOSITION_GET) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_GET) {
|
|
if (!next) {
|
|
if (!next) {
|
|
/* TODO: check for an easy way to get longer data */
|
|
/* TODO: check for an easy way to get longer data */
|
|
mg_cry(conn, "%s: Data too long for callback", __func__);
|
|
mg_cry(conn, "%s: Data too long for callback", __func__);
|
|
@@ -653,14 +677,12 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
url_encoded_field_get(conn,
|
|
url_encoded_field_get(conn,
|
|
nbeg,
|
|
nbeg,
|
|
(size_t)(nend - nbeg),
|
|
(size_t)(nend - nbeg),
|
|
- fbeg,
|
|
|
|
- (size_t)(fend - fbeg),
|
|
|
|
hend,
|
|
hend,
|
|
(size_t)(next - hend),
|
|
(size_t)(next - hend),
|
|
fdh);
|
|
fdh);
|
|
}
|
|
}
|
|
|
|
|
|
- if (disposition == FORM_DISPOSITION_STORE) {
|
|
|
|
|
|
+ if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
|
/* Store the content to a file */
|
|
/* Store the content to a file */
|
|
size_t towrite, n;
|
|
size_t towrite, n;
|
|
size_t flen = 0;
|
|
size_t flen = 0;
|
|
@@ -715,7 +737,7 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
}
|
|
}
|
|
|
|
|
|
/* Find boundary */
|
|
/* Find boundary */
|
|
- next = search_boundary(buf, buf_fill, boundary, bl);
|
|
|
|
|
|
+ next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
|
|
}
|
|
}
|
|
|
|
|
|
if (fstore) {
|
|
if (fstore) {
|
|
@@ -749,8 +771,8 @@ mg_handle_form_data(struct mg_connection *conn,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if ((disposition & FORM_DISPOSITION_ABORT)
|
|
|
|
- == FORM_DISPOSITION_ABORT) {
|
|
|
|
|
|
+ if ((field_storage & FORM_FIELD_STORAGE_ABORT)
|
|
|
|
+ == FORM_FIELD_STORAGE_ABORT) {
|
|
/* Stop parsing the request */
|
|
/* Stop parsing the request */
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|