712 lines
16 KiB
C
712 lines
16 KiB
C
/*
|
|
Copyright 2001-2022 John Wiseman G8BPQ
|
|
|
|
This file is part of LinBPQ/BPQ32.
|
|
|
|
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
|
|
*/
|
|
|
|
/*
|
|
|
|
Paula (G8PZT)'s Remote Host Protocol interface.
|
|
For now only sufficient support for WhatsPac
|
|
|
|
|
|
*/
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
#include "cheaders.h"
|
|
#include "bpq32.h"
|
|
|
|
int FindFreeStreamNoSem();
|
|
DllExport int APIENTRY DeallocateStream(int stream);
|
|
int SendMsgNoSem(int stream, char * msg, int len);
|
|
|
|
static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len);
|
|
static int GetJSONInt(char * _REPLYBUFFER, char * Name);
|
|
|
|
// Generally Can have multiple RHP connections and each can have multiple RHF Sessions
|
|
|
|
struct RHPSessionInfo
|
|
{
|
|
SOCKET Socket; // Websocks Socket
|
|
int BPQStream;
|
|
int Handle; // RHP session ID
|
|
int Seq;
|
|
char Local[12];
|
|
char Remote[12];
|
|
BOOL Connecting; // Set while waiting for connection to complete
|
|
BOOL Listening;
|
|
BOOL Connected;
|
|
};
|
|
|
|
struct RHPConnectionInfo
|
|
{
|
|
SOCKET socket;
|
|
struct RHPSessionInfo ** RHPSessions;
|
|
int NumberofRHPSessions;
|
|
};
|
|
|
|
// Struct passed by beginhread
|
|
|
|
struct RHPParamBlock
|
|
{
|
|
unsigned char * Msg;
|
|
int Len;
|
|
SOCKET Socket;
|
|
};
|
|
|
|
|
|
|
|
//struct RHPConnectionInfo ** RHPSockets = NULL;
|
|
//int NumberofRHPConnections = 0;
|
|
|
|
struct RHPSessionInfo ** RHPSessions;
|
|
int NumberofRHPSessions;
|
|
|
|
char ErrCodes[18][24] =
|
|
{"Ok", "Unspecified", "Bad or missing type", "Invalid handle", "No memory", "Bad or missing mode",
|
|
|
|
"Invalid local address",
|
|
"Invalid remote address" ,
|
|
"Bad or missing family" ,
|
|
"Duplicate socket" ,
|
|
"No such port" ,
|
|
"Invalid protocol" ,
|
|
"Bad parameter" ,
|
|
"No buffers" ,
|
|
"Unauthorised" ,
|
|
"No Route" ,
|
|
"Operation not supported"};
|
|
|
|
|
|
|
|
|
|
extern char pgm[256];
|
|
|
|
SOCKET agwsock;
|
|
|
|
extern int SemHeldByAPI;
|
|
|
|
char szBuff[80];
|
|
|
|
int WhatsPacConfigured = 1;
|
|
|
|
|
|
int RHPPaclen = 236;
|
|
|
|
|
|
int processRHCPOpen(SOCKET Socket, char * Msg, char * ReplyBuffer);
|
|
int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer);
|
|
int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer);
|
|
|
|
|
|
|
|
void SendWebSockMessage(SOCKET socket, char * Msg, int Len)
|
|
{
|
|
int Loops = 0;
|
|
int Sent;
|
|
int TxLen;
|
|
char * OutBuffer = Msg;
|
|
|
|
// WebSock Encode. Buffer has 10 bytes on front for header but header len depends on Msg len
|
|
|
|
|
|
if (Len < 126)
|
|
{
|
|
// Two Byte Header
|
|
|
|
OutBuffer[8] = 0x81; // Fin, Data
|
|
OutBuffer[9] = Len;
|
|
|
|
TxLen = Len + 2;
|
|
OutBuffer = &Msg[8];
|
|
}
|
|
else if (Len < 65536)
|
|
{
|
|
OutBuffer[6] = 0x81; // Fin, Data
|
|
OutBuffer[7] = 126; // Unmasked, Extended Len 16
|
|
OutBuffer[8] = Len >> 8;
|
|
OutBuffer[9] = Len & 0xff;
|
|
TxLen = Len + 4;
|
|
OutBuffer = &Msg[6];
|
|
}
|
|
else
|
|
{
|
|
OutBuffer[0] = 0x81; // Fin, Data
|
|
OutBuffer[1] = 127; // Unmasked, Extended Len 64 bits
|
|
// Len is 32 bits, so pad with zeros
|
|
OutBuffer[2] = 0;
|
|
OutBuffer[3] = 0;
|
|
OutBuffer[4] = 0;
|
|
OutBuffer[5] = 0;
|
|
OutBuffer[6] = (Len >> 24) & 0xff;
|
|
OutBuffer[7] = (Len >> 16) & 0xff;
|
|
OutBuffer[8] = (Len >> 8) & 0xff;
|
|
OutBuffer[9] = Len & 0xff;
|
|
|
|
TxLen = Len + + 10;
|
|
OutBuffer = &Msg[0];
|
|
}
|
|
|
|
// Send may block
|
|
|
|
Sent = send(socket, OutBuffer, TxLen, 0);
|
|
|
|
while (Sent != TxLen && Loops++ < 3000) // 100 secs max
|
|
{
|
|
if (Sent > 0) // something sent
|
|
{
|
|
TxLen -= Sent;
|
|
memmove(OutBuffer, &OutBuffer[Sent], TxLen);
|
|
}
|
|
|
|
Sleep(30);
|
|
Sent = send(socket, OutBuffer, TxLen, 0);
|
|
if (Sent == -1)
|
|
break;
|
|
}
|
|
|
|
free(Msg);
|
|
return;
|
|
}
|
|
|
|
void ProcessRHPWebSock(SOCKET Socket, char * Msg, int MsgLen);
|
|
|
|
void RHPThread(void * Params)
|
|
{
|
|
// Params and data buffer are malloced blocks so free when done with it
|
|
|
|
struct RHPParamBlock * Block = (struct RHPParamBlock *)Params;
|
|
|
|
ProcessRHPWebSock(Block->Socket, Block->Msg, Block->Len);
|
|
|
|
free(Block->Msg);
|
|
free(Params);
|
|
}
|
|
|
|
int RHPProcessHTTPMessage(void * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE)
|
|
{
|
|
// RHP messages can be sent over Websocks or normal http but I think WhatsPac only uses WebSocks
|
|
return 0;
|
|
}
|
|
|
|
void ProcessRHPWebSock(SOCKET Socket, char * Msg, int MsgLen)
|
|
{
|
|
int Loops = 0;
|
|
int InputLen = 0;
|
|
int Len;
|
|
|
|
char Value[16];
|
|
char * OutBuffer = malloc(250000);
|
|
|
|
// struct RHPConnectionInfo * RHPSocket = NULL;
|
|
// int n;
|
|
|
|
Msg[MsgLen] = 0;
|
|
|
|
// Find Connection Record. If none, create one
|
|
|
|
// I dont think I need connection records
|
|
|
|
/*
|
|
for (n = 0; n < NumberofRHPConnections; n++)
|
|
{
|
|
if (RHPSockets[n]->socket == socket)
|
|
{
|
|
RHPSocket = RHPSockets[n];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RHPSocket == 0)
|
|
{
|
|
// See if there is an old one we can reuse
|
|
|
|
for (n = 0; n < NumberofRHPConnections; n++)
|
|
{
|
|
if (RHPSockets[n]-Socket == -1)
|
|
{
|
|
RHPSocket = RHPSockets[n];
|
|
RHP
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RHPSocket == 0)
|
|
|
|
NumberofRHPConnections;
|
|
RHPSockets = realloc(RHPSockets, sizeof(void *) * (NumberofRHPConnections + 1));
|
|
RHPSocket = RHPSockets[NumberofRHPConnections] = zalloc(sizeof (struct RHPConnectionInfo));
|
|
NumberofRHPConnections++;
|
|
RHPSocket->socket = socket;
|
|
}
|
|
*/
|
|
|
|
// {"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128}
|
|
// {"type": "openReply", "id": 82, "handle": 1, "errCode": 0, "errText": "Ok"}
|
|
// {"seqno": 0, "type": "status", "handle": 1, "flags": 0}
|
|
// ("seqno": 1, "type": "close", "handle": 1}
|
|
// {"id":40,"type":"close","handle":1}
|
|
|
|
// {"seqno": 0, "type": "status", "handle": 1, "flags": 2}.~.
|
|
// {"seqno": 1, "type": "recv", "handle": 1, "data": "Welcome to G8BPQ's Test Switch in Nottingham \rType ? for list of available commands.\r"}.
|
|
|
|
GetJSONValue(Msg, "\"type\":", Value, 15);
|
|
|
|
if (_stricmp(Value, "open") == 0)
|
|
{
|
|
Len = processRHCPOpen(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
|
|
if (Len)
|
|
SendWebSockMessage(Socket, OutBuffer, Len);
|
|
return;
|
|
}
|
|
|
|
if (_stricmp(Value, "send") == 0)
|
|
{
|
|
Len = processRHCPSend(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
|
|
SendWebSockMessage(Socket, OutBuffer, Len);
|
|
return;
|
|
}
|
|
|
|
if (_stricmp(Value, "close") == 0)
|
|
{
|
|
Len = processRHCPClose(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
|
|
SendWebSockMessage(Socket, OutBuffer, Len);
|
|
return;
|
|
}
|
|
Debugprintf(Msg);
|
|
}
|
|
|
|
void ProcessRHPWebSockClosed(SOCKET socket)
|
|
{
|
|
// Close any connections on this scoket and delete socket entry
|
|
|
|
struct RHPSessionInfo * RHPSession = 0;
|
|
int n;
|
|
|
|
// Find and close any Sessions
|
|
|
|
for (n = 0; n < NumberofRHPSessions; n++)
|
|
{
|
|
if (RHPSessions[n]->Socket == socket)
|
|
{
|
|
RHPSession = RHPSessions[n];
|
|
|
|
if (RHPSession->BPQStream)
|
|
{
|
|
Disconnect(RHPSession->BPQStream);
|
|
DeallocateStream(RHPSession->BPQStream);
|
|
|
|
RHPSession->BPQStream = 0;
|
|
|
|
}
|
|
|
|
RHPSession->Connecting = 0;
|
|
|
|
// We can't send a close to RHP endpont as socket has gone
|
|
|
|
RHPSession->Connected = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
int processRHCPOpen(SOCKET Socket, char * Msg, char * ReplyBuffer)
|
|
{
|
|
//{"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128}
|
|
|
|
struct RHPSessionInfo * RHPSession = 0;
|
|
|
|
char * Value = malloc(strlen(Msg)); // Will always be long enough
|
|
int ID;
|
|
|
|
char pfam[16];
|
|
char Mode[16];
|
|
int Port;
|
|
char Local[16];
|
|
char Remote[16];
|
|
int flags;
|
|
int Handle = 1;
|
|
int Stream;
|
|
unsigned char AXCall[10];
|
|
int Len;
|
|
int n;
|
|
|
|
// ID seems to be used for control commands like open. SeqNo for data within a session (i Think!
|
|
|
|
ID = GetJSONInt(Msg, "\"id\":");
|
|
GetJSONValue(Msg, "\"pfam\":", pfam, 15);
|
|
GetJSONValue(Msg, "\"mode\":", Mode, 15);
|
|
Port = GetJSONInt(Msg, "\"port\":");
|
|
GetJSONValue(Msg, "\"local\":", Local, 15);
|
|
GetJSONValue(Msg, "\"remote\":", Remote, 15);
|
|
flags = GetJSONInt(Msg, "\"flags\":");
|
|
|
|
if (_stricmp(pfam, "ax25") != 0)
|
|
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
|
|
|
|
if (_stricmp(Mode, "stream") == 0)
|
|
{
|
|
{
|
|
// Allocate a RHP Session
|
|
|
|
// See if there is an old one we can reuse
|
|
|
|
for (n = 0; n < NumberofRHPSessions; n++)
|
|
{
|
|
if (RHPSessions[n]->BPQStream == 0)
|
|
{
|
|
RHPSession = RHPSessions[n];
|
|
Handle = n + 1;
|
|
Stream = RHPSessions[n]->BPQStream;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RHPSession == 0)
|
|
{
|
|
RHPSessions = realloc(RHPSessions, sizeof(void *) * (NumberofRHPSessions + 1));
|
|
RHPSession = RHPSessions[NumberofRHPSessions] = zalloc(sizeof (struct RHPSessionInfo));
|
|
NumberofRHPSessions++;
|
|
|
|
Handle = NumberofRHPSessions;
|
|
}
|
|
|
|
strcpy(pgm, "RHP");
|
|
Stream = FindFreeStreamNoSem();
|
|
strcpy(pgm, "bpq32.exe");
|
|
|
|
if (Stream == 255)
|
|
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
|
|
|
|
RHPSession->BPQStream = Stream;
|
|
RHPSession->Handle = Handle;
|
|
RHPSession->Connecting = TRUE;
|
|
RHPSession->Socket = Socket;
|
|
|
|
strcpy(RHPSession->Local, Local);
|
|
strcpy(RHPSession->Remote, Remote);
|
|
|
|
Connect(Stream);
|
|
|
|
ConvToAX25(Local, AXCall);
|
|
ChangeSessionCallsign(Stream, AXCall);
|
|
|
|
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\"}", ID, Handle);
|
|
}
|
|
}
|
|
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
|
|
}
|
|
|
|
int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer)
|
|
{
|
|
// {"type":"send","handle":1,"data":";;;;;;\r","id":70}
|
|
|
|
struct RHPSessionInfo * RHPSession;
|
|
|
|
int ID;
|
|
char * Data;
|
|
char * ptr;
|
|
int c;
|
|
int Len;
|
|
|
|
int Handle = 1;
|
|
|
|
Data = malloc(strlen(Msg));
|
|
|
|
ID = GetJSONInt(Msg, "\"id\":");
|
|
Handle = GetJSONInt(Msg, "\"handle\":");
|
|
GetJSONValue(Msg, "\"data\":", Data, strlen(Msg) - 1);
|
|
|
|
if (Handle < 1 || Handle > NumberofRHPSessions)
|
|
{
|
|
free(Data);
|
|
return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errtext\": \"Invalid handle\"}", ID, Handle);
|
|
}
|
|
|
|
RHPSession = RHPSessions[Handle - 1];
|
|
|
|
// Look for \ escapes
|
|
|
|
ptr = Data;
|
|
|
|
while (ptr = strchr(ptr, '\\'))
|
|
{
|
|
c = ptr[1];
|
|
|
|
switch (c)
|
|
{
|
|
case 'r':
|
|
|
|
*ptr = 13;
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
*ptr = '\\';
|
|
break;
|
|
|
|
case '"':
|
|
|
|
*ptr = '"';
|
|
break;
|
|
}
|
|
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
|
|
ptr++;
|
|
}
|
|
|
|
Debugprintf(Data);
|
|
|
|
Len = strlen(Data);
|
|
ptr = Data;
|
|
|
|
|
|
while (Len > RHPPaclen)
|
|
{
|
|
SendMsgNoSem(RHPSession->BPQStream, ptr, RHPPaclen);
|
|
Len -= RHPPaclen;
|
|
ptr += RHPPaclen;
|
|
}
|
|
|
|
SendMsgNoSem(RHPSession->BPQStream, ptr, Len);
|
|
|
|
free(Data);
|
|
return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\", \"status\": %d}", ID, Handle, 2);
|
|
}
|
|
|
|
|
|
int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer)
|
|
{
|
|
|
|
// {"id":70,"type":"close","handle":1}
|
|
|
|
|
|
struct RHPSessionInfo * RHPSession;
|
|
|
|
int ID;
|
|
int Handle = 1;
|
|
|
|
char * OutBuffer = malloc(256);
|
|
|
|
ID = GetJSONInt(Msg, "\"id\":");
|
|
Handle = GetJSONInt(Msg, "\"handle\":");
|
|
|
|
if (Handle < 1 || Handle > NumberofRHPSessions)
|
|
return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 12, \"errtext\": \"Invalid handle\"}", ID, Handle);
|
|
|
|
|
|
RHPSession = RHPSessions[Handle - 1];
|
|
Disconnect(RHPSession->BPQStream);
|
|
RHPSession->Connected = 0;
|
|
RHPSession->Connecting = 0;
|
|
|
|
DeallocateStream(RHPSession->BPQStream);
|
|
RHPSession->BPQStream = 0;
|
|
|
|
return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 0, \"errtext\": \"Ok\"}", ID, Handle);
|
|
}
|
|
|
|
char toHex[] = "0123456789abcdef";
|
|
|
|
void RHPPoll()
|
|
{
|
|
int Stream;
|
|
int n;
|
|
int state, change;
|
|
int Len;
|
|
char * RHPMsg;
|
|
unsigned char Buffer[2048]; // Space to escape control chars
|
|
int pktlen, count;
|
|
|
|
struct RHPSessionInfo * RHPSession;
|
|
|
|
for (n = 0; n < NumberofRHPSessions; n++)
|
|
{
|
|
RHPSession = RHPSessions[n];
|
|
Stream = RHPSession->BPQStream;
|
|
|
|
// See if connected state has changed
|
|
|
|
SessionState(Stream, &state, &change);
|
|
|
|
if (change == 1)
|
|
{
|
|
if (state == 1)
|
|
{
|
|
// Connected
|
|
|
|
RHPSession->Seq = 0;
|
|
RHPSession->Connecting = FALSE;
|
|
RHPSession->Connected = TRUE;
|
|
|
|
RHPMsg = malloc(256);
|
|
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"status\", \"handle\": %d, \"flags\": 2}", RHPSession->Seq++, RHPSession->Handle);
|
|
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
|
|
|
|
// Send RHP CTEXT
|
|
|
|
RHPMsg = malloc(256);
|
|
Sleep(10); // otherwise WhatsPac doesn't display connected
|
|
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"Connected to RHP Server\\r\"}", RHPSession->Seq++, RHPSession->Handle);
|
|
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
|
|
}
|
|
else
|
|
{
|
|
// Disconnected. Send Close to client
|
|
|
|
RHPMsg = malloc(256);
|
|
Len = sprintf(&RHPMsg[10], "{\"type\": \"close\", \"seqno\": %d, \"handle\": %d}", RHPSession->Seq++, RHPSession->Handle);
|
|
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
|
|
|
|
RHPSession->Connected = 0;
|
|
RHPSession->Connecting = 0;
|
|
|
|
DeallocateStream(RHPSession->BPQStream);
|
|
RHPSession->BPQStream = 0;
|
|
}
|
|
}
|
|
do
|
|
{
|
|
GetMsg(Stream, Buffer, &pktlen, &count);
|
|
|
|
if (pktlen > 0)
|
|
{
|
|
char * ptr = Buffer;
|
|
unsigned char c;
|
|
|
|
Buffer[pktlen] = 0;
|
|
|
|
// Message is JSON so Convert CR to \r, \ to \\ " to \"
|
|
|
|
// Looks like I need to escape everything not between 0x20 and 0x7f eg \U00c3
|
|
|
|
|
|
while (c = *(ptr))
|
|
{
|
|
switch (c)
|
|
{
|
|
case 13:
|
|
|
|
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
|
|
*(ptr++) = '\\';
|
|
*(ptr++) = 'r';
|
|
break;
|
|
|
|
case '"':
|
|
|
|
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
|
|
*(ptr++) = '\\';
|
|
*(ptr++) = '"';
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
|
|
*(ptr++) = '\\';
|
|
*(ptr++) = '\\';
|
|
break;
|
|
|
|
default:
|
|
|
|
if (c > 127)
|
|
{
|
|
memmove(ptr + 6, ptr + 1, strlen(ptr) + 1);
|
|
*(ptr++) = '\\';
|
|
*(ptr++) = 'u';
|
|
*(ptr++) = '0';
|
|
*(ptr++) = '0';
|
|
*(ptr++) = toHex[c >> 4];
|
|
*(ptr++) = toHex[c & 15];
|
|
break;
|
|
}
|
|
else
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
RHPMsg = malloc(2048);
|
|
|
|
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"%s\"}", RHPSession->Seq++, RHPSession->Handle, Buffer);
|
|
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
|
|
|
|
}
|
|
|
|
}
|
|
while (count > 0);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len)
|
|
{
|
|
char * ptr1, * ptr2;
|
|
|
|
Value[0] = 0;
|
|
|
|
ptr1 = strstr(_REPLYBUFFER, Name);
|
|
|
|
if (ptr1 == 0)
|
|
return;
|
|
|
|
ptr1 += (strlen(Name) + 1);
|
|
|
|
// "data":"{\"t\":\"c\",\"n\":\"John\",\"c\":\"G8BPQ\",\"lm\":1737912636,\"le\":1737883907,\"led\":1737758451,\"v\":0.33,\"cc\":[{\"cid\":1,\"lp\":1737917257201,\"le\":1737913735726,\"led\":1737905249785},{\"cid\":0,\"lp\":1737324074107,\"le\":1737323831510,\"led\":1737322973662},{\"cid\":5,\"lp\":1737992107419,\"le\":1737931466510,\"led\":1737770056244}]}\r","id":28}
|
|
|
|
// There may be escaped " in data stream
|
|
|
|
ptr2 = strchr(ptr1, '"');
|
|
|
|
while (*(ptr2 - 1) == '\\')
|
|
{
|
|
ptr2 = strchr(ptr2 + 2, '"');
|
|
}
|
|
|
|
|
|
if (ptr2)
|
|
{
|
|
size_t ValLen = ptr2 - ptr1;
|
|
if (ValLen > Len)
|
|
ValLen = Len;
|
|
|
|
memcpy(Value, ptr1, ValLen);
|
|
Value[ValLen] = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static int GetJSONInt(char * _REPLYBUFFER, char * Name)
|
|
{
|
|
char * ptr1;
|
|
|
|
ptr1 = strstr(_REPLYBUFFER, Name);
|
|
|
|
if (ptr1 == 0)
|
|
return 0;
|
|
|
|
ptr1 += (strlen(Name));
|
|
|
|
return atoi(ptr1);
|
|
}
|
|
|
|
|