mirror of
git://git.psyc.eu/libpsyc
synced 2024-08-15 03:19:02 +00:00
481 lines
14 KiB
Text
481 lines
14 KiB
Text
#include "global.h"
|
|
#include "interpret.h"
|
|
#include "stralloc.h"
|
|
#include "mapping.h"
|
|
#include "array.h"
|
|
#include "svalue.h"
|
|
#include "operators.h"
|
|
#include "array.h"
|
|
#include "builtin_functions.h"
|
|
#include "module.h"
|
|
|
|
#include <stdlib.h>
|
|
#ifndef DEFAULT_CMOD_STORAGE
|
|
#define DEFAULT_CMOD_STORAGE
|
|
#endif
|
|
|
|
// libpsyc dependencies
|
|
#include <psyc.h>
|
|
#include <psyc/parse.h>
|
|
#include <psyc/render.h>
|
|
#include <psyc/text.h>
|
|
|
|
#define MODULE_NAME "PSYC"
|
|
#define MODULE_MAJOR 0
|
|
#define MODULE_MINOR 2
|
|
#define MODULE_PATCH 0
|
|
|
|
/*! @module PSYC
|
|
*!
|
|
*! Implements PSYC parsing and rendering based on libpsyc
|
|
*!
|
|
*/
|
|
|
|
// psyctext helper
|
|
PsycTextValueRC lookup_value_mapping(const char *name, size_t len, PsycString *value, void *extra)
|
|
{
|
|
//printf("lookup_value_mapping called for %.*s\n", (int) len, name);
|
|
struct pike_string *key = make_shared_binary_string(name, len);
|
|
struct mapping *m = (struct mapping *) extra;
|
|
struct svalue *s = low_mapping_string_lookup(m, key);
|
|
free_string(key);
|
|
|
|
if (s == NULL) {
|
|
return PSYC_TEXT_VALUE_NOT_FOUND;
|
|
}
|
|
|
|
switch(s->type) {
|
|
case PIKE_T_STRING:
|
|
//printf("lookup returned %.*s\n", (int) s->u.string->len, STR0(s->u.string));
|
|
value->data = (char *) STR0(s->u.string);
|
|
value->length = s->u.string->len;
|
|
break;
|
|
default:
|
|
printf("lookup did return !string\n");
|
|
// FIXME: we need the automagic value conversion
|
|
value->data = "";
|
|
value->length = 0;
|
|
}
|
|
return PSYC_TEXT_VALUE_FOUND;
|
|
}
|
|
|
|
/*! @decl string psyc_text(string template, mapping vars)
|
|
*! @param template
|
|
*! template to be filled
|
|
*! @param vars
|
|
*! mapping(string:string) with substituions
|
|
*! @returns
|
|
*! rendered string
|
|
*/
|
|
PIKEFUN string psyc_text(string template, mapping vars) {
|
|
PsycTextState state;
|
|
PsycTextRC ret;
|
|
size_t len = 0;
|
|
// FIXME:
|
|
char buffer[512];
|
|
|
|
psyc_text_state_init(&state, (char *) STR0(template), template->len, buffer, 512);
|
|
do
|
|
{
|
|
ret = psyc_text(&state, lookup_value_mapping, vars);
|
|
len += psyc_text_bytes_written(&state);
|
|
switch (ret) {
|
|
case PSYC_TEXT_INCOMPLETE: // need to realloc buffer
|
|
//psyc_text_buffer_set(&state, buffer + len, BUFSIZE - len);
|
|
break;
|
|
case PSYC_TEXT_COMPLETE: // we're done
|
|
RETURN make_shared_binary_string(buffer, len);
|
|
case PSYC_TEXT_NO_SUBST: // no substituions, return original string
|
|
RETURN template;
|
|
}
|
|
} while (ret == PSYC_TEXT_INCOMPLETE);
|
|
}
|
|
|
|
/*! @decl string is_routingvar(string name)
|
|
*! @param name
|
|
*! name of the variable
|
|
*! @returns
|
|
*! 1 if it is a routing variable
|
|
*! 0 otherwise
|
|
*/
|
|
PIKEFUN int is_routingvar(string name) {
|
|
RETURN psyc_var_is_routing((char *) STR0(name), name->len);
|
|
}
|
|
|
|
/*! @decl string render(mapping rvars, mapping evars, string method, string|void body)
|
|
*! @param rvars
|
|
*! routing vars (mapping string:string)
|
|
*! @param evars
|
|
*! entity vars (mapping string:mixed
|
|
*! @param method
|
|
*! method name
|
|
*! @param data
|
|
*! body
|
|
*! @returns
|
|
*! serialized packet as a string
|
|
*/
|
|
PIKEFUN string render(mapping rvars, mapping evars, string method, string|void body) {
|
|
PsycPacket packet;
|
|
PsycHeader rheaders, eheaders;
|
|
|
|
struct keypair *k; // for mappings
|
|
INT32 e;
|
|
|
|
char oper = PSYC_OPERATOR_SET;
|
|
|
|
// fill headers
|
|
rheaders.lines = 0;
|
|
rheaders.modifiers = malloc(sizeof(PsycModifier) * rvars->data->size);
|
|
NEW_MAPPING_LOOP(rvars->data) {
|
|
if (k->ind.type == PIKE_T_STRING) {
|
|
switch(k->val.type) {
|
|
case PIKE_T_STRING:
|
|
psyc_modifier_init(&rheaders.modifiers[rheaders.lines++], oper,
|
|
(char *)STR0(k->ind.u.string), k->ind.u.string->len,
|
|
(char *)STR0(k->val.u.string), k->val.u.string->len,
|
|
PSYC_MODIFIER_ROUTING);
|
|
break;
|
|
default:
|
|
Pike_error("psyc render: unsupported non-string value in rvars\n");
|
|
break;
|
|
}
|
|
} else {
|
|
Pike_error("psyc render: unsupported non-string key in rvars\n");
|
|
}
|
|
}
|
|
|
|
eheaders.lines = 0;
|
|
eheaders.modifiers = malloc(sizeof(PsycModifier) * evars->data->size);
|
|
NEW_MAPPING_LOOP(evars->data) {
|
|
if (k->ind.type == PIKE_T_STRING) {
|
|
char *key;
|
|
size_t keylen;
|
|
char *val = NULL;
|
|
size_t vallen = 0;
|
|
struct pike_string *s;
|
|
|
|
key = (char *) STR0(k->ind.u.string);
|
|
keylen = k->ind.u.string->len;
|
|
|
|
switch(k->val.type) {
|
|
case PIKE_T_INT:
|
|
do {
|
|
struct string_builder b;
|
|
init_string_builder(&b, 0);
|
|
string_builder_append_integer(&b, k->val.u.integer,
|
|
10, APPEND_SIGNED, 0, 0);
|
|
s = finish_string_builder(&b);
|
|
val = (char *) STR0(s);
|
|
vallen = s->len;
|
|
free_string(s);
|
|
} while (0);
|
|
break;
|
|
case PIKE_T_STRING:
|
|
val = (char *) STR0(k->val.u.string);
|
|
vallen = k->val.u.string->len;
|
|
break;
|
|
/*
|
|
case PIKE_T_FLOAT:
|
|
printf("float value %f\n", k->val.u.float_number);
|
|
break;
|
|
*/
|
|
case PIKE_T_ARRAY:
|
|
do {
|
|
PsycString *elems = xcalloc(k->val.u.array->size, sizeof(PsycString));
|
|
PsycList list;
|
|
// FIXME: check for out of memory
|
|
for(e = 0; e < k->val.u.array->size; e++) {
|
|
struct svalue item = k->val.u.array->item[e];
|
|
switch(item.type) {
|
|
case PIKE_T_STRING:
|
|
elems[e] = (PsycString) { item.u.string->len, (char *) STR0(item.u.string) };
|
|
break;
|
|
default:
|
|
// FIXME: xfree(elems) ?
|
|
Pike_error("psyc_render: unsupported data type in list\n");
|
|
}
|
|
}
|
|
psyc_list_init(&list, elems, k->val.u.array->size, PSYC_LIST_CHECK_LENGTH);
|
|
|
|
struct pike_string *listbuf = begin_shared_string(list.length);
|
|
psyc_render_list(&list, (char *) STR0(listbuf), listbuf->len);
|
|
end_shared_string(listbuf);
|
|
val = (char *) STR0(listbuf);
|
|
vallen = listbuf->len;
|
|
xfree(elems);
|
|
} while (0);
|
|
break;
|
|
default:
|
|
Pike_error("psyc_render: unsupported value in evars\n");
|
|
break;
|
|
}
|
|
psyc_modifier_init(&eheaders.modifiers[eheaders.lines++], oper,
|
|
key, keylen,
|
|
val, vallen,
|
|
PSYC_MODIFIER_CHECK_LENGTH);
|
|
} else {
|
|
Pike_error("psyc render: unsupported non-string key in evars\n");
|
|
}
|
|
}
|
|
|
|
if (body != NULL) {
|
|
psyc_packet_init(&packet, rheaders.modifiers, rheaders.lines,
|
|
eheaders.modifiers, eheaders.lines,
|
|
(const char *)STR0(method), method->len,
|
|
(const char *)STR0(body), body->len,
|
|
PSYC_PACKET_CHECK_LENGTH);
|
|
} else { // body arg was not given
|
|
psyc_packet_init(&packet, rheaders.modifiers, rheaders.lines,
|
|
eheaders.modifiers, eheaders.lines,
|
|
(const char *)STR0(method), method->len,
|
|
NULL, 0, PSYC_PACKET_CHECK_LENGTH);
|
|
}
|
|
|
|
struct pike_string *s = begin_shared_string(packet.length);
|
|
psyc_render(&packet, (char *) STR0(s), packet.length);
|
|
// pop_n_elems(args);
|
|
RETURN end_shared_string(s);
|
|
}
|
|
|
|
PIKECLASS Parser {
|
|
CVAR PsycParseState parser;
|
|
CVAR struct pike_string *buffer;
|
|
CVAR int handle_packet;
|
|
CVAR int handle_error;
|
|
|
|
// packet state
|
|
CVAR struct mapping *rvars;
|
|
CVAR struct mapping *evars;
|
|
CVAR struct pike_string *method;
|
|
CVAR struct pike_string *body;
|
|
|
|
// for incomplete length prefixed entity var data / body data
|
|
CVAR struct string_builder incomplete;
|
|
|
|
INIT {
|
|
psyc_parse_state_init(&THIS->parser, PSYC_PARSE_ALL);
|
|
THIS->buffer = NULL;
|
|
THIS->handle_packet = find_identifier("handle_packet", Pike_fp->current_object->prog);
|
|
THIS->handle_error = find_identifier("handle_error", Pike_fp->current_object->prog);
|
|
THIS->rvars = allocate_mapping(0);
|
|
THIS->evars = allocate_mapping(0);
|
|
THIS->method = NULL;
|
|
THIS->body = NULL;
|
|
//THIS->body_buffer = NULL;
|
|
}
|
|
|
|
EXIT {
|
|
if (THIS->buffer != NULL) {
|
|
free_string(THIS->buffer);
|
|
}
|
|
if (THIS->rvars != NULL) {
|
|
do_free_mapping(THIS->rvars);
|
|
}
|
|
// FIXME: free packet state
|
|
}
|
|
|
|
PIKEFUN void feed(string data) {
|
|
char oper;
|
|
PsycString name, value;
|
|
int ret;
|
|
int err;
|
|
|
|
if (THIS->buffer != NULL) {
|
|
/* we have remaining buffer from previous input */
|
|
//printf("%d bytes remaining from previous read\n", THIS->buffer->len);
|
|
struct pike_string *tmp;
|
|
tmp = add_shared_strings(THIS->buffer, data);
|
|
free_string(THIS->buffer);
|
|
data = tmp;
|
|
THIS->buffer = NULL;
|
|
}
|
|
psyc_parse_buffer_set(&THIS->parser,
|
|
(char *) STR0(data), data->len);
|
|
do {
|
|
ret = psyc_parse(&THIS->parser, &oper, &name, &value);
|
|
switch(ret) {
|
|
case PSYC_PARSE_ROUTING:
|
|
// printf("R %.*s -> %.*s\n", (int)name.length, name.data, (int)value.length, value.data);
|
|
mapping_string_insert_string(THIS->rvars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
make_shared_binary_string(value.data, value.length));
|
|
break;
|
|
case PSYC_PARSE_ENTITY_START: // entity var with length
|
|
init_string_builder_alloc(&THIS->incomplete, psyc_parse_value_length(&THIS->parser), 0);
|
|
// fall thru
|
|
case PSYC_PARSE_ENTITY_CONT:
|
|
string_builder_append(&THIS->incomplete, MKPCHARP(value.data, 0), value.length);
|
|
break;
|
|
case PSYC_PARSE_ENTITY_END:
|
|
string_builder_append(&THIS->incomplete, MKPCHARP(value.data, 0), value.length);
|
|
do {
|
|
struct pike_string *tmp = finish_string_builder(&THIS->incomplete);
|
|
value.length = tmp->len;
|
|
value.data = (char *) STR0(tmp);
|
|
// FIXME: not sure if this is really safe
|
|
free_string(tmp);
|
|
} while (0);
|
|
// fall thru
|
|
case PSYC_PARSE_ENTITY:
|
|
//printf("E %.*s -> %.*s\n", (int)name.length, name.data, (int)value.length, value.data);
|
|
do {
|
|
err = 0;
|
|
int type = psyc_var_type(PSYC_S2ARG(&name));
|
|
struct svalue sv;
|
|
time_t timmy;
|
|
switch(type) {
|
|
case PSYC_TYPE_DATE:
|
|
if (psyc_parse_date(&value, &timmy)) {
|
|
sv.type = PIKE_T_INT; sv.u.integer = timmy;
|
|
mapping_string_insert(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
&sv);
|
|
} else {
|
|
err = 1;
|
|
}
|
|
break;
|
|
case PSYC_TYPE_TIME:
|
|
if (psyc_parse_time(&value, &timmy)) {
|
|
sv.type = PIKE_T_INT; sv.u.integer = timmy;
|
|
mapping_string_insert(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
&sv);
|
|
} else {
|
|
err = 2;
|
|
}
|
|
break;
|
|
case PSYC_TYPE_AMOUNT:
|
|
break;
|
|
case PSYC_TYPE_DEGREE:
|
|
if (value.length && value.data[0] >= '0' && value.data[0] <= '9') {
|
|
sv.type = PIKE_T_FLOAT; sv.u.float_number = (float) (value.data[0] - '0') / 10.0;
|
|
mapping_string_insert(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
&sv);
|
|
} else {
|
|
err = 3;
|
|
}
|
|
break;
|
|
case PSYC_TYPE_FLAG:
|
|
if (value.length && value.data[0] >= '0' && value.data[0] <= '1') {
|
|
sv.type = PIKE_T_INT; sv.u.integer = value.data[0] - '0';
|
|
mapping_string_insert(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
&sv);
|
|
} else {
|
|
err = 4;
|
|
}
|
|
break;
|
|
case PSYC_TYPE_LIST:
|
|
do {
|
|
struct array *elems = low_allocate_array(0, 32);
|
|
if (value.length > 0) {
|
|
int retl;
|
|
int count = 0;
|
|
PsycParseListState listState;
|
|
PsycString elem = (PsycString) {0, 0};
|
|
|
|
psyc_parse_list_state_init(&listState);
|
|
psyc_parse_list_buffer_set(&listState, PSYC_S2ARG(value));
|
|
do {
|
|
retl = psyc_parse_list(&listState, &elem);
|
|
switch(retl) {
|
|
case PSYC_PARSE_LIST_END: // last element
|
|
retl = 0;
|
|
case PSYC_PARSE_LIST_ELEM:
|
|
sv.type = PIKE_T_STRING; sv.u.string = make_shared_binary_string(elem.data, elem.length);
|
|
elems = array_insert(elems, &sv, count++);
|
|
break;
|
|
default:
|
|
err = 5;
|
|
break;
|
|
}
|
|
} while (retl > 0 && !err);
|
|
}
|
|
if (!err) {
|
|
sv.type = PIKE_T_ARRAY;
|
|
sv.u.array = elems;
|
|
mapping_string_insert(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
&sv);
|
|
}
|
|
free_array(elems);
|
|
} while (0);
|
|
break;
|
|
default: // string
|
|
mapping_string_insert_string(THIS->evars,
|
|
make_shared_binary_string(name.data, name.length),
|
|
make_shared_binary_string(value.data, value.length));
|
|
}
|
|
} while (0);
|
|
if (err) { // there was an error while
|
|
// FIXME
|
|
return;
|
|
}
|
|
break;
|
|
case PSYC_PARSE_BODY_START: // if length was given this is used for body
|
|
init_string_builder_alloc(&THIS->incomplete, psyc_parse_value_length(&THIS->parser), 0);
|
|
case PSYC_PARSE_BODY_CONT:
|
|
string_builder_append(&THIS->incomplete, MKPCHARP(value.data, 0), value.length);
|
|
break;
|
|
case PSYC_PARSE_BODY_END:
|
|
string_builder_append(&THIS->incomplete, MKPCHARP(value.data, 0), value.length);
|
|
do {
|
|
struct pike_string *tmp = finish_string_builder(&THIS->incomplete);
|
|
value.length = tmp->len;
|
|
value.data = (char *) STR0(tmp);
|
|
// FIXME: not sure if this is really safe
|
|
free_string(tmp);
|
|
} while (0);
|
|
// fall thru
|
|
case PSYC_PARSE_BODY:
|
|
THIS->method = make_shared_binary_string(name.data, name.length);
|
|
THIS->body = make_shared_binary_string(value.data, value.length);
|
|
break;
|
|
case PSYC_PARSE_COMPLETE: // apply the callback
|
|
push_mapping(THIS->rvars);
|
|
push_mapping(THIS->evars);
|
|
if (THIS->method == NULL) {
|
|
apply_low(Pike_fp->current_object, THIS->handle_packet, 2);
|
|
} else if (THIS->body == NULL) {
|
|
push_string(THIS->method);
|
|
apply_low(Pike_fp->current_object, THIS->handle_packet, 3);
|
|
} else {
|
|
push_string(THIS->method);
|
|
push_string(THIS->body);
|
|
apply_low(Pike_fp->current_object, THIS->handle_packet, 4);
|
|
}
|
|
|
|
// reset packet state
|
|
THIS->rvars = allocate_mapping(0);
|
|
THIS->evars = allocate_mapping(0);
|
|
THIS->method = NULL;
|
|
THIS->body = NULL;
|
|
break;
|
|
case PSYC_PARSE_INSUFFICIENT: // not enough data
|
|
if (psyc_parse_remaining_buffer(&THIS->parser) > 0) {
|
|
THIS->buffer = make_shared_binary_string(psyc_parse_remaining_buffer(&THIS->parser),
|
|
psyc_parse_remaining_length(&THIS->parser));
|
|
}
|
|
return;
|
|
default: // fatal error
|
|
push_int(ret);
|
|
apply_low(Pike_fp->current_object, THIS->handle_error, 1);
|
|
// FIXME: free stuff? or do we kill the socket and parser anyway
|
|
return;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
}
|
|
|
|
INIT {
|
|
|
|
}
|
|
|
|
EXTRA {
|
|
add_integer_constant("__version_major", MODULE_MAJOR, 0);
|
|
add_integer_constant("__version_minor", MODULE_MAJOR, 0);
|
|
add_integer_constant("__version_patch", MODULE_MAJOR, 0);
|
|
}
|