/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2003 Imendio AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <string.h>
#include "lm-internals.h"
#include "lm-message-node.h"
typedef struct {
gchar *key;
gchar *value;
} KeyValuePair;
static void message_node_free (LmMessageNode *node);
static LmMessageNode * message_node_last_child (LmMessageNode *node);
static void
message_node_free (LmMessageNode *node)
{
LmMessageNode *l;
GSList *list;
g_return_if_fail (node != NULL);
for (l = node->children; l;) {
LmMessageNode *next = l->next;
lm_message_node_unref (l);
l = next;
}
g_free (node->name);
g_free (node->value);
for (list = node->attributes; list; list = list->next) {
KeyValuePair *kvp = (KeyValuePair *) list->data;
g_free (kvp->key);
g_free (kvp->value);
g_free (kvp);
}
g_slist_free (node->attributes);
g_free (node);
}
static LmMessageNode *
message_node_last_child (LmMessageNode *node)
{
LmMessageNode *l;
g_return_val_if_fail (node != NULL, NULL);
if (!node->children) {
return NULL;
}
l = node->children;
while (l->next) {
l = l->next;
}
return l;
}
LmMessageNode *
_lm_message_node_new (const gchar *name)
{
LmMessageNode *node;
node = g_new0 (LmMessageNode, 1);
node->name = g_strdup (name);
node->value = NULL;
node->raw_mode = FALSE;
node->attributes = NULL;
node->next = NULL;
node->prev = NULL;
node->parent = NULL;
node->children = NULL;
node->ref_count = 1;
return node;
}
void
_lm_message_node_add_child_node (LmMessageNode *node, LmMessageNode *child)
{
LmMessageNode *prev;
g_return_if_fail (node != NULL);
prev = message_node_last_child (node);
lm_message_node_ref (child);
if (prev) {
prev->next = child;
child->prev = prev;
} else {
node->children = child;
}
child->parent = node;
}
/**
* lm_message_node_get_value:
* @node: an #LmMessageNode
*
* Retrieves the value of @node.
*
* Return value:
**/
EXPORT_C const gchar *
lm_message_node_get_value (LmMessageNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
return node->value;
}
/**
* lm_message_node_set_value:
* @node: an #LmMessageNode
* @value: the new value.
*
* Sets the value of @node. If a previous value is set it will be freed.
**/
EXPORT_C void
lm_message_node_set_value (LmMessageNode *node, const gchar *value)
{
g_return_if_fail (node != NULL);
g_free (node->value);
if (!value) {
node->value = NULL;
return;
}
node->value = g_strdup (value);
}
/**
* lm_message_node_add_child:
* @node: an #LmMessageNode
* @name: the name of the new child
* @value: value of the new child
*
* Add a child node with @name and value set to @value.
*
* Return value: the newly created child
**/
EXPORT_C LmMessageNode *
lm_message_node_add_child (LmMessageNode *node,
const gchar *name,
const gchar *value)
{
LmMessageNode *child;
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
child = _lm_message_node_new (name);
lm_message_node_set_value (child, value);
_lm_message_node_add_child_node (node, child);
lm_message_node_unref (child);
return child;
}
/**
* lm_message_node_set_attributes:
* @node: an #LmMessageNode
* @name: the first attribute, should be followed by a string with the value
* @Varargs: The rest of the name/value pairs
*
* Sets a list of attributes. The arguments should be names and corresponding
* value and needs to be ended with %NULL.
**/
EXPORT_C void
lm_message_node_set_attributes (LmMessageNode *node,
const gchar *name,
...)
{
va_list args;
g_return_if_fail (node != NULL);
for (va_start (args, name);
name;
name = (const gchar *) va_arg (args, gpointer)) {
const gchar *value;
value = (const gchar *) va_arg (args, gpointer);
lm_message_node_set_attribute (node, name, value);
}
va_end (args);
}
/**
* lm_message_node_set_attribute:
* @node: an #LmMessageNode
* @name: name of attribute
* @value: value of attribute.
*
* Sets the attribute @name to @value.
**/
EXPORT_C void
lm_message_node_set_attribute (LmMessageNode *node,
const gchar *name,
const gchar *value)
{
gboolean found = FALSE;
GSList *l;
g_return_if_fail (node != NULL);
g_return_if_fail (name != NULL);
g_return_if_fail (value != NULL);
for (l = node->attributes; l; l = l->next) {
KeyValuePair *kvp = (KeyValuePair *) l->data;
if (strcmp (kvp->key, name) == 0) {
g_free (kvp->value);
kvp->value = g_strdup (value);
found = TRUE;
break;
}
}
if (!found) {
KeyValuePair *kvp;
kvp = g_new0 (KeyValuePair, 1);
kvp->key = g_strdup (name);
kvp->value = g_strdup (value);
node->attributes = g_slist_prepend (node->attributes, kvp);
}
}
/**
* lm_message_node_get_attribute:
* @node: an #LmMessageNode
* @name: the attribute name
*
* Fetches the attribute @name from @node.
*
* Return value: the attribute value or %NULL if not set
**/
EXPORT_C const gchar *
lm_message_node_get_attribute (LmMessageNode *node, const gchar *name)
{
GSList *l;
const gchar *ret_val = NULL;
//g_return_val_if_fail (node != NULL, NULL);
//g_return_val_if_fail (name != NULL, NULL);
if(!node || !name)
return NULL;
for (l = node->attributes; l; l = l->next) {
KeyValuePair *kvp = (KeyValuePair *) l->data;
if (strcmp (kvp->key, name) == 0) {
ret_val = kvp->value;
}
}
return ret_val;
}
/**
* lm_message_node_get_child:
* @node: an #LmMessageNode
* @child_name: the childs name
*
* Fetches the child @child_name from @node. If child is not found as an
* immediate child of @node %NULL is returned.
*
* Return value: the child node or %NULL if not found
**/
EXPORT_C LmMessageNode *
lm_message_node_get_child (LmMessageNode *node, const gchar *child_name)
{
LmMessageNode *l;
// g_return_val_if_fail (node != NULL, NULL);
// g_return_val_if_fail (child_name != NULL, NULL);
if(!node || !child_name)
return NULL;
for (l = node->children; l; l = l->next) {
if (strcmp (l->name, child_name) == 0) {
return l;
}
}
return NULL;
}
/**
* lm_message_node_find_child:
* @node: A #LmMessageNode
* @child_name: The name of the child to find
*
* Locates a child among all children of @node. The entire tree will be search
* until a child with name @child_name is located.
*
* Return value: the located child or %NULL if not found
**/
EXPORT_C LmMessageNode *
lm_message_node_find_child (LmMessageNode *node,
const gchar *child_name)
{
LmMessageNode *l;
LmMessageNode *ret_val = NULL;
//g_return_val_if_fail (node != NULL, NULL);
//g_return_val_if_fail (child_name != NULL, NULL);
if(!node || !child_name)
return NULL;
for (l = node->children; l; l = l->next) {
if (strcmp (l->name, child_name) == 0) {
return l;
}
if (l->children) {
ret_val = lm_message_node_find_child (l, child_name);
if (ret_val) {
return ret_val;
}
}
}
return NULL;
}
/**
* lm_message_node_get_raw_mode:
* @node: an #LmMessageNode
*
* Checks if the nodes value should be sent as raw mode.
*
* Return value: %TRUE if nodes value should be sent as is and %FALSE if the value will be escaped before sending.
**/
EXPORT_C gboolean
lm_message_node_get_raw_mode (LmMessageNode *node)
{
//g_return_val_if_fail (node != NULL, FALSE);
return node? node->raw_mode : 0 ;
//return node->raw_mode;
}
/**
* lm_message_node_set_raw_mode:
* @node: an #LmMessageNode
* @raw_mode: boolean specifying if node value should be escaped or not.
*
* Set @raw_mode to %TRUE if you don't want to escape the value. You need to make sure the value is valid XML yourself.
**/
EXPORT_C void
lm_message_node_set_raw_mode (LmMessageNode *node, gboolean raw_mode)
{
//g_return_if_fail (node != NULL);
if(!node)
return;
node->raw_mode = raw_mode;
}
/**
* lm_message_node_ref:
* @node: an #LmMessageNode
*
* Adds a reference to @node.
*
* Return value: the node
**/
EXPORT_C LmMessageNode *
lm_message_node_ref (LmMessageNode *node)
{
//g_return_val_if_fail (node != NULL, NULL);
if(!node)
return NULL;
node->ref_count++;
return node;
}
/**
* lm_message_node_unref:
* @node: an #LmMessageNode
*
* Removes a reference from @node. When no more references are present the
* node is freed. When freed lm_message_node_unref() will be called on all
* children. If caller needs to keep references to the children a call to
* lm_message_node_ref() needs to be done before the call to
*lm_message_unref().
**/
EXPORT_C void
lm_message_node_unref (LmMessageNode *node)
{
//g_return_if_fail (node != NULL);
if(!node)
return;
node->ref_count--;
if (node->ref_count == 0) {
message_node_free (node);
}
}
/**
* lm_message_node_to_string:
* @node: an #LmMessageNode
*
* Returns an XML string representing the node. This is what is sent over the
* wire. This is used internally Loudmouth and is external for debugging
* purposes.
*
* Return value: an XML string representation of @node
**/
EXPORT_C gchar *
lm_message_node_to_string (LmMessageNode *node)
{
GString *ret;
GSList *l;
LmMessageNode *child;
g_return_val_if_fail (node != NULL, NULL);
if (node->name == NULL) {
return g_strdup ("");
}
ret = g_string_new ("<");
g_string_append (ret, node->name);
for (l = node->attributes; l; l = l->next) {
KeyValuePair *kvp = (KeyValuePair *) l->data;
if (node->raw_mode == FALSE) {
gchar *escaped;
escaped = g_markup_escape_text (kvp->value, -1);
g_string_append_printf (ret, " %s=\"%s\"",
kvp->key, escaped);
g_free (escaped);
} else {
g_string_append_printf (ret, " %s=\"%s\"",
kvp->key, kvp->value);
}
}
g_string_append_c (ret, '>');
if (node->value) {
gchar *tmp;
if (node->raw_mode == FALSE) {
tmp = g_markup_escape_text (node->value, -1);
g_string_append (ret, tmp);
g_free (tmp);
} else {
g_string_append (ret, node->value);
}
}
for (child = node->children; child; child = child->next) {
gchar *child_str = lm_message_node_to_string (child);
g_string_append_c (ret, ' ');
g_string_append (ret, child_str);
g_free (child_str);
}
g_string_append_printf (ret, "</%s>\n", node->name);
return g_string_free (ret, FALSE);
}