VentoyPlugson ---- A GUI ventoy.json configurator

This commit is contained in:
longpanda 2021-12-01 20:43:35 +08:00
parent 9eeb94e8b5
commit 4bf43ab9d4
227 changed files with 67510 additions and 33 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,793 @@
/* 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 = 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,
long long 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 transferred. */
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;
int64_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 += (int64_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")
|| !mg_strcasecmp(content_type, "APPLICATION/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 keys and values.
* The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
* Here we use "POST", and read the data from the request body.
* The data read on the fly, so it is not required to buffer the
* entire request in memory before processing it. */
for (;;) {
const char *val;
const char *next;
ptrdiff_t keylen, vallen;
ptrdiff_t used;
int end_of_key_value_pair_found = 0;
int get_block;
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. All 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);
}
}
get_block = 0;
/* 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 (field_storage == FORM_FIELD_STORAGE_GET) {
#if 0
if (!end_of_key_value_pair_found && !all_data_read) {
/* This callback will deliver partial contents */
}
#else
(void)all_data_read; /* avoid warning */
#endif
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : buf),
((get_block > 0) ? 0
: (size_t)keylen),
val,
(size_t)vallen,
fdh);
get_block++;
}
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 += (int64_t)n;
}
if (!end_of_key_value_pair_found) {
used = next - buf;
memmove(buf,
buf + (size_t)used,
sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
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. All 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 = buf;
}
}
} 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));
/* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
bl = 20;
while (content_type[bl] == ' ') {
bl++;
}
/* There has to be a BOUNDARY definition in the Content-Type header */
if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
/* Malformed request */
return -1;
}
boundary = content_type + bl + 9;
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 (;;) {
size_t towrite, n;
int get_block;
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 = 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 = 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_STORE) {
/* Store the content to a file */
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);
}
}
get_block = 0;
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 (field_storage == FORM_FIELD_STORAGE_GET) {
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0)
? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
get_block++;
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
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 += (int64_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);
}
towrite = (size_t)(next - hend);
if (field_storage == FORM_FIELD_STORAGE_GET) {
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0) ? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
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 += (int64_t)n;
}
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
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 */
break;
}
/* 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;
}

View file

@ -0,0 +1,468 @@
/*
* 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 *)(const 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));
}

View file

@ -0,0 +1,250 @@
/* This file is part of the CivetWeb web server.
* See https://github.com/civetweb/civetweb/
* (C) 2015 by the CivetWeb authors, MIT license.
*/
#include "duktape.h"
/* TODO: the mg context should be added to duktape as well */
/* Alternative: redefine a new, clean API from scratch (instead of using mg),
* or at least do not add problematic functions. */
/* For evaluation purposes, currently only "send" is supported.
* All other ~50 functions will be added later. */
/* Note: This is only experimental support, so the API may still change. */
static const char *civetweb_conn_id = "\xFF"
"civetweb_conn";
static const char *civetweb_ctx_id = "\xFF"
"civetweb_ctx";
static void *
mg_duk_mem_alloc(void *udata, duk_size_t size)
{
return mg_malloc(size);
}
static void *
mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
{
return mg_realloc(ptr, newsize);
}
static void
mg_duk_mem_free(void *udata, void *ptr)
{
mg_free(ptr);
}
static void
mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
{
/* Script is called "protected" (duk_peval_file), so script errors should
* never yield in a call to this function. Maybe calls prior to executing
* the script could raise a fatal error. */
struct mg_connection *conn;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
mg_cry(conn, "%s", msg);
}
static duk_ret_t
duk_itf_write(duk_context *ctx)
{
struct mg_connection *conn;
duk_double_t ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
/*
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
*/
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_write(conn, val, len);
duk_push_number(ctx, ret);
return 1;
}
static duk_ret_t
duk_itf_read(duk_context *ctx)
{
struct mg_connection *conn;
char buf[1024];
int len;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
len = mg_read(conn, buf, sizeof(buf));
duk_push_lstring(ctx, buf, len);
return 1;
}
static duk_ret_t
duk_itf_getoption(duk_context *ctx)
{
struct mg_context *cv_ctx;
const char *ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_ctx_id);
cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
if (!cv_ctx) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_get_option(cv_ctx, val);
if (ret) {
duk_push_string(ctx, ret);
} else {
duk_push_null(ctx);
}
return 1;
}
static void
mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
{
int i;
duk_context *ctx = NULL;
conn->must_close = 1;
/* Create Duktape interpreter state */
ctx = duk_create_heap(mg_duk_mem_alloc,
mg_duk_mem_realloc,
mg_duk_mem_free,
NULL,
mg_duk_fatal_handler);
if (!ctx) {
mg_cry(conn, "Failed to create a Duktape heap.");
goto exec_duktape_finished;
}
/* Add "conn" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
duk_push_string(ctx, conn->request_info.request_method);
duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
duk_push_string(ctx, conn->request_info.request_uri);
duk_put_prop_string(ctx, -2, "request_uri");
duk_push_string(ctx, conn->request_info.local_uri);
duk_put_prop_string(ctx, -2, "uri");
duk_push_string(ctx, conn->request_info.http_version);
duk_put_prop_string(ctx, -2, "http_version");
duk_push_string(ctx, conn->request_info.query_string);
duk_put_prop_string(ctx, -2, "query_string");
duk_push_string(ctx, conn->request_info.remote_addr);
duk_put_prop_string(ctx, -2, "remote_addr");
duk_push_int(ctx, conn->request_info.remote_port);
duk_put_prop_string(ctx, -2, "remote_port");
duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
duk_put_prop_string(ctx, -2, "server_port");
duk_push_object(ctx); /* subfolder "conn.http_headers" */
for (i = 0; i < conn->request_info.num_headers; i++) {
duk_push_string(ctx, conn->request_info.http_headers[i].value);
duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
}
duk_put_prop_string(ctx, -2, "http_headers");
duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
/* Add "civetweb" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_string(ctx, CIVETWEB_VERSION);
duk_put_prop_string(ctx, -2, "version");
duk_push_string(ctx, script_name);
duk_put_prop_string(ctx, -2, "script_name");
if (conn->ctx != NULL) {
duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)(conn->ctx));
duk_put_prop_string(ctx, -2, civetweb_ctx_id);
duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
if (conn->ctx->systemName != NULL) {
duk_push_string(ctx, conn->ctx->systemName);
duk_put_prop_string(ctx, -2, "system");
}
}
duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
duk_push_global_stash(ctx);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
if (duk_peval_file(ctx, script_name) != 0) {
mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
goto exec_duktape_finished;
}
duk_pop(ctx); /* ignore result */
exec_duktape_finished:
duk_destroy_heap(ctx);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,150 @@
#if !defined(MAX_TIMERS)
#define MAX_TIMERS MAX_WORKER_THREADS
#endif
typedef int (*taction)(void *arg);
struct ttimer {
double time;
double period;
taction action;
void *arg;
};
struct ttimers {
pthread_t threadid; /* Timer thread ID */
pthread_mutex_t mutex; /* Protects timer lists */
struct ttimer timers[MAX_TIMERS]; /* List of timers */
unsigned timer_count; /* Current size of timer list */
};
static int
timer_add(struct mg_context *ctx,
double next_time,
double period,
int is_relative,
taction action,
void *arg)
{
unsigned u, v;
int error = 0;
struct timespec now;
if (ctx->stop_flag) {
return 0;
}
if (is_relative) {
clock_gettime(CLOCK_MONOTONIC, &now);
next_time += now.tv_sec;
next_time += now.tv_nsec * 1.0E-9;
}
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count == MAX_TIMERS) {
error = 1;
} else {
for (u = 0; u < ctx->timers->timer_count; u++) {
if (ctx->timers->timers[u].time < next_time) {
for (v = ctx->timers->timer_count; v > u; v--) {
ctx->timers->timers[v] = ctx->timers->timers[v - 1];
}
break;
}
}
ctx->timers->timers[u].time = next_time;
ctx->timers->timers[u].period = period;
ctx->timers->timers[u].action = action;
ctx->timers->timers[u].arg = arg;
ctx->timers->timer_count++;
}
pthread_mutex_unlock(&ctx->timers->mutex);
return error;
}
static void
timer_thread_run(void *thread_func_param)
{
struct mg_context *ctx = (struct mg_context *)thread_func_param;
struct timespec now;
double d;
unsigned u;
int re_schedule;
struct ttimer t;
mg_set_thread_name("timer");
if (ctx->callbacks.init_thread) {
/* Timer thread */
ctx->callbacks.init_thread(ctx, 2);
}
#if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
/* TODO */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &request, &request)
== EINTR) { /*nop*/
;
}
#else
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
while (ctx->stop_flag == 0) {
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
t = ctx->timers->timers[0];
for (u = 1; u < ctx->timers->timer_count; u++) {
ctx->timers->timers[u - 1] = ctx->timers->timers[u];
}
ctx->timers->timer_count--;
pthread_mutex_unlock(&ctx->timers->mutex);
re_schedule = t.action(t.arg);
if (re_schedule && (t.period > 0)) {
timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
}
continue;
} else {
pthread_mutex_unlock(&ctx->timers->mutex);
}
mg_sleep(1);
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
}
#endif
}
#ifdef _WIN32
static unsigned __stdcall timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return 0;
}
#else
static void *
timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return NULL;
}
#endif /* _WIN32 */
static int
timers_init(struct mg_context *ctx)
{
ctx->timers = (struct ttimers *)mg_calloc(sizeof(struct ttimers), 1);
(void)pthread_mutex_init(&ctx->timers->mutex, NULL);
/* Start timer thread */
mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
return 0;
}
static void
timers_exit(struct mg_context *ctx)
{
if (ctx->timers) {
(void)pthread_mutex_destroy(&ctx->timers->mutex);
mg_free(ctx->timers);
}
}