Browse Source

Implementation of the JSON-RPC API and the Web Admin interface. (dnobori's internal note: 7579 - 7682)

Daiyuu Nobori 6 years ago
parent
commit
98b08c2ad1

File diff suppressed because it is too large
+ 1151 - 0
src/Cedar/Admin.c


+ 36 - 3
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);

+ 3 - 2
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)
 	{

+ 1 - 0
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;
 
 
 // ==============================================================

+ 37 - 25
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;
 	}

+ 2 - 1
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

+ 4 - 2
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)
 	{

+ 3 - 0
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
 };
 
 

+ 3 - 0
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

+ 2 - 0
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

+ 3 - 1
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

+ 2 - 0
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);

+ 1 - 0
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

+ 5 - 0
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

+ 9 - 4
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)
 {

+ 826 - 2
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

+ 8 - 1
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

+ 9 - 1
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);

+ 1 - 0
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
 

+ 73 - 25
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);
 		}
+	}
+	else
+	{
+		char dir_name[MAX_PATH];
 
-		FreeDir(dir);
+		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);
 		}
+	}
+	else
+	{
+		char dir_name[MAX_PATH];
+
+		Format(dir_name, sizeof(dir_name), "security_log/%s", hubname);
 
-		FreeDir(dir);
+		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);
 		}

+ 2 - 0
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
 };

+ 6 - 3
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

+ 2 - 1
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

+ 6 - 0
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);

+ 92 - 0
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;
 	}
 

+ 2 - 0
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);

+ 5 - 0
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;

+ 79 - 0
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)
 {

+ 6 - 0
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
 

+ 50 - 0
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)
 {

+ 3 - 0
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);

+ 918 - 72
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;
 }
 
+// Set CurrentJsonHint_GroupName to PACK
+void PackSetCurrentJsonGroupName(PACK *p, char *json_group_name)
+{
+	if (p == NULL)
+	{
+		return;
+	}
+
+	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);
+	}
+}
+
 // Add a bool type into the PACK
-void PackAddBool(PACK *p, char *name, bool b)
+ELEMENT *PackAddBool(PACK *p, char *name, bool b)
 {
-	PackAddInt(p, name, b ? 1 : 0);
+	ELEMENT *e = PackAddInt(p, name, b ? 1 : 0);
+	if (e != NULL)
+	{
+		e->JsonHint_IsBool = true;
+	}
+	return e;
 }
-void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
+ELEMENT *PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
 {
-	PackAddIntEx(p, name, b ? 1 : 0, index, 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
-void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
+ELEMENT *PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
 {
 	// Validate arguments
 	if (p == NULL || name == NULL || addr == NULL)
 	{
-		return;
+		return NULL;
 	}
 
-	PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
+	return PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
 }
-void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+ELEMENT *PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
 {
-	PackAddIp6AddrEx(p, name, addr, 0, 1);
+	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;
+		return NULL;
 	}
 
 	v = NewStrValue(str);
-	AddElement(p, NewElement(name, VALUE_STR, 1, &v));
+	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;
+	}
+
+	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;
 }
 
 
+
+

+ 62 - 19
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

+ 2116 - 0
src/Mayaqua/Str.c

@@ -11,6 +11,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
+#include <ctype.h>
 #include <stdarg.h>
 #include <time.h>
 #include <errno.h>
@@ -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);
+}
+
+
+
+
+

+ 255 - 0
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
 

Some files were not shown because too many files changed in this diff