efl/legacy/ecore/src/lib/ecore_dbus/ecore_dbus.c

592 lines
17 KiB
C

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#include "config.h"
#include "Ecore.h"
#include "ecore_private.h"
#include "Ecore_Con.h"
#include "Ecore_DBus.h"
#include "ecore_dbus_private.h"
/********************************************************************************/
/* About */
/********************************************************************************/
/* Author: Jorge Luis Zapata */
/* Author: Brian Mattern (rephorm) */
/* Version: 0.3.0 */
/********************************************************************************/
/* Todo */
/********************************************************************************/
/* free allocated data */
/* make other authentication mechanisms besides external */
/********************************************************************************/
/* Changelog */
/********************************************************************************/
/* 0.0 usable interface */
/* 0.1 change dbus spec version (0.11): */
/* different header format */
/* new type signature */
/* header length = 8 byte multiple */
/* paddings value must be null */
/* body need not to end in a 8 byte boundary */
/* new data type: variant,signature,dict */
/* ecore_oldlist cant join two lists so is difficult to handle compound */
/* data types (variant,struct,dict,array) in a stack way */
/* 0.2 change again the spec version (0.8) */
/* i realized that the first version was correct, when i read the spec */
/* for ecore_dbus 0.1 i was reading a previous version :( */
/* put again the data type byte in each marshaled data */
/* */
/* 29-03-05 */
/* 0.2.1 some segfault fixes, new tests */
/* 0.3.0 add ability to send signals, receive method class and respond to them */
/* add address parsing and functions to connect to standard busses */
/* change API of ecore_dbus_message_new_method_call() */
/* global variables */
EAPI int ECORE_DBUS_EVENT_SERVER_ADD = 0;
EAPI int ECORE_DBUS_EVENT_SERVER_DEL = 0;
EAPI int ECORE_DBUS_EVENT_SIGNAL = 0;
EAPI int ECORE_DBUS_EVENT_METHOD_CALL = 0;
/* private function declaration */
/* helper functions */
static char *_ecore_dbus_getuid(void);
static char *_ecore_dbus_hex_encode(char *src_str);
/* auth functions */
unsigned char *_ecore_dbus_auth_external(void *data);
/* con functions */
static int _ecore_dbus_event_server_add(void *data, int ev_type, void *ev);
static int _ecore_dbus_event_server_del(void *data, int ev_type, void *ev);
static int _ecore_dbus_event_server_data(void *data, int ev_type, void *ev);
static void _ecore_dbus_event_server_del_free(void *data, void *ev);
static void _ecore_dbus_event_server_data_free(void *data, void *ev);
static Ecore_DBus_Event_Server_Data *_ecore_dbus_event_create(Ecore_DBus_Server *svr, Ecore_DBus_Message *msg);
static void _ecore_dbus_method_hello_cb(void *data, Ecore_DBus_Method_Return *reply);
static void _ecore_dbus_method_error_cb(void *data, const char *error);
/* local variables */
static const Ecore_DBus_Auth auths[] = {
{"EXTERNAL", 1, {_ecore_dbus_auth_external, NULL, NULL, NULL, NULL}},
{"MAGIC_COOKIE", 0, {NULL, NULL, NULL, NULL, NULL}},
{"DBUS_COOKIE_SHA1", 0, {NULL, NULL, NULL, NULL, NULL}},
{"KERBEROS_V4", 0, {NULL, NULL, NULL, NULL, NULL}},
{"SKEY", 0, {NULL, NULL, NULL, NULL, NULL}},
};
static int init_count = 0;
static Ecore_List2 *servers = NULL;
static Ecore_Event_Handler *handler[3];
/* public functions */
EAPI int
ecore_dbus_init(void)
{
int i = 0;
if (++init_count != 1) return init_count;
ecore_con_init();
ECORE_DBUS_EVENT_SERVER_ADD = ecore_event_type_new();
ECORE_DBUS_EVENT_SERVER_DEL = ecore_event_type_new();
ECORE_DBUS_EVENT_SIGNAL = ecore_event_type_new();
ECORE_DBUS_EVENT_METHOD_CALL = ecore_event_type_new();
handler[i++] = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD,
_ecore_dbus_event_server_add, NULL);
handler[i++] = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL,
_ecore_dbus_event_server_del, NULL);
handler[i++] = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA,
_ecore_dbus_event_server_data, NULL);
return init_count;
}
EAPI int
ecore_dbus_shutdown(void)
{
int i = 0;
if (--init_count != 0) return init_count;
/* FIXME: Delete servers */
for (i = 0; i < 3; i++)
ecore_event_handler_del(handler[i]);
ecore_con_shutdown();
return init_count;
}
/**
* Connect to the system bus.
*/
EAPI Ecore_DBus_Server *
ecore_dbus_server_system_connect(const void *data)
{
Ecore_List *addrs;
Ecore_DBus_Server *svr;
char *bus_env;
/* get the system bus address from the environment */
bus_env = getenv("DBUS_SYSTEM_BUS_ADDRESS");
if (bus_env)
{
addrs = ecore_dbus_address_parse(bus_env);
if (addrs)
{
svr = ecore_dbus_address_list_connect(addrs, data);
ecore_list_destroy(addrs);
if (svr) return svr;
}
}
/* if we haven't returned already, try the default location */
return ecore_dbus_server_connect(ECORE_CON_LOCAL_SYSTEM, "/var/run/dbus/system_bus_socket", -1, data);
}
/**
* Connect to the session bus.
*/
EAPI Ecore_DBus_Server *
ecore_dbus_server_session_connect(const void *data)
{
Ecore_List *addrs;
Ecore_DBus_Server *svr;
char *bus_env;
/* get the session bus address from the environment */
bus_env = getenv("DBUS_SESSION_BUS_ADDRESS");
if (bus_env)
{
addrs = ecore_dbus_address_parse(bus_env);
if (addrs)
{
svr = ecore_dbus_address_list_connect(addrs, data);
ecore_list_destroy(addrs);
if (svr) return svr;
}
}
/*
* XXX try getting address from _DBUS_SESSION_BUS_ADDRESS property (STRING) set
* on the root window
*/
return NULL;
}
EAPI Ecore_DBus_Server *
ecore_dbus_server_starter_connect(const void *data)
{
Ecore_List *addrs;
Ecore_DBus_Server *svr;
char *bus_env;
/* get the session bus address from the environment */
bus_env = getenv("DBUS_STARTER_ADDRESS");
if (bus_env)
{
addrs = ecore_dbus_address_parse(bus_env);
if (addrs)
{
svr = ecore_dbus_address_list_connect(addrs, data);
ecore_list_destroy(addrs);
if (svr) return svr;
}
}
return NULL;
}
EAPI Ecore_DBus_Server *
ecore_dbus_server_connect(Ecore_Con_Type con_type, const char *name, int port,
const void *data)
{
/* XXX data isn't used! */
Ecore_DBus_Server *svr;
svr = calloc(1, sizeof(Ecore_DBus_Server));
if (!svr) return NULL;
svr->server =
ecore_con_server_connect(con_type, name, port, svr);
if (!svr->server)
{
fprintf(stderr, "Ecore_DBus Error: Couldn't connect to server\n");
free(svr);
return NULL;
}
svr->authenticated = 0;
svr->cnt_msg = 0;
svr->auth_type = -1;
svr->auth_type_transaction = 0;
svr->messages = ecore_hash_new(ecore_direct_hash, ecore_direct_compare);
ecore_hash_set_free_value(svr->messages, ECORE_FREE_CB(_ecore_dbus_message_free));
servers = _ecore_list2_append(servers, svr);
return svr;
}
EAPI void
ecore_dbus_server_del(Ecore_DBus_Server *svr)
{
if (svr->server) ecore_con_server_del(svr->server);
servers = _ecore_list2_remove(servers, svr);
if (svr->unique_name) free(svr->unique_name);
ecore_hash_destroy(svr->messages);
free(svr);
}
EAPI int
ecore_dbus_server_send(Ecore_DBus_Server *svr, char *command, int length)
{
int ret;
ret = ecore_con_server_send(svr->server, command, length);
printf("[ecore_dbus] ecore_dbus_server: %p ecore_con_server: %p sent %d of %d bytes\n",
svr, svr->server, ret, length);
return ret;
}
/* helper functions */
static char *
_ecore_dbus_getuid(void)
{
/* this calculation is from comp.lang.c faq */
#define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1) /* +1 for '-' */
int len;
char *uid;
char *tmp;
tmp = (char *)malloc(MAX_LONG_LEN);
len = snprintf(tmp, MAX_LONG_LEN, "%ld", (long) getuid());
uid = (char *)malloc(len + 1);
uid = memcpy(uid, tmp, len);
uid[len] = '\0';
free(tmp);
return uid;
}
/* encodes a string into a string of hex values */
/* each byte is two hex digits */
static char *
_ecore_dbus_hex_encode(char *src_str)
{
const char hexdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
char *enc_str;
char *p;
char *end;
int len;
int i = 0;
len = strlen(src_str);
p = src_str;
end = p + len;
enc_str = malloc(2 * len + 1);
while (p != end)
{
enc_str[i] = hexdigits[(*p >> 4)];
i++;
enc_str[i] = hexdigits[(*p & 0x0f)];
i++;
p++;
}
enc_str[i] = '\0';
return enc_str;
}
/* auth functions */
unsigned char *
_ecore_dbus_auth_external(void *data)
{
char *uid, *enc_uid, *msg;
uid = _ecore_dbus_getuid();
enc_uid = _ecore_dbus_hex_encode(uid);
free(uid);
msg = malloc(strlen(enc_uid) + 17);
sprintf(msg, "AUTH EXTERNAL %s\r\n", enc_uid);
free(enc_uid);
return (unsigned char *)msg;
}
/* con functions */
static int
_ecore_dbus_event_server_add(void *data, int ev_type, void *ev)
{
Ecore_DBus_Event_Server_Add *e;
Ecore_DBus_Server *svr;
e = ev;
svr = ecore_con_server_data_get(e->server);
if (!svr) return 1;
if (!_ecore_list2_find(servers, svr)) return 1;
ecore_dbus_server_send(svr, "\0", 1);
ecore_dbus_server_send(svr, "AUTH\r\n", 6);
printf("[ecore_dbus] begining auth process\n");
return 0;
}
static int
_ecore_dbus_event_server_del(void *udata, int ev_type, void *ev)
{
Ecore_Con_Event_Server_Del *e;
Ecore_DBus_Server *svr;
Ecore_DBus_Event_Server_Del *e2;
e = ev;
svr = ecore_con_server_data_get(e->server);
if (!svr) return 1;
if (!_ecore_list2_find(servers, svr)) return 1;
e2 = calloc(1, sizeof(Ecore_DBus_Event_Server_Del));
if (e2)
{
svr->server = NULL;
e2->server = svr;
ecore_event_add(ECORE_DBUS_EVENT_SERVER_DEL, e2, _ecore_dbus_event_server_del_free, NULL);
}
return 0;
}
static int
_ecore_dbus_event_server_data(void *udata, int ev_type, void *ev)
{
Ecore_Con_Event_Server_Data *e;
Ecore_DBus_Server *svr;
e = ev;
svr = ecore_con_server_data_get(e->server);
if (!svr) return 1;
if (!_ecore_list2_find(servers, svr)) return 1;
if (!svr->authenticated)
{
/* authentication protocol */
const Ecore_DBus_Auth *auth;
Ecore_DBus_Auth_Transaction trans;
if (!strncmp(e->data, "OK", 2))
{
printf("[ecore_dbus] auth type %s successful\n", auths[svr->auth_type].name);
ecore_dbus_server_send(svr, "BEGIN\r\n", 7);
svr->authenticated = 1;
/* Register on the bus */
ecore_dbus_method_hello(svr, _ecore_dbus_method_hello_cb, _ecore_dbus_method_error_cb, svr);
}
else if (!strncmp(e->data, "DATA", 4))
{
printf("[ecore_dbus] requiring data (unavailable)\n");
}
else if (!strncmp(e->data, "ERROR", 5))
{
printf("[ecore_dbus] auth process error\n");
}
else if (!strncmp(e->data, "REJECTED", 8))
{
unsigned char *msg;
if (svr->auth_type >= 0)
printf("[ecore_dbus] auth type %s rejected\n", auths[svr->auth_type].name);
svr->auth_type++;
auth = &auths[svr->auth_type];
trans = auth->transactions[0];
printf("[ecore_dbus] auth type %s started\n", auth->name);
msg = trans(NULL);
ecore_dbus_server_send(svr, (char *)msg, strlen((char *)msg));
free(msg);
}
}
else
{
/* message protocol */
Ecore_DBus_Message *msg;
unsigned int offset = 0;
printf("[ecore_dbus] received server data, %d bytes\n", e->size);
while (e->size)
{
Ecore_DBus_Event_Server_Data *ev2;
msg = _ecore_dbus_message_unmarshal(svr, (unsigned char *)(e->data) + offset, e->size);
if (!msg) break;
offset += msg->length;
e->size -= msg->length;
printf("[ecore_dbus] dbus message length %u bytes, still %d\n",
msg->length, e->size);
//ecore_dbus_message_print(msg);
/* Trap known messages */
ev2 = _ecore_dbus_event_create(svr, msg);
if (!ev2) break;
if (msg->type == ECORE_DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
Ecore_DBus_Message *sent;
sent = ecore_hash_remove(svr->messages, (void *)(ev2->header.reply_serial));
if ((sent) && (sent->cb.method_return))
{
sent->cb.method_return(sent->cb.data, ev2);
}
else
{
printf("[ecore_dbus] Reply without reply serial!\n");
}
if (sent) _ecore_dbus_message_free(sent);
_ecore_dbus_event_server_data_free(NULL, ev2);
}
else if (msg->type == ECORE_DBUS_MESSAGE_TYPE_ERROR)
{
Ecore_DBus_Message *sent;
sent = ecore_hash_remove(svr->messages, (void *)(ev2->header.reply_serial));
if ((sent) && (sent->cb.error))
{
char *error = NULL;
if ((ev2->args) && (ev2->args[0].type == ECORE_DBUS_DATA_TYPE_STRING))
error = ev2->args[0].value;
sent->cb.error(sent->cb.data, error);
}
else
{
printf("[ecore_dbus] Error without reply serial!\n");
}
if (sent) _ecore_dbus_message_free(sent);
_ecore_dbus_event_server_data_free(NULL, ev2);
}
else if (msg->type == ECORE_DBUS_MESSAGE_TYPE_SIGNAL)
{
Ecore_DBus_Event_Server_Data *ev2;
ev2 = _ecore_dbus_event_create(svr, msg);
ecore_event_add(ECORE_DBUS_EVENT_SIGNAL, ev2,
_ecore_dbus_event_server_data_free, NULL);
}
else if (msg->type == ECORE_DBUS_MESSAGE_TYPE_METHOD_CALL)
{
Ecore_DBus_Event_Server_Data *ev2;
ev2 = _ecore_dbus_event_create(svr, msg);
ecore_event_add(ECORE_DBUS_EVENT_METHOD_CALL, ev2,
_ecore_dbus_event_server_data_free, NULL);
}
else
{
printf("Ecore_DBus: Unknown return type %d\n", msg->type);
_ecore_dbus_event_server_data_free(NULL, ev2);
}
}
}
return 1;
}
static void
_ecore_dbus_event_server_del_free(void *data, void *ev)
{
Ecore_DBus_Event_Server_Del *event;
event = ev;
ecore_dbus_server_del(event->server);
free(ev);
}
static void
_ecore_dbus_event_server_data_free(void *data, void *ev)
{
Ecore_DBus_Event_Server_Data *event;
event = ev;
_ecore_dbus_message_free(event->message);
if (event->args) free(event->args);
free(ev);
}
static Ecore_DBus_Event_Server_Data *
_ecore_dbus_event_create(Ecore_DBus_Server *svr, Ecore_DBus_Message *msg)
{
Ecore_DBus_Event_Server_Data *ev;
unsigned int *serial;
ev = calloc(1, sizeof(Ecore_DBus_Event_Server_Data));
if (!ev) return NULL;
ev->server = svr;
ev->type = msg->type;
ev->message = msg;
ev->header.path = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_PATH);
ev->header.interface = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_INTERFACE);
ev->header.member = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_MEMBER);
ev->header.error_name = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_ERROR_NAME);
serial = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_REPLY_SERIAL);
if (serial)
ev->header.reply_serial = *serial;
ev->header.destination = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_DESTINATION);
ev->header.sender = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_SENDER);
ev->header.signature = ecore_dbus_message_header_field_get(msg, ECORE_DBUS_HEADER_FIELD_SIGNATURE);
if (!ecore_list_is_empty(msg->fields))
{
Ecore_DBus_Message_Field *f;
int i = 0;
ev->args = malloc(ecore_list_nodes(msg->fields) * sizeof(Ecore_DBus_Message_Arg));
ecore_list_goto_first(msg->fields);
while ((f = ecore_list_next(msg->fields)))
{
ev->args[i].type = f->type;
ev->args[i].value = _ecore_dbus_message_field_value_get(f);
i++;
}
}
return ev;
}
static void
_ecore_dbus_method_hello_cb(void *data, Ecore_DBus_Method_Return *reply)
{
Ecore_DBus_Event_Server_Add *svr_add;
Ecore_DBus_Server *svr;
char *name;
svr = data;
name = reply->args[0].value;
printf("Got unique name: %s\n", name);
if (svr->unique_name)
{
printf("Ecore_DBus: Already said hello %s - %s\n",
svr->unique_name, name);
free(svr->unique_name);
}
svr->unique_name = strdup(name);
svr_add = malloc(sizeof(Ecore_DBus_Event_Server_Add));
svr_add->server = svr;
ecore_event_add(ECORE_DBUS_EVENT_SERVER_ADD, svr_add, NULL, NULL);
}
static void
_ecore_dbus_method_error_cb(void *data, const char *error)
{
Ecore_DBus_Event_Server_Del *ev;
Ecore_DBus_Server *svr;
svr = data;
printf("Ecore_DBus: error %s\n", error);
ev = malloc(sizeof(Ecore_DBus_Event_Server_Del));
if (!ev) return;
ev->server = svr;
ecore_event_add(ECORE_DBUS_EVENT_SERVER_DEL, ev, _ecore_dbus_event_server_del_free, NULL);
}