From 98b08c2ad1a2c6f1f3c34aebfd72decf4ad17445 Mon Sep 17 00:00:00 2001 From: Daiyuu Nobori Date: Tue, 28 May 2019 12:51:51 +0900 Subject: [PATCH] Implementation of the JSON-RPC API and the Web Admin interface. (dnobori's internal note: 7579 - 7682) --- src/Cedar/Admin.c | 1408 ++++++++++++++++++++++++-- src/Cedar/Admin.h | 39 +- src/Cedar/CM.c | 5 +- src/Cedar/CedarType.h | 1 + src/Cedar/Client.c | 62 +- src/Cedar/Client.h | 3 +- src/Cedar/Command.c | 6 +- src/Cedar/Connection.h | 3 + src/Cedar/DDNS.c | 3 + src/Cedar/DDNS.h | 2 + src/Cedar/EtherLog.c | 4 +- src/Cedar/Hub.c | 2 + src/Cedar/Hub.h | 1 + src/Cedar/Listener.c | 5 + src/Cedar/Nat.c | 13 +- src/Cedar/Protocol.c | 828 +++++++++++++++- src/Cedar/Protocol.h | 9 +- src/Cedar/Remote.c | 10 +- src/Cedar/Remote.h | 1 + src/Cedar/Server.c | 102 +- src/Cedar/Server.h | 2 + src/Cedar/Session.c | 9 +- src/Cedar/Session.h | 3 +- src/Mayaqua/FileIO.c | 6 + src/Mayaqua/Kernel.c | 92 ++ src/Mayaqua/Kernel.h | 2 + src/Mayaqua/MayaType.h | 5 + src/Mayaqua/Memory.c | 79 ++ src/Mayaqua/Memory.h | 6 + src/Mayaqua/Network.c | 50 + src/Mayaqua/Network.h | 3 + src/Mayaqua/Pack.c | 1006 +++++++++++++++++-- src/Mayaqua/Pack.h | 81 +- src/Mayaqua/Str.c | 2116 ++++++++++++++++++++++++++++++++++++++++ src/Mayaqua/Str.h | 255 +++++ 35 files changed, 5991 insertions(+), 231 deletions(-) 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\n

This VPN Server could not verify that you are authorized to access to the \r\nserver in administrative mode.

\r\n

For web browser logins:
You must supply the HTTP basic \r\nauthentication credential as following.

\r\n\r\n

For JSON-RPC client logins:
Instead to HTTP basic \r\nauthentication, you can also specify the HTTP header parameters as following.

\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 Found

Not 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\n

Object 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