libApp
/* $Id$ */
/* Copyright (c) 2012-2022 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libApp */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#ifdef DEBUG
# include <stdio.h>
#endif
#include <string.h>
#include <errno.h>
#include <System.h>
#include "appmessage.h"
/* AppMessage */
/* private */
/* types */
struct _AppMessage
{
AppMessageType type;
AppMessageID id;
union
{
struct
{
char * method;
AppMessageCallArgument * args;
size_t args_cnt;
} call;
} t;
};
/* public */
/* functions */
/* appmessage_new_acknowledgement */
AppMessage * appmessage_new_acknowledgement(AppMessageID id)
{
AppMessage * message;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, id);
#endif
if((message = object_new(sizeof(*message))) == NULL)
return NULL;
message->type = AMT_ACKNOWLEDGEMENT;
message->id = id;
return message;
}
/* appmessage_new_call */
AppMessage * appmessage_new_call(char const * method,
AppMessageCallArgument * args, size_t args_cnt)
{
AppMessage * message;
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, method);
#endif
if((message = object_new(sizeof(*message))) == NULL)
return NULL;
message->type = AMT_CALL;
message->id = 0;
message->t.call.method = string_new(method);
if((message->t.call.args = object_new(sizeof(*args) * args_cnt))
== NULL)
{
message->t.call.args_cnt = 0;
appmessage_delete(message);
return NULL;
}
for(i = 0; i < args_cnt; i++)
{
message->t.call.args[i].direction = args[i].direction;
if((message->t.call.args[i].arg = variable_new_copy(
args[i].arg)) == NULL)
break;
}
message->t.call.args_cnt = i;
/* check for errors */
if(i != args_cnt)
{
appmessage_delete(message);
return NULL;
}
return message;
}
/* appmessage_new_callv */
AppMessage * appmessage_new_callv(char const * method, ...)
{
AppMessage * message;
va_list ap;
size_t i;
int type;
Variable * v;
AppMessageCallArgument * p;
if((message = object_new(sizeof(*message))) == NULL)
return NULL;
message->type = AMT_CALL;
message->id = 0;
message->t.call.method = string_new(method);
message->t.call.args = NULL;
message->t.call.args_cnt = 0;
/* check for errors */
if(message->t.call.method == NULL)
{
appmessage_delete(message);
return NULL;
}
/* copy the arguments */
va_start(ap, method);
for(i = 0; (type = va_arg(ap, int)) >= 0; i++)
{
if((p = realloc(message->t.call.args, sizeof(*p) * (i + 1)))
== NULL)
{
error_set_code(-errno, "%s", strerror(errno));
appmessage_delete(message);
message = NULL;
break;
}
message->t.call.args = p;
if((v = variable_new(type, va_arg(ap, void *))) == NULL)
{
appmessage_delete(message);
message = NULL;
break;
}
message->t.call.args[i].direction = AMCD_IN; /* XXX */
message->t.call.args[i].arg = v;
message->t.call.args_cnt = i + 1;
}
va_end(ap);
return message;
}
/* appmessage_new_callv_variables */
AppMessage * appmessage_new_callv_variables(char const * method, ...)
{
AppMessage * message;
va_list ap;
size_t i;
Variable * v;
AppMessageCallArgument * p;
if((message = object_new(sizeof(*message))) == NULL)
return NULL;
message->type = AMT_CALL;
message->id = 0;
message->t.call.method = string_new(method);
message->t.call.args = NULL;
message->t.call.args_cnt = 0;
/* check for errors */
if(message->t.call.method == NULL)
{
appmessage_delete(message);
return NULL;
}
/* copy the arguments */
va_start(ap, method);
for(i = 0; (v = va_arg(ap, Variable *)) != NULL; i++)
{
if((p = realloc(message->t.call.args, sizeof(*p) * (i + 1)))
== NULL)
{
error_set_code(-errno, "%s", strerror(errno));
appmessage_delete(message);
message = NULL;
break;
}
message->t.call.args = p;
if((v = variable_new_copy(v)) == NULL)
{
appmessage_delete(message);
message = NULL;
break;
}
message->t.call.args[i].direction = AMCD_IN; /* FIXME */
message->t.call.args[i].arg = v;
message->t.call.args_cnt = i + 1;
}
va_end(ap);
return message;
}
/* appmessage_new_deserialize */
static AppMessage * _new_deserialize_acknowledgement(AppMessage * message,
char const * data, const size_t size, size_t pos);
static AppMessage * _new_deserialize_call(AppMessage * message,
char const * data, const size_t size, size_t pos);
static AppMessage * _new_deserialize_id(AppMessage * message, char const * data,
const size_t size, size_t * pos);
AppMessage * appmessage_new_deserialize(Buffer * buffer)
{
AppMessage * message;
char const * data = buffer_get_data(buffer);
size_t size = buffer_get_size(buffer);
size_t pos = 0;
size_t s;
Variable * v;
uint8_t u8;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if((message = object_new(sizeof(*message))) == NULL)
return NULL;
s = size;
if((v = variable_new_deserialize_type(VT_UINT8, &s, &data[pos]))
== NULL)
{
object_delete(message);
return NULL;
}
pos += s;
size -= s;
/* XXX may fail */
variable_get_as(v, VT_UINT8, &u8, NULL);
variable_delete(v);
switch((message->type = u8))
{
case AMT_ACKNOWLEDGEMENT:
return _new_deserialize_acknowledgement(message, data,
size, pos);
case AMT_CALL:
return _new_deserialize_call(message, data, size, pos);
default:
error_set_code(1, "%s%u", "Unknown message type ", u8);
/* XXX should not happen */
object_delete(message);
return NULL;
}
}
static AppMessage * _new_deserialize_acknowledgement(AppMessage * message,
char const * data, const size_t size, size_t pos)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
return _new_deserialize_id(message, data, size, &pos);
}
static AppMessage * _new_deserialize_call(AppMessage * message,
char const * data, const size_t size, size_t pos)
{
size_t s;
Variable * v;
size_t i;
AppMessageCallArgument * p;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(_new_deserialize_id(message, data, size, &pos) == NULL)
return NULL;
message->t.call.method = NULL;
message->t.call.args = NULL;
message->t.call.args_cnt = 0;
s = size;
if((v = variable_new_deserialize_type(VT_STRING, &s, &data[pos]))
== NULL)
{
error_set_code(1, "%s", "Could not obtain the AppMessage call"
" method");
appmessage_delete(message);
return NULL;
}
pos += s;
/* XXX may fail */
variable_get_as(v, VT_STRING, &message->t.call.method, NULL);
variable_delete(v);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__,
message->t.call.method);
#endif
/* deserialize the arguments */
for(i = 0; pos < size; i++)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %lu\n", __func__, i);
#endif
if((p = realloc(message->t.call.args, sizeof(*p) * (i + 1)))
== NULL)
{
error_set_code(-errno, "%s", strerror(errno));
appmessage_delete(message);
return NULL;
}
message->t.call.args = p;
s = size - pos;
if((v = variable_new_deserialize(&s, &data[pos])) == NULL)
{
appmessage_delete(message);
return NULL;
}
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %lu (%u)\n", __func__, i,
variable_get_type(v));
#endif
pos += s;
message->t.call.args[i].direction = AMCD_IN; /* XXX */
message->t.call.args[i].arg = v;
message->t.call.args_cnt = i + 1;
}
return message;
}
static AppMessage * _new_deserialize_id(AppMessage * message, char const * data,
const size_t size, size_t * pos)
{
int ret = 0;
size_t s = size;
Variable * v;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if((v = variable_new_deserialize_type(VT_UINT32, &s, &data[*pos]))
== NULL)
{
error_set_code(1, "%s", "Could not obtain the AppMessage ID");
appmessage_delete(message);
return NULL;
}
ret = variable_get_as(v, VT_UINT32, &message->id, NULL);
variable_delete(v);
if(ret != 0)
{
appmessage_delete(message);
return NULL;
}
*pos += s;
return message;
}
/* appmessage_delete */
static void _delete_call(AppMessage * message);
void appmessage_delete(AppMessage * message)
{
switch(message->type)
{
case AMT_ACKNOWLEDGEMENT:
break;
case AMT_CALL:
_delete_call(message);
break;
}
object_delete(message);
}
static void _delete_call(AppMessage * message)
{
size_t i;
for(i = 0; i < message->t.call.args_cnt; i++)
variable_delete(message->t.call.args[i].arg);
free(message->t.call.args);
string_delete(message->t.call.method);
}
/* accessors */
/* appmessage_get_id */
AppMessageID appmessage_get_id(AppMessage * message)
{
return message->id;
}
/* appmessage_get_method */
String const * appmessage_get_method(AppMessage * message)
{
if(message->type == AMT_CALL)
return message->t.call.method;
return NULL;
}
/* appmessage_get_type */
AppMessageType appmessage_get_type(AppMessage * message)
{
return message->type;
}
/* appmessage_set_id */
void appmessage_set_id(AppMessage * message, AppMessageID id)
{
message->id = id;
}
/* useful */
/* appmessage_serialize */
static int _serialize_acknowledgement(AppMessage * message, Buffer * buffer,
Buffer * b);
static int _serialize_append(Buffer * buffer, Buffer * b);
static int _serialize_call(AppMessage * message, Buffer * buffer, Buffer * b);
static int _serialize_id(AppMessage * message, Buffer * buffer, Buffer * b);
static int _serialize_type(AppMessage * message, Buffer * buffer, Buffer * b);
int appmessage_serialize(AppMessage * message, Buffer * buffer)
{
Buffer * b;
if((b = buffer_new(0, NULL)) == NULL)
return -1;
/* reset the output buffer */
buffer_set_size(buffer, 0);
if(_serialize_type(message, buffer, b) != 0)
return -1;
switch(message->type)
{
case AMT_ACKNOWLEDGEMENT:
return _serialize_acknowledgement(message, buffer, b);
case AMT_CALL:
return _serialize_call(message, buffer, b);
default:
return -error_set_code(1, "%s%u",
"Unable to serialize message type ",
message->type);
}
}
static int _serialize_acknowledgement(AppMessage * message, Buffer * buffer,
Buffer * b)
{
return _serialize_id(message, buffer, b);
}
static int _serialize_append(Buffer * buffer, Buffer * b)
{
return buffer_set_data(buffer, buffer_get_size(buffer),
buffer_get_data(b), buffer_get_size(b));
}
static int _serialize_call(AppMessage * message, Buffer * buffer, Buffer * b)
{
int ret;
Variable * v;
size_t i;
if(_serialize_id(message, buffer, b) != 0)
return -1;
if((v = variable_new(VT_STRING, message->t.call.method)) == NULL)
return -1;
ret = (variable_serialize(v, b, 0) == 0
&& _serialize_append(buffer, b) == 0) ? 0 : -1;
variable_delete(v);
if(ret != 0)
return ret;
for(i = 0; i < message->t.call.args_cnt; i++)
{
if(message->t.call.args[i].direction == AMCD_OUT)
continue;
if(variable_serialize(message->t.call.args[i].arg, b, 1) != 0
|| _serialize_append(buffer, b) != 0)
break;
}
buffer_delete(b);
return (i == message->t.call.args_cnt) ? 0 : -1;
}
static int _serialize_id(AppMessage * message, Buffer * buffer, Buffer * b)
{
int ret;
Variable * v;
if((v = variable_new(VT_UINT32, &message->id)) == NULL)
return -1;
ret = (variable_serialize(v, b, 0) == 0
&& _serialize_append(buffer, b) == 0) ? 0 : -1;
variable_delete(v);
return ret;
}
static int _serialize_type(AppMessage * message, Buffer * buffer, Buffer * b)
{
int ret;
Variable * v;
if((v = variable_new(VT_UINT8, &message->type)) == NULL)
return -1;
ret = (variable_serialize(v, b, 0) == 0
&& _serialize_append(buffer, b) == 0) ? 0 : -1;
variable_delete(v);
return ret;
}