text: /* Copyright (c) 2013-2017 the Civetweb developers
   * Copyright (c) 2013 No Face Press, LLC
   *
   * License http://opensource.org/licenses/mit-license.php MIT License
   */
  
  #include "CivetServer.h"
  
  #include <stdlib.h>
  #include <string.h>
  #include <assert.h>
  #include <stdexcept>
  
  #ifndef UNUSED_PARAMETER
  #define UNUSED_PARAMETER(x) (void)(x)
  #endif
  
  bool
  CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handleHead(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handlePatch(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	return false;
  }
  
  bool
  CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
  {
  	UNUSED_PARAMETER(server);
  	UNUSED_PARAMETER(conn);
  	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);
  
  	// 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) {
  			return handler->handleGet(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "POST") == 0) {
  			return handler->handlePost(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "HEAD") == 0) {
  			return handler->handleHead(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "PUT") == 0) {
  			return handler->handlePut(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "DELETE") == 0) {
  			return handler->handleDelete(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
  			return handler->handleOptions(me, conn) ? 1 : 0;
  		} else if (strcmp(request_info->request_method, "PATCH") == 0) {
  			return handler->handlePatch(me, conn) ? 1 : 0;
  		}
  	}
  
  	return 0; // No handler found
  }
  
  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)
      : context(0)
  {
  	struct CivetCallbacks callbacks;
  
  	if (_callbacks) {
  		callbacks = *_callbacks;
  		userCloseHandler = _callbacks->connection_close;
  	} else {
  		userCloseHandler = NULL;
  	}
  	callbacks.connection_close = closeHandler;
  	context = mg_start(&callbacks, this, options);
  	if (context == NULL)
  		throw CivetException("null context when constructing CivetServer. "
  		                     "Possible problem binding to port.");
  }
  
  CivetServer::CivetServer(std::vector<std::string> options,
                           const struct CivetCallbacks *_callbacks)
      : context(0)
  {
  	struct CivetCallbacks callbacks;
  
  	if (_callbacks) {
  		callbacks = *_callbacks;
  		userCloseHandler = _callbacks->connection_close;
  	} else {
  		userCloseHandler = NULL;
  	}
  	callbacks.connection_close = closeHandler;
  
  	std::vector<const char *> pointers(options.size());
  	for (size_t i = 0; i < options.size(); i++) {
  		pointers[i] = (options[i].c_str());
  	}
  	pointers.push_back(0);
  
  	context = mg_start(&callbacks, this, &pointers[0]);
  	if (context == NULL)
  		throw CivetException("null context when constructing CivetServer. "
  		                     "Possible problem binding to port.");
  }
  
  CivetServer::~CivetServer()
  {
  	close();
  }
  
  void
  CivetServer::closeHandler(const struct mg_connection *conn)
  {
  	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;
  
  	if (me->userCloseHandler)
  		me->userCloseHandler(conn);
  	mg_lock_context(me->context);
  	me->connections.erase(const_cast<struct mg_connection *>(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());
  }
  
  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)
  {
  	int i, j, a, b;
  #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
  
  	dst.clear();
  	for (i = j = 0; i < (int)src_len; i++, j++) {
  		if (i < (int)src_len - 2 && src[i] == '%'
  		    && isxdigit(*(const unsigned char *)(src + i + 1))
  		    && isxdigit(*(const unsigned char *)(src + i + 2))) {
  			a = tolower(*(const unsigned char *)(src + i + 1));
  			b = tolower(*(const unsigned char *)(src + i + 2));
  			dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));
  			i += 2;
  		} else if (is_form_url_encoded && src[i] == '+') {
  			dst.push_back(' ');
  		} else {
  			dst.push_back(src[i]);
  		}
  	}
  }
  
  bool
  CivetServer::getParam(struct mg_connection *conn,
                        const char *name,
                        std::string &dst,
                        size_t occurrence)
  {
  	const char *formParams = 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_lock_connection(conn);
  	mg_unlock_context(me->context);
  
  	if (conobj.postData != NULL) {
  		formParams = conobj.postData;
  	} else {
  		const char *con_len_str = mg_get_header(conn, "Content-Length");
  		if (con_len_str) {
  			unsigned long con_len = atoi(con_len_str);
  			if (con_len > 0) {
  				// Add one extra character: in case the post-data is a text, it
  				// is required as 0-termination.
  				// Do not increment con_len, since the 0 terminating is not part
  				// of the content (text or binary).
  				conobj.postData = (char *)malloc(con_len + 1);
  				if (conobj.postData != NULL) {
  					// malloc may fail for huge requests
  					mg_read(conn, conobj.postData, con_len);
  					conobj.postData[con_len] = 0;
  					formParams = conobj.postData;
  					conobj.postDataLen = con_len;
  				}
  			}
  		}
  	}
  	if (formParams == NULL) {
  		// get requests do store html <form> field values in the http
  		// query_string
  		formParams = ri->query_string;
  	}
  	mg_unlock_connection(conn);
  
  	if (formParams != NULL) {
  		return getParam(formParams, strlen(formParams), name, dst, occurrence);
  	}
  
  	return false;
  }
  
  bool
  CivetServer::getParam(const char *data,
                        size_t data_len,
                        const char *name,
                        std::string &dst,
                        size_t occurrence)
  {
  	const char *p, *e, *s;
  	size_t name_len;
  
  	dst.clear();
  	if (data == NULL || name == NULL || data_len == 0) {
  		return false;
  	}
  	name_len = strlen(name);
  	e = data + data_len;
  
  	// data is "var1=val1&var2=val2...". Find variable first
  	for (p = data; p + name_len < e; p++) {
  		if ((p == data || p[-1] == '&') && p[name_len] == '='
  		    && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
  
  			// Point p to variable value
  			p += name_len + 1;
  
  			// Point s to the end of the value
  			s = (const char *)memchr(p, '&', (size_t)(e - p));
  			if (s == NULL) {
  				s = e;
  			}
  			assert(s >= p);
  
  			// Decode variable into destination buffer
  			urlDecode(p, (int)(s - p), dst, true);
  			return true;
  		}
  	}
  	return false;
  }
  
  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)
  {
  	static const char *dont_escape = "._-$,;~()";
  	static const char *hex = "0123456789abcdef";
  
  	if (!append)
  		dst.clear();
  
  	for (; src_len > 0; src++, src_len--) {
  		if (isalnum(*(const unsigned char *)src)
  		    || strchr(dont_escape, *(const unsigned char *)src) != NULL) {
  			dst.push_back(*src);
  		} else {
  			dst.push_back('%');
  			dst.push_back(hex[(*(const unsigned char *)src) >> 4]);
  			dst.push_back(hex[(*(const unsigned char *)src) & 0xf]);
  		}
  	}
  }
  
  std::vector<int>
  CivetServer::getListeningPorts()
  {
  	std::vector<int> ports(10);
  	std::vector<int> ssl(10);
  	size_t size = mg_get_ports(context, ports.size(), &ports[0], &ssl[0]);
  	ports.resize(size);
  	ssl.resize(size);
  	return ports;
  }
  
  CivetServer::CivetConnection::CivetConnection()
  {
  	postData = NULL;
  	postDataLen = 0;
  }
  
  CivetServer::CivetConnection::~CivetConnection()
  {
  	free(postData);
  }
  