mirror of
https://github.com/libretro/RetroArch
synced 2025-01-29 09:32:52 +00:00
Added an embedded HTTP server to RetroArch; Mapped the /mmaps URI to a JSON-based RESTful API to retrieve memory maps
This commit is contained in:
parent
8eb97d37b2
commit
292335b84c
@ -1012,6 +1012,13 @@ XML
|
||||
#include "../database_info.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
HTTP SERVER
|
||||
============================================================ */
|
||||
#if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB)
|
||||
#include "httpserver/civetweb.c"
|
||||
#include "httpserver/httpserver.c"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
13062
httpserver/civetweb.c
Normal file
13062
httpserver/civetweb.c
Normal file
File diff suppressed because it is too large
Load Diff
1014
httpserver/civetweb.h
Normal file
1014
httpserver/civetweb.h
Normal file
File diff suppressed because it is too large
Load Diff
740
httpserver/handle_form.inl
Normal file
740
httpserver/handle_form.inl
Normal file
@ -0,0 +1,740 @@
|
||||
/* Copyright (c) 2016 the Civetweb developers
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
static int
|
||||
url_encoded_field_found(const struct mg_connection *conn,
|
||||
const char *key,
|
||||
size_t key_len,
|
||||
const char *filename,
|
||||
size_t filename_len,
|
||||
char *path,
|
||||
size_t path_len,
|
||||
struct mg_form_data_handler *fdh)
|
||||
{
|
||||
char key_dec[1024];
|
||||
char filename_dec[1024];
|
||||
int key_dec_len;
|
||||
int filename_dec_len;
|
||||
int ret;
|
||||
|
||||
key_dec_len =
|
||||
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)) {
|
||||
return FORM_FIELD_STORAGE_SKIP;
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
filename_dec_len = mg_url_decode(filename,
|
||||
(int)filename_len,
|
||||
filename_dec,
|
||||
(int)sizeof(filename_dec),
|
||||
1);
|
||||
|
||||
if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
|
||||
|| (filename_dec_len < 0)) {
|
||||
/* Log error message and skip this field. */
|
||||
mg_cry(conn, "%s: Cannot decode filename", __func__);
|
||||
return FORM_FIELD_STORAGE_SKIP;
|
||||
}
|
||||
}
|
||||
else {
|
||||
filename_dec[0] = 0;
|
||||
}
|
||||
|
||||
ret =
|
||||
fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
|
||||
|
||||
if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
|
||||
if (fdh->field_get == NULL) {
|
||||
mg_cry(conn, "%s: Function \"Get\" not available", __func__);
|
||||
return FORM_FIELD_STORAGE_SKIP;
|
||||
}
|
||||
}
|
||||
if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
|
||||
if (fdh->field_store == NULL) {
|
||||
mg_cry(conn, "%s: Function \"Store\" not available", __func__);
|
||||
return FORM_FIELD_STORAGE_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
url_encoded_field_get(const struct mg_connection *conn,
|
||||
const char *key,
|
||||
size_t key_len,
|
||||
const char *value,
|
||||
size_t value_len,
|
||||
struct mg_form_data_handler *fdh)
|
||||
{
|
||||
char key_dec[1024];
|
||||
|
||||
char *value_dec = (char*)mg_malloc(value_len + 1);
|
||||
int value_dec_len;
|
||||
|
||||
if (!value_dec) {
|
||||
/* Log error message and stop parsing the form data. */
|
||||
mg_cry(conn,
|
||||
"%s: Not enough memory (required: %lu)",
|
||||
__func__,
|
||||
(unsigned long)(value_len + 1));
|
||||
return FORM_FIELD_STORAGE_ABORT;
|
||||
}
|
||||
|
||||
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
|
||||
|
||||
value_dec_len =
|
||||
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
|
||||
|
||||
return fdh->field_get(key_dec,
|
||||
value_dec,
|
||||
(size_t)value_dec_len,
|
||||
fdh->user_data);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
field_stored(const struct mg_connection *conn,
|
||||
const char *path,
|
||||
size_t file_size,
|
||||
struct mg_form_data_handler *fdh)
|
||||
{
|
||||
/* Equivalent to "upload" callback of "mg_upload". */
|
||||
|
||||
(void)conn; /* we do not need mg_cry here, so conn is currently unused */
|
||||
|
||||
return fdh->field_store(path, file_size, fdh->user_data);
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
search_boundary(const char *buf,
|
||||
size_t buf_len,
|
||||
const char *boundary,
|
||||
size_t boundary_len)
|
||||
{
|
||||
/* We must do a binary search here, not a string search, since the buffer
|
||||
* may contain '\x00' bytes, if binary data is transfered. */
|
||||
int clen = (int)buf_len - (int)boundary_len - 4;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= clen; i++) {
|
||||
if (!memcmp(buf + i, "\r\n--", 4)) {
|
||||
if (!memcmp(buf + i + 4, boundary, boundary_len)) {
|
||||
return buf + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mg_handle_form_request(struct mg_connection *conn,
|
||||
struct mg_form_data_handler *fdh)
|
||||
{
|
||||
const char *content_type;
|
||||
char path[512];
|
||||
char buf[1024];
|
||||
int field_storage;
|
||||
int buf_fill = 0;
|
||||
int r;
|
||||
int field_count = 0;
|
||||
struct file fstore = STRUCT_FILE_INITIALIZER;
|
||||
size_t file_size = 0; /* init here, to a avoid a false positive
|
||||
"uninitialized variable used" warning */
|
||||
|
||||
int has_body_data =
|
||||
(conn->request_info.content_length > 0) || (conn->is_chunked);
|
||||
|
||||
/* There are three ways to encode data from a HTML form:
|
||||
* 1) method: GET (default)
|
||||
* The form data is in the HTTP query string.
|
||||
* 2) method: POST, enctype: "application/x-www-form-urlencoded"
|
||||
* The form data is in the request body.
|
||||
* The body is url encoded (the default encoding for POST).
|
||||
* 3) method: POST, enctype: "multipart/form-data".
|
||||
* The form data is in the request body of a multipart message.
|
||||
* This is the typical way to handle file upload from a form.
|
||||
*/
|
||||
|
||||
if (!has_body_data) {
|
||||
const char *data;
|
||||
|
||||
if (strcmp(conn->request_info.request_method, "GET")) {
|
||||
/* No body data, but not a GET request.
|
||||
* This is not a valid form request. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* GET request: form data is in the query string. */
|
||||
/* The entire data has already been loaded, so there is no nead to
|
||||
* call mg_read. We just need to split the query string into key-value
|
||||
* pairs. */
|
||||
data = conn->request_info.query_string;
|
||||
if (!data) {
|
||||
/* No query string. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Split data in a=1&b=xy&c=3&c=4 ... */
|
||||
while (*data) {
|
||||
const char *val = strchr(data, '=');
|
||||
const char *next;
|
||||
ptrdiff_t keylen, vallen;
|
||||
|
||||
if (!val) {
|
||||
break;
|
||||
}
|
||||
keylen = val - data;
|
||||
|
||||
/* In every "field_found" callback we ask what to do with the
|
||||
* 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
|
||||
* 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)
|
||||
* (currently not implemented)
|
||||
* FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
|
||||
*/
|
||||
memset(path, 0, sizeof(path));
|
||||
field_count++;
|
||||
field_storage = url_encoded_field_found(conn,
|
||||
data,
|
||||
(size_t)keylen,
|
||||
NULL,
|
||||
0,
|
||||
path,
|
||||
sizeof(path) - 1,
|
||||
fdh);
|
||||
|
||||
val++;
|
||||
next = strchr(val, '&');
|
||||
if (next) {
|
||||
vallen = next - val;
|
||||
next++;
|
||||
}
|
||||
else {
|
||||
vallen = (ptrdiff_t)strlen(val);
|
||||
next = val + vallen;
|
||||
}
|
||||
|
||||
if (field_storage == FORM_FIELD_STORAGE_GET) {
|
||||
/* Call callback */
|
||||
url_encoded_field_get(
|
||||
conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
|
||||
}
|
||||
if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
||||
/* Store the content to a file */
|
||||
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
file_size = 0;
|
||||
if (fstore.fp != NULL) {
|
||||
size_t n =
|
||||
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
|
||||
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
|
||||
mg_cry(conn,
|
||||
"%s: Cannot write file %s",
|
||||
__func__,
|
||||
path);
|
||||
fclose(fstore.fp);
|
||||
fstore.fp = NULL;
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
file_size += (size_t)n;
|
||||
|
||||
if (fstore.fp) {
|
||||
r = fclose(fstore.fp);
|
||||
if (r == 0) {
|
||||
/* stored successfully */
|
||||
field_stored(conn, path, file_size, fdh);
|
||||
}
|
||||
else {
|
||||
mg_cry(conn,
|
||||
"%s: Error saving file %s",
|
||||
__func__,
|
||||
path);
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* This should avoid the need to store data in the server:
|
||||
* It should neither be stored in memory, like
|
||||
* "field_storage=get" does, nor in a file like
|
||||
* "field_storage=store".
|
||||
* However, for a "GET" request this does not make any much
|
||||
* sense, since the data is already stored in memory, as it is
|
||||
* part of the query string.
|
||||
*/
|
||||
/* } */
|
||||
|
||||
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
|
||||
== FORM_FIELD_STORAGE_ABORT) {
|
||||
/* Stop parsing the request */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Proceed to next entry */
|
||||
data = next;
|
||||
}
|
||||
|
||||
return field_count;
|
||||
}
|
||||
|
||||
content_type = mg_get_header(conn, "Content-Type");
|
||||
|
||||
if (!content_type
|
||||
|| !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")) {
|
||||
/* The form data is in the request body data, encoded in key/value
|
||||
* pairs. */
|
||||
int all_data_read = 0;
|
||||
|
||||
/* Read body data and split it in a=1&b&c=3&c=4 ... */
|
||||
/* The encoding is like in the "GET" case above, but here we read data
|
||||
* on the fly */
|
||||
for (;;) {
|
||||
/* TODO(high): Handle (text) fields with data size > sizeof(buf). */
|
||||
const char *val;
|
||||
const char *next;
|
||||
ptrdiff_t keylen, vallen;
|
||||
ptrdiff_t used;
|
||||
int end_of_key_value_pair_found = 0;
|
||||
|
||||
if ((size_t)buf_fill < (sizeof(buf) - 1)) {
|
||||
|
||||
size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
|
||||
r = mg_read(conn, buf + (size_t)buf_fill, to_read);
|
||||
if (r < 0) {
|
||||
/* read error */
|
||||
return -1;
|
||||
}
|
||||
if (r != (int)to_read) {
|
||||
/* TODO: Create a function to get "all_data_read" from
|
||||
* the conn object. Add data is read if the Content-Length
|
||||
* has been reached, or if chunked encoding is used and
|
||||
* the end marker has been read, or if the connection has
|
||||
* been closed. */
|
||||
all_data_read = 1;
|
||||
}
|
||||
buf_fill += r;
|
||||
buf[buf_fill] = 0;
|
||||
if (buf_fill < 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
val = strchr(buf, '=');
|
||||
|
||||
if (!val) {
|
||||
break;
|
||||
}
|
||||
keylen = val - buf;
|
||||
val++;
|
||||
|
||||
/* Call callback */
|
||||
memset(path, 0, sizeof(path));
|
||||
field_count++;
|
||||
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 */
|
||||
break;
|
||||
}
|
||||
|
||||
if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
||||
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
file_size = 0;
|
||||
if (!fstore.fp) {
|
||||
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop to read values larger than sizeof(buf)-keylen-2 */
|
||||
do {
|
||||
next = strchr(val, '&');
|
||||
if (next) {
|
||||
vallen = next - val;
|
||||
next++;
|
||||
end_of_key_value_pair_found = 1;
|
||||
}
|
||||
else {
|
||||
vallen = (ptrdiff_t)strlen(val);
|
||||
next = val + vallen;
|
||||
}
|
||||
|
||||
if (fstore.fp) {
|
||||
size_t n =
|
||||
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
|
||||
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
|
||||
mg_cry(conn,
|
||||
"%s: Cannot write file %s",
|
||||
__func__,
|
||||
path);
|
||||
fclose(fstore.fp);
|
||||
fstore.fp = NULL;
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
file_size += (size_t)n;
|
||||
}
|
||||
if (field_storage == FORM_FIELD_STORAGE_GET) {
|
||||
if (!end_of_key_value_pair_found && !all_data_read) {
|
||||
/* TODO: check for an easy way to get longer data */
|
||||
mg_cry(conn,
|
||||
"%s: Data too long for callback",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
/* Call callback */
|
||||
url_encoded_field_get(
|
||||
conn, buf, (size_t)keylen, val, (size_t)vallen, fdh);
|
||||
}
|
||||
|
||||
if (!end_of_key_value_pair_found) {
|
||||
/* TODO: read more data */
|
||||
break;
|
||||
}
|
||||
|
||||
} while (!end_of_key_value_pair_found);
|
||||
|
||||
if (fstore.fp) {
|
||||
r = fclose(fstore.fp);
|
||||
if (r == 0) {
|
||||
/* stored successfully */
|
||||
field_stored(conn, path, file_size, fdh);
|
||||
}
|
||||
else {
|
||||
mg_cry(conn, "%s: Error saving file %s", __func__, path);
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
|
||||
/* Proceed to next entry */
|
||||
used = next - buf;
|
||||
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
|
||||
buf_fill -= (int)used;
|
||||
}
|
||||
|
||||
return field_count;
|
||||
}
|
||||
|
||||
if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
|
||||
/* The form data is in the request body data, encoded as multipart
|
||||
* content (see https://www.ietf.org/rfc/rfc1867.txt,
|
||||
* https://www.ietf.org/rfc/rfc2388.txt). */
|
||||
const char *boundary;
|
||||
size_t bl;
|
||||
ptrdiff_t used;
|
||||
struct mg_request_info part_header;
|
||||
char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
|
||||
const char *content_disp;
|
||||
const char *next;
|
||||
|
||||
memset(&part_header, 0, sizeof(part_header));
|
||||
|
||||
/* There has to be a BOUNDARY definition in the Content-Type header */
|
||||
if (mg_strncasecmp(content_type + 21, "BOUNDARY=", 9)) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
boundary = content_type + 30;
|
||||
bl = strlen(boundary);
|
||||
|
||||
if (bl + 800 > sizeof(buf)) {
|
||||
/* Sanity check: The algorithm can not work if bl >= sizeof(buf),
|
||||
* and it will not work effectively, if the buf is only a few byte
|
||||
* larger than bl, or it buf can not hold the multipart header
|
||||
* plus the boundary.
|
||||
* Check some reasonable number here, that should be fulfilled by
|
||||
* any reasonable request from every browser. If it is not
|
||||
* fulfilled, it might be a hand-made request, intended to
|
||||
* interfere with the algorithm. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
||||
r = mg_read(conn,
|
||||
buf + (size_t)buf_fill,
|
||||
sizeof(buf) - 1 - (size_t)buf_fill);
|
||||
if (r < 0) {
|
||||
/* read error */
|
||||
return -1;
|
||||
}
|
||||
buf_fill += r;
|
||||
buf[buf_fill] = 0;
|
||||
if (buf_fill < 1) {
|
||||
/* No data */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf[0] != '-' || buf[1] != '-') {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
if (strncmp(buf + 2, boundary, bl)) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
|
||||
/* Every part must end with \r\n, if there is another part.
|
||||
* The end of the request has an extra -- */
|
||||
if (((size_t)buf_fill != (size_t)(bl + 6))
|
||||
|| (strncmp(buf + bl + 2, "--\r\n", 4))) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
/* End of the request */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Next, we need to get the part header: Read until \r\n\r\n */
|
||||
hbuf = buf + bl + 4;
|
||||
hend = strstr(hbuf, "\r\n\r\n");
|
||||
if (!hend) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_http_headers(&hbuf, &part_header);
|
||||
if ((hend + 2) != hbuf) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Skip \r\n\r\n */
|
||||
hend += 4;
|
||||
|
||||
/* According to the RFC, every part has to have a header field like:
|
||||
* Content-Disposition: form-data; name="..." */
|
||||
content_disp = get_header(&part_header, "Content-Disposition");
|
||||
if (!content_disp) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the mandatory name="..." part of the Content-Disposition
|
||||
* header. */
|
||||
nbeg = (char*)strstr(content_disp, "name=\"");
|
||||
if (!nbeg) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
nbeg += 6;
|
||||
nend = strchr(nbeg, '\"');
|
||||
if (!nend) {
|
||||
/* Malformed request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the optional filename="..." part of the Content-Disposition
|
||||
* header. */
|
||||
fbeg = (char*)strstr(content_disp, "filename=\"");
|
||||
if (fbeg) {
|
||||
fbeg += 10;
|
||||
fend = strchr(fbeg, '\"');
|
||||
if (!fend) {
|
||||
/* Malformed request (the filename field is optional, but if
|
||||
* it exists, it needs to be terminated correctly). */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: check Content-Type */
|
||||
/* Content-Type: application/octet-stream */
|
||||
|
||||
}
|
||||
else {
|
||||
fend = fbeg;
|
||||
}
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
field_count++;
|
||||
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,
|
||||
* otherwise next will be NULL. */
|
||||
next = search_boundary(hbuf,
|
||||
(size_t)((buf - hbuf) + buf_fill),
|
||||
boundary,
|
||||
bl);
|
||||
|
||||
if (field_storage == FORM_FIELD_STORAGE_GET) {
|
||||
if (!next) {
|
||||
/* TODO: check for an easy way to get longer data */
|
||||
mg_cry(conn, "%s: Data too long for callback", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Call callback */
|
||||
url_encoded_field_get(conn,
|
||||
nbeg,
|
||||
(size_t)(nend - nbeg),
|
||||
hend,
|
||||
(size_t)(next - hend),
|
||||
fdh);
|
||||
}
|
||||
|
||||
if (field_storage == FORM_FIELD_STORAGE_STORE) {
|
||||
/* Store the content to a file */
|
||||
size_t towrite, n;
|
||||
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
file_size = 0;
|
||||
|
||||
if (!fstore.fp) {
|
||||
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
|
||||
}
|
||||
|
||||
while (!next) {
|
||||
/* Set "towrite" to the number of bytes available
|
||||
* in the buffer */
|
||||
towrite = (size_t)(buf - hend + buf_fill);
|
||||
/* Subtract the boundary length, to deal with
|
||||
* cases the boundary is only partially stored
|
||||
* in the buffer. */
|
||||
towrite -= bl + 4;
|
||||
|
||||
if (fstore.fp) {
|
||||
|
||||
/* Store the content of the buffer. */
|
||||
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
|
||||
if ((n != towrite) || (ferror(fstore.fp))) {
|
||||
mg_cry(conn,
|
||||
"%s: Cannot write file %s",
|
||||
__func__,
|
||||
path);
|
||||
fclose(fstore.fp);
|
||||
fstore.fp = NULL;
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
file_size += (size_t)n;
|
||||
}
|
||||
|
||||
memmove(buf, hend + towrite, bl + 4);
|
||||
buf_fill = (int)(bl + 4);
|
||||
hend = buf;
|
||||
|
||||
/* Read new data */
|
||||
r = mg_read(conn,
|
||||
buf + (size_t)buf_fill,
|
||||
sizeof(buf) - 1 - (size_t)buf_fill);
|
||||
if (r < 0) {
|
||||
/* read error */
|
||||
return -1;
|
||||
}
|
||||
buf_fill += r;
|
||||
buf[buf_fill] = 0;
|
||||
if (buf_fill < 1) {
|
||||
/* No data */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find boundary */
|
||||
next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
|
||||
}
|
||||
|
||||
if (fstore.fp) {
|
||||
towrite = (size_t)(next - hend);
|
||||
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
|
||||
if ((n != towrite) || (ferror(fstore.fp))) {
|
||||
mg_cry(conn,
|
||||
"%s: Cannot write file %s",
|
||||
__func__,
|
||||
path);
|
||||
fclose(fstore.fp);
|
||||
fstore.fp = NULL;
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
file_size += (size_t)n;
|
||||
}
|
||||
|
||||
if (fstore.fp) {
|
||||
r = fclose(fstore.fp);
|
||||
if (r == 0) {
|
||||
/* stored successfully */
|
||||
field_stored(conn, path, file_size, fdh);
|
||||
}
|
||||
else {
|
||||
mg_cry(conn,
|
||||
"%s: Error saving file %s",
|
||||
__func__,
|
||||
path);
|
||||
remove_bad_file(conn, path);
|
||||
}
|
||||
fstore.fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
|
||||
== FORM_FIELD_STORAGE_ABORT) {
|
||||
/* Stop parsing the request */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remove from the buffer */
|
||||
used = next - buf + 2;
|
||||
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
|
||||
buf_fill -= (int)used;
|
||||
}
|
||||
|
||||
/* All parts handled */
|
||||
return field_count;
|
||||
}
|
||||
|
||||
/* Unknown Content-Type */
|
||||
return -1;
|
||||
}
|
272
httpserver/httpserver.c
Normal file
272
httpserver/httpserver.c
Normal file
@ -0,0 +1,272 @@
|
||||
#include <libretro.h>
|
||||
#include "system.h"
|
||||
#include "runloop.h"
|
||||
#include "compat/zlib.h"
|
||||
#include "civetweb.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static struct mg_callbacks s_httpserver_callbacks;
|
||||
static struct mg_context* s_httpserver_ctx;
|
||||
|
||||
/* Based on https://github.com/zeromq/rfc/blob/master/src/spec_32.c */
|
||||
static void httpserver_z85_encode_inplace(Bytef* data, size_t size)
|
||||
{
|
||||
static char digits[85 + 1] =
|
||||
{
|
||||
"0123456789"
|
||||
"abcdefghij"
|
||||
"klmnopqrst"
|
||||
"uvwxyzABCD"
|
||||
"EFGHIJKLMN"
|
||||
"OPQRSTUVWX"
|
||||
"YZ.-:+=^!/"
|
||||
"*?&<>()[]{"
|
||||
"}@%$#"
|
||||
};
|
||||
|
||||
Bytef* source = data + size - 4;
|
||||
Bytef* dest = data + size * 5 / 4 - 5;
|
||||
uLong value;
|
||||
|
||||
dest[5] = 0;
|
||||
|
||||
if (source >= data)
|
||||
{
|
||||
do
|
||||
{
|
||||
value = source[0] * 256 * 256 * 256;
|
||||
value += source[1] * 256 * 256;
|
||||
value += source[2] * 256;
|
||||
value += source[3];
|
||||
source -= 4;
|
||||
|
||||
dest[4] = digits[value % 85];
|
||||
value /= 85;
|
||||
dest[3] = digits[value % 85];
|
||||
value /= 85;
|
||||
dest[2] = digits[value % 85];
|
||||
value /= 85;
|
||||
dest[1] = digits[value % 85];
|
||||
dest[0] = digits[value / 85];
|
||||
dest -= 5;
|
||||
|
||||
} while (source >= data);
|
||||
}
|
||||
}
|
||||
|
||||
static int httpserver_error(struct mg_connection* conn, unsigned code, const char* fmt, ...)
|
||||
{
|
||||
const char* reason;
|
||||
char buffer[1024];
|
||||
va_list args;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 404:
|
||||
reason = "Not Found";
|
||||
break;
|
||||
|
||||
case 405:
|
||||
reason = "Method Not Allowed";
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Send unknown codes as 500 */
|
||||
code = 500;
|
||||
reason = "Internal Server Error";
|
||||
break;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
|
||||
mg_printf(conn, "HTTP/1.1 %u %s\r\nContent-Type: text/plain\r\n\r\n", code, reason);
|
||||
mg_printf(conn, "%u %s\r\n\r\n%s", code, reason, buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int httpserver_handle_get_mmaps(struct mg_connection* conn, void* cbdata)
|
||||
{
|
||||
const struct mg_request_info* req = mg_get_request_info(conn);
|
||||
const char* comma = "";
|
||||
rarch_system_info_t* system;
|
||||
const struct retro_memory_map* mmaps;
|
||||
const struct retro_memory_descriptor* mmap;
|
||||
unsigned id;
|
||||
|
||||
if (strcmp(req->request_method, "GET"))
|
||||
{
|
||||
return httpserver_error(conn, 405, "Unimplemented method in %s: %s", __FUNCTION__, req->request_method);
|
||||
}
|
||||
|
||||
if (!runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system))
|
||||
{
|
||||
return httpserver_error(conn, 500, "Could not get system information in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
mmaps = &system->mmaps;
|
||||
mmap = mmaps->descriptors;
|
||||
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n");
|
||||
mg_printf(conn, "[");
|
||||
|
||||
for (id = 0; id < mmaps->num_descriptors; id++, mmap++, comma = ",")
|
||||
{
|
||||
mg_printf(conn, "%s", comma);
|
||||
|
||||
mg_printf(conn,
|
||||
"{"
|
||||
"\"id\":%u,"
|
||||
"\"flags\":" STRING_REP_UINT64 ","
|
||||
"\"ptr\":\"%p\","
|
||||
"\"offset\":" STRING_REP_UINT64 ","
|
||||
"\"start\":" STRING_REP_UINT64 ","
|
||||
"\"select\":" STRING_REP_UINT64 ","
|
||||
"\"disconnect\":" STRING_REP_UINT64 ","
|
||||
"\"len\":" STRING_REP_UINT64 ","
|
||||
"\"addrspace\":",
|
||||
id,
|
||||
mmap->flags,
|
||||
mmap->ptr,
|
||||
mmap->offset,
|
||||
mmap->start,
|
||||
mmap->select,
|
||||
mmap->disconnect,
|
||||
mmap->len
|
||||
);
|
||||
|
||||
if (mmap->addrspace)
|
||||
{
|
||||
mg_printf(conn, "\"%s\"", mmap->addrspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
mg_printf(conn, "null");
|
||||
}
|
||||
|
||||
mg_printf(conn, "}");
|
||||
}
|
||||
|
||||
mg_printf(conn, "]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int httpserver_handle_get_mmap(struct mg_connection* conn, void* cbdata)
|
||||
{
|
||||
static const char* hexdigits = "0123456789ABCDEF";
|
||||
|
||||
const struct mg_request_info* req = mg_get_request_info(conn);
|
||||
const char* comma = "";
|
||||
rarch_system_info_t* system;
|
||||
const struct retro_memory_map* mmaps;
|
||||
const struct retro_memory_descriptor* mmap;
|
||||
unsigned id;
|
||||
uLong buflen;
|
||||
Bytef* buffer;
|
||||
|
||||
if (strcmp(req->request_method, "GET"))
|
||||
{
|
||||
return httpserver_error(conn, 405, "Unimplemented method in %s: %s", __FUNCTION__, req->request_method);
|
||||
}
|
||||
|
||||
if (sscanf(req->request_uri, "/mmaps/%u", &id) != 1)
|
||||
{
|
||||
return httpserver_error(conn, 500, "Malformed request in %s: %s", __FUNCTION__, req->request_uri);
|
||||
}
|
||||
|
||||
if (!runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system))
|
||||
{
|
||||
return httpserver_error(conn, 500, "Could not get system information in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
mmaps = &system->mmaps;
|
||||
|
||||
if (id >= mmaps->num_descriptors)
|
||||
{
|
||||
return httpserver_error(conn, 404, "Invalid memory map id in %s: %u", __FUNCTION__, id);
|
||||
}
|
||||
|
||||
mmap = mmaps->descriptors + id;
|
||||
buflen = compressBound(mmap->len);
|
||||
buffer = (Bytef*)malloc(((buflen + 3) / 4) * 5);
|
||||
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return httpserver_error(conn, 500, "Out of memory in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
if (compress2(buffer, &buflen, (Bytef*)mmap->ptr, mmap->len, Z_BEST_COMPRESSION) != Z_OK)
|
||||
{
|
||||
free((void*)buffer);
|
||||
return httpserver_error(conn, 500, "Error during compression in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
buffer[buflen] = 0;
|
||||
buffer[buflen + 1] = 0;
|
||||
buffer[buflen + 2] = 0;
|
||||
httpserver_z85_encode_inplace(buffer, (buflen + 3) & ~3);
|
||||
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n");
|
||||
mg_printf(conn,
|
||||
"{"
|
||||
"\"length\":" STRING_REP_UINT64 ","
|
||||
"\"compressedLength\":" STRING_REP_ULONG ","
|
||||
"\"bytesZ85\":\"%s\""
|
||||
"}",
|
||||
mmap->len,
|
||||
(size_t)buflen,
|
||||
(char*)buffer
|
||||
);
|
||||
|
||||
free((void*)buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int httpserver_handle_mmaps(struct mg_connection* conn, void* cbdata)
|
||||
{
|
||||
const struct mg_request_info* req = mg_get_request_info(conn);
|
||||
unsigned id;
|
||||
|
||||
if (sscanf(req->request_uri, "/mmaps/%u", &id) == 1)
|
||||
{
|
||||
return httpserver_handle_get_mmap(conn, cbdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
return httpserver_handle_get_mmaps(conn, cbdata);
|
||||
}
|
||||
}
|
||||
|
||||
int httpserver_init(unsigned port)
|
||||
{
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%u", port);
|
||||
str[sizeof(str) - 1] = 0;
|
||||
|
||||
const char* options[] =
|
||||
{
|
||||
"listening_ports", str,
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
memset(&s_httpserver_callbacks, 0, sizeof(s_httpserver_callbacks));
|
||||
s_httpserver_ctx = mg_start(&s_httpserver_callbacks, NULL, options);
|
||||
|
||||
if (s_httpserver_ctx == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
mg_set_request_handler(s_httpserver_ctx, "/mmaps", httpserver_handle_mmaps, NULL);
|
||||
mg_set_request_handler(s_httpserver_ctx, "/mmaps/", httpserver_handle_mmaps, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void httpserver_destroy()
|
||||
{
|
||||
mg_stop(s_httpserver_ctx);
|
||||
}
|
15
httpserver/httpserver.h
Normal file
15
httpserver/httpserver.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __RARCH_HTTPSERVR_H
|
||||
#define __RARCH_HTTPSERVR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int httpserver_init(unsigned port);
|
||||
void httpserver_destroy();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RARCH_HTTPSERVR_H */
|
469
httpserver/md5.inl
Normal file
469
httpserver/md5.inl
Normal file
@ -0,0 +1,469 @@
|
||||
/*
|
||||
* This an amalgamation of md5.c and md5.h into a single file
|
||||
* with all static declaration to reduce linker conflicts
|
||||
* in Civetweb.
|
||||
*
|
||||
* The MD5_STATIC declaration was added to facilitate static
|
||||
* inclusion.
|
||||
* No Face Press, LLC
|
||||
*/
|
||||
|
||||
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef md5_INCLUDED
|
||||
#define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* This package supports both compile-time and run-time determination of CPU
|
||||
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
|
||||
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
|
||||
* defined as non-zero, the code will be compiled to run only on big-endian
|
||||
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
|
||||
* run on either big- or little-endian CPUs, but will run slightly less
|
||||
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
|
||||
*/
|
||||
|
||||
typedef unsigned char md5_byte_t; /* 8-bit byte */
|
||||
typedef unsigned int md5_word_t; /* 32-bit word */
|
||||
|
||||
/* Define the state of the MD5 Algorithm. */
|
||||
typedef struct md5_state_s {
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
} md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
MD5_STATIC void md5_init(md5_state_t *pms);
|
||||
|
||||
/* Append a string to the message. */
|
||||
MD5_STATIC void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
|
||||
|
||||
/* Finish the message and return the digest. */
|
||||
MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef MD5_STATIC
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
#define BYTE_ORDER (0)
|
||||
#endif
|
||||
|
||||
#define T_MASK ((md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 (0x242070db)
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 (0x4787c62a)
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 (0x698098d8)
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 (0x6b901122)
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 (0x49b40821)
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 (0x265e5a51)
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 (0x02441453)
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 (0x21e1cde6)
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 (0x455a14ed)
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 (0x676f02d9)
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 (0x6d9d6122)
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 (0x4bdecfa9)
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 (0x289b7ec6)
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 (0x04881d05)
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 (0x1fa27cf8)
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 (0x432aff97)
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 (0x655b59c3)
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 (0x6fa87e4f)
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 (0x4e0811a1)
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 (0x2ad7d2bb)
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
|
||||
d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
md5_word_t X[16];
|
||||
#else
|
||||
/* Define storage for little-endian or both types of CPUs. */
|
||||
md5_word_t xbuf[16];
|
||||
const md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned, a direct assignment is possible */
|
||||
/* cast through a (void *) should avoid a compiler warning,
|
||||
see
|
||||
https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
|
||||
*/
|
||||
X = (const md5_word_t *)(void *)data;
|
||||
}
|
||||
else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
#if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
#else
|
||||
#define xbuf X /* (static only) */
|
||||
#endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
|
||||
+ (md5_word_t)(xp[2] << 16)
|
||||
+ (md5_word_t)(xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* Round 1. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti) \
|
||||
t = a + F(b, c, d) + X[k] + Ti; \
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, T16);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti) \
|
||||
t = a + G(b, c, d) + X[k] + Ti; \
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, T32);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti) \
|
||||
t = a + H(b, c, d) + X[k] + Ti; \
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, T48);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti) \
|
||||
t = a + I(b, c, d) + X[k] + Ti; \
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, T64);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
pms->abcd[2] += c;
|
||||
pms->abcd[3] += d;
|
||||
}
|
||||
|
||||
MD5_STATIC void
|
||||
md5_init(md5_state_t *pms)
|
||||
{
|
||||
pms->count[0] = pms->count[1] = 0;
|
||||
pms->abcd[0] = 0x67452301;
|
||||
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
MD5_STATIC void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
|
||||
{
|
||||
const md5_byte_t *p = data;
|
||||
size_t left = nbytes;
|
||||
size_t offset = (pms->count[0] >> 3) & 63;
|
||||
md5_word_t nbits = (md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += (md5_word_t)(nbytes >> 29);
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
MD5_STATIC void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
@ -1502,6 +1502,7 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
settings->input.libretro_device[i] = RETRO_DEVICE_JOYPAD;
|
||||
}
|
||||
runloop_ctl(RUNLOOP_CTL_HTTPSERVER_INIT, NULL);
|
||||
runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_INIT, NULL);
|
||||
break;
|
||||
case RARCH_CTL_SET_PATHS_REDIRECT:
|
||||
|
14
runloop.c
14
runloop.c
@ -68,6 +68,10 @@
|
||||
#include "network/netplay/netplay.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB)
|
||||
#include "httpserver/httpserver.h"
|
||||
#endif
|
||||
|
||||
#include "verbosity.h"
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
@ -1232,6 +1236,16 @@ bool runloop_ctl(enum runloop_ctl_state state, void *data)
|
||||
*key_event = &runloop_frontend_key_event;
|
||||
}
|
||||
break;
|
||||
case RUNLOOP_CTL_HTTPSERVER_INIT:
|
||||
#if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB)
|
||||
httpserver_init(8888);
|
||||
#endif
|
||||
break;
|
||||
case RUNLOOP_CTL_HTTPSERVER_DESTROY:
|
||||
#if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB)
|
||||
httpserver_destroy();
|
||||
#endif
|
||||
break;
|
||||
case RUNLOOP_CTL_NONE:
|
||||
default:
|
||||
break;
|
||||
|
@ -128,7 +128,11 @@ enum runloop_ctl_state
|
||||
/* System info */
|
||||
RUNLOOP_CTL_SYSTEM_INFO_GET,
|
||||
RUNLOOP_CTL_SYSTEM_INFO_INIT,
|
||||
RUNLOOP_CTL_SYSTEM_INFO_FREE
|
||||
RUNLOOP_CTL_SYSTEM_INFO_FREE,
|
||||
|
||||
/* HTTP server */
|
||||
RUNLOOP_CTL_HTTPSERVER_INIT,
|
||||
RUNLOOP_CTL_HTTPSERVER_DESTROY
|
||||
};
|
||||
|
||||
typedef struct rarch_dir_list rarch_dir_list_t;
|
||||
|
Loading…
x
Reference in New Issue
Block a user