Mercurial > hg
view mcabber/src/xmpp_iqrequest.c @ 1627:ff22a18abfe6
Load module only once
* do not load module, that is already loaded
* coding style improvements
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Wed, 21 Oct 2009 00:17:45 +0300 |
parents | 351427ef0b4b |
children |
line wrap: on
line source
/* * xmpp_iqrequest.c -- Jabber IQ request handling * * Copyright (C) 2008-2009 Frank Zschockelt <mcabber@freakysoft.de> * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net> * * 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 <string.h> #include <stdlib.h> #include "xmpp_helper.h" #include "xmpp_iq.h" #include "screen.h" #include "utils.h" #include "settings.h" #include "hooks.h" #include "hbuf.h" extern LmMessageNode *bookmarks; extern LmMessageNode *rosternotes; static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data); static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data); static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data); static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data); static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data); static struct IqRequestHandlers { const gchar *xmlns; const gchar *querytag; LmHandleMessageFunction handler; } iq_request_handlers[] = { {NS_ROSTER, "query", &cb_roster}, {NS_VERSION,"query", &cb_version}, {NS_TIME, "query", &cb_time}, {NS_LAST, "query", &cb_last}, {NS_VCARD, "vCard", &cb_vcard}, {NULL, NULL, NULL} }; // Enum for vCard attributes enum vcard_attr { vcard_home = 1<<0, vcard_work = 1<<1, vcard_postal = 1<<2, vcard_voice = 1<<3, vcard_fax = 1<<4, vcard_cell = 1<<5, vcard_inet = 1<<6, vcard_pref = 1<<7, }; // xmlns has to be a namespace from iq_request_handlers[].xmlns void xmpp_iq_request(const char *fulljid, const char *xmlns) { LmMessage *iq; LmMessageNode *query; LmMessageHandler *handler; int i; iq = lm_message_new_with_sub_type(fulljid, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); for (i = 0; strcmp(iq_request_handlers[i].xmlns, xmlns) != 0 ; ++i) ; query = lm_message_node_add_child(iq->node, iq_request_handlers[i].querytag, NULL); lm_message_node_set_attribute(query, "xmlns", xmlns); handler = lm_message_handler_new(iq_request_handlers[i].handler, NULL, FALSE); lm_connection_send_with_reply(lconnection, iq, handler, NULL); lm_message_handler_unref(handler); lm_message_unref(iq); } // This callback is reached when mcabber receives the first roster update // after the connection. static LmHandlerResult cb_roster(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *x; const char *ns; // Only execute the hook if the roster has been successfully retrieved if (lm_message_get_sub_type(m) != LM_MESSAGE_SUB_TYPE_RESULT) return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; x = lm_message_node_find_child(m->node, "query"); if (!x) return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; ns = lm_message_node_get_attribute(x, "xmlns"); if (ns && !strcmp(ns, NS_ROSTER)) handle_iq_roster(NULL, c, m, user_data); // Post-login stuff hook_execute_internal("hook-post-connect"); return LM_HANDLER_RESULT_REMOVE_MESSAGE; } static LmHandlerResult cb_version(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *ansqry; const char *p, *bjid; char *tmp; char *buf; ansqry = lm_message_node_get_child(m->node, "query"); if (!ansqry) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result!"); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } // Display IQ result sender... p = lm_message_get_from(m); if (!p) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:version result (no sender name)."); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } bjid = p; buf = g_strdup_printf("Received IQ:version result from <%s>", bjid); scr_LogPrint(LPRINT_LOGNORM, "%s", buf); // bjid should now really be the "bare JID", let's strip the resource tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); if (tmp) *tmp = '\0'; scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); g_free(buf); // Get result data... p = lm_message_node_get_child_value(ansqry, "name"); if (p) { buf = g_strdup_printf("Name: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } p = lm_message_node_get_child_value(ansqry, "version"); if (p) { buf = g_strdup_printf("Version: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } p = lm_message_node_get_child_value(ansqry, "os"); if (p) { buf = g_strdup_printf("OS: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } return LM_HANDLER_RESULT_REMOVE_MESSAGE; } static LmHandlerResult cb_time(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *ansqry; const char *p, *bjid; char *tmp; char *buf; ansqry = lm_message_node_get_child(m->node, "query"); if (!ansqry) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result!"); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } // Display IQ result sender... p = lm_message_get_from(m); if (!p) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:time result (no sender name)."); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } bjid = p; buf = g_strdup_printf("Received IQ:time result from <%s>", bjid); scr_LogPrint(LPRINT_LOGNORM, "%s", buf); // bjid should now really be the "bare JID", let's strip the resource tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); if (tmp) *tmp = '\0'; scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); g_free(buf); // Get result data... p = lm_message_node_get_child_value(ansqry, "utc"); if (p) { buf = g_strdup_printf("UTC: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } p = lm_message_node_get_child_value(ansqry, "tz"); if (p) { buf = g_strdup_printf("TZ: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } p = lm_message_node_get_child_value(ansqry, "display"); if (p) { buf = g_strdup_printf("Time: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } return LM_HANDLER_RESULT_REMOVE_MESSAGE; } static LmHandlerResult cb_last(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *ansqry; const char *p, *bjid; char *buf, *tmp; ansqry = lm_message_node_get_child(m->node, "query"); if (!ansqry) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result!"); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } // Display IQ result sender... p = lm_message_get_from(m); if (!p) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:last result (no sender name)."); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } bjid = p; buf = g_strdup_printf("Received IQ:last result from <%s>", bjid); scr_LogPrint(LPRINT_LOGNORM, "%s", buf); // bjid should now really be the "bare JID", let's strip the resource tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); if (tmp) *tmp = '\0'; scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); g_free(buf); // Get result data... p = lm_message_node_get_attribute(ansqry, "seconds"); if (p) { long int s; GString *sbuf; sbuf = g_string_new("Idle time: "); s = atol(p); // Days if (s > 86400L) { g_string_append_printf(sbuf, "%ldd ", s/86400L); s %= 86400L; } // hh:mm:ss g_string_append_printf(sbuf, "%02ld:", s/3600L); s %= 3600L; g_string_append_printf(sbuf, "%02ld:%02ld", s/60L, s%60L); scr_WriteIncomingMessage(bjid, sbuf->str, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_string_free(sbuf, TRUE); } else { scr_WriteIncomingMessage(bjid, "No idle time reported.", 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); } p = lm_message_node_get_value(ansqry); if (p) { buf = g_strdup_printf("Status message: %s", p); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); g_free(buf); } return LM_HANDLER_RESULT_REMOVE_MESSAGE; } static void display_vcard_item(const char *bjid, const char *label, enum vcard_attr vcard_attrib, const char *text) { char *buf; if (!text || !bjid || !label) return; buf = g_strdup_printf("%s: %s%s%s%s%s%s%s%s%s%s", label, (vcard_attrib & vcard_home ? "[home]" : ""), (vcard_attrib & vcard_work ? "[work]" : ""), (vcard_attrib & vcard_postal ? "[postal]" : ""), (vcard_attrib & vcard_voice ? "[voice]" : ""), (vcard_attrib & vcard_fax ? "[fax]" : ""), (vcard_attrib & vcard_cell ? "[cell]" : ""), (vcard_attrib & vcard_inet ? "[inet]" : ""), (vcard_attrib & vcard_pref ? "[pref]" : ""), (vcard_attrib ? " " : ""), text); scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0); g_free(buf); } static void handle_vcard_node(const char *barejid, LmMessageNode *vcardnode) { LmMessageNode *x; const char *p; for (x = vcardnode->children ; x; x = x->next) { const char *data; enum vcard_attr vcard_attrib = 0; p = x->name; data = lm_message_node_get_value(x); if (!p || !data) continue; if (!strcmp(p, "FN")) display_vcard_item(barejid, "Name", vcard_attrib, data); else if (!strcmp(p, "NICKNAME")) display_vcard_item(barejid, "Nickname", vcard_attrib, data); else if (!strcmp(p, "URL")) display_vcard_item(barejid, "URL", vcard_attrib, data); else if (!strcmp(p, "BDAY")) display_vcard_item(barejid, "Birthday", vcard_attrib, data); else if (!strcmp(p, "TZ")) display_vcard_item(barejid, "Timezone", vcard_attrib, data); else if (!strcmp(p, "TITLE")) display_vcard_item(barejid, "Title", vcard_attrib, data); else if (!strcmp(p, "ROLE")) display_vcard_item(barejid, "Role", vcard_attrib, data); else if (!strcmp(p, "DESC")) display_vcard_item(barejid, "Comment", vcard_attrib, data); else if (!strcmp(p, "N")) { data = lm_message_node_get_child_value(x, "FAMILY"); display_vcard_item(barejid, "Family Name", vcard_attrib, data); data = lm_message_node_get_child_value(x, "GIVEN"); display_vcard_item(barejid, "Given Name", vcard_attrib, data); data = lm_message_node_get_child_value(x, "MIDDLE"); display_vcard_item(barejid, "Middle Name", vcard_attrib, data); } else if (!strcmp(p, "ORG")) { data = lm_message_node_get_child_value(x, "ORGNAME"); display_vcard_item(barejid, "Organisation name", vcard_attrib, data); data = lm_message_node_get_child_value(x, "ORGUNIT"); display_vcard_item(barejid, "Organisation unit", vcard_attrib, data); } else { // The HOME, WORK and PREF attributes are common to the remaining fields // (ADR, TEL & EMAIL) if (lm_message_node_get_child(x, "HOME")) vcard_attrib |= vcard_home; if (lm_message_node_get_child(x, "WORK")) vcard_attrib |= vcard_work; if (lm_message_node_get_child(x, "PREF")) vcard_attrib |= vcard_pref; if (!strcmp(p, "ADR")) { // Address if (lm_message_node_get_child(x, "POSTAL")) vcard_attrib |= vcard_postal; data = lm_message_node_get_child_value(x, "EXTADD"); display_vcard_item(barejid, "Addr (ext)", vcard_attrib, data); data = lm_message_node_get_child_value(x, "STREET"); display_vcard_item(barejid, "Street", vcard_attrib, data); data = lm_message_node_get_child_value(x, "LOCALITY"); display_vcard_item(barejid, "Locality", vcard_attrib, data); data = lm_message_node_get_child_value(x, "REGION"); display_vcard_item(barejid, "Region", vcard_attrib, data); data = lm_message_node_get_child_value(x, "PCODE"); display_vcard_item(barejid, "Postal code", vcard_attrib, data); data = lm_message_node_get_child_value(x, "CTRY"); display_vcard_item(barejid, "Country", vcard_attrib, data); } else if (!strcmp(p, "TEL")) { // Telephone data = lm_message_node_get_child_value(x, "NUMBER"); if (data) { if (lm_message_node_get_child(x, "VOICE")) vcard_attrib |= vcard_voice; if (lm_message_node_get_child(x, "FAX")) vcard_attrib |= vcard_fax; if (lm_message_node_get_child(x, "CELL")) vcard_attrib |= vcard_cell; display_vcard_item(barejid, "Phone", vcard_attrib, data); } } else if (!strcmp(p, "EMAIL")) { // Email if (lm_message_node_get_child(x, "INTERNET")) vcard_attrib |= vcard_inet; data = lm_message_node_get_child_value(x, "USERID"); display_vcard_item(barejid, "Email", vcard_attrib, data); } } } } static LmHandlerResult cb_vcard(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *ansqry; const char *p, *bjid; char *buf, *tmp; // Display IQ result sender... p = lm_message_get_from(m); if (!p) { scr_LogPrint(LPRINT_LOGNORM, "Invalid IQ:vCard result (no sender name)."); return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } bjid = p; buf = g_strdup_printf("Received IQ:vCard result from <%s>", bjid); scr_LogPrint(LPRINT_LOGNORM, "%s", buf); // Get the vCard node ansqry = lm_message_node_get_child(m->node, "vCard"); if (!ansqry) { scr_LogPrint(LPRINT_LOGNORM, "Empty IQ:vCard result!"); g_free(buf); return LM_HANDLER_RESULT_REMOVE_MESSAGE; } // bjid should really be the "bare JID", let's strip the resource tmp = strchr(bjid, JID_RESOURCE_SEPARATOR); if (tmp) *tmp = '\0'; scr_WriteIncomingMessage(bjid, buf, 0, HBB_PREFIX_INFO, 0); g_free(buf); // Get result data... handle_vcard_node(bjid, ansqry); return LM_HANDLER_RESULT_REMOVE_MESSAGE; } static void storage_bookmarks_parse_conference(LmMessageNode *node) { const char *fjid, *name, *autojoin; const char *pstatus, *awhois; char *bjid; GSList *room_elt; fjid = lm_message_node_get_attribute(node, "jid"); if (!fjid) return; name = lm_message_node_get_attribute(node, "name"); autojoin = lm_message_node_get_attribute(node, "autojoin"); awhois = lm_message_node_get_attribute(node, "autowhois"); pstatus = lm_message_node_get_child_value(node, "print_status"); bjid = jidtodisp(fjid); // Bare jid // Make sure this is a room (it can be a conversion user->room) room_elt = roster_find(bjid, jidsearch, 0); if (!room_elt) { room_elt = roster_add_user(bjid, name, NULL, ROSTER_TYPE_ROOM, sub_none, -1); } else { buddy_settype(room_elt->data, ROSTER_TYPE_ROOM); /* // If the name is available, should we use it? // I don't think so, it would be confusing because this item is already // in the roster. if (name) buddy_setname(room_elt->data, name); */ } // Set the print_status and auto_whois values if (pstatus) { enum room_printstatus i; for (i = status_none; i <= status_all; i++) if (!strcasecmp(pstatus, strprintstatus[i])) break; if (i <= status_all) buddy_setprintstatus(room_elt->data, i); } if (awhois) { enum room_autowhois i = autowhois_default; if (!strcmp(awhois, "1")) i = autowhois_on; else if (!strcmp(awhois, "0")) i = autowhois_off; if (i != autowhois_default) buddy_setautowhois(room_elt->data, i); } // Is autojoin set? // If it is, we'll look up for more information (nick? password?) and // try to join the room. if (autojoin && !strcmp(autojoin, "1")) { const char *nick, *passwd; char *tmpnick = NULL; nick = lm_message_node_get_child_value(node, "nick"); passwd = lm_message_node_get_child_value(node, "password"); if (!nick || !*nick) nick = tmpnick = default_muc_nickname(NULL); // Let's join now scr_LogPrint(LPRINT_LOGNORM, "Auto-join bookmark <%s>", bjid); xmpp_room_join(bjid, nick, passwd); g_free(tmpnick); } g_free(bjid); } static LmHandlerResult cb_storage_bookmarks(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *x, *ansqry; char *p; if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { // No server support, or no bookmarks? p = m->node->children->name; if (p && !strcmp(p, "item-not-found")) { // item-no-found means the server has Private Storage, but it's // currently empty. if (bookmarks) lm_message_node_unref(bookmarks); bookmarks = lm_message_node_new("storage", "storage:bookmarks"); // We return 0 so that the IQ error message be // not displayed, as it isn't a real error. return LM_HANDLER_RESULT_REMOVE_MESSAGE; } return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error } ansqry = lm_message_node_get_child(m->node, "query"); ansqry = lm_message_node_get_child(ansqry, "storage"); if (!ansqry) { scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! (storage:bookmarks)"); return 0; } // Walk through the storage tags for (x = ansqry->children ; x; x = x->next) { // If the current node is a conference item, parse it and update the roster if (x->name && !strcmp(x->name, "conference")) storage_bookmarks_parse_conference(x); } // "Copy" the bookmarks node if (bookmarks) lm_message_node_unref(bookmarks); lm_message_node_deep_ref(ansqry); bookmarks = ansqry; return 0; } static LmHandlerResult cb_storage_rosternotes(LmMessageHandler *h, LmConnection *c, LmMessage *m, gpointer user_data) { LmMessageNode *ansqry; if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) { const char *p; // No server support, or no roster notes? p = m->node->children->name; if (p && !strcmp(p, "item-not-found")) { // item-no-found means the server has Private Storage, but it's // currently empty. if (rosternotes) lm_message_node_unref(rosternotes); rosternotes = lm_message_node_new("storage", "storage:rosternotes"); // We return 0 so that the IQ error message be // not displayed, as it isn't a real error. return LM_HANDLER_RESULT_REMOVE_MESSAGE; } return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // Unhandled error } ansqry = lm_message_node_get_child(m->node, "query"); ansqry = lm_message_node_get_child(ansqry, "storage"); if (!ansqry) { scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! " "(storage:rosternotes)"); return LM_HANDLER_RESULT_REMOVE_MESSAGE; } // Copy the rosternotes node if (rosternotes) lm_message_node_unref(rosternotes); lm_message_node_deep_ref(ansqry); rosternotes = ansqry; return 0; } static struct IqRequestStorageHandlers { const gchar *storagens; LmHandleMessageFunction handler; } iq_request_storage_handlers[] = { {"storage:rosternotes", &cb_storage_rosternotes}, {"storage:bookmarks", &cb_storage_bookmarks}, {NULL, NULL} }; void xmpp_request_storage(const gchar *storage) { LmMessage *iq; LmMessageNode *query; LmMessageHandler *handler; int i; iq = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); query = lm_message_node_add_child(iq->node, "query", NULL); lm_message_node_set_attribute(query, "xmlns", NS_PRIVATE); lm_message_node_set_attribute(lm_message_node_add_child (query, "storage", NULL), "xmlns", storage); for (i = 0; strcmp(iq_request_storage_handlers[i].storagens, storage) != 0; ++i) ; handler = lm_message_handler_new(iq_request_storage_handlers[i].handler, NULL, FALSE); lm_connection_send_with_reply(lconnection, iq, handler, NULL); lm_message_handler_unref(handler); lm_message_unref(iq); } /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */