# HG changeset patch # User Nathan Walp # Date 1129513389 0 # Node ID 828802f2251bed066b8224dbfddfaf2be2dce2a7 # Parent e860157296d4e7658a912e43d889149cc8b0322f [gaim-migrate @ 13961] Jabber User Directory searching... This works when the stars are aligned, and breaks otherwise. It hasn't been tested on any "x:data" supporting servers (because I don't know of any yet). It doesn't let you know if there was an error. Really, it doesn't do a lot of things. But you can search. You do get results. The results themselves, however, are a tad off. users.jabber.org is sending back results along the lines of: faceprint@faceprint.comfaceprint which is obviously switched. I'll ping the appropriate people to figure that out. committer: Tailor Script diff -r e860157296d4 -r 828802f2251b src/protocols/jabber/buddy.c --- a/src/protocols/jabber/buddy.c Sun Oct 16 18:26:32 2005 +0000 +++ b/src/protocols/jabber/buddy.c Mon Oct 17 01:43:09 2005 +0000 @@ -33,6 +33,7 @@ #include "jabber.h" #include "iq.h" #include "presence.h" +#include "xdata.h" void jabber_buddy_free(JabberBuddy *jb) { @@ -1140,3 +1141,253 @@ } return NULL; } + +static void user_search_result_add_buddy_cb(GaimConnection *gc, GList *row) +{ + /* XXX find out the jid */ + gaim_blist_request_add_buddy(gaim_connection_get_account(gc), + g_list_nth_data(row, 0), NULL, NULL); +} + +static void user_search_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + GaimNotifySearchResults *results; + GaimNotifySearchColumn *column; + xmlnode *x, *query, *item, *field; + + /* XXX error checking? */ + if(!(query = xmlnode_get_child(packet, "query"))) + return; + + results = gaim_notify_searchresults_new(); + if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) { + xmlnode *reported; + gaim_debug_info("jabber", "new-skool\n"); + if((reported = xmlnode_get_child(x, "reported"))) { + xmlnode *field = xmlnode_get_child(reported, "field"); + while(field) { + /* XXX keep track of this order, use it below */ + const char *var = xmlnode_get_attrib(field, "var"); + const char *label = xmlnode_get_attrib(field, "label"); + if(var) { + column = gaim_notify_searchresults_column_new(label ? label : var); + gaim_notify_searchresults_column_add(results, column); + } + field = xmlnode_get_next_twin(field); + } + } + item = xmlnode_get_child(x, "item"); + while(item) { + GList *row = NULL; + field = xmlnode_get_child(item, "field"); + while(field) { + xmlnode *valuenode = xmlnode_get_child(item, "value"); + if(valuenode) { + char *value = xmlnode_get_data(valuenode); + row = g_list_append(row, value); + } + field = xmlnode_get_next_twin(field); + } + gaim_notify_searchresults_row_add(results, row); + + item = xmlnode_get_next_twin(item); + } + } else { + /* old skool */ + gaim_debug_info("jabber", "old-skool\n"); + + column = gaim_notify_searchresults_column_new("JID"); + gaim_notify_searchresults_column_add(results, column); + column = gaim_notify_searchresults_column_new("First"); + gaim_notify_searchresults_column_add(results, column); + column = gaim_notify_searchresults_column_new("Last"); + gaim_notify_searchresults_column_add(results, column); + column = gaim_notify_searchresults_column_new("Nickname"); + gaim_notify_searchresults_column_add(results, column); + column = gaim_notify_searchresults_column_new("E-Mail"); + gaim_notify_searchresults_column_add(results, column); + + for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { + const char *jid; + xmlnode *node; + GList *row = NULL; + + if(!(jid = xmlnode_get_attrib(item, "jid"))) + continue; + + row = g_list_append(row, g_strdup(jid)); + node = xmlnode_get_child(item, "first"); + row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); + node = xmlnode_get_child(item, "last"); + row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); + node = xmlnode_get_child(item, "nick"); + row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); + node = xmlnode_get_child(item, "email"); + row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); + gaim_debug_info("jabber", "row=%d\n", row); + gaim_notify_searchresults_row_add(results, row); + } + } + + gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_ADD_BUDDY, + user_search_result_add_buddy_cb); + + gaim_notify_searchresults(js->gc, NULL, NULL, _("The following are the results of your search"), results, NULL, NULL); +} + +static void user_search_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) +{ + xmlnode *query; + JabberIq *iq; + + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); + query = xmlnode_get_child(iq->node, "query"); + + xmlnode_insert_child(query, result); + + jabber_iq_set_callback(iq, user_search_result_cb, NULL); + jabber_iq_send(iq); +} + +struct user_search_info { + JabberStream *js; + char *directory_server; +}; + +static void user_search_cancel_cb(struct user_search_info *usi, GaimRequestFields *fields) +{ + g_free(usi->directory_server); + g_free(usi); +} + +static void user_search_cb(struct user_search_info *usi, GaimRequestFields *fields) +{ + JabberStream *js = usi->js; + JabberIq *iq; + xmlnode *query; + GList *groups, *flds; + + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); + query = xmlnode_get_child(iq->node, "query"); + + for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) { + for(flds = gaim_request_field_group_get_fields(groups->data); + flds; flds = flds->next) { + GaimRequestField *field = flds->data; + const char *id = gaim_request_field_get_id(field); + const char *value = gaim_request_field_string_get_value(field); + + if(value && (!strcmp(id, "first") || !strcmp(id, "last") || !strcmp(id, "nick") || !strcmp(id, "email"))) { + xmlnode *y = xmlnode_new_child(query, id); + xmlnode_insert_data(y, value, -1); + } + } + } + + jabber_iq_set_callback(iq, user_search_result_cb, NULL); + xmlnode_set_attrib(iq->node, "to", usi->directory_server); + jabber_iq_send(iq); + + g_free(usi->directory_server); + g_free(usi); +} + +static void user_search_fields_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + xmlnode *query, *x; + const char *from; + + /* i forget, do i have to check for error? XXX */ + if(!(from= xmlnode_get_attrib(packet, "from"))) + return; + + + if(!(query = xmlnode_get_child(packet, "query"))) + return; + + if((x = xmlnode_get_child_with_namespace(packet, "x", "jabber:x:data"))) { + jabber_x_data_request(js, x, user_search_x_data_cb, NULL); + return; + } else { + struct user_search_info *usi; + xmlnode *instnode; + char *instructions; + GaimRequestFields *fields; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + /* old skool */ + fields = gaim_request_fields_new(); + group = gaim_request_field_group_new(NULL); + gaim_request_fields_add_group(fields, group); + + if((instnode = xmlnode_get_child(query, "instructions"))) + instructions = xmlnode_get_data(instnode); + else + instructions = g_strdup(_("Fill in one or more fields to search " + "for any matching Jabber users.")); + + if(xmlnode_get_child(query, "first")) { + field = gaim_request_field_string_new("first", _("First Name"), + NULL, FALSE); + gaim_request_field_group_add_field(group, field); + } + if(xmlnode_get_child(query, "last")) { + field = gaim_request_field_string_new("last", _("Last Name"), + NULL, FALSE); + gaim_request_field_group_add_field(group, field); + } + if(xmlnode_get_child(query, "nick")) { + field = gaim_request_field_string_new("nick", _("Nickname"), + NULL, FALSE); + gaim_request_field_group_add_field(group, field); + } + if(xmlnode_get_child(query, "email")) { + field = gaim_request_field_string_new("email", _("E-Mail Address"), + NULL, FALSE); + gaim_request_field_group_add_field(group, field); + } + + usi = g_new0(struct user_search_info, 1); + usi->js = js; + usi->directory_server = g_strdup(from); + + gaim_request_fields(js->gc, _("Search for Jabber users"), + _("Search for Jabber users"), instructions, fields, + _("Search"), G_CALLBACK(user_search_cb), + _("Cancel"), G_CALLBACK(user_search_cancel_cb), usi); + + g_free(instructions); + } +} + +static void jabber_user_search_ok(JabberStream *js, const char *directory) +{ + JabberIq *iq; + + /* XXX: should probably better validate the directory we're given */ + if(!directory || !*directory) { + gaim_notify_error(js->gc, _("Invalid Directory"), _("Invalid Directory"), NULL); + return; + } + + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:search"); + xmlnode_set_attrib(iq->node, "to", directory); + + jabber_iq_set_callback(iq, user_search_fields_result_cb, NULL); + + jabber_iq_send(iq); +} + +void jabber_user_search_begin(GaimPluginAction *action) +{ + GaimConnection *gc = (GaimConnection *) action->context; + JabberStream *js = gc->proto_data; + + gaim_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"), + _("Select a user directory to search"), + js->user_directories ? js->user_directories->data : "users.jabber.org", + FALSE, FALSE, NULL, + _("Search Directory"), GAIM_CALLBACK(jabber_user_search_ok), + _("Cancel"), NULL, js); +} diff -r e860157296d4 -r 828802f2251b src/protocols/jabber/buddy.h --- a/src/protocols/jabber/buddy.h Sun Oct 16 18:26:32 2005 +0000 +++ b/src/protocols/jabber/buddy.h Mon Oct 17 01:43:09 2005 +0000 @@ -87,4 +87,6 @@ const char *jabber_buddy_state_get_status_id(JabberBuddyState state); JabberBuddyState jabber_buddy_status_id_get_state(const char *id); +void jabber_user_search_begin(GaimPluginAction *); + #endif /* _GAIM_JABBER_BUDDY_H_ */ diff -r e860157296d4 -r 828802f2251b src/protocols/jabber/disco.c --- a/src/protocols/jabber/disco.c Sun Oct 16 18:26:32 2005 +0000 +++ b/src/protocols/jabber/disco.c Mon Oct 17 01:43:09 2005 +0000 @@ -106,10 +106,14 @@ if(!category || !type) continue; - /* we found a groupchat or MUC server, add it to the list */ - /* XXX: actually check for protocol/muc or gc-1.0 support */ - if(!strcmp(category, "conference") && !strcmp(type, "text")) + if(!strcmp(category, "conference") && !strcmp(type, "text")) { + /* we found a groupchat or MUC server, add it to the list */ + /* XXX: actually check for protocol/muc or gc-1.0 support */ js->chat_servers = g_list_append(js->chat_servers, g_strdup(from)); + } else if(!strcmp(category, "directory") && !strcmp(type, "user")) { + /* we found a JUD */ + js->user_directories = g_list_append(js->user_directories, g_strdup(from)); + } } else if(!strcmp(child->name, "feature")) { const char *var = xmlnode_get_attrib(child, "var"); @@ -122,6 +126,10 @@ capabilities |= JABBER_CAP_SI_FILE_XFER; else if(!strcmp(var, "http://jabber.org/protocol/bytestreams")) capabilities |= JABBER_CAP_BYTESTREAMS; + else if(!strcmp(var, "jabber:iq:search")) + capabilities |= JABBER_CAP_IQ_SEARCH; + else if(!strcmp(var, "jabber:iq:register")) + capabilities |= JABBER_CAP_IQ_REGISTER; } } diff -r e860157296d4 -r 828802f2251b src/protocols/jabber/jabber.c --- a/src/protocols/jabber/jabber.c Sun Oct 16 18:26:32 2005 +0000 +++ b/src/protocols/jabber/jabber.c Mon Oct 17 01:43:09 2005 +0000 @@ -810,6 +810,10 @@ g_free(js->chat_servers->data); js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers); } + while(js->user_directories) { + g_free(js->user_directories->data); + js->user_directories = g_list_delete_link(js->user_directories, js->user_directories); + } if(js->stream_id) g_free(js->stream_id); if(js->user) @@ -1174,6 +1178,10 @@ m = g_list_append(m, act); /* } */ + act = gaim_plugin_action_new(_("Search for users"), + jabber_user_search_begin); + m = g_list_append(m, act); + return m; } diff -r e860157296d4 -r 828802f2251b src/protocols/jabber/jabber.h --- a/src/protocols/jabber/jabber.h Sun Oct 16 18:26:32 2005 +0000 +++ b/src/protocols/jabber/jabber.h Mon Oct 17 01:43:09 2005 +0000 @@ -39,6 +39,8 @@ JABBER_CAP_BYTESTREAMS = 1 << 4, JABBER_CAP_IBB = 1 << 5, JABBER_CAP_CHAT_STATES = 1 << 6, + JABBER_CAP_IQ_SEARCH = 1 << 7, + JABBER_CAP_IQ_REGISTER = 1 << 8, JABBER_CAP_RETRIEVED = 1 << 31 } JabberCapabilities; @@ -80,6 +82,7 @@ GHashTable *chats; GList *chat_servers; GaimRoomlist *roomlist; + GList *user_directories; GHashTable *iq_callbacks; GHashTable *disco_callbacks;