/* diversity-xmpp.c - DiversityXmpp * * Copyright 2008 OpenMoko, Inc. * Authored by Daniel Willmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU 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 #include #include #include #include #include #include #include #include enum { STATE_CHANGED, USERINFO_NEEDED, SERVERINFO_NEEDED, GEOLOC_MESSAGE_RECEIVED, LAST_SIGNAL }; #define DIVERSITY_XMPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), DIVERSITY_TYPE_XMPP, DiversityXmppPrivate)) typedef struct _DiversityXmppPrivate DiversityXmppPrivate; struct _DiversityXmppPrivate { gchar *jid; gchar *username; gchar *password; gchar *resource; gchar *server; gint port; guint presence; gchar *statusmsg; LmConnection *connection; LmMessageHandler *msghandle; XmppDisco *disco; XmppCaps *caps; }; static guint xmpp_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE(DiversityXmpp, diversity_xmpp, DIVERSITY_TYPE_EQUIPMENT); static void diversity_xmpp_dispose(GObject *obj) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(obj); ((GObjectClass *) diversity_xmpp_parent_class)->dispose(obj); } static void diversity_xmpp_finalize(GObject *obj) { GError *error = NULL; DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(obj); if (priv->username) { g_free(priv->username); priv->username = NULL; } if (priv->password) { g_free(priv->password); priv->password = NULL; } if (priv->resource) { g_free(priv->resource); priv->resource = NULL; } if (priv->server) { g_free(priv->server); priv->server = NULL; } /* Unregister message handlers */ lm_connection_unregister_message_handler(priv->connection, priv->msghandle, LM_MESSAGE_TYPE_PRESENCE); lm_message_handler_unref(priv->msghandle); priv->msghandle = NULL; g_object_unref(priv->caps); g_object_unref(priv->disco); if (!lm_connection_close(priv->connection, &error)) { g_debug("lm_connection_close failed: %s", error->message); } lm_connection_unref(priv->connection); ((GObjectClass *) diversity_xmpp_parent_class)->finalize(obj); } static gboolean diversity_xmpp_equip(DiversityEquipment *eqp) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(eqp); return TRUE; } static void diversity_xmpp_unequip(DiversityEquipment *eqp) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(eqp); } static void diversity_xmpp_class_init(DiversityXmppClass *klass) { GObjectClass *o_class = (GObjectClass *) klass; DiversityEquipmentClass *eqp_class = (DiversityEquipmentClass *) klass; o_class->dispose = diversity_xmpp_dispose; o_class->finalize = diversity_xmpp_finalize; eqp_class->equip = diversity_xmpp_equip; eqp_class->unequip = diversity_xmpp_unequip; g_type_class_add_private(klass, sizeof(DiversityXmppPrivate)); xmpp_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(DiversityXmppClass, state_changed), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); xmpp_signals[USERINFO_NEEDED] = g_signal_new("userinfo-needed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(DiversityXmppClass, userinfo_needed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); xmpp_signals[SERVERINFO_NEEDED] = g_signal_new("serverinfo-needed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(DiversityXmppClass, serverinfo_needed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); xmpp_signals[GEOLOC_MESSAGE_RECEIVED] = g_signal_new("geoloc-message-received", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(DiversityXmppClass, geoloc_message_received), NULL, NULL, xmpp_marshal_VOID__STRING_DOUBLE_DOUBLE_DOUBLE_DOUBLE, G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); } static void diversity_xmpp_init(DiversityXmpp *xmpp) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); priv->jid = NULL; priv->username = NULL; priv->password = NULL; priv->resource = NULL; priv->server = NULL; priv->port = 5222; priv->presence = XMPP_PRESENCE_OFFLINE; priv->statusmsg = NULL; } static LmHandlerResult xmpp_handle_msg(LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer user_data); DiversityXmpp *diversity_xmpp_new(const gchar *name, GError **error) { DiversityXmpp *xmpp; DiversityXmppPrivate *priv; XmppDiscoinfo *discoinfo; if (!name) return NULL; xmpp = g_object_new(DIVERSITY_TYPE_XMPP, "name", name, NULL); priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); priv->connection = lm_connection_new (NULL); priv->disco = xmpp_disco_new(priv->connection, NULL); xmpp_geoloc_init(xmpp); priv->caps = xmpp_caps_new(xmpp, priv->disco, NULL); /* Message handlers for various types of messages */ priv->msghandle = lm_message_handler_new(xmpp_handle_msg, NULL, NULL); lm_connection_register_message_handler(priv->connection, priv->msghandle, LM_MESSAGE_TYPE_MESSAGE, LM_HANDLER_PRIORITY_NORMAL); return xmpp; } void diversity_xmpp_userinfo_set(DiversityXmpp *xmpp, struct DiversityXmppUserInfo *userinfo) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); if (userinfo->jid) { if (priv->jid) g_free(priv->jid); priv->jid = g_strdup(userinfo->jid); } if (userinfo->username) { if (priv->username) g_free(priv->username); priv->username = g_strdup(userinfo->username); } if (userinfo->password) { if (priv->password) g_free(priv->password); priv->password = g_strdup(userinfo->password); } if (userinfo->resource) { if (priv->resource) g_free(priv->resource); priv->resource = g_strdup(userinfo->resource); } } void diversity_xmpp_serverinfo_set(DiversityXmpp *xmpp, struct DiversityXmppServerInfo *serverinfo) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); if (serverinfo->server) { if (priv->server) g_free(priv->server); priv->server = g_strdup(serverinfo->server); } if (serverinfo->port) { priv->port = serverinfo->port; } } static void diversity_xmpp_query_roster(DiversityXmpp *xmpp) { LmMessage *rosterqry; LmMessageNode *query; DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); rosterqry = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); query = lm_message_node_add_child (rosterqry->node, "query", NULL); lm_message_node_set_attributes (query, "xmlns", "jabber:iq:roster", NULL); lm_connection_send_with_reply(priv->connection, rosterqry, priv->msghandle, NULL); lm_message_unref(rosterqry); } static void diversity_xmpp_authenticated(LmConnection *connection, gboolean success, DiversityXmpp *xmpp) { if (!success) { /* TODO: Send authentication failed signal */ g_debug("Authentication failed"); return; } g_debug("Connection authenticated"); diversity_xmpp_query_roster(xmpp); diversity_xmpp_presence_set(xmpp, XMPP_PRESENCE_ONLINE, NULL); } static void diversity_xmpp_authenticate(LmConnection *connection, gboolean success, DiversityXmpp *xmpp) { GError *error = NULL; DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); if (!success) { g_debug("Connection failed"); return; } g_debug("Authenticating connection: %s/%s", priv->username, priv->resource); if (!lm_connection_authenticate(connection, priv->username, priv->password, priv->resource, (LmResultFunction)diversity_xmpp_authenticated, xmpp, NULL, &error)) { /* TODO: Return a unauthenticated Signal */ g_debug("lm_connection_authenticate failed: %s", error->message); } } int diversity_xmpp_connect(DiversityXmpp *xmpp) { GError *error = NULL; DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); if (!priv->jid) { priv->jid = g_new0(char, strlen(priv->username)+ strlen(priv->server)+2); g_snprintf(priv->jid, strlen(priv->username)+strlen(priv->server)+2, "%s@%s", priv->username, priv->server); } lm_connection_set_jid(priv->connection, priv->jid); if (priv->port) lm_connection_set_port(priv->connection, priv->port); if (priv->server) lm_connection_set_server(priv->connection, priv->server); g_debug("Opening XMPP connection to %s:%i", lm_connection_get_server(priv->connection), priv->port); if (!lm_connection_open (priv->connection, (LmResultFunction) diversity_xmpp_authenticate, xmpp, NULL, &error)) { g_debug ("lm_connection_open failed: %s", error->message); return FALSE; } return TRUE; } static void _diversity_xmpp_presence_set(DiversityXmpp *xmpp) { LmMessage *presence; LmMessageNode *node; DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); presence = lm_message_new(NULL, LM_MESSAGE_TYPE_PRESENCE); switch (priv->presence) { case XMPP_PRESENCE_ONLINE: break; case XMPP_PRESENCE_CHAT: node = lm_message_node_add_child(presence->node, "show", "chat"); break; case XMPP_PRESENCE_AWAY: node = lm_message_node_add_child(presence->node, "show", "away"); break; case XMPP_PRESENCE_NA: node = lm_message_node_add_child(presence->node, "show", "xa"); break; case XMPP_PRESENCE_DND: node = lm_message_node_add_child(presence->node, "show", "dnd"); break; case XMPP_PRESENCE_INVISIBLE: lm_message_node_set_attributes(presence->node, "type", "invisible"); break; case XMPP_PRESENCE_OFFLINE: break; } if (priv->statusmsg) { node = lm_message_node_add_child (presence->node, "status", priv->statusmsg); } node = lm_message_node_add_child(presence->node, "priority", "5"); xmpp_caps_add_c_node(priv->caps, presence->node); lm_connection_send(priv->connection, presence, NULL); lm_message_unref(presence); } void diversity_xmpp_presence_set(DiversityXmpp *xmpp, guint presence, gchar *statusmsg) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); if ((priv->presence == presence) && (((priv->statusmsg) == NULL && (statusmsg == NULL)) || (priv->statusmsg && statusmsg && !strcmp(priv->statusmsg, statusmsg)))) { return; } if (priv->presence != presence) { priv->presence = presence; } if (priv->statusmsg && statusmsg &&(!strcmp(priv->statusmsg, statusmsg))) { if (priv->statusmsg) g_free(priv->statusmsg); priv->statusmsg = g_strdup(statusmsg); } _diversity_xmpp_presence_set(xmpp); } LmConnection *diversity_xmpp_get_connection(DiversityXmpp *xmpp) { DiversityXmppPrivate *priv = DIVERSITY_XMPP_GET_PRIVATE(xmpp); return priv->connection; } static LmHandlerResult xmpp_handle_msg(LmMessageHandler *handler, LmConnection *connection, LmMessage *message, gpointer user_data) { LmMessageNode *msgnode; msgnode = lm_message_get_node(message); g_debug("\n%s", lm_message_node_to_string(msgnode)); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; }