diff --git a/src/Cedar/Admin.c b/src/Cedar/Admin.c
index 72833cd1..ded4c684 100644
--- a/src/Cedar/Admin.c
+++ b/src/Cedar/Admin.c
@@ -274,6 +274,1157 @@ CAPSLIST *ScGetCapsEx(RPC *rpc)
return t;
}
+
+
+// Process server side include
+BUF *AdminWebProcessServerSideInclude(BUF *src_txt, char *filename, UINT depth)
+{
+ char *src_str;
+ UINT src_str_size;
+ UINT i, len;
+ BUF *ret = NULL;
+ UINT pos = 0;
+ char dirname[MAX_PATH];
+ if (src_txt == NULL || filename == NULL || depth >= 4)
+ {
+ return CloneBuf(src_txt);
+ }
+ if (EndWith(filename, ".html") == false)
+ {
+ // We process only .html files
+ return CloneBuf(src_txt);
+ }
+
+ GetDirNameFromFilePath(dirname, sizeof(dirname), filename);
+
+ src_str_size = src_txt->Size + 1;
+ src_str = ZeroMalloc(src_str_size);
+
+ Copy(src_str, src_txt->Buf, src_txt->Size);
+
+ len = StrLen(src_str);
+
+ ret = NewBuf();
+
+ for (i = 0;i < len;i++)
+ {
+ char *start_tag = "";
+ UINT x;
+
+ Zero(inc_filename, sizeof(inc_filename));
+
+ StrCpy(inc_filename, sizeof(inc_filename), src_str + i + StrLen(start_tag) + 1);
+ inc_filename[b - (i + StrLen(start_tag) + 1)] = 0;
+
+ x = SearchStrEx(src_str, end_tag, b + 1, true);
+
+ if ((x != INFINITE) && (x >= (b + 1)))
+ {
+ BUF *inc_buf;
+ char full_inc_filename[MAX_PATH];
+
+ if (StartWith(inc_filename, "/"))
+ {
+ Format(full_inc_filename, sizeof(full_inc_filename), "|wwwroot/%s", inc_filename + 1);
+ }
+ else
+ {
+ StrCpy(full_inc_filename, sizeof(full_inc_filename), dirname);
+ StrCat(full_inc_filename, sizeof(full_inc_filename), "/");
+ StrCat(full_inc_filename, sizeof(full_inc_filename), inc_filename);
+ }
+
+ Debug("dirname = %s, full_inc_filename (src) = %s\n\n", dirname, full_inc_filename);
+ NormalizePath(full_inc_filename, sizeof(full_inc_filename), full_inc_filename);
+
+ if (StartWith(full_inc_filename, "|wwwroot/") == false
+ && StartWith(full_inc_filename, "|wwwroot\\") == false)
+ {
+ char tmp[MAX_PATH];
+ Format(tmp, sizeof(tmp), "|wwwroot/%s", full_inc_filename);
+ StrCpy(full_inc_filename, sizeof(full_inc_filename), tmp);
+ }
+
+ Debug("inc_filename = %s\nfull_inc_filename = %s\n\n", inc_filename, full_inc_filename);
+
+ inc_buf = ReadDump(full_inc_filename);
+
+ if (inc_buf != NULL)
+ {
+ BUF *inc_buf2;
+
+ inc_buf2 = AdminWebProcessServerSideInclude(inc_buf, full_inc_filename, depth + 1);
+
+ BufSkipUtf8Bom(inc_buf2);
+ WriteBufBufWithOffset(ret, inc_buf2);
+
+ FreeBuf(inc_buf);
+ FreeBuf(inc_buf2);
+ }
+ else
+ {
+ Debug("Loading SSI '%s' error.\n", inc_buf);
+ }
+
+ i = (x + StrLen(end_tag) - 1);
+
+ is_ssi = true;
+ }
+ }
+ }
+ }
+
+ if (is_ssi == false)
+ {
+ WriteBufChar(ret, src_str[i]);
+ }
+ }
+
+ Free(src_str);
+
+ return ret;
+}
+
+// Handle the file request
+bool AdminWebHandleFileRequest(ADMIN *a, CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_src, char *query_string, char *virtual_root_dir, char *physical_root_dir)
+{
+ bool ret = false;
+ char url[MAX_PATH];
+ UINT i, len;
+ if (a == NULL || c == NULL || s == NULL || h == NULL || url == NULL || query_string == NULL ||
+ virtual_root_dir == NULL || physical_root_dir == NULL)
+ {
+ return false;
+ }
+
+ StrCpy(url, sizeof(url), url_src);
+
+ len = StrLen(url);
+ for (i = 0;i < len;i++)
+ {
+ if (url[i] == '\\')
+ {
+ url[i] = '/';
+ }
+ }
+
+ // Is dangerous URL?
+ if (InStr(url, "..") || InStr(url, "//") || InStr(url, "\\\\") || InStr(url, "/\\") || InStr(url, "\\/"))
+ {
+ ret = AdminWebSend404Error(s, h);
+ }
+ else
+ {
+ char filename[MAX_PATH];
+ bool is_index_file = false;
+
+ BUF *b = AdminWebTryFindAndReadFile(virtual_root_dir, physical_root_dir, url,
+ filename, sizeof(filename), &is_index_file);
+
+ if (b == NULL)
+ {
+ ret = AdminWebSend404Error(s, h);
+ }
+ else
+ {
+ if (is_index_file && EndWith(url, "/") == false)
+ {
+ char url2[MAX_PATH];
+ StrCpy(url2, sizeof(url2), url);
+ StrCat(url2, sizeof(url2), "/");
+ ret = AdminWebSend302Redirect(s, url2, query_string, h);
+ }
+ else if (is_index_file == false && EndWith(url, "/"))
+ {
+ char url2[MAX_PATH];
+ TrimEndWith(url2, sizeof(url2), url, "/");
+ ret = AdminWebSend302Redirect(s, url2, query_string, h);
+ }
+ else
+ {
+ BUF *b2 = AdminWebProcessServerSideInclude(b, filename, 0);
+ char *mime = GetMimeTypeFromFileName(filename);
+
+ if (mime == NULL)
+ {
+ mime = "application/octet-stream";
+ }
+
+ ret = AdminWebSendBody(s, 200, "OK", b2->Buf, b2->Size, mime, NULL, NULL, h);
+
+ FreeBuf(b2);
+ }
+ FreeBuf(b);
+ }
+ }
+
+ return ret;
+}
+
+// Try to find a file, and if exists return the file contents
+BUF *AdminWebTryFindAndReadFile(char *vroot, char *proot, char *url, char *ret_filename, UINT ret_filename_size, bool *is_index_html)
+{
+ char tmp[MAX_PATH];
+ char tmp2[MAX_PATH];
+ UINT vroot_len;
+ UINT url_len;
+ char relative_path[MAX_PATH];
+ BUF *b;
+ if (vroot == NULL || proot == NULL || url == NULL || ret_filename == NULL || is_index_html == NULL)
+ {
+ return NULL;
+ }
+
+ *is_index_html = false;
+
+ if (StartWith(url, vroot) == false)
+ {
+ return NULL;
+ }
+
+ vroot_len = StrLen(vroot);
+ url_len = StrLen(url);
+
+ StrCpy(relative_path, sizeof(relative_path), url + vroot_len);
+
+ if (StartWith(relative_path, "/"))
+ {
+ char tmp3[MAX_PATH];
+
+ StrCpy(tmp3, sizeof(tmp3), relative_path + 1);
+ StrCpy(relative_path, sizeof(relative_path), tmp3);
+ }
+
+ CombinePath(tmp, sizeof(tmp), proot, relative_path);
+
+ // index.html
+ CombinePath(tmp2, sizeof(tmp2), tmp, "index.html");
+ b = AdminWebTryOneFile(tmp2, ret_filename, ret_filename_size);
+ if (b != NULL)
+ {
+ *is_index_html = true;
+ return b;
+ }
+
+ // dirname/filename
+ StrCpy(tmp2, sizeof(tmp2), tmp);
+ b = AdminWebTryOneFile(tmp2, ret_filename, ret_filename_size);
+ if (b != NULL)
+ {
+ return b;
+ }
+
+ return NULL;
+}
+BUF *AdminWebTryOneFile(char *filename, char *ret_filename, UINT ret_filename_size)
+{
+ BUF *b;
+ if (filename == NULL || ret_filename == NULL)
+ {
+ return NULL;
+ }
+
+ b = ReadDump(filename);
+ if (b == NULL)
+ {
+ return NULL;
+ }
+
+ StrCpy(ret_filename, ret_filename_size, filename);
+
+ return b;
+}
+
+// Send a 401 Unauthorized error
+bool AdminWebSendUnauthorized(SOCK *s, HTTP_HEADER *http_request_headers)
+{
+ char *http_401_str = "\r\n
\r\n401 Unauthorized\r\n\r\n" CEDAR_SERVER_STR ": Administrative authentication required.
\r\nThis VPN Server could not verify that you are authorized to access to the \r\nserver in administrative mode.
\r\nFor web browser logins:
You must supply the HTTP basic \r\nauthentication credential as following.
\r\n\r\n\t- To login to the VPN server as the entire server administrator, specify empty or "administrator" as the username field, and specify the server administrative \r\n\tpassword as the password field.
\r\n\t- To login to a particular Virtual Hub as the hub administrator, specify \r\n\tthe hub name as the username field, and specify the hub administrative \r\n\tpassword as the password field.
\r\n
\r\nFor JSON-RPC client logins:
Instead to HTTP basic \r\nauthentication, you can also specify the HTTP header parameters as following.
\r\n\r\n\t- X-VPNADMIN-HUBNAME: Empty to login to the VPN Server as the entire \r\n\tserver administrator, or specify the target Virtual Hub name as the hub \r\n\tadministrator.
\r\n\t- X-VPNADMIN-PASSWORD: Specify the administrative password.
\r\n
\r\n\r\n";
+ bool ret;
+ // Validate arguments
+ if (s == NULL || http_request_headers == NULL)
+ {
+ return false;
+ }
+
+ // Creating a Data
+ ret = AdminWebSendBody(s, 401, "Unauthorized", http_401_str, StrLen(http_401_str), HTTP_CONTENT_TYPE,
+ "WWW-Authenticate",
+ "Basic realm=\"Username 'administrator' for entire VPN Server privilege, or specify Virtual Hub name as the username for specified Virtual Hub administrative privilege.\"",
+ http_request_headers);
+
+ return ret;
+}
+
+// Send reply
+bool AdminWebSendBody(SOCK *s, UINT status_code, char *status_string, UCHAR *data, UINT data_size, char *content_type, char *add_header_name, char *add_header_value,
+ HTTP_HEADER *request_headers)
+{
+ HTTP_HEADER *h;
+ char date_str[MAX_SIZE];
+ char error_code_str[16];
+ bool ret = false;
+ HTTP_VALUE *origin;
+ if (s == NULL || status_string == NULL || (data_size != 0 && data == NULL) || request_headers == NULL)
+ {
+ return false;
+ }
+ if (content_type == NULL)
+ {
+ content_type = "text/html; charset=utf-8";
+ }
+
+ ToStr(error_code_str, status_code);
+ GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+
+ h = NewHttpHeader("HTTP/1.1", error_code_str, status_string);
+
+ if (StrCmpi(request_headers->Method, "OPTIONS") == 0)
+ {
+ AddHttpValue(h, NewHttpValue("Allow", "OPTIONS, GET, POST"));
+ }
+
+ AddHttpValue(h, NewHttpValue("Cache-Control", "no-cache"));
+ AddHttpValue(h, NewHttpValue("Content-Type", content_type));
+ AddHttpValue(h, NewHttpValue("Date", date_str));
+ AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
+ AddHttpValue(h, NewHttpValue("Access-Control-Allow-Methods", "OPTIONS,GET,POST"));
+ AddHttpValue(h, NewHttpValue("Access-Control-Allow-Headers", "X-VPNADMIN-HUBNAME,X-VPNADMIN-PASSWORD"));
+ AddHttpValue(h, NewHttpValue("Access-Control-Allow-Credentials", "true"));
+
+ origin = GetHttpValue(request_headers, "Origin");
+ if (origin != NULL)
+ {
+ AddHttpValue(h, NewHttpValue("Access-Control-Allow-Origin", origin->Data));
+ }
+
+ if (add_header_name != NULL && add_header_value != NULL)
+ {
+ AddHttpValue(h, NewHttpValue(add_header_name, add_header_value));
+ }
+
+ ret = PostHttp(s, h, data, data_size);
+
+ FreeHttpHeader(h);
+
+ return ret;
+}
+
+// Send 404 error
+bool AdminWebSend404Error(SOCK *s, HTTP_HEADER *request_headers)
+{
+ char *body = "\r\n404 Not FoundNot Found
The requested URL was not found on this server.
\r\n";
+ if (s == NULL || request_headers == NULL)
+ {
+ return false;
+ }
+
+ return AdminWebSendBody(s, 404, "Not Found", body, StrLen(body), NULL, NULL, NULL, request_headers);
+}
+
+// Send 302 redirect
+bool AdminWebSend302Redirect(SOCK *s, char *url, char *query_string, HTTP_HEADER *request_headers)
+{
+ bool ret = false;
+ char *txt;
+ UINT txt_size;
+ char *url2;
+ UINT url2_size;
+ char *body = "Object moved\r\nObject moved to here.
\r\n";
+ if (s == NULL || url == NULL || request_headers == NULL)
+ {
+ return false;
+ }
+
+ url2_size = (StrSize(url) + StrSize(query_string) + MAX_SIZE) * 2;
+ url2 = ZeroMalloc(url2_size);
+
+ StrCpy(url2, url2_size, url);
+ if (IsEmptyStr(query_string) == false)
+ {
+ StrCat(url2, url2_size, "?");
+ StrCat(url2, url2_size, query_string);
+ }
+
+ txt_size = (StrSize(body) + StrSize(url2) + MAX_SIZE) * 2;
+ txt = ZeroMalloc(txt_size);
+
+ ReplaceStrEx(txt, txt_size, body, "$URL$", url2, false);
+
+ ret = AdminWebSendBody(s, 302, "Found", txt, StrLen(txt), NULL, "Location", url2, request_headers);
+
+ Free(txt);
+
+ Free(url2);
+
+ return ret;
+}
+
+// "/admin" web page POST handler
+void AdminWebProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_size, char *url_target)
+{
+ ADMIN *a;
+ UCHAR *data;
+ char url[MAX_PATH];
+ char query_string[MAX_SIZE];
+ UINT i;
+ if (c == NULL || s == NULL || h == NULL || url_target == NULL)
+ {
+ return;
+ }
+
+ a = JsonRpcAuthLogin(c->Cedar, s, h);
+ if (a == NULL)
+ {
+ RecvAllWithDiscard(s, post_data_size, s->SecureMode);
+ AdminWebSendUnauthorized(s, h);
+ return;
+ }
+
+ if (post_data_size > a->MaxJsonRpcRecvSize)
+ {
+ Disconnect(s);
+ return;
+ }
+
+ data = ZeroMalloc(post_data_size + 1);
+
+ if (RecvAll(s, data, post_data_size, s->SecureMode))
+ {
+ c->JsonRpcAuthed = true;
+#ifndef GC_SOFTETHER_OSS
+ RemoveDosEntry(c->Listener, s);
+#endif // GC_SOFTETHER_OSS
+
+ // Divide url_target into URL and query string
+ StrCpy(url, sizeof(url), url_target);
+ Zero(query_string, sizeof(query_string));
+ i = SearchStr(url, "?", 0);
+ if (i != INFINITE)
+ {
+ StrCpy(query_string, sizeof(query_string), url + i + 1);
+ url[i] = 0;
+ }
+
+ AdminWebHandleFileRequest(a, c, s, h, url, query_string, "/admin", "|wwwroot/admin");
+ }
+
+ Free(data);
+ Free(a);
+}
+
+// "/admin" web page GET handler
+void AdminWebProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target)
+{
+ ADMIN *a;
+ char url[MAX_PATH];
+ char query_string[MAX_SIZE];
+ UINT i;
+ if (c == NULL || s == NULL || h == NULL || url_target == NULL)
+ {
+ return;
+ }
+
+ a = JsonRpcAuthLogin(c->Cedar, s, h);
+ if (a == NULL)
+ {
+ AdminWebSendUnauthorized(s, h);
+ return;
+ }
+
+ c->JsonRpcAuthed = true;
+#ifndef GC_SOFTETHER_OSS
+ RemoveDosEntry(c->Listener, s);
+#endif // GC_SOFTETHER_OSS
+
+ // Divide url_target into URL and query string
+ StrCpy(url, sizeof(url), url_target);
+ Zero(query_string, sizeof(query_string));
+ i = SearchStr(url, "?", 0);
+ if (i != INFINITE)
+ {
+ StrCpy(query_string, sizeof(query_string), url + i + 1);
+ url[i] = 0;
+ }
+
+ AdminWebHandleFileRequest(a, c, s, h, url, query_string, "/admin", "|wwwroot/admin");
+
+ Free(a);
+}
+
+// New JSON-RPC Result
+JSON_VALUE *JsonRpcNewResponse(PACK *p)
+{
+ JSON_VALUE *jv;
+ JSON_OBJECT *jo;
+ JSON_VALUE *jv2;
+
+ if (p == NULL)
+ {
+ return NULL;
+ }
+
+ jv = JsonNewObject();
+ jo = JsonValueGetObject(jv);
+
+ jv2 = PackToJson(p);
+
+ JsonSet(jo, "result", jv2);
+
+ return jv;
+}
+
+// New JSON-RPC Error
+JSON_VALUE *JsonRpcNewError(int code, wchar_t *message)
+{
+ wchar_t msg[MAX_PATH];
+ JSON_VALUE *jv;
+ JSON_OBJECT *jo;
+ JSON_VALUE *jv2;
+ JSON_OBJECT *jo2;
+
+ if (UniIsEmptyStr(message))
+ {
+ UniFormat(msg, sizeof(msg), L"Error code %u", code);
+ }
+ else
+ {
+ UniFormat(msg, sizeof(msg), L"Error code %u: %s", code, message);
+ }
+
+ jv = JsonNewObject();
+ jo = JsonValueGetObject(jv);
+
+ jv2 = JsonNewObject();
+ jo2 = JsonValueGetObject(jv2);
+
+ JsonSet(jo, "error", jv2);
+
+ JsonSetNumber(jo2, "code", (UINT64)code);
+ JsonSetUniStr(jo2, "message", msg);
+
+ return jv;
+}
+
+// JSON-RPC process request object
+JSON_VALUE *JsonRpcProcRequestObject(ADMIN *admin, CONNECTION *c, SOCK *s, JSON_VALUE *json_req, char *method_name)
+{
+ PACK *pack_request;
+ JSON_VALUE *ret = NULL;
+ if (c == NULL || s == NULL || json_req == NULL || admin == NULL)
+ {
+ return NULL;
+ }
+
+ pack_request = JsonToPack(json_req);
+
+ PackAddStr(pack_request, "function_name", method_name);
+
+ if (pack_request != NULL)
+ {
+ RPC *rpc;
+ PACK *pack_response;
+ UINT err;
+
+ // RPC Server
+ rpc = StartRpcServer(s, AdminDispatch, admin);
+
+ admin->Rpc = rpc;
+
+ pack_response = CallRpcDispatcher(rpc, pack_request);
+
+ if (pack_response == NULL)
+ {
+ pack_response = PackError(ERR_NOT_SUPPORTED);
+ }
+
+ RpcFreeEx(rpc, true);
+
+ FreePack(pack_request);
+
+ // Construct response object
+ err = GetErrorFromPack(pack_response);
+ if (err != 0)
+ {
+ // Return the error
+ ret = JsonRpcNewError(err, _E(err));
+ }
+ else
+ {
+ // Return the PACK
+ ret = JsonRpcNewResponse(pack_response);
+ }
+
+ SLog(admin->Server->Cedar, "LS_API_RPC_CALL",
+ &s->RemoteIP, s->RemotePort, s->RemoteHostname,
+ method_name, err, _E(err));
+
+ FreePack(pack_response);
+ }
+
+ return ret;
+}
+
+// JSON-RPC HTTP user authentication
+bool HttpParseBasicAuthHeader(HTTP_HEADER *h, char *username, UINT username_size, char *password, UINT password_size)
+{
+ bool ret = false;
+ HTTP_VALUE *auth_value;
+ HTTP_VALUE *vpnadmin_hubname;
+ HTTP_VALUE *vpnadmin_password;
+ if (h == NULL || username == NULL || password == NULL)
+ {
+ return false;
+ }
+
+ auth_value = GetHttpValue(h, "Authorization");
+ vpnadmin_hubname = GetHttpValue(h, "X-VPNADMIN-HUBNAME");
+ vpnadmin_password = GetHttpValue(h, "X-VPNADMIN-PASSWORD");
+
+ if (vpnadmin_password != NULL)
+ {
+ if (vpnadmin_hubname == NULL)
+ {
+ StrCpy(username, username_size, "");
+ }
+ else
+ {
+ StrCpy(username, username_size, vpnadmin_hubname->Data);
+ }
+
+ StrCpy(password, password_size, vpnadmin_password->Data);
+
+ ret = true;
+ }
+
+ if (ret == false && auth_value != NULL)
+ {
+ char key[32], value[MAX_SIZE];
+
+ if (GetKeyAndValue(auth_value->Data, key, sizeof(key), value, sizeof(value), " \t"))
+ {
+ if (StrCmpi(key, "Basic") == 0 && IsEmptyStr(value) == false)
+ {
+ UINT b64_dest_size = StrSize(value) * 2 + 256;
+ char *b64_dest = ZeroMalloc(b64_dest_size);
+
+ Decode64(b64_dest, value);
+
+ if (IsEmptyStr(b64_dest) == false)
+ {
+ if (b64_dest[0] == ':')
+ {
+ // Empty username
+ StrCpy(username, username_size, "");
+ StrCpy(password, password_size, b64_dest + 1);
+ ret = true;
+ }
+ else
+ {
+ if (GetKeyAndValue(b64_dest, username, username_size, password, password_size, ":"))
+ {
+ ret = true;
+ }
+ }
+ }
+
+ Free(b64_dest);
+ }
+ }
+ }
+
+ return ret;
+}
+
+// JSON-RPC Login
+ADMIN *JsonRpcAuthLogin(CEDAR *c, SOCK *sock, HTTP_HEADER *h)
+{
+ ADMIN *a = NULL;
+ char username[MAX_HUBNAME_LEN + 1];
+ char password[MAX_PASSWORD_LEN + 1];
+ SERVER *s;
+ char empty_pw_hash[SHA1_SIZE];
+
+ if (c == NULL || h == NULL || sock == NULL)
+ {
+ return NULL;
+ }
+
+ s = c->Server;
+
+ HashAdminPassword(empty_pw_hash, "");
+
+ Zero(username, sizeof(username));
+ Zero(password, sizeof(password));
+
+ if (HttpParseBasicAuthHeader(h, username, sizeof(username), password, sizeof(password)))
+ {
+ char pw_hash[SHA1_SIZE];
+ bool is_server_admin = false;
+ bool is_hub_admin = false;
+ char hub_name[MAX_HUBNAME_LEN + 1];
+
+ HashAdminPassword(pw_hash, password);
+
+ Zero(hub_name, sizeof(hub_name));
+
+ // Check if the server administrator password is empty. If yes, login always success.
+ if (Cmp(s->HashedPassword, empty_pw_hash, SHA1_SIZE) == 0)
+ {
+ is_server_admin = true;
+ }
+ else
+ {
+ if (IsEmptyStr(username) || StrCmpi(username, ADMINISTRATOR_USERNAME) == 0)
+ {
+ // If the username is empty or 'administrator', verify with the server admin password.
+ if (Cmp(s->HashedPassword, pw_hash, SHA1_SIZE) == 0)
+ {
+ is_server_admin = true;
+ }
+ }
+ }
+
+ if (is_server_admin == false)
+ {
+ HUB *h;
+ // Hub admin mode
+ LockHubList(c);
+ {
+ h = GetHub(c, username);
+ }
+ UnlockHubList(c);
+
+ if (h != NULL)
+ {
+ Lock(h->lock);
+ {
+ if (Cmp(pw_hash, h->HashedPassword, SHA1_SIZE) == 0)
+ {
+ is_hub_admin = true;
+
+ StrCpy(hub_name, sizeof(hub_name), h->Name);
+ }
+ }
+ Unlock(h->lock);
+
+ ReleaseHub(h);
+ }
+ }
+
+ if (is_server_admin || is_hub_admin)
+ {
+ if (CheckAdminSourceAddress(sock, hub_name))
+ {
+ a = ZeroMalloc(sizeof(ADMIN));
+
+ a->Server = s;
+ a->ServerAdmin = is_server_admin;
+ a->ClientBuild = c->Build;
+
+ if (is_hub_admin)
+ {
+ StrCpy(a->dummy1, sizeof(a->dummy1), hub_name);
+ a->HubName = a->dummy1;
+ }
+ }
+ }
+ }
+
+ if (a != NULL)
+ {
+ char admin_mode[256];
+ if (a->ServerAdmin)
+ {
+ a->MaxJsonRpcRecvSize = ADMIN_RPC_MAX_POST_SIZE_BY_SERVER_ADMIN;
+ }
+ else
+ {
+ a->MaxJsonRpcRecvSize = ADMIN_RPC_MAX_POST_SIZE_BY_HUB_ADMIN;
+ }
+
+ if (IsEmptyStr(a->HubName))
+ {
+ StrCpy(admin_mode, sizeof(admin_mode),
+ "Entire VPN Server Admin Mode");
+ }
+ else
+ {
+ Format(admin_mode, sizeof(admin_mode),
+ "Virtual Hub Admin Mode for '%s'",
+ a->HubName);
+ }
+
+ SLog(s->Cedar, "LS_API_AUTH_OK",
+ &sock->RemoteIP, sock->RemotePort, sock->RemoteHostname,
+ admin_mode, username, h->Method, h->Target);
+ }
+ else
+ {
+ SLog(s->Cedar, "LS_API_AUTH_ERROR",
+ &sock->RemoteIP, sock->RemotePort, sock->RemoteHostname,
+ username, h->Method, h->Target);
+ }
+
+
+ return a;
+}
+
+// Query string to JSON list value
+JSON_VALUE *QueryStringToJsonListValue(char *qs)
+{
+ TOKEN_LIST *t;
+ UINT i;
+ LIST *distinct_list = NULL;
+ JSON_VALUE *v = NULL;
+ JSON_OBJECT *o = NULL;
+ if (qs == NULL)
+ {
+ return NULL;
+ }
+
+ t = ParseTokenWithoutNullStr(qs, "&");
+ if (t == NULL)
+ {
+ return NULL;
+ }
+
+ distinct_list = NewStrList();
+
+ v = JsonNewObject();
+ o = JsonValueGetObject(v);
+
+ for (i = 0;i < t->NumTokens;i++)
+ {
+ char *token = t->Token[i];
+ UINT pos;
+
+ pos = SearchStr(token, "=", 0);
+ if (pos != INFINITE)
+ {
+ char *key_decoded;
+ char *value_decoded;
+ char *key = CopyStr(token);
+ char *value = CopyStr(token + pos + 1);
+
+ key[pos] = 0;
+ key_decoded = UrlDecode(key);
+ value_decoded = UrlDecode(value);
+
+ if (key_decoded != NULL && value_decoded != NULL)
+ {
+ if (AddStrToStrListDistinct(distinct_list, key_decoded))
+ {
+ JsonSetStr(o, key_decoded, value_decoded);
+ }
+ }
+
+ Free(value_decoded);
+ Free(key_decoded);
+ Free(key);
+ Free(value);
+ }
+ }
+
+ FreeToken(t);
+
+ FreeStrList(distinct_list);
+
+ return v;
+}
+
+// Construct new JSON-RPC dummy request
+JSON_VALUE *ConstructDummyJsonRpcRequest(char *method_name, JSON_VALUE *p)
+{
+ JSON_VALUE *ret;
+ JSON_OBJECT *ret_object;
+ UCHAR rand[16];
+ char id_str[64];
+
+ Rand(rand, sizeof(rand));
+
+ BinToStr(id_str, sizeof(id_str), rand, sizeof(rand));
+
+ ret = JsonNewObject();
+ ret_object = JsonObject(ret);
+
+ JsonSetStr(ret_object, "jsonrpc", "2.0");
+ JsonSetStr(ret_object, "method", method_name);
+ JsonSet(ret_object, "params", p);
+ JsonSetStr(ret_object, "id", id_str);
+
+ return ret;
+}
+
+// JSON-RPC Options Dispatch
+void JsonRpcProcOptions(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target)
+{
+ if (c == NULL || s == NULL || h == NULL || url_target == NULL)
+ {
+ return;
+ }
+
+ c->JsonRpcAuthed = true;
+
+#ifndef GC_SOFTETHER_OSS
+ RemoveDosEntry(c->Listener, s);
+#endif // GC_SOFTETHER_OSS
+
+ AdminWebSendBody(s, 200, "OK", NULL, 0, NULL, NULL, NULL, h);
+}
+
+// JSON-RPC GET Dispatch
+void JsonRpcProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target)
+{
+ ADMIN *a;
+ char url[MAX_PATH];
+ char query_string[MAX_SIZE];
+ UINT i;
+ bool reply_sent = false;
+ if (c == NULL || s == NULL || h == NULL || url_target == NULL)
+ {
+ return;
+ }
+
+ a = JsonRpcAuthLogin(c->Cedar, s, h);
+ if (a == NULL)
+ {
+ AdminWebSendUnauthorized(s, h);
+ return;
+ }
+
+ c->JsonRpcAuthed = true;
+
+#ifndef GC_SOFTETHER_OSS
+ RemoveDosEntry(c->Listener, s);
+#endif // GC_SOFTETHER_OSS
+
+ // Divide url_target into URL and query string
+ StrCpy(url, sizeof(url), url_target);
+ Zero(query_string, sizeof(query_string));
+ i = SearchStr(url, "?", 0);
+ if (i != INFINITE)
+ {
+ StrCpy(query_string, sizeof(query_string), url + i + 1);
+ url[i] = 0;
+ }
+
+ if (StartWith(url, "/api/"))
+ {
+ // Call a method
+ JSON_VALUE *params_value = NULL;
+ JSON_OBJECT *params_object = NULL;
+ UINT i;
+ char method_name[MAX_PATH];
+
+ StrCpy(method_name, sizeof(method_name), url + 5);
+
+ i = SearchStr(method_name, "/", 0);
+ if (i != INFINITE)
+ {
+ method_name[i] = 0;
+ }
+
+ if (IsEmptyStr(method_name) == false)
+ {
+ // Call a method
+ params_value = QueryStringToJsonListValue(query_string);
+
+ if (params_value != NULL)
+ {
+ JSON_VALUE *json_ret = NULL;
+ char id[96];
+ char *ret_str = NULL;
+
+ GetDateTimeStrMilli64(id, sizeof(id), LocalTime64());
+
+ params_object = JsonObject(params_value);
+
+ // Process the request
+ json_ret = JsonRpcProcRequestObject(a, c, s, params_value, method_name);
+
+ if (json_ret == NULL)
+ {
+ json_ret = JsonRpcNewError(ERR_INTERNAL_ERROR, L"Internal error");
+ }
+
+ JsonSetStr(JsonObject(json_ret), "jsonrpc", "2.0");
+ JsonSetStr(JsonObject(json_ret), "id", id);
+
+ ret_str = JsonToStr(json_ret);
+
+ AdminWebSendBody(s, 200, "OK", ret_str, StrLen(ret_str), "text/plain; charset=UTF-8", NULL, NULL, h);
+
+ Free(ret_str);
+ JsonFree(json_ret);
+ JsonFree(params_value);
+ }
+ }
+ }
+
+
+ if (reply_sent == false)
+ {
+ BUF *html_buf = ReadDump("|vpnserver_api_doc.html");
+
+ if (html_buf != NULL)
+ {
+ AdminWebSendBody(s, 200, "OK", html_buf->Buf, html_buf->Size, "text/html; charset=UTF-8", NULL, NULL, h);
+
+ FreeBuf(html_buf);
+ }
+ else
+ {
+ AdminWebSend404Error(s, h);
+ }
+ }
+
+ if (a->LogFileList != NULL)
+ {
+ FreeEnumLogFile(a->LogFileList);
+ }
+ Free(a);
+}
+
+// JSON-RPC POST Dispatch
+void JsonRpcProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_size)
+{
+ ADMIN *a;
+ UCHAR *data;
+ if (c == NULL || s == NULL || h == NULL)
+ {
+ return;
+ }
+
+ a = JsonRpcAuthLogin(c->Cedar, s, h);
+ if (a == NULL)
+ {
+ RecvAllWithDiscard(s, post_data_size, s->SecureMode);
+ AdminWebSendUnauthorized(s, h);
+ return;
+ }
+
+ if (post_data_size > a->MaxJsonRpcRecvSize)
+ {
+ Disconnect(s);
+ return;
+ }
+
+ data = ZeroMalloc(post_data_size + 1);
+
+ if (RecvAll(s, data, post_data_size, s->SecureMode))
+ {
+ // Parse JSON
+ JSON_VALUE *json_req = StrToJson(data);
+ JSON_OBJECT *json_req_object = JsonObject(json_req);
+ JSON_VALUE *json_ret = NULL;
+ char *res = NULL;
+ char *request_id = NULL;
+ char *method_name = NULL;
+
+ c->JsonRpcAuthed = true;
+
+#ifndef GC_SOFTETHER_OSS
+ RemoveDosEntry(c->Listener, s);
+#endif // GC_SOFTETHER_OSS
+
+ if (json_req == NULL || json_req_object == NULL)
+ {
+ // Parse error
+ json_ret = JsonRpcNewError(ERR_INVALID_PARAMETER, L"Parameter is invalid: JSON-RPC Parse Error");
+ }
+ else
+ {
+ // check the JSON-RPC version
+ char *ver_str = JsonGetStr(json_req_object, "jsonrpc");
+ if (StrCmpi(ver_str, "2.0") != 0)
+ {
+ // Invalid version
+ json_ret = JsonRpcNewError(ERR_INVALID_PARAMETER, L"JSON-RPC version is invalid");
+ }
+ else
+ {
+ JSON_VALUE *params_value = NULL;
+ JSON_OBJECT *params_object = NULL;
+
+ // Get Request ID
+ request_id = JsonGetStr(json_req_object, "id");
+
+ // Get method name
+ method_name = JsonGetStr(json_req_object, "method");
+
+ // Get parameters
+ params_value = JsonGet(json_req_object, "params");
+ params_object = JsonObject(params_value);
+
+ if (IsEmptyStr(method_name))
+ {
+ // method is empty
+ json_ret = JsonRpcNewError(ERR_INVALID_PARAMETER, L"JSON-RPC method name is empty");
+ }
+ else if (params_value == NULL || params_object == NULL)
+ {
+ // params is empty
+ json_ret = JsonRpcNewError(ERR_INVALID_PARAMETER, L"JSON-RPC parameter is empty");
+ }
+ else
+ {
+ // Process the request
+ json_ret = JsonRpcProcRequestObject(a, c, s, params_value, method_name);
+ }
+ }
+ }
+
+ if (json_ret == NULL)
+ {
+ json_ret = JsonRpcNewError(ERR_INTERNAL_ERROR, L"Internal error");
+ }
+
+ JsonSetStr(JsonObject(json_ret), "jsonrpc", "2.0");
+ if (request_id == NULL)
+ {
+ request_id = "0";
+ }
+ JsonSetStr(JsonObject(json_ret), "id", request_id);
+
+ res = JsonToStr(json_ret);
+
+ AdminWebSendBody(s, 200, "OK", res, StrLen(res), "application/json", NULL, NULL, h);
+
+ Free(res);
+
+ JsonFree(json_ret);
+ JsonFree(json_req);
+ }
+
+ Free(data);
+
+ if (a->LogFileList != NULL)
+ {
+ FreeEnumLogFile(a->LogFileList);
+ }
+ Free(a);
+}
+
// Dispatch routine for Administration RPC
PACK *AdminDispatch(RPC *rpc, char *name, PACK *p)
{
@@ -1794,8 +2945,20 @@ UINT StReadLogFile(ADMIN *a, RPC_READ_LOG_FILE *t)
// Check the permission to read the log file
if (a->LogFileList == NULL)
{
- // Cache not found
- return ERR_OBJECT_NOT_FOUND;
+ // Enum the log files first
+ RPC_ENUM_LOG_FILE elf;
+ UINT elf_ret;
+
+ Zero(&elf, sizeof(elf));
+
+ elf_ret = StEnumLogFile(a, &elf);
+
+ FreeRpcEnumLogFile(&elf);
+
+ if (elf_ret != ERR_NO_ERROR)
+ {
+ return elf_ret;
+ }
}
if (CheckLogFileNameFromEnumList(a->LogFileList, logfilename, servername) == false)
{
@@ -1860,6 +3023,10 @@ UINT StReadLogFile(ADMIN *a, RPC_READ_LOG_FILE *t)
ALog(a, NULL, "LA_READ_LOG_FILE", servername, logfilename);
}
+ StrCpy(t->FilePath, sizeof(t->FilePath), logfilename);
+ StrCpy(t->ServerName, sizeof(t->ServerName), servername);
+ t->Offset = offset;
+
return ERR_NO_ERROR;
}
@@ -2150,7 +3317,7 @@ UINT StSetCrl(ADMIN *a, RPC_CRL *t)
if (crl == NULL)
{
- ret = ERR_INTERNAL_ERROR;
+ ret = ERR_OBJECT_NOT_FOUND;
}
else
{
@@ -2219,7 +3386,7 @@ UINT StGetCrl(ADMIN *a, RPC_CRL *t)
if (crl == NULL)
{
- ret = ERR_INTERNAL_ERROR;
+ ret = ERR_OBJECT_NOT_FOUND;
}
else
{
@@ -2279,7 +3446,7 @@ UINT StDelCrl(ADMIN *a, RPC_CRL *t)
if (crl == NULL)
{
- ret = ERR_INTERNAL_ERROR;
+ ret = ERR_OBJECT_NOT_FOUND;
}
else
{
@@ -3176,6 +4343,7 @@ UINT StGetHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
StrCpy(e->Name, sizeof(e->Name), a->Name);
e->Value = a->Value;
+ UniStrCpy(e->Descrption, sizeof(e->Descrption), GetHubAdminOptionHelpString(e->Name));
}
}
UnlockList(h->AdminOptionList);
@@ -3208,6 +4376,7 @@ UINT StGetDefaultHubAdminOptions(ADMIN *a, RPC_ADMIN_OPTION *t)
StrCpy(a->Name, sizeof(a->Name), admin_options[i].Name);
a->Value = admin_options[i].Value;
+ UniStrCpy(a->Descrption, sizeof(a->Descrption), GetHubAdminOptionHelpString(a->Name));
}
return ERR_NO_ERROR;
@@ -3712,6 +4881,7 @@ UINT SiEnumIpTable(SERVER *s, char *hubname, RPC_ENUM_IP_TABLE *t)
StrCpy(e->SessionName, sizeof(e->SessionName), table->Session->Name);
e->Ip = IPToUINT(&table->Ip);
Copy(&e->IpV6, &table->Ip, sizeof(IP));
+ Copy(&e->IpAddress, &table->Ip, sizeof(IP));
e->DhcpAllocated = table->DhcpAllocated;
e->CreatedTime = TickToTime(table->CreatedTime);
e->UpdatedTime = TickToTime(table->UpdatedTime);
@@ -4163,6 +5333,8 @@ UINT StGetSessionStatus(ADMIN *a, RPC_SESSION_STATUS *t)
Copy(&t->ClientIp6, &s->Connection->ClientIp.ipv6_addr, sizeof(t->ClientIp6));
}
+ CopyIP(&t->ClientIpAddress, &s->Connection->ClientIp);
+
StrCpy(t->ClientHostName, sizeof(t->ClientHostName), s->Connection->ClientHostname);
}
}
@@ -5324,31 +6496,34 @@ UINT StSetAccessList(ADMIN *a, RPC_ENUM_ACCESS_LIST *t)
{
UINT i;
- // Confirm whether the access list of form which cannot handle by the old client already exists
- if (a->ClientBuild < 6560)
+ if (a->ClientBuild != 0)
{
- for (i = 0;i < LIST_NUM(h->AccessList);i++)
+ // Confirm whether the access list of form which cannot handle by the old client already exists
+ if (a->ClientBuild < 6560)
{
- ACCESS *access = LIST_DATA(h->AccessList, i);
- if (access->IsIPv6 ||
- access->Jitter != 0 || access->Loss != 0 || access->Delay != 0)
+ for (i = 0;i < LIST_NUM(h->AccessList);i++)
{
- ret = ERR_VERSION_INVALID;
- break;
+ ACCESS *access = LIST_DATA(h->AccessList, i);
+ if (access->IsIPv6 ||
+ access->Jitter != 0 || access->Loss != 0 || access->Delay != 0)
+ {
+ ret = ERR_VERSION_INVALID;
+ break;
+ }
}
}
- }
- if (a->ClientBuild < 8234)
- {
- for (i = 0;i < LIST_NUM(h->AccessList);i++)
+ if (a->ClientBuild < 8234)
{
- ACCESS *access = LIST_DATA(h->AccessList, i);
-
- if (IsEmptyStr(access->RedirectUrl) == false)
+ for (i = 0;i < LIST_NUM(h->AccessList);i++)
{
- ret = ERR_VERSION_INVALID;
- break;
+ ACCESS *access = LIST_DATA(h->AccessList, i);
+
+ if (IsEmptyStr(access->RedirectUrl) == false)
+ {
+ ret = ERR_VERSION_INVALID;
+ break;
+ }
}
}
}
@@ -6449,6 +7624,7 @@ UINT StGetCa(ADMIN *a, RPC_HUB_GET_CA *t)
FreeRpcHubGetCa(t);
Zero(t, sizeof(RPC_HUB_GET_CA));
+ t->Key = key;
StrCpy(t->HubName, sizeof(t->HubName), hubname);
CHECK_RIGHT;
@@ -7044,6 +8220,7 @@ UINT StGetSecureNATStatus(ADMIN *a, RPC_NAT_STATUS *t)
ReleaseHub(h);
+ StrCpy(t->HubName, sizeof(t->HubName), hubname);
ret = ERR_NO_ERROR;
return ret;
@@ -7895,6 +9072,13 @@ UINT StSetHub(ADMIN *a, RPC_CREATE_HUB *t)
return ERR_NOT_SUPPORTED;
}
+ // For JSON-RPC
+ if (StrLen(t->AdminPasswordPlainText) != 0)
+ {
+ Sha0(t->HashedPassword, t->AdminPasswordPlainText, StrLen(t->AdminPasswordPlainText));
+ HashPassword(t->SecurePassword, ADMINISTRATOR_USERNAME, t->AdminPasswordPlainText);
+ }
+
if (IsZero(t->HashedPassword, sizeof(t->HashedPassword)) == false &&
IsZero(t->SecurePassword, sizeof(t->SecurePassword)) == false)
{
@@ -8065,6 +9249,15 @@ UINT StCreateHub(ADMIN *a, RPC_CREATE_HUB *t)
ALog(a, NULL, "LA_CREATE_HUB", t->HubName);
+ // For JSON-RPC
+ if ((IsZero(t->HashedPassword, sizeof(t->HashedPassword)) &&
+ IsZero(t->SecurePassword, sizeof(t->SecurePassword))) ||
+ StrLen(t->AdminPasswordPlainText) != 0)
+ {
+ Sha0(t->HashedPassword, t->AdminPasswordPlainText, StrLen(t->AdminPasswordPlainText));
+ HashPassword(t->SecurePassword, ADMINISTRATOR_USERNAME, t->AdminPasswordPlainText);
+ }
+
h = NewHub(c, t->HubName, &o);
Copy(h->HashedPassword, t->HashedPassword, SHA1_SIZE);
Copy(h->SecurePassword, t->SecurePassword, SHA1_SIZE);
@@ -8518,6 +9711,11 @@ UINT StSetServerPassword(ADMIN *a, RPC_SET_PASSWORD *t)
{
SERVER_ADMIN_ONLY;
+ if (IsZero(t->HashedPassword, sizeof(t->HashedPassword)))
+ {
+ // For JSON-RPC
+ HashAdminPassword(t->HashedPassword, t->PlainTextPassword);
+ }
Copy(a->Server->HashedPassword, t->HashedPassword, SHA1_SIZE);
@@ -8887,6 +10085,8 @@ void InDDnsClientStatus(DDNS_CLIENT_STATUS *t, PACK *p)
PackGetStr(p, "DnsSuffix", t->DnsSuffix, sizeof(t->DnsSuffix));
PackGetStr(p, "CurrentIPv4", t->CurrentIPv4, sizeof(t->CurrentIPv4));
PackGetStr(p, "CurrentIPv6", t->CurrentIPv6, sizeof(t->CurrentIPv6));
+ PackGetUniStr(p, "ErrStr_IPv4", t->ErrStr_IPv4, sizeof(t->ErrStr_IPv4));
+ PackGetUniStr(p, "ErrStr_IPv6", t->ErrStr_IPv6, sizeof(t->ErrStr_IPv6));
}
void OutDDnsClientStatus(PACK *p, DDNS_CLIENT_STATUS *t)
{
@@ -8903,6 +10103,8 @@ void OutDDnsClientStatus(PACK *p, DDNS_CLIENT_STATUS *t)
PackAddStr(p, "DnsSuffix", t->DnsSuffix);
PackAddStr(p, "CurrentIPv4", t->CurrentIPv4);
PackAddStr(p, "CurrentIPv6", t->CurrentIPv6);
+ PackAddUniStr(p, "ErrStr_IPv4", t->ErrStr_IPv4);
+ PackAddUniStr(p, "ErrStr_IPv6", t->ErrStr_IPv6);
}
// INTERNET_SETTING
@@ -9056,6 +10258,7 @@ void OutRpcEnumEtherIpId(PACK *p, RPC_ENUM_ETHERIP_ID *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "Settings");
for (i = 0;i < t->NumItem;i++)
{
ETHERIP_ID *e = &t->IdList[i];
@@ -9065,6 +10268,7 @@ void OutRpcEnumEtherIpId(PACK *p, RPC_ENUM_ETHERIP_ID *t)
PackAddStrEx(p, "UserName", e->UserName, i, t->NumItem);
PackAddStrEx(p, "Password", e->Password, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumEtherIpId(RPC_ENUM_ETHERIP_ID *t)
{
@@ -9235,6 +10439,7 @@ void OutRpcEnumEthVLan(PACK *p, RPC_ENUM_ETH_VLAN *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "Devices");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_ETH_VLAN_ITEM *e = &t->Items[i];
@@ -9247,6 +10452,7 @@ void OutRpcEnumEthVLan(PACK *p, RPC_ENUM_ETH_VLAN *t)
PackAddBoolEx(p, "Support", e->Support, i, t->NumItem);
PackAddBoolEx(p, "Enabled", e->Enabled, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumEthVLan(RPC_ENUM_ETH_VLAN *t)
{
@@ -9294,6 +10500,7 @@ void OutRpcEnumLogFile(PACK *p, RPC_ENUM_LOG_FILE *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "LogFiles");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_LOG_FILE_ITEM *e = &t->Items[i];
@@ -9301,8 +10508,9 @@ void OutRpcEnumLogFile(PACK *p, RPC_ENUM_LOG_FILE *t)
PackAddStrEx(p, "FilePath", e->FilePath, i, t->NumItem);
PackAddStrEx(p, "ServerName", e->ServerName, i, t->NumItem);
PackAddIntEx(p, "FileSize", e->FileSize, i, t->NumItem);
- PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumItem);
+ PackAddTime64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumLogFile(RPC_ENUM_LOG_FILE *t)
{
@@ -9437,12 +10645,13 @@ void InRpcAcList(RPC_AC_LIST *t, PACK *p)
o = NewAcList();
PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
- num = PackGetInt(p, "NumItem");
+ num = PackGetIndexCount(p, "IpAddress");
for (i = 0;i < num;i++)
{
AC *ac = ZeroMalloc(sizeof(AC));
+ ac->Id = PackGetIntEx(p, "Id", i);
ac->Deny = PackGetBoolEx(p, "Deny", i);
PackGetIpEx(p, "IpAddress", &ac->IpAddress, i);
ac->Masked = PackGetBoolEx(p, "Masked", i);
@@ -9478,10 +10687,12 @@ void OutRpcAcList(PACK *p, RPC_AC_LIST *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "ACList");
for (i = 0;i < num;i++)
{
AC *ac = LIST_DATA(o, i);
+ PackAddIntEx(p, "Id", ac->Id, i, num);
PackAddBoolEx(p, "Deny", ac->Deny, i, num);
PackAddIpEx(p, "IpAddress", &ac->IpAddress, i, num);
PackAddBoolEx(p, "Masked", ac->Masked, i, num);
@@ -9490,6 +10701,7 @@ void OutRpcAcList(PACK *p, RPC_AC_LIST *t)
PackAddIntEx(p, "Priority", ac->Priority, i, num);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcAcList(RPC_AC_LIST *t)
{
@@ -9560,6 +10772,7 @@ void OutRpcEnumCrl(PACK *p, RPC_ENUM_CRL *t)
PackAddStr(p, "HubName", t->HubName);
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "CRLList");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_CRL_ITEM *e = &t->Items[i];
@@ -9567,6 +10780,7 @@ void OutRpcEnumCrl(PACK *p, RPC_ENUM_CRL *t)
PackAddIntEx(p, "Key", e->Key, i, t->NumItem);
PackAddUniStrEx(p, "CrlInfo", e->CrlInfo, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumCrl(RPC_ENUM_CRL *t)
{
@@ -9739,6 +10953,7 @@ void OutRpcEnumL3Table(PACK *p, RPC_ENUM_L3TABLE *t)
PackAddInt(p, "NumItem", t->NumItem);
PackAddStr(p, "Name", t->Name);
+ PackSetCurrentJsonGroupName(p, "L3Table");
for (i = 0;i < t->NumItem;i++)
{
RPC_L3TABLE *e = &t->Items[i];
@@ -9748,6 +10963,7 @@ void OutRpcEnumL3Table(PACK *p, RPC_ENUM_L3TABLE *t)
PackAddIp32Ex(p, "GatewayAddress", e->GatewayAddress, i, t->NumItem);
PackAddIntEx(p, "Metric", e->Metric, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumL3Table(RPC_ENUM_L3TABLE *t)
{
@@ -9821,6 +11037,7 @@ void OutRpcEnumL3If(PACK *p, RPC_ENUM_L3IF *t)
PackAddInt(p, "NumItem", t->NumItem);
PackAddStr(p, "Name", t->Name);
+ PackSetCurrentJsonGroupName(p, "L3IFList");
for (i = 0;i < t->NumItem;i++)
{
RPC_L3IF *f = &t->Items[i];
@@ -9829,6 +11046,7 @@ void OutRpcEnumL3If(PACK *p, RPC_ENUM_L3IF *t)
PackAddIp32Ex(p, "IpAddress", f->IpAddress, i, t->NumItem);
PackAddIp32Ex(p, "SubnetMask", f->SubnetMask, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumL3If(RPC_ENUM_L3IF *t)
{
@@ -9929,6 +11147,7 @@ void OutRpcEnumL3Sw(PACK *p, RPC_ENUM_L3SW *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "L3SWList");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_L3SW_ITEM *s = &t->Items[i];
@@ -9939,6 +11158,7 @@ void OutRpcEnumL3Sw(PACK *p, RPC_ENUM_L3SW *t)
PackAddBoolEx(p, "Active", s->Active, i, t->NumItem);
PackAddBoolEx(p, "Online", s->Online, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumL3Sw(RPC_ENUM_L3SW *t)
{
@@ -9983,12 +11203,14 @@ void OutRpcEnumEth(PACK *p, RPC_ENUM_ETH *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "EthList");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_ETH_ITEM *e = &t->Items[i];
PackAddStrEx(p, "DeviceName", e->DeviceName, i, t->NumItem);
PackAddUniStrEx(p, "NetworkConnectionName", e->NetworkConnectionName, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumEth(RPC_ENUM_ETH *t)
{
@@ -10064,6 +11286,7 @@ void OutRpcEnumLocalBridge(PACK *p, RPC_ENUM_LOCALBRIDGE *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "LocalBridgeList");
for (i = 0;i < t->NumItem;i++)
{
RPC_LOCALBRIDGE *e = &t->Items[i];
@@ -10074,6 +11297,7 @@ void OutRpcEnumLocalBridge(PACK *p, RPC_ENUM_LOCALBRIDGE *t)
PackAddBoolEx(p, "Active", e->Active, i, t->NumItem);
PackAddBoolEx(p, "TapMode", e->TapMode, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumLocalBridge(RPC_ENUM_LOCALBRIDGE *t)
{
@@ -10303,8 +11527,11 @@ void SiEnumLocalSession(SERVER *s, char *hubname, RPC_ENUM_SESSION *t)
StrCpy(e->Name, sizeof(e->Name), s->Name);
StrCpy(e->Username, sizeof(e->Username), s->Username);
e->Ip = IPToUINT(&s->Connection->ClientIp);
+ CopyIP(&e->ClientIP, &s->Connection->ClientIp);
StrCpy(e->Hostname, sizeof(e->Hostname), s->Connection->ClientHostname);
e->MaxNumTcp = s->MaxConnection;
+ e->CreatedTime = Tick64ToTime64(s->CreatedTime);
+ e->LastCommTime = Tick64ToTime64(s->LastCommTime);
e->LinkMode = s->LinkModeServer;
e->SecureNATMode = s->SecureNATMode;
e->BridgeMode = s->BridgeMode;
@@ -10400,6 +11627,8 @@ void OutRpcEnumLicenseKey(PACK *p, RPC_ENUM_LICENSE_KEY *t)
}
PackAddInt(p, "NumItem", t->NumItem);
+
+ PackSetCurrentJsonGroupName(p, "LicenseKeyList");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_LICENSE_KEY_ITEM *e = &t->Items[i];
@@ -10408,12 +11637,13 @@ void OutRpcEnumLicenseKey(PACK *p, RPC_ENUM_LICENSE_KEY *t)
PackAddStrEx(p, "LicenseKey", e->LicenseKey, i, t->NumItem);
PackAddStrEx(p, "LicenseId", e->LicenseId, i, t->NumItem);
PackAddStrEx(p, "LicenseName", e->LicenseName, i, t->NumItem);
- PackAddInt64Ex(p, "Expires", e->Expires, i, t->NumItem);
+ PackAddTime64Ex(p, "Expires", e->Expires, i, t->NumItem);
PackAddIntEx(p, "Status", e->Status, i, t->NumItem);
PackAddIntEx(p, "ProductId", e->ProductId, i, t->NumItem);
PackAddInt64Ex(p, "SystemId", e->SystemId, i, t->NumItem);
PackAddIntEx(p, "SerialId", e->SerialId, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumLicenseKey(RPC_ENUM_LICENSE_KEY *t)
{
@@ -10463,17 +11693,17 @@ void OutRpcLicenseStatus(PACK *p, RPC_LICENSE_STATUS *t)
PackAddInt(p, "EditionId", t->EditionId);
PackAddStr(p, "EditionStr", t->EditionStr);
PackAddInt64(p, "SystemId", t->SystemId);
- PackAddInt64(p, "SystemExpires", t->SystemExpires);
+ PackAddTime64(p, "SystemExpires", t->SystemExpires);
PackAddInt(p, "NumClientConnectLicense", t->NumClientConnectLicense);
PackAddInt(p, "NumBridgeConnectLicense", t->NumBridgeConnectLicense);
// v3.0
PackAddBool(p, "NeedSubscription", t->NeedSubscription);
PackAddBool(p, "AllowEnterpriseFunction", t->AllowEnterpriseFunction);
- PackAddInt64(p, "SubscriptionExpires", t->SubscriptionExpires);
+ PackAddTime64(p, "SubscriptionExpires", t->SubscriptionExpires);
PackAddBool(p, "IsSubscriptionExpired", t->IsSubscriptionExpired);
PackAddInt(p, "NumUserCreationLicense", t->NumUserCreationLicense);
- PackAddInt64(p, "ReleaseDate", t->ReleaseDate);
+ PackAddTime64(p, "ReleaseDate", t->ReleaseDate);
}
// RPC_ADMIN_OPTION
@@ -10487,7 +11717,7 @@ void InRpcAdminOption(RPC_ADMIN_OPTION *t, PACK *p)
}
Zero(t, sizeof(RPC_ADMIN_OPTION));
- t->NumItem = PackGetInt(p, "NumItem");
+ t->NumItem = PackGetIndexCount(p, "Name");
t->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * t->NumItem);
PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
@@ -10498,6 +11728,7 @@ void InRpcAdminOption(RPC_ADMIN_OPTION *t, PACK *p)
PackGetStrEx(p, "Name", o->Name, sizeof(o->Name), i);
o->Value = PackGetIntEx(p, "Value", i);
+ PackGetUniStrEx(p, "Descrption", o->Descrption, sizeof(o->Descrption), i);
}
}
void OutRpcAdminOption(PACK *p, RPC_ADMIN_OPTION *t)
@@ -10513,13 +11744,16 @@ void OutRpcAdminOption(PACK *p, RPC_ADMIN_OPTION *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "AdminOptionList");
for (i = 0;i < t->NumItem;i++)
{
ADMIN_OPTION *o = &t->Items[i];
PackAddStrEx(p, "Name", o->Name, i, t->NumItem);
PackAddIntEx(p, "Value", o->Value, i, t->NumItem);
+ PackAddUniStrEx(p, "Descrption", o->Descrption, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcAdminOption(RPC_ADMIN_OPTION *t)
{
@@ -10686,7 +11920,7 @@ void OutRpcServerInfo(PACK *p, RPC_SERVER_INFO *t)
PackAddInt(p, "ServerBuildInt", t->ServerBuildInt);
PackAddStr(p, "ServerHostName", t->ServerHostName);
PackAddInt(p, "ServerType", t->ServerType);
- PackAddInt64(p, "ServerBuildDate", t->ServerBuildDate);
+ PackAddTime64(p, "ServerBuildDate", t->ServerBuildDate);
PackAddStr(p, "ServerFamilyName", t->ServerFamilyName);
OutRpcOsInfo(p, &t->OsInfo);
}
@@ -10761,13 +11995,13 @@ void OutRpcServerStatus(PACK *p, RPC_SERVER_STATUS *t)
PackAddInt(p, "NumIpTables", t->NumIpTables);
PackAddInt(p, "NumUsers", t->NumUsers);
PackAddInt(p, "NumGroups", t->NumGroups);
- PackAddInt64(p, "CurrentTime", t->CurrentTime);
+ PackAddTime64(p, "CurrentTime", t->CurrentTime);
PackAddInt64(p, "CurrentTick", t->CurrentTick);
PackAddInt(p, "AssignedBridgeLicenses", t->AssignedBridgeLicenses);
PackAddInt(p, "AssignedClientLicenses", t->AssignedClientLicenses);
PackAddInt(p, "AssignedBridgeLicensesTotal", t->AssignedBridgeLicensesTotal);
PackAddInt(p, "AssignedClientLicensesTotal", t->AssignedClientLicensesTotal);
- PackAddInt64(p, "StartTime", t->StartTime);
+ PackAddTime64(p, "StartTime", t->StartTime);
OutRpcTraffic(p, &t->Traffic);
@@ -10830,12 +12064,14 @@ void OutRpcListenerList(PACK *p, RPC_LISTENER_LIST *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "ListenerList");
for (i = 0;i < t->NumPort;i++)
{
PackAddIntEx(p, "Ports", t->Ports[i], i, t->NumPort);
PackAddBoolEx(p, "Enables", t->Enables[i], i, t->NumPort);
PackAddBoolEx(p, "Errors", t->Errors[i], i, t->NumPort);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcListenerList(RPC_LISTENER_LIST *t)
{
@@ -10904,6 +12140,7 @@ void InRpcSetPassword(RPC_SET_PASSWORD *t, PACK *p)
Zero(t, sizeof(RPC_SET_PASSWORD));
PackGetData2(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+ PackGetStr(p, "PlainTextPassword", t->PlainTextPassword, sizeof(t->PlainTextPassword));
}
void OutRpcSetPassword(PACK *p, RPC_SET_PASSWORD *t)
{
@@ -10914,6 +12151,7 @@ void OutRpcSetPassword(PACK *p, RPC_SET_PASSWORD *t)
}
PackAddData(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
+ PackAddStr(p, "PlainTextPassword", t->PlainTextPassword);
}
// RPC_FARM
@@ -10938,6 +12176,7 @@ void InRpcFarm(RPC_FARM *t, PACK *p)
PackGetStr(p, "ControllerName", t->ControllerName, sizeof(t->ControllerName));
t->ControllerPort = PackGetInt(p, "ControllerPort");
PackGetData2(p, "MemberPassword", t->MemberPassword, sizeof(t->MemberPassword));
+ PackGetStr(p, "MemberPasswordPlaintext", t->MemberPasswordPlaintext, sizeof(t->MemberPasswordPlaintext));
t->Weight = PackGetInt(p, "Weight");
t->ControllerOnly = PackGetBool(p, "ControllerOnly");
}
@@ -10959,6 +12198,7 @@ void OutRpcFarm(PACK *p, RPC_FARM *t)
PackAddStr(p, "ControllerName", t->ControllerName);
PackAddInt(p, "ControllerPort", t->ControllerPort);
PackAddData(p, "MemberPassword", t->MemberPassword, sizeof(t->MemberPassword));
+ PackAddStr(p, "MemberPasswordPlaintext", t->MemberPasswordPlaintext);
PackAddInt(p, "Weight", t->Weight);
PackAddBool(p, "ControllerOnly", t->ControllerOnly);
}
@@ -11044,7 +12284,7 @@ void OutRpcFarmInfo(PACK *p, RPC_FARM_INFO *t)
PackAddInt(p, "Id", t->Id);
PackAddBool(p, "Controller", t->Controller);
- PackAddInt64(p, "ConnectedTime", t->ConnectedTime);
+ PackAddTime64(p, "ConnectedTime", t->ConnectedTime);
PackAddIp32(p, "Ip", t->Ip);
PackAddStr(p, "Hostname", t->Hostname);
PackAddInt(p, "Point", t->Point);
@@ -11053,11 +12293,15 @@ void OutRpcFarmInfo(PACK *p, RPC_FARM_INFO *t)
PackAddIntEx(p, "Ports", t->Ports[i], i, t->NumPort);
}
PackAddX(p, "ServerCert", t->ServerCert);
+
+ PackSetCurrentJsonGroupName(p, "HubsList");
for (i = 0;i < t->NumFarmHub;i++)
{
PackAddStrEx(p, "HubName", t->FarmHubs[i].HubName, i, t->NumFarmHub);
PackAddBoolEx(p, "DynamicHub", t->FarmHubs[i].DynamicHub, i, t->NumFarmHub);
}
+ PackSetCurrentJsonGroupName(p, NULL);
+
PackAddInt(p, "NumSessions", t->NumSessions);
PackAddInt(p, "NumTcpConnections", t->NumTcpConnections);
PackAddInt(p, "Weight", t->Weight);
@@ -11114,13 +12358,14 @@ void OutRpcEnumFarm(PACK *p, RPC_ENUM_FARM *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "FarmMemberList");
for (i = 0;i < t->NumFarm;i++)
{
RPC_ENUM_FARM_ITEM *e = &t->Farms[i];
PackAddIntEx(p, "Id", e->Id, i, t->NumFarm);
PackAddBoolEx(p, "Controller", e->Controller, i, t->NumFarm);
- PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumFarm);
+ PackAddTime64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumFarm);
PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumFarm);
PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumFarm);
PackAddIntEx(p, "Point", e->Point, i, t->NumFarm);
@@ -11130,6 +12375,7 @@ void OutRpcEnumFarm(PACK *p, RPC_ENUM_FARM *t)
PackAddIntEx(p, "AssignedClientLicense", e->AssignedClientLicense, i, t->NumFarm);
PackAddIntEx(p, "AssignedBridgeLicense", e->AssignedBridgeLicense, i, t->NumFarm);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumFarm(RPC_ENUM_FARM *t)
{
@@ -11175,9 +12421,9 @@ void OutRpcFarmConnectionStatus(PACK *p, RPC_FARM_CONNECTION_STATUS *t)
PackAddInt(p, "Port", t->Port);
PackAddBool(p, "Online", t->Online);
PackAddInt(p, "LastError", t->LastError);
- PackAddInt64(p, "StartedTime", t->StartedTime);
- PackAddInt64(p, "CurrentConnectedTime", t->CurrentConnectedTime);
- PackAddInt64(p, "FirstConnectedTime", t->FirstConnectedTime);
+ PackAddTime64(p, "StartedTime", t->StartedTime);
+ PackAddTime64(p, "CurrentConnectedTime", t->CurrentConnectedTime);
+ PackAddTime64(p, "FirstConnectedTime", t->FirstConnectedTime);
PackAddInt(p, "NumConnected", t->NumConnected);
PackAddInt(p, "NumTry", t->NumTry);
PackAddInt(p, "NumFailed", t->NumFailed);
@@ -11275,6 +12521,7 @@ void InRpcCreateHub(RPC_CREATE_HUB *t, PACK *p)
PackGetStr(p, "HubName", t->HubName, sizeof(t->HubName));
PackGetData2(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
PackGetData2(p, "SecurePassword", t->SecurePassword, sizeof(t->SecurePassword));
+ PackGetStr(p, "AdminPasswordPlainText", t->AdminPasswordPlainText, sizeof(t->AdminPasswordPlainText));
t->Online = PackGetBool(p, "Online");
InRpcHubOption(&t->HubOption, p);
t->HubType = PackGetInt(p, "HubType");
@@ -11291,6 +12538,7 @@ void OutRpcCreateHub(PACK *p, RPC_CREATE_HUB *t)
PackAddData(p, "HashedPassword", t->HashedPassword, sizeof(t->HashedPassword));
PackAddData(p, "SecurePassword", t->SecurePassword, sizeof(t->SecurePassword));
PackAddBool(p, "Online", t->Online);
+ PackAddStr(p, "AdminPasswordPlainText", t->AdminPasswordPlainText);
OutRpcHubOption(p, &t->HubOption);
PackAddInt(p, "HubType", t->HubType);
}
@@ -11339,6 +12587,7 @@ void OutRpcEnumHub(PACK *p, RPC_ENUM_HUB *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "HubList");
for (i = 0;i < t->NumHub;i++)
{
RPC_ENUM_HUB_ITEM *e = &t->Hubs[i];
@@ -11351,14 +12600,15 @@ void OutRpcEnumHub(PACK *p, RPC_ENUM_HUB *t)
PackAddIntEx(p, "NumGroups", e->NumGroups, i, t->NumHub);
PackAddIntEx(p, "NumMacTables", e->NumMacTables, i, t->NumHub);
PackAddIntEx(p, "NumIpTables", e->NumIpTables, i, t->NumHub);
- PackAddInt64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumHub);
- PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumHub);
- PackAddInt64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumHub);
+ PackAddTime64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumHub);
+ PackAddTime64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumHub);
+ PackAddTime64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumHub);
PackAddIntEx(p, "NumLogin", e->NumLogin, i, t->NumHub);
PackAddBoolEx(p, "IsTrafficFilled", e->IsTrafficFilled, i, t->NumHub);
OutRpcTrafficEx(&e->Traffic, p, i, t->NumHub);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumHub(RPC_ENUM_HUB *t)
{
@@ -11429,6 +12679,7 @@ void OutRpcEnumConnection(PACK *p, RPC_ENUM_CONNECTION *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "ConnectionList");
for (i = 0;i < t->NumConnection;i++)
{
RPC_ENUM_CONNECTION_ITEM *e = &t->Connections[i];
@@ -11437,9 +12688,10 @@ void OutRpcEnumConnection(PACK *p, RPC_ENUM_CONNECTION *t)
PackAddIntEx(p, "Port", e->Port, i, t->NumConnection);
PackAddStrEx(p, "Name", e->Name, i, t->NumConnection);
PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumConnection);
- PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumConnection);
+ PackAddTime64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumConnection);
PackAddIntEx(p, "Type", e->Type, i, t->NumConnection);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumConnection(RPC_ENUM_CONNECTION *t)
{
@@ -11509,7 +12761,7 @@ void OutRpcConnectionInfo(PACK *p, RPC_CONNECTION_INFO *t)
PackAddStr(p, "Name", t->Name);
PackAddIp32(p, "Ip", t->Ip);
PackAddInt(p, "Port", t->Port);
- PackAddInt64(p, "ConnectedTime", t->ConnectedTime);
+ PackAddTime64(p, "ConnectedTime", t->ConnectedTime);
PackAddStr(p, "Hostname", t->Hostname);
PackAddStr(p, "ServerStr", t->ServerStr);
PackAddStr(p, "ClientStr", t->ClientStr);
@@ -11594,9 +12846,9 @@ void OutRpcHubStatus(PACK *p, RPC_HUB_STATUS *t)
PackAddInt(p, "NumIpTables", t->NumIpTables);
PackAddBool(p, "SecureNATEnabled", t->SecureNATEnabled);
OutRpcTraffic(p, &t->Traffic);
- PackAddInt64(p, "LastCommTime", t->LastCommTime);
- PackAddInt64(p, "CreatedTime", t->CreatedTime);
- PackAddInt64(p, "LastLoginTime", t->LastLoginTime);
+ PackAddTime64(p, "LastCommTime", t->LastCommTime);
+ PackAddTime64(p, "CreatedTime", t->CreatedTime);
+ PackAddTime64(p, "LastLoginTime", t->LastLoginTime);
PackAddInt(p, "NumLogin", t->NumLogin);
}
@@ -11711,6 +12963,7 @@ void OutRpcHubEnumCa(PACK *p, RPC_HUB_ENUM_CA *t)
}
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "CAList");
for (i = 0;i < t->NumCa;i++)
{
RPC_HUB_ENUM_CA_ITEM *e = &t->Ca[i];
@@ -11718,8 +12971,9 @@ void OutRpcHubEnumCa(PACK *p, RPC_HUB_ENUM_CA *t)
PackAddIntEx(p, "Key", e->Key, i, t->NumCa);
PackAddUniStrEx(p, "SubjectName", e->SubjectName, i, t->NumCa);
PackAddUniStrEx(p, "IssuerName", e->IssuerName, i, t->NumCa);
- PackAddInt64Ex(p, "Expires", e->Expires, i, t->NumCa);
+ PackAddTime64Ex(p, "Expires", e->Expires, i, t->NumCa);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcHubEnumCa(RPC_HUB_ENUM_CA *t)
{
@@ -11886,6 +13140,7 @@ void InRpcEnumLink(RPC_ENUM_LINK *t, PACK *p)
e->ConnectedTime = PackGetInt64Ex(p, "ConnectedTime", i);
e->Connected = PackGetBoolEx(p, "Connected", i);
e->LastError = PackGetIntEx(p, "LastError", i);
+ PackGetStrEx(p, "LinkHubName", e->HubName, sizeof(e->HubName), i);
}
}
void OutRpcEnumLink(PACK *p, RPC_ENUM_LINK *t)
@@ -11899,6 +13154,7 @@ void OutRpcEnumLink(PACK *p, RPC_ENUM_LINK *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "LinkList");
for (i = 0;i < t->NumLink;i++)
{
RPC_ENUM_LINK_ITEM *e = &t->Links[i];
@@ -11907,10 +13163,12 @@ void OutRpcEnumLink(PACK *p, RPC_ENUM_LINK *t)
PackAddStrEx(p, "ConnectedHubName", e->HubName, i, t->NumLink);
PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumLink);
PackAddBoolEx(p, "Online", e->Online, i, t->NumLink);
- PackAddInt64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumLink);
+ PackAddTime64Ex(p, "ConnectedTime", e->ConnectedTime, i, t->NumLink);
PackAddBoolEx(p, "Connected", e->Connected, i, t->NumLink);
PackAddIntEx(p, "LastError", e->LastError, i, t->NumLink);
+ PackAddStrEx(p, "TargetHubName", e->HubName, i, t->NumLink);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumLink(RPC_ENUM_LINK *t)
{
@@ -12184,12 +13442,14 @@ void OutRpcEnumAccessList(PACK *p, RPC_ENUM_ACCESS_LIST *a)
}
PackAddStr(p, "HubName", a->HubName);
+ PackSetCurrentJsonGroupName(p, "AccessList");
for (i = 0;i < a->NumAccess;i++)
{
ACCESS *e = &a->Accesses[i];
OutRpcAccessEx(p, e, i, a->NumAccess);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a)
{
@@ -12203,7 +13463,7 @@ void FreeRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a)
}
// AUTHDATA
-void *InRpcAuthData(PACK *p, UINT *authtype)
+void *InRpcAuthData(PACK *p, UINT *authtype, char *username)
{
wchar_t tmp[MAX_SIZE];
AUTHPASSWORD *pw;
@@ -12212,6 +13472,7 @@ void *InRpcAuthData(PACK *p, UINT *authtype)
AUTHRADIUS *radius;
AUTHNT *nt;
BUF *b;
+ char plain_pw[MAX_SIZE];
// Validate arguments
if (p == NULL)
{
@@ -12230,6 +13491,15 @@ void *InRpcAuthData(PACK *p, UINT *authtype)
pw = ZeroMalloc(sizeof(AUTHPASSWORD));
PackGetData2(p, "HashedKey", pw->HashedKey, sizeof(pw->HashedKey));
PackGetData2(p, "NtLmSecureHash", pw->NtLmSecureHash, sizeof(pw->NtLmSecureHash));
+
+ if (PackGetStr(p, "Auth_Password", plain_pw, sizeof(plain_pw)))
+ {
+ if (IsZero(pw->HashedKey, sizeof(pw->HashedKey)))
+ {
+ HashPassword(pw->HashedKey, username, plain_pw);
+ GenerateNtPasswordHash(pw->NtLmSecureHash, plain_pw);
+ }
+ }
return pw;
case AUTHTYPE_USERCERT:
@@ -12347,7 +13617,7 @@ void InRpcSetUser(RPC_SET_USER *t, PACK *p)
t->CreatedTime = PackGetInt64(p, "CreatedTime");
t->UpdatedTime = PackGetInt64(p, "UpdatedTime");
t->ExpireTime = PackGetInt64(p, "ExpireTime");
- t->AuthData = InRpcAuthData(p, &t->AuthType);
+ t->AuthData = InRpcAuthData(p, &t->AuthType, t->Name);
t->NumLogin = PackGetInt(p, "NumLogin");
InRpcTraffic(&t->Traffic, p);
@@ -12371,9 +13641,9 @@ void OutRpcSetUser(PACK *p, RPC_SET_USER *t)
PackAddStr(p, "GroupName", t->GroupName);
PackAddUniStr(p, "Realname", t->Realname);
PackAddUniStr(p, "Note", t->Note);
- PackAddInt64(p, "CreatedTime", t->CreatedTime);
- PackAddInt64(p, "UpdatedTime", t->UpdatedTime);
- PackAddInt64(p, "ExpireTime", t->ExpireTime);
+ PackAddTime64(p, "CreatedTime", t->CreatedTime);
+ PackAddTime64(p, "UpdatedTime", t->UpdatedTime);
+ PackAddTime64(p, "ExpireTime", t->ExpireTime);
OutRpcAuthData(p, t->AuthData, t->AuthType);
PackAddInt(p, "NumLogin", t->NumLogin);
OutRpcTraffic(p, &t->Traffic);
@@ -12444,6 +13714,7 @@ void OutRpcEnumUser(PACK *p, RPC_ENUM_USER *t)
}
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "UserList");
for (i = 0;i < t->NumUser;i++)
{
RPC_ENUM_USER_ITEM *e = &t->Users[i];
@@ -12453,7 +13724,7 @@ void OutRpcEnumUser(PACK *p, RPC_ENUM_USER *t)
PackAddUniStrEx(p, "Realname", e->Realname, i, t->NumUser);
PackAddUniStrEx(p, "Note", e->Note, i, t->NumUser);
PackAddIntEx(p, "AuthType", e->AuthType, i, t->NumUser);
- PackAddInt64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumUser);
+ PackAddTime64Ex(p, "LastLoginTime", e->LastLoginTime, i, t->NumUser);
PackAddIntEx(p, "NumLogin", e->NumLogin, i, t->NumUser);
PackAddBoolEx(p, "DenyAccess", e->DenyAccess, i, t->NumUser);
@@ -12461,8 +13732,9 @@ void OutRpcEnumUser(PACK *p, RPC_ENUM_USER *t)
OutRpcTrafficEx(&e->Traffic, p, i, t->NumUser);
PackAddBoolEx(p, "IsExpiresFilled", e->IsExpiresFilled, i, t->NumUser);
- PackAddInt64Ex(p, "Expires", e->Expires, i, t->NumUser);
+ PackAddTime64Ex(p, "Expires", e->Expires, i, t->NumUser);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumUser(RPC_ENUM_USER *t)
{
@@ -12559,6 +13831,7 @@ void OutRpcEnumGroup(PACK *p, RPC_ENUM_GROUP *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "GroupList");
for (i = 0;i < t->NumGroup;i++)
{
RPC_ENUM_GROUP_ITEM *e = &t->Groups[i];
@@ -12569,6 +13842,7 @@ void OutRpcEnumGroup(PACK *p, RPC_ENUM_GROUP *t)
PackAddIntEx(p, "NumUsers", e->NumUsers, i, t->NumGroup);
PackAddBoolEx(p, "DenyAccess", e->DenyAccess, i, t->NumGroup);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumGroup(RPC_ENUM_GROUP *t)
{
@@ -12628,6 +13902,7 @@ void InRpcEnumSession(RPC_ENUM_SESSION *t, PACK *p)
PackGetStrEx(p, "Name", e->Name, sizeof(e->Name), i);
PackGetStrEx(p, "Username", e->Username, sizeof(e->Username), i);
e->Ip = PackGetIntEx(p, "Ip", i);
+ PackGetIpEx(p, "ClientIP", &e->ClientIP, i);
PackGetStrEx(p, "Hostname", e->Hostname, sizeof(e->Hostname), i);
e->MaxNumTcp = PackGetIntEx(p, "MaxNumTcp", i);
e->CurrentNumTcp = PackGetIntEx(p, "CurrentNumTcp", i);
@@ -12646,6 +13921,8 @@ void InRpcEnumSession(RPC_ENUM_SESSION *t, PACK *p)
e->IsDormantEnabled = PackGetBoolEx(p, "IsDormantEnabled", i);
e->IsDormant = PackGetBoolEx(p, "IsDormant", i);
e->LastCommDormant = PackGetInt64Ex(p, "LastCommDormant", i);
+ e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
+ e->LastCommTime = PackGetInt64Ex(p, "LastCommTime", i);
}
}
void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t)
@@ -12658,6 +13935,7 @@ void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t)
}
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "SessionList");
for (i = 0;i < t->NumSession;i++)
{
RPC_ENUM_SESSION_ITEM *e = &t->Sessions[i];
@@ -12665,6 +13943,7 @@ void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t)
PackAddStrEx(p, "Name", e->Name, i, t->NumSession);
PackAddStrEx(p, "Username", e->Username, i, t->NumSession);
PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumSession);
+ PackAddIpEx(p, "ClientIP", &e->ClientIP, i, t->NumSession);
PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumSession);
PackAddIntEx(p, "MaxNumTcp", e->MaxNumTcp, i, t->NumSession);
PackAddIntEx(p, "CurrentNumTcp", e->CurrentNumTcp, i, t->NumSession);
@@ -12682,8 +13961,11 @@ void OutRpcEnumSession(PACK *p, RPC_ENUM_SESSION *t)
PackAddDataEx(p, "UniqueId", e->UniqueId, sizeof(e->UniqueId), i, t->NumSession);
PackAddBoolEx(p, "IsDormantEnabled", e->IsDormantEnabled, i, t->NumSession);
PackAddBoolEx(p, "IsDormant", e->IsDormant, i, t->NumSession);
- PackAddInt64Ex(p, "LastCommDormant", e->LastCommDormant, i, t->NumSession);
+ PackAddTime64Ex(p, "LastCommDormant", e->LastCommDormant, i, t->NumSession);
+ PackAddTime64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumSession);
+ PackAddTime64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumSession);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumSession(RPC_ENUM_SESSION *t)
{
@@ -12814,6 +14096,7 @@ void InRpcSessionStatus(RPC_SESSION_STATUS *t, PACK *p)
t->ClientIp = PackGetIp32(p, "SessionStatus_ClientIp");
PackGetData2(p, "SessionStatus_ClientIp6", t->ClientIp6, sizeof(t->ClientIp6));
PackGetStr(p, "SessionStatus_ClientHostName", t->ClientHostName, sizeof(t->ClientHostName));
+ PackGetIp(p, "Client_Ip_Address", &t->ClientIpAddress);
InRpcClientGetConnectionStatus(&t->Status, p);
InRpcNodeInfo(&t->NodeInfo, p);
@@ -12834,6 +14117,7 @@ void OutRpcSessionStatus(PACK *p, RPC_SESSION_STATUS *t)
PackAddIp32(p, "SessionStatus_ClientIp", t->ClientIp);
PackAddData(p, "SessionStatus_ClientIp6", t->ClientIp6, sizeof(t->ClientIp6));
PackAddStr(p, "SessionStatus_ClientHostName", t->ClientHostName);
+ PackAddIp(p, "Client_Ip_Address", &t->ClientIpAddress);
OutRpcClientGetConnectionStatus(p, &t->Status);
OutRpcNodeInfo(p, &t->NodeInfo);
@@ -12914,6 +14198,7 @@ void OutRpcEnumMacTable(PACK *p, RPC_ENUM_MAC_TABLE *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "MacTable");
for (i = 0;i < t->NumMacTable;i++)
{
RPC_ENUM_MAC_TABLE_ITEM *e = &t->MacTables[i];
@@ -12922,11 +14207,12 @@ void OutRpcEnumMacTable(PACK *p, RPC_ENUM_MAC_TABLE *t)
PackAddStrEx(p, "SessionName", e->SessionName, i, t->NumMacTable);
PackAddDataEx(p, "MacAddress", e->MacAddress, sizeof(e->MacAddress), i, t->NumMacTable);
PackAddIntEx(p, "VlanId", e->VlanId, i, t->NumMacTable);
- PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumMacTable);
- PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumMacTable);
+ PackAddTime64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumMacTable);
+ PackAddTime64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumMacTable);
PackAddBoolEx(p, "RemoteItem", e->RemoteItem, i, t->NumMacTable);
PackAddStrEx(p, "RemoteHostname", e->RemoteHostname, i, t->NumMacTable);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumMacTable(RPC_ENUM_MAC_TABLE *t)
{
@@ -12965,6 +14251,7 @@ void InRpcEnumIpTable(RPC_ENUM_IP_TABLE *t, PACK *p)
{
UINTToIP(&e->IpV6, e->Ip);
}
+ PackGetIp(p, "IpAddress", &e->IpAddress);
e->DhcpAllocated = PackGetBoolEx(p, "DhcpAllocated", i);
e->CreatedTime = PackGetInt64Ex(p, "CreatedTime", i);
e->UpdatedTime = PackGetInt64Ex(p, "UpdatedTime", i);
@@ -12983,6 +14270,7 @@ void OutRpcEnumIpTable(PACK *p, RPC_ENUM_IP_TABLE *t)
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "IpTable");
for (i = 0;i < t->NumIpTable;i++)
{
RPC_ENUM_IP_TABLE_ITEM *e = &t->IpTables[i];
@@ -12991,12 +14279,14 @@ void OutRpcEnumIpTable(PACK *p, RPC_ENUM_IP_TABLE *t)
PackAddStrEx(p, "SessionName", e->SessionName, i, t->NumIpTable);
PackAddIp32Ex(p, "Ip", e->Ip, i, t->NumIpTable);
PackAddIpEx(p, "IpV6", &e->IpV6, i, t->NumIpTable);
+ PackAddIpEx(p, "IpAddress", &e->IpAddress, i, t->NumIpTable);
PackAddBoolEx(p, "DhcpAllocated", e->DhcpAllocated, i, t->NumIpTable);
- PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumIpTable);
- PackAddInt64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumIpTable);
+ PackAddTime64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumIpTable);
+ PackAddTime64Ex(p, "UpdatedTime", e->UpdatedTime, i, t->NumIpTable);
PackAddBoolEx(p, "RemoteItem", e->RemoteItem, i, t->NumIpTable);
PackAddStrEx(p, "RemoteHostname", e->RemoteHostname, i, t->NumIpTable);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumIpTable(RPC_ENUM_IP_TABLE *t)
{
diff --git a/src/Cedar/Admin.h b/src/Cedar/Admin.h
index d747c91d..0d58e8d6 100644
--- a/src/Cedar/Admin.h
+++ b/src/Cedar/Admin.h
@@ -32,6 +32,8 @@ struct ADMIN
LIST *LogFileList; // Accessible log file list
UINT ClientBuild; // Build number of the client
RPC_WINVER ClientWinVer; // Windows version of client
+ UINT MaxJsonRpcRecvSize; // Max JSON-RPC Receive Size
+ char dummy1[MAX_HUBNAME_LEN + 1]; // hubname buffer (dummy)
};
// Test
@@ -118,7 +120,8 @@ struct RPC_INT
// Set Password
struct RPC_SET_PASSWORD
{
- UCHAR HashedPassword[SHA1_SIZE]; // Hashed password
+ UCHAR HashedPassword[SHA1_SIZE]; // Hashed password (for traditional RPC)
+ char PlainTextPassword[MAX_SIZE]; // Plaintext password (for JSON-RPC)
};
// Server farm configuration *
@@ -131,6 +134,7 @@ struct RPC_FARM
char ControllerName[MAX_HOST_NAME_LEN + 1]; // Controller name
UINT ControllerPort; // Controller port
UCHAR MemberPassword[SHA1_SIZE]; // Member password
+ char MemberPasswordPlaintext[MAX_SIZE]; // Member password (plaintext)
UINT Weight; // Performance ratio
bool ControllerOnly; // Only controller function
};
@@ -236,6 +240,7 @@ struct RPC_CREATE_HUB
char HubName[MAX_HUBNAME_LEN + 1]; // HUB Name
UCHAR HashedPassword[SHA1_SIZE]; // Administrative password
UCHAR SecurePassword[SHA1_SIZE]; // Administrator password
+ char AdminPasswordPlainText[MAX_SIZE]; // Password (plaintext)
bool Online; // Online flag
RPC_HUB_OPTION HubOption; // HUB options
UINT HubType; // Type of HUB
@@ -553,6 +558,7 @@ struct RPC_ENUM_SESSION_ITEM
char RemoteHostname[MAX_HOST_NAME_LEN + 1]; // Remote server name
char Username[MAX_USERNAME_LEN + 1]; // User name
UINT Ip; // IP address (IPv4)
+ IP ClientIP; // IP address (IPv4 / IPv6)
char Hostname[MAX_HOST_NAME_LEN + 1]; // Host name
UINT MaxNumTcp; // Maximum number of TCP connections
UINT CurrentNumTcp; // Number of currentl TCP connections
@@ -569,6 +575,8 @@ struct RPC_ENUM_SESSION_ITEM
bool IsDormantEnabled; // Is the dormant state enabled
bool IsDormant; // Is in the dormant state
UINT64 LastCommDormant; // Last comm interval in the dormant state
+ UINT64 CreatedTime; // Creation date and time
+ UINT64 LastCommTime; // Last communication date and time
};
// Disconnect the session
@@ -605,8 +613,9 @@ struct RPC_ENUM_IP_TABLE_ITEM
{
UINT Key; // Key
char SessionName[MAX_SESSION_NAME_LEN + 1]; // Session name
- UINT Ip; // IP address
+ UINT Ip; // IPv4 address
IP IpV6; // IPv6 address
+ IP IpAddress; // IPv4 / IPv6 Address
bool DhcpAllocated; // Assigned by the DHCP
UINT64 CreatedTime; // Creation date and time
UINT64 UpdatedTime; // Updating date
@@ -892,6 +901,10 @@ struct RPC_AZURE_STATUS
bool IsConnected; // Whether it's connected
};
+// Constants
+#define ADMIN_RPC_MAX_POST_SIZE_BY_SERVER_ADMIN MAX_PACK_SIZE
+#define ADMIN_RPC_MAX_POST_SIZE_BY_HUB_ADMIN (8 * 1024 * 1024)
+
// Function prototype
UINT AdminAccept(CONNECTION *c, PACK *p);
@@ -916,6 +929,26 @@ BUF *DownloadFileFromServer(RPC *r, char *server_name, char *filepath, UINT tota
bool CheckAdminSourceAddress(SOCK *sock, char *hubname);
void SiEnumSessionMain(SERVER *s, RPC_ENUM_SESSION *t);
bool SiIsEmptyPassword(void *hash_password);
+void JsonRpcProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_size);
+void JsonRpcProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target);
+void JsonRpcProcOptions(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target);
+JSON_VALUE *JsonRpcProcRequestObject(ADMIN *admin, CONNECTION *c, SOCK *s, JSON_VALUE *json_req, char *method_name);
+JSON_VALUE *JsonRpcNewError(int code, wchar_t *message);
+JSON_VALUE *JsonRpcNewResponse(PACK *p);
+bool HttpParseBasicAuthHeader(HTTP_HEADER *h, char *username, UINT username_size, char *password, UINT password_size);
+ADMIN *JsonRpcAuthLogin(CEDAR *c, SOCK *sock, HTTP_HEADER *h);
+JSON_VALUE *QueryStringToJsonListValue(char *qs);
+JSON_VALUE *ConstructDummyJsonRpcRequest(char *method_name, JSON_VALUE *p);
+void AdminWebProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_size, char *url_target);
+void AdminWebProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target);
+bool AdminWebHandleFileRequest(ADMIN *a, CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_src, char *query_string, char *virtual_root_dir, char *physical_root_dir);
+BUF *AdminWebProcessServerSideInclude(BUF *src_txt, char *filename, UINT depth);
+bool AdminWebSendBody(SOCK *s, UINT status_code, char *status_string, UCHAR *data, UINT data_size, char *content_type, char *add_header_name, char *add_header_value, HTTP_HEADER *request_headers);
+bool AdminWebSend404Error(SOCK *s, HTTP_HEADER *request_headers);
+bool AdminWebSend302Redirect(SOCK *s, char *url, char *query_string, HTTP_HEADER *request_headers);
+BUF *AdminWebTryFindAndReadFile(char *vroot, char *proot, char *url, char *ret_filename, UINT ret_filename_size, bool *is_index_html);
+BUF *AdminWebTryOneFile(char *filename, char *ret_filename, UINT ret_filename_size);
+bool AdminWebSendUnauthorized(SOCK *s, HTTP_HEADER *http_request_headers);
UINT StTest(ADMIN *a, RPC_TEST *t);
UINT StGetServerInfo(ADMIN *a, RPC_SERVER_INFO *t);
@@ -1291,7 +1324,7 @@ void OutRpcAccess(PACK *p, ACCESS *a);
void InRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a, PACK *p);
void OutRpcEnumAccessList(PACK *p, RPC_ENUM_ACCESS_LIST *a);
void FreeRpcEnumAccessList(RPC_ENUM_ACCESS_LIST *a);
-void *InRpcAuthData(PACK *p, UINT *authtype);
+void *InRpcAuthData(PACK *p, UINT *authtype, char *username);
void OutRpcAuthData(PACK *p, void *authdata, UINT authtype);
void FreeRpcAuthData(void *authdata, UINT authtype);
void InRpcSetUser(RPC_SET_USER *t, PACK *p);
diff --git a/src/Cedar/CM.c b/src/Cedar/CM.c
index a6da6e4f..c908f30d 100644
--- a/src/Cedar/CM.c
+++ b/src/Cedar/CM.c
@@ -9578,8 +9578,9 @@ void CmPrintStatusToListViewEx(LVB *b, RPC_CLIENT_GET_CONNECTION_STATUS *s, bool
GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->StartTime), NULL);
LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_START_TIME"), tmp);
- GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablishedTime), NULL);
- LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablishedTime == 0 ? _UU("CM_ST_NONE") : tmp);
+ GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablisiedTime), NULL);
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ LvInsertAdd(b, 0, NULL, 2, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablisiedTime == 0 ? _UU("CM_ST_NONE") : tmp);
if (s->Connected)
{
diff --git a/src/Cedar/CedarType.h b/src/Cedar/CedarType.h
index 8b821996..e9190e25 100644
--- a/src/Cedar/CedarType.h
+++ b/src/Cedar/CedarType.h
@@ -137,6 +137,7 @@ typedef struct BLACK BLACK;
typedef struct SEND_SIGNATURE_PARAM SEND_SIGNATURE_PARAM;
typedef struct UPDATE_CLIENT UPDATE_CLIENT;
typedef struct UPDATE_CLIENT_SETTING UPDATE_CLIENT_SETTING;
+typedef struct HTTP_MIME_TYPE HTTP_MIME_TYPE;
// ==============================================================
diff --git a/src/Cedar/Client.c b/src/Cedar/Client.c
index 2936d054..d587d6e3 100644
--- a/src/Cedar/Client.c
+++ b/src/Cedar/Client.c
@@ -3810,14 +3810,16 @@ void OutRpcClientEnumCa(PACK *p, RPC_CLIENT_ENUM_CA *e)
PackAddNum(p, "NumItem", e->NumItem);
+ PackSetCurrentJsonGroupName(p, "CAList");
for (i = 0;i < e->NumItem;i++)
{
RPC_CLIENT_ENUM_CA_ITEM *item = e->Items[i];
PackAddIntEx(p, "Key", item->Key, i, e->NumItem);
PackAddUniStrEx(p, "SubjectName", item->SubjectName, i, e->NumItem);
PackAddUniStrEx(p, "IssuerName", item->IssuerName, i, e->NumItem);
- PackAddInt64Ex(p, "Expires", item->Expires, i, e->NumItem);
+ PackAddTime64Ex(p, "Expires", item->Expires, i, e->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
// RPC_GET_ISSUER
@@ -4088,6 +4090,7 @@ void OutRpcClientEnumSecure(PACK *p, RPC_CLIENT_ENUM_SECURE *e)
PackAddNum(p, "NumItem", e->NumItem);
+ PackSetCurrentJsonGroupName(p, "SecureDeviceList");
for (i = 0;i < e->NumItem;i++)
{
RPC_CLIENT_ENUM_SECURE_ITEM *item = e->Items[i];
@@ -4097,6 +4100,7 @@ void OutRpcClientEnumSecure(PACK *p, RPC_CLIENT_ENUM_SECURE *e)
PackAddStrEx(p, "DeviceName", item->DeviceName, i, e->NumItem);
PackAddStrEx(p, "Manufacturer", item->Manufacturer, i, e->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
// RPC_USE_SECURE
@@ -4153,11 +4157,13 @@ void OutRpcEnumObjectInSecure(PACK *p, RPC_ENUM_OBJECT_IN_SECURE *e)
PackAddNum(p, "NumItem", e->NumItem);
PackAddInt(p, "hWnd", e->hWnd);
+ PackSetCurrentJsonGroupName(p, "ObjectList");
for (i = 0;i < e->NumItem;i++)
{
PackAddStrEx(p, "ItemName", e->ItemName[i], i, e->NumItem);
PackAddIntEx(p, "ItemType", e->ItemType[i], i, e->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
// RPC_CLIENT_CREATE_VLAN
@@ -4277,6 +4283,7 @@ void OutRpcClientEnumVLan(PACK *p, RPC_CLIENT_ENUM_VLAN *v)
PackAddNum(p, "NumItem", v->NumItem);
+ PackSetCurrentJsonGroupName(p, "VLanList");
for (i = 0;i < v->NumItem;i++)
{
RPC_CLIENT_ENUM_VLAN_ITEM *item = v->Items[i];
@@ -4286,6 +4293,7 @@ void OutRpcClientEnumVLan(PACK *p, RPC_CLIENT_ENUM_VLAN *v)
PackAddStrEx(p, "MacAddress", item->MacAddress, i, v->NumItem);
PackAddStrEx(p, "Version", item->Version, i, v->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
// CLIENT_OPTION
@@ -4352,10 +4360,10 @@ void OutRpcClientOption(PACK *p, CLIENT_OPTION *c)
PackAddInt(p, "NumRetry", c->NumRetry);
PackAddInt(p, "RetryInterval", c->RetryInterval);
PackAddInt(p, "MaxConnection", c->MaxConnection);
- PackAddInt(p, "UseEncrypt", c->UseEncrypt);
- PackAddInt(p, "UseCompress", c->UseCompress);
- PackAddInt(p, "HalfConnection", c->HalfConnection);
- PackAddInt(p, "NoRoutingTracking", c->NoRoutingTracking);
+ PackAddBool(p, "UseEncrypt", c->UseEncrypt);
+ PackAddBool(p, "UseCompress", c->UseCompress);
+ PackAddBool(p, "HalfConnection", c->HalfConnection);
+ PackAddBool(p, "NoRoutingTracking", c->NoRoutingTracking);
PackAddInt(p, "AdditionalConnectionInterval", c->AdditionalConnectionInterval);
PackAddInt(p, "ConnectionDisconnectSpan", c->ConnectionDisconnectSpan);
PackAddBool(p, "HideStatusWindow", c->HideStatusWindow);
@@ -4568,6 +4576,7 @@ void OutRpcClientEnumAccount(PACK *p, RPC_CLIENT_ENUM_ACCOUNT *e)
PackAddNum(p, "NumItem", e->NumItem);
+ PackSetCurrentJsonGroupName(p, "AccountList");
for (i = 0;i < e->NumItem;i++)
{
RPC_CLIENT_ENUM_ACCOUNT_ITEM *item = e->Items[i];
@@ -4583,10 +4592,11 @@ void OutRpcClientEnumAccount(PACK *p, RPC_CLIENT_ENUM_ACCOUNT *e)
PackAddBoolEx(p, "Connected", item->Connected, i, e->NumItem);
PackAddIntEx(p, "Port", item->Port, i, e->NumItem);
PackAddStrEx(p, "HubName", item->HubName, i, e->NumItem);
- PackAddInt64Ex(p, "CreateDateTime", item->CreateDateTime, i, e->NumItem);
- PackAddInt64Ex(p, "UpdateDateTime", item->UpdateDateTime, i, e->NumItem);
- PackAddInt64Ex(p, "LastConnectDateTime", item->LastConnectDateTime, i, e->NumItem);
+ PackAddTime64Ex(p, "CreateDateTime", item->CreateDateTime, i, e->NumItem);
+ PackAddTime64Ex(p, "UpdateDateTime", item->UpdateDateTime, i, e->NumItem);
+ PackAddTime64Ex(p, "LastConnectDateTime", item->LastConnectDateTime, i, e->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
// RPC_CLIENT_DELETE_ACCOUNT
@@ -4702,9 +4712,9 @@ void OutRpcClientGetAccount(PACK *p, RPC_CLIENT_GET_ACCOUNT *c)
PackAddData(p, "ShortcutKey", c->ShortcutKey, SHA1_SIZE);
- PackAddInt64(p, "CreateDateTime", c->CreateDateTime);
- PackAddInt64(p, "UpdateDateTime", c->UpdateDateTime);
- PackAddInt64(p, "LastConnectDateTime", c->LastConnectDateTime);
+ PackAddTime64(p, "CreateDateTime", c->CreateDateTime);
+ PackAddTime64(p, "UpdateDateTime", c->UpdateDateTime);
+ PackAddTime64(p, "LastConnectDateTime", c->LastConnectDateTime);
}
// RPC_CLIENT_CONNECT
@@ -4792,7 +4802,8 @@ void InRpcClientGetConnectionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *s, PACK *p
s->NumTcpConnectionsDownload = PackGetInt(p, "NumTcpConnectionsDownload");
s->StartTime = PackGetInt64(p, "StartTime");
- s->FirstConnectionEstablishedTime = PackGetInt64(p, "FirstConnectionEstablishedTime");
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ s->FirstConnectionEstablisiedTime = PackGetInt64(p, "FirstConnectionEstablisiedTime");
s->CurrentConnectionEstablishTime = PackGetInt64(p, "CurrentConnectionEstablishTime");
s->TotalSendSize = PackGetInt64(p, "TotalSendSize");
s->TotalRecvSize = PackGetInt64(p, "TotalRecvSize");
@@ -4852,32 +4863,32 @@ void OutRpcClientGetConnectionStatus(PACK *p, RPC_CLIENT_GET_CONNECTION_STATUS *
PackAddData(p, "SessionKey", c->SessionKey, SHA1_SIZE);
- PackAddInt(p, "Active", c->Active);
- PackAddInt(p, "Connected", c->Connected);
+ PackAddBool(p, "Active", c->Active);
+ PackAddBool(p, "Connected", c->Connected);
PackAddInt(p, "SessionStatus", c->SessionStatus);
PackAddInt(p, "ServerPort", c->ServerPort);
PackAddInt(p, "ServerProductVer", c->ServerProductVer);
PackAddInt(p, "ServerProductBuild", c->ServerProductBuild);
PackAddInt(p, "NumConnectionsEstablished", c->NumConnectionsEstablished);
- PackAddInt(p, "HalfConnection", c->HalfConnection);
- PackAddInt(p, "QoS", c->QoS);
+ PackAddBool(p, "HalfConnection", c->HalfConnection);
+ PackAddBool(p, "QoS", c->QoS);
PackAddInt(p, "MaxTcpConnections", c->MaxTcpConnections);
PackAddInt(p, "NumTcpConnections", c->NumTcpConnections);
PackAddInt(p, "NumTcpConnectionsUpload", c->NumTcpConnectionsUpload);
PackAddInt(p, "NumTcpConnectionsDownload", c->NumTcpConnectionsDownload);
- PackAddInt(p, "UseEncrypt", c->UseEncrypt);
- PackAddInt(p, "UseCompress", c->UseCompress);
- PackAddInt(p, "IsRUDPSession", c->IsRUDPSession);
+ PackAddBool(p, "UseEncrypt", c->UseEncrypt);
+ PackAddBool(p, "UseCompress", c->UseCompress);
+ PackAddBool(p, "IsRUDPSession", c->IsRUDPSession);
PackAddStr(p, "UnderlayProtocol", c->UnderlayProtocol);
- PackAddInt(p, "IsUdpAccelerationEnabled", c->IsUdpAccelerationEnabled);
- PackAddInt(p, "IsUsingUdpAcceleration", c->IsUsingUdpAcceleration);
+ PackAddBool(p, "IsUdpAccelerationEnabled", c->IsUdpAccelerationEnabled);
+ PackAddBool(p, "IsUsingUdpAcceleration", c->IsUsingUdpAcceleration);
PackAddBool(p, "IsBridgeMode", c->IsBridgeMode);
PackAddBool(p, "IsMonitorMode", c->IsMonitorMode);
- PackAddInt64(p, "StartTime", c->StartTime);
- PackAddInt64(p, "FirstConnectionEstablishedTime", c->FirstConnectionEstablishedTime);
- PackAddInt64(p, "CurrentConnectionEstablishTime", c->CurrentConnectionEstablishTime);
+ PackAddTime64(p, "StartTime", c->StartTime);
+ PackAddTime64(p, "FirstConnectionEstablisiedTime", c->FirstConnectionEstablisiedTime);
+ PackAddTime64(p, "CurrentConnectionEstablishTime", c->CurrentConnectionEstablishTime);
PackAddInt64(p, "TotalSendSize", c->TotalSendSize);
PackAddInt64(p, "TotalRecvSize", c->TotalRecvSize);
PackAddInt64(p, "TotalSendSizeReal", c->TotalSendSizeReal);
@@ -5852,7 +5863,8 @@ void CiGetSessionStatus(RPC_CLIENT_GET_CONNECTION_STATUS *st, SESSION *s)
// Connection start time
st->StartTime = TickToTime(s->CreatedTime);
// Connection completion time of the first connection
- st->FirstConnectionEstablishedTime = TickToTime(s->FirstConnectionEstablishedTime);
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ st->FirstConnectionEstablisiedTime = TickToTime(s->FirstConnectionEstablisiedTime);
// Number of connections have been established so far
st->NumConnectionsEstablished = s->NumConnectionsEstablished;
}
diff --git a/src/Cedar/Client.h b/src/Cedar/Client.h
index cb8bba7c..013ddbfb 100644
--- a/src/Cedar/Client.h
+++ b/src/Cedar/Client.h
@@ -325,7 +325,8 @@ struct RPC_CLIENT_GET_CONNECTION_STATUS
X *ServerX; // Server certificate
X *ClientX; // Client certificate
UINT64 StartTime; // Connection start time
- UINT64 FirstConnectionEstablishedTime; // Connection completion time of the first connection
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ UINT64 FirstConnectionEstablisiedTime; // Connection completion time of the first connection
UINT64 CurrentConnectionEstablishTime; // Connection completion time of this connection
UINT NumConnectionsEstablished; // Number of connections have been established so far
bool HalfConnection; // Half-connection
diff --git a/src/Cedar/Command.c b/src/Cedar/Command.c
index 4e8b53bf..23dc9481 100644
--- a/src/Cedar/Command.c
+++ b/src/Cedar/Command.c
@@ -15077,8 +15077,10 @@ void CmdPrintStatusToListViewEx(CT *ct, RPC_CLIENT_GET_CONNECTION_STATUS *s, boo
GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->StartTime), NULL);
CtInsert(ct, _UU("CM_ST_START_TIME"), tmp);
- GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablishedTime), NULL);
- CtInsert(ct, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablishedTime == 0 ? _UU("CM_ST_NONE") : tmp);
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ GetDateTimeStrEx64(tmp, sizeof(tmp), SystemToLocal64(s->FirstConnectionEstablisiedTime), NULL);
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ CtInsert(ct, _UU("CM_ST_FIRST_ESTAB_TIME"), s->FirstConnectionEstablisiedTime == 0 ? _UU("CM_ST_NONE") : tmp);
if (s->Connected)
{
diff --git a/src/Cedar/Connection.h b/src/Cedar/Connection.h
index 66858ed7..bed31883 100644
--- a/src/Cedar/Connection.h
+++ b/src/Cedar/Connection.h
@@ -216,6 +216,9 @@ struct CONNECTION
UINT LastPacketQueueSize; // The last queue size of packets
UINT LastRecvFifoTotalSize; // The last RecvFifo total size
UINT LastRecvBlocksNum; // The last ReceivedBlocks num
+ bool IsJsonRpc; // Is JSON-RPC
+ bool JsonRpcAuthed; // JSON-RPC Authed
+ LISTENER *Listener; // Listener ref
};
diff --git a/src/Cedar/DDNS.c b/src/Cedar/DDNS.c
index 8436cfbc..5a2fec7b 100644
--- a/src/Cedar/DDNS.c
+++ b/src/Cedar/DDNS.c
@@ -37,6 +37,9 @@ void DCGetStatus(DDNS_CLIENT *c, DDNS_CLIENT_STATUS *st)
Copy(&st->InternetSetting, &c->InternetSetting, sizeof(INTERNET_SETTING));
}
Unlock(c->Lock);
+
+ UniStrCpy(st->ErrStr_IPv4, sizeof(st->ErrStr_IPv4), _E(st->Err_IPv4));
+ UniStrCpy(st->ErrStr_IPv6, sizeof(st->ErrStr_IPv6), _E(st->Err_IPv6));
}
// Set the Internet settings
diff --git a/src/Cedar/DDNS.h b/src/Cedar/DDNS.h
index fb7def82..bd6a6fc1 100644
--- a/src/Cedar/DDNS.h
+++ b/src/Cedar/DDNS.h
@@ -111,6 +111,8 @@ struct DDNS_REGISTER_PARAM
struct DDNS_CLIENT_STATUS
{
UINT Err_IPv4, Err_IPv6; // Last error
+ wchar_t ErrStr_IPv4[MAX_SIZE];
+ wchar_t ErrStr_IPv6[MAX_SIZE];
char CurrentHostName[DDNS_MAX_HOSTNAME + 1]; // Current host name
char CurrentFqdn[MAX_SIZE]; // Current FQDN
char DnsSuffix[MAX_SIZE]; // DNS suffix
diff --git a/src/Cedar/EtherLog.c b/src/Cedar/EtherLog.c
index e02756b4..dc057a97 100644
--- a/src/Cedar/EtherLog.c
+++ b/src/Cedar/EtherLog.c
@@ -558,6 +558,7 @@ void OutRpcEnumDevice(PACK *p, RPC_ENUM_DEVICE *t)
PackAddInt(p, "NumItem", t->NumItem);
+ PackSetCurrentJsonGroupName(p, "DeviceList");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_DEVICE_ITEM *d = &t->Items[i];
@@ -565,6 +566,7 @@ void OutRpcEnumDevice(PACK *p, RPC_ENUM_DEVICE *t)
PackAddStrEx(p, "DeviceName", d->DeviceName, i, t->NumItem);
PackAddBoolEx(p, "Active", d->Active, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
PackAddBool(p, "IsLicenseSupported", t->IsLicenseSupported);
}
@@ -605,7 +607,7 @@ void OutRpcElLicenseStatus(PACK *p, RPC_EL_LICENSE_STATUS *t)
PackAddBool(p, "Valid", t->Valid);
PackAddInt64(p, "SystemId", t->SystemId);
- PackAddInt64(p, "SystemExpires", t->SystemExpires);
+ PackAddTime64(p, "SystemExpires", t->SystemExpires);
}
// Listener thread
diff --git a/src/Cedar/Hub.c b/src/Cedar/Hub.c
index 1815b35b..aac02954 100644
--- a/src/Cedar/Hub.c
+++ b/src/Cedar/Hub.c
@@ -681,6 +681,8 @@ void HubOptionStructToData(RPC_ADMIN_OPTION *ao, HUB_OPTION *o, char *hub_name)
{
ADMIN_OPTION *a = LIST_DATA(aol, i);
+ UniStrCpy(a->Descrption, sizeof(a->Descrption), GetHubAdminOptionHelpString(a->Name));
+
Copy(&ao->Items[i], a, sizeof(ADMIN_OPTION));
Free(a);
diff --git a/src/Cedar/Hub.h b/src/Cedar/Hub.h
index 3f11cc38..bde62b1e 100644
--- a/src/Cedar/Hub.h
+++ b/src/Cedar/Hub.h
@@ -287,6 +287,7 @@ struct ADMIN_OPTION
{
char Name[MAX_ADMIN_OPTION_NAME_LEN + 1]; // Name
UINT Value; // Data
+ wchar_t Descrption[MAX_SIZE]; // Descrption
};
// Certificate Revocation List entry
diff --git a/src/Cedar/Listener.c b/src/Cedar/Listener.c
index 42b07a0d..f365ddc8 100644
--- a/src/Cedar/Listener.c
+++ b/src/Cedar/Listener.c
@@ -150,6 +150,9 @@ void TCPAcceptedThread(THREAD *t, void *param)
// Create a connection
c = NewServerConnection(r->Cedar, s, t);
+ AddRef(r->ref);
+ c->Listener = r;
+
// Register to Cedar as a transient connection
AddConnection(c->Cedar, c);
@@ -169,6 +172,8 @@ void TCPAcceptedThread(THREAD *t, void *param)
// Release
SLog(r->Cedar, "LS_CONNECTION_END_1", c->Name);
+ ReleaseListener(c->Listener);
+ c->Listener = NULL;
ReleaseConnection(c);
// Release
diff --git a/src/Cedar/Nat.c b/src/Cedar/Nat.c
index 41c93049..63852fb5 100644
--- a/src/Cedar/Nat.c
+++ b/src/Cedar/Nat.c
@@ -797,18 +797,20 @@ void OutRpcEnumDhcp(PACK *p, RPC_ENUM_DHCP *t)
PackAddInt(p, "NumItem", t->NumItem);
PackAddStr(p, "HubName", t->HubName);
+ PackSetCurrentJsonGroupName(p, "DhcpTable");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_DHCP_ITEM *e = &t->Items[i];
PackAddIntEx(p, "Id", e->Id, i, t->NumItem);
- PackAddInt64Ex(p, "LeasedTime", e->LeasedTime, i, t->NumItem);
- PackAddInt64Ex(p, "ExpireTime", e->ExpireTime, i, t->NumItem);
+ PackAddTime64Ex(p, "LeasedTime", e->LeasedTime, i, t->NumItem);
+ PackAddTime64Ex(p, "ExpireTime", e->ExpireTime, i, t->NumItem);
PackAddDataEx(p, "MacAddress", e->MacAddress, 6, i, t->NumItem);
PackAddIp32Ex(p, "IpAddress", e->IpAddress, i, t->NumItem);
PackAddIntEx(p, "Mask", e->Mask, i, t->NumItem);
PackAddStrEx(p, "Hostname", e->Hostname, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumDhcp(RPC_ENUM_DHCP *t)
{
@@ -865,6 +867,8 @@ void OutRpcEnumNat(PACK *p, RPC_ENUM_NAT *t)
PackAddInt(p, "NumItem", t->NumItem);
PackAddStr(p, "HubName", t->HubName);
+
+ PackSetCurrentJsonGroupName(p, "NatTable");
for (i = 0;i < t->NumItem;i++)
{
RPC_ENUM_NAT_ITEM *e = &t->Items[i];
@@ -877,12 +881,13 @@ void OutRpcEnumNat(PACK *p, RPC_ENUM_NAT *t)
PackAddIp32Ex(p, "DestIp", e->DestIp, i, t->NumItem);
PackAddStrEx(p, "DestHost", e->DestHost, i, t->NumItem);
PackAddIntEx(p, "DestPort", e->DestPort, i, t->NumItem);
- PackAddInt64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumItem);
- PackAddInt64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumItem);
+ PackAddTime64Ex(p, "CreatedTime", e->CreatedTime, i, t->NumItem);
+ PackAddTime64Ex(p, "LastCommTime", e->LastCommTime, i, t->NumItem);
PackAddInt64Ex(p, "SendSize", e->SendSize, i, t->NumItem);
PackAddInt64Ex(p, "RecvSize", e->RecvSize, i, t->NumItem);
PackAddIntEx(p, "TcpStatus", e->TcpStatus, i, t->NumItem);
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcEnumNat(RPC_ENUM_NAT *t)
{
diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c
index 186c70dc..e54dc353 100644
--- a/src/Cedar/Protocol.c
+++ b/src/Cedar/Protocol.c
@@ -9,6 +9,726 @@
static UCHAR ssl_packet_start[3] = {0x17, 0x03, 0x00};
+// MIME list from https://www.freeformatter.com/mime-types-list.html
+static HTTP_MIME_TYPE http_mime_types[] =
+{
+ {".x3d", "application/vnd.hzn-3d-crossword"},
+ {".3gp", "video/3gpp"},
+ {".3g2", "video/3gpp2"},
+ {".mseq", "application/vnd.mseq"},
+ {".pwn", "application/vnd.3m.post-it-notes"},
+ {".plb", "application/vnd.3gpp.pic-bw-large"},
+ {".psb", "application/vnd.3gpp.pic-bw-small"},
+ {".pvb", "application/vnd.3gpp.pic-bw-var"},
+ {".tcap", "application/vnd.3gpp2.tcap"},
+ {".7z", "application/x-7z-compressed"},
+ {".abw", "application/x-abiword"},
+ {".ace", "application/x-ace-compressed"},
+ {".acc", "application/vnd.americandynamics.acc"},
+ {".acu", "application/vnd.acucobol"},
+ {".atc", "application/vnd.acucorp"},
+ {".adp", "audio/adpcm"},
+ {".aab", "application/x-authorware-bin"},
+ {".aam", "application/x-authorware-map"},
+ {".aas", "application/x-authorware-seg"},
+ {".air", "application/vnd.adobe.air-application-installer-package+zip"},
+ {".swf", "application/x-shockwave-flash"},
+ {".fxp", "application/vnd.adobe.fxp"},
+ {".pdf", "application/pdf"},
+ {".ppd", "application/vnd.cups-ppd"},
+ {".dir", "application/x-director"},
+ {".xdp", "application/vnd.adobe.xdp+xml"},
+ {".xfdf", "application/vnd.adobe.xfdf"},
+ {".aac", "audio/x-aac"},
+ {".ahead", "application/vnd.ahead.space"},
+ {".azf", "application/vnd.airzip.filesecure.azf"},
+ {".azs", "application/vnd.airzip.filesecure.azs"},
+ {".azw", "application/vnd.amazon.ebook"},
+ {".ami", "application/vnd.amiga.ami"},
+ {".apk", "application/vnd.android.package-archive"},
+ {".cii", "application/vnd.anser-web-certificate-issue-initiation"},
+ {".fti", "application/vnd.anser-web-funds-transfer-initiation"},
+ {".atx", "application/vnd.antix.game-component"},
+ {".dmg", "application/x-apple-diskimage"},
+ {".mpkg", "application/vnd.apple.installer+xml"},
+ {".aw", "application/applixware"},
+ {".les", "application/vnd.hhe.lesson-player"},
+ {".swi", "application/vnd.aristanetworks.swi"},
+ {".s", "text/x-asm"},
+ {".atomcat", "application/atomcat+xml"},
+ {".atomsvc", "application/atomsvc+xml"},
+ {".atom", "application/atom+xml"},
+ {".ac", "application/pkix-attr-cert"},
+ {".aif", "audio/x-aiff"},
+ {".avi", "video/x-msvideo"},
+ {".aep", "application/vnd.audiograph"},
+ {".dxf", "image/vnd.dxf"},
+ {".dwf", "model/vnd.dwf"},
+ {".par", "text/plain-bas"},
+ {".bcpio", "application/x-bcpio"},
+ {".bin", "application/octet-stream"},
+ {".bmp", "image/bmp"},
+ {".torrent", "application/x-bittorrent"},
+ {".cod", "application/vnd.rim.cod"},
+ {".mpm", "application/vnd.blueice.multipass"},
+ {".bmi", "application/vnd.bmi"},
+ {".sh", "application/x-sh"},
+ {".btif", "image/prs.btif"},
+ {".rep", "application/vnd.businessobjects"},
+ {".bz", "application/x-bzip"},
+ {".bz2", "application/x-bzip2"},
+ {".csh", "application/x-csh"},
+ {".c", "text/x-c"},
+ {".cdxml", "application/vnd.chemdraw+xml"},
+ {".css", "text/css"},
+ {".cdx", "chemical/x-cdx"},
+ {".cml", "chemical/x-cml"},
+ {".csml", "chemical/x-csml"},
+ {".cdbcmsg", "application/vnd.contact.cmsg"},
+ {".cla", "application/vnd.claymore"},
+ {".c4g", "application/vnd.clonk.c4group"},
+ {".sub", "image/vnd.dvb.subtitle"},
+ {".cdmia", "application/cdmi-capability"},
+ {".cdmic", "application/cdmi-container"},
+ {".cdmid", "application/cdmi-domain"},
+ {".cdmio", "application/cdmi-object"},
+ {".cdmiq", "application/cdmi-queue"},
+ {".c11amc", "application/vnd.cluetrust.cartomobile-config"},
+ {".c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"},
+ {".ras", "image/x-cmu-raster"},
+ {".dae", "model/vnd.collada+xml"},
+ {".csv", "text/csv"},
+ {".cpt", "application/mac-compactpro"},
+ {".wmlc", "application/vnd.wap.wmlc"},
+ {".cgm", "image/cgm"},
+ {".ice", "x-conference/x-cooltalk"},
+ {".cmx", "image/x-cmx"},
+ {".xar", "application/vnd.xara"},
+ {".cmc", "application/vnd.cosmocaller"},
+ {".cpio", "application/x-cpio"},
+ {".clkx", "application/vnd.crick.clicker"},
+ {".clkk", "application/vnd.crick.clicker.keyboard"},
+ {".clkp", "application/vnd.crick.clicker.palette"},
+ {".clkt", "application/vnd.crick.clicker.template"},
+ {".clkw", "application/vnd.crick.clicker.wordbank"},
+ {".wbs", "application/vnd.criticaltools.wbs+xml"},
+ {".cryptonote", "application/vnd.rig.cryptonote"},
+ {".cif", "chemical/x-cif"},
+ {".cmdf", "chemical/x-cmdf"},
+ {".cu", "application/cu-seeme"},
+ {".cww", "application/prs.cww"},
+ {".curl", "text/vnd.curl"},
+ {".dcurl", "text/vnd.curl.dcurl"},
+ {".mcurl", "text/vnd.curl.mcurl"},
+ {".scurl", "text/vnd.curl.scurl"},
+ {".car", "application/vnd.curl.car"},
+ {".pcurl", "application/vnd.curl.pcurl"},
+ {".cmp", "application/vnd.yellowriver-custom-menu"},
+ {".dssc", "application/dssc+der"},
+ {".xdssc", "application/dssc+xml"},
+ {".deb", "application/x-debian-package"},
+ {".uva", "audio/vnd.dece.audio"},
+ {".uvi", "image/vnd.dece.graphic"},
+ {".uvh", "video/vnd.dece.hd"},
+ {".uvm", "video/vnd.dece.mobile"},
+ {".uvu", "video/vnd.uvvu.mp4"},
+ {".uvp", "video/vnd.dece.pd"},
+ {".uvs", "video/vnd.dece.sd"},
+ {".uvv", "video/vnd.dece.video"},
+ {".dvi", "application/x-dvi"},
+ {".seed", "application/vnd.fdsn.seed"},
+ {".dtb", "application/x-dtbook+xml"},
+ {".res", "application/x-dtbresource+xml"},
+ {".ait", "application/vnd.dvb.ait"},
+ {".svc", "application/vnd.dvb.service"},
+ {".eol", "audio/vnd.digital-winds"},
+ {".djvu", "image/vnd.djvu"},
+ {".dtd", "application/xml-dtd"},
+ {".mlp", "application/vnd.dolby.mlp"},
+ {".wad", "application/x-doom"},
+ {".dpg", "application/vnd.dpgraph"},
+ {".dra", "audio/vnd.dra"},
+ {".dfac", "application/vnd.dreamfactory"},
+ {".dts", "audio/vnd.dts"},
+ {".dtshd", "audio/vnd.dts.hd"},
+ {".dwg", "image/vnd.dwg"},
+ {".geo", "application/vnd.dynageo"},
+ {".es", "application/ecmascript"},
+ {".mag", "application/vnd.ecowin.chart"},
+ {".mmr", "image/vnd.fujixerox.edmics-mmr"},
+ {".rlc", "image/vnd.fujixerox.edmics-rlc"},
+ {".exi", "application/exi"},
+ {".mgz", "application/vnd.proteus.magazine"},
+ {".epub", "application/epub+zip"},
+ {".eml", "message/rfc822"},
+ {".nml", "application/vnd.enliven"},
+ {".xpr", "application/vnd.is-xpr"},
+ {".xif", "image/vnd.xiff"},
+ {".xfdl", "application/vnd.xfdl"},
+ {".emma", "application/emma+xml"},
+ {".ez2", "application/vnd.ezpix-album"},
+ {".ez3", "application/vnd.ezpix-package"},
+ {".fst", "image/vnd.fst"},
+ {".fvt", "video/vnd.fvt"},
+ {".fbs", "image/vnd.fastbidsheet"},
+ {".fe_launch", "application/vnd.denovo.fcselayout-link"},
+ {".f4v", "video/x-f4v"},
+ {".flv", "video/x-flv"},
+ {".fpx", "image/vnd.fpx"},
+ {".npx", "image/vnd.net-fpx"},
+ {".flx", "text/vnd.fmi.flexstor"},
+ {".fli", "video/x-fli"},
+ {".ftc", "application/vnd.fluxtime.clip"},
+ {".fdf", "application/vnd.fdf"},
+ {".f", "text/x-fortran"},
+ {".mif", "application/vnd.mif"},
+ {".fm", "application/vnd.framemaker"},
+ {".fh", "image/x-freehand"},
+ {".fsc", "application/vnd.fsc.weblaunch"},
+ {".fnc", "application/vnd.frogans.fnc"},
+ {".ltf", "application/vnd.frogans.ltf"},
+ {".ddd", "application/vnd.fujixerox.ddd"},
+ {".xdw", "application/vnd.fujixerox.docuworks"},
+ {".xbd", "application/vnd.fujixerox.docuworks.binder"},
+ {".oas", "application/vnd.fujitsu.oasys"},
+ {".oa2", "application/vnd.fujitsu.oasys2"},
+ {".oa3", "application/vnd.fujitsu.oasys3"},
+ {".fg5", "application/vnd.fujitsu.oasysgp"},
+ {".bh2", "application/vnd.fujitsu.oasysprs"},
+ {".spl", "application/x-futuresplash"},
+ {".fzs", "application/vnd.fuzzysheet"},
+ {".g3", "image/g3fax"},
+ {".gmx", "application/vnd.gmx"},
+ {".gtw", "model/vnd.gtw"},
+ {".txd", "application/vnd.genomatix.tuxedo"},
+ {".ggb", "application/vnd.geogebra.file"},
+ {".ggt", "application/vnd.geogebra.tool"},
+ {".gdl", "model/vnd.gdl"},
+ {".gex", "application/vnd.geometry-explorer"},
+ {".gxt", "application/vnd.geonext"},
+ {".g2w", "application/vnd.geoplan"},
+ {".g3w", "application/vnd.geospace"},
+ {".gsf", "application/x-font-ghostscript"},
+ {".bdf", "application/x-font-bdf"},
+ {".gtar", "application/x-gtar"},
+ {".texinfo", "application/x-texinfo"},
+ {".gnumeric", "application/x-gnumeric"},
+ {".kml", "application/vnd.google-earth.kml+xml"},
+ {".kmz", "application/vnd.google-earth.kmz"},
+ {".gqf", "application/vnd.grafeq"},
+ {".gif", "image/gif"},
+ {".gv", "text/vnd.graphviz"},
+ {".gac", "application/vnd.groove-account"},
+ {".ghf", "application/vnd.groove-help"},
+ {".gim", "application/vnd.groove-identity-message"},
+ {".grv", "application/vnd.groove-injector"},
+ {".gtm", "application/vnd.groove-tool-message"},
+ {".tpl", "application/vnd.groove-tool-template"},
+ {".vcg", "application/vnd.groove-vcard"},
+ {".h261", "video/h261"},
+ {".h263", "video/h263"},
+ {".h264", "video/h264"},
+ {".hpid", "application/vnd.hp-hpid"},
+ {".hps", "application/vnd.hp-hps"},
+ {".hdf", "application/x-hdf"},
+ {".rip", "audio/vnd.rip"},
+ {".hbci", "application/vnd.hbci"},
+ {".jlt", "application/vnd.hp-jlyt"},
+ {".pcl", "application/vnd.hp-pcl"},
+ {".hpgl", "application/vnd.hp-hpgl"},
+ {".hvs", "application/vnd.yamaha.hv-script"},
+ {".hvd", "application/vnd.yamaha.hv-dic"},
+ {".hvp", "application/vnd.yamaha.hv-voice"},
+ {".sfd-hdstx", "application/vnd.hydrostatix.sof-data"},
+ {".stk", "application/hyperstudio"},
+ {".hal", "application/vnd.hal+xml"},
+ {".htm", "text/html; charset=utf-8"},
+ {".html", "text/html; charset=utf-8"},
+ {".irm", "application/vnd.ibm.rights-management"},
+ {".sc", "application/vnd.ibm.secure-container"},
+ {".ics", "text/calendar"},
+ {".icc", "application/vnd.iccprofile"},
+ {".ico", "image/x-icon"},
+ {".igl", "application/vnd.igloader"},
+ {".ief", "image/ief"},
+ {".ivp", "application/vnd.immervision-ivp"},
+ {".ivu", "application/vnd.immervision-ivu"},
+ {".rif", "application/reginfo+xml"},
+ {".3dml", "text/vnd.in3d.3dml"},
+ {".spot", "text/vnd.in3d.spot"},
+ {".igs", "model/iges"},
+ {".i2g", "application/vnd.intergeo"},
+ {".cdy", "application/vnd.cinderella"},
+ {".xpw", "application/vnd.intercon.formnet"},
+ {".fcs", "application/vnd.isac.fcs"},
+ {".ipfix", "application/ipfix"},
+ {".cer", "application/pkix-cert"},
+ {".pki", "application/pkixcmp"},
+ {".crl", "application/pkix-crl"},
+ {".pkipath", "application/pkix-pkipath"},
+ {".igm", "application/vnd.insors.igm"},
+ {".rcprofile", "application/vnd.ipunplugged.rcprofile"},
+ {".irp", "application/vnd.irepository.package+xml"},
+ {".jad", "text/vnd.sun.j2me.app-descriptor"},
+ {".jar", "application/java-archive"},
+ {".class", "application/java-vm"},
+ {".jnlp", "application/x-java-jnlp-file"},
+ {".ser", "application/java-serialized-object"},
+ {".java", "text/x-java-source"},
+ {".js", "application/javascript"},
+ {".json", "application/json"},
+ {".joda", "application/vnd.joost.joda-archive"},
+ {".jpm", "video/jpm"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".pjpeg", "image/pjpeg"},
+ {".jpgv", "video/jpeg"},
+ {".ktz", "application/vnd.kahootz"},
+ {".mmd", "application/vnd.chipnuts.karaoke-mmd"},
+ {".karbon", "application/vnd.kde.karbon"},
+ {".chrt", "application/vnd.kde.kchart"},
+ {".kfo", "application/vnd.kde.kformula"},
+ {".flw", "application/vnd.kde.kivio"},
+ {".kon", "application/vnd.kde.kontour"},
+ {".kpr", "application/vnd.kde.kpresenter"},
+ {".ksp", "application/vnd.kde.kspread"},
+ {".kwd", "application/vnd.kde.kword"},
+ {".htke", "application/vnd.kenameaapp"},
+ {".kia", "application/vnd.kidspiration"},
+ {".kne", "application/vnd.kinar"},
+ {".sse", "application/vnd.kodak-descriptor"},
+ {".lasxml", "application/vnd.las.las+xml"},
+ {".latex", "application/x-latex"},
+ {".lbd", "application/vnd.llamagraphics.life-balance.desktop"},
+ {".lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"},
+ {".jam", "application/vnd.jam"},
+ {"0.123", "application/vnd.lotus-1-2-3"},
+ {".apr", "application/vnd.lotus-approach"},
+ {".pre", "application/vnd.lotus-freelance"},
+ {".nsf", "application/vnd.lotus-notes"},
+ {".org", "application/vnd.lotus-organizer"},
+ {".scm", "application/vnd.lotus-screencam"},
+ {".lwp", "application/vnd.lotus-wordpro"},
+ {".lvp", "audio/vnd.lucent.voice"},
+ {".m3u", "audio/x-mpegurl"},
+ {".m4v", "video/x-m4v"},
+ {".hqx", "application/mac-binhex40"},
+ {".portpkg", "application/vnd.macports.portpkg"},
+ {".mgp", "application/vnd.osgeo.mapguide.package"},
+ {".mrc", "application/marc"},
+ {".mrcx", "application/marcxml+xml"},
+ {".mxf", "application/mxf"},
+ {".nbp", "application/vnd.wolfram.player"},
+ {".ma", "application/mathematica"},
+ {".mathml", "application/mathml+xml"},
+ {".mbox", "application/mbox"},
+ {".mc1", "application/vnd.medcalcdata"},
+ {".mscml", "application/mediaservercontrol+xml"},
+ {".cdkey", "application/vnd.mediastation.cdkey"},
+ {".mwf", "application/vnd.mfer"},
+ {".mfm", "application/vnd.mfmp"},
+ {".msh", "model/mesh"},
+ {".mads", "application/mads+xml"},
+ {".mets", "application/mets+xml"},
+ {".mods", "application/mods+xml"},
+ {".meta4", "application/metalink4+xml"},
+ {".mcd", "application/vnd.mcd"},
+ {".flo", "application/vnd.micrografx.flo"},
+ {".igx", "application/vnd.micrografx.igx"},
+ {".es3", "application/vnd.eszigno3+xml"},
+ {".mdb", "application/x-msaccess"},
+ {".asf", "video/x-ms-asf"},
+ {".exe", "application/x-msdownload"},
+ {".cil", "application/vnd.ms-artgalry"},
+ {".cab", "application/vnd.ms-cab-compressed"},
+ {".ims", "application/vnd.ms-ims"},
+ {".application", "application/x-ms-application"},
+ {".clp", "application/x-msclip"},
+ {".mdi", "image/vnd.ms-modi"},
+ {".eot", "application/vnd.ms-fontobject"},
+ {".xls", "application/vnd.ms-excel"},
+ {".xlam", "application/vnd.ms-excel.addin.macroenabled.12"},
+ {".xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"},
+ {".xltm", "application/vnd.ms-excel.template.macroenabled.12"},
+ {".xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"},
+ {".chm", "application/vnd.ms-htmlhelp"},
+ {".crd", "application/x-mscardfile"},
+ {".lrm", "application/vnd.ms-lrm"},
+ {".mvb", "application/x-msmediaview"},
+ {".mny", "application/x-msmoney"},
+ {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
+ {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
+ {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
+ {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
+ {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
+ {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
+ {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
+ {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
+ {".obd", "application/x-msbinder"},
+ {".thmx", "application/vnd.ms-officetheme"},
+ {".onetoc", "application/onenote"},
+ {".pya", "audio/vnd.ms-playready.media.pya"},
+ {".pyv", "video/vnd.ms-playready.media.pyv"},
+ {".ppt", "application/vnd.ms-powerpoint"},
+ {".ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"},
+ {".sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"},
+ {".pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"},
+ {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"},
+ {".potm", "application/vnd.ms-powerpoint.template.macroenabled.12"},
+ {".mpp", "application/vnd.ms-project"},
+ {".pub", "application/x-mspublisher"},
+ {".scd", "application/x-msschedule"},
+ {".xap", "application/x-silverlight-app"},
+ {".stl", "application/vnd.ms-pki.stl"},
+ {".cat", "application/vnd.ms-pki.seccat"},
+ {".vsd", "application/vnd.visio"},
+ {".vsdx", "application/vnd.visio2013"},
+ {".wm", "video/x-ms-wm"},
+ {".wma", "audio/x-ms-wma"},
+ {".wax", "audio/x-ms-wax"},
+ {".wmx", "video/x-ms-wmx"},
+ {".wmd", "application/x-ms-wmd"},
+ {".wpl", "application/vnd.ms-wpl"},
+ {".wmz", "application/x-ms-wmz"},
+ {".wmv", "video/x-ms-wmv"},
+ {".wvx", "video/x-ms-wvx"},
+ {".wmf", "application/x-msmetafile"},
+ {".trm", "application/x-msterminal"},
+ {".doc", "application/msword"},
+ {".docm", "application/vnd.ms-word.document.macroenabled.12"},
+ {".dotm", "application/vnd.ms-word.template.macroenabled.12"},
+ {".wri", "application/x-mswrite"},
+ {".wps", "application/vnd.ms-works"},
+ {".xbap", "application/x-ms-xbap"},
+ {".xps", "application/vnd.ms-xpsdocument"},
+ {".mid", "audio/midi"},
+ {".mpy", "application/vnd.ibm.minipay"},
+ {".afp", "application/vnd.ibm.modcap"},
+ {".rms", "application/vnd.jcp.javame.midlet-rms"},
+ {".tmo", "application/vnd.tmobile-livetv"},
+ {".prc", "application/x-mobipocket-ebook"},
+ {".mbk", "application/vnd.mobius.mbk"},
+ {".dis", "application/vnd.mobius.dis"},
+ {".plc", "application/vnd.mobius.plc"},
+ {".mqy", "application/vnd.mobius.mqy"},
+ {".msl", "application/vnd.mobius.msl"},
+ {".txf", "application/vnd.mobius.txf"},
+ {".daf", "application/vnd.mobius.daf"},
+ {".fly", "text/vnd.fly"},
+ {".mpc", "application/vnd.mophun.certificate"},
+ {".mpn", "application/vnd.mophun.application"},
+ {".mj2", "video/mj2"},
+ {".mpga", "audio/mpeg"},
+ {".mxu", "video/vnd.mpegurl"},
+ {".mpeg", "video/mpeg"},
+ {".m21", "application/mp21"},
+ {".mp4a", "audio/mp4"},
+ {".mp4", "video/mp4"},
+ {".mp4", "application/mp4"},
+ {".m3u8", "application/vnd.apple.mpegurl"},
+ {".mus", "application/vnd.musician"},
+ {".msty", "application/vnd.muvee.style"},
+ {".mxml", "application/xv+xml"},
+ {".ngdat", "application/vnd.nokia.n-gage.data"},
+ {".n-gage", "application/vnd.nokia.n-gage.symbian.install"},
+ {".ncx", "application/x-dtbncx+xml"},
+ {".nc", "application/x-netcdf"},
+ {".nlu", "application/vnd.neurolanguage.nlu"},
+ {".dna", "application/vnd.dna"},
+ {".nnd", "application/vnd.noblenet-directory"},
+ {".nns", "application/vnd.noblenet-sealer"},
+ {".nnw", "application/vnd.noblenet-web"},
+ {".rpst", "application/vnd.nokia.radio-preset"},
+ {".rpss", "application/vnd.nokia.radio-presets"},
+ {".n3", "text/n3"},
+ {".edm", "application/vnd.novadigm.edm"},
+ {".edx", "application/vnd.novadigm.edx"},
+ {".ext", "application/vnd.novadigm.ext"},
+ {".gph", "application/vnd.flographit"},
+ {".ecelp4800", "audio/vnd.nuera.ecelp4800"},
+ {".ecelp7470", "audio/vnd.nuera.ecelp7470"},
+ {".ecelp9600", "audio/vnd.nuera.ecelp9600"},
+ {".oda", "application/oda"},
+ {".ogx", "application/ogg"},
+ {".oga", "audio/ogg"},
+ {".ogv", "video/ogg"},
+ {".dd2", "application/vnd.oma.dd2+xml"},
+ {".oth", "application/vnd.oasis.opendocument.text-web"},
+ {".opf", "application/oebps-package+xml"},
+ {".qbo", "application/vnd.intu.qbo"},
+ {".oxt", "application/vnd.openofficeorg.extension"},
+ {".osf", "application/vnd.yamaha.openscoreformat"},
+ {".weba", "audio/webm"},
+ {".webm", "video/webm"},
+ {".odc", "application/vnd.oasis.opendocument.chart"},
+ {".otc", "application/vnd.oasis.opendocument.chart-template"},
+ {".odb", "application/vnd.oasis.opendocument.database"},
+ {".odf", "application/vnd.oasis.opendocument.formula"},
+ {".odft", "application/vnd.oasis.opendocument.formula-template"},
+ {".odg", "application/vnd.oasis.opendocument.graphics"},
+ {".otg", "application/vnd.oasis.opendocument.graphics-template"},
+ {".odi", "application/vnd.oasis.opendocument.image"},
+ {".oti", "application/vnd.oasis.opendocument.image-template"},
+ {".odp", "application/vnd.oasis.opendocument.presentation"},
+ {".otp", "application/vnd.oasis.opendocument.presentation-template"},
+ {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
+ {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
+ {".odt", "application/vnd.oasis.opendocument.text"},
+ {".odm", "application/vnd.oasis.opendocument.text-master"},
+ {".ott", "application/vnd.oasis.opendocument.text-template"},
+ {".ktx", "image/ktx"},
+ {".sxc", "application/vnd.sun.xml.calc"},
+ {".stc", "application/vnd.sun.xml.calc.template"},
+ {".sxd", "application/vnd.sun.xml.draw"},
+ {".std", "application/vnd.sun.xml.draw.template"},
+ {".sxi", "application/vnd.sun.xml.impress"},
+ {".sti", "application/vnd.sun.xml.impress.template"},
+ {".sxm", "application/vnd.sun.xml.math"},
+ {".sxw", "application/vnd.sun.xml.writer"},
+ {".sxg", "application/vnd.sun.xml.writer.global"},
+ {".stw", "application/vnd.sun.xml.writer.template"},
+ {".otf", "application/x-font-otf"},
+ {".osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"},
+ {".dp", "application/vnd.osgi.dp"},
+ {".pdb", "application/vnd.palm"},
+ {".p", "text/x-pascal"},
+ {".paw", "application/vnd.pawaafile"},
+ {".pclxl", "application/vnd.hp-pclxl"},
+ {".efif", "application/vnd.picsel"},
+ {".pcx", "image/x-pcx"},
+ {".psd", "image/vnd.adobe.photoshop"},
+ {".prf", "application/pics-rules"},
+ {".pic", "image/x-pict"},
+ {".chat", "application/x-chat"},
+ {".p10", "application/pkcs10"},
+ {".p12", "application/x-pkcs12"},
+ {".p7m", "application/pkcs7-mime"},
+ {".p7s", "application/pkcs7-signature"},
+ {".p7r", "application/x-pkcs7-certreqresp"},
+ {".p7b", "application/x-pkcs7-certificates"},
+ {".p8", "application/pkcs8"},
+ {".plf", "application/vnd.pocketlearn"},
+ {".pnm", "image/x-portable-anymap"},
+ {".pbm", "image/x-portable-bitmap"},
+ {".pcf", "application/x-font-pcf"},
+ {".pfr", "application/font-tdpfr"},
+ {".pgn", "application/x-chess-pgn"},
+ {".pgm", "image/x-portable-graymap"},
+ {".png", "image/png"},
+ {".png", "image/x-citrix-png"},
+ {".png", "image/x-png"},
+ {".ppm", "image/x-portable-pixmap"},
+ {".pskcxml", "application/pskc+xml"},
+ {".pml", "application/vnd.ctc-posml"},
+ {".ai", "application/postscript"},
+ {".pfa", "application/x-font-type1"},
+ {".pbd", "application/vnd.powerbuilder6"},
+ {".pgp", "application/pgp-encrypted"},
+ {".pgp", "application/pgp-signature"},
+ {".box", "application/vnd.previewsystems.box"},
+ {".ptid", "application/vnd.pvi.ptid1"},
+ {".pls", "application/pls+xml"},
+ {".str", "application/vnd.pg.format"},
+ {".ei6", "application/vnd.pg.osasli"},
+ {".dsc", "text/prs.lines.tag"},
+ {".psf", "application/x-font-linux-psf"},
+ {".qps", "application/vnd.publishare-delta-tree"},
+ {".wg", "application/vnd.pmi.widget"},
+ {".qxd", "application/vnd.quark.quarkxpress"},
+ {".esf", "application/vnd.epson.esf"},
+ {".msf", "application/vnd.epson.msf"},
+ {".ssf", "application/vnd.epson.ssf"},
+ {".qam", "application/vnd.epson.quickanime"},
+ {".qfx", "application/vnd.intu.qfx"},
+ {".qt", "video/quicktime"},
+ {".rar", "application/x-rar-compressed"},
+ {".ram", "audio/x-pn-realaudio"},
+ {".rmp", "audio/x-pn-realaudio-plugin"},
+ {".rsd", "application/rsd+xml"},
+ {".rm", "application/vnd.rn-realmedia"},
+ {".bed", "application/vnd.realvnc.bed"},
+ {".mxl", "application/vnd.recordare.musicxml"},
+ {".musicxml", "application/vnd.recordare.musicxml+xml"},
+ {".rnc", "application/relax-ng-compact-syntax"},
+ {".rdz", "application/vnd.data-vision.rdz"},
+ {".rdf", "application/rdf+xml"},
+ {".rp9", "application/vnd.cloanto.rp9"},
+ {".jisp", "application/vnd.jisp"},
+ {".rtf", "application/rtf"},
+ {".rtx", "text/richtext"},
+ {".link66", "application/vnd.route66.link66+xml"},
+ {".rss", "application/rss+xml"},
+ {".shf", "application/shf+xml"},
+ {".st", "application/vnd.sailingtracker.track"},
+ {".svg", "image/svg+xml"},
+ {".sus", "application/vnd.sus-calendar"},
+ {".sru", "application/sru+xml"},
+ {".setpay", "application/set-payment-initiation"},
+ {".setreg", "application/set-registration-initiation"},
+ {".sema", "application/vnd.sema"},
+ {".semd", "application/vnd.semd"},
+ {".semf", "application/vnd.semf"},
+ {".see", "application/vnd.seemail"},
+ {".snf", "application/x-font-snf"},
+ {".spq", "application/scvp-vp-request"},
+ {".spp", "application/scvp-vp-response"},
+ {".scq", "application/scvp-cv-request"},
+ {".scs", "application/scvp-cv-response"},
+ {".sdp", "application/sdp"},
+ {".etx", "text/x-setext"},
+ {".movie", "video/x-sgi-movie"},
+ {".ifm", "application/vnd.shana.informed.formdata"},
+ {".itp", "application/vnd.shana.informed.formtemplate"},
+ {".iif", "application/vnd.shana.informed.interchange"},
+ {".ipk", "application/vnd.shana.informed.package"},
+ {".tfi", "application/thraud+xml"},
+ {".shar", "application/x-shar"},
+ {".rgb", "image/x-rgb"},
+ {".slt", "application/vnd.epson.salt"},
+ {".aso", "application/vnd.accpac.simply.aso"},
+ {".imp", "application/vnd.accpac.simply.imp"},
+ {".twd", "application/vnd.simtech-mindmapper"},
+ {".csp", "application/vnd.commonspace"},
+ {".saf", "application/vnd.yamaha.smaf-audio"},
+ {".mmf", "application/vnd.smaf"},
+ {".spf", "application/vnd.yamaha.smaf-phrase"},
+ {".teacher", "application/vnd.smart.teacher"},
+ {".svd", "application/vnd.svd"},
+ {".rq", "application/sparql-query"},
+ {".srx", "application/sparql-results+xml"},
+ {".gram", "application/srgs"},
+ {".grxml", "application/srgs+xml"},
+ {".ssml", "application/ssml+xml"},
+ {".skp", "application/vnd.koan"},
+ {".sgml", "text/sgml"},
+ {".sdc", "application/vnd.stardivision.calc"},
+ {".sda", "application/vnd.stardivision.draw"},
+ {".sdd", "application/vnd.stardivision.impress"},
+ {".smf", "application/vnd.stardivision.math"},
+ {".sdw", "application/vnd.stardivision.writer"},
+ {".sgl", "application/vnd.stardivision.writer-global"},
+ {".sm", "application/vnd.stepmania.stepchart"},
+ {".sit", "application/x-stuffit"},
+ {".sitx", "application/x-stuffitx"},
+ {".sdkm", "application/vnd.solent.sdkm+xml"},
+ {".xo", "application/vnd.olpc-sugar"},
+ {".au", "audio/basic"},
+ {".wqd", "application/vnd.wqd"},
+ {".sis", "application/vnd.symbian.install"},
+ {".smi", "application/smil+xml"},
+ {".xsm", "application/vnd.syncml+xml"},
+ {".bdm", "application/vnd.syncml.dm+wbxml"},
+ {".xdm", "application/vnd.syncml.dm+xml"},
+ {".sv4cpio", "application/x-sv4cpio"},
+ {".sv4crc", "application/x-sv4crc"},
+ {".sbml", "application/sbml+xml"},
+ {".tsv", "text/tab-separated-values"},
+ {".tiff", "image/tiff"},
+ {".tao", "application/vnd.tao.intent-module-archive"},
+ {".tar", "application/x-tar"},
+ {".tcl", "application/x-tcl"},
+ {".tex", "application/x-tex"},
+ {".tfm", "application/x-tex-tfm"},
+ {".tei", "application/tei+xml"},
+ {".txt", "text/plain; charset=utf-8"},
+ {".md", "text/markdown; charset=utf-8"},
+ {".dxp", "application/vnd.spotfire.dxp"},
+ {".sfs", "application/vnd.spotfire.sfs"},
+ {".tsd", "application/timestamped-data"},
+ {".tpt", "application/vnd.trid.tpt"},
+ {".mxs", "application/vnd.triscape.mxs"},
+ {".t", "text/troff"},
+ {".tra", "application/vnd.trueapp"},
+ {".ttf", "application/x-font-ttf"},
+ {".ttl", "text/turtle"},
+ {".umj", "application/vnd.umajin"},
+ {".uoml", "application/vnd.uoml+xml"},
+ {".unityweb", "application/vnd.unity"},
+ {".ufd", "application/vnd.ufdl"},
+ {".uri", "text/uri-list"},
+ {".utz", "application/vnd.uiq.theme"},
+ {".ustar", "application/x-ustar"},
+ {".uu", "text/x-uuencode"},
+ {".vcs", "text/x-vcalendar"},
+ {".vcf", "text/x-vcard"},
+ {".vcd", "application/x-cdlink"},
+ {".vsf", "application/vnd.vsf"},
+ {".wrl", "model/vrml"},
+ {".vcx", "application/vnd.vcx"},
+ {".mts", "model/vnd.mts"},
+ {".vtu", "model/vnd.vtu"},
+ {".vis", "application/vnd.visionary"},
+ {".viv", "video/vnd.vivo"},
+ {".ccxml", "application/ccxml+xml"},
+ {".vxml", "application/voicexml+xml"},
+ {".src", "application/x-wais-source"},
+ {".wbxml", "application/vnd.wap.wbxml"},
+ {".wbmp", "image/vnd.wap.wbmp"},
+ {".wav", "audio/x-wav"},
+ {".davmount", "application/davmount+xml"},
+ {".woff", "application/x-font-woff"},
+ {".wspolicy", "application/wspolicy+xml"},
+ {".webp", "image/webp"},
+ {".wtb", "application/vnd.webturbo"},
+ {".wgt", "application/widget"},
+ {".hlp", "application/winhlp"},
+ {".wml", "text/vnd.wap.wml"},
+ {".wmls", "text/vnd.wap.wmlscript"},
+ {".wmlsc", "application/vnd.wap.wmlscriptc"},
+ {".wpd", "application/vnd.wordperfect"},
+ {".stf", "application/vnd.wt.stf"},
+ {".wsdl", "application/wsdl+xml"},
+ {".xbm", "image/x-xbitmap"},
+ {".xpm", "image/x-xpixmap"},
+ {".xwd", "image/x-xwindowdump"},
+ {".der", "application/x-x509-ca-cert"},
+ {".fig", "application/x-xfig"},
+ {".xhtml", "application/xhtml+xml"},
+ {".xml", "application/xml"},
+ {".xdf", "application/xcap-diff+xml"},
+ {".xenc", "application/xenc+xml"},
+ {".xer", "application/patch-ops-error+xml"},
+ {".rl", "application/resource-lists+xml"},
+ {".rs", "application/rls-services+xml"},
+ {".rld", "application/resource-lists-diff+xml"},
+ {".xslt", "application/xslt+xml"},
+ {".xop", "application/xop+xml"},
+ {".xpi", "application/x-xpinstall"},
+ {".xspf", "application/xspf+xml"},
+ {".xul", "application/vnd.mozilla.xul+xml"},
+ {".xyz", "chemical/x-xyz"},
+ {".yaml", "text/yaml"},
+ {".yang", "application/yang"},
+ {".yin", "application/yin+xml"},
+ {".zir", "application/vnd.zul"},
+ {".zip", "application/zip"},
+ {".zmm", "application/vnd.handheld-entertainment+xml"},
+ {".zaz", "application/vnd.zzazz.deck+xml"},
+};
+
+// Get HTTP MIME type from filename
+char *GetMimeTypeFromFileName(char *filename)
+{
+ UINT i;
+ UINT num = sizeof(http_mime_types) / sizeof(HTTP_MIME_TYPE);
+ if (filename == NULL)
+ {
+ return NULL;
+ }
+
+ for (i = 0;i < num;i++)
+ {
+ HTTP_MIME_TYPE *a = &http_mime_types[i];
+
+ if (EndWith(filename, a->Extension))
+ {
+ return a->MimeType;
+ }
+ }
+
+ return NULL;
+}
+
// Download and save intermediate certificates if necessary
bool DownloadAndSaveIntermediateCertificatesIfNecessary(X *x)
{
@@ -1183,6 +1903,11 @@ bool ServerAccept(CONNECTION *c)
error_detail_2 = NULL;
if (ServerDownloadSignature(c, &error_detail_2) == false)
{
+ if (c->Type == CONNECTION_TYPE_ADMIN_RPC)
+ {
+ c->Err = ERR_NO_ERROR;
+ }
+
if (error_detail_2 == NULL)
{
error_detail = "ServerDownloadSignature";
@@ -5045,11 +5770,11 @@ PACK *PackWelcome(SESSION *s)
}
#define PACK_ADD_POLICY_BOOL(name, value) \
- PackAddInt(p, "policy:" name, y->value == false ? 0 : 1)
+ PackAddBool(p, "policy:" name, y->value == false ? 0 : 1)
#define PACK_ADD_POLICY_UINT(name, value) \
PackAddInt(p, "policy:" name, y->value)
#define PACK_GET_POLICY_BOOL(name, value) \
- y->value = (PackGetInt(p, "policy:" name) == 0 ? false : true)
+ y->value = (PackGetBool(p, "policy:" name))
#define PACK_GET_POLICY_UINT(name, value) \
y->value = PackGetInt(p, "policy:" name)
@@ -5560,6 +6285,10 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
if (h == NULL)
{
c->Err = ERR_CLIENT_IS_NOT_VPN;
+ if (c->IsJsonRpc)
+ {
+ c->Err = ERR_DISCONNECTED;
+ }
return false;
}
@@ -5568,6 +6297,43 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
{
// Receive the data since it's POST
data_size = GetContentLength(h);
+
+ if (server->DisableJsonRpcWebApi == false)
+ {
+ if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0)
+ {
+ c->IsJsonRpc = true;
+ c->Type = CONNECTION_TYPE_ADMIN_RPC;
+
+ JsonRpcProcPost(c, s, h, data_size);
+
+ FreeHttpHeader(h);
+
+ if (c->JsonRpcAuthed)
+ {
+ num = 0;
+ }
+
+ continue;
+ }
+ else if (StartWith(h->Target, "/admin"))
+ {
+ c->IsJsonRpc = true;
+ c->Type = CONNECTION_TYPE_ADMIN_RPC;
+
+ AdminWebProcPost(c, s, h, data_size, h->Target);
+
+ FreeHttpHeader(h);
+
+ if (c->JsonRpcAuthed)
+ {
+ num = 0;
+ }
+
+ continue;
+ }
+ }
+
if ((data_size > MAX_WATERMARK_SIZE || data_size < SizeOfWaterMark()) && (data_size != StrLen(HTTP_VPN_TARGET_POSTDATA)))
{
// Data is too large
@@ -5616,6 +6382,25 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
}
}
}
+ else if (StrCmpi(h->Method, "OPTIONS") == 0)
+ {
+ if (server->DisableJsonRpcWebApi == false)
+ {
+ if (StrCmpi(h->Target, "/api") == 0 || StrCmpi(h->Target, "/api/") == 0 || StartWith(h->Target, "/admin"))
+ {
+ c->IsJsonRpc = true;
+ c->Type = CONNECTION_TYPE_ADMIN_RPC;
+
+ JsonRpcProcOptions(c, s, h, h->Target);
+
+ FreeHttpHeader(h);
+
+ num = 0;
+
+ continue;
+ }
+ }
+ }
else if (StrCmpi(h->Method, "SSTP_DUPLEX_POST") == 0 && (server->DisableSSTPServer == false || s->IsReverseAcceptedSocket
) &&
GetServerCapsBool(server, "b_support_sstp") && GetNoSstp() == false)
@@ -5733,6 +6518,45 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str)
}
}
+ if (b == false)
+ {
+ if (server->DisableJsonRpcWebApi == false)
+ {
+ if (StartWith(h->Target, "/api?") || StartWith(h->Target, "/api/") || StrCmpi(h->Target, "/api") == 0)
+ {
+ c->IsJsonRpc = true;
+ c->Type = CONNECTION_TYPE_ADMIN_RPC;
+
+ JsonRpcProcGet(c, s, h, h->Target);
+
+ if (c->JsonRpcAuthed)
+ {
+ num = 0;
+ }
+
+ FreeHttpHeader(h);
+
+ continue;
+ }
+ else if (StartWith(h->Target, "/admin"))
+ {
+ c->IsJsonRpc = true;
+ c->Type = CONNECTION_TYPE_ADMIN_RPC;
+
+ AdminWebProcGet(c, s, h, h->Target);
+
+ if (c->JsonRpcAuthed)
+ {
+ num = 0;
+ }
+
+ FreeHttpHeader(h);
+
+ continue;
+ }
+ }
+ }
+
if (b == false)
{
// Not Found
diff --git a/src/Cedar/Protocol.h b/src/Cedar/Protocol.h
index dbec853b..78e4b6ef 100644
--- a/src/Cedar/Protocol.h
+++ b/src/Cedar/Protocol.h
@@ -8,6 +8,13 @@
#ifndef PROTOCOL_H
#define PROTOCOL_H
+// MIME types
+struct HTTP_MIME_TYPE
+{
+ char *Extension;
+ char *MimeType;
+};
+
// The parameters that will be passed to the certificate confirmation thread
struct CHECK_CERT_THREAD_PROC
{
@@ -190,6 +197,6 @@ X *FindCertIssuerFromCertList(LIST *o, X *x);
bool TryGetRootCertChain(LIST *o, X *x, bool auto_save, X **found_root_x);
bool TryGetParentCertFromCertList(LIST *o, X *x, LIST *found_chain);
bool DownloadAndSaveIntermediateCertificatesIfNecessary(X *x);
-
+char *GetMimeTypeFromFileName(char *filename);
#endif // PROTOCOL_H
diff --git a/src/Cedar/Remote.c b/src/Cedar/Remote.c
index bdde16db..742c83c3 100644
--- a/src/Cedar/Remote.c
+++ b/src/Cedar/Remote.c
@@ -15,6 +15,10 @@ void EndRpc(RPC *rpc)
// Release the RPC
void RpcFree(RPC *rpc)
+{
+ RpcFreeEx(rpc, false);
+}
+void RpcFreeEx(RPC *rpc, bool no_disconnect)
{
// Validate arguments
if (rpc == NULL)
@@ -22,7 +26,11 @@ void RpcFree(RPC *rpc)
return;
}
- Disconnect(rpc->Sock);
+ if (no_disconnect == false)
+ {
+ Disconnect(rpc->Sock);
+ }
+
ReleaseSock(rpc->Sock);
DeleteLock(rpc->Lock);
diff --git a/src/Cedar/Remote.h b/src/Cedar/Remote.h
index 835590f0..49de5056 100644
--- a/src/Cedar/Remote.h
+++ b/src/Cedar/Remote.h
@@ -42,6 +42,7 @@ bool RpcIsOk(PACK *p);
UINT RpcGetError(PACK *p);
void EndRpc(RPC *rpc);
void RpcFree(RPC *rpc);
+void RpcFreeEx(RPC *rpc, bool no_disconnect);
#endif // REMOTE_H
diff --git a/src/Cedar/Server.c b/src/Cedar/Server.c
index 4f90b01f..4f268bb4 100644
--- a/src/Cedar/Server.c
+++ b/src/Cedar/Server.c
@@ -974,52 +974,70 @@ LIST *EnumLogFile(char *hubname)
// Enumerate in the packet_log
Format(tmp, sizeof(tmp), "%s/packet_log", exe_dir);
- dir = EnumDir(tmp);
- if (dir != NULL)
+
+ if (hubname == NULL)
{
- UINT i;
- for (i = 0;i < dir->NumFiles;i++)
+ dir = EnumDir(tmp);
+ if (dir != NULL)
{
- DIRENT *e = dir->File[i];
-
- if (e->Folder)
+ UINT i;
+ for (i = 0;i < dir->NumFiles;i++)
{
- char dir_name[MAX_PATH];
+ DIRENT *e = dir->File[i];
- if (hubname == NULL || StrCmpi(hubname, e->FileName) == 0)
+ if (e->Folder)
{
+ char dir_name[MAX_PATH];
Format(dir_name, sizeof(dir_name), "packet_log/%s", e->FileName);
EnumLogFileDir(o, dir_name);
}
}
- }
- FreeDir(dir);
+ FreeDir(dir);
+ }
+ }
+ else
+ {
+ char dir_name[MAX_PATH];
+
+ Format(dir_name, sizeof(dir_name), "packet_log/%s", hubname);
+
+ EnumLogFileDir(o, dir_name);
}
// Enumerate in the security_log
Format(tmp, sizeof(tmp), "%s/security_log", exe_dir);
- dir = EnumDir(tmp);
- if (dir != NULL)
+
+ if (hubname == NULL)
{
- UINT i;
- for (i = 0;i < dir->NumFiles;i++)
+ dir = EnumDir(tmp);
+ if (dir != NULL)
{
- DIRENT *e = dir->File[i];
-
- if (e->Folder)
+ UINT i;
+ for (i = 0;i < dir->NumFiles;i++)
{
- char dir_name[MAX_PATH];
+ DIRENT *e = dir->File[i];
- if (hubname == NULL || StrCmpi(hubname, e->FileName) == 0)
+ if (e->Folder)
{
+ char dir_name[MAX_PATH];
+
Format(dir_name, sizeof(dir_name), "security_log/%s", e->FileName);
+
EnumLogFileDir(o, dir_name);
}
}
- }
- FreeDir(dir);
+ FreeDir(dir);
+ }
+ }
+ else
+ {
+ char dir_name[MAX_PATH];
+
+ Format(dir_name, sizeof(dir_name), "security_log/%s", hubname);
+
+ EnumLogFileDir(o, dir_name);
}
return o;
@@ -1731,14 +1749,37 @@ void OutRpcCapsList(PACK *p, CAPSLIST *t)
return;
}
+ PackSetCurrentJsonGroupName(p, "CapsList");
for (i = 0;i < LIST_NUM(t->CapsList);i++)
{
char tmp[MAX_SIZE];
+ char ct_key[MAX_PATH];
+ wchar_t ct_description[MAX_PATH];
+ wchar_t *w;
CAPS *c = LIST_DATA(t->CapsList, i);
Format(tmp, sizeof(tmp), "caps_%s", c->Name);
+
+ Format(ct_key, sizeof(ct_key), "CT_%s", c->Name);
+
+ Zero(ct_description, sizeof(ct_description));
+ w = _UU(ct_key);
+ if (UniIsEmptyStr(w) == false)
+ {
+ UniStrCpy(ct_description, sizeof(ct_description), w);
+ }
+ else
+ {
+ StrToUni(ct_description, sizeof(ct_description), c->Name);
+ }
+
PackAddInt(p, tmp, c->Value);
+
+ PackAddStrEx(p, "CapsName", c->Name, i, LIST_NUM(t->CapsList));
+ PackAddIntEx(p, "CapsValue", c->Value, i, LIST_NUM(t->CapsList));
+ PackAddUniStrEx(p, "CapsDescrption", ct_description, i, LIST_NUM(t->CapsList));
}
+ PackSetCurrentJsonGroupName(p, NULL);
}
void FreeRpcCapsList(CAPSLIST *t)
{
@@ -5982,7 +6023,11 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f)
c->SslAcceptSettings.Tls_Disable1_2 = CfgGetBool(f, "Tls_Disable1_2");
s->StrictSyslogDatetimeFormat = CfgGetBool(f, "StrictSyslogDatetimeFormat");
- // Bits of Diffie-Hellman parameters
+
+ // Disable JSON-RPC Web API
+ s->DisableJsonRpcWebApi = CfgGetBool(f, "DisableJsonRpcWebApi");
+
+ // Bits of Diffie-Hellman parameters
c->DhParamBits = CfgGetInt(f, "DhParamBits");
if (c->DhParamBits == 0)
{
@@ -6314,6 +6359,9 @@ void SiWriteServerCfg(FOLDER *f, SERVER *s)
CfgAddBool(f, "DisableSessionReconnect", GetGlobalServerFlag(GSF_DISABLE_SESSION_RECONNECT));
CfgAddBool(f, "StrictSyslogDatetimeFormat", s->StrictSyslogDatetimeFormat);
+
+ // Disable JSON-RPC Web API
+ CfgAddBool(f, "DisableJsonRpcWebApi", s->DisableJsonRpcWebApi);
}
Unlock(c->lock);
}
@@ -7031,7 +7079,7 @@ FARM_MEMBER *SiGetNextFarmMember(SERVER *s, CONNECTION *c, HUB *h)
PackAddIntEx(p, "NumTcpConnections", f->NumTcpConnections, i, num);
PackAddIntEx(p, "NumHubs", LIST_NUM(f->HubList), i, num);
PackAddBoolEx(p, "Me", f->Me, i, num);
- PackAddInt64Ex(p, "ConnectedTime", f->ConnectedTime, i, num);
+ PackAddTime64Ex(p, "ConnectedTime", f->ConnectedTime, i, num);
PackAddInt64Ex(p, "SystemId", f->SystemId, i, num);
PackAddBoolEx(p, "DoNotSelect", do_not_select, i, num);
}
@@ -7060,7 +7108,7 @@ FARM_MEMBER *SiGetNextFarmMember(SERVER *s, CONNECTION *c, HUB *h)
PackAddStr(p, "CipherName", c->CipherName);
PackAddStr(p, "ClientStr", c->ClientStr);
PackAddInt(p, "ClientVer", c->ClientVer);
- PackAddInt64(p, "ConnectedTime", Tick64ToTime64(c->ConnectedTick));
+ PackAddTime64(p, "ConnectedTime", Tick64ToTime64(c->ConnectedTick));
PackAddStr(p, "HubName", h->Name);
PackAddBool(p, "StaticHub", h->Type == HUB_TYPE_FARM_STATIC);
@@ -7200,8 +7248,8 @@ void SiCalledEnumHub(SERVER *s, PACK *p, PACK *req)
PackAddIntEx(p, "NumIpTables", LIST_NUM(h->IpTable), i, num);
- PackAddInt64Ex(p, "LastCommTime", h->LastCommTime, i, num);
- PackAddInt64Ex(p, "CreatedTime", h->CreatedTime, i, num);
+ PackAddTime64Ex(p, "LastCommTime", h->LastCommTime, i, num);
+ PackAddTime64Ex(p, "CreatedTime", h->CreatedTime, i, num);
}
Unlock(h->lock);
}
diff --git a/src/Cedar/Server.h b/src/Cedar/Server.h
index b9be8a3f..5e9233e6 100644
--- a/src/Cedar/Server.h
+++ b/src/Cedar/Server.h
@@ -267,6 +267,7 @@ struct SERVER
IP ListenIP; // Listen IP
bool StrictSyslogDatetimeFormat; // Make syslog datetime format strict RFC3164
+ bool DisableJsonRpcWebApi; // Disable JSON-RPC Web API
};
@@ -290,6 +291,7 @@ struct RPC_SESSION_STATUS
RPC_CLIENT_GET_CONNECTION_STATUS Status; // Status
UINT ClientIp; // Client IP address
UCHAR ClientIp6[16]; // Client IPv6 address
+ IP ClientIpAddress; // Client IP address (IPv4/IPv6)
char ClientHostName[MAX_HOST_NAME_LEN + 1]; // Client host name
NODE_INFO NodeInfo; // Node information
};
diff --git a/src/Cedar/Session.c b/src/Cedar/Session.c
index 64071115..ab3bc970 100644
--- a/src/Cedar/Session.c
+++ b/src/Cedar/Session.c
@@ -108,9 +108,9 @@ void SessionMain(SESSION *s)
s->NumConnectionsEstablished++;
s->CurrentConnectionEstablishTime = Tick64();
- if (s->FirstConnectionEstablishedTime == 0)
+ if (s->FirstConnectionEstablisiedTime == 0) /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
{
- s->FirstConnectionEstablishedTime = Tick64();
+ s->FirstConnectionEstablisiedTime = Tick64(); /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
}
if (s->ServerMode == false && s->Cedar->Client != NULL)
@@ -1158,7 +1158,10 @@ void StopSessionEx(SESSION *s, bool no_wait)
// Server and client mode
if (s->Connection)
{
- StopConnection(s->Connection, no_wait);
+ CONNECTION *c = s->Connection;
+ AddRef(c->ref);
+ StopConnection(c, no_wait);
+ ReleaseConnection(c);
}
// Wait until the stop
diff --git a/src/Cedar/Session.h b/src/Cedar/Session.h
index ba65f026..a17e0547 100644
--- a/src/Cedar/Session.h
+++ b/src/Cedar/Session.h
@@ -157,7 +157,8 @@ struct SESSION
UINT NumDisconnected; // Number of socket disconnection
bool NoReconnectToSession; // Disable to reconnect to the session
char UnderlayProtocol[64]; // Physical communication protocol
- UINT64 FirstConnectionEstablishedTime; // Connection completion time of the first connection
+ /* !!! Do not correct the spelling to keep the backward protocol compatibility !!! */
+ UINT64 FirstConnectionEstablisiedTime; // Connection completion time of the first connection
UINT64 CurrentConnectionEstablishTime; // Completion time of this connection
UINT NumConnectionsEstablished; // Number of connections established so far
UINT AdjustMss; // MSS adjustment value
diff --git a/src/Mayaqua/FileIO.c b/src/Mayaqua/FileIO.c
index 2e39bf94..2868de18 100644
--- a/src/Mayaqua/FileIO.c
+++ b/src/Mayaqua/FileIO.c
@@ -1127,6 +1127,12 @@ void BuildHamcore(char *dst_filename, char *src_dir, bool unix_only)
}
}
+ if (InStr(rpath, "\\node_modules\\"))
+ {
+ // Exclude node_modules in the hamcore\webroot
+ ok = false;
+ }
+
if (ok)
{
b = ReadDump(s);
diff --git a/src/Mayaqua/Kernel.c b/src/Mayaqua/Kernel.c
index 2447a9f3..3d1ee3d2 100644
--- a/src/Mayaqua/Kernel.c
+++ b/src/Mayaqua/Kernel.c
@@ -1408,11 +1408,103 @@ void GetDateTimeStrMilli(char *str, UINT size, SYSTEMTIME *st)
st->wMilliseconds);
}
+
+// Convert string RFC3339 format (example: 2017-09-27T18:25:55.434-9:00) to UINT64
+UINT64 DateTimeStrRFC3339ToSystemTime64(char *str)
+{
+ SYSTEMTIME st;
+ if (DateTimeStrRFC3339ToSystemTime(&st, str))
+ {
+ return SystemToUINT64(&st);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Convert string RFC3339 format (example: 2017-09-27T18:25:55.434-9:00) to SYSTEMTIME
+bool DateTimeStrRFC3339ToSystemTime(SYSTEMTIME *st, char *str)
+{
+ bool ok = false;
+ UINT index_plus;
+ char tmp[MAX_PATH];
+ Zero(st, sizeof(SYSTEMTIME));
+ if (st == NULL || str == NULL)
+ {
+ return false;
+ }
+
+ StrCpy(tmp, sizeof(tmp), str);
+
+ index_plus = SearchStrEx(tmp, "+", 0, false);
+ if (index_plus != INFINITE)
+ {
+ tmp[index_plus] = 0;
+ }
+
+ if (StrLen(tmp) >= 19)
+ {
+ if (tmp[4] == '-' && tmp[7] == '-' && tmp[10] == 'T' && tmp[13] == ':' &&
+ tmp[16] == ':')
+ {
+ char str_year[16], str_month[16], str_day[16], str_hour[16], str_minute[16],
+ str_second[16], str_msec[16];
+
+ StrCpy(str_year, sizeof(str_year), tmp + 0);
+ str_year[4] = 0;
+
+ StrCpy(str_month, sizeof(str_month), tmp + 5);
+ str_month[2] = 0;
+
+ StrCpy(str_day, sizeof(str_day), tmp + 8);
+ str_day[2] = 0;
+
+ StrCpy(str_hour, sizeof(str_hour), tmp + 11);
+ str_hour[2] = 0;
+
+ StrCpy(str_minute, sizeof(str_minute), tmp + 14);
+ str_minute[2] = 0;
+
+ StrCpy(str_second, sizeof(str_second), tmp + 17);
+ str_second[2] = 0;
+
+ str_msec[0] = 0;
+
+ if (StrLen(tmp) >= 21 && tmp[19] == '.')
+ {
+ StrCpy(str_msec, sizeof(str_msec), tmp + 20);
+ str_msec[StrLen(tmp) - 21] = 0;
+ while (StrLen(str_msec) < 3)
+ {
+ StrCat(str_msec, sizeof(str_msec), "0");
+ }
+ str_msec[3] = 0;
+ }
+
+ st->wYear = ToInt(str_year);
+ st->wMonth = ToInt(str_month);
+ st->wDay = ToInt(str_day);
+ st->wHour = ToInt(str_hour);
+ st->wMinute = ToInt(str_minute);
+ st->wSecond = ToInt(str_second);
+ st->wMilliseconds = ToInt(str_msec);
+
+ NormalizeSystem(st);
+
+ ok = true;
+ }
+ }
+
+ return ok;
+}
+
// Get the date and time string in RFC3339 format (example: 2017-09-27T18:25:55.434-9:00)
void GetDateTimeStrRFC3339(char *str, UINT size, SYSTEMTIME *st, int timezone_min){
// Validate arguments
if (str == NULL || st == NULL)
{
+ ClearStr(str, size);
return;
}
diff --git a/src/Mayaqua/Kernel.h b/src/Mayaqua/Kernel.h
index 3ec217e6..f2db8e22 100644
--- a/src/Mayaqua/Kernel.h
+++ b/src/Mayaqua/Kernel.h
@@ -137,6 +137,8 @@ void GetDateTimeStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale);
void GetDateStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale);
void GetTimeStrMilli64(char *str, UINT size, UINT64 sec64);
void GetDateTimeStrRFC3339(char *str, UINT size, SYSTEMTIME *st, int timezone_min);
+bool DateTimeStrRFC3339ToSystemTime(SYSTEMTIME *st, char *str);
+UINT64 DateTimeStrRFC3339ToSystemTime64(char *str);
UINT64 SafeTime64(UINT64 sec64);
bool Run(char *filename, char *arg, bool hide, bool wait);
bool RunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait);
diff --git a/src/Mayaqua/MayaType.h b/src/Mayaqua/MayaType.h
index f7b43570..ce3f9492 100644
--- a/src/Mayaqua/MayaType.h
+++ b/src/Mayaqua/MayaType.h
@@ -347,6 +347,9 @@ typedef struct PRAND PRAND;
// Str.h
typedef struct TOKEN_LIST TOKEN_LIST;
typedef struct INI_ENTRY INI_ENTRY;
+typedef struct JSON_OBJECT JSON_OBJECT;
+typedef struct JSON_ARRAY JSON_ARRAY;
+typedef struct JSON_VALUE JSON_VALUE;
// Internat.h
typedef struct UNI_TOKEN_LIST UNI_TOKEN_LIST;
@@ -383,6 +386,8 @@ typedef struct INSTANCE INSTANCE;
typedef struct VALUE VALUE;
typedef struct ELEMENT ELEMENT;
typedef struct PACK PACK;
+typedef struct JSONPACKHINT JSONPACKHINT;
+typedef struct JSONPACKHINT_ITEM JSONPACKHINT_ITEM;
// Cfg.h
typedef struct FOLDER FOLDER;
diff --git a/src/Mayaqua/Memory.c b/src/Mayaqua/Memory.c
index 43e6e406..506103de 100644
--- a/src/Mayaqua/Memory.c
+++ b/src/Mayaqua/Memory.c
@@ -1413,6 +1413,48 @@ bool ReplaceListPointer(LIST *o, void *oldptr, void *newptr)
return false;
}
+// New string list
+LIST *NewStrList()
+{
+ return NewListFast(CompareStr);
+}
+
+// Release string list
+void ReleaseStrList(LIST *o)
+{
+ UINT i;
+ if (o == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ char *s = LIST_DATA(o, i);
+ Free(s);
+ }
+
+ ReleaseList(o);
+}
+
+// Add a string distinct to the string list
+bool AddStrToStrListDistinct(LIST *o, char *str)
+{
+ if (o == NULL || str == NULL)
+ {
+ return false;
+ }
+
+ if (IsInListStr(o, str) == false)
+ {
+ Add(o, CopyStr(str));
+
+ return true;
+ }
+
+ return false;
+}
+
// Examine whether a string items are present in the list
bool IsInListStr(LIST *o, char *str)
{
@@ -2948,6 +2990,43 @@ void WriteBufBuf(BUF *b, BUF *bb)
WriteBuf(b, bb->Buf, bb->Size);
}
+// Write the buffer (from the offset) to a buffer
+void WriteBufBufWithOffset(BUF *b, BUF *bb)
+{
+ // Validate arguments
+ if (b == NULL || bb == NULL)
+ {
+ return;
+ }
+
+ WriteBuf(b, ((UCHAR *)bb->Buf) + bb->Current, bb->Size - bb->Current);
+}
+
+// Skip UTF-8 BOM
+bool BufSkipUtf8Bom(BUF *b)
+{
+ if (b == NULL)
+ {
+ return false;
+ }
+
+ SeekBufToBegin(b);
+
+ if (b->Size >= 3)
+ {
+ UCHAR *data = b->Buf;
+
+ if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF)
+ {
+ SeekBuf(b, 3, 1);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
// Read into a buffer from the buffer
BUF *ReadBufFromBuf(BUF *b, UINT size)
{
diff --git a/src/Mayaqua/Memory.h b/src/Mayaqua/Memory.h
index 62c441fe..acf32089 100644
--- a/src/Mayaqua/Memory.h
+++ b/src/Mayaqua/Memory.h
@@ -208,7 +208,9 @@ BUF *NewBufFromMemory(void *buf, UINT size);
void ClearBuf(BUF *b);
void WriteBuf(BUF *b, void *buf, UINT size);
void WriteBufBuf(BUF *b, BUF *bb);
+void WriteBufBufWithOffset(BUF *b, BUF *bb);
UINT ReadBuf(BUF *b, void *buf, UINT size);
+bool BufSkipUtf8Bom(BUF *b);
BUF *ReadBufFromBuf(BUF *b, UINT size);
void AdjustBufSize(BUF *b, UINT new_size);
void SeekBuf(BUF *b, UINT offset, int mode);
@@ -357,5 +359,9 @@ void CleanupSharedBuffer(SHARED_BUFFER *b);
void AppendBufUtf8(BUF *b, wchar_t *str);
void AppendBufStr(BUF *b, char *str);
+LIST *NewStrList();
+void ReleaseStrList(LIST *o);
+bool AddStrToStrListDistinct(LIST *o, char *str);
+
#endif // MEMORY_H
diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c
index 8d11d1ad..38422823 100644
--- a/src/Mayaqua/Network.c
+++ b/src/Mayaqua/Network.c
@@ -7229,6 +7229,12 @@ bool IsIP4(IP *ip)
return (IsIP6(ip) ? false : true);
}
+// Copy the IP address
+void CopyIP(IP *dst, IP *src)
+{
+ Copy(dst, src, sizeof(IP));
+}
+
// Get the number of clients connected from the specified IP address
UINT GetNumIpClient(IP *ip)
{
@@ -11368,6 +11374,50 @@ void InitSockSet(SOCKSET *set)
Zero(set, sizeof(SOCKSET));
}
+// Receive data and discard all of them
+bool RecvAllWithDiscard(SOCK *sock, UINT size, bool secure)
+{
+ static UCHAR buffer[4096];
+ UINT recv_size, sz, ret;
+ if (sock == NULL)
+ {
+ return false;
+ }
+ if (size == 0)
+ {
+ return true;
+ }
+ if (sock->AsyncMode)
+ {
+ return false;
+ }
+
+ recv_size = 0;
+
+ while (true)
+ {
+ sz = MIN(size - recv_size, sizeof(buffer));
+ ret = Recv(sock, buffer, sz, secure);
+ if (ret == 0)
+ {
+ return false;
+ }
+ if (ret == SOCK_LATER)
+ {
+ // I suppose that this is safe because the RecvAll() function is used only
+ // if the sock->AsyncMode == true. And the Recv() function may return
+ // SOCK_LATER only if the sock->AsyncMode == false. Therefore the call of
+ // Recv() function in the RecvAll() function never returns SOCK_LATER.
+ return false;
+ }
+ recv_size += ret;
+ if (recv_size >= size)
+ {
+ return true;
+ }
+ }
+}
+
// Receive all by TCP
bool RecvAll(SOCK *sock, void *data, UINT size, bool secure)
{
diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h
index 99e4a0d8..a9a980a6 100644
--- a/src/Mayaqua/Network.h
+++ b/src/Mayaqua/Network.h
@@ -953,6 +953,7 @@ UINT GetContentLength(HTTP_HEADER *header);
void GetHttpDateStr(char *str, UINT size, UINT64 t);
bool HttpSendForbidden(SOCK *s, char *target, char *server_id);
bool HttpSendNotFound(SOCK *s, char *target);
+bool HttpSendBody(SOCK *s, void *data, UINT size, char *contents_type);
bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version);
bool HttpServerSend(SOCK *s, PACK *p);
bool HttpClientSend(SOCK *s, PACK *p);
@@ -1193,6 +1194,7 @@ void SendAdd(SOCK *sock, void *data, UINT size);
bool SendNow(SOCK *sock, int secure);
bool RecvAll(SOCK *sock, void *data, UINT size, bool secure);
bool RecvAllEx(SOCK *sock, void **data_new_ptr, UINT size, bool secure);
+bool RecvAllWithDiscard(SOCK *sock, UINT size, bool secure);
void InitSockSet(SOCKSET *set);
void AddSockSet(SOCKSET *set, SOCK *sock);
CANCEL *NewCancel();
@@ -1308,6 +1310,7 @@ void SocketTimeoutThread(THREAD *t, void *param);
SOCKET_TIMEOUT_PARAM *NewSocketTimeout(SOCK *sock);
void FreeSocketTimeout(SOCKET_TIMEOUT_PARAM *ttp);
+void CopyIP(IP *dst, IP *src);
bool IsIP6(IP *ip);
bool IsIP4(IP *ip);
void IPv6AddrToIP(IP *ip, IPV6_ADDR *addr);
diff --git a/src/Mayaqua/Pack.c b/src/Mayaqua/Pack.c
index 2d3d2a9f..7d5b137b 100644
--- a/src/Mayaqua/Pack.c
+++ b/src/Mayaqua/Pack.c
@@ -644,13 +644,13 @@ ELEMENT *NewElement(char *name, UINT type, UINT num_value, VALUE **values)
}
// Memory allocation
- e = Malloc(sizeof(ELEMENT));
+ e = ZeroMalloc(sizeof(ELEMENT));
StrCpy(e->name, sizeof(e->name), name);
e->num_value = num_value;
e->type = type;
// Copy of the pointer list to the element
- e->values = (VALUE **)Malloc(sizeof(VALUE *) * num_value);
+ e->values = (VALUE **)ZeroMalloc(sizeof(VALUE *) * num_value);
for (i = 0;i < e->num_value;i++)
{
e->values[i] = values[i];
@@ -765,6 +765,9 @@ bool AddElement(PACK *p, ELEMENT *e)
return false;
}
+ // Set JsonHint_GroupName
+ StrCpy(e->JsonHint_GroupName, sizeof(e->JsonHint_GroupName), p->CurrentJsonHint_GroupName);
+
// Adding
Add(p->elements, e);
return true;
@@ -788,6 +791,11 @@ void FreePack(PACK *p)
}
Free(elements);
+ if (p->json_subitem_names != NULL)
+ {
+ FreeStrList(p->json_subitem_names);
+ }
+
ReleaseList(p->elements);
Free(p);
}
@@ -798,7 +806,7 @@ PACK *NewPack()
PACK *p;
// Memory allocation
- p = MallocEx(sizeof(PACK), true);
+ p = ZeroMallocEx(sizeof(PACK), true);
// Creating a List
p->elements = NewListFast(ComparePackName);
@@ -824,6 +832,12 @@ K *PackGetK(PACK *p, char *name)
}
k = BufToK(b, true, false, NULL);
+
+ if (k == NULL)
+ {
+ k = BufToK(b, true, true, NULL);
+ }
+
FreeBuf(b);
return k;
@@ -847,49 +861,61 @@ X *PackGetX(PACK *p, char *name)
}
x = BufToX(b, false);
+
+ if (x == NULL)
+ {
+ x = BufToX(b, true);
+ }
+
FreeBuf(b);
return x;
}
// Add the K to the PACK
-void PackAddK(PACK *p, char *name, K *k)
+ELEMENT *PackAddK(PACK *p, char *name, K *k)
{
BUF *b;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || k == NULL)
{
- return;
+ return NULL;
}
b = KToBuf(k, false, NULL);
if (b == NULL)
{
- return;
+ return NULL;
}
- PackAddBuf(p, name, b);
+ e = PackAddBuf(p, name, b);
FreeBuf(b);
+
+ return e;
}
// Add an X into the PACK
-void PackAddX(PACK *p, char *name, X *x)
+ELEMENT *PackAddX(PACK *p, char *name, X *x)
{
BUF *b;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || x == NULL)
{
- return;
+ return NULL;
}
b = XToBuf(x, false);
if (b == NULL)
{
- return;
+ return NULL;
}
- PackAddBuf(p, name, b);
+ e = PackAddBuf(p, name, b);
FreeBuf(b);
+
+ return e;
}
// Get a buffer from the PACK
@@ -1052,30 +1078,65 @@ bool PackGetBoolEx(PACK *p, char *name, UINT index)
return PackGetIntEx(p, name, index) == 0 ? false : true;
}
-// Add a bool type into the PACK
-void PackAddBool(PACK *p, char *name, bool b)
+// Set CurrentJsonHint_GroupName to PACK
+void PackSetCurrentJsonGroupName(PACK *p, char *json_group_name)
{
- PackAddInt(p, name, b ? 1 : 0);
-}
-void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
-{
- PackAddIntEx(p, name, b ? 1 : 0, index, total);
-}
-
-// Add the IPV6_ADDR to the PACK
-void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
-{
- // Validate arguments
- if (p == NULL || name == NULL || addr == NULL)
+ if (p == NULL)
{
return;
}
- PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
+ if (json_group_name == NULL)
+ {
+ ClearStr(p->CurrentJsonHint_GroupName, sizeof(p->CurrentJsonHint_GroupName));
+ }
+ else
+ {
+ StrCpy(p->CurrentJsonHint_GroupName, sizeof(p->CurrentJsonHint_GroupName), json_group_name);
+
+ if (p->json_subitem_names == NULL)
+ {
+ p->json_subitem_names = NewStrList();
+ }
+
+ AddStrToStrListDistinct(p->json_subitem_names, json_group_name);
+ }
}
-void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+
+// Add a bool type into the PACK
+ELEMENT *PackAddBool(PACK *p, char *name, bool b)
{
- PackAddIp6AddrEx(p, name, addr, 0, 1);
+ ELEMENT *e = PackAddInt(p, name, b ? 1 : 0);
+ if (e != NULL)
+ {
+ e->JsonHint_IsBool = true;
+ }
+ return e;
+}
+ELEMENT *PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
+{
+ ELEMENT *e = PackAddIntEx(p, name, b ? 1 : 0, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsBool = true;
+ }
+ return e;
+}
+
+// Add the IPV6_ADDR to the PACK
+ELEMENT *PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
+{
+ // Validate arguments
+ if (p == NULL || name == NULL || addr == NULL)
+ {
+ return NULL;
+ }
+
+ return PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
+}
+ELEMENT *PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+{
+ return PackAddIp6AddrEx(p, name, addr, 0, 1);
}
// Get an IPV6_ADDR from the PACK
@@ -1097,6 +1158,10 @@ bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
// Add the IP to the PACK
void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)
+{
+ PackAddIp32Ex2(p, name, ip32, index, total, false);
+}
+void PackAddIp32Ex2(PACK *p, char *name, UINT ip32, UINT index, UINT total, bool is_single)
{
IP ip;
// Validate arguments
@@ -1107,32 +1172,45 @@ void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)
UINTToIP(&ip, ip32);
- PackAddIpEx(p, name, &ip, index, total);
+ PackAddIpEx2(p, name, &ip, index, total, is_single);
}
void PackAddIp32(PACK *p, char *name, UINT ip32)
{
- PackAddIp32Ex(p, name, ip32, 0, 1);
+ PackAddIp32Ex2(p, name, ip32, 0, 1, true);
}
void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
+{
+ PackAddIpEx2(p, name, ip, index, total, false);
+}
+void PackAddIpEx2(PACK *p, char *name, IP *ip, UINT index, UINT total, bool is_single)
{
UINT i;
bool b = false;
char tmp[MAX_PATH];
+ ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || ip == NULL)
{
return;
}
+ if (total >= 2)
+ {
+ is_single = false;
+ }
b = IsIP6(ip);
Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);
- PackAddBoolEx(p, tmp, b, index, total);
+ e = PackAddBoolEx(p, tmp, b, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
Format(tmp, sizeof(tmp), "%s@ipv6_array", name);
if (b)
{
- PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);
+ e = PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
else
{
@@ -1140,17 +1218,23 @@ void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
Zero(dummy, sizeof(dummy));
- PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);
+ e = PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);
if (b)
{
- PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);
+ e = PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
else
{
- PackAddIntEx(p, tmp, 0, index, total);
+ e = PackAddIntEx(p, tmp, 0, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
i = IPToUINT(ip);
@@ -1160,11 +1244,13 @@ void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
i = Swap32(i);
}
- PackAddIntEx(p, name, i, index, total);
+ e = PackAddIntEx(p, name, i, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
void PackAddIp(PACK *p, char *name, IP *ip)
{
- PackAddIpEx(p, name, ip, 0, 1);
+ PackAddIpEx2(p, name, ip, 0, 1, true);
}
// Get an IP from the PACK
@@ -1344,34 +1430,35 @@ bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index)
}
// Add the buffer to the PACK (array)
-void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)
+ELEMENT *PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)
{
// Validate arguments
if (p == NULL || name == NULL || b == NULL || total == 0)
{
- return;
+ return NULL;
}
- PackAddDataEx(p, name, b->Buf, b->Size, index, total);
+ return PackAddDataEx(p, name, b->Buf, b->Size, index, total);
}
// Add the data to the PACK (array)
-void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)
+ELEMENT *PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || data == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewDataValue(data, size);
e = GetElement(p, name, VALUE_DATA);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_DATA);
e->values[index] = v;
}
else
@@ -1387,53 +1474,68 @@ void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT
e->type = VALUE_DATA;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add the buffer to the PACK
-void PackAddBuf(PACK *p, char *name, BUF *b)
+ELEMENT *PackAddBuf(PACK *p, char *name, BUF *b)
{
// Validate arguments
if (p == NULL || name == NULL || b == NULL)
{
- return;
+ return NULL;
}
- PackAddData(p, name, b->Buf, b->Size);
+ return PackAddData(p, name, b->Buf, b->Size);
}
// Add the data to the PACK
-void PackAddData(PACK *p, char *name, void *data, UINT size)
+ELEMENT *PackAddData(PACK *p, char *name, void *data, UINT size)
{
VALUE *v;
+ ELEMENT *e;
// Validate arguments
if (p == NULL || data == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewDataValue(data, size);
- AddElement(p, NewElement(name, VALUE_DATA, 1, &v));
+ e = NewElement(name, VALUE_DATA, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+
+ return e;
}
// Add a 64 bit integer (array) to the PACK
-void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
+ELEMENT *PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewInt64Value(i);
e = GetElement(p, name, VALUE_INT64);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_INT64);
e->values[index] = v;
}
else
@@ -1449,27 +1551,36 @@ void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
e->type = VALUE_INT64;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add an integer to the PACK (array)
-void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
+ELEMENT *PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewIntValue(i);
e = GetElement(p, name, VALUE_INT);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_INT);
e->values[index] = v;
}
else
@@ -1485,61 +1596,103 @@ void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
e->type = VALUE_INT;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
+// Add 64 bit integer time value to the PACK
+ELEMENT *PackAddTime64(PACK *p, char *name, UINT64 i)
+{
+ ELEMENT *e = PackAddInt64(p, name, i);
+ if (e != NULL)
+ {
+ e->JsonHint_IsDateTime = true;
+ }
+ return e;
+}
+ELEMENT *PackAddTime64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
+{
+ ELEMENT *e = PackAddInt64Ex(p, name, i, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsDateTime = true;
+ }
+ return e;
+}
+
+
// Add a 64 bit integer to the PACK
-void PackAddInt64(PACK *p, char *name, UINT64 i)
+ELEMENT *PackAddInt64(PACK *p, char *name, UINT64 i)
{
VALUE *v;
+ ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewInt64Value(i);
- AddElement(p, NewElement(name, VALUE_INT64, 1, &v));
+ e = NewElement(name, VALUE_INT64, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add the number of items to the PACK
-void PackAddNum(PACK *p, char *name, UINT num)
+ELEMENT *PackAddNum(PACK *p, char *name, UINT num)
{
- PackAddInt(p, name, num);
+ return PackAddInt(p, name, num);
}
// Add an integer to the PACK
-void PackAddInt(PACK *p, char *name, UINT i)
+ELEMENT *PackAddInt(PACK *p, char *name, UINT i)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewIntValue(i);
- AddElement(p, NewElement(name, VALUE_INT, 1, &v));
+ e = NewElement(name, VALUE_INT, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add a Unicode string (array) to the PACK
-void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)
+ELEMENT *PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || unistr == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewUniStrValue(unistr);
e = GetElement(p, name, VALUE_UNISTR);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_UNISTR);
e->values[index] = v;
}
else
@@ -1555,41 +1708,55 @@ void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT tota
e->type = VALUE_UNISTR;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add a Unicode string to the PACK
-void PackAddUniStr(PACK *p, char *name, wchar_t *unistr)
+ELEMENT *PackAddUniStr(PACK *p, char *name, wchar_t *unistr)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || unistr == NULL)
{
- return;
+ return NULL;
}
v = NewUniStrValue(unistr);
- AddElement(p, NewElement(name, VALUE_UNISTR, 1, &v));
+ e = NewElement(name, VALUE_UNISTR, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add a string to the PACK (array)
-void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
+ELEMENT *PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || str == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewStrValue(str);
e = GetElement(p, name, VALUE_STR);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_STR);
e->values[index] = v;
}
else
@@ -1605,22 +1772,701 @@ void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
e->type = VALUE_STR;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add a string to the PACK
-void PackAddStr(PACK *p, char *name, char *str)
+ELEMENT *PackAddStr(PACK *p, char *name, char *str)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || str == NULL)
+ {
+ return NULL;
+ }
+
+ v = NewStrValue(str);
+ e = NewElement(name, VALUE_STR, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
+}
+
+// Add an element of PACK array to JSON Array
+void PackArrayElementToJsonArray(JSON_ARRAY *ja, PACK *p, ELEMENT *e, UINT index)
+{
+ if (ja == NULL || p == NULL || e == NULL || index >= e->num_value)
{
return;
}
- v = NewStrValue(str);
- AddElement(p, NewElement(name, VALUE_STR, 1, &v));
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ IP ip;
+ if (PackGetIpEx(p, e->name, &ip, index))
+ {
+ char ip_str[64];
+ IPToStr(ip_str, sizeof(ip_str), &ip);
+ JsonArrayAddStr(ja, ip_str);
+ }
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ JsonArrayAddBool(ja, PackGetBoolEx(p, e->name, index));
+ }
+ else
+ {
+ JsonArrayAddNumber(ja, PackGetIntEx(p, e->name, index));
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ JsonArrayAddNumber(ja, PackGetInt64Ex(p, e->name, index));
+ }
+ else
+ {
+ char dtstr[64];
+
+ SystemTime64ToJsonStr(dtstr, sizeof(dtstr), PackGetInt64Ex(p, e->name, index));
+ JsonArrayAddStr(ja, dtstr);
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ BUF *buf = PackGetBufEx(p, e->name, index);
+ if (buf != NULL)
+ {
+ JsonArrayAddData(ja, buf->Buf, buf->Size);
+ FreeBuf(buf);
+ }
+ else
+ {
+ UCHAR zero = 0;
+ JsonArrayAddData(ja, &zero, 0);
+ }
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonArrayAddStr(ja, e->values[index]->Str);
+ }
+ else
+ {
+ JsonArrayAddStr(ja, "");
+ }
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonArrayAddUniStr(ja, e->values[index]->UniStr);
+ }
+ else
+ {
+ JsonArrayAddUniStr(ja, L"");
+ }
+ }
+ break;
+ }
+}
+
+// Add an element of PACK to JSON Object
+void PackElementToJsonObject(JSON_OBJECT *o, PACK *p, ELEMENT *e, UINT index)
+{
+ char *suffix;
+ char name[MAX_PATH];
+ if (o == NULL || p == NULL || e == NULL)
+ {
+ return;
+ }
+
+ suffix = DetermineJsonSuffixForPackElement(e);
+
+ if (suffix == NULL)
+ {
+ return;
+ }
+
+ StrCpy(name, sizeof(name), e->name);
+ StrCat(name, sizeof(name), suffix);
+
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ IP ip;
+ if (PackGetIpEx(p, e->name, &ip, index))
+ {
+ char ip_str[64];
+ IPToStr(ip_str, sizeof(ip_str), &ip);
+ JsonSetStr(o, name, ip_str);
+ }
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ JsonSetBool(o, name, PackGetBoolEx(p, e->name, index));
+ }
+ else
+ {
+ JsonSetNumber(o, name, PackGetIntEx(p, e->name, index));
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ JsonSetNumber(o, name, PackGetInt64Ex(p, e->name, index));
+ }
+ else
+ {
+ char dtstr[64];
+
+ SystemTime64ToJsonStr(dtstr, sizeof(dtstr), PackGetInt64Ex(p, e->name, index));
+ JsonSetStr(o, name, dtstr);
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ BUF *buf = PackGetBufEx(p, e->name, index);
+ if (buf != NULL)
+ {
+ JsonSetData(o, name, buf->Buf, buf->Size);
+ FreeBuf(buf);
+ }
+ else
+ {
+ UCHAR zero = 0;
+ JsonSetData(o, name, &zero, 0);
+ }
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonSetStr(o, name, e->values[index]->Str);
+ }
+ else
+ {
+ JsonSetStr(o, name, "");
+ }
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonSetUniStr(o, name, e->values[index]->UniStr);
+ }
+ else
+ {
+ JsonSetUniStr(o, name, L"");
+ }
+ }
+ break;
+ }
+}
+
+// Determine JSON element suffix for PACK element
+char *DetermineJsonSuffixForPackElement(ELEMENT *e)
+{
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ return "_ip";
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ return "_bool";
+ }
+ else
+ {
+ return "_u32";
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ return "_u64";
+ }
+ else
+ {
+ return "_dt";
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_bin";
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_str";
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_utf";
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+// Convert JSON to PACK
+PACK *JsonToPack(JSON_VALUE *v)
+{
+ PACK *p = NULL;
+ JSON_OBJECT *jo;
+ if (v == NULL)
+ {
+ return NULL;
+ }
+
+ p = NewPack();
+
+ jo = JsonValueGetObject(v);
+
+ if (jo != NULL)
+ {
+ UINT i;
+ for (i = 0;i < jo->count;i++)
+ {
+ char *name = jo->names[i];
+ JSON_VALUE *value = jo->values[i];
+
+ if (value->type == JSON_TYPE_ARRAY)
+ {
+ UINT j;
+ JSON_ARRAY *ja = value->value.array;
+
+ for (j = 0;j < ja->count;j++)
+ {
+ if (ja->items[j]->type != JSON_TYPE_OBJECT)
+ {
+ JsonTryParseValueAddToPack(p, ja->items[j], name, j, ja->count, false);
+ }
+ else
+ {
+ JSON_VALUE *v = ja->items[j];
+ JSON_OBJECT *o = v->value.object;
+ UINT k;
+
+ for (k = 0;k < o->count;k++)
+ {
+ char *name2 = o->names[k];
+ JSON_VALUE *value2 = o->values[k];
+
+ PackSetCurrentJsonGroupName(p, name);
+ JsonTryParseValueAddToPack(p, value2, name2, j, ja->count, false);
+ PackSetCurrentJsonGroupName(p, NULL);
+ }
+ }
+ }
+ }
+ else
+ {
+ JsonTryParseValueAddToPack(p, value, name, 0, 1, true);
+ }
+ }
+ }
+
+ return p;
+}
+
+ELEMENT *ElementNullSafe(ELEMENT *p)
+{
+ static ELEMENT dummy;
+ if (p == NULL)
+ {
+ Zero(&dummy, sizeof(dummy));
+ return &dummy;
+ }
+ return p;
+}
+
+bool JsonTryParseValueAddToPack(PACK *p, JSON_VALUE *v, char *v_name, UINT index, UINT total, bool is_single)
+{
+ char name[MAX_PATH];
+ bool ok = true;
+ if (p == NULL || v == NULL)
+ {
+ return false;
+ }
+
+ if (TrimEndWith(name, sizeof(name), v_name, "_bool"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, MAKEBOOL(v->value.number), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, ToBool(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_u32"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, (UINT)v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, ToInt(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_u64"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, ToInt64(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_str"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddStrEx(p, name, MAKEBOOL(v->value.boolean) ? "true" : "false", index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ char tmp[64];
+ ToStr64(tmp, v->value.number);
+ ElementNullSafe(PackAddStrEx(p, name, tmp, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddStrEx(p, name, v->value.string, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_utf"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddUniStrEx(p, name, MAKEBOOL(v->value.boolean) ? L"true" : L"false", index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ char tmp[64];
+ wchar_t tmp2[64];
+ ToStr64(tmp, v->value.number);
+ StrToUni(tmp2, sizeof(tmp2), tmp);
+ ElementNullSafe(PackAddUniStrEx(p, name, tmp2, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ wchar_t *uni = CopyUtfToUni(v->value.string);
+ ElementNullSafe(PackAddUniStrEx(p, name, uni, index, total))->JsonHint_IsArray = !is_single;
+ Free(uni);
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_bin"))
+ {
+ if (v->type == JSON_TYPE_STRING)
+ {
+ UINT len = StrLen(v->value.string);
+ UCHAR *data = ZeroMalloc(len * 4 + 64);
+ UINT size = B64_Decode(data, v->value.string, len);
+ ElementNullSafe(PackAddDataEx(p, name, data, size, index, total))->JsonHint_IsArray = !is_single;
+ Free(data);
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_dt"))
+ {
+ if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ UINT64 time = DateTimeStrRFC3339ToSystemTime64(v->value.string);
+ ELEMENT *e = PackAddInt64Ex(p, name, time, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsArray = !is_single;
+ e->JsonHint_IsDateTime = true;
+ }
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_ip"))
+ {
+ if (v->type == JSON_TYPE_STRING)
+ {
+ IP ip;
+ if (StrToIP(&ip, v->value.string))
+ {
+ PackAddIpEx2(p, name, &ip, index, total, is_single);
+ ok = true;
+ }
+ }
+ }
+
+ return ok;
+}
+
+// Convert JSON string to PACK
+PACK *JsonStrToPack(char *str)
+{
+ JSON_VALUE *v = StrToJson(str);
+ PACK *ret;
+
+ if (v == NULL)
+ {
+ return NULL;
+ }
+
+ ret = JsonToPack(v);
+
+ JsonFree(v);
+
+ return ret;
+}
+
+// Convert PACK to JSON string
+char *PackToJsonStr(PACK *p)
+{
+ char *ret;
+ JSON_VALUE *json = PackToJson(p);
+
+ ret = JsonToStr(json);
+
+ JsonFree(json);
+
+ return ret;
+}
+
+// Convert PACK to JSON
+JSON_VALUE *PackToJson(PACK *p)
+{
+ JSON_VALUE *v;
+ JSON_OBJECT *o;
+ UINT i, j, k;
+ LIST *json_group_id_list;
+ if (p == NULL)
+ {
+ return JsonNewObject();
+ }
+
+ json_group_id_list = NewStrList();
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (IsEmptyStr(e->JsonHint_GroupName) == false)
+ {
+ AddStrToStrListDistinct(json_group_id_list, e->JsonHint_GroupName);
+ }
+ }
+ }
+
+ for (i = 0;i < LIST_NUM(p->json_subitem_names);i++)
+ {
+ char *group_name = LIST_DATA(p->json_subitem_names, i);
+
+ if (IsEmptyStr(group_name) == false)
+ {
+ AddStrToStrListDistinct(json_group_id_list, group_name);
+ }
+ }
+
+ v = JsonNewObject();
+ o = JsonValueGetObject(v);
+
+ for (k = 0;k < LIST_NUM(json_group_id_list);k++)
+ {
+ char *group_name = LIST_DATA(json_group_id_list, k);
+ UINT array_count = INFINITE;
+ bool ok = true;
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (StrCmpi(e->JsonHint_GroupName, group_name) == 0)
+ {
+ if (array_count == INFINITE)
+ {
+ array_count = e->num_value;
+ }
+ else
+ {
+ if (array_count != e->num_value)
+ {
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (array_count == INFINITE)
+ {
+ array_count = 0;
+ }
+
+ if (ok)
+ {
+ JSON_VALUE **json_objects = ZeroMalloc(sizeof(void *) * array_count);
+ JSON_VALUE *jav = JsonNewArray();
+ JSON_ARRAY *ja = JsonArray(jav);
+
+ JsonSet(o, group_name, jav);
+
+ for (j = 0;j < array_count;j++)
+ {
+ json_objects[j] = JsonNewObject();
+
+ JsonArrayAdd(ja, json_objects[j]);
+ }
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (StrCmpi(e->JsonHint_GroupName, group_name) == 0)
+ {
+ for (j = 0;j < e->num_value;j++)
+ {
+ PackElementToJsonObject(JsonValueGetObject(json_objects[j]),
+ p, e, j);
+ }
+ }
+ }
+ }
+
+ Free(json_objects);
+ }
+ }
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (IsEmptyStr(e->JsonHint_GroupName))
+ {
+ char *suffix = DetermineJsonSuffixForPackElement(e);
+
+ if (suffix != NULL)
+ {
+ JSON_VALUE *jav = JsonNewArray();
+ JSON_ARRAY *ja = JsonArray(jav);
+ char name[MAX_PATH];
+
+ for (j = 0;j < e->num_value;j++)
+ {
+ PackArrayElementToJsonArray(ja, p, e, j);
+ }
+
+ StrCpy(name, sizeof(name), e->name);
+ StrCat(name, sizeof(name), suffix);
+
+ JsonSet(o, name, jav);
+ }
+ }
+ }
+ else if (e->num_value == 1)
+ {
+ PackElementToJsonObject(o, p, e, 0);
+ }
+ }
+
+ ReleaseStrList(json_group_id_list);
+
+ return v;
}
+
+
diff --git a/src/Mayaqua/Pack.h b/src/Mayaqua/Pack.h
index 9d0c33cd..f0d553b8 100644
--- a/src/Mayaqua/Pack.h
+++ b/src/Mayaqua/Pack.h
@@ -55,12 +55,38 @@ struct ELEMENT
UINT num_value; // Number of values (>=1)
UINT type; // Type
VALUE **values; // List of pointers to the value
+ bool JsonHint_IsArray;
+ bool JsonHint_IsBool;
+ bool JsonHint_IsDateTime;
+ bool JsonHint_IsIP;
+ char JsonHint_GroupName[MAX_ELEMENT_NAME_LEN + 1];
};
// PACK object
struct PACK
{
LIST *elements; // Element list
+ LIST *json_subitem_names; // JSON sub-item names
+ char CurrentJsonHint_GroupName[MAX_ELEMENT_NAME_LEN + 1];
+};
+
+
+#define MAX_JSONPACK_HINT_ITEMS 64
+#define JSONPACK_HINT_TYPE_ARRAY 1
+
+// JSON/PACK converter hint element
+struct JSONPACKHINT_ITEM
+{
+ UINT Type;
+ char ArrayNumNameInPack[MAX_ELEMENT_NAME_LEN + 1];
+ char ArrayMembersInPack[MAX_SIZE + 1];
+};
+
+// JSON/PACK converter hint
+struct JSONPACKHINT
+{
+ UINT NumHints;
+ JSONPACKHINT_ITEM Hints[MAX_JSONPACK_HINT_ITEMS];
};
@@ -100,21 +126,23 @@ TOKEN_LIST *GetPackElementNames(PACK *p);
X *PackGetX(PACK *p, char *name);
K *PackGetK(PACK *p, char *name);
-void PackAddX(PACK *p, char *name, X *x);
-void PackAddK(PACK *p, char *name, K *k);
-void PackAddStr(PACK *p, char *name, char *str);
-void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);
-void PackAddUniStr(PACK *p, char *name, wchar_t *unistr);
-void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total);
-void PackAddInt(PACK *p, char *name, UINT i);
-void PackAddNum(PACK *p, char *name, UINT num);
-void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total);
-void PackAddInt64(PACK *p, char *name, UINT64 i);
-void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
-void PackAddData(PACK *p, char *name, void *data, UINT size);
-void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total);
-void PackAddBuf(PACK *p, char *name, BUF *b);
-void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total);
+ELEMENT *PackAddX(PACK *p, char *name, X *x);
+ELEMENT *PackAddK(PACK *p, char *name, K *k);
+ELEMENT *PackAddStr(PACK *p, char *name, char *str);
+ELEMENT *PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);
+ELEMENT *PackAddUniStr(PACK *p, char *name, wchar_t *unistr);
+ELEMENT *PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total);
+ELEMENT *PackAddInt(PACK *p, char *name, UINT i);
+ELEMENT *PackAddNum(PACK *p, char *name, UINT num);
+ELEMENT *PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total);
+ELEMENT *PackAddInt64(PACK *p, char *name, UINT64 i);
+ELEMENT *PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
+ELEMENT *PackAddTime64(PACK *p, char *name, UINT64 i);
+ELEMENT *PackAddTime64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
+ELEMENT *PackAddData(PACK *p, char *name, void *data, UINT size);
+ELEMENT *PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total);
+ELEMENT *PackAddBuf(PACK *p, char *name, BUF *b);
+ELEMENT *PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total);
bool PackGetStr(PACK *p, char *name, char *str, UINT size);
bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index);
bool PackGetUniStr(PACK *p, char *name, wchar_t *unistr, UINT size);
@@ -133,23 +161,38 @@ bool PackGetDataEx(PACK *p, char *name, void *data, UINT index);
BUF *PackGetBuf(PACK *p, char *name);
BUF *PackGetBufEx(PACK *p, char *name, UINT index);
bool PackGetBool(PACK *p, char *name);
-void PackAddBool(PACK *p, char *name, bool b);
-void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total);
+ELEMENT *PackAddBool(PACK *p, char *name, bool b);
+ELEMENT *PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total);
bool PackGetBoolEx(PACK *p, char *name, UINT index);
void PackAddIp(PACK *p, char *name, IP *ip);
void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total);
+void PackAddIpEx2(PACK *p, char *name, IP *ip, UINT index, UINT total, bool is_single);
bool PackGetIp(PACK *p, char *name, IP *ip);
bool PackGetIpEx(PACK *p, char *name, IP *ip, UINT index);
UINT PackGetIp32(PACK *p, char *name);
UINT PackGetIp32Ex(PACK *p, char *name, UINT index);
void PackAddIp32(PACK *p, char *name, UINT ip32);
void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total);
-void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total);
+void PackAddIp32Ex2(PACK *p, char *name, UINT ip32, UINT index, UINT total, bool is_single);
+ELEMENT *PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total);
bool PackGetIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index);
-void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
+ELEMENT *PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
bool PackGetData2(PACK *p, char *name, void *data, UINT size);
bool PackGetDataEx2(PACK *p, char *name, void *data, UINT size, UINT index);
bool PackIsValueExists(PACK *p, char *name);
+void PackSetCurrentJsonGroupName(PACK *p, char *json_group_name);
+ELEMENT *ElementNullSafe(ELEMENT *p);
+
+JSON_VALUE *PackToJson(PACK *p);
+char *PackToJsonStr(PACK *p);
+
+PACK *JsonToPack(JSON_VALUE *v);
+PACK *JsonStrToPack(char *str);
+
+void PackArrayElementToJsonArray(JSON_ARRAY *ja, PACK *p, ELEMENT *e, UINT index);
+void PackElementToJsonObject(JSON_OBJECT *o, PACK *p, ELEMENT *e, UINT index);
+char *DetermineJsonSuffixForPackElement(ELEMENT *e);
+bool JsonTryParseValueAddToPack(PACK *p, JSON_VALUE *v, char *v_name, UINT index, UINT total, bool is_single);
#endif // PACK_H
diff --git a/src/Mayaqua/Str.c b/src/Mayaqua/Str.c
index 13a1a2e3..ee4d3f50 100644
--- a/src/Mayaqua/Str.c
+++ b/src/Mayaqua/Str.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -36,6 +37,60 @@ static BYTESTR bytestr[] =
{0, "Bytes"},
};
+// Decode URL string
+char *UrlDecode(char *url_str)
+{
+ UINT i, len;
+ BUF *b;
+ char *ret;
+ if (url_str == NULL)
+ {
+ return NULL;
+ }
+
+ len = StrLen(url_str);
+
+ b = NewBuf();
+
+ for (i = 0;i < len;i++)
+ {
+ char c = url_str[i];
+
+ if (c == '%' && ((i + 2) < len))
+ {
+ char hex_str[8];
+ UINT value;
+
+ hex_str[0] = url_str[i + 1];
+ hex_str[1] = url_str[i + 2];
+ hex_str[2] = 0;
+
+ value = HexToInt(hex_str);
+
+ WriteBufChar(b, (UCHAR)value);
+
+ i += 2;
+ continue;
+ }
+ else
+ {
+ if (c == '+')
+ {
+ c = ' ';
+ }
+ WriteBufChar(b, c);
+ }
+ }
+
+ WriteBufChar(b, 0);
+
+ ret = CopyStr(b->Buf);
+
+ FreeBuf(b);
+
+ return ret;
+}
+
// Change the case of the string by the bit array
void SetStrCaseAccordingToBits(char *str, UINT bits)
{
@@ -1408,6 +1463,74 @@ UINT64 ToInt64(char *str)
return ret;
}
+
+UINT64 Json_ToInt64Ex(char *str, char **endptr, bool *error)
+{
+ UINT i;
+ UINT64 ret = 0;
+ if (error != NULL) *error = true;
+ // Validate arguments
+ if (str == NULL)
+ {
+ if (endptr != NULL)
+ {
+ *endptr = NULL;
+ }
+ return 0;
+ }
+
+ for (i = 0;;i++)
+ {
+ char c = str[i];
+ if (endptr != NULL)
+ {
+ *endptr = &str[i];
+ }
+ if (c == 0)
+ {
+ break;
+ }
+ if ('0' <= c && c <= '9')
+ {
+ ret = ret * (UINT64)10 + (UINT64)(c - '0');
+ if (error != NULL) *error = false;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+// Trim EndWith
+bool TrimEndWith(char *dst, UINT dst_size, char *str, char *key)
+{
+ if (dst == NULL || str == NULL)
+ {
+ ClearStr(dst, dst_size);
+ return false;
+ }
+
+ StrCpy(dst, dst_size, str);
+
+ if (EndWith(str, key))
+ {
+ UINT src_len = StrLen(str);
+ UINT key_len = StrLen(key);
+
+ if (src_len >= key_len)
+ {
+ dst[src_len - key_len] = 0;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
// Check whether the str ends with the key
bool EndWith(char *str, char *key)
{
@@ -3021,3 +3144,1996 @@ UINT StrLen(char *str)
}
+
+
+
+
+// *** JSON strings support
+// Original source code from Parson ( http://kgabis.github.com/parson/ )
+// Modified by dnobori
+/*
+Parson ( http://kgabis.github.com/parson/ )
+Copyright (c) 2012 - 2017 Krzysztof Gabis
+
+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.
+*/
+
+
+
+/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you
+* don't have to. */
+#define sscanf THINK_TWICE_ABOUT_USING_SSCANF
+
+#define STARTING_CAPACITY 16
+#define MAX_NESTING 2048
+#define FLOAT_FORMAT "%1.17g"
+
+#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
+#define SKIP_CHAR(str) ((*str)++)
+#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }
+
+static JSON_Malloc_Function parson_malloc = Malloc;
+static JSON_Free_Function parson_free = Free;
+
+#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
+
+/* Various */
+static void remove_comments(char *string, char *start_token, char *end_token);
+static char * parson_strndup(char *string, UINT n);
+static char * parson_strdup(char *string);
+static int hex_char_to_int(char c);
+static int parse_utf16_hex(char *string, unsigned int *result);
+static int num_bytes_in_utf8_sequence(unsigned char c);
+static int verify_utf8_sequence(unsigned char *string, int *len);
+static int is_valid_utf8(char *string, UINT string_len);
+static int is_decimal(char *string, UINT length);
+
+/* JSON Object */
+static JSON_OBJECT * json_object_init(JSON_VALUE *wrapping_value);
+static UINT json_object_add(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+static UINT json_object_resize(JSON_OBJECT *object, UINT new_capacity);
+static JSON_VALUE * json_object_nget_value(JSON_OBJECT *object, char *name, UINT n);
+static void json_object_free(JSON_OBJECT *object);
+
+/* JSON Array */
+static JSON_ARRAY * json_array_init(JSON_VALUE *wrapping_value);
+static UINT json_array_add(JSON_ARRAY *array, JSON_VALUE *value);
+static UINT json_array_resize(JSON_ARRAY *array, UINT new_capacity);
+static void json_array_free(JSON_ARRAY *array);
+
+/* JSON Value */
+static JSON_VALUE * json_value_init_string_no_copy(char *string);
+
+/* Parser */
+static UINT skip_quotes(char **string);
+static int parse_utf16(char **unprocessed, char **processed);
+static char * process_string(char *input, UINT len);
+static char * get_quoted_string(char **string);
+static JSON_VALUE * parse_object_value(char **string, UINT nesting);
+static JSON_VALUE * parse_array_value(char **string, UINT nesting);
+static JSON_VALUE * parse_string_value(char **string);
+static JSON_VALUE * parse_boolean_value(char **string);
+static JSON_VALUE * parse_number_value(char **string);
+static JSON_VALUE * parse_null_value(char **string);
+static JSON_VALUE * parse_value(char **string, UINT nesting);
+
+/* Serialization */
+static int json_serialize_to_buffer_r(JSON_VALUE *value, char *buf, int level, int is_pretty, char *num_buf);
+static int json_serialize_string(char *string, char *buf);
+static int append_indent(char *buf, int level);
+static int append_string(char *buf, char *string);
+
+/* Various */
+static char * parson_strndup(char *string, UINT n) {
+ char *output_string = (char*)parson_malloc(n + 1);
+ if (!output_string) {
+ return NULL;
+ }
+ output_string[n] = '\0';
+ strncpy(output_string, string, n);
+ return output_string;
+}
+
+static char * parson_strdup(char *string) {
+ return parson_strndup(string, StrLen(string));
+}
+
+static int hex_char_to_int(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ return -1;
+}
+
+static int parse_utf16_hex(char *s, unsigned int *result) {
+ int x1, x2, x3, x4;
+ if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') {
+ return 0;
+ }
+ x1 = hex_char_to_int(s[0]);
+ x2 = hex_char_to_int(s[1]);
+ x3 = hex_char_to_int(s[2]);
+ x4 = hex_char_to_int(s[3]);
+ if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {
+ return 0;
+ }
+ *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);
+ return 1;
+}
+
+static int num_bytes_in_utf8_sequence(unsigned char c) {
+ if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
+ return 0;
+ }
+ else if ((c & 0x80) == 0) { /* 0xxxxxxx */
+ return 1;
+ }
+ else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
+ return 2;
+ }
+ else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
+ return 3;
+ }
+ else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
+ return 4;
+ }
+ return 0; /* won't happen */
+}
+
+static int verify_utf8_sequence(unsigned char *string, int *len) {
+ unsigned int cp = 0;
+ *len = num_bytes_in_utf8_sequence(string[0]);
+
+ if (*len == 1) {
+ cp = string[0];
+ }
+ else if (*len == 2 && IS_CONT(string[1])) {
+ cp = string[0] & 0x1F;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ }
+ else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
+ cp = ((unsigned char)string[0]) & 0xF;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ }
+ else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
+ cp = string[0] & 0x7;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ cp = (cp << 6) | (string[3] & 0x3F);
+ }
+ else {
+ return 0;
+ }
+
+ /* overlong encodings */
+ if ((cp < 0x80 && *len > 1) ||
+ (cp < 0x800 && *len > 2) ||
+ (cp < 0x10000 && *len > 3)) {
+ return 0;
+ }
+
+ /* invalid unicode */
+ if (cp > 0x10FFFF) {
+ return 0;
+ }
+
+ /* surrogate halves */
+ if (cp >= 0xD800 && cp <= 0xDFFF) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_valid_utf8(char *string, UINT string_len) {
+ int len = 0;
+ char *string_end = string + string_len;
+ while (string < string_end) {
+ if (!verify_utf8_sequence((unsigned char*)string, &len)) {
+ return 0;
+ }
+ string += len;
+ }
+ return 1;
+}
+
+static int is_decimal(char *string, UINT length) {
+ if (length > 1 && string[0] == '0' && string[1] != '.') {
+ return 0;
+ }
+ if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
+ return 0;
+ }
+ while (length--) {
+ if (strchr("xX", string[length])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void remove_comments(char *string, char *start_token, char *end_token) {
+ int in_string = 0, escaped = 0;
+ UINT i;
+ char *ptr = NULL, current_char;
+ UINT start_token_len = StrLen(start_token);
+ UINT end_token_len = StrLen(end_token);
+ if (start_token_len == 0 || end_token_len == 0) {
+ return;
+ }
+ while ((current_char = *string) != '\0') {
+ if (current_char == '\\' && !escaped) {
+ escaped = 1;
+ string++;
+ continue;
+ }
+ else if (current_char == '\"' && !escaped) {
+ in_string = !in_string;
+ }
+ else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
+ for (i = 0; i < start_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = string + start_token_len;
+ ptr = strstr(string, end_token);
+ if (!ptr) {
+ return;
+ }
+ for (i = 0; i < (ptr - string) + end_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = ptr + end_token_len - 1;
+ }
+ escaped = 0;
+ string++;
+ }
+}
+
+/* JSON Object */
+static JSON_OBJECT * json_object_init(JSON_VALUE *wrapping_value) {
+ JSON_OBJECT *new_obj = (JSON_OBJECT*)parson_malloc(sizeof(JSON_OBJECT));
+ if (new_obj == NULL) {
+ return NULL;
+ }
+ new_obj->wrapping_value = wrapping_value;
+ new_obj->names = (char**)NULL;
+ new_obj->values = (JSON_VALUE**)NULL;
+ new_obj->capacity = 0;
+ new_obj->count = 0;
+ return new_obj;
+}
+
+static UINT json_object_add(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ UINT index = 0;
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonGet(object, name) != NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (object->count >= object->capacity) {
+ UINT new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);
+ if (json_object_resize(object, new_capacity) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ index = object->count;
+ object->names[index] = parson_strdup(name);
+ if (object->names[index] == NULL) {
+ return JSON_RET_ERROR;
+ }
+ value->parent = JsonGetWrappingValue(object);
+ object->values[index] = value;
+ object->count++;
+ return JSON_RET_OK;
+}
+
+static UINT json_object_resize(JSON_OBJECT *object, UINT new_capacity) {
+ char **temp_names = NULL;
+ JSON_VALUE **temp_values = NULL;
+
+ if ((object->names == NULL && object->values != NULL) ||
+ (object->names != NULL && object->values == NULL) ||
+ new_capacity == 0) {
+ return JSON_RET_ERROR; /* Shouldn't happen */
+ }
+ temp_names = (char**)parson_malloc(new_capacity * sizeof(char*));
+ if (temp_names == NULL) {
+ return JSON_RET_ERROR;
+ }
+ temp_values = (JSON_VALUE**)parson_malloc(new_capacity * sizeof(JSON_VALUE*));
+ if (temp_values == NULL) {
+ parson_free(temp_names);
+ return JSON_RET_ERROR;
+ }
+ if (object->names != NULL && object->values != NULL && object->count > 0) {
+ memcpy(temp_names, object->names, object->count * sizeof(char*));
+ memcpy(temp_values, object->values, object->count * sizeof(JSON_VALUE*));
+ }
+ parson_free(object->names);
+ parson_free(object->values);
+ object->names = temp_names;
+ object->values = temp_values;
+ object->capacity = new_capacity;
+ return JSON_RET_OK;
+}
+
+static JSON_VALUE * json_object_nget_value(JSON_OBJECT *object, char *name, UINT n) {
+ UINT i, name_length;
+ for (i = 0; i < JsonGetCount(object); i++) {
+ name_length = StrLen(object->names[i]);
+ if (name_length != n) {
+ continue;
+ }
+ if (strncmp(object->names[i], name, n) == 0) {
+ return object->values[i];
+ }
+ }
+ return NULL;
+}
+
+static void json_object_free(JSON_OBJECT *object) {
+ UINT i;
+ for (i = 0; i < object->count; i++) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ }
+ parson_free(object->names);
+ parson_free(object->values);
+ parson_free(object);
+}
+
+/* JSON Array */
+static JSON_ARRAY * json_array_init(JSON_VALUE *wrapping_value) {
+ JSON_ARRAY *new_array = (JSON_ARRAY*)parson_malloc(sizeof(JSON_ARRAY));
+ if (new_array == NULL) {
+ return NULL;
+ }
+ new_array->wrapping_value = wrapping_value;
+ new_array->items = (JSON_VALUE**)NULL;
+ new_array->capacity = 0;
+ new_array->count = 0;
+ return new_array;
+}
+
+static UINT json_array_add(JSON_ARRAY *array, JSON_VALUE *value) {
+ if (array->count >= array->capacity) {
+ UINT new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
+ if (json_array_resize(array, new_capacity) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ value->parent = JsonArrayGetWrappingValue(array);
+ array->items[array->count] = value;
+ array->count++;
+ return JSON_RET_OK;
+}
+
+static UINT json_array_resize(JSON_ARRAY *array, UINT new_capacity) {
+ JSON_VALUE **new_items = NULL;
+ if (new_capacity == 0) {
+ return JSON_RET_ERROR;
+ }
+ new_items = (JSON_VALUE**)parson_malloc(new_capacity * sizeof(JSON_VALUE*));
+ if (new_items == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (array->items != NULL && array->count > 0) {
+ memcpy(new_items, array->items, array->count * sizeof(JSON_VALUE*));
+ }
+ parson_free(array->items);
+ array->items = new_items;
+ array->capacity = new_capacity;
+ return JSON_RET_OK;
+}
+
+static void json_array_free(JSON_ARRAY *array) {
+ UINT i;
+ for (i = 0; i < array->count; i++) {
+ JsonFree(array->items[i]);
+ }
+ parson_free(array->items);
+ parson_free(array);
+}
+
+/* JSON Value */
+static JSON_VALUE * json_value_init_string_no_copy(char *string) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_STRING;
+ new_value->value.string = string;
+ return new_value;
+}
+
+/* Parser */
+static UINT skip_quotes(char **string) {
+ if (**string != '\"') {
+ return JSON_RET_ERROR;
+ }
+ SKIP_CHAR(string);
+ while (**string != '\"') {
+ if (**string == '\0') {
+ return JSON_RET_ERROR;
+ }
+ else if (**string == '\\') {
+ SKIP_CHAR(string);
+ if (**string == '\0') {
+ return JSON_RET_ERROR;
+ }
+ }
+ SKIP_CHAR(string);
+ }
+ SKIP_CHAR(string);
+ return JSON_RET_OK;
+}
+
+static int parse_utf16(char **unprocessed, char **processed) {
+ unsigned int cp, lead, trail;
+ int parse_succeeded = 0;
+ char *processed_ptr = *processed;
+ char *unprocessed_ptr = *unprocessed;
+ unprocessed_ptr++; /* skips u */
+ parse_succeeded = parse_utf16_hex(unprocessed_ptr, &cp);
+ if (!parse_succeeded) {
+ return JSON_RET_ERROR;
+ }
+ if (cp < 0x80) {
+ processed_ptr[0] = (char)cp; /* 0xxxxxxx */
+ }
+ else if (cp < 0x800) {
+ processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
+ processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 1;
+ }
+ else if (cp < 0xD800 || cp > 0xDFFF) {
+ processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
+ processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 2;
+ }
+ else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
+ lead = cp;
+ unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */
+ if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') {
+ return JSON_RET_ERROR;
+ }
+ parse_succeeded = parse_utf16_hex(unprocessed_ptr, &trail);
+ if (!parse_succeeded || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
+ return JSON_RET_ERROR;
+ }
+ cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;
+ processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
+ processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr += 3;
+ }
+ else { /* trail surrogate before lead surrogate */
+ return JSON_RET_ERROR;
+ }
+ unprocessed_ptr += 3;
+ *processed = processed_ptr;
+ *unprocessed = unprocessed_ptr;
+ return JSON_RET_OK;
+}
+
+
+/* Copies and processes passed string up to supplied length.
+Example: "\u006Corem ipsum" -> lorem ipsum */
+static char* process_string(char *input, UINT len) {
+ char *input_ptr = input;
+ UINT initial_size = (len + 1) * sizeof(char);
+ UINT final_size = 0;
+ char *output = NULL, *output_ptr = NULL, *resized_output = NULL;
+ output = (char*)parson_malloc(initial_size);
+ if (output == NULL) {
+ goto error;
+ }
+ output_ptr = output;
+ while ((*input_ptr != '\0') && (UINT)(input_ptr - input) < len) {
+ if (*input_ptr == '\\') {
+ input_ptr++;
+ switch (*input_ptr) {
+ case '\"': *output_ptr = '\"'; break;
+ case '\\': *output_ptr = '\\'; break;
+ case '/': *output_ptr = '/'; break;
+ case 'b': *output_ptr = '\b'; break;
+ case 'f': *output_ptr = '\f'; break;
+ case 'n': *output_ptr = '\n'; break;
+ case 'r': *output_ptr = '\r'; break;
+ case 't': *output_ptr = '\t'; break;
+ case 'u':
+ if (parse_utf16(&input_ptr, &output_ptr) == JSON_RET_ERROR) {
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+ }
+ else if ((unsigned char)*input_ptr < 0x20) {
+ goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
+ }
+ else {
+ *output_ptr = *input_ptr;
+ }
+ output_ptr++;
+ input_ptr++;
+ }
+ *output_ptr = '\0';
+ /* resize to new length */
+ final_size = (UINT)(output_ptr - output) + 1;
+ /* todo: don't resize if final_size == initial_size */
+ resized_output = (char*)parson_malloc(final_size);
+ if (resized_output == NULL) {
+ goto error;
+ }
+ memcpy(resized_output, output, final_size);
+ parson_free(output);
+ return resized_output;
+error:
+ parson_free(output);
+ return NULL;
+}
+
+/* Return processed contents of a string between quotes and
+skips passed argument to a matching quote. */
+static char * get_quoted_string(char **string) {
+ char *string_start = *string;
+ UINT string_len = 0;
+ UINT status = skip_quotes(string);
+ if (status != JSON_RET_OK) {
+ return NULL;
+ }
+ string_len = (UINT)(*string - string_start - 2); /* length without quotes */
+ return process_string(string_start + 1, string_len);
+}
+
+static JSON_VALUE * parse_value(char **string, UINT nesting) {
+ if (nesting > MAX_NESTING) {
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ switch (**string) {
+ case '{':
+ return parse_object_value(string, nesting + 1);
+ case '[':
+ return parse_array_value(string, nesting + 1);
+ case '\"':
+ return parse_string_value(string);
+ case 'f': case 't':
+ return parse_boolean_value(string);
+ case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return parse_number_value(string);
+ case 'n':
+ return parse_null_value(string);
+ default:
+ return NULL;
+ }
+}
+
+static JSON_VALUE * parse_object_value(char **string, UINT nesting) {
+ JSON_VALUE *output_value = JsonNewObject(), *new_value = NULL;
+ JSON_OBJECT *output_object = JsonValueGetObject(output_value);
+ char *new_key = NULL;
+ if (output_value == NULL || **string != '{') {
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == '}') { /* empty object */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_key = get_quoted_string(string);
+ if (new_key == NULL) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ':') {
+ parson_free(new_key);
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ new_value = parse_value(string, nesting);
+ if (new_value == NULL) {
+ parson_free(new_key);
+ JsonFree(output_value);
+ return NULL;
+ }
+ if (json_object_add(output_object, new_key, new_value) == JSON_RET_ERROR) {
+ parson_free(new_key);
+ JsonFree(new_value);
+ JsonFree(output_value);
+ return NULL;
+ }
+ parson_free(new_key);
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != '}' || /* Trim object after parsing is over */
+ json_object_resize(output_object, JsonGetCount(output_object)) == JSON_RET_ERROR) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_VALUE * parse_array_value(char **string, UINT nesting) {
+ JSON_VALUE *output_value = JsonNewArray(), *new_array_value = NULL;
+ JSON_ARRAY *output_array = JsonValueGetArray(output_value);
+ if (!output_value || **string != '[') {
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == ']') { /* empty array */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_array_value = parse_value(string, nesting);
+ if (new_array_value == NULL) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ if (json_array_add(output_array, new_array_value) == JSON_RET_ERROR) {
+ JsonFree(new_array_value);
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ']' || /* Trim array after parsing is over */
+ json_array_resize(output_array, JsonArrayGetCount(output_array)) == JSON_RET_ERROR) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_VALUE * parse_string_value(char **string) {
+ JSON_VALUE *value = NULL;
+ char *new_string = get_quoted_string(string);
+ if (new_string == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(new_string);
+ if (value == NULL) {
+ parson_free(new_string);
+ return NULL;
+ }
+ return value;
+}
+
+static JSON_VALUE * parse_boolean_value(char **string) {
+ UINT true_token_size = SIZEOF_TOKEN("true");
+ UINT false_token_size = SIZEOF_TOKEN("false");
+ if (strncmp("true", *string, true_token_size) == 0) {
+ *string += true_token_size;
+ return JsonNewBool(1);
+ }
+ else if (strncmp("false", *string, false_token_size) == 0) {
+ *string += false_token_size;
+ return JsonNewBool(0);
+ }
+ return NULL;
+}
+
+static JSON_VALUE * parse_number_value(char **string) {
+ char *end;
+ bool error = false;
+ UINT64 number = 0;
+ number = Json_ToInt64Ex(*string, &end, &error);
+
+ if (error)
+ {
+ return NULL;
+ }
+ *string = end;
+ return JsonNewNumber(number);
+}
+
+static JSON_VALUE * parse_null_value(char **string) {
+ UINT token_size = SIZEOF_TOKEN("null");
+ if (strncmp("null", *string, token_size) == 0) {
+ *string += token_size;
+ return JsonNewNull();
+ }
+ return NULL;
+}
+
+/* Serialization */
+#define APPEND_STRING(str) do { written = append_string(buf, (str));\
+ if (written < 0) { return -1; }\
+ if (buf != NULL) { buf += written; }\
+ written_total += written; } while(0)
+
+#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\
+ if (written < 0) { return -1; }\
+ if (buf != NULL) { buf += written; }\
+ written_total += written; } while(0)
+
+static int json_serialize_to_buffer_r(JSON_VALUE *value, char *buf, int level, int is_pretty, char *num_buf)
+{
+ char *key = NULL, *string = NULL;
+ JSON_VALUE *temp_value = NULL;
+ JSON_ARRAY *array = NULL;
+ JSON_OBJECT *object = NULL;
+ UINT i = 0, count = 0;
+ UINT64 num = 0;
+ int written = -1, written_total = 0;
+ char tmp[32];
+
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_ARRAY:
+ array = JsonValueGetArray(value);
+ count = JsonArrayGetCount(array);
+ APPEND_STRING("[");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ temp_value = JsonArrayGet(array, i);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("]");
+ return written_total;
+ case JSON_TYPE_OBJECT:
+ object = JsonValueGetObject(value);
+ count = JsonGetCount(object);
+ APPEND_STRING("{");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ key = JsonGetName(object, i);
+ if (key == NULL) {
+ return -1;
+ }
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ written = json_serialize_string(key, buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ APPEND_STRING(":");
+ if (is_pretty) {
+ APPEND_STRING(" ");
+ }
+ temp_value = JsonGet(object, key);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("}");
+ return written_total;
+ case JSON_TYPE_STRING:
+ string = JsonValueGetStr(value);
+ if (string == NULL) {
+ return -1;
+ }
+ written = json_serialize_string(string, buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSON_TYPE_BOOL:
+ if (JsonValueGetBool(value)) {
+ APPEND_STRING("true");
+ }
+ else {
+ APPEND_STRING("false");
+ }
+ return written_total;
+ case JSON_TYPE_NUMBER:
+ num = JsonValueGetNumber(value);
+ if (buf != NULL) {
+ num_buf = buf;
+ }
+ ToStr64(tmp, num);
+ Copy(num_buf, tmp, StrLen(tmp));
+ written = StrLen(tmp);
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSON_TYPE_NULL:
+ APPEND_STRING("null");
+ return written_total;
+ case JSON_TYPE_ERROR:
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+static int json_serialize_string(char *string, char *buf) {
+ UINT i = 0, len = StrLen(string);
+ char c = '\0';
+ int written = -1, written_total = 0;
+ APPEND_STRING("\"");
+ for (i = 0; i < len; i++) {
+ c = string[i];
+ switch (c) {
+ case '\"': APPEND_STRING("\\\""); break;
+ case '\\': APPEND_STRING("\\\\"); break;
+ case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */
+ case '\b': APPEND_STRING("\\b"); break;
+ case '\f': APPEND_STRING("\\f"); break;
+ case '\n': APPEND_STRING("\\n"); break;
+ case '\r': APPEND_STRING("\\r"); break;
+ case '\t': APPEND_STRING("\\t"); break;
+ case '\x00': APPEND_STRING("\\u0000"); break;
+ case '\x01': APPEND_STRING("\\u0001"); break;
+ case '\x02': APPEND_STRING("\\u0002"); break;
+ case '\x03': APPEND_STRING("\\u0003"); break;
+ case '\x04': APPEND_STRING("\\u0004"); break;
+ case '\x05': APPEND_STRING("\\u0005"); break;
+ case '\x06': APPEND_STRING("\\u0006"); break;
+ case '\x07': APPEND_STRING("\\u0007"); break;
+ /* '\x08' duplicate: '\b' */
+ /* '\x09' duplicate: '\t' */
+ /* '\x0a' duplicate: '\n' */
+ case '\x0b': APPEND_STRING("\\u000b"); break;
+ /* '\x0c' duplicate: '\f' */
+ /* '\x0d' duplicate: '\r' */
+ case '\x0e': APPEND_STRING("\\u000e"); break;
+ case '\x0f': APPEND_STRING("\\u000f"); break;
+ case '\x10': APPEND_STRING("\\u0010"); break;
+ case '\x11': APPEND_STRING("\\u0011"); break;
+ case '\x12': APPEND_STRING("\\u0012"); break;
+ case '\x13': APPEND_STRING("\\u0013"); break;
+ case '\x14': APPEND_STRING("\\u0014"); break;
+ case '\x15': APPEND_STRING("\\u0015"); break;
+ case '\x16': APPEND_STRING("\\u0016"); break;
+ case '\x17': APPEND_STRING("\\u0017"); break;
+ case '\x18': APPEND_STRING("\\u0018"); break;
+ case '\x19': APPEND_STRING("\\u0019"); break;
+ case '\x1a': APPEND_STRING("\\u001a"); break;
+ case '\x1b': APPEND_STRING("\\u001b"); break;
+ case '\x1c': APPEND_STRING("\\u001c"); break;
+ case '\x1d': APPEND_STRING("\\u001d"); break;
+ case '\x1e': APPEND_STRING("\\u001e"); break;
+ case '\x1f': APPEND_STRING("\\u001f"); break;
+ default:
+ if (buf != NULL) {
+ buf[0] = c;
+ buf += 1;
+ }
+ written_total += 1;
+ break;
+ }
+ }
+ APPEND_STRING("\"");
+ return written_total;
+}
+
+static int append_indent(char *buf, int level) {
+ int i;
+ int written = -1, written_total = 0;
+ for (i = 0; i < level; i++) {
+ APPEND_STRING(" ");
+ }
+ return written_total;
+}
+
+static int append_string(char *buf, char *string) {
+ if (buf == NULL) {
+ return (int)strlen(string);
+ }
+ return sprintf(buf, "%s", string);
+}
+
+#undef APPEND_STRING
+#undef APPEND_INDENT
+
+JSON_VALUE * JsonParseString(char *string) {
+ if (string == NULL) {
+ return NULL;
+ }
+ if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') {
+ string = string + 3; /* Support for UTF-8 BOM */
+ }
+ return parse_value((char**)&string, 0);
+}
+
+JSON_VALUE * JsonParseStringWithComments(char *string) {
+ JSON_VALUE *result = NULL;
+ char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
+ string_mutable_copy = parson_strdup(string);
+ if (string_mutable_copy == NULL) {
+ return NULL;
+ }
+ remove_comments(string_mutable_copy, "/*", "*/");
+ remove_comments(string_mutable_copy, "//", "\n");
+ string_mutable_copy_ptr = string_mutable_copy;
+ result = parse_value((char**)&string_mutable_copy_ptr, 0);
+ parson_free(string_mutable_copy);
+ return result;
+}
+
+/* JSON Object API */
+
+JSON_VALUE * JsonGet(JSON_OBJECT *object, char *name) {
+ if (object == NULL || name == NULL) {
+ return NULL;
+ }
+ return json_object_nget_value(object, name, StrLen(name));
+}
+
+char * JsonGetStr(JSON_OBJECT *object, char *name) {
+ return JsonValueGetStr(JsonGet(object, name));
+}
+
+UINT64 JsonGetNumber(JSON_OBJECT *object, char *name) {
+ return JsonValueGetNumber(JsonGet(object, name));
+}
+
+JSON_OBJECT * JsonGetObj(JSON_OBJECT *object, char *name) {
+ return JsonValueGetObject(JsonGet(object, name));
+}
+
+JSON_ARRAY * JsonGetArray(JSON_OBJECT *object, char *name) {
+ return JsonValueGetArray(JsonGet(object, name));
+}
+
+bool JsonGetBool(JSON_OBJECT *object, char *name) {
+ return JsonValueGetBool(JsonGet(object, name));
+}
+
+JSON_VALUE * JsonDotGet(JSON_OBJECT *object, char *name) {
+ char *dot_position = strchr(name, '.');
+ if (!dot_position) {
+ return JsonGet(object, name);
+ }
+ object = JsonValueGetObject(json_object_nget_value(object, name, (UINT)(dot_position - name)));
+ return JsonDotGet(object, dot_position + 1);
+}
+
+char * JsonDotGetStr(JSON_OBJECT *object, char *name) {
+ return JsonValueGetStr(JsonDotGet(object, name));
+}
+
+UINT64 JsonDotGetNumber(JSON_OBJECT *object, char *name) {
+ return JsonValueGetNumber(JsonDotGet(object, name));
+}
+
+JSON_OBJECT * JsonDotGetObj(JSON_OBJECT *object, char *name) {
+ return JsonValueGetObject(JsonDotGet(object, name));
+}
+
+JSON_ARRAY * JsonDotGetArray(JSON_OBJECT *object, char *name) {
+ return JsonValueGetArray(JsonDotGet(object, name));
+}
+
+bool JsonDotGetBool(JSON_OBJECT *object, char *name) {
+ return JsonValueGetBool(JsonDotGet(object, name));
+}
+
+UINT JsonGetCount(JSON_OBJECT *object) {
+ return object ? object->count : 0;
+}
+
+char * JsonGetName(JSON_OBJECT *object, UINT index) {
+ if (object == NULL || index >= JsonGetCount(object)) {
+ return NULL;
+ }
+ return object->names[index];
+}
+
+JSON_VALUE * JsonGetValueAt(JSON_OBJECT *object, UINT index) {
+ if (object == NULL || index >= JsonGetCount(object)) {
+ return NULL;
+ }
+ return object->values[index];
+}
+
+JSON_VALUE *JsonGetWrappingValue(JSON_OBJECT *object) {
+ return object->wrapping_value;
+}
+
+int JsonIsExists(JSON_OBJECT *object, char *name) {
+ return JsonGet(object, name) != NULL;
+}
+
+int JsonIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type) {
+ JSON_VALUE *val = JsonGet(object, name);
+ return val != NULL && JsonValueGetType(val) == type;
+}
+
+int JsonDotIsExists(JSON_OBJECT *object, char *name) {
+ return JsonDotGet(object, name) != NULL;
+}
+
+int JsonDotIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type) {
+ JSON_VALUE *val = JsonDotGet(object, name);
+ return val != NULL && JsonValueGetType(val) == type;
+}
+
+/* JSON Array API */
+JSON_VALUE * JsonArrayGet(JSON_ARRAY *array, UINT index) {
+ if (array == NULL || index >= JsonArrayGetCount(array)) {
+ return NULL;
+ }
+ return array->items[index];
+}
+
+char * JsonArrayGetStr(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetStr(JsonArrayGet(array, index));
+}
+
+UINT64 JsonArrayGetNumber(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetNumber(JsonArrayGet(array, index));
+}
+
+JSON_OBJECT * JsonArrayGetObj(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetObject(JsonArrayGet(array, index));
+}
+
+JSON_ARRAY * JsonArrayGetArray(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetArray(JsonArrayGet(array, index));
+}
+
+bool JsonArrayGetBool(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetBool(JsonArrayGet(array, index));
+}
+
+UINT JsonArrayGetCount(JSON_ARRAY *array) {
+ return array ? array->count : 0;
+}
+
+JSON_VALUE * JsonArrayGetWrappingValue(JSON_ARRAY *array) {
+ return array->wrapping_value;
+}
+
+/* JSON Value API */
+UINT JsonValueGetType(JSON_VALUE *value) {
+ return value ? value->type : JSON_TYPE_ERROR;
+}
+
+JSON_OBJECT * JsonValueGetObject(JSON_VALUE *value) {
+ if (value == NULL)
+ {
+ return NULL;
+ }
+ return JsonValueGetType(value) == JSON_TYPE_OBJECT ? value->value.object : NULL;
+}
+
+JSON_ARRAY * JsonValueGetArray(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_ARRAY ? value->value.array : NULL;
+}
+
+char * JsonValueGetStr(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_STRING ? value->value.string : NULL;
+}
+
+UINT64 JsonValueGetNumber(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_NUMBER ? value->value.number : 0;
+}
+
+bool JsonValueGetBool(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_BOOL ? value->value.boolean : 0;
+}
+
+JSON_VALUE * JsonValueGetParent(JSON_VALUE *value) {
+ return value ? value->parent : NULL;
+}
+
+void JsonFree(JSON_VALUE *value) {
+ if (value == NULL)
+ {
+ return;
+ }
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_OBJECT:
+ json_object_free(value->value.object);
+ break;
+ case JSON_TYPE_STRING:
+ parson_free(value->value.string);
+ break;
+ case JSON_TYPE_ARRAY:
+ json_array_free(value->value.array);
+ break;
+ default:
+ break;
+ }
+ parson_free(value);
+}
+
+JSON_VALUE * JsonNewObject(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_OBJECT;
+ new_value->value.object = json_object_init(new_value);
+ if (!new_value->value.object) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_VALUE * JsonNewArray(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_ARRAY;
+ new_value->value.array = json_array_init(new_value);
+ if (!new_value->value.array) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_VALUE * JsonNewStr(char *string) {
+ char *copy = NULL;
+ JSON_VALUE *value;
+ UINT string_len = 0;
+ if (string == NULL) {
+ return NULL;
+ }
+ string_len = StrLen(string);
+ if (!is_valid_utf8(string, string_len)) {
+ return NULL;
+ }
+ copy = parson_strndup(string, string_len);
+ if (copy == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(copy);
+ if (value == NULL) {
+ parson_free(copy);
+ }
+ return value;
+}
+
+JSON_VALUE * JsonNewNumber(UINT64 number) {
+ JSON_VALUE *new_value = NULL;
+ new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (new_value == NULL) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_NUMBER;
+ new_value->value.number = number;
+ return new_value;
+}
+
+JSON_VALUE * JsonNewBool(int boolean) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_BOOL;
+ new_value->value.boolean = boolean ? 1 : 0;
+ return new_value;
+}
+
+JSON_VALUE * JsonNewNull(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_NULL;
+ return new_value;
+}
+
+JSON_VALUE * JsonDeepCopy(JSON_VALUE *value) {
+ UINT i = 0;
+ JSON_VALUE *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;
+ char *temp_string = NULL, *temp_key = NULL;
+ char *temp_string_copy = NULL;
+ JSON_ARRAY *temp_array = NULL, *temp_array_copy = NULL;
+ JSON_OBJECT *temp_object = NULL, *temp_object_copy = NULL;
+
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_ARRAY:
+ temp_array = JsonValueGetArray(value);
+ return_value = JsonNewArray();
+ if (return_value == NULL) {
+ return NULL;
+ }
+ temp_array_copy = JsonValueGetArray(return_value);
+ for (i = 0; i < JsonArrayGetCount(temp_array); i++) {
+ temp_value = JsonArrayGet(temp_array, i);
+ temp_value_copy = JsonDeepCopy(temp_value);
+ if (temp_value_copy == NULL) {
+ JsonFree(return_value);
+ return NULL;
+ }
+ if (json_array_add(temp_array_copy, temp_value_copy) == JSON_RET_ERROR) {
+ JsonFree(return_value);
+ JsonFree(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSON_TYPE_OBJECT:
+ temp_object = JsonValueGetObject(value);
+ return_value = JsonNewObject();
+ if (return_value == NULL) {
+ return NULL;
+ }
+ temp_object_copy = JsonValueGetObject(return_value);
+ for (i = 0; i < JsonGetCount(temp_object); i++) {
+ temp_key = JsonGetName(temp_object, i);
+ temp_value = JsonGet(temp_object, temp_key);
+ temp_value_copy = JsonDeepCopy(temp_value);
+ if (temp_value_copy == NULL) {
+ JsonFree(return_value);
+ return NULL;
+ }
+ if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSON_RET_ERROR) {
+ JsonFree(return_value);
+ JsonFree(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSON_TYPE_BOOL:
+ return JsonNewBool(JsonValueGetBool(value));
+ case JSON_TYPE_NUMBER:
+ return JsonNewNumber(JsonValueGetNumber(value));
+ case JSON_TYPE_STRING:
+ temp_string = JsonValueGetStr(value);
+ if (temp_string == NULL) {
+ return NULL;
+ }
+ temp_string_copy = parson_strdup(temp_string);
+ if (temp_string_copy == NULL) {
+ return NULL;
+ }
+ return_value = json_value_init_string_no_copy(temp_string_copy);
+ if (return_value == NULL) {
+ parson_free(temp_string_copy);
+ }
+ return return_value;
+ case JSON_TYPE_NULL:
+ return JsonNewNull();
+ case JSON_TYPE_ERROR:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+UINT JsonGetSerializationSize(JSON_VALUE *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf);
+ return res < 0 ? 0 : (UINT)(res + 1);
+}
+
+UINT JsonSerializeToBuffer(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes) {
+ int written = -1;
+ UINT needed_size_in_bytes = JsonGetSerializationSize(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSON_RET_ERROR;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL);
+ if (written < 0) {
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+char * JsonSerializeToString(JSON_VALUE *value) {
+ UINT serialization_result = JSON_RET_ERROR;
+ UINT buf_size_bytes = JsonGetSerializationSize(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result = JsonSerializeToBuffer(value, buf, buf_size_bytes);
+ if (serialization_result == JSON_RET_ERROR) {
+ JsonFreeString(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+UINT JsonGetSerializationSizePretty(JSON_VALUE *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf);
+ return res < 0 ? 0 : (UINT)(res + 1);
+}
+
+UINT JsonSerializeToBufferPretty(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes) {
+ int written = -1;
+ UINT needed_size_in_bytes = JsonGetSerializationSizePretty(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSON_RET_ERROR;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL);
+ if (written < 0) {
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+JSON_VALUE *StrToJson(char *str)
+{
+ if (str == NULL)
+ {
+ return NULL;
+ }
+
+ return JsonParseString(str);
+}
+
+char *JsonToStr(JSON_VALUE *v)
+{
+ return JsonSerializeToStringPretty(v);
+}
+char * JsonSerializeToStringPretty(JSON_VALUE *value) {
+ UINT serialization_result = JSON_RET_ERROR;
+ UINT buf_size_bytes = JsonGetSerializationSizePretty(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result = JsonSerializeToBufferPretty(value, buf, buf_size_bytes);
+ if (serialization_result == JSON_RET_ERROR) {
+ JsonFreeString(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+void JsonFreeString(char *string) {
+ parson_free(string);
+}
+
+UINT JsonArrayDelete(JSON_ARRAY *array, UINT ix) {
+ UINT to_move_bytes = 0;
+ if (array == NULL || ix >= JsonArrayGetCount(array)) {
+ return JSON_RET_ERROR;
+ }
+ JsonFree(JsonArrayGet(array, ix));
+ to_move_bytes = (JsonArrayGetCount(array) - 1 - ix) * sizeof(JSON_VALUE*);
+ memmove(array->items + ix, array->items + ix + 1, to_move_bytes);
+ array->count -= 1;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplace(JSON_ARRAY *array, UINT ix, JSON_VALUE *value) {
+ if (array == NULL || value == NULL || value->parent != NULL || ix >= JsonArrayGetCount(array)) {
+ return JSON_RET_ERROR;
+ }
+ JsonFree(JsonArrayGet(array, ix));
+ value->parent = JsonArrayGetWrappingValue(array);
+ array->items[ix] = value;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceStr(JSON_ARRAY *array, UINT i, char* string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceNumber(JSON_ARRAY *array, UINT i, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceBool(JSON_ARRAY *array, UINT i, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceNull(JSON_ARRAY *array, UINT i) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayDeleteAll(JSON_ARRAY *array) {
+ UINT i = 0;
+ if (array == NULL) {
+ return JSON_RET_ERROR;
+ }
+ for (i = 0; i < JsonArrayGetCount(array); i++) {
+ JsonFree(JsonArrayGet(array, i));
+ }
+ array->count = 0;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAdd(JSON_ARRAY *array, JSON_VALUE *value) {
+ if (array == NULL || value == NULL || value->parent != NULL) {
+ return JSON_RET_ERROR;
+ }
+ return json_array_add(array, value);
+}
+
+UINT JsonArrayAddStr(JSON_ARRAY *array, char *string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddUniStr(JSON_ARRAY *array, wchar_t *string)
+{
+ UINT ret;
+ char *utf8 = CopyUniToUtf(string);
+
+ ret = JsonArrayAddStr(array, utf8);
+
+ Free(utf8);
+ return ret;
+}
+
+UINT JsonArrayAddNumber(JSON_ARRAY *array, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddData(JSON_ARRAY *array, void *data, UINT size)
+{
+ UINT ret;
+ char *b64 = ZeroMalloc(size * 4 + 32);
+ B64_Encode(b64, data, size);
+
+ ret = JsonArrayAddStr(array, b64);
+
+ Free(b64);
+ return ret;
+}
+
+UINT JsonArrayAddBool(JSON_ARRAY *array, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddNull(JSON_ARRAY *array) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonSet(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ UINT i = 0;
+ JSON_VALUE *old_value;
+ if (object == NULL || name == NULL || value == NULL || value->parent != NULL) {
+ return JSON_RET_ERROR;
+ }
+ old_value = JsonGet(object, name);
+ if (old_value != NULL) { /* free and overwrite old value */
+ JsonFree(old_value);
+ for (i = 0; i < JsonGetCount(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ value->parent = JsonGetWrappingValue(object);
+ object->values[i] = value;
+ return JSON_RET_OK;
+ }
+ }
+ }
+ /* add new key value pair */
+ return json_object_add(object, name, value);
+}
+
+UINT JsonSetData(JSON_OBJECT *object, char *name, void *data, UINT size)
+{
+ UINT ret;
+ char *b64 = ZeroMalloc(size * 4 + 32);
+ B64_Encode(b64, data, size);
+
+ ret = JsonSetStr(object, name, b64);
+
+ Free(b64);
+ return ret;
+}
+
+UINT JsonSetStr(JSON_OBJECT *object, char *name, char *string) {
+ return JsonSet(object, name, JsonNewStr(string));
+}
+
+UINT JsonSetUniStr(JSON_OBJECT *object, char *name, wchar_t *string)
+{
+ UINT ret;
+ char *utf8 = CopyUniToUtf(string);
+
+ ret = JsonSetStr(object, name, utf8);
+
+ Free(utf8);
+ return ret;
+}
+
+UINT JsonSetNumber(JSON_OBJECT *object, char *name, UINT64 number) {
+ return JsonSet(object, name, JsonNewNumber(number));
+}
+
+UINT JsonSetBool(JSON_OBJECT *object, char *name, int boolean) {
+ return JsonSet(object, name, JsonNewBool(boolean));
+}
+
+UINT JsonSetNull(JSON_OBJECT *object, char *name) {
+ return JsonSet(object, name, JsonNewNull());
+}
+
+UINT JsonDotSet(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ char *dot_pos = NULL;
+ char *current_name = NULL;
+ JSON_OBJECT *temp_obj = NULL;
+ JSON_VALUE *new_value = NULL;
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ dot_pos = strchr(name, '.');
+ if (dot_pos == NULL) {
+ return JsonSet(object, name, value);
+ }
+ else {
+ current_name = parson_strndup(name, (UINT)(dot_pos - name));
+ temp_obj = JsonGetObj(object, current_name);
+ if (temp_obj == NULL) {
+ new_value = JsonNewObject();
+ if (new_value == NULL) {
+ parson_free(current_name);
+ return JSON_RET_ERROR;
+ }
+ if (json_object_add(object, current_name, new_value) == JSON_RET_ERROR) {
+ JsonFree(new_value);
+ parson_free(current_name);
+ return JSON_RET_ERROR;
+ }
+ temp_obj = JsonGetObj(object, current_name);
+ }
+ parson_free(current_name);
+ return JsonDotSet(temp_obj, dot_pos + 1, value);
+ }
+}
+
+UINT JsonDotSetStr(JSON_OBJECT *object, char *name, char *string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetNumber(JSON_OBJECT *object, char *name, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetBool(JSON_OBJECT *object, char *name, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetNull(JSON_OBJECT *object, char *name) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDelete(JSON_OBJECT *object, char *name) {
+ UINT i = 0, last_item_index = 0;
+ if (object == NULL || JsonGet(object, name) == NULL) {
+ return JSON_RET_ERROR;
+ }
+ last_item_index = JsonGetCount(object) - 1;
+ for (i = 0; i < JsonGetCount(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ if (i != last_item_index) { /* Replace key value pair with one from the end */
+ object->names[i] = object->names[last_item_index];
+ object->values[i] = object->values[last_item_index];
+ }
+ object->count -= 1;
+ return JSON_RET_OK;
+ }
+ }
+ return JSON_RET_ERROR; /* No execution path should end here */
+}
+
+UINT JsonDotDelete(JSON_OBJECT *object, char *name) {
+ char *dot_pos = strchr(name, '.');
+ char *current_name = NULL;
+ JSON_OBJECT *temp_obj = NULL;
+ if (dot_pos == NULL) {
+ return JsonDelete(object, name);
+ }
+ else {
+ current_name = parson_strndup(name, (UINT)(dot_pos - name));
+ temp_obj = JsonGetObj(object, current_name);
+ parson_free(current_name);
+ if (temp_obj == NULL) {
+ return JSON_RET_ERROR;
+ }
+ return JsonDotDelete(temp_obj, dot_pos + 1);
+ }
+}
+
+UINT JsonDeleteAll(JSON_OBJECT *object) {
+ UINT i = 0;
+ if (object == NULL) {
+ return JSON_RET_ERROR;
+ }
+ for (i = 0; i < JsonGetCount(object); i++) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ }
+ object->count = 0;
+ return JSON_RET_OK;
+}
+
+UINT JsonValidate(JSON_VALUE *schema, JSON_VALUE *value) {
+ JSON_VALUE *temp_schema_value = NULL, *temp_value = NULL;
+ JSON_ARRAY *schema_array = NULL, *value_array = NULL;
+ JSON_OBJECT *schema_object = NULL, *value_object = NULL;
+ UINT schema_type = JSON_TYPE_ERROR, value_type = JSON_TYPE_ERROR;
+ char *key = NULL;
+ UINT i = 0, count = 0;
+ if (schema == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ schema_type = JsonValueGetType(schema);
+ value_type = JsonValueGetType(value);
+ if (schema_type != value_type && schema_type != JSON_TYPE_NULL) { /* null represents all values */
+ return JSON_RET_ERROR;
+ }
+ switch (schema_type) {
+ case JSON_TYPE_ARRAY:
+ schema_array = JsonValueGetArray(schema);
+ value_array = JsonValueGetArray(value);
+ count = JsonArrayGetCount(schema_array);
+ if (count == 0) {
+ return JSON_RET_OK; /* Empty array allows all types */
+ }
+ /* Get first value from array, rest is ignored */
+ temp_schema_value = JsonArrayGet(schema_array, 0);
+ for (i = 0; i < JsonArrayGetCount(value_array); i++) {
+ temp_value = JsonArrayGet(value_array, i);
+ if (JsonValidate(temp_schema_value, temp_value) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ return JSON_RET_OK;
+ case JSON_TYPE_OBJECT:
+ schema_object = JsonValueGetObject(schema);
+ value_object = JsonValueGetObject(value);
+ count = JsonGetCount(schema_object);
+ if (count == 0) {
+ return JSON_RET_OK; /* Empty object allows all objects */
+ }
+ else if (JsonGetCount(value_object) < count) {
+ return JSON_RET_ERROR; /* Tested object mustn't have less name-value pairs than schema */
+ }
+ for (i = 0; i < count; i++) {
+ key = JsonGetName(schema_object, i);
+ temp_schema_value = JsonGet(schema_object, key);
+ temp_value = JsonGet(value_object, key);
+ if (temp_value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonValidate(temp_schema_value, temp_value) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ return JSON_RET_OK;
+ case JSON_TYPE_STRING: case JSON_TYPE_NUMBER: case JSON_TYPE_BOOL: case JSON_TYPE_NULL:
+ return JSON_RET_OK; /* equality already tested before switch */
+ case JSON_TYPE_ERROR: default:
+ return JSON_RET_ERROR;
+ }
+}
+
+int JsonCmp(JSON_VALUE *a, JSON_VALUE *b) {
+ JSON_OBJECT *a_object = NULL, *b_object = NULL;
+ JSON_ARRAY *a_array = NULL, *b_array = NULL;
+ char *a_string = NULL, *b_string = NULL;
+ char *key = NULL;
+ UINT a_count = 0, b_count = 0, i = 0;
+ UINT a_type, b_type;
+ UINT64 a_num, b_num;
+ a_type = JsonValueGetType(a);
+ b_type = JsonValueGetType(b);
+ if (a_type != b_type) {
+ return 0;
+ }
+ switch (a_type) {
+ case JSON_TYPE_ARRAY:
+ a_array = JsonValueGetArray(a);
+ b_array = JsonValueGetArray(b);
+ a_count = JsonArrayGetCount(a_array);
+ b_count = JsonArrayGetCount(b_array);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ if (!JsonCmp(JsonArrayGet(a_array, i),
+ JsonArrayGet(b_array, i))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSON_TYPE_OBJECT:
+ a_object = JsonValueGetObject(a);
+ b_object = JsonValueGetObject(b);
+ a_count = JsonGetCount(a_object);
+ b_count = JsonGetCount(b_object);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ key = JsonGetName(a_object, i);
+ if (!JsonCmp(JsonGet(a_object, key),
+ JsonGet(b_object, key))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSON_TYPE_STRING:
+ a_string = JsonValueGetStr(a);
+ b_string = JsonValueGetStr(b);
+ if (a_string == NULL || b_string == NULL) {
+ return 0; /* shouldn't happen */
+ }
+ return strcmp(a_string, b_string) == 0;
+ case JSON_TYPE_BOOL:
+ return JsonValueGetBool(a) == JsonValueGetBool(b);
+ case JSON_TYPE_NUMBER:
+ a_num = JsonValueGetNumber(a);
+ b_num = JsonValueGetNumber(b);
+ return a_num == b_num;
+ case JSON_TYPE_ERROR:
+ return 1;
+ case JSON_TYPE_NULL:
+ return 1;
+ default:
+ return 1;
+ }
+}
+
+UINT JsonType(JSON_VALUE *value) {
+ return JsonValueGetType(value);
+}
+
+JSON_OBJECT * JsonObject(JSON_VALUE *value) {
+ return JsonValueGetObject(value);
+}
+
+JSON_ARRAY * JsonArray(JSON_VALUE *value) {
+ return JsonValueGetArray(value);
+}
+
+char * JsonString(JSON_VALUE *value) {
+ return JsonValueGetStr(value);
+}
+
+UINT64 JsonNumber(JSON_VALUE *value) {
+ return JsonValueGetNumber(value);
+}
+
+int JsonBool(JSON_VALUE *value) {
+ return JsonValueGetBool(value);
+}
+
+void JsonSetAllocationFunctions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {
+ parson_malloc = malloc_fun;
+ parson_free = free_fun;
+}
+
+// SYSTEMTIME to JSON string
+void SystemTimeToJsonStr(char *dst, UINT size, SYSTEMTIME *t)
+{
+ if (dst == NULL)
+ {
+ return;
+ }
+
+ if (t == NULL)
+ {
+ ClearStr(dst, size);
+ }
+ else
+ {
+ GetDateTimeStrRFC3339(dst, size, t, 0);
+ }
+}
+
+// UINT64 System Time to JSON string
+void SystemTime64ToJsonStr(char *dst, UINT size, UINT64 t)
+{
+ SYSTEMTIME st;
+ if (dst == NULL)
+ {
+ return;
+ }
+
+ if (t == 0)
+ {
+ ClearStr(dst, size);
+ }
+
+ UINT64ToSystem(&st, t);
+
+ SystemTimeToJsonStr(dst, size, &st);
+}
+
+
+
+
+
diff --git a/src/Mayaqua/Str.h b/src/Mayaqua/Str.h
index 6bb31256..6ff9051c 100644
--- a/src/Mayaqua/Str.h
+++ b/src/Mayaqua/Str.h
@@ -79,7 +79,9 @@ void BinToStrW(wchar_t *str, UINT str_size, void *data, UINT data_size);
void PrintBin(void *data, UINT size);
bool StartWith(char *str, char *key);
bool EndWith(char *str, char *key);
+bool TrimEndWith(char *dst, UINT dst_size, char *str, char *key);
UINT64 ToInt64(char *str);
+UINT64 Json_ToInt64Ex(char *str, char **endptr, bool *error);
void ToStr64(char *str, UINT64 value);
TOKEN_LIST *ParseCmdLine(char *str);
TOKEN_LIST *CopyToken(TOKEN_LIST *src);
@@ -128,6 +130,259 @@ LIST *StrToIntList(char *str, bool sorted);
void NormalizeIntListStr(char *dst, UINT dst_size, char *src, bool sorted, char *separate_str);
void ClearStr(char *str, UINT str_size);
void SetStrCaseAccordingToBits(char *str, UINT bits);
+char *UrlDecode(char *url_str);
+
+
+// *** JSON strings support
+// Original source code from Parson ( http://kgabis.github.com/parson/ )
+// Modified by dnobori
+/*
+Parson ( http://kgabis.github.com/parson/ )
+Copyright (c) 2012 - 2017 Krzysztof Gabis
+
+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.
+*/
+
+
+/* Type definitions */
+typedef union JSON_VALUE_UNION {
+ char *string;
+ UINT64 number;
+ JSON_OBJECT *object;
+ JSON_ARRAY *array;
+ int boolean;
+ int null;
+} JSON_VALUE_UNION;
+
+struct JSON_VALUE {
+ JSON_VALUE *parent;
+ UINT type;
+ JSON_VALUE_UNION value;
+};
+
+struct JSON_OBJECT {
+ JSON_VALUE *wrapping_value;
+ char **names;
+ JSON_VALUE **values;
+ UINT count;
+ UINT capacity;
+};
+
+struct JSON_ARRAY {
+ JSON_VALUE *wrapping_value;
+ JSON_VALUE **items;
+ UINT count;
+ UINT capacity;
+};
+
+
+enum JSON_TYPES {
+ JSON_TYPE_ERROR = -1,
+ JSON_TYPE_NULL = 1,
+ JSON_TYPE_STRING = 2,
+ JSON_TYPE_NUMBER = 3,
+ JSON_TYPE_OBJECT = 4,
+ JSON_TYPE_ARRAY = 5,
+ JSON_TYPE_BOOL = 6
+};
+typedef unsigned int UINT;
+
+enum JSON_RETS {
+ JSON_RET_OK = 0,
+ JSON_RET_ERROR = -1
+};
+
+typedef void * (*JSON_Malloc_Function)(UINT);
+typedef void(*JSON_Free_Function)(void *);
+
+/* Call only once, before calling any other function from parson API. If not called, malloc and free
+from stdlib will be used for all allocations */
+void JsonSetAllocationFunctions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
+
+/* Parses first JSON value in a string, returns NULL in case of error */
+JSON_VALUE * JsonParseString(char *string);
+
+/* Parses first JSON value in a string and ignores comments (/ * * / and //),
+returns NULL in case of error */
+JSON_VALUE * JsonParseStringWithComments(char *string);
+
+/* Serialization */
+UINT JsonGetSerializationSize(JSON_VALUE *value); /* returns 0 on fail */
+UINT JsonSerializeToBuffer(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes);
+char * JsonSerializeToString(JSON_VALUE *value);
+
+/* Pretty serialization */
+UINT JsonGetSerializationSizePretty(JSON_VALUE *value); /* returns 0 on fail */
+UINT JsonSerializeToBufferPretty(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes);
+char * JsonSerializeToStringPretty(JSON_VALUE *value);
+char *JsonToStr(JSON_VALUE *v);
+
+void JsonFreeString(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
+
+ /* Comparing */
+int JsonCmp(JSON_VALUE *a, JSON_VALUE *b);
+
+/* Validation
+This is *NOT* JSON Schema. It validates json by checking if object have identically
+named fields with matching types.
+For example schema {"name":"", "age":0} will validate
+{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
+but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
+In case of arrays, only first value in schema is checked against all values in tested array.
+Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
+null validates values of every type.
+*/
+UINT JsonValidate(JSON_VALUE *schema, JSON_VALUE *value);
+
+/*
+* JSON Object
+*/
+JSON_VALUE * JsonGet(JSON_OBJECT *object, char *name);
+char * JsonGetStr(JSON_OBJECT *object, char *name);
+JSON_OBJECT * JsonGetObj(JSON_OBJECT *object, char *name);
+JSON_ARRAY * JsonGetArray(JSON_OBJECT *object, char *name);
+UINT64 JsonGetNumber(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+bool JsonGetBool(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+
+ /* dotget functions enable addressing values with dot notation in nested objects,
+ just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
+ Because valid names in JSON can contain dots, some values may be inaccessible
+ this way. */
+JSON_VALUE * JsonDotGet(JSON_OBJECT *object, char *name);
+char * JsonDotGetStr(JSON_OBJECT *object, char *name);
+JSON_OBJECT * JsonDotGetObj(JSON_OBJECT *object, char *name);
+JSON_ARRAY * JsonDotGetArray(JSON_OBJECT *object, char *name);
+UINT64 JsonDotGetNumber(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+bool JsonDotGetBool(JSON_OBJECT *object, char *name); /* returns -1 on fail */
+
+ /* Functions to get available names */
+UINT JsonGetCount(JSON_OBJECT *object);
+char * JsonGetName(JSON_OBJECT *object, UINT index);
+JSON_VALUE * JsonGetValueAt(JSON_OBJECT *object, UINT index);
+JSON_VALUE * JsonGetWrappingValue(JSON_OBJECT *object);
+
+/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
+* a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
+int JsonIsExists(JSON_OBJECT *object, char *name);
+int JsonIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type);
+
+int JsonDotIsExists(JSON_OBJECT *object, char *name);
+int JsonDotIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type);
+
+/* Creates new name-value pair or frees and replaces old value with a new one.
+* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonSet(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+UINT JsonSetStr(JSON_OBJECT *object, char *name, char *string);
+UINT JsonSetUniStr(JSON_OBJECT *object, char *name, wchar_t *string);
+UINT JsonSetNumber(JSON_OBJECT *object, char *name, UINT64 number);
+UINT JsonSetBool(JSON_OBJECT *object, char *name, int boolean);
+UINT JsonSetNull(JSON_OBJECT *object, char *name);
+UINT JsonSetData(JSON_OBJECT *object, char *name, void *data, UINT size);
+
+/* Works like dotget functions, but creates whole hierarchy if necessary.
+* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonDotSet(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+UINT JsonDotSetStr(JSON_OBJECT *object, char *name, char *string);
+UINT JsonDotSetNumber(JSON_OBJECT *object, char *name, UINT64 number);
+UINT JsonDotSetBool(JSON_OBJECT *object, char *name, int boolean);
+UINT JsonDotSetNull(JSON_OBJECT *object, char *name);
+
+/* Frees and removes name-value pair */
+UINT JsonDelete(JSON_OBJECT *object, char *name);
+
+/* Works like dotget function, but removes name-value pair only on exact match. */
+UINT JsonDotDelete(JSON_OBJECT *object, char *key);
+
+/* Removes all name-value pairs in object */
+UINT JsonDeleteAll(JSON_OBJECT *object);
+
+/*
+*JSON Array
+*/
+JSON_VALUE * JsonArrayGet(JSON_ARRAY *array, UINT index);
+char * JsonArrayGetStr(JSON_ARRAY *array, UINT index);
+JSON_OBJECT * JsonArrayGetObj(JSON_ARRAY *array, UINT index);
+JSON_ARRAY * JsonArrayGetArray(JSON_ARRAY *array, UINT index);
+UINT64 JsonArrayGetNumber(JSON_ARRAY *array, UINT index); /* returns 0 on fail */
+bool JsonArrayGetBool(JSON_ARRAY *array, UINT index); /* returns 0 on fail */
+UINT JsonArrayGetCount(JSON_ARRAY *array);
+JSON_VALUE * JsonArrayGetWrappingValue(JSON_ARRAY *array);
+
+/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
+* Order of values in array may change during execution. */
+UINT JsonArrayDelete(JSON_ARRAY *array, UINT i);
+
+/* Frees and removes from array value at given index and replaces it with given one.
+* Does nothing and returns JSONFailure if index doesn't exist.
+* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonArrayReplace(JSON_ARRAY *array, UINT i, JSON_VALUE *value);
+UINT JsonArrayReplaceStr(JSON_ARRAY *array, UINT i, char* string);
+UINT JsonArrayReplaceNumber(JSON_ARRAY *array, UINT i, UINT64 number);
+UINT JsonArrayReplaceBool(JSON_ARRAY *array, UINT i, int boolean);
+UINT JsonArrayReplaceNull(JSON_ARRAY *array, UINT i);
+
+/* Frees and removes all values from array */
+UINT JsonArrayDeleteAll(JSON_ARRAY *array);
+
+/* Appends new value at the end of array.
+* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonArrayAdd(JSON_ARRAY *array, JSON_VALUE *value);
+UINT JsonArrayAddStr(JSON_ARRAY *array, char *string);
+UINT JsonArrayAddUniStr(JSON_ARRAY *array, wchar_t *string);
+UINT JsonArrayAddNumber(JSON_ARRAY *array, UINT64 number);
+UINT JsonArrayAddData(JSON_ARRAY *array, void *data, UINT size);
+UINT JsonArrayAddBool(JSON_ARRAY *array, int boolean);
+UINT JsonArrayAddNull(JSON_ARRAY *array);
+
+
+/*
+*JSON Value
+*/
+JSON_VALUE * JsonNewObject(void);
+JSON_VALUE * JsonNewArray(void);
+JSON_VALUE * JsonNewStr(char *string); /* copies passed string */
+JSON_VALUE * JsonNewNumber(UINT64 number);
+JSON_VALUE * JsonNewBool(int boolean);
+JSON_VALUE * JsonNewNull(void);
+JSON_VALUE * JsonDeepCopy(JSON_VALUE *value);
+void JsonFree(JSON_VALUE *value);
+
+UINT JsonValueGetType(JSON_VALUE *value);
+JSON_OBJECT * JsonValueGetObject(JSON_VALUE *value);
+JSON_ARRAY * JsonValueGetArray(JSON_VALUE *value);
+char * JsonValueGetStr(JSON_VALUE *value);
+UINT64 JsonValueGetNumber(JSON_VALUE *value);
+bool JsonValueGetBool(JSON_VALUE *value);
+JSON_VALUE * JsonValueGetParent(JSON_VALUE *value);
+
+/* Same as above, but shorter */
+UINT JsonType(JSON_VALUE *value);
+JSON_OBJECT * JsonObject(JSON_VALUE *value);
+JSON_ARRAY * JsonArray(JSON_VALUE *value);
+char * JsonString(JSON_VALUE *value);
+UINT64 JsonNumber(JSON_VALUE *value);
+int JsonBool(JSON_VALUE *value);
+
+void SystemTimeToJsonStr(char *dst, UINT size, SYSTEMTIME *t);
+void SystemTime64ToJsonStr(char *dst, UINT size, UINT64 t);
+
+JSON_VALUE *StrToJson(char *str);
#endif // STR_H