- Currently disabled attempts at logging commands
- Added a warning for excessive data rates on monitors - Added statistics to devser and thus to scriptcontext - Added byte concatenation to dynstring - Added aborting for reflection generation to fourmess.c - Added data checksum testing to hipadaba, use for update tests - Fixed interrupt discovery in network.c, caused invalid interrupt codes which in turn confused sicscron which had to be fixed too. - Renamed ubcalc into ubcalcint in order to reclaim the ubcalc for Jurg - Added an a3offset to tasub in order to fix what I perceive an IS problem - Added support for the newer version of the Siemens SPS, the S7 - Added a not yet fully working sinqhttpopt driver which talks to http HM without libghttp
This commit is contained in:
470
sinqhttpopt.c
Normal file
470
sinqhttpopt.c
Normal file
@ -0,0 +1,470 @@
|
||||
/**
|
||||
* This is an asynchronous protocol implementation for HTTP.
|
||||
* It includes special features to store binary data coming
|
||||
* from a SINQ http histogram memory in a sinqdata object.
|
||||
* Which has to be specified on initialisation.
|
||||
*
|
||||
* copyright: see file COPYRIGHT
|
||||
*
|
||||
* After finding that libghttp actually blocks on sockets, this is
|
||||
* a reimplementation doing everything itself.
|
||||
*
|
||||
* Mark Koennecke, November 2010
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ascon.h>
|
||||
#include <ascon.i>
|
||||
#include <unistd.h>
|
||||
#include <sicsdata.h>
|
||||
#include <HistMem.h>
|
||||
#include "sicshipadaba.h"
|
||||
#include <stptok.h>
|
||||
extern char *trim(char *txt);
|
||||
/*-------------------------------------------------------------------------------
|
||||
* taken from libghttp
|
||||
*/
|
||||
static const char b64_alphabet[65] = {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/=" };
|
||||
|
||||
static char *
|
||||
http_base64_encode(const char *text) {
|
||||
/* The tricky thing about this is doing the padding at the end,
|
||||
* doing the bit manipulation requires a bit of concentration only */
|
||||
char *buffer = NULL;
|
||||
char *point = NULL;
|
||||
int inlen = 0;
|
||||
int outlen = 0;
|
||||
|
||||
/* check our args */
|
||||
if (text == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Use 'buffer' to store the output. Work out how big it should be...
|
||||
* This must be a multiple of 4 bytes */
|
||||
|
||||
inlen = strlen( text );
|
||||
/* check our arg...avoid a pesky FPE */
|
||||
if (inlen == 0)
|
||||
{
|
||||
buffer = malloc(sizeof(char));
|
||||
buffer[0] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
outlen = (inlen*4)/3;
|
||||
if( (inlen % 3) > 0 ) /* got to pad */
|
||||
outlen += 4 - (inlen % 3);
|
||||
|
||||
buffer = malloc( outlen + 1 ); /* +1 for the \0 */
|
||||
memset(buffer, 0, outlen + 1); /* initialize to zero */
|
||||
|
||||
/* now do the main stage of conversion, 3 bytes at a time,
|
||||
* leave the trailing bytes (if there are any) for later */
|
||||
|
||||
for( point=buffer; inlen>=3; inlen-=3, text+=3 ) {
|
||||
*(point++) = b64_alphabet[ *text>>2 ];
|
||||
*(point++) = b64_alphabet[ (*text<<4 & 0x30) | *(text+1)>>4 ];
|
||||
*(point++) = b64_alphabet[ (*(text+1)<<2 & 0x3c) | *(text+2)>>6 ];
|
||||
*(point++) = b64_alphabet[ *(text+2) & 0x3f ];
|
||||
}
|
||||
|
||||
/* Now deal with the trailing bytes */
|
||||
if( inlen ) {
|
||||
/* We always have one trailing byte */
|
||||
*(point++) = b64_alphabet[ *text>>2 ];
|
||||
*(point++) = b64_alphabet[ (*text<<4 & 0x30) |
|
||||
(inlen==2?*(text+1)>>4:0) ];
|
||||
*(point++) = (inlen==1?'=':b64_alphabet[ *(text+1)<<2 & 0x3c ] );
|
||||
*(point++) = '=';
|
||||
}
|
||||
|
||||
*point = '\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
char *userName;
|
||||
char *password;
|
||||
pSICSData binData;
|
||||
pHdb node;
|
||||
int sockHandle;
|
||||
int bytesExpected;
|
||||
char *contentType;
|
||||
} HttpProt, *pHttpProt;
|
||||
/*---------------------------------------------------------------------*/
|
||||
static int HTTPcallback(int handle, void *userData)
|
||||
{
|
||||
Ascon *a = (Ascon *)userData;
|
||||
pHttpProt pHttp = NULL;
|
||||
unsigned char ch;
|
||||
int length;
|
||||
char *data;
|
||||
|
||||
if(a == NULL){
|
||||
printf("really serious error in HTTPcallback\n");
|
||||
return 0;
|
||||
}
|
||||
pHttp = (pHttpProt)a->private;
|
||||
|
||||
data = (char *)ANETreadPtr(handle,&length);
|
||||
if(data != NULL){
|
||||
DynStringConcatBytes(a->rdBuffer,data,length);
|
||||
}
|
||||
ANETreadConsume(handle,length);
|
||||
return 1;
|
||||
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static void sendAuth(pHttpProt pHttp)
|
||||
{
|
||||
char buffer[256];
|
||||
char *encoded = NULL;
|
||||
|
||||
if(pHttp->userName != NULL){
|
||||
snprintf(buffer,255,"%s:%s", pHttp->userName,pHttp->password);
|
||||
encoded = http_base64_encode(buffer);
|
||||
if(encoded != NULL){
|
||||
snprintf(buffer,255,"Authorization: Basic %s\r\n\r\n", encoded);
|
||||
free(encoded);
|
||||
}
|
||||
} else {
|
||||
strcpy(buffer,"\r\n");
|
||||
}
|
||||
ANETwrite(pHttp->sockHandle,buffer,strlen(buffer));
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static void sendGet(pHttpProt pHttp, char *path)
|
||||
{
|
||||
char buffer[1024];
|
||||
char hostname[256];
|
||||
|
||||
gethostname(hostname,255);
|
||||
snprintf(buffer,1024,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
|
||||
path, hostname);
|
||||
ANETwrite(pHttp->sockHandle, buffer,strlen(buffer));
|
||||
sendAuth(pHttp);
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static void sendPost(pHttpProt pHttp, char *path, char *data)
|
||||
{
|
||||
char buffer[1024];
|
||||
char hostname[256];
|
||||
|
||||
gethostname(hostname,255);
|
||||
snprintf(buffer,1024,"POST %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
|
||||
path, hostname);
|
||||
ANETwrite(pHttp->sockHandle, buffer,strlen(buffer));
|
||||
snprintf(buffer,1024,"Content-Length: %d\r\n", strlen(data));
|
||||
ANETwrite(pHttp->sockHandle, buffer,strlen(buffer));
|
||||
sendAuth(pHttp);
|
||||
ANETwrite(pHttp->sockHandle,data,strlen(data));
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static void sendRequest(pHttpProt pHttp, char *data)
|
||||
{
|
||||
char *path, *dataCopy, *pPtr;
|
||||
|
||||
dataCopy = strdup(data);
|
||||
|
||||
pPtr = strchr(dataCopy,':');
|
||||
if(pPtr == NULL){
|
||||
path = dataCopy;
|
||||
sendGet(pHttp,path);
|
||||
free(dataCopy);
|
||||
return;
|
||||
} else {
|
||||
if(strstr(dataCopy,"node") != NULL){
|
||||
path = pPtr+1;
|
||||
pPtr = strchr(path,':');
|
||||
*pPtr = '\0';
|
||||
pHttp->node = FindHdbNode(NULL,path,pServ->dummyCon);
|
||||
path = pPtr+1;
|
||||
sendGet(pHttp,path);
|
||||
free(dataCopy);
|
||||
return;
|
||||
} else if(strstr(dataCopy,"post") != NULL){
|
||||
path = pPtr+1;
|
||||
pPtr = strchr(path,':');
|
||||
*pPtr = '\0';
|
||||
sendPost(pHttp,path, pPtr+1);
|
||||
free(dataCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static void handleReply(Ascon * a)
|
||||
{
|
||||
char *pPtr = NULL, *pType = NULL, *path = NULL;
|
||||
int len, i, *dataPtr = NULL;
|
||||
HistInt *hmData = NULL;
|
||||
pHttpProt pHttp = (pHttpProt) a->private;
|
||||
|
||||
pPtr = GetCharArray(a->rdBuffer);
|
||||
len = GetDynStringLength(a->rdBuffer);
|
||||
if (strstr(pPtr, "ERROR") != NULL) {
|
||||
AsconError(a, pPtr, 0);
|
||||
} else if (strstr(pPtr, "Authentication Error") != NULL) {
|
||||
AsconError(a, pPtr, 0);
|
||||
} else {
|
||||
pType = pHttp->contentType;
|
||||
if (strstr(pType, "sinqhm") != NULL) {
|
||||
hmData = (HistInt *) pPtr;
|
||||
len = len / sizeof(HistInt);
|
||||
if(pHttp->node == NULL){
|
||||
clearSICSData(pHttp->binData);
|
||||
dataPtr = getSICSDataPointer(pHttp->binData, 0, len);
|
||||
for (i = 0; i < len; i++) {
|
||||
dataPtr[i] = htonl(hmData[i]);
|
||||
}
|
||||
assignSICSType(pHttp->binData, 0, len, INTTYPE);
|
||||
DynStringClear(a->rdBuffer);
|
||||
DynStringCopy(a->rdBuffer, "SICSDATA");
|
||||
} else {
|
||||
if(pHttp->node->value.arrayLength != len){
|
||||
if(pHttp->node->value.v.intArray != NULL){
|
||||
free(pHttp->node->value.v.intArray);
|
||||
}
|
||||
pHttp->node->value.v.intArray = malloc(len*sizeof(int));
|
||||
if(pHttp->node->value.v.intArray == NULL){
|
||||
AsconError(a,"Out of memory ",0);
|
||||
return;
|
||||
}
|
||||
pHttp->node->value.arrayLength = len;
|
||||
}
|
||||
for(i = 0; i < len; i++){
|
||||
pHttp->node->value.v.intArray[i] = htonl(hmData[i]);
|
||||
}
|
||||
NotifyHipadabaPar(pHttp->node,NULL);
|
||||
/*
|
||||
path = GetHipadabaPath(pHttp->node);
|
||||
if(path != NULL){
|
||||
printf("Sinqhttpprot has updated node: %s\n", path);
|
||||
free(path);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static int processHeader(Ascon *a)
|
||||
{
|
||||
char line[132], *pPtr, *data, *colon;
|
||||
pHttpProt pHttp = (pHttpProt) a->private;
|
||||
int status, length;
|
||||
|
||||
data = GetCharArray(a->rdBuffer);
|
||||
assert(data != NULL);
|
||||
/*
|
||||
* printf("%s",data);
|
||||
*/
|
||||
|
||||
pPtr = data;
|
||||
pPtr = stptok(pPtr, line, 132, "\n");
|
||||
sscanf(line,"HTTP/1.%*d %03d", &status);
|
||||
if(status != 200 && status != 201){
|
||||
AsconError(a,line,AsconFailure);
|
||||
return 0;
|
||||
}
|
||||
while((pPtr = stptok(pPtr,line,132,"\n")) != NULL){
|
||||
colon = strchr(line,':');
|
||||
if(colon != NULL){
|
||||
*colon = '\0';
|
||||
colon++;
|
||||
strtolower(line);
|
||||
if(strstr(line,"content-length") != NULL){
|
||||
pHttp->bytesExpected = atoi(trim(colon));
|
||||
}
|
||||
if(strstr(line,"content-type") != NULL){
|
||||
if(pHttp->contentType != NULL){
|
||||
free(pHttp->contentType);
|
||||
}
|
||||
pHttp->contentType = strdup(trim(colon));
|
||||
}
|
||||
if(strstr(line,"connection") != NULL){
|
||||
strtolower(colon);
|
||||
if(strstr(colon,"close") != NULL){
|
||||
pHttp->bytesExpected = INT32_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Hack off the header
|
||||
*/
|
||||
pPtr = strstr(data, "\r\n\r\n");
|
||||
length = GetDynStringLength(a->rdBuffer);
|
||||
length -= 4 + (pPtr - data);
|
||||
if(length > 0){
|
||||
data = malloc(length*sizeof(char));
|
||||
if(data == NULL){
|
||||
AsconError(a,"Out of memory", AsconFailure);
|
||||
return 0;
|
||||
}
|
||||
memcpy(data,pPtr+4,length);
|
||||
DynStringClear(a->rdBuffer);
|
||||
DynStringConcatBytes(a->rdBuffer,data,length);
|
||||
free(data);
|
||||
} else {
|
||||
DynStringClear(a->rdBuffer);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static int HttpHandler(Ascon * a)
|
||||
{
|
||||
pHttpProt pHttp = (pHttpProt) a->private;
|
||||
int socke, selStat, port, status;
|
||||
fd_set rmask;
|
||||
struct timeval tmo = { 0, 0 };
|
||||
char buffer[1024];
|
||||
char *hostport, *colon;
|
||||
|
||||
switch (a->state) {
|
||||
case AsconConnectStart:
|
||||
a->state = AsconConnecting;
|
||||
break;
|
||||
case AsconConnecting:
|
||||
a->state = AsconConnectDone; /* success */
|
||||
break;
|
||||
case AsconWriteStart:
|
||||
if(!ANETvalidHandle(pHttp->sockHandle)){
|
||||
hostport = strdup(a->hostport);
|
||||
colon = strchr(hostport, ':');
|
||||
if (colon == NULL){
|
||||
port = 80;
|
||||
} else {
|
||||
*colon = '\0';
|
||||
port = atoi(colon + 1);
|
||||
}
|
||||
if (port <= 0) {
|
||||
AsconError(a, "bad port number", 0);
|
||||
return 1;
|
||||
}
|
||||
pHttp->sockHandle = ANETconnect(hostport,port);
|
||||
free(hostport);
|
||||
if(pHttp->sockHandle < 0){
|
||||
AsconError(a,"Failed to connect", 0);
|
||||
} else {
|
||||
ANETsetReadCallback(pHttp->sockHandle,
|
||||
HTTPcallback, a,NULL);
|
||||
a->state = AsconWriting;
|
||||
}
|
||||
} else {
|
||||
a->state = AsconWriting;
|
||||
}
|
||||
return 1;
|
||||
break;
|
||||
case AsconWriting:
|
||||
sendRequest(pHttp,GetCharArray(a->wrBuffer));
|
||||
a->state = AsconWriteDone;
|
||||
a->start = DoubleTime();
|
||||
pHttp->bytesExpected = -1;
|
||||
DynStringClear(a->rdBuffer);
|
||||
return 1;
|
||||
break;
|
||||
case AsconReading:
|
||||
ANETprocess();
|
||||
/**
|
||||
* Here we have basically three conditions to check:
|
||||
* - <cr><ld><cr><lf> detected means that we received
|
||||
* the complete header.
|
||||
* - enough bytes read: termination
|
||||
* - socket closed means all data has been read
|
||||
*/
|
||||
if(strstr(GetCharArray(a->rdBuffer), "\r\n\r\n") != NULL){
|
||||
status = processHeader(a);
|
||||
if(status != 1){
|
||||
a->state = AsconReadDone;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(!ANETvalidHandle(pHttp->sockHandle)){
|
||||
handleReply(a);
|
||||
a->state = AsconReadDone;
|
||||
return 1;
|
||||
}
|
||||
if(pHttp->bytesExpected > 0 && GetDynStringLength(a->rdBuffer) >= pHttp->bytesExpected ){
|
||||
handleReply(a);
|
||||
a->state = AsconReadDone;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return AsconStdHandler(a);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
static void killHttp(void *data)
|
||||
{
|
||||
pHttpProt prot = (pHttpProt) data;
|
||||
if (prot == NULL) {
|
||||
return;
|
||||
}
|
||||
if (prot->password != NULL) {
|
||||
free(prot->password);
|
||||
}
|
||||
if (prot->userName != NULL) {
|
||||
free(prot->userName);
|
||||
}
|
||||
if(prot->contentType != NULL){
|
||||
free(prot->contentType);
|
||||
}
|
||||
free(prot);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
static int HttpProtInit(Ascon * a, SConnection * con,
|
||||
int argc, char *argv[])
|
||||
{
|
||||
pHttpProt pHttp = NULL;
|
||||
|
||||
pHttp = calloc(sizeof(HttpProt), 1);
|
||||
if (pHttp == NULL) {
|
||||
SCWrite(con, "ERROR: out of memory in HttpProtInit", eError);
|
||||
return 0;
|
||||
}
|
||||
if (argc < 3) {
|
||||
return 0;
|
||||
}
|
||||
a->hostport = strdup(argv[1]);
|
||||
pHttp->binData = (pSICSData) FindCommandData(pServ->pSics,
|
||||
argv[2], "SICSData");
|
||||
if (pHttp->binData == NULL) {
|
||||
SCWrite(con, "ERROR: SICSData object not found", eError);
|
||||
return 0;
|
||||
}
|
||||
if (argc > 3) {
|
||||
a->timeout = atof(argv[3]);
|
||||
} else {
|
||||
a->timeout = 10.;
|
||||
}
|
||||
if (argc > 5) {
|
||||
pHttp->userName = strdup(argv[4]);
|
||||
pHttp->password = strdup(argv[5]);
|
||||
}
|
||||
pHttp->sockHandle = -10;
|
||||
pHttp->contentType = strdup("text/html");
|
||||
a->private = pHttp;
|
||||
a->killPrivate = killHttp;
|
||||
a->state = AsconConnectStart;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
void AddHttpOptProtocoll()
|
||||
{
|
||||
AsconProtocol *prot = NULL;
|
||||
|
||||
prot = calloc(sizeof(AsconProtocol), 1);
|
||||
prot->name = strdup("sinqhttpopt");
|
||||
prot->init = HttpProtInit;
|
||||
prot->handler = HttpHandler;
|
||||
AsconInsertProtocol(prot);
|
||||
}
|
Reference in New Issue
Block a user