loudmouth/src/lm-message.c
changeset 0 d0f3a028347a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loudmouth/src/lm-message.c	Tue Feb 02 01:10:06 2010 +0200
@@ -0,0 +1,373 @@
+/* -*- 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.h"
+
+#define PRIV(o) ((LmMessage *)o)->priv
+
+
+#ifdef EMULATOR
+#include "libloudmouth_wsd_solution.h"
+
+GET_STATIC_ARRAY_FROM_TLS(type_names, lm_message, TypeNames)
+  #define type_names (GET_WSD_VAR_NAME(type_names, lm_message, s)())
+  
+GET_STATIC_ARRAY_FROM_TLS(sub_type_names,lm_message,SubTypeNames)
+	#define sub_type_names (GET_WSD_VAR_NAME(sub_type_names,lm_message,s)())	
+
+
+#else
+static struct TypeNames 
+{
+        LmMessageType  type;
+        const gchar   *name;
+} type_names[] = {
+	{ LM_MESSAGE_TYPE_MESSAGE,         "message"         },
+	{ LM_MESSAGE_TYPE_PRESENCE,        "presence"        },
+	{ LM_MESSAGE_TYPE_IQ,              "iq"              },
+	{ LM_MESSAGE_TYPE_STREAM,          "stream:stream"   },
+	{ LM_MESSAGE_TYPE_STREAM_FEATURES, "stream:features" },
+	{ LM_MESSAGE_TYPE_STREAM_ERROR,    "stream:error"    },
+	{ LM_MESSAGE_TYPE_AUTH,            "auth"            },
+	{ LM_MESSAGE_TYPE_CHALLENGE,       "challenge"       },
+	{ LM_MESSAGE_TYPE_RESPONSE,        "response"        },
+	{ LM_MESSAGE_TYPE_SUCCESS,         "success"         },
+	{ LM_MESSAGE_TYPE_FAILURE,         "failure"         },
+	{ LM_MESSAGE_TYPE_PROCEED,         "proceed"         },
+	{ LM_MESSAGE_TYPE_STARTTLS,        "starttls"        },
+	{ LM_MESSAGE_TYPE_UNKNOWN,         NULL              }
+};
+
+static struct SubTypeNames 
+{
+        LmMessageSubType  type;
+        const gchar      *name;
+} sub_type_names[] = {
+	{ LM_MESSAGE_SUB_TYPE_NORMAL,          "normal"        },
+        { LM_MESSAGE_SUB_TYPE_CHAT,            "chat"          },
+	{ LM_MESSAGE_SUB_TYPE_GROUPCHAT,       "groupchat"     },
+	{ LM_MESSAGE_SUB_TYPE_HEADLINE,        "headline"      },
+	{ LM_MESSAGE_SUB_TYPE_UNAVAILABLE,     "unavailable"   },
+        { LM_MESSAGE_SUB_TYPE_PROBE,           "probe"         },
+	{ LM_MESSAGE_SUB_TYPE_SUBSCRIBE,       "subscribe"     },
+	{ LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE,     "unsubscribe"   },
+	{ LM_MESSAGE_SUB_TYPE_SUBSCRIBED,      "subscribed"    },
+	{ LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED,    "unsubscribed"  },
+	{ LM_MESSAGE_SUB_TYPE_GET,             "get"           },
+	{ LM_MESSAGE_SUB_TYPE_SET,             "set"           },
+	{ LM_MESSAGE_SUB_TYPE_RESULT,          "result"        }, 
+	{ LM_MESSAGE_SUB_TYPE_ERROR,           "error"         }
+};
+#endif
+
+struct LmMessagePriv {
+	LmMessageType    type;
+	LmMessageSubType sub_type;
+	gint             ref_count;
+};
+
+static LmMessageType
+message_type_from_string (const gchar *type_str)
+{
+        gint i;
+
+        if (!type_str) {
+                return LM_MESSAGE_TYPE_UNKNOWN;
+        }
+
+        for (i = LM_MESSAGE_TYPE_MESSAGE;
+	     i < LM_MESSAGE_TYPE_UNKNOWN;
+	     ++i) {
+                if (strcmp (type_str, type_names[i].name) == 0) {
+                        return type_names[i].type;
+                }
+        }
+
+        return LM_MESSAGE_TYPE_UNKNOWN;
+}
+
+
+const gchar *
+_lm_message_type_to_string (LmMessageType type)
+{
+        if (type < LM_MESSAGE_TYPE_MESSAGE ||
+            type > LM_MESSAGE_TYPE_STARTTLS) {
+                type = LM_MESSAGE_TYPE_UNKNOWN;
+        }
+
+        return type_names[type].name;
+}
+
+static LmMessageSubType
+message_sub_type_from_string (const gchar *type_str)
+{
+        gint i;
+
+        if (!type_str) {
+                return LM_MESSAGE_SUB_TYPE_NOT_SET;
+        }
+
+        for (i = LM_MESSAGE_SUB_TYPE_NORMAL;
+	     i <= LM_MESSAGE_SUB_TYPE_ERROR;
+	     ++i) {
+                if (g_ascii_strcasecmp (type_str, 
+					sub_type_names[i].name) == 0) {
+                        return i;
+                }
+        }
+
+        return LM_MESSAGE_SUB_TYPE_NOT_SET;
+}
+
+const gchar *
+_lm_message_sub_type_to_string (LmMessageSubType type)
+{
+        if (type < LM_MESSAGE_SUB_TYPE_NORMAL ||
+            type > LM_MESSAGE_SUB_TYPE_ERROR) {
+		return NULL;
+        }
+
+        return sub_type_names[type].name;
+}
+
+static LmMessageSubType
+message_sub_type_when_unset (LmMessageType type) {
+	LmMessageSubType sub_type = LM_MESSAGE_SUB_TYPE_NORMAL;
+
+	switch (type) {
+	case LM_MESSAGE_TYPE_MESSAGE:
+		/* A message without type should be handled like a message with
+		 * type=normal, but we won't set it to that since then the user
+		 * will not know if it's set or not.
+		 */
+		sub_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
+		break;
+	case LM_MESSAGE_TYPE_PRESENCE:
+		sub_type = LM_MESSAGE_SUB_TYPE_AVAILABLE;
+		break;
+	case LM_MESSAGE_TYPE_IQ:
+		sub_type = LM_MESSAGE_SUB_TYPE_GET;
+		break;
+	default:
+		break;
+	}
+
+	return sub_type;
+}
+
+LmMessage *
+_lm_message_new_from_node (LmMessageNode *node)
+{
+	LmMessage        *m;
+	LmMessageType     type;
+	LmMessageSubType  sub_type;
+	const gchar      *sub_type_str;
+	
+	type = message_type_from_string (node->name);
+
+	if (type == LM_MESSAGE_TYPE_UNKNOWN) {
+		return NULL;
+	}
+
+	sub_type_str = lm_message_node_get_attribute (node, "type");
+	if (sub_type_str) {
+		sub_type = message_sub_type_from_string (sub_type_str);
+	} else {
+		sub_type = message_sub_type_when_unset (type);
+	}
+
+	m = g_new0 (LmMessage, 1);
+	m->priv = g_new0 (LmMessagePriv, 1);
+	
+	PRIV(m)->ref_count = 1;
+	PRIV(m)->type = type;
+	PRIV(m)->sub_type = sub_type;
+	
+	m->node = lm_message_node_ref (node);
+	
+	return m;
+}
+
+/**
+ * lm_message_new:
+ * @to: receipient jid
+ * @type: message type
+ * 
+ * Creates a new #LmMessage which can be sent with lm_connection_send() or 
+ * lm_connection_send_with_reply(). If @to is %NULL the message is sent to the
+ * server. The returned message should be unreferenced with lm_message_unref() 
+ * when caller is finished with it.
+ * 
+ * Return value: a newly created #LmMessage
+ **/
+EXPORT_C LmMessage *
+lm_message_new (const gchar *to, LmMessageType type)
+{
+	LmMessage *m;
+	gchar     *id;
+
+	m       = g_new0 (LmMessage, 1);
+	m->priv = g_new0 (LmMessagePriv, 1);
+
+	PRIV(m)->ref_count = 1;
+	PRIV(m)->type      = type;
+	PRIV(m)->sub_type  = message_sub_type_when_unset (type);
+	
+	m->node = _lm_message_node_new (_lm_message_type_to_string (type));
+
+	id = _lm_utils_generate_id ();
+	lm_message_node_set_attribute (m->node, "id", id);
+	g_free (id);
+	
+	if (to) {
+		lm_message_node_set_attribute (m->node, "to", to);
+	}
+
+	if (type == LM_MESSAGE_TYPE_IQ) {
+		lm_message_node_set_attribute (m->node, "type", "get");
+	}
+	
+	return m;
+}
+
+/**
+ * lm_message_new_with_sub_type:
+ * @to: receipient jid
+ * @type: message type
+ * @sub_type: message sub type
+ * 
+ * Creates a new #LmMessage with sub type set. See lm_message_new() for more 
+ * information.
+ * 
+ * Return value: a newly created #LmMessage
+ **/
+EXPORT_C LmMessage *
+lm_message_new_with_sub_type (const gchar      *to,
+			      LmMessageType     type, 
+			      LmMessageSubType  sub_type)
+{
+	LmMessage   *m;
+	const gchar *type_str;
+
+	m = lm_message_new (to, type);
+
+	type_str = _lm_message_sub_type_to_string (sub_type);
+
+	if (type_str) {
+		lm_message_node_set_attributes (m->node,
+						"type", type_str, NULL);
+		PRIV(m)->sub_type = sub_type;
+	}
+
+	return m;
+}
+
+/**
+ * lm_message_get_type:
+ * @message: an #LmMessage
+ * 
+ * Fetches the type of @message.
+ * 
+ * Return value: the message type
+ **/
+EXPORT_C LmMessageType
+lm_message_get_type (LmMessage *message)
+{
+	g_return_val_if_fail (message != NULL, LM_MESSAGE_TYPE_UNKNOWN);
+	
+	return PRIV(message)->type;
+}
+
+/**
+ * lm_message_get_sub_type:
+ * @message: 
+ * 
+ * Fetches the sub type of @message.
+ * 
+ * Return value: the message sub type
+ **/
+EXPORT_C LmMessageSubType
+lm_message_get_sub_type (LmMessage *message)
+{
+	g_return_val_if_fail (message != NULL, LM_MESSAGE_TYPE_UNKNOWN);
+	
+	return PRIV(message)->sub_type;
+}
+
+/**
+ * lm_message_get_node:
+ * @message: an #LmMessage
+ * 
+ * Retrieves the root node from @message.
+ * 
+ * Return value: an #LmMessageNode
+ **/
+EXPORT_C LmMessageNode *
+lm_message_get_node (LmMessage *message)
+{
+	g_return_val_if_fail (message != NULL, NULL);
+	
+	return message->node;
+}
+
+/**
+ * lm_message_ref:
+ * @message: an #LmMessage
+ * 
+ * Adds a reference to @message.
+ * 
+ * Return value: the message
+ **/
+EXPORT_C LmMessage *
+lm_message_ref (LmMessage *message)
+{
+	g_return_val_if_fail (message != NULL, NULL);
+	
+	PRIV(message)->ref_count++;
+	
+	return message;
+}
+
+/**
+ * lm_message_unref:
+ * @message: an #LmMessage
+ * 
+ * Removes a reference from @message. When no more references are present the 
+ * message is freed.
+ **/
+EXPORT_C void
+lm_message_unref (LmMessage *message)
+{
+	//g_return_if_fail (message != NULL);
+	if(message==NULL)
+		return;
+	
+	PRIV(message)->ref_count--;
+	
+	if (PRIV(message)->ref_count == 0) {
+		lm_message_node_unref (message->node);
+		g_free (message->priv);
+		g_free (message);
+	}
+}