isbear@23:
isbear@23: /* Copyright 2009 Myhailo Danylenko
isbear@23:
isbear@23: This file is part of lua-lm.
isbear@23:
isbear@23: lua-lm is free software: you can redistribute it and/or modify
isbear@23: it under the terms of the GNU General Public License as published by
isbear@23: the Free Software Foundation, either version 2 of the License, or
isbear@23: (at your option) any later version.
isbear@23:
isbear@23: This program is distributed in the hope that it will be useful,
isbear@23: but WITHOUT ANY WARRANTY; without even the implied warranty of
isbear@23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
isbear@23: GNU General Public License for more details.
isbear@23:
isbear@23: You should have received a copy of the GNU General Public License
isbear@23: along with this program. If not, see . */
isbear@0:
isbear@0: #include
isbear@0: #include
isbear@0: #include
isbear@17: #include
isbear@0:
isbear@6: #include "config.h"
isbear@0: #include "util.h"
isbear@0: #include "lm_types.h"
isbear@7: #include "lm_message_node.h"
isbear@0:
isbear@0: /// lm.message
isbear@0: /// Module, representing individual message.
isbear@0: /// Message have a type and optionally a sub type.
isbear@8: /// Message have a set common methods with message node,
isbear@39: /// these are name, next, prev, parent, value, child, children,
isbear@8: /// find_child, attribute, raw, xml and path. They just save
isbear@8: /// you typing :node() each time and save memory for
isbear@8: /// one node object.
isbear@0:
isbear@0: /// message type
isbear@0: /// Message type (root tag type).
isbear@4: /// G:
isbear@11: const string2enum_t type_lm_message[] = {
isbear@0: { "message", LM_MESSAGE_TYPE_MESSAGE },
isbear@0: { "presence", LM_MESSAGE_TYPE_PRESENCE },
isbear@0: { "iq", LM_MESSAGE_TYPE_IQ },
isbear@0: { "stream", LM_MESSAGE_TYPE_STREAM },
isbear@0: { "stream error", LM_MESSAGE_TYPE_STREAM_ERROR },
isbear@0: { "stream features", LM_MESSAGE_TYPE_STREAM_FEATURES },
isbear@0: { "auth", LM_MESSAGE_TYPE_AUTH },
isbear@0: { "challenge", LM_MESSAGE_TYPE_CHALLENGE },
isbear@0: { "response", LM_MESSAGE_TYPE_RESPONSE },
isbear@0: { "success", LM_MESSAGE_TYPE_SUCCESS },
isbear@0: { "failure", LM_MESSAGE_TYPE_FAILURE },
isbear@0: { "proceed", LM_MESSAGE_TYPE_PROCEED },
isbear@0: { "starttls", LM_MESSAGE_TYPE_STARTTLS },
isbear@0: { "unknown", LM_MESSAGE_TYPE_UNKNOWN },
isbear@0: { "stream:stream", LM_MESSAGE_TYPE_STREAM },
isbear@0: { "stream:error", LM_MESSAGE_TYPE_STREAM_ERROR },
isbear@0: { "stream:features", LM_MESSAGE_TYPE_STREAM_FEATURES },
isbear@6: { "stream_error", LM_MESSAGE_TYPE_STREAM_ERROR },
isbear@6: { "stream_features", LM_MESSAGE_TYPE_STREAM_FEATURES },
isbear@17: { NULL, LM_MESSAGE_TYPE_MESSAGE },
isbear@0: };
isbear@0:
isbear@0: /// message sub type
isbear@0: /// Message subtype, not all combinations of type and subtype are possible.
isbear@4: /// G:
isbear@11: const string2enum_t sub_type_lm_message[] = {
isbear@0: { "not set", LM_MESSAGE_SUB_TYPE_NOT_SET },
isbear@0: { "available", LM_MESSAGE_SUB_TYPE_AVAILABLE },
isbear@0: { "normal", LM_MESSAGE_SUB_TYPE_NORMAL },
isbear@0: { "chat", LM_MESSAGE_SUB_TYPE_CHAT },
isbear@0: { "groupchat", LM_MESSAGE_SUB_TYPE_GROUPCHAT },
isbear@0: { "headline", LM_MESSAGE_SUB_TYPE_HEADLINE },
isbear@0: { "unavailable", LM_MESSAGE_SUB_TYPE_UNAVAILABLE },
isbear@0: { "probe", LM_MESSAGE_SUB_TYPE_PROBE },
isbear@0: { "subscribe", LM_MESSAGE_SUB_TYPE_SUBSCRIBE },
isbear@0: { "unsubscribe", LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE },
isbear@0: { "subscribed", LM_MESSAGE_SUB_TYPE_SUBSCRIBED },
isbear@0: { "unsubscribed", LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED },
isbear@0: { "get", LM_MESSAGE_SUB_TYPE_GET },
isbear@0: { "set", LM_MESSAGE_SUB_TYPE_SET },
isbear@0: { "result", LM_MESSAGE_SUB_TYPE_RESULT },
isbear@0: { "error", LM_MESSAGE_SUB_TYPE_ERROR },
isbear@0: { "not_set", LM_MESSAGE_SUB_TYPE_NOT_SET },
isbear@17: { NULL, LM_MESSAGE_SUB_TYPE_NOT_SET },
isbear@0: };
isbear@0:
isbear@0: /// lm.message.new
isbear@0: /// Creates new message object.
isbear@12: /// Note: you can specify nil as to argument to send message to server.
isbear@0: /// A: string (to), message type, message sub type (optional)
isbear@0: /// R: lm message object
isbear@11: static int new_lm_message (lua_State *L)
isbear@0: {
isbear@12: const char *to = NULL;
isbear@11: int type = luaL_checkenum (L, 2, type_lm_message);
isbear@0: LmMessage *message;
isbear@12:
isbear@12: if (lua_type (L, 1) != LUA_TNIL)
isbear@12: to = luaL_checkstring (L, 1);
isbear@6: if (lua_gettop (L) > 2)
isbear@0: message = lm_message_new_with_sub_type (to, type,
isbear@11: luaL_checkenum (L, 3, sub_type_lm_message));
isbear@6: else
isbear@0: message = lm_message_new (to, type);
isbear@11: bless_lm_message (L, message);
isbear@0: lm_message_unref (message);
isbear@38: D ("Message %p created", message);
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@17: /// message table
isbear@17: /// Table describes xml structure of the message, the only exception is mtype key of root table.
isbear@17: /// mtype is a string of form "-", eg "iq-set".
isbear@17: /// Best way to learn how this table is organized, is just to look at next example:
isbear@17: /// [ lm.message.create { mtype = 'iq-result', to = 'foo@bar.xyz',
isbear@17: /// command = { xmlns = 'http://jabber.org/protocol/commands', node = 'http://jabber.org/protocol/rc#set-status', status = 'executing', sessionid = 'set-status:aaa3',
isbear@17: /// x = { xmlns = 'jabber:x:data', type = 'form',
isbear@17: /// title = { "Change Status" },
isbear@17: /// instructions = { "Choose the status and status message" },
isbear@17: /// field = {{ type = 'hidden', var = 'FORM_TYPE',
isbear@17: /// value = { "http://jabber.org/protocol/rc" },
isbear@17: /// },{ type = 'list-single', label = 'Status', var = 'status',
isbear@17: /// required = { },
isbear@17: /// value = { "online" },
isbear@17: /// option = {{ label = 'Chat',
isbear@17: /// value = { "chat" },
isbear@17: /// },{ label = 'Online',
isbear@17: /// value = { "online" },
isbear@17: /// },{ label = 'Away',
isbear@17: /// value = { "away" },
isbear@17: /// },{ label = 'Extended Away',
isbear@17: /// value = { "xa" },
isbear@17: /// },{ label = 'Do Not Disturb',
isbear@17: /// value = { "dnd" },
isbear@17: /// },{ label = 'Invisible',
isbear@17: /// value = { "invisible" },
isbear@17: /// },{ label = 'Offline',
isbear@17: /// value = { "offline" },
isbear@17: /// }},
isbear@17: /// },{ type = 'text-single', label = 'Priority', var = 'status-priority',
isbear@17: /// value = { "5" },
isbear@17: /// },{ type = 'text-multi', label = 'Message', var = 'status-message' }},
isbear@17: /// },
isbear@17: /// },
isbear@17: /// }
isbear@17: /// ]
isbear@17: static void fill_lm_node (lua_State *L, LmMessageNode *node, int index)
isbear@17: {
isbear@17: int top = lua_gettop (L); // 0
isbear@17: for (lua_pushnil (L); lua_next (L, index) != 0; lua_pop (L, lua_gettop (L) - top - 1)) // 2 value
isbear@17: if (lua_type (L, top + 2) == LUA_TTABLE) {
isbear@17: const char *name = lua_tostring (L, top + 1);
isbear@17: lua_pushinteger (L, 1); // 3 1
isbear@17: lua_gettable (L, top + 2); // 3 value[1]
isbear@17: if (lua_type (L, top + 3) == LUA_TTABLE) {
isbear@17: int i = 1;
isbear@17: do {
isbear@17: fill_lm_node (L, lm_message_node_add_child (node, name, NULL), top + 3);
isbear@17: lua_pop (L, 1); // 2 value
isbear@17: lua_pushinteger (L, ++i); // 3 i
isbear@17: lua_gettable (L, top + 2); // 3 value[i]
isbear@17: } while (lua_type (L, top + 3) == LUA_TTABLE);
isbear@17: } else
isbear@17: fill_lm_node (L, lm_message_node_add_child (node, name, NULL), top + 2);
isbear@17: } else if (lua_type (L, top + 1) == LUA_TNUMBER && lua_tointeger (L, top + 1) == 1)
isbear@17: lm_message_node_set_value (node, lua_tostring (L, top + 2));
isbear@17: else
isbear@17: lm_message_node_set_attribute (node, lua_tostring (L, top + 1), lua_tostring (L, top + 2));
isbear@17: }
isbear@17:
isbear@17: /// lm.message.create
isbear@17: /// Creates new message object and fills it from message table.
isbear@17: /// Note, that table fields are not checked for their types, so, on wrong input results may be undefined.
isbear@17: /// A: message table
isbear@17: /// R: lm message object
isbear@17: static int create_lm_message (lua_State *L)
isbear@17: {
isbear@17: const char *mtype;
isbear@37: const char *st = NULL;
isbear@17: const char *to = NULL;
isbear@17: LmMessage *message;
isbear@17: luaL_checktype (L, 1, LUA_TTABLE);
isbear@17:
isbear@17: lua_getfield (L, 1, "mtype");
isbear@17: mtype = lua_tostring (L, -1);
isbear@37: if (mtype)
isbear@37: st = strchr (mtype, '-');
isbear@17:
isbear@17: lua_getfield (L, 1, "to");
isbear@17: if (lua_type (L, -1) == LUA_TSTRING)
isbear@17: to = lua_tostring (L, -1);
isbear@17:
isbear@17: if (st) {
isbear@17: LmMessageType mt;
isbear@17: lua_pushlstring (L, mtype, st - mtype);
isbear@17: mt = luaL_checkenum (L, -1, type_lm_message);
isbear@17: lua_pop (L, 1);
isbear@17: message = lm_message_new_with_sub_type (to, mt, string2enum (st + 1, sub_type_lm_message));
isbear@17: } else
isbear@17: message = lm_message_new (to, luaL_checkenum (L, -2, type_lm_message));
isbear@17:
isbear@17: lua_pop (L, 2);
isbear@17: lua_pushnil (L);
isbear@17: lua_setfield (L, 1, "mtype");
isbear@17: lua_pushnil (L);
isbear@17: lua_setfield (L, 1, "to");
isbear@17:
isbear@17: fill_lm_node (L, lm_message_get_node (message), 1);
isbear@17:
isbear@17: bless_lm_message (L, message);
isbear@17: lm_message_unref (message);
isbear@38: D ("Message %p created", message);
isbear@17:
isbear@17: return 1;
isbear@17: }
isbear@17:
isbear@0: /// lm.message.bless
isbear@0: /// Blesses given pointer to lm message object.
isbear@0: /// A: lightuserdata (C lm message object)
isbear@0: /// R: lm message object
isbear@11: static int bless_lua_lm_message (lua_State *L)
isbear@0: {
isbear@0: luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm message lightuserdata expected");
isbear@11: bless_lm_message (L, lua_touserdata (L, 1));
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@0: /// message:node
isbear@0: /// Returns root node object of message.
isbear@0: /// R: lm message node object
isbear@11: static int node_lm_message (lua_State *L)
isbear@0: {
isbear@0: llm_message_t *object = luaL_checklm_message (L, 1);
isbear@0: LmMessageNode *node = lm_message_get_node (object->message);
isbear@11: bless_lm_node (L, node);
isbear@0: // XXX lm_message_node_unref (node);
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@0: /// message:type
isbear@0: /// Returns two strings: message type and message sub type.
isbear@0: /// R: message type, message sub type
isbear@11: static int kind_lm_message (lua_State *L)
isbear@0: {
isbear@0: llm_message_t *object = luaL_checklm_message (L, 1);
isbear@11: luaL_pushenum (L, lm_message_get_type (object->message), type_lm_message);
isbear@11: luaL_pushenum (L, lm_message_get_sub_type (object->message), sub_type_lm_message);
isbear@0: return 2;
isbear@0: }
isbear@0:
isbear@0: /// message:pointer
isbear@0: /// Returns pointer to underlying C loudmouth structure.
isbear@0: /// R: lightuserdata
isbear@11: static int pointer_lm_message (lua_State *L)
isbear@0: {
isbear@0: llm_message_t *object = luaL_checklm_message (L, 1);
isbear@0: lua_pushlightuserdata (L, object->message);
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@11: static int gc_lm_message (lua_State *L)
isbear@0: {
isbear@6: llm_message_t *message = luaL_checklm_message (L, 1);
isbear@38: D ("Message %p gc called", message);
isbear@6: lm_message_unref (message->message);
isbear@0: return 0;
isbear@0: }
isbear@0:
isbear@11: static const luaL_Reg reg_f_lm_message[] = {
isbear@17: { "new", new_lm_message },
isbear@17: { "create", create_lm_message },
isbear@17: { "bless", bless_lua_lm_message },
isbear@17: { NULL, NULL },
isbear@0: };
isbear@0:
isbear@11: static const luaL_Reg reg_m_lm_message[] = {
isbear@12: { "node", node_lm_message },
isbear@12: { "type", kind_lm_message },
isbear@7: // These methods are common for message and message node
isbear@17: { "name", name_lm_node },
isbear@11: { "next", next_lm_node },
isbear@11: { "prev", prev_lm_node },
isbear@11: { "parent", parent_lm_node },
isbear@11: { "value", value_lm_node },
isbear@11: { "child", child_lm_node },
isbear@39: { "children", children_lm_node },
isbear@11: { "find_child", find_child_lm_node },
isbear@11: { "attribute", attribute_lm_node },
isbear@11: { "raw", raw_lm_node },
isbear@11: { "xml", xml_lm_node },
isbear@11: { "path", path_lm_node },
isbear@7: // End common methods
isbear@12: { "pointer", pointer_lm_message },
isbear@12: { "__gc", gc_lm_message },
isbear@12: { NULL, NULL },
isbear@0: };
isbear@0:
isbear@0: int luaopen_lm_message (lua_State *L)
isbear@0: {
isbear@0: luaL_newmetatable (L, "loudmouth.message");
isbear@19: lua_pushvalue (L, -1);
isbear@19: lua_setfield (L, -2, "__index");
isbear@54: luaL_setfuncs (L, reg_m_lm_message, 0);
isbear@0: lua_pop (L, 1);
isbear@16: lua_newtable (L); // XXX we can specify here exact amount of fields
isbear@54: luaL_setfuncs (L, reg_f_lm_message, 0);
isbear@0: return 1;
isbear@0: }
isbear@0: