/*-
* $Id: rr-message-start.c,v 1.22 2002/11/08 12:35:28 jonas Exp $
*
* See the file LICENSE for redistribution information.
* If you have not received a copy of the license, please contact CodeFactory
* by email at info@codefactory.se, or on the web at http://www.codefactory.se/
* You may also write to: CodeFactory AB, SE-903 47, Umeå, Sweden.
*
* Copyright (c) 2002 Jonas Borgström <jonas@codefactory.se>
* Copyright (c) 2002 Daniel Lundin <daniel@codefactory.se>
* Copyright (c) 2002 CodeFactory AB. All rights reserved.
*/
#include <librr/rr.h>
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <stdio.h>
static GObjectClass *parent_class = NULL;
static void finalize (GObject *object);
static gboolean process_frame (RRMessage *message, RRFrame *frame,
GError **error);
static RRFrame *get_frame (RRMessage *stat, gsize max_size);
static void
free_item (gpointer data, gpointer user_data)
{
RRStartItem *item = (RRStartItem *)data;
g_free (item->piggyback);
g_free (item);
}
static void
finalize (GObject *object)
{
RRMessageStart *start = (RRMessageStart *)object;
g_mutex_free (start->done_mutex);
g_cond_free (start->done_cond);
g_slist_foreach (start->in_list, free_item, 0);
g_slist_foreach (start->out_list, (GFunc)g_object_unref, 0);
g_slist_free (start->in_list);
g_slist_free (start->out_list);
g_free (start->server_name);
if (start->client_init_error)
g_error_free (start->client_init_error);
parent_class->finalize (object);
}
static void
rr_message_start_init (GObject *object)
{
RRMessageStart *start = (RRMessageStart *)object;
RRMessage *msg = (RRMessage *)object;
start->done_mutex = g_mutex_new ();
start->done_cond = g_cond_new ();
msg->type = RR_FRAME_TYPE_MSG;
}
static void
rr_message_start_class_init (GObjectClass *klass)
{
RRMessageClass *msg_class = (RRMessageClass *)klass;
klass->finalize = finalize;
msg_class->get_frame = get_frame;
msg_class->process_frame = process_frame;
parent_class = g_type_class_peek_parent (klass);
}
GType
rr_message_start_get_type (void)
{
static GType rr_type = 0;
if (!rr_type) {
static GTypeInfo type_info = {
sizeof (RRMessageStartClass),
NULL,
NULL,
(GClassInitFunc) rr_message_start_class_init,
NULL,
NULL,
sizeof (RRMessageStart),
16,
(GInstanceInitFunc) rr_message_start_init
};
rr_type = g_type_register_static (RR_TYPE_MESSAGE,
"RRMessageStart",
&type_info, 0);
}
return rr_type;
}
RRMessageStart *
rr_message_start_new (gint number, const gchar *server_name)
{
RRMessageStart *start;
start = g_object_new (RR_TYPE_MESSAGE_START, NULL);
start->number = number;
if (server_name)
start->server_name = g_strdup (server_name);
return start;
}
GSList *
rr_message_start_get_channel_list (RRMessageStart *start)
{
g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL);
return start->in_list;
}
void
rr_message_start_add_channel (RRMessageStart *start,
RRConnection *conn,
GType type,
gpointer config_data)
{
RRChannel *channel;
GError *error = NULL;
gpointer global_config = NULL;
channel = g_object_new (type, NULL);
channel->id = start->number;
channel->instance_config = config_data;
if (conn->profreg) {
global_config = rr_profile_registry_get_global_config (conn->profreg, type);
}
channel->global_config = global_config;
rr_channel_set_connection (channel, conn);
/* Give the channel a chance to generate a piggyback */
if (!rr_channel_client_init (channel, &error)) {
if (error) {
if (start->client_init_error)
g_error_free (start->client_init_error);
start->client_init_error = error;
}
rr_debug1 ("message_start::add_channel "
"rr_channel_client_init failed: %s, %s",
G_OBJECT_TYPE_NAME (G_OBJECT (channel)),
error->message);
g_object_unref (G_OBJECT (channel));
}
else
start->out_list = g_slist_append (start->out_list, channel);
}
static void
append_channel (gpointer data, gpointer user_data)
{
RRChannel *channel = data;
GString *str = (GString *)user_data;
const gchar *uri, *piggyback;
g_return_if_fail (RR_IS_CHANNEL (channel));
uri = rr_channel_get_uri (G_OBJECT_TYPE (G_OBJECT (channel)));
piggyback = rr_channel_get_piggyback (channel);
if (piggyback) {
g_string_append_printf (str,
" <profile uri='%s'>\r\n"
" <![CDATA[%s]]>\r\n"
" </profile>\r\n",
uri, piggyback);
}
else
g_string_append_printf (str, " <profile uri='%s' />\r\n", uri);
}
static gboolean
process_frame (RRMessage *message, RRFrame *frame, GError **error)
{
RRMessageStart *start;
RRConnection *conn;
xmlDocPtr doc;
xmlNodePtr node;
gboolean ret;
gchar *server_name;
xmlChar *str;
const gchar *data;
gint len;
g_return_val_if_fail (RR_IS_MESSAGE_START (message), FALSE);
g_return_val_if_fail (RR_IS_FRAME (frame), FALSE);
g_return_val_if_fail (RR_IS_CHANNEL (message->channel), FALSE);
g_return_val_if_fail (RR_IS_CONNECTION (message->channel->connection),
FALSE);
conn = message->channel->connection;
start = RR_MESSAGE_START (message);
data = rr_frame_mime_get_body (frame);
len = rr_frame_mime_get_body_size (frame);
doc = xmlParseMemory (data, len);
if (doc == NULL) {
g_set_error (error, RR_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, "Invalid start message.");
return FALSE;
}
node = xmlDocGetRootElement (doc);
if (strcmp (node->name, "start") != 0)
goto err;
if ((str = xmlGetProp (node, "number")) == NULL)
goto err;
start->number = atoi (str);
xmlFree (str);
if ((server_name = xmlGetProp (node, "serverName"))) {
start->server_name = g_strdup (server_name);
xmlFree (server_name);
}
node = node->children;
while (node) {
xmlChar *uri, *piggyback = NULL;
GType type;
RRStartItem *item;
/* Ignore none element nodes */
while (node && node->type != XML_ELEMENT_NODE)
node = node->next;
if (node == NULL)
break;
if (strcmp (node->name, "profile") != 0)
goto err;
if ((uri = xmlGetProp (node, "uri")) == NULL)
goto err;
type = rr_profile_registry_lookup_by_uri (conn->profreg, uri);
xmlFree (uri);
if (type == G_TYPE_INVALID) {
node = node->next;
continue;
}
/* Extract the piggyback data if it exists */
if (node->children) {
xmlNode *child, *cdata_node = NULL;
child = node->children;
while (child) {
if (child->type == XML_CDATA_SECTION_NODE &&
child->content) {
cdata_node = child;
break;
}
child = child->next;
}
if (cdata_node && child->content)
piggyback = cdata_node->content;
else if (node->children->type == XML_TEXT_NODE &&
node->children->content)
piggyback = node->children->content;
}
item = g_new (RRStartItem, 1);
item->type = type;
item->piggyback = g_strdup (piggyback);
start->in_list = g_slist_append (start->in_list, item);
node = node->next;
}
ret = TRUE;
goto end;
err:
g_set_error (error, RR_ERROR, RR_BEEP_CODE_PARAM_ERROR,
RR_GERROR_DEFAULT_MESSAGE);
ret = FALSE;
end:
xmlFreeDoc (doc);
return ret;
}
static RRFrame *
get_frame (RRMessage *message, gsize max_size)
{
RRMessageStart *start = RR_MESSAGE_START(message);
RRFrame *frame;
GString *str = g_string_new (RR_BEEP_MIME_HEADER);
g_assert (RR_IS_MESSAGE (start));
if (start->server_name)
g_string_append_printf (str, "<start number='%d' serverName='%s'>\r\n",
start->number, start->server_name);
else
g_string_append_printf (str, "<start number='%d'>\r\n",
start->number);
g_slist_foreach (start->out_list, append_channel, str);
g_string_append (str, "</start>\r\n");
if (str->len > max_size) {
g_string_free (str, TRUE);
return NULL;
}
frame = rr_frame_new (RR_FRAME_TYPE_MSG,
message->channel->id, FALSE, message->msgno,
str->len, 0, str->str, TRUE);
g_string_free (str, FALSE);
return frame;
}
void
rr_message_start_done (RRMessageStart *start, RRChannel *channel,
GError *error)
{
g_return_if_fail (RR_IS_MESSAGE_START (start));
g_mutex_lock (start->done_mutex);
start->done_channel = channel;
if (error)
start->done_error = g_error_copy (error);
start->done = TRUE;
g_cond_broadcast (start->done_cond);
g_mutex_unlock (start->done_mutex);
}
RRChannel *
rr_message_start_wait_for_reply (RRMessageStart *start, GError **error)
{
g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL);
g_mutex_lock (start->done_mutex);
while (!start->done)
g_cond_wait (start->done_cond, start->done_mutex);
g_mutex_unlock (start->done_mutex);
if (start->done_error) {
g_propagate_error (error, start->done_error);
start->done_error = NULL;
}
return start->done_channel;
}
/**
* rr_message_start_get_server_name:
* @start: A #RRMessageStart.
*
* Returns the "serverName" attribute for the first successful "start" element
* received by a BEEP peer is meaningful for the duration of the BEEP
* session.
*
* Return value: the %serverName.
**/
const gchar *
rr_message_start_get_server_name (RRMessageStart *start)
{
g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL);
return start->server_name;
}
gboolean
rr_message_start_empty_request_p (RRMessageStart *start)
{
g_return_val_if_fail (RR_IS_MESSAGE_START (start), TRUE);
return start->out_list == NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1