diff -r 000000000000 -r 84fdfb0344c9 lm_connection.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lm_connection.c Sun Feb 01 21:28:57 2009 +0200 @@ -0,0 +1,542 @@ + +#include +#include +#include // GDestroyNotify, GMainContext +#include + +#include "util.h" +#include "lm_types.h" +#include "lm_message.h" +#include "lm_message_handler.h" + +/// lm.connection +/// Central module, representing connection to the server. +/// You should create a new connection object, then open it (establish +/// connection), then authenticate to the server. + +/// connection state +/// Stirng, representing current connection state. +/// V: closed, opening, open, authenticating, authenticated +const string2enum_t llm_connection_state[] = { + { "closed", LM_CONNECTION_STATE_CLOSED }, + { "opening", LM_CONNECTION_STATE_OPENING }, + { "open", LM_CONNECTION_STATE_OPEN }, + { "authenticating", LM_CONNECTION_STATE_AUTHENTICATING }, + { "authenticated", LM_CONNECTION_STATE_AUTHENTICATED }, + { NULL, 0 }, // XXX +}; + +/// handler priority +/// String, according to which handler will be placed into one +/// of three handler groups. +/// V: last, normal, first +const string2enum_t llm_handler_priority[] = { + { "last", LM_HANDLER_PRIORITY_LAST }, + { "normal", LM_HANDLER_PRIORITY_NORMAL }, + { "first", LM_HANDLER_PRIORITY_FIRST }, + { NULL, 0 }, // XXX +}; + +/// disconnect reason +/// String, indicating the reason of disconnection occured. +/// V: ok, ping time out, hup, error, resource conflict, invalid xml, unknown, ping_time_out, resource_conflict, invalid_xml +const string2enum_t llm_disconnect_reason[] = { + { "ok", LM_DISCONNECT_REASON_OK }, + { "ping time out", LM_DISCONNECT_REASON_PING_TIME_OUT }, + { "hup", LM_DISCONNECT_REASON_HUP }, + { "error", LM_DISCONNECT_REASON_ERROR }, + { "resource conflict", LM_DISCONNECT_REASON_RESOURCE_CONFLICT }, + { "invalid xml", LM_DISCONNECT_REASON_INVALID_XML }, + { "unknown", LM_DISCONNECT_REASON_UNKNOWN }, + { "ping_time_out", LM_DISCONNECT_REASON_PING_TIME_OUT }, + { "resource_conflict", LM_DISCONNECT_REASON_RESOURCE_CONFLICT }, + { "invalid_xml", LM_DISCONNECT_REASON_INVALID_XML }, + { NULL, 0 }, // XXX +}; + +/// lm.connection.new +/// Creates a new connection (closed). +/// A: string (server name), lightuserdata (C glib main context object, optional) +/// R: lm connection object +static int llm_connection_new (lua_State *L) +{ + const char *server = luaL_checkstring (L, 1); + LmConnection *connection; + if (lua_gettop (L) < 2) { + connection = lm_connection_new (server); + lua_pop (L, 1); + } else { + luaL_argcheck (L, lua_islightuserdata (L, 2), 2, "glib main context lightuserdata expected"); + connection = lm_connection_new_with_context (server, (GMainContext *) lua_touserdata (L, 2)); + lua_pop (L, 2); + } + llm_connection_bless (L, connection); + lm_connection_unref (connection); + return 1; +} + +/// lm.connection.bless +/// Blesses given pointer to lm connection object. +/// Note: it adds a reference to connection. +/// A: lightuserdata (C lm connection object) +/// R: lm connection object +static int llm_connection_bless_lua (lua_State *L) +{ + luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "loudmouth connection lightuserdata expected"); + llm_connection_bless (L, (LmConnection *) lua_touserdata (L, 1)); + lua_remove (L, -2); + return 1; +} + +/// connection callback function +/// User function, that will be called on connection establishment operation end, +/// eg. successful/unsuccessful opening or authentication. +/// A: lm connection object, boolean (success) +static void llm_connection_callback (LmConnection *connection, int success, llm_callback_t *cb) +{ + lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); + llm_connection_bless (cb->L, connection); + // XXX lm_connection_unref (connection); + lua_pushboolean (cb->L, success); + if (lua_pcall (cb->L, 2, 0, 0)) { + // XXX lua_error (cb->L); + lua_pop (cb->L, 1); + return; + } +} + +/// connection:open +/// Opens connection to the server and then calls callback function. +/// A: connection callback function +/// R: boolean (success) +static int llm_connection_open (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + llm_callback_t *cb; + luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); + + cb = luaL_malloc (L, sizeof (llm_callback_t)); + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + cb->L = L; + + lua_pushboolean (L, lm_connection_open (object->connection, + (LmResultFunction)llm_connection_callback, cb, + (GDestroyNotify)llm_callback_destroy, NULL)); + lua_remove (L, -2); + return 1; +} + +/// connection:authenticate +/// Tries to authenticate against opened connection, then calls callback function. +/// A: string (username), string (password), string (resource), connection callback function +/// R: boolean (success) +static int llm_connection_authenticate (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + const char *username = luaL_checkstring (L, 2); + const char *password = luaL_checkstring (L, 3); + const char *resource = luaL_checkstring (L, 4); + llm_callback_t *cb; + int status; + luaL_argcheck (L, lua_isfunction (L, 5), 5, "function expected"); + + cb = luaL_malloc (L, sizeof (llm_callback_t)); + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + cb->L = L; + + status = lm_connection_authenticate (object->connection, username, password, resource, + (LmResultFunction)llm_connection_callback, cb, + (GDestroyNotify)llm_callback_destroy, NULL); + lua_pop (L, 4); + lua_pushboolean (L, status); + return 1; +} + +/// connection:port +/// Gets or sets server port to connect. +/// A: integer (optional) +/// R: integer (when called with no args) +static int llm_connection_port (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + lm_connection_set_port (object->connection, luaL_checkint (L, 2)); + lua_pop (L, 2); + return 0; + } else { // Get + lua_pushnumber (L, lm_connection_get_port (object->connection)); + lua_remove (L, -2); + return 1; + } +} + +/// connection:server +/// Gets or sets server to connect to. +/// A: string (optional, server name) +/// R: string (when called with no args) +static int llm_connection_server (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + lm_connection_set_server (object->connection, luaL_checkstring (L, 2)); + lua_pop (L, 2); + return 0; + } else { // Get + lua_pushstring (L, lm_connection_get_server (object->connection)); + lua_remove (L, -2); + return 1; + } +} + +/// connection:jid +/// Gets or sets jid for connection. +/// A: string (optional) +/// R: string (when called with no args) +static int llm_connection_jid (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + lm_connection_set_jid (object->connection, luaL_checkstring (L, 2)); + lua_pop (L, 2); + return 0; + } else { // Get + lua_pushstring (L, lm_connection_get_jid (object->connection)); + lua_remove (L, -2); + return 1; + } +} + +/// connection:keep_alive_rate +/// Gets or sets keep alive packets rate for connection. +/// Note, that on some platforms there is no get function even in +/// loudmouth versions, that should have it according to documentation. +/// integer (optional, seconds) +/// integer (when called with no args) +static int llm_connection_keep_alive_rate (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + lm_connection_set_keep_alive_rate (object->connection, luaL_checkint (L, 2)); + lua_pop (L, 2); + return 0; + } else { // Get +#ifdef HAVE_LM_CONNECTION_GET_KEEP_ALIVE_RATE + lua_pushnumber (L, lm_connection_get_keep_alive_rate (object->connection)); + lua_remove (L, -2); +#else + lua_pop (L, 1); + lua_pushstring (L, "Sorry, your loudmouth have no get_keep_alive_rate"); + lua_error (L); +#endif + return 1; + } +} + +/// connection:proxy +/// Gets or sets proxy server for connection. +/// A: lm proxy object (optional) +/// R: lm proxy object or nil (when called with no args) +static int llm_connection_proxy (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + llm_proxy_t *proxy = luaL_checklm_proxy (L, 2); + lm_connection_set_proxy (object->connection, proxy->proxy); + lua_pop (L, 2); + return 0; + } else { // Get + LmProxy *proxy = lm_connection_get_proxy (object->connection); + lua_pop (L, 1); + if (proxy) { + llm_proxy_bless (L, proxy); + // XXX lm_proxy_unref (proxy); + } else + lua_pushnil (L); + return 1; + } +} + +/// connection:ssl +/// Gets or sets ssl object for connection. +/// A: lm ssl object (optional) +/// R: lm ssl object or nil (when called with no args) +static int llm_connection_ssl (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + if (lua_gettop (L) > 1) { // Set + llm_ssl_t *ssl = luaL_checklm_ssl (L, 2); + lm_connection_set_ssl (object->connection, ssl->ssl); + lua_pop (L, 2); + return 0; + } else { // Get + LmSSL *ssl = lm_connection_get_ssl (object->connection); + lua_pop (L, 1); + if (ssl) { + llm_ssl_bless (L, ssl); + // XXX lm_ssl_unref (ssl); + } else + lua_pushnil (L); + return 1; + } +} + +/// connection:close +/// Close connection. +/// R: boolean (success) +static int llm_connection_close (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + lua_pushboolean (L, lm_connection_close (object->connection, NULL)); + lua_remove (L, -2); + return 1; +} + +/// connection:status +/// Returns string, describing connection state. +/// R: connection state +static int llm_connection_status (lua_State *L) +{ + llm_connection_t *connection = luaL_checklm_connection (L, 1); + luaL_pushenum (L, lm_connection_get_state (connection->connection), llm_connection_state) + lua_remove (L, -2); + return 1; +} + +/// connection:send +/// Sends message (object). If specified, handler function will be called upon +/// receiving of response to that message. +/// Handler function can be either message handler object or just a function. +/// A: lm message object, message handler callback function or lm message handler object (optional) +/// R: boolean (success) +static int llm_connection_send (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + llm_message_t *message = luaL_checklm_message (L, 2); + int status; + if (lua_gettop (L) < 3) // Send + status = lm_connection_send (object->connection, message->message, NULL); + else if (lua_isfunction (L, 3)) { // Send w/reply, func + llm_callback_t *cb = luaL_malloc (L, sizeof (llm_callback_t)); + LmMessageHandler *handler; + + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + cb->L = L; + handler = lm_message_handler_new ( + (LmHandleMessageFunction)llm_message_handler_callback, + cb, (GDestroyNotify)llm_callback_destroy); + status = lm_connection_send_with_reply (object->connection, + message->message, handler, NULL); + lm_message_handler_unref (handler); + } else { // Send w/reply, handler + llm_message_handler_t *handler = luaL_checklm_message_handler (L, 3); + status = lm_connection_send_with_reply (object->connection, message->message, + handler->message_handler, NULL); + lua_pop (L, 1); + }; + lua_pop (L, 2); + lua_pushboolean (L, status); + return 1; +} + +/// connection:send_raw +/// Sends arbitrary string to opened connection. +/// A: string +/// R: boolean (status) +static int llm_connection_send_raw (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + const char *string = luaL_checkstring (L, 2); + int status = lm_connection_send_raw (object->connection, string, NULL); + lua_pop (L, 2); + lua_pushboolean (L, status); + return 1; +} + +/// connection:handler +/// Registers or unregisters handler function for a given type of messages. +/// To unregister handler, omit the priority argument. +/// Handler function can be specified as plain function or message handler object. +/// Though, you can unregister only a message handler object. +/// A: message handler callback function or lm message handler object, message type, handler priority (optional) +static int llm_connection_handler (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + int type = luaL_checkenum (L, 3, llm_message_type); + + if (lua_gettop (L) > 3) { // Register + int priority = luaL_checkenum (L, 4, llm_handler_priority); + + if (lua_isfunction (L, 2)) { // Function + LmMessageHandler *handler; + llm_callback_t *cb = luaL_malloc (L, sizeof (llm_callback_t)); + lua_pushvalue (L, 2); + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + cb->L = L; + + handler = lm_message_handler_new ( + (LmHandleMessageFunction)llm_message_handler_callback, + cb, (GDestroyNotify)llm_callback_destroy); + lm_connection_register_message_handler (object->connection, + handler, type, priority); + lm_message_handler_unref (handler); + } else { // Object + llm_message_handler_t *handler = luaL_checklm_message_handler (L, 2); + lm_connection_register_message_handler (object->connection, + handler->message_handler, + type, priority); + } + lua_pop (L, 1); + } else { // Unregister + llm_message_handler_t *handler = luaL_checklm_message_handler (L, 2); + lm_connection_unregister_message_handler (object->connection, + handler->message_handler, + type); + } + lua_pop (L, 2); + return 0; +} + +/// disconnect callback function +/// Function, that will be called when disconnection occurs. +/// A: lm connection object, disconnect reason +void llm_disconnect_callback (LmConnection *connection, LmDisconnectReason reason, llm_callback_t *cb) +{ + lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); + llm_connection_bless (cb->L, connection); + // XXX lm_connection_unref (connection); + luaL_pushenum (cb->L, reason, llm_disconnect_reason); + if (lua_pcall (cb->L, 2, 0, 0)) { + // XXX lua_error (cb->L); + lua_pop (cb->L, 1); + return; + } +} + +/// connection:ondisconnect +/// Sets callback, that will be called on connection disconnect. +/// A: disconnect callback function +static int llm_connection_ondisconnect (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + llm_callback_t *cb; + luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); + + cb = luaL_malloc (L, sizeof (llm_callback_t)); + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + cb->L = L; + + lm_connection_set_disconnect_function (object->connection, + (LmDisconnectFunction)llm_disconnect_callback, + cb, (GDestroyNotify)llm_callback_destroy); + lua_pop (L, 1); + return 0; +} + +/// connection:open_wait +/// Synchronous open call, that will block until connection will be opened. +/// R: boolean (success) +static int llm_connection_open_wait (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + lua_pushboolean (L, lm_connection_open_and_block (object->connection, NULL)); + lua_remove (L, -2); + return 1; +} + +/// connection:authenticate_wait +/// Synchronous authentication call, that will wait until the end of authentication. +/// A: string (username), string (password), string (resource) +/// R: boolean (success) +static int llm_connection_authenticate_wait (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + const char *username = luaL_checkstring (L, 2); + const char *password = luaL_checkstring (L, 3); + const char *resource = luaL_checkstring (L, 4); + int status = lm_connection_authenticate_and_block (object->connection, username, + password, resource, NULL); + lua_pop (L, 4); + lua_pushboolean (L, status); + return 1; +} + +/// connection:send_wait +/// Synchronous call, that will send message and wait for reply to it. +/// A: lm message object +/// R: lm message object or nil +static int llm_connection_send_wait (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + llm_message_t *message = luaL_checklm_message (L, 2); + LmMessage *new = lm_connection_send_with_reply_and_block (object->connection, + message->message, NULL); + lua_pop (L, 2); + if (!new) + lua_pushnil (L); + else { + llm_message_bless (L, new); + lm_message_unref (new); // XXX + } + return 1; +} + +/// connection:pointer +/// Returns pointer to underlying C loudmouth structure. +/// R: lightuserdata +static int llm_connection_pointer (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + lua_pushlightuserdata (L, object->connection); + lua_remove (L, -2); + return 1; +} + +static int llm_connection_gc (lua_State *L) +{ + llm_connection_t *object = luaL_checklm_connection (L, 1); + lm_connection_unref (object->connection); + lua_pop (L, 1); + return 0; +} + +static const luaL_Reg llm_connection_reg_f[] = { + { "new", llm_connection_new }, + { "bless", llm_connection_bless_lua }, + { NULL, NULL }, +}; + +static const luaL_Reg llm_connection_reg_m[] = { + { "open", llm_connection_open }, + { "close", llm_connection_close }, + { "authenticate", llm_connection_authenticate }, + { "port", llm_connection_port }, + { "server", llm_connection_server }, + { "jid", llm_connection_jid }, + { "keep_alive_rate", llm_connection_keep_alive_rate }, + { "state", llm_connection_status }, + { "proxy", llm_connection_proxy }, + { "ssl", llm_connection_ssl }, + { "send", llm_connection_send }, + { "send_raw", llm_connection_send_raw }, + { "handler", llm_connection_handler }, + { "ondisconnect", llm_connection_ondisconnect }, + { "open_wait", llm_connection_open_wait }, + { "authenticate_wait", llm_connection_authenticate_wait }, + { "send_wait", llm_connection_send_wait }, + { "pointer", llm_connection_pointer }, + { "__gc", llm_connection_gc }, + { NULL, NULL }, +}; + +int luaopen_lm_connection (lua_State *L) +{ + luaL_newmetatable (L, "loudmouth.connection"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + luaL_register (L, NULL, llm_connection_reg_m); + lua_pop (L, 1); + luaL_register (L, "lm.connection", llm_connection_reg_f); + return 1; +} +