move to new cspot

This commit is contained in:
philippe44
2023-03-25 16:48:41 -07:00
parent c712b78931
commit 008c36facf
2983 changed files with 465270 additions and 13569 deletions

View File

@@ -0,0 +1,787 @@
/* Copyright (c) 2013-2020 the Civetweb developers
* Copyright (c) 2013 No Face Press, LLC
*
* License http://opensource.org/licenses/mit-license.php MIT License
*/
#include "CivetServer.h"
#include <assert.h>
#include <stdexcept>
#include <string.h>
#ifndef UNUSED_PARAMETER
#define UNUSED_PARAMETER(x) (void)(x)
#endif
#ifndef MAX_PARAM_BODY_LENGTH
// Set a default limit for parameters in a form body: 2 MB
#define MAX_PARAM_BODY_LENGTH (1024 * 1024 * 2)
#endif
bool
CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handleGet(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handlePost(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handleHead(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handleHead(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handlePut(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handlePatch(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handlePatch(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handleDelete(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return false;
}
bool
CivetHandler::handleOptions(CivetServer *server,
struct mg_connection *conn,
int *status_code)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
if (status_code) {
*status_code = -1;
}
return false;
}
bool
CivetWebSocketHandler::handleConnection(CivetServer *server,
const struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return true;
}
void
CivetWebSocketHandler::handleReadyState(CivetServer *server,
struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return;
}
bool
CivetWebSocketHandler::handleData(CivetServer *server,
struct mg_connection *conn,
int bits,
char *data,
size_t data_len)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
UNUSED_PARAMETER(bits);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(data_len);
return true;
}
void
CivetWebSocketHandler::handleClose(CivetServer *server,
const struct mg_connection *conn)
{
UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn);
return;
}
int
CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
int http_status_code = -1;
bool status_ok = false;
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return 0;
mg_lock_context(me->context);
me->connections[conn] = CivetConnection();
mg_unlock_context(me->context);
CivetHandler *handler = (CivetHandler *)cbdata;
if (handler) {
if (strcmp(request_info->request_method, "GET") == 0) {
status_ok = handler->handleGet(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handleGet(me, conn);
}
} else if (strcmp(request_info->request_method, "POST") == 0) {
status_ok = handler->handlePost(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handlePost(me, conn);
}
} else if (strcmp(request_info->request_method, "HEAD") == 0) {
status_ok = handler->handleHead(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handleHead(me, conn);
}
} else if (strcmp(request_info->request_method, "PUT") == 0) {
status_ok = handler->handlePut(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handlePut(me, conn);
}
} else if (strcmp(request_info->request_method, "DELETE") == 0) {
status_ok = handler->handleDelete(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handleDelete(me, conn);
}
} else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
status_ok = handler->handleOptions(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handleOptions(me, conn);
}
} else if (strcmp(request_info->request_method, "PATCH") == 0) {
status_ok = handler->handlePatch(me, conn, &http_status_code);
if (http_status_code < 0) {
status_ok = handler->handlePatch(me, conn);
}
}
}
if (http_status_code < 0) {
http_status_code = status_ok ? 1 : 0;
}
return http_status_code;
}
int
CivetServer::authHandler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return 0;
mg_lock_context(me->context);
me->connections[conn] = CivetConnection();
mg_unlock_context(me->context);
CivetAuthHandler *handler = (CivetAuthHandler *)cbdata;
if (handler) {
return handler->authorize(me, conn) ? 1 : 0;
}
return 0; // No handler found
}
int
CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,
void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return 0;
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
if (handler) {
return handler->handleConnection(me, conn) ? 0 : 1;
}
return 1; // No handler found, close connection
}
void
CivetServer::webSocketReadyHandler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return;
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
if (handler) {
handler->handleReadyState(me, conn);
}
}
int
CivetServer::webSocketDataHandler(struct mg_connection *conn,
int bits,
char *data,
size_t data_len,
void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return 0;
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
if (handler) {
return handler->handleData(me, conn, bits, data, data_len) ? 1 : 0;
}
return 1; // No handler found
}
void
CivetServer::webSocketCloseHandler(const struct mg_connection *conn,
void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return;
CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
if (handler) {
handler->handleClose(me, conn);
}
}
CivetCallbacks::CivetCallbacks()
{
memset(this, 0, sizeof(*this));
}
CivetServer::CivetServer(const char **options,
const struct CivetCallbacks *_callbacks,
const void *UserContextIn)
: context(0)
{
struct CivetCallbacks callbacks;
UserContext = UserContextIn;
if (_callbacks) {
callbacks = *_callbacks;
userCloseHandler = _callbacks->connection_close;
} else {
userCloseHandler = NULL;
}
callbacks.connection_close = closeHandler;
struct mg_init_data mg_start_init_data = {0};
mg_start_init_data.callbacks = &callbacks;
mg_start_init_data.user_data = this;
mg_start_init_data.configuration_options = options;
struct mg_error_data mg_start_error_data = {0};
char errtxtbuf[256] = {0};
mg_start_error_data.text = errtxtbuf;
mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
context = mg_start2(&mg_start_init_data, &mg_start_error_data);
if (context == NULL) {
std::string exceptionMsg =
"null context when constructing CivetServer. "
"Possible problem binding to port. Error: ";
exceptionMsg += errtxtbuf;
printf("CivetServer: %s\n", exceptionMsg.c_str());
throw CivetException(exceptionMsg);
}
}
CivetServer::CivetServer(const std::vector<std::string> &options,
const struct CivetCallbacks *_callbacks,
const void *UserContextIn)
: context(0)
{
struct CivetCallbacks callbacks;
UserContext = UserContextIn;
if (_callbacks) {
callbacks = *_callbacks;
userCloseHandler = _callbacks->connection_close;
} else {
userCloseHandler = NULL;
}
callbacks.connection_close = closeHandler;
std::vector<const char *> pointers(options.size() + 1);
for (size_t i = 0; i < options.size(); i++) {
pointers[i] = (options[i].c_str());
}
pointers.back() = NULL;
struct mg_init_data mg_start_init_data = {0};
mg_start_init_data.callbacks = &callbacks;
mg_start_init_data.user_data = this;
mg_start_init_data.configuration_options = &pointers[0];
struct mg_error_data mg_start_error_data = {0};
char errtxtbuf[256] = {0};
mg_start_error_data.text = errtxtbuf;
mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
context = mg_start2(&mg_start_init_data, &mg_start_error_data);
if (context == NULL) {
std::string exceptionMsg =
"null context when constructing CivetServer. "
"Possible problem binding to port. Error: ";
exceptionMsg += errtxtbuf;
throw CivetException(exceptionMsg);
}
}
CivetServer::~CivetServer()
{
close();
}
void
CivetServer::closeHandler(const struct mg_connection *conn)
{
CivetServer *me = (CivetServer *)mg_get_user_data(mg_get_context(conn));
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return;
if (me->userCloseHandler) {
me->userCloseHandler(conn);
}
mg_lock_context(me->context);
me->connections.erase(conn);
mg_unlock_context(me->context);
}
void
CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
{
mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
}
void
CivetServer::addWebSocketHandler(const std::string &uri,
CivetWebSocketHandler *handler)
{
mg_set_websocket_handler(context,
uri.c_str(),
webSocketConnectionHandler,
webSocketReadyHandler,
webSocketDataHandler,
webSocketCloseHandler,
handler);
}
void
CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler)
{
mg_set_auth_handler(context, uri.c_str(), authHandler, handler);
}
void
CivetServer::removeHandler(const std::string &uri)
{
mg_set_request_handler(context, uri.c_str(), NULL, NULL);
}
void
CivetServer::removeWebSocketHandler(const std::string &uri)
{
mg_set_websocket_handler(
context, uri.c_str(), NULL, NULL, NULL, NULL, NULL);
}
void
CivetServer::removeAuthHandler(const std::string &uri)
{
mg_set_auth_handler(context, uri.c_str(), NULL, NULL);
}
void
CivetServer::close()
{
if (context) {
mg_stop(context);
context = 0;
}
}
int
CivetServer::getCookie(struct mg_connection *conn,
const std::string &cookieName,
std::string &cookieValue)
{
// Maximum cookie length as per microsoft is 4096.
// http://msdn.microsoft.com/en-us/library/ms178194.aspx
char _cookieValue[4096];
const char *cookie = mg_get_header(conn, "Cookie");
int lRead = mg_get_cookie(cookie,
cookieName.c_str(),
_cookieValue,
sizeof(_cookieValue));
cookieValue.clear();
cookieValue.append(_cookieValue);
return lRead;
}
const char *
CivetServer::getHeader(struct mg_connection *conn,
const std::string &headerName)
{
return mg_get_header(conn, headerName.c_str());
}
const char *
CivetServer::getMethod(struct mg_connection *conn)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
return request_info->request_method;
}
void
CivetServer::urlDecode(const char *src,
std::string &dst,
bool is_form_url_encoded)
{
urlDecode(src, strlen(src), dst, is_form_url_encoded);
}
void
CivetServer::urlDecode(const char *src,
size_t src_len,
std::string &dst,
bool is_form_url_encoded)
{
// assign enough buffer
std::vector<char> buf(src_len + 1);
int r = mg_url_decode(src,
static_cast<int>(src_len),
&buf[0],
static_cast<int>(buf.size()),
is_form_url_encoded);
if (r < 0) {
// never reach here
throw std::out_of_range("");
}
// dst can contain NUL characters
dst.assign(buf.begin(), buf.begin() + r);
}
bool
CivetServer::getParam(struct mg_connection *conn,
const char *name,
std::string &dst,
size_t occurrence)
{
const char *formParams = NULL;
const char *queryString = NULL;
const struct mg_request_info *ri = mg_get_request_info(conn);
assert(ri != NULL);
CivetServer *me = (CivetServer *)(ri->user_data);
assert(me != NULL);
mg_lock_context(me->context);
CivetConnection &conobj = me->connections[conn];
mg_unlock_context(me->context);
mg_lock_connection(conn);
if (conobj.postData.empty()) {
// check if there is a request body
for (;;) {
char buf[2048];
int r = mg_read(conn, buf, sizeof(buf));
try {
if (r == 0) {
conobj.postData.push_back('\0');
break;
} else if ((r < 0)
|| ((conobj.postData.size() + r)
> MAX_PARAM_BODY_LENGTH)) {
conobj.postData.assign(1, '\0');
break;
}
conobj.postData.insert(conobj.postData.end(), buf, buf + r);
} catch (...) {
conobj.postData.clear();
break;
}
}
}
if (!conobj.postData.empty()) {
// check if form parameter are already stored
formParams = &conobj.postData[0];
}
if (ri->query_string != NULL) {
// get requests do store html <form> field values in the http
// query_string
queryString = ri->query_string;
}
mg_unlock_connection(conn);
bool get_param_success = false;
if (formParams != NULL) {
get_param_success =
getParam(formParams, strlen(formParams), name, dst, occurrence);
}
if (!get_param_success && queryString != NULL) {
get_param_success =
getParam(queryString, strlen(queryString), name, dst, occurrence);
}
return get_param_success;
}
bool
CivetServer::getParam(const char *data,
size_t data_len,
const char *name,
std::string &dst,
size_t occurrence)
{
char buf[256];
int r = mg_get_var2(data, data_len, name, buf, sizeof(buf), occurrence);
if (r >= 0) {
// dst can contain NUL characters
dst.assign(buf, r);
return true;
} else if (r == -2) {
// more buffer
std::vector<char> vbuf(sizeof(buf) * 2);
for (;;) {
r = mg_get_var2(
data, data_len, name, &vbuf[0], vbuf.size(), occurrence);
if (r >= 0) {
dst.assign(vbuf.begin(), vbuf.begin() + r);
return true;
} else if (r != -2) {
break;
}
// more buffer
vbuf.resize(vbuf.size() * 2);
}
}
dst.clear();
return false;
}
std::string
CivetServer::getPostData(struct mg_connection *conn)
{
mg_lock_connection(conn);
std::string postdata;
char buf[2048];
int r = mg_read(conn, buf, sizeof(buf));
while (r > 0) {
postdata.append(buf, r);
r = mg_read(conn, buf, sizeof(buf));
}
mg_unlock_connection(conn);
return postdata;
}
void
CivetServer::urlEncode(const char *src, std::string &dst, bool append)
{
urlEncode(src, strlen(src), dst, append);
}
void
CivetServer::urlEncode(const char *src,
size_t src_len,
std::string &dst,
bool append)
{
if (!append)
dst.clear();
for (; src_len > 0; src++, src_len--) {
if (*src == '\0') {
// src and dst can contain NUL characters without encoding
dst.push_back(*src);
} else {
char buf[2] = {*src, '\0'};
char dst_buf[4];
if (mg_url_encode(buf, dst_buf, sizeof(dst_buf)) < 0) {
// never reach here
throw std::out_of_range("");
}
dst.append(dst_buf);
}
}
}
std::vector<int>
CivetServer::getListeningPorts()
{
std::vector<struct mg_server_port> server_ports = getListeningPortsFull();
std::vector<int> ports(server_ports.size());
for (size_t i = 0; i < server_ports.size(); i++) {
ports[i] = server_ports[i].port;
}
return ports;
}
std::vector<struct mg_server_port>
CivetServer::getListeningPortsFull()
{
std::vector<struct mg_server_port> server_ports(8);
for (;;) {
int size = mg_get_server_ports(context,
static_cast<int>(server_ports.size()),
&server_ports[0]);
if (size < static_cast<int>(server_ports.size())) {
server_ports.resize(size < 0 ? 0 : size);
break;
}
server_ports.resize(server_ports.size() * 2);
}
return server_ports;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
#include "civetweb.h"
static void log_access(const struct mg_connection *conn) {
}

View File

@@ -0,0 +1,4 @@
#include "civetweb.h"
static void
mg_cry_internal_impl(const struct mg_connection *conn, const char *func, unsigned line, const char *fmt, va_list ap) {}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,736 @@
/* Copyright (c) 2013-2017 the Civetweb developers
* Copyright (c) 2013 No Face Press, LLC
*
* License http://opensource.org/licenses/mit-license.php MIT License
*/
#ifndef CIVETSERVER_HEADER_INCLUDED
#define CIVETSERVER_HEADER_INCLUDED
#ifdef __cplusplus
#include "civetweb.h"
#include <map>
#include <stdexcept>
#include <string>
#include <vector>
#ifndef CIVETWEB_CXX_API
#if defined(_WIN32)
#if defined(CIVETWEB_CXX_DLL_EXPORTS)
#define CIVETWEB_CXX_API __declspec(dllexport)
#elif defined(CIVETWEB_CXX_DLL_IMPORTS)
#define CIVETWEB_CXX_API __declspec(dllimport)
#else
#define CIVETWEB_CXX_API
#endif
#elif __GNUC__ >= 4
#define CIVETWEB_CXX_API __attribute__((visibility("default")))
#else
#define CIVETWEB_CXX_API
#endif
#endif
// forward declaration
class CivetServer;
/**
* Exception class for thrown exceptions within the CivetHandler object.
*/
class CIVETWEB_CXX_API CivetException : public std::runtime_error
{
public:
CivetException(const std::string &msg) : std::runtime_error(msg)
{
}
};
/**
* Basic interface for a URI request handler. Handlers implementations
* must be reentrant.
*/
class CIVETWEB_CXX_API CivetHandler
{
public:
/**
* Destructor
*/
virtual ~CivetHandler()
{
}
/**
* Callback method for GET request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handleGet(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for GET request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handleGet(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for POST request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handlePost(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for POST request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handlePost(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for HEAD request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handleHead(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for HEAD request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handleHead(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for PUT request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handlePut(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for PUT request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handlePut(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for DELETE request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handleDelete(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for DELETE request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handleDelete(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for OPTIONS request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handleOptions(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for OPTIONS request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handleOptions(CivetServer *server,
struct mg_connection *conn,
int *status_code);
/**
* Callback method for PATCH request.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if implemented, false otherwise
*/
virtual bool handlePatch(CivetServer *server, struct mg_connection *conn);
/**
* Callback method for PATCH request.
*
* @param server - the calling server
* @param conn - the connection information
* @param status_code - pointer to return status code
* @returns true if implemented, false otherwise
*/
virtual bool handlePatch(CivetServer *server,
struct mg_connection *conn,
int *status_code);
};
/**
* Basic interface for a URI authorization handler. Handler implementations
* must be reentrant.
*/
class CIVETWEB_CXX_API CivetAuthHandler
{
public:
/**
* Destructor
*/
virtual ~CivetAuthHandler()
{
}
/**
* Callback method for authorization requests. It is up the this handler
* to generate 401 responses if authorization fails.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if authorization succeeded, false otherwise
*/
virtual bool authorize(CivetServer *server, struct mg_connection *conn) = 0;
};
/**
* Basic interface for a websocket handler. Handlers implementations
* must be reentrant.
*/
class CIVETWEB_CXX_API CivetWebSocketHandler
{
public:
/**
* Destructor
*/
virtual ~CivetWebSocketHandler()
{
}
/**
* Callback method for when the client intends to establish a websocket
*connection, before websocket handshake.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true to keep socket open, false to close it
*/
virtual bool handleConnection(CivetServer *server,
const struct mg_connection *conn);
/**
* Callback method for when websocket handshake is successfully completed,
*and connection is ready for data exchange.
*
* @param server - the calling server
* @param conn - the connection information
*/
virtual void handleReadyState(CivetServer *server,
struct mg_connection *conn);
/**
* Callback method for when a data frame has been received from the client.
*
* @param server - the calling server
* @param conn - the connection information
* @bits: first byte of the websocket frame, see websocket RFC at
*http://tools.ietf.org/html/rfc6455, section 5.2
* @data, data_len: payload, with mask (if any) already applied.
* @returns true to keep socket open, false to close it
*/
virtual bool handleData(CivetServer *server,
struct mg_connection *conn,
int bits,
char *data,
size_t data_len);
/**
* Callback method for when the connection is closed.
*
* @param server - the calling server
* @param conn - the connection information
*/
virtual void handleClose(CivetServer *server,
const struct mg_connection *conn);
};
/**
* CivetCallbacks
*
* wrapper for mg_callbacks
*/
struct CIVETWEB_CXX_API CivetCallbacks : public mg_callbacks {
CivetCallbacks();
};
/**
* CivetServer
*
* Basic class for embedded web server. This has an URL mapping built-in.
*/
class CIVETWEB_CXX_API CivetServer
{
public:
/**
* Constructor
*
* This automatically starts the sever.
* It is good practice to call getContext() after this in case there
* were errors starting the server.
*
* Note: CivetServer should not be used as a static instance in a Windows
* DLL, since the constructor creates threads and the destructor joins
* them again (creating/joining threads should not be done in static
* constructors).
*
* @param options - the web server options.
* @param callbacks - optional web server callback methods.
*
* @throws CivetException
*/
CivetServer(const char **options,
const struct CivetCallbacks *callbacks = 0,
const void *UserContext = 0);
CivetServer(const std::vector<std::string> &options,
const struct CivetCallbacks *callbacks = 0,
const void *UserContext = 0);
/**
* Destructor
*/
virtual ~CivetServer();
/**
* close()
*
* Stops server and frees resources.
*/
void close();
/**
* getContext()
*
* @return the context or 0 if not running.
*/
const struct mg_context *
getContext() const
{
return context;
}
/**
* addHandler(const std::string &, CivetHandler *)
*
* Adds a URI handler. If there is existing URI handler, it will
* be replaced with this one.
*
* URI's are ordered and prefix (REST) URI's are supported.
*
* @param uri - URI to match.
* @param handler - handler instance to use.
*/
void addHandler(const std::string &uri, CivetHandler *handler);
void
addHandler(const std::string &uri, CivetHandler &handler)
{
addHandler(uri, &handler);
}
/**
* addWebSocketHandler
*
* Adds a WebSocket handler for a specific URI. If there is existing URI
*handler, it will
* be replaced with this one.
*
* URI's are ordered and prefix (REST) URI's are supported.
*
* @param uri - URI to match.
* @param handler - handler instance to use.
*/
void addWebSocketHandler(const std::string &uri,
CivetWebSocketHandler *handler);
void
addWebSocketHandler(const std::string &uri, CivetWebSocketHandler &handler)
{
addWebSocketHandler(uri, &handler);
}
/**
* removeHandler(const std::string &)
*
* Removes a handler.
*
* @param uri - the exact URL used in addHandler().
*/
void removeHandler(const std::string &uri);
/**
* removeWebSocketHandler(const std::string &)
*
* Removes a web socket handler.
*
* @param uri - the exact URL used in addWebSocketHandler().
*/
void removeWebSocketHandler(const std::string &uri);
/**
* addAuthHandler(const std::string &, CivetAuthHandler *)
*
* Adds a URI authorization handler. If there is existing URI authorization
* handler, it will be replaced with this one.
*
* URI's are ordered and prefix (REST) URI's are supported.
*
* @param uri - URI to match.
* @param handler - authorization handler instance to use.
*/
void addAuthHandler(const std::string &uri, CivetAuthHandler *handler);
void
addAuthHandler(const std::string &uri, CivetAuthHandler &handler)
{
addAuthHandler(uri, &handler);
}
/**
* removeAuthHandler(const std::string &)
*
* Removes an authorization handler.
*
* @param uri - the exact URL used in addAuthHandler().
*/
void removeAuthHandler(const std::string &uri);
/**
* getListeningPorts()
*
* Returns a list of ports that are listening
*
* @return A vector of ports
*/
std::vector<int> getListeningPorts();
/**
* getListeningPorts()
*
* Variant of getListeningPorts() returning the full port information
* (protocol, SSL, ...)
*
* @return A vector of ports
*/
std::vector<struct mg_server_port> getListeningPortsFull();
/**
* getCookie(struct mg_connection *conn, const std::string &cookieName,
* std::string &cookieValue)
*
* Puts the cookie value string that matches the cookie name in the
* cookieValue destination string.
*
* @param conn - the connection information
* @param cookieName - cookie name to get the value from
* @param cookieValue - cookie value is returned using this reference
* @returns the size of the cookie value string read.
*/
static int getCookie(struct mg_connection *conn,
const std::string &cookieName,
std::string &cookieValue);
/**
* getHeader(struct mg_connection *conn, const std::string &headerName)
* @param conn - the connection information
* @param headerName - header name to get the value from
* @returns a char array which contains the header value as string
*/
static const char *getHeader(struct mg_connection *conn,
const std::string &headerName);
/**
* getMethod(struct mg_connection *conn)
* @param conn - the connection information
* @returns method of HTTP request
*/
static const char *getMethod(struct mg_connection *conn);
/**
* getParam(struct mg_connection *conn, const char *, std::string &, size_t)
*
* Returns a query which contained in the supplied buffer. The
* occurrence value is a zero-based index of a particular key name. This
* should not be confused with the index over all of the keys. Note that
*this
* function assumes that parameters are sent as text in http query string
* format, which is the default for web forms. This function will work for
* html forms with method="GET" and method="POST" attributes. In other
*cases,
* you may use a getParam version that directly takes the data instead of
*the
* connection as a first argument.
*
* @param conn - parameters are read from the data sent through this
*connection
* @param name - the key to search for
* @param dst - the destination string
* @param occurrence - the occurrence of the selected name in the query (0
*based).
* @return true if key was found
*/
static bool getParam(struct mg_connection *conn,
const char *name,
std::string &dst,
size_t occurrence = 0);
/**
* getParam(const std::string &, const char *, std::string &, size_t)
*
* Returns a query parameter contained in the supplied buffer. The
* occurrence value is a zero-based index of a particular key name. This
* should not be confused with the index over all of the keys.
*
* @param data - the query string (text)
* @param name - the key to search for
* @param dst - the destination string
* @param occurrence - the occurrence of the selected name in the query (0
*based).
* @return true if key was found
*/
static bool
getParam(const std::string &data,
const char *name,
std::string &dst,
size_t occurrence = 0)
{
return getParam(data.c_str(), data.length(), name, dst, occurrence);
}
/**
* getParam(const char *, size_t, const char *, std::string &, size_t)
*
* Returns a query parameter contained in the supplied buffer. The
* occurrence value is a zero-based index of a particular key name. This
* should not be confused with the index over all of the keys.
*
* @param data the - query string (text)
* @param data_len - length of the query string
* @param name - the key to search for
* @param dst - the destination string
* @param occurrence - the occurrence of the selected name in the query (0
*based).
* @return true if key was found
*/
static bool getParam(const char *data,
size_t data_len,
const char *name,
std::string &dst,
size_t occurrence = 0);
/**
* getPostData(struct mg_connection *)
*
* Returns response body from a request made as POST. Since the
* connections map is protected, it can't be directly accessed.
* This uses string to store post data to handle big posts.
*
* @param conn - connection from which post data will be read
* @return Post data (empty if not available).
*/
static std::string getPostData(struct mg_connection *conn);
/**
* urlDecode(const std::string &, std::string &, bool)
*
* @param src - string to be decoded
* @param dst - destination string
* @param is_form_url_encoded - true if form url encoded
* form-url-encoded data differs from URI encoding in a way that it
* uses '+' as character for space, see RFC 1866 section 8.2.1
* http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
*/
static void
urlDecode(const std::string &src,
std::string &dst,
bool is_form_url_encoded = true)
{
urlDecode(src.c_str(), src.length(), dst, is_form_url_encoded);
}
/**
* urlDecode(const char *, size_t, std::string &, bool)
*
* @param src - buffer to be decoded
* @param src_len - length of buffer to be decoded
* @param dst - destination string
* @param is_form_url_encoded - true if form url encoded
* form-url-encoded data differs from URI encoding in a way that it
* uses '+' as character for space, see RFC 1866 section 8.2.1
* http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
*/
static void urlDecode(const char *src,
size_t src_len,
std::string &dst,
bool is_form_url_encoded = true);
/**
* urlDecode(const char *, std::string &, bool)
*
* @param src - buffer to be decoded (0 terminated)
* @param dst - destination string
* @param is_form_url_encoded true - if form url encoded
* form-url-encoded data differs from URI encoding in a way that it
* uses '+' as character for space, see RFC 1866 section 8.2.1
* http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
*/
static void urlDecode(const char *src,
std::string &dst,
bool is_form_url_encoded = true);
/**
* urlEncode(const std::string &, std::string &, bool)
*
* @param src - buffer to be encoded
* @param dst - destination string
* @param append - true if string should not be cleared before encoding.
*/
static void
urlEncode(const std::string &src, std::string &dst, bool append = false)
{
urlEncode(src.c_str(), src.length(), dst, append);
}
/**
* urlEncode(const char *, size_t, std::string &, bool)
*
* @param src - buffer to be encoded (0 terminated)
* @param dst - destination string
* @param append - true if string should not be cleared before encoding.
*/
static void
urlEncode(const char *src, std::string &dst, bool append = false);
/**
* urlEncode(const char *, size_t, std::string &, bool)
*
* @param src - buffer to be encoded
* @param src_len - length of buffer to be decoded
* @param dst - destination string
* @param append - true if string should not be cleared before encoding.
*/
static void urlEncode(const char *src,
size_t src_len,
std::string &dst,
bool append = false);
// generic user context which can be set/read,
// the server does nothing with this apart from keep it.
const void *
getUserContext() const
{
return UserContext;
}
protected:
class CivetConnection
{
public:
std::vector<char> postData;
};
struct mg_context *context;
std::map<const struct mg_connection *, CivetConnection> connections;
// generic user context which can be set/read,
// the server does nothing with this apart from keep it.
const void *UserContext;
private:
/**
* requestHandler(struct mg_connection *, void *cbdata)
*
* Handles the incoming request.
*
* @param conn - the connection information
* @param cbdata - pointer to the CivetHandler instance.
* @returns 0 if implemented, false otherwise
*/
static int requestHandler(struct mg_connection *conn, void *cbdata);
static int webSocketConnectionHandler(const struct mg_connection *conn,
void *cbdata);
static void webSocketReadyHandler(struct mg_connection *conn, void *cbdata);
static int webSocketDataHandler(struct mg_connection *conn,
int bits,
char *data,
size_t data_len,
void *cbdata);
static void webSocketCloseHandler(const struct mg_connection *conn,
void *cbdata);
/**
* authHandler(struct mg_connection *, void *cbdata)
*
* Handles the authorization requests.
*
* @param conn - the connection information
* @param cbdata - pointer to the CivetAuthHandler instance.
* @returns 1 if authorized, 0 otherwise
*/
static int authHandler(struct mg_connection *conn, void *cbdata);
/**
* closeHandler(struct mg_connection *)
*
* Handles closing a request (internal handler)
*
* @param conn - the connection information
*/
static void closeHandler(const struct mg_connection *conn);
/**
* Stores the user provided close handler
*/
void (*userCloseHandler)(const struct mg_connection *conn);
};
#endif /* __cplusplus */
#endif /* CIVETSERVER_HEADER_INCLUDED */

View File

@@ -0,0 +1,12 @@
#pragma once
#define NO_SSL
#define USE_WEBSOCKET
#define NO_FILESYSTEMS
#define NO_FILES
#define NO_CGI
#define NO_CACHING
#define NO_ATOMICS
#define MG_EXTERNAL_FUNCTION_mg_cry_internal_impl
#define MG_EXTERNAL_FUNCTION_log_access

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
/* Reimplementation of pattern matching */
/* This file is part of the CivetWeb web server.
* See https://github.com/civetweb/civetweb/
*/
/* Initialize structure with 0 matches */
static void
match_context_reset(struct mg_match_context *mcx)
{
mcx->num_matches = 0;
memset(mcx->match, 0, sizeof(mcx->match));
}
/* Add a new match to the list of matches */
static void
match_context_push(const char *str, size_t len, struct mg_match_context *mcx)
{
if (mcx->num_matches < MG_MATCH_CONTEXT_MAX_MATCHES) {
mcx->match[mcx->num_matches].str = str;
mcx->match[mcx->num_matches].len = len;
mcx->num_matches++;
}
}
static ptrdiff_t
mg_match_impl(const char *pat,
size_t pat_len,
const char *str,
struct mg_match_context *mcx)
{
/* Parse string */
size_t i_pat = 0; /* Pattern index */
size_t i_str = 0; /* Pattern index */
uint8_t case_sensitive = ((mcx != NULL) ? mcx->case_sensitive : 0);
while (i_pat < pat_len) {
/* Pattern ? matches one character, except / and NULL character */
if ((pat[i_pat] == '?') && (str[i_str] != '\0')
&& (str[i_str] != '/')) {
size_t i_str_start = i_str;
do {
/* Advance as long as there are ? */
i_pat++;
i_str++;
} while ((pat[i_pat] == '?') && (str[i_str] != '\0')
&& (str[i_str] != '/') && (i_pat < pat_len));
/* If we have a match context, add the substring we just found */
if (mcx) {
match_context_push(str + i_str_start, i_str - i_str_start, mcx);
}
/* Reached end of pattern ? */
if (i_pat == pat_len) {
return (ptrdiff_t)i_str;
}
}
/* Pattern $ matches end of string */
if (pat[i_pat] == '$') {
return (str[i_str] == '\0') ? (ptrdiff_t)i_str : -1;
}
/* Pattern * or ** matches multiple characters */
if (pat[i_pat] == '*') {
size_t len; /* lenght matched by "*" or "**" */
ptrdiff_t ret;
i_pat++;
if ((pat[i_pat] == '*') && (i_pat < pat_len)) {
/* Pattern ** matches all */
i_pat++;
len = strlen(str + i_str);
} else {
/* Pattern * matches all except / character */
len = strcspn(str + i_str, "/");
}
if (i_pat == pat_len) {
/* End of pattern reached. Add all to match context. */
if (mcx) {
match_context_push(str + i_str, len, mcx);
}
return (i_str + len);
}
/* This loop searches for the longest possible match */
do {
ret = mg_match_impl(pat + i_pat,
(pat_len - (size_t)i_pat),
str + i_str + len,
mcx);
} while ((ret == -1) && (len-- > 0));
/* If we have a match context, add the substring we just found */
if (ret >= 0) {
if (mcx) {
match_context_push(str + i_str, len, mcx);
}
return (i_str + ret + len);
}
return -1;
}
/* Single character compare */
if (case_sensitive) {
if (pat[i_pat] != str[i_str]) {
/* case sensitive compare: mismatch */
return -1;
}
} else if (lowercase(&pat[i_pat]) != lowercase(&str[i_str])) {
/* case insensitive compare: mismatch */
return -1;
}
i_pat++;
i_str++;
}
return (ptrdiff_t)i_str;
}
static ptrdiff_t
mg_match_alternatives(const char *pat,
size_t pat_len,
const char *str,
struct mg_match_context *mcx)
{
const char *match_alternative = (const char *)memchr(pat, '|', pat_len);
if (mcx != NULL) {
match_context_reset(mcx);
}
while (match_alternative != NULL) {
/* Split at | for alternative match */
size_t left_size = (size_t)(match_alternative - pat);
/* Try left string first */
ptrdiff_t ret = mg_match_impl(pat, left_size, str, mcx);
if (ret >= 0) {
/* A 0-byte match is also valid */
return ret;
}
/* Reset possible incomplete match data */
if (mcx != NULL) {
match_context_reset(mcx);
}
/* If no match: try right side */
pat += left_size + 1;
pat_len -= left_size + 1;
match_alternative = (const char *)memchr(pat, '|', pat_len);
}
/* Handled all | operators. This is the final string. */
return mg_match_impl(pat, pat_len, str, mcx);
}
static int
match_compare(const void *p1, const void *p2, void *user)
{
const struct mg_match_element *e1 = (const struct mg_match_element *)p1;
const struct mg_match_element *e2 = (const struct mg_match_element *)p2;
/* unused */
(void)user;
if (e1->str > e2->str) {
return +1;
}
if (e1->str < e2->str) {
return -1;
}
return 0;
}
#if defined(MG_EXPERIMENTAL_INTERFACES)
CIVETWEB_API
#else
static
#endif
ptrdiff_t
mg_match(const char *pat, const char *str, struct mg_match_context *mcx)
{
size_t pat_len = strlen(pat);
ptrdiff_t ret = mg_match_alternatives(pat, pat_len, str, mcx);
if (mcx != NULL) {
if (ret < 0) {
/* Remove possible incomplete data */
match_context_reset(mcx);
} else {
/* Join "?*" to one pattern. */
size_t i, j;
/* Use difference of two array elements instead of sizeof, since
* there may be some additional padding bytes. */
size_t elmsize =
(size_t)(&mcx->match[1]) - (size_t)(&mcx->match[0]);
/* First sort the matches by address ("str" begin to end) */
mg_sort(mcx->match, mcx->num_matches, elmsize, match_compare, NULL);
/* Join consecutive matches */
i = 1;
while (i < mcx->num_matches) {
if ((mcx->match[i - 1].str + mcx->match[i - 1].len)
== mcx->match[i].str) {
/* Two matches are consecutive. Join length. */
mcx->match[i - 1].len += mcx->match[i].len;
/* Shift all list elements. */
for (j = i + 1; j < mcx->num_matches; j++) {
mcx->match[j - 1].len = mcx->match[j].len;
mcx->match[j - 1].str = mcx->match[j].str;
}
/* Remove/blank last list element. */
mcx->num_matches--;
mcx->match[mcx->num_matches].str = NULL;
mcx->match[mcx->num_matches].len = 0;
} else {
i++;
}
}
}
}
return ret;
}
static ptrdiff_t
match_prefix(const char *pattern, size_t pattern_len, const char *str)
{
if (pattern == NULL) {
return -1;
}
return mg_match_alternatives(pattern, pattern_len, str, NULL);
}
static ptrdiff_t
match_prefix_strlen(const char *pattern, const char *str)
{
if (pattern == NULL) {
return -1;
}
return mg_match_alternatives(pattern, strlen(pattern), str, NULL);
}
/* End of match.inl */

View File

@@ -0,0 +1,472 @@
/*
* 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.
*/
#if !defined(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;
#if defined(__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]);
#if defined(__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.
*/
#if !defined(MD5_STATIC)
#include <stdint.h>
#include <string.h>
#endif
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#if defined(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 (!(((uintptr_t)data) & 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));
}
/* End of md5.inl */

View File

@@ -0,0 +1,339 @@
/* response.inl
*
* Bufferring for HTTP headers for HTTP response.
* This function are only intended to be used at the server side.
* Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2.
*
* This file is part of the CivetWeb project.
*/
#if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2)
#error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set"
#endif
/* Internal function to free header list */
static void
free_buffered_response_header_list(struct mg_connection *conn)
{
#if !defined(NO_RESPONSE_BUFFERING)
while (conn->response_info.num_headers > 0) {
conn->response_info.num_headers--;
mg_free((void *)conn->response_info
.http_headers[conn->response_info.num_headers]
.name);
conn->response_info.http_headers[conn->response_info.num_headers].name =
0;
mg_free((void *)conn->response_info
.http_headers[conn->response_info.num_headers]
.value);
conn->response_info.http_headers[conn->response_info.num_headers]
.value = 0;
}
#else
(void)conn; /* Nothing to do */
#endif
}
/* Send first line of HTTP/1.x response */
static int
send_http1_response_status_line(struct mg_connection *conn)
{
const char *status_txt;
const char *http_version = conn->request_info.http_version;
int status_code = conn->status_code;
if ((status_code < 100) || (status_code > 999)) {
/* Set invalid status code to "500 Internal Server Error" */
status_code = 500;
}
if (!http_version) {
http_version = "1.0";
}
/* mg_get_response_code_text will never return NULL */
status_txt = mg_get_response_code_text(conn, conn->status_code);
if (mg_printf(
conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt)
< 10) {
/* Network sending failed */
return 0;
}
return 1;
}
/* Initialize a new HTTP response
* Parameters:
* conn: Current connection handle.
* status: HTTP status code (e.g., 200 for "OK").
* Return:
* 0: ok
* -1: parameter error
* -2: invalid connection type
* -3: invalid connection status
* -4: network error (only if built with NO_RESPONSE_BUFFERING)
*/
int
mg_response_header_start(struct mg_connection *conn, int status)
{
int ret = 0;
if ((conn == NULL) || (status < 100) || (status > 999)) {
/* Parameter error */
return -1;
}
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
/* Only allowed in server context */
return -2;
}
if (conn->request_state != 0) {
/* only allowed if nothing was sent up to now */
return -3;
}
conn->status_code = status;
conn->request_state = 1;
/* Buffered response is stored, unbuffered response will be sent directly,
* but we can only send HTTP/1.x response here */
#if !defined(NO_RESPONSE_BUFFERING)
free_buffered_response_header_list(conn);
#else
if (!send_http1_response_status_line(conn)) {
ret = -4;
};
conn->request_state = 1; /* Reset from 10 to 1 */
#endif
return ret;
}
/* Add a new HTTP response header line
* Parameters:
* conn: Current connection handle.
* header: Header name.
* value: Header value.
* value_len: Length of header value, excluding the terminating zero.
* Use -1 for "strlen(value)".
* Return:
* 0: ok
* -1: parameter error
* -2: invalid connection type
* -3: invalid connection status
* -4: too many headers
* -5: out of memory
*/
int
mg_response_header_add(struct mg_connection *conn,
const char *header,
const char *value,
int value_len)
{
#if !defined(NO_RESPONSE_BUFFERING)
int hidx;
#endif
if ((conn == NULL) || (header == NULL) || (value == NULL)) {
/* Parameter error */
return -1;
}
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
/* Only allowed in server context */
return -2;
}
if (conn->request_state != 1) {
/* only allowed if mg_response_header_start has been called before */
return -3;
}
#if !defined(NO_RESPONSE_BUFFERING)
hidx = conn->response_info.num_headers;
if (hidx >= MG_MAX_HEADERS) {
/* Too many headers */
return -4;
}
/* Alloc new element */
conn->response_info.http_headers[hidx].name =
mg_strdup_ctx(header, conn->phys_ctx);
if (value_len >= 0) {
char *hbuf =
(char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);
if (hbuf) {
memcpy(hbuf, value, (unsigned)value_len);
hbuf[value_len] = 0;
}
conn->response_info.http_headers[hidx].value = hbuf;
} else {
conn->response_info.http_headers[hidx].value =
mg_strdup_ctx(value, conn->phys_ctx);
}
if ((conn->response_info.http_headers[hidx].name == 0)
|| (conn->response_info.http_headers[hidx].value == 0)) {
/* Out of memory */
mg_free((void *)conn->response_info.http_headers[hidx].name);
conn->response_info.http_headers[hidx].name = 0;
mg_free((void *)conn->response_info.http_headers[hidx].value);
conn->response_info.http_headers[hidx].value = 0;
return -5;
}
/* OK, header stored */
conn->response_info.num_headers++;
#else
if (value_len >= 0) {
mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value);
} else {
mg_printf(conn, "%s: %s\r\n", header, value);
}
conn->request_state = 1; /* Reset from 10 to 1 */
#endif
return 0;
}
/* forward */
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);
/* Add a complete header string (key + value).
* Parameters:
* conn: Current connection handle.
* http1_headers: Header line(s) in the form "name: value".
* Return:
* >=0: no error, number of header lines added
* -1: parameter error
* -2: invalid connection type
* -3: invalid connection status
* -4: too many headers
* -5: out of memory
*/
int
mg_response_header_add_lines(struct mg_connection *conn,
const char *http1_headers)
{
struct mg_header add_hdr[MG_MAX_HEADERS];
int num_hdr, i, ret;
char *workbuffer, *parse;
/* We need to work on a copy of the work buffer, sice parse_http_headers
* will modify */
workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);
if (!workbuffer) {
/* Out of memory */
return -5;
}
/* Call existing method to split header buffer */
parse = workbuffer;
num_hdr = parse_http_headers(&parse, add_hdr);
ret = num_hdr;
for (i = 0; i < num_hdr; i++) {
int lret =
mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);
if ((ret > 0) && (lret < 0)) {
/* Store error return value */
ret = lret;
}
}
/* mg_response_header_add created a copy, so we can free the original */
mg_free(workbuffer);
return ret;
}
#if defined(USE_HTTP2)
static int http2_send_response_headers(struct mg_connection *conn);
#endif
/* Send http response
* Parameters:
* conn: Current connection handle.
* Return:
* 0: ok
* -1: parameter error
* -2: invalid connection type
* -3: invalid connection status
* -4: network send failed
*/
int
mg_response_header_send(struct mg_connection *conn)
{
#if !defined(NO_RESPONSE_BUFFERING)
int i;
int has_date = 0;
int has_connection = 0;
#endif
if (conn == NULL) {
/* Parameter error */
return -1;
}
if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
|| (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
/* Only allowed in server context */
return -2;
}
if (conn->request_state != 1) {
/* only allowed if mg_response_header_start has been called before */
return -3;
}
/* State: 2 */
conn->request_state = 2;
#if !defined(NO_RESPONSE_BUFFERING)
#if defined(USE_HTTP2)
if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
int ret = http2_send_response_headers(conn);
return (ret ? 0 : -4);
}
#endif
/* Send */
if (!send_http1_response_status_line(conn)) {
return -4;
};
for (i = 0; i < conn->response_info.num_headers; i++) {
mg_printf(conn,
"%s: %s\r\n",
conn->response_info.http_headers[i].name,
conn->response_info.http_headers[i].value);
/* Check for some special headers */
if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
has_date = 1;
}
if (!mg_strcasecmp("Connection",
conn->response_info.http_headers[i].name)) {
has_connection = 1;
}
}
if (!has_date) {
time_t curtime = time(NULL);
char date[64];
gmt_time_string(date, sizeof(date), &curtime);
mg_printf(conn, "Date: %s\r\n", date);
}
if (!has_connection) {
mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));
}
#endif
mg_write(conn, "\r\n", 2);
conn->request_state = 3;
/* ok */
return 0;
}

View File

@@ -0,0 +1,323 @@
/*
SHA-1 in C
By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
However, on 16 bit machines an int is too small for the shifts being done
against
it. This caused the hash function to generate incorrect values if len was
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
an
int and a long are the same size in those environments.
--
I also corrected a few compiler warnings generated by Borland C.
1. Added #include <process.h> for exit() prototype
2. Removed unused variable 'j' in SHA1Final
3. Changed exit(0) to return(0) at end of main.
ALL changes I made can be located by searching for comments containing 'JHB'
-----------------
Modified 8/98
By Steve Reid <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> and used return() instead of exit()
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-----------------
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Modified 07/2002
By Ralph Giles <giles@ghostscript.com>
Still 100% public domain
modified for use with stdint types, autoconf
code cleanup, removed attribution comments
switched SHA1Final() argument order for consistency
use SHA1_ prefix for public api
move public api to sha1.h
*/
/*
11/2016 adapted for CivetWeb:
include sha1.h in sha1.c,
rename to sha1.inl
remove unused #ifdef sections
make endian independent
align buffer to 4 bytes
remove unused variable assignments
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
#include <stdint.h>
#include <string.h>
typedef struct {
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
} SHA_CTX;
#define SHA1_DIGEST_SIZE 20
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
typedef union {
uint8_t c[64];
uint32_t l[16];
} CHAR64LONG16;
static uint32_t
blk0(CHAR64LONG16 *block, int i)
{
static const uint32_t n = 1u;
if ((*((uint8_t *)(&n))) == 1) {
/* little endian / intel byte order */
block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00)
| (rol(block->l[i], 8) & 0x00FF00FF);
}
return block->l[i];
}
#define blk(block, i) \
((block)->l[(i)&15] = \
rol((block)->l[((i) + 13) & 15] ^ (block)->l[((i) + 8) & 15] \
^ (block)->l[((i) + 2) & 15] ^ (block)->l[(i)&15], \
1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R1(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R2(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); \
w = rol(w, 30);
#define R3(v, w, x, y, z, i) \
z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); \
w = rol(w, 30);
#define R4(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); \
w = rol(w, 30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
static void
SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
/* Must use an aligned, read/write buffer */
CHAR64LONG16 block[1];
memcpy(block, buffer, sizeof(block));
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a, b, c, d, e, 0);
R0(e, a, b, c, d, 1);
R0(d, e, a, b, c, 2);
R0(c, d, e, a, b, 3);
R0(b, c, d, e, a, 4);
R0(a, b, c, d, e, 5);
R0(e, a, b, c, d, 6);
R0(d, e, a, b, c, 7);
R0(c, d, e, a, b, 8);
R0(b, c, d, e, a, 9);
R0(a, b, c, d, e, 10);
R0(e, a, b, c, d, 11);
R0(d, e, a, b, c, 12);
R0(c, d, e, a, b, 13);
R0(b, c, d, e, a, 14);
R0(a, b, c, d, e, 15);
R1(e, a, b, c, d, 16);
R1(d, e, a, b, c, 17);
R1(c, d, e, a, b, 18);
R1(b, c, d, e, a, 19);
R2(a, b, c, d, e, 20);
R2(e, a, b, c, d, 21);
R2(d, e, a, b, c, 22);
R2(c, d, e, a, b, 23);
R2(b, c, d, e, a, 24);
R2(a, b, c, d, e, 25);
R2(e, a, b, c, d, 26);
R2(d, e, a, b, c, 27);
R2(c, d, e, a, b, 28);
R2(b, c, d, e, a, 29);
R2(a, b, c, d, e, 30);
R2(e, a, b, c, d, 31);
R2(d, e, a, b, c, 32);
R2(c, d, e, a, b, 33);
R2(b, c, d, e, a, 34);
R2(a, b, c, d, e, 35);
R2(e, a, b, c, d, 36);
R2(d, e, a, b, c, 37);
R2(c, d, e, a, b, 38);
R2(b, c, d, e, a, 39);
R3(a, b, c, d, e, 40);
R3(e, a, b, c, d, 41);
R3(d, e, a, b, c, 42);
R3(c, d, e, a, b, 43);
R3(b, c, d, e, a, 44);
R3(a, b, c, d, e, 45);
R3(e, a, b, c, d, 46);
R3(d, e, a, b, c, 47);
R3(c, d, e, a, b, 48);
R3(b, c, d, e, a, 49);
R3(a, b, c, d, e, 50);
R3(e, a, b, c, d, 51);
R3(d, e, a, b, c, 52);
R3(c, d, e, a, b, 53);
R3(b, c, d, e, a, 54);
R3(a, b, c, d, e, 55);
R3(e, a, b, c, d, 56);
R3(d, e, a, b, c, 57);
R3(c, d, e, a, b, 58);
R3(b, c, d, e, a, 59);
R4(a, b, c, d, e, 60);
R4(e, a, b, c, d, 61);
R4(d, e, a, b, c, 62);
R4(c, d, e, a, b, 63);
R4(b, c, d, e, a, 64);
R4(a, b, c, d, e, 65);
R4(e, a, b, c, d, 66);
R4(d, e, a, b, c, 67);
R4(c, d, e, a, b, 68);
R4(b, c, d, e, a, 69);
R4(a, b, c, d, e, 70);
R4(e, a, b, c, d, 71);
R4(d, e, a, b, c, 72);
R4(c, d, e, a, b, 73);
R4(b, c, d, e, a, 74);
R4(a, b, c, d, e, 75);
R4(e, a, b, c, d, 76);
R4(d, e, a, b, c, 77);
R4(c, d, e, a, b, 78);
R4(b, c, d, e, a, 79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
}
/* SHA1Init - Initialize new context */
SHA_API void
SHA1_Init(SHA_CTX *context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
SHA_API void
SHA1_Update(SHA_CTX *context, const uint8_t *data, const uint32_t len)
{
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += (len << 3)) < j) {
context->count[1]++;
}
context->count[1] += (len >> 29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
i = 64 - j;
memcpy(&context->buffer[j], data, i);
SHA1_Transform(context->state, context->buffer);
for (; i + 63 < len; i += 64) {
SHA1_Transform(context->state, &data[i]);
}
j = 0;
} else {
i = 0;
}
memcpy(&context->buffer[j], &data[i], len - i);
}
/* Add padding and return the message digest. */
SHA_API void
SHA1_Final(unsigned char *digest, SHA_CTX *context)
{
uint32_t i;
uint8_t finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] =
(uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8))
& 255); /* Endian independent */
}
SHA1_Update(context, (uint8_t *)"\x80", 1);
while ((context->count[0] & 504) != 448) {
SHA1_Update(context, (uint8_t *)"\x00", 1);
}
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
digest[i] =
(uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
}
/* Wipe variables */
memset(context, '\0', sizeof(*context));
}
/* End of sha1.inl */

View File

@@ -0,0 +1,48 @@
/* Sort function. */
/* from https://github.com/bel2125/sort_r */
static void
mg_sort(void *data,
size_t elemcount,
size_t elemsize,
int (*compfunc)(const void *data1, const void *data2, void *userarg),
void *userarg)
{
/* We cannot use qsort_r here. For a detailed reason, see
* https://github.com/civetweb/civetweb/issues/1048#issuecomment-1047093014
* https://stackoverflow.com/questions/39560773/different-declarations-of-qsort-r-on-mac-and-linux
*/
/* We use ShellSort here with this gap sequence: https://oeis.org/A102549 */
int A102549[9] = {1, 4, 10, 23, 57, 132, 301, 701, 1750};
int Aidx, gap, i, j, k;
void *tmp = alloca(elemsize);
for (Aidx = 8; Aidx >= 0; Aidx--) {
gap = A102549[Aidx];
if (gap > ((int)elemcount / 2)) {
continue;
}
for (i = 0; i < gap; i++) {
for (j = i; j < (int)elemcount; j += gap) {
memcpy(tmp, (void *)((ptrdiff_t)data + elemsize * j), elemsize);
for (k = j; k >= gap; k -= gap) {
void *cmp =
(void *)((ptrdiff_t)data + elemsize * (k - gap));
int cmpres = compfunc(cmp, tmp, userarg);
if (cmpres > 0) {
memcpy((void *)((ptrdiff_t)data + elemsize * k),
cmp,
elemsize);
} else {
break;
}
}
memcpy((void *)((ptrdiff_t)data + elemsize * k), tmp, elemsize);
}
}
}
}
/* end if sort.inl */