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: