Files
sics/secopprot3.c

198 lines
5.0 KiB
C

#include <errno.h>
#include <tcl.h>
#include <json-c/json.h>
#include "uselect.h"
#include "ascon.h"
#include "ascon.i"
#include "script.h"
/*
* protocol for SECoP
*
* Markus Zolliker Feb 2019
*/
/* the first item in this linked list contains a partial or empty buffer.
all the following are the messages in the queue */
typedef struct secop_msg {
struct secop_msg *next;
char *message;
} SecopMsg;
typedef struct secop_buf {
SecopMsg *queue;
pDynString buffer;
} SecopBuf;
/*----------------------------------------------------------------------------*/
void SecopKillPrivate(void *private) {
SecopBuf *msgbuf = private;
SecopMsg *msg, *next;
for (msg = msgbuf->queue; msg != NULL; msg = next) {
next = msg->next;
if (msg->message) {
free(msg->message);
}
free(msg);
}
DeleteDynString(msgbuf->buffer);
free(msgbuf);
}
/*----------------------------------------------------------------------------*/
int Secop3ReadChars(int fd, char *bytes, int maxbytes)
{
fd_set rmask;
struct timeval tmo = { 0, 0 };
int ret;
FD_ZERO(&rmask);
FD_SET(fd, &rmask);
ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo);
if (ret <= 0)
return ret;
ret = recv(fd, bytes, maxbytes, 0);
/* PrintChar(*chr); fflush(stdout); */
if (ret > 0)
return ret;
if (ret == 0) {
errno = ECONNRESET;
return ASCON_DISCONNECTED;
}
return ASCON_RECV_ERROR;
}
/*----------------------------------------------------------------------------*/
int Secop3ProtHandler(Ascon *a) {
int ret;
char *result;
char *space;
char *json;
char *specifier;
Tcl_Obj *tcllist[3], *tclobj;
int l, l1, l2;
struct json_object *jobj;
char buffer[1024];
char *bytes;
char *str;
SecopBuf *msgbuf;
SecopMsg *msg, **msgp;
if (a->state <= AsconConnectDone) {
/* handle connect states */
return AsconStdHandler(a);
}
/* treat incoming messages always */
msgbuf = a->private;
if (msgbuf == NULL) {
msgbuf = calloc(1, sizeof *msgbuf);
a->private = msgbuf;
a->killPrivate = SecopKillPrivate;
msgbuf->buffer = CreateDynString(60, 65536);
}
ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1);
while (ret > 0) {
assert(ret < sizeof buffer);
buffer[ret] = '\0';
bytes = buffer;
str = strchr(bytes, '\n');
while (str) {
l = str - bytes;
DynStringConcatBytes(msgbuf->buffer, bytes, l);
/* append at end of queue */
for (msgp = &msgbuf->queue; *msgp != NULL; msgp = &(*msgp)->next);
msg = calloc(1, sizeof *msg);
*msgp = msg;
msg->message = strdup(GetCharArray(msgbuf->buffer));
DynStringClear(msgbuf->buffer);
bytes += l + 1;
str = strchr(bytes, '\n');
}
DynStringConcat(msgbuf->buffer, bytes);
ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1);
}
if (ret < 0) {
AsconError(a, "SECoP.rd", errno);
}
switch (a->state) {
case AsconWriting:
/* do not skip garbage */
l = GetDynStringLength(a->wrBuffer) - a->wrPos;
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
if (ret < 0) {
AsconError(a, "SECoP.wr", errno); /* sets state to AsconFailed */
} else {
a->wrPos += ret;
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
a->state = AsconWriteDone;
}
}
return 1;
case AsconWriteStart:
if (GetDynStringLength(a->wrBuffer) == 0) {
/* do not send empty message */
a->state = AsconWriteDone;
return 1;
}
if (a->sendTerminator) {
DynStringConcat(a->wrBuffer, a->sendTerminator);
}
a->wrPos = 0;
a->state = AsconWriting;
break;
case AsconReadStart:
a->state = AsconReadDone;
msgbuf = a->private;
if (msgbuf->queue == NULL) {
DynStringClear(a->rdBuffer);
return 1;
}
msg = msgbuf->queue;
msgbuf->queue = msg->next;
space = strchr(msg->message, ' ');
jobj = NULL;
specifier = "";
if (space) {
l1 = space - msg->message;
specifier = space + 1;
json = strchr(specifier, ' ');
if (json) {
l2 = json - specifier;
json++;
jobj = json_tokener_parse(json);
} else {
l2 = strlen(specifier);
}
} else {
l1 = strlen(msg->message);
l2 = 0;
}
tcllist[0] = Tcl_NewStringObj(msg->message, l1);
tcllist[1] = Tcl_NewStringObj(specifier, l2);
if (jobj) {
tcllist[2] = json2tcl(jobj);
tclobj = Tcl_NewListObj(3, tcllist);
} else {
tclobj = Tcl_NewListObj(2, tcllist);
}
DynStringCopy(a->rdBuffer, Tcl_GetString(tclobj));
Tcl_DecrRefCount(tclobj); /* this line avoids a big memory leak! */
a->responseValid = 1;
json_object_put(jobj); /* free jobj */
free(msg->message);
free(msg);
break;
}
return 1;
}
/*----------------------------------------------------------------------------*/
void AddSecop3Protocol() {
static AsconProtocol secopprot;
secopprot.name = "SECoP3";
secopprot.handler = Secop3ProtHandler;
secopprot.init = AsconStdInit;
AsconInsertProtocol(&secopprot);
}