Initial commit for mongoose httpd
This commit is contained in:
parent
2beb0f29af
commit
63e9cbed40
35 changed files with 6963 additions and 481 deletions
3
3rdparty/CMakeLists.txt
vendored
3
3rdparty/CMakeLists.txt
vendored
|
@ -6,3 +6,6 @@ endif()
|
|||
if (ENABLE_IAX)
|
||||
add_subdirectory(iaxclient/lib)
|
||||
endif()
|
||||
|
||||
add_subdirectory(mongoose)
|
||||
add_subdirectory(cjson)
|
||||
|
|
11
3rdparty/cjson/CMakeLists.txt
vendored
Normal file
11
3rdparty/cjson/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
cJSON.c
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
cJSON.h
|
||||
)
|
||||
|
||||
flightgear_component(CJSON "${SOURCES}" "${HEADERS}")
|
20
3rdparty/cjson/LICENSE
vendored
Normal file
20
3rdparty/cjson/LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
|
247
3rdparty/cjson/README
vendored
Normal file
247
3rdparty/cjson/README
vendored
Normal file
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
Welcome to cJSON.
|
||||
|
||||
cJSON aims to be the dumbest possible parser that you can get your job done with.
|
||||
It's a single file of C, and a single header file.
|
||||
|
||||
JSON is described best here: http://www.json.org/
|
||||
It's like XML, but fat-free. You use it to move data around, store things, or just
|
||||
generally represent your program's state.
|
||||
|
||||
|
||||
First up, how do I build?
|
||||
Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
|
||||
For example, to build the test app:
|
||||
|
||||
gcc cJSON.c test.c -o test -lm
|
||||
./test
|
||||
|
||||
|
||||
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
|
||||
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
|
||||
in one of two modes: Auto and Manual. Let's have a quick run-through.
|
||||
|
||||
|
||||
I lifted some JSON from this page: http://www.json.org/fatfree.html
|
||||
That page inspired me to write cJSON, which is a parser that tries to share the same
|
||||
philosophy as JSON itself. Simple, dumb, out of the way.
|
||||
|
||||
Some JSON:
|
||||
{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {
|
||||
"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,
|
||||
"frame rate": 24
|
||||
}
|
||||
}
|
||||
|
||||
Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
|
||||
you have a char * to it. Everything is a cJSON struct.
|
||||
Get it parsed:
|
||||
cJSON *root = cJSON_Parse(my_json_string);
|
||||
|
||||
This is an object. We're in C. We don't have objects. But we do have structs.
|
||||
What's the framerate?
|
||||
|
||||
cJSON *format = cJSON_GetObjectItem(root,"format");
|
||||
int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
|
||||
|
||||
|
||||
Want to change the framerate?
|
||||
cJSON_GetObjectItem(format,"frame rate")->valueint=25;
|
||||
|
||||
Back to disk?
|
||||
char *rendered=cJSON_Print(root);
|
||||
|
||||
Finished? Delete the root (this takes care of everything else).
|
||||
cJSON_Delete(root);
|
||||
|
||||
That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
|
||||
before you dereference them. If you want to see how you'd build this struct in code?
|
||||
cJSON *root,*fmt;
|
||||
root=cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
|
||||
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
|
||||
cJSON_AddStringToObject(fmt,"type", "rect");
|
||||
cJSON_AddNumberToObject(fmt,"width", 1920);
|
||||
cJSON_AddNumberToObject(fmt,"height", 1080);
|
||||
cJSON_AddFalseToObject (fmt,"interlace");
|
||||
cJSON_AddNumberToObject(fmt,"frame rate", 24);
|
||||
|
||||
Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
|
||||
Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
|
||||
a few from elsewhere.
|
||||
|
||||
What about manual mode? First up you need some detail.
|
||||
Let's cover how the cJSON objects represent the JSON data.
|
||||
cJSON doesn't distinguish arrays from objects in handling; just type.
|
||||
Each cJSON has, potentially, a child, siblings, value, a name.
|
||||
|
||||
The root object has: Object Type and a Child
|
||||
The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling:
|
||||
Sibling has type Object, name "format", and a child.
|
||||
That child has type String, name "type", value "rect", and a sibling:
|
||||
Sibling has type Number, name "width", value 1920, and a sibling:
|
||||
Sibling has type Number, name "height", value 1080, and a sibling:
|
||||
Sibling hs type False, name "interlace", and a sibling:
|
||||
Sibling has type Number, name "frame rate", value 24
|
||||
|
||||
Here's the structure:
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev;
|
||||
struct cJSON *child;
|
||||
|
||||
int type;
|
||||
|
||||
char *valuestring;
|
||||
int valueint;
|
||||
double valuedouble;
|
||||
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
By default all values are 0 unless set by virtue of being meaningful.
|
||||
|
||||
next/prev is a doubly linked list of siblings. next takes you to your sibling,
|
||||
prev takes you back from your sibling to you.
|
||||
Only objects and arrays have a "child", and it's the head of the doubly linked list.
|
||||
A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0.
|
||||
The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in
|
||||
cJSON.h
|
||||
|
||||
A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read
|
||||
valuedouble.
|
||||
|
||||
Any entry which is in the linked list which is the child of an object will have a "string"
|
||||
which is the "name" of the entry. When I said "name" in the above example, that's "string".
|
||||
"string" is the JSON name for the 'variable name' if you will.
|
||||
|
||||
Now you can trivially walk the lists, recursively, and parse as you please.
|
||||
You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take
|
||||
the root object, and traverse the structure (which is, formally, an N-tree),
|
||||
and tokenise as you please. If you wanted to build a callback style parser, this is how
|
||||
you'd do it (just an example, since these things are very specific):
|
||||
|
||||
void parse_and_callback(cJSON *item,const char *prefix)
|
||||
{
|
||||
while (item)
|
||||
{
|
||||
char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
|
||||
sprintf(newprefix,"%s/%s",prefix,item->name);
|
||||
int dorecurse=callback(newprefix, item->type, item);
|
||||
if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
|
||||
item=item->next;
|
||||
free(newprefix);
|
||||
}
|
||||
}
|
||||
|
||||
The prefix process will build you a separated list, to simplify your callback handling.
|
||||
The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
|
||||
let you invoke it per-item. For the item above, your callback might look like this:
|
||||
|
||||
int callback(const char *name,int type,cJSON *item)
|
||||
{
|
||||
if (!strcmp(name,"name")) { /* populate name */ }
|
||||
else if (!strcmp(name,"format/type") { /* handle "rect" */ }
|
||||
else if (!strcmp(name,"format/width") { /* 800 */ }
|
||||
else if (!strcmp(name,"format/height") { /* 600 */ }
|
||||
else if (!strcmp(name,"format/interlace") { /* false */ }
|
||||
else if (!strcmp(name,"format/frame rate") { /* 24 */ }
|
||||
return 1;
|
||||
}
|
||||
|
||||
Alternatively, you might like to parse iteratively.
|
||||
You'd use:
|
||||
|
||||
void parse_object(cJSON *item)
|
||||
{
|
||||
int i; for (i=0;i<cJSON_GetArraySize(item);i++)
|
||||
{
|
||||
cJSON *subitem=cJSON_GetArrayItem(item,i);
|
||||
// handle subitem.
|
||||
}
|
||||
}
|
||||
|
||||
Or, for PROPER manual mode:
|
||||
|
||||
void parse_object(cJSON *item)
|
||||
{
|
||||
cJSON *subitem=item->child;
|
||||
while (subitem)
|
||||
{
|
||||
// handle subitem
|
||||
if (subitem->child) parse_object(subitem->child);
|
||||
|
||||
subitem=subitem->next;
|
||||
}
|
||||
}
|
||||
|
||||
Of course, this should look familiar, since this is just a stripped-down version
|
||||
of the callback-parser.
|
||||
|
||||
This should cover most uses you'll find for parsing. The rest should be possible
|
||||
to infer.. and if in doubt, read the source! There's not a lot of it! ;)
|
||||
|
||||
|
||||
In terms of constructing JSON data, the example code above is the right way to do it.
|
||||
You can, of course, hand your sub-objects to other functions to populate.
|
||||
Also, if you find a use for it, you can manually build the objects.
|
||||
For instance, suppose you wanted to build an array of objects?
|
||||
|
||||
cJSON *objects[24];
|
||||
|
||||
cJSON *Create_array_of_anything(cJSON **items,int num)
|
||||
{
|
||||
int i;cJSON *prev, *root=cJSON_CreateArray();
|
||||
for (i=0;i<24;i++)
|
||||
{
|
||||
if (!i) root->child=objects[i];
|
||||
else prev->next=objects[i], objects[i]->prev=prev;
|
||||
prev=objects[i];
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
and simply: Create_array_of_anything(objects,24);
|
||||
|
||||
cJSON doesn't make any assumptions about what order you create things in.
|
||||
You can attach the objects, as above, and later add children to each
|
||||
of those objects.
|
||||
|
||||
As soon as you call cJSON_Print, it renders the structure to text.
|
||||
|
||||
|
||||
|
||||
The test.c code shows how to handle a bunch of typical cases. If you uncomment
|
||||
the code, it'll load, parse and print a bunch of test files, also from json.org,
|
||||
which are more complex than I'd care to try and stash into a const char array[].
|
||||
|
||||
|
||||
Enjoy cJSON!
|
||||
|
||||
|
||||
- Dave Gamble, Aug 2009
|
596
3rdparty/cjson/cJSON.c
vendored
Normal file
596
3rdparty/cjson/cJSON.c
vendored
Normal file
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* cJSON */
|
||||
/* JSON parser in C. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
static const char *ep;
|
||||
|
||||
const char *cJSON_GetErrorPtr(void) {return ep;}
|
||||
|
||||
static int cJSON_strcasecmp(const char *s1,const char *s2)
|
||||
{
|
||||
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
|
||||
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
|
||||
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
|
||||
}
|
||||
|
||||
static void *(*cJSON_malloc)(size_t sz) = malloc;
|
||||
static void (*cJSON_free)(void *ptr) = free;
|
||||
|
||||
static char* cJSON_strdup(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
char* copy;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if (!(copy = (char*)cJSON_malloc(len))) return 0;
|
||||
memcpy(copy,str,len);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void cJSON_InitHooks(cJSON_Hooks* hooks)
|
||||
{
|
||||
if (!hooks) { /* Reset hooks */
|
||||
cJSON_malloc = malloc;
|
||||
cJSON_free = free;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
|
||||
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
|
||||
}
|
||||
|
||||
/* Internal constructor. */
|
||||
static cJSON *cJSON_New_Item(void)
|
||||
{
|
||||
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
|
||||
if (node) memset(node,0,sizeof(cJSON));
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Delete a cJSON structure. */
|
||||
void cJSON_Delete(cJSON *c)
|
||||
{
|
||||
cJSON *next;
|
||||
while (c)
|
||||
{
|
||||
next=c->next;
|
||||
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
|
||||
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
|
||||
if (c->string) cJSON_free(c->string);
|
||||
cJSON_free(c);
|
||||
c=next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the input text to generate a number, and populate the result into item. */
|
||||
static const char *parse_number(cJSON *item,const char *num)
|
||||
{
|
||||
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
|
||||
|
||||
if (*num=='-') sign=-1,num++; /* Has sign? */
|
||||
if (*num=='0') num++; /* is zero */
|
||||
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
|
||||
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
|
||||
if (*num=='e' || *num=='E') /* Exponent? */
|
||||
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
|
||||
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
|
||||
}
|
||||
|
||||
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
|
||||
|
||||
item->valuedouble=n;
|
||||
item->valueint=(int)n;
|
||||
item->type=cJSON_Number;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Render the number nicely from the given item into a string. */
|
||||
static char *print_number(cJSON *item)
|
||||
{
|
||||
char *str;
|
||||
double d=item->valuedouble;
|
||||
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
|
||||
{
|
||||
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
|
||||
if (str) sprintf(str,"%d",item->valueint);
|
||||
}
|
||||
else
|
||||
{
|
||||
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
|
||||
if (str)
|
||||
{
|
||||
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
|
||||
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
|
||||
else sprintf(str,"%f",d);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static unsigned parse_hex4(const char *str)
|
||||
{
|
||||
unsigned h=0;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Parse the input text into an unescaped cstring, and populate item. */
|
||||
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
static const char *parse_string(cJSON *item,const char *str)
|
||||
{
|
||||
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
|
||||
if (*str!='\"') {ep=str;return 0;} /* not a string! */
|
||||
|
||||
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
|
||||
|
||||
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
|
||||
if (!out) return 0;
|
||||
|
||||
ptr=str+1;ptr2=out;
|
||||
while (*ptr!='\"' && *ptr)
|
||||
{
|
||||
if (*ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
ptr++;
|
||||
switch (*ptr)
|
||||
{
|
||||
case 'b': *ptr2++='\b'; break;
|
||||
case 'f': *ptr2++='\f'; break;
|
||||
case 'n': *ptr2++='\n'; break;
|
||||
case 'r': *ptr2++='\r'; break;
|
||||
case 't': *ptr2++='\t'; break;
|
||||
case 'u': /* transcode utf16 to utf8. */
|
||||
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
|
||||
|
||||
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
|
||||
|
||||
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
|
||||
{
|
||||
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
|
||||
uc2=parse_hex4(ptr+3);ptr+=6;
|
||||
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
|
||||
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
|
||||
}
|
||||
|
||||
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
|
||||
|
||||
switch (len) {
|
||||
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 1: *--ptr2 =(uc | firstByteMark[len]);
|
||||
}
|
||||
ptr2+=len;
|
||||
break;
|
||||
default: *ptr2++=*ptr; break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
*ptr2=0;
|
||||
if (*ptr=='\"') ptr++;
|
||||
item->valuestring=out;
|
||||
item->type=cJSON_String;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Render the cstring provided to an escaped version that can be printed. */
|
||||
static char *print_string_ptr(const char *str)
|
||||
{
|
||||
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
|
||||
|
||||
if (!str) return cJSON_strdup("");
|
||||
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
|
||||
|
||||
out=(char*)cJSON_malloc(len+3);
|
||||
if (!out) return 0;
|
||||
|
||||
ptr2=out;ptr=str;
|
||||
*ptr2++='\"';
|
||||
while (*ptr)
|
||||
{
|
||||
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
*ptr2++='\\';
|
||||
switch (token=*ptr++)
|
||||
{
|
||||
case '\\': *ptr2++='\\'; break;
|
||||
case '\"': *ptr2++='\"'; break;
|
||||
case '\b': *ptr2++='b'; break;
|
||||
case '\f': *ptr2++='f'; break;
|
||||
case '\n': *ptr2++='n'; break;
|
||||
case '\r': *ptr2++='r'; break;
|
||||
case '\t': *ptr2++='t'; break;
|
||||
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr2++='\"';*ptr2++=0;
|
||||
return out;
|
||||
}
|
||||
/* Invote print_string_ptr (which is useful) on an item. */
|
||||
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
|
||||
|
||||
/* Predeclare these prototypes. */
|
||||
static const char *parse_value(cJSON *item,const char *value);
|
||||
static char *print_value(cJSON *item,int depth,int fmt);
|
||||
static const char *parse_array(cJSON *item,const char *value);
|
||||
static char *print_array(cJSON *item,int depth,int fmt);
|
||||
static const char *parse_object(cJSON *item,const char *value);
|
||||
static char *print_object(cJSON *item,int depth,int fmt);
|
||||
|
||||
/* Utility to jump whitespace and cr/lf */
|
||||
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
|
||||
|
||||
/* Parse an object - create a new root, and populate. */
|
||||
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
|
||||
{
|
||||
const char *end=0;
|
||||
cJSON *c=cJSON_New_Item();
|
||||
ep=0;
|
||||
if (!c) return 0; /* memory fail */
|
||||
|
||||
end=parse_value(c,skip(value));
|
||||
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
|
||||
|
||||
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
|
||||
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
|
||||
if (return_parse_end) *return_parse_end=end;
|
||||
return c;
|
||||
}
|
||||
/* Default options for cJSON_Parse */
|
||||
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
|
||||
|
||||
/* Render a cJSON item/entity/structure to text. */
|
||||
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
|
||||
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
|
||||
|
||||
/* Parser core - when encountering text, process appropriately. */
|
||||
static const char *parse_value(cJSON *item,const char *value)
|
||||
{
|
||||
if (!value) return 0; /* Fail on null. */
|
||||
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
|
||||
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
|
||||
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
|
||||
if (*value=='\"') { return parse_string(item,value); }
|
||||
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
|
||||
if (*value=='[') { return parse_array(item,value); }
|
||||
if (*value=='{') { return parse_object(item,value); }
|
||||
|
||||
ep=value;return 0; /* failure. */
|
||||
}
|
||||
|
||||
/* Render a value to text. */
|
||||
static char *print_value(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char *out=0;
|
||||
if (!item) return 0;
|
||||
switch ((item->type)&255)
|
||||
{
|
||||
case cJSON_NULL: out=cJSON_strdup("null"); break;
|
||||
case cJSON_False: out=cJSON_strdup("false");break;
|
||||
case cJSON_True: out=cJSON_strdup("true"); break;
|
||||
case cJSON_Number: out=print_number(item);break;
|
||||
case cJSON_String: out=print_string(item);break;
|
||||
case cJSON_Array: out=print_array(item,depth,fmt);break;
|
||||
case cJSON_Object: out=print_object(item,depth,fmt);break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an array from input text. */
|
||||
static const char *parse_array(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='[') {ep=value;return 0;} /* not an array! */
|
||||
|
||||
item->type=cJSON_Array;
|
||||
value=skip(value+1);
|
||||
if (*value==']') return value+1; /* empty array. */
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
if (!item->child) return 0; /* memory fail */
|
||||
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_value(child,skip(value+1)));
|
||||
if (!value) return 0; /* memory fail */
|
||||
}
|
||||
|
||||
if (*value==']') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an array to text */
|
||||
static char *print_array(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char **entries;
|
||||
char *out=0,*ptr,*ret;int len=5;
|
||||
cJSON *child=item->child;
|
||||
int numentries=0,i=0,fail=0;
|
||||
|
||||
/* How many entries in the array? */
|
||||
while (child) numentries++,child=child->next;
|
||||
/* Explicitly handle numentries==0 */
|
||||
if (!numentries)
|
||||
{
|
||||
out=(char*)cJSON_malloc(3);
|
||||
if (out) strcpy(out,"[]");
|
||||
return out;
|
||||
}
|
||||
/* Allocate an array to hold the values for each */
|
||||
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
memset(entries,0,numentries*sizeof(char*));
|
||||
/* Retrieve all the results: */
|
||||
child=item->child;
|
||||
while (child && !fail)
|
||||
{
|
||||
ret=print_value(child,depth+1,fmt);
|
||||
entries[i++]=ret;
|
||||
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
|
||||
child=child->next;
|
||||
}
|
||||
|
||||
/* If we didn't fail, try to malloc the output string */
|
||||
if (!fail) out=(char*)cJSON_malloc(len);
|
||||
/* If that fails, we fail. */
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure. */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
|
||||
cJSON_free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output array. */
|
||||
*out='[';
|
||||
ptr=out+1;*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
|
||||
cJSON_free(entries[i]);
|
||||
}
|
||||
cJSON_free(entries);
|
||||
*ptr++=']';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an object from the text. */
|
||||
static const char *parse_object(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='{') {ep=value;return 0;} /* not an object! */
|
||||
|
||||
item->type=cJSON_Object;
|
||||
value=skip(value+1);
|
||||
if (*value=='}') return value+1; /* empty array. */
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
if (!item->child) return 0;
|
||||
value=skip(parse_string(child,skip(value)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_string(child,skip(value+1)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
}
|
||||
|
||||
if (*value=='}') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an object to text. */
|
||||
static char *print_object(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char **entries=0,**names=0;
|
||||
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
|
||||
cJSON *child=item->child;
|
||||
int numentries=0,fail=0;
|
||||
/* Count the number of entries. */
|
||||
while (child) numentries++,child=child->next;
|
||||
/* Explicitly handle empty object case */
|
||||
if (!numentries)
|
||||
{
|
||||
out=(char*)cJSON_malloc(fmt?depth+4:3);
|
||||
if (!out) return 0;
|
||||
ptr=out;*ptr++='{';
|
||||
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
/* Allocate space for the names and the objects */
|
||||
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
names=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!names) {cJSON_free(entries);return 0;}
|
||||
memset(entries,0,sizeof(char*)*numentries);
|
||||
memset(names,0,sizeof(char*)*numentries);
|
||||
|
||||
/* Collect all the results into our arrays: */
|
||||
child=item->child;depth++;if (fmt) len+=depth;
|
||||
while (child)
|
||||
{
|
||||
names[i]=str=print_string_ptr(child->string);
|
||||
entries[i++]=ret=print_value(child,depth,fmt);
|
||||
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
|
||||
child=child->next;
|
||||
}
|
||||
|
||||
/* Try to allocate the output string */
|
||||
if (!fail) out=(char*)cJSON_malloc(len);
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
|
||||
cJSON_free(names);cJSON_free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output: */
|
||||
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
|
||||
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
|
||||
*ptr++=':';if (fmt) *ptr++='\t';
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) *ptr++=',';
|
||||
if (fmt) *ptr++='\n';*ptr=0;
|
||||
cJSON_free(names[i]);cJSON_free(entries[i]);
|
||||
}
|
||||
|
||||
cJSON_free(names);cJSON_free(entries);
|
||||
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Get Array size/item / object item. */
|
||||
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
|
||||
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
|
||||
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
|
||||
|
||||
/* Utility for array list handling. */
|
||||
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
|
||||
/* Utility for handling references. */
|
||||
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
|
||||
|
||||
/* Add item to array/object. */
|
||||
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
|
||||
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
|
||||
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
|
||||
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
|
||||
|
||||
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
|
||||
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
|
||||
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
|
||||
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
|
||||
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
|
||||
|
||||
/* Replace array/object items with new ones. */
|
||||
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
|
||||
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
|
||||
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
|
||||
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
|
||||
|
||||
/* Create basic types: */
|
||||
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
|
||||
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
|
||||
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
|
||||
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
|
||||
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
|
||||
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
|
||||
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
|
||||
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
|
||||
|
||||
/* Create Arrays: */
|
||||
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
|
||||
/* Duplication */
|
||||
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
|
||||
{
|
||||
cJSON *newitem,*cptr,*nptr=0,*newchild;
|
||||
/* Bail on bad ptr */
|
||||
if (!item) return 0;
|
||||
/* Create new item */
|
||||
newitem=cJSON_New_Item();
|
||||
if (!newitem) return 0;
|
||||
/* Copy over all vars */
|
||||
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
|
||||
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
|
||||
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
|
||||
/* If non-recursive, then we're done! */
|
||||
if (!recurse) return newitem;
|
||||
/* Walk the ->next chain for the child. */
|
||||
cptr=item->child;
|
||||
while (cptr)
|
||||
{
|
||||
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
|
||||
if (!newchild) {cJSON_Delete(newitem);return 0;}
|
||||
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
|
||||
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
|
||||
cptr=cptr->next;
|
||||
}
|
||||
return newitem;
|
||||
}
|
||||
|
||||
void cJSON_Minify(char *json)
|
||||
{
|
||||
char *into=json;
|
||||
while (*json)
|
||||
{
|
||||
if (*json==' ') json++;
|
||||
else if (*json=='\t') json++; // Whitespace characters.
|
||||
else if (*json=='\r') json++;
|
||||
else if (*json=='\n') json++;
|
||||
else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line.
|
||||
else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments.
|
||||
else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive.
|
||||
else *into++=*json++; // All other characters.
|
||||
}
|
||||
*into=0; // and null-terminate.
|
||||
}
|
143
3rdparty/cjson/cJSON.h
vendored
Normal file
143
3rdparty/cjson/cJSON.h
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_False 0
|
||||
#define cJSON_True 1
|
||||
#define cJSON_NULL 2
|
||||
#define cJSON_Number 3
|
||||
#define cJSON_String 4
|
||||
#define cJSON_Array 5
|
||||
#define cJSON_Object 6
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
|
||||
int type; /* The type of the item, as above. */
|
||||
|
||||
char *valuestring; /* The item's string, if type==cJSON_String */
|
||||
int valueint; /* The item's number, if type==cJSON_Number */
|
||||
double valuedouble; /* The item's number, if type==cJSON_Number */
|
||||
|
||||
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks {
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
|
||||
extern cJSON *cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern char *cJSON_Print(cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
extern char *cJSON_PrintUnformatted(cJSON *item);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
extern void cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
extern int cJSON_GetArraySize(cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
|
||||
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
extern const char *cJSON_GetErrorPtr(void);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
extern cJSON *cJSON_CreateNull(void);
|
||||
extern cJSON *cJSON_CreateTrue(void);
|
||||
extern cJSON *cJSON_CreateFalse(void);
|
||||
extern cJSON *cJSON_CreateBool(int b);
|
||||
extern cJSON *cJSON_CreateNumber(double num);
|
||||
extern cJSON *cJSON_CreateString(const char *string);
|
||||
extern cJSON *cJSON_CreateArray(void);
|
||||
extern cJSON *cJSON_CreateObject(void);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
|
||||
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
|
||||
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
|
||||
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
|
||||
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
|
||||
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
|
||||
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
|
||||
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
|
||||
|
||||
extern void cJSON_Minify(char *json);
|
||||
|
||||
/* Macros for creating things quickly. */
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
|
||||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
|
||||
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
|
||||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
|
||||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
156
3rdparty/cjson/test.c
vendored
Normal file
156
3rdparty/cjson/test.c
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
/* Parse text to JSON, then render back to text, and print! */
|
||||
void doit(char *text)
|
||||
{
|
||||
char *out;cJSON *json;
|
||||
|
||||
json=cJSON_Parse(text);
|
||||
if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());}
|
||||
else
|
||||
{
|
||||
out=cJSON_Print(json);
|
||||
cJSON_Delete(json);
|
||||
printf("%s\n",out);
|
||||
free(out);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a file, parse, render back, etc. */
|
||||
void dofile(char *filename)
|
||||
{
|
||||
FILE *f=fopen(filename,"rb");fseek(f,0,SEEK_END);long len=ftell(f);fseek(f,0,SEEK_SET);
|
||||
char *data=(char*)malloc(len+1);fread(data,1,len,f);fclose(f);
|
||||
doit(data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* Used by some code below as an example datatype. */
|
||||
struct record {const char *precision;double lat,lon;const char *address,*city,*state,*zip,*country; };
|
||||
|
||||
/* Create a bunch of objects as demonstration. */
|
||||
void create_objects()
|
||||
{
|
||||
cJSON *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. */
|
||||
|
||||
/* Here we construct some JSON standards, from the JSON site. */
|
||||
|
||||
/* Our "Video" datatype: */
|
||||
root=cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
|
||||
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
|
||||
cJSON_AddStringToObject(fmt,"type", "rect");
|
||||
cJSON_AddNumberToObject(fmt,"width", 1920);
|
||||
cJSON_AddNumberToObject(fmt,"height", 1080);
|
||||
cJSON_AddFalseToObject (fmt,"interlace");
|
||||
cJSON_AddNumberToObject(fmt,"frame rate", 24);
|
||||
|
||||
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out); /* Print to text, Delete the cJSON, print it, release the string. */
|
||||
|
||||
/* Our "days of the week" array: */
|
||||
const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
|
||||
root=cJSON_CreateStringArray(strings,7);
|
||||
|
||||
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
|
||||
|
||||
/* Our matrix: */
|
||||
int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};
|
||||
root=cJSON_CreateArray();
|
||||
for (i=0;i<3;i++) cJSON_AddItemToArray(root,cJSON_CreateIntArray(numbers[i],3));
|
||||
|
||||
/* cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */
|
||||
|
||||
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
|
||||
|
||||
|
||||
/* Our "gallery" item: */
|
||||
int ids[4]={116,943,234,38793};
|
||||
root=cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(root, "Image", img=cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(img,"Width",800);
|
||||
cJSON_AddNumberToObject(img,"Height",600);
|
||||
cJSON_AddStringToObject(img,"Title","View from 15th Floor");
|
||||
cJSON_AddItemToObject(img, "Thumbnail", thm=cJSON_CreateObject());
|
||||
cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
|
||||
cJSON_AddNumberToObject(thm,"Height",125);
|
||||
cJSON_AddStringToObject(thm,"Width","100");
|
||||
cJSON_AddItemToObject(img,"IDs", cJSON_CreateIntArray(ids,4));
|
||||
|
||||
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
|
||||
|
||||
/* Our array of "records": */
|
||||
struct record fields[2]={
|
||||
{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
|
||||
{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};
|
||||
|
||||
root=cJSON_CreateArray();
|
||||
for (i=0;i<2;i++)
|
||||
{
|
||||
cJSON_AddItemToArray(root,fld=cJSON_CreateObject());
|
||||
cJSON_AddStringToObject(fld, "precision", fields[i].precision);
|
||||
cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
|
||||
cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
|
||||
cJSON_AddStringToObject(fld, "Address", fields[i].address);
|
||||
cJSON_AddStringToObject(fld, "City", fields[i].city);
|
||||
cJSON_AddStringToObject(fld, "State", fields[i].state);
|
||||
cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
|
||||
cJSON_AddStringToObject(fld, "Country", fields[i].country);
|
||||
}
|
||||
|
||||
/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */
|
||||
|
||||
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
|
||||
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
/* a bunch of json: */
|
||||
char text1[]="{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}";
|
||||
char text2[]="[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
|
||||
char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n";
|
||||
char text4[]="{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http:/*www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": \"100\"\n },\n \"IDs\": [116, 943, 234, 38793]\n }\n }";
|
||||
char text5[]="[\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.7668,\n \"Longitude\": -122.3959,\n \"Address\": \"\",\n \"City\": \"SAN FRANCISCO\",\n \"State\": \"CA\",\n \"Zip\": \"94107\",\n \"Country\": \"US\"\n },\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.371991,\n \"Longitude\": -122.026020,\n \"Address\": \"\",\n \"City\": \"SUNNYVALE\",\n \"State\": \"CA\",\n \"Zip\": \"94085\",\n \"Country\": \"US\"\n }\n ]";
|
||||
|
||||
/* Process each json textblock by parsing, then rebuilding: */
|
||||
doit(text1);
|
||||
doit(text2);
|
||||
doit(text3);
|
||||
doit(text4);
|
||||
doit(text5);
|
||||
|
||||
/* Parse standard testfiles: */
|
||||
/* dofile("../../tests/test1"); */
|
||||
/* dofile("../../tests/test2"); */
|
||||
/* dofile("../../tests/test3"); */
|
||||
/* dofile("../../tests/test4"); */
|
||||
/* dofile("../../tests/test5"); */
|
||||
|
||||
/* Now some samplecode for building objects concisely: */
|
||||
create_objects();
|
||||
|
||||
return 0;
|
||||
}
|
22
3rdparty/cjson/tests/test1
vendored
Normal file
22
3rdparty/cjson/tests/test1
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
3rdparty/cjson/tests/test2
vendored
Normal file
11
3rdparty/cjson/tests/test2
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [
|
||||
{"value": "New", "onclick": "CreateNewDoc()"},
|
||||
{"value": "Open", "onclick": "OpenDoc()"},
|
||||
{"value": "Close", "onclick": "CloseDoc()"}
|
||||
]
|
||||
}
|
||||
}}
|
26
3rdparty/cjson/tests/test3
vendored
Normal file
26
3rdparty/cjson/tests/test3
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
88
3rdparty/cjson/tests/test4
vendored
Normal file
88
3rdparty/cjson/tests/test4
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
{"web-app": {
|
||||
"servlet": [
|
||||
{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500}},
|
||||
{
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"}},
|
||||
{
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||
|
||||
{
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||
{
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true}}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"},
|
||||
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
27
3rdparty/cjson/tests/test5
vendored
Normal file
27
3rdparty/cjson/tests/test5
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
{"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [
|
||||
{"id": "Open"},
|
||||
{"id": "OpenNew", "label": "Open New"},
|
||||
null,
|
||||
{"id": "ZoomIn", "label": "Zoom In"},
|
||||
{"id": "ZoomOut", "label": "Zoom Out"},
|
||||
{"id": "OriginalView", "label": "Original View"},
|
||||
null,
|
||||
{"id": "Quality"},
|
||||
{"id": "Pause"},
|
||||
{"id": "Mute"},
|
||||
null,
|
||||
{"id": "Find", "label": "Find..."},
|
||||
{"id": "FindAgain", "label": "Find Again"},
|
||||
{"id": "Copy"},
|
||||
{"id": "CopyAgain", "label": "Copy Again"},
|
||||
{"id": "CopySVG", "label": "Copy SVG"},
|
||||
{"id": "ViewSVG", "label": "View SVG"},
|
||||
{"id": "ViewSource", "label": "View Source"},
|
||||
{"id": "SaveAs", "label": "Save As"},
|
||||
null,
|
||||
{"id": "Help"},
|
||||
{"id": "About", "label": "About Adobe CVG Viewer..."}
|
||||
]
|
||||
}}
|
11
3rdparty/mongoose/CMakeLists.txt
vendored
Normal file
11
3rdparty/mongoose/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
mongoose.c
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
mongoose.h
|
||||
)
|
||||
|
||||
flightgear_component(Mongoose "${SOURCES}" "${HEADERS}")
|
16
3rdparty/mongoose/LICENSE
vendored
Normal file
16
3rdparty/mongoose/LICENSE
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see http://www.gnu.org/licenses.
|
||||
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, 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.
|
||||
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in http://cesanta.com/products.html.
|
4211
3rdparty/mongoose/mongoose.c
vendored
Normal file
4211
3rdparty/mongoose/mongoose.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
123
3rdparty/mongoose/mongoose.h
vendored
Normal file
123
3rdparty/mongoose/mongoose.h
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This library is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this library under the terms of the GNU General
|
||||
// Public License, 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.
|
||||
//
|
||||
// Alternatively, you can license this library under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
|
||||
|
||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||
#define MONGOOSE_HEADER_INCLUDED
|
||||
|
||||
#define MONGOOSE_VERSION "5.2"
|
||||
|
||||
#include <stdio.h> // required for FILE
|
||||
#include <stddef.h> // required for size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// This structure contains information about HTTP request.
|
||||
struct mg_connection {
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
|
||||
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
||||
const char *local_ip; // Local IP address
|
||||
unsigned short remote_port; // Client's port
|
||||
unsigned short local_port; // Local port number
|
||||
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[30];
|
||||
|
||||
char *content; // POST (or websocket message) data, or NULL
|
||||
size_t content_len; // content length
|
||||
|
||||
int is_websocket; // Connection is a websocket connection
|
||||
int status_code; // HTTP status code for HTTP error handler
|
||||
int wsbits; // First byte of the websocket frame
|
||||
void *server_param; // Parameter passed to mg_add_uri_handler()
|
||||
void *connection_param; // Placeholder for connection-specific data
|
||||
};
|
||||
|
||||
struct mg_server; // Opaque structure describing server instance
|
||||
typedef int (*mg_handler_t)(struct mg_connection *);
|
||||
|
||||
// Server management functions
|
||||
struct mg_server *mg_create_server(void *server_param);
|
||||
void mg_destroy_server(struct mg_server **);
|
||||
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
|
||||
void mg_set_request_handler(struct mg_server *, mg_handler_t);
|
||||
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
||||
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
|
||||
const char **mg_get_valid_option_names(void);
|
||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||
void mg_set_listening_socket(struct mg_server *, int sock);
|
||||
int mg_get_listening_socket(struct mg_server *);
|
||||
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
|
||||
|
||||
// Connection management functions
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
int mg_websocket_write(struct mg_connection *, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
// Deprecated in favor of mg_send_* interface
|
||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||
int mg_parse_multipart(const char *buf, int buf_len,
|
||||
char *var_name, int var_name_len,
|
||||
char *file_name, int file_name_len,
|
||||
const char **data, int *data_len);
|
||||
|
||||
// Utility functions
|
||||
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||
char *mg_md5(char buf[33], ...);
|
||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||
|
||||
// Callback function return codes
|
||||
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
|
||||
enum { MG_AUTH_FAIL, MG_AUTH_OK };
|
||||
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
|
||||
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
|
||||
|
||||
// HTTP client events
|
||||
enum {
|
||||
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
||||
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
||||
};
|
||||
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
|
||||
mg_handler_t handler, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // MONGOOSE_HEADER_INCLUDED
|
|
@ -121,6 +121,7 @@
|
|||
#include <Cockpit/cockpitDisplayManager.hxx>
|
||||
#include <Network/HTTPClient.hxx>
|
||||
#include <Network/fgcom.hxx>
|
||||
#include <Network/http/httpd.hxx>
|
||||
|
||||
#include <Viewer/CameraGroup.hxx>
|
||||
|
||||
|
@ -767,6 +768,12 @@ void fgCreateSubsystems(bool duringReset) {
|
|||
globals->add_subsystem("fgcom", new FGCom);
|
||||
#endif
|
||||
|
||||
{
|
||||
SGSubsystem * httpd = flightgear::http::FGHttpd::createInstance( fgGetNode(flightgear::http::PROPERTY_ROOT) );
|
||||
if( NULL != httpd )
|
||||
globals->add_subsystem("httpd", httpd );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the lighting subsystem.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
#include <Network/AV400Sim.hxx>
|
||||
#include <Network/AV400WSim.hxx>
|
||||
#include <Network/garmin.hxx>
|
||||
#include <Network/httpd.hxx>
|
||||
#include <Network/http/httpd.hxx>
|
||||
#include <Network/igc.hxx>
|
||||
#include <Network/jpg-httpd.hxx>
|
||||
#include <Network/joyclient.hxx>
|
||||
|
@ -149,10 +149,6 @@ FGIO::parse_port_config( const string& config )
|
|||
} else if ( protocol == "igc" ) {
|
||||
IGCProtocol *igc = new IGCProtocol;
|
||||
io = igc;
|
||||
} else if ( protocol == "httpd" ) {
|
||||
// determine port
|
||||
string port = tokens[1];
|
||||
return new FGHttpd( atoi(port.c_str()) );
|
||||
} else if ( protocol == "jpg-httpd" ) {
|
||||
// determine port
|
||||
int port = simgear::strutils::to_int(tokens[1]);
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include <Viewer/viewer.hxx>
|
||||
#include <Viewer/viewmgr.hxx>
|
||||
#include <Environment/presets.hxx>
|
||||
#include <Network/http/httpd.hxx>
|
||||
#include "AircraftDirVisitorBase.hxx"
|
||||
|
||||
#include <osg/Version>
|
||||
|
@ -949,6 +950,16 @@ fgOptStartDateGmt( const char *arg )
|
|||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptHttpd( const char * arg )
|
||||
{
|
||||
int port = atoi(arg);
|
||||
if( port > 0 ) {
|
||||
fgSetInt( string(flightgear::http::PROPERTY_ROOT).append("/listening-port").c_str(), port );
|
||||
}
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgSetupProxy( const char *arg )
|
||||
{
|
||||
|
@ -1554,7 +1565,7 @@ struct OptionDesc {
|
|||
{"hud-culled", false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "culled", 0 },
|
||||
{"atcsim", true, OPTION_CHANNEL, "", false, "dummy", 0 },
|
||||
{"atlas", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
||||
{"httpd", true, OPTION_CHANNEL, "", false, "", 0 },
|
||||
{"httpd", true, OPTION_FUNC , "", false, "", fgOptHttpd },
|
||||
{"jpg-httpd", true, OPTION_CHANNEL, "", false, "", 0 },
|
||||
{"native", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
||||
{"native-ctrls", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
||||
|
|
|
@ -10,7 +10,6 @@ set(SOURCES
|
|||
atlas.cxx
|
||||
garmin.cxx
|
||||
generic.cxx
|
||||
httpd.cxx
|
||||
HTTPClient.cxx
|
||||
igc.cxx
|
||||
joyclient.cxx
|
||||
|
@ -27,7 +26,7 @@ set(SOURCES
|
|||
pve.cxx
|
||||
ray.cxx
|
||||
rul.cxx
|
||||
jpg-httpd.cxx
|
||||
jpg-httpd.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
|
@ -40,7 +39,6 @@ set(HEADERS
|
|||
atlas.hxx
|
||||
garmin.hxx
|
||||
generic.hxx
|
||||
httpd.hxx
|
||||
HTTPClient.hxx
|
||||
igc.hxx
|
||||
joyclient.hxx
|
||||
|
@ -57,7 +55,7 @@ set(HEADERS
|
|||
pve.hxx
|
||||
ray.hxx
|
||||
rul.hxx
|
||||
jpg-httpd.hxx
|
||||
jpg-httpd.hxx
|
||||
)
|
||||
|
||||
if(ENABLE_IAX)
|
||||
|
@ -71,3 +69,5 @@ if(RTI_FOUND)
|
|||
add_subdirectory(HLA)
|
||||
endif()
|
||||
|
||||
add_subdirectory(http)
|
||||
|
||||
|
|
21
src/Network/http/CMakeLists.txt
Normal file
21
src/Network/http/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
httpd.cxx
|
||||
ScreenshotUriHandler.cxx
|
||||
PropertyUriHandler.cxx
|
||||
JsonUriHandler.cxx
|
||||
RunUriHandler.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
urihandler.hxx
|
||||
httpd.hxx
|
||||
ScreenshotUriHandler.hxx
|
||||
PropertyUriHandler.hxx
|
||||
JsonUriHandler.hxx
|
||||
RunUriHandler.hxx
|
||||
HTTPRequest.hxx
|
||||
)
|
||||
|
||||
flightgear_component(Http "${SOURCES}" "${HEADERS}")
|
65
src/Network/http/HTTPRequest.hxx
Normal file
65
src/Network/http/HTTPRequest.hxx
Normal file
|
@ -0,0 +1,65 @@
|
|||
// HTTPRequest.hxx -- Wraps a http Request
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_HTTPREQUEST_HXX
|
||||
#define FG_HTTPREQUEST_HXX
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class HTTPRequest
|
||||
{
|
||||
public:
|
||||
HTTPRequest() {}
|
||||
virtual ~HTTPRequest() {}
|
||||
|
||||
std::string Method;
|
||||
std::string Uri;
|
||||
std::string HttpVersion;
|
||||
std::string QueryString;
|
||||
|
||||
std::string remoteAddress;
|
||||
int remotePort;
|
||||
std::string localAddress;
|
||||
int localPort;
|
||||
|
||||
std::string Content;
|
||||
|
||||
class StringMap : public std::map<std::string,std::string> {
|
||||
public:
|
||||
std::string get( const std::string & key ) const {
|
||||
const_iterator it = find( key );
|
||||
return it == end() ? "" : it->second;
|
||||
}
|
||||
};
|
||||
|
||||
StringMap RequestVariables;
|
||||
|
||||
StringMap HeaderVariables;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace flightgear
|
||||
|
||||
#endif // FG_HTTPREQUEST_HXX
|
51
src/Network/http/HTTPResponse.hxx
Normal file
51
src/Network/http/HTTPResponse.hxx
Normal file
|
@ -0,0 +1,51 @@
|
|||
// HTTPResponse.hxx -- Wraps a http Response
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_HTTPRESPONSE_HXX
|
||||
#define FG_HTTPRESPONSE_HXX
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class HTTPResponse
|
||||
{
|
||||
public:
|
||||
HTTPResponse();
|
||||
virtual ~HTTPResponse() {}
|
||||
|
||||
int StatusCode;
|
||||
std::string Content;
|
||||
|
||||
typedef std::map<std::string,std::string> Header_t;
|
||||
Header_t Header;
|
||||
};
|
||||
|
||||
inline HTTPResponse::HTTPResponse() :
|
||||
StatusCode(200)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace flightgear
|
||||
|
||||
#endif // FG_HTTPRESPONSE_HXX
|
109
src/Network/http/JsonUriHandler.cxx
Normal file
109
src/Network/http/JsonUriHandler.cxx
Normal file
|
@ -0,0 +1,109 @@
|
|||
// JsonUriHandler.cxx -- json interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "JsonUriHandler.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <3rdparty/cjson/cJSON.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
static const char * getPropertyTypeString( simgear::props::Type type )
|
||||
{
|
||||
switch( type ) {
|
||||
case simgear::props::NONE: return "-";
|
||||
case simgear::props::ALIAS: return "alias";
|
||||
case simgear::props::BOOL: return "bool";
|
||||
case simgear::props::INT: return "int";
|
||||
case simgear::props::LONG: return "long";
|
||||
case simgear::props::FLOAT: return "float";
|
||||
case simgear::props::DOUBLE: return "double";
|
||||
case simgear::props::STRING: return "string";
|
||||
case simgear::props::UNSPECIFIED: return "unspecified";
|
||||
case simgear::props::EXTENDED: return "extended";
|
||||
case simgear::props::VEC3D: return "vec3d";
|
||||
case simgear::props::VEC4D: return "vec4d";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static cJSON * PropToJson( SGPropertyNode_ptr n )
|
||||
{
|
||||
cJSON * jsonProp = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(jsonProp, "path", cJSON_CreateString(n->getPath().c_str()));
|
||||
cJSON_AddItemToObject(jsonProp, "name", cJSON_CreateString(n->getDisplayName().c_str()));
|
||||
cJSON_AddItemToObject(jsonProp, "value", cJSON_CreateString(n->getStringValue()));
|
||||
cJSON_AddItemToObject(jsonProp, "type", cJSON_CreateString(getPropertyTypeString(n->getType())));
|
||||
return jsonProp;
|
||||
}
|
||||
|
||||
bool JsonUriHandler::handleGetRequest( const HTTPRequest & request, HTTPResponse & response )
|
||||
{
|
||||
response.Header["Content-Type"] = "application/json; charset=ISO-8859-1";
|
||||
|
||||
string propertyPath = request.Uri;
|
||||
|
||||
// strip the uri prefix of our handler
|
||||
propertyPath = propertyPath.substr( getUri().size() );
|
||||
|
||||
// strip the querystring
|
||||
size_t pos = propertyPath.find( '?' );
|
||||
if( pos != string::npos ) {
|
||||
propertyPath = propertyPath.substr( 0, pos-1 );
|
||||
}
|
||||
|
||||
// skip trailing '/' - not very efficient but shouldn't happen too often
|
||||
while( false == propertyPath.empty() && propertyPath[ propertyPath.length()-1 ] == '/' )
|
||||
propertyPath = propertyPath.substr(0,propertyPath.length()-1);
|
||||
|
||||
SGPropertyNode_ptr node = fgGetNode( string("/") + propertyPath );
|
||||
if( false == node.valid() ) {
|
||||
response.StatusCode = 400;
|
||||
response.Content = "Node not found: " + propertyPath;
|
||||
SG_LOG(SG_NETWORK,SG_ALERT, response.Content );
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
cJSON * json = PropToJson( node );
|
||||
if( node->nChildren() > 0 ) {
|
||||
cJSON * jsonArray = cJSON_CreateArray();
|
||||
for( int i = 0; i < node->nChildren(); i++ )
|
||||
cJSON_AddItemToArray( jsonArray, PropToJson( node->getChild(i) ) );
|
||||
cJSON_AddItemToObject( json, "children", jsonArray );
|
||||
}
|
||||
|
||||
char * jsonString = cJSON_Print( json );
|
||||
response.Content = jsonString;
|
||||
free( jsonString );
|
||||
cJSON_Delete( json );
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
38
src/Network/http/JsonUriHandler.hxx
Normal file
38
src/Network/http/JsonUriHandler.hxx
Normal file
|
@ -0,0 +1,38 @@
|
|||
// JsonUriHandler.hxx -- json interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_JSON_URI_HANDLER_HXX
|
||||
#define __FG_JSON_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class JsonUriHandler : public URIHandler {
|
||||
public:
|
||||
JsonUriHandler( const char * uri = "/json/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleGetRequest( const HTTPRequest & request, HTTPResponse & response );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
411
src/Network/http/PropertyUriHandler.cxx
Normal file
411
src/Network/http/PropertyUriHandler.cxx
Normal file
|
@ -0,0 +1,411 @@
|
|||
// PropertyUriHandler.cxx -- a web form interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "PropertyUriHandler.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class DOMElement {
|
||||
public:
|
||||
virtual ~DOMElement() {}
|
||||
virtual std::string render() const = 0;
|
||||
};
|
||||
|
||||
class DOMTextElement : public DOMElement {
|
||||
public:
|
||||
DOMTextElement( const string & text ) : _text(text) {}
|
||||
virtual std::string render() const { return _text; }
|
||||
|
||||
private:
|
||||
string _text;
|
||||
};
|
||||
|
||||
|
||||
class DOMNode : public DOMElement {
|
||||
public:
|
||||
DOMNode( const string & name ) : _name(name) {}
|
||||
virtual ~DOMNode();
|
||||
|
||||
virtual std::string render() const;
|
||||
virtual void addChild( DOMElement * child );
|
||||
virtual void setAttribute( const string & name, const string & value );
|
||||
protected:
|
||||
string _name;
|
||||
typedef vector<const DOMElement*> Children_t;
|
||||
typedef map<string,string> Attributes_t;
|
||||
Children_t _children;
|
||||
Attributes_t _attributes;
|
||||
};
|
||||
|
||||
DOMNode::~DOMNode()
|
||||
{
|
||||
for( Children_t::const_iterator it = _children.begin(); it != _children.end(); ++it )
|
||||
delete *it;
|
||||
}
|
||||
|
||||
string DOMNode::render() const
|
||||
{
|
||||
string reply;
|
||||
reply.append( "<" ).append( _name );
|
||||
for( Attributes_t::const_iterator it = _attributes.begin(); it != _attributes.end(); ++it ) {
|
||||
reply.append( " " );
|
||||
reply.append( it->first );
|
||||
reply.append( "=\"" );
|
||||
reply.append( it->second );
|
||||
reply.append( "\"" );
|
||||
}
|
||||
|
||||
if( _children.empty() ) {
|
||||
reply.append( " />\r" );
|
||||
return reply;
|
||||
} else {
|
||||
reply.append( ">" );
|
||||
}
|
||||
|
||||
for( Children_t::const_iterator it = _children.begin(); it != _children.end(); ++it ) {
|
||||
reply.append( (*it)->render() );
|
||||
}
|
||||
|
||||
reply.append( "</" ).append( _name ).append( ">\r" );
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void DOMNode::addChild( DOMElement * child )
|
||||
{
|
||||
_children.push_back( child );
|
||||
}
|
||||
|
||||
void DOMNode::setAttribute( const string & name, const string & value )
|
||||
{
|
||||
_attributes[name] = value;
|
||||
}
|
||||
|
||||
class SortedChilds : public simgear::PropertyList {
|
||||
public:
|
||||
SortedChilds( SGPropertyNode_ptr node ) {
|
||||
for (int i = 0; i < node->nChildren(); i++)
|
||||
push_back(node->getChild(i));
|
||||
std::sort(begin(), end(), CompareNodes());
|
||||
}
|
||||
private:
|
||||
class CompareNodes {
|
||||
public:
|
||||
bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
|
||||
int r = strcmp(a->getName(), b->getName());
|
||||
return r ? r < 0 : a->getIndex() < b->getIndex();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static const char * getPropertyTypeString( simgear::props::Type type )
|
||||
{
|
||||
switch( type ) {
|
||||
case simgear::props::NONE: return "";
|
||||
case simgear::props::ALIAS: return "alias";
|
||||
case simgear::props::BOOL: return "bool";
|
||||
case simgear::props::INT: return "int";
|
||||
case simgear::props::LONG: return "long";
|
||||
case simgear::props::FLOAT: return "float";
|
||||
case simgear::props::DOUBLE: return "double";
|
||||
case simgear::props::STRING: return "string";
|
||||
case simgear::props::UNSPECIFIED: return "unspecified";
|
||||
case simgear::props::EXTENDED: return "extended";
|
||||
case simgear::props::VEC3D: return "vec3d";
|
||||
case simgear::props::VEC4D: return "vec4d";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
DOMElement * createHeader( const string & prefix, const string & propertyPath )
|
||||
{
|
||||
using namespace simgear::strutils;
|
||||
|
||||
string path = prefix;
|
||||
|
||||
DOMNode * root = new DOMNode( "div" );
|
||||
root->setAttribute( "id", "breadcrumb" );
|
||||
|
||||
DOMNode * headline = new DOMNode( "h3" );
|
||||
root->addChild( headline );
|
||||
headline->addChild( new DOMTextElement("FlightGear Property Browser") );
|
||||
|
||||
DOMNode * breadcrumb = new DOMNode("ul");
|
||||
root->addChild( breadcrumb );
|
||||
|
||||
DOMNode * li = new DOMNode("li");
|
||||
breadcrumb->addChild( li );
|
||||
DOMNode * a = new DOMNode("a");
|
||||
li->addChild( a );
|
||||
a->setAttribute( "href", path );
|
||||
a->addChild( new DOMTextElement( "[root]" ) );
|
||||
|
||||
string_list items = split( propertyPath, "/" );
|
||||
for( string_list::iterator it = items.begin(); it != items.end(); ++it ) {
|
||||
if( (*it).empty() ) continue;
|
||||
path.append( *it ).append( "/" );
|
||||
|
||||
li = new DOMNode("li");
|
||||
breadcrumb->addChild( li );
|
||||
a = new DOMNode("a");
|
||||
li->addChild( a );
|
||||
a->setAttribute( "href", path );
|
||||
a->addChild( new DOMTextElement( (*it) ) );
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static DOMElement * renderPropertyValueElement( SGPropertyNode_ptr node )
|
||||
{
|
||||
string value = node->getStringValue();
|
||||
int len = value.length();
|
||||
|
||||
if( len < 15 ) len = 15;
|
||||
|
||||
DOMNode * root;
|
||||
|
||||
if( len < 60 ) {
|
||||
root = new DOMNode( "input" );
|
||||
root->setAttribute( "type", "text" );
|
||||
root->setAttribute( "name", node->getDisplayName() );
|
||||
root->setAttribute( "value", value );
|
||||
root->setAttribute( "size", boost::lexical_cast<std::string>( len ) );
|
||||
root->setAttribute( "maxlength", "2047" );
|
||||
} else {
|
||||
int rows = (len / 60)+1;
|
||||
int cols = 60;
|
||||
root = new DOMNode( "textarea" );
|
||||
root->setAttribute( "name", node->getDisplayName() );
|
||||
root->setAttribute( "cols", boost::lexical_cast<std::string>( cols ) );
|
||||
root->setAttribute( "rows", boost::lexical_cast<std::string>( rows ) );
|
||||
root->setAttribute( "maxlength", "2047" );
|
||||
root->addChild( new DOMTextElement( value ) );
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
bool PropertyUriHandler::handleGetRequest( const HTTPRequest & request, HTTPResponse & response )
|
||||
{
|
||||
|
||||
string propertyPath = request.Uri;
|
||||
|
||||
// strip the uri prefix of our handler
|
||||
propertyPath = propertyPath.substr( getUri().size() );
|
||||
|
||||
// strip the querystring
|
||||
size_t pos = propertyPath.find( '?' );
|
||||
if( pos != string::npos ) {
|
||||
propertyPath = propertyPath.substr( 0, pos-1 );
|
||||
}
|
||||
|
||||
// skip trailing '/' - not very efficient but shouldn't happen too often
|
||||
while( false == propertyPath.empty() && propertyPath[ propertyPath.length()-1 ] == '/' )
|
||||
propertyPath = propertyPath.substr(0,propertyPath.length()-1);
|
||||
|
||||
if( request.RequestVariables.get("submit") == "update" ) {
|
||||
// update leaf
|
||||
string value = request.RequestVariables.get("value");
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "httpd: setting " << propertyPath << " to '" << value << "'" );
|
||||
fgSetString( propertyPath.c_str(), value );
|
||||
}
|
||||
|
||||
if( request.RequestVariables.get("submit") == "set" ) {
|
||||
for( HTTPRequest::StringMap::const_iterator it = request.RequestVariables.begin(); it != request.RequestVariables.end(); ++it ) {
|
||||
if( it->first == "submit" ) continue;
|
||||
string pp = propertyPath + "/" + it->first;
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "httpd: setting " << pp << " to '" << it->second << "'" );
|
||||
fgSetString( pp, it->second );
|
||||
}
|
||||
}
|
||||
|
||||
// build the response
|
||||
DOMNode * html = new DOMNode( "html" );
|
||||
html->setAttribute( "lang", "en" );
|
||||
|
||||
DOMNode * head = new DOMNode( "head" );
|
||||
html->addChild( head );
|
||||
|
||||
DOMNode * e;
|
||||
e = new DOMNode( "title" );
|
||||
head->addChild( e );
|
||||
e->addChild( new DOMTextElement( string("FlightGear Property Browser - ") + propertyPath ) );
|
||||
|
||||
e = new DOMNode( "link" );
|
||||
head->addChild( e );
|
||||
e->setAttribute( "href", "/css/props.css" );
|
||||
e->setAttribute( "rel", "stylesheet" );
|
||||
e->setAttribute( "type", "text/css" );
|
||||
|
||||
DOMNode * body = new DOMNode( "body" );
|
||||
html->addChild( body );
|
||||
|
||||
SGPropertyNode_ptr node = fgGetNode( string("/") + propertyPath );
|
||||
if( false == node.valid() ) {
|
||||
DOMNode * headline = new DOMNode( "h3" );
|
||||
body->addChild( headline );
|
||||
headline->addChild( new DOMTextElement( "Non-existent node requested!" ) );
|
||||
e = new DOMNode( "b" );
|
||||
e->addChild( new DOMTextElement( propertyPath ) );
|
||||
// does not exist
|
||||
body->addChild( e );
|
||||
|
||||
} else if( node->nChildren() > 0 ) {
|
||||
// Render the list of children
|
||||
body->addChild( createHeader( getUri(), propertyPath ));
|
||||
|
||||
DOMNode * table = new DOMNode("table");
|
||||
body->addChild( table );
|
||||
|
||||
DOMNode * tr = new DOMNode( "tr" );
|
||||
table->addChild( tr );
|
||||
|
||||
DOMNode * th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( " " ) );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Property" ) );
|
||||
th->setAttribute( "id", "property" );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Value" ) );
|
||||
th->setAttribute( "id", "value" );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Type" ) );
|
||||
th->setAttribute( "id", "type" );
|
||||
|
||||
SortedChilds sortedChilds( node );
|
||||
for(SortedChilds::iterator it = sortedChilds.begin(); it != sortedChilds.end(); ++it ) {
|
||||
tr = new DOMNode( "tr" );
|
||||
table->addChild( tr );
|
||||
|
||||
SGPropertyNode_ptr child = *it;
|
||||
string name = child->getDisplayName(true);
|
||||
|
||||
DOMNode * td;
|
||||
|
||||
// Expand Link
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "expand" );
|
||||
if ( child->nChildren() > 0 ) {
|
||||
DOMNode * a = new DOMNode("a");
|
||||
td->addChild( a );
|
||||
a->setAttribute( "href", getUri() + propertyPath + "/" + name );
|
||||
a->addChild( new DOMTextElement( "(+)" ));
|
||||
}
|
||||
|
||||
// Property Name
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "property" );
|
||||
DOMNode * a = new DOMNode("a");
|
||||
td->addChild( a );
|
||||
a->setAttribute( "href", getUri() + propertyPath + "/" + name );
|
||||
a->addChild( new DOMTextElement( name ) );
|
||||
|
||||
// Value
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "value" );
|
||||
if ( child->nChildren() == 0 ) {
|
||||
DOMNode * form = new DOMNode("form");
|
||||
td->addChild( form );
|
||||
form->setAttribute( "method", "GET" );
|
||||
form->setAttribute( "action", getUri() + propertyPath );
|
||||
|
||||
e = new DOMNode( "input" );
|
||||
form->addChild( e );
|
||||
e->setAttribute( "type", "submit" );
|
||||
e->setAttribute( "value", "set" );
|
||||
e->setAttribute( "name", "submit" );
|
||||
|
||||
form->addChild( renderPropertyValueElement( node->getNode( name ) ) );
|
||||
|
||||
} else {
|
||||
td->addChild( new DOMTextElement( " " ) );
|
||||
}
|
||||
|
||||
// Property Type
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "type" );
|
||||
td->addChild(
|
||||
new DOMTextElement( getPropertyTypeString(node->getNode( name )->getType()) ) );
|
||||
|
||||
}
|
||||
} else {
|
||||
// Render a single property
|
||||
body->addChild( createHeader( getUri(), propertyPath ));
|
||||
e = new DOMNode( "div" );
|
||||
body->addChild( e );
|
||||
|
||||
e->setAttribute( "id", "currentvalue" );
|
||||
e->addChild( new DOMTextElement( "Current Value: " ) );
|
||||
e->addChild( new DOMTextElement( node->getStringValue() ) );
|
||||
|
||||
DOMNode * form = new DOMNode("form");
|
||||
body->addChild( form );
|
||||
form->setAttribute( "method", "GET" );
|
||||
form->setAttribute( "action", getUri() + propertyPath );
|
||||
|
||||
e = new DOMNode( "input" );
|
||||
form->addChild( e );
|
||||
e->setAttribute( "type", "submit" );
|
||||
e->setAttribute( "value", "update" );
|
||||
e->setAttribute( "name", "submit" );
|
||||
|
||||
form->addChild( renderPropertyValueElement( node ) );
|
||||
}
|
||||
|
||||
// Send the response
|
||||
response.Content = "<!DOCTYPE html>";
|
||||
response.Content.append( html->render() );
|
||||
delete html;
|
||||
response.Header["Content-Type"] = "text/html; charset=ISO-8859-1";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
38
src/Network/http/PropertyUriHandler.hxx
Normal file
38
src/Network/http/PropertyUriHandler.hxx
Normal file
|
@ -0,0 +1,38 @@
|
|||
// PropertyUriHandler.cxx -- a web form interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_PROPERTY_URI_HANDLER_HXX
|
||||
#define __FG_PROPERTY_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class PropertyUriHandler : public URIHandler {
|
||||
public:
|
||||
PropertyUriHandler( const char * uri = "/prop/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleGetRequest( const HTTPRequest & request, HTTPResponse & response );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_PROPERTY_URI_HANDLER_HXX
|
55
src/Network/http/RunUriHandler.cxx
Normal file
55
src/Network/http/RunUriHandler.cxx
Normal file
|
@ -0,0 +1,55 @@
|
|||
// RunUriHandler.cxx -- Run a flightgear command
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "RunUriHandler.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
bool RunUriHandler::handleRequest( const HTTPRequest & request, HTTPResponse & response )
|
||||
{
|
||||
response.Header["Content-Type"] = "text/plain";
|
||||
string command = request.RequestVariables.get("value");
|
||||
if( command.empty() ) {
|
||||
response.StatusCode = 400;
|
||||
response.Content = "command not specified";
|
||||
return true;
|
||||
}
|
||||
|
||||
SGPropertyNode args;
|
||||
if ( globals->get_commands()->execute(command.c_str(), &args) ) {
|
||||
response.Content = "ok.";
|
||||
return true;
|
||||
}
|
||||
|
||||
response.Content = "command '" + command + "' failed.";
|
||||
SG_LOG( SG_NETWORK, SG_ALERT, response.Content );
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
38
src/Network/http/RunUriHandler.hxx
Normal file
38
src/Network/http/RunUriHandler.hxx
Normal file
|
@ -0,0 +1,38 @@
|
|||
// RunUriHandler.hxx -- Provide screenshots via http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_RUN_URI_HANDLER_HXX
|
||||
#define __FG_RUN_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class RunUriHandler : public URIHandler {
|
||||
public:
|
||||
RunUriHandler( const char * uri = "/run.cgi" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_RUN_URI_HANDLER_HXX
|
39
src/Network/http/ScreenshotUriHandler.cxx
Normal file
39
src/Network/http/ScreenshotUriHandler.cxx
Normal file
|
@ -0,0 +1,39 @@
|
|||
// ScreenshotUriHandler.cxx -- Provide screenshots via http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "ScreenshotUriHandler.hxx"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
bool ScreenshotUriHandler::handleRequest( const HTTPRequest & request, HTTPResponse & response )
|
||||
{
|
||||
response.Header["Content-Type"] = "image/jpeg";
|
||||
// string response = "Screenshot - not yet implemented :-(";
|
||||
// mg_send_data( connection, response.c_str(), response.length() );
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
38
src/Network/http/ScreenshotUriHandler.hxx
Normal file
38
src/Network/http/ScreenshotUriHandler.hxx
Normal file
|
@ -0,0 +1,38 @@
|
|||
// ScreenshotUriHandler.hxx -- Provide screenshots via http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class ScreenshotUriHandler : public URIHandler {
|
||||
public:
|
||||
ScreenshotUriHandler( const char * uri = "/screenshot/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
244
src/Network/http/httpd.cxx
Normal file
244
src/Network/http/httpd.cxx
Normal file
|
@ -0,0 +1,244 @@
|
|||
// httpd.cxx -- a http daemon subsystem based on Mongoose http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "httpd.hxx"
|
||||
#include "HTTPRequest.hxx"
|
||||
#include "ScreenshotUriHandler.hxx"
|
||||
#include "PropertyUriHandler.hxx"
|
||||
#include "JsonUriHandler.hxx"
|
||||
#include "RunUriHandler.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Include/version.h>
|
||||
#include <3rdparty/mongoose/mongoose.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
const char * PROPERTY_ROOT = "/sim/http";
|
||||
|
||||
class MongooseHttpd : public FGHttpd {
|
||||
public:
|
||||
MongooseHttpd( SGPropertyNode_ptr );
|
||||
~MongooseHttpd();
|
||||
void init();
|
||||
void bind();
|
||||
void unbind();
|
||||
void shutdown();
|
||||
void update( double dt );
|
||||
|
||||
private:
|
||||
int requestHandler( struct mg_connection * );
|
||||
static int staticRequestHandler( struct mg_connection * );
|
||||
|
||||
struct mg_server *_server;
|
||||
SGPropertyNode_ptr _configNode;
|
||||
|
||||
typedef int (MongooseHttpd::*handler_t)( struct mg_connection * );
|
||||
typedef vector<SGSharedPtr<URIHandler> > URIHandlerMap;
|
||||
URIHandlerMap _uriHandlers;
|
||||
};
|
||||
|
||||
MongooseHttpd::MongooseHttpd( SGPropertyNode_ptr configNode ) :
|
||||
_server(NULL),
|
||||
_configNode(configNode)
|
||||
{
|
||||
}
|
||||
|
||||
MongooseHttpd::~MongooseHttpd()
|
||||
{
|
||||
mg_destroy_server(&_server);
|
||||
}
|
||||
|
||||
void MongooseHttpd::init()
|
||||
{
|
||||
SGPropertyNode_ptr n = _configNode->getNode("uri-handler");
|
||||
if( n.valid() ) {
|
||||
const char * uri;
|
||||
|
||||
if( (uri=n->getStringValue( "screenshot" ))[0] != 0 ) {
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"httpd: adding screenshot uri handler at " << uri );
|
||||
_uriHandlers.push_back( new flightgear::http::ScreenshotUriHandler( uri ) );
|
||||
}
|
||||
|
||||
if( (uri=n->getStringValue( "property" ))[0] != 0 ) {
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"httpd: adding screenshot property handler at " << uri );
|
||||
_uriHandlers.push_back( new flightgear::http::PropertyUriHandler( uri ) );
|
||||
}
|
||||
|
||||
if( (uri=n->getStringValue( "json" ))[0] != 0 ) {
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"httpd: adding json property handler at " << uri );
|
||||
_uriHandlers.push_back( new flightgear::http::JsonUriHandler( uri ) );
|
||||
}
|
||||
|
||||
if( (uri=n->getStringValue( "run" ))[0] != 0 ) {
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"httpd: adding run handler at " << uri );
|
||||
_uriHandlers.push_back( new flightgear::http::RunUriHandler( uri ) );
|
||||
}
|
||||
}
|
||||
|
||||
_server = mg_create_server(this);
|
||||
|
||||
n = _configNode->getNode("options");
|
||||
if( n.valid() ) {
|
||||
|
||||
string docRoot = n->getStringValue( "document-root", fgGetString("/sim/fg-root") );
|
||||
if( docRoot[0] != '/' ) docRoot.insert(0,"/").insert(0,fgGetString("/sim/fg-root"));
|
||||
|
||||
mg_set_option(_server, "document_root", docRoot.c_str() );
|
||||
|
||||
mg_set_option(_server, "listening_port", n->getStringValue( "listening-port", "8080" ) );
|
||||
// mg_set_option(_server, "url_rewrites", n->getStringValue( "url-rewrites", "" ) );
|
||||
mg_set_option(_server, "enable_directory_listing", n->getStringValue( "enable-directory-listing", "yes" ) );
|
||||
mg_set_option(_server, "idle_timeout_ms", n->getStringValue( "idle-timeout-ms", "30000" ) );
|
||||
mg_set_option(_server, "index_files", n->getStringValue( "index-files", "index.html" ) );
|
||||
}
|
||||
|
||||
mg_set_request_handler( _server, MongooseHttpd::staticRequestHandler );
|
||||
}
|
||||
|
||||
void MongooseHttpd::bind()
|
||||
{
|
||||
}
|
||||
|
||||
void MongooseHttpd::unbind()
|
||||
{
|
||||
mg_destroy_server(&_server);
|
||||
_uriHandlers.clear();
|
||||
}
|
||||
|
||||
void MongooseHttpd::shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void MongooseHttpd::update( double dt )
|
||||
{
|
||||
mg_poll_server( _server, 0 );
|
||||
}
|
||||
|
||||
|
||||
class MongooseHTTPRequest : public HTTPRequest {
|
||||
private:
|
||||
inline string NotNull( const char * cp ) { return NULL == cp ? "" : cp; }
|
||||
public:
|
||||
MongooseHTTPRequest( struct mg_connection * connection ) {
|
||||
Method = NotNull(connection->request_method);
|
||||
Uri = urlDecode( NotNull(connection->uri));
|
||||
HttpVersion = NotNull(connection->http_version);
|
||||
QueryString = urlDecode(NotNull(connection->query_string));
|
||||
|
||||
remoteAddress = NotNull(connection->remote_ip);
|
||||
remotePort = connection->remote_port;
|
||||
localAddress = NotNull(connection->local_ip);
|
||||
localPort = connection->local_port;
|
||||
|
||||
using namespace simgear::strutils;
|
||||
string_list pairs = split(string(QueryString), "&");
|
||||
for( string_list::iterator it = pairs.begin(); it != pairs.end(); ++it ) {
|
||||
string_list nvp = split( *it, "=" );
|
||||
if( nvp.size() != 2 ) continue;
|
||||
RequestVariables.insert( make_pair( nvp[0], nvp[1] ) );
|
||||
}
|
||||
|
||||
for( int i = 0; i < connection->num_headers; i++ )
|
||||
HeaderVariables[connection->http_headers[i].name] = connection->http_headers[i].value;
|
||||
|
||||
Content = NotNull(connection->content);
|
||||
|
||||
}
|
||||
|
||||
string urlDecode( const string & s ) {
|
||||
string r = "";
|
||||
int max = s.length();
|
||||
int a, b;
|
||||
for ( int i = 0; i < max; i++ ) {
|
||||
if ( s[i] == '+' ) {
|
||||
r += ' ';
|
||||
} else if ( s[i] == '%' && i + 2 < max
|
||||
&& isxdigit(s[i + 1])
|
||||
&& isxdigit(s[i + 2]) ) {
|
||||
i++;
|
||||
a = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
|
||||
i++;
|
||||
b = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
|
||||
r += (char)(a * 16 + b);
|
||||
} else {
|
||||
r += s[i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
int MongooseHttpd::requestHandler( struct mg_connection * connection )
|
||||
{
|
||||
MongooseHTTPRequest request(connection);
|
||||
HTTPResponse response;
|
||||
response.Header["Server"] = "FlightGear/" FLIGHTGEAR_VERSION " Mongoose/" MONGOOSE_VERSION;
|
||||
response.Header["Connection"] = "close";
|
||||
response.Header["Cache-Control"] = "no-cache";
|
||||
{
|
||||
char buf[64];
|
||||
time_t now = time(NULL);
|
||||
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||
response.Header["Date"] = buf;
|
||||
}
|
||||
|
||||
bool processed = false;
|
||||
for( URIHandlerMap::iterator it = _uriHandlers.begin(); it != _uriHandlers.end(); ++it ) {
|
||||
URIHandler * handler = *it;
|
||||
const string uri = connection->uri;
|
||||
if( uri.find( handler->getUri() ) == 0 ) {
|
||||
processed = handler->handleRequest( request, response );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( processed ) {
|
||||
for( HTTPResponse::Header_t::const_iterator it = response.Header.begin(); it != response.Header.end(); ++it ) {
|
||||
const string name = it->first;
|
||||
const string value = it->second;
|
||||
if( name.empty() || value.empty() ) continue;
|
||||
mg_send_header( connection, name.c_str(), value.c_str() );
|
||||
}
|
||||
mg_send_status( connection, response.StatusCode );
|
||||
mg_send_data( connection, response.Content.c_str(), response.Content.length() );
|
||||
}
|
||||
|
||||
return processed ? MG_REQUEST_PROCESSED : MG_REQUEST_NOT_PROCESSED;
|
||||
}
|
||||
|
||||
int MongooseHttpd::staticRequestHandler( struct mg_connection * connection )
|
||||
{
|
||||
return static_cast<MongooseHttpd*>(connection->server_param)->requestHandler( connection );
|
||||
}
|
||||
|
||||
FGHttpd * FGHttpd::createInstance( SGPropertyNode_ptr configNode )
|
||||
{
|
||||
return configNode.valid() ? new MongooseHttpd( configNode ) : NULL;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
|
@ -1,9 +1,8 @@
|
|||
// httpd.hxx -- FGFS http property manager interface / external script
|
||||
// and control class
|
||||
// httpd.hxx -- a http daemon subsystem based on Mongoose http
|
||||
//
|
||||
// Written by Curtis Olson, started June 2001.
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
|
@ -18,33 +17,28 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef FG_HTTPD_HXX
|
||||
#define FG_HTTPD_HXX
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
#ifndef _FG_HTTPD_HXX
|
||||
#define _FG_HTTPD_HXX
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
extern const char * PROPERTY_ROOT;
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
class HttpdServer;
|
||||
|
||||
class FGHttpd : public FGProtocol
|
||||
class FGHttpd : public SGSubsystem
|
||||
{
|
||||
int port;
|
||||
HttpdServer *server;
|
||||
|
||||
public:
|
||||
FGHttpd( int p );
|
||||
~FGHttpd();
|
||||
|
||||
bool open();
|
||||
bool process();
|
||||
bool close();
|
||||
static FGHttpd * createInstance( SGPropertyNode_ptr configNode );
|
||||
};
|
||||
|
||||
#endif // _FG_HTTPD_HXX
|
||||
}
|
||||
} // namespace flightgear
|
||||
|
||||
#endif // FG_HTTPD_HXX
|
||||
|
||||
|
61
src/Network/http/urihandler.hxx
Normal file
61
src/Network/http/urihandler.hxx
Normal file
|
@ -0,0 +1,61 @@
|
|||
// urihandler.hxx -- a base class for URI handler
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_URI_HANDLER_HXX
|
||||
#define __FG_URI_HANDLER_HXX
|
||||
|
||||
#include "HTTPRequest.hxx"
|
||||
#include "HTTPResponse.hxx"
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class URIHandler : public SGReferenced {
|
||||
public:
|
||||
URIHandler( const char * uri ) : _uri(uri) {}
|
||||
virtual ~URIHandler() {}
|
||||
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response ) {
|
||||
if( request.Method == "GET" ) return handleGetRequest( request, response );
|
||||
if( request.Method == "PUT" ) return handlePutRequest( request, response );
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool handleGetRequest( const HTTPRequest & request, HTTPResponse & response ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool handlePutRequest( const HTTPRequest & request, HTTPResponse & response ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string & getUri() const { return _uri; }
|
||||
|
||||
private:
|
||||
std::string _uri;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_URI_HANDLER_HXX
|
|
@ -1,445 +0,0 @@
|
|||
// httpd.cxx -- FGFS http property manager interface / external script
|
||||
// and control class
|
||||
//
|
||||
// Written by Curtis Olson, started June 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program 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 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <algorithm> // sort()
|
||||
#include <cstdlib> // atoi() atof()
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/sg_netChat.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "httpd.hxx"
|
||||
|
||||
using std::string;
|
||||
|
||||
/* simple httpd server that makes an hasty stab at following the http
|
||||
1.1 rfc. */
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// class HttpdChannel
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpdChannel : public simgear::NetChat
|
||||
{
|
||||
simgear::NetBuffer buffer;
|
||||
|
||||
string urlEncode(string);
|
||||
string urlDecode(string);
|
||||
|
||||
public:
|
||||
|
||||
HttpdChannel() : buffer(512) { setTerminator("\r\n"); }
|
||||
|
||||
virtual void collectIncomingData (const char* s, int n) {
|
||||
buffer.append(s,n);
|
||||
}
|
||||
|
||||
// Handle the actual http request
|
||||
virtual void foundTerminator(void);
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// class HttpdServer
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpdServer : private simgear::NetChannel
|
||||
{
|
||||
virtual bool writable (void) { return false; }
|
||||
|
||||
virtual void handleAccept (void) {
|
||||
simgear::IPAddress addr;
|
||||
int handle = accept ( &addr );
|
||||
SG_LOG( SG_IO, SG_INFO, "Client " << addr.getHost() << ":" << addr.getPort() << " connected" );
|
||||
|
||||
HttpdChannel *hc = new HttpdChannel;
|
||||
hc->setHandle ( handle );
|
||||
poller.addChannel( hc );
|
||||
}
|
||||
|
||||
simgear::NetChannelPoller poller;
|
||||
public:
|
||||
|
||||
HttpdServer ( int port );
|
||||
|
||||
void poll()
|
||||
{
|
||||
poller.poll();
|
||||
}
|
||||
};
|
||||
|
||||
HttpdServer::HttpdServer(int port)
|
||||
{
|
||||
if (!open())
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT, "Failed to open HTTP port.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != bind( "", port ))
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT, "Failed to bind HTTP port.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != listen( 5 ))
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT, "Failed to listen on HTTP port.");
|
||||
return;
|
||||
}
|
||||
|
||||
poller.addChannel(this);
|
||||
|
||||
SG_LOG(SG_IO, SG_ALERT, "Httpd server started on port " << port);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// class FGHttpd
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
FGHttpd::FGHttpd(int p) :
|
||||
port(p),
|
||||
server(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
FGHttpd::~FGHttpd()
|
||||
{
|
||||
delete server;
|
||||
}
|
||||
|
||||
bool FGHttpd::open() {
|
||||
if ( is_enabled() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring" );
|
||||
return false;
|
||||
}
|
||||
|
||||
server = new HttpdServer( port );
|
||||
|
||||
set_hz( 15 ); // default to processing requests @ 15Hz
|
||||
set_enabled( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FGHttpd::process() {
|
||||
server->poll();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FGHttpd::close() {
|
||||
if (!server)
|
||||
return true;
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, "closing FGHttpd" );
|
||||
|
||||
delete server;
|
||||
server = NULL;
|
||||
set_enabled( false );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class CompareNodes {
|
||||
public:
|
||||
bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
|
||||
int r = strcmp(a->getName(), b->getName());
|
||||
return r ? r < 0 : a->getIndex() < b->getIndex();
|
||||
}
|
||||
};
|
||||
|
||||
static string makeLinksForPath( const string & request )
|
||||
{
|
||||
using namespace simgear::strutils;
|
||||
string reply = "<A HREF=\"/\">/</A>";
|
||||
string path = "/";
|
||||
|
||||
string_list sl = split( request, "/" );
|
||||
for( string_list::iterator it = sl.begin(); it != sl.end(); ++it ) {
|
||||
if( (*it).empty() ) continue;
|
||||
path.append( *it ).append( "/" );
|
||||
reply.append("<A HREF=\"")
|
||||
.append(path)
|
||||
.append("\">")
|
||||
.append(*it)
|
||||
.append("</A>/");
|
||||
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
// Handle http GET requests
|
||||
void HttpdChannel::foundTerminator (void) {
|
||||
|
||||
closeWhenDone ();
|
||||
|
||||
const string s = buffer.getData();
|
||||
|
||||
if ( s.find( "GET " ) == 0 ) {
|
||||
SG_LOG( SG_IO, SG_INFO, "echo: " << s );
|
||||
|
||||
string rest = s.substr(4);
|
||||
string request;
|
||||
string tmp;
|
||||
|
||||
string::size_type pos = rest.find( " " );
|
||||
if ( pos != string::npos ) {
|
||||
request = rest.substr( 0, pos );
|
||||
} else {
|
||||
request = "/";
|
||||
}
|
||||
|
||||
SGPropertyNode *node = NULL;
|
||||
pos = request.find( "?" );
|
||||
if ( pos != string::npos ) {
|
||||
// request to update property value
|
||||
string args = request.substr( pos + 1 );
|
||||
request = request.substr( 0, pos );
|
||||
SG_LOG( SG_IO, SG_INFO, "'" << request << "' '" << args << "'" );
|
||||
request = urlDecode(request);
|
||||
|
||||
// parse args looking for "value="
|
||||
bool done = false;
|
||||
while ( ! done ) {
|
||||
string arg;
|
||||
pos = args.find("&");
|
||||
if ( pos != string::npos ) {
|
||||
arg = args.substr( 0, pos );
|
||||
args = args.substr( pos + 1 );
|
||||
} else {
|
||||
arg = args;
|
||||
done = true;
|
||||
}
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, " arg = " << arg );
|
||||
string::size_type apos = arg.find("=");
|
||||
if ( apos != string::npos ) {
|
||||
string a = arg.substr( 0, apos );
|
||||
string b = arg.substr( apos + 1 );
|
||||
SG_LOG( SG_IO, SG_INFO, " a = " << a << " b = " << b );
|
||||
if ( request == "/run.cgi" ) {
|
||||
// execute a command
|
||||
if ( a == "value" ) {
|
||||
SGPropertyNode args;
|
||||
if ( !globals->get_commands()
|
||||
->execute(urlDecode(b).c_str(), &args) )
|
||||
{
|
||||
SG_LOG( SG_NETWORK, SG_ALERT,
|
||||
"Command " << urlDecode(b)
|
||||
<< " failed.");
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if ( a == "value" ) {
|
||||
// update a property value
|
||||
fgSetString( request.c_str(),
|
||||
urlDecode(b).c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request = urlDecode(request);
|
||||
}
|
||||
|
||||
node = globals->get_props()->getNode(request.c_str());
|
||||
|
||||
string response = "";
|
||||
response += "<HTML LANG=\"en\">";
|
||||
response += getTerminator();
|
||||
|
||||
response += "<HEAD>";
|
||||
response += getTerminator();
|
||||
|
||||
response += "<TITLE>FlightGear - ";
|
||||
response += request;
|
||||
response += "</TITLE>";
|
||||
response += getTerminator();
|
||||
|
||||
response += "</HEAD>";
|
||||
response += getTerminator();
|
||||
|
||||
response += "<BODY>";
|
||||
response += getTerminator();
|
||||
|
||||
if (node == NULL) {
|
||||
response += "<H3>Non-existent node requested!</H3>";
|
||||
response += getTerminator();
|
||||
|
||||
response += "<B>";
|
||||
response += request.c_str();
|
||||
response += "</B> does not exist.";
|
||||
response += getTerminator();
|
||||
} else if ( node->nChildren() > 0 ) {
|
||||
// request is a path with children
|
||||
response += "<H3>Contents of \"";
|
||||
response += makeLinksForPath( request );
|
||||
response += "\"</H3>";
|
||||
response += getTerminator();
|
||||
|
||||
|
||||
vector<SGPropertyNode *> children;
|
||||
for (int i = 0; i < node->nChildren(); i++)
|
||||
children.push_back(node->getChild(i));
|
||||
std::sort(children.begin(), children.end(), CompareNodes());
|
||||
|
||||
vector<SGPropertyNode *>::iterator it, end = children.end();
|
||||
for (it = children.begin(); it != end; ++it) {
|
||||
SGPropertyNode *child = *it;
|
||||
string name = child->getDisplayName(true);
|
||||
string line = "";
|
||||
if ( child->nChildren() > 0 ) {
|
||||
line += "<B><A HREF=\"";
|
||||
line += request;
|
||||
if ( request.substr(request.length() - 1, 1) != "/" ) {
|
||||
line += "/";
|
||||
}
|
||||
line += urlEncode(name);
|
||||
line += "\">";
|
||||
line += name;
|
||||
line += "</A></B>";
|
||||
line += "/<BR>";
|
||||
} else {
|
||||
string value = node->getStringValue ( name.c_str(), "" );
|
||||
line += "<B>";
|
||||
line += name;
|
||||
line += "</B> <A HREF=\"";
|
||||
line += request;
|
||||
if ( request.substr(request.length() - 1, 1) != "/" ) {
|
||||
line += "/";
|
||||
}
|
||||
line += urlEncode(name);
|
||||
line += "\">(";
|
||||
line += value;
|
||||
line += ")</A><BR>";
|
||||
}
|
||||
response += line;
|
||||
response += getTerminator();
|
||||
}
|
||||
} else {
|
||||
// request for an individual data member
|
||||
string value = node->getStringValue();
|
||||
|
||||
response += "<form method=\"GET\" action=\"";
|
||||
response += urlEncode(request);
|
||||
response += "\">";
|
||||
response += "<B>";
|
||||
response += makeLinksForPath(request);
|
||||
response += "</B> = ";
|
||||
response += "<input type=text name=value size=\"15\" value=\"";
|
||||
response += value;
|
||||
response += "\" maxlength=2047>";
|
||||
response += "<input type=submit value=\"update\" name=\"submit\">";
|
||||
response += "</FORM>";
|
||||
}
|
||||
response += "</BODY>";
|
||||
response += getTerminator();
|
||||
|
||||
response += "</HTML>";
|
||||
response += getTerminator();
|
||||
|
||||
push( "HTTP/1.1 200 OK" );
|
||||
push( getTerminator() );
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, "size = " << response.length() );
|
||||
char ctmp[256];
|
||||
sprintf(ctmp, "Content-Length: %u", (unsigned)response.length());
|
||||
push( ctmp );
|
||||
push( getTerminator() );
|
||||
|
||||
push( "Connection: close" );
|
||||
push( getTerminator() );
|
||||
|
||||
push( "Content-Type: text/html" );
|
||||
push( getTerminator() );
|
||||
push( getTerminator() );
|
||||
|
||||
push( response.c_str() );
|
||||
}
|
||||
|
||||
buffer.remove();
|
||||
}
|
||||
|
||||
|
||||
// encode everything but "a-zA-Z0-9_.-/" (see RFC2396)
|
||||
string HttpdChannel::urlEncode(string s) {
|
||||
string r = "";
|
||||
|
||||
for ( int i = 0; i < (int)s.length(); i++ ) {
|
||||
if ( isalnum(s[i]) || s[i] == '_' || s[i] == '.'
|
||||
|| s[i] == '-' || s[i] == '/' ) {
|
||||
r += s[i];
|
||||
} else {
|
||||
char buf[16];
|
||||
sprintf(buf, "%%%02X", (unsigned char)s[i]);
|
||||
r += buf;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
string HttpdChannel::urlDecode(string s) {
|
||||
string r = "";
|
||||
int max = s.length();
|
||||
int a, b;
|
||||
|
||||
for ( int i = 0; i < max; i++ ) {
|
||||
if ( s[i] == '+' ) {
|
||||
r += ' ';
|
||||
} else if ( s[i] == '%' && i + 2 < max
|
||||
&& isxdigit(s[i + 1])
|
||||
&& isxdigit(s[i + 2]) ) {
|
||||
i++;
|
||||
a = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
|
||||
i++;
|
||||
b = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
|
||||
r += (char)(a * 16 + b);
|
||||
} else {
|
||||
r += s[i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
Loading…
Add table
Reference in a new issue