Wed, 10 Apr 2024 00:05:21 -0500
Remove the old Jabber/XMPP protocol plugin
We've already started the new version and we're not really using any of this
code. So rather than keep it around and other migrations have broken it, we're
just going to shed it now.
Testing Done:
Sewer surfed with the turtles.
Reviewed at https://reviews.imfreedom.org/r/3073/
--- a/libpurple/meson.build Tue Apr 09 23:36:32 2024 -0500 +++ b/libpurple/meson.build Wed Apr 10 00:05:21 2024 -0500 @@ -440,7 +440,6 @@ subdir('tests') subdir('plugins') -subdir('protocols') test( 'purple_license_headers',
--- a/libpurple/protocols/jabber/adhoccommands.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,364 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "adhoccommands.h" -#include <string.h> -#include "xdata.h" -#include "iq.h" - -static void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd); - -void -jabber_adhoc_commands_free(JabberAdHocCommands *cmd) -{ - g_return_if_fail(cmd != NULL); - - g_free(cmd->jid); - g_free(cmd->node); - g_free(cmd->name); - g_free(cmd); -} - -static void -do_adhoc_ignoreme(G_GNUC_UNUSED JabberStream *js, ...) { - /* we don't have to do anything */ -} - -typedef struct { - char *sessionid; - char *who; - char *node; - GList *actionslist; -} JabberAdHocActionInfo; - -static void -jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, PurpleXmlNode *query) -{ - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - PurpleXmlNode *item; - - if ((jid = jabber_id_new(from))) { - if (jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) - jbr = jabber_buddy_find_resource(jb, jid->resource); - jabber_id_free(jid); - } - - if(!jbr) - return; - - /* since the list we just received is complete, wipe the old one */ - g_clear_list(&jbr->commands, (GDestroyNotify)jabber_adhoc_commands_free); - - for(item = query->child; item; item = item->next) { - JabberAdHocCommands *cmd; - if(item->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - if(!purple_strequal(item->name, "item")) - continue; - cmd = g_new0(JabberAdHocCommands, 1); - - cmd->jid = g_strdup(purple_xmlnode_get_attrib(item,"jid")); - cmd->node = g_strdup(purple_xmlnode_get_attrib(item,"node")); - cmd->name = g_strdup(purple_xmlnode_get_attrib(item,"name")); - - jbr->commands = g_list_append(jbr->commands,cmd); - } -} - -void -jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query; - const char *node; - - if (type == JABBER_IQ_ERROR) - return; - - query = purple_xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_ITEMS); - if (!query) - return; - node = purple_xmlnode_get_attrib(query, "node"); - if (!purple_strequal(node, "http://jabber.org/protocol/commands")) - return; - - jabber_adhoc_got_buddy_list(js, from, query); -} - -static void jabber_adhoc_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data); - -static void do_adhoc_action_cb(JabberStream *js, PurpleXmlNode *result, const char *actionhandle, gpointer user_data) { - PurpleXmlNode *command; - JabberAdHocActionInfo *actionInfo = user_data; - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); - jabber_iq_set_callback(iq, jabber_adhoc_parse, NULL); - - purple_xmlnode_set_attrib(iq->node, "to", actionInfo->who); - command = purple_xmlnode_new_child(iq->node,"command"); - purple_xmlnode_set_namespace(command,"http://jabber.org/protocol/commands"); - purple_xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid); - purple_xmlnode_set_attrib(command,"node",actionInfo->node); - - /* cancel is handled differently on ad-hoc commands than regular forms */ - if (purple_strequal(purple_xmlnode_get_namespace(result), "jabber:x:data") && - purple_strequal(purple_xmlnode_get_attrib(result, "type"), "cancel")) { - purple_xmlnode_set_attrib(command,"action","cancel"); - } else { - if(actionhandle) - purple_xmlnode_set_attrib(command,"action",actionhandle); - purple_xmlnode_insert_child(command,result); - } - - g_list_free_full(actionInfo->actionslist, g_free); - g_free(actionInfo->sessionid); - g_free(actionInfo->who); - g_free(actionInfo->node); - - jabber_iq_send(iq); -} - -static void -jabber_adhoc_parse(JabberStream *js, const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *command = purple_xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands"); - const char *status = purple_xmlnode_get_attrib(command,"status"); - PurpleXmlNode *xdata = purple_xmlnode_get_child_with_namespace(command,"x","jabber:x:data"); - - if (type == JABBER_IQ_ERROR) { - char *msg = jabber_parse_error(js, packet, NULL); - if(!msg) - msg = g_strdup(_("Unknown Error")); - - purple_notify_error(NULL, _("Ad-Hoc Command Failed"), - _("Ad-Hoc Command Failed"), msg, - purple_request_cpar_from_connection(js->gc)); - g_free(msg); - return; - } - - if(!status) - return; - - if(purple_strequal(status,"completed")) { - /* display result */ - PurpleXmlNode *note = purple_xmlnode_get_child(command,"note"); - - if(note) { - char *data = purple_xmlnode_get_data(note); - purple_notify_info(NULL, from, data, NULL, - purple_request_cpar_from_connection(js->gc)); - g_free(data); - } - - if(xdata) - jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL); - return; - } - if(purple_strequal(status,"executing")) { - /* this command needs more steps */ - PurpleXmlNode *actions, *action; - int actionindex = 0; - GList *actionslist = NULL; - JabberAdHocActionInfo *actionInfo; - if(!xdata) - return; /* shouldn't happen */ - - actions = purple_xmlnode_get_child(command,"actions"); - if(!actions) { - JabberXDataAction *defaultaction = g_new0(JabberXDataAction, 1); - defaultaction->name = g_strdup(_("execute")); - defaultaction->handle = g_strdup("execute"); - actionslist = g_list_append(actionslist, defaultaction); - } else { - const char *defaultactionhandle = purple_xmlnode_get_attrib(actions, "execute"); - int index = 0; - for(action = actions->child; action; action = action->next, ++index) { - if(action->type == PURPLE_XMLNODE_TYPE_TAG) { - JabberXDataAction *newaction = g_new0(JabberXDataAction, 1); - newaction->name = g_strdup(_(action->name)); - newaction->handle = g_strdup(action->name); - actionslist = g_list_append(actionslist, newaction); - if(defaultactionhandle && purple_strequal(defaultactionhandle, action->name)) - actionindex = index; - } - } - } - - actionInfo = g_new0(JabberAdHocActionInfo, 1); - actionInfo->sessionid = g_strdup(purple_xmlnode_get_attrib(command,"sessionid")); - actionInfo->who = g_strdup(from); - actionInfo->node = g_strdup(purple_xmlnode_get_attrib(command,"node")); - actionInfo->actionslist = actionslist; - - jabber_x_data_request_with_actions(js,xdata,actionslist,actionindex,do_adhoc_action_cb,actionInfo); - } -} - -void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data) { - if (PURPLE_IS_BUDDY(node)) { - JabberAdHocCommands *cmd = data; - PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleAccount *account = purple_buddy_get_account(buddy); - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = purple_connection_get_protocol_data(gc); - - jabber_adhoc_execute(js, cmd); - } -} - -static void -jabber_adhoc_got_server_list(JabberStream *js, G_GNUC_UNUSED const char *from, - PurpleXmlNode *query) -{ - PurpleXmlNode *item; - - if(!query) - return; - - /* clean current list (just in case there is one) */ - g_clear_list(&js->commands, (GDestroyNotify)jabber_adhoc_commands_free); - - /* re-fill list */ - for(item = query->child; item; item = item->next) { - JabberAdHocCommands *cmd; - if(item->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - if(!purple_strequal(item->name, "item")) - continue; - cmd = g_new0(JabberAdHocCommands, 1); - cmd->jid = g_strdup(purple_xmlnode_get_attrib(item,"jid")); - cmd->node = g_strdup(purple_xmlnode_get_attrib(item,"node")); - cmd->name = g_strdup(purple_xmlnode_get_attrib(item,"name")); - - js->commands = g_list_append(js->commands,cmd); - } - - if (js->state == JABBER_STREAM_CONNECTED) { - PurpleProtocol *protocol = purple_connection_get_protocol(js->gc); - - purple_protocol_actions_changed(PURPLE_PROTOCOL_ACTIONS(protocol), - purple_connection_get_account(js->gc)); - } -} - -static void -jabber_adhoc_server_got_list_cb(JabberStream *js, const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query", - NS_DISCO_ITEMS); - - jabber_adhoc_got_server_list(js, from, query); - -} - -void jabber_adhoc_got_list(JabberStream *js, const char *from, PurpleXmlNode *query) -{ - if (purple_strequal(from, js->user->domain)) { - jabber_adhoc_got_server_list(js, from, query); - } else { - jabber_adhoc_got_buddy_list(js, from, query); - } -} - -void jabber_adhoc_server_get_list(JabberStream *js) { - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS); - PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(iq->node, "query", - NS_DISCO_ITEMS); - - purple_xmlnode_set_attrib(iq->node,"to",js->user->domain); - purple_xmlnode_set_attrib(query,"node","http://jabber.org/protocol/commands"); - - jabber_iq_set_callback(iq,jabber_adhoc_server_got_list_cb,NULL); - jabber_iq_send(iq); -} - -static void -jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); - PurpleXmlNode *command = purple_xmlnode_new_child(iq->node,"command"); - purple_xmlnode_set_attrib(iq->node,"to",cmd->jid); - purple_xmlnode_set_namespace(command,"http://jabber.org/protocol/commands"); - purple_xmlnode_set_attrib(command,"node",cmd->node); - purple_xmlnode_set_attrib(command,"action","execute"); - - jabber_iq_set_callback(iq,jabber_adhoc_parse,NULL); - - jabber_iq_send(iq); -} - -static void jabber_adhoc_server_execute(PurpleProtocolAction *action) { - JabberAdHocCommands *cmd = action->user_data; - if(cmd) { - PurpleConnection *gc = (PurpleConnection *) action->connection; - JabberStream *js = purple_connection_get_protocol_data(gc); - - jabber_adhoc_execute(js, cmd); - } -} - -void jabber_adhoc_init_server_commands(JabberStream *js, GList **m) { - GList *cmdlst; - JabberBuddy *jb; - - /* also add commands for other clients connected to the same account on another resource */ - char *accountname = g_strdup_printf("%s@%s", js->user->node, js->user->domain); - if((jb = jabber_buddy_find(js, accountname, TRUE))) { - GList *iter; - for(iter = jb->resources; iter; iter = g_list_next(iter)) { - JabberBuddyResource *jbr = iter->data; - GList *riter; - for(riter = jbr->commands; riter; riter = g_list_next(riter)) { - JabberAdHocCommands *cmd = riter->data; - char *cmdname = g_strdup_printf("%s (%s)",cmd->name,jbr->name); - PurpleProtocolAction *act = purple_protocol_action_new(cmdname, jabber_adhoc_server_execute); - act->user_data = cmd; - *m = g_list_append(*m, act); - g_free(cmdname); - } - } - } - g_free(accountname); - - /* now add server commands */ - for(cmdlst = js->commands; cmdlst; cmdlst = g_list_next(cmdlst)) { - JabberAdHocCommands *cmd = cmdlst->data; - PurpleProtocolAction *act = purple_protocol_action_new(cmd->name, jabber_adhoc_server_execute); - act->user_data = cmd; - *m = g_list_append(*m, act); - } -}
--- a/libpurple/protocols/jabber/adhoccommands.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_ADHOCCOMMANDS_H -#define PURPLE_JABBER_ADHOCCOMMANDS_H - -#include "jabber.h" - -/* Implementation of XEP-0050 */ - -typedef struct { - char *jid; - char *node; - char *name; -} JabberAdHocCommands; - -void jabber_adhoc_commands_free(JabberAdHocCommands *cmd); - -void jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data); - -void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data); - -void jabber_adhoc_got_list(JabberStream *js, const char *from, PurpleXmlNode *query); - -void jabber_adhoc_server_get_list(JabberStream *js); - -void jabber_adhoc_init_server_commands(JabberStream *js, GList **m); - -#endif /* PURPLE_JABBER_ADHOCCOMMANDS_H */
--- a/libpurple/protocols/jabber/auth.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,557 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "auth.h" -#include "disco.h" -#include "jabber.h" -#include "jutil.h" -#include "iq.h" - -static GSList *auth_mechs = NULL; - -static void auth_old_result_cb(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data); - -static void finish_plaintext_authentication(JabberStream *js) -{ - JabberIq *iq; - PurpleXmlNode *query, *x; - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); - query = purple_xmlnode_get_child(iq->node, "query"); - x = purple_xmlnode_new_child(query, "username"); - purple_xmlnode_insert_data(x, js->user->node, -1); - x = purple_xmlnode_new_child(query, "resource"); - purple_xmlnode_insert_data(x, js->user->resource, -1); - x = purple_xmlnode_new_child(query, "password"); - purple_xmlnode_insert_data(x, purple_connection_get_password(js->gc), -1); - jabber_iq_set_callback(iq, auth_old_result_cb, NULL); - jabber_iq_send(iq); -} - -static void allow_plaintext_auth(PurpleAccount *account) -{ - PurpleConnection *gc; - JabberStream *js; - - purple_account_set_bool(account, "auth_plain_in_clear", TRUE); - - gc = purple_account_get_connection(account); - js = purple_connection_get_protocol_data(gc); - - finish_plaintext_authentication(js); -} - -static void disallow_plaintext_auth(PurpleAccount *account) -{ - purple_connection_error(purple_account_get_connection(account), - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Server requires plaintext authentication over an unencrypted stream")); -} - -static void -auth_old_pass_cb(PurpleConnection *gc, PurpleRequestPage *page) { - PurpleAccount *account; - JabberStream *js; - const char *entry; - gboolean remember; - - /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */ - PURPLE_ASSERT_CONNECTION_IS_VALID(gc); - - account = purple_connection_get_account(gc); - js = purple_connection_get_protocol_data(gc); - - entry = purple_request_page_get_string(page, "password"); - remember = purple_request_page_get_bool(page, "remember"); - - if (!entry || !*entry) - { - purple_notify_error(account, NULL, - _("Password is required to sign on."), NULL, - purple_request_cpar_from_connection(gc)); - return; - } - - if(remember) { - PurpleCredentialManager *manager = NULL; - - purple_account_set_remember_password(account, TRUE); - - manager = purple_credential_manager_get_default(); - purple_credential_manager_write_password_async(manager, account, entry, - NULL, NULL, NULL); - } - - /* Store the new password in our connection. */ - purple_connection_set_password(gc, entry); - - /* Restart our connection. */ - jabber_auth_start_old(js); -} - -static void -auth_no_pass_cb(PurpleConnection *gc, G_GNUC_UNUSED PurpleRequestPage *page) { - /* TODO: the password prompt dialog doesn't get disposed if the account disconnects */ - PURPLE_ASSERT_CONNECTION_IS_VALID(gc); - - /* Disable the account as the user has cancelled connecting */ - purple_account_set_enabled(purple_connection_get_account(gc), FALSE); -} - -static void -auth_old_read_pass_cb(GObject *source, GAsyncResult *result, gpointer data) { - JabberStream *js = data; - PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(source); - GError *error = NULL; - char *password = NULL; - - password = purple_credential_manager_read_password_finish(manager, result, - &error); - if(password == NULL || error != NULL) { - PurpleAccount *account = NULL; - const char *message = "unknown error"; - - if(error != NULL) { - message = error->message; - } - - purple_debug_warning("jabber", "failed to read password from the " - "credential manager : %s", message); - - g_clear_error(&error); - - account = purple_connection_get_account(js->gc); - purple_account_request_password(account, G_CALLBACK(auth_old_pass_cb), - G_CALLBACK(auth_no_pass_cb), - js->gc); - - return; - } - - /* Save the password in the connection. */ - purple_connection_set_password(js->gc, password); - purple_str_wipe(password); - - /* Restart the authentication process. */ - jabber_auth_start_old(js); -} - -void -jabber_auth_start(JabberStream *js, PurpleXmlNode *packet) -{ - GSList *mechanisms = NULL; - GSList *l; - PurpleXmlNode *response = NULL; - PurpleXmlNode *mechs, *mechnode; - JabberSaslState state; - char *msg = NULL; - - mechs = purple_xmlnode_get_child(packet, "mechanisms"); - if(!mechs) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - return; - } - - for(mechnode = purple_xmlnode_get_child(mechs, "mechanism"); mechnode; - mechnode = purple_xmlnode_get_next_twin(mechnode)) - { - char *mech_name = purple_xmlnode_get_data(mechnode); - - if (mech_name && *mech_name) - mechanisms = g_slist_prepend(mechanisms, mech_name); - else - g_free(mech_name); - - } - - for (l = auth_mechs; l; l = l->next) { - JabberSaslMech *possible = l->data; - - /* Can we find this mechanism in the server's list? */ - if (g_slist_find_custom(mechanisms, possible->name, (GCompareFunc)strcmp)) { - js->auth_mech = possible; - break; - } - } - - g_slist_free_full(mechanisms, g_free); - - if (js->auth_mech == NULL) { - /* Found no good mechanisms... */ - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - _("Server does not use any supported authentication method")); - return; - } - - state = js->auth_mech->start(js, mechs, &response, &msg); - if (state == JABBER_SASL_STATE_FAIL) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - msg ? msg : _("Unknown Error")); - } else if (response) { - jabber_send(js, response); - purple_xmlnode_free(response); - } - - g_free(msg); -} - -static void -auth_old_result_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - if (type == JABBER_IQ_RESULT) { - jabber_stream_set_state(js, JABBER_STREAM_POST_AUTH); - jabber_disco_items_server(js); - } else { - PurpleAccount *account; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - char *msg = jabber_parse_error(js, packet, &reason); - PurpleXmlNode *error; - const char *err_code; - - account = purple_connection_get_account(js->gc); - - /* FIXME: Why is this not in jabber_parse_error? */ - if((error = purple_xmlnode_get_child(packet, "error")) && - (err_code = purple_xmlnode_get_attrib(error, "code")) && - purple_strequal(err_code, "401")) { - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - /* Clear the password if it isn't being saved */ - if(!purple_account_get_remember_password(account)) { - PurpleCredentialManager *manager = NULL; - - manager = purple_credential_manager_get_default(); - - purple_credential_manager_clear_password_async(manager, account, - NULL, NULL, - NULL); - } - } - - purple_connection_error(js->gc, reason, msg); - g_free(msg); - } -} - -static void -auth_old_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - JabberIq *iq; - PurpleXmlNode *query, *x; - const char *pw = purple_connection_get_password(js->gc); - - if (type == JABBER_IQ_ERROR) { - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - char *msg = jabber_parse_error(js, packet, &reason); - purple_connection_error(js->gc, reason, msg); - g_free(msg); - } else if (type == JABBER_IQ_RESULT) { - query = purple_xmlnode_get_child(packet, "query"); - if (js->stream_id && *js->stream_id && - purple_xmlnode_get_child(query, "digest")) { - char *s, *hash; - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); - query = purple_xmlnode_get_child(iq->node, "query"); - x = purple_xmlnode_new_child(query, "username"); - purple_xmlnode_insert_data(x, js->user->node, -1); - x = purple_xmlnode_new_child(query, "resource"); - purple_xmlnode_insert_data(x, js->user->resource, -1); - - x = purple_xmlnode_new_child(query, "digest"); - s = g_strdup_printf("%s%s", js->stream_id, pw); - hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1, - s, -1); - purple_xmlnode_insert_data(x, hash, -1); - g_free(hash); - g_free(s); - jabber_iq_set_callback(iq, auth_old_result_cb, NULL); - jabber_iq_send(iq); - } else if ((x = purple_xmlnode_get_child(query, "crammd5"))) { - /* For future reference, this appears to be a custom OS X extension - * to non-SASL authentication. - */ - const char *challenge; - gchar *digest; - - /* Calculate the MHAC-MD5 digest */ - challenge = purple_xmlnode_get_attrib(x, "challenge"); - digest = g_compute_hmac_for_string(G_CHECKSUM_MD5, - (guchar *)pw, strlen(pw), - challenge, -1); - - g_return_if_fail(digest != NULL); - - /* Create the response query */ - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); - query = purple_xmlnode_get_child(iq->node, "query"); - - x = purple_xmlnode_new_child(query, "username"); - purple_xmlnode_insert_data(x, js->user->node, -1); - x = purple_xmlnode_new_child(query, "resource"); - purple_xmlnode_insert_data(x, js->user->resource, -1); - - x = purple_xmlnode_new_child(query, "crammd5"); - - purple_xmlnode_insert_data(x, digest, 32); - g_free(digest); - - jabber_iq_set_callback(iq, auth_old_result_cb, NULL); - jabber_iq_send(iq); - - } else if(purple_xmlnode_get_child(query, "password")) { - PurpleAccount *account = purple_connection_get_account(js->gc); - if(!jabber_stream_is_ssl(js) && !purple_account_get_bool(account, - "auth_plain_in_clear", FALSE)) - { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), - purple_contact_info_get_username(info)); - purple_request_yes_no(js->gc, _("Plaintext Authentication"), - _("Plaintext Authentication"), - msg, - 1, - purple_request_cpar_from_account(account), - account, allow_plaintext_auth, - disallow_plaintext_auth); - g_free(msg); - return; - } - finish_plaintext_authentication(js); - } else { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - _("Server does not use any supported authentication method")); - return; - } - } -} - -void jabber_auth_start_old(JabberStream *js) -{ - PurpleAccount *account; - JabberIq *iq; - PurpleXmlNode *query, *username; - - account = purple_connection_get_account(js->gc); - - /* - * We can end up here without encryption if the server doesn't support - * <stream:features/> and we're not using old-style SSL. If the user - * is requiring SSL/TLS, we need to enforce it. - */ - if (!jabber_stream_is_ssl(js) && - purple_strequal("require_tls", - purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("You require encryption, but it is not available on this server.")); - return; - } - - /* - * IQ Auth doesn't have support for resource binding, so we need to pick a - * default resource so it will work properly. jabberd14 throws an error and - * iChat server just fails silently. - */ - if (!js->user->resource || *js->user->resource == '\0') { - g_free(js->user->resource); - js->user->resource = g_strdup("Home"); - } - - /* With Cyrus SASL, passwords are optional for this protocol. So, we need to - * do our own password prompting here - */ - - if (!purple_connection_get_password(js->gc)) { - PurpleCredentialManager *manager = NULL; - - manager = purple_credential_manager_get_default(); - - purple_credential_manager_read_password_async(manager, account, NULL, - auth_old_read_pass_cb, - js); - return; - } - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth"); - - query = purple_xmlnode_get_child(iq->node, "query"); - username = purple_xmlnode_new_child(query, "username"); - purple_xmlnode_insert_data(username, js->user->node, -1); - - jabber_iq_set_callback(iq, auth_old_cb, NULL); - - jabber_iq_send(iq); -} - -void -jabber_auth_handle_challenge(JabberStream *js, PurpleXmlNode *packet) -{ - const char *ns = purple_xmlnode_get_namespace(packet); - - if (!purple_strequal(ns, NS_XMPP_SASL)) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - return; - } - - if (js->auth_mech && js->auth_mech->handle_challenge) { - PurpleXmlNode *response = NULL; - char *msg = NULL; - JabberSaslState state = js->auth_mech->handle_challenge(js, packet, &response, &msg); - if (state == JABBER_SASL_STATE_FAIL) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - msg ? msg : _("Invalid challenge from server")); - } else if (response) { - jabber_send(js, response); - purple_xmlnode_free(response); - } - - g_free(msg); - } else - purple_debug_warning("jabber", "Received unexpected (and unhandled) <challenge/>\n"); -} - -void jabber_auth_handle_success(JabberStream *js, PurpleXmlNode *packet) -{ - const char *ns = purple_xmlnode_get_namespace(packet); - - if (!purple_strequal(ns, NS_XMPP_SASL)) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - return; - } - - if (js->auth_mech && js->auth_mech->handle_success) { - char *msg = NULL; - JabberSaslState state = js->auth_mech->handle_success(js, packet, &msg); - - if (state == JABBER_SASL_STATE_FAIL) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - msg ? msg : _("Invalid response from server")); - return; - } else if (state == JABBER_SASL_STATE_CONTINUE) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - msg ? msg : _("Server thinks authentication is complete, but client does not")); - return; - } - - g_free(msg); - } - - /* - * The stream will be reinitialized later in jabber_recv_cb_ssl() or - * jabber_bosh_connection_send. - */ - js->reinit = TRUE; - jabber_stream_set_state(js, JABBER_STREAM_POST_AUTH); -} - -void jabber_auth_handle_failure(JabberStream *js, PurpleXmlNode *packet) -{ - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - char *msg = NULL; - - if (js->auth_mech && js->auth_mech->handle_failure) { - PurpleXmlNode *stanza = NULL; - JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg); - - if (state != JABBER_SASL_STATE_FAIL) { - if (stanza) { - jabber_send(js, stanza); - purple_xmlnode_free(stanza); - } - - return; - } - } - - if (!msg) - msg = jabber_parse_error(js, packet, &reason); - - if (!msg) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - } else { - purple_connection_error(js->gc, reason, msg); - g_free(msg); - } -} - -static gint compare_mech(gconstpointer a, gconstpointer b) -{ - const JabberSaslMech *mech_a = a; - const JabberSaslMech *mech_b = b; - - /* higher priority comes *before* lower priority in the list */ - if (mech_a->priority > mech_b->priority) - return -1; - else if (mech_a->priority < mech_b->priority) - return 1; - /* This really shouldn't happen */ - return 0; -} - -static void -jabber_auth_add_mech(JabberSaslMech *mech) { - auth_mechs = g_slist_insert_sorted(auth_mechs, mech, compare_mech); -} - -void jabber_auth_init(void) -{ - JabberSaslMech **tmp; - gint count, i; - - jabber_auth_add_mech(jabber_auth_get_plain_mech()); - jabber_auth_add_mech(jabber_auth_get_digest_md5_mech()); -#ifdef HAVE_WEBEX_TOKEN - jabber_auth_add_mech(jabber_auth_get_webex_token_mech()); -#endif - - tmp = jabber_auth_get_scram_mechs(&count); - for (i = 0; i < count; ++i) - jabber_auth_add_mech(tmp[i]); -} - -void jabber_auth_uninit(void) -{ - g_clear_slist(&auth_mechs, NULL); -}
--- a/libpurple/protocols/jabber/auth.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * @file auth.h Authentication routines - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_AUTH_H -#define PURPLE_JABBER_AUTH_H - -typedef struct _JabberSaslMech JabberSaslMech; - -#include <purple.h> - -#include "jabber.h" - -typedef enum { - JABBER_SASL_STATE_FAIL = -1, /* Abort, Retry, Fail? */ - JABBER_SASL_STATE_OK = 0, /* Hooray! */ - JABBER_SASL_STATE_CONTINUE = 1 /* More authentication required */ -} JabberSaslState; - -struct _JabberSaslMech { - gint8 priority; /* Higher priority will be tried before lower priority */ - const gchar *name; - JabberSaslState (*start)(JabberStream *js, PurpleXmlNode *mechanisms, PurpleXmlNode **reply, char **msg); - JabberSaslState (*handle_challenge)(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **reply, char **msg); - JabberSaslState (*handle_success)(JabberStream *js, PurpleXmlNode *packet, char **msg); - JabberSaslState (*handle_failure)(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **reply, char **msg); - void (*dispose)(JabberStream *js); -}; - -void jabber_auth_start(JabberStream *js, PurpleXmlNode *packet); -void jabber_auth_start_old(JabberStream *js); -void jabber_auth_handle_challenge(JabberStream *js, PurpleXmlNode *packet); -void jabber_auth_handle_success(JabberStream *js, PurpleXmlNode *packet); -void jabber_auth_handle_failure(JabberStream *js, PurpleXmlNode *packet); - -JabberSaslMech *jabber_auth_get_plain_mech(void); -JabberSaslMech *jabber_auth_get_digest_md5_mech(void); -JabberSaslMech **jabber_auth_get_scram_mechs(gint *count); -JabberSaslMech *jabber_auth_get_webex_token_mech(void); - -void jabber_auth_init(void); -void jabber_auth_uninit(void); - -#endif /* PURPLE_JABBER_AUTH_H */
--- a/libpurple/protocols/jabber/auth_digest_md5.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "auth_digest_md5.h" -#include "auth.h" -#include "jabber.h" - -static JabberSaslState -digest_md5_start(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED PurpleXmlNode *packet, PurpleXmlNode **response, - G_GNUC_UNUSED char **error) -{ - PurpleXmlNode *auth = purple_xmlnode_new("auth"); - purple_xmlnode_set_namespace(auth, NS_XMPP_SASL); - purple_xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5"); - - *response = auth; - return JABBER_SASL_STATE_CONTINUE; -} - -/* Parts of this algorithm are inspired by stuff in libgsasl */ -GHashTable* jabber_auth_digest_md5_parse(const char *challenge) -{ - const char *token_start, *val_start, *val_end, *cur; - GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - - cur = challenge; - while(*cur != '\0') { - /* Find the end of the token */ - gboolean in_quotes = FALSE; - char *name, *value = NULL; - token_start = cur; - while (*cur != '\0' && (in_quotes || *cur != ',')) { - if (*cur == '"') - in_quotes = !in_quotes; - cur++; - } - - /* Find start of value. */ - val_start = strchr(token_start, '='); - if (val_start == NULL || val_start > cur) - val_start = cur; - - if (token_start != val_start) { - name = g_strndup(token_start, val_start - token_start); - - if (val_start != cur) { - val_start++; - while (val_start != cur && (*val_start == ' ' || *val_start == '\t' - || *val_start == '\r' || *val_start == '\n' - || *val_start == '"')) - val_start++; - - val_end = cur; - while (val_end >= val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t' - || *val_end == '\r' || *val_end == '\n' - || *val_end == '"' || *val_end == '\0')) - val_end--; - - if (val_end - val_start + 1 >= 0) - value = g_strndup(val_start, val_end - val_start + 1); - } - - g_hash_table_replace(ret, name, value); - } - - /* Find the start of the next token, if there is one */ - if (*cur != '\0') { - cur++; - while (*cur == ' ' || *cur == ',' || *cur == '\t' - || *cur == '\r' || *cur == '\n') - cur++; - } - } - - return ret; -} - -static char * -generate_response_value(JabberID *jid, const char *passwd, const char *nonce, - const char *cnonce, const char *a2, const char *realm) -{ - GChecksum *hash; - gsize digest_len = 16; - - gchar *a1, *convnode=NULL, *convpasswd = NULL, *ha1, *ha2, *kd, *x, *z; - - if((convnode = g_convert(jid->node, -1, "iso-8859-1", "utf-8", - NULL, NULL, NULL)) == NULL) { - convnode = g_strdup(jid->node); - } - if(passwd && ((convpasswd = g_convert(passwd, -1, "iso-8859-1", - "utf-8", NULL, NULL, NULL)) == NULL)) { - convpasswd = g_strdup(passwd); - } - - hash = g_checksum_new(G_CHECKSUM_MD5); - - x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd ? convpasswd : ""); - g_checksum_update(hash, (const guchar *)x, -1); - - a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce); - - g_checksum_get_digest(hash, (guint8 *)a1, &digest_len); - g_checksum_free(hash); - - ha1 = g_compute_checksum_for_string(G_CHECKSUM_MD5, a1, -1); - ha2 = g_compute_checksum_for_string(G_CHECKSUM_MD5, a2, -1); - - kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2); - - z = g_compute_checksum_for_string(G_CHECKSUM_MD5, kd, -1); - - g_free(convnode); - g_free(convpasswd); - g_free(x); - g_free(a1); - g_free(ha1); - g_free(ha2); - g_free(kd); - - return z; -} - -static JabberSaslState -digest_md5_handle_challenge(JabberStream *js, PurpleXmlNode *packet, - PurpleXmlNode **response, char **msg) -{ - PurpleXmlNode *reply = NULL; - char *enc_in = purple_xmlnode_get_data(packet); - char *dec_in; - char *enc_out; - gsize size = 0; - GHashTable *parts; - JabberSaslState state = JABBER_SASL_STATE_CONTINUE; - - if (!enc_in) { - *msg = g_strdup(_("Invalid response from server")); - return JABBER_SASL_STATE_FAIL; - } - - dec_in = (char *)g_base64_decode(enc_in, &size); - purple_debug_misc("jabber", "decoded challenge (%" - G_GSIZE_FORMAT "): %s\n", - size, - dec_in); - - parts = jabber_auth_digest_md5_parse(dec_in); - - if (g_hash_table_lookup(parts, "rspauth")) { - char *rspauth = g_hash_table_lookup(parts, "rspauth"); - char *expected_rspauth = js->auth_mech_data; - - if (rspauth && purple_strequal(rspauth, expected_rspauth)) { - reply = purple_xmlnode_new("response"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - } else { - *msg = g_strdup(_("Invalid challenge from server")); - state = JABBER_SASL_STATE_FAIL; - } - g_free(js->auth_mech_data); - js->auth_mech_data = NULL; - } else { - /* assemble a response, and send it */ - /* see RFC 2831 */ - char *realm; - char *nonce; - - /* Make sure the auth string contains everything that should be there. - This isn't everything in RFC2831, but it is what we need. */ - - nonce = g_hash_table_lookup(parts, "nonce"); - - /* we're actually supposed to prompt the user for a realm if - * the server doesn't send one, but that really complicates things, - * so i'm not gonna worry about it until is poses a problem to - * someone, or I get really bored */ - realm = g_hash_table_lookup(parts, "realm"); - if(!realm) - realm = js->user->domain; - - if (nonce == NULL || realm == NULL) { - *msg = g_strdup(_("Invalid challenge from server")); - state = JABBER_SASL_STATE_FAIL; - } else { - GString *response = g_string_new(""); - char *a2; - char *auth_resp; - char *cnonce; - - cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), - g_random_int()); - - a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); - auth_resp = generate_response_value(js->user, - purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); - g_free(a2); - - a2 = g_strdup_printf(":xmpp/%s", realm); - js->auth_mech_data = generate_response_value(js->user, - purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); - g_free(a2); - - g_string_append_printf(response, "username=\"%s\"", js->user->node); - g_string_append_printf(response, ",realm=\"%s\"", realm); - g_string_append_printf(response, ",nonce=\"%s\"", nonce); - g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); - g_string_append_printf(response, ",nc=00000001"); - g_string_append_printf(response, ",qop=auth"); - g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); - g_string_append_printf(response, ",response=%s", auth_resp); - g_string_append_printf(response, ",charset=utf-8"); - - g_free(auth_resp); - g_free(cnonce); - - enc_out = g_base64_encode((guchar *)response->str, response->len); - - purple_debug_misc("jabber", "decoded response (%" - G_GSIZE_FORMAT "): %s\n", - response->len, response->str); - - reply = purple_xmlnode_new("response"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - purple_xmlnode_insert_data(reply, enc_out, -1); - - g_free(enc_out); - - g_string_free(response, TRUE); - } - } - - g_free(enc_in); - g_free(dec_in); - g_hash_table_destroy(parts); - - *response = reply; - return state; -} - -static void -digest_md5_dispose(JabberStream *js) -{ - g_free(js->auth_mech_data); - js->auth_mech_data = NULL; -} - -static JabberSaslMech digest_md5_mech = { - 10, /* priority */ - "DIGEST-MD5", /* name */ - digest_md5_start, - digest_md5_handle_challenge, - NULL, /* handle_success */ - NULL, /* handle_failure */ - digest_md5_dispose, -}; - -JabberSaslMech *jabber_auth_get_digest_md5_mech(void) -{ - return &digest_md5_mech; -}
--- a/libpurple/protocols/jabber/auth_digest_md5.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * @file auth_digest_md5.h Implementation of SASL DIGEST-MD5 authentication - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_AUTH_DIGEST_MD5_H -#define PURPLE_JABBER_AUTH_DIGEST_MD5_H - -#include "jabber.h" - -/* - * Every function in this file is ONLY exposed for tests. - * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR. - */ - -/* - * Parse a DIGEST-MD5 challenge. - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -GHashTable *jabber_auth_digest_md5_parse(const char *challenge); - -#endif /* PURPLE_JABBER_AUTH_DIGEST_MD5_H */
--- a/libpurple/protocols/jabber/auth_plain.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "jabber.h" -#include "auth.h" - -static PurpleXmlNode *finish_plaintext_authentication(JabberStream *js) -{ - PurpleXmlNode *auth; - GString *response; - gchar *enc_out; - - auth = purple_xmlnode_new("auth"); - purple_xmlnode_set_namespace(auth, NS_XMPP_SASL); - - response = g_string_new(""); - response = g_string_append_c(response, '\0'); - response = g_string_append(response, js->user->node); - response = g_string_append_c(response, '\0'); - response = g_string_append(response, - purple_connection_get_password(js->gc)); - - enc_out = g_base64_encode((guchar *)response->str, response->len); - - purple_xmlnode_set_attrib(auth, "mechanism", "PLAIN"); - purple_xmlnode_insert_data(auth, enc_out, -1); - g_free(enc_out); - g_string_free(response, TRUE); - - return auth; -} - -static void allow_plaintext_auth(PurpleAccount *account) -{ - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = purple_connection_get_protocol_data(gc); - PurpleXmlNode *response; - - purple_account_set_bool(account, "auth_plain_in_clear", TRUE); - - response = finish_plaintext_authentication(js); - jabber_send(js, response); - purple_xmlnode_free(response); -} - -static void disallow_plaintext_auth(PurpleAccount *account) -{ - purple_connection_error(purple_account_get_connection(account), - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Server requires plaintext authentication over an unencrypted stream")); -} - -static JabberSaslState -jabber_plain_start(JabberStream *js, G_GNUC_UNUSED PurpleXmlNode *packet, - PurpleXmlNode **response, G_GNUC_UNUSED char **error) -{ - PurpleAccount *account = purple_connection_get_account(js->gc); - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - char *msg; - - if (jabber_stream_is_ssl(js) || purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) { - *response = finish_plaintext_authentication(js); - return JABBER_SASL_STATE_OK; - } - - msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), - purple_contact_info_get_username(info)); - purple_request_yes_no(js->gc, _("Plaintext Authentication"), - _("Plaintext Authentication"), - msg, - 1, - purple_request_cpar_from_account(account), - account, allow_plaintext_auth, disallow_plaintext_auth); - g_free(msg); - return JABBER_SASL_STATE_CONTINUE; -} - -static JabberSaslMech plain_mech = { - 0, /* priority */ - "PLAIN", /* name */ - jabber_plain_start, - NULL, /* handle_challenge */ - NULL, /* handle_success */ - NULL, /* handle_failure */ - NULL /* dispose */ -}; - -JabberSaslMech *jabber_auth_get_plain_mech(void) -{ - return &plain_mech; -}
--- a/libpurple/protocols/jabber/auth_scram.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,598 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "auth.h" -#include "auth_scram.h" - -static const JabberScramHash hashes[] = { - { "-SHA-1", G_CHECKSUM_SHA1 }, -}; - -static const JabberScramHash *mech_to_hash(const char *mech) -{ - gsize i; - - g_return_val_if_fail(mech != NULL && *mech != '\0', NULL); - - for (i = 0; i < G_N_ELEMENTS(hashes); ++i) { - if (strstr(mech, hashes[i].mech_substr)) - return &(hashes[i]); - } - - purple_debug_error("jabber", "Unknown SCRAM mechanism %s\n", mech); - g_return_val_if_reached(NULL); -} - -guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str, - GString *salt, guint iterations) -{ - GHmac *hmac; - gsize digest_len; - guchar *result; - guint i; - guchar *prev, *tmp; - - g_return_val_if_fail(hash != NULL, NULL); - g_return_val_if_fail(str != NULL && str->len > 0, NULL); - g_return_val_if_fail(salt != NULL && salt->len > 0, NULL); - g_return_val_if_fail(iterations > 0, NULL); - - digest_len = g_checksum_type_get_length(hash->type); - prev = g_new0(guchar, digest_len); - tmp = g_new0(guchar, digest_len); - result = g_new0(guchar, digest_len); - - hmac = g_hmac_new(hash->type, (guchar *)str->str, str->len); - - /* Append INT(1), a four-octet encoding of the integer 1, most significant - * octet first. */ - g_string_append_len(salt, "\0\0\0\1", 4); - - /* Compute U0 */ - g_hmac_update(hmac, (guchar *)salt->str, salt->len); - g_hmac_get_digest(hmac, result, &digest_len); - g_hmac_unref(hmac); - - memcpy(prev, result, digest_len); - - /* Compute U1...Ui */ - for (i = 1; i < iterations; ++i) { - guint j; - hmac = g_hmac_new(hash->type, (guchar *)str->str, str->len); - g_hmac_update(hmac, prev, digest_len); - g_hmac_get_digest(hmac, tmp, &digest_len); - g_hmac_unref(hmac); - - for (j = 0; j < digest_len; ++j) - result[j] ^= tmp[j]; - - memcpy(prev, tmp, digest_len); - } - - g_free(tmp); - g_free(prev); - return result; -} - -/* - * Helper functions for doing the SCRAM calculations. The first argument - * is the hash algorithm. All buffers must be of the appropriate size - * according to the JabberScramHash. - * - * "str" is a NULL-terminated string for jabber_scram_hmac(). - * - * Needless to say, these are fragile. - */ -static void -jabber_scram_hmac(const JabberScramHash *hash, guchar *out, const guchar *key, const gchar *str) -{ - GHmac *hmac; - gsize digest_len = g_checksum_type_get_length(hash->type); - - hmac = g_hmac_new(hash->type, key, digest_len); - g_hmac_update(hmac, (guchar *)str, -1); - g_hmac_get_digest(hmac, out, &digest_len); - g_hmac_unref(hmac); -} - -static void -jabber_scram_hash(const JabberScramHash *hash, guchar *out, const guchar *data) -{ - GChecksum *checksum; - gsize digest_len = g_checksum_type_get_length(hash->type); - - checksum = g_checksum_new(hash->type); - g_checksum_update(checksum, data, digest_len); - g_checksum_get_digest(checksum, out, &digest_len); - g_checksum_free(checksum); -} - -gboolean -jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations) -{ - guint hash_len = g_checksum_type_get_length(data->hash->type); - guint i; - - GString *pass = g_string_new(data->password); - - guchar *salted_password; - guchar *client_key, *stored_key, *client_signature, *server_key; - - data->client_proof = g_string_sized_new(hash_len); - data->client_proof->len = hash_len; - data->server_signature = g_string_sized_new(hash_len); - data->server_signature->len = hash_len; - - salted_password = jabber_scram_hi(data->hash, pass, salt, iterations); - - memset(pass->str, 0, pass->allocated_len); - g_string_free(pass, TRUE); - - if (!salted_password) - return FALSE; - - client_key = g_new0(guchar, hash_len); - stored_key = g_new0(guchar, hash_len); - client_signature = g_new0(guchar, hash_len); - server_key = g_new0(guchar, hash_len); - - /* client_key = HMAC(salted_password, "Client Key") */ - jabber_scram_hmac(data->hash, client_key, salted_password, "Client Key"); - /* server_key = HMAC(salted_password, "Server Key") */ - jabber_scram_hmac(data->hash, server_key, salted_password, "Server Key"); - g_free(salted_password); - - /* stored_key = HASH(client_key) */ - jabber_scram_hash(data->hash, stored_key, client_key); - - /* client_signature = HMAC(stored_key, auth_message) */ - jabber_scram_hmac(data->hash, client_signature, stored_key, data->auth_message->str); - /* server_signature = HMAC(server_key, auth_message) */ - jabber_scram_hmac(data->hash, (guchar *)data->server_signature->str, server_key, data->auth_message->str); - - /* client_proof = client_key XOR client_signature */ - for (i = 0; i < hash_len; ++i) - data->client_proof->str[i] = client_key[i] ^ client_signature[i]; - - g_free(server_key); - g_free(client_signature); - g_free(stored_key); - g_free(client_key); - - return TRUE; -} - -static gboolean -parse_server_step1(JabberScramData *data, const char *challenge, - gchar **out_nonce, GString **out_salt, guint *out_iterations) -{ - char **tokens; - char *token, *decoded, *tmp; - gsize len; - char *nonce = NULL; - GString *salt = NULL; - guint iterations; - - tokens = g_strsplit(challenge, ",", -1); - if (tokens == NULL) - return FALSE; - - token = tokens[0]; - if (token[0] != 'r' || token[1] != '=') - goto err; - - /* Ensure that the first cnonce_len bytes of the nonce are the original - * cnonce we sent to the server. - */ - if (0 != strncmp(data->cnonce, token + 2, strlen(data->cnonce))) - goto err; - - nonce = g_strdup(token + 2); - - /* The Salt, base64-encoded */ - token = tokens[1]; - if (token[0] != 's' || token[1] != '=') - goto err; - - decoded = (gchar *)g_base64_decode(token + 2, &len); - if (!decoded || *decoded == '\0') { - g_free(decoded); - goto err; - } - salt = g_string_new_len(decoded, len); - g_free(decoded); - - /* The iteration count */ - token = tokens[2]; - if (token[0] != 'i' || token[1] != '=' || token[2] == '\0') - goto err; - - /* Validate the string */ - for (tmp = token + 2; *tmp; ++tmp) - if (!g_ascii_isdigit(*tmp)) - goto err; - - iterations = strtoul(token + 2, NULL, 10); - - g_strfreev(tokens); - *out_nonce = nonce; - *out_salt = salt; - *out_iterations = iterations; - return TRUE; - -err: - g_free(nonce); - if (salt) - g_string_free(salt, TRUE); - g_strfreev(tokens); - return FALSE; -} - -static gboolean -parse_server_step2(G_GNUC_UNUSED JabberScramData *data, const char *challenge, - char **out_verifier) -{ - char **tokens; - char *token; - - tokens = g_strsplit(challenge, ",", -1); - if (tokens == NULL) - return FALSE; - - token = tokens[0]; - if (token[0] != 'v' || token[1] != '=' || token[2] == '\0') { - g_strfreev(tokens); - return FALSE; - } - - *out_verifier = g_strdup(token + 2); - g_strfreev(tokens); - return TRUE; -} - -gboolean -jabber_scram_feed_parser(JabberScramData *data, gchar *in, gchar **out) -{ - gboolean ret; - - g_return_val_if_fail(data != NULL, FALSE); - - g_string_append_c(data->auth_message, ','); - g_string_append(data->auth_message, in); - - if (data->step == 1) { - gchar *nonce, *proof; - GString *salt; - guint iterations; - - ret = parse_server_step1(data, in, &nonce, &salt, &iterations); - if (!ret) - return FALSE; - - g_string_append_c(data->auth_message, ','); - - /* "biws" is the base64 encoding of "n,,". I promise. */ - g_string_append_printf(data->auth_message, "c=%s,r=%s", "biws", nonce); -#ifdef CHANNEL_BINDING -#error fix this -#endif - - ret = jabber_scram_calc_proofs(data, salt, iterations); - - g_string_free(salt, TRUE); - salt = NULL; - if (!ret) { - g_free(nonce); - return FALSE; - } - - proof = g_base64_encode((guchar *)data->client_proof->str, data->client_proof->len); - *out = g_strdup_printf("c=%s,r=%s,p=%s", "biws", nonce, proof); - g_free(nonce); - g_free(proof); - } else if (data->step == 2) { - gchar *server_sig, *enc_server_sig; - gsize len; - - ret = parse_server_step2(data, in, &enc_server_sig); - if (!ret) - return FALSE; - - server_sig = (gchar *)g_base64_decode(enc_server_sig, &len); - g_free(enc_server_sig); - - if (server_sig == NULL || len != data->server_signature->len) { - g_free(server_sig); - return FALSE; - } - - if (0 != memcmp(server_sig, data->server_signature->str, len)) { - g_free(server_sig); - return FALSE; - } - g_free(server_sig); - - *out = NULL; - } else { - purple_debug_error("jabber", "SCRAM: There is no step %d\n", data->step); - return FALSE; - } - - return TRUE; -} - -static gchar *escape_username(const gchar *in) -{ - gchar *tmp, *tmp2; - - tmp = purple_strreplace(in, "=", "=3D"); - tmp2 = purple_strreplace(tmp, ",", "=2C"); - g_free(tmp); - return tmp2; -} - -static JabberSaslState -scram_start(JabberStream *js, G_GNUC_UNUSED PurpleXmlNode *mechanisms, - PurpleXmlNode **out, char **error) -{ - PurpleXmlNode *reply; - JabberScramData *data; - guint64 cnonce; -#ifdef CHANNEL_BINDING - gboolean binding_supported = TRUE; -#endif - gchar *dec_out, *enc_out; - gchar *prepped_node, *tmp; - gchar *prepped_pass; - - prepped_node = jabber_saslprep(js->user->node); - if (!prepped_node) { - *error = g_strdup(_("Unable to canonicalize username")); - return JABBER_SASL_STATE_FAIL; - } - - tmp = escape_username(prepped_node); - g_free(prepped_node); - prepped_node = tmp; - - prepped_pass = jabber_saslprep(purple_connection_get_password(js->gc)); - if (!prepped_pass) { - g_free(prepped_node); - *error = g_strdup(_("Unable to canonicalize password")); - return JABBER_SASL_STATE_FAIL; - } - - data = js->auth_mech_data = g_new0(JabberScramData, 1); - data->hash = mech_to_hash(js->auth_mech->name); - data->password = prepped_pass; - -#ifdef CHANNEL_BINDING - if (strstr(js->auth_mech_name, "-PLUS")) - data->channel_binding = TRUE; -#endif - cnonce = ((guint64)g_random_int() << 32) | g_random_int(); - data->cnonce = g_base64_encode((guchar *)&cnonce, sizeof(cnonce)); - - data->auth_message = g_string_new(NULL); - g_string_printf(data->auth_message, "n=%s,r=%s", - prepped_node, data->cnonce); - g_free(prepped_node); - - data->step = 1; - - reply = purple_xmlnode_new("auth"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - purple_xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name); - - /* TODO: Channel binding */ - dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str); - enc_out = g_base64_encode((guchar *)dec_out, strlen(dec_out)); - purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out); - - purple_xmlnode_insert_data(reply, enc_out, -1); - - g_free(enc_out); - g_free(dec_out); - - *out = reply; - return JABBER_SASL_STATE_CONTINUE; -} - -static JabberSaslState -scram_handle_challenge(JabberStream *js, PurpleXmlNode *challenge, PurpleXmlNode **out, char **error) -{ - JabberScramData *data = js->auth_mech_data; - PurpleXmlNode *reply; - gchar *enc_in, *dec_in = NULL; - gchar *enc_out = NULL, *dec_out = NULL; - gsize len; - JabberSaslState state = JABBER_SASL_STATE_FAIL; - - enc_in = purple_xmlnode_get_data(challenge); - if (!enc_in || *enc_in == '\0') { - reply = purple_xmlnode_new("abort"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - data->step = -1; - *error = g_strdup(_("Invalid challenge from server")); - goto out; - } - - dec_in = (gchar *)g_base64_decode(enc_in, &len); - if (!dec_in || len != strlen(dec_in)) { - /* Danger afoot; SCRAM shouldn't contain NUL bytes */ - reply = purple_xmlnode_new("abort"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - data->step = -1; - *error = g_strdup(_("Malicious challenge from server")); - goto out; - } - - purple_debug_misc("jabber", "decoded challenge: %s\n", dec_in); - - if (!jabber_scram_feed_parser(data, dec_in, &dec_out)) { - reply = purple_xmlnode_new("abort"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - data->step = -1; - *error = g_strdup(_("Invalid challenge from server")); - goto out; - } - - data->step += 1; - - reply = purple_xmlnode_new("response"); - purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); - - purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)"); - if (dec_out) { - enc_out = g_base64_encode((guchar *)dec_out, strlen(dec_out)); - purple_xmlnode_insert_data(reply, enc_out, -1); - } - - state = JABBER_SASL_STATE_CONTINUE; - -out: - g_free(enc_in); - g_free(dec_in); - g_free(enc_out); - g_free(dec_out); - - *out = reply; - return state; -} - -static JabberSaslState -scram_handle_success(JabberStream *js, PurpleXmlNode *packet, char **error) -{ - JabberScramData *data = js->auth_mech_data; - char *enc_in, *dec_in; - char *dec_out = NULL; - gsize len; - - enc_in = purple_xmlnode_get_data(packet); - if (data->step != 3 && (!enc_in || *enc_in == '\0')) { - *error = g_strdup(_("Invalid challenge from server")); - g_free(enc_in); - return JABBER_SASL_STATE_FAIL; - } - - if (data->step == 3) { - /* - * If the server took the slow approach (sending the verifier - * as a challenge/response pair), we get here. - */ - g_free(enc_in); - return JABBER_SASL_STATE_OK; - } - - if (data->step != 2) { - *error = g_strdup(_("Unexpected response from server")); - g_free(enc_in); - return JABBER_SASL_STATE_FAIL; - } - - dec_in = (gchar *)g_base64_decode(enc_in, &len); - g_free(enc_in); - if (!dec_in || len != strlen(dec_in)) { - /* Danger afoot; SCRAM shouldn't contain NUL bytes */ - g_free(dec_in); - *error = g_strdup(_("Malicious challenge from server")); - return JABBER_SASL_STATE_FAIL; - } - - purple_debug_misc("jabber", "decoded success: %s\n", dec_in); - - if (!jabber_scram_feed_parser(data, dec_in, &dec_out) || dec_out != NULL) { - g_free(dec_in); - g_free(dec_out); - *error = g_strdup(_("Invalid challenge from server")); - return JABBER_SASL_STATE_FAIL; - } - - g_free(dec_in); - /* Hooray */ - return JABBER_SASL_STATE_OK; -} - -void jabber_scram_data_destroy(JabberScramData *data) -{ - g_free(data->cnonce); - if (data->auth_message) - g_string_free(data->auth_message, TRUE); - if (data->client_proof) - g_string_free(data->client_proof, TRUE); - if (data->server_signature) - g_string_free(data->server_signature, TRUE); - if (data->password) { - memset(data->password, 0, strlen(data->password)); - g_free(data->password); - } - - g_free(data); -} - -static void scram_dispose(JabberStream *js) -{ - if (js->auth_mech_data) { - jabber_scram_data_destroy(js->auth_mech_data); - js->auth_mech_data = NULL; - } -} - -static JabberSaslMech scram_sha1_mech = { - 50, /* priority */ - "SCRAM-SHA-1", /* name */ - scram_start, - scram_handle_challenge, - scram_handle_success, - NULL, /* handle_failure */ - scram_dispose -}; - -#ifdef CHANNEL_BINDING -/* With channel binding */ -static JabberSaslMech scram_sha1_plus_mech = { - scram_sha1_mech.priority + 1, /* priority */ - "SCRAM-SHA-1-PLUS", /* name */ - scram_start, - scram_handle_challenge, - scram_handle_success, - NULL, /* handle_failure */ - scram_dispose -}; -#endif - -JabberSaslMech **jabber_auth_get_scram_mechs(gint *count) -{ - static JabberSaslMech *mechs[] = { - &scram_sha1_mech, -#ifdef CHANNEL_BINDING - &scram_sha1_plus_mech, -#endif - }; - - *count = G_N_ELEMENTS(mechs); - return mechs; -}
--- a/libpurple/protocols/jabber/auth_scram.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/** - * @file auth_scram.h Implementation of SASL-SCRAM authentication - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_AUTH_SCRAM_H -#define PURPLE_JABBER_AUTH_SCRAM_H - -/* - * Every function in this file is ONLY exposed for tests. - * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR. - */ - -/* Per-connection state stored between messages. - * This is stored in js->auth_data_mech. - */ -typedef struct { - const char *mech_substr; - GChecksumType type; -} JabberScramHash; - -typedef struct { - const JabberScramHash *hash; - char *cnonce; - GString *auth_message; - - GString *client_proof; - GString *server_signature; - - gchar *password; - gboolean channel_binding; - int step; -} JabberScramData; - -#include "auth.h" - -/** - * Implements the Hi() function as described in the SASL-SCRAM I-D. - * - * @param hash The struct corresponding to the hash function to be used. - * @param str The string to perform the PBKDF2 operation on. - * @param salt The salt. - * @param iterations The number of iterations to perform. - * - * @returns A newly allocated string containing the result. The string is - * NOT null-terminated and its length is the length of the binary - * output of the hash function in-use. - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str, - GString *salt, guint iterations); - -/** - * Calculates the proofs as described in Section 3 of the SASL-SCRAM I-D. - * - * @param data A JabberScramData structure. hash and auth_message must be - * set. client_proof and server_signature will be set as a result - * of this function. - * @param salt The salt (as specified by the server) - * @param iterations The number of iterations to perform. - * - * @returns TRUE if the proofs were successfully calculated. FALSE otherwise. - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -gboolean jabber_scram_calc_proofs(JabberScramData *data, GString *salt, - guint iterations); - -/** - * Feed the algorithm with the data from the server. - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -gboolean jabber_scram_feed_parser(JabberScramData *data, gchar *in, gchar **out); - -/** - * Clean up and destroy the data struct - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -void jabber_scram_data_destroy(JabberScramData *data); - -#endif /* PURPLE_JABBER_AUTH_SCRAM_H */
--- a/libpurple/protocols/jabber/auth_webex.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <purple.h> - -#include "jabber.h" -#include "auth.h" - -static PurpleXmlNode *finish_webex_authentication(JabberStream *js) -{ - PurpleXmlNode *auth; - - auth = purple_xmlnode_new("auth"); - purple_xmlnode_set_namespace(auth, NS_XMPP_SASL); - - purple_xmlnode_set_attrib(auth, "mechanism", "WEBEX-TOKEN"); - purple_xmlnode_insert_data(auth, purple_connection_get_password(js->gc), -1); - - return auth; -} - -static JabberSaslState -jabber_webex_start(JabberStream *js, G_GNUC_UNUSED PurpleXmlNode *packet, - PurpleXmlNode **response, G_GNUC_UNUSED char **error) -{ - *response = finish_webex_authentication(js); - return JABBER_SASL_STATE_OK; -} - -static JabberSaslMech webex_token_mech = { - 101, /* priority */ - "WEBEX-TOKEN", /* name */ - jabber_webex_start, - NULL, /* handle_challenge */ - NULL, /* handle_success */ - NULL, /* handle_failure */ - NULL /* dispose */ -}; - -JabberSaslMech *jabber_auth_get_webex_token_mech(void) -{ - return &webex_token_mech; -}
--- a/libpurple/protocols/jabber/bosh.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,544 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include <libsoup/soup.h> - -#include "bosh.h" - -/* -TODO: test, what happens, if the http server (BOSH server) doesn't support -keep-alive (sends connection: close). -*/ - -#define JABBER_BOSH_SEND_DELAY 250 - -#define JABBER_BOSH_TIMEOUT 10 - -static gchar *jabber_bosh_useragent = NULL; - -struct _PurpleJabberBOSHConnection { - JabberStream *js; - SoupSession *payload_reqs; - - gchar *url; - gboolean is_ssl; - gboolean is_terminating; - - gchar *sid; - guint64 rid; /* Must be big enough to hold 2^53 - 1 */ - - GString *send_buff; - guint send_timer; -}; - -static SoupMessage *jabber_bosh_connection_http_request_new( - PurpleJabberBOSHConnection *conn, GString *data); -static void -jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn); -static void -jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn); - -void -jabber_bosh_init(void) -{ - PurpleUi *ui = purple_core_get_ui(); - const gchar *ui_name = NULL; - const gchar *ui_version = NULL; - - if(PURPLE_IS_UI(ui)) { - ui_name = purple_ui_get_name(ui); - ui_version = purple_ui_get_version(ui); - } - - if(ui_name) { - jabber_bosh_useragent = g_strdup_printf( - "%s%s%s (libpurple " VERSION ")", ui_name, - ui_version ? " " : "", ui_version ? ui_version : ""); - } else { - jabber_bosh_useragent = g_strdup("libpurple " VERSION); - } -} - -void jabber_bosh_uninit(void) -{ - g_clear_pointer(&jabber_bosh_useragent, g_free); -} - -PurpleJabberBOSHConnection* -jabber_bosh_connection_new(JabberStream *js, const gchar *url) -{ - PurpleJabberBOSHConnection *conn; - PurpleAccount *account; - GProxyResolver *resolver; - GError *error = NULL; - const gchar *scheme; - - account = purple_connection_get_account(js->gc); - resolver = purple_proxy_get_proxy_resolver(account, &error); - if (resolver == NULL) { - purple_debug_error("jabber-bosh", - "Unable to get account proxy resolver: %s", - error->message); - g_error_free(error); - return NULL; - } - - scheme = g_uri_peek_scheme(url); - if (scheme == NULL) { - purple_debug_error("jabber-bosh", "Unable to parse given BOSH URL: %s", - url); - g_object_unref(resolver); - return NULL; - } - - conn = g_new0(PurpleJabberBOSHConnection, 1); - conn->payload_reqs = soup_session_new_with_options( - "proxy-resolver", resolver, - "timeout", JABBER_BOSH_TIMEOUT + 2, - "user-agent", jabber_bosh_useragent, - NULL); - conn->url = g_strdup(url); - conn->js = js; - conn->is_ssl = g_str_equal(scheme, "https"); - conn->send_buff = g_string_new(NULL); - - /* - * Random 64-bit integer masked off by 2^52 - 1. - * - * This should produce a random integer in the range [0, 2^52). It's - * unlikely we'll send enough packets in one session to overflow the - * rid. - */ - conn->rid = (((guint64)g_random_int() << 32) | g_random_int()); - conn->rid &= 0xFFFFFFFFFFFFFLL; - - g_object_unref(resolver); - - jabber_bosh_connection_session_create(conn); - - return conn; -} - -void -jabber_bosh_connection_destroy(PurpleJabberBOSHConnection *conn) -{ - if (conn == NULL || conn->is_terminating) - return; - conn->is_terminating = TRUE; - - if (conn->sid != NULL) { - purple_debug_info("jabber-bosh", - "Terminating a session for %p\n", conn); - jabber_bosh_connection_send_now(conn); - } - - g_clear_handle_id(&conn->send_timer, g_source_remove); - - soup_session_abort(conn->payload_reqs); - - g_clear_object(&conn->payload_reqs); - g_string_free(conn->send_buff, TRUE); - conn->send_buff = NULL; - - g_free(conn->sid); - conn->sid = NULL; - g_free(conn->url); - conn->url = NULL; - - g_free(conn); -} - -gboolean -jabber_bosh_connection_is_ssl(const PurpleJabberBOSHConnection *conn) -{ - return conn->is_ssl; -} - -static PurpleXmlNode * -jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn, - SoupMessage *msg, GBytes *response_body, - GError *error) -{ - gconstpointer body = NULL; - gsize length = 0; - PurpleXmlNode *root; - const gchar *type; - - g_return_val_if_fail(conn != NULL, NULL); - g_return_val_if_fail(msg != NULL, NULL); - - if (conn->is_terminating || purple_account_is_disconnecting( - purple_connection_get_account(conn->js->gc))) - { - return NULL; - } - - if(error != NULL || - !SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) { - gchar *tmp = g_strdup_printf( - _("Unable to connect: %s"), - error ? error->message : soup_message_get_reason_phrase(msg)); - purple_connection_error(conn->js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return NULL; - } - - body = g_bytes_get_data(response_body, &length); - root = purple_xmlnode_from_str(body, length); - - type = purple_xmlnode_get_attrib(root, "type"); - if (purple_strequal(type, "terminate")) { - purple_connection_error(conn->js->gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH " - "connection manager terminated your session.")); - purple_xmlnode_free(root); - return NULL; - } - - return root; -} - -static void -jabber_bosh_connection_recv(GObject *source, GAsyncResult *result, - gpointer data) -{ - SoupMessage *msg = data; - PurpleJabberBOSHConnection *bosh_conn = NULL; - GBytes *response_body = NULL; - GError *error = NULL; - PurpleXmlNode *node, *child; - - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source), - result, &error); - bosh_conn = g_object_get_data(G_OBJECT(msg), "bosh-connection"); - - if (response_body != NULL && purple_debug_is_verbose() && - purple_debug_is_unsafe()) - { - const gchar *body = NULL; - gsize length = 0; - - body = g_bytes_get_data(response_body, &length); - purple_debug_misc("jabber-bosh", "received: %.*s", (int)length, body); - } - - node = jabber_bosh_connection_parse(bosh_conn, msg, response_body, error); - g_clear_pointer(&response_body, g_bytes_unref); - g_clear_error(&error); - g_clear_object(&msg); - - if (node == NULL) { - return; - } - - child = node->child; - while (child != NULL) { - /* jabber_process_packet might free child */ - PurpleXmlNode *next = child->next; - const gchar *xmlns; - - if (child->type != PURPLE_XMLNODE_TYPE_TAG) { - child = next; - continue; - } - - /* Workaround for non-compliant servers that don't stamp - * the right xmlns on these packets. See #11315. - */ - xmlns = purple_xmlnode_get_namespace(child); - if ((xmlns == NULL || purple_strequal(xmlns, NS_BOSH)) && - (purple_strequal(child->name, "iq") || - purple_strequal(child->name, "message") || - purple_strequal(child->name, "presence"))) - { - purple_xmlnode_set_namespace(child, NS_XMPP_CLIENT); - } - - jabber_process_packet(bosh_conn->js, &child); - - child = next; - } - - jabber_bosh_connection_send(bosh_conn, NULL); -} - -static void -jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn) -{ - SoupMessage *req; - GString *data; - - g_return_if_fail(conn != NULL); - - g_clear_handle_id(&conn->send_timer, g_source_remove); - - if (conn->sid == NULL) - return; - - data = g_string_new(NULL); - - /* missing parameters: route, from, ack */ - g_string_printf(data, "<body " - "rid='%" G_GUINT64_FORMAT "' " - "sid='%s' " - "xmlns='" NS_BOSH "' " - "xmlns:xmpp='" NS_XMPP_BOSH "' ", - ++conn->rid, conn->sid); - - if (conn->js->reinit && !conn->is_terminating) { - g_string_append(data, "xmpp:restart='true'/>"); - conn->js->reinit = FALSE; - } else { - if (conn->is_terminating) - g_string_append(data, "type='terminate' "); - g_string_append_c(data, '>'); - g_string_append_len(data, conn->send_buff->str, - conn->send_buff->len); - g_string_append(data, "</body>"); - g_string_set_size(conn->send_buff, 0); - } - - if (purple_debug_is_verbose() && purple_debug_is_unsafe()) - purple_debug_misc("jabber-bosh", "sending: %s\n", data->str); - - req = jabber_bosh_connection_http_request_new(conn, data); - - if (conn->is_terminating) { - soup_session_send_async(conn->payload_reqs, req, G_PRIORITY_DEFAULT, - NULL, NULL, NULL); - g_free(conn->sid); - conn->sid = NULL; - } else { - g_object_set_data(G_OBJECT(req), "bosh-connection", conn); - soup_session_send_and_read_async(conn->payload_reqs, req, - G_PRIORITY_DEFAULT, NULL, - jabber_bosh_connection_recv, req); - } -} - -static gboolean -jabber_bosh_connection_send_delayed(gpointer _conn) -{ - PurpleJabberBOSHConnection *conn = _conn; - - conn->send_timer = 0; - jabber_bosh_connection_send_now(conn); - - return FALSE; -} - -void -jabber_bosh_connection_send(PurpleJabberBOSHConnection *conn, - const gchar *data) -{ - g_return_if_fail(conn != NULL); - - if (data) - g_string_append(conn->send_buff, data); - - if (conn->send_timer == 0) { - conn->send_timer = g_timeout_add( - JABBER_BOSH_SEND_DELAY, - jabber_bosh_connection_send_delayed, conn); - } -} - -void -jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn) -{ - g_return_if_fail(conn != NULL); - - jabber_bosh_connection_send_now(conn); -} - -static gboolean -jabber_bosh_version_check(const gchar *version, int major_req, int minor_min) -{ - const gchar *dot; - int major, minor = 0; - - if (version == NULL) - return FALSE; - - major = atoi(version); - dot = strchr(version, '.'); - if (dot) - minor = atoi(dot + 1); - - if (major != major_req) - return FALSE; - if (minor < minor_min) - return FALSE; - return TRUE; -} - -static void -jabber_bosh_connection_session_created(GObject *source, GAsyncResult *result, - gpointer data) -{ - SoupMessage *msg = data; - PurpleJabberBOSHConnection *bosh_conn = NULL; - GBytes *response_body = NULL; - GError *error = NULL; - PurpleXmlNode *node, *features; - const gchar *sid, *ver, *inactivity_str; - int inactivity = 0; - - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source), - result, &error); - bosh_conn = g_object_get_data(G_OBJECT(msg), "bosh-connection"); - - if (response_body != NULL && purple_debug_is_verbose() && - purple_debug_is_unsafe()) - { - const gchar *body = NULL; - gsize length = 0; - - body = g_bytes_get_data(response_body, &length); - purple_debug_misc("jabber-bosh", "received (session creation): %.*s", - (int)length, body); - } - - node = jabber_bosh_connection_parse(bosh_conn, msg, response_body, error); - g_clear_pointer(&response_body, g_bytes_unref); - g_clear_error(&error); - g_clear_object(&msg); - - if (node == NULL) { - return; - } - - sid = purple_xmlnode_get_attrib(node, "sid"); - ver = purple_xmlnode_get_attrib(node, "ver"); - inactivity_str = purple_xmlnode_get_attrib(node, "inactivity"); - /* requests = purple_xmlnode_get_attrib(node, "requests"); */ - - if (!sid) { - purple_connection_error(bosh_conn->js->gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("No BOSH session ID given")); - purple_xmlnode_free(node); - return; - } - - if (ver == NULL) { - purple_debug_info("jabber-bosh", "Missing version in BOSH initiation\n"); - } else if (!jabber_bosh_version_check(ver, 1, 6)) { - purple_debug_error("jabber-bosh", - "Unsupported BOSH version: %s\n", ver); - purple_connection_error(bosh_conn->js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unsupported version of BOSH protocol")); - purple_xmlnode_free(node); - return; - } - - purple_debug_misc("jabber-bosh", "Session created for %p\n", bosh_conn); - - bosh_conn->sid = g_strdup(sid); - - if (inactivity_str) - inactivity = atoi(inactivity_str); - if (inactivity < 0 || inactivity > 3600) { - purple_debug_warning("jabber-bosh", "Ignoring invalid " - "inactivity value: %s\n", inactivity_str); - inactivity = 0; - } - if (inactivity > 0) { - inactivity -= 5; /* rounding */ - if (inactivity <= 0) - inactivity = 1; - bosh_conn->js->max_inactivity = inactivity; - if (bosh_conn->js->inactivity_timer == 0) { - purple_debug_misc("jabber-bosh", "Starting inactivity " - "timer for %d secs (compensating for " - "rounding)\n", inactivity); - jabber_stream_restart_inactivity_timer(bosh_conn->js); - } - } - - jabber_stream_set_state(bosh_conn->js, JABBER_STREAM_AUTHENTICATING); - - /* FIXME: Depending on receiving features might break with some hosts */ - features = purple_xmlnode_get_child(node, "features"); - jabber_stream_features_parse(bosh_conn->js, features); - - purple_xmlnode_free(node); - - jabber_bosh_connection_send(bosh_conn, NULL); -} - -static void -jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn) -{ - SoupMessage *req; - GString *data; - - purple_debug_misc("jabber-bosh", "Requesting Session Create for %p\n", - conn); - - data = g_string_new(NULL); - - /* missing optional parameters: route, from, ack */ - g_string_printf(data, "<body content='text/xml; charset=utf-8' " - "rid='%" G_GUINT64_FORMAT "' " - "to='%s' " - "xml:lang='en' " - "ver='1.10' " - "wait='%d' " - "hold='1' " - "xmlns='" NS_BOSH "' " - "xmpp:version='1.0' " - "xmlns:xmpp='urn:xmpp:xbosh' " - "/>", - ++conn->rid, conn->js->user->domain, JABBER_BOSH_TIMEOUT); - - req = jabber_bosh_connection_http_request_new(conn, data); - g_object_set_data(G_OBJECT(req), "bosh-connection", conn); - soup_session_send_and_read_async(conn->payload_reqs, req, - G_PRIORITY_DEFAULT, NULL, - jabber_bosh_connection_session_created, - req); -} - -static SoupMessage * -jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn, - GString *data) -{ - SoupMessage *req; - GBytes *body = NULL; - - jabber_stream_restart_inactivity_timer(conn->js); - - req = soup_message_new("POST", conn->url); - body = g_string_free_to_bytes(data); - soup_message_set_request_body_from_bytes(req, "text/xml; charset=utf-8", - body); - g_bytes_unref(body); - - return req; -}
--- a/libpurple/protocols/jabber/bosh.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/** - * @file bosh.h Bidirectional-streams over Synchronous HTTP (BOSH) - * (XEP-0124 and XEP-0206) - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_BOSH_H -#define PURPLE_JABBER_BOSH_H - -typedef struct _PurpleJabberBOSHConnection PurpleJabberBOSHConnection; - -#include "jabber.h" - -void -jabber_bosh_init(void); - -void -jabber_bosh_uninit(void); - -PurpleJabberBOSHConnection * -jabber_bosh_connection_new(JabberStream *js, const gchar *url); - -void -jabber_bosh_connection_destroy(PurpleJabberBOSHConnection *conn); - -gboolean -jabber_bosh_connection_is_ssl(const PurpleJabberBOSHConnection *conn); - -void -jabber_bosh_connection_send(PurpleJabberBOSHConnection *conn, - const gchar *data); - -void -jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn); - -#endif /* PURPLE_JABBER_BOSH_H */
--- a/libpurple/protocols/jabber/buddy.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1957 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "glibcompat.h" - -#include "buddy.h" -#include "chat.h" -#include "jabber.h" -#include "iq.h" -#include "presence.h" -#include "useravatar.h" -#include "xdata.h" -#include "pep.h" -#include "adhoccommands.h" - -typedef struct { - long idle_seconds; -} JabberBuddyInfoResource; - -typedef struct { - JabberStream *js; - JabberBuddy *jb; - char *jid; - GSList *ids; - GHashTable *resources; - guint timeout_handle; - GSList *vcard_images; - PurpleNotifyUserInfo *user_info; - long last_seconds; - gchar *last_message; -} JabberBuddyInfo; - -static void -jabber_buddy_resource_free(JabberBuddyResource *jbr) -{ - g_return_if_fail(jbr != NULL); - - g_list_free_full(jbr->commands, (GDestroyNotify)jabber_adhoc_commands_free); - g_free(jbr->name); - g_free(jbr->status); - g_free(jbr->thread_id); - g_free(jbr->client.name); - g_free(jbr->client.version); - g_free(jbr->client.os); - g_clear_pointer(&jbr->tz_off, g_time_zone_unref); - g_free(jbr); -} - -void jabber_buddy_free(JabberBuddy *jb) -{ - g_return_if_fail(jb != NULL); - - g_free(jb->error_msg); - g_list_free_full(jb->resources, (GDestroyNotify)jabber_buddy_resource_free); - - g_free(jb); -} - -JabberBuddy *jabber_buddy_find(JabberStream *js, const char *name, - gboolean create) -{ - JabberBuddy *jb; - char *realname; - - if (js->buddies == NULL) - return NULL; - - if(!(realname = jabber_get_bare_jid(name))) - return NULL; - - jb = g_hash_table_lookup(js->buddies, realname); - - if(!jb && create) { - jb = g_new0(JabberBuddy, 1); - g_hash_table_insert(js->buddies, realname, jb); - } else - g_free(realname); - - return jb; -} - -/* Returns -1 if a is a higher priority resource than b, or is - * "more available" than b. 0 if they're the same, and 1 if b is - * higher priority/more available than a. - */ -static gint resource_compare_cb(gconstpointer a, gconstpointer b) -{ - const JabberBuddyResource *jbra = a; - const JabberBuddyResource *jbrb = b; - JabberBuddyState state_a, state_b; - - if (jbra->priority != jbrb->priority) - return jbra->priority > jbrb->priority ? -1 : 1; - - /* Fold the states for easier comparison */ - /* TODO: Differentiate online/chat and away/dnd? */ - switch (jbra->state) { - case JABBER_BUDDY_STATE_ONLINE: - case JABBER_BUDDY_STATE_CHAT: - state_a = JABBER_BUDDY_STATE_ONLINE; - break; - case JABBER_BUDDY_STATE_AWAY: - case JABBER_BUDDY_STATE_DND: - state_a = JABBER_BUDDY_STATE_AWAY; - break; - case JABBER_BUDDY_STATE_XA: - state_a = JABBER_BUDDY_STATE_XA; - break; - case JABBER_BUDDY_STATE_UNAVAILABLE: - state_a = JABBER_BUDDY_STATE_UNAVAILABLE; - break; - default: - state_a = JABBER_BUDDY_STATE_UNKNOWN; - break; - } - - switch (jbrb->state) { - case JABBER_BUDDY_STATE_ONLINE: - case JABBER_BUDDY_STATE_CHAT: - state_b = JABBER_BUDDY_STATE_ONLINE; - break; - case JABBER_BUDDY_STATE_AWAY: - case JABBER_BUDDY_STATE_DND: - state_b = JABBER_BUDDY_STATE_AWAY; - break; - case JABBER_BUDDY_STATE_XA: - state_b = JABBER_BUDDY_STATE_XA; - break; - case JABBER_BUDDY_STATE_UNAVAILABLE: - state_b = JABBER_BUDDY_STATE_UNAVAILABLE; - break; - default: - state_b = JABBER_BUDDY_STATE_UNKNOWN; - break; - } - - if (state_a == state_b) { - if (jbra->idle == jbrb->idle) - return 0; - else if ((jbra->idle && !jbrb->idle) || - (jbra->idle && jbrb->idle && jbra->idle < jbrb->idle)) - return 1; - else - return -1; - } - - if (state_a == JABBER_BUDDY_STATE_ONLINE) - return -1; - else if (state_a == JABBER_BUDDY_STATE_AWAY && - (state_b == JABBER_BUDDY_STATE_XA || - state_b == JABBER_BUDDY_STATE_UNAVAILABLE || - state_b == JABBER_BUDDY_STATE_UNKNOWN)) - return -1; - else if (state_a == JABBER_BUDDY_STATE_XA && - (state_b == JABBER_BUDDY_STATE_UNAVAILABLE || - state_b == JABBER_BUDDY_STATE_UNKNOWN)) - return -1; - else if (state_a == JABBER_BUDDY_STATE_UNAVAILABLE && - state_b == JABBER_BUDDY_STATE_UNKNOWN) - return -1; - - return 1; -} - -JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb, - const char *resource) -{ - GList *l; - - if (!jb) - return NULL; - - if (resource == NULL) - return jb->resources ? jb->resources->data : NULL; - - for (l = jb->resources; l; l = l->next) - { - JabberBuddyResource *jbr = l->data; - if (purple_strequal(resource, jbr->name)) - return jbr; - } - - return NULL; -} - -JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource, - int priority, JabberBuddyState state, const char *status) -{ - /* TODO: Optimization: Only reinsert if priority+state changed */ - JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource); - if (jbr) { - jb->resources = g_list_remove(jb->resources, jbr); - } else { - jbr = g_new0(JabberBuddyResource, 1); - jbr->jb = jb; - jbr->name = g_strdup(resource); - jbr->capabilities = JABBER_CAP_NONE; - } - jbr->priority = priority; - jbr->state = state; - g_free(jbr->status); - jbr->status = g_strdup(status); - - jb->resources = g_list_insert_sorted(jb->resources, jbr, - resource_compare_cb); - return jbr; -} - -void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource) -{ - JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource); - - if(!jbr) - return; - - jbr->jb->resources = g_list_remove(jbr->jb->resources, jbr); - jabber_buddy_resource_free(jbr); -} - -/******* - * This is the old vCard stuff taken from the old prpl. vCards, by definition - * are a temporary thing until jabber can get its act together and come up - * with a format for user information, hence the namespace of 'vcard-temp' - * - * Since I don't feel like putting that much work into something that's - * _supposed_ to go away, i'm going to just copy the kludgy old code here, - * and make it purdy when jabber comes up with a standards-track JEP to - * replace vcard-temp - * --Nathan - *******/ - -/*---------------------------------------*/ -/* Jabber "set info" (vCard) support */ -/*---------------------------------------*/ - -/* - * V-Card format: - * - * <vCard prodid='' version='' xmlns=''> - * <FN></FN> - * <N> - * <FAMILY/> - * <GIVEN/> - * </N> - * <NICKNAME/> - * <URL/> - * <ADR> - * <STREET/> - * <EXTADD/> - * <LOCALITY/> - * <REGION/> - * <PCODE/> - * <COUNTRY/> - * </ADR> - * <TEL/> - * <EMAIL/> - * <ORG> - * <ORGNAME/> - * <ORGUNIT/> - * </ORG> - * <TITLE/> - * <ROLE/> - * <DESC/> - * <BDAY/> - * </vCard> - * - * See also: - * - * http://docs.jabber.org/proto/html/vcard-temp.html - * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd - */ - -/* - * Cross-reference user-friendly V-Card entry labels to vCard XML tags - * and attributes. - * - * Order is (or should be) unimportant. For example: we have no way of - * knowing in what order real data will arrive. - * - * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag - * name, XML tag's parent tag "path" (relative to vCard node). - * - * List is terminated by a NULL label pointer. - * - * Entries with no label text, but with XML tag and parent tag - * entries, are used by V-Card XML construction routines to - * "automagically" construct the appropriate XML node tree. - * - * Thoughts on future direction/expansion - * - * This is a "simple" vCard. - * - * It is possible for nodes other than the "vCard" node to have - * attributes. Should that prove necessary/desirable, add an - * "attributes" pointer to the vcard_template struct, create the - * necessary tag_attr structs, and add 'em to the vcard_dflt_data - * array. - * - * The above changes will (obviously) require changes to the vCard - * construction routines. - */ - -struct vcard_template { - char *label; /* label text pointer */ - char *tag; /* tag text */ - char *ptag; /* parent tag "path" text */ -} const vcard_template_data[] = { - {N_("Full Name"), "FN", NULL}, - {N_("Family Name"), "FAMILY", "N"}, - {N_("Given Name"), "GIVEN", "N"}, - {N_("Nickname"), "NICKNAME", NULL}, - {N_("URL"), "URL", NULL}, - {N_("Street Address"), "STREET", "ADR"}, - {N_("Extended Address"), "EXTADD", "ADR"}, - {N_("Locality"), "LOCALITY", "ADR"}, - {N_("Region"), "REGION", "ADR"}, - {N_("Postal Code"), "PCODE", "ADR"}, - {N_("Country"), "CTRY", "ADR"}, - {N_("Telephone"), "NUMBER", "TEL"}, - {N_("Email"), "USERID", "EMAIL"}, - {N_("Organization Name"), "ORGNAME", "ORG"}, - {N_("Organization Unit"), "ORGUNIT", "ORG"}, - {N_("Job Title"), "TITLE", NULL}, - {N_("Role"), "ROLE", NULL}, - {N_("Birthday"), "BDAY", NULL}, - {N_("Description"), "DESC", NULL}, - {"", "N", NULL}, - {"", "ADR", NULL}, - {"", "ORG", NULL}, - {NULL, NULL, NULL} -}; - -/* - * The "vCard" tag's attribute list... - */ -struct tag_attr { - char *attr; - char *value; -} const vcard_tag_attr_list[] = { - {"prodid", "-//HandGen//NONSGML vGen v1.0//EN"}, - {"version", "2.0", }, - {"xmlns", "vcard-temp", }, - {NULL, NULL}, -}; - - -/* - * Insert a tag node into an PurpleXmlNode tree, recursively inserting parent tag - * nodes as necessary - * - * Returns pointer to inserted node - * - * Note to hackers: this code is designed to be re-entrant (it's recursive--it - * calls itself), so don't put any "static"s in here! - */ -static PurpleXmlNode *insert_tag_to_parent_tag(PurpleXmlNode *start, const char *parent_tag, const char *new_tag) -{ - PurpleXmlNode *x = NULL; - - /* - * If the parent tag wasn't specified, see if we can get it - * from the vCard template struct. - */ - if(parent_tag == NULL) { - const struct vcard_template *vc_tp = vcard_template_data; - - while(vc_tp->label != NULL) { - if(purple_strequal(vc_tp->tag, new_tag)) { - parent_tag = vc_tp->ptag; - break; - } - ++vc_tp; - } - } - - /* - * If we have a parent tag... - */ - if(parent_tag != NULL ) { - /* - * Try to get the parent node for a tag - */ - if((x = purple_xmlnode_get_child(start, parent_tag)) == NULL) { - /* - * Descend? - */ - char *grand_parent = g_strdup(parent_tag); - char *parent; - - if((parent = strrchr(grand_parent, '/')) != NULL) { - *(parent++) = '\0'; - x = insert_tag_to_parent_tag(start, grand_parent, parent); - } else { - x = purple_xmlnode_new_child(start, grand_parent); - } - g_free(grand_parent); - } else { - /* - * We found *something* to be the parent node. - * Note: may be the "root" node! - */ - PurpleXmlNode *y; - if((y = purple_xmlnode_get_child(x, new_tag)) != NULL) { - return(y); - } - } - } - - /* - * insert the new tag into its parent node - */ - return(purple_xmlnode_new_child((x == NULL? start : x), new_tag)); -} - -/* - * Send vCard info to Jabber server - */ -void -jabber_set_info(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const char *info) -{ - PurpleImage *img; - JabberIq *iq; - JabberStream *js = purple_connection_get_protocol_data(gc); - PurpleXmlNode *vc_node; - const struct tag_attr *tag_attr; - - /* if we haven't grabbed the remote vcard yet, we can't - * assume that what we have here is correct */ - if(!js->vcard_fetched) { - PurpleImage *image; - g_free(js->initial_avatar_hash); - image = purple_buddy_icons_find_account_icon(purple_connection_get_account(gc)); - if (image != NULL) { - js->initial_avatar_hash = g_compute_checksum_for_data( - G_CHECKSUM_SHA1, - purple_image_get_data(image), - purple_image_get_data_size(image) - ); - g_object_unref(image); - } else { - js->initial_avatar_hash = NULL; - } - return; - } - - g_free(js->avatar_hash); - js->avatar_hash = NULL; - - /* - * Send only if there's actually any *information* to send - */ - vc_node = info ? purple_xmlnode_from_str(info, -1) : NULL; - - if (vc_node && (!vc_node->name || - g_ascii_strncasecmp(vc_node->name, "vCard", 5))) { - purple_xmlnode_free(vc_node); - vc_node = NULL; - } - - if ((img = purple_buddy_icons_find_account_icon(purple_connection_get_account(gc)))) { - gconstpointer avatar_data; - gsize avatar_len; - PurpleXmlNode *photo, *binval, *type; - gchar *enc; - - if(!vc_node) { - vc_node = purple_xmlnode_new("vCard"); - for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr) - purple_xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value); - } - - avatar_data = purple_image_get_data(img); - avatar_len = purple_image_get_data_size(img); - /* Get rid of an old PHOTO if one exists. - * TODO: This may want to be modified to remove all old PHOTO - * children, at the moment some people have managed to get - * multiple PHOTO entries in their vCard. */ - if((photo = purple_xmlnode_get_child(vc_node, "PHOTO"))) { - purple_xmlnode_free(photo); - } - photo = purple_xmlnode_new_child(vc_node, "PHOTO"); - type = purple_xmlnode_new_child(photo, "TYPE"); - purple_xmlnode_insert_data(type, "image/png", -1); - binval = purple_xmlnode_new_child(photo, "BINVAL"); - enc = g_base64_encode(avatar_data, avatar_len); - - js->avatar_hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, - avatar_data, avatar_len); - - purple_xmlnode_insert_data(binval, enc, -1); - g_free(enc); - g_object_unref(img); - } else if (vc_node) { - PurpleXmlNode *photo; - /* TODO: Remove all PHOTO children? (see above note) */ - if ((photo = purple_xmlnode_get_child(vc_node, "PHOTO"))) { - purple_xmlnode_free(photo); - } - } - - if (vc_node != NULL) { - iq = jabber_iq_new(js, JABBER_IQ_SET); - purple_xmlnode_insert_child(iq->node, vc_node); - jabber_iq_send(iq); - - /* Send presence to update vcard-temp:x:update */ - jabber_presence_send(js, FALSE); - } -} - -void -jabber_set_buddy_icon(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, PurpleImage *img) -{ - PurpleAccount *account = purple_connection_get_account(gc); - - /* Publish the avatar as specified in XEP-0084 */ - jabber_avatar_set(purple_connection_get_protocol_data(gc), img); - /* Set the image in our vCard */ - jabber_set_info(NULL, gc, purple_account_get_user_info(account)); - - /* TODO: Fake image to ourselves, since a number of servers do not echo - * back our presence to us. To do this without uselessly copying the data - * of the image, we need purple_buddy_icons_set_for_user_image (i.e. takes - * an existing icon/stored image). */ -} - -/* - * This is the callback from the "ok clicked" for "set vCard" - * - * Sets the vCard with data from PurpleRequestFields. - */ -static void -jabber_format_info(PurpleConnection *gc, PurpleRequestPage *page) { - PurpleXmlNode *vc_node; - PurpleProtocol *protocol = NULL; - const char *text; - char *p; - const struct vcard_template *vc_tp; - const struct tag_attr *tag_attr; - - vc_node = purple_xmlnode_new("vCard"); - - for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr) - purple_xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value); - - for (vc_tp = vcard_template_data; vc_tp->label != NULL; vc_tp++) { - if (*vc_tp->label == '\0') - continue; - - text = purple_request_page_get_string(page, vc_tp->tag); - - if(!purple_strempty(text)) { - PurpleXmlNode *xp; - - purple_debug_info("jabber", "Setting %s to '%s'\n", vc_tp->tag, text); - - if ((xp = insert_tag_to_parent_tag(vc_node, - NULL, vc_tp->tag)) != NULL) { - - purple_xmlnode_insert_data(xp, text, -1); - } - } - } - - p = purple_xmlnode_to_str(vc_node, NULL); - purple_xmlnode_free(vc_node); - - purple_account_set_user_info(purple_connection_get_account(gc), p); - protocol = purple_connection_get_protocol(gc); - if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, set_info)) { - purple_protocol_server_set_info(PURPLE_PROTOCOL_SERVER(protocol), - gc, p); - } - - g_free(p); -} - -/* - * This gets executed as the protocol action. - * - * Creates a new PurpleRequestFields struct, gets the XML-formatted user_info - * string (if any) into GSLists for the (multi-entry) edit dialog and - * calls the set_vcard dialog. - */ -void -jabber_setup_set_info(G_GNUC_UNUSED GSimpleAction *action, GVariant *parameter, - G_GNUC_UNUSED gpointer data) -{ - const char *account_id = NULL; - PurpleAccountManager *manager = NULL; - PurpleAccount *account = NULL; - PurpleConnection *connection = NULL; - PurpleRequestPage *page; - PurpleRequestGroup *group; - PurpleRequestField *field; - const struct vcard_template *vc_tp; - const char *user_info; - char *cdata = NULL; - PurpleXmlNode *x_vc_data = NULL; - - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) { - g_critical("XMPP Set Info action parameter is of incorrect type %s", - g_variant_get_type_string(parameter)); - } - - account_id = g_variant_get_string(parameter, NULL); - manager = purple_account_manager_get_default(); - account = purple_account_manager_find_by_id(manager, account_id); - connection = purple_account_get_connection(account); - - page = purple_request_page_new(); - group = purple_request_group_new(NULL); - purple_request_page_add_group(page, group); - - /* - * Get existing, XML-formatted, user info - */ - if((user_info = purple_account_get_user_info(account)) != NULL) { - x_vc_data = purple_xmlnode_from_str(user_info, -1); - } - - /* - * Set up GSLists for edit with labels from "template," data from user info - */ - for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { - PurpleXmlNode *data_node; - if((vc_tp->label)[0] == '\0') - continue; - - if (x_vc_data != NULL) { - if(vc_tp->ptag == NULL) { - data_node = purple_xmlnode_get_child(x_vc_data, vc_tp->tag); - } else { - gchar *tag = g_strdup_printf("%s/%s", vc_tp->ptag, vc_tp->tag); - data_node = purple_xmlnode_get_child(x_vc_data, tag); - g_free(tag); - } - if(data_node) - cdata = purple_xmlnode_get_data(data_node); - } - - if(purple_strequal(vc_tp->tag, "DESC")) { - field = purple_request_field_string_new(vc_tp->tag, - _(vc_tp->label), cdata, - TRUE); - } else { - field = purple_request_field_string_new(vc_tp->tag, - _(vc_tp->label), cdata, - FALSE); - } - - g_free(cdata); - cdata = NULL; - - purple_request_group_add_field(group, field); - } - - if(x_vc_data != NULL) - purple_xmlnode_free(x_vc_data); - - purple_request_fields(connection, _("Edit XMPP vCard"), - _("Edit XMPP vCard"), - _("All items below are optional. Enter only the " - "information with which you feel comfortable."), - page, - _("Save"), G_CALLBACK(jabber_format_info), - _("Cancel"), NULL, - purple_request_cpar_from_connection(connection), - connection); - - g_clear_object(&account); -} - -/*---------------------------------------*/ -/* End Jabber "set info" (vCard) support */ -/*---------------------------------------*/ - -/****** - * end of that ancient crap that needs to die - ******/ - -static void jabber_buddy_info_destroy(JabberBuddyInfo *jbi) -{ - /* Remove the timeout, which would otherwise trigger jabber_buddy_get_info_timeout() */ - g_clear_handle_id(&jbi->timeout_handle, g_source_remove); - - g_slist_free(jbi->ids); - g_free(jbi->jid); - g_hash_table_destroy(jbi->resources); - g_free(jbi->last_message); - purple_notify_user_info_destroy(jbi->user_info); - g_free(jbi); -} - -static void -add_jbr_info(JabberBuddyInfo *jbi, const char *resource, - JabberBuddyResource *jbr) -{ - JabberBuddyInfoResource *jbir; - PurpleNotifyUserInfo *user_info; - - jbir = g_hash_table_lookup(jbi->resources, resource); - user_info = jbi->user_info; - - if (jbr && jbr->client.name) { - char *tmp = - g_strdup_printf("%s%s%s", jbr->client.name, - (jbr->client.version ? " " : ""), - (jbr->client.version ? jbr->client.version : "")); - /* TODO: Check whether it's correct to call prepend_pair_html, - or if we should be using prepend_pair_plaintext */ - purple_notify_user_info_prepend_pair_html(user_info, _("Client"), tmp); - g_free(tmp); - - if (jbr->client.os) { - /* TODO: Check whether it's correct to call prepend_pair_html, - or if we should be using prepend_pair_plaintext */ - purple_notify_user_info_prepend_pair_html(user_info, _("Operating System"), jbr->client.os); - } - } - - if (jbr && jbr->tz_off != NULL) { - GDateTime *dt = NULL; - char *timestamp = NULL; - - dt = g_date_time_new_now(jbr->tz_off); - - timestamp = g_date_time_format(dt, "%X %:z"); - g_date_time_unref(dt); - - purple_notify_user_info_prepend_pair_plaintext(user_info, _("Local Time"), timestamp); - g_free(timestamp); - } - - if (jbir && jbir->idle_seconds > 0) { - char *idle = purple_str_seconds_to_string(jbir->idle_seconds); - purple_notify_user_info_prepend_pair_plaintext(user_info, _("Idle"), idle); - g_free(idle); - } - - if (jbr) { - char *purdy = NULL; - char *tmp; - char priority[12]; - const char *status_name = jabber_buddy_state_get_name(jbr->state); - - if (jbr->status) { - tmp = g_markup_escape_text(jbr->status, -1); - purdy = purple_strdup_withhtml(tmp); - g_free(tmp); - - if (purple_strequal(status_name, purdy)) - status_name = NULL; - } - - tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""), - ((status_name && purdy) ? ": " : ""), - (purdy ? purdy : "")); - purple_notify_user_info_prepend_pair_html(user_info, _("Status"), tmp); - - g_snprintf(priority, sizeof(priority), "%d", jbr->priority); - purple_notify_user_info_prepend_pair_plaintext(user_info, _("Priority"), priority); - - g_free(tmp); - g_free(purdy); - } else { - purple_notify_user_info_prepend_pair_plaintext(user_info, _("Status"), _("Unknown")); - } -} - -static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi) -{ - char *resource_name; - JabberBuddyResource *jbr; - GList *resources; - PurpleNotifyUserInfo *user_info; - - /* not yet */ - if (jbi->ids) - return; - - user_info = jbi->user_info; - resource_name = jabber_get_resource(jbi->jid); - - /* If we have one or more pairs from the vcard, put a section break above it */ - if (g_queue_get_length(purple_notify_user_info_get_entries(user_info))) - purple_notify_user_info_prepend_section_break(user_info); - - /* Add the information about the user's resource(s) */ - if (resource_name) { - jbr = jabber_buddy_find_resource(jbi->jb, resource_name); - add_jbr_info(jbi, resource_name, jbr); - } else { - /* TODO: This is in priority-ascending order (lowest prio first), because - * everything is prepended. Is that ok? */ - for (resources = jbi->jb->resources; resources; resources = resources->next) { - jbr = resources->data; - - /* put a section break between resources, this is not needed if - we are at the first, because one was already added for the vcard - section */ - if (resources != jbi->jb->resources) - purple_notify_user_info_prepend_section_break(user_info); - - add_jbr_info(jbi, jbr->name, jbr); - - if (jbr->name) { - /* TODO: Check whether it's correct to call prepend_pair_html, - or if we should be using prepend_pair_plaintext */ - purple_notify_user_info_prepend_pair_html(user_info, _("Resource"), jbr->name); - } - } - } - - if (!jbi->jb->resources) { - /* the buddy is offline */ - gboolean is_domain = jabber_jid_is_domain(jbi->jid); - - if (jbi->last_seconds > 0) { - char *last = purple_str_seconds_to_string(jbi->last_seconds); - gchar *message = NULL; - const gchar *title = NULL; - if (is_domain) { - title = _("Uptime"); - message = last; - last = NULL; - } else { - title = _("Logged Off"); - message = g_strdup_printf(_("%s ago"), last); - } - purple_notify_user_info_prepend_pair_plaintext(user_info, title, message); - g_free(last); - g_free(message); - } - - if (!is_domain) { - gchar *status = - g_strdup_printf("%s%s%s", _("Offline"), - jbi->last_message ? ": " : "", - jbi->last_message ? jbi->last_message : ""); - /* TODO: Check whether it's correct to call prepend_pair_html, - or if we should be using prepend_pair_plaintext */ - purple_notify_user_info_prepend_pair_html(user_info, _("Status"), status); - g_free(status); - } - } - - g_free(resource_name); - - purple_notify_userinfo(jbi->js->gc, jbi->jid, user_info, NULL, NULL); - - g_slist_free_full(jbi->vcard_images, g_object_unref); - - jbi->js->pending_buddy_info_requests = g_slist_remove(jbi->js->pending_buddy_info_requests, jbi); - - jabber_buddy_info_destroy(jbi); -} - -static void jabber_buddy_info_remove_id(JabberBuddyInfo *jbi, const char *id) -{ - GSList *l; - char *comp_id; - - if(!id) - return; - - l = g_slist_find_custom(jbi->ids, id, (GCompareFunc)g_strcmp0); - if(l) { - comp_id = l->data; - jbi->ids = g_slist_delete_link(jbi->ids, l); - g_free(comp_id); - } -} - -static void -jabber_vcard_save_mine(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *vcard, *photo, *binval; - char *txt, *vcard_hash = NULL; - PurpleAccount *account; - - if (type == JABBER_IQ_ERROR) { - PurpleXmlNode *error; - purple_debug_warning("jabber", "Server returned error while retrieving vCard\n"); - - error = purple_xmlnode_get_child(packet, "error"); - if (!error || !purple_xmlnode_get_child(error, "item-not-found")) - return; - } - - account = purple_connection_get_account(js->gc); - - if((vcard = purple_xmlnode_get_child(packet, "vCard")) || - (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) - { - txt = purple_xmlnode_to_str(vcard, NULL); - purple_account_set_user_info(account, txt); - g_free(txt); - } else { - /* if we have no vCard, then lets not overwrite what we might have locally */ - } - - js->vcard_fetched = TRUE; - - if (vcard && (photo = purple_xmlnode_get_child(vcard, "PHOTO")) && - (binval = purple_xmlnode_get_child(photo, "BINVAL"))) { - gsize size; - char *bintext = purple_xmlnode_get_data(binval); - if (bintext) { - guchar *data = g_base64_decode(bintext, &size); - g_free(bintext); - - if (data) { - vcard_hash = g_compute_checksum_for_data( - G_CHECKSUM_SHA1, data, size); - g_free(data); - } - } - } - - /* Republish our vcard if the photo is different than the server's */ - if (js->initial_avatar_hash && !purple_strequal(vcard_hash, js->initial_avatar_hash)) { - jabber_set_info(NULL, js->gc, purple_account_get_user_info(account)); - } else if (vcard_hash) { - /* A photo is in the vCard. Advertise its hash */ - js->avatar_hash = vcard_hash; - vcard_hash = NULL; - - /* Send presence to update vcard-temp:x:update */ - jabber_presence_send(js, FALSE); - } - - g_free(vcard_hash); -} - -void jabber_vcard_fetch_mine(JabberStream *js) -{ - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); - - PurpleXmlNode *vcard = purple_xmlnode_new_child(iq->node, "vCard"); - purple_xmlnode_set_namespace(vcard, "vcard-temp"); - jabber_iq_set_callback(iq, jabber_vcard_save_mine, NULL); - - jabber_iq_send(iq); -} - -static void jabber_vcard_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data) -{ - char *bare_jid; - char *text; - char *serverside_alias = NULL; - PurpleXmlNode *vcard; - PurpleAccount *account; - JabberBuddyInfo *jbi = data; - PurpleNotifyUserInfo *user_info; - - g_return_if_fail(jbi != NULL); - - jabber_buddy_info_remove_id(jbi, id); - - if (type == JABBER_IQ_ERROR) { - purple_debug_info("jabber", "Got error response for vCard\n"); - jabber_buddy_info_show_if_ready(jbi); - return; - } - - user_info = jbi->user_info; - account = purple_connection_get_account(js->gc); - - if(from == NULL) { - from = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)); - } - bare_jid = jabber_get_bare_jid(from); - - /* TODO: Is the query xmlns='vcard-temp' version of this still necessary? */ - if((vcard = purple_xmlnode_get_child(packet, "vCard")) || - (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) { - PurpleXmlNode *child; - for(child = vcard->child; child; child = child->next) - { - PurpleXmlNode *child2; - - if(child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - text = purple_xmlnode_get_data(child); - if(text && purple_strequal(child->name, "FN")) { - if (!serverside_alias) - serverside_alias = g_strdup(text); - - purple_notify_user_info_add_pair_plaintext(user_info, _("Full Name"), text); - } else if(purple_strequal(child->name, "N")) { - for(child2 = child->child; child2; child2 = child2->next) - { - char *text2; - - if(child2->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - text2 = purple_xmlnode_get_data(child2); - if(text2 && purple_strequal(child2->name, "FAMILY")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Family Name"), text2); - } else if(text2 && purple_strequal(child2->name, "GIVEN")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Given Name"), text2); - } else if(text2 && purple_strequal(child2->name, "MIDDLE")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Middle Name"), text2); - } - g_free(text2); - } - } else if(text && purple_strequal(child->name, "NICKNAME")) { - /* Prefer the Nickcname to the Full Name as the serverside alias if it's not just part of the jid. - * Ignore it if it's part of the jid. */ - if (strstr(bare_jid, text) == NULL) { - g_free(serverside_alias); - serverside_alias = g_strdup(text); - - purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), text); - } - } else if(text && purple_strequal(child->name, "BDAY")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Birthday"), text); - } else if(purple_strequal(child->name, "ADR")) { - gboolean address_line_added = FALSE; - - for(child2 = child->child; child2; child2 = child2->next) - { - char *text2; - - if(child2->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - text2 = purple_xmlnode_get_data(child2); - if (text2 == NULL) - continue; - - /* We do this here so that it's not added if all the child - * elements are empty. */ - if (!address_line_added) - { - purple_notify_user_info_add_section_header(user_info, _("Address")); - address_line_added = TRUE; - } - - if(purple_strequal(child2->name, "POBOX")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("P.O. Box"), text2); - } else if (purple_strequal(child2->name, "EXTADD") || purple_strequal(child2->name, "EXTADR")) { - /* - * EXTADD is correct, EXTADR is generated by other - * clients. The next time someone reads this, remove - * EXTADR. - */ - purple_notify_user_info_add_pair_plaintext(user_info, _("Extended Address"), text2); - } else if(purple_strequal(child2->name, "STREET")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Street Address"), text2); - } else if(purple_strequal(child2->name, "LOCALITY")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Locality"), text2); - } else if(purple_strequal(child2->name, "REGION")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Region"), text2); - } else if(purple_strequal(child2->name, "PCODE")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Postal Code"), text2); - } else if(purple_strequal(child2->name, "CTRY") - || purple_strequal(child2->name, "COUNTRY")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Country"), text2); - } - g_free(text2); - } - - if (address_line_added) - purple_notify_user_info_add_section_break(user_info); - - } else if(purple_strequal(child->name, "TEL")) { - char *number; - if((child2 = purple_xmlnode_get_child(child, "NUMBER"))) { - /* show what kind of number it is */ - number = purple_xmlnode_get_data(child2); - if(number) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number); - g_free(number); - } - } else if((number = purple_xmlnode_get_data(child))) { - /* lots of clients (including purple) do this, but it's - * out of spec */ - purple_notify_user_info_add_pair_plaintext(user_info, _("Telephone"), number); - g_free(number); - } - } else if(purple_strequal(child->name, "EMAIL")) { - char *userid, *escaped; - if((child2 = purple_xmlnode_get_child(child, "USERID"))) { - /* show what kind of email it is */ - userid = purple_xmlnode_get_data(child2); - if(userid) { - char *mailto; - escaped = g_markup_escape_text(userid, -1); - mailto = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", escaped, escaped); - purple_notify_user_info_add_pair_html(user_info, _("Email"), mailto); - - g_free(mailto); - g_free(escaped); - g_free(userid); - } - } else if((userid = purple_xmlnode_get_data(child))) { - /* lots of clients (including purple) do this, but it's - * out of spec */ - char *mailto; - - escaped = g_markup_escape_text(userid, -1); - mailto = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", escaped, escaped); - purple_notify_user_info_add_pair_html(user_info, _("Email"), mailto); - - g_free(mailto); - g_free(escaped); - g_free(userid); - } - } else if(purple_strequal(child->name, "ORG")) { - for(child2 = child->child; child2; child2 = child2->next) - { - char *text2; - - if(child2->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - text2 = purple_xmlnode_get_data(child2); - if(text2 && purple_strequal(child2->name, "ORGNAME")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Organization Name"), text2); - } else if(text2 && purple_strequal(child2->name, "ORGUNIT")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Organization Unit"), text2); - } - g_free(text2); - } - } else if(text && purple_strequal(child->name, "TITLE")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Job Title"), text); - } else if(text && purple_strequal(child->name, "ROLE")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Role"), text); - } else if(text && purple_strequal(child->name, "DESC")) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Description"), text); - } else if(purple_strequal(child->name, "PHOTO") || - purple_strequal(child->name, "LOGO")) { - char *bintext = NULL; - PurpleXmlNode *binval; - - if ((binval = purple_xmlnode_get_child(child, "BINVAL")) && - (bintext = purple_xmlnode_get_data(binval))) { - gsize size; - guchar *data; - gboolean photo = purple_strequal(child->name, "PHOTO"); - - data = g_base64_decode(bintext, &size); - if (data) { - PurpleImage *img; - guint img_id; - char *img_text; - char *hash; - - img = purple_image_new_from_data(data, size); - img_id = purple_image_store_add(img); - - jbi->vcard_images = g_slist_prepend(jbi->vcard_images, img); - img_text = g_strdup_printf("<img src='" - PURPLE_IMAGE_STORE_PROTOCOL "%u'>", img_id); - - purple_notify_user_info_add_pair_html(user_info, (photo ? _("Photo") : _("Logo")), img_text); - - hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, size); - purple_buddy_icons_set_for_user(account, bare_jid, data, size, hash); - g_free(hash); - g_free(img_text); - } - g_free(bintext); - } - } - g_free(text); - } - } - - if (serverside_alias) { - PurpleBuddy *b; - /* If we found a serverside alias, set it and tell the core */ - purple_serv_got_alias(js->gc, bare_jid, serverside_alias); - b = purple_blist_find_buddy(account, bare_jid); - if (b) { - purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", serverside_alias); - } - - g_free(serverside_alias); - } - - g_free(bare_jid); - - jabber_buddy_info_show_if_ready(jbi); -} - -static void jabber_buddy_info_resource_free(gpointer data) -{ - JabberBuddyInfoResource *jbri = data; - g_free(jbri); -} - -static guint jbir_hash(gconstpointer v) -{ - if (v) - return g_str_hash(v); - else - return 0; -} - -static gboolean jbir_equal(gconstpointer v1, gconstpointer v2) -{ - const gchar *resource_1 = v1; - const gchar *resource_2 = v2; - - return purple_strequal(resource_1, resource_2); -} - -static void -jabber_version_parse(G_GNUC_UNUSED JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *packet, - gpointer data) -{ - JabberBuddyInfo *jbi = data; - PurpleXmlNode *query; - char *resource_name; - - g_return_if_fail(jbi != NULL); - - jabber_buddy_info_remove_id(jbi, id); - - if(!from) - return; - - resource_name = jabber_get_resource(from); - - if(resource_name) { - if (type == JABBER_IQ_RESULT) { - if((query = purple_xmlnode_get_child(packet, "query"))) { - JabberBuddyResource *jbr = jabber_buddy_find_resource(jbi->jb, resource_name); - if(jbr) { - PurpleXmlNode *node; - if((node = purple_xmlnode_get_child(query, "name"))) { - jbr->client.name = purple_xmlnode_get_data(node); - } - if((node = purple_xmlnode_get_child(query, "version"))) { - jbr->client.version = purple_xmlnode_get_data(node); - } - if((node = purple_xmlnode_get_child(query, "os"))) { - jbr->client.os = purple_xmlnode_get_data(node); - } - } - } - } - g_free(resource_name); - } - - jabber_buddy_info_show_if_ready(jbi); -} - -static void jabber_last_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data) -{ - JabberBuddyInfo *jbi = data; - PurpleXmlNode *query; - char *resource_name; - const char *seconds; - - g_return_if_fail(jbi != NULL); - - jabber_buddy_info_remove_id(jbi, id); - - if(!from) - return; - - resource_name = jabber_get_resource(from); - - if(resource_name) { - if (type == JABBER_IQ_RESULT) { - if((query = purple_xmlnode_get_child(packet, "query"))) { - seconds = purple_xmlnode_get_attrib(query, "seconds"); - if(seconds) { - char *end = NULL; - long sec = strtol(seconds, &end, 10); - JabberBuddy *jb = NULL; - char *resource = NULL; - char *buddy_name = NULL; - JabberBuddyResource *jbr = NULL; - - if(end != seconds) { - JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); - if(jbir) { - jbir->idle_seconds = sec; - } - } - /* Update the idle time of the buddy resource, if we got it. - This will correct the value when a server doesn't mark - delayed presence and we got the presence when signing on */ - jb = jabber_buddy_find(js, from, FALSE); - if (jb) { - resource = jabber_get_resource(from); - buddy_name = jabber_get_bare_jid(from); - /* if the resource already has an idle time set, we - must have gotten it originally from a presence. In - this case we update it. Otherwise don't update it, to - avoid setting an idle and not getting informed about - the resource getting unidle */ - if (resource && buddy_name) { - jbr = jabber_buddy_find_resource(jb, resource); - if (jbr) { - if (jbr->idle) { - if (sec) { - jbr->idle = time(NULL) - sec; - } else { - jbr->idle = 0; - } - - if (jbr == - jabber_buddy_find_resource(jb, NULL)) { - purple_protocol_got_user_idle(purple_connection_get_account(js->gc), - buddy_name, jbr->idle, jbr->idle); - } - } - } - } - g_free(resource); - g_free(buddy_name); - } - } - } - } - g_free(resource_name); - } - - jabber_buddy_info_show_if_ready(jbi); -} - -static void -jabber_last_offline_parse(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, JabberIqType type, - const char *id, PurpleXmlNode *packet, gpointer data) -{ - JabberBuddyInfo *jbi = data; - PurpleXmlNode *query; - const char *seconds; - - g_return_if_fail(jbi != NULL); - - jabber_buddy_info_remove_id(jbi, id); - - if (type == JABBER_IQ_RESULT) { - if((query = purple_xmlnode_get_child(packet, "query"))) { - seconds = purple_xmlnode_get_attrib(query, "seconds"); - if(seconds) { - char *end = NULL; - long sec = strtol(seconds, &end, 10); - if(end != seconds) { - jbi->last_seconds = sec; - } - } - jbi->last_message = purple_xmlnode_get_data(query); - } - } - - jabber_buddy_info_show_if_ready(jbi); -} - -static void -jabber_time_parse(G_GNUC_UNUSED JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *packet, - gpointer data) -{ - JabberBuddyInfo *jbi = data; - JabberBuddyResource *jbr; - char *resource_name; - - g_return_if_fail(jbi != NULL); - - jabber_buddy_info_remove_id(jbi, id); - - if (!from) - return; - - resource_name = jabber_get_resource(from); - jbr = resource_name ? jabber_buddy_find_resource(jbi->jb, resource_name) : NULL; - g_free(resource_name); - if (jbr) { - if (type == JABBER_IQ_RESULT) { - PurpleXmlNode *time = purple_xmlnode_get_child(packet, "time"); - PurpleXmlNode *tzo = time ? purple_xmlnode_get_child(time, "tzo") : NULL; - char *tzo_data = tzo ? purple_xmlnode_get_data(tzo) : NULL; - if (tzo_data) { - char *c = tzo_data; - int hours, minutes; - if (tzo_data[0] == 'Z' && tzo_data[1] == '\0') { - jbr->tz_off = g_time_zone_new_offset(0); - } else { - gboolean offset_positive = (tzo_data[0] == '+'); - /* [+-]HH:MM */ - if (((*c == '+' || *c == '-') && (c = c + 1)) && - sscanf(c, "%02d:%02d", &hours, &minutes) == 2) - { - gint32 tz_off = 60*60*hours + 60*minutes; - if (!offset_positive) { - tz_off *= -1; - } - - jbr->tz_off = g_time_zone_new_offset(tz_off); - - } else { - purple_debug_info("jabber", "Ignoring malformed timezone %s", - tzo_data); - } - } - - g_free(tzo_data); - } - } - } - - jabber_buddy_info_show_if_ready(jbi); -} - -void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js) -{ - g_clear_slist(&js->pending_buddy_info_requests, - (GDestroyNotify)jabber_buddy_info_destroy); -} - -static gboolean jabber_buddy_get_info_timeout(gpointer data) -{ - JabberBuddyInfo *jbi = data; - - /* remove the pending callbacks */ - while(jbi->ids) { - char *id = jbi->ids->data; - jabber_iq_remove_callback_by_id(jbi->js, id); - jbi->ids = g_slist_delete_link(jbi->ids, jbi->ids); - g_free(id); - } - - jbi->js->pending_buddy_info_requests = g_slist_remove(jbi->js->pending_buddy_info_requests, jbi); - jbi->timeout_handle = 0; - - jabber_buddy_info_show_if_ready(jbi); - - return FALSE; -} - -static gboolean _client_is_blacklisted(JabberBuddyResource *jbr, const char *ns) -{ - /* can't be blacklisted if we don't know what you're running yet */ - if(!jbr->client.name) - return FALSE; - - if(purple_strequal(ns, NS_LAST_ACTIVITY)) { - if(purple_strequal(jbr->client.name, "Trillian")) { - /* verified by nwalp 2007/05/09 */ - if(purple_strequal(jbr->client.version, "3.1.0.121") || - /* verified by nwalp 2007/09/19 */ - purple_strequal(jbr->client.version, "3.1.7.0")) { - return TRUE; - } - } - } - - return FALSE; -} - -static void -dispatch_queries_for_resource(JabberStream *js, JabberBuddyInfo *jbi, - gboolean is_bare_jid, const char *jid, - JabberBuddyResource *jbr) -{ - JabberIq *iq; - JabberBuddyInfoResource *jbir; - char *full_jid = NULL; - const char *to; - - if (is_bare_jid && jbr->name) { - full_jid = g_strdup_printf("%s/%s", jid, jbr->name); - to = full_jid; - } else - to = jid; - - jbir = g_new0(JabberBuddyInfoResource, 1); - g_hash_table_insert(jbi->resources, g_strdup(jbr->name), jbir); - - if(!jbr->client.name) { - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:version"); - purple_xmlnode_set_attrib(iq->node, "to", to); - jabber_iq_set_callback(iq, jabber_version_parse, jbi); - jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); - jabber_iq_send(iq); - } - - /* this is to fix the feeling of irritation I get when trying - * to get info on a friend running Trillian, which doesn't - * respond (with an error or otherwise) to jabber:iq:last - * requests. There are a number of Trillian users in my - * office. */ - if(!_client_is_blacklisted(jbr, NS_LAST_ACTIVITY)) { - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_LAST_ACTIVITY); - purple_xmlnode_set_attrib(iq->node, "to", to); - jabber_iq_set_callback(iq, jabber_last_parse, jbi); - jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); - jabber_iq_send(iq); - } - - if (jbr->tz_off == NULL && - (jbr->caps == NULL || - jabber_resource_has_capability(jbr, NS_ENTITY_TIME))) { - PurpleXmlNode *child; - iq = jabber_iq_new(js, JABBER_IQ_GET); - purple_xmlnode_set_attrib(iq->node, "to", to); - child = purple_xmlnode_new_child(iq->node, "time"); - purple_xmlnode_set_namespace(child, NS_ENTITY_TIME); - jabber_iq_set_callback(iq, jabber_time_parse, jbi); - jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); - jabber_iq_send(iq); - } - - g_free(full_jid); -} - -static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *jid) -{ - JabberIq *iq; - PurpleXmlNode *vcard; - GList *resources; - JabberBuddy *jb; - JabberBuddyInfo *jbi; - const char *slash; - gboolean is_bare_jid; - - jb = jabber_buddy_find(js, jid, TRUE); - - /* invalid JID */ - if(!jb) - return; - - slash = strchr(jid, '/'); - is_bare_jid = (slash == NULL); - - jbi = g_new0(JabberBuddyInfo, 1); - jbi->jid = g_strdup(jid); - jbi->js = js; - jbi->jb = jb; - jbi->resources = g_hash_table_new_full(jbir_hash, jbir_equal, g_free, jabber_buddy_info_resource_free); - jbi->user_info = purple_notify_user_info_new(); - - iq = jabber_iq_new(js, JABBER_IQ_GET); - - purple_xmlnode_set_attrib(iq->node, "to", jid); - vcard = purple_xmlnode_new_child(iq->node, "vCard"); - purple_xmlnode_set_namespace(vcard, "vcard-temp"); - - jabber_iq_set_callback(iq, jabber_vcard_parse, jbi); - jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); - - jabber_iq_send(iq); - - if (is_bare_jid) { - if (jb->resources) { - for(resources = jb->resources; resources; resources = resources->next) { - JabberBuddyResource *jbr = resources->data; - dispatch_queries_for_resource(js, jbi, is_bare_jid, jid, jbr); - } - } else { - /* user is offline, send a jabber:iq:last to find out last time online */ - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_LAST_ACTIVITY); - purple_xmlnode_set_attrib(iq->node, "to", jid); - jabber_iq_set_callback(iq, jabber_last_offline_parse, jbi); - jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); - jabber_iq_send(iq); - } - } else { - JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, slash + 1); - if (jbr) - dispatch_queries_for_resource(js, jbi, is_bare_jid, jid, jbr); - else - purple_debug_warning("jabber", "jabber_buddy_get_info_for_jid() " - "was passed JID %s, but there is no corresponding " - "JabberBuddyResource!\n", jid); - } - - js->pending_buddy_info_requests = g_slist_prepend(js->pending_buddy_info_requests, jbi); - jbi->timeout_handle = g_timeout_add_seconds(30, jabber_buddy_get_info_timeout, jbi); -} - -void -jabber_buddy_get_info(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const char *who) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberID *jid = jabber_id_new(who); - - if (!jid) - return; - - if (jid->node && jabber_chat_find(js, jid->node, jid->domain)) { - /* For a conversation, include the resource (indicates the user). */ - jabber_buddy_get_info_for_jid(js, who); - } else { - char *bare_jid = jabber_get_bare_jid(who); - jabber_buddy_get_info_for_jid(js, bare_jid); - g_free(bare_jid); - } - - jabber_id_free(jid); -} - -static void jabber_buddy_set_invisibility(JabberStream *js, const char *who, - gboolean invisible) -{ - PurplePresence *gpresence; - PurpleAccount *account; - PurpleStatus *status; - JabberBuddy *jb = jabber_buddy_find(js, who, TRUE); - PurpleXmlNode *presence; - JabberBuddyState state; - char *msg; - int priority; - - account = purple_connection_get_account(js->gc); - gpresence = purple_account_get_presence(account); - status = purple_presence_get_active_status(gpresence); - - purple_status_to_jabber(status, &state, &msg, &priority); - presence = jabber_presence_create_js(js, state, msg, priority); - - g_free(msg); - - purple_xmlnode_set_attrib(presence, "to", who); - if(invisible) { - purple_xmlnode_set_attrib(presence, "type", "invisible"); - jb->invisible |= JABBER_INVIS_BUDDY; - } else { - jb->invisible &= ~JABBER_INVIS_BUDDY; - } - - jabber_send(js, presence); - purple_xmlnode_free(presence); -} - -static void -jabber_buddy_make_invisible(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - JabberStream *js; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - js = purple_connection_get_protocol_data(gc); - - jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), TRUE); -} - -static void -jabber_buddy_make_visible(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - JabberStream *js; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - js = purple_connection_get_protocol_data(gc); - - jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), FALSE); -} - -static void cancel_presence_notification(gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - JabberStream *js; - - buddy = data; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - js = purple_connection_get_protocol_data(gc); - - jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribed"); -} - -static void -jabber_buddy_cancel_presence_notification(PurpleBlistNode *node, - G_GNUC_UNUSED gpointer data) -{ - PurpleBuddy *buddy; - PurpleAccount *account; - PurpleConnection *gc; - const gchar *name; - char *msg; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - name = purple_buddy_get_name(buddy); - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - - msg = g_strdup_printf(_("%s will no longer be able to see your status " - "updates. Do you want to continue?"), name); - purple_request_yes_no(gc, NULL, _("Cancel Presence Notification"), - msg, 0 /* Yes */, purple_request_cpar_from_account(account), buddy, - cancel_presence_notification, NULL /* Do nothing */); - g_free(msg); -} - -static void -jabber_buddy_rerequest_auth(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - JabberStream *js; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - js = purple_connection_get_protocol_data(gc); - - jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "subscribe"); -} - -static void -jabber_buddy_unsubscribe(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - JabberStream *js; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - js = purple_connection_get_protocol_data(gc); - - jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribe"); -} - -static void -jabber_buddy_login(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - if(PURPLE_IS_BUDDY(node)) { - /* simply create a directed presence of the current status */ - PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - JabberStream *js = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - PurplePresence *gpresence = purple_account_get_presence(account); - PurpleStatus *status = purple_presence_get_active_status(gpresence); - PurpleXmlNode *presence; - JabberBuddyState state; - char *msg; - int priority; - - purple_status_to_jabber(status, &state, &msg, &priority); - presence = jabber_presence_create_js(js, state, msg, priority); - - g_free(msg); - - purple_xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy)); - - jabber_send(js, presence); - purple_xmlnode_free(presence); - } -} - -static void -jabber_buddy_logout(PurpleBlistNode *node, G_GNUC_UNUSED gpointer data) -{ - if(PURPLE_IS_BUDDY(node)) { - /* simply create a directed unavailable presence */ - PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - JabberStream *js = purple_connection_get_protocol_data(gc); - PurpleXmlNode *presence; - - presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNAVAILABLE, NULL, 0); - - purple_xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy)); - - jabber_send(js, presence); - purple_xmlnode_free(presence); - } -} - -static GList *jabber_buddy_menu(PurpleBuddy *buddy) -{ - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - JabberStream *js = purple_connection_get_protocol_data(gc); - const char *name = purple_buddy_get_name(buddy); - JabberBuddy *jb = jabber_buddy_find(js, name, TRUE); - GList *jbrs; - - GList *m = NULL; - PurpleActionMenu *act; - - if(!jb) - return m; - - if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 && - jb != js->user_jb) { - if(jb->invisible & JABBER_INVIS_BUDDY) { - act = purple_action_menu_new(_("Un-hide From"), - G_CALLBACK(jabber_buddy_make_visible), - NULL, NULL); - } else { - act = purple_action_menu_new(_("Temporarily Hide From"), - G_CALLBACK(jabber_buddy_make_invisible), - NULL, NULL); - } - m = g_list_append(m, act); - } - - if(jb->subscription & JABBER_SUB_FROM && jb != js->user_jb) { - act = purple_action_menu_new(_("Cancel Presence Notification"), - G_CALLBACK(jabber_buddy_cancel_presence_notification), - NULL, NULL); - m = g_list_append(m, act); - } - - if(!(jb->subscription & JABBER_SUB_TO)) { - act = purple_action_menu_new(_("(Re-)Request authorization"), - G_CALLBACK(jabber_buddy_rerequest_auth), - NULL, NULL); - m = g_list_append(m, act); - - } else if (jb != js->user_jb) { - - /* shouldn't this just happen automatically when the buddy is - removed? */ - act = purple_action_menu_new(_("Unsubscribe"), - G_CALLBACK(jabber_buddy_unsubscribe), - NULL, NULL); - m = g_list_append(m, act); - } - - /* - * This if-condition implements parts of XEP-0100: Gateway Interaction - * - * According to stpeter, there is no way to know if a jid on the roster is a gateway without sending a disco#info. - * However, since the gateway might appear offline to us, we cannot get that information. Therefore, I just assume - * that gateways on the roster can be identified by having no '@' in their jid. This is a faily safe assumption, since - * people don't tend to have a server or other service there. - * - * TODO: Use disco#info... - */ - if (strchr(name, '@') == NULL) { - act = purple_action_menu_new(_("Log In"), - G_CALLBACK(jabber_buddy_login), - NULL, NULL); - m = g_list_append(m, act); - act = purple_action_menu_new(_("Log Out"), - G_CALLBACK(jabber_buddy_logout), - NULL, NULL); - m = g_list_append(m, act); - } - - /* add all ad hoc commands to the action menu */ - for(jbrs = jb->resources; jbrs; jbrs = g_list_next(jbrs)) { - JabberBuddyResource *jbr = jbrs->data; - GList *commands; - if (!jbr->commands) - continue; - for(commands = jbr->commands; commands; commands = g_list_next(commands)) { - JabberAdHocCommands *cmd = commands->data; - act = purple_action_menu_new(cmd->name, G_CALLBACK(jabber_adhoc_execute_action), cmd, NULL); - m = g_list_append(m, act); - } - } - - return m; -} - -GList * -jabber_blist_node_menu(G_GNUC_UNUSED PurpleProtocolClient *client, - PurpleBlistNode *node) -{ - if(PURPLE_IS_BUDDY(node)) { - return jabber_buddy_menu((PurpleBuddy *) node); - } else { - return NULL; - } -} - -gboolean -jabber_resource_know_capabilities(const JabberBuddyResource *jbr) -{ - return jbr->caps != NULL; -} - -gboolean -jabber_resource_has_capability(const JabberBuddyResource *jbr, const gchar *cap) -{ - const GList *node = NULL; - - if (jbr->caps == NULL) { - purple_debug_info("jabber", - "Unable to find caps: nothing known about buddy\n"); - return FALSE; - } - - node = g_list_find_custom(jbr->caps->features, cap, (GCompareFunc)strcmp); - return (node != NULL); -} - -const gchar * -jabber_resource_get_identity_category_type(const JabberBuddyResource *jbr, - const gchar *category) -{ - const GList *iter = NULL; - - if (jbr->caps != NULL) { - for (iter = jbr->caps->identities ; iter ; iter = g_list_next(iter)) { - const JabberIdentity *identity = - (JabberIdentity *) iter->data; - - if (purple_strequal(identity->category, category)) { - return identity->type; - } - } - } - - return NULL; -}
--- a/libpurple/protocols/jabber/buddy.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/** - * @file buddy.h Buddy handlers - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_BUDDY_H -#define PURPLE_JABBER_BUDDY_H - -typedef struct _JabberBuddy JabberBuddy; - -#include <purple.h> - -#include "jabber.h" -#include "caps.h" -#include "jutil.h" - -struct _JabberBuddy { - /** - * A sorted list of resources in priority descending order. - * This means that the first resource in the list is the - * "most available" (see resource_compare_cb in buddy.c for - * details). Don't play with this yourself, let - * jabber_buddy_track_resource and jabber_buddy_remove_resource do it. - */ - GList *resources; - char *error_msg; - enum { - JABBER_INVISIBLE_NONE = 0, - JABBER_INVISIBLE_SERVER = 1 << 1, - JABBER_INVIS_BUDDY = 1 << 2 - } invisible; - enum { - JABBER_SUB_NONE = 0, - JABBER_SUB_PENDING = 1 << 1, - JABBER_SUB_TO = 1 << 2, - JABBER_SUB_FROM = 1 << 3, - JABBER_SUB_BOTH = (JABBER_SUB_TO | JABBER_SUB_FROM), - JABBER_SUB_REMOVE = 1 << 4 - } subscription; -}; - -typedef struct { - JabberBuddy *jb; - char *name; - int priority; - JabberBuddyState state; - char *status; - time_t idle; - JabberCapabilities capabilities; - char *thread_id; - enum { - JABBER_CHAT_STATES_UNKNOWN, - JABBER_CHAT_STATES_UNSUPPORTED, - JABBER_CHAT_STATES_SUPPORTED - } chat_states; - struct { - char *version; - char *name; - char *os; - } client; - GTimeZone *tz_off; - JabberCapsClientInfo *caps; - GList *commands; - gboolean commands_fetched; -} JabberBuddyResource; - -void jabber_buddy_free(JabberBuddy *jb); -JabberBuddy *jabber_buddy_find(JabberStream *js, const char *name, - gboolean create); -JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb, - const char *resource); -JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource, - int priority, JabberBuddyState state, const char *status); -void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource); -void jabber_buddy_get_info(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const char *who); - -GList *jabber_blist_node_menu(PurpleProtocolClient *client, PurpleBlistNode *node); - -void jabber_set_info(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const char *info); -void jabber_setup_set_info(GSimpleAction *action, GVariant *parameter, gpointer data); -void jabber_set_buddy_icon(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleImage *img); - -void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js); - -void jabber_vcard_fetch_mine(JabberStream *js); - -gboolean jabber_resource_know_capabilities(const JabberBuddyResource *jbr); -gboolean jabber_resource_has_capability(const JabberBuddyResource *jbr, - const gchar *cap); - -const gchar * -jabber_resource_get_identity_category_type(const JabberBuddyResource *jbr, - const gchar *category); - -#endif /* PURPLE_JABBER_BUDDY_H */
--- a/libpurple/protocols/jabber/caps.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,661 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "caps.h" -#include "iq.h" -#include "presence.h" -#include "xdata.h" - -#define JABBER_CAPS_FILENAME "xmpp-caps.xml" - -typedef struct { - gchar *var; - GList *values; -} JabberDataFormField; - -static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo */ -static guint save_timer = 0; - -static guint jabber_caps_hash(gconstpointer data) { - const JabberCapsTuple *key = data; - guint nodehash = g_str_hash(key->node); - guint verhash = g_str_hash(key->ver); - guint hashhash = g_str_hash(key->hash); - return nodehash ^ verhash ^ hashhash; -} - -static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { - const JabberCapsTuple *name1 = v1; - const JabberCapsTuple *name2 = v2; - - return purple_strequal(name1->node, name2->node) && - purple_strequal(name1->ver, name2->ver) && - purple_strequal(name1->hash, name2->hash); -} - -void -jabber_caps_client_info_destroy(JabberCapsClientInfo *info) -{ - if (info == NULL) - return; - - g_list_free_full(info->identities, (GDestroyNotify)jabber_identity_free); - - g_list_free_full(info->features, g_free); - - g_list_free_full(info->forms, (GDestroyNotify)purple_xmlnode_free); - - g_free((char *)info->tuple.node); - g_free((char *)info->tuple.ver); - g_free((char *)info->tuple.hash); - - g_free(info); -} - -static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { - const JabberCapsTuple *tuple = key; - const JabberCapsClientInfo *props = value; - PurpleXmlNode *root = user_data; - PurpleXmlNode *client = purple_xmlnode_new_child(root, "client"); - GList *iter; - - purple_xmlnode_set_attrib(client, "node", tuple->node); - purple_xmlnode_set_attrib(client, "ver", tuple->ver); - purple_xmlnode_set_attrib(client, "hash", tuple->hash); - for(iter = props->identities; iter; iter = g_list_next(iter)) { - JabberIdentity *id = iter->data; - PurpleXmlNode *identity = purple_xmlnode_new_child(client, "identity"); - purple_xmlnode_set_attrib(identity, "category", id->category); - purple_xmlnode_set_attrib(identity, "type", id->type); - if (id->name) - purple_xmlnode_set_attrib(identity, "name", id->name); - if (id->lang) - purple_xmlnode_set_attrib(identity, "lang", id->lang); - } - - for(iter = props->features; iter; iter = g_list_next(iter)) { - const char *feat = iter->data; - PurpleXmlNode *feature = purple_xmlnode_new_child(client, "feature"); - purple_xmlnode_set_attrib(feature, "var", feat); - } - - for(iter = props->forms; iter; iter = g_list_next(iter)) { - /* FIXME: See #7814 */ - PurpleXmlNode *xdata = iter->data; - purple_xmlnode_insert_child(client, purple_xmlnode_copy(xdata)); - } -} - -static gboolean -do_jabber_caps_store(G_GNUC_UNUSED gpointer data) -{ - char *str; - int length = 0; - PurpleXmlNode *root = purple_xmlnode_new("capabilities"); - - g_hash_table_foreach(capstable, jabber_caps_store_client, root); - str = purple_xmlnode_to_formatted_str(root, &length); - purple_xmlnode_free(root); - purple_util_write_data_to_cache_file(JABBER_CAPS_FILENAME, str, length); - g_free(str); - - save_timer = 0; - return FALSE; -} - -static void -schedule_caps_save(void) -{ - if (save_timer == 0) - save_timer = g_timeout_add_seconds(5, do_jabber_caps_store, NULL); -} - -static void -jabber_caps_load(void) -{ - PurpleXmlNode *capsdata = purple_util_read_xml_from_cache_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); - PurpleXmlNode *client; - - if(!capsdata) - return; - - if (!purple_strequal(capsdata->name, "capabilities")) { - purple_xmlnode_free(capsdata); - return; - } - - for (client = capsdata->child; client; client = client->next) { - if (client->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - if (purple_strequal(client->name, "client")) { - JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1); - JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple; - PurpleXmlNode *child; - key->node = g_strdup(purple_xmlnode_get_attrib(client,"node")); - key->ver = g_strdup(purple_xmlnode_get_attrib(client,"ver")); - key->hash = g_strdup(purple_xmlnode_get_attrib(client,"hash")); - - for (child = client->child; child; child = child->next) { - if (child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - if (purple_strequal(child->name, "feature")) { - const char *var = purple_xmlnode_get_attrib(child, "var"); - if(!var) - continue; - value->features = g_list_append(value->features,g_strdup(var)); - } else if (purple_strequal(child->name, "identity")) { - const char *category = purple_xmlnode_get_attrib(child, "category"); - const char *type = purple_xmlnode_get_attrib(child, "type"); - const char *name = purple_xmlnode_get_attrib(child, "name"); - const char *lang = purple_xmlnode_get_attrib(child, "lang"); - JabberIdentity *id; - - if (!category || !type) - continue; - - id = jabber_identity_new(category, type, lang, name); - value->identities = g_list_append(value->identities,id); - } else if (purple_strequal(child->name, "x")) { - /* TODO: See #7814 -- this might cause problems if anyone - * ever actually specifies forms. In fact, for this to - * work properly, that bug needs to be fixed in - * purple_xmlnode_from_str, not the output version... */ - value->forms = g_list_append(value->forms, purple_xmlnode_copy(child)); - } - } - - g_hash_table_replace(capstable, key, value); - } - } - purple_xmlnode_free(capsdata); -} - -void jabber_caps_init(void) -{ - capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, NULL, (GDestroyNotify)jabber_caps_client_info_destroy); - jabber_caps_load(); -} - -void jabber_caps_uninit(void) -{ - if(save_timer != 0) { - g_clear_handle_id(&save_timer, g_source_remove); - do_jabber_caps_store(NULL); - } - g_clear_pointer(&capstable, g_hash_table_destroy); -} - -typedef struct { - jabber_caps_get_info_cb cb; - gpointer cb_data; - - char *who; - char *node; - char *ver; - char *hash; - - JabberCapsClientInfo *info; -} jabber_caps_cbplususerdata; - -static void -cbplususerdata_destroy(jabber_caps_cbplususerdata *data) -{ - if (data == NULL) - return; - - g_free(data->who); - g_free(data->node); - g_free(data->ver); - g_free(data->hash); - - /* If we have info here, it's already in the capstable, so don't free it */ - - g_free(data); -} - -static void -jabber_caps_get_info_complete(jabber_caps_cbplususerdata *userdata) -{ - if (userdata->cb) { - userdata->cb(userdata->info, userdata->cb_data); - userdata->info = NULL; - } -} - -static void -jabber_caps_client_iqcb(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - gpointer data) -{ - jabber_caps_cbplususerdata *userdata = data; - PurpleXmlNode *query = NULL; - JabberCapsClientInfo *info = NULL, *value; - JabberCapsTuple key; - gchar *hash = NULL; - GChecksumType hash_type; - gboolean supported_hash = TRUE; - - query = purple_xmlnode_get_child_with_namespace(packet, "query", - NS_DISCO_INFO); - if(query == NULL || type == JABBER_IQ_ERROR) { - userdata->cb(NULL, userdata->cb_data); - cbplususerdata_destroy(userdata); - return; - } - - /* check hash */ - info = jabber_caps_parse_client_info(query); - - if(purple_strequal(userdata->hash, "sha-1")) { - hash_type = G_CHECKSUM_SHA1; - } else if(purple_strequal(userdata->hash, "md5")) { - hash_type = G_CHECKSUM_MD5; - } else { - supported_hash = FALSE; - } - - if (supported_hash) { - hash = jabber_caps_calculate_hash(info, hash_type); - } - - if (hash == NULL || !purple_strequal(hash, userdata->ver)) { - purple_debug_warning("jabber", - "Could not validate caps info from %s. " - "Expected %s, got %s", - purple_xmlnode_get_attrib(packet, "from"), - userdata->ver, hash ? hash : "(null)"); - - userdata->cb(NULL, userdata->cb_data); - jabber_caps_client_info_destroy(info); - cbplususerdata_destroy(userdata); - g_free(hash); - return; - } - - g_free(hash); - - key.node = userdata->node; - key.ver = userdata->ver; - key.hash = userdata->hash; - - /* Use the copy of this data already in the table if it exists or insert - * a new one if we need to */ - if ((value = g_hash_table_lookup(capstable, &key))) { - jabber_caps_client_info_destroy(info); - info = value; - } else { - JabberCapsTuple *n_key = NULL; - - if (G_UNLIKELY(info == NULL)) { - g_warn_if_reached(); - return; - } - - n_key = (JabberCapsTuple *)&info->tuple; - n_key->node = userdata->node; - n_key->ver = userdata->ver; - n_key->hash = userdata->hash; - userdata->node = userdata->ver = userdata->hash = NULL; - - /* The capstable gets a reference */ - g_hash_table_insert(capstable, n_key, info); - schedule_caps_save(); - } - - userdata->info = info; - - jabber_caps_get_info_complete(userdata); - - cbplususerdata_destroy(userdata); -} - -void -jabber_caps_get_info(JabberStream *js, const char *who, const char *node, - const char *ver, const char *hash, - jabber_caps_get_info_cb cb, gpointer user_data) -{ - JabberCapsClientInfo *info = NULL; - JabberCapsTuple key; - jabber_caps_cbplususerdata *userdata = NULL; - JabberIq *iq = NULL; - PurpleXmlNode *query = NULL; - char *nodever = NULL; - - /* Using this in a read-only fashion, so the cast is OK */ - key.node = (char *)node; - key.ver = (char *)ver; - key.hash = (char *)hash; - - info = g_hash_table_lookup(capstable, &key); - if (info != NULL) { - /* We already have all the information we care about */ - if (cb) { - cb(info, user_data); - } - return; - } - - userdata = g_new0(jabber_caps_cbplususerdata, 1); - /* We start out with 0 references. Every query takes one */ - userdata->cb = cb; - userdata->cb_data = user_data; - userdata->who = g_strdup(who); - userdata->node = g_strdup(node); - userdata->ver = g_strdup(ver); - userdata->hash = g_strdup(hash); - - /* If we don't have the basic information about the client, we need to - * fetch it. */ - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO); - query = purple_xmlnode_get_child_with_namespace(iq->node, "query", - NS_DISCO_INFO); - nodever = g_strdup_printf("%s#%s", node, ver); - purple_xmlnode_set_attrib(query, "node", nodever); - g_free(nodever); - purple_xmlnode_set_attrib(iq->node, "to", who); - - jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata); - jabber_iq_send(iq); -} - -static gint -jabber_xdata_compare(gconstpointer a, gconstpointer b) -{ - const PurpleXmlNode *aformtypefield = a; - const PurpleXmlNode *bformtypefield = b; - char *aformtype; - char *bformtype; - int result; - - aformtype = jabber_x_data_get_formtype(aformtypefield); - bformtype = jabber_x_data_get_formtype(bformtypefield); - - result = strcmp(aformtype, bformtype); - g_free(aformtype); - g_free(bformtype); - return result; -} - -JabberCapsClientInfo *jabber_caps_parse_client_info(PurpleXmlNode *query) -{ - PurpleXmlNode *child; - JabberCapsClientInfo *info; - - if (!query || !purple_strequal(query->name, "query") || - !purple_strequal(query->xmlns, NS_DISCO_INFO)) - return NULL; - - info = g_new0(JabberCapsClientInfo, 1); - - for(child = query->child; child; child = child->next) { - if (child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - if (purple_strequal(child->name, "identity")) { - /* parse identity */ - const char *category = purple_xmlnode_get_attrib(child, "category"); - const char *type = purple_xmlnode_get_attrib(child, "type"); - const char *name = purple_xmlnode_get_attrib(child, "name"); - const char *lang = purple_xmlnode_get_attrib(child, "lang"); - JabberIdentity *id; - - if (!category || !type) - continue; - - id = jabber_identity_new(category, type, lang, name); - info->identities = g_list_append(info->identities, id); - } else if (purple_strequal(child->name, "feature")) { - /* parse feature */ - const char *var = purple_xmlnode_get_attrib(child, "var"); - if (var) - info->features = g_list_prepend(info->features, g_strdup(var)); - } else if (purple_strequal(child->name, "x")) { - if (purple_strequal(child->xmlns, "jabber:x:data")) { - /* x-data form */ - PurpleXmlNode *dataform = purple_xmlnode_copy(child); - info->forms = g_list_append(info->forms, dataform); - } - } - } - return info; -} - -static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) -{ - const JabberDataFormField *ac = a; - const JabberDataFormField *bc = b; - - return strcmp(ac->var, bc->var); -} - -static GList* jabber_caps_xdata_get_fields(const PurpleXmlNode *x) -{ - GList *fields = NULL; - PurpleXmlNode *field; - - if (!x) - return NULL; - - for (field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) { - PurpleXmlNode *value; - JabberDataFormField *xdatafield = g_new0(JabberDataFormField, 1); - xdatafield->var = g_strdup(purple_xmlnode_get_attrib(field, "var")); - - for (value = purple_xmlnode_get_child(field, "value"); value; value = purple_xmlnode_get_next_twin(value)) { - gchar *val = purple_xmlnode_get_data(value); - xdatafield->values = g_list_prepend(xdatafield->values, val); - } - - xdatafield->values = g_list_sort(xdatafield->values, (GCompareFunc)strcmp); - fields = g_list_prepend(fields, xdatafield); - } - - fields = g_list_sort(fields, jabber_caps_xdata_field_compare); - return fields; -} - -static void -append_escaped_string(GChecksum *hash, const gchar *str) -{ - g_return_if_fail(hash != NULL); - - if (str && *str) { - char *tmp = g_markup_escape_text(str, -1); - g_checksum_update(hash, (const guchar *)tmp, -1); - g_free(tmp); - } - - g_checksum_update(hash, (const guchar *)"<", -1); -} - -gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, - GChecksumType hash_type) -{ - GChecksum *hash; - GList *node; - guint8 *checksum; - gsize checksum_size; - gchar *ret; - - if (!info) - return NULL; - - /* sort identities, features and x-data forms */ - info->identities = g_list_sort(info->identities, jabber_identity_compare); - info->features = g_list_sort(info->features, (GCompareFunc)strcmp); - info->forms = g_list_sort(info->forms, jabber_xdata_compare); - - hash = g_checksum_new(hash_type); - - if (hash == NULL) { - return NULL; - } - - /* Add identities to the hash data */ - for (node = info->identities; node; node = node->next) { - JabberIdentity *id = (JabberIdentity*)node->data; - char *category = g_markup_escape_text(id->category, -1); - char *type = g_markup_escape_text(id->type, -1); - char *lang = NULL; - char *name = NULL; - char *tmp; - - if (id->lang) - lang = g_markup_escape_text(id->lang, -1); - if (id->name) - name = g_markup_escape_text(id->name, -1); - - tmp = g_strconcat(category, "/", type, "/", lang ? lang : "", - "/", name ? name : "", "<", NULL); - - g_checksum_update(hash, (const guchar *)tmp, -1); - - g_free(tmp); - g_free(category); - g_free(type); - g_free(lang); - g_free(name); - } - - /* concat features to the verification string */ - for (node = info->features; node; node = node->next) { - append_escaped_string(hash, node->data); - } - - /* concat x-data forms to the verification string */ - for(node = info->forms; node; node = node->next) { - PurpleXmlNode *data = (PurpleXmlNode *)node->data; - gchar *formtype = jabber_x_data_get_formtype(data); - GList *fields = jabber_caps_xdata_get_fields(data); - - /* append FORM_TYPE's field value to the verification string */ - append_escaped_string(hash, formtype); - g_free(formtype); - - while (fields) { - JabberDataFormField *field = (JabberDataFormField*)fields->data; - - if (!purple_strequal(field->var, "FORM_TYPE")) { - /* Append the "var" attribute */ - append_escaped_string(hash, field->var); - /* Append <value/> elements' cdata */ - while (field->values) { - append_escaped_string(hash, field->values->data); - g_free(field->values->data); - field->values = g_list_delete_link(field->values, - field->values); - } - } else { - g_list_free_full(field->values, g_free); - } - - g_free(field->var); - g_free(field); - - fields = g_list_delete_link(fields, fields); - } - } - - checksum_size = g_checksum_type_get_length(hash_type); - checksum = g_new(guint8, checksum_size); - - /* generate hash */ - g_checksum_get_digest(hash, checksum, &checksum_size); - - ret = g_base64_encode(checksum, checksum_size); - g_free(checksum); - g_checksum_free(hash); - - return ret; -} - -void jabber_caps_calculate_own_hash(JabberStream *js) { - JabberCapsClientInfo info; - GList *iter = NULL; - GList *features = NULL; - - if (!jabber_identities && !jabber_features) { - /* This really shouldn't ever happen */ - purple_debug_warning("jabber", "No features or identities, cannot calculate own caps hash.\n"); - g_free(js->caps_hash); - js->caps_hash = NULL; - return; - } - - /* build the currently-supported list of features */ - if (jabber_features) { - for (iter = jabber_features; iter; iter = iter->next) { - JabberFeature *feat = iter->data; - if(!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { - features = g_list_append(features, feat->namespace); - } - } - } - - info.features = features; - /* TODO: This copy can go away, I think, since jabber_identities - * is pre-sorted, so the sort in calculate_hash should be idempotent. - * However, I want to test that. --darkrain - */ - info.identities = g_list_copy(jabber_identities); - info.forms = NULL; - - g_free(js->caps_hash); - js->caps_hash = jabber_caps_calculate_hash(&info, G_CHECKSUM_SHA1); - g_list_free(info.identities); - g_list_free(info.features); -} - -const gchar* jabber_caps_get_own_hash(JabberStream *js) -{ - if (!js->caps_hash) - jabber_caps_calculate_own_hash(js); - - return js->caps_hash; -} - -void -jabber_caps_broadcast_change(void) -{ - PurpleAccountManager *manager = NULL; - GList *node, *accounts; - - manager = purple_account_manager_get_default(); - accounts = purple_account_manager_get_enabled(manager); - - for (node = accounts; node; node = node->next) { - PurpleAccount *account = node->data; - const char *protocol_id = purple_account_get_protocol_id(account); - if (purple_strequal("prpl-jabber", protocol_id) && purple_account_is_connected(account)) { - PurpleConnection *gc = purple_account_get_connection(account); - jabber_presence_send(purple_connection_get_protocol_data(gc), TRUE); - } - } - - g_list_free(accounts); -} -
--- a/libpurple/protocols/jabber/caps.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_CAPS_H -#define PURPLE_JABBER_CAPS_H - -typedef struct _JabberCapsClientInfo JabberCapsClientInfo; - -#include "jabber.h" - -/* Implementation of XEP-0115 - Entity Capabilities */ - -typedef struct { - const char *node; - const char *ver; - const char *hash; -} JabberCapsTuple; - -struct _JabberCapsClientInfo { - GList *identities; /* JabberIdentity */ - GList *features; /* char * */ - GList *forms; /* PurpleXmlNode * */ - - const JabberCapsTuple tuple; -}; - -typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer user_data); - -void jabber_caps_init(void); -void jabber_caps_uninit(void); - -/** - * Main entity capabilities function to get the capabilities of a contact. - * - * The callback will be called synchronously if we already have the - * capabilities for the specified (node,ver,hash). - */ -void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, - const char *ver, const char *hash, - jabber_caps_get_info_cb cb, - gpointer user_data); - -/** - * Takes a JabberCapsClientInfo pointer and returns the caps hash according to - * XEP-0115 Version 1.5. - * - * @param info A JabberCapsClientInfo pointer. - * @param hash_type GChecksumType to be used. Either sha-1 or md5. - * @return The base64 encoded SHA-1 hash; must be freed by caller - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, - GChecksumType hash_type); - -/** - * Calculate SHA1 hash for own featureset. - */ -void jabber_caps_calculate_own_hash(JabberStream *js); - -/** Get the current caps hash. - * @ret hash -**/ -const gchar* jabber_caps_get_own_hash(JabberStream *js); - -/** - * Broadcast a new calculated hash using a <presence> stanza. - */ -void jabber_caps_broadcast_change(void); - -/** - * Parse the <query/> element from an IQ stanza into a JabberCapsClientInfo - * struct. - * - * Exposed for tests - * - * @param query The 'query' element from an IQ reply stanza. - * @returns A JabberCapsClientInfo struct, or NULL on error - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -JabberCapsClientInfo *jabber_caps_parse_client_info(PurpleXmlNode *query); - -/** - * Release memory of a JabberCapsClientInfo struct - * returned by jabber_caps_parse_client_info. - * - * Exposed for tests - * - * @param info The info object to free. - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -void jabber_caps_client_info_destroy(JabberCapsClientInfo *info); - -#endif /* PURPLE_JABBER_CAPS_H */
--- a/libpurple/protocols/jabber/chat.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1225 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "chat.h" -#include "iq.h" -#include "message.h" -#include "presence.h" -#include "xdata.h" -#include "data.h" - -GList * -jabber_chat_info(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - G_GNUC_UNUSED PurpleConnection *connection) -{ - GList *m = NULL; - PurpleProtocolChatEntry *pce; - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Room"); - pce->identifier = "room"; - pce->required = TRUE; - m = g_list_append(m, pce); - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Server"); - pce->identifier = "server"; - pce->required = TRUE; - m = g_list_append(m, pce); - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Handle"); - pce->identifier = "handle"; - pce->required = TRUE; - m = g_list_append(m, pce); - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Password"); - pce->identifier = "password"; - pce->secret = TRUE; - m = g_list_append(m, pce); - - return m; -} - -GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name) -{ - GHashTable *defaults; - JabberStream *js = purple_connection_get_protocol_data(gc); - - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - g_hash_table_insert(defaults, "handle", g_strdup(js->user->node)); - - if (js->chat_servers) - g_hash_table_insert(defaults, "server", g_strdup(js->chat_servers->data)); - - if (chat_name != NULL) { - JabberID *jid = jabber_id_new(chat_name); - if(jid) { - g_hash_table_insert(defaults, "room", g_strdup(jid->node)); - if(jid->domain) - g_hash_table_replace(defaults, "server", g_strdup(jid->domain)); - if(jid->resource) - g_hash_table_replace(defaults, "handle", g_strdup(jid->resource)); - jabber_id_free(jid); - } - } - - return defaults; -} - -JabberChat *jabber_chat_find(JabberStream *js, const char *room, - const char *server) -{ - JabberChat *chat = NULL; - - g_return_val_if_fail(room != NULL, NULL); - g_return_val_if_fail(server != NULL, NULL); - - if(NULL != js->chats) - { - char *room_jid = g_strdup_printf("%s@%s", room, server); - - chat = g_hash_table_lookup(js->chats, room_jid); - g_free(room_jid); - } - - return chat; -} - -static gboolean -find_by_id_cb(G_GNUC_UNUSED gpointer key, gpointer value, gpointer user_data) -{ - JabberChat *chat = value; - - return chat->id == GPOINTER_TO_INT(user_data); -} - -JabberChat *jabber_chat_find_by_id(JabberStream *js, int id) -{ - return g_hash_table_find(js->chats, find_by_id_cb, GINT_TO_POINTER(id)); -} - -JabberChat *jabber_chat_find_by_conv(PurpleChatConversation *conv) -{ - PurpleAccount *account = purple_conversation_get_account(PURPLE_CONVERSATION(conv)); - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js; - int id; - if (!gc) - return NULL; - js = purple_connection_get_protocol_data(gc); - id = purple_chat_conversation_get_id(conv); - return jabber_chat_find_by_id(js, id); -} - -void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *name) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberChat *chat; - PurpleXmlNode *message, *body, *x, *invite; - char *room_jid; - - chat = jabber_chat_find_by_id(js, id); - if(!chat) - return; - - message = purple_xmlnode_new("message"); - - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - if(chat->muc) { - purple_xmlnode_set_attrib(message, "to", room_jid); - x = purple_xmlnode_new_child(message, "x"); - purple_xmlnode_set_namespace(x, "http://jabber.org/protocol/muc#user"); - invite = purple_xmlnode_new_child(x, "invite"); - purple_xmlnode_set_attrib(invite, "to", name); - if (msg) { - body = purple_xmlnode_new_child(invite, "reason"); - purple_xmlnode_insert_data(body, msg, -1); - } - } else { - purple_xmlnode_set_attrib(message, "to", name); - /* - * Putting the reason into the body was an 'undocumented protocol, - * ...not part of "groupchat 1.0"'. - * http://xmpp.org/extensions/attic/jep-0045-1.16.html#invite - * - * Left here for compatibility. - */ - if (msg) { - body = purple_xmlnode_new_child(message, "body"); - purple_xmlnode_insert_data(body, msg, -1); - } - - x = purple_xmlnode_new_child(message, "x"); - purple_xmlnode_set_attrib(x, "jid", room_jid); - - /* The better place for it! XEP-0249 style. */ - if (msg) - purple_xmlnode_set_attrib(x, "reason", msg); - purple_xmlnode_set_namespace(x, "jabber:x:conference"); - } - - jabber_send(js, message); - purple_xmlnode_free(message); - g_free(room_jid); -} - -void jabber_chat_member_free(JabberChatMember *jcm); - -gchar * -jabber_get_chat_name(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - GHashTable *data) -{ - char *room, *server, *chat_name = NULL; - - room = g_hash_table_lookup(data, "room"); - server = g_hash_table_lookup(data, "server"); - - if (room && server) { - chat_name = g_strdup_printf("%s@%s", room, server); - } - return chat_name; -} - -static void insert_in_hash_table(gpointer key, gpointer value, gpointer user_data) -{ - GHashTable *hash_table = (GHashTable *)user_data; - g_hash_table_insert(hash_table, g_strdup(key), g_strdup(value)); -} - -static JabberChat * -jabber_chat_new(JabberStream *js, const char *room, const char *server, - const char *handle, G_GNUC_UNUSED const char *password, - GHashTable *data) -{ - JabberChat *chat; - char *jid; - - if (jabber_chat_find(js, room, server) != NULL) - return NULL; - - chat = g_new0(JabberChat, 1); - chat->js = js; - chat->joined = 0; - - chat->room = g_strdup(room); - chat->server = g_strdup(server); - chat->handle = g_strdup(handle); - - /* Copy the data hash table to chat->components */ - chat->components = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - if (data == NULL) { - g_hash_table_insert(chat->components, g_strdup("handle"), g_strdup(handle)); - g_hash_table_insert(chat->components, g_strdup("room"), g_strdup(room)); - g_hash_table_insert(chat->components, g_strdup("server"), g_strdup(server)); - /* g_hash_table_insert(chat->components, g_strdup("password"), g_strdup(server)); */ - } else { - g_hash_table_foreach(data, insert_in_hash_table, chat->components); - } - - chat->members = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, - (GDestroyNotify)jabber_chat_member_free); - - jid = g_strdup_printf("%s@%s", room, server); - g_hash_table_insert(js->chats, jid, chat); - - return chat; -} - -/* - * jabber_join_chat: - * @room: The room to join. This MUST be normalized already. - * @server: The server the room is on. This MUST be normalized already. - * @password: (nullable): The password (if required) to join the room. - * @data: (nullable): The chat hash table. If NULL, it will be generated for - * current core<>protocol API interface. - * - * In-protocol function for joining a chat room. Doesn't require sticking goop - * into a hash table. - */ -static JabberChat * -jabber_join_chat(JabberStream *js, const char *room, const char *server, - const char *handle, const char *password, GHashTable *data) -{ - JabberChat *chat; - - PurpleConnection *gc; - PurpleAccount *account; - PurpleStatus *status; - - PurpleXmlNode *presence, *x; - JabberBuddyState state; - char *msg; - int priority; - - char *jid; - - chat = jabber_chat_new(js, room, server, handle, password, data); - if (chat == NULL) - return NULL; - - gc = js->gc; - account = purple_connection_get_account(gc); - status = purple_account_get_active_status(account); - purple_status_to_jabber(status, &state, &msg, &priority); - - presence = jabber_presence_create_js(js, state, msg, priority); - g_free(msg); - - jid = g_strdup_printf("%s@%s/%s", room, server, handle); - purple_xmlnode_set_attrib(presence, "to", jid); - g_free(jid); - - x = purple_xmlnode_new_child(presence, "x"); - purple_xmlnode_set_namespace(x, "http://jabber.org/protocol/muc"); - - if (password && *password) { - PurpleXmlNode *p = purple_xmlnode_new_child(x, "password"); - purple_xmlnode_insert_data(p, password, -1); - } - - jabber_send(js, presence); - purple_xmlnode_free(presence); - - return chat; -} - -void jabber_chat_join(PurpleConnection *gc, GHashTable *data) -{ - char *room, *server, *handle, *passwd; - JabberID *jid; - JabberStream *js = purple_connection_get_protocol_data(gc); - char *tmp; - - room = g_hash_table_lookup(data, "room"); - server = g_hash_table_lookup(data, "server"); - handle = g_hash_table_lookup(data, "handle"); - passwd = g_hash_table_lookup(data, "password"); - - if(!room || !server) - return; - - if(!handle) - handle = js->user->node; - - if(!jabber_nodeprep_validate(room)) { - char *buf = g_strdup_printf(_("%s is not a valid room name"), room); - purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), - buf, purple_request_cpar_from_connection(gc)); - purple_serv_got_join_chat_failed(gc, data); - g_free(buf); - return; - } else if(!jabber_domain_validate(server)) { - char *buf = g_strdup_printf(_("%s is not a valid server name"), server); - purple_notify_error(gc, _("Invalid Server Name"), - _("Invalid Server Name"), buf, - purple_request_cpar_from_connection(gc)); - purple_serv_got_join_chat_failed(gc, data); - g_free(buf); - return; - } else if(!jabber_resourceprep_validate(handle)) { - char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle); - purple_notify_error(gc, _("Invalid Room Handle"), - _("Invalid Room Handle"), buf, - purple_request_cpar_from_connection(gc)); - purple_serv_got_join_chat_failed(gc, data); - g_free(buf); - return; - } - - /* Normalize the room and server parameters */ - tmp = g_strdup_printf("%s@%s", room, server); - jid = jabber_id_new(tmp); - g_free(tmp); - - if (jid == NULL) { - /* TODO: Error message */ - - g_return_if_reached(); - } - - /* - * Now that we've done all that nice core-interface stuff, let's join - * this room! - */ - jabber_join_chat(js, jid->node, jid->domain, handle, passwd, data); - jabber_id_free(jid); -} - -void -jabber_chat_leave(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *gc, gint id) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberChat *chat = jabber_chat_find_by_id(js, id); - - if(!chat) - return; - - jabber_chat_part(chat, NULL); - - chat->left = TRUE; -} - -void jabber_chat_destroy(JabberChat *chat) -{ - JabberStream *js = chat->js; - char *room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - g_hash_table_remove(js->chats, room_jid); - g_free(room_jid); -} - -void jabber_chat_free(JabberChat *chat) -{ - if(chat->config_dialog_handle) - purple_request_close(chat->config_dialog_type, chat->config_dialog_handle); - - g_free(chat->room); - g_free(chat->server); - g_free(chat->handle); - g_hash_table_destroy(chat->members); - g_hash_table_destroy(chat->components); - - g_clear_pointer(&chat->joined, g_date_time_unref); - - g_free(chat); -} - -gboolean jabber_chat_find_buddy(PurpleChatConversation *conv, const char *name) -{ - return purple_chat_conversation_has_user(conv, name); -} - -gchar * -jabber_chat_user_real_name(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *gc, gint id, const gchar *who) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberChat *chat; - JabberChatMember *jcm; - - chat = jabber_chat_find_by_id(js, id); - - if(!chat) - return NULL; - - jcm = g_hash_table_lookup(chat->members, who); - if (jcm != NULL && jcm->jid) - return g_strdup(jcm->jid); - - - return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who); -} - -static void jabber_chat_room_configure_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data) -{ - JabberChat *chat = data; - PurpleXmlNode *query; - JabberIq *iq; - char *to = g_strdup_printf("%s@%s", chat->room, chat->server); - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "http://jabber.org/protocol/muc#owner"); - purple_xmlnode_set_attrib(iq->node, "to", to); - g_free(to); - - query = purple_xmlnode_get_child(iq->node, "query"); - - purple_xmlnode_insert_child(query, result); - - jabber_iq_send(iq); -} - -static void -jabber_chat_room_configure_cb(JabberStream *js, const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query, *x; - char *msg; - JabberChat *chat; - JabberID *jid; - - if (!from) - return; - - if (type == JABBER_IQ_RESULT) { - jid = jabber_id_new(from); - - if(!jid) - return; - - chat = jabber_chat_find(js, jid->node, jid->domain); - jabber_id_free(jid); - - if(!chat) - return; - - if(!(query = purple_xmlnode_get_child(packet, "query"))) - return; - - for(x = purple_xmlnode_get_child(query, "x"); x; x = purple_xmlnode_get_next_twin(x)) { - const char *xmlns; - if(!(xmlns = purple_xmlnode_get_namespace(x))) - continue; - - if(purple_strequal(xmlns, "jabber:x:data")) { - chat->config_dialog_type = PURPLE_REQUEST_FIELDS; - chat->config_dialog_handle = jabber_x_data_request(js, x, jabber_chat_room_configure_x_data_cb, chat); - return; - } - } - } else if (type == JABBER_IQ_ERROR) { - char *msg = jabber_parse_error(js, packet, NULL); - - purple_notify_error(js->gc, _("Configuration error"), - _("Configuration error"), msg, - purple_request_cpar_from_connection(js->gc)); - - g_free(msg); - return; - } - - msg = g_strdup_printf("Unable to configure room %s", from); - - purple_notify_info(js->gc, _("Unable to configure"), - _("Unable to configure"), msg, - purple_request_cpar_from_connection(js->gc)); - g_free(msg); - -} - -void jabber_chat_request_room_configure(JabberChat *chat) { - JabberIq *iq; - char *room_jid; - - if(!chat) - return; - - chat->config_dialog_handle = NULL; - - if(!chat->muc) { - purple_notify_error(chat->js->gc, _("Room Configuration Error"), - _("Room Configuration Error"), - _("This room is not capable of being configured"), - purple_request_cpar_from_connection(chat->js->gc)); - return; - } - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, - "http://jabber.org/protocol/muc#owner"); - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - - jabber_iq_set_callback(iq, jabber_chat_room_configure_cb, NULL); - - jabber_iq_send(iq); - - g_free(room_jid); -} - -void jabber_chat_create_instant_room(JabberChat *chat) { - JabberIq *iq; - PurpleXmlNode *query, *x; - char *room_jid; - - if(!chat) - return; - - chat->config_dialog_handle = NULL; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, - "http://jabber.org/protocol/muc#owner"); - query = purple_xmlnode_get_child(iq->node, "query"); - x = purple_xmlnode_new_child(query, "x"); - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - purple_xmlnode_set_namespace(x, "jabber:x:data"); - purple_xmlnode_set_attrib(x, "type", "submit"); - - jabber_iq_send(iq); - - g_free(room_jid); -} - -static void -jabber_chat_register_x_data_result_cb(JabberStream *js, - G_GNUC_UNUSED const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - if (type == JABBER_IQ_ERROR) { - char *msg = jabber_parse_error(js, packet, NULL); - - purple_notify_error(js->gc, _("Registration error"), - _("Registration error"), msg, - purple_request_cpar_from_connection(js->gc)); - - g_free(msg); - return; - } -} - -static void jabber_chat_register_x_data_cb(JabberStream *js, PurpleXmlNode *result, gpointer data) -{ - JabberChat *chat = data; - PurpleXmlNode *query; - JabberIq *iq; - char *to = g_strdup_printf("%s@%s", chat->room, chat->server); - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); - purple_xmlnode_set_attrib(iq->node, "to", to); - g_free(to); - - query = purple_xmlnode_get_child(iq->node, "query"); - - purple_xmlnode_insert_child(query, result); - - jabber_iq_set_callback(iq, jabber_chat_register_x_data_result_cb, NULL); - - jabber_iq_send(iq); -} - -static void -jabber_chat_register_cb(JabberStream *js, const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query, *x; - char *msg; - JabberChat *chat; - JabberID *jid; - - if (!from) - return; - - if (type == JABBER_IQ_RESULT) { - jid = jabber_id_new(from); - - if(!jid) - return; - - chat = jabber_chat_find(js, jid->node, jid->domain); - jabber_id_free(jid); - - if(!chat) - return; - - if(!(query = purple_xmlnode_get_child(packet, "query"))) - return; - - for(x = purple_xmlnode_get_child(query, "x"); x; x = purple_xmlnode_get_next_twin(x)) { - const char *xmlns; - - if(!(xmlns = purple_xmlnode_get_namespace(x))) - continue; - - if(purple_strequal(xmlns, "jabber:x:data")) { - jabber_x_data_request(js, x, jabber_chat_register_x_data_cb, chat); - return; - } - } - } else if (type == JABBER_IQ_ERROR) { - char *msg = jabber_parse_error(js, packet, NULL); - - purple_notify_error(js->gc, _("Registration error"), - _("Registration error"), msg, - purple_request_cpar_from_connection(js->gc)); - - g_free(msg); - return; - } - - msg = g_strdup_printf("Unable to configure room %s", from); - - purple_notify_info(js->gc, _("Unable to configure"), _("Unable to " - "configure"), msg, purple_request_cpar_from_connection(js->gc)); - g_free(msg); - -} - -void jabber_chat_register(JabberChat *chat) -{ - JabberIq *iq; - char *room_jid; - - if(!chat) - return; - - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, "jabber:iq:register"); - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - g_free(room_jid); - - jabber_iq_set_callback(iq, jabber_chat_register_cb, NULL); - - jabber_iq_send(iq); -} - -/* merge this with the function below when we get everyone on the same page wrt /commands */ -void jabber_chat_change_topic(JabberChat *chat, const char *topic) -{ - JabberMessage *jm; - - jm = g_new0(JabberMessage, 1); - jm->js = chat->js; - jm->type = JABBER_MESSAGE_GROUPCHAT; - jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); - - if (topic && *topic) - jm->subject = g_strdup(topic); - else - jm->subject = g_strdup(""); - - jabber_message_send(jm); - jabber_message_free(jm); -} - -void -jabber_chat_set_topic(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *gc, gint id, const gchar *topic) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberChat *chat = jabber_chat_find_by_id(js, id); - - if(!chat) - return; - - jabber_chat_change_topic(chat, topic); -} - - -gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick) -{ - PurpleXmlNode *presence; - char *full_jid; - PurpleAccount *account; - PurpleStatus *status; - JabberBuddyState state; - char *msg; - int priority; - - if(!chat->muc) { - purple_conversation_write_system_message( - PURPLE_CONVERSATION(chat->conv), - _("Nick changing not supported in non-MUC chatrooms"), 0); - return FALSE; - } - - account = purple_connection_get_account(chat->js->gc); - status = purple_account_get_active_status(account); - - purple_status_to_jabber(status, &state, &msg, &priority); - - presence = jabber_presence_create_js(chat->js, state, msg, priority); - full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, nick); - purple_xmlnode_set_attrib(presence, "to", full_jid); - g_free(full_jid); - g_free(msg); - - jabber_send(chat->js, presence); - purple_xmlnode_free(presence); - - return TRUE; -} - -void jabber_chat_part(JabberChat *chat, const char *msg) -{ - char *room_jid; - PurpleXmlNode *presence; - - room_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, - chat->handle); - presence = purple_xmlnode_new("presence"); - purple_xmlnode_set_attrib(presence, "to", room_jid); - purple_xmlnode_set_attrib(presence, "type", "unavailable"); - if(msg) { - PurpleXmlNode *status = purple_xmlnode_new_child(presence, "status"); - purple_xmlnode_insert_data(status, msg, -1); - } - jabber_send(chat->js, presence); - - purple_xmlnode_free(presence); - g_free(room_jid); -} - -static void -roomlist_disco_result_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query; - PurpleXmlNode *item; - - if(!js->roomlist) - return; - - if (type == JABBER_IQ_ERROR) { - char *err = jabber_parse_error(js, packet, NULL); - purple_notify_error(js->gc, _("Error"), - _("Error retrieving room list"), err, - purple_request_cpar_from_connection(js->gc)); - purple_roomlist_set_in_progress(js->roomlist, FALSE); - g_object_unref(js->roomlist); - js->roomlist = NULL; - g_free(err); - return; - } - - if(!(query = purple_xmlnode_get_child(packet, "query"))) { - char *err = jabber_parse_error(js, packet, NULL); - purple_notify_error(js->gc, _("Error"), - _("Error retrieving room list"), err, - purple_request_cpar_from_connection(js->gc)); - purple_roomlist_set_in_progress(js->roomlist, FALSE); - g_object_unref(js->roomlist); - js->roomlist = NULL; - g_free(err); - return; - } - - for(item = purple_xmlnode_get_child(query, "item"); item; - item = purple_xmlnode_get_next_twin(item)) { - const char *name; - PurpleRoomlistRoom *room; - JabberID *jid; - - if(!(jid = jabber_id_new(purple_xmlnode_get_attrib(item, "jid")))) - continue; - name = purple_xmlnode_get_attrib(item, "name"); - - - room = purple_roomlist_room_new(jid->node, name); - purple_roomlist_room_add_field(room, "room", g_strdup(jid->node)); - purple_roomlist_room_add_field(room, "server", g_strdup(jid->domain)); - purple_roomlist_room_add(js->roomlist, room); - g_object_unref(room); - - jabber_id_free(jid); - } - purple_roomlist_set_in_progress(js->roomlist, FALSE); - g_object_unref(js->roomlist); - js->roomlist = NULL; -} - -static void -roomlist_cancel_cb(JabberStream *js, G_GNUC_UNUSED const char *server) { - if(js->roomlist) { - purple_roomlist_set_in_progress(js->roomlist, FALSE); - g_object_unref(js->roomlist); - js->roomlist = NULL; - } -} - -static void roomlist_ok_cb(JabberStream *js, const char *server) -{ - JabberIq *iq; - - if(!js->roomlist) - return; - - if(!server || !*server) { - purple_notify_error(js->gc, _("Invalid Server"), - _("Invalid Server"), NULL, - purple_request_cpar_from_connection(js->gc)); - purple_roomlist_set_in_progress(js->roomlist, FALSE); - return; - } - - purple_roomlist_set_in_progress(js->roomlist, TRUE); - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS); - - purple_xmlnode_set_attrib(iq->node, "to", server); - - jabber_iq_set_callback(iq, roomlist_disco_result_cb, NULL); - - jabber_iq_send(iq); -} - -PurpleRoomlist * -jabber_roomlist_get_list(G_GNUC_UNUSED PurpleProtocolRoomlist *protocol_roomlist, - PurpleConnection *gc) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - if(js->roomlist) - g_object_unref(js->roomlist); - - js->roomlist = purple_roomlist_new(purple_connection_get_account(js->gc)); - - purple_request_input(gc, _("Enter a Conference Server"), _("Enter a Conference Server"), - _("Select a conference server to query"), - js->chat_servers ? js->chat_servers->data : NULL, - FALSE, FALSE, NULL, - _("Find Rooms"), G_CALLBACK(roomlist_ok_cb), - _("Cancel"), G_CALLBACK(roomlist_cancel_cb), - purple_request_cpar_from_connection(gc), - js); - - return js->roomlist; -} - -void -jabber_roomlist_cancel(G_GNUC_UNUSED PurpleProtocolRoomlist *protocol_roomlist, - PurpleRoomlist *list) -{ - PurpleAccount *account; - PurpleConnection *gc; - JabberStream *js; - - account = purple_roomlist_get_account(list); - gc = purple_account_get_connection(account); - js = purple_connection_get_protocol_data(gc); - - purple_roomlist_set_in_progress(list, FALSE); - - if (js->roomlist == list) { - js->roomlist = NULL; - g_object_unref(list); - } -} - -char * -jabber_roomlist_room_serialize(G_GNUC_UNUSED PurpleProtocolRoomlist *protocol_roomlist, - PurpleRoomlistRoom *room) -{ - const gchar *room_name = NULL, *server = NULL; - - room_name = purple_roomlist_room_get_field(room, "room"); - server = purple_roomlist_room_get_field(room, "server"); - - return g_strdup_printf("%s@%s", room_name, server); -} - -void jabber_chat_member_free(JabberChatMember *jcm) -{ - g_free(jcm->handle); - g_free(jcm->jid); - g_free(jcm); -} - -void -jabber_chat_track_handle(JabberChat *chat, const char *handle, const char *jid, - G_GNUC_UNUSED const char *affiliation, - G_GNUC_UNUSED const char *role) -{ - JabberChatMember *jcm = g_new0(JabberChatMember, 1); - - jcm->handle = g_strdup(handle); - jcm->jid = g_strdup(jid); - - g_hash_table_replace(chat->members, jcm->handle, jcm); - - /* XXX: keep track of role and affiliation */ -} - -void jabber_chat_remove_handle(JabberChat *chat, const char *handle) -{ - g_hash_table_remove(chat->members, handle); -} - -gboolean jabber_chat_ban_user(JabberChat *chat, const char *who, const char *why) -{ - JabberChatMember *jcm; - const char *jid; - char *to; - JabberIq *iq; - PurpleXmlNode *query, *item, *reason; - - jcm = g_hash_table_lookup(chat->members, who); - if (jcm && jcm->jid) - jid = jcm->jid; - else if (strchr(who, '@') != NULL) - jid = who; - else - return FALSE; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, - "http://jabber.org/protocol/muc#admin"); - - to = g_strdup_printf("%s@%s", chat->room, chat->server); - purple_xmlnode_set_attrib(iq->node, "to", to); - g_free(to); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - purple_xmlnode_set_attrib(item, "jid", jid); - purple_xmlnode_set_attrib(item, "affiliation", "outcast"); - if(why) { - reason = purple_xmlnode_new_child(item, "reason"); - purple_xmlnode_insert_data(reason, why, -1); - } - - jabber_iq_send(iq); - - return TRUE; -} - -gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, const char *affiliation) -{ - JabberChatMember *jcm; - const char *jid; - char *to; - JabberIq *iq; - PurpleXmlNode *query, *item; - - jcm = g_hash_table_lookup(chat->members, who); - if (jcm && jcm->jid) - jid = jcm->jid; - else if (strchr(who, '@') != NULL) - jid = who; - else - return FALSE; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, - "http://jabber.org/protocol/muc#admin"); - - to = g_strdup_printf("%s@%s", chat->room, chat->server); - purple_xmlnode_set_attrib(iq->node, "to", to); - g_free(to); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - purple_xmlnode_set_attrib(item, "jid", jid); - purple_xmlnode_set_attrib(item, "affiliation", affiliation); - - jabber_iq_send(iq); - - return TRUE; -} - -static void -jabber_chat_affiliation_list_cb(JabberStream *js, - G_GNUC_UNUSED const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - JabberChat *chat; - PurpleXmlNode *query, *item; - int chat_id = GPOINTER_TO_INT(data); - GString *buf; - - if(!(chat = jabber_chat_find_by_id(js, chat_id))) - return; - - if (type == JABBER_IQ_ERROR) - return; - - if(!(query = purple_xmlnode_get_child(packet, "query"))) - return; - - buf = g_string_new(_("Affiliations:")); - - item = purple_xmlnode_get_child(query, "item"); - if (item) { - for( ; item; item = purple_xmlnode_get_next_twin(item)) { - const char *jid = purple_xmlnode_get_attrib(item, "jid"); - const char *affiliation = purple_xmlnode_get_attrib(item, "affiliation"); - if (jid && affiliation) - g_string_append_printf(buf, "\n%s %s", jid, affiliation); - } - } else { - buf = g_string_append_c(buf, '\n'); - buf = g_string_append_len(buf, _("No users found"), -1); - } - - purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv), - buf->str, PURPLE_MESSAGE_NO_LOG); - - g_string_free(buf, TRUE); -} - -gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation) -{ - JabberIq *iq; - char *room_jid; - PurpleXmlNode *query, *item; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, - "http://jabber.org/protocol/muc#admin"); - - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - purple_xmlnode_set_attrib(item, "affiliation", affiliation); - - jabber_iq_set_callback(iq, jabber_chat_affiliation_list_cb, GINT_TO_POINTER(chat->id)); - jabber_iq_send(iq); - - return TRUE; -} - -gboolean jabber_chat_role_user(JabberChat *chat, const char *who, - const char *role, const char *why) -{ - char *to; - JabberIq *iq; - PurpleXmlNode *query, *item; - JabberChatMember *jcm; - - jcm = g_hash_table_lookup(chat->members, who); - - if (!jcm || !jcm->handle) - return FALSE; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, - "http://jabber.org/protocol/muc#admin"); - - to = g_strdup_printf("%s@%s", chat->room, chat->server); - purple_xmlnode_set_attrib(iq->node, "to", to); - g_free(to); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - purple_xmlnode_set_attrib(item, "nick", jcm->handle); - purple_xmlnode_set_attrib(item, "role", role); - if (why) { - PurpleXmlNode *reason = purple_xmlnode_new_child(item, "reason"); - purple_xmlnode_insert_data(reason, why, -1); - } - - jabber_iq_send(iq); - - return TRUE; -} - -static void -jabber_chat_role_list_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - JabberChat *chat; - PurpleXmlNode *query, *item; - int chat_id = GPOINTER_TO_INT(data); - GString *buf; - - if(!(chat = jabber_chat_find_by_id(js, chat_id))) - return; - - if (type == JABBER_IQ_ERROR) - return; - - if(!(query = purple_xmlnode_get_child(packet, "query"))) - return; - - buf = g_string_new(_("Roles:")); - - item = purple_xmlnode_get_child(query, "item"); - if (item) { - for( ; item; item = purple_xmlnode_get_next_twin(item)) { - const char *jid = purple_xmlnode_get_attrib(item, "jid"); - const char *role = purple_xmlnode_get_attrib(item, "role"); - if (jid && role) - g_string_append_printf(buf, "\n%s %s", jid, role); - } - } else { - buf = g_string_append_c(buf, '\n'); - buf = g_string_append_len(buf, _("No users found"), -1); - } - - purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv), - buf->str, PURPLE_MESSAGE_NO_LOG); - - g_string_free(buf, TRUE); -} - -gboolean jabber_chat_role_list(JabberChat *chat, const char *role) -{ - JabberIq *iq; - char *room_jid; - PurpleXmlNode *query, *item; - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, - "http://jabber.org/protocol/muc#admin"); - - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - purple_xmlnode_set_attrib(item, "role", role); - - jabber_iq_set_callback(iq, jabber_chat_role_list_cb, GINT_TO_POINTER(chat->id)); - jabber_iq_send(iq); - - return TRUE; -} - -static void -jabber_chat_disco_traffic_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - gpointer data) -{ - JabberChat *chat; - int chat_id = GPOINTER_TO_INT(data); - - if(!(chat = jabber_chat_find_by_id(js, chat_id))) - return; - - /* defaults, in case the conference server doesn't - * support this request */ - chat->xhtml = TRUE; -} - -void jabber_chat_disco_traffic(JabberChat *chat) -{ - JabberIq *iq; - PurpleXmlNode *query; - char *room_jid; - - room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - - iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET, NS_DISCO_INFO); - - purple_xmlnode_set_attrib(iq->node, "to", room_jid); - - query = purple_xmlnode_get_child(iq->node, "query"); - - purple_xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/muc#traffic"); - - jabber_iq_set_callback(iq, jabber_chat_disco_traffic_cb, GINT_TO_POINTER(chat->id)); - - jabber_iq_send(iq); - - g_free(room_jid); -}
--- a/libpurple/protocols/jabber/chat.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/** - * @file chat.h Chat stuff - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_CHAT_H -#define PURPLE_JABBER_CHAT_H - -#include <purple.h> - -#include "jabber.h" - -typedef struct { - char *handle; - char *jid; -} JabberChatMember; - - -typedef struct { - JabberStream *js; - char *room; - char *server; - char *handle; - GHashTable *components; - int id; - PurpleChatConversation *conv; - gboolean muc; - gboolean xhtml; - PurpleRequestType config_dialog_type; - void *config_dialog_handle; - GHashTable *members; - gboolean left; - GDateTime *joined; -} JabberChat; - -GList *jabber_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *connection); -GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name); -char *jabber_get_chat_name(PurpleProtocolChat *protocol_chat, GHashTable *data); - -void jabber_chat_join(PurpleConnection *gc, GHashTable *data); -JabberChat *jabber_chat_find(JabberStream *js, const char *room, - const char *server); -JabberChat *jabber_chat_find_by_id(JabberStream *js, int id); -JabberChat *jabber_chat_find_by_conv(PurpleChatConversation *conv); -void jabber_chat_destroy(JabberChat *chat); -void jabber_chat_free(JabberChat *chat); -gboolean jabber_chat_find_buddy(PurpleChatConversation *conv, const char *name); -void jabber_chat_invite(PurpleConnection *gc, int id, const char *message, - const char *name); -void jabber_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id); -char *jabber_chat_user_real_name(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, const char *who); -void jabber_chat_request_room_configure(JabberChat *chat); -void jabber_chat_create_instant_room(JabberChat *chat); -void jabber_chat_register(JabberChat *chat); -void jabber_chat_change_topic(JabberChat *chat, const char *topic); -void jabber_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, const char *topic); -gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick); -void jabber_chat_part(JabberChat *chat, const char *msg); -void jabber_chat_track_handle(JabberChat *chat, const char *handle, - const char *jid, const char *affiliation, const char *role); -void jabber_chat_remove_handle(JabberChat *chat, const char *handle); -gboolean jabber_chat_ban_user(JabberChat *chat, const char *who, - const char *why); -gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, - const char *affiliation); -gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation); -gboolean jabber_chat_role_user(JabberChat *chat, const char *who, - const char *role, const char *why); -gboolean jabber_chat_role_list(JabberChat *chat, const char *role); - -PurpleRoomlist *jabber_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc); -void jabber_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list); -char *jabber_roomlist_room_serialize(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlistRoom *room); - -void jabber_chat_disco_traffic(JabberChat *chat); - -#endif /* PURPLE_JABBER_CHAT_H */
--- a/libpurple/protocols/jabber/data.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,474 +0,0 @@ -/* - * purple - Handling of XEP-0231: Bits of Binary. - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <stdlib.h> -#include <glib.h> -#include <string.h> - -#include <purple.h> -#include "libpurple/glibcompat.h" - -#include "data.h" -#include "iq.h" - -static GHashTable *local_data_by_alt = NULL; -static GHashTable *local_data_by_cid = NULL; -static GHashTable *remote_data_by_cid = NULL; - -JabberData * -jabber_data_create_from_data(gconstpointer rawdata, gsize size, - const char *type, gboolean ephemeral, - G_GNUC_UNUSED JabberStream *js) -{ - JabberData *data; - gchar *checksum; - - g_return_val_if_fail(rawdata != NULL, NULL); - g_return_val_if_fail(size > 0, NULL); - g_return_val_if_fail(type != NULL, NULL); - - checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA1, rawdata, size); - - data = g_new0(JabberData, 1); - data->cid = g_strdup_printf("sha1+%s@bob.xmpp.org", checksum); - data->type = g_strdup(type); - data->size = size; - data->ephemeral = ephemeral; - data->data = g_memdup2(rawdata, size); - - g_free(checksum); - return data; -} - -static void -jabber_data_delete(gpointer cbdata) -{ - JabberData *data = cbdata; - - g_free(data->cid); - g_free(data->type); - g_free(data->data); - g_free(data); -} - - -JabberData * -jabber_data_create_from_xml(PurpleXmlNode *tag) -{ - JabberData *data; - gchar *raw_data = NULL; - const gchar *cid, *type; - - g_return_val_if_fail(tag != NULL, NULL); - - /* check if this is a "data" tag */ - if (!purple_strequal(tag->name, "data")) { - purple_debug_error("jabber", "Invalid data element\n"); - return NULL; - } - - cid = purple_xmlnode_get_attrib(tag, "cid"); - type = purple_xmlnode_get_attrib(tag, "type"); - - if (!cid || !type) { - purple_debug_error("jabber", "cid or type missing\n"); - return NULL; - } - - raw_data = purple_xmlnode_get_data(tag); - - if (raw_data == NULL || *raw_data == '\0') { - purple_debug_error("jabber", "data element was empty"); - g_free(raw_data); - return NULL; - } - - data = g_new0(JabberData, 1); - data->data = g_base64_decode(raw_data, &data->size); - g_free(raw_data); - - if (data->data == NULL) { - purple_debug_error("jabber", "Malformed base64 data\n"); - g_free(data); - return NULL; - } - - data->cid = g_strdup(cid); - data->type = g_strdup(type); - - return data; -} - -void -jabber_data_destroy(JabberData *data) -{ - g_return_if_fail(data != NULL); - - jabber_data_delete(data); -} - -const char * -jabber_data_get_cid(const JabberData *data) -{ - g_return_val_if_fail(data != NULL, NULL); - - return data->cid; -} - - -const char * -jabber_data_get_type(const JabberData *data) -{ - g_return_val_if_fail(data != NULL, NULL); - - return data->type; -} - -gsize -jabber_data_get_size(const JabberData *data) -{ - g_return_val_if_fail(data != NULL, 0); - - return data->size; -} - -gpointer -jabber_data_get_data(const JabberData *data) -{ - g_return_val_if_fail(data != NULL, NULL); - - return data->data; -} - -PurpleXmlNode * -jabber_data_get_xml_definition(const JabberData *data) -{ - PurpleXmlNode *tag; - char *base64data; - - g_return_val_if_fail(data != NULL, NULL); - - tag = purple_xmlnode_new("data"); - base64data = g_base64_encode(data->data, data->size); - - purple_xmlnode_set_namespace(tag, NS_BOB); - purple_xmlnode_set_attrib(tag, "cid", data->cid); - purple_xmlnode_set_attrib(tag, "type", data->type); - - purple_xmlnode_insert_data(tag, base64data, -1); - - g_free(base64data); - - return tag; -} - -PurpleXmlNode * -jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt) -{ - PurpleXmlNode *img; - char *src; - - g_return_val_if_fail(data != NULL, NULL); - g_return_val_if_fail(alt != NULL, NULL); - - img = purple_xmlnode_new("img"); - purple_xmlnode_set_attrib(img, "alt", alt); - - src = g_strconcat("cid:", data->cid, NULL); - purple_xmlnode_set_attrib(img, "src", src); - g_free(src); - - return img; -} - -static PurpleXmlNode * -jabber_data_get_xml_request(const gchar *cid) -{ - PurpleXmlNode *tag = purple_xmlnode_new("data"); - - purple_xmlnode_set_namespace(tag, NS_BOB); - purple_xmlnode_set_attrib(tag, "cid", cid); - - return tag; -} - -static gboolean -jabber_data_has_valid_hash(const JabberData *data) -{ - const gchar *cid = jabber_data_get_cid(data); - gchar **cid_parts = g_strsplit(cid, "@", -1); - guint num_cid_parts = 0; - gboolean ret = FALSE; - - if (cid_parts) - num_cid_parts = g_strv_length(cid_parts); - - if (num_cid_parts == 2 && purple_strequal(cid_parts[1], "bob.xmpp.org")) { - gchar **sub_parts = g_strsplit(cid_parts[0], "+", -1); - guint num_sub_parts = 0; - - if (sub_parts) - num_sub_parts = g_strv_length(sub_parts); - - if (num_sub_parts == 2) { - const gchar *hash_algo = sub_parts[0]; - const gchar *hash_value = sub_parts[1]; - GChecksumType hash_type; - gboolean valid_hash_type = TRUE; - - if (purple_strequal(hash_algo, "sha1")) - hash_type = G_CHECKSUM_SHA1; - else if (purple_strequal(hash_algo, "sha256")) - hash_type = G_CHECKSUM_SHA256; - else if (purple_strequal(hash_algo, "sha512")) - hash_type = G_CHECKSUM_SHA512; - else if (purple_strequal(hash_algo, "md5")) - hash_type = G_CHECKSUM_MD5; - else - valid_hash_type = FALSE; - - if (valid_hash_type) { - gchar *digest = g_compute_checksum_for_data( - hash_type, jabber_data_get_data(data), - jabber_data_get_size(data)); - - ret = purple_strequal(digest, hash_value); - - if (!ret) - purple_debug_warning("jabber", "Unable to validate BoB " - "hash; expecting %s, got %s\n", - cid, digest); - - g_free(digest); - } else { - purple_debug_warning("jabber", "Unable to validate BoB hash; " - "unknown hash algorithm %s\n", hash_algo); - } - } else { - purple_debug_warning("jabber", "Malformed BoB CID\n"); - } - - g_strfreev(sub_parts); - } - - g_strfreev(cid_parts); - return ret; -} - - -typedef struct { - gpointer userdata; - gchar *alt; - gboolean ephemeral; - JabberDataRequestCallback *cb; -} JabberDataRequestData; - -static void -jabber_data_request_cb(JabberStream *js, const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - gpointer data) -{ - JabberDataRequestData *request_data = (JabberDataRequestData *) data; - gpointer userdata = request_data->userdata; - gchar *alt = request_data->alt; - gboolean ephemeral = request_data->ephemeral; - JabberDataRequestCallback *cb = request_data->cb; - - PurpleXmlNode *data_element = purple_xmlnode_get_child(packet, "data"); - PurpleXmlNode *item_not_found = purple_xmlnode_get_child(packet, "item-not-found"); - - /* did we get a data element as result? */ - if (data_element && type == JABBER_IQ_RESULT) { - JabberData *data = jabber_data_create_from_xml(data_element); - - if (data && !ephemeral) { - jabber_data_associate_remote(js, from, data); - } - cb(data, alt, userdata); - } else if (item_not_found) { - purple_debug_info("jabber", - "Responder didn't recognize requested data\n"); - cb(NULL, alt, userdata); - } else { - purple_debug_warning("jabber", "Unknown response to data request\n"); - cb(NULL, alt, userdata); - } - - g_free(request_data); -} - -void -jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who, - gchar *alt, gboolean ephemeral, JabberDataRequestCallback cb, - gpointer userdata) -{ - JabberIq *request; - PurpleXmlNode *data_request; - JabberDataRequestData *data; - - g_return_if_fail(cid != NULL); - g_return_if_fail(who != NULL); - g_return_if_fail(alt != NULL); - - request = jabber_iq_new(js, JABBER_IQ_GET); - data_request = jabber_data_get_xml_request(cid); - data = g_new0(JabberDataRequestData, 1); - - data->userdata = userdata; - data->alt = alt; - data->ephemeral = ephemeral; - data->cb = cb; - - purple_xmlnode_set_attrib(request->node, "to", who); - jabber_iq_set_callback(request, jabber_data_request_cb, data); - purple_xmlnode_insert_child(request->node, data_request); - jabber_iq_send(request); -} - -const JabberData * -jabber_data_find_local_by_alt(const gchar *alt) -{ - purple_debug_info("jabber", "looking up local data object with alt = %s\n", alt); - return g_hash_table_lookup(local_data_by_alt, alt); -} - -const JabberData * -jabber_data_find_local_by_cid(const gchar *cid) -{ - purple_debug_info("jabber", "lookup local data object with cid = %s\n", cid); - return g_hash_table_lookup(local_data_by_cid, cid); -} - -const JabberData * -jabber_data_find_remote_by_cid(JabberStream *js, const gchar *who, - const gchar *cid) -{ - const JabberData *data = g_hash_table_lookup(remote_data_by_cid, cid); - purple_debug_info("jabber", "lookup remote data object with cid = %s\n", cid); - - if (data == NULL) { - gchar *jid_cid = - g_strdup_printf("%s@%s/%s%s%s", js->user->node, js->user->domain, - js->user->resource, who, cid); - purple_debug_info("jabber", - "didn't find BoB object by pure CID, try including JIDs: %s\n", - jid_cid); - data = g_hash_table_lookup(remote_data_by_cid, jid_cid); - g_free(jid_cid); - } - return data; -} - -void -jabber_data_associate_local(JabberData *data, const gchar *alt) -{ - g_return_if_fail(data != NULL); - - purple_debug_info("jabber", "associating local data object\n alt = %s, cid = %s\n", - alt , jabber_data_get_cid(data)); - if (alt) - g_hash_table_insert(local_data_by_alt, g_strdup(alt), data); - g_hash_table_insert(local_data_by_cid, g_strdup(jabber_data_get_cid(data)), - data); -} - -void -jabber_data_associate_remote(JabberStream *js, const gchar *who, JabberData *data) -{ - gchar *cid; - - g_return_if_fail(data != NULL); - - if (jabber_data_has_valid_hash(data)) { - cid = g_strdup(jabber_data_get_cid(data)); - } else { - cid = g_strdup_printf("%s@%s/%s%s%s", js->user->node, js->user->domain, - js->user->resource, who, jabber_data_get_cid(data)); - } - - purple_debug_info("jabber", "associating remote BoB object with cid = %s\n", - cid); - - g_hash_table_insert(remote_data_by_cid, cid, data); -} - -/* Handles iq requests. */ -static void -jabber_data_parse(JabberStream *js, const char *who, - G_GNUC_UNUSED JabberIqType type, const char *id, - PurpleXmlNode *data_node) -{ - JabberIq *result = NULL; - const char *cid = purple_xmlnode_get_attrib(data_node, "cid"); - const JabberData *data = cid ? jabber_data_find_local_by_cid(cid) : NULL; - - if (!data) { - PurpleXmlNode *item_not_found = purple_xmlnode_new("item-not-found"); - - result = jabber_iq_new(js, JABBER_IQ_ERROR); - if (who) - purple_xmlnode_set_attrib(result->node, "to", who); - purple_xmlnode_set_attrib(result->node, "id", id); - purple_xmlnode_insert_child(result->node, item_not_found); - } else { - result = jabber_iq_new(js, JABBER_IQ_RESULT); - if (who) - purple_xmlnode_set_attrib(result->node, "to", who); - purple_xmlnode_set_attrib(result->node, "id", id); - purple_xmlnode_insert_child(result->node, - jabber_data_get_xml_definition(data)); - /* if the data object is temporary, destroy it and remove the references - to it */ - if (data->ephemeral) { - g_hash_table_remove(local_data_by_cid, cid); - } - } - jabber_iq_send(result); -} - -void -jabber_data_init(void) -{ - if (purple_debug_is_verbose()) - purple_debug_misc("jabber", "creating hash tables for data objects"); - local_data_by_alt = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); - local_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, jabber_data_delete); - remote_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, jabber_data_delete); - - jabber_iq_register_handler("data", NS_BOB, jabber_data_parse); -} - -void -jabber_data_uninit(void) -{ - if (purple_debug_is_verbose()) - purple_debug_info("jabber", "destroying hash tables for data objects"); - g_clear_pointer(&local_data_by_alt, g_hash_table_destroy); - g_clear_pointer(&local_data_by_cid, g_hash_table_destroy); - g_clear_pointer(&remote_data_by_cid, g_hash_table_destroy); -}
--- a/libpurple/protocols/jabber/data.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * purple - Handling of XEP-0231: Bits of Binary. - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_DATA_H -#define PURPLE_JABBER_DATA_H - -#include <purple.h> - -#include "jabber.h" - -#include <glib.h> - -#define JABBER_DATA_MAX_SIZE 8192 - - -typedef struct { - char *cid; - char *type; - gsize size; - gpointer data; - gboolean ephemeral; -} JabberData; - -typedef void (JabberDataRequestCallback)(JabberData *data, gchar *alt, - gpointer userdata); - - -/* creates a JabberData instance from raw data */ -JabberData *jabber_data_create_from_data(gconstpointer data, gsize size, - const char *type, gboolean ephemeral, JabberStream *js); - -/* create a JabberData instance from an XML "data" element (as defined by - XEP 0231 */ -JabberData *jabber_data_create_from_xml(PurpleXmlNode *tag); - -/* destroy a JabberData instance, NOT to be used on data that has been - associated, since they get "owned" */ -void jabber_data_destroy(JabberData *data); - -const char *jabber_data_get_cid(const JabberData *data); -const char *jabber_data_get_type(const JabberData *data); - -gsize jabber_data_get_size(const JabberData *data); -gpointer jabber_data_get_data(const JabberData *data); - -/* returns the XML definition for the data element */ -PurpleXmlNode *jabber_data_get_xml_definition(const JabberData *data); - -/* returns an XHTML-IM "img" tag given a data instance */ -PurpleXmlNode *jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt); - -void jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who, - gchar *alt, gboolean ephemeral, JabberDataRequestCallback cb, - gpointer userdata); - -/* lookup functions */ -const JabberData *jabber_data_find_local_by_alt(const gchar *alt); -const JabberData *jabber_data_find_local_by_cid(const gchar *cid); -const JabberData *jabber_data_find_remote_by_cid(JabberStream *js, - const gchar *who, const gchar *cid); - -/* store data objects */ -void jabber_data_associate_local(JabberData *data, const gchar *alt); -void jabber_data_associate_remote(JabberStream *js, const gchar *who, - JabberData *data); - -void jabber_data_init(void); -void jabber_data_uninit(void); - -#endif /* PURPLE_JABBER_DATA_H */
--- a/libpurple/protocols/jabber/disco.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,533 +0,0 @@ -/* - * purple - Jabber Service Discovery - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purple.h> - -#include "adhoccommands.h" -#include "buddy.h" -#include "disco.h" -#include "iq.h" -#include "jabber.h" -#include "jingle/jingle.h" -#include "pep.h" -#include "presence.h" -#include "roster.h" -#include "useravatar.h" - -struct _jabber_disco_info_cb_data { - gpointer data; - JabberDiscoInfoCallback *callback; -}; - -struct _jabber_disco_items_cb_data { - gpointer data; - JabberDiscoItemsCallback *callback; -}; - -#define SUPPORT_FEATURE(x) { \ - feature = purple_xmlnode_new_child(query, "feature"); \ - purple_xmlnode_set_attrib(feature, "var", x); \ -} - -static void -jabber_disco_bytestream_server_cb(JabberStream *js, const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - JabberBytestreamsStreamhost *sh = data; - PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query", - NS_BYTESTREAMS); - - if (from && purple_strequal(from, sh->jid) && query != NULL) { - PurpleXmlNode *sh_node = purple_xmlnode_get_child(query, "streamhost"); - if (sh_node) { - const char *jid = purple_xmlnode_get_attrib(sh_node, "jid"); - const char *port = purple_xmlnode_get_attrib(sh_node, "port"); - - - if (jid == NULL || !purple_strequal(jid, from)) - purple_debug_error("jabber", "Invalid jid(%s) for bytestream.\n", - jid ? jid : "(null)"); - - sh->host = g_strdup(purple_xmlnode_get_attrib(sh_node, "host")); - sh->zeroconf = g_strdup(purple_xmlnode_get_attrib(sh_node, "zeroconf")); - if (port != NULL) - sh->port = atoi(port); - } - } - - purple_debug_info("jabber", "Discovered bytestream proxy server: " - "jid='%s' host='%s' port='%d' zeroconf='%s'\n", - from ? from : "", sh->host ? sh->host : "", - sh->port, sh->zeroconf ? sh->zeroconf : ""); - - /* TODO: When we support zeroconf proxies, fix this to handle them */ - if (!(sh->jid && sh->host && sh->port > 0)) { - js->bs_proxies = g_list_remove(js->bs_proxies, sh); - jabber_bytestreams_streamhost_free(sh); - } -} - - -void jabber_disco_info_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *in_query) -{ - if(type == JABBER_IQ_GET) { - PurpleXmlNode *query, *identity, *feature; - JabberIq *iq; - const char *node = purple_xmlnode_get_attrib(in_query, "node"); - char *node_uri = NULL; - - /* create custom caps node URI */ - node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_own_hash(js), NULL); - - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, NS_DISCO_INFO); - - jabber_iq_set_id(iq, id); - - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - query = purple_xmlnode_get_child(iq->node, "query"); - - if(node) - purple_xmlnode_set_attrib(query, "node", node); - - if(!node || purple_strequal(node, node_uri)) { - GList *features, *identities; - for(identities = jabber_identities; identities; identities = identities->next) { - JabberIdentity *ident = (JabberIdentity*)identities->data; - identity = purple_xmlnode_new_child(query, "identity"); - purple_xmlnode_set_attrib(identity, "category", ident->category); - purple_xmlnode_set_attrib(identity, "type", ident->type); - if (ident->lang) - purple_xmlnode_set_attrib(identity, "xml:lang", ident->lang); - if (ident->name) - purple_xmlnode_set_attrib(identity, "name", ident->name); - } - for(features = jabber_features; features; features = features->next) { - JabberFeature *feat = (JabberFeature*)features->data; - if (!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { - feature = purple_xmlnode_new_child(query, "feature"); - purple_xmlnode_set_attrib(feature, "var", feat->namespace); - } - } - } else { - PurpleXmlNode *error, *inf; - - /* XXX: gross hack, implement jabber_iq_set_type or something */ - purple_xmlnode_set_attrib(iq->node, "type", "error"); - iq->type = JABBER_IQ_ERROR; - - error = purple_xmlnode_new_child(query, "error"); - purple_xmlnode_set_attrib(error, "code", "404"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - inf = purple_xmlnode_new_child(error, "item-not-found"); - purple_xmlnode_set_namespace(inf, NS_XMPP_STANZAS); - } - g_free(node_uri); - jabber_iq_send(iq); - } else if (type == JABBER_IQ_SET) { - /* wtf? seriously. wtf‽ */ - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); - PurpleXmlNode *error, *bad_request; - - /* Free the <query/> */ - purple_xmlnode_free(purple_xmlnode_get_child(iq->node, "query")); - /* Add an error */ - error = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(error, "type", "modify"); - bad_request = purple_xmlnode_new_child(error, "bad-request"); - purple_xmlnode_set_namespace(bad_request, NS_XMPP_STANZAS); - - jabber_iq_set_id(iq, id); - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - - jabber_iq_send(iq); - } -} - -static void -jabber_disco_info_cb(JabberStream *js, const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - gpointer data) -{ - struct _jabber_disco_info_cb_data *jdicd = data; - PurpleXmlNode *query; - - query = purple_xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_INFO); - - if (type == JABBER_IQ_RESULT && query) { - PurpleXmlNode *child; - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - JabberCapabilities capabilities = JABBER_CAP_NONE; - - if((jid = jabber_id_new(from))) { - if(jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) - jbr = jabber_buddy_find_resource(jb, jid->resource); - jabber_id_free(jid); - } - - if(jbr) - capabilities = jbr->capabilities; - - for(child = query->child; child; child = child->next) { - if(child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - if(purple_strequal(child->name, "identity")) { - const char *category = purple_xmlnode_get_attrib(child, "category"); - const char *type = purple_xmlnode_get_attrib(child, "type"); - if(!category || !type) - continue; - - if(purple_strequal(category, "conference") && purple_strequal(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_prepend(js->chat_servers, g_strdup(from)); - } else if(purple_strequal(category, "proxy") && purple_strequal(type, "bytestreams")) { - /* This is a bytestream proxy */ - JabberIq *iq; - JabberBytestreamsStreamhost *sh; - - purple_debug_info("jabber", "Found bytestream proxy server: %s\n", from); - - sh = g_new0(JabberBytestreamsStreamhost, 1); - sh->jid = g_strdup(from); - js->bs_proxies = g_list_prepend(js->bs_proxies, sh); - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, - NS_BYTESTREAMS); - purple_xmlnode_set_attrib(iq->node, "to", sh->jid); - jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh); - jabber_iq_send(iq); - } - - } else if(purple_strequal(child->name, "feature")) { - const char *var = purple_xmlnode_get_attrib(child, "var"); - if(!var) - continue; - - if(purple_strequal(var, "http://jabber.org/protocol/si")) - capabilities |= JABBER_CAP_SI; - else if(purple_strequal(var, "http://jabber.org/protocol/si/profile/file-transfer")) - capabilities |= JABBER_CAP_SI_FILE_XFER; - else if(purple_strequal(var, NS_BYTESTREAMS)) - capabilities |= JABBER_CAP_BYTESTREAMS; - else if(purple_strequal(var, "jabber:iq:search")) - capabilities |= JABBER_CAP_IQ_SEARCH; - else if(purple_strequal(var, "jabber:iq:register")) - capabilities |= JABBER_CAP_IQ_REGISTER; - else if(purple_strequal(var, NS_PING)) - capabilities |= JABBER_CAP_PING; - else if(purple_strequal(var, NS_DISCO_ITEMS)) - capabilities |= JABBER_CAP_ITEMS; - else if(purple_strequal(var, "http://jabber.org/protocol/commands")) { - capabilities |= JABBER_CAP_ADHOC; - } - else if(purple_strequal(var, NS_IBB)) { - purple_debug_info("jabber", "remote supports IBB\n"); - capabilities |= JABBER_CAP_IBB; - } - } - } - - js->chat_servers = g_list_reverse(js->chat_servers); - - capabilities |= JABBER_CAP_RETRIEVED; - - if(jbr) - jbr->capabilities = capabilities; - - if (jdicd && jdicd->callback) - jdicd->callback(js, from, capabilities, jdicd->data); - } else { /* type == JABBER_IQ_ERROR or query == NULL */ - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - JabberCapabilities capabilities = JABBER_CAP_NONE; - - if((jid = jabber_id_new(from))) { - if(jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) - jbr = jabber_buddy_find_resource(jb, jid->resource); - jabber_id_free(jid); - } - - if(jbr) - capabilities = jbr->capabilities; - - if (jdicd && jdicd->callback) - jdicd->callback(js, from, capabilities, jdicd->data); - } - - g_free(jdicd); -} - -void jabber_disco_items_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *query) -{ - if(type == JABBER_IQ_GET) { - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, - NS_DISCO_ITEMS); - - /* preserve node */ - PurpleXmlNode *iq_query = purple_xmlnode_get_child(iq->node, "query"); - const char *node = purple_xmlnode_get_attrib(query, "node"); - if(node) - purple_xmlnode_set_attrib(iq_query,"node",node); - - jabber_iq_set_id(iq, id); - - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - jabber_iq_send(iq); - } -} - -static void -jabber_disco_finish_server_info_result_cb(JabberStream *js) -{ - const char *ft_proxies; - - /* - * This *should* happen only if the server supports vcard-temp, but there - * are apparently some servers that don't advertise it even though they - * support it. - */ - jabber_vcard_fetch_mine(js); - - if (js->pep) - jabber_avatar_fetch_mine(js); - - /* Yes, please! */ - jabber_roster_request(js); - - if (js->server_caps & JABBER_CAP_ADHOC) { - /* The server supports ad-hoc commands, so let's request the list */ - jabber_adhoc_server_get_list(js); - } - - /* If the server supports blocking, request the block list */ - if (js->server_caps & JABBER_CAP_BLOCKING) { - jabber_request_block_list(js); - } - - /* If the server supports carbons, enable them! */ - if(js->server_caps & JABBER_CAP_MESSAGE_CARBONS) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); - PurpleXmlNode *enable = purple_xmlnode_new_child(iq->node, "enable"); - - purple_xmlnode_set_namespace(enable, NS_MESSAGE_CARBONS); - - jabber_iq_send(iq); - } - - /* If there are manually specified bytestream proxies, query them */ - ft_proxies = purple_account_get_string(purple_connection_get_account(js->gc), "ft_proxies", NULL); - if (ft_proxies) { - JabberIq *iq; - JabberBytestreamsStreamhost *sh; - int i; - char *tmp; - gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0); - - for(i = 0; ft_proxy_list[i]; i++) { - g_strstrip(ft_proxy_list[i]); - if(!(*ft_proxy_list[i])) - continue; - - /* We used to allow specifying a port directly here; get rid of it */ - if((tmp = strchr(ft_proxy_list[i], ':'))) - *tmp = '\0'; - - sh = g_new0(JabberBytestreamsStreamhost, 1); - sh->jid = g_strdup(ft_proxy_list[i]); - js->bs_proxies = g_list_prepend(js->bs_proxies, sh); - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_BYTESTREAMS); - purple_xmlnode_set_attrib(iq->node, "to", sh->jid); - jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh); - jabber_iq_send(iq); - } - - g_strfreev(ft_proxy_list); - } - -} - -static void -jabber_disco_server_info_result_cb(JabberStream *js, const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query, *child; - - if (!from || !purple_strequal(from, js->user->domain)) { - jabber_disco_finish_server_info_result_cb(js); - return; - } - - if (type == JABBER_IQ_ERROR) { - /* A common way to get here is for the server not to support xmlns http://jabber.org/protocol/disco#info */ - jabber_disco_finish_server_info_result_cb(js); - return; - } - - query = purple_xmlnode_get_child(packet, "query"); - - if (!query) { - jabber_disco_finish_server_info_result_cb(js); - return; - } - - for (child = purple_xmlnode_get_child(query, "identity"); child; - child = purple_xmlnode_get_next_twin(child)) { - const char *category, *type, *name; - category = purple_xmlnode_get_attrib(child, "category"); - type = purple_xmlnode_get_attrib(child, "type"); - if(purple_strequal(category, "pubsub") && purple_strequal(type, "pep")) { - js->pep = TRUE; - } - if (!purple_strequal(category, "server")) - continue; - if (!purple_strequal(type, "im")) - continue; - - name = purple_xmlnode_get_attrib(child, "name"); - if (!name) - continue; - - g_free(js->server_name); - js->server_name = g_strdup(name); - } - - for (child = purple_xmlnode_get_child(query, "feature"); child; - child = purple_xmlnode_get_next_twin(child)) { - const char *var; - var = purple_xmlnode_get_attrib(child, "var"); - if (!var) - continue; - - if (purple_strequal("http://jabber.org/protocol/commands", var)) { - js->server_caps |= JABBER_CAP_ADHOC; - } else if (purple_strequal(NS_SIMPLE_BLOCKING, var)) { - js->server_caps |= JABBER_CAP_BLOCKING; - } else if (purple_strequal(NS_MESSAGE_CARBONS, var)) { - js->server_caps |= JABBER_CAP_MESSAGE_CARBONS; - } - } - - jabber_disco_finish_server_info_result_cb(js); -} - -static void -jabber_disco_server_items_result_cb(JabberStream *js, const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query, *child; - - if (!from || !purple_strequal(from, js->user->domain)) - return; - - if (type == JABBER_IQ_ERROR) - return; - - g_clear_list(&js->chat_servers, g_free); - - query = purple_xmlnode_get_child(packet, "query"); - - for(child = purple_xmlnode_get_child(query, "item"); child; - child = purple_xmlnode_get_next_twin(child)) { - JabberIq *iq; - const char *jid; - - if(!(jid = purple_xmlnode_get_attrib(child, "jid"))) - continue; - - /* we don't actually care about the specific nodes, - * so we won't query them */ - if(purple_xmlnode_get_attrib(child, "node") != NULL) - continue; - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO); - purple_xmlnode_set_attrib(iq->node, "to", jid); - jabber_iq_set_callback(iq, jabber_disco_info_cb, NULL); - jabber_iq_send(iq); - } -} - -void jabber_disco_items_server(JabberStream *js) -{ - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_ITEMS); - - purple_xmlnode_set_attrib(iq->node, "to", js->user->domain); - - jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL); - jabber_iq_send(iq); - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO); - purple_xmlnode_set_attrib(iq->node, "to", js->user->domain); - jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL); - jabber_iq_send(iq); -} - -void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCallback *callback, gpointer data) -{ - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - struct _jabber_disco_info_cb_data *jdicd; - JabberIq *iq; - - if((jid = jabber_id_new(who))) { - if(jid->resource && (jb = jabber_buddy_find(js, who, TRUE))) - jbr = jabber_buddy_find_resource(jb, jid->resource); - jabber_id_free(jid); - } - - if(jbr && jbr->capabilities & JABBER_CAP_RETRIEVED) { - callback(js, who, jbr->capabilities, data); - return; - } - - jdicd = g_new0(struct _jabber_disco_info_cb_data, 1); - jdicd->data = data; - jdicd->callback = callback; - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO); - purple_xmlnode_set_attrib(iq->node, "to", who); - - jabber_iq_set_callback(iq, jabber_disco_info_cb, jdicd); - jabber_iq_send(iq); -} -
--- a/libpurple/protocols/jabber/disco.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * @file disco.h Jabber Service Discovery - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_DISCO_H -#define PURPLE_JABBER_DISCO_H - -#include "jabber.h" - -typedef struct { - const char *jid; /* MUST */ - const char *node; /* SHOULD */ - const char *name; /* MAY */ -} JabberDiscoItem; - -typedef void (JabberDiscoInfoCallback)(JabberStream *js, const char *who, - JabberCapabilities capabilities, gpointer data); - -typedef void (JabberDiscoItemsCallback)(JabberStream *js, const char *jid, - const char *node, GSList *items, gpointer data); - -void jabber_disco_info_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *in_query); -void jabber_disco_items_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *query); - -void jabber_disco_items_server(JabberStream *js); - -void jabber_disco_info_do(JabberStream *js, const char *who, - JabberDiscoInfoCallback *callback, gpointer data); - -#endif /* PURPLE_JABBER_DISCO_H */
--- a/libpurple/protocols/jabber/ibb.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,514 +0,0 @@ -/* - * purple - Handling of XEP-0047: In-Band Bytestreams. - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <math.h> - -#include <purple.h> - -#include "ibb.h" - -#define JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE 4096 - -static GHashTable *jabber_ibb_sessions = NULL; -static GList *open_handlers = NULL; - -static JabberStream *jabber_ibb_session_get_js(JabberIBBSession *sess); - -JabberIBBSession * -jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who, - gpointer user_data) -{ - JabberIBBSession *sess = g_new0(JabberIBBSession, 1); - sess->js = js; - if (sid) { - sess->sid = g_strdup(sid); - } else { - sess->sid = jabber_get_next_id(js); - } - sess->who = g_strdup(who); - sess->block_size = JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE; - sess->state = JABBER_IBB_SESSION_NOT_OPENED; - sess->user_data = user_data; - - g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess); - - return sess; -} - -JabberIBBSession * -jabber_ibb_session_create_from_xmlnode(JabberStream *js, const char *from, - const char *id, PurpleXmlNode *open, gpointer user_data) -{ - JabberIBBSession *sess = NULL; - const gchar *sid = purple_xmlnode_get_attrib(open, "sid"); - const gchar *block_size = purple_xmlnode_get_attrib(open, "block-size"); - - if (!open) { - return NULL; - } - - if (!sid || !block_size) { - purple_debug_error("jabber", - "IBB session open tag requires sid and block-size attributes\n"); - return NULL; - } - - sess = jabber_ibb_session_create(js, sid, from, user_data); - sess->id = g_strdup(id); - sess->block_size = atoi(block_size); - /* if we create a session from an incoming <open/> request, it means the - session is immediately open... */ - sess->state = JABBER_IBB_SESSION_OPENED; - - return sess; -} - -void -jabber_ibb_session_destroy(JabberIBBSession *sess) -{ - purple_debug_info("jabber", "IBB: destroying session %p %s\n", sess, - sess->sid); - - if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) { - jabber_ibb_session_close(sess); - } - - if (sess->last_iq_id) { - purple_debug_info("jabber", "IBB: removing callback for <iq/> %s\n", - sess->last_iq_id); - jabber_iq_remove_callback_by_id(jabber_ibb_session_get_js(sess), - sess->last_iq_id); - g_free(sess->last_iq_id); - sess->last_iq_id = NULL; - } - - g_hash_table_remove(jabber_ibb_sessions, sess->sid); - g_free(sess->id); - g_free(sess->sid); - g_free(sess->who); - g_free(sess); -} - -static const gchar * -jabber_ibb_session_get_sid(const JabberIBBSession *sess) -{ - return sess->sid; -} - -static JabberStream * -jabber_ibb_session_get_js(JabberIBBSession *sess) -{ - return sess->js; -} - -static const gchar * -jabber_ibb_session_get_who(const JabberIBBSession *sess) -{ - return sess->who; -} - -static guint16 -jabber_ibb_session_get_send_seq(const JabberIBBSession *sess) -{ - return sess->send_seq; -} - -static guint16 -jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess) -{ - return sess->recv_seq; -} - -JabberIBBSessionState -jabber_ibb_session_get_state(const JabberIBBSession *sess) -{ - return sess->state; -} - -gsize -jabber_ibb_session_get_block_size(const JabberIBBSession *sess) -{ - return sess->block_size; -} - -gsize -jabber_ibb_session_get_max_data_size(const JabberIBBSession *sess) -{ - return (gsize) floor((sess->block_size - 2) * (float) 3 / 4); -} - -gpointer -jabber_ibb_session_get_user_data(JabberIBBSession *sess) -{ - return sess->user_data; -} - -void -jabber_ibb_session_set_opened_callback(JabberIBBSession *sess, - JabberIBBOpenedCallback *cb) -{ - sess->opened_cb = cb; -} - -void -jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess, - JabberIBBSentCallback *cb) -{ - sess->data_sent_cb = cb; -} - -void -jabber_ibb_session_set_closed_callback(JabberIBBSession *sess, - JabberIBBClosedCallback *cb) -{ - sess->closed_cb = cb; -} - -void -jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess, - JabberIBBDataCallback *cb) -{ - sess->data_received_cb = cb; -} - -void -jabber_ibb_session_set_error_callback(JabberIBBSession *sess, - JabberIBBErrorCallback *cb) -{ - sess->error_cb = cb; -} - -static void -jabber_ibb_session_opened_cb(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - gpointer data) -{ - JabberIBBSession *sess = (JabberIBBSession *) data; - - if (type == JABBER_IQ_ERROR) { - sess->state = JABBER_IBB_SESSION_ERROR; - } else { - sess->state = JABBER_IBB_SESSION_OPENED; - } - - if (sess->opened_cb) { - sess->opened_cb(sess); - } -} - -void -jabber_ibb_session_open(JabberIBBSession *sess) -{ - if (jabber_ibb_session_get_state(sess) != JABBER_IBB_SESSION_NOT_OPENED) { - purple_debug_error("jabber", - "jabber_ibb_session called on an already open stream\n"); - } else { - JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET); - PurpleXmlNode *open = purple_xmlnode_new("open"); - gchar block_size[10]; - - purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); - purple_xmlnode_set_namespace(open, NS_IBB); - purple_xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess)); - g_snprintf(block_size, sizeof(block_size), "%" G_GSIZE_FORMAT, - jabber_ibb_session_get_block_size(sess)); - purple_xmlnode_set_attrib(open, "block-size", block_size); - purple_xmlnode_insert_child(set->node, open); - - jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess); - - jabber_iq_send(set); - } -} - -void -jabber_ibb_session_close(JabberIBBSession *sess) -{ - JabberIBBSessionState state = jabber_ibb_session_get_state(sess); - - if (state != JABBER_IBB_SESSION_OPENED && state != JABBER_IBB_SESSION_ERROR) { - purple_debug_error("jabber", - "jabber_ibb_session_close called on a session that has not been" - "opened\n"); - } else { - JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), - JABBER_IQ_SET); - PurpleXmlNode *close = purple_xmlnode_new("close"); - - purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); - purple_xmlnode_set_namespace(close, NS_IBB); - purple_xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess)); - purple_xmlnode_insert_child(set->node, close); - jabber_iq_send(set); - sess->state = JABBER_IBB_SESSION_CLOSED; - } -} - -static void -jabber_ibb_session_send_acknowledge_cb(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - gpointer data) -{ - JabberIBBSession *sess = (JabberIBBSession *) data; - - if (sess) { - /* reset callback */ - g_free(sess->last_iq_id); - sess->last_iq_id = NULL; - - if (type == JABBER_IQ_ERROR) { - jabber_ibb_session_close(sess); - sess->state = JABBER_IBB_SESSION_ERROR; - - if (sess->error_cb) { - sess->error_cb(sess); - } - } else { - if (sess->data_sent_cb) { - sess->data_sent_cb(sess); - } - } - } else { - /* the session has gone away, it was probably cancelled */ - purple_debug_info("jabber", - "got response from send data, but IBB session is no longer active\n"); - } -} - -void -jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data, - gsize size) -{ - JabberIBBSessionState state = jabber_ibb_session_get_state(sess); - - purple_debug_info("jabber", "sending data block of %" G_GSIZE_FORMAT " bytes on IBB stream\n", - size); - - if (state != JABBER_IBB_SESSION_OPENED) { - purple_debug_error("jabber", - "trying to send data on a non-open IBB session\n"); - } else if (size > jabber_ibb_session_get_max_data_size(sess)) { - purple_debug_error("jabber", - "trying to send a too large packet in the IBB session\n"); - } else { - JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), - JABBER_IQ_SET); - PurpleXmlNode *data_element = purple_xmlnode_new("data"); - char *base64 = g_base64_encode(data, size); - char seq[10]; - g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess)); - - purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); - purple_xmlnode_set_namespace(data_element, NS_IBB); - purple_xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess)); - purple_xmlnode_set_attrib(data_element, "seq", seq); - purple_xmlnode_insert_data(data_element, base64, -1); - - purple_xmlnode_insert_child(set->node, data_element); - - purple_debug_info("jabber", - "IBB: setting send <iq/> callback for session %p %s\n", sess, - sess->sid); - jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess); - sess->last_iq_id = g_strdup(purple_xmlnode_get_attrib(set->node, "id")); - purple_debug_info("jabber", "IBB: set sess->last_iq_id: %s\n", - sess->last_iq_id); - jabber_iq_send(set); - - g_free(base64); - (sess->send_seq)++; - } -} - -static void -jabber_ibb_send_error_response(JabberStream *js, const char *to, const char *id) -{ - JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR); - PurpleXmlNode *error = purple_xmlnode_new("error"); - PurpleXmlNode *item_not_found = purple_xmlnode_new("item-not-found"); - - purple_xmlnode_set_namespace(item_not_found, NS_XMPP_STANZAS); - purple_xmlnode_set_attrib(error, "code", "440"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - jabber_iq_set_id(result, id); - purple_xmlnode_set_attrib(result->node, "to", to); - purple_xmlnode_insert_child(error, item_not_found); - purple_xmlnode_insert_child(result->node, error); - - jabber_iq_send(result); -} - -/* Handle incoming packet. */ -static void -jabber_ibb_parse(JabberStream *js, const char *who, - G_GNUC_UNUSED JabberIqType type, const char *id, - PurpleXmlNode *child) -{ - const char *name = child->name; - gboolean data = purple_strequal(name, "data"); - gboolean close = purple_strequal(name, "close"); - gboolean open = purple_strequal(name, "open"); - const gchar *sid = (data || close) ? - purple_xmlnode_get_attrib(child, "sid") : NULL; - JabberIBBSession *sess = - sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL; - - if (sess) { - - if (!purple_strequal(who, jabber_ibb_session_get_who(sess))) { - /* the iq comes from a different JID than the remote JID of the - session, ignore it */ - purple_debug_error("jabber", - "Got IBB iq from wrong JID, ignoring\n"); - } else if (data) { - const gchar *seq_attr = purple_xmlnode_get_attrib(child, "seq"); - guint16 seq = (seq_attr ? atoi(seq_attr) : 0); - - /* reject the data, and set the session in error if we get an - out-of-order packet */ - if (seq_attr && seq == jabber_ibb_session_get_recv_seq(sess)) { - /* sequence # is the expected... */ - JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); - - jabber_iq_set_id(result, id); - purple_xmlnode_set_attrib(result->node, "to", who); - - if (sess->data_received_cb) { - gchar *base64 = purple_xmlnode_get_data(child); - gsize size; - gpointer rawdata = g_base64_decode(base64, &size); - - g_free(base64); - - if (rawdata) { - purple_debug_info("jabber", - "got %" G_GSIZE_FORMAT " bytes of data on IBB stream\n", - size); - /* we accept other clients to send up to block-size - of _unencoded_ data, since there's been some confusions - regarding the interpretation of this attribute - (including previous versions of libpurple) */ - if (size > jabber_ibb_session_get_block_size(sess)) { - purple_debug_error("jabber", - "IBB: received a too large packet\n"); - if (sess->error_cb) - sess->error_cb(sess); - g_free(rawdata); - return; - } else { - purple_debug_info("jabber", - "calling IBB callback for received data\n"); - sess->data_received_cb(sess, rawdata, size); - } - g_free(rawdata); - } else { - purple_debug_error("jabber", - "IBB: invalid BASE64 data received\n"); - if (sess->error_cb) - sess->error_cb(sess); - return; - - } - } - - (sess->recv_seq)++; - jabber_iq_send(result); - - } else { - purple_debug_error("jabber", - "Received an out-of-order/invalid IBB packet\n"); - sess->state = JABBER_IBB_SESSION_ERROR; - - if (sess->error_cb) { - sess->error_cb(sess); - } - } - } else if (close) { - sess->state = JABBER_IBB_SESSION_CLOSED; - purple_debug_info("jabber", "IBB: received close\n"); - - if (sess->closed_cb) { - purple_debug_info("jabber", "IBB: calling closed handler\n"); - sess->closed_cb(sess); - } - } - } else if (open) { - JabberIq *result; - const GList *iterator; - - /* run all open handlers registered until one returns true */ - for (iterator = open_handlers ; iterator ; - iterator = g_list_next(iterator)) { - JabberIBBOpenHandler *handler = iterator->data; - - if (handler(js, who, id, child)) { - result = jabber_iq_new(js, JABBER_IQ_RESULT); - purple_xmlnode_set_attrib(result->node, "to", who); - jabber_iq_set_id(result, id); - jabber_iq_send(result); - return; - } - } - /* no open callback returned success, reject */ - jabber_ibb_send_error_response(js, who, id); - } else { - /* send error reply */ - jabber_ibb_send_error_response(js, who, id); - } -} - -void -jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb) -{ - open_handlers = g_list_append(open_handlers, cb); -} - -void -jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb) -{ - open_handlers = g_list_remove(open_handlers, cb); -} - -void -jabber_ibb_init(void) -{ - jabber_ibb_sessions = g_hash_table_new(g_str_hash, g_str_equal); - - jabber_add_feature(NS_IBB, NULL); - - jabber_iq_register_handler("close", NS_IBB, jabber_ibb_parse); - jabber_iq_register_handler("data", NS_IBB, jabber_ibb_parse); - jabber_iq_register_handler("open", NS_IBB, jabber_ibb_parse); -} - -void -jabber_ibb_uninit(void) -{ - g_clear_pointer(&jabber_ibb_sessions, g_hash_table_destroy); - g_clear_list(&open_handlers, NULL); -}
--- a/libpurple/protocols/jabber/ibb.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * purple - Handling of XEP-0047: In-Band Bytestreams. - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_IBB_H -#define PURPLE_JABBER_IBB_H - -#include "jabber.h" -#include "iq.h" - -typedef struct _JabberIBBSession JabberIBBSession; - -typedef void -(JabberIBBDataCallback)(JabberIBBSession *, const gpointer data, gsize size); - -typedef void (JabberIBBOpenedCallback)(JabberIBBSession *); -typedef void (JabberIBBClosedCallback)(JabberIBBSession *); -typedef void (JabberIBBErrorCallback)(JabberIBBSession *); -typedef void (JabberIBBSentCallback)(JabberIBBSession *); - -typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, const char *from, - const char *id, PurpleXmlNode *open); - -typedef enum { - JABBER_IBB_SESSION_NOT_OPENED, - JABBER_IBB_SESSION_OPENED, - JABBER_IBB_SESSION_CLOSED, - JABBER_IBB_SESSION_ERROR -} JabberIBBSessionState; - -struct _JabberIBBSession { - JabberStream *js; - gchar *who; - gchar *sid; - gchar *id; - guint16 send_seq; - guint16 recv_seq; - gsize block_size; - - /* session state */ - JabberIBBSessionState state; - - /* user data (f.ex. a handle to a PurpleXfer) */ - gpointer user_data; - - /* callbacks */ - JabberIBBOpenedCallback *opened_cb; - JabberIBBSentCallback *data_sent_cb; - JabberIBBClosedCallback *closed_cb; - /* callback for receiving data */ - JabberIBBDataCallback *data_received_cb; - JabberIBBErrorCallback *error_cb; - - /* store the last sent IQ (to permit cancel of callback) */ - gchar *last_iq_id; -}; - -JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid, - const gchar *who, gpointer user_data); -JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js, - const gchar *from, const gchar *id, PurpleXmlNode *open, gpointer user_data); - -void jabber_ibb_session_destroy(JabberIBBSession *sess); - -void jabber_ibb_session_set_opened_callback(JabberIBBSession *sess, - JabberIBBOpenedCallback *cb); -void jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess, - JabberIBBSentCallback *cb); -void jabber_ibb_session_set_closed_callback(JabberIBBSession *sess, - JabberIBBClosedCallback *cb); -void jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess, - JabberIBBDataCallback *cb); -void jabber_ibb_session_set_error_callback(JabberIBBSession *sess, - JabberIBBErrorCallback *cb); - -void jabber_ibb_session_open(JabberIBBSession *sess); -void jabber_ibb_session_close(JabberIBBSession *sess); -void jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data, - gsize size); - -JabberIBBSessionState jabber_ibb_session_get_state(const JabberIBBSession *sess); - -gsize jabber_ibb_session_get_block_size(const JabberIBBSession *sess); - -/* get maximum size data block to send (in bytes) - (before encoded to BASE64) */ -gsize jabber_ibb_session_get_max_data_size(const JabberIBBSession *sess); - -gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess); - -/* add a handler for open session */ -void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb); -void jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb); - -void jabber_ibb_init(void); -void jabber_ibb_uninit(void); - -#endif /* PURPLE_JABBER_IBB_H */
--- a/libpurple/protocols/jabber/iq.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,562 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "buddy.h" -#include "disco.h" -#include "iq.h" -#include "jingle/jingle.h" -#include "oob.h" -#include "roster.h" -#include "si.h" -#include "ping.h" -#include "adhoccommands.h" -#include "data.h" -#include "ibb.h" - -static GHashTable *iq_handlers = NULL; -static GHashTable *signal_iq_handlers = NULL; - -struct _JabberIqCallbackData { - JabberIqCallback *callback; - gpointer data; - JabberID *to; -}; - -void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd) -{ - jabber_id_free(jcd->to); - g_free(jcd); -} - -JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type) -{ - JabberIq *iq; - - iq = g_new0(JabberIq, 1); - - iq->type = type; - - iq->node = purple_xmlnode_new("iq"); - switch(iq->type) { - case JABBER_IQ_SET: - purple_xmlnode_set_attrib(iq->node, "type", "set"); - break; - case JABBER_IQ_GET: - purple_xmlnode_set_attrib(iq->node, "type", "get"); - break; - case JABBER_IQ_ERROR: - purple_xmlnode_set_attrib(iq->node, "type", "error"); - break; - case JABBER_IQ_RESULT: - purple_xmlnode_set_attrib(iq->node, "type", "result"); - break; - case JABBER_IQ_NONE: - /* this shouldn't ever happen */ - break; - } - - iq->js = js; - - if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) { - iq->id = jabber_get_next_id(js); - purple_xmlnode_set_attrib(iq->node, "id", iq->id); - } - - return iq; -} - -JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, - const char *xmlns) -{ - JabberIq *iq = jabber_iq_new(js, type); - PurpleXmlNode *query; - - query = purple_xmlnode_new_child(iq->node, "query"); - purple_xmlnode_set_namespace(query, xmlns); - - return iq; -} - -void -jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data) -{ - iq->callback = callback; - iq->callback_data = data; -} - -void jabber_iq_set_id(JabberIq *iq, const char *id) -{ - g_free(iq->id); - - if(id) { - purple_xmlnode_set_attrib(iq->node, "id", id); - iq->id = g_strdup(id); - } else { - purple_xmlnode_remove_attrib(iq->node, "id"); - iq->id = NULL; - } -} - -void jabber_iq_send(JabberIq *iq) -{ - JabberIqCallbackData *jcd; - g_return_if_fail(iq != NULL); - - jabber_send(iq->js, iq->node); - - if(iq->id && iq->callback) { - jcd = g_new0(JabberIqCallbackData, 1); - jcd->callback = iq->callback; - jcd->data = iq->callback_data; - jcd->to = jabber_id_new(purple_xmlnode_get_attrib(iq->node, "to")); - - g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd); - } - - jabber_iq_free(iq); -} - -void jabber_iq_free(JabberIq *iq) -{ - g_return_if_fail(iq != NULL); - - g_free(iq->id); - purple_xmlnode_free(iq->node); - g_free(iq); -} - -static void -jabber_iq_last_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, G_GNUC_UNUSED PurpleXmlNode *packet) -{ - JabberIq *iq; - PurpleXmlNode *query; - char *idle_time; - - if(type == JABBER_IQ_GET) { - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, NS_LAST_ACTIVITY); - jabber_iq_set_id(iq, id); - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - - query = purple_xmlnode_get_child(iq->node, "query"); - - idle_time = - g_strdup_printf("%" G_GINT64_FORMAT, - (gint64)(js->idle ? time(NULL) - js->idle : 0)); - purple_xmlnode_set_attrib(query, "seconds", idle_time); - g_free(idle_time); - - jabber_iq_send(iq); - } -} - -static void jabber_time_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *child) -{ - JabberIq *iq; - - if(type == JABBER_IQ_GET) { - PurpleXmlNode *tzo, *utc; - GDateTime *now, *now_utc; - gchar *date, *tz; - - iq = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(iq, id); - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - - child = purple_xmlnode_new_child(iq->node, child->name); - purple_xmlnode_set_namespace(child, NS_ENTITY_TIME); - - /* <tzo>-06:00</tzo> */ - now = g_date_time_new_now_local(); - tz = g_date_time_format(now, "%:z"); - tzo = purple_xmlnode_new_child(child, "tzo"); - purple_xmlnode_insert_data(tzo, tz, -1); - g_free(tz); - - /* <utc>2006-12-19T17:58:35Z</utc> */ - now_utc = g_date_time_to_utc(now); - date = g_date_time_format(now_utc, "%FT%TZ"); - utc = purple_xmlnode_new_child(child, "utc"); - purple_xmlnode_insert_data(utc, date, -1); - g_free(date); - - g_date_time_unref(now); - g_date_time_unref(now_utc); - - jabber_iq_send(iq); - } else { - /* TODO: Errors */ - } -} - -static void -jabber_iq_version_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, G_GNUC_UNUSED PurpleXmlNode *packet) -{ - JabberIq *iq; - PurpleXmlNode *query; - - if(type == JABBER_IQ_GET) { - PurpleUi *ui; - const char *ui_name = NULL, *ui_version = NULL; - - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version"); - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - jabber_iq_set_id(iq, id); - - query = purple_xmlnode_get_child(iq->node, "query"); - - ui = purple_core_get_ui(); - - if(PURPLE_IS_UI(ui)) { - ui_name = purple_ui_get_name(ui); - ui_version = purple_ui_get_version(ui); - } - - if(NULL != ui_name && NULL != ui_version) { - char *version_complete = g_strdup_printf("%s (libpurple " VERSION ")", ui_version); - purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), ui_name, -1); - purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), version_complete, -1); - g_free(version_complete); - } else { - purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), "libpurple", -1); - purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), VERSION, -1); - } - - jabber_iq_send(iq); - } -} - -void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id) -{ - g_hash_table_remove(js->iq_callbacks, id); -} - -/** - * Verify that the 'from' attribute of an IQ reply is a valid match for - * a given IQ request. The expected behavior is outlined in section - * 8.1.2.1 of the XMPP CORE spec (RFC 6120). We consider the reply to - * be a valid match if any of the following is true: - * - Request 'to' matches reply 'from' (including the case where - * neither are set). - * - Request 'to' was my JID (bare or full) and reply 'from' is empty. - * - Request 'to' was empty and reply 'from' is my JID. The spec says - * we should only allow bare JID, but we also allow full JID for - * compatibility with some servers. - * - Request 'to' was empty and reply 'from' is server JID. Not allowed by - * any spec, but for compatibility with some servers. - * - * These rules should allow valid IQ replies while preventing spoofed - * ones. - * - * For more discussion see the "Spoofing of iq ids and misbehaving - * servers" email thread from January 2014 on the jdev and security - * mailing lists. Also see https://developer.pidgin.im/ticket/15879 - * - * @return TRUE if this reply is valid for the given request. - */ -static gboolean does_reply_from_match_request_to(JabberStream *js, JabberID *to, JabberID *from) -{ - if (jabber_id_equal(to, from)) { - /* Request 'to' matches reply 'from' */ - return TRUE; - } - - if (!from && purple_strequal(to->node, js->user->node) - && purple_strequal(to->domain, js->user->domain)) { - /* Request 'to' was my JID (bare or full) and reply 'from' is empty */ - return TRUE; - } - - if (!to && purple_strequal(from->domain, js->user->domain)) { - /* Request 'to' is empty and reply 'from' domain matches our domain */ - - if (!from->node && !from->resource) { - /* Reply 'from' is server bare JID */ - return TRUE; - } - - if (purple_strequal(from->node, js->user->node) - && (!from->resource || purple_strequal(from->resource, js->user->resource))) { - /* Reply 'from' is my full or bare JID */ - return TRUE; - } - } - - return FALSE; -} - -void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet) -{ - JabberIqCallbackData *jcd; - PurpleXmlNode *child, *error, *x; - const char *xmlns; - const char *iq_type, *id, *from; - JabberIqType type = JABBER_IQ_NONE; - gboolean signal_return; - JabberID *from_id; - - from = purple_xmlnode_get_attrib(packet, "from"); - id = purple_xmlnode_get_attrib(packet, "id"); - iq_type = purple_xmlnode_get_attrib(packet, "type"); - - /* - * Ensure the 'from' attribute is valid. No point in handling a stanza - * of which we don't understand where it came from. - */ - from_id = jabber_id_new(from); - - if (from && !from_id) { - purple_debug_error("jabber", "Received an iq with an invalid from: %s\n", from); - return; - } - - /* - * child will be either the first tag child or NULL if there is no child. - * Historically, we used just the 'query' subchild, but newer XEPs use - * differently named children. Grabbing the first child is (for the time - * being) sufficient. - */ - for (child = packet->child; child; child = child->next) { - if (child->type == PURPLE_XMLNODE_TYPE_TAG) - break; - } - - if (iq_type) { - if (purple_strequal(iq_type, "get")) - type = JABBER_IQ_GET; - else if (purple_strequal(iq_type, "set")) - type = JABBER_IQ_SET; - else if (purple_strequal(iq_type, "result")) - type = JABBER_IQ_RESULT; - else if (purple_strequal(iq_type, "error")) - type = JABBER_IQ_ERROR; - } - - if (type == JABBER_IQ_NONE) { - purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", - iq_type ? iq_type : "(null)"); - jabber_id_free(from_id); - return; - } - - /* All IQs must have an ID, so send an error for a set/get that doesn't */ - if(!id || !*id) { - - if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); - - purple_xmlnode_free(iq->node); - iq->node = purple_xmlnode_copy(packet); - if (from) { - purple_xmlnode_set_attrib(iq->node, "to", from); - purple_xmlnode_remove_attrib(iq->node, "from"); - } - - purple_xmlnode_set_attrib(iq->node, "type", "error"); - /* This id is clearly not useful, but we must put something there for a valid stanza */ - iq->id = jabber_get_next_id(js); - purple_xmlnode_set_attrib(iq->node, "id", iq->id); - error = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(error, "type", "modify"); - x = purple_xmlnode_new_child(error, "bad-request"); - purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); - - jabber_iq_send(iq); - } else - purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", - iq_type); - - jabber_id_free(from_id); - return; - } - - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), - "jabber-receiving-iq", js->gc, iq_type, id, from, packet)); - if (signal_return) { - jabber_id_free(from_id); - return; - } - - /* First, lets see if a special callback got registered */ - if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) { - jcd = g_hash_table_lookup(js->iq_callbacks, id); - if (jcd) { - if (does_reply_from_match_request_to(js, jcd->to, from_id)) { - jcd->callback(js, from, type, id, packet, jcd->data); - jabber_iq_remove_callback_by_id(js, id); - jabber_id_free(from_id); - return; - } else { - char *expected_to; - - if (jcd->to) { - expected_to = jabber_id_get_full_jid(jcd->to); - } else { - expected_to = jabber_id_get_bare_jid(js->user); - } - - purple_debug_error("jabber", "Got a result iq with id %s from %s instead of expected %s!\n", id, from ? from : "(null)", expected_to); - - g_free(expected_to); - } - } - } - - /* - * Apparently not, so let's see if we have a pre-defined handler - * or if an outside plugin is interested. - */ - if(child && (xmlns = purple_xmlnode_get_namespace(child))) { - char *key = g_strdup_printf("%s %s", child->name, xmlns); - JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key); - int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key)); - g_free(key); - - if (signal_ref > 0) { - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-watched-iq", - js->gc, iq_type, id, from, child)); - if (signal_return) { - jabber_id_free(from_id); - return; - } - } - - if(jih) { - jih(js, from, type, id, child); - jabber_id_free(from_id); - return; - } - } - - purple_debug_misc("jabber", "Unhandled IQ with id %s\n", id); - - /* If we get here, send the default error reply mandated by XMPP-CORE */ - if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); - - purple_xmlnode_free(iq->node); - iq->node = purple_xmlnode_copy(packet); - if (from) { - purple_xmlnode_set_attrib(iq->node, "to", from); - purple_xmlnode_remove_attrib(iq->node, "from"); - } - - purple_xmlnode_set_attrib(iq->node, "type", "error"); - error = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - purple_xmlnode_set_attrib(error, "code", "501"); - x = purple_xmlnode_new_child(error, "feature-not-implemented"); - purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); - - jabber_iq_send(iq); - } - - jabber_id_free(from_id); -} - -void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc) -{ - /* - * This is valid because nodes nor namespaces cannot have spaces in them - * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and - * http://www.w3.org/TR/REC-xml-names/) - */ - char *key = g_strdup_printf("%s %s", node, xmlns); - g_hash_table_replace(iq_handlers, key, handlerfunc); -} - -void jabber_iq_signal_register(const gchar *node, const gchar *xmlns) -{ - gchar *key; - int ref; - - g_return_if_fail(node != NULL && *node != '\0'); - g_return_if_fail(xmlns != NULL && *xmlns != '\0'); - - key = g_strdup_printf("%s %s", node, xmlns); - ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key)); - if (ref == 0) { - g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(1)); - } else { - g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref + 1)); - g_free(key); - } -} - -void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns) -{ - gchar *key; - int ref; - - g_return_if_fail(node != NULL && *node != '\0'); - g_return_if_fail(xmlns != NULL && *xmlns != '\0'); - - key = g_strdup_printf("%s %s", node, xmlns); - ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key)); - - if (ref == 1) { - g_hash_table_remove(signal_iq_handlers, key); - } else if (ref > 1) { - g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref - 1)); - } - - g_free(key); -} - -void jabber_iq_init(void) -{ - iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - signal_iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - jabber_iq_register_handler("jingle", JINGLE, jingle_parse); - jabber_iq_register_handler("ping", NS_PING, jabber_ping_parse); - jabber_iq_register_handler("query", NS_BYTESTREAMS, - jabber_bytestreams_parse); - jabber_iq_register_handler("query", NS_DISCO_INFO, jabber_disco_info_parse); - jabber_iq_register_handler("query", NS_DISCO_ITEMS, jabber_disco_items_parse); - jabber_iq_register_handler("query", NS_LAST_ACTIVITY, jabber_iq_last_parse); - jabber_iq_register_handler("query", NS_OOB_IQ_DATA, jabber_oob_parse); - jabber_iq_register_handler("query", "jabber:iq:roster", - jabber_roster_parse); - jabber_iq_register_handler("query", "jabber:iq:version", - jabber_iq_version_parse); - jabber_iq_register_handler("block", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push); - jabber_iq_register_handler("unblock", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push); - jabber_iq_register_handler("time", NS_ENTITY_TIME, jabber_time_parse); - -} - -void jabber_iq_uninit(void) -{ - g_clear_pointer(&iq_handlers, g_hash_table_destroy); - g_clear_pointer(&signal_iq_handlers, g_hash_table_destroy); -}
--- a/libpurple/protocols/jabber/iq.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -/** - * @file iq.h JabberID handlers - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_IQ_H -#define PURPLE_JABBER_IQ_H - -#include <purple.h> - -typedef enum { - JABBER_IQ_SET, - JABBER_IQ_GET, - JABBER_IQ_RESULT, - JABBER_IQ_ERROR, - JABBER_IQ_NONE -} JabberIqType; - -#include "jabber.h" - -typedef struct _JabberIq JabberIq; -typedef struct _JabberIqCallbackData JabberIqCallbackData; - -/** - * A JabberIqHandler is called to process an incoming IQ stanza. - * Handlers typically process unsolicited incoming GETs or SETs for their - * registered namespace, but may be called to handle the results of a - * GET or SET that we generated if no JabberIqCallback was generated - * The handler may be called for the results of a GET or SET (RESULT or ERROR) - * that we generated - * if the generating function did not register a JabberIqCallback. - * - * @param js The JabberStream object. - * @param from The remote entity (the from attribute on the <iq/> stanza) - * @param type The IQ type. - * @param id The IQ id (the id attribute on the <iq/> stanza) - * @param child The child element of the <iq/> stanza that matches the name - * and namespace registered with jabber_iq_register_handler. - * - * @see jabber_iq_register_handler() - * @see JabberIqCallback - */ -typedef void (JabberIqHandler)(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *child); - -/** - * A JabberIqCallback is called to process the results of a GET or SET that - * we send to a remote entity. The callback is matched based on the id - * of the incoming stanza (which matches the one on the initial stanza). - * - * @param js The JabberStream object. - * @param from The remote entity (the from attribute on the <iq/> stanza) - * @param type The IQ type. The only possible values are JABBER_IQ_RESULT - * and JABBER_IQ_ERROR. - * @param id The IQ id (the id attribute on the <iq/> stanza) - * @param packet The <iq/> stanza - * @param data The callback data passed to jabber_iq_set_callback() - * - * @see jabber_iq_set_callback() - */ -typedef void (JabberIqCallback)(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data); - -struct _JabberIq { - JabberIqType type; - char *id; - PurpleXmlNode *node; - - JabberIqCallback *callback; - gpointer callback_data; - - JabberStream *js; -}; - -JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type); -JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, - const char *xmlns); - -void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet); - -void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd); -void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id); -void jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *cb, gpointer data); -void jabber_iq_set_id(JabberIq *iq, const char *id); - -void jabber_iq_send(JabberIq *iq); -void jabber_iq_free(JabberIq *iq); - -void jabber_iq_init(void); -void jabber_iq_uninit(void); - -void jabber_iq_register_handler(const char *node, const char *xmlns, - JabberIqHandler *func); - -/* Connected to namespace-handler registration signals */ -void jabber_iq_signal_register(const gchar *node, const gchar *xmlns); -void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns); - -#endif /* PURPLE_JABBER_IQ_H */
--- a/libpurple/protocols/jabber/jabber.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3312 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purpleconfig.h> - -#include <errno.h> - -#include <glib/gi18n-lib.h> - -#include <gplugin.h> -#include <gplugin-native.h> - -#include <birb.h> - -#include <purple.h> - -#include "auth.h" -#include "buddy.h" -#include "caps.h" -#include "chat.h" -#include "data.h" -#include "disco.h" -#include "ibb.h" -#include "iq.h" -#include "jutil.h" -#include "message.h" -#include "parser.h" -#include "presence.h" -#include "jabber.h" -#include "roster.h" -#include "oob.h" -#include "ping.h" -#include "si.h" -#include "xdata.h" -#include "pep.h" -#include "adhoccommands.h" -#include "xmpp.h" - -#include "jingle/jingle.h" -#include "jingle/content.h" -#include "jingle/iceudp.h" -#include "jingle/rawudp.h" -#include "jingle/rtp.h" -#include "jingle/session.h" - -#define PING_TIMEOUT 60 -/* Send a whitespace keepalive to the server if we haven't sent - * anything in the last 120 seconds - */ -#define DEFAULT_INACTIVITY_TIME 120 - -GList *jabber_features = NULL; -GList *jabber_identities = NULL; - -static PurpleProtocol *xmpp_protocol = NULL; - -static GHashTable *jabber_cmds = NULL; /* PurpleProtocol * => GSList of ids */ - -static gint plugin_ref = 0; - -static void jabber_send_raw(PurpleProtocolServer *protocol_server, JabberStream *js, const gchar *data, gint len); -static void jabber_remove_feature(const gchar *namespace); -static gboolean jabber_initiate_media(PurpleProtocolMedia *media, PurpleAccount *account, const char *who, PurpleMediaSessionType type); -static PurpleMediaCaps jabber_get_media_caps(PurpleProtocolMedia *media, PurpleAccount *account, const char *who); - -static void jabber_stream_init(JabberStream *js) -{ - char *open_stream; - - g_free(js->stream_id); - js->stream_id = NULL; - - open_stream = g_strdup_printf("<stream:stream to='%s' " - "xmlns='" NS_XMPP_CLIENT "' " - "xmlns:stream='" NS_XMPP_STREAMS "' " - "version='1.0'>", - js->user->domain); - /* setup the parser fresh for each stream */ - jabber_parser_setup(js); - jabber_send_raw(NULL, js, open_stream, -1); - js->reinit = FALSE; - g_free(open_stream); -} - -static void -jabber_session_initialized_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - if (type == JABBER_IQ_RESULT) { - jabber_disco_items_server(js); - } else { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - ("Error initializing session")); - } -} - -static void jabber_session_init(JabberStream *js) -{ - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); - PurpleXmlNode *session; - - jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL); - - session = purple_xmlnode_new_child(iq->node, "session"); - purple_xmlnode_set_namespace(session, NS_XMPP_SESSION); - - jabber_iq_send(iq); -} - -static void -jabber_bind_result_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *bind; - - if (type == JABBER_IQ_RESULT && - (bind = purple_xmlnode_get_child_with_namespace(packet, "bind", NS_XMPP_BIND))) { - PurpleXmlNode *jid; - char *full_jid; - if((jid = purple_xmlnode_get_child(bind, "jid")) && (full_jid = purple_xmlnode_get_data(jid))) { - jabber_id_free(js->user); - - js->user = jabber_id_new(full_jid); - if (js->user == NULL) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - g_free(full_jid); - return; - } - - js->user_jb = jabber_buddy_find(js, full_jid, TRUE); - js->user_jb->subscription |= JABBER_SUB_BOTH; - - purple_connection_set_display_name(js->gc, full_jid); - - g_free(full_jid); - } - } else { - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - char *msg = jabber_parse_error(js, packet, &reason); - purple_connection_error(js->gc, reason, msg); - g_free(msg); - - return; - } - - jabber_session_init(js); -} - -static char * -jabber_prep_resource(char *input) -{ - const gchar *hostname = NULL, *dot = NULL; - gchar *result = NULL; - - /* Empty resource == don't send any */ - if (input == NULL || *input == '\0') - return NULL; - - if (strstr(input, "__HOSTNAME__") == NULL) - return g_strdup(input); - - /* Replace __HOSTNAME__ with hostname */ - hostname = g_get_host_name(); - - /* We want only the short hostname, not the FQDN - this will prevent the - * resource string from being unreasonably long on systems which stuff the - * whole FQDN in the hostname */ - if ((dot = strchr(hostname, '.')) != NULL) { - gchar *short_hostname = g_strndup(hostname, dot - hostname); - result = purple_strreplace(input, "__HOSTNAME__", short_hostname); - g_free(short_hostname); - } else { - result = purple_strreplace(input, "__HOSTNAME__", hostname); - } - - return result; -} - -static gboolean -jabber_process_starttls(JabberStream *js, PurpleXmlNode *packet) -{ - PurpleAccount *account = NULL; - PurpleXmlNode *starttls = NULL; - - /* It's a secure BOSH connection, just return FALSE and skip, without doing - * anything extra. XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any - * Transport Layer Security (TLS) feature since BOSH channel encryption - * SHOULD be negotiated at the HTTP layer. - * - * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH - * connection, so it is not necessary to check if SSL is supported. - */ - if (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) { - return FALSE; - } - - /* Otherwise, it's a standard XMPP connection, or a HTTP (insecure) BOSH connection. - * We request STARTTLS for standard XMPP connections, but we do nothing for insecure - * BOSH connections, per XEP-0206. */ - if(!js->bosh) { - jabber_send_raw(NULL, js, - "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1); - return TRUE; - } - - /* It's an insecure standard XMPP connection, or an insecure BOSH connection, let's - * ignore STARTTLS even it's required by the server to prevent disabling HTTP BOSH - * entirely (sysadmin is responsible to provide HTTPS-only BOSH if security is required), - * and emit errors if encryption is required by the user. */ - starttls = purple_xmlnode_get_child(packet, "starttls"); - if(!js->bosh && purple_xmlnode_get_child(starttls, "required")) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("Server requires TLS/SSL, but no TLS/SSL support was found.")); - return TRUE; - } - - account = purple_connection_get_account(js->gc); - if (purple_strequal("require_tls", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("You require encryption, but no TLS/SSL support was found.")); - return TRUE; - } - - return FALSE; -} - -void jabber_stream_features_parse(JabberStream *js, PurpleXmlNode *packet) -{ - PurpleAccount *account = purple_connection_get_account(js->gc); - const char *connection_security = - purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS); - - if (purple_xmlnode_get_child(packet, "starttls")) { - if (jabber_process_starttls(js, packet)) { - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); - return; - } - } else if (purple_strequal(connection_security, "require_tls") && !jabber_stream_is_ssl(js)) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("You require encryption, but it is not available on this server.")); - return; - } - - if(purple_xmlnode_get_child(packet, "mechanisms")) { - jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); - jabber_auth_start(js, packet); - } else if(purple_xmlnode_get_child(packet, "bind")) { - PurpleXmlNode *bind, *resource; - char *requested_resource; - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); - bind = purple_xmlnode_new_child(iq->node, "bind"); - purple_xmlnode_set_namespace(bind, NS_XMPP_BIND); - requested_resource = jabber_prep_resource(js->user->resource); - - if (requested_resource != NULL) { - resource = purple_xmlnode_new_child(bind, "resource"); - purple_xmlnode_insert_data(resource, requested_resource, -1); - g_free(requested_resource); - } - - jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL); - - jabber_iq_send(iq); - } else if (purple_xmlnode_get_child_with_namespace(packet, "ver", NS_ROSTER_VERSIONING)) { - js->server_caps |= JABBER_CAP_ROSTER_VERSIONING; - } else /* if(purple_xmlnode_get_child_with_namespace(packet, "auth")) */ { - /* If we get an empty stream:features packet, or we explicitly get - * an auth feature with namespace http://jabber.org/features/iq-auth - * we should revert back to iq:auth authentication, even though we're - * connecting to an XMPP server. */ - jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); - jabber_auth_start_old(js); - } -} - -static void jabber_stream_handle_error(JabberStream *js, PurpleXmlNode *packet) -{ - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - char *msg = jabber_parse_error(js, packet, &reason); - - purple_connection_error(js->gc, reason, msg); - - g_free(msg); -} - -static void tls_init(JabberStream *js); - -void jabber_process_packet(JabberStream *js, PurpleXmlNode **packet) -{ - const char *name; - const char *xmlns; - - purple_signal_emit(purple_connection_get_protocol(js->gc), "jabber-receiving-xmlnode", js->gc, packet); - - /* if the signal leaves us with a null packet, we're done */ - if(NULL == *packet) - return; - - name = (*packet)->name; - xmlns = purple_xmlnode_get_namespace(*packet); - - if (purple_strequal(name, "iq")) { - jabber_iq_parse(js, *packet); - } else if (purple_strequal(name, "presence")) { - jabber_presence_parse(js, *packet); - } else if (purple_strequal(name, "message")) { - jabber_message_parse(js, *packet); - } else if (purple_strequal(xmlns, NS_XMPP_STREAMS)) { - if (purple_strequal(name, "features")) - jabber_stream_features_parse(js, *packet); - else if (purple_strequal(name, "error")) - jabber_stream_handle_error(js, *packet); - } else if (purple_strequal(xmlns, NS_XMPP_SASL)) { - if (js->state != JABBER_STREAM_AUTHENTICATING) - purple_debug_warning("jabber", "Ignoring spurious SASL stanza %s\n", name); - else { - if (purple_strequal(name, "challenge")) - jabber_auth_handle_challenge(js, *packet); - else if (purple_strequal(name, "success")) - jabber_auth_handle_success(js, *packet); - else if (purple_strequal(name, "failure")) - jabber_auth_handle_failure(js, *packet); - } - } else if (purple_strequal(xmlns, NS_XMPP_TLS)) { - if (js->state != JABBER_STREAM_INITIALIZING_ENCRYPTION || - G_IS_TLS_CONNECTION(js->stream)) { - purple_debug_warning("jabber", "Ignoring spurious %s\n", name); - } else { - if (purple_strequal(name, "proceed")) - tls_init(js); - /* TODO: Handle <failure/>, I guess? */ - } - } else { - purple_debug_warning("jabber", "Unknown packet: %s\n", name); - } -} - -static void -jabber_push_bytes_cb(GObject *source, GAsyncResult *res, gpointer data) -{ - BirbQueuedOutputStream *stream = BIRB_QUEUED_OUTPUT_STREAM(source); - JabberStream *js = data; - gboolean result; - GError *error = NULL; - - result = birb_queued_output_stream_push_bytes_finish(stream, res, &error); - - if (!result) { - birb_queued_output_stream_clear_queue(stream); - - if (error->code != G_IO_ERROR_CANCELLED) { - g_prefix_error(&error, "%s", _("Lost connection with server: ")); - purple_connection_take_error(js->gc, error); - } else { - g_error_free(error); - } - } -} - -static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len) -{ - GBytes *output; - gboolean success = TRUE; - - g_return_val_if_fail(len > 0, FALSE); - - if (js->state == JABBER_STREAM_CONNECTED) - jabber_stream_restart_inactivity_timer(js); - - output = g_bytes_new(data, len); - birb_queued_output_stream_push_bytes_async( - BIRB_QUEUED_OUTPUT_STREAM(js->output), output, G_PRIORITY_DEFAULT, - js->cancellable, jabber_push_bytes_cb, js); - g_bytes_unref(output); - - return success; -} - -static void -jabber_send_raw(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - JabberStream *js, const char *data, gint len) -{ - PurpleConnection *gc; - PurpleAccount *account; - - gc = js->gc; - account = purple_connection_get_account(gc); - - g_return_if_fail(data != NULL); - - /* because printing a tab to debug every minute gets old */ - if (!purple_strequal(data, "\t")) { - const char *username; - char *text = NULL, *last_part = NULL, *tag_start = NULL; - - /* Because debug logs with plaintext passwords make me sad */ - if (!purple_debug_is_unsafe() && js->state != JABBER_STREAM_CONNECTED && - /* Either <auth> or <query><password>... */ - (((tag_start = strstr(data, "<auth ")) && - strstr(data, "xmlns='" NS_XMPP_SASL "'")) || - ((tag_start = strstr(data, "<query ")) && - strstr(data, "xmlns='jabber:iq:auth'>") && - (tag_start = strstr(tag_start, "<password>"))))) { - char *data_start, *tag_end = strchr(tag_start, '>'); - text = g_strdup(data); - - /* Better to print out some wacky debugging than crash - * due to a plugin sending bad xml */ - if (tag_end == NULL) - tag_end = tag_start; - - data_start = text + (tag_end - data) + 1; - - last_part = strchr(data_start, '<'); - *data_start = '\0'; - } - - username = purple_connection_get_display_name(gc); - if(username == NULL) { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - username = purple_contact_info_get_username(info); - } - - purple_debug_misc("jabber", "Sending%s (%s): %s%s%s\n", - jabber_stream_is_ssl(js) ? " (ssl)" : "", username, - text ? text : data, - last_part ? "password removed" : "", - last_part ? last_part : ""); - - g_free(text); - } - - purple_signal_emit(purple_connection_get_protocol(gc), "jabber-sending-text", gc, &data); - if (data == NULL) - return; - - if (len == -1) - len = strlen(data); - - if (js->bosh) - jabber_bosh_connection_send(js->bosh, data); - else - do_jabber_send_raw(js, data, len); -} - -static gint -jabber_protocol_send_raw(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const gchar *buf, gint len) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - g_return_val_if_fail(js != NULL, -1); - /* TODO: It's probably worthwhile to restrict this to when the account - * state is CONNECTED, but I can /almost/ envision reasons for wanting - * to do things during the connection process. - */ - - jabber_send_raw(NULL, js, buf, len); - return (len < 0 ? (int)strlen(buf) : len); -} - -static void -jabber_send_signal_cb(PurpleConnection *pc, PurpleXmlNode **packet, - G_GNUC_UNUSED gpointer unused) -{ - JabberStream *js; - char *txt; - int len; - - if (NULL == packet) - return; - - PURPLE_ASSERT_CONNECTION_IS_VALID(pc); - - js = purple_connection_get_protocol_data(pc); - - if (NULL == js) - return; - - if (js->bosh) - if (purple_strequal((*packet)->name, "message") || - purple_strequal((*packet)->name, "iq") || - purple_strequal((*packet)->name, "presence")) - purple_xmlnode_set_namespace(*packet, NS_XMPP_CLIENT); - txt = purple_xmlnode_to_str(*packet, &len); - jabber_send_raw(NULL, js, txt, len); - g_free(txt); -} - -void jabber_send(JabberStream *js, PurpleXmlNode *packet) -{ - purple_signal_emit(purple_connection_get_protocol(js->gc), "jabber-sending-xmlnode", js->gc, &packet); -} - -static gboolean jabber_keepalive_timeout(PurpleConnection *gc) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Ping timed out")); - js->keepalive_timeout = 0; - return FALSE; -} - -static void -jabber_keepalive(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - if (js->keepalive_timeout == 0) { - jabber_keepalive_ping(js); - js->keepalive_timeout = g_timeout_add_seconds(120, - G_SOURCE_FUNC(jabber_keepalive_timeout), gc); - } -} - -static int -jabber_get_keepalive_interval(G_GNUC_UNUSED PurpleProtocolServer *protocol_server) -{ - return PING_TIMEOUT; -} - -static gboolean -jabber_recv_cb(GObject *stream, gpointer data) -{ - PurpleConnection *gc = data; - JabberStream *js = purple_connection_get_protocol_data(gc); - gssize len; - gchar buf[4096]; - GError *error = NULL; - - PURPLE_ASSERT_CONNECTION_IS_VALID(gc); - - do { - len = g_pollable_input_stream_read_nonblocking( - G_POLLABLE_INPUT_STREAM(stream), buf, sizeof(buf) - 1, - js->cancellable, &error); - if (len == 0) { - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Server closed the connection")); - js->inpa = 0; - return G_SOURCE_REMOVE; - } else if (len < 0) { - if (error->code == G_IO_ERROR_WOULD_BLOCK) { - g_error_free(error); - return G_SOURCE_CONTINUE; - } else if (error->code == G_IO_ERROR_CANCELLED) { - g_error_free(error); - } else { - g_prefix_error(&error, "%s", - _("Lost connection with server: ")); - purple_connection_take_error(js->gc, error); - } - js->inpa = 0; - return G_SOURCE_REMOVE; - } - - purple_connection_update_last_received(gc); - buf[len] = '\0'; - purple_debug_misc("jabber", "Recv (%" G_GSSIZE_FORMAT "): %s", len, - buf); - jabber_parser_process(js, buf, len); - if(js->reinit) - jabber_stream_init(js); - } while (len > 0); - - return G_SOURCE_CONTINUE; -} - -static void -jabber_stream_connect_finish(JabberStream *js, GIOStream *stream) -{ - GSource *source; - - js->stream = stream; - js->input = g_object_ref(g_io_stream_get_input_stream(js->stream)); - js->output = birb_queued_output_stream_new( - g_io_stream_get_output_stream(js->stream)); - - if (js->state == JABBER_STREAM_CONNECTING) { - jabber_send_raw(NULL, js, "<?xml version='1.0' ?>", -1); - } - - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); - source = g_pollable_input_stream_create_source( - G_POLLABLE_INPUT_STREAM(js->input), js->cancellable); - g_source_set_callback(source, G_SOURCE_FUNC(jabber_recv_cb), js->gc, NULL); - js->inpa = g_source_attach(source, NULL); - g_source_unref(source); -} - -static void -jabber_login_callback(GObject *source_object, GAsyncResult *res, gpointer data) -{ - GSocketClient *client = G_SOCKET_CLIENT(source_object); - JabberStream *js = data; - GSocketConnection *conn; - GIOStream *stream; - gboolean is_old_ssl = g_socket_client_get_tls(client); - GError *error = NULL; - - conn = g_socket_client_connect_to_host_finish(client, res, &error); - if (conn == NULL) { - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - g_error_free(error); - return; - } else if (is_old_ssl) { - /* Old-style SSL only makes a direct connection, or fails. */ - purple_connection_take_error(js->gc, error); - return; - } - g_error_free(error); - - purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - - purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - - return; - } - - if (is_old_ssl) { - stream = G_IO_STREAM(g_tcp_wrapper_connection_get_base_io_stream( - G_TCP_WRAPPER_CONNECTION(conn))); - } else { - stream = G_IO_STREAM(conn); - } - - jabber_stream_connect_finish(js, stream); - - if (is_old_ssl) { - /* Tell the app that we're doing encryption */ - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); - } -} - -static void -tls_handshake_cb(GObject *source_object, GAsyncResult *res, gpointer data) -{ - JabberStream *js = data; - GError *error = NULL; - - if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source_object), res, - &error)) { - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - /* Connection already closed/freed. Escape. */ - } else if (g_error_matches(error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) { - /* In Gio, a handshake error is because of the cert */ - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR, - _("SSL peer presented an invalid certificate")); - } else { - /* Report any other errors as handshake failing */ - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("SSL Handshake Failed")); - } - - g_error_free(error); - return; - } - - jabber_stream_connect_finish(js, js->stream); - - /* Tell the app that we're doing encryption */ - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); -} - -static void tls_init(JabberStream *js) -{ - GSocketConnectable *identity; - GIOStream *tls_conn; - GError *error = NULL; - - g_clear_handle_id(&js->inpa, g_source_remove); - js->input = NULL; - g_filter_output_stream_set_close_base_stream( - G_FILTER_OUTPUT_STREAM(js->output), FALSE); - g_output_stream_close(G_OUTPUT_STREAM(js->output), js->cancellable, NULL); - js->output = NULL; - - identity = g_network_address_new(js->certificate_CN, 0); - tls_conn = g_tls_client_connection_new(js->stream, identity, &error); - g_object_unref(identity); - - if (tls_conn == NULL) { - purple_debug_warning("jabber", - "Error creating TLS client connection: %s", - error->message); - g_clear_error(&error); - purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("SSL Connection Failed")); - return; - } - - g_clear_object(&js->stream); - js->stream = G_IO_STREAM(tls_conn); - - g_tls_connection_handshake_async(G_TLS_CONNECTION(tls_conn), - G_PRIORITY_DEFAULT, js->cancellable, - tls_handshake_cb, js); -} - -static void -srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data) -{ - GSocketClient *client = G_SOCKET_CLIENT(source_object); - JabberStream *js = data; - GSocketConnection *conn; - GError *error = NULL; - - conn = g_socket_client_connect_to_service_finish(client, result, &error); - if (error) { - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - /* Do nothing; cancelled. */ - - } else if (g_error_matches(error, G_RESOLVER_ERROR, - G_RESOLVER_ERROR_NOT_FOUND)) { - /* If there was no response, then attempt fallback behaviour of XMPP - * Core 3.2.2. */ - purple_debug_warning( - "jabber", - "SRV lookup failed, proceeding with normal connection : %s", - error->message); - - g_socket_client_connect_to_host_async( - js->client, js->user->domain, - purple_account_get_int( - purple_connection_get_account(js->gc), "port", - 5222), - js->cancellable, jabber_login_callback, js); - - } else { - /* If resolving failed or connecting failed, then just error out, as - * in XMPP Core 3.2.1 step 8. */ - purple_connection_g_error(js->gc, error); - } - - g_error_free(error); - return; - } - - jabber_stream_connect_finish(js, G_IO_STREAM(conn)); -} - -static JabberStream * -jabber_stream_new(PurpleAccount *account) -{ - PurpleConnection *gc = purple_account_get_connection(account); - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - GProxyResolver *resolver; - GError *error = NULL; - JabberStream *js; - PurplePresence *presence; - gchar *user; - gchar *slash; - - resolver = purple_proxy_get_proxy_resolver(account, &error); - if (resolver == NULL) { - purple_debug_error("jabber", "Unable to get account proxy resolver: %s", - error->message); - g_error_free(error); - return NULL; - } - - js = g_new0(JabberStream, 1); - purple_connection_set_protocol_data(gc, js); - js->gc = gc; - js->http_conns = soup_session_new_with_options("proxy-resolver", resolver, - NULL); - g_object_unref(resolver); - - /* we might want to expose this at some point */ - js->cancellable = g_cancellable_new(); - - user = g_strdup(purple_contact_info_get_username(info)); - /* jabber_id_new doesn't accept "user@domain/" as valid */ - slash = strchr(user, '/'); - if (slash && *(slash + 1) == '\0') - *slash = '\0'; - js->user = jabber_id_new(user); - - if (!js->user) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Invalid XMPP ID")); - g_free(user); - /* Destroying the connection will free the JabberStream */ - return NULL; - } - - if (!js->user->node || *(js->user->node) == '\0') { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Invalid XMPP ID. Username portion must be set.")); - g_free(user); - /* Destroying the connection will free the JabberStream */ - return NULL; - } - - if (!js->user->domain || *(js->user->domain) == '\0') { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Invalid XMPP ID. Domain must be set.")); - g_free(user); - /* Destroying the connection will free the JabberStream */ - return NULL; - } - - js->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)jabber_buddy_free); - - /* This is overridden during binding, but we need it here - * in case the server only does legacy non-sasl auth!. - */ - purple_connection_set_display_name(gc, user); - - js->user_jb = jabber_buddy_find(js, user, TRUE); - g_free(user); - if (!js->user_jb) { - /* This basically *can't* fail, but for good measure... */ - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Invalid XMPP ID")); - /* Destroying the connection will free the JabberStream */ - g_return_val_if_reached(NULL); - } - - js->user_jb->subscription |= JABBER_SUB_BOTH; - - js->iq_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)jabber_iq_callbackdata_free); - js->chats = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)jabber_chat_free); - js->next_id = g_random_int(); - js->keepalive_timeout = 0; - js->max_inactivity = DEFAULT_INACTIVITY_TIME; - /* Set the default protocol version to 1.0. Overridden in parser.c. */ - js->protocol_version.major = 1; - js->protocol_version.minor = 0; - js->sessions = NULL; - - /* if we are idle, set idle-ness on the stream (this could happen if we get - disconnected and the reconnects while being idle. I don't think it makes - sense to do this when registering a new account... */ - presence = purple_account_get_presence(account); - if (purple_presence_is_idle(presence)) { - GDateTime *idle = purple_presence_get_idle_time(presence); - - js->idle = 0; - if(idle != NULL) { - js->idle = g_date_time_to_unix(idle); - } - } - - return js; -} - -static void -jabber_stream_connect(JabberStream *js) -{ - PurpleConnection *gc = js->gc; - PurpleAccount *account = purple_connection_get_account(gc); - const char *connect_server = purple_account_get_string(account, - "connect_server", ""); - const char *bosh_url = purple_account_get_string(account, - "bosh_url", ""); - GError *error = NULL; - - jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); - - /* If both BOSH and a Connect Server are specified, we prefer BOSH. I'm not - * attached to that choice, though. - */ - if (*bosh_url) { - js->bosh = jabber_bosh_connection_new(js, bosh_url); - if (!js->bosh) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Malformed BOSH URL")); - } - - return; - } - - js->client = purple_gio_socket_client_new(account, &error); - if (js->client == NULL) { - purple_connection_take_error(gc, error); - return; - } - - js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); - - /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */ - if (purple_strequal("old_ssl", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) { - g_socket_client_set_tls(js->client, TRUE); - g_socket_client_connect_to_host_async( - js->client, js->certificate_CN, - purple_account_get_int(account, "port", 5223), js->cancellable, - jabber_login_callback, js); - return; - } - - /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll - * invoke the magic of SRV lookups, to figure out host and port */ - if(connect_server[0]) { - g_socket_client_connect_to_host_async( - js->client, connect_server, - purple_account_get_int(account, "port", 5222), js->cancellable, - jabber_login_callback, js); - } else { - g_socket_client_connect_to_service_async(js->client, js->user->domain, - "xmpp-client", js->cancellable, - srv_resolved_cb, js); - } -} - -static void -jabber_login(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleAccount *account) { - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js; - PurpleImage *image; - - purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML | - PURPLE_CONNECTION_FLAG_NO_IMAGES); - js = jabber_stream_new(account); - if (js == NULL) - return; - - /* replace old default proxies with the new default: NULL - * TODO: these can eventually be removed */ - if (purple_strequal("proxy.jabber.org", purple_account_get_string(account, "ft_proxies", "")) - || purple_strequal("proxy.eu.jabber.org", purple_account_get_string(account, "ft_proxies", ""))) - purple_account_set_string(account, "ft_proxies", NULL); - - /* - * Calculate the avatar hash for our current image so we know (when we - * fetch our vCard and PEP avatar) if we should send our avatar to the - * server. - */ - image = purple_buddy_icons_find_account_icon(account); - if (image != NULL) { - js->initial_avatar_hash = g_compute_checksum_for_data( - G_CHECKSUM_SHA1, - purple_image_get_data(image), - purple_image_get_data_size(image) - ); - g_object_unref(image); - } - - jabber_stream_connect(js); -} - -/* TODO: As Will pointed out in IRC, after being notified by the core to - * shutdown, we should async. wait for the server to send us the stream - * termination before destroying everything. That seems like it would require - * changing the semantics of protocol's close(), so it's a good idea for 3.0.0. - */ -static void -jabber_close(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleConnection *gc) { - JabberStream *js = purple_connection_get_protocol_data(gc); - - /* Close all of the open Jingle sessions on this stream */ - jingle_terminate_sessions(js); - - if (js->bosh) { - jabber_bosh_connection_destroy(js->bosh); - js->bosh = NULL; - } else if (js->output != NULL) { - /* We should emit the stream termination message here - * normally, but since we destroy the jabber stream just - * after, it has no way to effectively go out on the - * wire. Moreover, it causes a connection lost error in - * the output queued stream that triggers an - * heap-use-after-free error in jabber_push_bytes_cb(). - * - * This case happens when disabling the jabber account - * from the dialog box. - * - * jabber_send_raw(js, "</stream:stream>", -1); - */ - g_clear_handle_id(&js->inpa, g_source_remove); - purple_gio_graceful_close(js->stream, js->input, - G_OUTPUT_STREAM(js->output)); - } - - g_clear_object(&js->output); - g_clear_object(&js->input); - g_clear_object(&js->stream); - - jabber_buddy_remove_all_pending_buddy_info_requests(js); - - jabber_parser_free(js); - - g_clear_pointer(&js->iq_callbacks, g_hash_table_destroy); - g_clear_pointer(&js->buddies, g_hash_table_destroy); - g_clear_pointer(&js->chats, g_hash_table_destroy); - - g_list_free_full(js->chat_servers, g_free); - - g_list_free_full(js->bs_proxies, (GDestroyNotify)jabber_bytestreams_streamhost_free); - - if (js->http_conns) { - soup_session_abort(js->http_conns); - g_object_unref(js->http_conns); - } - - g_free(js->stream_id); - g_clear_pointer(&js->user, jabber_id_free); - g_free(js->initial_avatar_hash); - g_free(js->avatar_hash); - g_free(js->caps_hash); - - if (js->auth_mech && js->auth_mech->dispose) - js->auth_mech->dispose(js); - g_free(js->serverFQDN); - g_list_free_full(js->commands, (GDestroyNotify)jabber_adhoc_commands_free); - g_free(js->server_name); - g_free(js->certificate_CN); - g_free(js->old_msg); - g_free(js->old_avatarhash); - - g_clear_handle_id(&js->keepalive_timeout, g_source_remove); - g_clear_handle_id(&js->inactivity_timer, g_source_remove); - g_clear_handle_id(&js->conn_close_timeout, g_source_remove); - - g_cancellable_cancel(js->cancellable); - g_object_unref(js->cancellable); - - g_free(js); - - purple_connection_set_protocol_data(gc, NULL); -} - -void jabber_stream_set_state(JabberStream *js, JabberStreamState state) -{ - js->state = state; - if(state == JABBER_STREAM_INITIALIZING) { - jabber_stream_init(js); - } else if(state == JABBER_STREAM_CONNECTED) { - /* Send initial presence */ - jabber_presence_send(js, TRUE); - /* Start up the inactivity timer */ - jabber_stream_restart_inactivity_timer(js); - - purple_connection_set_state(js->gc, PURPLE_CONNECTION_STATE_CONNECTED); - } -} - -char *jabber_get_next_id(JabberStream *js) -{ - return g_strdup_printf("purple%x", js->next_id++); -} - - -static void -jabber_idle_set(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, gint idle) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - js->idle = idle ? time(NULL) - idle : idle; - - /* send out an updated prescence */ - purple_debug_info("jabber", "sending updated presence for idle\n"); - jabber_presence_send(js, FALSE); -} - -void -jabber_blocklist_parse_push(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *child) -{ -#if 0 - JabberIq *result; - PurpleXmlNode *item; - PurpleAccount *account; - gboolean is_block; - GSList *deny; - - if (!jabber_is_own_account(js, from)) { - PurpleXmlNode *error, *x; - result = jabber_iq_new(js, JABBER_IQ_ERROR); - purple_xmlnode_set_attrib(result->node, "id", id); - if (from) - purple_xmlnode_set_attrib(result->node, "to", from); - - error = purple_xmlnode_new_child(result->node, "error"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - x = purple_xmlnode_new_child(error, "not-allowed"); - purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); - - jabber_iq_send(result); - return; - } - - account = purple_connection_get_account(js->gc); - is_block = purple_strequal(child->name, "block"); - - item = purple_xmlnode_get_child(child, "item"); - if (!is_block && item == NULL) { - /* Unblock everyone */ - purple_debug_info("jabber", "Received unblock push. Unblocking everyone.\n"); - - while ((deny = purple_account_privacy_get_denied(account)) != NULL) { - purple_account_privacy_deny_remove(account, deny->data, TRUE); - } - } else if (item == NULL) { - /* An empty <block/> is bogus */ - PurpleXmlNode *error, *x; - result = jabber_iq_new(js, JABBER_IQ_ERROR); - purple_xmlnode_set_attrib(result->node, "id", id); - - error = purple_xmlnode_new_child(result->node, "error"); - purple_xmlnode_set_attrib(error, "type", "modify"); - x = purple_xmlnode_new_child(error, "bad-request"); - purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); - - jabber_iq_send(result); - return; - } else { - for ( ; item; item = purple_xmlnode_get_next_twin(item)) { - const char *jid = purple_xmlnode_get_attrib(item, "jid"); - if (jid == NULL || *jid == '\0') - continue; - - if (is_block) - purple_account_privacy_deny_add(account, jid, TRUE); - else - purple_account_privacy_deny_remove(account, jid, TRUE); - } - } - - result = jabber_iq_new(js, JABBER_IQ_RESULT); - purple_xmlnode_set_attrib(result->node, "id", id); - jabber_iq_send(result); -#endif -} - -#if 0 -static void jabber_blocklist_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *packet, gpointer data) -{ - PurpleXmlNode *blocklist, *item; - PurpleAccount *account; - GSList *deny; - - blocklist = purple_xmlnode_get_child_with_namespace(packet, - "blocklist", NS_SIMPLE_BLOCKING); - account = purple_connection_get_account(js->gc); - - if (type == JABBER_IQ_ERROR || blocklist == NULL) - return; - - /* This is the only privacy method supported by XEP-0191 */ - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS); - - /* - * TODO: When account->deny is something more than a hash table, this can - * be re-written to find the set intersection and difference. - */ - while ((deny = purple_account_privacy_get_denied(account))) - purple_account_privacy_deny_remove(account, deny->data, TRUE); - - item = purple_xmlnode_get_child(blocklist, "item"); - while (item != NULL) { - const char *jid = purple_xmlnode_get_attrib(item, "jid"); - purple_account_privacy_deny_add(account, jid, TRUE); - item = purple_xmlnode_get_next_twin(item); - } -} -#endif - -void -jabber_request_block_list(G_GNUC_UNUSED JabberStream *js) -{ -#if 0 - JabberIq *iq; - PurpleXmlNode *blocklist; - - iq = jabber_iq_new(js, JABBER_IQ_GET); - - blocklist = purple_xmlnode_new_child(iq->node, "blocklist"); - purple_xmlnode_set_namespace(blocklist, NS_SIMPLE_BLOCKING); - - jabber_iq_set_callback(iq, jabber_blocklist_parse, NULL); - - jabber_iq_send(iq); -#endif -} - -#if 0 -static void -jabber_add_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc, - const char *who) -{ - JabberStream *js; - JabberIq *iq; - PurpleXmlNode *block, *item; - - g_return_if_fail(who != NULL && *who != '\0'); - - js = purple_connection_get_protocol_data(gc); - if (js == NULL) - return; - - if (!(js->server_caps & JABBER_CAP_BLOCKING)) - { - purple_notify_error(NULL, _("Server doesn't support blocking"), - _("Server doesn't support blocking"), NULL, - purple_request_cpar_from_connection(gc)); - return; - } - - iq = jabber_iq_new(js, JABBER_IQ_SET); - - block = purple_xmlnode_new_child(iq->node, "block"); - purple_xmlnode_set_namespace(block, NS_SIMPLE_BLOCKING); - - item = purple_xmlnode_new_child(block, "item"); - purple_xmlnode_set_attrib(item, "jid", who); - - jabber_iq_send(iq); -} - -static void -jabber_remove_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc, - const char *who) -{ - JabberStream *js; - JabberIq *iq; - PurpleXmlNode *unblock, *item; - - g_return_if_fail(who != NULL && *who != '\0'); - - js = purple_connection_get_protocol_data(gc); - if (js == NULL) - return; - - if (!(js->server_caps & JABBER_CAP_BLOCKING)) - return; - - iq = jabber_iq_new(js, JABBER_IQ_SET); - - unblock = purple_xmlnode_new_child(iq->node, "unblock"); - purple_xmlnode_set_namespace(unblock, NS_SIMPLE_BLOCKING); - - item = purple_xmlnode_new_child(unblock, "item"); - purple_xmlnode_set_attrib(item, "jid", who); - - jabber_iq_send(iq); -} -#endif - -void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb) { - JabberFeature *feat; - - g_return_if_fail(namespace != NULL); - - feat = g_new0(JabberFeature,1); - feat->namespace = g_strdup(namespace); - feat->is_enabled = cb; - - /* try to remove just in case it already exists in the list */ - jabber_remove_feature(namespace); - - jabber_features = g_list_append(jabber_features, feat); -} - -static void jabber_feature_free(JabberFeature *feature) { - g_return_if_fail(feature != NULL); - - g_free(feature->namespace); - g_free(feature); -} - -static void -jabber_remove_feature(const char *namespace) { - GList *feature; - for(feature = jabber_features; feature; feature = feature->next) { - JabberFeature *feat = (JabberFeature*)feature->data; - if(purple_strequal(feat->namespace, namespace)) { - jabber_feature_free(feat); - jabber_features = g_list_delete_link(jabber_features, feature); - break; - } - } -} - -gint -jabber_identity_compare(gconstpointer a, gconstpointer b) -{ - const JabberIdentity *ac; - const JabberIdentity *bc; - gint cat_cmp; - gint typ_cmp; - - ac = a; - bc = b; - - cat_cmp = g_strcmp0(ac->category, bc->category); - if (cat_cmp != 0) { - return cat_cmp; - } - - typ_cmp = g_strcmp0(ac->type, bc->type); - if (typ_cmp != 0) { - return typ_cmp; - } - - return g_strcmp0(ac->lang, bc->lang); -} - -JabberIdentity *jabber_identity_new(const gchar *category, const gchar *type, - const gchar *lang, const gchar *name) -{ - JabberIdentity *id = g_new0(JabberIdentity, 1); - id->category = g_strdup(category); - id->type = g_strdup(type); - id->lang = g_strdup(lang); - id->name = g_strdup(name); - return id; -} - -void jabber_identity_free(JabberIdentity *id) -{ - g_return_if_fail(id != NULL); - - g_free(id->category); - g_free(id->type); - g_free(id->lang); - g_free(id->name); - g_free(id); -} - -/* - * jabber_add_identity: - * @category: The category of the identity. - * @type: The type of the identity. - * @language: (nullable): The language localization of the name. - * @name: The name of the identity. - * - * Adds an identity to this jabber library instance. For list of valid values - * visit the website of the XMPP Registrar - * (http://xmpp.org/registrar/disco-categories.html#client) - * - * Like with jabber_add_feature, if you call this while accounts are connected, - * Bad Things will happen. - */ -static void -jabber_add_identity(const gchar *category, const gchar *type, - const gchar *lang, const gchar *name) -{ - GList *identity; - JabberIdentity *ident; - - /* both required according to XEP-0030 */ - g_return_if_fail(category != NULL); - g_return_if_fail(type != NULL); - - ident = jabber_identity_new(category, type, lang, name); - - /* Check if this identity is already there... */ - identity = g_list_find_custom(jabber_identities, ident, jabber_identity_compare); - if (identity != NULL) { - jabber_identity_free(ident); - return; - } - - jabber_identities = g_list_insert_sorted(jabber_identities, ident, - jabber_identity_compare); -} - -void jabber_bytestreams_streamhost_free(JabberBytestreamsStreamhost *sh) -{ - g_return_if_fail(sh != NULL); - - g_free(sh->jid); - g_free(sh->host); - g_free(sh->zeroconf); - g_free(sh); -} - -gboolean jabber_stream_is_ssl(JabberStream *js) -{ - return (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) || - (!js->bosh && G_IS_TLS_CONNECTION(js->stream)); -} - -static gboolean -inactivity_cb(gpointer data) -{ - JabberStream *js = data; - - /* We want whatever is sent to set this. It's okay because - * the eventloop unsets it via the return FALSE. - */ - js->inactivity_timer = 0; - - if (js->bosh) { - jabber_bosh_connection_send_keepalive(js->bosh); - } else { - jabber_send_raw(NULL, js, "\t", 1); - } - - return FALSE; -} - -void jabber_stream_restart_inactivity_timer(JabberStream *js) -{ - g_clear_handle_id(&js->inactivity_timer, g_source_remove); - - g_return_if_fail(js->max_inactivity > 0); - - js->inactivity_timer = - g_timeout_add_seconds(js->max_inactivity, - inactivity_cb, js); -} - -static const char * -jabber_list_emblem(G_GNUC_UNUSED PurpleProtocolClient *client, PurpleBuddy *b) -{ - JabberStream *js; - JabberBuddy *jb = NULL; - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(b)); - - if(!gc) - return NULL; - - js = purple_connection_get_protocol_data(gc); - if(js) - jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE); - - if(!PURPLE_BUDDY_IS_ONLINE(b)) { - if(jb && (jb->subscription & JABBER_SUB_PENDING || - !(jb->subscription & JABBER_SUB_TO))) - return "not-authorized"; - } - - if (jb) { - JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, NULL); - if (jbr) { - const gchar *client_type = - jabber_resource_get_identity_category_type(jbr, "client"); - - if (client_type) { - if (purple_strequal(client_type, "phone")) { - return "mobile"; - } else if (purple_strequal(client_type, "web")) { - return "external"; - } else if (purple_strequal(client_type, "handheld")) { - return "hiptop"; - } else if (purple_strequal(client_type, "bot")) { - return "bot"; - } - /* the default value "pc" falls through and has no emblem */ - } - } - } - - return NULL; -} - -static GList * -jabber_status_types(G_GNUC_UNUSED PurpleProtocol *protocol, - G_GNUC_UNUSED PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - GValue *priority_value; - GValue *buzz_enabled; - - priority_value = purple_value_new(G_TYPE_INT); - g_value_set_int(priority_value, 1); - buzz_enabled = purple_value_new(G_TYPE_BOOLEAN); - g_value_set_boolean(buzz_enabled, TRUE); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE), - NULL, TRUE, TRUE, FALSE, - "priority", _("Priority"), priority_value, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - "nick", _("Nickname"), purple_value_new(G_TYPE_STRING), - "buzz", _("Allow Buzz"), buzz_enabled, - NULL); - types = g_list_prepend(types, type); - - priority_value = purple_value_new(G_TYPE_INT); - g_value_set_int(priority_value, 1); - buzz_enabled = purple_value_new(G_TYPE_BOOLEAN); - g_value_set_boolean(buzz_enabled, TRUE); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT), - _("Chatty"), TRUE, TRUE, FALSE, - "priority", _("Priority"), priority_value, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - "nick", _("Nickname"), purple_value_new(G_TYPE_STRING), - "buzz", _("Allow Buzz"), buzz_enabled, - NULL); - types = g_list_prepend(types, type); - - priority_value = purple_value_new(G_TYPE_INT); - g_value_set_int(priority_value, 0); - buzz_enabled = purple_value_new(G_TYPE_BOOLEAN); - g_value_set_boolean(buzz_enabled, TRUE); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY), - NULL, TRUE, TRUE, FALSE, - "priority", _("Priority"), priority_value, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - "nick", _("Nickname"), purple_value_new(G_TYPE_STRING), - "buzz", _("Allow Buzz"), buzz_enabled, - NULL); - types = g_list_prepend(types, type); - - priority_value = purple_value_new(G_TYPE_INT); - g_value_set_int(priority_value, 0); - buzz_enabled = purple_value_new(G_TYPE_BOOLEAN); - g_value_set_boolean(buzz_enabled, TRUE); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA), - NULL, TRUE, TRUE, FALSE, - "priority", _("Priority"), priority_value, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - "nick", _("Nickname"), purple_value_new(G_TYPE_STRING), - "buzz", _("Allow Buzz"), buzz_enabled, - NULL); - types = g_list_prepend(types, type); - - priority_value = purple_value_new(G_TYPE_INT); - g_value_set_int(priority_value, 0); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_DND), - _("Do Not Disturb"), TRUE, TRUE, FALSE, - "priority", _("Priority"), priority_value, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - "nick", _("Nickname"), purple_value_new(G_TYPE_STRING), - NULL); - types = g_list_prepend(types, type); - - /* - if(js->protocol_version == JABBER_PROTO_0_9) - "Invisible" - */ - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(G_TYPE_STRING), - NULL); - types = g_list_prepend(types, type); - - return g_list_reverse(types); -} - -static void -jabber_password_change_result_cb(JabberStream *js, - G_GNUC_UNUSED const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - if (type == JABBER_IQ_RESULT) { - PurpleAccount *account = purple_connection_get_account(js->gc); - PurpleCredentialManager *manager = NULL; - - purple_notify_info(js->gc, _("Password Changed"), _("Password " - "Changed"), _("Your password has been changed."), - purple_request_cpar_from_connection(js->gc)); - - manager = purple_credential_manager_get_default(); - purple_credential_manager_write_password_async(manager, account, - (const gchar *)data, - NULL, NULL, NULL); - } else { - char *msg = jabber_parse_error(js, packet, NULL); - - purple_notify_error(js->gc, _("Error changing password"), - _("Error changing password"), msg, - purple_request_cpar_from_connection(js->gc)); - g_free(msg); - } - - g_free(data); -} - -static void -jabber_password_change_cb(JabberStream *js, PurpleRequestPage *page) { - const char *p1, *p2; - JabberIq *iq; - PurpleXmlNode *query, *y; - - p1 = purple_request_page_get_string(page, "password1"); - p2 = purple_request_page_get_string(page, "password2"); - - if(!purple_strequal(p1, p2)) { - purple_notify_error(js->gc, NULL, - _("New passwords do not match."), NULL, - purple_request_cpar_from_connection(js->gc)); - return; - } - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); - - purple_xmlnode_set_attrib(iq->node, "to", js->user->domain); - - query = purple_xmlnode_get_child(iq->node, "query"); - - y = purple_xmlnode_new_child(query, "username"); - purple_xmlnode_insert_data(y, js->user->node, -1); - y = purple_xmlnode_new_child(query, "password"); - purple_xmlnode_insert_data(y, p1, -1); - - jabber_iq_set_callback(iq, jabber_password_change_result_cb, g_strdup(p1)); - - jabber_iq_send(iq); -} - -static void -jabber_password_change(G_GNUC_UNUSED GSimpleAction *action, GVariant *parameter, - G_GNUC_UNUSED gpointer data) -{ - const char *account_id = NULL; - PurpleAccountManager *manager = NULL; - PurpleAccount *account = NULL; - PurpleConnection *connection = NULL; - JabberStream *js = NULL; - PurpleRequestPage *page; - PurpleRequestGroup *group; - PurpleRequestField *field; - - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) { - g_critical("XMPP Change Password action parameter is of incorrect type %s", - g_variant_get_type_string(parameter)); - } - - account_id = g_variant_get_string(parameter, NULL); - manager = purple_account_manager_get_default(); - account = purple_account_manager_find_by_id(manager, account_id); - connection = purple_account_get_connection(account); - g_clear_object(&account); - js = purple_connection_get_protocol_data(connection); - - page = purple_request_page_new(); - group = purple_request_group_new(NULL); - purple_request_page_add_group(page, group); - - field = purple_request_field_string_new("password1", _("Password"), - "", FALSE); - purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field), - TRUE); - purple_request_field_set_required(field, TRUE); - purple_request_group_add_field(group, field); - - field = purple_request_field_string_new("password2", _("Password (again)"), - "", FALSE); - purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field), - TRUE); - purple_request_field_set_required(field, TRUE); - purple_request_group_add_field(group, field); - - purple_request_fields(connection, _("Change XMPP Password"), - _("Change XMPP Password"), - _("Please enter your new password"), - page, - _("OK"), G_CALLBACK(jabber_password_change_cb), - _("Cancel"), NULL, - purple_request_cpar_from_connection(connection), js); -} - -static const gchar * -xmpp_protocol_actions_get_prefix(G_GNUC_UNUSED PurpleProtocolActions *actions) -{ - return "prpl-xmpp"; -} - -static GActionGroup * -xmpp_protocol_actions_get_action_group(G_GNUC_UNUSED PurpleProtocolActions *actions, - PurpleConnection *connection) -{ - JabberStream *js = purple_connection_get_protocol_data(connection); - GSimpleActionGroup *group = NULL; - GActionEntry entries[] = { - { - .name = "set-user-info", - .activate = jabber_setup_set_info, - .parameter_type = "s", - }, - { - .name = "change-password", - .activate = jabber_password_change, - .parameter_type = "s", - }, - }; - gsize nentries = G_N_ELEMENTS(entries); - - group = g_simple_action_group_new(); - g_action_map_add_action_entries(G_ACTION_MAP(group), entries, nentries, - NULL); - - if(js->pep) { - jabber_pep_add_action_entries(group); - } - -#if 0 - if(js->commands) { - jabber_adhoc_add_server_action_entries(js, group); - } -#endif - - return G_ACTION_GROUP(group); -} - -static GMenu * -xmpp_protocol_actions_get_menu(G_GNUC_UNUSED PurpleProtocolActions *actions, - G_GNUC_UNUSED PurpleConnection *connection) -{ - GMenu *menu = NULL; - GMenuItem *item = NULL; - - menu = g_menu_new(); - - item = g_menu_item_new(_("Set User Info..."), "prpl-xmpp.set-user-info"); - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s", - "account"); - g_menu_append_item(menu, item); - g_object_unref(item); - - item = g_menu_item_new(_("Change Password..."), - "prpl-xmpp.change-password"); - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s", - "account"); - g_menu_append_item(menu, item); - g_object_unref(item); - - jabber_pep_append_menu(menu); - -#if 0 - if(js->commands) { - jabber_adhoc_append_server_menu(js, menu); - } -#endif - - return menu; -} - -static PurpleChat * -jabber_find_blist_chat(G_GNUC_UNUSED PurpleProtocolClient *client, - PurpleAccount *account, const char *name) -{ - PurpleBlistNode *gnode, *cnode; - JabberID *jid; - - if(!(jid = jabber_id_new(name))) - return NULL; - - for (gnode = purple_blist_get_default_root(); gnode; - gnode = purple_blist_node_get_sibling_next(gnode)) { - for(cnode = purple_blist_node_get_first_child(gnode); - cnode; - cnode = purple_blist_node_get_sibling_next(cnode)) { - PurpleChat *chat = (PurpleChat*)cnode; - const char *room, *server; - GHashTable *components; - if(!PURPLE_IS_CHAT(cnode)) - continue; - - if (purple_chat_get_account(chat) != account) - continue; - - components = purple_chat_get_components(chat); - if(!(room = g_hash_table_lookup(components, "room"))) - continue; - if(!(server = g_hash_table_lookup(components, "server"))) - continue; - - /* FIXME: Collate is wrong in a few cases here; this should be prepped */ - if(jid->node && jid->domain && - !g_utf8_collate(room, jid->node) && !g_utf8_collate(server, jid->domain)) { - jabber_id_free(jid); - return chat; - } - } - } - jabber_id_free(jid); - return NULL; -} - -static void -jabber_convo_closed(G_GNUC_UNUSED PurpleProtocolClient *client, - PurpleConnection *gc, const char *who) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr; - - if(!(jid = jabber_id_new(who))) - return; - - if((jb = jabber_buddy_find(js, who, TRUE)) && - (jbr = jabber_buddy_find_resource(jb, jid->resource))) { - g_free(jbr->thread_id); - jbr->thread_id = NULL; - } - - jabber_id_free(jid); -} - -static const gchar * -jabber_client_normalize(G_GNUC_UNUSED PurpleProtocolClient *client, - PurpleAccount *account, const char *who) -{ - return jabber_normalize(account, who); -} - -char *jabber_parse_error(JabberStream *js, - PurpleXmlNode *packet, - PurpleConnectionError *reason) -{ - PurpleXmlNode *error; - const char *code = NULL, *text = NULL; - const char *xmlns = purple_xmlnode_get_namespace(packet); - char *cdata = NULL; - -#define SET_REASON(x) \ - if(reason != NULL) { *reason = x; } - - if((error = purple_xmlnode_get_child(packet, "error"))) { - PurpleXmlNode *t = purple_xmlnode_get_child_with_namespace(error, "text", NS_XMPP_STANZAS); - if (t) - cdata = purple_xmlnode_get_data(t); - - code = purple_xmlnode_get_attrib(error, "code"); - - /* Stanza errors */ - if(purple_xmlnode_get_child(error, "bad-request")) { - text = _("Bad Request"); - } else if(purple_xmlnode_get_child(error, "conflict")) { - SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE); - text = _("Conflict"); - } else if(purple_xmlnode_get_child(error, "feature-not-implemented")) { - text = _("Feature Not Implemented"); - } else if(purple_xmlnode_get_child(error, "forbidden")) { - text = _("Forbidden"); - } else if(purple_xmlnode_get_child(error, "gone")) { - text = _("Gone"); - } else if(purple_xmlnode_get_child(error, "internal-server-error")) { - text = _("Internal Server Error"); - } else if(purple_xmlnode_get_child(error, "item-not-found")) { - text = _("Item Not Found"); - } else if(purple_xmlnode_get_child(error, "jid-malformed")) { - text = _("Malformed XMPP ID"); - } else if(purple_xmlnode_get_child(error, "not-acceptable")) { - text = _("Not Acceptable"); - } else if(purple_xmlnode_get_child(error, "not-allowed")) { - text = _("Not Allowed"); - } else if(purple_xmlnode_get_child(error, "not-authorized")) { - text = _("Not Authorized"); - } else if(purple_xmlnode_get_child(error, "payment-required")) { - text = _("Payment Required"); - } else if(purple_xmlnode_get_child(error, "recipient-unavailable")) { - text = _("Recipient Unavailable"); - } else if(purple_xmlnode_get_child(error, "redirect")) { - /* XXX */ - } else if(purple_xmlnode_get_child(error, "registration-required")) { - text = _("Registration Required"); - } else if(purple_xmlnode_get_child(error, "remote-server-not-found")) { - text = _("Remote Server Not Found"); - } else if(purple_xmlnode_get_child(error, "remote-server-timeout")) { - text = _("Remote Server Timeout"); - } else if(purple_xmlnode_get_child(error, "resource-constraint")) { - text = _("Server Overloaded"); - } else if(purple_xmlnode_get_child(error, "service-unavailable")) { - text = _("Service Unavailable"); - } else if(purple_xmlnode_get_child(error, "subscription-required")) { - text = _("Subscription Required"); - } else if(purple_xmlnode_get_child(error, "unexpected-request")) { - text = _("Unexpected Request"); - } else if(purple_xmlnode_get_child(error, "undefined-condition")) { - text = _("Unknown Error"); - } - } else if(purple_strequal(xmlns, NS_XMPP_SASL)) { - /* Most common reason can be the default */ - SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR); - if(purple_xmlnode_get_child(packet, "aborted")) { - text = _("Authorization Aborted"); - } else if(purple_xmlnode_get_child(packet, "incorrect-encoding")) { - text = _("Incorrect encoding in authorization"); - } else if(purple_xmlnode_get_child(packet, "invalid-authzid")) { - text = _("Invalid authzid"); - } else if(purple_xmlnode_get_child(packet, "invalid-mechanism")) { - text = _("Invalid Authorization Mechanism"); - } else if(purple_xmlnode_get_child(packet, "mechanism-too-weak")) { - SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE); - text = _("Authorization mechanism too weak"); - } else if(purple_xmlnode_get_child(packet, "not-authorized")) { - SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED); - /* Clear the password if it isn't being saved */ - if (!purple_account_get_remember_password(purple_connection_get_account(js->gc))) { - PurpleAccount *account = purple_connection_get_account(js->gc); - PurpleCredentialManager *manager = NULL; - - manager = purple_credential_manager_get_default(); - purple_credential_manager_clear_password_async(manager, account, - NULL, NULL, - NULL); - } - text = _("Not Authorized"); - } else if(purple_xmlnode_get_child(packet, "temporary-auth-failure")) { - text = _("Temporary Authentication Failure"); - } else { - SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED); - text = _("Authentication Failure"); - } - } else if(purple_strequal(packet->name, "stream:error") || - (purple_strequal(packet->name, "error") && - purple_strequal(xmlns, NS_XMPP_STREAMS))) { - /* Most common reason as default: */ - SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR); - if(purple_xmlnode_get_child(packet, "bad-format")) { - text = _("Bad Format"); - } else if(purple_xmlnode_get_child(packet, "bad-namespace-prefix")) { - text = _("Bad Namespace Prefix"); - } else if(purple_xmlnode_get_child(packet, "conflict")) { - SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE); - text = _("Resource Conflict"); - } else if(purple_xmlnode_get_child(packet, "connection-timeout")) { - text = _("Connection Timeout"); - } else if(purple_xmlnode_get_child(packet, "host-gone")) { - text = _("Host Gone"); - } else if(purple_xmlnode_get_child(packet, "host-unknown")) { - text = _("Host Unknown"); - } else if(purple_xmlnode_get_child(packet, "improper-addressing")) { - text = _("Improper Addressing"); - } else if(purple_xmlnode_get_child(packet, "internal-server-error")) { - text = _("Internal Server Error"); - } else if(purple_xmlnode_get_child(packet, "invalid-id")) { - text = _("Invalid ID"); - } else if(purple_xmlnode_get_child(packet, "invalid-namespace")) { - text = _("Invalid Namespace"); - } else if(purple_xmlnode_get_child(packet, "invalid-xml")) { - text = _("Invalid XML"); - } else if(purple_xmlnode_get_child(packet, "nonmatching-hosts")) { - text = _("Non-matching Hosts"); - } else if(purple_xmlnode_get_child(packet, "not-authorized")) { - text = _("Not Authorized"); - } else if(purple_xmlnode_get_child(packet, "policy-violation")) { - text = _("Policy Violation"); - } else if(purple_xmlnode_get_child(packet, "remote-connection-failed")) { - text = _("Remote Connection Failed"); - } else if(purple_xmlnode_get_child(packet, "resource-constraint")) { - text = _("Resource Constraint"); - } else if(purple_xmlnode_get_child(packet, "restricted-xml")) { - text = _("Restricted XML"); - } else if(purple_xmlnode_get_child(packet, "see-other-host")) { - text = _("See Other Host"); - } else if(purple_xmlnode_get_child(packet, "system-shutdown")) { - text = _("System Shutdown"); - } else if(purple_xmlnode_get_child(packet, "undefined-condition")) { - text = _("Undefined Condition"); - } else if(purple_xmlnode_get_child(packet, "unsupported-encoding")) { - text = _("Unsupported Encoding"); - } else if(purple_xmlnode_get_child(packet, "unsupported-stanza-type")) { - text = _("Unsupported Stanza Type"); - } else if(purple_xmlnode_get_child(packet, "unsupported-version")) { - text = _("Unsupported Version"); - } else if(purple_xmlnode_get_child(packet, "xml-not-well-formed")) { - text = _("XML Not Well Formed"); - } else { - text = _("Stream Error"); - } - } - -#undef SET_REASON - - if(text || cdata) { - char *ret = g_strdup_printf("%s%s%s", code ? code : "", - code ? ": " : "", text ? text : cdata); - g_free(cdata); - return ret; - } else { - return NULL; - } -} - -static PurpleCmdRet -jabber_cmd_chat_config(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - G_GNUC_UNUSED char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat) - return PURPLE_CMD_RET_FAILED; - - jabber_chat_request_room_configure(chat); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_register(PurpleConversation *conv, - G_GNUC_UNUSED const char *cmd, - G_GNUC_UNUSED char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat) - return PURPLE_CMD_RET_FAILED; - - jabber_chat_register(chat); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_topic(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat) - return PURPLE_CMD_RET_FAILED; - - if (args && args[0] && *args[0]) - jabber_chat_change_topic(chat, args[0]); - else { - const char *cur = purple_chat_conversation_get_topic(PURPLE_CHAT_CONVERSATION(conv)); - char *buf, *tmp, *tmp2; - - if (cur) { - tmp = g_markup_escape_text(cur, -1); - tmp2 = purple_markup_linkify(tmp); - buf = g_strdup_printf(_("current topic is: %s"), tmp2); - g_free(tmp); - g_free(tmp2); - } else - buf = g_strdup(_("No topic is set")); - purple_conversation_write_system_message(conv, buf, PURPLE_MESSAGE_NO_LOG); - g_free(buf); - } - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_nick(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if(!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - if (!jabber_resourceprep_validate(args[0])) { - *error = g_strdup(_("Invalid nickname")); - return PURPLE_CMD_RET_FAILED; - } - - if (jabber_chat_change_nick(chat, args[0])) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet -jabber_cmd_chat_part(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat) - return PURPLE_CMD_RET_FAILED; - - jabber_chat_part(chat, args ? args[0] : NULL); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_ban(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if(!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - if(!jabber_chat_ban_user(chat, args[0], args[1])) { - *error = g_strdup_printf(_("Unable to ban user %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_affiliate(PurpleConversation *conv, - G_GNUC_UNUSED const char *cmd, char **args, - char **error, G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - if (!purple_strequal(args[0], "owner") && - !purple_strequal(args[0], "admin") && - !purple_strequal(args[0], "member") && - !purple_strequal(args[0], "outcast") && - !purple_strequal(args[0], "none")) { - *error = g_strdup_printf(_("Unknown affiliation: \"%s\""), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - if (args[1]) { - int i; - char **nicks = g_strsplit(args[1], " ", -1); - - for (i = 0; nicks[i]; ++i) - if (!jabber_chat_affiliate_user(chat, nicks[i], args[0])) { - *error = g_strdup_printf(_("Unable to affiliate user %s as \"%s\""), nicks[i], args[0]); - g_strfreev(nicks); - return PURPLE_CMD_RET_FAILED; - } - - g_strfreev(nicks); - } else { - jabber_chat_affiliation_list(chat, args[0]); - } - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_role(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if (!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - if (!purple_strequal(args[0], "moderator") && - !purple_strequal(args[0], "participant") && - !purple_strequal(args[0], "visitor") && - !purple_strequal(args[0], "none")) { - *error = g_strdup_printf(_("Unknown role: \"%s\""), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - if (args[1]) { - int i; - char **nicks = g_strsplit(args[1], " ", -1); - - for (i = 0; nicks[i]; i++) - if (!jabber_chat_role_user(chat, nicks[i], args[0], NULL)) { - *error = g_strdup_printf(_("Unable to set role \"%s\" for user: %s"), - args[0], nicks[i]); - g_strfreev(nicks); - return PURPLE_CMD_RET_FAILED; - } - - g_strfreev(nicks); - } else { - jabber_chat_role_list(chat, args[0]); - } - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_invite(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED gpointer data) -{ - if(!args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - jabber_chat_invite(purple_conversation_get_connection(conv), - purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv)), args[1] ? args[1] : "", - args[0]); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_join(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED gpointer data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - GHashTable *components; - JabberID *jid = NULL; - const char *room = NULL, *server = NULL, *handle = NULL; - - if (!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - - if (strchr(args[0], '@')) - jid = jabber_id_new(args[0]); - if (jid) { - room = jid->node; - server = jid->domain; - handle = jid->resource ? jid->resource : chat->handle; - } else { - /* If jabber_id_new failed, the user may have just passed in - * a room name. For backward compatibility, handle that here. - */ - if (strchr(args[0], '@')) { - *error = g_strdup(_("Invalid XMPP ID")); - return PURPLE_CMD_RET_FAILED; - } - - room = args[0]; - server = chat->server; - handle = chat->handle; - } - - g_hash_table_insert(components, "room", (gpointer)room); - g_hash_table_insert(components, "server", (gpointer)server); - g_hash_table_insert(components, "handle", (gpointer)handle); - - if (args[1]) - g_hash_table_insert(components, "password", args[1]); - - jabber_chat_join(purple_conversation_get_connection(conv), components); - - g_hash_table_destroy(components); - jabber_id_free(jid); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_kick(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED void *data) -{ - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - - if(!chat || !args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - if(!jabber_chat_role_user(chat, args[0], "none", args[1])) { - *error = g_strdup_printf(_("Unable to kick user %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_chat_msg(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, G_GNUC_UNUSED char **error, - G_GNUC_UNUSED void *data) -{ - PurpleAccount *account = NULL; - PurpleConnection *pc = NULL; - PurpleProtocol *prpl = NULL; - PurpleMessage *msg = NULL; - JabberChat *chat = jabber_chat_find_by_conv(PURPLE_CHAT_CONVERSATION(conv)); - char *who; - const gchar *me = NULL; - - if (!chat) - return PURPLE_CMD_RET_FAILED; - - account = purple_connection_get_account(pc); - me = purple_contact_info_get_name_for_display(PURPLE_CONTACT_INFO(account)); - - who = g_strdup_printf("%s@%s/%s", chat->room, chat->server, args[0]); - pc = purple_conversation_get_connection(conv); - prpl = purple_connection_get_protocol(pc); - - msg = purple_message_new_outgoing(me, who, args[1], 0); - - jabber_message_send_im(PURPLE_PROTOCOL_IM(prpl), pc, NULL, msg); - - g_free(who); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet -jabber_cmd_ping(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd, - char **args, char **error, G_GNUC_UNUSED void *data) -{ - PurpleAccount *account; - PurpleConnection *pc; - - if(!args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - account = purple_conversation_get_account(conv); - pc = purple_account_get_connection(account); - - if(!jabber_ping_jid(purple_connection_get_protocol_data(pc), args[0])) { - *error = g_strdup_printf(_("Unable to ping user %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - return PURPLE_CMD_RET_OK; -} - -static gboolean -jabber_offline_message(G_GNUC_UNUSED PurpleProtocolClient *client, - G_GNUC_UNUSED PurpleBuddy *buddy) -{ - return TRUE; -} - -static gboolean -jabber_audio_enabled(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *namespace) -{ - PurpleMediaManager *manager = purple_media_manager_get(); - PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); - - return (caps & (PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION)); -} - -static gboolean -jabber_video_enabled(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *namespace) -{ - PurpleMediaManager *manager = purple_media_manager_get(); - PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); - - return (caps & (PURPLE_MEDIA_CAPS_VIDEO | PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION)); -} - -typedef struct { - PurpleProtocolMedia *media; - PurpleAccount *account; - gchar *who; - PurpleMediaSessionType type; - -} JabberMediaRequest; - -static void -jabber_media_cancel_cb(JabberMediaRequest *request, - G_GNUC_UNUSED PurpleRequestPage *page) -{ - g_free(request->who); - g_free(request); -} - -static void -jabber_media_ok_cb(JabberMediaRequest *request, PurpleRequestPage *page) { - const gchar *selected = purple_request_page_get_choice(page, "resource"); - gchar *who = g_strdup_printf("%s/%s", request->who, selected); - jabber_initiate_media(request->media, request->account, who, request->type); - - g_free(who); - g_free(request->who); - g_free(request); -} - -static gboolean -jabber_initiate_media(PurpleProtocolMedia *media, PurpleAccount *account, - const gchar *who, PurpleMediaSessionType type) -{ - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - char *resource = NULL; - - if (!js) { - purple_debug_error("jabber", - "jabber_initiate_media: NULL stream\n"); - return FALSE; - } - - jb = jabber_buddy_find(js, who, FALSE); - - if(!jb || !jb->resources || - (((resource = jabber_get_resource(who)) != NULL) - && (jbr = jabber_buddy_find_resource(jb, resource)) == NULL)) { - /* no resources online, we're trying to initiate with someone - * whose presence we're not subscribed to, or - * someone who is offline. Let's inform the user */ - char *msg; - - if(!jb) { - msg = g_strdup_printf(_("Unable to initiate media with %s: invalid JID"), who); - } else if(jb->subscription & JABBER_SUB_TO && !jb->resources) { - msg = g_strdup_printf(_("Unable to initiate media with %s: user is not online"), who); - } else if(resource) { - msg = g_strdup_printf(_("Unable to initiate media with %s: resource is not online"), who); - } else { - msg = g_strdup_printf(_("Unable to initiate media with %s: not subscribed to user presence"), who); - } - - purple_notify_error(account, _("Media Initiation Failed"), - _("Media Initiation Failed"), msg, - purple_request_cpar_from_connection(gc)); - g_free(msg); - g_free(resource); - return FALSE; - } else if(jbr != NULL) { - /* they've specified a resource, no need to ask or - * default or anything, just do it */ - - g_free(resource); - - return jingle_rtp_initiate_media(js, who, type); - } else if(!jb->resources->next) { - /* only 1 resource online (probably our most common case) - * so no need to ask who to initiate with */ - gchar *name; - gboolean result; - jbr = jb->resources->data; - name = g_strdup_printf("%s/%s", who, jbr->name); - result = jabber_initiate_media(media, account, name, type); - g_free(name); - return result; - } else { - /* we've got multiple resources, - * we need to pick one to initiate with */ - GList *l; - char *msg; - PurpleRequestPage *page = NULL; - PurpleRequestField *field = NULL; - PurpleRequestFieldChoice *choice = NULL; - PurpleRequestGroup *group = NULL; - JabberMediaRequest *request; - - field = purple_request_field_choice_new("resource", _("Resource"), 0); - choice = PURPLE_REQUEST_FIELD_CHOICE(field); - for(l = jb->resources; l; l = l->next) - { - JabberBuddyResource *ljbr = l->data; - PurpleMediaCaps caps; - gchar *name; - name = g_strdup_printf("%s/%s", who, ljbr->name); - caps = jabber_get_media_caps(media, account, name); - g_free(name); - - if ((type & PURPLE_MEDIA_AUDIO) && - (type & PURPLE_MEDIA_VIDEO)) { - if (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) { - jbr = ljbr; - purple_request_field_choice_add_full(choice, jbr->name, - g_strdup(jbr->name), - g_free); - } - } else if (type & (PURPLE_MEDIA_AUDIO) && - (caps & PURPLE_MEDIA_CAPS_AUDIO)) { - jbr = ljbr; - purple_request_field_choice_add_full(choice, jbr->name, - g_strdup(jbr->name), - g_free); - }else if (type & (PURPLE_MEDIA_VIDEO) && - (caps & PURPLE_MEDIA_CAPS_VIDEO)) { - jbr = ljbr; - purple_request_field_choice_add_full(choice, jbr->name, - g_strdup(jbr->name), - g_free); - } - } - - if (jbr == NULL) { - purple_debug_error("jabber", - "No resources available\n"); - return FALSE; - } - - if(g_list_length(purple_request_field_choice_get_elements(choice)) <= 1) { - gchar *name; - gboolean result; - g_object_unref(field); - name = g_strdup_printf("%s/%s", who, jbr->name); - result = jabber_initiate_media(media, account, name, type); - g_free(name); - return result; - } - - msg = g_strdup_printf(_("Please select the resource of %s with which you would like to start a media session."), who); - page = purple_request_page_new(); - group = purple_request_group_new(NULL); - request = g_new0(JabberMediaRequest, 1); - request->media = media; - request->account = account; - request->who = g_strdup(who); - request->type = type; - - purple_request_group_add_field(group, field); - purple_request_page_add_group(page, group); - purple_request_fields(account, _("Select a Resource"), msg, - NULL, page, _("Initiate Media"), - G_CALLBACK(jabber_media_ok_cb), _("Cancel"), - G_CALLBACK(jabber_media_cancel_cb), - purple_request_cpar_from_account(account), - request); - - g_free(msg); - return TRUE; - } - - return FALSE; -} - -static PurpleMediaCaps -jabber_get_media_caps(G_GNUC_UNUSED PurpleProtocolMedia *media, - PurpleAccount *account, const char *who) -{ - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = purple_connection_get_protocol_data(gc); - JabberBuddy *jb; - JabberBuddyResource *jbr; - PurpleMediaCaps total = PURPLE_MEDIA_CAPS_NONE; - gchar *resource; - GList *specific = NULL, *l; - - if (!js) { - purple_debug_info("jabber", - "jabber_can_do_media: NULL stream\n"); - return FALSE; - } - - jb = jabber_buddy_find(js, who, FALSE); - - if (!jb || !jb->resources) { - /* no resources online, we're trying to get caps for someone - * whose presence we're not subscribed to, or - * someone who is offline. */ - return total; - - } else if ((resource = jabber_get_resource(who)) != NULL) { - /* they've specified a resource, no need to ask or - * default or anything, just do it */ - jbr = jabber_buddy_find_resource(jb, resource); - g_free(resource); - - if (!jbr) { - purple_debug_error("jabber", "jabber_get_media_caps:" - " Can't find resource %s\n", who); - return total; - } - - l = specific = g_list_prepend(specific, jbr); - - } else { - /* we've got multiple resources, combine their caps */ - l = jb->resources; - } - - for (; l; l = l->next) { - PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE; - jbr = l->data; - - if (jabber_resource_has_capability(jbr, - JINGLE_APP_RTP_SUPPORT_AUDIO)) - caps |= PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION | - PURPLE_MEDIA_CAPS_AUDIO; - if (jabber_resource_has_capability(jbr, - JINGLE_APP_RTP_SUPPORT_VIDEO)) - caps |= PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION | - PURPLE_MEDIA_CAPS_VIDEO; - if (caps & PURPLE_MEDIA_CAPS_AUDIO && caps & - PURPLE_MEDIA_CAPS_VIDEO) - caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO; - if (caps != PURPLE_MEDIA_CAPS_NONE) { - if (!jabber_resource_has_capability(jbr, - JINGLE_TRANSPORT_ICEUDP) && - !jabber_resource_has_capability(jbr, - JINGLE_TRANSPORT_RAWUDP)) { - purple_debug_info("jingle-rtp", "Buddy doesn't " - "support the same transport types\n"); - caps = PURPLE_MEDIA_CAPS_NONE; - } else - caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION | - PURPLE_MEDIA_CAPS_CHANGE_DIRECTION; - } - - total |= caps; - } - - g_clear_list(&specific, NULL); - - return total; -} - -static gboolean -jabber_can_receive_file(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer, - PurpleConnection *gc, const char *who) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - if (js) { - JabberBuddy *jb = jabber_buddy_find(js, who, FALSE); - GList *iter; - gboolean has_resources_without_caps = FALSE; - - /* if we didn't find a JabberBuddy, we don't have presence for this - buddy, let's assume they can receive files, disco should tell us - when actually trying */ - if (jb == NULL) - return TRUE; - - /* find out if there is any resources without caps */ - for (iter = jb->resources; iter ; iter = g_list_next(iter)) { - JabberBuddyResource *jbr = (JabberBuddyResource *) iter->data; - - if (!jabber_resource_know_capabilities(jbr)) { - has_resources_without_caps = TRUE; - } - } - - if (has_resources_without_caps) { - /* there is at least one resource which we don't have caps for, - let's assume they can receive files... */ - return TRUE; - } else { - /* we have caps for all the resources, see if at least one has - right caps */ - for (iter = jb->resources; iter ; iter = g_list_next(iter)) { - JabberBuddyResource *jbr = (JabberBuddyResource *) iter->data; - - if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER) - && (jabber_resource_has_capability(jbr, - NS_BYTESTREAMS) - || jabber_resource_has_capability(jbr, NS_IBB))) { - return TRUE; - } - } - return FALSE; - } - } else { - return TRUE; - } -} - -static void -jabber_register_commands(PurpleProtocol *protocol) -{ - GSList *commands = NULL; - PurpleCmdId id; - const gchar *proto_id = purple_protocol_get_id(protocol); - - id = purple_cmd_register("config", "", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, - jabber_cmd_chat_config, _("config: Configure a chat room."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("configure", "", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, - jabber_cmd_chat_config, _("configure: Configure a chat room."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, - jabber_cmd_chat_nick, _("nick <new nickname>: " - "Change your nickname."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("part", "s", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_part, - _("part [message]: Leave the room."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("register", "", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, - jabber_cmd_chat_register, - _("register: Register with a chat room."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - /* XXX: there needs to be a core /topic cmd, methinks */ - id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_topic, - _("topic [new topic]: View or change the topic."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_ban, - _("ban <user> [reason]: Ban a user from the room."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, - jabber_cmd_chat_affiliate, _("affiliate " - "<owner|admin|member|outcast|none> [nick1] [nick2] ...: " - "Get the users with an affiliation or set users' affiliation " - "with the room."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_role, - _("role <moderator|participant|visitor|none> [nick1] " - "[nick2] ...: Get the users with a role or set users' role " - "with the room."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_invite, - _("invite <user> [message]: Invite a user to the room."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_join, - _("join: <room[@server]> [password]: Join a chat."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, proto_id, jabber_cmd_chat_kick, - _("kick <user> [reason]: Kick a user from the room."), - NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, - jabber_cmd_chat_msg, _("msg <user> <message>: " - "Send a private message to another user."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PROTOCOL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | - PURPLE_CMD_FLAG_PROTOCOL_ONLY, proto_id, jabber_cmd_ping, - _("ping <jid>: Ping a user/component/server."), NULL); - commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); - - g_hash_table_insert(jabber_cmds, protocol, commands); -} - -static void cmds_free_func(gpointer value) -{ - GSList *commands = value; - g_slist_free_full(commands, - (GDestroyNotify)(GCallback)purple_cmd_unregister); -} - -static void jabber_unregister_commands(PurpleProtocol *protocol) -{ - g_hash_table_remove(jabber_cmds, protocol); -} - -static gboolean -find_acct_cb(PurpleAccount *account, const gchar *protocol) { - const gchar *account_protocol_id = NULL; - - account_protocol_id = purple_account_get_protocol_id(account); - return (purple_strequal(protocol, account_protocol_id) && - purple_account_is_connected(account)); -} - -static PurpleAccount *find_acct(const char *protocol, const char *acct_id) -{ - PurpleAccountManager *manager = NULL; - PurpleAccount *acct = NULL; - - manager = purple_account_manager_get_default(); - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_account_manager_find(manager, acct_id, protocol); - if (acct && !purple_account_is_connected(acct)) { - g_clear_object(&acct); - } - } else { /* Otherwise find an active account for the protocol */ - acct = purple_account_manager_find_custom(manager, - (GEqualFunc)find_acct_cb, - protocol); - } - - return acct; -} - -static gboolean -xmpp_uri_handler(const char *proto, const char *user, GHashTable *params, - gpointer user_data) -{ - PurpleProtocol *protocol = (PurpleProtocol *)user_data; - const gchar *acct_id = NULL; - PurpleAccount *acct; - - g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE); - - if (g_ascii_strcasecmp(proto, "xmpp")) - return FALSE; - - if (params != NULL) { - acct_id = g_hash_table_lookup(params, "account"); - } - - acct = find_acct(XMPP_PROTOCOL_ID, acct_id); - - if (!acct) - return FALSE; - - /* xmpp:romeo@montague.net?message;subject=Test%20Message;body=Here%27s%20a%20test%20message */ - /* params is NULL if the URI has no '?' (or anything after it) */ - if (!params || g_hash_table_lookup_extended(params, "message", NULL, NULL)) { - if (user && *user) { - PurpleConversation *im = purple_im_conversation_new(acct, user); - const gchar *body = NULL; - - purple_conversation_present(im); - - if(params != NULL) { - body = g_hash_table_lookup(params, "body"); - } - - if(body && *body) { - purple_conversation_send_confirm(im, body); - } - - g_clear_object(&acct); - - return TRUE; - } - } else if (g_hash_table_lookup_extended(params, "roster", NULL, NULL)) { - char *name = g_hash_table_lookup(params, "name"); - if (user && *user) { - purple_blist_request_add_buddy(acct, user, NULL, name); - g_clear_object(&acct); - return TRUE; - } - } else if (g_hash_table_lookup_extended(params, "join", NULL, NULL)) { - PurpleConnection *gc = purple_account_get_connection(acct); - if (user && *user) { - GHashTable *params = jabber_chat_info_defaults(gc, user); - jabber_chat_join(gc, params); - } - g_clear_object(&acct); - return TRUE; - } - - g_clear_object(&acct); - - return FALSE; -} - -static void -jabber_do_init(void) -{ - PurpleUi *ui = purple_core_get_ui(); - const gchar *ui_type; - const gchar *type = "pc"; /* default client type, if unknown or - unspecified */ - const gchar *ui_name = NULL; - - jabber_cmds = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cmds_free_func); - - ui_type = ui ? purple_ui_get_client_type(ui) : NULL; - if (ui_type) { - if (purple_strequal(ui_type, "pc") || - purple_strequal(ui_type, "console") || - purple_strequal(ui_type, "phone") || - purple_strequal(ui_type, "handheld") || - purple_strequal(ui_type, "web") || - purple_strequal(ui_type, "bot")) { - type = ui_type; - } - } - - if (ui) - ui_name = purple_ui_get_name(ui); - if (ui_name == NULL) - ui_name = GETTEXT_PACKAGE; - - jabber_add_identity("client", type, NULL, ui_name); - - /* initialize jabber_features list */ - jabber_add_feature(NS_LAST_ACTIVITY, NULL); - jabber_add_feature(NS_OOB_IQ_DATA, NULL); - jabber_add_feature(NS_ENTITY_TIME, NULL); - jabber_add_feature("jabber:iq:version", NULL); - jabber_add_feature("jabber:x:conference", NULL); - jabber_add_feature(NS_BYTESTREAMS, NULL); - jabber_add_feature("http://jabber.org/protocol/caps", NULL); - jabber_add_feature("http://jabber.org/protocol/chatstates", NULL); - jabber_add_feature(NS_DISCO_INFO, NULL); - jabber_add_feature(NS_DISCO_ITEMS, NULL); - jabber_add_feature(NS_IBB, NULL); - jabber_add_feature("http://jabber.org/protocol/muc", NULL); - jabber_add_feature("http://jabber.org/protocol/muc#user", NULL); - jabber_add_feature("http://jabber.org/protocol/si", NULL); - jabber_add_feature(NS_SI_FILE_TRANSFER, NULL); - jabber_add_feature(NS_XHTML_IM, NULL); - jabber_add_feature(NS_PING, NULL); - - /* Bits Of Binary */ - jabber_add_feature(NS_BOB, NULL); - - /* Jingle features! */ - jabber_add_feature(JINGLE, NULL); - - jabber_add_feature(JINGLE_APP_RTP, NULL); - jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO, jabber_audio_enabled); - jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, jabber_video_enabled); - jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, NULL); - jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, NULL); - - g_signal_connect(G_OBJECT(purple_media_manager_get()), "ui-caps-changed", - G_CALLBACK(jabber_caps_broadcast_change), NULL); - - /* reverse order of unload_plugin */ - jabber_iq_init(); - jabber_presence_init(); - jabber_caps_init(); - /* PEP things should be init via jabber_pep_init, not here */ - jabber_pep_init(); - jabber_data_init(); - jabber_bosh_init(); - - /* TODO: Implement adding and retrieving own features via IPC API */ - - jabber_ibb_init(); - jabber_si_init(); - - jabber_auth_init(); -} - -static void -jabber_do_uninit(void) -{ - /* reverse order of jabber_do_init */ - jabber_bosh_uninit(); - jabber_data_uninit(); - jabber_si_uninit(); - jabber_ibb_uninit(); - /* PEP things should be uninit via jabber_pep_uninit, not here */ - jabber_pep_uninit(); - jabber_caps_uninit(); - jabber_presence_uninit(); - jabber_iq_uninit(); - - g_signal_handlers_disconnect_by_func(purple_media_manager_get(), - jabber_caps_broadcast_change, NULL); - - jabber_auth_uninit(); - g_clear_list(&jabber_features, (GDestroyNotify)jabber_feature_free); - g_clear_list(&jabber_identities, (GDestroyNotify)jabber_identity_free); - - g_clear_pointer(&jabber_cmds, g_hash_table_destroy); -} - -static void jabber_init_protocol(PurpleProtocol *protocol) -{ - ++plugin_ref; - - if (plugin_ref == 1) - jabber_do_init(); - - jabber_register_commands(protocol); - - purple_signal_register(protocol, "jabber-register-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - G_TYPE_NONE, 2, - G_TYPE_STRING, /* node */ - G_TYPE_STRING); /* namespace */ - - purple_signal_register(protocol, "jabber-unregister-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - G_TYPE_NONE, 2, - G_TYPE_STRING, /* node */ - G_TYPE_STRING); /* namespace */ - - purple_signal_connect(protocol, "jabber-register-namespace-watcher", - protocol, G_CALLBACK(jabber_iq_signal_register), NULL); - purple_signal_connect(protocol, "jabber-unregister-namespace-watcher", - protocol, G_CALLBACK(jabber_iq_signal_unregister), NULL); - - - purple_signal_register(protocol, "jabber-receiving-xmlnode", - purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, - PURPLE_TYPE_CONNECTION, - G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */ - - purple_signal_register(protocol, "jabber-sending-xmlnode", - purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, - PURPLE_TYPE_CONNECTION, - G_TYPE_POINTER); /* pointer to a PurpleXmlNode* */ - - /* - * Do not remove this or the plugin will fail. Completely. You have been - * warned! - */ - purple_signal_connect_priority(protocol, "jabber-sending-xmlnode", - protocol, G_CALLBACK(jabber_send_signal_cb), - NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); - - purple_signal_register(protocol, "jabber-sending-text", - purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, - PURPLE_TYPE_CONNECTION, - G_TYPE_POINTER); /* pointer to a string */ - - purple_signal_register(protocol, "jabber-receiving-message", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, - G_TYPE_BOOLEAN, 6, - PURPLE_TYPE_CONNECTION, - G_TYPE_STRING, /* type */ - G_TYPE_STRING, /* id */ - G_TYPE_STRING, /* from */ - G_TYPE_STRING, /* to */ - PURPLE_TYPE_XMLNODE); - - purple_signal_register(protocol, "jabber-receiving-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - G_TYPE_BOOLEAN, 5, - PURPLE_TYPE_CONNECTION, - G_TYPE_STRING, /* type */ - G_TYPE_STRING, /* id */ - G_TYPE_STRING, /* from */ - PURPLE_TYPE_XMLNODE); - - purple_signal_register(protocol, "jabber-watched-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - G_TYPE_BOOLEAN, 5, - PURPLE_TYPE_CONNECTION, - G_TYPE_STRING, /* type */ - G_TYPE_STRING, /* id */ - G_TYPE_STRING, /* from */ - PURPLE_TYPE_XMLNODE); /* child */ - - purple_signal_register(protocol, "jabber-receiving-presence", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, - G_TYPE_BOOLEAN, 4, - PURPLE_TYPE_CONNECTION, - G_TYPE_STRING, /* type */ - G_TYPE_STRING, /* from */ - PURPLE_TYPE_XMLNODE); -} - -static void jabber_uninit_protocol(PurpleProtocol *protocol) -{ - g_return_if_fail(plugin_ref > 0); - - purple_signals_unregister_by_instance(protocol); - jabber_unregister_commands(protocol); - - --plugin_ref; - if (plugin_ref == 0) - jabber_do_uninit(); -} - -static PurpleBuddyIconSpec * -jabber_protocol_get_buddy_icon_spec(G_GNUC_UNUSED PurpleProtocol *protocol) { - return purple_buddy_icon_spec_new("png", - 32, 32, 96, 96, 0, - PURPLE_ICON_SCALE_SEND | - PURPLE_ICON_SCALE_DISPLAY); -} - -static void -jabber_protocol_init(G_GNUC_UNUSED JabberProtocol *self) { -} - -static void -jabber_protocol_class_init(JabberProtocolClass *klass) -{ - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); - - protocol_class->get_buddy_icon_spec = jabber_protocol_get_buddy_icon_spec; - - protocol_class->login = jabber_login; - protocol_class->close = jabber_close; - protocol_class->status_types = jabber_status_types; -} - -static void -jabber_protocol_class_finalize(G_GNUC_UNUSED JabberProtocolClass *klass) -{ -} - -static void -xmpp_protocol_actions_iface_init(PurpleProtocolActionsInterface *iface) -{ - iface->get_prefix = xmpp_protocol_actions_get_prefix; - iface->get_action_group = xmpp_protocol_actions_get_action_group; - iface->get_menu = xmpp_protocol_actions_get_menu; -} - -static void -jabber_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface) -{ - client_iface->list_emblem = jabber_list_emblem; - client_iface->blist_node_menu = jabber_blist_node_menu; - client_iface->convo_closed = jabber_convo_closed; - client_iface->normalize = jabber_client_normalize; - client_iface->find_blist_chat = jabber_find_blist_chat; - client_iface->offline_message = jabber_offline_message; -} - -static void -jabber_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface) -{ - server_iface->set_info = jabber_set_info; - server_iface->get_info = jabber_buddy_get_info; - server_iface->set_status = jabber_set_status; - server_iface->set_idle = jabber_idle_set; - server_iface->add_buddy = jabber_roster_add_buddy; - server_iface->remove_buddy = jabber_roster_remove_buddy; - server_iface->keepalive = jabber_keepalive; - server_iface->get_keepalive_interval = jabber_get_keepalive_interval; - server_iface->alias_buddy = jabber_roster_alias_change; - server_iface->group_buddy = jabber_roster_group_change; - server_iface->rename_group = jabber_roster_group_rename; - server_iface->set_buddy_icon = jabber_set_buddy_icon; - server_iface->send_raw = jabber_protocol_send_raw; -} - -static void -jabber_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface) -{ - im_iface->send = jabber_message_send_im; - im_iface->send_typing = jabber_send_typing; -} - -static GHashTable * -jabber_protocol_chat_info_defaults(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *connection, - const gchar *name) -{ - return jabber_chat_info_defaults(connection, name); -} - -static void -jabber_protocol_chat_join(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *connection, GHashTable *components) -{ - jabber_chat_join(connection, components); -} - -static void -jabber_protocol_chat_invite(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *connection, gint id, - const gchar *message, const gchar *who) -{ - jabber_chat_invite(connection, id, message, who); -} - -static void -jabber_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface) -{ - chat_iface->info = jabber_chat_info; - chat_iface->info_defaults = jabber_protocol_chat_info_defaults; - chat_iface->join = jabber_protocol_chat_join; - chat_iface->get_name = jabber_get_chat_name; - chat_iface->invite = jabber_protocol_chat_invite; - chat_iface->leave = jabber_chat_leave; - chat_iface->send = jabber_message_send_chat; - chat_iface->get_user_real_name = jabber_chat_user_real_name; - chat_iface->set_topic = jabber_chat_set_topic; -} - -static void -jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface) -{ - roomlist_iface->get_list = jabber_roomlist_get_list; - roomlist_iface->cancel = jabber_roomlist_cancel; - roomlist_iface->room_serialize = jabber_roomlist_room_serialize; -} - -static void -jabber_protocol_media_iface_init(PurpleProtocolMediaInterface *media_iface) -{ - media_iface->initiate_session = jabber_initiate_media; - media_iface->get_caps = jabber_get_media_caps; -} - -static void -jabber_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface) -{ - xfer_iface->can_receive = jabber_can_receive_file; - xfer_iface->send_file = jabber_si_xfer_send; - xfer_iface->new_xfer = jabber_si_new_xfer; -} - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JabberProtocol, - jabber_protocol, - PURPLE_TYPE_PROTOCOL, - G_TYPE_FLAG_ABSTRACT, - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ACTIONS, - xmpp_protocol_actions_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT, - jabber_protocol_client_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER, - jabber_protocol_server_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM, - jabber_protocol_im_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT, - jabber_protocol_chat_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST, - jabber_protocol_roomlist_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_MEDIA, - jabber_protocol_media_iface_init) - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER, - jabber_protocol_xfer_iface_init)) - -static GPluginPluginInfo * -jabber_query(G_GNUC_UNUSED GError **error) -{ - return purple_plugin_info_new( - "id", "prpl-xmpp", - "name", "XMPP Protocols", - "version", DISPLAY_VERSION, - "category", N_("Protocol"), - "summary", N_("XMPP Protocol Plugin"), - "description", N_("XMPP Protocol Plugin"), - "website", PURPLE_WEBSITE, - "abi-version", PURPLE_ABI_VERSION, - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, - NULL - ); -} - -static gboolean -jabber_load(GPluginPlugin *plugin, GError **error) -{ - PurpleProtocolManager *manager = purple_protocol_manager_get_default(); - - jingle_session_register(plugin); - - jingle_transport_register(plugin); - jingle_iceudp_register(plugin); - jingle_rawudp_register(plugin); - - jingle_content_register(plugin); - jingle_rtp_register(plugin); - - jabber_protocol_register_type(G_TYPE_MODULE(plugin)); - - xmpp_protocol_register(plugin); - - jabber_oob_xfer_register(G_TYPE_MODULE(plugin)); - jabber_si_xfer_register(G_TYPE_MODULE(plugin)); - - xmpp_protocol = xmpp_protocol_new(); - if(!purple_protocol_manager_register(manager, xmpp_protocol, error)) { - g_clear_object(&xmpp_protocol); - - return FALSE; - } - - purple_signal_connect(purple_get_core(), "uri-handler", xmpp_protocol, - G_CALLBACK(xmpp_uri_handler), xmpp_protocol); - - jabber_init_protocol(xmpp_protocol); - - return TRUE; -} - -static gboolean -jabber_unload(G_GNUC_UNUSED GPluginPlugin *plugin, - G_GNUC_UNUSED gboolean shutdown, GError **error) -{ - PurpleProtocolManager *manager = purple_protocol_manager_get_default(); - - if(!purple_protocol_manager_unregister(manager, xmpp_protocol, error)) { - return FALSE; - } - - purple_signal_disconnect(purple_get_core(), "uri-handler", - xmpp_protocol, G_CALLBACK(xmpp_uri_handler)); - - jabber_uninit_protocol(xmpp_protocol); - - g_clear_object(&xmpp_protocol); - - return TRUE; -} - -GPLUGIN_NATIVE_PLUGIN_DECLARE(jabber)
--- a/libpurple/protocols/jabber/jabber.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +0,0 @@ -/** - * @file jabber.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JABBER_H -#define PURPLE_JABBER_JABBER_H - -typedef enum { - JABBER_CAP_NONE = 0, -/* JABBER_CAP_XHTML = 1 << 0, */ -/* JABBER_CAP_COMPOSING = 1 << 1, */ - JABBER_CAP_SI = 1 << 2, - JABBER_CAP_SI_FILE_XFER = 1 << 3, - 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_PING = 1 << 11, - JABBER_CAP_ADHOC = 1 << 12, - JABBER_CAP_BLOCKING = 1 << 13, - - JABBER_CAP_ITEMS = 1 << 14, - JABBER_CAP_ROSTER_VERSIONING = 1 << 15, - - JABBER_CAP_MESSAGE_CARBONS = 1 << 19, - - JABBER_CAP_RETRIEVED = 1 << 31 -} JabberCapabilities; - -typedef struct _JabberStream JabberStream; - -#include <libxml/parser.h> -#include <glib.h> -#include <gmodule.h> -#include <gio/gio.h> -#include <libsoup/soup.h> - -#include <purple.h> - -#ifdef PURPLE_XMPP_COMPILATION -#define PURPLE_XMPP_EXTERN_FOR_TESTS _PURPLE_EXPORT extern -#else -#define PURPLE_XMPP_EXTERN_FOR_TESTS _PURPLE_IMPORT extern -#endif - -#include "namespaces.h" - -#include "auth.h" -#include "iq.h" -#include "jutil.h" -#include "buddy.h" -#include "bosh.h" - -#define CAPS0115_NODE "https://pidgin.im/" - -#define JABBER_TYPE_PROTOCOL (jabber_protocol_get_type()) -G_DECLARE_DERIVABLE_TYPE(JabberProtocol, jabber_protocol, JABBER, PROTOCOL, - PurpleProtocol) - -#define JABBER_DEFAULT_REQUIRE_TLS "require_starttls" - -/* Index into attention_types list */ -#define JABBER_BUZZ 0 - -typedef enum { - JABBER_STREAM_OFFLINE, - JABBER_STREAM_CONNECTING, - JABBER_STREAM_INITIALIZING, - JABBER_STREAM_INITIALIZING_ENCRYPTION, - JABBER_STREAM_AUTHENTICATING, - JABBER_STREAM_POST_AUTH, - JABBER_STREAM_CONNECTED -} JabberStreamState; - -struct _JabberProtocolClass { - PurpleProtocolClass parent_class; -}; - -struct _JabberStream -{ - guint inpa; - - GCancellable *cancellable; - - xmlParserCtxt *context; - PurpleXmlNode *current; - - struct { - guint8 major; - guint8 minor; - } protocol_version; - - JabberSaslMech *auth_mech; - gpointer auth_mech_data; - - /** - * The header from the opening <stream/> tag. This being NULL is treated - * as a special condition in the parsing code (signifying the next - * stanza started is an opening stream tag), and its being missing on - * the stream header is treated as a fatal error. - */ - char *stream_id; - JabberStreamState state; - - GHashTable *buddies; - - /* - * This boolean was added to eliminate a heinous bug where we would - * get into a loop with the server and move a buddy back and forth - * from one group to another. - * - * The sequence goes something like this: - * 1. Our resource and another resource both approve an authorization - * request at the exact same time. We put the buddy in group A and - * the other resource put the buddy in group B. - * 2. The server receives the roster add for group B and sends us a - * roster push. - * 3. We receive this roster push and modify our local blist. This - * triggers us to send a roster add for group B. - * 4. The server receives our earlier roster add for group A and sends us a - * roster push. - * 5. We receive this roster push and modify our local blist. This - * triggers us to send a roster add for group A. - * 6. The server receives our earlier roster add for group B and sends - * us a roster push. - * (repeat steps 3 through 6 ad infinitum) - * - * This boolean is used to short-circuit the sending of a roster add - * when we receive a roster push. - * - * See these bug reports: - * https://trac.adium.im/ticket/8834 - * https://developer.pidgin.im/ticket/5484 - * https://developer.pidgin.im/ticket/6188 - */ - gboolean currently_parsing_roster_push; - - GHashTable *chats; - GList *chat_servers; - PurpleRoomlist *roomlist; - - GHashTable *iq_callbacks; - int next_id; - - GList *bs_proxies; - GList *oob_file_transfers; - GList *file_transfers; - - time_t idle; - time_t old_idle; - - JabberID *user; - JabberBuddy *user_jb; - - PurpleConnection *gc; - GSocketClient *client; - GIOStream *stream; - GInputStream *input; - GOutputStream *output; - - char *initial_avatar_hash; - char *avatar_hash; - GSList *pending_avatar_requests; - - GSList *pending_buddy_info_requests; - - gboolean reinit; - - JabberCapabilities server_caps; - char *server_name; - - char *serverFQDN; - - gboolean vcard_fetched; - - /* Entity Capabilities hash */ - char *caps_hash; - - /* does the local server support PEP? */ - gboolean pep; - - /* Is Buzz enabled? */ - gboolean allowBuzz; - - /* A list of JabberAdHocCommands supported by the server */ - GList *commands; - - /* last presence update to check for differences */ - JabberBuddyState old_state; - char *old_msg; - int old_priority; - char *old_avatarhash; - - char *certificate_CN; - - /* A purple timeout tag for the keepalive */ - guint keepalive_timeout; - guint max_inactivity; - guint inactivity_timer; - guint conn_close_timeout; - - PurpleJabberBOSHConnection *bosh; - - SoupSession *http_conns; - - /* keep a hash table of JingleSessions */ - GHashTable *sessions; -}; - -typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); - -typedef struct -{ - gchar *namespace; - JabberFeatureEnabled *is_enabled; -} JabberFeature; - -typedef struct -{ - gchar *category; - gchar *type; - gchar *name; - gchar *lang; -} JabberIdentity; - -typedef struct { - char *jid; - char *host; - guint16 port; - char *zeroconf; -} JabberBytestreamsStreamhost; - -/* what kind of additional features as returned from disco#info are supported? */ -extern GList *jabber_features; -/* A sorted list of identities advertised. Use jabber_add_identity to add - * so it remains sorted. - */ -extern GList *jabber_identities; - -/** - * Returns the GType for the JabberProtocol object. - */ - -void jabber_stream_features_parse(JabberStream *js, PurpleXmlNode *packet); -void jabber_process_packet(JabberStream *js, PurpleXmlNode **packet); -void jabber_send(JabberStream *js, PurpleXmlNode *data); - -void jabber_stream_set_state(JabberStream *js, JabberStreamState state); - -char *jabber_get_next_id(JabberStream *js); - -/** Parse an error into a human-readable string and optionally a disconnect - * reason. - * @param js the stream on which the error occurred. - * @param packet the error packet - * @param reason where to store the disconnection reason, or @c NULL if you - * don't care or you don't intend to close the connection. - */ -char *jabber_parse_error(JabberStream *js, PurpleXmlNode *packet, PurpleConnectionError *reason); - -/** - * Add a feature to the list of features advertised via disco#info. If you - * call this while accounts are connected, Bad Things(TM) will happen because - * the Entity Caps hash will be out-of-date (which should be fixed :/) - * - * @param namespace The namespace of the feature - * @param cb A callback determining whether or not this feature - * will advertised; may be NULL. - */ -void jabber_add_feature(const gchar *namespace, JabberFeatureEnabled cb); - -JabberIdentity *jabber_identity_new(const gchar *category, const gchar *type, const gchar *lang, const gchar *name); -void jabber_identity_free(JabberIdentity *id); - -/** - * GCompareFunc for JabberIdentity structs. - */ -gint jabber_identity_compare(gconstpointer a, gconstpointer b); - -void jabber_bytestreams_streamhost_free(JabberBytestreamsStreamhost *sh); - -/** - * Returns true if this connection is over a secure (SSL) stream. Use this - * instead of checking js->gsc because BOSH stores its PurpleSslConnection - * members in its own data structure. - */ -gboolean jabber_stream_is_ssl(JabberStream *js); - -/** - * Restart the "we haven't sent anything in a while and should send - * something or the server will kick us off" timer (obviously - * called when sending something. It's exposed for BOSH.) - */ -void jabber_stream_restart_inactivity_timer(JabberStream *js); - -/** Protocol functions */ -void jabber_blocklist_parse_push(JabberStream *js, const char *from, - JabberIqType type, const char *id, - PurpleXmlNode *child); -void jabber_request_block_list(JabberStream *js); - -#endif /* PURPLE_JABBER_JABBER_H */
--- a/libpurple/protocols/jabber/jingle/content.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,477 +0,0 @@ -/** - * @file content.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "content.h" -#include "jingle.h" - -#include <string.h> - -typedef struct -{ - JingleSession *session; - gchar *description_type; - gchar *creator; - gchar *disposition; - gchar *name; - gchar *senders; - JingleTransport *transport; - JingleTransport *pending_transport; -} JingleContentPrivate; - -enum { - PROP_0, - PROP_SESSION, - PROP_CREATOR, - PROP_DISPOSITION, - PROP_NAME, - PROP_SENDERS, - PROP_TRANSPORT, - PROP_PENDING_TRANSPORT, - N_PROPERTIES, -}; - -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JingleContent, - jingle_content, - G_TYPE_OBJECT, - 0, - G_ADD_PRIVATE_DYNAMIC(JingleContent) -); - -/****************************************************************************** - * JingleContent Implementation - *****************************************************************************/ -static JingleContent * -jingle_content_parse_internal(PurpleXmlNode *content) -{ - PurpleXmlNode *description = purple_xmlnode_get_child(content, "description"); - const gchar *type = purple_xmlnode_get_namespace(description); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - const gchar *disposition = purple_xmlnode_get_attrib(content, "disposition"); - const gchar *senders = purple_xmlnode_get_attrib(content, "senders"); - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - JingleTransport *transport = - jingle_transport_parse(purple_xmlnode_get_child(content, "transport")); - if (transport == NULL) - return NULL; - - if (senders == NULL) - senders = "both"; - - return jingle_content_create(type, creator, disposition, name, senders, transport); -} - -static PurpleXmlNode * -jingle_content_to_xml_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action) -{ - PurpleXmlNode *node = purple_xmlnode_new_child(jingle, "content"); - gchar *creator = jingle_content_get_creator(content); - gchar *name = jingle_content_get_name(content); - gchar *senders = jingle_content_get_senders(content); - gchar *disposition = jingle_content_get_disposition(content); - - purple_xmlnode_set_attrib(node, "creator", creator); - purple_xmlnode_set_attrib(node, "name", name); - purple_xmlnode_set_attrib(node, "senders", senders); - if (!purple_strequal("session", disposition)) - purple_xmlnode_set_attrib(node, "disposition", disposition); - - g_free(disposition); - g_free(senders); - g_free(name); - g_free(creator); - - if (action != JINGLE_CONTENT_REMOVE) { - JingleTransport *transport; - - if (action != JINGLE_TRANSPORT_ACCEPT && - action != JINGLE_TRANSPORT_INFO && - action != JINGLE_TRANSPORT_REJECT && - action != JINGLE_TRANSPORT_REPLACE) { - PurpleXmlNode *description = purple_xmlnode_new_child(node, "description"); - - purple_xmlnode_set_namespace(description, - jingle_content_get_description_type(content)); - } - - if (action != JINGLE_TRANSPORT_REJECT && action == JINGLE_TRANSPORT_REPLACE) - transport = jingle_content_get_pending_transport(content); - else - transport = jingle_content_get_transport(content); - - jingle_transport_to_xml(transport, node, action); - g_object_unref(transport); - } - - return node; -} - -/****************************************************************************** - * GObject Stuff - *****************************************************************************/ -static void -jingle_content_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - JingleContent *content = JINGLE_CONTENT(object); - JingleContentPrivate *priv = jingle_content_get_instance_private(content); - - switch (prop_id) { - case PROP_SESSION: - priv->session = g_value_get_object(value); - break; - case PROP_CREATOR: - g_free(priv->creator); - priv->creator = g_value_dup_string(value); - break; - case PROP_DISPOSITION: - g_free(priv->disposition); - priv->disposition = g_value_dup_string(value); - break; - case PROP_NAME: - g_free(priv->name); - priv->name = g_value_dup_string(value); - break; - case PROP_SENDERS: - g_free(priv->senders); - priv->senders = g_value_dup_string(value); - break; - case PROP_TRANSPORT: - if (priv->transport) - g_object_unref(priv->transport); - priv->transport = g_value_get_object(value); - break; - case PROP_PENDING_TRANSPORT: - if (priv->pending_transport) - g_object_unref(priv->pending_transport); - priv->pending_transport = g_value_get_object(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_content_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - JingleContent *content = JINGLE_CONTENT(object); - JingleContentPrivate *priv = jingle_content_get_instance_private(content); - - switch (prop_id) { - case PROP_SESSION: - g_value_set_object(value, priv->session); - break; - case PROP_CREATOR: - g_value_set_string(value, priv->creator); - break; - case PROP_DISPOSITION: - g_value_set_string(value, priv->disposition); - break; - case PROP_NAME: - g_value_set_string(value, priv->name); - break; - case PROP_SENDERS: - g_value_set_string(value, priv->senders); - break; - case PROP_TRANSPORT: - g_value_set_object(value, priv->transport); - break; - case PROP_PENDING_TRANSPORT: - g_value_set_object(value, priv->pending_transport); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_content_init(G_GNUC_UNUSED JingleContent *content) -{ -} - -static void -jingle_content_finalize (GObject *content) -{ - JingleContentPrivate *priv = jingle_content_get_instance_private(JINGLE_CONTENT(content)); - - purple_debug_info("jingle","jingle_content_finalize\n"); - - g_free(priv->description_type); - g_free(priv->creator); - g_free(priv->disposition); - g_free(priv->name); - g_free(priv->senders); - g_object_unref(priv->transport); - if (priv->pending_transport) - g_object_unref(priv->pending_transport); - - G_OBJECT_CLASS(jingle_content_parent_class)->finalize(content); -} - - -static void -jingle_content_class_finalize(G_GNUC_UNUSED JingleContentClass *klass) -{ -} - -static void -jingle_content_class_init (JingleContentClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - - obj_class->finalize = jingle_content_finalize; - obj_class->set_property = jingle_content_set_property; - obj_class->get_property = jingle_content_get_property; - - klass->to_xml = jingle_content_to_xml_internal; - klass->parse = jingle_content_parse_internal; - - properties[PROP_SESSION] = g_param_spec_object("session", - "Jingle Session", - "The jingle session parent of this content.", - JINGLE_TYPE_SESSION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_CREATOR] = g_param_spec_string("creator", - "Creator", - "The participant that created this content.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_DISPOSITION] = g_param_spec_string("disposition", - "Disposition", - "The disposition of the content.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_NAME] = g_param_spec_string("name", - "Name", - "The name of this content.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_SENDERS] = g_param_spec_string("senders", - "Senders", - "The sender of this content.", - NULL, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_TRANSPORT] = g_param_spec_object("transport", - "transport", - "The transport of this content.", - JINGLE_TYPE_TRANSPORT, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_PENDING_TRANSPORT] = g_param_spec_object("pending-transport", - "Pending transport", - "The pending transport contained within this content", - JINGLE_TYPE_TRANSPORT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); -} - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_content_register(PurplePlugin *plugin) -{ - jingle_content_register_type(G_TYPE_MODULE(plugin)); -} - -JingleContent * -jingle_content_create(const gchar *type, const gchar *creator, - const gchar *disposition, const gchar *name, - const gchar *senders, JingleTransport *transport) -{ - - - JingleContent *content = g_object_new(jingle_get_type(type), - "creator", creator, - "disposition", disposition != NULL ? disposition : "session", - "name", name, - "senders", senders != NULL ? senders : "both", - "transport", transport, - NULL); - return content; -} - -JingleSession *jingle_content_get_session(JingleContent *content) -{ - JingleSession *session; - g_object_get(content, "session", &session, NULL); - return session; -} - -const gchar * -jingle_content_get_description_type(JingleContent *content) -{ - return JINGLE_CONTENT_GET_CLASS(content)->description_type; -} - -gchar * -jingle_content_get_creator(JingleContent *content) -{ - gchar *creator; - g_object_get(content, "creator", &creator, NULL); - return creator; -} - -gchar * -jingle_content_get_disposition(JingleContent *content) -{ - gchar *disposition; - g_object_get(content, "disposition", &disposition, NULL); - return disposition; -} - -gchar * -jingle_content_get_name(JingleContent *content) -{ - gchar *name; - g_object_get(content, "name", &name, NULL); - return name; -} - -gchar * -jingle_content_get_senders(JingleContent *content) -{ - gchar *senders; - g_object_get(content, "senders", &senders, NULL); - return senders; -} - -JingleTransport * -jingle_content_get_transport(JingleContent *content) -{ - JingleTransport *transport; - g_object_get(content, "transport", &transport, NULL); - return transport; -} - -JingleTransport * -jingle_content_get_pending_transport(JingleContent *content) -{ - JingleTransport *pending_transport; - g_object_get(content, "pending-transport", &pending_transport, NULL); - return pending_transport; -} - -void -jingle_content_set_session(JingleContent *content, JingleSession *session) -{ - g_return_if_fail(JINGLE_IS_CONTENT(content)); - g_return_if_fail(JINGLE_IS_SESSION(session)); - g_object_set(content, "session", session, NULL); -} - -void -jingle_content_set_pending_transport(JingleContent *content, JingleTransport *transport) -{ - g_object_set(content, "pending-transport", transport, NULL); -} - -void -jingle_content_accept_transport(JingleContent *content) -{ - JingleContentPrivate *priv = NULL; - GObject *obj; - - g_return_if_fail(JINGLE_IS_CONTENT(content)); - - priv = jingle_content_get_instance_private(content); - - if (priv->transport) - g_object_unref(priv->transport); - - priv->transport = priv->pending_transport; - priv->pending_transport = NULL; - - obj = G_OBJECT(content); - g_object_freeze_notify(obj); - g_object_notify_by_pspec(obj, properties[PROP_TRANSPORT]); - g_object_notify_by_pspec(obj, properties[PROP_PENDING_TRANSPORT]); - g_object_thaw_notify(obj); -} - -void -jingle_content_remove_pending_transport(JingleContent *content) -{ - JingleContentPrivate *priv = NULL; - - g_return_if_fail(JINGLE_IS_CONTENT(content)); - - priv = jingle_content_get_instance_private(content); - - if (priv->pending_transport) { - g_object_unref(priv->pending_transport); - priv->pending_transport = NULL; - } - - g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_PENDING_TRANSPORT]); -} - -void -jingle_content_modify(JingleContent *content, const gchar *senders) -{ - g_object_set(content, "senders", senders, NULL); -} - -JingleContent * -jingle_content_parse(PurpleXmlNode *content) -{ - const gchar *type = purple_xmlnode_get_namespace(purple_xmlnode_get_child(content, "description")); - GType jingle_type = jingle_get_type(type); - - if (jingle_type != G_TYPE_NONE) { - return JINGLE_CONTENT_CLASS(g_type_class_ref(jingle_type))->parse(content); - } else { - return NULL; - } -} - -PurpleXmlNode * -jingle_content_to_xml(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action) -{ - g_return_val_if_fail(content != NULL, NULL); - g_return_val_if_fail(JINGLE_IS_CONTENT(content), NULL); - return JINGLE_CONTENT_GET_CLASS(content)->to_xml(content, jingle, action); -} - -void -jingle_content_handle_action(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action) -{ - g_return_if_fail(content != NULL); - g_return_if_fail(JINGLE_IS_CONTENT(content)); - JINGLE_CONTENT_GET_CLASS(content)->handle_action(content, xmlcontent, action); -} -
--- a/libpurple/protocols/jabber/jingle/content.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * @file content.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_CONTENT_H -#define PURPLE_JABBER_JINGLE_CONTENT_H - - -#include "jabber.h" -#include "jingle.h" -#include "session.h" -#include "transport.h" - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define JINGLE_TYPE_CONTENT jingle_content_get_type() - -typedef struct _JingleContent JingleContent; - -/** The content class */ -struct _JingleContentClass -{ - GObjectClass parent_class; /**< The parent class. */ - - PurpleXmlNode *(*to_xml) (JingleContent *content, PurpleXmlNode *jingle, JingleActionType action); - JingleContent *(*parse) (PurpleXmlNode *content); - void (*handle_action) (JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action); - const gchar *description_type; -}; - -/** - * Gets the content class's GType - * - * @return The content class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_DERIVABLE_TYPE(JingleContent, jingle_content, JINGLE, CONTENT, - GObject) - -/** - * Registers the JingleContent type in the type system. - */ -void jingle_content_register(PurplePlugin *plugin); - -JingleContent *jingle_content_create(const gchar *type, const gchar *creator, - const gchar *disposition, const gchar *name, - const gchar *senders, JingleTransport *transport); - -JingleSession *jingle_content_get_session(JingleContent *content); -const gchar *jingle_content_get_description_type(JingleContent *content); -gchar *jingle_content_get_creator(JingleContent *content); -gchar *jingle_content_get_disposition(JingleContent *content); -gchar *jingle_content_get_name(JingleContent *content); -gchar *jingle_content_get_senders(JingleContent *content); -JingleTransport *jingle_content_get_transport(JingleContent *content); -JingleTransport *jingle_content_get_pending_transport(JingleContent *content); - -void jingle_content_set_session(JingleContent *content, JingleSession *session); -void jingle_content_set_pending_transport(JingleContent *content, JingleTransport *transport); -void jingle_content_accept_transport(JingleContent *content); -void jingle_content_remove_pending_transport(JingleContent *content); -void jingle_content_modify(JingleContent *content, const gchar *senders); - -#define jingle_content_create_content_accept(session) \ - jingle_session_to_packet(session, JINGLE_CONTENT_ACCEPT) -#define jingle_content_create_content_add(session) \ - jingle_session_to_packet(session, JINGLE_CONTENT_ADD) -#define jingle_content_create_content_modify(session) \ - jingle_session_to_packet(session, JINGLE_CONTENT_MODIFY) -#define jingle_content_create_content_remove(session) \ - jingle_session_to_packet(session, JINGLE_CONTENT_REMOVE) - -JingleContent *jingle_content_parse(PurpleXmlNode *content); -PurpleXmlNode *jingle_content_to_xml(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action); -void jingle_content_handle_action(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_CONTENT_H */ -
--- a/libpurple/protocols/jabber/jingle/iceudp.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,499 +0,0 @@ -/** - * @file iceudp.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "iceudp.h" -#include "jingle.h" - -#include <string.h> - -struct _JingleIceUdp -{ - JingleTransport parent; -}; - -typedef struct -{ - GList *local_candidates; - GList *remote_candidates; -} JingleIceUdpPrivate; - -enum { - PROP_0, - PROP_LOCAL_CANDIDATES, - PROP_REMOTE_CANDIDATES, - N_PROPERTIES, -}; - -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JingleIceUdp, - jingle_iceudp, - JINGLE_TYPE_TRANSPORT, - G_TYPE_FLAG_FINAL, - G_ADD_PRIVATE_DYNAMIC(JingleIceUdp) -); - -/****************************************************************************** - * JingleIceUdp Transport Implementation - *****************************************************************************/ -static void -jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate) -{ - JingleIceUdp *iceudp = JINGLE_ICEUDP(transport); - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - PurpleMediaCandidateType type; - gchar *ip; - gchar *username; - gchar *password; - gchar *foundation; - JingleIceUdpCandidate *iceudp_candidate; - GList *iter; - - ip = purple_media_candidate_get_ip(candidate); - username = purple_media_candidate_get_username(candidate); - password = purple_media_candidate_get_password(candidate); - type = purple_media_candidate_get_candidate_type(candidate); - foundation = purple_media_candidate_get_foundation(candidate); - - iceudp_candidate = jingle_iceudp_candidate_new(id, - purple_media_candidate_get_component_id(candidate), - foundation, generation, ip, 0, - purple_media_candidate_get_port(candidate), - purple_media_candidate_get_priority(candidate), "udp", - type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : - "", username, password); - iceudp_candidate->reladdr = purple_media_candidate_get_base_ip(candidate); - iceudp_candidate->relport = purple_media_candidate_get_base_port(candidate); - - g_free(password); - g_free(username); - g_free(foundation); - g_free(ip); - - for (iter = priv->local_candidates; iter; iter = g_list_next(iter)) { - JingleIceUdpCandidate *c = iter->data; - if (purple_strequal(c->id, id)) { - generation = c->generation + 1; - - g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c); - priv->local_candidates = g_list_delete_link( - priv->local_candidates, iter); - - iceudp_candidate->generation = generation; - - priv->local_candidates = g_list_append( - priv->local_candidates, iceudp_candidate); - - g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_LOCAL_CANDIDATES]); - - return; - } - } - - priv->local_candidates = g_list_append( - priv->local_candidates, iceudp_candidate); - - g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_LOCAL_CANDIDATES]); -} - -static GList * -jingle_iceudp_get_remote_candidates(JingleTransport *transport) -{ - JingleIceUdp *iceudp = JINGLE_ICEUDP(transport); - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - GList *candidates = priv->remote_candidates; - GList *ret = NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - JingleIceUdpCandidate *candidate = candidates->data; - PurpleMediaCandidate *new_candidate = purple_media_candidate_new( - candidate->foundation, candidate->component, - purple_strequal(candidate->type, "host") ? - PURPLE_MEDIA_CANDIDATE_TYPE_HOST : - purple_strequal(candidate->type, "srflx") ? - PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX : - purple_strequal(candidate->type, "prflx") ? - PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX : - purple_strequal(candidate->type, "relay") ? - PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0, - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - candidate->ip, candidate->port); - g_object_set(new_candidate, - "base-ip", candidate->reladdr, - "base-port", candidate->relport, - "username", candidate->username, - "password", candidate->password, - "priority", candidate->priority, - NULL); - ret = g_list_append(ret, new_candidate); - } - - return ret; -} - -static JingleIceUdpCandidate * -jingle_iceudp_get_remote_candidate_by_id(JingleIceUdp *iceudp, - const gchar *id) -{ - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - GList *iter = priv->remote_candidates; - for (; iter; iter = g_list_next(iter)) { - JingleIceUdpCandidate *candidate = iter->data; - if (purple_strequal(candidate->id, id)) { - return candidate; - } - } - return NULL; -} - -static void -jingle_iceudp_add_remote_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate) -{ - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - JingleIceUdpCandidate *iceudp_candidate = - jingle_iceudp_get_remote_candidate_by_id(iceudp, - candidate->id); - if (iceudp_candidate != NULL) { - priv->remote_candidates = g_list_remove( - priv->remote_candidates, iceudp_candidate); - g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, iceudp_candidate); - } - priv->remote_candidates = g_list_append(priv->remote_candidates, candidate); - - g_object_notify_by_pspec(G_OBJECT(iceudp), properties[PROP_REMOTE_CANDIDATES]); -} - -static JingleTransport * -jingle_iceudp_parse_internal(PurpleXmlNode *iceudp) -{ - JingleTransport *transport = JINGLE_TRANSPORT_CLASS(jingle_iceudp_parent_class)->parse(iceudp); - PurpleXmlNode *candidate = purple_xmlnode_get_child(iceudp, "candidate"); - JingleIceUdpCandidate *iceudp_candidate = NULL; - - const gchar *username = purple_xmlnode_get_attrib(iceudp, "ufrag"); - const gchar *password = purple_xmlnode_get_attrib(iceudp, "pwd"); - - for (; candidate; candidate = purple_xmlnode_get_next_twin(candidate)) { - const gchar *relport = purple_xmlnode_get_attrib(candidate, "rel-port"); - const gchar *component = purple_xmlnode_get_attrib(candidate, "component"); - const gchar *foundation = purple_xmlnode_get_attrib(candidate, "foundation"); - const gchar *generation = purple_xmlnode_get_attrib(candidate, "generation"); - const gchar *id = purple_xmlnode_get_attrib(candidate, "id"); - const gchar *ip = purple_xmlnode_get_attrib(candidate, "ip"); - const gchar *network = purple_xmlnode_get_attrib(candidate, "network"); - const gchar *port = purple_xmlnode_get_attrib(candidate, "port"); - const gchar *priority = purple_xmlnode_get_attrib(candidate, "priority"); - const gchar *protocol = purple_xmlnode_get_attrib(candidate, "protocol"); - const gchar *type = purple_xmlnode_get_attrib(candidate, "type"); - - if (!component || !foundation || !generation || !id || !ip || - !network || !port || !priority || !protocol || !type) - continue; - - iceudp_candidate = jingle_iceudp_candidate_new( - id, - atoi(component), - foundation, - atoi(generation), - ip, - atoi(network), - atoi(port), - atoi(priority), - protocol, - type, - username, password); - iceudp_candidate->reladdr = g_strdup( - purple_xmlnode_get_attrib(candidate, "rel-addr")); - iceudp_candidate->relport = - relport != NULL ? atoi(relport) : 0; - iceudp_candidate->rem_known = TRUE; - jingle_iceudp_add_remote_candidate(JINGLE_ICEUDP(transport), iceudp_candidate); - } - - return transport; -} - -static PurpleXmlNode * -jingle_iceudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action) -{ - PurpleXmlNode *node = JINGLE_TRANSPORT_CLASS(jingle_iceudp_parent_class)->to_xml(transport, content, action); - - if (action == JINGLE_SESSION_INITIATE || - action == JINGLE_SESSION_ACCEPT || - action == JINGLE_TRANSPORT_INFO || - action == JINGLE_CONTENT_ADD || - action == JINGLE_TRANSPORT_REPLACE) { - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(JINGLE_ICEUDP(transport)); - GList *iter = priv->local_candidates; - gboolean used_candidate = FALSE; - - for (; iter; iter = g_list_next(iter)) { - JingleIceUdpCandidate *candidate = iter->data; - PurpleXmlNode *xmltransport; - gchar *component, *generation, *network, - *port, *priority; - - if (candidate->rem_known == TRUE) - continue; - - used_candidate = TRUE; - candidate->rem_known = TRUE; - - xmltransport = purple_xmlnode_new_child(node, "candidate"); - component = g_strdup_printf("%d", candidate->component); - generation = g_strdup_printf("%d", - candidate->generation); - network = g_strdup_printf("%d", candidate->network); - port = g_strdup_printf("%d", candidate->port); - priority = g_strdup_printf("%d", candidate->priority); - - purple_xmlnode_set_attrib(xmltransport, "component", component); - purple_xmlnode_set_attrib(xmltransport, "foundation", candidate->foundation); - purple_xmlnode_set_attrib(xmltransport, "generation", generation); - purple_xmlnode_set_attrib(xmltransport, "id", candidate->id); - purple_xmlnode_set_attrib(xmltransport, "ip", candidate->ip); - purple_xmlnode_set_attrib(xmltransport, "network", network); - purple_xmlnode_set_attrib(xmltransport, "port", port); - purple_xmlnode_set_attrib(xmltransport, "priority", priority); - purple_xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol); - - if (candidate->reladdr != NULL && - (!purple_strequal(candidate->ip, candidate->reladdr) || - (candidate->port != candidate->relport))) { - gchar *relport = g_strdup_printf("%d", - candidate->relport); - purple_xmlnode_set_attrib(xmltransport, "rel-addr", - candidate->reladdr); - purple_xmlnode_set_attrib(xmltransport, "rel-port", - relport); - g_free(relport); - } - - purple_xmlnode_set_attrib(xmltransport, "type", candidate->type); - - g_free(component); - g_free(generation); - g_free(network); - g_free(port); - g_free(priority); - } - - if (used_candidate == TRUE) { - JingleIceUdpCandidate *candidate = - priv->local_candidates->data; - purple_xmlnode_set_attrib(node, "pwd", candidate->password); - purple_xmlnode_set_attrib(node, "ufrag", candidate->username); - } - } - - return node; -} - -/****************************************************************************** - * JingleIceUdp GObject Implementation - *****************************************************************************/ -static void -jingle_iceudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - JingleIceUdp *iceudp = JINGLE_ICEUDP(object); - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - - switch (prop_id) { - case PROP_LOCAL_CANDIDATES: - priv->local_candidates = - g_value_get_pointer(value); - break; - case PROP_REMOTE_CANDIDATES: - priv->remote_candidates = - g_value_get_pointer(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_iceudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - JingleIceUdp *iceudp = JINGLE_ICEUDP(object); - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - - switch (prop_id) { - case PROP_LOCAL_CANDIDATES: - g_value_set_pointer(value, priv->local_candidates); - break; - case PROP_REMOTE_CANDIDATES: - g_value_set_pointer(value, priv->remote_candidates); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_iceudp_init(G_GNUC_UNUSED JingleIceUdp *iceudp) -{ -} - -static void -jingle_iceudp_candidate_free(JingleIceUdpCandidate *candidate); - -static void -jingle_iceudp_finalize (GObject *object) -{ - JingleIceUdp *iceudp = JINGLE_ICEUDP(object); - JingleIceUdpPrivate *priv = jingle_iceudp_get_instance_private(iceudp); - - purple_debug_info("jingle","jingle_iceudp_finalize\n"); - - g_list_free_full(priv->local_candidates, - (GDestroyNotify)jingle_iceudp_candidate_free); - g_list_free_full(priv->remote_candidates, - (GDestroyNotify)jingle_iceudp_candidate_free); - - G_OBJECT_CLASS(jingle_iceudp_parent_class)->finalize(object); -} - -static void -jingle_iceudp_class_finalize(G_GNUC_UNUSED JingleIceUdpClass *klass) -{ -} - -static void -jingle_iceudp_class_init (JingleIceUdpClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - JingleTransportClass *transport_class = JINGLE_TRANSPORT_CLASS(klass); - - obj_class->finalize = jingle_iceudp_finalize; - obj_class->set_property = jingle_iceudp_set_property; - obj_class->get_property = jingle_iceudp_get_property; - - transport_class->to_xml = jingle_iceudp_to_xml_internal; - transport_class->parse = jingle_iceudp_parse_internal; - transport_class->transport_type = JINGLE_TRANSPORT_ICEUDP; - transport_class->add_local_candidate = jingle_iceudp_add_local_candidate; - transport_class->get_remote_candidates = jingle_iceudp_get_remote_candidates; - - properties[PROP_LOCAL_CANDIDATES] = g_param_spec_pointer("local-candidates", - "Local candidates", - "The local candidates for this transport.", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - properties[PROP_REMOTE_CANDIDATES] = g_param_spec_pointer("remote-candidates", - "Remote candidates", - "The remote candidates for this transport.", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); -} - -/****************************************************************************** - * JingleIceUdpCandidate Boxed Implementation - *****************************************************************************/ -static JingleIceUdpCandidate * -jingle_iceudp_candidate_copy(JingleIceUdpCandidate *candidate) -{ - JingleIceUdpCandidate *new_candidate = g_new0(JingleIceUdpCandidate, 1); - new_candidate->id = g_strdup(candidate->id); - new_candidate->component = candidate->component; - new_candidate->foundation = g_strdup(candidate->foundation); - new_candidate->generation = candidate->generation; - new_candidate->ip = g_strdup(candidate->ip); - new_candidate->network = candidate->network; - new_candidate->port = candidate->port; - new_candidate->priority = candidate->priority; - new_candidate->protocol = g_strdup(candidate->protocol); - new_candidate->type = g_strdup(candidate->type); - - new_candidate->username = g_strdup(candidate->username); - new_candidate->password = g_strdup(candidate->password); - - new_candidate->rem_known = candidate->rem_known; - - return new_candidate; -} - -static void -jingle_iceudp_candidate_free(JingleIceUdpCandidate *candidate) -{ - g_free(candidate->foundation); - g_free(candidate->id); - g_free(candidate->ip); - g_free(candidate->protocol); - g_free(candidate->reladdr); - g_free(candidate->type); - - g_free(candidate->username); - g_free(candidate->password); - g_free(candidate); -} - -G_DEFINE_BOXED_TYPE(JingleIceUdpCandidate, jingle_iceudp_candidate, - jingle_iceudp_candidate_copy, jingle_iceudp_candidate_free) - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_iceudp_register(PurplePlugin *plugin) { - jingle_iceudp_register_type(G_TYPE_MODULE(plugin)); -} - -JingleIceUdpCandidate * -jingle_iceudp_candidate_new(const gchar *id, - guint component, const gchar *foundation, - guint generation, const gchar *ip, - guint network, guint port, guint priority, - const gchar *protocol, const gchar *type, - const gchar *username, const gchar *password) -{ - JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1); - candidate->id = g_strdup(id); - candidate->component = component; - candidate->foundation = g_strdup(foundation); - candidate->generation = generation; - candidate->ip = g_strdup(ip); - candidate->network = network; - candidate->port = port; - candidate->priority = priority; - candidate->protocol = g_strdup(protocol); - candidate->type = g_strdup(type); - - candidate->username = g_strdup(username); - candidate->password = g_strdup(password); - - candidate->rem_known = FALSE; - return candidate; -}
--- a/libpurple/protocols/jabber/jingle/iceudp.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/** - * @file iceudp.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_ICEUDP_H -#define PURPLE_JABBER_JINGLE_ICEUDP_H - -#include <glib.h> -#include <glib-object.h> - -#include "transport.h" - -G_BEGIN_DECLS - -#define JINGLE_TYPE_ICEUDP jingle_iceudp_get_type() -#define JINGLE_TYPE_ICEUDP_CANDIDATE jingle_iceudp_candidate_get_type() - -/** @copydoc _JingleIceUdpCandidate */ -typedef struct _JingleIceUdpCandidate JingleIceUdpCandidate; - -struct _JingleIceUdpCandidate -{ - gchar *id; - guint component; - gchar *foundation; - guint generation; - gchar *ip; - guint network; - guint port; - guint priority; - gchar *protocol; - gchar *reladdr; - guint relport; - gchar *type; - - gchar *username; - gchar *password; - - gboolean rem_known; /* TRUE if the remote side knows - * about this candidate */ -}; - -GType jingle_iceudp_candidate_get_type(void); - -/** - * Gets the iceudp class's GType - * - * @return The iceudp class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_FINAL_TYPE(JingleIceUdp, jingle_iceudp, JINGLE, ICEUDP, - JingleTransport) - -/** - * Registers the JingleIceUdp type in the type system. - */ -void jingle_iceudp_register(PurplePlugin *plugin); - -JingleIceUdpCandidate *jingle_iceudp_candidate_new(const gchar *id, - guint component, const gchar *foundation, guint generation, - const gchar *ip, guint network, guint port, guint priority, - const gchar *protocol, const gchar *type, - const gchar *username, const gchar *password); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_ICEUDP_H */ -
--- a/libpurple/protocols/jabber/jingle/jingle.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,493 +0,0 @@ -/* - * @file jingle.c - * - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purple.h> - -#include "content.h" -#include "jingle.h" -#include "session.h" -#include "iceudp.h" -#include "rawudp.h" -#include "rtp.h" - -#include <string.h> -#include <gst/gst.h> - -GType -jingle_get_type(const gchar *type) -{ - if (type == NULL) - return G_TYPE_NONE; - - if (purple_strequal(type, JINGLE_TRANSPORT_RAWUDP)) - return JINGLE_TYPE_RAWUDP; - else if (purple_strequal(type, JINGLE_TRANSPORT_ICEUDP)) - return JINGLE_TYPE_ICEUDP; - else if (purple_strequal(type, JINGLE_APP_RTP)) - return JINGLE_TYPE_RTP; - else - return G_TYPE_NONE; -} - -static void -jingle_handle_unknown_type(G_GNUC_UNUSED JingleSession *session, - G_GNUC_UNUSED PurpleXmlNode *jingle) -{ - /* Send error */ -} - -static void -jingle_handle_content_accept(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - jingle_session_accept_content(session, name, creator); - /* signal here */ - } -} - -static void -jingle_handle_content_add(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - JingleContent *pending_content = - jingle_content_parse(content); - if (pending_content == NULL) { - purple_debug_error("jingle", - "Error parsing \"content-add\" content.\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unsupported-applications")); - } else { - jingle_session_add_pending_content(session, - pending_content); - } - } - - /* XXX: signal here */ -} - -static void -jingle_handle_content_modify(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *local_content = jingle_session_find_content(session, name, creator); - - if (local_content != NULL) { - const gchar *senders = purple_xmlnode_get_attrib(content, "senders"); - gchar *local_senders = jingle_content_get_senders(local_content); - if (!purple_strequal(senders, local_senders)) - jingle_content_modify(local_content, senders); - g_free(local_senders); - } else { - purple_debug_error("jingle", "content_modify: unknown content\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unknown-applications")); - } - } -} - -static void -jingle_handle_content_reject(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - jingle_session_remove_pending_content(session, name, creator); - /* signal here */ - } -} - -static void -jingle_handle_content_remove(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - jingle_session_remove_content(session, name, creator); - } -} - -static void -jingle_handle_description_info(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - jingle_session_accept_session(session); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *parsed_content = - jingle_session_find_content(session, name, creator); - if (parsed_content == NULL) { - purple_debug_error("jingle", "Error parsing content\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unsupported-applications")); - } else { - jingle_content_handle_action(parsed_content, content, - JINGLE_DESCRIPTION_INFO); - } - } -} - -static void -jingle_handle_security_info(JingleSession *session, PurpleXmlNode *jingle) -{ - jabber_iq_send(jingle_session_create_ack(session, jingle)); -} - -static void -jingle_handle_session_accept(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - jingle_session_accept_session(session); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *parsed_content = - jingle_session_find_content(session, name, creator); - if (parsed_content == NULL) { - purple_debug_error("jingle", "Error parsing content\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unsupported-applications")); - } else { - jingle_content_handle_action(parsed_content, content, - JINGLE_SESSION_ACCEPT); - } - } -} - -static void -jingle_handle_session_info(JingleSession *session, PurpleXmlNode *jingle) -{ - jabber_iq_send(jingle_session_create_ack(session, jingle)); - /* XXX: call signal */ -} - -static void -jingle_handle_session_initiate(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - JingleContent *parsed_content = jingle_content_parse(content); - if (parsed_content == NULL) { - purple_debug_error("jingle", "Error parsing content\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unsupported-applications")); - } else { - jingle_session_add_content(session, parsed_content); - /* since we don't have the possibility to enable - * our local video after the video connection is - * established, we ignore senders of type - * 'initiator', and reply with senders of type - * 'both' instead. - */ - jingle_content_modify(parsed_content, "both"); - jingle_content_handle_action(parsed_content, content, - JINGLE_SESSION_INITIATE); - } - } - - jabber_iq_send(jingle_session_create_ack(session, jingle)); -} - -static void -jingle_handle_session_terminate(JingleSession *session, PurpleXmlNode *jingle) -{ - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - jingle_session_handle_action(session, jingle, - JINGLE_SESSION_TERMINATE); - /* display reason? */ - g_object_unref(session); -} - -static void -jingle_handle_transport_accept(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *content = jingle_session_find_content(session, name, creator); - jingle_content_accept_transport(content); - } -} - -static void -jingle_handle_transport_info(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *parsed_content = - jingle_session_find_content(session, name, creator); - if (parsed_content == NULL) { - purple_debug_error("jingle", "Error parsing content\n"); - jabber_iq_send(jingle_session_terminate_packet(session, - "unsupported-applications")); - } else { - jingle_content_handle_action(parsed_content, content, - JINGLE_TRANSPORT_INFO); - } - } -} - -static void -jingle_handle_transport_reject(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - JingleContent *content = jingle_session_find_content(session, name, creator); - jingle_content_remove_pending_transport(content); - } -} - -static void -jingle_handle_transport_replace(JingleSession *session, PurpleXmlNode *jingle) -{ - PurpleXmlNode *content = purple_xmlnode_get_child(jingle, "content"); - - jabber_iq_send(jingle_session_create_ack(session, jingle)); - - for (; content; content = purple_xmlnode_get_next_twin(content)) { - const gchar *name = purple_xmlnode_get_attrib(content, "name"); - const gchar *creator = purple_xmlnode_get_attrib(content, "creator"); - PurpleXmlNode *xmltransport = purple_xmlnode_get_child(content, "transport"); - JingleTransport *transport = jingle_transport_parse(xmltransport); - JingleContent *content = jingle_session_find_content(session, name, creator); - - jingle_content_set_pending_transport(content, transport); - } -} - -typedef struct { - const char *name; - void (*handler)(JingleSession*, PurpleXmlNode*); -} JingleAction; - -static const JingleAction jingle_actions[] = { - {"unknown-type", jingle_handle_unknown_type}, - {"content-accept", jingle_handle_content_accept}, - {"content-add", jingle_handle_content_add}, - {"content-modify", jingle_handle_content_modify}, - {"content-reject", jingle_handle_content_reject}, - {"content-remove", jingle_handle_content_remove}, - {"description-info", jingle_handle_description_info}, - {"security-info", jingle_handle_security_info}, - {"session-accept", jingle_handle_session_accept}, - {"session-info", jingle_handle_session_info}, - {"session-initiate", jingle_handle_session_initiate}, - {"session-terminate", jingle_handle_session_terminate}, - {"transport-accept", jingle_handle_transport_accept}, - {"transport-info", jingle_handle_transport_info}, - {"transport-reject", jingle_handle_transport_reject}, - {"transport-replace", jingle_handle_transport_replace}, -}; - -const gchar * -jingle_get_action_name(JingleActionType action) -{ - return jingle_actions[action].name; -} - -JingleActionType -jingle_get_action_type(const gchar *action) -{ - static const int num_actions = - sizeof(jingle_actions)/sizeof(JingleAction); - /* Start at 1 to skip the unknown-action type */ - int i = 1; - for (; i < num_actions; ++i) { - if (purple_strequal(action, jingle_actions[i].name)) - return i; - } - return JINGLE_UNKNOWN_TYPE; -} - -void -jingle_parse(JabberStream *js, const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *jingle) -{ - const gchar *action; - const gchar *sid; - JingleActionType action_type; - JingleSession *session; - - if (type != JABBER_IQ_SET) { - /* TODO: send iq error here */ - return; - } - - if (!(action = purple_xmlnode_get_attrib(jingle, "action"))) { - /* TODO: send iq error here */ - return; - } - - action_type = jingle_get_action_type(action); - - purple_debug_info("jabber", "got Jingle package action = %s\n", - action); - - if (!(sid = purple_xmlnode_get_attrib(jingle, "sid"))) { - /* send iq error here */ - return; - } - - if (!(session = jingle_session_find_by_sid(js, sid)) - && !purple_strequal(action, "session-initiate")) { - purple_debug_error("jingle", "jabber_jingle_session_parse couldn't find session\n"); - /* send iq error here */ - return; - } - - if (action_type == JINGLE_SESSION_INITIATE) { - if (session) { - /* This should only happen if you start a session with yourself */ - purple_debug_error("jingle", "Jingle session with " - "id={%s} already exists\n", sid); - /* send iq error */ - return; - } else { - char *own_jid = g_strdup_printf("%s@%s/%s", js->user->node, - js->user->domain, js->user->resource); - session = jingle_session_create(js, sid, own_jid, from, FALSE); - g_free(own_jid); - } - } - - jingle_actions[action_type].handler(session, jingle); -} - -void -jingle_terminate_sessions(JabberStream *js) -{ - if (js->sessions) { - GList *list = g_hash_table_get_values(js->sessions); - g_list_free_full(list, g_object_unref); - g_clear_pointer(&js->sessions, g_hash_table_unref); - } -} - -static void -jingle_create_relay_info(const gchar *ip, guint port, const gchar *username, - const gchar *password, const gchar *relay_type, GPtrArray *relay_info) -{ - GValue value; - GstStructure *turn_setup = gst_structure_new("relay-info", - "ip", G_TYPE_STRING, ip, - "port", G_TYPE_UINT, port, - "username", G_TYPE_STRING, username, - "password", G_TYPE_STRING, password, - "relay-type", G_TYPE_STRING, relay_type, - NULL); - purple_debug_info("jabber", "created gst_structure %p\n", turn_setup); - if (turn_setup) { - memset(&value, 0, sizeof(GValue)); - g_value_init(&value, GST_TYPE_STRUCTURE); - gst_value_set_structure(&value, turn_setup); - g_ptr_array_add(relay_info, &value); - gst_structure_free(turn_setup); - } -} - -static void -jingle_param_free(GValue *value) -{ - g_value_unset(value); - g_free(value); -} - -GHashTable * -jingle_get_params(G_GNUC_UNUSED JabberStream *js, const gchar *relay_ip, - guint relay_udp, guint relay_tcp, guint relay_ssltcp, - const gchar *relay_username, const gchar *relay_password) -{ - GHashTable *params = NULL; - GValue *value = NULL; - - if(relay_ip) { - GPtrArray *relay_info = g_ptr_array_new_full(1, (GDestroyNotify)gst_structure_free); - - params = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)jingle_param_free); - - if (relay_udp) { - jingle_create_relay_info(relay_ip, relay_udp, relay_username, - relay_password, "udp", relay_info); - } - if (relay_tcp) { - jingle_create_relay_info(relay_ip, relay_tcp, relay_username, - relay_password, "tcp", relay_info); - } - if (relay_ssltcp) { - jingle_create_relay_info(relay_ip, relay_ssltcp, relay_username, - relay_password, "tls", relay_info); - } - value = g_new(GValue, 1); - g_value_init(value, G_TYPE_PTR_ARRAY); - g_value_take_boxed(value, relay_info); - g_hash_table_insert(params, "relay-info", value); - } - - return params; -}
--- a/libpurple/protocols/jabber/jingle/jingle.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * @file jingle.h - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA - */ - -#ifndef PURPLE_JABBER_JINGLE_H -#define PURPLE_JABBER_JINGLE_H - -#include "jabber.h" - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define JINGLE "urn:xmpp:jingle:1" -#define JINGLE_ERROR "urn:xmpp:jingle:errors:0" -#define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1" -#define JINGLE_APP_RTP "urn:xmpp:jingle:apps:rtp:1" -#define JINGLE_APP_RTP_ERROR "urn:xmpp:jingle:apps:rtp:errors:1" -#define JINGLE_APP_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:1" -#define JINGLE_APP_RTP_SUPPORT_AUDIO "urn:xmpp:jingle:apps:rtp:audio" -#define JINGLE_APP_RTP_SUPPORT_VIDEO "urn:xmpp:jingle:apps:rtp:video" -#define JINGLE_APP_XML "urn:xmpp:tmp:jingle:apps:xmlstream" -#define JINGLE_DTMF "urn:xmpp:jingle:dtmf:0" -#define JINGLE_TRANSPORT_S5B "urn:xmpp:jingle:transports:s5b:0" -#define JINGLE_TRANSPORT_IBB "urn:xmpp:jingle:transports:ibb:0" -#define JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:1" -#define JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:1" - -typedef enum { - JINGLE_UNKNOWN_TYPE, - JINGLE_CONTENT_ACCEPT, - JINGLE_CONTENT_ADD, - JINGLE_CONTENT_MODIFY, - JINGLE_CONTENT_REJECT, - JINGLE_CONTENT_REMOVE, - JINGLE_DESCRIPTION_INFO, - JINGLE_SECURITY_INFO, - JINGLE_SESSION_ACCEPT, - JINGLE_SESSION_INFO, - JINGLE_SESSION_INITIATE, - JINGLE_SESSION_TERMINATE, - JINGLE_TRANSPORT_ACCEPT, - JINGLE_TRANSPORT_INFO, - JINGLE_TRANSPORT_REJECT, - JINGLE_TRANSPORT_REPLACE, -} JingleActionType; - -const gchar *jingle_get_action_name(JingleActionType action); -JingleActionType jingle_get_action_type(const gchar *action); - -GType jingle_get_type(const gchar *type); - -void jingle_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, PurpleXmlNode *child); - -void jingle_terminate_sessions(JabberStream *js); - -/* create a parameter array given autoconfigured STUN (and later perhaps TURN). */ -GHashTable *jingle_get_params(JabberStream *js, const gchar *relay_ip, - guint relay_udp, guint relay_tcp, guint relay_ssltcp, - const gchar *relay_username, const gchar *relay_password); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_H */
--- a/libpurple/protocols/jabber/jingle/rawudp.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,371 +0,0 @@ -/** - * @file rawudp.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "rawudp.h" -#include "jingle.h" - -#include <string.h> - -struct _JingleRawUdp -{ - JingleTransport parent; -}; - -typedef struct -{ - GList *local_candidates; - GList *remote_candidates; -} JingleRawUdpPrivate; - -enum { - PROP_0, - PROP_LOCAL_CANDIDATES, - PROP_REMOTE_CANDIDATES, - N_PROPERTIES, -}; -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JingleRawUdp, - jingle_rawudp, - JINGLE_TYPE_TRANSPORT, - G_TYPE_FLAG_FINAL, - G_ADD_PRIVATE_DYNAMIC(JingleRawUdp) -); - -/****************************************************************************** - * JingleRawUdp Transport Implementation - *****************************************************************************/ -static GList * -jingle_rawudp_get_remote_candidates(JingleTransport *transport) -{ - JingleRawUdp *rawudp = JINGLE_RAWUDP(transport); - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - GList *candidates = priv->remote_candidates; - GList *ret = NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - JingleRawUdpCandidate *candidate = candidates->data; - ret = g_list_append(ret, purple_media_candidate_new("", - candidate->component, - PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - candidate->ip, candidate->port)); - } - - return ret; -} - -static JingleRawUdpCandidate * -jingle_rawudp_get_remote_candidate_by_id(JingleRawUdp *rawudp, gchar *id) -{ - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - - GList *iter = priv->remote_candidates; - for (; iter; iter = g_list_next(iter)) { - JingleRawUdpCandidate *candidate = iter->data; - if (purple_strequal(candidate->id, id)) { - return candidate; - } - } - return NULL; -} - -static void -jingle_rawudp_add_remote_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate) -{ - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - JingleRawUdpCandidate *rawudp_candidate = - jingle_rawudp_get_remote_candidate_by_id(rawudp, candidate->id); - if (rawudp_candidate != NULL) { - priv->remote_candidates = g_list_remove( - priv->remote_candidates, rawudp_candidate); - g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate); - } - priv->remote_candidates = g_list_append(priv->remote_candidates, candidate); - - g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_REMOTE_CANDIDATES]); -} - -static PurpleXmlNode * -jingle_rawudp_to_xml_internal(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action) -{ - PurpleXmlNode *node = JINGLE_TRANSPORT_CLASS(jingle_rawudp_parent_class)->to_xml(transport, content, action); - - if (action == JINGLE_SESSION_INITIATE || - action == JINGLE_TRANSPORT_INFO || - action == JINGLE_SESSION_ACCEPT) { - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(JINGLE_RAWUDP(transport)); - GList *iter = priv->local_candidates; - - for (; iter; iter = g_list_next(iter)) { - JingleRawUdpCandidate *candidate = iter->data; - PurpleXmlNode *xmltransport; - gchar *generation, *component, *port; - - if (candidate->rem_known == TRUE) - continue; - candidate->rem_known = TRUE; - - xmltransport = purple_xmlnode_new_child(node, "candidate"); - generation = g_strdup_printf("%d", candidate->generation); - component = g_strdup_printf("%d", candidate->component); - port = g_strdup_printf("%d", candidate->port); - - purple_xmlnode_set_attrib(xmltransport, "generation", generation); - purple_xmlnode_set_attrib(xmltransport, "component", component); - purple_xmlnode_set_attrib(xmltransport, "id", candidate->id); - purple_xmlnode_set_attrib(xmltransport, "ip", candidate->ip); - purple_xmlnode_set_attrib(xmltransport, "port", port); - - g_free(port); - g_free(generation); - } - } - - return node; -} - -static JingleTransport * -jingle_rawudp_parse_internal(PurpleXmlNode *rawudp) -{ - JingleTransport *transport = JINGLE_TRANSPORT_CLASS(jingle_rawudp_parent_class)->parse(rawudp); - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(JINGLE_RAWUDP(transport)); - PurpleXmlNode *candidate = purple_xmlnode_get_child(rawudp, "candidate"); - JingleRawUdpCandidate *rawudp_candidate = NULL; - - for (; candidate; candidate = purple_xmlnode_get_next_twin(candidate)) { - const gchar *id = purple_xmlnode_get_attrib(candidate, "id"); - const gchar *generation = purple_xmlnode_get_attrib(candidate, "generation"); - const gchar *component = purple_xmlnode_get_attrib(candidate, "component"); - const gchar *ip = purple_xmlnode_get_attrib(candidate, "ip"); - const gchar *port = purple_xmlnode_get_attrib(candidate, "port"); - - if (!id || !generation || !component || !ip || !port) - continue; - - rawudp_candidate = jingle_rawudp_candidate_new( - id, - atoi(generation), - atoi(component), - ip, - atoi(port)); - rawudp_candidate->rem_known = TRUE; - jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate); - } - - if (rawudp_candidate != NULL && - g_list_length(priv->remote_candidates) == 1) { - /* manufacture rtcp candidate */ - rawudp_candidate = g_boxed_copy(JINGLE_TYPE_RAWUDP_CANDIDATE, rawudp_candidate); - rawudp_candidate->component = 2; - rawudp_candidate->port = rawudp_candidate->port + 1; - rawudp_candidate->rem_known = TRUE; - jingle_rawudp_add_remote_candidate(JINGLE_RAWUDP(transport), rawudp_candidate); - } - - return transport; -} - -static void -jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate) -{ - JingleRawUdp *rawudp = JINGLE_RAWUDP(transport); - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - gchar *ip; - JingleRawUdpCandidate *rawudp_candidate; - GList *iter; - - ip = purple_media_candidate_get_ip(candidate); - rawudp_candidate = jingle_rawudp_candidate_new(id, generation, - purple_media_candidate_get_component_id(candidate), - ip, purple_media_candidate_get_port(candidate)); - g_free(ip); - - for (iter = priv->local_candidates; iter; iter = g_list_next(iter)) { - JingleRawUdpCandidate *c = iter->data; - if (purple_strequal(c->id, id)) { - generation = c->generation + 1; - - g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c); - priv->local_candidates = g_list_delete_link( - priv->local_candidates, iter); - - rawudp_candidate->generation = generation; - - priv->local_candidates = g_list_append( - priv->local_candidates, rawudp_candidate); - - g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_LOCAL_CANDIDATES]); - - return; - } - } - - priv->local_candidates = g_list_append( - priv->local_candidates, rawudp_candidate); - - g_object_notify_by_pspec(G_OBJECT(rawudp), properties[PROP_LOCAL_CANDIDATES]); -} - -/****************************************************************************** - * JingleRawUdp GObject Stuff - *****************************************************************************/ -static void -jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - JingleRawUdp *rawudp = JINGLE_RAWUDP(object); - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - - switch (prop_id) { - case PROP_LOCAL_CANDIDATES: - priv->local_candidates = g_value_get_pointer(value); - break; - case PROP_REMOTE_CANDIDATES: - priv->remote_candidates = g_value_get_pointer(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_rawudp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - JingleRawUdp *rawudp = JINGLE_RAWUDP(object); - JingleRawUdpPrivate *priv = jingle_rawudp_get_instance_private(rawudp); - - switch (prop_id) { - case PROP_LOCAL_CANDIDATES: - g_value_set_pointer(value, priv->local_candidates); - break; - case PROP_REMOTE_CANDIDATES: - g_value_set_pointer(value, priv->remote_candidates); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_rawudp_init(G_GNUC_UNUSED JingleRawUdp *rawudp) -{ -} - -static void -jingle_rawudp_finalize (GObject *rawudp) -{ -/* JingleRawUdpPrivate *priv = JINGLE_RAWUDP_GET_PRIVATE(rawudp); */ - purple_debug_info("jingle","jingle_rawudp_finalize\n"); - - G_OBJECT_CLASS(jingle_rawudp_parent_class)->finalize(rawudp); -} - -static void -jingle_rawudp_class_finalize(G_GNUC_UNUSED JingleRawUdpClass *klass) { -} - -static void -jingle_rawudp_class_init (JingleRawUdpClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - JingleTransportClass *transport_class = JINGLE_TRANSPORT_CLASS(klass); - - obj_class->finalize = jingle_rawudp_finalize; - obj_class->set_property = jingle_rawudp_set_property; - obj_class->get_property = jingle_rawudp_get_property; - - transport_class->to_xml = jingle_rawudp_to_xml_internal; - transport_class->parse = jingle_rawudp_parse_internal; - transport_class->transport_type = JINGLE_TRANSPORT_RAWUDP; - transport_class->add_local_candidate = jingle_rawudp_add_local_candidate; - transport_class->get_remote_candidates = jingle_rawudp_get_remote_candidates; - - properties[PROP_LOCAL_CANDIDATES] = g_param_spec_pointer("local-candidates", - "Local candidates", - "The local candidates for this transport.", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - properties[PROP_REMOTE_CANDIDATES] = g_param_spec_pointer("remote-candidates", - "Remote candidates", - "The remote candidates for this transport.", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); -} - -/****************************************************************************** - * JingleRawUdpCandidate Boxed Type - *****************************************************************************/ -static JingleRawUdpCandidate * -jingle_rawudp_candidate_copy(JingleRawUdpCandidate *candidate) -{ - JingleRawUdpCandidate *new_candidate = g_new0(JingleRawUdpCandidate, 1); - new_candidate->generation = candidate->generation; - new_candidate->component = candidate->component; - new_candidate->id = g_strdup(candidate->id); - new_candidate->ip = g_strdup(candidate->ip); - new_candidate->port = candidate->port; - - new_candidate->rem_known = candidate->rem_known; - return new_candidate; -} - -static void -jingle_rawudp_candidate_free(JingleRawUdpCandidate *candidate) -{ - g_free(candidate->id); - g_free(candidate->ip); -} - -G_DEFINE_BOXED_TYPE(JingleRawUdpCandidate, jingle_rawudp_candidate, - jingle_rawudp_candidate_copy, jingle_rawudp_candidate_free) - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_rawudp_register(PurplePlugin *plugin) { - jingle_rawudp_register_type(G_TYPE_MODULE(plugin)); -} - -JingleRawUdpCandidate * -jingle_rawudp_candidate_new(const gchar *id, guint generation, guint component, const gchar *ip, guint port) -{ - JingleRawUdpCandidate *candidate = g_new0(JingleRawUdpCandidate, 1); - candidate->generation = generation; - candidate->component = component; - candidate->id = g_strdup(id); - candidate->ip = g_strdup(ip); - candidate->port = port; - - candidate->rem_known = FALSE; - return candidate; -}
--- a/libpurple/protocols/jabber/jingle/rawudp.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/** - * @file rawudp.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_RAWUDP_H -#define PURPLE_JABBER_JINGLE_RAWUDP_H - -#include <glib.h> -#include <glib-object.h> - -#include "transport.h" - -G_BEGIN_DECLS - -#define JINGLE_TYPE_RAWUDP jingle_rawudp_get_type() -#define JINGLE_TYPE_RAWUDP_CANDIDATE jingle_rawudp_candidate_get_type() - -/** @copydoc _JingleRawUdpCandidate */ -typedef struct _JingleRawUdpCandidate JingleRawUdpCandidate; - -struct _JingleRawUdpCandidate -{ - guint generation; - guint component; - gchar *id; - gchar *ip; - guint port; - - gboolean rem_known; /* TRUE if the remote side knows - * about this candidate */ -}; - -GType jingle_rawudp_candidate_get_type(void); - -/** - * Gets the rawudp class's GType - * - * @return The rawudp class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_FINAL_TYPE(JingleRawUdp, jingle_rawudp, JINGLE, RAWUDP, - JingleTransport) - -/** - * Registers the JingleRawUdp type in the type system. - */ -void jingle_rawudp_register(PurplePlugin *plugin); - -JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id, - guint generation, guint component, const gchar *ip, guint port); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_RAWUDP_H */ -
--- a/libpurple/protocols/jabber/jingle/rtp.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,933 +0,0 @@ -/** - * @file rtp.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <purpleconfig.h> - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "jabber.h" -#include "jingle.h" -#include "iceudp.h" -#include "rawudp.h" -#include "rtp.h" -#include "session.h" - -#include <string.h> - -struct _JingleRtp -{ - JingleContent parent; -}; - -typedef struct -{ - gchar *media_type; - gchar *ssrc; -} JingleRtpPrivate; - -static JingleContent *jingle_rtp_parse_internal(PurpleXmlNode *rtp); -static PurpleXmlNode *jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action); -static void jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *jingle, JingleActionType action); - -static PurpleMedia *jingle_rtp_get_media(JingleSession *session); - -enum { - PROP_0, - PROP_MEDIA_TYPE, - PROP_SSRC, - N_PROPERTIES, -}; - -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JingleRtp, - jingle_rtp, - JINGLE_TYPE_CONTENT, - G_TYPE_FLAG_FINAL, - G_ADD_PRIVATE_DYNAMIC(JingleRtp) -); - -/****************************************************************************** - * Helpers - *****************************************************************************/ -static JingleTransport * -jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates) -{ - JingleTransport *transport; - - transport = jingle_transport_create(type); - if (!transport) - return NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - PurpleMediaCandidate *candidate = candidates->data; - if (purple_media_candidate_get_protocol(candidate) == - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) { - gchar *id = jabber_get_next_id(jingle_session_get_js(session)); - jingle_transport_add_local_candidate(transport, id, generation, candidate); - g_free(id); - } - } - - return transport; -} - -static void jingle_rtp_ready(JingleSession *session); - -static void -jingle_rtp_candidates_prepared_cb(PurpleMedia *media, - gchar *sid, gchar *name, JingleSession *session) -{ - JingleContent *content = jingle_session_find_content( - session, sid, NULL); - JingleTransport *oldtransport, *transport; - GList *candidates; - - purple_debug_info("jingle-rtp", "jingle_rtp_candidates_prepared_cb\n"); - - if (content == NULL) { - purple_debug_error("jingle-rtp", - "jingle_rtp_candidates_prepared_cb: " - "Can't find session %s\n", sid); - return; - } - - oldtransport = jingle_content_get_transport(content); - candidates = purple_media_get_local_candidates(media, sid, name); - transport = jingle_rtp_candidates_to_transport( - session, jingle_transport_get_transport_type(oldtransport), - 0, candidates); - - purple_media_candidate_list_free(candidates); - g_object_unref(oldtransport); - - jingle_content_set_pending_transport(content, transport); - jingle_content_accept_transport(content); - - jingle_rtp_ready(session); -} - -static void -jingle_rtp_codecs_changed_cb(G_GNUC_UNUSED PurpleMedia *media, char *sid, - JingleSession *session) -{ - purple_debug_info("jingle-rtp", "jingle_rtp_codecs_changed_cb: " - "session_id: %s jingle_session: %p\n", sid, session); - jingle_rtp_ready(session); -} - -static void -jingle_rtp_new_candidate_cb(G_GNUC_UNUSED PurpleMedia *media, char *sid, - G_GNUC_UNUSED char *name, - PurpleMediaCandidate *candidate, - JingleSession *session) -{ - JingleContent *content = jingle_session_find_content(session, sid, NULL); - JingleTransport *transport; - gchar *id; - - purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n"); - - if (content == NULL) { - purple_debug_error("jingle-rtp", - "jingle_rtp_new_candidate_cb: " - "Can't find session %s\n", sid); - return; - } - - transport = jingle_content_get_transport(content); - - id = jabber_get_next_id(jingle_session_get_js(session)); - jingle_transport_add_local_candidate(transport, id, 1, candidate); - g_free(id); - - g_object_unref(transport); - - jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO)); -} - -static void -jingle_rtp_initiate_ack_cb(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - gpointer data) -{ - JingleSession *session = data; - - if (type == JABBER_IQ_ERROR || purple_xmlnode_get_child(packet, "error")) { - purple_media_end(jingle_rtp_get_media(session), NULL, NULL); - g_object_unref(session); - return; - } -} - -static void -jingle_rtp_state_changed_cb(G_GNUC_UNUSED PurpleMedia *media, - PurpleMediaState state, char *sid, char *name, - G_GNUC_UNUSED JingleSession *session) -{ - purple_debug_info("jingle-rtp", "state-changed: state %d " - "id: %s name: %s\n", state, sid ? sid : "(null)", - name ? name : "(null)"); -} - -static void -jingle_rtp_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, - char *sid, char *name, G_GNUC_UNUSED gboolean local, - JingleSession *session) -{ - purple_debug_info("jingle-rtp", "stream-info: type %d " - "id: %s name: %s\n", type, sid ? sid : "(null)", - name ? name : "(null)"); - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - if (type == PURPLE_MEDIA_INFO_HANGUP || - type == PURPLE_MEDIA_INFO_REJECT) { - jabber_iq_send(jingle_session_terminate_packet( - session, type == PURPLE_MEDIA_INFO_HANGUP ? - "success" : "decline")); - - g_signal_handlers_disconnect_by_func(media, - jingle_rtp_state_changed_cb, - session); - g_signal_handlers_disconnect_by_func(media, - jingle_rtp_stream_info_cb, - session); - g_signal_handlers_disconnect_by_func(media, - jingle_rtp_new_candidate_cb, - session); - - g_object_unref(session); - /* The same signal is emitted *four* times in case of acceptance - * by purple_media_stream_info() (stream acceptance, session - * acceptance, participant acceptance, and conference acceptance). - * We only react to the first one, where sid and name are given - * non-null values. - */ - } else if (type == PURPLE_MEDIA_INFO_ACCEPT && sid && name && - jingle_session_is_initiator(session) == FALSE) { - - jingle_rtp_ready(session); - } -} - -static void -jingle_rtp_ready(JingleSession *session) -{ - PurpleMedia *media = jingle_rtp_get_media(session); - - if (purple_media_candidates_prepared(media, NULL, NULL) && - purple_media_codecs_ready(media, NULL) && - (jingle_session_is_initiator(session) == TRUE || - purple_media_accepted(media, NULL, NULL))) { - if (jingle_session_is_initiator(session)) { - JabberIq *iq = jingle_session_to_packet( - session, JINGLE_SESSION_INITIATE); - jabber_iq_set_callback(iq, - jingle_rtp_initiate_ack_cb, session); - jabber_iq_send(iq); - } else { - jabber_iq_send(jingle_session_to_packet(session, - JINGLE_SESSION_ACCEPT)); - } - - g_signal_handlers_disconnect_by_func(media, - jingle_rtp_candidates_prepared_cb, - session); - g_signal_handlers_disconnect_by_func(media, - jingle_rtp_codecs_changed_cb, - session); - g_signal_connect(media, "new-candidate", - G_CALLBACK(jingle_rtp_new_candidate_cb), session); - } -} - -static PurpleMedia * -jingle_rtp_create_media(JingleContent *content) -{ - JingleSession *session = jingle_content_get_session(content); - JabberStream *js = jingle_session_get_js(session); - gchar *remote_jid = jingle_session_get_remote_jid(session); - - PurpleMedia *media = purple_media_manager_create_media( - purple_media_manager_get(), - purple_connection_get_account(js->gc), - "fsrtpconference", remote_jid, - jingle_session_is_initiator(session)); - g_free(remote_jid); - - if (!media) { - purple_debug_error("jingle-rtp", "Couldn't create media session\n"); - return NULL; - } - - purple_media_set_protocol_data(media, session); - - /* connect callbacks */ - g_signal_connect(G_OBJECT(media), "candidates-prepared", - G_CALLBACK(jingle_rtp_candidates_prepared_cb), session); - g_signal_connect(G_OBJECT(media), "codecs-changed", - G_CALLBACK(jingle_rtp_codecs_changed_cb), session); - g_signal_connect(G_OBJECT(media), "state-changed", - G_CALLBACK(jingle_rtp_state_changed_cb), session); - g_signal_connect(G_OBJECT(media), "stream-info", - G_CALLBACK(jingle_rtp_stream_info_cb), session); - - g_object_unref(session); - return media; -} - -static gboolean -jingle_rtp_init_media(JingleContent *content) -{ - JingleSession *session = jingle_content_get_session(content); - PurpleMedia *media = jingle_rtp_get_media(session); - gchar *creator; - gchar *media_type; - gchar *remote_jid; - gchar *senders; - gchar *name; - const gchar *transmitter; - gboolean is_audio; - gboolean is_creator; - PurpleMediaSessionType type; - JingleTransport *transport; - GHashTable *params = NULL; - - /* maybe this create ought to just be in initiate and handle initiate */ - if (media == NULL) { - media = jingle_rtp_create_media(content); - - if (media == NULL) - return FALSE; - } - - media_type = jingle_rtp_get_media_type(content); - if (media_type == NULL) { - g_object_unref(session); - return FALSE; - } - - name = jingle_content_get_name(content); - remote_jid = jingle_session_get_remote_jid(session); - senders = jingle_content_get_senders(content); - transport = jingle_content_get_transport(content); - - if (JINGLE_IS_RAWUDP(transport)) - transmitter = "rawudp"; - else if (JINGLE_IS_ICEUDP(transport)) - transmitter = "nice"; - else - transmitter = "notransmitter"; - g_object_unref(transport); - - is_audio = purple_strequal(media_type, "audio"); - - if (purple_strequal(senders, "both")) - type = is_audio ? PURPLE_MEDIA_AUDIO - : PURPLE_MEDIA_VIDEO; - else if (purple_strequal(senders, "initiator") == - jingle_session_is_initiator(session)) - type = is_audio ? PURPLE_MEDIA_SEND_AUDIO - : PURPLE_MEDIA_SEND_VIDEO; - else - type = is_audio ? PURPLE_MEDIA_RECV_AUDIO - : PURPLE_MEDIA_RECV_VIDEO; - - params = - jingle_get_params(jingle_session_get_js(session), NULL, 0, 0, 0, - NULL, NULL); - - creator = jingle_content_get_creator(content); - if (creator == NULL) { - g_free(name); - g_free(media_type); - g_free(remote_jid); - g_free(senders); - g_hash_table_destroy(params); - g_object_unref(session); - return FALSE; - } - - if (purple_strequal(creator, "initiator")) - is_creator = jingle_session_is_initiator(session); - else - is_creator = !jingle_session_is_initiator(session); - g_free(creator); - - if(!purple_media_add_stream(media, name, remote_jid, - type, is_creator, transmitter, params)) { - purple_media_end(media, NULL, NULL); - /* TODO: How much clean-up is necessary here? (does calling - purple_media_end lead to cleaning up Jingle structs?) */ - return FALSE; - } - - g_free(name); - g_free(media_type); - g_free(remote_jid); - g_free(senders); - g_hash_table_destroy(params); - g_object_unref(session); - - return TRUE; -} - -static GList * -jingle_rtp_parse_codecs(PurpleXmlNode *description) -{ - GList *codecs = NULL; - PurpleXmlNode *codec_element = NULL; - const char *encoding_name,*id, *clock_rate; - PurpleMediaCodec *codec; - const gchar *media = purple_xmlnode_get_attrib(description, "media"); - PurpleMediaSessionType type; - - if (media == NULL) { - purple_debug_warning("jingle-rtp", "missing media type\n"); - return NULL; - } - - if (purple_strequal(media, "video")) { - type = PURPLE_MEDIA_VIDEO; - } else if (purple_strequal(media, "audio")) { - type = PURPLE_MEDIA_AUDIO; - } else { - purple_debug_warning("jingle-rtp", "unknown media type: %s\n", - media); - return NULL; - } - - for (codec_element = purple_xmlnode_get_child(description, "payload-type") ; - codec_element ; - codec_element = purple_xmlnode_get_next_twin(codec_element)) { - PurpleXmlNode *param; - gchar *codec_str; - encoding_name = purple_xmlnode_get_attrib(codec_element, "name"); - - id = purple_xmlnode_get_attrib(codec_element, "id"); - clock_rate = purple_xmlnode_get_attrib(codec_element, "clockrate"); - - codec = purple_media_codec_new(atoi(id), encoding_name, - type, - clock_rate ? atoi(clock_rate) : 0); - - for (param = purple_xmlnode_get_child(codec_element, "parameter"); - param; param = purple_xmlnode_get_next_twin(param)) { - purple_media_codec_add_optional_parameter(codec, - purple_xmlnode_get_attrib(param, "name"), - purple_xmlnode_get_attrib(param, "value")); - } - - codec_str = purple_media_codec_to_string(codec); - purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str); - g_free(codec_str); - - codecs = g_list_append(codecs, codec); - } - return codecs; -} - -static void -add_parameter_to_payload(PurpleKeyValuePair *mparam, PurpleXmlNode *payload) -{ - PurpleXmlNode *param = purple_xmlnode_new_child(payload, "parameter"); - purple_xmlnode_set_attrib(param, "name", mparam->key); - purple_xmlnode_set_attrib(param, "value", mparam->value); -} - -static void -jingle_rtp_add_payload(gpointer codec, gpointer description) -{ - gchar *id, *name, *clockrate, *channels; - gchar *codec_str; - PurpleXmlNode *payload = purple_xmlnode_new_child(description, "payload-type"); - - id = g_strdup_printf("%d", purple_media_codec_get_id(codec)); - name = purple_media_codec_get_encoding_name(codec); - clockrate = g_strdup_printf("%d", purple_media_codec_get_clock_rate(codec)); - channels = g_strdup_printf("%d", purple_media_codec_get_channels(codec)); - - purple_xmlnode_set_attrib(payload, "name", name); - purple_xmlnode_set_attrib(payload, "id", id); - purple_xmlnode_set_attrib(payload, "clockrate", clockrate); - purple_xmlnode_set_attrib(payload, "channels", channels); - - g_free(channels); - g_free(clockrate); - g_free(name); - g_free(id); - - g_list_foreach(purple_media_codec_get_optional_parameters(codec), - (GFunc)add_parameter_to_payload, payload); - - codec_str = purple_media_codec_to_string(codec); - purple_debug_info("jingle", "adding codec: %s", codec_str); - g_free(codec_str); -} - -/****************************************************************************** - * JingleContent Implementation - *****************************************************************************/ -static PurpleXmlNode * -jingle_rtp_to_xml_internal(JingleContent *rtp, PurpleXmlNode *content, JingleActionType action) -{ - PurpleXmlNode *node = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->to_xml(rtp, content, action); - PurpleXmlNode *description = purple_xmlnode_get_child(node, "description"); - if (description != NULL) { - JingleSession *session = jingle_content_get_session(rtp); - PurpleMedia *media = jingle_rtp_get_media(session); - gchar *media_type = jingle_rtp_get_media_type(rtp); - gchar *ssrc = jingle_rtp_get_ssrc(rtp); - gchar *name = jingle_content_get_name(rtp); - GList *codecs = purple_media_get_codecs(media, name); - - purple_xmlnode_set_attrib(description, "media", media_type); - - if (ssrc != NULL) - purple_xmlnode_set_attrib(description, "ssrc", ssrc); - - g_free(media_type); - g_free(name); - g_object_unref(session); - - g_list_foreach(codecs, jingle_rtp_add_payload, description); - purple_media_codec_list_free(codecs); - } - return node; -} - -static JingleContent * -jingle_rtp_parse_internal(PurpleXmlNode *rtp) -{ - JingleContent *content = JINGLE_CONTENT_CLASS(jingle_rtp_parent_class)->parse(rtp); - PurpleXmlNode *description = purple_xmlnode_get_child(rtp, "description"); - const gchar *media_type = purple_xmlnode_get_attrib(description, "media"); - const gchar *ssrc = purple_xmlnode_get_attrib(description, "ssrc"); - purple_debug_info("jingle-rtp", "rtp parse\n"); - g_object_set(content, "media-type", media_type, NULL); - if (ssrc != NULL) - g_object_set(content, "ssrc", ssrc, NULL); - return content; -} - -static void -jingle_rtp_handle_action_internal(JingleContent *content, PurpleXmlNode *xmlcontent, JingleActionType action) -{ - switch (action) { - case JINGLE_SESSION_ACCEPT: - case JINGLE_SESSION_INITIATE: { - JingleSession *session; - JingleTransport *transport; - PurpleXmlNode *description; - GList *candidates; - GList *codecs; - gchar *name; - gchar *remote_jid; - PurpleMedia *media; - - session = jingle_content_get_session(content); - - if (action == JINGLE_SESSION_INITIATE && - !jingle_rtp_init_media(content)) { - /* XXX: send error */ - jabber_iq_send(jingle_session_terminate_packet( - session, "general-error")); - g_object_unref(session); - break; - } - - transport = jingle_transport_parse( - purple_xmlnode_get_child(xmlcontent, "transport")); - description = purple_xmlnode_get_child(xmlcontent, "description"); - candidates = jingle_transport_get_remote_candidates(transport); - codecs = jingle_rtp_parse_codecs(description); - name = jingle_content_get_name(content); - remote_jid = jingle_session_get_remote_jid(session); - - media = jingle_rtp_get_media(session); - purple_media_set_remote_codecs(media, - name, remote_jid, codecs); - purple_media_add_remote_candidates(media, - name, remote_jid, candidates); - - if (action == JINGLE_SESSION_ACCEPT) - purple_media_stream_info(media, - PURPLE_MEDIA_INFO_ACCEPT, - name, remote_jid, FALSE); - - g_free(remote_jid); - g_free(name); - g_object_unref(session); - g_object_unref(transport); - purple_media_candidate_list_free(candidates); - purple_media_codec_list_free(codecs); - break; - } - case JINGLE_SESSION_TERMINATE: { - JingleSession *session = jingle_content_get_session(content); - PurpleMedia *media = jingle_rtp_get_media(session); - - if (media != NULL) { - purple_media_end(media, NULL, NULL); - } - - g_object_unref(session); - break; - } - case JINGLE_TRANSPORT_INFO: { - JingleSession *session = jingle_content_get_session(content); - JingleTransport *transport = jingle_transport_parse( - purple_xmlnode_get_child(xmlcontent, "transport")); - GList *candidates = jingle_transport_get_remote_candidates(transport); - gchar *name = jingle_content_get_name(content); - gchar *remote_jid = - jingle_session_get_remote_jid(session); - - purple_media_add_remote_candidates( - jingle_rtp_get_media(session), - name, remote_jid, candidates); - - g_free(remote_jid); - g_free(name); - g_object_unref(session); - g_object_unref(transport); - purple_media_candidate_list_free(candidates); - break; - } - case JINGLE_DESCRIPTION_INFO: { - JingleSession *session = - jingle_content_get_session(content); - PurpleXmlNode *description = purple_xmlnode_get_child( - xmlcontent, "description"); - GList *codecs, *iter, *iter2, *remote_codecs = - jingle_rtp_parse_codecs(description); - gchar *name = jingle_content_get_name(content); - gchar *remote_jid = - jingle_session_get_remote_jid(session); - PurpleMedia *media; - - media = jingle_rtp_get_media(session); - - /* - * This may have problems if description-info is - * received without the optional parameters for a - * codec with configuration info (such as THEORA - * or H264). The local configuration info may be - * set for the remote codec. - * - * As of 2.6.3 there's no API to support getting - * the remote codecs specifically, just the - * intersection. Another option may be to cache - * the remote codecs received in initiate/accept. - */ - codecs = purple_media_get_codecs(media, name); - - for (iter = codecs; iter; iter = g_list_next(iter)) { - guint id; - - id = purple_media_codec_get_id(iter->data); - iter2 = remote_codecs; - - for (; iter2; iter2 = g_list_next(iter2)) { - if (purple_media_codec_get_id( - iter2->data) != id) - continue; - - g_object_unref(iter->data); - iter->data = iter2->data; - remote_codecs = g_list_delete_link( - remote_codecs, iter2); - break; - } - } - - codecs = g_list_concat(codecs, remote_codecs); - - purple_media_set_remote_codecs(media, - name, remote_jid, codecs); - - purple_media_codec_list_free (codecs); - g_free(remote_jid); - g_free(name); - g_object_unref(session); - break; - } - default: - break; - } -} - - -/****************************************************************************** - * GObject Stuff - *****************************************************************************/ -static void -jingle_rtp_init(G_GNUC_UNUSED JingleRtp *rtp) -{ -} - -static void -jingle_rtp_finalize (GObject *rtp) -{ - JingleRtpPrivate *priv = jingle_rtp_get_instance_private(JINGLE_RTP(rtp)); - purple_debug_info("jingle-rtp","jingle_rtp_finalize\n"); - - g_free(priv->media_type); - g_free(priv->ssrc); - - G_OBJECT_CLASS(jingle_rtp_parent_class)->finalize(rtp); -} - -static void -jingle_rtp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - JingleRtp *rtp = JINGLE_RTP(object); - JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp); - - switch (prop_id) { - case PROP_MEDIA_TYPE: - g_free(priv->media_type); - priv->media_type = g_value_dup_string(value); - break; - case PROP_SSRC: - g_free(priv->ssrc); - priv->ssrc = g_value_dup_string(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_rtp_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - JingleRtp *rtp = JINGLE_RTP(object); - JingleRtpPrivate *priv = jingle_rtp_get_instance_private(rtp); - - switch (prop_id) { - case PROP_MEDIA_TYPE: - g_value_set_string(value, priv->media_type); - break; - case PROP_SSRC: - g_value_set_string(value, priv->ssrc); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -jingle_rtp_class_finalize(G_GNUC_UNUSED JingleRtpClass *klass) { -} - -static void -jingle_rtp_class_init (JingleRtpClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - JingleContentClass *content_class = JINGLE_CONTENT_CLASS(klass); - - obj_class->finalize = jingle_rtp_finalize; - obj_class->set_property = jingle_rtp_set_property; - obj_class->get_property = jingle_rtp_get_property; - - content_class->to_xml = jingle_rtp_to_xml_internal; - content_class->parse = jingle_rtp_parse_internal; - content_class->description_type = JINGLE_APP_RTP; - content_class->handle_action = jingle_rtp_handle_action_internal; - - properties[PROP_MEDIA_TYPE] = g_param_spec_string("media-type", - "Media Type", - "The media type (\"audio\" or \"video\") for this rtp session.", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_SSRC] = g_param_spec_string("ssrc", - "ssrc", - "The ssrc for this rtp session.", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); -} - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_rtp_register(PurplePlugin *plugin) { - jingle_rtp_register_type(G_TYPE_MODULE(plugin)); -} - -gchar * -jingle_rtp_get_media_type(JingleContent *content) -{ - gchar *media_type; - g_object_get(content, "media-type", &media_type, NULL); - return media_type; -} - -gchar * -jingle_rtp_get_ssrc(JingleContent *content) -{ - gchar *ssrc; - g_object_get(content, "ssrc", &ssrc, NULL); - return ssrc; -} - -static PurpleMedia * -jingle_rtp_get_media(JingleSession *session) -{ - JabberStream *js = jingle_session_get_js(session); - PurpleMedia *media = NULL; - GList *iter = purple_media_manager_get_media_by_account( - purple_media_manager_get(), - purple_connection_get_account(js->gc)); - - for (; iter; iter = g_list_delete_link(iter, iter)) { - JingleSession *media_session = - purple_media_get_protocol_data(iter->data); - if (media_session == session) { - media = iter->data; - break; - } - } - g_clear_list(&iter, NULL); - - return media; -} - -gboolean -jingle_rtp_initiate_media(JabberStream *js, const gchar *who, - PurpleMediaSessionType type) -{ - /* create content negotiation */ - JingleSession *session; - JingleContent *content; - JingleTransport *transport; - JabberBuddy *jb; - JabberBuddyResource *jbr; - gboolean ret = FALSE; - const gchar *transport_type; - - gchar *resource = NULL, *me = NULL, *sid = NULL; - - /* construct JID to send to */ - jb = jabber_buddy_find(js, who, FALSE); - if (!jb) { - purple_debug_error("jingle-rtp", "Could not find Jabber buddy\n"); - goto out; - } - - resource = jabber_get_resource(who); - jbr = jabber_buddy_find_resource(jb, resource); - - if (!jbr) { - purple_debug_error("jingle-rtp", "Could not find buddy's resource - %s\n", resource); - goto out; - } - - if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_ICEUDP)) { - transport_type = JINGLE_TRANSPORT_ICEUDP; - } else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) { - transport_type = JINGLE_TRANSPORT_RAWUDP; - } else { - purple_debug_error("jingle-rtp", "Resource doesn't support " - "the same transport types\n"); - goto out; - } - - /* set ourselves as initiator */ - me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource); - - sid = jabber_get_next_id(js); - session = jingle_session_create(js, sid, me, who, TRUE); - - - if (type & PURPLE_MEDIA_AUDIO) { - JingleRtpPrivate *priv = NULL; - - transport = jingle_transport_create(transport_type); - content = jingle_content_create(JINGLE_APP_RTP, "initiator", - "session", "audio-session", "both", transport); - - priv = jingle_rtp_get_instance_private(JINGLE_RTP(content)); - - jingle_session_add_content(session, content); - priv->media_type = g_strdup("audio"); - jingle_rtp_init_media(content); - g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]); - } - if (type & PURPLE_MEDIA_VIDEO) { - JingleRtpPrivate *priv = NULL; - - transport = jingle_transport_create(transport_type); - content = jingle_content_create(JINGLE_APP_RTP, "initiator", - "session", "video-session", "both", transport); - - priv = jingle_rtp_get_instance_private(JINGLE_RTP(content)); - - jingle_session_add_content(session, content); - priv->media_type = g_strdup("video"); - jingle_rtp_init_media(content); - g_object_notify_by_pspec(G_OBJECT(content), properties[PROP_MEDIA_TYPE]); - } - - if (jingle_rtp_get_media(session) == NULL) { - goto out; - } - - ret = TRUE; - -out: - g_free(me); - g_free(resource); - g_free(sid); - return ret; -} - -void -jingle_rtp_terminate_session(JabberStream *js, const gchar *who) -{ - JingleSession *session; -/* XXX: This may cause file transfers and xml sessions to stop as well */ - session = jingle_session_find_by_jid(js, who); - - if (session) { - PurpleMedia *media = jingle_rtp_get_media(session); - if (media) { - purple_debug_info("jingle-rtp", "hanging up media\n"); - purple_media_stream_info(media, - PURPLE_MEDIA_INFO_HANGUP, - NULL, NULL, TRUE); - } - } -}
--- a/libpurple/protocols/jabber/jingle/rtp.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * @file rtp.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_RTP_H -#define PURPLE_JABBER_JINGLE_RTP_H - -#include <glib.h> -#include <glib-object.h> - -#include <purple.h> - -#include "content.h" - -G_BEGIN_DECLS - -#define JINGLE_TYPE_RTP jingle_rtp_get_type() - -/** - * Gets the rtp class's GType - * - * @return The rtp class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_FINAL_TYPE(JingleRtp, jingle_rtp, JINGLE, RTP, JingleContent) - -/** - * Registers the JingleRtp type in the type system. - */ -void jingle_rtp_register(PurplePlugin *plugin); - -gchar *jingle_rtp_get_media_type(JingleContent *content); -gchar *jingle_rtp_get_ssrc(JingleContent *content); - -gboolean jingle_rtp_initiate_media(JabberStream *js, - const gchar *who, - PurpleMediaSessionType type); -void jingle_rtp_terminate_session(JabberStream *js, const gchar *who); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_RTP_H */ -
--- a/libpurple/protocols/jabber/jingle/session.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,685 +0,0 @@ -/** - * @file session.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "content.h" -#include "session.h" -#include "jingle.h" - -#include <string.h> - -struct _JingleSession -{ - GObject parent; -}; - -typedef struct -{ - gchar *sid; - JabberStream *js; - gchar *remote_jid; - gchar *local_jid; - gboolean is_initiator; - gboolean state; - GList *contents; - GList *pending_contents; -} JingleSessionPrivate; - -enum { - PROP_0, - PROP_SID, - PROP_JS, - PROP_REMOTE_JID, - PROP_LOCAL_JID, - PROP_IS_INITIATOR, - PROP_STATE, - PROP_CONTENTS, - PROP_PENDING_CONTENTS, - N_PROPERTIES, -}; - -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - JingleSession, - jingle_session, - G_TYPE_OBJECT, - G_TYPE_FLAG_FINAL, - G_ADD_PRIVATE_DYNAMIC(JingleSession) -); - -/****************************************************************************** - * Helpers - *****************************************************************************/ -static gboolean -find_by_jid_ghr(G_GNUC_UNUSED gpointer key, gpointer value, gpointer user_data) -{ - JingleSession *session = (JingleSession *)value; - const gchar *jid = user_data; - gboolean use_bare = strchr(jid, '/') == NULL; - gchar *remote_jid = jingle_session_get_remote_jid(session); - gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid) - : g_strdup(remote_jid); - g_free(remote_jid); - if (purple_strequal(jid, cmp_jid)) { - g_free(cmp_jid); - return TRUE; - } - g_free(cmp_jid); - - return FALSE; -} - -static PurpleXmlNode * -jingle_add_jingle_packet(JingleSession *session, - JabberIq *iq, JingleActionType action) -{ - PurpleXmlNode *jingle = iq ? - purple_xmlnode_new_child(iq->node, "jingle") : - purple_xmlnode_new("jingle"); - gchar *local_jid = jingle_session_get_local_jid(session); - gchar *remote_jid = jingle_session_get_remote_jid(session); - gchar *sid = jingle_session_get_sid(session); - - purple_xmlnode_set_namespace(jingle, JINGLE); - purple_xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action)); - - if (jingle_session_is_initiator(session)) { - purple_xmlnode_set_attrib(jingle, "initiator", local_jid); - purple_xmlnode_set_attrib(jingle, "responder", remote_jid); - } else { - purple_xmlnode_set_attrib(jingle, "initiator", remote_jid); - purple_xmlnode_set_attrib(jingle, "responder", local_jid); - } - - purple_xmlnode_set_attrib(jingle, "sid", sid); - - g_free(local_jid); - g_free(remote_jid); - g_free(sid); - - return jingle; -} - -static JabberIq * -jingle_create_iq(JingleSession *session) -{ - JabberStream *js = jingle_session_get_js(session); - JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET); - gchar *from = jingle_session_get_local_jid(session); - gchar *to = jingle_session_get_remote_jid(session); - - purple_xmlnode_set_attrib(result->node, "from", from); - purple_xmlnode_set_attrib(result->node, "to", to); - - g_free(from); - g_free(to); - return result; -} - -/****************************************************************************** - * GObject Implementation - *****************************************************************************/ -static void -jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - JingleSession *session = JINGLE_SESSION(object); - JingleSessionPrivate *priv = jingle_session_get_instance_private(session); - - switch (prop_id) { - case PROP_SID: - g_free(priv->sid); - priv->sid = g_value_dup_string(value); - break; - case PROP_JS: - priv->js = g_value_get_pointer(value); - break; - case PROP_REMOTE_JID: - g_free(priv->remote_jid); - priv->remote_jid = g_value_dup_string(value); - break; - case PROP_LOCAL_JID: - g_free(priv->local_jid); - priv->local_jid = g_value_dup_string(value); - break; - case PROP_IS_INITIATOR: - priv->is_initiator = g_value_get_boolean(value); - break; - case PROP_STATE: - priv->state = g_value_get_boolean(value); - break; - case PROP_CONTENTS: - priv->contents = g_value_get_pointer(value); - break; - case PROP_PENDING_CONTENTS: - priv->pending_contents = g_value_get_pointer(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - JingleSession *session = JINGLE_SESSION(object); - JingleSessionPrivate *priv = jingle_session_get_instance_private(session); - - switch (prop_id) { - case PROP_SID: - g_value_set_string(value, priv->sid); - break; - case PROP_JS: - g_value_set_pointer(value, priv->js); - break; - case PROP_REMOTE_JID: - g_value_set_string(value, priv->remote_jid); - break; - case PROP_LOCAL_JID: - g_value_set_string(value, priv->local_jid); - break; - case PROP_IS_INITIATOR: - g_value_set_boolean(value, priv->is_initiator); - break; - case PROP_STATE: - g_value_set_boolean(value, priv->state); - break; - case PROP_CONTENTS: - g_value_set_pointer(value, priv->contents); - break; - case PROP_PENDING_CONTENTS: - g_value_set_pointer(value, priv->pending_contents); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -jingle_session_init(G_GNUC_UNUSED JingleSession *session) -{ -} - -static void -jingle_session_finalize (GObject *session) -{ - JingleSessionPrivate *priv = jingle_session_get_instance_private(JINGLE_SESSION(session)); - purple_debug_info("jingle","jingle_session_finalize\n"); - - g_hash_table_remove(priv->js->sessions, priv->sid); - - g_free(priv->sid); - g_free(priv->remote_jid); - g_free(priv->local_jid); - - g_list_free_full(priv->contents, g_object_unref); - g_list_free_full(priv->pending_contents, g_object_unref); - - G_OBJECT_CLASS(jingle_session_parent_class)->finalize(session); -} - -static void -jingle_session_class_finalize(G_GNUC_UNUSED JingleSessionClass *klass) -{ -} - -static void -jingle_session_class_init (JingleSessionClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - - obj_class->finalize = jingle_session_finalize; - obj_class->set_property = jingle_session_set_property; - obj_class->get_property = jingle_session_get_property; - - properties[PROP_SID] = g_param_spec_string("sid", - "Session ID", - "The unique session ID of the Jingle Session.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_JS] = g_param_spec_pointer("js", - "JabberStream", - "The Jabber stream associated with this session.", - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_REMOTE_JID] = g_param_spec_string("remote-jid", - "Remote JID", - "The JID of the remote participant.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_LOCAL_JID] = g_param_spec_string("local-jid", - "Local JID", - "The JID of the local participant.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_IS_INITIATOR] = g_param_spec_boolean("is-initiator", - "Is Initiator", - "Whether or not the local JID is the initiator of the session.", - FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = g_param_spec_boolean("state", - "State", - "The state of the session (PENDING=FALSE, ACTIVE=TRUE).", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - properties[PROP_CONTENTS] = g_param_spec_pointer("contents", - "Contents", - "The active contents contained within this session", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - properties[PROP_PENDING_CONTENTS] = g_param_spec_pointer("pending-contents", - "Pending contents", - "The pending contents contained within this session", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); -} - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_session_register(PurplePlugin *plugin) { - jingle_session_register_type(G_TYPE_MODULE(plugin)); -} - -JingleSession * -jingle_session_create(JabberStream *js, const gchar *sid, - const gchar *local_jid, const gchar *remote_jid, - gboolean is_initiator) -{ - JingleSession *session = g_object_new(jingle_session_get_type(), - "js", js, - "sid", sid, - "local-jid", local_jid, - "remote-jid", remote_jid, - "is_initiator", is_initiator, - NULL); - - /* insert it into the hash table */ - if (!js->sessions) { - purple_debug_info("jingle", - "Creating hash table for sessions\n"); - js->sessions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - } - purple_debug_info("jingle", - "inserting session with key: %s into table\n", sid); - g_hash_table_insert(js->sessions, g_strdup(sid), session); - - return session; -} - -JabberStream * -jingle_session_get_js(JingleSession *session) -{ - JabberStream *js; - g_object_get(session, "js", &js, NULL); - return js; -} - -gchar * -jingle_session_get_sid(JingleSession *session) -{ - gchar *sid; - g_object_get(session, "sid", &sid, NULL); - return sid; -} - -gchar * -jingle_session_get_local_jid(JingleSession *session) -{ - gchar *local_jid; - g_object_get(session, "local-jid", &local_jid, NULL); - return local_jid; -} - -gchar * -jingle_session_get_remote_jid(JingleSession *session) -{ - gchar *remote_jid; - g_object_get(session, "remote-jid", &remote_jid, NULL); - return remote_jid; -} - -gboolean -jingle_session_is_initiator(JingleSession *session) -{ - gboolean is_initiator; - g_object_get(session, "is-initiator", &is_initiator, NULL); - return is_initiator; -} - -gboolean -jingle_session_get_state(JingleSession *session) -{ - gboolean state; - g_object_get(session, "state", &state, NULL); - return state; -} - -GList * -jingle_session_get_contents(JingleSession *session) -{ - GList *contents; - g_object_get(session, "contents", &contents, NULL); - return contents; -} - -GList * -jingle_session_get_pending_contents(JingleSession *session) -{ - GList *pending_contents; - g_object_get(session, "pending-contents", &pending_contents, NULL); - return pending_contents; -} - -JingleSession * -jingle_session_find_by_sid(JabberStream *js, const gchar *sid) -{ - JingleSession *session = NULL; - - if (js->sessions) - session = g_hash_table_lookup(js->sessions, sid); - - purple_debug_info("jingle", "find_by_id %s\n", sid); - purple_debug_info("jingle", "lookup: %p\n", session); - - return session; -} - -JingleSession * -jingle_session_find_by_jid(JabberStream *js, const gchar *jid) -{ - return js->sessions != NULL ? - g_hash_table_find(js->sessions, - find_by_jid_ghr, (gpointer)jid) : NULL; -} - -JabberIq * -jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle) -{ - JabberIq *result = jabber_iq_new( - jingle_session_get_js(session), - JABBER_IQ_RESULT); - PurpleXmlNode *packet = purple_xmlnode_get_parent(jingle); - jabber_iq_set_id(result, purple_xmlnode_get_attrib(packet, "id")); - purple_xmlnode_set_attrib(result->node, "from", purple_xmlnode_get_attrib(packet, "to")); - purple_xmlnode_set_attrib(result->node, "to", purple_xmlnode_get_attrib(packet, "from")); - return result; -} - -PurpleXmlNode * -jingle_session_to_xml(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action) -{ - if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) { - GList *iter; - if (action == JINGLE_CONTENT_ACCEPT - || action == JINGLE_CONTENT_ADD - || action == JINGLE_CONTENT_REMOVE) - iter = jingle_session_get_pending_contents(session); - else - iter = jingle_session_get_contents(session); - - for (; iter; iter = g_list_next(iter)) { - jingle_content_to_xml(iter->data, jingle, action); - } - } - return jingle; -} - -JabberIq * -jingle_session_to_packet(JingleSession *session, JingleActionType action) -{ - JabberIq *iq = jingle_create_iq(session); - PurpleXmlNode *jingle = jingle_add_jingle_packet(session, iq, action); - jingle_session_to_xml(session, jingle, action); - return iq; -} - -void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action) -{ - GList *iter; - if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE) - iter = jingle_session_get_pending_contents(session); - else - iter = jingle_session_get_contents(session); - - for (; iter; iter = g_list_next(iter)) { - jingle_content_handle_action(iter->data, jingle, action); - } -} - -JingleContent * -jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator) -{ - JingleSessionPrivate *priv = NULL; - GList *iter; - - g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL); - - if (name == NULL) - return NULL; - - priv = jingle_session_get_instance_private(session); - - iter = priv->contents; - for (; iter; iter = g_list_next(iter)) { - JingleContent *content = iter->data; - gchar *cname = jingle_content_get_name(content); - gboolean result = purple_strequal(name, cname); - g_free(cname); - - if (creator != NULL) { - gchar *ccreator = jingle_content_get_creator(content); - result = (result && purple_strequal(creator, ccreator)); - g_free(ccreator); - } - - if (result == TRUE) - return content; - } - return NULL; -} - -JingleContent * -jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator) -{ - JingleSessionPrivate *priv = NULL; - GList *iter; - - g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL); - - if (name == NULL) - return NULL; - - priv = jingle_session_get_instance_private(session); - - iter = priv->pending_contents; - for (; iter; iter = g_list_next(iter)) { - JingleContent *content = iter->data; - gchar *cname = jingle_content_get_name(content); - gboolean result = purple_strequal(name, cname); - g_free(cname); - - if (creator != NULL) { - gchar *ccreator = jingle_content_get_creator(content); - result = (result && purple_strequal(creator, ccreator)); - g_free(ccreator); - } - - if (result == TRUE) - return content; - } - return NULL; -} - -void -jingle_session_add_content(JingleSession *session, JingleContent* content) -{ - JingleSessionPrivate *priv = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - priv = jingle_session_get_instance_private(session); - - priv->contents = g_list_append(priv->contents, content); - jingle_content_set_session(content, session); - - g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]); -} - -void -jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator) -{ - JingleSessionPrivate *priv = NULL; - JingleContent *content = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - priv = jingle_session_get_instance_private(session); - content = jingle_session_find_content(session, name, creator); - - if (content) { - priv->contents = g_list_remove(priv->contents, content); - g_object_unref(content); - - g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]); - } -} - -void -jingle_session_add_pending_content(JingleSession *session, JingleContent* content) -{ - JingleSessionPrivate *priv = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - priv = jingle_session_get_instance_private(session); - - priv->pending_contents = g_list_append(priv->pending_contents, content); - jingle_content_set_session(content, session); - - g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]); -} - -void -jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator) -{ - JingleSessionPrivate *priv = NULL; - JingleContent *content = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - priv = jingle_session_get_instance_private(session); - content = jingle_session_find_pending_content(session, name, creator); - - if (content) { - priv->pending_contents = g_list_remove(priv->pending_contents, content); - g_object_unref(content); - - g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]); - } -} - -void -jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator) -{ - JingleContent *content = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - content = jingle_session_find_pending_content(session, name, creator); - if (content) { - g_object_ref(content); - jingle_session_remove_pending_content(session, name, creator); - jingle_session_add_content(session, content); - } -} - -void -jingle_session_accept_session(JingleSession *session) -{ - JingleSessionPrivate *priv = NULL; - - g_return_if_fail(JINGLE_IS_SESSION(session)); - - priv = jingle_session_get_instance_private(session); - - priv->state = TRUE; - - g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_STATE]); -} - -JabberIq * -jingle_session_terminate_packet(JingleSession *session, const gchar *reason) -{ - JabberIq *iq = NULL; - - g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL); - - iq = jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE); - - if (reason != NULL) { - PurpleXmlNode *reason_node; - PurpleXmlNode *jingle = purple_xmlnode_get_child(iq->node, "jingle"); - reason_node = purple_xmlnode_new_child(jingle, "reason"); - purple_xmlnode_new_child(reason_node, reason); - } - - return iq; -} - -JabberIq * -jingle_session_redirect_packet(JingleSession *session, const gchar *sid) -{ - JabberIq *iq = NULL; - PurpleXmlNode *alt_session; - - g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL); - - iq = jingle_session_terminate_packet(session, "alternative-session"); - - if (sid == NULL) - return iq; - - alt_session = purple_xmlnode_get_child(iq->node, - "jingle/reason/alternative-session"); - - if (alt_session != NULL) { - PurpleXmlNode *sid_node = purple_xmlnode_new_child(alt_session, "sid"); - purple_xmlnode_insert_data(sid_node, sid, -1); - } - - return iq; -}
--- a/libpurple/protocols/jabber/jingle/session.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * @file session.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_SESSION_H -#define PURPLE_JABBER_JINGLE_SESSION_H - -#include "iq.h" -#include "jabber.h" - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define JINGLE_TYPE_SESSION jingle_session_get_type() - -struct _JingleContent; - -/** - * Gets the session class's GType - * - * @return The session class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_FINAL_TYPE(JingleSession, jingle_session, JINGLE, SESSION, GObject) - -/** - * Registers the JingleSession type in the type system. - */ -void jingle_session_register(PurplePlugin *plugin); - -JingleSession *jingle_session_create(JabberStream *js, const gchar *sid, - const gchar *local_jid, const gchar *remote_jid, - gboolean is_initiator); -JabberStream *jingle_session_get_js(JingleSession *session); -gchar *jingle_session_get_sid(JingleSession *session); -gchar *jingle_session_get_local_jid(JingleSession *session); -gchar *jingle_session_get_remote_jid(JingleSession *session); -gboolean jingle_session_is_initiator(JingleSession *session); -gboolean jingle_session_get_state(JingleSession *session); - -GList *jingle_session_get_contents(JingleSession *session); -GList *jingle_session_get_pending_contents(JingleSession *session); - -JingleSession *jingle_session_find_by_sid(JabberStream *js, const gchar *sid); -JingleSession *jingle_session_find_by_jid(JabberStream *js, const gchar *jid); - -JabberIq *jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle); -PurpleXmlNode *jingle_session_to_xml(JingleSession *session, PurpleXmlNode *parent, JingleActionType action); -JabberIq *jingle_session_to_packet(JingleSession *session, JingleActionType action); - -void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action); - -struct _JingleContent *jingle_session_find_content(JingleSession *session, - const gchar *name, const gchar *creator); -struct _JingleContent *jingle_session_find_pending_content(JingleSession *session, - const gchar *name, const gchar *creator); - -void jingle_session_add_content(JingleSession *session, struct _JingleContent* content); -void jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator); -void jingle_session_add_pending_content(JingleSession *session, struct _JingleContent* content); -void jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator); -void jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator); -void jingle_session_accept_session(JingleSession *session); -JabberIq *jingle_session_terminate_packet(JingleSession *session, const gchar *reason); -JabberIq *jingle_session_redirect_packet(JingleSession *session, const gchar *sid); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_SESSION_H */ -
--- a/libpurple/protocols/jabber/jingle/transport.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/** - * @file transport.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "transport.h" -#include "jingle.h" - -#include <string.h> - -G_DEFINE_DYNAMIC_TYPE(JingleTransport, jingle_transport, G_TYPE_OBJECT); - -/****************************************************************************** - * Transport Implementation - *****************************************************************************/ -static JingleTransport * -jingle_transport_parse_internal(PurpleXmlNode *transport) -{ - const gchar *type = purple_xmlnode_get_namespace(transport); - return jingle_transport_create(type); -} - -static void -jingle_transport_add_local_candidate_internal(G_GNUC_UNUSED JingleTransport *transport, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED guint generation, - G_GNUC_UNUSED PurpleMediaCandidate *candidate) -{ - /* Nothing to do */ -} - -static PurpleXmlNode * -jingle_transport_to_xml_internal(JingleTransport *transport, - PurpleXmlNode *content, - G_GNUC_UNUSED JingleActionType action) -{ - PurpleXmlNode *node = purple_xmlnode_new_child(content, "transport"); - purple_xmlnode_set_namespace(node, jingle_transport_get_transport_type(transport)); - return node; -} - -static GList * -jingle_transport_get_remote_candidates_internal(G_GNUC_UNUSED JingleTransport *transport) -{ - return NULL; -} - -/****************************************************************************** - * GObject Stuff - *****************************************************************************/ -static void -jingle_transport_init(G_GNUC_UNUSED JingleTransport *transport) -{ -} - -static void -jingle_transport_class_finalize(G_GNUC_UNUSED JingleTransportClass *klass) -{ -} - -static void -jingle_transport_class_init (JingleTransportClass *klass) -{ - klass->to_xml = jingle_transport_to_xml_internal; - klass->parse = jingle_transport_parse_internal; - klass->add_local_candidate = jingle_transport_add_local_candidate_internal; - klass->get_remote_candidates = jingle_transport_get_remote_candidates_internal; -} - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jingle_transport_register(PurplePlugin *plugin) { - jingle_transport_register_type(G_TYPE_MODULE(plugin)); -} - -JingleTransport * -jingle_transport_create(const gchar *type) -{ - return g_object_new(jingle_get_type(type), NULL); -} - -const gchar * -jingle_transport_get_transport_type(JingleTransport *transport) -{ - return JINGLE_TRANSPORT_GET_CLASS(transport)->transport_type; -} - -void -jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, - guint generation, PurpleMediaCandidate *candidate) -{ - JINGLE_TRANSPORT_GET_CLASS(transport)->add_local_candidate(transport, id, - generation, candidate); -} - -GList * -jingle_transport_get_remote_candidates(JingleTransport *transport) -{ - return JINGLE_TRANSPORT_GET_CLASS(transport)->get_remote_candidates(transport); -} - -JingleTransport * -jingle_transport_parse(PurpleXmlNode *transport) -{ - const gchar *type_name = purple_xmlnode_get_namespace(transport); - GType type = jingle_get_type(type_name); - if (type == G_TYPE_NONE) - return NULL; - - return JINGLE_TRANSPORT_CLASS(g_type_class_ref(type))->parse(transport); -} - -PurpleXmlNode * -jingle_transport_to_xml(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action) -{ - g_return_val_if_fail(transport != NULL, NULL); - g_return_val_if_fail(JINGLE_IS_TRANSPORT(transport), NULL); - return JINGLE_TRANSPORT_GET_CLASS(transport)->to_xml(transport, content, action); -}
--- a/libpurple/protocols/jabber/jingle/transport.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/** - * @file transport.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JINGLE_TRANSPORT_H -#define PURPLE_JABBER_JINGLE_TRANSPORT_H - -#include <glib.h> -#include <glib-object.h> - -#include <purple.h> - -#include "jingle.h" - -G_BEGIN_DECLS - -#define JINGLE_TYPE_TRANSPORT jingle_transport_get_type() - -typedef struct _JingleTransport JingleTransport; - -/** The transport class */ -struct _JingleTransportClass -{ - GObjectClass parent_class; /**< The parent class. */ - - const gchar *transport_type; - PurpleXmlNode *(*to_xml) (JingleTransport *transport, PurpleXmlNode *content, JingleActionType action); - JingleTransport *(*parse) (PurpleXmlNode *transport); - void (*add_local_candidate) (JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); - GList *(*get_remote_candidates) (JingleTransport *transport); -}; - -/** - * Gets the transport class's GType - * - * @return The transport class's GType. - */ -G_MODULE_EXPORT -G_DECLARE_DERIVABLE_TYPE(JingleTransport, jingle_transport, JINGLE, TRANSPORT, - GObject) - -/** - * Registers the JingleTransport type in the type system. - */ -void jingle_transport_register(PurplePlugin *plugin); - -JingleTransport *jingle_transport_create(const gchar *type); -const gchar *jingle_transport_get_transport_type(JingleTransport *transport); - -void jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); -GList *jingle_transport_get_remote_candidates(JingleTransport *transport); - -JingleTransport *jingle_transport_parse(PurpleXmlNode *transport); -PurpleXmlNode *jingle_transport_to_xml(JingleTransport *transport, PurpleXmlNode *content, JingleActionType action); - -G_END_DECLS - -#endif /* PURPLE_JABBER_JINGLE_TRANSPORT_H */ -
--- a/libpurple/protocols/jabber/jutil.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,604 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "chat.h" -#include "presence.h" -#include "jutil.h" - -#include <idna.h> -#include <stringprep.h> -static char idn_buffer[1024]; - -static gboolean jabber_nodeprep(char *str, size_t buflen) -{ - return stringprep_xmpp_nodeprep(str, buflen) == STRINGPREP_OK; -} - -static gboolean jabber_resourceprep(char *str, size_t buflen) -{ - return stringprep_xmpp_resourceprep(str, buflen) == STRINGPREP_OK; -} - -static JabberID* -jabber_idn_validate(const char *str, const char *at, const char *slash, - const char *null) -{ - const char *node = NULL; - const char *domain = NULL; - const char *resource = NULL; - int node_len = 0; - int domain_len = 0; - int resource_len = 0; - char *out; - JabberID *jid; - - /* Ensure no parts are > 1023 bytes */ - if (at) { - node = str; - node_len = at - str; - - domain = at + 1; - if (slash) { - domain_len = slash - (at + 1); - resource = slash + 1; - resource_len = null - (slash + 1); - } else { - domain_len = null - (at + 1); - } - } else { - domain = str; - - if (slash) { - domain_len = slash - str; - resource = slash + 1; - resource_len = null - (slash + 1); - } else { - domain_len = null - str; - } - } - - if (node && node_len > 1023) - return NULL; - if (domain_len > 1023) - return NULL; - if (resource && resource_len > 1023) - return NULL; - - jid = g_new0(JabberID, 1); - - if (node) { - strncpy(idn_buffer, node, node_len); - idn_buffer[node_len] = '\0'; - - if (!jabber_nodeprep(idn_buffer, sizeof(idn_buffer))) { - jabber_id_free(jid); - jid = NULL; - goto out; - } - - jid->node = g_strdup(idn_buffer); - } - - /* domain *must* be here */ - strncpy(idn_buffer, domain, domain_len); - idn_buffer[domain_len] = '\0'; - if (domain[0] == '[') { /* IPv6 address */ - gboolean valid = FALSE; - - if (domain_len > 2 && idn_buffer[domain_len - 1] == ']') { - GInetAddress *addr; - idn_buffer[domain_len - 1] = '\0'; - addr = g_inet_address_new_from_string(idn_buffer + 1); - if (addr != NULL) { - valid = (g_inet_address_get_family(addr) == - G_SOCKET_FAMILY_IPV6); - g_object_unref(addr); - } - } - - if (!valid) { - jabber_id_free(jid); - jid = NULL; - goto out; - } - - jid->domain = g_strndup(domain, domain_len); - } else { - /* Apply nameprep */ - if (stringprep_nameprep(idn_buffer, sizeof(idn_buffer)) != STRINGPREP_OK) { - jabber_id_free(jid); - jid = NULL; - goto out; - } - - /* And now ToASCII */ - if (idna_to_ascii_8z(idn_buffer, &out, IDNA_USE_STD3_ASCII_RULES) != IDNA_SUCCESS) { - jabber_id_free(jid); - jid = NULL; - goto out; - } - - /* This *MUST* be freed using 'free', not 'g_free' */ - free(out); - jid->domain = g_strdup(idn_buffer); - } - - if (resource) { - strncpy(idn_buffer, resource, resource_len); - idn_buffer[resource_len] = '\0'; - - if (!jabber_resourceprep(idn_buffer, sizeof(idn_buffer))) { - jabber_id_free(jid); - jid = NULL; - goto out; - } else - jid->resource = g_strdup(idn_buffer); - } - -out: - return jid; -} - -gboolean jabber_nodeprep_validate(const char *str) -{ - gboolean result; - - if(!str) - return TRUE; - - if(strlen(str) > 1023) - return FALSE; - - strncpy(idn_buffer, str, sizeof(idn_buffer) - 1); - idn_buffer[sizeof(idn_buffer) - 1] = '\0'; - result = jabber_nodeprep(idn_buffer, sizeof(idn_buffer)); - return result; -} - -gboolean jabber_domain_validate(const char *str) -{ - const char *c; - size_t len; - - if(!str) - return TRUE; - - len = strlen(str); - if (len > 1023) - return FALSE; - - c = str; - - if (*c == '[') { - /* Check if str is a valid IPv6 identifier */ - GInetAddress *addr; - gboolean valid = FALSE; - - if (len <= 2 || *(c + len - 1) != ']') { - return FALSE; - } - - /* Ugly, but in-place */ - *(gchar *)(c + len - 1) = '\0'; - addr = g_inet_address_new_from_string(c + 1); - if (addr != NULL) { - valid = (g_inet_address_get_family(addr) == G_SOCKET_FAMILY_IPV6); - g_object_unref(addr); - } - *(gchar *)(c + len - 1) = ']'; - - return valid; - } - - while(c && *c) { - gunichar ch = g_utf8_get_char(c); - /* The list of characters allowed in domain names is pretty small */ - if ((ch <= 0x7F && !( (ch >= 'a' && ch <= 'z') - || (ch >= '0' && ch <= '9') - || (ch >= 'A' && ch <= 'Z') - || ch == '.' - || ch == '-' )) || (ch >= 0x80 && !g_unichar_isgraph(ch))) - return FALSE; - - c = g_utf8_next_char(c); - } - - return TRUE; -} - -gboolean jabber_resourceprep_validate(const char *str) -{ - gboolean result; - - if(!str) - return TRUE; - - if(strlen(str) > 1023) - return FALSE; - - strncpy(idn_buffer, str, sizeof(idn_buffer) - 1); - idn_buffer[sizeof(idn_buffer) - 1] = '\0'; - result = jabber_resourceprep(idn_buffer, sizeof(idn_buffer)); - return result; -} - -char *jabber_saslprep(const char *in) -{ - char *out; - - g_return_val_if_fail(in != NULL, NULL); - g_return_val_if_fail(strlen(in) <= sizeof(idn_buffer) - 1, NULL); - - strncpy(idn_buffer, in, sizeof(idn_buffer) - 1); - idn_buffer[sizeof(idn_buffer) - 1] = '\0'; - - if (STRINGPREP_OK != stringprep(idn_buffer, sizeof(idn_buffer), 0, - stringprep_saslprep)) { - memset(idn_buffer, 0, sizeof(idn_buffer)); - return NULL; - } - - out = g_strdup(idn_buffer); - memset(idn_buffer, 0, sizeof(idn_buffer)); - return out; -} - -static JabberID* -jabber_id_new_internal(const char *str, gboolean allow_terminating_slash) -{ - const char *at = NULL; - const char *slash = NULL; - const char *c; - gboolean needs_validation = FALSE; - JabberID *jid; - - if (!str) - return NULL; - - for (c = str; *c != '\0'; c++) - { - switch (*c) { - case '@': - if (!slash) { - if (at) { - /* Multiple @'s in the node/domain portion, not a valid JID! */ - return NULL; - } - if (c == str) { - /* JIDs cannot start with @ */ - return NULL; - } - if (c[1] == '\0') { - /* JIDs cannot end with @ */ - return NULL; - } - at = c; - } - break; - - case '/': - if (!slash) { - if (c == str) { - /* JIDs cannot start with / */ - return NULL; - } - if (c[1] == '\0' && !allow_terminating_slash) { - /* JIDs cannot end with / */ - return NULL; - } - slash = c; - } - break; - - default: - /* characters allowed everywhere */ - if ((*c >= 'a' && *c <= 'z') - || (*c >= '0' && *c <= '9') - || (*c >= 'A' && *c <= 'Z') - || *c == '.' || *c == '-') - /* We're good */ - break; - - /* - * Hmm, this character is a bit more exotic. Better fall - * back to using the more expensive UTF-8 compliant - * stringprep functions. - */ - needs_validation = TRUE; - break; - } - } - - if (!needs_validation) { - /* JID is made of only ASCII characters--just lowercase and return */ - jid = g_new0(JabberID, 1); - - if (at) { - jid->node = g_ascii_strdown(str, at - str); - if (slash) { - jid->domain = g_ascii_strdown(at + 1, slash - (at + 1)); - if (*(slash + 1)) - jid->resource = g_strdup(slash + 1); - } else { - jid->domain = g_ascii_strdown(at + 1, -1); - } - } else { - if (slash) { - jid->domain = g_ascii_strdown(str, slash - str); - if (*(slash + 1)) - jid->resource = g_strdup(slash + 1); - } else { - jid->domain = g_ascii_strdown(str, -1); - } - } - return jid; - } - - /* - * If we get here, there are some non-ASCII chars in the string, so - * we'll need to validate it, normalize, and finally do a full jabber - * nodeprep on the jid. - */ - - if (!g_utf8_validate(str, -1, NULL)) - return NULL; - - return jabber_idn_validate(str, at, slash, c /* points to the null */); -} - -void -jabber_id_free(JabberID *jid) -{ - if(jid) { - g_free(jid->node); - g_free(jid->domain); - g_free(jid->resource); - g_free(jid); - } -} - - -gboolean -jabber_id_equal(const JabberID *jid1, const JabberID *jid2) -{ - if (!jid1 && !jid2) { - /* Both are null therefore equal */ - return TRUE; - } - - if (!jid1 || !jid2) { - /* One is null, other is non-null, therefore not equal */ - return FALSE; - } - - return purple_strequal(jid1->node, jid2->node) && - purple_strequal(jid1->domain, jid2->domain) && - purple_strequal(jid1->resource, jid2->resource); -} - -char *jabber_get_resource(const char *in) -{ - JabberID *jid = jabber_id_new(in); - char *out; - - if(!jid) - return NULL; - - out = g_strdup(jid->resource); - jabber_id_free(jid); - - return out; -} - -char * -jabber_get_bare_jid(const char *in) -{ - JabberID *jid = jabber_id_new(in); - char *out; - - if (!jid) - return NULL; - out = jabber_id_get_bare_jid(jid); - jabber_id_free(jid); - - return out; -} - -char * -jabber_id_get_bare_jid(const JabberID *jid) -{ - g_return_val_if_fail(jid != NULL, NULL); - - return g_strconcat(jid->node ? jid->node : "", - jid->node ? "@" : "", - jid->domain, - NULL); -} - -char * -jabber_id_get_full_jid(const JabberID *jid) -{ - g_return_val_if_fail(jid != NULL, NULL); - - return g_strconcat(jid->node ? jid->node : "", - jid->node ? "@" : "", - jid->domain, - jid->resource ? "/" : "", - jid->resource ? jid->resource : "", - NULL); -} - -gboolean -jabber_jid_is_domain(const char *jid) -{ - const char *c; - - for (c = jid; *c; ++c) { - if (*c == '@' || *c == '/') - return FALSE; - } - - return TRUE; -} - - -JabberID * -jabber_id_new(const char *str) -{ - return jabber_id_new_internal(str, FALSE); -} - -const char *jabber_normalize(const PurpleAccount *account, const char *in) -{ - PurpleConnection *gc = NULL; - JabberStream *js = NULL; - static char buf[3072]; /* maximum legal length of a jabber jid */ - JabberID *jid; - - if (account) { - gc = purple_account_get_connection((PurpleAccount *)account); - } - if (gc) - js = purple_connection_get_protocol_data(gc); - - jid = jabber_id_new_internal(in, TRUE); - if(!jid) - return NULL; - - if(js && jid->node && jid->resource && - jabber_chat_find(js, jid->node, jid->domain)) - g_snprintf(buf, sizeof(buf), "%s@%s/%s", jid->node, jid->domain, - jid->resource); - else - g_snprintf(buf, sizeof(buf), "%s%s%s", jid->node ? jid->node : "", - jid->node ? "@" : "", jid->domain); - - jabber_id_free(jid); - - return buf; -} - -gboolean -jabber_is_own_account(JabberStream *js, const char *str) -{ - JabberID *jid; - gboolean equal; - - if (str == NULL) - return TRUE; - - g_return_val_if_fail(*str != '\0', FALSE); - - jid = jabber_id_new(str); - if (!jid) - return FALSE; - - equal = (purple_strequal(jid->node, js->user->node) && - purple_strequal(jid->domain, js->user->domain) && - (jid->resource == NULL || - purple_strequal(jid->resource, js->user->resource))); - jabber_id_free(jid); - return equal; -} - -static const struct { - const char *status_id; /* link to core */ - const char *show; /* The show child's cdata in a presence stanza */ - const char *readable; /* readable representation */ - JabberBuddyState state; -} jabber_statuses[] = { - { "offline", NULL, N_("Offline"), JABBER_BUDDY_STATE_UNAVAILABLE }, - { "available", NULL, N_("Available"), JABBER_BUDDY_STATE_ONLINE}, - { "freeforchat", "chat", N_("Chatty"), JABBER_BUDDY_STATE_CHAT }, - { "away", "away", N_("Away"), JABBER_BUDDY_STATE_AWAY }, - { "extended_away", "xa", N_("Extended Away"), JABBER_BUDDY_STATE_XA }, - { "dnd", "dnd", N_("Do Not Disturb"), JABBER_BUDDY_STATE_DND }, - { "error", NULL, N_("Error"), JABBER_BUDDY_STATE_ERROR } -}; - -const char * -jabber_buddy_state_get_name(const JabberBuddyState state) -{ - gsize i; - for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) - if (jabber_statuses[i].state == state) - return _(jabber_statuses[i].readable); - - return _("Unknown"); -} - -JabberBuddyState -jabber_buddy_status_id_get_state(const char *id) -{ - gsize i; - if (!id) - return JABBER_BUDDY_STATE_UNKNOWN; - - for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) - if (purple_strequal(id, jabber_statuses[i].status_id)) - return jabber_statuses[i].state; - - return JABBER_BUDDY_STATE_UNKNOWN; -} - -JabberBuddyState jabber_buddy_show_get_state(const char *id) -{ - gsize i; - - g_return_val_if_fail(id != NULL, JABBER_BUDDY_STATE_UNKNOWN); - - for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) - if (jabber_statuses[i].show && purple_strequal(id, jabber_statuses[i].show)) - return jabber_statuses[i].state; - - purple_debug_warning("jabber", "Invalid value of presence <show/> " - "attribute: %s\n", id); - return JABBER_BUDDY_STATE_UNKNOWN; -} - -const char * -jabber_buddy_state_get_show(JabberBuddyState state) -{ - gsize i; - for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) - if (state == jabber_statuses[i].state) - return jabber_statuses[i].show; - - return NULL; -} - -const char * -jabber_buddy_state_get_status_id(JabberBuddyState state) -{ - gsize i; - for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) - if (state == jabber_statuses[i].state) - return jabber_statuses[i].status_id; - - return NULL; -} -
--- a/libpurple/protocols/jabber/jutil.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/** - * @file jutil.h utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_JUTIL_H -#define PURPLE_JABBER_JUTIL_H - -typedef struct { - char *node; - char *domain; - char *resource; -} JabberID; - -typedef enum { - JABBER_BUDDY_STATE_UNKNOWN = -2, - JABBER_BUDDY_STATE_ERROR = -1, - JABBER_BUDDY_STATE_UNAVAILABLE = 0, - JABBER_BUDDY_STATE_ONLINE, - JABBER_BUDDY_STATE_CHAT, - JABBER_BUDDY_STATE_AWAY, - JABBER_BUDDY_STATE_XA, - JABBER_BUDDY_STATE_DND -} JabberBuddyState; - -#include "jabber.h" - -PURPLE_XMPP_EXTERN_FOR_TESTS -JabberID* jabber_id_new(const char *str); - -/** - * Compare two JIDs for equality. In addition to the node and domain, - * the resources of the two JIDs must also be equal (or both absent). - */ -gboolean jabber_id_equal(const JabberID *jid1, const JabberID *jid2); - -PURPLE_XMPP_EXTERN_FOR_TESTS -void jabber_id_free(JabberID *jid); - -PURPLE_XMPP_EXTERN_FOR_TESTS -char *jabber_get_resource(const char *jid); -PURPLE_XMPP_EXTERN_FOR_TESTS -char *jabber_get_bare_jid(const char *jid); -char *jabber_id_get_bare_jid(const JabberID *jid); -char *jabber_id_get_full_jid(const JabberID *jid); - -gboolean jabber_jid_is_domain(const char *jid); - -PURPLE_XMPP_EXTERN_FOR_TESTS -const char *jabber_normalize(const PurpleAccount *account, const char *in); - -/* Returns true if JID is the bare JID of our account. */ -gboolean jabber_is_own_account(JabberStream *js, const char *jid); - -PURPLE_XMPP_EXTERN_FOR_TESTS -gboolean jabber_nodeprep_validate(const char *); -gboolean jabber_domain_validate(const char *); -gboolean jabber_resourceprep_validate(const char *); - -/** - * Apply the SASLprep profile of stringprep to the string passed in. - * - * @returns A newly allocated string containing the normalized version - * of the input, or NULL if an error occurred (the string could - * not be normalized) - */ -PURPLE_XMPP_EXTERN_FOR_TESTS -char *jabber_saslprep(const char *); - -/* state -> readable name */ -const char *jabber_buddy_state_get_name(JabberBuddyState state); -/* state -> core id */ -const char *jabber_buddy_state_get_status_id(JabberBuddyState state); -/* state -> show attr (for presence stanza) */ -const char *jabber_buddy_state_get_show(JabberBuddyState state); -/* core id -> state */ -JabberBuddyState jabber_buddy_status_id_get_state(const char *id); -/* show attr (presence stanza) -> state */ -JabberBuddyState jabber_buddy_show_get_state(const char *id); - -#endif /* PURPLE_JABBER_JUTIL_H */
--- a/libpurple/protocols/jabber/meson.build Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -####################################################################### -# Check for Internationalized Domain Name support -####################################################################### - -JABBER_SOURCES = [ - 'adhoccommands.c', - 'adhoccommands.h', - 'auth.c', - 'auth.h', - 'auth_digest_md5.c', - 'auth_digest_md5.h', - 'auth_plain.c', - 'auth_scram.c', - 'auth_scram.h', - 'auth_webex.c', - 'buddy.c', - 'buddy.h', - 'bosh.c', - 'bosh.h', - 'caps.c', - 'caps.h', - 'chat.c', - 'chat.h', - 'data.c', - 'data.h', - 'disco.c', - 'disco.h', - 'ibb.c', - 'ibb.h', - 'iq.c', - 'iq.h', - 'jabber.c', - 'jabber.h', - 'jingle/jingle.c', - 'jingle/jingle.h', - 'jingle/content.c', - 'jingle/content.h', - 'jingle/iceudp.c', - 'jingle/iceudp.h', - 'jingle/rawudp.c', - 'jingle/rawudp.h', - 'jingle/rtp.c', - 'jingle/rtp.h', - 'jingle/session.c', - 'jingle/session.h', - 'jingle/transport.c', - 'jingle/transport.h', - 'jutil.c', - 'jutil.h', - 'message.c', - 'message.h', - 'namespaces.h', - 'oob.c', - 'oob.h', - 'parser.c', - 'parser.h', - 'pep.c', - 'pep.h', - 'ping.c', - 'ping.h', - 'presence.c', - 'presence.h', - 'roster.c', - 'roster.h', - 'si.c', - 'si.h', - 'useravatar.c', - 'useravatar.h', - 'usernick.c', - 'usernick.h', - 'xdata.c', - 'xdata.h', - 'xmpp.c', - 'xmpp.h' -] - -if IS_WIN32 - jabber_link_args = ['-Wl,--export-all-symbols'] -else - jabber_link_args = [] -endif - -if DYNAMIC_JABBER - idn = dependency('libidn', version : '>= 0.0.0') - - xmpp_resources = gnome.compile_resources('xmppresource', - 'resources/xmpp.gresource.xml', - source_dir : 'resources', - c_name : 'xmpp') - JABBER_SOURCES += xmpp_resources - - jabber_prpl = shared_library('jabber', JABBER_SOURCES, - c_args : ['-DPURPLE_XMPP_COMPILATION', '-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-XMPP"'], - gnu_symbol_visibility : 'hidden', - link_args : jabber_link_args, - dependencies : [birb_dep, gstreamer, idn, libxml, libpurple_dep, libsoup, glib, gio, math], - install : true, - install_dir : PURPLE_PLUGINDIR) - - devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir()) - - subdir('tests') -endif
--- a/libpurple/protocols/jabber/message.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1002 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "adhoccommands.h" -#include "buddy.h" -#include "chat.h" -#include "data.h" -#include "message.h" -#include "pep.h" -#include "iq.h" - -#include <string.h> - -static GString *jm_body_with_oob(JabberMessage *jm) { - GList *etc; - GString *body = g_string_new(""); - - if(jm->xhtml) - g_string_append(body, jm->xhtml); - else if(jm->body) - g_string_append(body, jm->body); - - for(etc = jm->etc; etc; etc = etc->next) { - PurpleXmlNode *x = etc->data; - const char *xmlns = purple_xmlnode_get_namespace(x); - if(purple_strequal(xmlns, NS_OOB_X_DATA)) { - PurpleXmlNode *url, *desc; - char *urltxt, *desctxt; - - url = purple_xmlnode_get_child(x, "url"); - desc = purple_xmlnode_get_child(x, "desc"); - - if(!url) - continue; - - urltxt = purple_xmlnode_get_data(url); - desctxt = desc ? purple_xmlnode_get_data(desc) : urltxt; - - if(body->len && !purple_strequal(body->str, urltxt)) - g_string_append_printf(body, "<br/><a href='%s'>%s</a>", - urltxt, desctxt); - else - g_string_printf(body, "<a href='%s'>%s</a>", - urltxt, desctxt); - - g_free(urltxt); - - if(desctxt != urltxt) - g_free(desctxt); - } - } - - return body; -} - -void jabber_message_free(JabberMessage *jm) -{ - g_free(jm->from); - g_free(jm->to); - g_free(jm->id); - g_free(jm->subject); - g_free(jm->body); - g_free(jm->xhtml); - g_free(jm->password); - g_free(jm->error); - g_free(jm->thread_id); - g_list_free(jm->etc); - g_list_free(jm->eventitems); - - g_clear_pointer(&jm->sent, g_date_time_unref); - - g_free(jm); -} - -static void handle_chat(JabberMessage *jm) -{ - const gchar *contact = jm->outgoing ? jm->to : jm->from; - JabberID *jid = jabber_id_new(contact); - - PurpleConnection *gc; - PurpleConversationManager *manager; - PurpleAccount *account; - PurpleMessageFlags flags = 0; - JabberBuddy *jb; - JabberBuddyResource *jbr; - GString *body; - - if(!jid) - return; - - manager = purple_conversation_manager_get_default(); - - gc = jm->js->gc; - account = purple_connection_get_account(gc); - - jb = jabber_buddy_find(jm->js, contact, TRUE); - jbr = jabber_buddy_find_resource(jb, jid->resource); - - if (jbr && jm->chat_state != JM_STATE_NONE) - jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED; - - switch(jm->chat_state) { - case JM_STATE_COMPOSING: - purple_serv_got_typing(gc, contact, 0, PURPLE_IM_TYPING); - break; - case JM_STATE_PAUSED: - purple_serv_got_typing(gc, contact, 0, PURPLE_IM_TYPED); - break; - case JM_STATE_GONE: { - PurpleConversation *im = NULL; - - im = purple_conversation_manager_find_im(manager, account, contact); - - if (im && jid->node && jid->domain) { - char buf[256]; - PurpleBuddy *buddy; - - g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain); - - if ((buddy = purple_blist_find_buddy(account, buf))) { - const char *who; - char *escaped; - - who = purple_buddy_get_alias(buddy); - escaped = g_markup_escape_text(who, -1); - - g_snprintf(buf, sizeof(buf), - _("%s has left the conversation."), escaped); - g_free(escaped); - - /* At some point when we restructure PurpleConversation, - * this should be able to be implemented by removing the - * user from the conversation like we do with chats now. */ - purple_conversation_write_system_message(im, buf, 0); - } - } - purple_serv_got_typing_stopped(gc, contact); - break; - } - default: - purple_serv_got_typing_stopped(gc, contact); - } - - body = jm_body_with_oob(jm); - - if(body && body->len) { - if (jid->resource) { - /* - * We received a message from a specific resource, so - * we probably want a reply to go to this specific - * resource (i.e. bind/lock the conversation to this - * resource). - * - * This works because purple_im_conversation_send gets the name - * from purple_conversation_get_name() - */ - PurpleConversation *im; - - im = purple_conversation_manager_find_im(manager, account, contact); - if (im && !purple_strequal(contact, - purple_conversation_get_name(im))) { - purple_debug_info("jabber", "Binding conversation to %s\n", - contact); - purple_conversation_set_name(im, contact); - } - } - - if(jbr) { - /* Treat SUPPORTED as a terminal with no escape :) */ - if (jbr->chat_states != JABBER_CHAT_STATES_SUPPORTED) { - if (jm->chat_state != JM_STATE_NONE) - jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED; - else - jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; - } - - g_free(jbr->thread_id); - jbr->thread_id = g_strdup(jm->thread_id); - } - - if(jm->forwarded) { - flags |= PURPLE_MESSAGE_FORWARDED; - } - flags |= jm->outgoing ? PURPLE_MESSAGE_SEND : PURPLE_MESSAGE_RECV; - - purple_serv_got_im(gc, contact, body->str, flags, - (time_t)g_date_time_to_unix(jm->sent)); - } - - jabber_id_free(jid); - - if(body) - g_string_free(body, TRUE); -} - -static void handle_headline(JabberMessage *jm) -{ - char *title; - GString *body; - - if(!jm->xhtml && !jm->body) - return; /* ignore headlines without any content */ - - body = jm_body_with_oob(jm); - title = g_strdup_printf(_("Message from %s"), jm->from); - - purple_notify_formatted(jm->js->gc, title, jm->subject ? jm->subject : title, - NULL, body->str, NULL, NULL); - - g_free(title); - g_string_free(body, TRUE); -} - -static void handle_groupchat(JabberMessage *jm) -{ - JabberID *jid = jabber_id_new(jm->from); - JabberChat *chat; - PurpleMessageFlags messageFlags = 0; - - if(!jid) - return; - - chat = jabber_chat_find(jm->js, jid->node, jid->domain); - - if(!chat) - return; - - if(jm->subject) { - /* TODO: use set_topic_full when we support contact info's. */ - purple_conversation_set_topic(PURPLE_CONVERSATION(chat->conv), - jm->subject); - messageFlags |= PURPLE_MESSAGE_NO_LOG; - if(!jm->xhtml && !jm->body) { - char *msg, *tmp, *tmp2; - tmp = g_markup_escape_text(jm->subject, -1); - tmp2 = purple_markup_linkify(tmp); - if(jid->resource) - msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, tmp2); - else - msg = g_strdup_printf(_("The topic is: %s"), tmp2); - purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv), - msg, messageFlags); - g_free(tmp); - g_free(tmp2); - g_free(msg); - } - } - - if(jm->xhtml || jm->body) { - if(jid->resource) { - time_t sent = (time_t)g_date_time_to_unix(jm->sent); - - purple_serv_got_chat_in(jm->js->gc, chat->id, jid->resource, - messageFlags | (jm->delayed ? PURPLE_MESSAGE_DELAYED : 0), - jm->xhtml ? jm->xhtml : jm->body, sent); - } else if(chat->muc) { - purple_conversation_write_system_message( - PURPLE_CONVERSATION(chat->conv), - jm->xhtml ? jm->xhtml : jm->body, messageFlags); - } - } - - jabber_id_free(jid); -} - -static void handle_groupchat_invite(JabberMessage *jm) -{ - GHashTable *components; - JabberID *jid = jabber_id_new(jm->to); - - if(!jid) - return; - - components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - g_hash_table_replace(components, "room", g_strdup(jid->node)); - g_hash_table_replace(components, "server", g_strdup(jid->domain)); - g_hash_table_replace(components, "handle", g_strdup(jm->js->user->node)); - g_hash_table_replace(components, "password", g_strdup(jm->password)); - - jabber_id_free(jid); - purple_serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components); -} - -static void handle_error(JabberMessage *jm) -{ - char *buf; - - if(!jm->body) - return; - - buf = g_strdup_printf(_("Message delivery to %s failed: %s"), - jm->from, jm->error ? jm->error : ""); - - purple_notify_formatted(jm->js->gc, _("XMPP Message Error"), _("XMPP Message Error"), buf, - jm->xhtml ? jm->xhtml : jm->body, NULL, NULL); - - g_free(buf); -} - -static gchar * -jabber_message_xml_to_string_strip_img_smileys(PurpleXmlNode *xhtml) -{ - gchar *markup = purple_xmlnode_to_str(xhtml, NULL); - int len = strlen(markup); - int pos = 0; - GString *out = g_string_new(NULL); - - while (pos < len) { - /* this is a bit cludgy, maybe there is a better way to do this... - we need to find all <img> tags within the XHTML and replace those - tags with the value of their "alt" attributes */ - if (g_str_has_prefix(&(markup[pos]), "<img")) { - PurpleXmlNode *img = NULL; - int pos2 = pos; - const gchar *src; - - for (; pos2 < len ; pos2++) { - if (g_str_has_prefix(&(markup[pos2]), "/>")) { - pos2 += 2; - break; - } else if (g_str_has_prefix(&(markup[pos2]), "</img>")) { - pos2 += 5; - break; - } - } - - /* note, if the above loop didn't find the end of the <img> tag, - it the parsed string will be until the end of the input string, - in which case purple_xmlnode_from_str will bail out and return NULL, - in this case the "if" statement below doesn't trigger and the - text is copied unchanged */ - img = purple_xmlnode_from_str(&(markup[pos]), pos2 - pos); - src = purple_xmlnode_get_attrib(img, "src"); - - if (g_str_has_prefix(src, "cid:")) { - const gchar *alt = purple_xmlnode_get_attrib(img, "alt"); - /* if the "alt" attribute is empty, put the cid as smiley string */ - if (alt && alt[0] != '\0') { - /* if the "alt" is the same as the CID, as Jabbim does, - this prevents linkification... */ - if (purple_email_is_valid(alt)) { - gchar *safe_alt = g_strdup_printf("smiley:%s", alt); - out = g_string_append(out, safe_alt); - g_free(safe_alt); - } else { - gchar *alt_escaped = g_markup_escape_text(alt, -1); - out = g_string_append(out, alt_escaped); - g_free(alt_escaped); - } - } else { - out = g_string_append(out, src); - } - pos += pos2 - pos; - } else { - out = g_string_append_c(out, markup[pos]); - pos++; - } - - purple_xmlnode_free(img); - - } else { - out = g_string_append_c(out, markup[pos]); - pos++; - } - } - - g_free(markup); - return g_string_free(out, FALSE); -} - -void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet) -{ - JabberMessage *jm; - const char *id, *from, *to, *type; - PurpleXmlNode *child = NULL, *received = NULL; - gboolean signal_return; - gboolean delayed = FALSE, is_outgoing = FALSE, is_forwarded = FALSE; - GDateTime *timestamp = g_date_time_new_now_utc(); - - /* Check if we have a carbons received element from our own account. */ - from = purple_xmlnode_get_attrib(packet, "from"); - if(from != NULL && jabber_is_own_account(js, from)) { - PurpleXmlNode *forwarded = NULL; - - /* We check if this is a received carbon first. */ - received = purple_xmlnode_get_child_with_namespace(packet, "received", - NS_MESSAGE_CARBONS); - if(received != NULL) { - forwarded = purple_xmlnode_get_child_with_namespace(received, - "forwarded", - NS_FORWARD); - } else { - PurpleXmlNode *sent = NULL; - - sent = purple_xmlnode_get_child_with_namespace(packet, "sent", - NS_MESSAGE_CARBONS); - if(sent != NULL) { - forwarded = purple_xmlnode_get_child_with_namespace(sent, - "forwarded", - NS_FORWARD); - is_outgoing = TRUE; - } - } - - if(forwarded != NULL) { - PurpleXmlNode *fwd_msg = NULL; - - fwd_msg = purple_xmlnode_get_child_with_namespace(forwarded, - "message", - NS_XMPP_CLIENT); - if(fwd_msg != NULL) { - PurpleXmlNode *delay = NULL; - - /* We have a forwarded message, so update the packet to point - * to it directly. - */ - packet = fwd_msg; - is_forwarded = TRUE; - - /* Now check if it was a delayed message and if so, grab the - * timestamp that the server sent. - */ - delay = purple_xmlnode_get_child_with_namespace(forwarded, - "delay", - NS_DELAYED_DELIVERY); - if(delay != NULL) { - GDateTime *delayed_ts = NULL; - GTimeZone *tz = g_time_zone_new_utc(); - const gchar *ts = purple_xmlnode_get_attrib(delay, - "stamp"); - - delayed_ts = g_date_time_new_from_iso8601(ts, tz); - g_time_zone_unref(tz); - - if(delayed_ts != NULL) { - delayed = TRUE; - g_date_time_unref(timestamp); - timestamp = delayed_ts; - } - } - } - } - } - - /* If the message was forwarded, packet is now pointing to the forwarded - * message. - */ - from = purple_xmlnode_get_attrib(packet, "from"); - id = purple_xmlnode_get_attrib(packet, "id"); - to = purple_xmlnode_get_attrib(packet, "to"); - type = purple_xmlnode_get_attrib(packet, "type"); - - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), - "jabber-receiving-message", js->gc, type, id, from, to, packet)); - if (signal_return) - return; - - jm = g_new0(JabberMessage, 1); - jm->js = js; - jm->sent = timestamp; - jm->delayed = delayed; - jm->chat_state = JM_STATE_NONE; - jm->forwarded = is_forwarded; - jm->outgoing = is_outgoing; - - if(type) { - if(purple_strequal(type, "normal")) - jm->type = JABBER_MESSAGE_NORMAL; - else if(purple_strequal(type, "chat")) - jm->type = JABBER_MESSAGE_CHAT; - else if(purple_strequal(type, "groupchat")) - jm->type = JABBER_MESSAGE_GROUPCHAT; - else if(purple_strequal(type, "headline")) - jm->type = JABBER_MESSAGE_HEADLINE; - else if(purple_strequal(type, "error")) - jm->type = JABBER_MESSAGE_ERROR; - else - jm->type = JABBER_MESSAGE_OTHER; - } else { - jm->type = JABBER_MESSAGE_NORMAL; - } - - jm->from = g_strdup(from); - jm->to = g_strdup(to); - jm->id = g_strdup(id); - - for(child = packet->child; child; child = child->next) { - const char *xmlns = purple_xmlnode_get_namespace(child); - if(child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - if(purple_strequal(child->name, "error")) { - const char *code = purple_xmlnode_get_attrib(child, "code"); - char *code_txt = NULL; - char *text = purple_xmlnode_get_data(child); - if (!text) { - PurpleXmlNode *enclosed_text_node; - - if ((enclosed_text_node = purple_xmlnode_get_child(child, "text"))) - text = purple_xmlnode_get_data(enclosed_text_node); - } - - if(code) - code_txt = g_strdup_printf(_("(Code %s)"), code); - - if(!jm->error) - jm->error = g_strdup_printf("%s%s%s", - text ? text : "", - text && code_txt ? " " : "", - code_txt ? code_txt : ""); - - g_free(code_txt); - g_free(text); - } else if (xmlns == NULL) { - /* QuLogic: Not certain this is correct, but it would have happened - with the previous code. */ - if(purple_strequal(child->name, "x")) - jm->etc = g_list_append(jm->etc, child); - /* The following tests expect xmlns != NULL */ - continue; - } else if(purple_strequal(child->name, "subject") && purple_strequal(xmlns, NS_XMPP_CLIENT)) { - if(!jm->subject) { - jm->subject = purple_xmlnode_get_data(child); - if(!jm->subject) - jm->subject = g_strdup(""); - } - } else if(purple_strequal(child->name, "thread") && purple_strequal(xmlns, NS_XMPP_CLIENT)) { - if(!jm->thread_id) - jm->thread_id = purple_xmlnode_get_data(child); - } else if(purple_strequal(child->name, "body") && purple_strequal(xmlns, NS_XMPP_CLIENT)) { - if(!jm->body) { - char *msg = purple_xmlnode_get_data(child); - char *escaped = g_markup_escape_text(msg, -1); - jm->body = purple_strdup_withhtml(escaped); - g_free(escaped); - g_free(msg); - } - } else if(purple_strequal(child->name, "html") && purple_strequal(xmlns, NS_XHTML_IM)) { - if(!jm->xhtml && purple_xmlnode_get_child(child, "body")) { - char *c; - gchar *reformatted_xhtml; - - purple_xmlnode_strip_prefixes(child); - - /* reformat xhtml so that img tags with a "cid:" src gets - translated to the bare text of the emoticon (the "alt" attrib) */ - /* this is done also when custom smiley retrieval is turned off, - this way the receiver always sees the shortcut instead */ - reformatted_xhtml = - jabber_message_xml_to_string_strip_img_smileys(child); - - jm->xhtml = reformatted_xhtml; - - /* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention - * treated \n as a newline for compatibility with other protocols - */ - for (c = jm->xhtml; *c != '\0'; c++) { - if (*c == '\n') - *c = ' '; - } - } - } else if(purple_strequal(child->name, "active") && purple_strequal(xmlns,"http://jabber.org/protocol/chatstates")) { - jm->chat_state = JM_STATE_ACTIVE; - } else if(purple_strequal(child->name, "composing") && purple_strequal(xmlns,"http://jabber.org/protocol/chatstates")) { - jm->chat_state = JM_STATE_COMPOSING; - } else if(purple_strequal(child->name, "paused") && purple_strequal(xmlns,"http://jabber.org/protocol/chatstates")) { - jm->chat_state = JM_STATE_PAUSED; - } else if(purple_strequal(child->name, "inactive") && purple_strequal(xmlns,"http://jabber.org/protocol/chatstates")) { - jm->chat_state = JM_STATE_INACTIVE; - } else if(purple_strequal(child->name, "gone") && purple_strequal(xmlns,"http://jabber.org/protocol/chatstates")) { - jm->chat_state = JM_STATE_GONE; - } else if(purple_strequal(child->name, "event") && purple_strequal(xmlns,"http://jabber.org/protocol/pubsub#event")) { - PurpleXmlNode *items; - jm->type = JABBER_MESSAGE_EVENT; - for(items = purple_xmlnode_get_child(child,"items"); items; items = items->next) - jm->eventitems = g_list_append(jm->eventitems, items); - } else if(purple_strequal(child->name, "delay") && purple_strequal(xmlns, NS_DELAYED_DELIVERY)) { - const char *stamp = purple_xmlnode_get_attrib(child, "stamp"); - if(stamp != NULL) { - GDateTime *delayed_ts = NULL; - GTimeZone *tz = g_time_zone_new_utc(); - - delayed_ts = g_date_time_new_from_iso8601(stamp, tz); - g_time_zone_unref(tz); - - if(delayed_ts != NULL) { - jm->delayed = TRUE; - g_date_time_unref(jm->sent); - jm->sent = delayed_ts; - } - } - } else if(purple_strequal(child->name, "x")) { - if(purple_strequal(xmlns, NS_DELAYED_DELIVERY_LEGACY)) { - const char *stamp = purple_xmlnode_get_attrib(child, "stamp"); - - if(stamp != NULL) { - GDateTime *delayed_ts = NULL; - GTimeZone *tz = g_time_zone_new_utc(); - - delayed_ts = g_date_time_new_from_iso8601(stamp, tz); - g_time_zone_unref(tz); - - if(delayed_ts != NULL) { - jm->delayed = TRUE; - g_date_time_unref(jm->sent); - jm->sent = delayed_ts; - } - } - } else if(purple_strequal(xmlns, "jabber:x:conference") && - jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE && - jm->type != JABBER_MESSAGE_ERROR) { - const char *jid = purple_xmlnode_get_attrib(child, "jid"); - if(jid) { - const char *reason = purple_xmlnode_get_attrib(child, "reason"); - const char *password = purple_xmlnode_get_attrib(child, "password"); - - jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; - g_free(jm->to); - jm->to = g_strdup(jid); - - if (reason) { - g_free(jm->body); - jm->body = g_strdup(reason); - } - - if (password) { - g_free(jm->password); - jm->password = g_strdup(password); - } - } - } else if(purple_strequal(xmlns, "http://jabber.org/protocol/muc#user") && - jm->type != JABBER_MESSAGE_ERROR) { - PurpleXmlNode *invite = purple_xmlnode_get_child(child, "invite"); - if(invite) { - PurpleXmlNode *reason, *password; - const char *jid = purple_xmlnode_get_attrib(invite, "from"); - g_free(jm->to); - jm->to = jm->from; - jm->from = g_strdup(jid); - if((reason = purple_xmlnode_get_child(invite, "reason"))) { - g_free(jm->body); - jm->body = purple_xmlnode_get_data(reason); - } - if((password = purple_xmlnode_get_child(child, "password"))) { - g_free(jm->password); - jm->password = purple_xmlnode_get_data(password); - } - - jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; - } - } else { - jm->etc = g_list_append(jm->etc, child); - } - } else if (purple_strequal(child->name, "query")) { - const char *node = purple_xmlnode_get_attrib(child, "node"); - if (purple_strequal(xmlns, NS_DISCO_ITEMS) - && purple_strequal(node, "http://jabber.org/protocol/commands")) { - jabber_adhoc_got_list(js, jm->from, child); - } - } - } - - switch(jm->type) { - case JABBER_MESSAGE_OTHER: - purple_debug_info("jabber", - "Received message of unknown type: %s\n", type); - G_GNUC_FALLTHROUGH; - case JABBER_MESSAGE_NORMAL: - case JABBER_MESSAGE_CHAT: - handle_chat(jm); - break; - case JABBER_MESSAGE_HEADLINE: - handle_headline(jm); - break; - case JABBER_MESSAGE_GROUPCHAT: - handle_groupchat(jm); - break; - case JABBER_MESSAGE_GROUPCHAT_INVITE: - handle_groupchat_invite(jm); - break; - case JABBER_MESSAGE_EVENT: - jabber_handle_event(jm); - break; - case JABBER_MESSAGE_ERROR: - handle_error(jm); - break; - } - jabber_message_free(jm); -} - -void jabber_message_send(JabberMessage *jm) -{ - PurpleXmlNode *message, *child; - const char *type = NULL; - - message = purple_xmlnode_new("message"); - - switch(jm->type) { - case JABBER_MESSAGE_NORMAL: - type = "normal"; - break; - case JABBER_MESSAGE_CHAT: - case JABBER_MESSAGE_GROUPCHAT_INVITE: - type = "chat"; - break; - case JABBER_MESSAGE_HEADLINE: - type = "headline"; - break; - case JABBER_MESSAGE_GROUPCHAT: - type = "groupchat"; - break; - case JABBER_MESSAGE_ERROR: - type = "error"; - break; - case JABBER_MESSAGE_OTHER: - default: - type = NULL; - break; - } - - if(type) - purple_xmlnode_set_attrib(message, "type", type); - - if (jm->id) - purple_xmlnode_set_attrib(message, "id", jm->id); - - purple_xmlnode_set_attrib(message, "to", jm->to); - - if(jm->thread_id) { - child = purple_xmlnode_new_child(message, "thread"); - purple_xmlnode_insert_data(child, jm->thread_id, -1); - } - - child = NULL; - switch(jm->chat_state) - { - case JM_STATE_ACTIVE: - child = purple_xmlnode_new_child(message, "active"); - break; - case JM_STATE_COMPOSING: - child = purple_xmlnode_new_child(message, "composing"); - break; - case JM_STATE_PAUSED: - child = purple_xmlnode_new_child(message, "paused"); - break; - case JM_STATE_INACTIVE: - child = purple_xmlnode_new_child(message, "inactive"); - break; - case JM_STATE_GONE: - child = purple_xmlnode_new_child(message, "gone"); - break; - case JM_STATE_NONE: - /* yep, nothing */ - break; - } - if(child) - purple_xmlnode_set_namespace(child, "http://jabber.org/protocol/chatstates"); - - if(jm->subject) { - child = purple_xmlnode_new_child(message, "subject"); - purple_xmlnode_insert_data(child, jm->subject, -1); - } - - if(jm->body) { - child = purple_xmlnode_new_child(message, "body"); - purple_xmlnode_insert_data(child, jm->body, -1); - } - - if(jm->xhtml) { - if ((child = purple_xmlnode_from_str(jm->xhtml, -1))) { - purple_xmlnode_insert_child(message, child); - } else { - purple_debug_error("jabber", - "XHTML translation/validation failed, returning: %s\n", - jm->xhtml); - } - } - - jabber_send(jm->js, message); - - purple_xmlnode_free(message); -} - -/* - * Compare the XHTML and plain strings passed in for "equality". Any HTML markup - * other than <br/> (matches a newline) in the XHTML will cause this to return - * FALSE. - */ -static gboolean -jabber_xhtml_plain_equal(const char *xhtml_escaped, const char *plain) -{ - int i = 0; - int j = 0; - gboolean ret; - char *xhtml = purple_unescape_html(xhtml_escaped); - - while (xhtml[i] && plain[j]) { - if (xhtml[i] == plain[j]) { - i += 1; - j += 1; - continue; - } - - if (plain[j] == '\n' && !strncmp(xhtml+i, "<br/>", 5)) { - i += 5; - j += 1; - continue; - } - - g_free(xhtml); - return FALSE; - } - - /* Are we at the end of both strings? */ - ret = (xhtml[i] == plain[j]) && (xhtml[i] == '\0'); - g_free(xhtml); - return ret; -} - -int -jabber_message_send_im(G_GNUC_UNUSED PurpleProtocolIM *pim, - PurpleConnection *gc, - G_GNUC_UNUSED PurpleConversation *conversation, - PurpleMessage *msg) -{ - JabberMessage *jm; - JabberBuddy *jb; - JabberBuddyResource *jbr; - char *xhtml; - char *tmp; - char *resource; - const gchar *rcpt = purple_message_get_recipient(msg); - - if (!rcpt || purple_message_is_empty(msg)) - return 0; - - resource = jabber_get_resource(rcpt); - - jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), rcpt, TRUE); - jbr = jabber_buddy_find_resource(jb, resource); - - g_free(resource); - - jm = g_new0(JabberMessage, 1); - jm->js = purple_connection_get_protocol_data(gc); - jm->type = JABBER_MESSAGE_CHAT; - jm->chat_state = JM_STATE_ACTIVE; - jm->to = g_strdup(rcpt); - jm->id = jabber_get_next_id(jm->js); - - if(jbr) { - if(jbr->thread_id) - jm->thread_id = jbr->thread_id; - - if (jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED) - jm->chat_state = JM_STATE_NONE; - else { - /* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states) - jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */ - } - } - - tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg)); - purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body); - g_free(tmp); - - /* - * For backward compatibility with user expectations or for those not on - * the user's roster, allow sending XHTML-IM markup. - */ - if (jbr == NULL || jbr->caps == NULL || - jabber_resource_has_capability(jbr, NS_XHTML_IM)) { - if (!jabber_xhtml_plain_equal(xhtml, jm->body)) - /* Wrap the message in <p/> for great interoperability justice. */ - jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml); - } - - g_free(xhtml); - - jabber_message_send(jm); - jabber_message_free(jm); - return 1; -} - -gint -jabber_message_send_chat(G_GNUC_UNUSED PurpleProtocolChat *protocol_chat, - PurpleConnection *gc, gint id, - G_GNUC_UNUSED PurpleConversation *conversation, - PurpleMessage *msg) -{ - JabberChat *chat; - JabberMessage *jm; - JabberStream *js; - char *xhtml; - char *tmp; - - if (!gc || purple_message_is_empty(msg)) - return 0; - - js = purple_connection_get_protocol_data(gc); - chat = jabber_chat_find_by_id(js, id); - - if(!chat) - return 0; - - jm = g_new0(JabberMessage, 1); - jm->js = purple_connection_get_protocol_data(gc); - jm->type = JABBER_MESSAGE_GROUPCHAT; - jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); - jm->id = jabber_get_next_id(jm->js); - - tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg)); - purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body); - g_free(tmp); - - if (chat->xhtml && !jabber_xhtml_plain_equal(xhtml, jm->body)) - /* Wrap the message in <p/> for greater interoperability justice. */ - jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml); - - g_free(xhtml); - - jabber_message_send(jm); - jabber_message_free(jm); - - return 1; -} - -unsigned int -jabber_send_typing(G_GNUC_UNUSED PurpleProtocolIM *pim, PurpleConnection *gc, - const char *who, PurpleIMTypingState state) -{ - JabberStream *js; - JabberMessage *jm; - JabberBuddy *jb; - JabberBuddyResource *jbr; - char *resource; - - js = purple_connection_get_protocol_data(gc); - jb = jabber_buddy_find(js, who, TRUE); - if (!jb) - return 0; - - resource = jabber_get_resource(who); - jbr = jabber_buddy_find_resource(jb, resource); - g_free(resource); - - /* We know this entity doesn't support chat states */ - if (jbr && jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED) - return 0; - - /* *If* we don't have presence /and/ the buddy can't see our - * presence, don't send typing notifications. - */ - if (!jbr && !(jb->subscription & JABBER_SUB_FROM)) - return 0; - - /* TODO: figure out threading */ - jm = g_new0(JabberMessage, 1); - jm->js = js; - jm->type = JABBER_MESSAGE_CHAT; - jm->to = g_strdup(who); - jm->id = jabber_get_next_id(jm->js); - - if(PURPLE_IM_TYPING == state) - jm->chat_state = JM_STATE_COMPOSING; - else if(PURPLE_IM_TYPED == state) - jm->chat_state = JM_STATE_PAUSED; - else - jm->chat_state = JM_STATE_ACTIVE; - - /* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states) - jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */ - - jabber_message_send(jm); - jabber_message_free(jm); - - return 0; -}
--- a/libpurple/protocols/jabber/message.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/** - * @file message.h Message handlers - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_MESSAGE_H -#define PURPLE_JABBER_MESSAGE_H - -#include <purple.h> - -#include "jabber.h" - -typedef struct { - JabberStream *js; - enum { - JABBER_MESSAGE_NORMAL, - JABBER_MESSAGE_CHAT, - JABBER_MESSAGE_GROUPCHAT, - JABBER_MESSAGE_HEADLINE, - JABBER_MESSAGE_ERROR, - JABBER_MESSAGE_GROUPCHAT_INVITE, - JABBER_MESSAGE_EVENT, - JABBER_MESSAGE_OTHER - } type; - GDateTime *sent; - gboolean delayed; - gboolean forwarded; - gboolean outgoing; - char *id; - char *from; - char *to; - char *subject; - char *body; - char *xhtml; - char *password; - char *error; - char *thread_id; - enum { - JM_STATE_NONE, - JM_STATE_ACTIVE, - JM_STATE_COMPOSING, - JM_STATE_PAUSED, - JM_STATE_INACTIVE, - JM_STATE_GONE - } chat_state; - GList *etc; - GList *eventitems; -} JabberMessage; - -void jabber_message_free(JabberMessage *jm); - -void jabber_message_send(JabberMessage *jm); - -void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet); -int jabber_message_send_im(PurpleProtocolIM *pim, PurpleConnection *gc, PurpleConversation *conversation, PurpleMessage *msg); -int jabber_message_send_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, PurpleConversation *conversation, PurpleMessage *msg); - -unsigned int jabber_send_typing(PurpleProtocolIM *pim, PurpleConnection *gc, const char *who, PurpleIMTypingState state); - -#endif /* PURPLE_JABBER_MESSAGE_H */
--- a/libpurple/protocols/jabber/namespaces.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_NAMESPACES_H -#define PURPLE_JABBER_NAMESPACES_H - -#define NS_XMPP_BIND "urn:ietf:params:xml:ns:xmpp-bind" -#define NS_XMPP_CLIENT "jabber:client" -#define NS_XMPP_SASL "urn:ietf:params:xml:ns:xmpp-sasl" -#define NS_XMPP_SESSION "urn:ietf:params:xml:ns:xmpp-session" -#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" -#define NS_XMPP_STREAMS "http://etherx.jabber.org/streams" -#define NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls" - -/* XEP-0012 Last Activity (and XEP-0256 Last Activity in Presence) */ -#define NS_LAST_ACTIVITY "jabber:iq:last" - -/* XEP-0030 Service Discovery */ -#define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" -#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" - -/* XEP-0047 IBB (In-band bytestreams) */ -#define NS_IBB "http://jabber.org/protocol/ibb" - -/* XEP-0065 SOCKS5 Bytestreams */ -#define NS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" - -/* XEP-0066 Out of Band Data (OOB) */ -#define NS_OOB_IQ_DATA "jabber:iq:oob" -#define NS_OOB_X_DATA "jabber:x:oob" - -/* XEP-0071 XHTML-IM (rich-text messages) */ -#define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im" -#define NS_XHTML "http://www.w3.org/1999/xhtml" - -/* XEP-0084 v1.1 User Avatar */ -#define NS_AVATAR_1_1_DATA "urn:xmpp:avatar:data" -#define NS_AVATAR_1_1_METADATA "urn:xmpp:avatar:metadata" - -/* XEP-0096 SI File Transfer */ -#define NS_SI_FILE_TRANSFER "http://jabber.org/protocol/si/profile/file-transfer" - -/* XEP-0124 Bidirectional-streams Over Synchronous HTTP (BOSH) */ -#define NS_BOSH "http://jabber.org/protocol/httpbind" - -/* XEP-0191 Simple Communications Blocking */ -#define NS_SIMPLE_BLOCKING "urn:xmpp:blocking" - -/* XEP-0199 Ping */ -#define NS_PING "urn:xmpp:ping" - -/* XEP-0202 Entity Time */ -#define NS_ENTITY_TIME "urn:xmpp:time" - -/* XEP-0203 Delayed Delivery (and legacy delayed delivery) */ -#define NS_DELAYED_DELIVERY "urn:xmpp:delay" -#define NS_DELAYED_DELIVERY_LEGACY "jabber:x:delay" - -/* XEP-0206 XMPP over BOSH */ -#define NS_XMPP_BOSH "urn:xmpp:xbosh" - -/* XEP-0231 BoB (Bits of Binary) */ -#define NS_BOB "urn:xmpp:bob" - -/* XEP-0233 XMPP Server Registration for use with Kerberos V5 */ -#define NS_XMPP_SERVER_REGISTRATION "urn:xmpp:domain-based-name:1" - -/* XEP-0237 Roster Versioning */ -#define NS_ROSTER_VERSIONING "urn:xmpp:features:rosterver" - -/* XEP-0264 File Transfer Thumbnails (Thumbs) */ -#define NS_THUMBS "urn:xmpp:thumbs:0" - -/* XEP-0280 Message Carbons */ -#define NS_MESSAGE_CARBONS "urn:xmpp:carbons:2" - -/* XEP-0297 Stanza Forwarding */ -#define NS_FORWARD "urn:xmpp:forward:0" - -#endif /* PURPLE_JABBER_NAMESPACES_H */
--- a/libpurple/protocols/jabber/oob.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <purple.h> - -#include "jabber.h" -#include "iq.h" -#include "oob.h" - -/* Number of bytes to read asynchronously per chunk. */ -#define JABBER_OOB_READ_SIZE (1024*1024) - -struct _JabberOOBXfer { - JabberStream *js; - gchar *iq_id; - gchar *url; - GCancellable *cancellable; - SoupMessage *msg; -}; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED(JabberOOBXfer, jabber_oob_xfer, - PURPLE_TYPE_XFER, G_TYPE_FLAG_FINAL, {}) - -static void jabber_oob_xfer_xfer_init(PurpleXfer *xfer) -{ - purple_xfer_start(xfer, -1, NULL, 0); -} - -static void jabber_oob_xfer_end(PurpleXfer *xfer) -{ - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - JabberIq *iq; - - iq = jabber_iq_new(jox->js, JABBER_IQ_RESULT); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - jabber_iq_set_id(iq, jox->iq_id); - - jabber_iq_send(iq); -} - -static void -jabber_oob_xfer_got_content_length(SoupMessage *msg, gpointer user_data) -{ - PurpleXfer *xfer = user_data; - SoupMessageHeaders *headers; - goffset total; - - headers = soup_message_get_response_headers(msg); - total = soup_message_headers_get_content_length(headers); - - purple_xfer_set_size(xfer, total); -} - -static void -jabber_oob_xfer_writer(GObject *source, GAsyncResult *result, gpointer data) { - GInputStream *input = G_INPUT_STREAM(source); - PurpleXfer *xfer = data; - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - GBytes *bytes = NULL; - GError *error = NULL; - gpointer buffer = NULL; - gsize size = 0; - - bytes = g_input_stream_read_bytes_finish(input, result, &error); - if(bytes == NULL || error != NULL) { - purple_debug_error("jabber", "Error reading OOB data: %s", - error ? error->message : "unknown error"); - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); - purple_xfer_end(xfer); - g_clear_pointer(&bytes, g_bytes_unref); - g_clear_object(&input); - g_clear_error(&error); - jox->msg = NULL; - return; - } - - buffer = g_bytes_unref_to_data(bytes, &size); - - /* We've reached the end of the file. */ - if(size == 0) { - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); - g_clear_object(&input); - jox->msg = NULL; - return; - } - - if (!purple_xfer_write_file(xfer, buffer, size)) { - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL); - purple_xfer_end(xfer); - g_clear_object(&input); - jox->msg = NULL; - return; - } - - /* Asynchronously read the next chunk of data. */ - g_input_stream_read_bytes_async(input, JABBER_OOB_READ_SIZE, - G_PRIORITY_DEFAULT, jox->cancellable, - jabber_oob_xfer_writer, xfer); -} - -static void -jabber_oob_xfer_send_cb(GObject *source, GAsyncResult *result, gpointer data) { - PurpleXfer *xfer = data; - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - GInputStream *input = NULL; - GError *error = NULL; - - if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(jox->msg))) { - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); - purple_xfer_end(xfer); - jox->msg = NULL; - return; - } - - input = soup_session_send_finish(SOUP_SESSION(source), result, &error); - if(input == NULL || error != NULL) { - purple_debug_error("jabber", "Error sending OOB request: %s", - error ? error->message : "unknown error"); - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); - purple_xfer_end(xfer); - g_clear_object(&input); - g_clear_error(&error); - jox->msg = NULL; - return; - } - - /* Asynchronously read an initial chunk of data. */ - g_input_stream_read_bytes_async(input, JABBER_OOB_READ_SIZE, - G_PRIORITY_DEFAULT, jox->cancellable, - jabber_oob_xfer_writer, xfer); -} - -static void jabber_oob_xfer_start(PurpleXfer *xfer) -{ - SoupMessage *msg; - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - - msg = soup_message_new("GET", jox->url); - soup_message_add_header_handler( - msg, "got-headers", "Content-Length", - G_CALLBACK(jabber_oob_xfer_got_content_length), xfer); - soup_session_send_async(jox->js->http_conns, msg, G_PRIORITY_DEFAULT, - jox->cancellable, jabber_oob_xfer_send_cb, xfer); -} - -static void jabber_oob_xfer_recv_error(PurpleXfer *xfer, const char *code) { - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - JabberIq *iq; - PurpleXmlNode *y, *z; - - iq = jabber_iq_new(jox->js, JABBER_IQ_ERROR); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - jabber_iq_set_id(iq, jox->iq_id); - y = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(y, "code", code); - if(purple_strequal(code, "406")) { - z = purple_xmlnode_new_child(y, "not-acceptable"); - purple_xmlnode_set_attrib(y, "type", "modify"); - purple_xmlnode_set_namespace(z, NS_XMPP_STANZAS); - } else if(purple_strequal(code, "404")) { - z = purple_xmlnode_new_child(y, "not-found"); - purple_xmlnode_set_attrib(y, "type", "cancel"); - purple_xmlnode_set_namespace(z, NS_XMPP_STANZAS); - } - jabber_iq_send(iq); -} - -static void jabber_oob_xfer_recv_denied(PurpleXfer *xfer) { - jabber_oob_xfer_recv_error(xfer, "406"); -} - -static void jabber_oob_xfer_recv_cancelled(PurpleXfer *xfer) { - JabberOOBXfer *jox = JABBER_OOB_XFER(xfer); - - g_cancellable_cancel(jox->cancellable); - - jabber_oob_xfer_recv_error(xfer, "404"); -} - -void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, PurpleXmlNode *querynode) { - JabberOOBXfer *jox; - const gchar *filename, *slash; - gchar *url; - PurpleXmlNode *urlnode; - - if(type != JABBER_IQ_SET) - return; - - if(!from) - return; - - if(!(urlnode = purple_xmlnode_get_child(querynode, "url"))) - return; - - url = purple_xmlnode_get_data(urlnode); - if (!url) - return; - - jox = g_object_new( - JABBER_TYPE_OOB_XFER, - "account", purple_connection_get_account(js->gc), - "type", PURPLE_XFER_TYPE_RECEIVE, - "remote-user", from, - NULL - ); - - jox->iq_id = g_strdup(id); - jox->js = js; - jox->url = url; - - slash = strrchr(url, '/'); - if (slash == NULL) { - filename = url; - } else { - filename = slash + 1; - } - - purple_xfer_set_filename(PURPLE_XFER(jox), filename); - - js->oob_file_transfers = g_list_append(js->oob_file_transfers, jox); - - purple_xfer_request(PURPLE_XFER(jox)); -} - -static void -jabber_oob_xfer_init(JabberOOBXfer *xfer) { - xfer->cancellable = g_cancellable_new(); -} - -static void -jabber_oob_xfer_finalize(GObject *obj) { - JabberOOBXfer *jox = JABBER_OOB_XFER(obj); - - jox->js->oob_file_transfers = g_list_remove(jox->js->oob_file_transfers, - jox); - - g_free(jox->iq_id); - g_free(jox->url); - if(jox->cancellable != NULL) { - g_cancellable_cancel(jox->cancellable); - } - g_clear_object(&jox->cancellable); - - G_OBJECT_CLASS(jabber_oob_xfer_parent_class)->finalize(obj); -} - -static void -jabber_oob_xfer_class_finalize(G_GNUC_UNUSED JabberOOBXferClass *klass) { -} - -static void -jabber_oob_xfer_class_init(JabberOOBXferClass *klass) { - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass); - - obj_class->finalize = jabber_oob_xfer_finalize; - - xfer_class->init = jabber_oob_xfer_xfer_init; - xfer_class->end = jabber_oob_xfer_end; - xfer_class->request_denied = jabber_oob_xfer_recv_denied; - xfer_class->cancel_recv = jabber_oob_xfer_recv_cancelled; - xfer_class->start = jabber_oob_xfer_start; -} - -void -jabber_oob_xfer_register(GTypeModule *module) { - jabber_oob_xfer_register_type(module); -}
--- a/libpurple/protocols/jabber/oob.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * @file oob.h out-of-band transfer functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_OOB_H -#define PURPLE_JABBER_OOB_H - -#include "jabber.h" - -G_BEGIN_DECLS - -#define JABBER_TYPE_OOB_XFER (jabber_oob_xfer_get_type()) -G_DECLARE_FINAL_TYPE(JabberOOBXfer, jabber_oob_xfer, JABBER, OOB_XFER, PurpleXfer); - -void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, PurpleXmlNode *querynode); - -void jabber_oob_xfer_register(GTypeModule *module); - -G_END_DECLS - -#endif /* PURPLE_JABBER_OOB_H */
--- a/libpurple/protocols/jabber/parser.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,317 +0,0 @@ -/* - * purple - Jabber XML parser stuff - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <libxml/parser.h> - -#include <purple.h> - -#include "jabber.h" -#include "parser.h" - -static void -jabber_parser_element_start_libxml(void *user_data, - const xmlChar *element_name, - const xmlChar *prefix, - const xmlChar *namespace, int nb_namespaces, - const xmlChar **namespaces, - int nb_attributes, - G_GNUC_UNUSED int nb_defaulted, - const xmlChar **attributes) -{ - JabberStream *js = user_data; - PurpleXmlNode *node; - int i, j; - - if(!element_name) { - return; - } else if (js->stream_id == NULL) { - /* Sanity checking! */ - if (0 != xmlStrcmp(element_name, (xmlChar *) "stream") || - 0 != xmlStrcmp(namespace, (xmlChar *) NS_XMPP_STREAMS)) { - /* We were expecting a <stream:stream/> opening stanza, but - * didn't get it. Bad! - */ - purple_debug_error("jabber", "Expecting stream header, got %s with " - "xmlns %s\n", element_name, namespace); - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - _("XMPP stream header missing")); - return; - } - - js->protocol_version.major = 0; - js->protocol_version.minor = 9; - - for (i = 0; i < nb_attributes * 5; i += 5) { - int attrib_len = attributes[i+4] - attributes[i+3]; - char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len); - - if(!xmlStrcmp(attributes[i], (xmlChar*) "version")) { - const char *dot = strchr(attrib, '.'); - - js->protocol_version.major = atoi(attrib); - js->protocol_version.minor = dot ? atoi(dot + 1) : 0; - - if (js->protocol_version.major > 1) { - /* TODO: Send <unsupported-version/> error */ - purple_connection_error(js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - _("XMPP Version Mismatch")); - g_free(attrib); - return; - } - - if (js->protocol_version.major == 0 && js->protocol_version.minor != 9) { - purple_debug_warning("jabber", "Treating version %s as 0.9 for backward " - "compatibility\n", attrib); - } - g_free(attrib); - } else if(!xmlStrcmp(attributes[i], (xmlChar*) "id")) { - g_free(js->stream_id); - js->stream_id = attrib; - } else { - g_free(attrib); - } - } - - if (js->stream_id == NULL) { - /* Let's make up a placeholder stream ID, which we need to do - * because we flag on it being NULL as a special case in this - * parsing code. - */ - js->stream_id = g_strdup(""); - purple_debug_info("jabber", "Server failed to specify a stream " - "ID (underspecified in rfc3920, but intended " - "to be a MUST; digest legacy auth may fail.\n"); - } - } else { - - if(js->current) - node = purple_xmlnode_new_child(js->current, (const char*) element_name); - else - node = purple_xmlnode_new((const char*) element_name); - purple_xmlnode_set_namespace(node, (const char*) namespace); - purple_xmlnode_set_prefix(node, (const char *)prefix); - - if (nb_namespaces != 0) { - node->namespace_map = g_hash_table_new_full( - g_str_hash, g_str_equal, g_free, g_free); - - for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) { - const char *key = (const char *)namespaces[j]; - const char *val = (const char *)namespaces[j + 1]; - g_hash_table_insert(node->namespace_map, - g_strdup(key ? key : ""), g_strdup(val ? val : "")); - } - } - for(i=0; i < nb_attributes * 5; i+=5) { - const char *name = (const char *)attributes[i]; - const char *prefix = (const char *)attributes[i+1]; - const char *attrib_ns = (const char *)attributes[i+2]; - char *txt; - int attrib_len = attributes[i+4] - attributes[i+3]; - char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len); - - txt = attrib; - attrib = purple_unescape_text(txt); - g_free(txt); - purple_xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib); - g_free(attrib); - } - - js->current = node; - } -} - -static void -jabber_parser_element_end_libxml(void *user_data, const xmlChar *element_name, - G_GNUC_UNUSED const xmlChar *prefix, - G_GNUC_UNUSED const xmlChar *namespace) -{ - JabberStream *js = user_data; - - if(!js->current) - return; - - if(js->current->parent) { - if(!xmlStrcmp((xmlChar*) js->current->name, element_name)) - js->current = js->current->parent; - } else { - PurpleXmlNode *packet = js->current; - js->current = NULL; - jabber_process_packet(js, &packet); - if (packet != NULL) - purple_xmlnode_free(packet); - } -} - -static void -jabber_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) -{ - JabberStream *js = user_data; - - if(!js->current) - return; - - if(!text || !text_len) - return; - - purple_xmlnode_insert_data(js->current, (const char*) text, text_len); -} - -static void -jabber_parser_structured_error_handler(void *user_data, const xmlError *error) -{ - JabberStream *js = user_data; - - if (error->level == XML_ERR_WARNING - && purple_strequal(error->message, "xmlns: URI vcard-temp is not absolute\n")) - /* - * This message happens when parsing vcards, and is normal, so don't - * bother logging it because people scare easily. - */ - return; - - if (error->level == XML_ERR_FATAL && error->code == XML_ERR_DOCUMENT_END) - /* - * This is probably more annoying than the vcard-temp error; it occurs - * because we disconnect in most cases without waiting for the receiving - * </stream:stream> (limitations of libpurple) - */ - return; - - purple_debug_error("jabber", "XML parser error for JabberStream %p: " - "Domain %i, code %i, level %i: %s", - js, - error->domain, error->code, error->level, - (error->message ? error->message : "(null)\n")); -} - -static xmlSAXHandler jabber_parser_libxml = { - NULL, /*internalSubset*/ - NULL, /*isStandalone*/ - NULL, /*hasInternalSubset*/ - NULL, /*hasExternalSubset*/ - NULL, /*resolveEntity*/ - NULL, /*getEntity*/ - NULL, /*entityDecl*/ - NULL, /*notationDecl*/ - NULL, /*attributeDecl*/ - NULL, /*elementDecl*/ - NULL, /*unparsedEntityDecl*/ - NULL, /*setDocumentLocator*/ - NULL, /*startDocument*/ - NULL, /*endDocument*/ - NULL, /*startElement*/ - NULL, /*endElement*/ - NULL, /*reference*/ - jabber_parser_element_text_libxml, /*characters*/ - NULL, /*ignorableWhitespace*/ - NULL, /*processingInstruction*/ - NULL, /*comment*/ - NULL, /*warning*/ - NULL, /*error*/ - NULL, /*fatalError*/ - NULL, /*getParameterEntity*/ - NULL, /*cdataBlock*/ - NULL, /*externalSubset*/ - XML_SAX2_MAGIC, /*initialized*/ - NULL, /*_private*/ - jabber_parser_element_start_libxml, /*startElementNs*/ - jabber_parser_element_end_libxml, /*endElementNs*/ - (xmlStructuredErrorFunc)jabber_parser_structured_error_handler /*serror*/ -}; - -void -jabber_parser_setup(JabberStream *js) -{ - /* This seems backwards, but it makes sense. The libxml code creates - * the parser context when you try to use it (this way, it can figure - * out the encoding at creation time. So, setting up the parser is - * just a matter of destroying any current parser. */ - jabber_parser_free(js); -} - -void jabber_parser_free(JabberStream *js) { - if (js->context) { - xmlParseChunk(js->context, NULL,0,1); - xmlFreeParserCtxt(js->context); - js->context = NULL; - } -} - -void jabber_parser_process(JabberStream *js, const char *buf, int len) -{ - int ret; - - if (js->context == NULL) { - /* libxml inconsistently starts parsing on creating the - * parser, so do a ParseChunk right afterwards to force it. */ - js->context = xmlCreatePushParserCtxt(&jabber_parser_libxml, js, buf, len, NULL); - xmlParseChunk(js->context, "", 0, 0); - } else if ((ret = xmlParseChunk(js->context, buf, len, 0)) != XML_ERR_OK) { - const xmlError *err = xmlCtxtGetLastError(js->context); - /* - * libxml2 uses a global setting to determine whether or not to store - * warnings. Other libraries may set this, which causes err to be - * NULL. See #8136 for details. - */ - xmlErrorLevel level = XML_ERR_WARNING; - - if (err) - level = err->level; - - switch (level) { - case XML_ERR_NONE: - purple_debug_info("jabber", "xmlParseChunk returned info %i\n", ret); - break; - case XML_ERR_WARNING: - purple_debug_warning("jabber", "xmlParseChunk returned warning %i\n", ret); - break; - case XML_ERR_ERROR: - purple_debug_error("jabber", "xmlParseChunk returned error %i\n", ret); - break; - case XML_ERR_FATAL: - purple_debug_error("jabber", "xmlParseChunk returned fatal %i\n", ret); - purple_connection_error (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("XML Parse error")); - break; - } - } - - if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 && - !purple_connection_get_error_info(js->gc) && - (js->state == JABBER_STREAM_INITIALIZING || - js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION)) { - /* - * Legacy servers don't advertise features, so if we've just gotten - * the opening <stream:stream> and there was no version, we need to - * immediately start legacy IQ auth. - */ - jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); - jabber_auth_start_old(js); - } -} -
--- a/libpurple/protocols/jabber/parser.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * @file parser.h XML parser functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_PARSER_H -#define PURPLE_JABBER_PARSER_H - -#include "jabber.h" - -void jabber_parser_setup(JabberStream *js); -void jabber_parser_free(JabberStream *js); -void jabber_parser_process(JabberStream *js, const char *buf, int len); - -#endif /* PURPLE_JABBER_PARSER_H */
--- a/libpurple/protocols/jabber/pep.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include "pep.h" -#include "iq.h" -#include <string.h> -#include "useravatar.h" -#include "usernick.h" - -static GHashTable *pep_handlers = NULL; - -void jabber_pep_init(void) { - if(!pep_handlers) { - pep_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - /* register PEP handlers */ - jabber_avatar_init(); - jabber_nick_init(); - } -} - -void jabber_pep_uninit(void) { - /* any PEP handlers that need to clean things up go here. The standard - * cleanup of removing the handler and feature are handled here and by - * jabber_features_destroy() in jabber.c - */ - g_clear_pointer(&pep_handlers, g_hash_table_destroy); -} - -void -jabber_pep_add_action_entries(GSimpleActionGroup *group) { - jabber_nick_add_action_entries(group); -} - -void -jabber_pep_append_menu(GMenu *menu) { - /* register the PEP-specific actions */ - jabber_nick_append_menu(menu); -} - -void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc) { - gchar *notifyns = g_strdup_printf("%s+notify", xmlns); - jabber_add_feature(notifyns, NULL); /* receiving PEPs is always supported */ - g_free(notifyns); - g_hash_table_replace(pep_handlers, g_strdup(xmlns), handlerfunc); -} - -static void -do_pep_iq_request_item_callback(JabberStream *js, const char *from, - JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - PurpleXmlNode *pubsub; - PurpleXmlNode *items = NULL; - JabberPEPHandler *cb = data; - - if (type == JABBER_IQ_RESULT) { - pubsub = purple_xmlnode_get_child_with_namespace(packet, "pubsub", "http://jabber.org/protocol/pubsub"); - if(pubsub) - items = purple_xmlnode_get_child(pubsub, "items"); - } - - cb(js, from, items); -} - -void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); - PurpleXmlNode *pubsub, *items; - - if (to) - purple_xmlnode_set_attrib(iq->node, "to", to); - - pubsub = purple_xmlnode_new_child(iq->node,"pubsub"); - purple_xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub"); - - items = purple_xmlnode_new_child(pubsub, "items"); - purple_xmlnode_set_attrib(items,"node",node); - - if (id) { - PurpleXmlNode *item = purple_xmlnode_new_child(items, "item"); - purple_xmlnode_set_attrib(item, "id", id); - } else - /* Most recent item */ - purple_xmlnode_set_attrib(items, "max_items", "1"); - - jabber_iq_set_callback(iq,do_pep_iq_request_item_callback,(gpointer)cb); - - jabber_iq_send(iq); -} - -gboolean -jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, - G_GNUC_UNUSED const char *namespace) -{ - return js->pep; -} - -void jabber_handle_event(JabberMessage *jm) { - /* this may be called even when the own server doesn't support pep! */ - JabberPEPHandler *jph; - GList *itemslist; - char *jid; - - if (jm->type != JABBER_MESSAGE_EVENT) - return; - - jid = jabber_get_bare_jid(jm->from); - - for(itemslist = jm->eventitems; itemslist; itemslist = itemslist->next) { - PurpleXmlNode *items = (PurpleXmlNode*)itemslist->data; - const char *nodename = purple_xmlnode_get_attrib(items,"node"); - - if(nodename && (jph = g_hash_table_lookup(pep_handlers, nodename))) - jph(jm->js, jid, items); - } - - /* discard items we don't have a handler for */ - g_free(jid); -} - -void jabber_pep_publish(JabberStream *js, PurpleXmlNode *publish) { - JabberIq *iq; - PurpleXmlNode *pubsub; - - if(js->pep != TRUE) { - /* ignore when there's no PEP support on the server */ - purple_xmlnode_free(publish); - return; - } - - iq = jabber_iq_new(js, JABBER_IQ_SET); - - pubsub = purple_xmlnode_new("pubsub"); - purple_xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub"); - - purple_xmlnode_insert_child(pubsub, publish); - - purple_xmlnode_insert_child(iq->node, pubsub); - - jabber_iq_send(iq); -}
--- a/libpurple/protocols/jabber/pep.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_PEP_H -#define PURPLE_JABBER_PEP_H - -#include "jabber.h" -#include "message.h" -#include "buddy.h" - -void jabber_pep_init(void); -void jabber_pep_uninit(void); - -void jabber_pep_add_action_entries(GSimpleActionGroup *group); -void jabber_pep_append_menu(GMenu *menu); - -/* - * Callback for receiving PEP events. - * - * @parameter js The JabberStream this item was received on - * @parameter items The <items/>-tag with the <item/>-children - */ -typedef void (JabberPEPHandler)(JabberStream *js, const char *from, PurpleXmlNode *items); - -/* - * Registers a callback for PEP events. Also automatically announces this receiving capability via disco#info. - * Don't forget to use jabber_add_feature when supporting the sending of PEP events of this type. - * - * @parameter xmlns The namespace for this event - * @parameter handlerfunc The callback to be used when receiving an event with this namespace - */ -void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc); - -/* - * Request a specific item from another PEP node. - * - * @parameter js The JabberStream that should be used - * @parameter to The target PEP node - * @parameter node The node name of the item that is requested - * @parameter id The item id of the requested item (may be NULL) - * @parameter cb The callback to be used when this item is received - * - * The items element passed to the callback will be NULL if any error occurred (like a permission error, node doesn't exist etc.) - */ -void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb); - -/* - * Default callback that can be used for namespaces which should only be enabled when PEP is supported - * - * @parameter js The JabberStream struct for this connection - * @parameter namespace The namespace that's queried, ignored. - * - * @returns TRUE when PEP is enabled, FALSE otherwise - */ -gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *namespace); - -void jabber_handle_event(JabberMessage *jm); - -/* - * Publishes PEP item(s) - * - * @parameter js The JabberStream associated with the connection this event should be published - * @parameter publish The publish node. This could be for example <publish node='http://jabber.org/protocol/tune'/> with an <item/> as subnode - */ -void jabber_pep_publish(JabberStream *js, PurpleXmlNode *publish); - -#endif /* PURPLE_JABBER_PEP_H */
--- a/libpurple/protocols/jabber/ping.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purple.h> - -#include "jabber.h" -#include "ping.h" -#include "iq.h" - -static void -jabber_keepalive_pong_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - g_clear_handle_id(&js->keepalive_timeout, g_source_remove); -} - -void -jabber_ping_parse(JabberStream *js, const char *from, JabberIqType type, - const char *id, G_GNUC_UNUSED PurpleXmlNode *ping) -{ - if (type == JABBER_IQ_GET) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_RESULT); - - if (from) - purple_xmlnode_set_attrib(iq->node, "to", from); - purple_xmlnode_set_attrib(iq->node, "id", id); - - jabber_iq_send(iq); - } else if (type == JABBER_IQ_SET) { - /* XXX: error */ - } -} - -static void -jabber_ping_result_cb(G_GNUC_UNUSED JabberStream *js, - G_GNUC_UNUSED const char *from, JabberIqType type, - G_GNUC_UNUSED const char *id, - G_GNUC_UNUSED PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - if (type == JABBER_IQ_RESULT) - purple_debug_info("jabber", "PONG!\n"); - else - purple_debug_info("jabber", "ping not supported\n"); -} - -void jabber_keepalive_ping(JabberStream *js) -{ - JabberIq *iq; - PurpleXmlNode *ping; - - iq = jabber_iq_new(js, JABBER_IQ_GET); - ping = purple_xmlnode_new_child(iq->node, "ping"); - purple_xmlnode_set_namespace(ping, NS_PING); - - jabber_iq_set_callback(iq, jabber_keepalive_pong_cb, NULL); - jabber_iq_send(iq); -} - -gboolean jabber_ping_jid(JabberStream *js, const char *jid) -{ - JabberIq *iq; - PurpleXmlNode *ping; - - iq = jabber_iq_new(js, JABBER_IQ_GET); - if (jid) - purple_xmlnode_set_attrib(iq->node, "to", jid); - - ping = purple_xmlnode_new_child(iq->node, "ping"); - purple_xmlnode_set_namespace(ping, NS_PING); - - jabber_iq_set_callback(iq, jabber_ping_result_cb, NULL); - jabber_iq_send(iq); - - return TRUE; -}
--- a/libpurple/protocols/jabber/ping.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/** - * @file ping.h ping functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_PING_H -#define PURPLE_JABBER_PING_H - -#include <purple.h> - -#include "jabber.h" -#include "iq.h" - -void jabber_ping_parse(JabberStream *js, const char *from, - JabberIqType, const char *id, PurpleXmlNode *child); -gboolean jabber_ping_jid(JabberStream *js, const char *jid); -void jabber_keepalive_ping(JabberStream *js); - -#endif /* PURPLE_JABBER_PING_H */
--- a/libpurple/protocols/jabber/presence.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1248 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "buddy.h" -#include "chat.h" -#include "presence.h" -#include "iq.h" -#include "jutil.h" -#include "adhoccommands.h" - -static GHashTable *presence_handlers = NULL; - -static const struct { - const char *name; - JabberPresenceType type; -} jabber_presence_types[] = { - { "error", JABBER_PRESENCE_ERROR }, - { "probe", JABBER_PRESENCE_PROBE }, - { "unavailable", JABBER_PRESENCE_UNAVAILABLE }, - { "subscribe", JABBER_PRESENCE_SUBSCRIBE }, - { "subscribed", JABBER_PRESENCE_SUBSCRIBED }, - { "unsubscribe", JABBER_PRESENCE_UNSUBSCRIBE }, - { "unsubscribed", JABBER_PRESENCE_UNSUBSCRIBED } - /* { NULL, JABBER_PRESENCE_AVAILABLE } the default */ -}; - -typedef void (JabberPresenceHandler)(JabberStream *js, - JabberPresence *presence, - PurpleXmlNode *child); - -static JabberPresenceType -str_to_presence_type(const char *type) -{ - gsize i; - - if (type == NULL) - return JABBER_PRESENCE_AVAILABLE; - - for (i = 0; i < G_N_ELEMENTS(jabber_presence_types); ++i) - if (purple_strequal(type, jabber_presence_types[i].name)) - return jabber_presence_types[i].type; - - purple_debug_warning("jabber", "Unknown presence type '%s'\n", type); - return JABBER_PRESENCE_AVAILABLE; -} - -static void -chats_send_presence_foreach(G_GNUC_UNUSED gpointer key, gpointer val, - gpointer user_data) -{ - JabberChat *chat = val; - PurpleXmlNode *presence = user_data; - char *chat_full_jid; - - if(!chat->conv || chat->left) - return; - - chat_full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, - chat->handle); - - purple_xmlnode_set_attrib(presence, "to", chat_full_jid); - jabber_send(chat->js, presence); - g_free(chat_full_jid); -} - -void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status) -{ - PurpleAccount *account; - PurplePresence *presence; - JabberBuddy *jb; - JabberBuddyResource *jbr; - const char *username; - JabberBuddyState state; - char *msg; - int priority; - - g_return_if_fail(js->user != NULL); - - account = purple_connection_get_account(js->gc); - username = purple_connection_get_display_name(js->gc); - presence = purple_account_get_presence(account); - if (status == NULL) - status = purple_presence_get_active_status(presence); - purple_status_to_jabber(status, &state, &msg, &priority); - - jb = js->user_jb; - - if (state == JABBER_BUDDY_STATE_UNAVAILABLE || - state == JABBER_BUDDY_STATE_UNKNOWN) { - jabber_buddy_remove_resource(jb, js->user->resource); - } else { - jbr = jabber_buddy_track_resource(jb, js->user->resource, priority, - state, msg); - - jbr->idle = 0; - if(purple_presence_is_idle(presence)) { - GDateTime *idle_since = purple_presence_get_idle_time(presence); - - if(idle_since != NULL) { - jbr->idle = g_date_time_to_unix(idle_since); - } - } - } - - /* - * While we need to track the status of this resource, the core - * only cares if we're on our own buddy list. - */ - if (purple_blist_find_buddy(account, username)) { - jbr = jabber_buddy_find_resource(jb, NULL); - if (jbr) { - purple_protocol_got_user_status(account, username, - jabber_buddy_state_get_status_id(jbr->state), - "priority", jbr->priority, - jbr->status ? "message" : NULL, jbr->status, - NULL); - purple_protocol_got_user_idle(account, username, jbr->idle, jbr->idle); - } else { - purple_protocol_got_user_status(account, username, "offline", - msg ? "message" : NULL, msg, - NULL); - } - } - g_free(msg); -} - -void -jabber_set_status(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - JabberStream *js; - - if (!purple_account_is_connected(account)) - return; - - if (purple_status_is_exclusive(status) && !purple_status_is_active(status)) { - /* An exclusive status can't be deactivated. You should just - * activate some other exclusive status. */ - return; - } - - gc = purple_account_get_connection(account); - js = purple_connection_get_protocol_data(gc); - - jabber_presence_send(js, FALSE); -} - -void jabber_presence_send(JabberStream *js, gboolean force) -{ - PurpleAccount *account; - PurpleXmlNode *presence, *x, *photo; - char *stripped = NULL; - JabberBuddyState state; - int priority; - gboolean allowBuzz; - PurplePresence *p; - PurpleStatus *status; - - account = purple_connection_get_account(js->gc); - p = purple_account_get_presence(account); - status = purple_presence_get_active_status(p); - - /* we don't want to send presence before we've gotten our roster */ - if (js->state != JABBER_STREAM_CONNECTED) { - purple_debug_misc("jabber", "attempt to send presence before roster retrieved\n"); - return; - } - - purple_status_to_jabber(status, &state, &stripped, &priority); - - /* check for buzz support */ - allowBuzz = purple_status_get_attr_boolean(status,"buzz"); - /* changing the buzz state has to trigger a re-broadcasting of the presence for caps */ - - /* check if there are any differences to the <presence> and send them in that case */ - if (force || allowBuzz != js->allowBuzz || js->old_state != state || - !purple_strequal(js->old_msg, stripped) || js->old_priority != priority || - !purple_strequal(js->old_avatarhash, js->avatar_hash) || js->old_idle != js->idle) { - /* Need to update allowBuzz before creating the presence (with caps) */ - js->allowBuzz = allowBuzz; - - presence = jabber_presence_create_js(js, state, stripped, priority); - - /* Per XEP-0153 4.1, we must always send the <x> */ - x = purple_xmlnode_new_child(presence, "x"); - purple_xmlnode_set_namespace(x, "vcard-temp:x:update"); - /* - * FIXME: Per XEP-0153 4.3.2 bullet 2, we must not publish our - * image hash if another resource has logged in and updated the - * vcard avatar. Requires changes in jabber_presence_parse. - */ - if (js->vcard_fetched) { - /* Always publish a <photo>; it's empty if we have no image. */ - photo = purple_xmlnode_new_child(x, "photo"); - if (js->avatar_hash) - purple_xmlnode_insert_data(photo, js->avatar_hash, -1); - } - - jabber_send(js, presence); - - g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence); - purple_xmlnode_free(presence); - - /* update old values */ - - g_free(js->old_msg); - g_free(js->old_avatarhash); - js->old_msg = g_strdup(stripped); - js->old_avatarhash = g_strdup(js->avatar_hash); - js->old_state = state; - js->old_priority = priority; - js->old_idle = js->idle; - } - g_free(stripped); - -#undef CHANGED - - jabber_presence_fake_to_self(js, status); -} - -PurpleXmlNode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority) -{ - PurpleXmlNode *show, *status, *presence, *pri, *c; - const char *show_string = NULL; - - g_return_val_if_fail(js !=NULL, NULL); - - presence = purple_xmlnode_new("presence"); - - if(state == JABBER_BUDDY_STATE_UNAVAILABLE) - purple_xmlnode_set_attrib(presence, "type", "unavailable"); - else if(state != JABBER_BUDDY_STATE_ONLINE && - state != JABBER_BUDDY_STATE_UNKNOWN && - state != JABBER_BUDDY_STATE_ERROR) - show_string = jabber_buddy_state_get_show(state); - - if(show_string) { - show = purple_xmlnode_new_child(presence, "show"); - purple_xmlnode_insert_data(show, show_string, -1); - } - - if(msg) { - status = purple_xmlnode_new_child(presence, "status"); - purple_xmlnode_insert_data(status, msg, -1); - } - - if(priority) { - char *pstr = g_strdup_printf("%d", priority); - pri = purple_xmlnode_new_child(presence, "priority"); - purple_xmlnode_insert_data(pri, pstr, -1); - g_free(pstr); - } - - /* if we are idle and not offline, include idle */ - if (js->idle && state != JABBER_BUDDY_STATE_UNAVAILABLE) { - PurpleXmlNode *query = purple_xmlnode_new_child(presence, "query"); - gchar seconds[10]; - g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle)); - - purple_xmlnode_set_namespace(query, NS_LAST_ACTIVITY); - purple_xmlnode_set_attrib(query, "seconds", seconds); - } - - /* JEP-0115 */ - /* calculate hash */ - jabber_caps_calculate_own_hash(js); - /* create xml */ - c = purple_xmlnode_new_child(presence, "c"); - purple_xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); - purple_xmlnode_set_attrib(c, "node", CAPS0115_NODE); - purple_xmlnode_set_attrib(c, "hash", "sha-1"); - purple_xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js)); - - return presence; -} - -struct _jabber_add_permit { - PurpleConnection *gc; - JabberStream *js; - char *who; -}; - -static void -authorize_add_cb(G_GNUC_UNUSED PurpleAuthorizationRequest *request, - gpointer data) -{ - struct _jabber_add_permit *jap = data; - - PURPLE_ASSERT_CONNECTION_IS_VALID(jap->gc); - - jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc), - jap->who, "subscribed"); - - g_free(jap->who); - g_free(jap); -} - -static void -deny_add_cb(G_GNUC_UNUSED PurpleAuthorizationRequest *request, - G_GNUC_UNUSED const char *message, gpointer data) -{ - struct _jabber_add_permit *jap = data; - - PURPLE_ASSERT_CONNECTION_IS_VALID(jap->gc); - - jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc), - jap->who, "unsubscribed"); - - g_free(jap->who); - g_free(jap); -} - -static void -jabber_vcard_parse_avatar(JabberStream *js, const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - JabberBuddy *jb = NULL; - PurpleXmlNode *vcard, *photo, *binval, *fn, *nick; - char *text; - - if(!from) - return; - - jb = jabber_buddy_find(js, from, TRUE); - - js->pending_avatar_requests = g_slist_remove(js->pending_avatar_requests, jb); - - if((vcard = purple_xmlnode_get_child(packet, "vCard")) || - (vcard = purple_xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) { - /* The logic here regarding the nickname and full name is copied from - * buddy.c:jabber_vcard_parse. */ - gchar *nickname = NULL; - if ((fn = purple_xmlnode_get_child(vcard, "FN"))) - nickname = purple_xmlnode_get_data(fn); - - if ((nick = purple_xmlnode_get_child(vcard, "NICKNAME"))) { - char *tmp = purple_xmlnode_get_data(nick); - char *bare_jid = jabber_get_bare_jid(from); - if (tmp && strstr(bare_jid, tmp) == NULL) { - g_free(nickname); - nickname = tmp; - } else - g_free(tmp); - - g_free(bare_jid); - } - - if (nickname) { - purple_serv_got_alias(js->gc, from, nickname); - g_free(nickname); - } - - if ((photo = purple_xmlnode_get_child(vcard, "PHOTO"))) { - guchar *data = NULL; - gchar *hash = NULL; - gsize size = 0; - - if ((binval = purple_xmlnode_get_child(photo, "BINVAL")) && - (text = purple_xmlnode_get_data(binval))) { - data = g_base64_decode(text, &size); - g_free(text); - - if (data) { - hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, - size); - } - } - - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, data, size, hash); - - g_free(hash); - } - } -} - -typedef struct { - JabberStream *js; - JabberBuddy *jb; - char *from; -} JabberPresenceCapabilities; - -static void -jabber_presence_set_capabilities(JabberCapsClientInfo *info, - JabberPresenceCapabilities *userdata) -{ - JabberBuddyResource *jbr; - char *resource = strchr(userdata->from, '/'); - - if (resource) - resource += 1; - - jbr = jabber_buddy_find_resource(userdata->jb, resource); - if (!jbr) { - g_free(userdata->from); - g_free(userdata); - return; - } - - jbr->caps = info; - - purple_protocol_got_media_caps( - purple_connection_get_account(userdata->js->gc), - userdata->from); - if (info == NULL) - goto out; - - if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) { - JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, NS_DISCO_ITEMS); - PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(iq->node, "query", NS_DISCO_ITEMS); - purple_xmlnode_set_attrib(iq->node, "to", userdata->from); - purple_xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); - jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); - jabber_iq_send(iq); - - jbr->commands_fetched = TRUE; - } - -out: - g_free(userdata->from); - g_free(userdata); -} - -static gboolean -handle_presence_chat(JabberStream *js, JabberPresence *presence, PurpleXmlNode *packet) -{ - static int i = 1; - PurpleChatUserFlags flags = PURPLE_CHAT_USER_NONE; - JabberChat *chat = presence->chat; - - if (presence->state == JABBER_BUDDY_STATE_ERROR) { - char *title, *msg = jabber_parse_error(js, packet, NULL); - - if (!chat->conv) { - title = g_strdup_printf(_("Error joining chat %s"), presence->from); - purple_serv_got_join_chat_failed(js->gc, chat->components); - } else { - title = g_strdup_printf(_("Error in chat %s"), presence->from); - if (g_hash_table_size(chat->members) == 0) - purple_serv_got_chat_left(js->gc, chat->id); - } - purple_notify_error(js->gc, title, title, msg, - purple_request_cpar_from_connection(js->gc)); - g_free(title); - g_free(msg); - - if (g_hash_table_size(chat->members) == 0) - /* Only destroy the chat if the error happened while joining */ - jabber_chat_destroy(chat); - return FALSE; - } - - if (presence->type == JABBER_PRESENCE_AVAILABLE) { - const char *jid = NULL; - const char *affiliation = NULL; - const char *role = NULL; - gboolean is_our_resource = FALSE; /* Is the presence about us? */ - JabberBuddyResource *jbr; - - /* - * XEP-0045 mandates the presence to include a resource (which is - * treated as the chat nick). Some non-compliant servers allow - * joining without a nick. - */ - if (!presence->jid_from->resource) - return FALSE; - - if (presence->chat_info.item) { - jid = purple_xmlnode_get_attrib(presence->chat_info.item, "jid"); - affiliation = purple_xmlnode_get_attrib(presence->chat_info.item, "affiliation"); - role = purple_xmlnode_get_attrib(presence->chat_info.item, "role"); - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110)) || - purple_strequal(presence->jid_from->resource, chat->handle) || - purple_strequal(presence->to, jid)) - is_our_resource = TRUE; - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(201))) { - chat->config_dialog_type = PURPLE_REQUEST_ACTION; - chat->config_dialog_handle = - purple_request_action(js->gc, - _("Create New Room"), - _("Create New Room"), - _("You are creating a new room. Would" - " you like to configure it, or" - " accept the default settings?"), - /* Default Action */ 1, - purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat->conv)), - chat, 2, - _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), - _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(210))) { - /* server rewrote room-nick */ - g_free(chat->handle); - chat->handle = g_strdup(presence->jid_from->resource); - } - - if (purple_strequal(affiliation, "owner")) - flags |= PURPLE_CHAT_USER_FOUNDER; - if (role) { - if (purple_strequal(role, "moderator")) - flags |= PURPLE_CHAT_USER_OP; - else if (purple_strequal(role, "participant")) - flags |= PURPLE_CHAT_USER_VOICE; - } - - if(!chat->conv) { - PurpleConversation *conv; - char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain); - chat->id = i++; - conv = purple_serv_got_joined_chat(js->gc, chat->id, room_jid); - chat->conv = PURPLE_CHAT_CONVERSATION(conv); - purple_chat_conversation_set_nick(chat->conv, chat->handle); - - jabber_chat_disco_traffic(chat); - g_free(room_jid); - } - - jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status); - jbr->commands_fetched = TRUE; - - jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role); - - if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource)) { - gboolean new_arrival = FALSE; - - if(chat->joined != NULL) { - gint newer = -1; - - if(presence->sent != NULL) { - newer = g_date_time_compare(presence->sent, chat->joined); - } - - if(!presence->delayed || newer > 0) { - new_arrival = TRUE; - } - } - - purple_chat_conversation_add_user(chat->conv, - presence->jid_from->resource, - jid, flags, new_arrival); - } else { - purple_chat_user_set_flags(purple_chat_conversation_find_user(chat->conv, presence->jid_from->resource), - flags); - } - - if (is_our_resource && chat->joined == NULL) { - chat->joined = g_date_time_new_now_utc(); - } - - } else if (presence->type == JABBER_PRESENCE_UNAVAILABLE) { - gboolean nick_change = FALSE; - gboolean kick = FALSE; - gboolean is_our_resource = FALSE; /* Is the presence about us? */ - - const char *jid = NULL; - - /* If the chat nick is invalid, we haven't yet joined, or we've - * already left (it was probably us leaving after we closed the - * chat), we don't care. - */ - if (!presence->jid_from->resource || !chat->conv || chat->left) { - if (chat->left && - presence->jid_from->resource && chat->handle && purple_strequal(presence->jid_from->resource, chat->handle)) - jabber_chat_destroy(chat); - return FALSE; - } - - is_our_resource = purple_strequal(presence->jid_from->resource, chat->handle); - - jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); - - if (presence->chat_info.item) - jid = purple_xmlnode_get_attrib(presence->chat_info.item, "jid"); - - if (chat->muc) { - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) { - is_our_resource = TRUE; - chat->joined = 0; - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(301))) { - /* XXX: We got banned. YAY! (No GIR, that's bad) */ - } - - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) { - const char *nick = NULL; - if (presence->chat_info.item) - nick = purple_xmlnode_get_attrib(presence->chat_info.item, "nick"); - - /* nick change */ - if (!nick) { - purple_debug_warning("jabber", "Chat presence indicating a nick change, but no new nickname!\n"); - } else { - nick_change = TRUE; - - if (purple_strequal(presence->jid_from->resource, chat->handle)) { - /* Changing our own nickname */ - g_free(chat->handle); - /* TODO: This should be resourceprep'd */ - chat->handle = g_strdup(nick); - } - - purple_chat_conversation_rename_user(chat->conv, - presence->jid_from->resource, - nick); - jabber_chat_remove_handle(chat, - presence->jid_from->resource); - } - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(307))) { - /* Someone was kicked from the room */ - const char *actor = NULL; - char *reason = NULL; - char *tmp; - - kick = TRUE; - - if (presence->chat_info.item) { - PurpleXmlNode *node; - - node = purple_xmlnode_get_child(presence->chat_info.item, "actor"); - if (node) - actor = purple_xmlnode_get_attrib(node, "jid"); - node = purple_xmlnode_get_child(presence->chat_info.item, "reason"); - if (node) - reason = purple_xmlnode_get_data(node); - } - - if (reason == NULL) - reason = g_strdup(_("No reason")); - - if (is_our_resource) { - if (actor) - tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), - actor, reason); - else - tmp = g_strdup_printf(_("You have been kicked: (%s)"), - reason); - } else { - if (actor) - tmp = g_strdup_printf(_("Kicked by %s (%s)"), - actor, reason); - else - tmp = g_strdup_printf(_("Kicked (%s)"), - reason); - } - - g_free(presence->status); - presence->status = tmp; - - g_free(reason); - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(321))) { - /* XXX: removed due to an affiliation change */ - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(322))) { - /* XXX: removed because room is now members-only */ - } - - if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(332))) { - /* XXX: removed due to system shutdown */ - } - } - - /* - * Possibly another connected resource of our JID (see XEP-0045 - * v1.24 section 7.1.10) being disconnected. Should be - * distinguished by the item_jid. - * Also possibly works around bits of an Openfire bug. See - * #8319. - */ - if (is_our_resource && jid && !purple_strequal(presence->to, jid)) { - /* TODO: When the above is a loop, this needs to still act - * sanely for all cases (this code is a little fragile). */ - if (!kick && !nick_change) - /* Presumably, kicks and nick changes also affect us. */ - is_our_resource = FALSE; - } - - if(!nick_change) { - if (is_our_resource) { - if (kick) { - gchar *msg = g_strdup_printf("%s: %s", - presence->jid_from->resource, - presence->status); - purple_conversation_write_system_message(PURPLE_CONVERSATION(chat->conv), msg, 0); - } - - purple_serv_got_chat_left(js->gc, chat->id); - jabber_chat_destroy(chat); - } else { - purple_chat_conversation_remove_user(chat->conv, presence->jid_from->resource, - presence->status); - jabber_chat_remove_handle(chat, presence->jid_from->resource); - } - } - } - - return TRUE; -} - -static gboolean -handle_presence_contact(JabberStream *js, JabberPresence *presence) -{ - JabberBuddyResource *jbr; - PurpleAccount *account; - PurpleBuddy *b; - char *buddy_name; - PurpleConversation *im; - PurpleConversationManager *manager; - - buddy_name = jabber_id_get_bare_jid(presence->jid_from); - - account = purple_connection_get_account(js->gc); - b = purple_blist_find_buddy(account, buddy_name); - - manager = purple_conversation_manager_get_default(); - /* - * Unbind/unlock from sending messages to a specific resource on - * presence changes. This is locked to a specific resource when - * receiving a message (in message.c). - */ - im = purple_conversation_manager_find_im(manager, account, buddy_name); - if (im) { - purple_debug_info("jabber", "Changed conversation binding from %s to %s", - purple_conversation_get_name(im), buddy_name); - purple_conversation_set_name(im, buddy_name); - } - - if (b == NULL) { - if (presence->jb != js->user_jb) { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - purple_debug_warning("jabber", - "Got presence for unknown buddy %s on account %s (%p)", - buddy_name, - purple_contact_info_get_username(info), - account); - g_free(buddy_name); - return FALSE; - } else { - /* this is a different resource of our own account. Resume even when this account isn't on our blist */ - } - } - - if (b && presence->vcard_avatar_hash) { - const char *ah = presence->vcard_avatar_hash[0] != '\0' ? - presence->vcard_avatar_hash : NULL; - const char *ah2 = purple_buddy_icons_get_checksum_for_user(b); - if (!purple_strequal(ah, ah2)) { - /* XXX this is a crappy way of trying to prevent - * someone from spamming us with presence packets - * and causing us to DoS ourselves...what we really - * need is a queue system that can throttle itself, - * but i'm too tired to write that right now */ - if(!g_slist_find(js->pending_avatar_requests, presence->jb)) { - JabberIq *iq; - PurpleXmlNode *vcard; - - js->pending_avatar_requests = - g_slist_prepend(js->pending_avatar_requests, presence->jb); - - iq = jabber_iq_new(js, JABBER_IQ_GET); - purple_xmlnode_set_attrib(iq->node, "to", buddy_name); - vcard = purple_xmlnode_new_child(iq->node, "vCard"); - purple_xmlnode_set_namespace(vcard, "vcard-temp"); - - jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); - jabber_iq_send(iq); - } - } - } - - if (presence->state == JABBER_BUDDY_STATE_ERROR || - presence->type == JABBER_PRESENCE_UNAVAILABLE || - presence->type == JABBER_PRESENCE_UNSUBSCRIBED) { - jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); - } else { - jbr = jabber_buddy_track_resource(presence->jb, - presence->jid_from->resource, presence->priority, - presence->state, presence->status); - jbr->idle = presence->idle ? time(NULL) - presence->idle : 0; - } - - jbr = jabber_buddy_find_resource(presence->jb, NULL); - if (jbr) { - purple_protocol_got_user_status(account, buddy_name, - jabber_buddy_state_get_status_id(jbr->state), - "priority", jbr->priority, - "message", jbr->status, - NULL); - purple_protocol_got_user_idle(account, buddy_name, - jbr->idle, jbr->idle); - if (presence->nickname) - purple_serv_got_alias(js->gc, buddy_name, presence->nickname); - } else { - purple_protocol_got_user_status(account, buddy_name, - jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), - presence->status ? "message" : NULL, presence->status, - NULL); - } - g_free(buddy_name); - - return TRUE; -} - -void jabber_presence_parse(JabberStream *js, PurpleXmlNode *packet) -{ - const char *type; - JabberBuddyResource *jbr = NULL; - gboolean signal_return, ret; - JabberPresence presence; - PurpleXmlNode *child; - - memset(&presence, 0, sizeof(presence)); - /* defaults */ - presence.state = JABBER_BUDDY_STATE_UNKNOWN; - presence.sent = g_date_time_new_now_utc(); - /* interesting values */ - presence.from = purple_xmlnode_get_attrib(packet, "from"); - presence.to = purple_xmlnode_get_attrib(packet, "to"); - type = purple_xmlnode_get_attrib(packet, "type"); - presence.type = str_to_presence_type(type); - - presence.jb = jabber_buddy_find(js, presence.from, TRUE); - g_return_if_fail(presence.jb != NULL); - - presence.jid_from = jabber_id_new(presence.from); - if (presence.jid_from == NULL) { - purple_debug_error("jabber", "Ignoring presence with malformed 'from' " - "JID: %s\n", presence.from); - return; - } - - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), - "jabber-receiving-presence", js->gc, type, presence.from, packet)); - if (signal_return) { - goto out; - } - - if (presence.jid_from->node) - presence.chat = jabber_chat_find(js, presence.jid_from->node, - presence.jid_from->domain); - g_free(presence.jb->error_msg); - presence.jb->error_msg = NULL; - - if (presence.type == JABBER_PRESENCE_AVAILABLE) { - presence.state = JABBER_BUDDY_STATE_ONLINE; - } else if (presence.type == JABBER_PRESENCE_ERROR) { - /* TODO: Is this handled properly? Should it be treated as per-jbr? */ - char *msg = jabber_parse_error(js, packet, NULL); - presence.state = JABBER_BUDDY_STATE_ERROR; - presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); - } else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) { - /* TODO: Move to handle_subscribe() (so nick is extracted by the - * PresenceHandler */ - struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); - gboolean onlist = FALSE; - PurpleAccount *account; - PurpleAuthorizationRequest *request = NULL; - PurpleNotification *notification = NULL; - PurpleNotificationManager *manager = NULL; - PurpleBuddy *buddy; - PurpleXmlNode *nick; - - account = purple_connection_get_account(js->gc); - buddy = purple_blist_find_buddy(account, presence.from); - nick = purple_xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); - if (nick) - presence.nickname = purple_xmlnode_get_data(nick); - - if (buddy) { - if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) - onlist = TRUE; - } - - jap->gc = js->gc; - jap->who = g_strdup(presence.from); - jap->js = js; - - request = purple_authorization_request_new(account, presence.from); - purple_authorization_request_set_alias(request, presence.nickname); - purple_authorization_request_set_add(request, !onlist); - g_signal_connect(request, "accepted", G_CALLBACK(authorize_add_cb), - jap); - g_signal_connect(request, "denied", G_CALLBACK(deny_add_cb), jap); - - notification = purple_notification_new_from_authorization_request(request); - manager = purple_notification_manager_get_default(); - purple_notification_manager_add(manager, notification); - - g_object_unref(notification); - - goto out; - } else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) { - /* This case (someone has approved our subscribe request) is handled - * by the roster push the server sends along with this. - */ - goto out; - } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBE) { - /* XXX I'm not sure this is the right way to handle this, it - * might be better to add "unsubscribe" to the presence status - * if lower down, but I'm not sure. */ - /* they are unsubscribing from our presence, we don't care */ - /* Well, maybe just a little, we might want/need to start - * acknowledging this (and the others) at some point. */ - goto out; - } else if (presence.type == JABBER_PRESENCE_PROBE) { - purple_debug_warning("jabber", "Ignoring presence probe\n"); - goto out; - } else if (presence.type == JABBER_PRESENCE_UNAVAILABLE) { - presence.state = JABBER_BUDDY_STATE_UNAVAILABLE; - } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBED) { - presence.state = JABBER_BUDDY_STATE_UNKNOWN; - } else { - purple_debug_warning("jabber", "Ignoring presence with invalid type " - "'%s'\n", type); - goto out; - } - - for (child = packet->child; child; child = child->next) { - const char *xmlns; - char *key; - JabberPresenceHandler *pih; - if (child->type != PURPLE_XMLNODE_TYPE_TAG) - continue; - - xmlns = purple_xmlnode_get_namespace(child); - key = g_strdup_printf("%s %s", child->name, xmlns ? xmlns : ""); - pih = g_hash_table_lookup(presence_handlers, key); - g_free(key); - if (pih) - pih(js, &presence, child); - } - - if (presence.delayed && presence.idle && presence.adjust_idle_for_delay) { - GDateTime *now = g_date_time_new_now_utc(); - GTimeSpan difference = 0; - - difference = g_date_time_difference(now, presence.sent); - - g_date_time_unref(now); - - /* Delayed and idle, so update idle time */ - presence.idle = presence.idle + (difference / G_TIME_SPAN_SECOND); - } - - /* TODO: Handle tracking jb(r) here? */ - - if (presence.chat) - ret = handle_presence_chat(js, &presence, packet); - else - ret = handle_presence_contact(js, &presence); - if (!ret) - goto out; - - if (presence.caps && presence.type == JABBER_PRESENCE_AVAILABLE) { - /* handle Entity Capabilities (XEP-0115) */ - const char *node = purple_xmlnode_get_attrib(presence.caps, "node"); - const char *ver = purple_xmlnode_get_attrib(presence.caps, "ver"); - const char *hash = purple_xmlnode_get_attrib(presence.caps, "hash"); - - /* v1.5 uses: node, ver, and hash. */ - if(node != NULL && *node != '\0' && - ver != NULL && *ver != '\0' && - hash != NULL && *hash != '\0') - { - jbr = jabber_buddy_find_resource(presence.jb, presence.jid_from->resource); - - /* Look it up if we don't already have all this information */ - if(jbr == NULL || jbr->caps == NULL || - !purple_strequal(node, jbr->caps->tuple.node) || - !purple_strequal(ver, jbr->caps->tuple.ver) || - !purple_strequal(hash, jbr->caps->tuple.hash)) - { - JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); - userdata->js = js; - userdata->jb = presence.jb; - userdata->from = g_strdup(presence.from); - jabber_caps_get_info(js, presence.from, node, ver, hash, - (jabber_caps_get_info_cb)jabber_presence_set_capabilities, - userdata); - } - } - } - -out: - g_slist_free(presence.chat_info.codes); - g_free(presence.status); - g_free(presence.vcard_avatar_hash); - g_free(presence.nickname); - jabber_id_free(presence.jid_from); - g_clear_pointer(&presence.sent, g_date_time_unref); -} - -void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type) -{ - PurpleXmlNode *presence = purple_xmlnode_new("presence"); - - purple_xmlnode_set_attrib(presence, "to", who); - purple_xmlnode_set_attrib(presence, "type", type); - - jabber_send(js, presence); - purple_xmlnode_free(presence); -} - -void purple_status_to_jabber(PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority) -{ - const char *status_id = NULL; - const char *formatted_msg = NULL; - - if(state) *state = JABBER_BUDDY_STATE_UNKNOWN; - if(msg) *msg = NULL; - if(priority) *priority = 0; - - if(!status) { - if(state) *state = JABBER_BUDDY_STATE_UNAVAILABLE; - } else { - if(state) { - status_id = purple_status_get_id(status); - *state = jabber_buddy_status_id_get_state(status_id); - } - - if(msg) { - formatted_msg = purple_status_get_attr_string(status, "message"); - - /* if the message is blank, then there really isn't a message */ - if(formatted_msg && *formatted_msg) - *msg = purple_markup_strip_html(formatted_msg); - } - - if(priority) - *priority = purple_status_get_attr_int(status, "priority"); - } -} - -/* Incoming presence handlers */ -static void -parse_priority(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *priority) -{ - char *p = purple_xmlnode_get_data(priority); - - if (presence->priority != 0) - purple_debug_warning("jabber", "presence stanza received with multiple " - "priority children!?\n"); - - if (p) { - presence->priority = atoi(p); - g_free(p); - } else - purple_debug_warning("jabber", "Empty <priority/> in presence!\n"); -} - -static void -parse_show(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *show) -{ - char *cdata; - - if (presence->type != JABBER_PRESENCE_AVAILABLE) { - purple_debug_warning("jabber", "<show/> present on presence, but " - "type is not default ('available')\n"); - return; - } - - cdata = purple_xmlnode_get_data(show); - if (cdata) { - presence->state = jabber_buddy_show_get_state(cdata); - g_free(cdata); - } else - purple_debug_warning("jabber", "<show/> present on presence, but " - "no contents!\n"); -} - -static void -parse_status(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *status) -{ - /* TODO: Check/track language attribute? */ - - g_free(presence->status); - presence->status = purple_xmlnode_get_data(status); -} - -static void -parse_delay(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *delay) -{ - GTimeZone *tz = g_time_zone_new_utc(); - const char *stamp = purple_xmlnode_get_attrib(delay, "stamp"); - - presence->delayed = TRUE; - presence->sent = g_date_time_new_from_iso8601(stamp, tz); - - g_time_zone_unref(tz); -} - -static void -parse_idle(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *query) -{ - const gchar *seconds = purple_xmlnode_get_attrib(query, "seconds"); - if (seconds) { - presence->idle = atoi(seconds); - presence->adjust_idle_for_delay = TRUE; - if (presence->idle < 0) { - purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds); - presence->idle = 0; - } - } -} - -static void -parse_caps(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *c) -{ - /* TODO: Move the rest of the caps handling in here, after changing the - * the "do we have details about this (node, ver) and exts" to not - * require the jbr to be present (since that happens later). - */ - presence->caps = c; -} - -static void -parse_nickname(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *nick) -{ - g_free(presence->nickname); - presence->nickname = purple_xmlnode_get_data(nick); -} - -static void -parse_vcard_avatar(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *x) -{ - PurpleXmlNode *photo = purple_xmlnode_get_child(x, "photo"); - - if (photo) { - char *hash_tmp = purple_xmlnode_get_data(photo); - g_free(presence->vcard_avatar_hash); - presence->vcard_avatar_hash = - hash_tmp ? hash_tmp : g_strdup(""); - } -} - -static void -parse_muc_user(G_GNUC_UNUSED JabberStream *js, JabberPresence *presence, - PurpleXmlNode *x) -{ - PurpleXmlNode *status; - - if (presence->chat == NULL) { - purple_debug_warning("jabber", "Ignoring MUC gloop on non-MUC presence\n"); - return; - } - - if (presence->chat->conv == NULL) - presence->chat->muc = TRUE; - - for (status = purple_xmlnode_get_child(x, "status"); status; - status = purple_xmlnode_get_next_twin(status)) { - const char *code = purple_xmlnode_get_attrib(status, "code"); - int val; - if (!code) - continue; - - val = atoi(code); - if (val == 0 || val < 0) { - purple_debug_warning("jabber", "Ignoring bogus status code '%s'\n", - code); - continue; - } - - presence->chat_info.codes = g_slist_prepend(presence->chat_info.codes, GINT_TO_POINTER(val)); - } - - presence->chat_info.item = purple_xmlnode_get_child(x, "item"); -} - -static void -jabber_presence_register_handler(const char *node, const char *xmlns, - JabberPresenceHandler *handler) -{ - /* - * This is valid because nodes nor namespaces cannot have spaces in them - * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and - * http://www.w3.org/TR/REC-xml-names/) - */ - char *key = g_strdup_printf("%s %s", node, xmlns); - g_hash_table_replace(presence_handlers, key, handler); -} - -void jabber_presence_init(void) -{ - presence_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - /* Core RFC things */ - jabber_presence_register_handler("priority", "jabber:client", parse_priority); - jabber_presence_register_handler("show", "jabber:client", parse_show); - jabber_presence_register_handler("status", "jabber:client", parse_status); - - /* XEPs */ - jabber_presence_register_handler("c", "http://jabber.org/protocol/caps", parse_caps); - jabber_presence_register_handler("delay", NS_DELAYED_DELIVERY, parse_delay); - jabber_presence_register_handler("nick", "http://jabber.org/protocol/nick", parse_nickname); - jabber_presence_register_handler("query", NS_LAST_ACTIVITY, parse_idle); - jabber_presence_register_handler("x", NS_DELAYED_DELIVERY_LEGACY, parse_delay); - jabber_presence_register_handler("x", "http://jabber.org/protocol/muc#user", parse_muc_user); - jabber_presence_register_handler("x", "vcard-temp:x:update", parse_vcard_avatar); -} - -void jabber_presence_uninit(void) -{ - g_clear_pointer(&presence_handlers , g_hash_table_destroy); -}
--- a/libpurple/protocols/jabber/presence.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/** - * @file presence.h Presence - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_PRESENCE_H -#define PURPLE_JABBER_PRESENCE_H - -typedef enum { - JABBER_PRESENCE_ERROR = -2, - JABBER_PRESENCE_PROBE = -1, - JABBER_PRESENCE_AVAILABLE, - JABBER_PRESENCE_UNAVAILABLE, - JABBER_PRESENCE_SUBSCRIBE, - JABBER_PRESENCE_SUBSCRIBED, - JABBER_PRESENCE_UNSUBSCRIBE, - JABBER_PRESENCE_UNSUBSCRIBED -} JabberPresenceType; - -typedef struct _JabberPresenceChatInfo JabberPresenceChatInfo; -typedef struct _JabberPresence JabberPresence; - -#include <purple.h> - -#include "buddy.h" -#include "chat.h" -#include "jabber.h" -#include "jutil.h" - -struct _JabberPresenceChatInfo { - GSList *codes; - PurpleXmlNode *item; -}; - -struct _JabberPresence { - JabberPresenceType type; - JabberID *jid_from; - const char *from; - const char *to; - const char *id; - - JabberBuddy *jb; - JabberChat *chat; - JabberPresenceChatInfo chat_info; - PurpleXmlNode *caps; /* TODO: Temporary, see presence.c:parse_caps */ - - JabberBuddyState state; - gchar *status; - int priority; - - char *vcard_avatar_hash; - char *nickname; - - gboolean delayed; - GDateTime *sent; - int idle; - gboolean adjust_idle_for_delay; -}; - -void jabber_presence_init(void); -void jabber_presence_uninit(void); - -void jabber_set_status(PurpleProtocolServer *protocol_server, PurpleAccount *account, PurpleStatus *status); - -/** - * Send a full presence stanza. - * - * @param js A JabberStream object. - * @param force Force sending the presence stanza, irrespective of whether - * the contents seem to have changed. - */ -void jabber_presence_send(JabberStream *js, gboolean force); - -PurpleXmlNode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority); -void jabber_presence_parse(JabberStream *js, PurpleXmlNode *packet); -void jabber_presence_subscription_set(JabberStream *js, const char *who, - const char *type); -void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status); -void purple_status_to_jabber(PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority); - -#endif /* PURPLE_JABBER_PRESENCE_H */
--- a/libpurple/protocols/jabber/resources/icons/16x16/apps/scalable/im-jabber.svg Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" - id="svg3302" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:docbase="/home/hbons/Desktop" - sodipodi:docname="jabber.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - inkscape:export-filename="/home/hbons/Desktop/xmpp16.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90" - version="1.0"> - <defs - id="defs3304"> - <linearGradient - id="linearGradient2234" - inkscape:collect="always"> - <stop - id="stop2236" - offset="0" - style="stop-color:#d9541e;stop-opacity:1" /> - <stop - id="stop2238" - offset="1" - style="stop-color:#717311;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient2218" - inkscape:collect="always"> - <stop - id="stop2220" - offset="0" - style="stop-color:#033e6f;stop-opacity:1" /> - <stop - id="stop2222" - offset="1" - style="stop-color:#0f97cb;stop-opacity:1" /> - </linearGradient> - <linearGradient - id="linearGradient2209" - inkscape:collect="always"> - <stop - id="stop2211" - offset="0" - style="stop-color:#439638;stop-opacity:1" /> - <stop - id="stop2213" - offset="1" - style="stop-color:#05757c;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3334"> - <stop - style="stop-color:#f0a530;stop-opacity:1;" - offset="0" - id="stop3336" /> - <stop - style="stop-color:#f0a530;stop-opacity:0;" - offset="1" - id="stop3338" /> - </linearGradient> - <filter - inkscape:collect="always" - x="-0.49491513" - width="1.9898303" - y="-1.0582331" - height="3.1164663" - id="filter3871"> - <feGaussianBlur - inkscape:collect="always" - stdDeviation="5.4785069" - id="feGaussianBlur3873" /> - </filter> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2218" - id="linearGradient2244" - gradientUnits="userSpaceOnUse" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" - gradientTransform="matrix(0.3406067,0,0,0.3698986,2.395e-3,-0.5863818)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3334" - id="linearGradient2252" - gradientUnits="userSpaceOnUse" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" - gradientTransform="matrix(-0.3406067,0,0,0.3698986,15.999925,-0.5863818)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2209" - id="linearGradient2216" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.3406067,0,0,0.3754611,0.1185465,-0.6525396)" - x1="18.734463" - y1="21.519651" - x2="15.642859" - y2="23.876795" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2234" - id="linearGradient2231" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.3406067,0,0,0.3698986,2.025084e-2,-0.6190852)" - x1="30.893675" - y1="21.130915" - x2="37.48666" - y2="23.216526" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="28" - inkscape:cx="21.256159" - inkscape:cy="8.9295837" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:grid-bbox="true" - inkscape:document-units="px" - inkscape:window-width="1434" - inkscape:window-height="823" - inkscape:window-x="3" - inkscape:window-y="43" - width="16px" - height="16px" /> - <metadata - id="metadata3307"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer1" - inkscape:label="Layer 1" - inkscape:groupmode="layer"> - <path - style="fill:#0a6fa2;fill-opacity:1;stroke:#02396a;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 15.5,2.5000001 L 10.454106,2.5327029 C 10.454106,4.6682012 8.4691474,12.778185 3.1541859,14.341671 C 9.641668,14.341671 15.5,7.1171759 15.5,2.5000001 z " - id="path2228" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient2231);stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 15.5,2.5062012 L 9.5791052,2.5000001 C 9.5791052,8.214349 8.5048611,12.745482 3.1899,14.308968 C 9.6773816,14.308968 15.5,7.123377 15.5,2.5062012 z " - id="path3388" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="arc" - style="opacity:0.29670332;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3871)" - id="path3425" - sodipodi:cx="22.273863" - sodipodi:cy="36.736797" - sodipodi:rx="13.283506" - sodipodi:ry="6.2124381" - d="M 35.557369 36.736797 A 13.283506 6.2124381 0 1 1 8.9903564,36.736797 A 13.283506 6.2124381 0 1 1 35.557369 36.736797 z" - transform="matrix(0.3743374,0,0,0.1458475,-0.1571981,8.780618)" /> - <path - style="fill:url(#linearGradient2252);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 14.997635,3.0728311 C 14.887455,4.1217876 13.42731,7.8391015 11.614614,10.013526 C 9.8225375,12.167186 7.1757566,13.602216 4.4938048,14.013055 C 6.5159292,13.016029 7.9668293,11.287886 8.900404,9.5049152 C 9.4616251,8.4330773 9.8347045,7.3512888 10.049952,6.3954548 C 10.244853,5.5299708 10.748164,3.6076606 10.677327,3.0472855 L 14.997635,3.0728311 z " - id="path3382" - sodipodi:nodetypes="cscsscc" /> - <path - style="fill:#e96d1f;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 11.998273,3.0857646 L 9.853875,3.0746296 C 10.052724,5.704186 9.8431306,10.840842 5,13.99819 C 10.262132,13.127062 12.135446,6.14782 11.998273,3.0857646 z " - id="path3384" - sodipodi:nodetypes="cccc" /> - <path - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 14.328472,3.5879657 C 13.402352,7.5116178 11.050991,9.896006 7.9932059,11.841578 C 9.4009194,9.3771388 10.613796,8.3204791 10.426187,3.617359 L 14.328472,3.5879657 z " - id="path3390" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#0a6fa2;fill-opacity:1;stroke:#02396a;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 0.50232,2.5000001 L 5.548214,2.5327029 C 5.548214,4.6682012 7.5331725,12.778185 12.848134,14.341671 C 6.3606519,14.341671 0.50232,7.1171759 0.50232,2.5000001 z " - id="path2230" - sodipodi:nodetypes="cccc" /> - <path - style="fill:url(#linearGradient2244);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.0046854,3.0728311 C 1.025579,4.1606915 2.5750102,7.8391015 4.3877063,10.013526 C 6.1797822,12.167186 8.8265635,13.602216 11.508516,14.013055 C 9.4863905,13.01603 8.0354905,11.287886 7.1019159,9.5049152 C 6.5406948,8.4330773 6.1676154,7.3512888 5.9523683,6.3954548 C 5.7574673,5.5299709 5.2541559,3.6076606 5.3249925,3.0472854 L 1.0046854,3.0728311 z " - id="path2236" - sodipodi:nodetypes="cscsscc" /> - <path - style="fill:#a0ce67;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 3.7118046,3.055775 L 6.0094407,3.1813771 C 5.9820727,4.0797021 5.9514165,10.838935 12,13.996284 C 5.4281278,13.125156 3.6296973,5.1178307 3.7118046,3.055775 z " - id="rect3326" - sodipodi:nodetypes="cccc" /> - <path - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.6738483,3.5879662 C 2.5999681,7.5116186 4.9513289,9.8960072 8.0091145,11.841579 C 6.6014005,9.3771399 5.3885239,8.3204801 5.5761331,3.6173594 L 1.6738483,3.5879662 z " - id="path2240" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient2216);stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 0.63632979,2.5000002 L 6.7242835,2.6127543 C 6.3286085,4.7177198 7.6493245,12.913003 12.964286,14.500001 C 6.4768045,14.500001 0.63632979,7.1866089 0.63632979,2.5000002 z " - id="path3352" - sodipodi:nodetypes="cccc" /> - </g> -</svg>
--- a/libpurple/protocols/jabber/resources/icons/22x22/apps/scalable/im-jabber.svg Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,251 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="24" - height="24" - id="svg3302" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:docbase="/home/hbons/Desktop" - sodipodi:docname="jabber.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - inkscape:export-filename="/home/hbons/Desktop/xmpp22.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90" - version="1.0"> - <defs - id="defs3304"> - <linearGradient - id="linearGradient2222" - inkscape:collect="always"> - <stop - id="stop2224" - offset="0" - style="stop-color:#429538;stop-opacity:1" /> - <stop - id="stop2226" - offset="1" - style="stop-color:#033e6f;stop-opacity:1" /> - </linearGradient> - <linearGradient - id="linearGradient2214" - inkscape:collect="always"> - <stop - id="stop2216" - offset="0" - style="stop-color:#033e6f;stop-opacity:1" /> - <stop - id="stop2218" - offset="1" - style="stop-color:#14a9de;stop-opacity:1" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3417"> - <stop - style="stop-color:#df5219;stop-opacity:1" - offset="0" - id="stop3419" /> - <stop - style="stop-color:#034072;stop-opacity:1" - offset="1" - id="stop3421" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3334"> - <stop - style="stop-color:#034072;stop-opacity:1" - offset="0" - id="stop3336" /> - <stop - style="stop-color:#109cd3;stop-opacity:1" - offset="1" - id="stop3338" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3417" - id="linearGradient3423" - x1="31.736355" - y1="20.841261" - x2="35.292381" - y2="22.255474" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.4696897,0,0,0.4693264,1.1291183,0.5593256)" /> - <filter - inkscape:collect="always" - x="-0.49491513" - width="1.9898303" - y="-1.0582331" - height="3.1164663" - id="filter3871"> - <feGaussianBlur - inkscape:collect="always" - stdDeviation="5.4785069" - id="feGaussianBlur3873" /> - </filter> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2222" - id="linearGradient2206" - gradientUnits="userSpaceOnUse" - x1="18.734463" - y1="21.519651" - x2="15.642859" - y2="23.876795" - gradientTransform="matrix(0.4696897,0,0,0.4693264,0.7859883,0.5593256)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3334" - id="linearGradient2427" - gradientUnits="userSpaceOnUse" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" - gradientTransform="matrix(-0.4731084,0,0,0.4693403,23.025142,0.6736289)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2214" - id="linearGradient2429" - gradientUnits="userSpaceOnUse" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" - gradientTransform="matrix(0.4731084,0,0,0.4693403,0.7795066,0.6736289)" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="14" - inkscape:cx="11.10389" - inkscape:cy="13.373032" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:grid-bbox="true" - inkscape:document-units="px" - inkscape:window-width="1434" - inkscape:window-height="823" - inkscape:window-x="3" - inkscape:window-y="43" - inkscape:snap-bbox="true" - inkscape:snap-nodes="false" - objecttolerance="9" - gridtolerance="12"> - <inkscape:grid - type="xygrid" - id="grid2421" - visible="true" - enabled="true" /> - </sodipodi:namedview> - <metadata - id="metadata3307"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer1" - inkscape:label="Layer 1" - inkscape:groupmode="layer"> - <path - sodipodi:type="arc" - style="opacity:0.29670332;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3871)" - id="path3425" - sodipodi:cx="22.273863" - sodipodi:cy="36.736797" - sodipodi:rx="13.283506" - sodipodi:ry="6.2124381" - d="M 35.557369,36.736797 A 13.283506,6.2124381 0 1 1 8.9903564,36.736797 A 13.283506,6.2124381 0 1 1 35.557369,36.736797 z" - transform="matrix(0.5199608,0,0,0.1850564,0.5826339,12.558808)" /> - <path - style="fill:#da6812;fill-opacity:1;stroke:#a24900;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 22.499999,4.5000001 L 14.975448,6.4912682 C 15.521075,9.1224749 12.829223,17.516252 5.5000001,19.499999 C 14.446104,19.499999 22.499999,10.35826 22.499999,4.5000001 z" - id="path3380" - sodipodi:nodetypes="cccc" /> - <path - style="fill:url(#linearGradient2427);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 22.005002,4.971026 C 21.851961,7.7335015 19.92301,11.265451 16.933871,14.123162 C 14.261115,16.678402 10.768225,18.676617 7.0429489,19.197904 C 9.8517135,17.932843 11.867038,15.740114 13.163789,13.477819 C 13.943334,12.117834 14.259517,10.820984 14.558499,9.6081884 C 14.82922,8.5100321 14.983993,6.7178749 14.8856,6.0068516 L 22.005002,4.971026 z" - id="path3382" - sodipodi:nodetypes="cscsscc" /> - <path - style="fill:#abbb25;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 18.868549,5.7525618 L 15.016096,6.7750535 C 15.049889,7.9148799 14.627471,15.172893 7.1591181,19.179048 C 15.27359,18.073731 18.969928,8.3689705 18.868549,5.7525618 z" - id="path3384" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient3423);stroke-width:1.00000024;stroke-miterlimit:4;stroke-opacity:1" - d="M 22.499999,4.5000003 L 14.317055,5.5583642 C 14.862682,8.1895711 12.829223,17.516254 5.4999998,19.500001 C 14.446104,19.500001 22.499999,10.358261 22.499999,4.5000003 z" - id="path3388" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#df5219;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 19.203087,5.4052024 L 15.293888,5.9416777 C 15.327681,7.0815041 14.627471,15.172893 7.1591181,19.179048 C 15.27359,18.073731 19.304466,8.0216111 19.203087,5.4052024 z" - id="path3386" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#e96d1f;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 17.202112,5.6933762 L 14.951055,6.0174391 C 14.984849,7.1572655 14.688699,15.172893 7.2203467,19.179048 C 15.706546,17.068001 18.393936,6.9484519 17.202112,5.6933762 z" - id="path2228" - sodipodi:nodetypes="cccc" /> - <path - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 21.5,5.5000001 C 21.259562,7.8476013 19.283758,10.890908 16.721197,13.322762 C 14.636171,15.301438 12.341463,16.82458 9.4999988,17.5 C 11.488026,16.319811 12.990581,14.640432 13.991693,12.906761 C 14.701922,11.676826 14.851876,10.636782 15.125188,9.5362725 C 15.351957,8.6231762 15.486367,7.0468868 15.426863,6.3988159 L 21.5,5.5000001 z" - id="path3390" - sodipodi:nodetypes="cscsscc" /> - <path - style="fill:#da6812;fill-opacity:1;stroke:#a24900;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.5,4.5000001 L 9.0245518,6.4912682 C 8.4789251,9.1224749 11.170778,17.516252 18.499999,19.499999 C 9.5538954,19.499999 1.5,10.35826 1.5,4.5000001 z" - id="rect3321" - sodipodi:nodetypes="cccc" /> - <path - style="fill:url(#linearGradient2429);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.7996466,4.971026 C 1.9526876,7.7335015 3.8816385,11.265451 6.8707772,14.123162 C 9.5435341,16.678402 13.036424,18.676617 16.7617,19.197904 C 13.952935,17.932843 11.937611,15.740114 10.64086,13.477819 C 9.8613144,12.117834 9.3431008,10.745223 9.044119,9.532427 C 8.7733983,8.4342707 8.9721786,6.8188901 9.0705719,6.1078667 L 1.7996466,4.971026 z" - id="path3332" - sodipodi:nodetypes="cscsscc" /> - <path - style="fill:#08aec5;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 4.936101,5.7525618 L 8.7885541,6.7750535 C 8.7547609,7.9148799 9.1771789,15.172893 16.645531,19.179048 C 8.5310592,18.073731 4.8347205,8.3689705 4.936101,5.7525618 z" - id="rect3326" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#429538;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 4.6746077,5.4242622 L 8.9905846,5.9416777 C 8.9567914,7.0815041 9.1771789,15.172893 16.645531,19.179048 C 8.5310592,18.073731 4.4776451,8.3725446 4.6746077,5.4242622 z" - id="path3342" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#a0ce67;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 6.8152887,5.6933762 L 8.9905846,6.0679467 C 8.9567914,7.2077731 9.1771789,15.172893 16.645531,19.179048 C 8.1593314,17.068001 5.6948939,7.1270234 6.8152887,5.6933762 z" - id="path2220" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient2206);stroke-width:1.00000024;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.5000001,4.5000003 L 9.5252953,5.6445861 C 8.9796685,8.275793 11.170777,17.516254 18.5,19.500001 C 9.5538955,19.500001 1.5000001,10.358261 1.5000001,4.5000003 z" - id="path3352" - sodipodi:nodetypes="cccc" /> - <path - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.00000012;stroke-miterlimit:4;stroke-opacity:1" - d="M 2.6528401,5.6995163 C 3.1155917,8.0886161 4.8685432,11.14436 7.4405852,13.603314 C 9.0643917,15.155725 10.886287,16.515122 12.976833,17.423211 C 11.790214,16.318736 11.187637,15.199935 10.457851,13.926759 C 9.6473369,12.512747 9.1262733,11.255179 8.8124406,9.9821414 C 8.5924509,9.0897701 8.4306975,7.2046934 8.4453019,6.4949387 L 2.6528401,5.6995163 z" - id="path3366" - sodipodi:nodetypes="cscsscc" /> - </g> -</svg>
--- a/libpurple/protocols/jabber/resources/icons/scalable/apps/im-jabber.svg Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="48px" - height="48px" - id="svg3302" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:docbase="/home/hbons/Desktop" - sodipodi:docname="jabber.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - inkscape:export-filename="/home/hbons/Desktop/xmpp.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs3304"> - <linearGradient - id="linearGradient2222" - inkscape:collect="always"> - <stop - id="stop2224" - offset="0" - style="stop-color:#429538;stop-opacity:1" /> - <stop - id="stop2226" - offset="1" - style="stop-color:#033e6f;stop-opacity:1" /> - </linearGradient> - <linearGradient - id="linearGradient2214" - inkscape:collect="always"> - <stop - id="stop2216" - offset="0" - style="stop-color:#033e6f;stop-opacity:1" /> - <stop - id="stop2218" - offset="1" - style="stop-color:#14a9de;stop-opacity:1" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3417"> - <stop - style="stop-color:#df5219;stop-opacity:1" - offset="0" - id="stop3419" /> - <stop - style="stop-color:#034072;stop-opacity:1" - offset="1" - id="stop3421" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3334"> - <stop - style="stop-color:#034072;stop-opacity:1" - offset="0" - id="stop3336" /> - <stop - style="stop-color:#109cd3;stop-opacity:1" - offset="1" - id="stop3338" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2214" - id="linearGradient3340" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3334" - id="linearGradient3392" - gradientUnits="userSpaceOnUse" - x1="5.0133924" - y1="12.455358" - x2="15.638392" - y2="30.098215" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3417" - id="linearGradient3423" - x1="31.736355" - y1="20.841261" - x2="35.292381" - y2="22.255474" - gradientUnits="userSpaceOnUse" /> - <filter - inkscape:collect="always" - x="-0.49491513" - width="1.9898303" - y="-1.0582332" - height="3.1164664" - id="filter3871"> - <feGaussianBlur - inkscape:collect="always" - stdDeviation="5.4785069" - id="feGaussianBlur3873" /> - </filter> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient2222" - id="linearGradient2206" - gradientUnits="userSpaceOnUse" - x1="18.734463" - y1="21.519651" - x2="15.642859" - y2="23.876795" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="4.9497475" - inkscape:cx="81.961371" - inkscape:cy="4.928944" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:grid-bbox="true" - inkscape:document-units="px" - inkscape:window-width="1434" - inkscape:window-height="823" - inkscape:window-x="3" - inkscape:window-y="43" /> - <metadata - id="metadata3307"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer1" - inkscape:label="Layer 1" - inkscape:groupmode="layer"> - <path - sodipodi:type="arc" - style="opacity:0.2967033;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3871)" - id="path3425" - sodipodi:cx="22.273863" - sodipodi:cy="36.736797" - sodipodi:rx="13.283506" - sodipodi:ry="6.2124381" - d="M 35.557369 36.736797 A 13.283506 6.2124381 0 1 1 8.9903564,36.736797 A 13.283506 6.2124381 0 1 1 35.557369 36.736797 z" - transform="matrix(1.0990312,0,0,0.3942904,-0.4161261,25.323157)" /> - <path - style="fill:#da6812;fill-opacity:1;stroke:#a24900;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 45.5,8.3964466 L 29.479739,12.639268 C 30.641414,18.245615 24.910285,36.130347 9.305892,40.357143 C 28.352732,40.357143 45.5,20.878719 45.5,8.3964466 z " - id="path3380" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="inkscape:offset" - inkscape:radius="-0.56227452" - inkscape:original="M 1.53125 8.40625 C 1.53125 20.888522 18.671909 40.34375 37.71875 40.34375 C 22.114357 36.116954 16.369574 18.231347 17.53125 12.625 L 1.53125 8.40625 z " - style="fill:url(#linearGradient3392);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - id="path3382" - d="M 2.15625,9.15625 C 2.4797298,15.042119 6.5569158,22.567467 12.875,28.65625 C 18.524354,34.100573 25.907207,38.35807 33.78125,39.46875 C 27.844419,36.773348 23.584667,32.10141 20.84375,27.28125 C 19.19604,24.383597 18.100702,21.459043 17.46875,18.875 C 16.896533,16.535213 16.729528,14.546192 16.9375,13.03125 L 2.15625,9.15625 z " - transform="matrix(-1,0,0,1,47.020178,0)" /> - <path - style="fill:#abbb25;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 38.234464,10.821428 L 30.091607,13 C 30.163035,15.428571 29.270178,30.892857 13.484463,39.428571 C 30.635869,37.073525 38.448749,16.396079 38.234464,10.821428 z " - id="path3384" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient3423);stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 45.5,8.3964466 L 29.479739,12.639268 C 30.641414,18.245615 24.910285,36.130347 9.305892,40.357143 C 28.352732,40.357143 45.5,20.878719 45.5,8.3964466 z " - id="path3388" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#df5219;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 38.941571,10.619397 L 30.091607,13 C 30.163035,15.428571 29.270178,30.892857 13.484463,39.428571 C 30.635869,37.073525 39.155856,16.194048 38.941571,10.619397 z " - id="path3386" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#e96d1f;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 34.3385,11.986693 L 30.221025,13 C 30.292453,15.428571 29.399596,30.892857 13.613881,39.428571 C 31.551001,34.930668 35.951771,13.823779 34.3385,11.986693 z " - id="path2228" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="inkscape:offset" - inkscape:radius="-1.0305283" - inkscape:original="M 1.53125 8.40625 C 1.53125 20.888522 18.671909 40.34375 37.71875 40.34375 C 22.114357 36.116954 16.369574 18.231347 17.53125 12.625 L 1.53125 8.40625 z " - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - id="path3390" - d="M 2.78125,9.8125 C 3.3466531,15.418621 7.1614784,22.50518 13.1875,28.3125 C 18.090567,33.03762 24.31812,36.855835 31,38.46875 C 26.325029,35.650434 22.791678,31.640043 20.4375,27.5 C 18.767353,24.562889 17.642711,21.596789 17,18.96875 C 16.46674,16.788257 16.328823,14.953857 16.46875,13.40625 L 2.78125,9.8125 z " - transform="matrix(-1,0,0,1,47.020178,0)" /> - <path - style="fill:#da6812;fill-opacity:1;stroke:#a24900;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.5201775,8.3964466 L 17.540439,12.639268 C 16.378764,18.245615 22.109893,36.130347 37.714286,40.357143 C 18.667446,40.357143 1.5201775,20.878719 1.5201775,8.3964466 z " - id="rect3321" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="inkscape:offset" - inkscape:radius="-0.56227452" - inkscape:original="M 1.53125 8.40625 C 1.53125 20.888522 18.671909 40.34375 37.71875 40.34375 C 22.114357 36.116954 16.369574 18.231347 17.53125 12.625 L 1.53125 8.40625 z " - style="fill:url(#linearGradient3340);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - id="path3332" - d="M 2.15625,9.15625 C 2.4797298,15.042119 6.5569158,22.567467 12.875,28.65625 C 18.524354,34.100573 25.907207,38.35807 33.78125,39.46875 C 27.844419,36.773348 23.584667,32.10141 20.84375,27.28125 C 19.19604,24.383597 18.100702,21.459043 17.46875,18.875 C 16.896533,16.535213 16.729528,14.546192 16.9375,13.03125 L 2.15625,9.15625 z " /> - <path - style="fill:#08aec5;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 8.7857143,10.821428 L 16.928571,13 C 16.857143,15.428571 17.75,30.892857 33.535715,39.428571 C 16.384309,37.073525 8.5714286,16.396079 8.7857143,10.821428 z " - id="rect3326" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#429538;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 8.1796228,10.821428 L 16.928571,13 C 16.857143,15.428571 17.75,30.892857 33.535715,39.428571 C 16.384309,37.073525 7.7633066,17.103186 8.1796228,10.821428 z " - id="path3342" - sodipodi:nodetypes="cccc" /> - <path - style="fill:#a0ce67;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 12.811096,11.986693 L 16.928571,13 C 16.857143,15.428571 17.75,30.892857 33.535715,39.428571 C 15.598595,34.930668 11.197825,13.823779 12.811096,11.986693 z " - id="path2220" - sodipodi:nodetypes="cccc" /> - <path - style="fill:none;fill-opacity:1;stroke:url(#linearGradient2206);stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 1.5201775,8.3964466 L 17.540439,12.639268 C 16.378764,18.245615 22.109893,36.130347 37.714286,40.357143 C 18.667446,40.357143 1.5201775,20.878719 1.5201775,8.3964466 z " - id="path3352" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="inkscape:offset" - inkscape:radius="-1.0305283" - inkscape:original="M 1.53125 8.40625 C 1.53125 20.888522 18.671909 40.34375 37.71875 40.34375 C 22.114357 36.116954 16.369574 18.231347 17.53125 12.625 L 1.53125 8.40625 z " - style="opacity:0.4;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - id="path3366" - d="M 2.78125,9.8125 C 3.3466531,15.418621 7.1614784,22.50518 13.1875,28.3125 C 18.090567,33.03762 24.31812,36.855835 31,38.46875 C 26.325029,35.650434 22.791678,31.640043 20.4375,27.5 C 18.767353,24.562889 17.642711,21.596789 17,18.96875 C 16.46674,16.788257 16.328823,14.953857 16.46875,13.40625 L 2.78125,9.8125 z " /> - </g> -</svg>
--- a/libpurple/protocols/jabber/resources/xmpp.gresource.xml Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<gresources> - <gresource prefix="/im/pidgin/libpurple/xmpp"> - <file>icons/16x16/apps/im-jabber.png</file> - <file>icons/16x16/apps/scalable/im-jabber.svg</file> - <file>icons/22x22/apps/im-jabber.png</file> - <file>icons/22x22/apps/scalable/im-jabber.svg</file> - <file>icons/48x48/apps/im-jabber.png</file> - <file>icons/scalable/apps/im-jabber.svg</file> - </gresource> -</gresources>
--- a/libpurple/protocols/jabber/roster.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,521 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#include <purple.h> - -#include "buddy.h" -#include "chat.h" -#include "presence.h" -#include "roster.h" -#include "iq.h" - -#include <string.h> - -static const gchar *jabber_roster_group_get_global_name(PurpleGroup *group); - -/* Take a list of strings and join them with a ", " separator */ -static gchar *roster_groups_join(GSList *list) -{ - GString *out = g_string_new(NULL); - for ( ; list; list = list->next) { - out = g_string_append(out, (const char *)list->data); - if (list->next) - out = g_string_append(out, ", "); - } - - return g_string_free(out, FALSE); -} - -static void -roster_request_cb(JabberStream *js, const char *from, JabberIqType type, - const char *id, PurpleXmlNode *packet, - G_GNUC_UNUSED gpointer data) -{ - PurpleXmlNode *query; - - if (type == JABBER_IQ_ERROR) { - /* - * This shouldn't happen in any real circumstances and - * likely constitutes a server-side misconfiguration (i.e. - * explicitly not loading mod_roster...) - */ - purple_debug_error("jabber", "Error retrieving roster!?\n"); - jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); - return; - } - - query = purple_xmlnode_get_child(packet, "query"); - if (query == NULL) { - jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); - return; - } - - jabber_roster_parse(js, from, type, id, query); - jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); -} - -void jabber_roster_request(JabberStream *js) -{ - JabberIq *iq; - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster"); - - jabber_iq_set_callback(iq, roster_request_cb, NULL); - jabber_iq_send(iq); -} - -static void remove_purple_buddies(JabberStream *js, const char *jid) -{ - GSList *buddies; - - buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), jid); - - g_slist_free_full(buddies, (GDestroyNotify)purple_blist_remove_buddy); -} - -static void add_purple_buddy_to_groups(JabberStream *js, const char *jid, - const char *alias, GSList *groups) -{ - GSList *buddies, *l; - PurpleAccount *account = purple_connection_get_account(js->gc); - - buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), jid); - - if(!groups) { - if(!buddies) { - groups = g_slist_append(groups, g_strdup(JABBER_ROSTER_DEFAULT_GROUP)); - } else { - /* TODO: What should we do here? Removing the local buddies - * is wrong, but so is letting the group state get out of sync with - * the server. - */ - g_slist_free(buddies); - return; - } - } - - while(buddies) { - PurpleBuddy *b = buddies->data; - PurpleGroup *g = purple_buddy_get_group(b); - - buddies = g_slist_delete_link(buddies, buddies); - - /* XMPP groups are case-sensitive, but libpurple groups are - * case-insensitive. We treat a buddy in both "Friends" and "friends" - * as only being in one group, but if we push changes about the buddy - * to the server, the buddy will be dropped from one of the groups. - * Not optimal, but better than the alternative, I think. - */ - l = g_slist_find_custom(groups, purple_group_get_name(g), - (GCompareFunc)purple_utf8_strcasecmp); - if (!l && g == purple_blist_get_default_group()) { - l = g_slist_find_custom(groups, JABBER_ROSTER_DEFAULT_GROUP, - (GCompareFunc)purple_utf8_strcasecmp); - } - if (!l && g == purple_blist_get_default_group()) { - l = g_slist_find_custom(groups, - _purple_blist_get_localized_default_group_name(), - (GCompareFunc)purple_utf8_strcasecmp); - } - - if (l) { - /* The buddy is already on the local list. Update info. */ - const char *servernick, *balias; - - /* Previously stored serverside / buddy-supplied alias */ - if((servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick"))) - purple_serv_got_alias(js->gc, jid, servernick); - - /* Alias from our roster retrieval */ - balias = purple_buddy_get_local_alias(b); - if(alias && !purple_strequal(alias, balias)) - purple_serv_got_private_alias(js->gc, jid, alias); - g_free(l->data); - groups = g_slist_delete_link(groups, l); - } else { - /* This buddy isn't in the group on the server anymore */ - purple_debug_info("jabber", "jabber_roster_parse(): " - "Removing %s from group '%s' on the local list", - purple_buddy_get_name(b), - jabber_roster_group_get_global_name(g)); - purple_blist_remove_buddy(b); - } - } - - if (groups) { - char *tmp = roster_groups_join(groups); - purple_debug_info("jabber", "jabber_roster_parse(): Adding %s to " - "groups: %s\n", jid, tmp); - g_free(tmp); - } - - while(groups) { - PurpleGroup *g = purple_blist_find_group(groups->data); - PurpleBuddy *b = purple_buddy_new(account, jid, alias); - - if(!g) { - g = purple_group_new(groups->data); - purple_blist_add_group(g, NULL); - } - - purple_blist_add_buddy(b, NULL, g, NULL); - purple_buddy_set_local_alias(b, alias); - - g_free(groups->data); - groups = g_slist_delete_link(groups, groups); - } -} - -void jabber_roster_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *query) -{ - PurpleXmlNode *item, *group; - - if (!jabber_is_own_account(js, from)) { - purple_debug_warning("jabber", "Received bogon roster push from %s\n", - from); - return; - } - - js->currently_parsing_roster_push = TRUE; - - for(item = purple_xmlnode_get_child(query, "item"); item; item = purple_xmlnode_get_next_twin(item)) - { - const char *jid, *name, *subscription, *ask; - JabberBuddy *jb; - - subscription = purple_xmlnode_get_attrib(item, "subscription"); - jid = purple_xmlnode_get_attrib(item, "jid"); - name = purple_xmlnode_get_attrib(item, "name"); - ask = purple_xmlnode_get_attrib(item, "ask"); - - if(!jid) - continue; - - if(!(jb = jabber_buddy_find(js, jid, TRUE))) - continue; - - if(subscription) { - if (purple_strequal(subscription, "remove")) - jb->subscription = JABBER_SUB_REMOVE; - else if (jb == js->user_jb) - jb->subscription = JABBER_SUB_BOTH; - else if (purple_strequal(subscription, "none")) - jb->subscription = JABBER_SUB_NONE; - else if (purple_strequal(subscription, "to")) - jb->subscription = JABBER_SUB_TO; - else if (purple_strequal(subscription, "from")) - jb->subscription = JABBER_SUB_FROM; - else if (purple_strequal(subscription, "both")) - jb->subscription = JABBER_SUB_BOTH; - } - - if(purple_strequal(ask, "subscribe")) - jb->subscription |= JABBER_SUB_PENDING; - else - jb->subscription &= ~JABBER_SUB_PENDING; - - if(jb->subscription & JABBER_SUB_REMOVE) { - remove_purple_buddies(js, jid); - } else { - GSList *groups = NULL; - - for(group = purple_xmlnode_get_child(item, "group"); group; group = purple_xmlnode_get_next_twin(group)) { - char *group_name = purple_xmlnode_get_data(group); - - if (group_name == NULL || *group_name == '\0') - /* Changing this string? Look in add_purple_buddy_to_groups */ - group_name = g_strdup(JABBER_ROSTER_DEFAULT_GROUP); - - /* - * See the note in add_purple_buddy_to_groups; the core handles - * names case-insensitively and this is required to not - * end up with duplicates if a buddy is in, e.g., - * 'XMPP' and 'xmpp' - */ - if (g_slist_find_custom(groups, group_name, (GCompareFunc)purple_utf8_strcasecmp)) - g_free(group_name); - else - groups = g_slist_prepend(groups, group_name); - } - - add_purple_buddy_to_groups(js, jid, name, groups); - if (jb == js->user_jb) - jabber_presence_fake_to_self(js, NULL); - } - } - - if (type == JABBER_IQ_SET) { - JabberIq *ack = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(ack, id); - jabber_iq_send(ack); - } - - js->currently_parsing_roster_push = FALSE; -} - -/* jabber_roster_update frees the GSList* passed in */ -static void jabber_roster_update(JabberStream *js, const char *name, - GSList *groups) -{ - PurpleBuddy *b; - PurpleGroup *g; - GSList *l; - JabberIq *iq; - PurpleXmlNode *query, *item, *group; - const char *balias; - - if (js->currently_parsing_roster_push) - return; - - if(!(b = purple_blist_find_buddy(purple_connection_get_account(js->gc), name))) - return; - - if (groups) { - char *tmp = roster_groups_join(groups); - - purple_debug_info("jabber", "jabber_roster_update(%s): [Source: " - "groups]: groups: %s\n", name, tmp); - g_free(tmp); - } else { - GSList *buddies = purple_blist_find_buddies(purple_connection_get_account(js->gc), name); - char *tmp; - - if(!buddies) - return; - while(buddies) { - b = buddies->data; - g = purple_buddy_get_group(b); - groups = g_slist_append(groups, - (char *)jabber_roster_group_get_global_name(g)); - buddies = g_slist_delete_link(buddies, buddies); - } - - tmp = roster_groups_join(groups); - purple_debug_info("jabber", "jabber_roster_update(%s): [Source: local blist]: groups: %s\n", - name, tmp); - g_free(tmp); - } - - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster"); - - query = purple_xmlnode_get_child(iq->node, "query"); - item = purple_xmlnode_new_child(query, "item"); - - purple_xmlnode_set_attrib(item, "jid", name); - - balias = purple_buddy_get_local_alias(b); - purple_xmlnode_set_attrib(item, "name", balias ? balias : ""); - - for(l = groups; l; l = l->next) { - group = purple_xmlnode_new_child(item, "group"); - purple_xmlnode_insert_data(group, l->data, -1); - } - - g_slist_free(groups); - - jabber_iq_send(iq); -} - -void -jabber_roster_add_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, PurpleBuddy *buddy, - G_GNUC_UNUSED PurpleGroup *group, - G_GNUC_UNUSED const char *message) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - char *who; - JabberID *jid; - JabberBuddy *jb; - JabberBuddyResource *jbr; - const char *name; - - /* If we haven't received the roster yet, ignore any adds */ - if (js->state != JABBER_STREAM_CONNECTED) - return; - - name = purple_buddy_get_name(buddy); - jid = jabber_id_new(name); - if (jid == NULL) { - /* TODO: Remove the buddy from the list? */ - return; - } - - /* Adding a chat room or a chat user to the roster is *not* supported. */ - if (jid->node && jabber_chat_find(js, jid->node, jid->domain) != NULL) { - /* - * This is the same thing Bonjour does. If it causes problems, move - * it to an idle callback. - */ - purple_debug_warning("jabber", "Cowardly refusing to add a MUC user " - "to your buddy list and removing the buddy. " - "Buddies can only be added by real (non-MUC) " - "JID\n"); - purple_blist_remove_buddy(buddy); - jabber_id_free(jid); - return; - } - - who = jabber_id_get_bare_jid(jid); - if (jid->resource != NULL) { - /* - * If the buddy name added contains a resource, strip that off and - * rename the buddy. - */ - purple_buddy_set_name(buddy, who); - } - - jb = jabber_buddy_find(js, who, FALSE); - - purple_debug_info("jabber", "jabber_roster_add_buddy(): Adding %s\n", who); - - jabber_roster_update(js, who, NULL); - - if (jb == js->user_jb) { - jabber_presence_fake_to_self(js, NULL); - } else if(!jb || !(jb->subscription & JABBER_SUB_TO)) { - jabber_presence_subscription_set(js, who, "subscribe"); - } else if((jbr =jabber_buddy_find_resource(jb, NULL))) { - purple_protocol_got_user_status(purple_connection_get_account(gc), who, - jabber_buddy_state_get_status_id(jbr->state), - "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); - } - - g_free(who); -} - -void -jabber_roster_alias_change(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const gchar *name, - const gchar *alias) -{ - PurpleBuddy *b = purple_blist_find_buddy(purple_connection_get_account(gc), name); - - if(b != NULL) { - purple_buddy_set_local_alias(b, alias); - - purple_debug_info("jabber", "jabber_roster_alias_change(): Aliased %s to %s\n", - name, alias ? alias : "(null)"); - - jabber_roster_update(purple_connection_get_protocol_data(gc), name, NULL); - } -} - -void -jabber_roster_group_change(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const gchar *name, - const gchar *old_group, const gchar *new_group) -{ - GSList *buddies, *groups = NULL; - - if(!old_group || !new_group || purple_strequal(old_group, new_group)) - return; - - buddies = purple_blist_find_buddies(purple_connection_get_account(gc), name); - while(buddies) { - groups = g_slist_append(groups, (char*)new_group); - buddies = g_slist_delete_link(buddies, buddies); - } - - purple_debug_info("jabber", "jabber_roster_group_change(): Moving %s from %s to %s\n", - name, old_group, new_group); - - jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups); -} - -void -jabber_roster_group_rename(PurpleProtocolServer *protocol_server, - PurpleConnection *gc, const gchar *old_name, - PurpleGroup *group, GList *moved_buddies) -{ - GList *l; - const char *gname = jabber_roster_group_get_global_name(group); - - for(l = moved_buddies; l; l = l->next) { - PurpleBuddy *buddy = l->data; - - jabber_roster_group_change(protocol_server, gc, - purple_buddy_get_name(buddy), old_name, - gname); - } -} - -void -jabber_roster_remove_buddy(G_GNUC_UNUSED PurpleProtocolServer *protocol_server, - PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group) -{ - const char *name = purple_buddy_get_name(buddy); - GSList *buddies = purple_blist_find_buddies(purple_connection_get_account(gc), name); - - buddies = g_slist_remove(buddies, buddy); - if(buddies != NULL) { - PurpleBuddy *tmpbuddy; - PurpleGroup *tmpgroup; - GSList *groups = NULL; - - while(buddies) { - tmpbuddy = buddies->data; - tmpgroup = purple_buddy_get_group(tmpbuddy); - groups = g_slist_append(groups, - (char *)jabber_roster_group_get_global_name(tmpgroup)); - buddies = g_slist_delete_link(buddies, buddies); - } - - purple_debug_info("jabber", "jabber_roster_remove_buddy(): " - "Removing %s from %s", purple_buddy_get_name(buddy), - jabber_roster_group_get_global_name(group)); - - jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups); - } else { - JabberIq *iq = jabber_iq_new_query(purple_connection_get_protocol_data(gc), JABBER_IQ_SET, - "jabber:iq:roster"); - PurpleXmlNode *query = purple_xmlnode_get_child(iq->node, "query"); - PurpleXmlNode *item = purple_xmlnode_new_child(query, "item"); - - purple_xmlnode_set_attrib(item, "jid", name); - purple_xmlnode_set_attrib(item, "subscription", "remove"); - - purple_debug_info("jabber", "jabber_roster_remove_buddy(): Removing %s\n", - purple_buddy_get_name(buddy)); - - jabber_iq_send(iq); - } -} - -static const gchar * -jabber_roster_group_get_global_name(PurpleGroup *group) -{ - const gchar *name = NULL; - - if (group) - name = purple_group_get_name(group); - - if (name == NULL) - name = JABBER_ROSTER_DEFAULT_GROUP; - else if (purple_strequal(name, PURPLE_BLIST_DEFAULT_GROUP_NAME)) - name = JABBER_ROSTER_DEFAULT_GROUP; - else if (purple_strequal(name, _purple_blist_get_localized_default_group_name())) - name = JABBER_ROSTER_DEFAULT_GROUP; - - return name; -}
--- a/libpurple/protocols/jabber/roster.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/** - * @file roster.h Roster manipulation - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_ROSTER_H -#define PURPLE_JABBER_ROSTER_H - -/* it must *not* be localized */ -#define JABBER_ROSTER_DEFAULT_GROUP "Buddies" - -#include "jabber.h" - -void jabber_roster_request(JabberStream *js); - -void jabber_roster_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *query); - -void jabber_roster_add_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group, const gchar *message); -void jabber_roster_alias_change(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const gchar *name, - const gchar *alias); -void jabber_roster_group_change(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const gchar *name, - const gchar *old_group, const gchar *new_group); -void jabber_roster_group_rename(PurpleProtocolServer *protocol_server, PurpleConnection *gc, const gchar *old_name, - PurpleGroup *group, GList *moved_buddies); -void jabber_roster_remove_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group); - -#endif /* PURPLE_JABBER_ROSTER_H */
--- a/libpurple/protocols/jabber/si.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1909 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purpleconfig.h> - -#include <errno.h> -#include <sys/types.h> - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "buddy.h" -#include "data.h" -#include "disco.h" -#include "jabber.h" -#include "ibb.h" -#include "iq.h" -#include "si.h" - -#define STREAMHOST_CONNECT_TIMEOUT 5 -#define ENABLE_FT_THUMBNAILS 0 - -struct _JabberSIXfer { - PurpleXfer parent; - - JabberStream *js; - - GCancellable *cancellable; - GSocketClient *client; - GSocketService *service; - guint connect_timeout; - - gboolean accepted; - - char *stream_id; - char *iq_id; - - enum { - STREAM_METHOD_UNKNOWN = 0, - STREAM_METHOD_BYTESTREAMS = 2 << 1, - STREAM_METHOD_IBB = 2 << 2, - STREAM_METHOD_UNSUPPORTED = 2 << 30 - } stream_method; - - GList *streamhosts; - - gchar *socks_buf; - GSocketConnection *local_streamhost_conn; - - JabberIBBSession *ibb_session; - guint ibb_timeout_handle; - PurpleCircularBuffer *ibb_buffer; -}; - -G_DEFINE_DYNAMIC_TYPE_EXTENDED(JabberSIXfer, jabber_si_xfer, PURPLE_TYPE_XFER, - G_TYPE_FLAG_FINAL, {}) - -/* some forward declarations */ -static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer); - -static PurpleXfer* -jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from) -{ - GList *xfers; - - if(!sid || !from) - return NULL; - - for(xfers = js->file_transfers; xfers; xfers = xfers->next) { - PurpleXfer *xfer = xfers->data; - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - if(jsx->stream_id && purple_xfer_get_remote_user(xfer) && - purple_strequal(jsx->stream_id, sid) && purple_strequal(purple_xfer_get_remote_user(xfer), from)) - return xfer; - } - - return NULL; -} - - - -static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer); - -static void -jabber_si_bytestreams_try_next_streamhost(PurpleXfer *xfer, - const gchar *error_message) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data; - - purple_debug_warning( - "jabber", - "si connection failed, jid was %s, host was %s, error was %s", - streamhost->jid, streamhost->host, error_message); - jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); - jabber_bytestreams_streamhost_free(streamhost); - g_clear_object(&jsx->client); - jabber_si_bytestreams_attempt_connect(xfer); -} - -static void -jabber_si_bytestreams_connect_cb(GObject *source, GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - GIOStream *stream = NULL; - GError *error = NULL; - GSocket *socket = NULL; - JabberIq *iq = NULL; - JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data; - - stream = g_proxy_connect_finish(G_PROXY(source), result, &error); - if (stream == NULL) { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - purple_debug_error("jabber", - "Unable to connect to destination host: %s", - error->message); - jabber_si_bytestreams_try_next_streamhost( - xfer, "Unable to connect to destination host."); - } - - g_clear_error(&error); - return; - } - - if (!G_IS_SOCKET_CONNECTION(stream)) { - purple_debug_error("jabber", - "GProxy didn't return a GSocketConnection."); - jabber_si_bytestreams_try_next_streamhost( - xfer, "GProxy didn't return a GSocketConnection."); - g_object_unref(stream); - return; - } - - /* unknown file transfer type is assumed to be RECEIVE */ - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { - PurpleXmlNode *query, *activate; - iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS); - purple_xmlnode_set_attrib(iq->node, "to", streamhost->jid); - query = purple_xmlnode_get_child(iq->node, "query"); - purple_xmlnode_set_attrib(query, "sid", jsx->stream_id); - activate = purple_xmlnode_new_child(query, "activate"); - purple_xmlnode_insert_data(activate, purple_xfer_get_remote_user(xfer), -1); - - /* TODO: We need to wait for an activation result before starting */ - } else { - PurpleXmlNode *query, *su; - iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, NS_BYTESTREAMS); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - jabber_iq_set_id(iq, jsx->iq_id); - query = purple_xmlnode_get_child(iq->node, "query"); - su = purple_xmlnode_new_child(query, "streamhost-used"); - purple_xmlnode_set_attrib(su, "jid", streamhost->jid); - } - - jabber_iq_send(iq); - - jsx->local_streamhost_conn = G_SOCKET_CONNECTION(stream); - socket = g_socket_connection_get_socket(jsx->local_streamhost_conn); - purple_xfer_start(xfer, g_socket_get_fd(socket), NULL, -1); -} - -static void -jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer *jsx) -{ - g_clear_handle_id(&jsx->ibb_timeout_handle, g_source_remove); -} - -static gboolean -jabber_si_bytestreams_ibb_timeout_cb(gpointer data) -{ - PurpleXfer *xfer = (PurpleXfer *) data; - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - if (jsx && !jsx->ibb_session) { - purple_debug_info("jabber", - "jabber_si_bytestreams_ibb_timeout called and IBB session not set " - " up yet, cancel transfer"); - jabber_si_bytestreams_ibb_timeout_remove(jsx); - purple_xfer_cancel_local(xfer); - } - - return FALSE; -} - -/* This is called when we connect to the SOCKS5 proxy server (through any - * relevant account proxy) - */ -static void -jabber_si_bytestreams_socks5_connect_to_host_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberID *dstjid; - const JabberID *from_jid, *to_jid; - GSocketConnection *conn; - GProxy *proxy; - GSocketAddress *addr; - GInetSocketAddress *inet_addr; - GSocketAddress *proxy_addr; - gchar *dstaddr, *hash_input; - GError *error = NULL; - - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), - result, &error); - if (conn == NULL) { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - purple_debug_error("jabber", "Unable to connect to SOCKS5 host: %s", - error->message); - jabber_si_bytestreams_try_next_streamhost( - xfer, "Unable to connect to SOCKS5 host."); - } - - g_clear_error(&error); - return; - } - - proxy = g_proxy_get_default_for_protocol("socks5"); - if (proxy == NULL) { - purple_debug_error("jabber", "SOCKS5 proxy backend missing."); - jabber_si_bytestreams_try_next_streamhost( - xfer, "SOCKS5 proxy backend missing."); - g_object_unref(conn); - return; - } - - addr = g_socket_connection_get_remote_address(conn, &error); - if (addr == NULL) { - purple_debug_error( - "jabber", - "Unable to retrieve SOCKS5 host address from connection: %s", - error->message); - jabber_si_bytestreams_try_next_streamhost( - xfer, "Unable to retrieve SOCKS5 host address from connection"); - g_object_unref(conn); - g_object_unref(proxy); - g_clear_error(&error); - return; - } - - dstjid = jabber_id_new(purple_xfer_get_remote_user(xfer)); - - /* unknown file transfer type is assumed to be RECEIVE */ - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { - from_jid = jsx->js->user; - to_jid = dstjid; - } else { - from_jid = dstjid; - to_jid = jsx->js->user; - } - - /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */ - hash_input = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, - from_jid->node, from_jid->domain, - from_jid->resource, to_jid->node, - to_jid->domain, to_jid->resource); - dstaddr = g_compute_checksum_for_string(G_CHECKSUM_SHA1, hash_input, -1); - g_free(hash_input); - jabber_id_free(dstjid); - - inet_addr = G_INET_SOCKET_ADDRESS(addr); - - proxy_addr = - g_proxy_address_new(g_inet_socket_address_get_address(inet_addr), - g_inet_socket_address_get_port(inet_addr), - "socks5", dstaddr, 0, NULL, NULL); - g_object_unref(inet_addr); - - purple_debug_info("jabber", "Connecting to %s using SOCKS5 proxy", dstaddr); - - g_proxy_connect_async(proxy, G_IO_STREAM(conn), G_PROXY_ADDRESS(proxy_addr), - jsx->cancellable, jabber_si_bytestreams_connect_cb, - user_data); - - g_object_unref(proxy_addr); - g_object_unref(conn); - g_object_unref(proxy); - g_free(dstaddr); -} - -static void -jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberBytestreamsStreamhost *streamhost; - JabberID *dstjid; - - if(!jsx->streamhosts) { - JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR); - PurpleXmlNode *error, *inf; - - if(jsx->iq_id) - jabber_iq_set_id(iq, jsx->iq_id); - - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - error = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(error, "code", "404"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - inf = purple_xmlnode_new_child(error, "item-not-found"); - purple_xmlnode_set_namespace(inf, NS_XMPP_STANZAS); - - jabber_iq_send(iq); - - /* if IBB is available, revert to that before giving up... */ - if (jsx->stream_method & STREAM_METHOD_IBB) { - /* if we are the initializer, init IBB */ - purple_debug_info("jabber", - "jabber_si_bytestreams_attempt_connect: " - "no streamhosts found, trying IBB\n"); - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS; - } - /* if we are the sender, open an IBB session, but not if we already - did it, since we could have received the error <iq/> from the - receiver already... */ - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND - && !jsx->ibb_session) { - jabber_si_xfer_ibb_send_init(jsx->js, xfer); - } else { - /* setup a timeout to cancel waiting for IBB open */ - jsx->ibb_timeout_handle = g_timeout_add_seconds(30, - jabber_si_bytestreams_ibb_timeout_cb, xfer); - } - /* if we are the receiver, just wait for IBB open, callback is - already set up... */ - } else { - purple_xfer_cancel_local(xfer); - } - - return; - } - - streamhost = jsx->streamhosts->data; - dstjid = jabber_id_new(purple_xfer_get_remote_user(xfer)); - - /* TODO: Deal with zeroconf */ - - if(dstjid != NULL && streamhost->host && streamhost->port > 0) { - GError *error = NULL; - PurpleAccount *account; - - account = purple_connection_get_account(jsx->js->gc); - jsx->client = purple_gio_socket_client_new(account, &error); - if (jsx->client != NULL) { - if (purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_SEND) { - /* When selecting a streamhost, timeout after - * STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes - * forever - */ - g_socket_client_set_timeout(jsx->client, - STREAMHOST_CONNECT_TIMEOUT); - } - - purple_debug_info("jabber", - "Connecting to SOCKS5 streamhost proxy %s:%d", - streamhost->host, streamhost->port); - - g_socket_client_connect_to_host_async( - jsx->client, streamhost->host, streamhost->port, - jsx->cancellable, - jabber_si_bytestreams_socks5_connect_to_host_cb, xfer); - } else { - purple_debug_error( - "jabber", - "Failed to connect to SOCKS5 streamhost proxy: %s", - error->message); - g_clear_error(&error); - } - - jabber_id_free(dstjid); - } - - if (jsx->client == NULL) { - jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); - jabber_bytestreams_streamhost_free(streamhost); - jabber_si_bytestreams_attempt_connect(xfer); - } -} - -void jabber_bytestreams_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *query) -{ - PurpleXfer *xfer; - JabberSIXfer *jsx; - PurpleXmlNode *streamhost; - const char *sid; - - if(type != JABBER_IQ_SET) - return; - - if(!from) - return; - - if(!(sid = purple_xmlnode_get_attrib(query, "sid"))) - return; - - if(!(xfer = jabber_si_xfer_find(js, sid, from))) - return; - - jsx = JABBER_SI_XFER(xfer); - - if(!jsx->accepted) - return; - - g_free(jsx->iq_id); - jsx->iq_id = g_strdup(id); - - for(streamhost = purple_xmlnode_get_child(query, "streamhost"); streamhost; - streamhost = purple_xmlnode_get_next_twin(streamhost)) { - const char *jid, *host = NULL, *port, *zeroconf; - int portnum = 0; - - if((jid = purple_xmlnode_get_attrib(streamhost, "jid")) && - ((zeroconf = purple_xmlnode_get_attrib(streamhost, "zeroconf")) || - ((host = purple_xmlnode_get_attrib(streamhost, "host")) && - (port = purple_xmlnode_get_attrib(streamhost, "port")) && - (portnum = atoi(port))))) { - /* ignore 0.0.0.0 */ - if(purple_strequal(host, "0.0.0.0") == FALSE) { - JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1); - sh->jid = g_strdup(jid); - sh->host = g_strdup(host); - sh->port = portnum; - sh->zeroconf = g_strdup(zeroconf); - - /* If there were a lot of these, it'd be worthwhile to prepend and reverse. */ - jsx->streamhosts = g_list_append(jsx->streamhosts, sh); - } - } - } - - jabber_si_bytestreams_attempt_connect(xfer); -} - -static void -jabber_si_xfer_bytestreams_send_write_response_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_written = 0; - GError *error = NULL; - - if (!g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result, - &bytes_written, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_error(&error); - } else { - /* Before actually starting sending the file, we need to wait until the - * recipient sends the IQ result with <streamhost-used/> - */ - purple_debug_info("jabber", - "SOCKS5 connection negotiation completed. Waiting " - "for IQ result to start file transfer."); - } - - g_clear_pointer(&jsx->socks_buf, g_free); -} - -static void -jabber_si_xfer_bytestreams_send_read_request_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_read = 0; - GError *error = NULL; - GOutputStream *output = NULL; - gsize bufsize; - gchar *dstaddr, *hash; - gchar *host; - - purple_debug_info("jabber", - "in jabber_si_xfer_bytestreams_send_read_request_cb"); - - if (!g_input_stream_read_all_finish(G_INPUT_STREAM(source), result, - &bytes_read, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_error(&error); - return; - } - - dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id, - jsx->js->user->node, jsx->js->user->domain, - jsx->js->user->resource, purple_xfer_get_remote_user(xfer)); - - /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */ - hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1, dstaddr, -1); - - if (strncmp(hash, jsx->socks_buf + 5, 40) || jsx->socks_buf[45] != 0x00 || - jsx->socks_buf[46] != 0x00) { - if (jsx->socks_buf[45] != 0x00 || jsx->socks_buf[46] != 0x00) { - purple_debug_error("jabber", - "Got SOCKS5 BS conn with the wrong DST.PORT " - "(must be 0 - got[0x%x,0x%x]).", - jsx->socks_buf[45], jsx->socks_buf[46]); - } else { - purple_debug_error("jabber", - "Got SOCKS5 BS conn with the wrong DST.ADDR " - "(expected '%s' - got '%.40s').", - hash, jsx->socks_buf + 5); - } - purple_xfer_cancel_remote(xfer); - g_free(hash); - g_free(dstaddr); - return; - } - - g_free(hash); - g_free(dstaddr); - - g_free(jsx->socks_buf); - host = purple_network_get_my_ip_from_gio( - G_SOCKET_CONNECTION(jsx->js->stream)); - - bufsize = 5 + strlen(host) + 2; - jsx->socks_buf = g_malloc(bufsize); - - jsx->socks_buf[0] = 0x05; - jsx->socks_buf[1] = 0x00; - jsx->socks_buf[2] = 0x00; - jsx->socks_buf[3] = 0x03; - jsx->socks_buf[4] = strlen(host); - memcpy(jsx->socks_buf + 5, host, strlen(host)); - jsx->socks_buf[5 + strlen(host)] = 0x00; - jsx->socks_buf[6 + strlen(host)] = 0x00; - - output = g_io_stream_get_output_stream( - G_IO_STREAM(jsx->local_streamhost_conn)); - g_output_stream_write_all_async( - output, jsx->socks_buf, bufsize, G_PRIORITY_DEFAULT, - jsx->cancellable, jabber_si_xfer_bytestreams_send_write_response_cb, - xfer); - - g_free(host); -} - -static void -jabber_si_xfer_bytestreams_send_read_request_header_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GInputStream *input = G_INPUT_STREAM(source); - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_read = 0; - GError *error = NULL; - - purple_debug_info( - "jabber", - "in jabber_si_xfer_bytestreams_send_read_request_header_cb"); - - if (!g_input_stream_read_all_finish(input, result, &bytes_read, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_error(&error); - return; - } - - if (jsx->socks_buf[0] != 0x05 || jsx->socks_buf[1] != 0x01 || - jsx->socks_buf[3] != 0x03 || jsx->socks_buf[4] != 40) { - purple_debug_info( - "jabber", - "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]", - jsx->socks_buf[0], jsx->socks_buf[1], jsx->socks_buf[2], - jsx->socks_buf[3], jsx->socks_buf[4]); - purple_xfer_cancel_remote(xfer); - return; - } - - /* Read DST.ADDR and the following 2-byte port number. */ - purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num", - jsx->socks_buf[4] + 2); - g_input_stream_read_all_async( - input, jsx->socks_buf + 5, jsx->socks_buf[4] + 2, - G_PRIORITY_DEFAULT, jsx->cancellable, - jabber_si_xfer_bytestreams_send_read_request_cb, xfer); -} - -static void -jabber_si_xfer_bytestreams_send_write_method_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_written = 0; - GError *error = NULL; - - if (!g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result, - &bytes_written, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_pointer(&jsx->socks_buf, g_free); - g_clear_error(&error); - return; - } - - /* If we sent a "Success", wait for a response, otherwise give up and cancel */ - if (jsx->socks_buf[1] == 0x00) { - GInputStream *input = g_io_stream_get_input_stream( - G_IO_STREAM(jsx->local_streamhost_conn)); - - g_free(jsx->socks_buf); - /* Request is: Version, Command, Reserved, Address type, Address length, - * Address (up to 255), 2-byte port */ - jsx->socks_buf = g_malloc(5 + 255 + 2); - g_input_stream_read_all_async( - input, jsx->socks_buf, 5, G_PRIORITY_DEFAULT, jsx->cancellable, - jabber_si_xfer_bytestreams_send_read_request_header_cb, xfer); - } else { - purple_xfer_cancel_remote(xfer); - } -} - -static void -jabber_si_xfer_bytestreams_send_read_methods_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_read = 0; - GError *error = NULL; - GOutputStream *output; - gboolean valid_method_found = FALSE; - gint i; - - purple_debug_info("jabber", - "in jabber_si_xfer_bytestreams_send_read_methods_cb"); - - if (!g_input_stream_read_all_finish(G_INPUT_STREAM(source), result, - &bytes_read, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_error(&error); - return; - } - - purple_debug_info("jabber", "going to test %u different methods", - jsx->socks_buf[1]); - - for (i = 0; i < jsx->socks_buf[1]; i++) { - purple_debug_info("jabber", "testing %u", jsx->socks_buf[i + 2]); - if (jsx->socks_buf[i + 2] == 0x00) { - valid_method_found = TRUE; - break; - } - } - - g_free(jsx->socks_buf); - jsx->socks_buf = g_malloc(2); - jsx->socks_buf[0] = 0x05; - jsx->socks_buf[1] = valid_method_found ? 0x00 : 0xFF; - output = g_io_stream_get_output_stream( - G_IO_STREAM(jsx->local_streamhost_conn)); - g_output_stream_write_all_async( - output, jsx->socks_buf, 2, G_PRIORITY_DEFAULT, jsx->cancellable, - jabber_si_xfer_bytestreams_send_write_method_cb, xfer); -} - -static void -jabber_si_xfer_bytestreams_send_read_version_cb(GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GInputStream *input = G_INPUT_STREAM(source); - PurpleXfer *xfer = PURPLE_XFER(user_data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - gsize bytes_read = 0; - GError *error = NULL; - - purple_debug_info("jabber", - "in jabber_si_xfer_bytestreams_send_read_version_cb"); - - /* Try to read the SOCKS5 header */ - if (!g_input_stream_read_all_finish(input, result, &bytes_read, &error)) { - if (error->code != G_IO_ERROR_CANCELLED) { - purple_xfer_cancel_remote(xfer); - } - g_clear_error(&error); - return; - } - - purple_debug_info("jabber", "checking to make sure we're socks FIVE"); - if (jsx->socks_buf[0] != 0x05) { - purple_xfer_cancel_remote(xfer); - return; - } - - /* Number of methods is given in second byte. */ - purple_debug_info("jabber", "reading %u bytes for auth methods", - jsx->socks_buf[1]); - g_input_stream_read_all_async( - input, jsx->socks_buf + 2, jsx->socks_buf[1], G_PRIORITY_DEFAULT, - jsx->cancellable, jabber_si_xfer_bytestreams_send_read_methods_cb, - xfer); -} - -static gint -jabber_si_compare_jid(gconstpointer a, gconstpointer b) -{ - const JabberBytestreamsStreamhost *sh = a; - - if(!a) - return -1; - - return strcmp(sh->jid, (char *)b); -} - -static void -jabber_si_xfer_bytestreams_send_connected_cb(G_GNUC_UNUSED GSocketService *service, - GSocketConnection *connection, - GObject *source_object, - G_GNUC_UNUSED gpointer data) -{ - PurpleXfer *xfer = PURPLE_XFER(source_object); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - GInputStream *input = NULL; - - purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n"); - - jsx->local_streamhost_conn = g_object_ref(connection); - g_socket_service_stop(jsx->service); - g_clear_object(&jsx->service); - - /* Initial message is: Version, Method count, up to 255 methods. */ - jsx->socks_buf = g_malloc(2 + 255); - input = g_io_stream_get_input_stream( - G_IO_STREAM(jsx->local_streamhost_conn)); - g_input_stream_read_all_async( - input, jsx->socks_buf, 2, G_PRIORITY_DEFAULT, jsx->cancellable, - jabber_si_xfer_bytestreams_send_read_version_cb, xfer); -} - -static void -jabber_si_connect_proxy_cb(JabberStream *js, const char *from, - JabberIqType type, G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - PurpleXfer *xfer = data; - JabberSIXfer *jsx; - PurpleXmlNode *query, *streamhost_used; - const char *jid; - GList *matched; - - /* TODO: This need to send errors if we don't see what we're looking for */ - - /* Make sure that the xfer is actually still valid and we're not just receiving an old iq response */ - if (!g_list_find(js->file_transfers, xfer)) { - purple_debug_error("jabber", "Got bytestreams response for no longer existing xfer (%p)\n", xfer); - return; - } - - jsx = JABBER_SI_XFER(xfer); - - /* In the case of a direct file transfer, this is expected to return */ - if(!jsx) - return; - - if(type != JABBER_IQ_RESULT) { - purple_debug_info("jabber", - "jabber_si_xfer_connect_proxy_cb: type = error\n"); - /* if IBB is available, open IBB session */ - purple_debug_info("jabber", - "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n", - jsx->stream_method); - if (jsx->stream_method & STREAM_METHOD_IBB) { - /* if we previously tried bytestreams, we need to disable it. */ - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS; - } - - purple_debug_info("jabber", "IBB is possible, try it\n"); - /* if we are the sender and haven't already opened an IBB - session, do so now (we might already have failed to open - the bytestream proxy ourselves when receiving this <iq/> */ - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND - && !jsx->ibb_session) { - jabber_si_xfer_ibb_send_init(js, xfer); - } else { - jsx->ibb_timeout_handle = g_timeout_add_seconds(30, - jabber_si_bytestreams_ibb_timeout_cb, xfer); - } - /* if we are receiver, just wait for IBB open stanza, callback - is already set up */ - } else { - purple_xfer_cancel_remote(xfer); - } - return; - } - - if (!from) - return; - - if(!(query = purple_xmlnode_get_child(packet, "query"))) - return; - - if(!(streamhost_used = purple_xmlnode_get_child(query, "streamhost-used"))) - return; - - if(!(jid = purple_xmlnode_get_attrib(streamhost_used, "jid"))) - return; - - purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n", - jsx, jsx->streamhosts, jid); - - if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid))) - { - gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, - jsx->js->user->domain, jsx->js->user->resource); - if (purple_strequal(jid, my_jid)) { - GSocket *sock = NULL; - gint fd = -1; - purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n"); - sock = g_socket_connection_get_socket(jsx->local_streamhost_conn); - fd = g_socket_get_fd(sock); - _purple_network_set_common_socket_flags(fd); - purple_xfer_start(xfer, fd, NULL, -1); - } else { - /* if available, try to revert to IBB... */ - if (jsx->stream_method & STREAM_METHOD_IBB) { - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS; - } - - purple_debug_info("jabber", - "jabber_si_connect_proxy_cb: trying to revert to IBB\n"); - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { - jabber_si_xfer_ibb_send_init(jsx->js, xfer); - } else { - jsx->ibb_timeout_handle = g_timeout_add_seconds(30, - jabber_si_bytestreams_ibb_timeout_cb, xfer); - } - /* if we are the receiver, we are already set up...*/ - } else { - purple_debug_info("jabber", - "streamhost-used does not match any proxy that was offered to target\n"); - purple_xfer_cancel_local(xfer); - } - } - g_free(my_jid); - return; - } - - /* Clean up the local streamhost - it isn't going to be used.*/ - g_clear_object(&jsx->local_streamhost_conn); - if (jsx->service) { - g_socket_service_stop(jsx->service); - g_clear_object(&jsx->service); - } - - jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched); - g_list_free_full(jsx->streamhosts, (GDestroyNotify)jabber_bytestreams_streamhost_free); - - jsx->streamhosts = matched; - - jabber_si_bytestreams_attempt_connect(xfer); -} - -static void -jabber_si_xfer_bytestreams_send_local_info(PurpleXfer *xfer, GList *local_ips) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberIq *iq; - PurpleXmlNode *query, *streamhost; - char port[6]; - GList *tmp; - JabberBytestreamsStreamhost *sh, *sh2; - int streamhost_count = 0; - - iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - query = purple_xmlnode_get_child(iq->node, "query"); - - purple_xmlnode_set_attrib(query, "sid", jsx->stream_id); - - /* If we successfully started listening locally */ - if (local_ips) { - gchar *jid; - - jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, - jsx->js->user->domain, jsx->js->user->resource); - g_snprintf(port, sizeof(port), "%hu", purple_xfer_get_local_port(xfer)); - - /* Include the localhost's IPs (for in-network transfers) */ - while (local_ips) { - gchar *local_ip = local_ips->data; - streamhost_count++; - streamhost = purple_xmlnode_new_child(query, "streamhost"); - purple_xmlnode_set_attrib(streamhost, "jid", jid); - purple_xmlnode_set_attrib(streamhost, "host", local_ip); - purple_xmlnode_set_attrib(streamhost, "port", port); - g_free(local_ip); - local_ips = g_list_delete_link(local_ips, local_ips); - } - - g_free(jid); - } - - for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) { - sh = tmp->data; - - /* TODO: deal with zeroconf proxies */ - - if (!sh->jid) { - continue; - } else if (!sh->host) { - continue; - } else if (sh->port <= 0) { - continue; - } else if (g_list_find_custom(jsx->streamhosts, sh->jid, - jabber_si_compare_jid) != NULL) { - continue; - } - - streamhost_count++; - streamhost = purple_xmlnode_new_child(query, "streamhost"); - purple_xmlnode_set_attrib(streamhost, "jid", sh->jid); - purple_xmlnode_set_attrib(streamhost, "host", sh->host); - g_snprintf(port, sizeof(port), "%hu", sh->port); - purple_xmlnode_set_attrib(streamhost, "port", port); - - sh2 = g_new0(JabberBytestreamsStreamhost, 1); - sh2->jid = g_strdup(sh->jid); - sh2->host = g_strdup(sh->host); - /*sh2->zeroconf = g_strdup(sh->zeroconf);*/ - sh2->port = sh->port; - - jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2); - } - - /* We have no way of transferring, cancel the transfer */ - if (streamhost_count == 0) { - jabber_iq_free(iq); - - /* if available, revert to IBB */ - if (jsx->stream_method & STREAM_METHOD_IBB) { - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS; - } - purple_debug_info("jabber", "jabber_si_xfer_bytestreams_send_local_" - "info: trying to revert to IBB\n"); - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { - /* if we are the sender, init the IBB session... */ - jabber_si_xfer_ibb_send_init(jsx->js, xfer); - } else { - jsx->ibb_timeout_handle = g_timeout_add_seconds(30, - jabber_si_bytestreams_ibb_timeout_cb, xfer); - } - /* if we are the receiver, we should just wait... the IBB open - handler has already been set up... */ - } else { - /* We should probably notify the target, - but this really shouldn't ever happen */ - purple_xfer_cancel_local(xfer); - } - - return; - } - - jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer); - - jabber_iq_send(iq); -} - -static void -jabber_si_xfer_bytestreams_send_init(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - PurpleProxyType proxy_type; - GList *local_ips = NULL; - - /* TODO: This should probably be done with an account option instead of - * piggy-backing on the TOR proxy type. */ - proxy_type = purple_proxy_info_get_proxy_type( - purple_proxy_get_setup(purple_connection_get_account(jsx->js->gc))); - if (proxy_type == PURPLE_PROXY_TYPE_TOR) { - purple_debug_info("jabber", "Skipping attempting local streamhost.\n"); - jsx->service = NULL; - } else { - guint16 port = 0; - GError *error = NULL; - jsx->service = g_socket_service_new(); - port = purple_socket_listener_add_any_inet_port( - G_SOCKET_LISTENER(jsx->service), G_OBJECT(xfer), &error); - if (port != 0) { - purple_xfer_set_local_port(xfer, port); - } else { - purple_debug_error("jabber", - "Unable to create streamhost socket listener: " - "%s. Trying proxy instead.", - error->message); - g_error_free(error); - g_clear_object(&jsx->service); - } - } - - if (jsx->service) { - gchar *public_ip; - - /* Include the public IP (assuming there is a port mapped somehow) */ - public_ip = purple_network_get_my_ip_from_gio( - G_SOCKET_CONNECTION(jsx->js->stream)); - if (!purple_strequal(public_ip, "0.0.0.0")) { - local_ips = g_list_append(local_ips, public_ip); - } else { - g_free(public_ip); - } - - g_signal_connect( - G_OBJECT(jsx->service), "incoming", - G_CALLBACK(jabber_si_xfer_bytestreams_send_connected_cb), NULL); - } - - jabber_si_xfer_bytestreams_send_local_info(xfer, local_ips); -} - -static void -jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess) -{ - PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); - - purple_debug_error("jabber", "an error occurred during IBB file transfer\n"); - purple_xfer_cancel_remote(xfer); -} - -static void -jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess) -{ - PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); - - purple_debug_info("jabber", "the remote user closed the transfer\n"); - if (purple_xfer_get_bytes_remaining(xfer) > 0) { - purple_xfer_cancel_remote(xfer); - } else { - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); - } -} - -static void -jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data, - gsize size) -{ - PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - if ((goffset)size <= purple_xfer_get_bytes_remaining(xfer)) { - purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n", - size); - purple_circular_buffer_append(jsx->ibb_buffer, data, size); - purple_xfer_protocol_ready(xfer); - } else { - /* trying to write past size of file transfers negotiated size, - reject transfer to protect against malicious behaviour */ - purple_debug_error("jabber", - "IBB file transfer send more data than expected\n"); - purple_xfer_cancel_remote(xfer); - } - -} - -static gssize -jabber_si_xfer_ibb_read(PurpleXfer *xfer, guchar **out_buffer, size_t buf_size) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - guchar *buffer; - gsize size; - gsize tmp; - - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - return PURPLE_XFER_CLASS(jabber_si_xfer_parent_class)->read(xfer, out_buffer, buf_size); - } - - size = purple_circular_buffer_get_used(jsx->ibb_buffer); - - *out_buffer = buffer = g_malloc(size); - while ((tmp = purple_circular_buffer_get_max_read(jsx->ibb_buffer))) { - const gchar *output = purple_circular_buffer_get_output(jsx->ibb_buffer); - memcpy(buffer, output, tmp); - buffer += tmp; - purple_circular_buffer_mark_read(jsx->ibb_buffer, tmp); - } - - return size; -} - -static gboolean -jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id, - PurpleXmlNode *open) -{ - const gchar *sid = purple_xmlnode_get_attrib(open, "sid"); - PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who); - - if (xfer) { - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberIBBSession *sess = - jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer); - - jabber_si_bytestreams_ibb_timeout_remove(jsx); - - if (sess) { - /* setup callbacks here...*/ - jabber_ibb_session_set_data_received_callback(sess, - jabber_si_xfer_ibb_recv_data_cb); - jabber_ibb_session_set_closed_callback(sess, - jabber_si_xfer_ibb_closed_cb); - jabber_ibb_session_set_error_callback(sess, - jabber_si_xfer_ibb_error_cb); - - jsx->ibb_session = sess; - /* we handle up to block-size bytes of decoded data, to handle - clients interpreting the block-size attribute as that - (see also remark in ibb.c) */ - jsx->ibb_buffer = - purple_circular_buffer_new(jabber_ibb_session_get_block_size(sess)); - - /* start the transfer */ - purple_xfer_start(xfer, -1, NULL, 0); - return TRUE; - } else { - /* failed to create IBB session */ - purple_debug_error("jabber", "failed to create IBB session\n"); - purple_xfer_cancel_remote(xfer); - return FALSE; - } - } else { - /* we got an IBB <open/> for an unknown file transfer, pass along... */ - purple_debug_info("jabber", - "IBB open did not match any SI file transfer\n"); - return FALSE; - } -} - -static gssize -jabber_si_xfer_ibb_write(PurpleXfer *xfer, const guchar *buffer, size_t len) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberIBBSession *sess = jsx->ibb_session; - gsize packet_size; - - if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - purple_debug_error("jabber", "falling back to raw socket\n"); - return PURPLE_XFER_CLASS(jabber_si_xfer_parent_class)->write(xfer, buffer, len); - } - - packet_size = MIN(len, jabber_ibb_session_get_max_data_size(sess)); - - jabber_ibb_session_send_data(sess, buffer, packet_size); - - return packet_size; -} - -static void -jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess) -{ - PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); - goffset remaining = purple_xfer_get_bytes_remaining(xfer); - - if (remaining == 0) { - /* close the session */ - jabber_ibb_session_close(sess); - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); - } else { - /* send more... */ - purple_xfer_protocol_ready(xfer); - } -} - -static void -jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess) -{ - PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); - - if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) { - purple_xfer_start(xfer, -1, NULL, 0); - purple_xfer_protocol_ready(xfer); - } else { - /* error */ - purple_xfer_end(xfer); - } -} - -static void -jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id, - purple_xfer_get_remote_user(xfer), xfer); - - if (jsx->ibb_session) { - /* should set callbacks here... */ - jabber_ibb_session_set_opened_callback(jsx->ibb_session, - jabber_si_xfer_ibb_opened_cb); - jabber_ibb_session_set_data_sent_callback(jsx->ibb_session, - jabber_si_xfer_ibb_sent_cb); - jabber_ibb_session_set_closed_callback(jsx->ibb_session, - jabber_si_xfer_ibb_closed_cb); - jabber_ibb_session_set_error_callback(jsx->ibb_session, - jabber_si_xfer_ibb_error_cb); - - jsx->ibb_buffer = - purple_circular_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session)); - - /* open the IBB session */ - jabber_ibb_session_open(jsx->ibb_session); - - } else { - /* failed to create IBB session */ - purple_debug_error("jabber", - "failed to initiate IBB session for file transfer\n"); - purple_xfer_cancel_local(xfer); - } -} - -static void -jabber_si_xfer_send_method_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - G_GNUC_UNUSED JabberIqType type, - G_GNUC_UNUSED const char *id, - PurpleXmlNode *packet, gpointer data) -{ - PurpleXfer *xfer = data; - PurpleXmlNode *si, *feature, *x, *field, *value; - gboolean found_method = FALSE; - - if(!(si = purple_xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { - purple_xfer_cancel_remote(xfer); - return; - } - - if(!(feature = purple_xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) { - purple_xfer_cancel_remote(xfer); - return; - } - - if(!(x = purple_xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) { - purple_xfer_cancel_remote(xfer); - return; - } - - for(field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) { - const char *var = purple_xmlnode_get_attrib(field, "var"); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - if(purple_strequal(var, "stream-method")) { - if((value = purple_xmlnode_get_child(field, "value"))) { - char *val = purple_xmlnode_get_data(value); - if(purple_strequal(val, NS_BYTESTREAMS)) { - jabber_si_xfer_bytestreams_send_init(xfer); - jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; - found_method = TRUE; - } else if (purple_strequal(val, NS_IBB)) { - jsx->stream_method |= STREAM_METHOD_IBB; - if (!found_method) { - /* we haven't tried to init a bytestream session, yet - start IBB right away... */ - jabber_si_xfer_ibb_send_init(js, xfer); - found_method = TRUE; - } - } - g_free(val); - } - } - } - - if (!found_method) { - purple_xfer_cancel_remote(xfer); - } - -} - -static void jabber_si_xfer_send_request(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberIq *iq; - PurpleXmlNode *si, *file, *feature, *x, *field, *option, *value; - char buf[32]; -#if ENABLE_FT_THUMBNAILS - gconstpointer thumb; - gsize thumb_size; - - purple_xfer_prepare_thumbnail(xfer, "jpeg,png"); -#endif - purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer))); - - iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - si = purple_xmlnode_new_child(iq->node, "si"); - purple_xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); - jsx->stream_id = jabber_get_next_id(jsx->js); - purple_xmlnode_set_attrib(si, "id", jsx->stream_id); - purple_xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER); - - file = purple_xmlnode_new_child(si, "file"); - purple_xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER); - purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer)); - g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer)); - purple_xmlnode_set_attrib(file, "size", buf); - /* maybe later we'll do hash and date attribs */ - -#if ENABLE_FT_THUMBNAILS - /* add thumbnail, if appropriate */ - if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) { - const gchar *mimetype = purple_xfer_get_thumbnail_mimetype(xfer); - JabberData *thumbnail_data = - jabber_data_create_from_data(thumb, thumb_size, - mimetype, TRUE, jsx->js); - PurpleXmlNode *thumbnail = purple_xmlnode_new_child(file, "thumbnail"); - purple_xmlnode_set_namespace(thumbnail, NS_THUMBS); - purple_xmlnode_set_attrib(thumbnail, "cid", - jabber_data_get_cid(thumbnail_data)); - purple_xmlnode_set_attrib(thumbnail, "mime-type", mimetype); - /* cache data */ - jabber_data_associate_local(thumbnail_data, NULL); - } -#endif - - feature = purple_xmlnode_new_child(si, "feature"); - purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); - x = purple_xmlnode_new_child(feature, "x"); - purple_xmlnode_set_namespace(x, "jabber:x:data"); - purple_xmlnode_set_attrib(x, "type", "form"); - field = purple_xmlnode_new_child(x, "field"); - purple_xmlnode_set_attrib(field, "var", "stream-method"); - purple_xmlnode_set_attrib(field, "type", "list-single"); - /* maybe we should add an option to always skip bytestreams for people - behind troublesome firewalls */ - option = purple_xmlnode_new_child(field, "option"); - value = purple_xmlnode_new_child(option, "value"); - purple_xmlnode_insert_data(value, NS_BYTESTREAMS, -1); - option = purple_xmlnode_new_child(field, "option"); - value = purple_xmlnode_new_child(option, "value"); - purple_xmlnode_insert_data(value, NS_IBB, -1); - - jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); - - /* Store the IQ id so that we can cancel the callback */ - g_free(jsx->iq_id); - jsx->iq_id = g_strdup(iq->id); - - jabber_iq_send(iq); -} - -/* - * These four functions should only be called from the PurpleXfer functions - * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or - * purple_xfer_request_denied. - */ -static void jabber_si_xfer_cancel_send(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - /* if there is an IBB session active, send close on that */ - if (jsx->ibb_session) { - jabber_ibb_session_close(jsx->ibb_session); - } - purple_debug_info("jabber", "in jabber_si_xfer_cancel_send\n"); - - g_cancellable_cancel(jsx->cancellable); -} - - -static void jabber_si_xfer_request_denied(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberStream *js = jsx->js; - - /* - * TODO: It's probably an error if jsx->iq_id == NULL. g_return_if_fail - * might be warranted. - */ - if (jsx->iq_id && !jsx->accepted) { - JabberIq *iq; - PurpleXmlNode *error, *child; - iq = jabber_iq_new(js, JABBER_IQ_ERROR); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - jabber_iq_set_id(iq, jsx->iq_id); - - error = purple_xmlnode_new_child(iq->node, "error"); - purple_xmlnode_set_attrib(error, "type", "cancel"); - child = purple_xmlnode_new_child(error, "forbidden"); - purple_xmlnode_set_namespace(child, NS_XMPP_STANZAS); - child = purple_xmlnode_new_child(error, "text"); - purple_xmlnode_set_namespace(child, NS_XMPP_STANZAS); - purple_xmlnode_insert_data(child, "Offer Declined", -1); - - jabber_iq_send(iq); - } - - purple_debug_info("jabber", "in jabber_si_xfer_request_denied\n"); -} - - -static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - /* if there is an IBB session active, send close */ - if (jsx->ibb_session) { - jabber_ibb_session_close(jsx->ibb_session); - } - purple_debug_info("jabber", "in jabber_si_xfer_cancel_recv\n"); - - g_cancellable_cancel(jsx->cancellable); -} - - -static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who, - JabberCapabilities capabilities, gpointer data) -{ - PurpleXfer *xfer = PURPLE_XFER(data); - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - - if (capabilities & JABBER_CAP_IBB) { - purple_debug_info("jabber", - "jabber_si_xfer_send_disco_cb: remote JID supports IBB\n"); - jsx->stream_method |= STREAM_METHOD_IBB; - } - - if (capabilities & JABBER_CAP_SI_FILE_XFER) { - jabber_si_xfer_send_request(xfer); - } else { - char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); - purple_notify_error(js->gc, _("File Send Failed"), - _("File Send Failed"), msg, - purple_request_cpar_from_connection(js->gc)); - g_free(msg); - purple_xfer_cancel_local(xfer); - } -} - -static void -resource_select_cancel_cb(PurpleXfer *xfer, - G_GNUC_UNUSED PurpleRequestPage *page) -{ - purple_xfer_cancel_local(xfer); -} - -static void do_transfer_send(PurpleXfer *xfer, const char *resource) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - char **who_v = g_strsplit(purple_xfer_get_remote_user(xfer), "/", 2); - char *who; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - - jb = jabber_buddy_find(jsx->js, who_v[0], FALSE); - if (jb) { - jbr = jabber_buddy_find_resource(jb, resource); - } - - who = g_strdup_printf("%s/%s", who_v[0], resource); - g_strfreev(who_v); - purple_xfer_set_remote_user(xfer, who); - - if (jbr && jabber_resource_know_capabilities(jbr)) { - char *msg; - - if (jabber_resource_has_capability(jbr, NS_IBB)) - jsx->stream_method |= STREAM_METHOD_IBB; - if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) { - jabber_si_xfer_send_request(xfer); - g_free(who); - return; - } - - msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); - purple_notify_error(jsx->js->gc, _("File Send Failed"), - _("File Send Failed"), msg, - purple_request_cpar_from_connection(jsx->js->gc)); - g_free(msg); - purple_xfer_cancel_local(xfer); - } else { - jabber_disco_info_do(jsx->js, who, - jabber_si_xfer_send_disco_cb, xfer); - } - - g_free(who); -} - -static void -resource_select_ok_cb(PurpleXfer *xfer, PurpleRequestPage *page) -{ - const char *selected_label = purple_request_page_get_choice(page, "resource"); - - do_transfer_send(xfer, selected_label); -} - -static void jabber_si_xfer_xfer_init(PurpleXfer *xfer) -{ - JabberSIXfer *jsx = JABBER_SI_XFER(xfer); - JabberIq *iq; - if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) { - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL; - char *resource; - GList *resources = NULL; - - if(NULL != (resource = jabber_get_resource(purple_xfer_get_remote_user(xfer)))) { - /* they've specified a resource, no need to ask or - * default or anything, just do it */ - - do_transfer_send(xfer, resource); - g_free(resource); - return; - } - - jb = jabber_buddy_find(jsx->js, purple_xfer_get_remote_user(xfer), TRUE); - - if (jb) { - GList *l; - - for (l = jb->resources ; l ; l = g_list_next(l)) { - jbr = l->data; - - if (!jabber_resource_know_capabilities(jbr) || - (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER) - && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS) - || jabber_resource_has_capability(jbr, NS_IBB)))) { - resources = g_list_append(resources, jbr); - } - } - } - - if (!resources) { - /* no resources online, we're trying to send to someone - * whose presence we're not subscribed to, or - * someone who is offline. Let's inform the user */ - char *msg; - - if(!jb) { - msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), purple_xfer_get_remote_user(xfer)); - } else if(jb->subscription & JABBER_SUB_TO) { - msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), purple_xfer_get_remote_user(xfer)); - } else { - msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), purple_xfer_get_remote_user(xfer)); - } - - purple_notify_error(jsx->js->gc, _("File Send Failed"), - _("File Send Failed"), msg, - purple_request_cpar_from_connection(jsx->js->gc)); - g_free(msg); - } else if (g_list_length(resources) == 1) { - /* only 1 resource online (probably our most common case) - * so no need to ask who to send to */ - jbr = resources->data; - do_transfer_send(xfer, jbr->name); - } else { - /* we've got multiple resources, we need to pick one to send to */ - GList *l; - PurpleRequestPage *page = NULL; - PurpleRequestField *field = NULL; - PurpleRequestFieldChoice *choice = NULL; - PurpleRequestGroup *group = NULL; - char *msg = NULL; - - field = purple_request_field_choice_new("resource", _("Resource"), 0); - choice = PURPLE_REQUEST_FIELD_CHOICE(field); - for(l = resources; l; l = l->next) { - jbr = l->data; - purple_request_field_choice_add_full(choice, jbr->name, - g_strdup(jbr->name), - g_free); - } - - group = purple_request_group_new(NULL); - purple_request_group_add_field(group, field); - - page = purple_request_page_new(); - purple_request_page_add_group(page, group); - - msg = g_strdup_printf(_("Please select the resource of %s to " - "which you would like to send a file"), - purple_xfer_get_remote_user(xfer)); - - purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, page, - _("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb), - purple_request_cpar_from_connection(jsx->js->gc), xfer); - - g_free(msg); - } - - g_list_free(resources); - } else { - PurpleXmlNode *si, *feature, *x, *field, *value; - - iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT); - purple_xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer)); - if(jsx->iq_id) - jabber_iq_set_id(iq, jsx->iq_id); - else - purple_debug_error("jabber", "Sending SI result with new IQ id.\n"); - - jsx->accepted = TRUE; - - si = purple_xmlnode_new_child(iq->node, "si"); - purple_xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); - - feature = purple_xmlnode_new_child(si, "feature"); - purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); - - x = purple_xmlnode_new_child(feature, "x"); - purple_xmlnode_set_namespace(x, "jabber:x:data"); - purple_xmlnode_set_attrib(x, "type", "submit"); - field = purple_xmlnode_new_child(x, "field"); - purple_xmlnode_set_attrib(field, "var", "stream-method"); - - /* we should maybe "remember" if bytestreams has failed before (in the - same session) with this JID, and only present IBB as an option to - avoid unnecessary timeout */ - /* maybe we should have an account option to always just try IBB - for people who know their firewalls are very restrictive */ - if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { - value = purple_xmlnode_new_child(field, "value"); - purple_xmlnode_insert_data(value, NS_BYTESTREAMS, -1); - } else if(jsx->stream_method & STREAM_METHOD_IBB) { - value = purple_xmlnode_new_child(field, "value"); - purple_xmlnode_insert_data(value, NS_IBB, -1); - } - - jabber_iq_send(iq); - } -} - -PurpleXfer * -jabber_si_new_xfer(G_GNUC_UNUSED PurpleProtocolXfer *prplxfer, - PurpleConnection *gc, const char *who) -{ - JabberStream *js; - JabberSIXfer *jsx; - - js = purple_connection_get_protocol_data(gc); - - jsx = g_object_new( - JABBER_TYPE_SI_XFER, - "account", purple_connection_get_account(gc), - "type", PURPLE_XFER_TYPE_SEND, - "remote-user", who, - NULL - ); - - jsx->js = js; - js->file_transfers = g_list_append(js->file_transfers, jsx); - - return PURPLE_XFER(jsx); -} - -void jabber_si_xfer_send(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file) -{ - PurpleXfer *xfer; - - xfer = jabber_si_new_xfer(prplxfer, gc, who); - - if (file) - purple_xfer_request_accepted(xfer, file); - else - purple_xfer_request(xfer); -} - -#if ENABLE_FT_THUMBNAILS -static void -jabber_si_thumbnail_cb(JabberData *data, gchar *alt, gpointer userdata) -{ - PurpleXfer *xfer = (PurpleXfer *) userdata; - - if (data) { - purple_xfer_set_thumbnail(xfer, jabber_data_get_data(data), - jabber_data_get_size(data), jabber_data_get_type(data)); - /* data is ephemeral, get rid of now (the xfer re-owned the thumbnail */ - jabber_data_destroy(data); - } - - purple_xfer_request(xfer); -} -#endif - -static void -jabber_si_parse(JabberStream *js, const char *from, - G_GNUC_UNUSED JabberIqType type, const char *id, - PurpleXmlNode *si) -{ - JabberSIXfer *jsx; - PurpleXmlNode *file, *feature, *x, *field, *option, *value; -#if ENABLE_FT_THUMBNAILS - PurpleXmlNode *thumbnail; -#endif - const char *stream_id, *filename, *filesize_c, *profile; - goffset filesize = 0; - - if(!(profile = purple_xmlnode_get_attrib(si, "profile")) || - !purple_strequal(profile, NS_SI_FILE_TRANSFER)) - return; - - if(!(stream_id = purple_xmlnode_get_attrib(si, "id"))) - return; - - if(!(file = purple_xmlnode_get_child(si, "file"))) - return; - - if(!(filename = purple_xmlnode_get_attrib(file, "name"))) - return; - - if((filesize_c = purple_xmlnode_get_attrib(file, "size"))) - filesize = g_ascii_strtoull(filesize_c, NULL, 10); - - if(!(feature = purple_xmlnode_get_child(si, "feature"))) - return; - - if(!(x = purple_xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) - return; - - if(!from) - return; - - /* if they've already sent us this file transfer with the same damn id - * then we're gonna ignore it, until I think of something better to do - * with it */ - if(jabber_si_xfer_find(js, stream_id, from) != NULL) - return; - - jsx = g_object_new( - JABBER_TYPE_SI_XFER, - "account", purple_connection_get_account(js->gc), - "type", PURPLE_XFER_TYPE_RECEIVE, - "remote-user", from, - NULL - ); - - for(field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) { - const char *var = purple_xmlnode_get_attrib(field, "var"); - if(purple_strequal(var, "stream-method")) { - for(option = purple_xmlnode_get_child(field, "option"); option; - option = purple_xmlnode_get_next_twin(option)) { - if((value = purple_xmlnode_get_child(option, "value"))) { - char *val; - if((val = purple_xmlnode_get_data(value))) { - if(purple_strequal(val, NS_BYTESTREAMS)) { - jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; - } else if(purple_strequal(val, NS_IBB)) { - jsx->stream_method |= STREAM_METHOD_IBB; - } - g_free(val); - } - } - } - } - } - - if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { - g_object_unref(jsx); - return; - } - - jsx->js = js; - jsx->stream_id = g_strdup(stream_id); - jsx->iq_id = g_strdup(id); - - purple_xfer_set_filename(PURPLE_XFER(jsx), filename); - if(filesize > 0) { - purple_xfer_set_size(PURPLE_XFER(jsx), filesize); - } - - js->file_transfers = g_list_append(js->file_transfers, jsx); - -#if ENABLE_FT_THUMBNAILS - /* if there is a thumbnail, we should request it... */ - if ((thumbnail = purple_xmlnode_get_child_with_namespace(file, "thumbnail", - NS_THUMBS))) { - const char *cid = purple_xmlnode_get_attrib(thumbnail, "cid"); - if (cid) { - jabber_data_request(js, cid, purple_xfer_get_remote_user(PURPLE_XFER(jsx)), - NULL, TRUE, jabber_si_thumbnail_cb, jsx); - return; - } - } -#endif - - purple_xfer_request(PURPLE_XFER(jsx)); -} - -/****************************************************************************** - * GObject Implementation - *****************************************************************************/ -static void -jabber_si_xfer_init(JabberSIXfer *jsx) -{ - jsx->ibb_session = NULL; - jsx->cancellable = g_cancellable_new(); -} - -static void -jabber_si_xfer_finalize(GObject *obj) { - JabberSIXfer *jsx = JABBER_SI_XFER(obj); - JabberStream *js = jsx->js; - - js->file_transfers = g_list_remove(js->file_transfers, jsx); - - g_cancellable_cancel(jsx->cancellable); - g_clear_object(&jsx->cancellable); - - g_clear_object(&jsx->client); - g_clear_object(&jsx->local_streamhost_conn); - if (jsx->service) { - g_socket_service_stop(jsx->service); - } - g_clear_object(&jsx->service); - - if (jsx->iq_id != NULL) { - jabber_iq_remove_callback_by_id(js, jsx->iq_id); - } - - g_clear_handle_id(&jsx->connect_timeout, g_source_remove); - g_clear_handle_id(&jsx->ibb_timeout_handle, g_source_remove); - - g_list_free_full(jsx->streamhosts, (GDestroyNotify)jabber_bytestreams_streamhost_free); - - if (jsx->ibb_session) { - purple_debug_info("jabber", - "jabber_si_xfer_free: destroying IBB session\n"); - jabber_ibb_session_destroy(jsx->ibb_session); - } - - if (jsx->ibb_buffer) { - g_object_unref(jsx->ibb_buffer); - } - - purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx); - - g_free(jsx->stream_id); - g_free(jsx->iq_id); - g_free(jsx->socks_buf); - - G_OBJECT_CLASS(jabber_si_xfer_parent_class)->finalize(obj); -} - -static void -jabber_si_xfer_class_finalize(G_GNUC_UNUSED JabberSIXferClass *klass) { -} - -static void -jabber_si_xfer_class_init(JabberSIXferClass *klass) { - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass); - - obj_class->finalize = jabber_si_xfer_finalize; - - xfer_class->init = jabber_si_xfer_xfer_init; - xfer_class->request_denied = jabber_si_xfer_request_denied; - xfer_class->cancel_send = jabber_si_xfer_cancel_send; - xfer_class->cancel_recv = jabber_si_xfer_cancel_recv; - xfer_class->read = jabber_si_xfer_ibb_read; - xfer_class->write = jabber_si_xfer_ibb_write; -} - -/****************************************************************************** - * Public API - *****************************************************************************/ -void -jabber_si_xfer_register(GTypeModule *module) { - jabber_si_xfer_register_type(module); -} - -void -jabber_si_init(void) { - jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse); - - jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb); -} - -void -jabber_si_uninit(void) { - jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb); -}
--- a/libpurple/protocols/jabber/si.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/** - * @file si.h SI transfer functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_SI_H -#define PURPLE_JABBER_SI_H - -#include <purple.h> - -#include "jabber.h" - -G_BEGIN_DECLS - -#define JABBER_TYPE_SI_XFER (jabber_si_xfer_get_type()) -G_DECLARE_FINAL_TYPE(JabberSIXfer, jabber_si_xfer, JABBER, SI_XFER, PurpleXfer); - -void jabber_bytestreams_parse(JabberStream *js, const char *from, - JabberIqType type, const char *id, PurpleXmlNode *query); -PurpleXfer *jabber_si_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who); -void jabber_si_xfer_send(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file); - -void jabber_si_xfer_register(GTypeModule *module); - -void jabber_si_init(void); -void jabber_si_uninit(void); - -G_END_DECLS - -#endif /* PURPLE_JABBER_SI_H */
--- a/libpurple/protocols/jabber/tests/meson.build Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -foreach prog : ['caps', 'digest_md5', 'scram', 'jutil'] - e = executable( - f'test_jabber_@prog@', f'test_jabber_@prog@.c', - link_with : [jabber_prpl], - dependencies : [libxml, libpurple_dep, libsoup, glib]) - - jabberenv = environment() - jabberenv.set('XDG_CONFIG_DIR', meson.current_build_dir() / 'config') - - test(f'jabber_@prog@', e, env: jabberenv) -endforeach
--- a/libpurple/protocols/jabber/tests/test_jabber_caps.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -#include <glib.h> - -#include <purple.h> - -#include "protocols/jabber/caps.h" - -static void -test_jabber_caps_parse_invalid_nodes(void) { - PurpleXmlNode *query; - - g_assert_null(jabber_caps_parse_client_info(NULL)); - - /* Something other than a disco#info query */ - query = purple_xmlnode_new("foo"); - g_assert_null(jabber_caps_parse_client_info(query)); - purple_xmlnode_free(query); - - query = purple_xmlnode_new("query"); - g_assert_null(jabber_caps_parse_client_info(query)); - - purple_xmlnode_set_namespace(query, "jabber:iq:last"); - g_assert_null(jabber_caps_parse_client_info(query)); - purple_xmlnode_free(query); -} - -static void -_test_jabber_caps_match(GChecksumType hash_type, const gchar *in, const gchar *expected) { - PurpleXmlNode *query = purple_xmlnode_from_str(in, -1); - JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); - gchar *got = NULL; - - got = jabber_caps_calculate_hash(info, hash_type); - - g_assert_cmpstr(expected, ==, got); - g_free(got); - jabber_caps_client_info_destroy(info); - purple_xmlnode_free(query); -} - -static void -test_jabber_caps_calculate_from_xmlnode(void) { - _test_jabber_caps_match( - G_CHECKSUM_SHA1, - "<query xmlns='http://jabber.org/protocol/disco#info' node='http://tkabber.jabber.ru/#GNjxthSckUNvAIoCCJFttjl6VL8='><identity category='client' type='pc' name='Tkabber'/><x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='software'><value>Tkabber</value></field><field var='software_version'><value> ( 8.5.5 )</value></field><field var='os'><value>ATmega640-16AU</value></field><field var='os_version'><value/></field></x><feature var='games:board'/><feature var='http://jabber.org/protocol/activity'/><feature var='http://jabber.org/protocol/bytestreams'/><feature var='http://jabber.org/protocol/chatstates'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/commands'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='http://jabber.org/protocol/disco#items'/><feature var='http://jabber.org/protocol/feature-neg'/><feature var='http://jabber.org/protocol/geoloc'/><feature var='http://jabber.org/protocol/ibb'/><feature var='http://jabber.org/protocol/iqibb'/><feature var='http://jabber.org/protocol/muc'/><feature var='http://jabber.org/protocol/mute#ancestor'/><feature var='http://jabber.org/protocol/mute#editor'/><feature var='http://jabber.org/protocol/rosterx'/><feature var='http://jabber.org/protocol/si'/><feature var='http://jabber.org/protocol/si/profile/file-transfer'/><feature var='jabber:iq:avatar'/><feature var='jabber:iq:browse'/><feature var='jabber:iq:dtcp'/><feature var='jabber:iq:filexfer'/><feature var='jabber:iq:ibb'/><feature var='jabber:iq:inband'/><feature var='jabber:iq:jidlink'/><feature var='jabber:iq:last'/><feature var='jabber:iq:oob'/><feature var='jabber:iq:privacy'/><feature var='jabber:iq:time'/><feature var='jabber:iq:version'/><feature var='jabber:x:data'/><feature var='jabber:x:event'/><feature var='jabber:x:oob'/><feature var='urn:xmpp:ping'/><feature var='urn:xmpp:receipts'/><feature var='urn:xmpp:time'/></query>", - "sVNUrcvQ2ryBm4L+NRhXrRnd71c=" - ); -} - -gint -main(gint argc, gchar **argv) { - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/jabber/caps/parse invalid nodes", - test_jabber_caps_parse_invalid_nodes); - - g_test_add_func("/jabber/caps/calculate from xmlnode", - test_jabber_caps_calculate_from_xmlnode); - - return g_test_run(); -}
--- a/libpurple/protocols/jabber/tests/test_jabber_digest_md5.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -#include <glib.h> - -#include <purple.h> - -#include "protocols/jabber/auth_digest_md5.h" -#include "protocols/jabber/jutil.h" - -static void -test_jabber_digest_md5_parsing(void) { - GHashTable *table; - const gchar *value = NULL; - - #define check_value(name, expected) G_STMT_START {\ - value = g_hash_table_lookup(table, (name)); \ - g_assert_nonnull(value); \ - g_assert_cmpstr((expected), ==, value); \ - } G_STMT_END - - table = jabber_auth_digest_md5_parse("r=\"realm\",token= \" asdf\""); - check_value("r", "realm"); - check_value("token", "asdf"); - g_hash_table_destroy(table); - - table = jabber_auth_digest_md5_parse("r=\"a\", token= \" asdf\""); - check_value("r", "a"); - check_value("token", "asdf"); - g_hash_table_destroy(table); - - table = jabber_auth_digest_md5_parse("r=\"\", token= \" asdf\""); - check_value("r", ""); - check_value("token", "asdf"); - g_hash_table_destroy(table); - - table = jabber_auth_digest_md5_parse("realm=\"somerealm\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",charset=utf-8,algorithm=md5-sess"); - check_value("realm", "somerealm"); - check_value("nonce", "OA6MG9tEQGm2hh"); - check_value("qop", "auth"); - check_value("charset", "utf-8"); - check_value("algorithm", "md5-sess"); - g_hash_table_destroy(table); -} - -gint -main(gint argc, gchar **argv) { - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/jabber/digest/md5/parsing", - test_jabber_digest_md5_parsing); - - return g_test_run(); -}
--- a/libpurple/protocols/jabber/tests/test_jabber_jutil.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,290 +0,0 @@ -#include <glib.h> - -#include <purple.h> - -#include "protocols/jabber/jutil.h" - -PurpleTestStringData test_jabber_util_get_resource_exists_data[] = { - {"foo@bar/baz", "baz"}, - {"bar/baz", "baz"}, - {"foo@bar/baz/bat", "baz/bat"}, - {"bar/baz/bat", "baz/bat"}, - {NULL, NULL}, -}; -static void -test_jabber_util_get_resource_exists(const PurpleTestStringData *data) -{ - char *resource; - - resource = jabber_get_resource(data->input); - g_assert_cmpstr(data->output, ==, resource); - g_free(resource); -} - -PurpleTestStringData test_jabber_util_get_resource_none_data[] = { - {"foo@bar", NULL}, - {"bar", NULL}, - {NULL, NULL}, -}; -static void -test_jabber_util_get_resource_none(const PurpleTestStringData *data) -{ - g_assert_cmpstr(data->output, ==, jabber_get_resource(data->input)); -} - -PurpleTestStringData test_jabber_util_get_bare_jid_data[] = { - {"foo@bar", "foo@bar"}, - {"foo@bar/baz", "foo@bar"}, - {"bar", "bar"}, - {"bar/baz", "bar"}, - {NULL, NULL}, -}; -static void -test_jabber_util_get_bare_jid(const PurpleTestStringData *data) -{ - char *bare_jid; - - bare_jid = jabber_get_bare_jid(data->input); - g_assert_cmpstr(data->output, ==, bare_jid); - g_free(bare_jid); -} - -static void -test_jabber_util_nodeprep_validate(void) { - const gchar *data[] = { - "foo", - "%d", - "y\\z", - "a=", - "a,", - NULL, - }; - gchar *longnode; - gint i; - - for(i = 0; data[i]; i++) { - g_assert_true(jabber_nodeprep_validate(data[i])); - } - - longnode = g_strnfill(1023, 'a'); - g_assert_true(jabber_nodeprep_validate(longnode)); - g_free(longnode); - - longnode = g_strnfill(1024, 'a'); - g_assert_false(jabber_nodeprep_validate(longnode)); - g_free(longnode); -} - -const gchar *test_jabber_util_nodeprep_validate_illegal_chars_data[] = { - "don't", - "m@ke", - "\"me\"", - "&ngry", - "c:", - "a/b", - "4>2", - "4<7", - NULL, -}; -static void -test_jabber_util_nodeprep_validate_illegal_chars(const gchar *data) -{ - g_assert_false(jabber_nodeprep_validate(data)); -} - -static void -test_jabber_util_nodeprep_validate_too_long(void) { - gchar *longnode = g_strnfill(1024, 'a'); - - g_assert_false(jabber_nodeprep_validate(longnode)); - - g_free(longnode); -} - -const gchar *test_jabber_util_jabber_id_new_valid_data[] = { - "gmail.com", - "gmail.com/Test", - "gmail.com/Test@", - "gmail.com/@", - "gmail.com/Test@alkjaweflkj", - "noone@example.com", - "noone@example.com/Test12345", - "noone@example.com/Test@12345", - "noone@example.com/Te/st@12@//345", - "わいど@conference.jabber.org", - "まりるーむ@conference.jabber.org", - "noone@example.com/まりるーむ", - "noone@example/stuff.org", - "noone@nödåtXäYZ.example", - "noone@nödåtXäYZ.example/まりるーむ", - "noone@わいど.org", - "noone@まつ.おおかみ.net", - "noone@310.0.42.230/s", - "noone@[::1]", /* IPv6 */ - "noone@[3001:470:1f05:d58::2]", - "noone@[3001:470:1f05:d58::2]/foo", - "no=one@310.0.42.230", - "no,one@310.0.42.230", - NULL, -}; -static void -test_jabber_util_jabber_id_new_valid(const gchar *data) -{ - JabberID *jid = jabber_id_new(data); - - g_assert_nonnull(jid); - - jabber_id_free(jid); -} - -const gchar *test_jabber_util_jabber_id_new_invalid_data[] = { - "@gmail.com", - "@@gmail.com", - "noone@@example.com/Test12345", - "no@one@example.com/Test12345", - "@example.com/Test@12345", - "/Test@12345", - "noone@", - "noone/", - "noone@gmail_stuff.org", - "noone@gmail[stuff.org", - "noone@gmail\\stuff.org", - "noone@[::1]124", - "noone@2[::1]124/as", - "noone@まつ.おおかみ/\x01", - /* - * RFC 3454 Section 6 reads, in part, - * "If a string contains any RandALCat character, the - * string MUST NOT contain any LCat character." - * The character is U+066D (ARABIC FIVE POINTED STAR). - */ - "foo@example.com/٭simplexe٭", - NULL, -}; -static void -test_jabber_util_jabber_id_new_invalid(const gchar *jid) -{ - g_assert_null(jabber_id_new(jid)); -} - -#define assert_jid_parts(expect_node, expect_domain, str) G_STMT_START { \ - JabberID *jid = jabber_id_new(str); \ - g_assert_nonnull(jid); \ - g_assert_nonnull(jid->node); \ - g_assert_nonnull(jid->domain); \ - g_assert_null(jid->resource); \ - g_assert_cmpstr(expect_node, ==, jid->node); \ - g_assert_cmpstr(expect_domain, ==, jid->domain); \ - jabber_id_free(jid); \ -} G_STMT_END - - -static void -test_jabber_util_jid_parts(void) { - /* Ensure that jabber_id_new is properly lowercasing node and domains */ - assert_jid_parts("noone", "example.com", "NoOne@example.com"); - assert_jid_parts("noone", "example.com", "noone@ExaMPle.CoM"); - - /* These case-mapping tests culled from examining RFC3454 B.2 */ - - /* Cyrillic capital EF (U+0424) maps to lowercase EF (U+0444) */ - assert_jid_parts("ф", "example.com", "Ф@example.com"); - - /* - * These character (U+A664 and U+A665) are not mapped to anything in - * RFC3454 B.2. This first test *fails* when not using IDN because glib's - * case-folding/utf8_strdown improperly (for XMPP) lowercases the character. - * - * This is known, but not (very?) likely to actually cause a problem, so - * this test is commented out when using glib's functions. - */ - assert_jid_parts("Ꙥ", "example.com", "Ꙥ@example.com"); - assert_jid_parts("ꙥ", "example.com", "ꙥ@example.com"); - - /* U+04E9 to U+04E9 */ - assert_jid_parts("noone", "өexample.com", "noone@Өexample.com"); -} - -PurpleTestStringData test_jabber_util_jabber_normalize_data[] = { - {"NoOnE@ExAMplE.com", "noone@example.com"}, - {"NoOnE@ExampLE.cOM/", "noone@example.com"}, - {"NoONe@exAMPle.CoM/resource", "noone@example.com"}, - {NULL, NULL}, -}; -static void -test_jabber_util_jabber_normalize(const PurpleTestStringData *data) -{ - g_assert_cmpstr(data->output, ==, jabber_normalize(NULL, data->input)); -} - -gint -main(gint argc, gchar **argv) { - gchar *test_name; - gint i; - g_test_init(&argc, &argv, NULL); - g_test_set_nonfatal_assertions(); - - for (i = 0; test_jabber_util_get_resource_exists_data[i].input; i++) { - test_name = g_strdup_printf("/jabber/util/get_resource/exists/%d", i); - g_test_add_data_func( - test_name, &test_jabber_util_get_resource_exists_data[i], - (GTestDataFunc)test_jabber_util_get_resource_exists); - g_free(test_name); - } - for (i = 0; test_jabber_util_get_resource_none_data[i].input; i++) { - test_name = g_strdup_printf("/jabber/util/get_resource/none/%d", i); - g_test_add_data_func(test_name, - &test_jabber_util_get_resource_none_data[i], - (GTestDataFunc)test_jabber_util_get_resource_none); - g_free(test_name); - } - - for (i = 0; test_jabber_util_get_bare_jid_data[i].input; i++) { - test_name = g_strdup_printf("/jabber/util/get_bare_jid/%d", i); - g_test_add_data_func(test_name, &test_jabber_util_get_bare_jid_data[i], - (GTestDataFunc)test_jabber_util_get_bare_jid); - g_free(test_name); - } - - g_test_add_func("/jabber/util/nodeprep/validate/valid", - test_jabber_util_nodeprep_validate); - for (i = 0; test_jabber_util_nodeprep_validate_illegal_chars_data[i]; i++) { - test_name = g_strdup_printf( - "/jabber/util/nodeprep/validate/illegal_chars/%d", i); - g_test_add_data_func( - test_name, - test_jabber_util_nodeprep_validate_illegal_chars_data[i], - (GTestDataFunc) - test_jabber_util_nodeprep_validate_illegal_chars); - g_free(test_name); - } - g_test_add_func("/jabber/util/nodeprep/validate/too_long", - test_jabber_util_nodeprep_validate_too_long); - - for (i = 0; test_jabber_util_jabber_id_new_valid_data[i]; i++) { - test_name = g_strdup_printf("/jabber/util/id_new/valid/%d", i); - g_test_add_data_func( - test_name, test_jabber_util_jabber_id_new_valid_data[i], - (GTestDataFunc)test_jabber_util_jabber_id_new_valid); - g_free(test_name); - } - for (i = 0; test_jabber_util_jabber_id_new_invalid_data[i]; i++) { - test_name = g_strdup_printf("/jabber/util/id_new/invalid/%d", i); - g_test_add_data_func( - test_name, test_jabber_util_jabber_id_new_invalid_data[i], - (GTestDataFunc)test_jabber_util_jabber_id_new_invalid); - g_free(test_name); - } - g_test_add_func("/jabber/util/id_new/jid_parts", - test_jabber_util_jid_parts); - - for (i = 0; test_jabber_util_jabber_normalize_data[i].input; i++) { - test_name = g_strdup_printf("/jabber/util/normalize/%d", i); - g_test_add_data_func(test_name, - &test_jabber_util_jabber_normalize_data[i], - (GTestDataFunc)test_jabber_util_jabber_normalize); - g_free(test_name); - } - - return g_test_run(); -}
--- a/libpurple/protocols/jabber/tests/test_jabber_scram.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -#include <string.h> - -#include <purple.h> - -#include "protocols/jabber/auth_scram.h" -#include "protocols/jabber/jutil.h" - -static JabberScramHash sha1_mech = { "-SHA-1", G_CHECKSUM_SHA1 }; - -#define assert_pbkdf2_equal(password, salt, count, expected) { \ - GString *p = g_string_new(password); \ - GString *s = g_string_new(salt); \ - guchar *result = jabber_scram_hi(&sha1_mech, p, s, count); \ - g_assert_nonnull(result); \ - g_assert_cmpmem(result, 20, expected, 20); \ - g_string_free(s, TRUE); \ - g_string_free(p, TRUE); \ - g_free(result); \ -} - -static void -test_jabber_scram_pbkdf2(void) { - assert_pbkdf2_equal("password", "salt", 1, "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6"); - assert_pbkdf2_equal("password", "salt", 2, "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57"); - assert_pbkdf2_equal("password", "salt", 4096, "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1"); - /* This test is insane and takes forever, so it's disabled */ - /* - assert_pbkdf2_equal("password", "salt", 16777216, "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84"); - */ -} - -static void -test_jabber_scram_proofs(void) { - JabberScramData *data = g_new0(JabberScramData, 1); - gboolean ret; - GString *salt; - const char *client_proof; -/* const char *server_signature; */ - - data->hash = &sha1_mech; - data->password = g_strdup("password"); - data->auth_message = g_string_new("n=username@jabber.org,r=8jLxB5515dhFxBil5A0xSXMH," - "r=8jLxB5515dhFxBil5A0xSXMHabc,s=c2FsdA==,i=1," - "c=biws,r=8jLxB5515dhFxBil5A0xSXMHabc"); - client_proof = "\x48\x61\x30\xa5\x61\x0b\xae\xb9\xe4\x11\xa8\xfd\xa5\xcd\x34\x1d\x8a\x3c\x28\x17"; - - salt = g_string_new("salt"); - ret = jabber_scram_calc_proofs(data, salt, 1); - g_assert_true(ret); - - g_assert_cmpmem(client_proof, 20, data->client_proof->str, 20); - g_string_free(salt, TRUE); - - jabber_scram_data_destroy(data); -} - -#define assert_successful_exchange(pw, nonce, start_data, challenge1, response1, success) { \ - JabberScramData *data = g_new0(JabberScramData, 1); \ - gboolean ret; \ - gchar *out; \ - \ - data->step = 1; \ - data->hash = &sha1_mech; \ - data->password = jabber_saslprep(pw); \ - g_assert_nonnull(data->password); \ - data->cnonce = g_strdup(nonce); \ - data->auth_message = g_string_new(start_data); \ - \ - ret = jabber_scram_feed_parser(data, challenge1, &out); \ - g_assert_true(ret); \ - g_assert_cmpstr(response1, ==, out); \ - g_free(out); \ - \ - data->step = 2; \ - ret = jabber_scram_feed_parser(data, success, &out); \ - g_assert_true(ret); \ - g_assert_null(out); \ - \ - jabber_scram_data_destroy(data); \ -} - -static void -test_jabber_scram_exchange(void) { - assert_successful_exchange("password", "H7yDYKAWBCrM2Fa5SxGa4iez", - "n=paul,r=H7yDYKAWBCrM2Fa5SxGa4iez", - "r=H7yDYKAWBCrM2Fa5SxGa4iezFPVDPpDUcGxPkH3RzP,s=3rXeErP/os7jUNqU,i=4096", - "c=biws,r=H7yDYKAWBCrM2Fa5SxGa4iezFPVDPpDUcGxPkH3RzP,p=pXkak78EuwwOEwk2/h/OzD7NkEI=", - "v=ldX4EBNnOgDnNTOCmbSfBHAUCOs="); - - assert_successful_exchange("pass½word", "GNb2HsNI7VnTv8ABsE5AnY8W", - "n=paul,r=GNb2HsNI7VnTv8ABsE5AnY8W", - "r=GNb2HsNI7VnTv8ABsE5AnY8W/w/I3eRKM0I7jxFWOH,s=ysAriUjPzFqOXnMQ,i=4096", - "c=biws,r=GNb2HsNI7VnTv8ABsE5AnY8W/w/I3eRKM0I7jxFWOH,p=n/CtgdWjOYnLQ4m9Na+wPn9D2uY=", - "v=4TkZwKWy6JHNmrUbU2+IdAaXtos="); -} - -gint -main(gint argc, gchar **argv) { - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/jabber/scram/pbkdf2", - test_jabber_scram_pbkdf2); - g_test_add_func("/jabber/scram/proofs", - test_jabber_scram_proofs); - g_test_add_func("/jabber/scram/exchange", - test_jabber_scram_exchange); - - return g_test_run(); -}
--- a/libpurple/protocols/jabber/useravatar.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,361 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purpleconfig.h> - -#include <glib/gi18n-lib.h> - -#include <purple.h> -#include "libpurple/glibcompat.h" - -#include <libsoup/soup.h> - -#include "useravatar.h" -#include "pep.h" - -#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024) - -static void update_buddy_metadata(JabberStream *js, const char *from, PurpleXmlNode *items); - -void jabber_avatar_init(void) -{ - jabber_add_feature(NS_AVATAR_1_1_METADATA, - jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature(NS_AVATAR_1_1_DATA, - jabber_pep_namespace_only_when_pep_enabled_cb); - - jabber_pep_register_handler(NS_AVATAR_1_1_METADATA, - update_buddy_metadata); -} - -void jabber_avatar_set(JabberStream *js, PurpleImage *img) -{ - PurpleXmlNode *publish, *metadata, *item; - - if (!js->pep) - return; - - if (!img) { - publish = purple_xmlnode_new("publish"); - purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA); - - item = purple_xmlnode_new_child(publish, "item"); - metadata = purple_xmlnode_new_child(item, "metadata"); - purple_xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA); - - /* publish */ - jabber_pep_publish(js, publish); - } else { - /* - * TODO: This is pretty gross. The Jabber protocol really shouldn't - * do voodoo to try to determine the image type, height - * and width. - */ - /* A PNG header, including the IHDR, but nothing else */ - /* ATTN: this is in network byte order! */ - const struct { - guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */ - struct { - guint32 length; /* must be 0x0d */ - guchar type[4]; /* must be 'I' 'H' 'D' 'R' */ - guint32 width; - guint32 height; - guchar bitdepth; - guchar colortype; - guchar compression; - guchar filter; - guchar interlace; - } ihdr; - } *png = NULL; - - if (purple_image_get_data_size(img) > sizeof(*png)) - png = purple_image_get_data(img); - - /* check if the data is a valid png file (well, at least to some extent) */ - if(png && png->signature[0] == 0x89 && - png->signature[1] == 0x50 && - png->signature[2] == 0x4e && - png->signature[3] == 0x47 && - png->signature[4] == 0x0d && - png->signature[5] == 0x0a && - png->signature[6] == 0x1a && - png->signature[7] == 0x0a && - GUINT32_FROM_BE(png->ihdr.length) == 0x0d && - png->ihdr.type[0] == 'I' && - png->ihdr.type[1] == 'H' && - png->ihdr.type[2] == 'D' && - png->ihdr.type[3] == 'R') { - /* parse PNG header to get the size of the image (yes, this is required) */ - guint32 width = GUINT32_FROM_BE(png->ihdr.width); - guint32 height = GUINT32_FROM_BE(png->ihdr.height); - PurpleXmlNode *data, *info; - char *lengthstring, *widthstring, *heightstring; - - /* compute the sha1 hash */ - char *hash = g_compute_checksum_for_data( - G_CHECKSUM_SHA1, - purple_image_get_data(img), - purple_image_get_data_size(img)); - char *base64avatar = g_base64_encode( - purple_image_get_data(img), - purple_image_get_data_size(img)); - - publish = purple_xmlnode_new("publish"); - purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA); - - item = purple_xmlnode_new_child(publish, "item"); - purple_xmlnode_set_attrib(item, "id", hash); - - data = purple_xmlnode_new_child(item, "data"); - purple_xmlnode_set_namespace(data, NS_AVATAR_1_1_DATA); - - purple_xmlnode_insert_data(data, base64avatar, -1); - /* publish the avatar itself */ - jabber_pep_publish(js, publish); - - g_free(base64avatar); - - lengthstring = g_strdup_printf("%" G_GSIZE_FORMAT, - purple_image_get_data_size(img)); - widthstring = g_strdup_printf("%u", width); - heightstring = g_strdup_printf("%u", height); - - /* publish the metadata */ - publish = purple_xmlnode_new("publish"); - purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA); - - item = purple_xmlnode_new_child(publish, "item"); - purple_xmlnode_set_attrib(item, "id", hash); - - metadata = purple_xmlnode_new_child(item, "metadata"); - purple_xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA); - - info = purple_xmlnode_new_child(metadata, "info"); - purple_xmlnode_set_attrib(info, "id", hash); - purple_xmlnode_set_attrib(info, "type", "image/png"); - purple_xmlnode_set_attrib(info, "bytes", lengthstring); - purple_xmlnode_set_attrib(info, "width", widthstring); - purple_xmlnode_set_attrib(info, "height", heightstring); - - jabber_pep_publish(js, publish); - - g_free(lengthstring); - g_free(widthstring); - g_free(heightstring); - g_free(hash); - } else { - purple_debug_error("jabber", "Cannot set PEP avatar to non-PNG data\n"); - } - } -} - -static void -do_got_own_avatar_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - PurpleXmlNode *items) -{ - PurpleXmlNode *item = NULL, *metadata = NULL, *info = NULL; - PurpleAccount *account = purple_connection_get_account(js->gc); - const char *server_hash = NULL; - - if (items && (item = purple_xmlnode_get_child(items, "item")) && - (metadata = purple_xmlnode_get_child(item, "metadata")) && - (info = purple_xmlnode_get_child(metadata, "info"))) { - server_hash = purple_xmlnode_get_attrib(info, "id"); - } - - /* - * If we have an avatar and the server returned an error/malformed data, - * push our avatar. If the server avatar doesn't match the local one, push - * our avatar. - */ - if ((!items || !metadata) || - !purple_strequal(server_hash, js->initial_avatar_hash)) { - PurpleImage *img = purple_buddy_icons_find_account_icon(account); - jabber_avatar_set(js, img); - if (img) - g_object_unref(img); - } -} - -void jabber_avatar_fetch_mine(JabberStream *js) -{ - if (js->initial_avatar_hash) { - jabber_pep_request_item(js, NULL, NS_AVATAR_1_1_METADATA, NULL, - do_got_own_avatar_cb); - } -} - -typedef struct { - JabberStream *js; - char *from; - char *id; - SoupMessage *msg; -} JabberBuddyAvatarUpdateURLInfo; - -static void -do_buddy_avatar_update_fromurl(GObject *source, GAsyncResult *result, - gpointer data) -{ - JabberBuddyAvatarUpdateURLInfo *info = data; - GBytes *response_body = NULL; - gpointer icon_data = NULL; - gsize length = 0; - GError *error = NULL; - const gchar *error_message = NULL; - - if(SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(info->msg))) { - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source), - result, &error); - error_message = error ? error->message : "unknown"; - } else { - error_message = soup_message_get_reason_phrase(info->msg); - } - if(response_body == NULL) { - purple_debug_error("jabber", - "do_buddy_avatar_update_fromurl got error \"%s\"", - error_message); - goto out; - } - - icon_data = g_bytes_unref_to_data(response_body, &length); - purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), - info->from, icon_data, length, info->id); - -out: - g_free(info->from); - g_free(info->id); - g_object_unref(info->msg); - g_free(info); - g_clear_error(&error); -} - -static void -do_buddy_avatar_update_data(JabberStream *js, const char *from, PurpleXmlNode *items) -{ - PurpleXmlNode *item, *data; - const char *checksum; - char *b64data; - void *img; - size_t size; - if(!items) - return; - - item = purple_xmlnode_get_child(items, "item"); - if(!item) - return; - - data = purple_xmlnode_get_child(item, "data"); - if(!data) - return; - - checksum = purple_xmlnode_get_attrib(item,"id"); - if(!checksum) - return; - - b64data = purple_xmlnode_get_data(data); - if(!b64data) - return; - - img = g_base64_decode(b64data, &size); - if(!img) { - g_free(b64data); - return; - } - - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); - g_free(b64data); -} - -static void -update_buddy_metadata(JabberStream *js, const char *from, PurpleXmlNode *items) -{ - PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(js->gc), from); - const char *checksum; - PurpleXmlNode *item, *metadata; - if(!buddy) - return; - - if (!items) - return; - - item = purple_xmlnode_get_child(items,"item"); - if (!item) - return; - - metadata = purple_xmlnode_get_child(item, "metadata"); - if(!metadata) - return; - - checksum = purple_buddy_icons_get_checksum_for_user(buddy); - - /* <stop/> was the pre-v1.1 method of publishing an empty avatar */ - if(purple_xmlnode_get_child(metadata, "stop")) { - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); - } else { - PurpleXmlNode *info, *goodinfo = NULL; - gboolean has_children = FALSE; - - /* iterate over all info nodes to get one we can use */ - for(info = metadata->child; info; info = info->next) { - if(info->type == PURPLE_XMLNODE_TYPE_TAG) - has_children = TRUE; - if(info->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(info->name,"info")) { - const char *type = purple_xmlnode_get_attrib(info,"type"); - const char *id = purple_xmlnode_get_attrib(info,"id"); - - if(checksum && id && purple_strequal(id, checksum)) { - /* we already have that avatar, so we don't have to do anything */ - goodinfo = NULL; - break; - } - /* We'll only pick the png one for now. It's a very nice image format anyways. */ - if(id && !goodinfo && purple_strequal(type, "image/png")) - goodinfo = info; - } - } - if(has_children == FALSE) { - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); - } else if(goodinfo) { - const char *url = purple_xmlnode_get_attrib(goodinfo, "url"); - const char *id = purple_xmlnode_get_attrib(goodinfo,"id"); - - /* the avatar might either be stored in a pep node, or on a HTTP(S) URL */ - if(!url) { - jabber_pep_request_item(js, from, NS_AVATAR_1_1_DATA, id, - do_buddy_avatar_update_data); - } else { - SoupMessage *msg; - JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); - info->js = js; - info->from = g_strdup(from); - info->id = g_strdup(id); - - info->msg = msg = soup_message_new("GET", url); - soup_session_send_and_read_async(js->http_conns, msg, - G_PRIORITY_DEFAULT, NULL, - do_buddy_avatar_update_fromurl, - info); - } - } - } -}
--- a/libpurple/protocols/jabber/useravatar.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_USERAVATAR_H -#define PURPLE_JABBER_USERAVATAR_H - -#include "jabber.h" - -#include <purple.h> - -/* Implementation of XEP-0084 */ - -void jabber_avatar_init(void); -void jabber_avatar_set(JabberStream *js, PurpleImage *img); - -void jabber_avatar_fetch_mine(JabberStream *js); - -#endif /* PURPLE_JABBER_USERAVATAR_H */
--- a/libpurple/protocols/jabber/usernick.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "usernick.h" -#include "pep.h" -#include <string.h> - -static void jabber_nick_cb(JabberStream *js, const char *from, PurpleXmlNode *items) { - /* it doesn't make sense to have more than one item here, so let's just pick the first one */ - PurpleXmlNode *item = purple_xmlnode_get_child(items, "item"); - JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE); - PurpleXmlNode *nick; - char *nickname = NULL; - - /* ignore the nick of people not on our buddy list */ - if (!buddy || !item) - return; - - nick = purple_xmlnode_get_child_with_namespace(item, "nick", "http://jabber.org/protocol/nick"); - if (!nick) - return; - nickname = purple_xmlnode_get_data(nick); - purple_serv_got_alias(js->gc, from, nickname); - g_free(nickname); -} - -static void do_nick_set(JabberStream *js, const char *nick) { - PurpleXmlNode *publish, *nicknode; - - publish = purple_xmlnode_new("publish"); - purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick"); - nicknode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "nick"); - purple_xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick"); - - if(nick && nick[0] != '\0') - purple_xmlnode_insert_data(nicknode, nick, -1); - - jabber_pep_publish(js, publish); - /* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free - (yay for well-defined memory management rules) */ -} - -static void -do_nick_got_own_nick_cb(JabberStream *js, G_GNUC_UNUSED const char *from, - PurpleXmlNode *items) -{ - char *oldnickname = NULL; - PurpleXmlNode *item = NULL; - - if (items) - item = purple_xmlnode_get_child(items,"item"); - - if(item) { - PurpleXmlNode *nick = purple_xmlnode_get_child_with_namespace(item,"nick","http://jabber.org/protocol/nick"); - if(nick) - oldnickname = purple_xmlnode_get_data(nick); - } - - purple_request_input(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."), - _("This information is visible to all contacts on your contact list, so choose something appropriate."), - oldnickname, FALSE, FALSE, NULL, _("Set"), G_CALLBACK(do_nick_set), _("Cancel"), NULL, - purple_request_cpar_from_connection(js->gc), js); - g_free(oldnickname); -} - -static void -do_nick_set_nick(G_GNUC_UNUSED GSimpleAction *action, GVariant *parameter, - G_GNUC_UNUSED gpointer data) -{ - const char *account_id = NULL; - PurpleAccountManager *manager = NULL; - PurpleAccount *account = NULL; - PurpleConnection *connection = NULL; - JabberStream *js = NULL; - - if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) { - g_critical("XMPP Set Nickname action parameter is of incorrect type %s", - g_variant_get_type_string(parameter)); - } - - account_id = g_variant_get_string(parameter, NULL); - manager = purple_account_manager_get_default(); - account = purple_account_manager_find_by_id(manager, account_id); - connection = purple_account_get_connection(account); - g_clear_object(&account); - js = purple_connection_get_protocol_data(connection); - - /* since the nickname might have been changed by another resource of this account, we always have to request the old one - from the server to present as the default for the new one */ - jabber_pep_request_item(js, NULL, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb); -} - -void jabber_nick_init(void) { - jabber_add_feature("http://jabber.org/protocol/nick", jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_pep_register_handler("http://jabber.org/protocol/nick", jabber_nick_cb); -} - -void -jabber_nick_add_action_entries(GSimpleActionGroup *group) { - GActionEntry entries[] = { - { - .name = "set-nickname", - .activate = do_nick_set_nick, - .parameter_type = "s", - }, - }; - gsize nentries = G_N_ELEMENTS(entries); - - g_action_map_add_action_entries(G_ACTION_MAP(group), entries, nentries, - NULL); -} - -void -jabber_nick_append_menu(GMenu *menu) { - GMenuItem *item = NULL; - - item = g_menu_item_new(_("Set Nickname..."), "prpl-xmpp.set-nickname"); - g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s", - "account"); - g_menu_append_item(menu, item); - g_object_unref(item); -}
--- a/libpurple/protocols/jabber/usernick.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_USERNICK_H -#define PURPLE_JABBER_USERNICK_H - -#include "jabber.h" - -/* Implementation of XEP-0172 */ - -void jabber_nick_init(void); -void jabber_nick_append_menu(GMenu *menu); -void jabber_nick_add_action_entries(GSimpleActionGroup *group); - -#endif /* PURPLE_JABBER_USERNICK_H */
--- a/libpurple/protocols/jabber/xdata.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,470 +0,0 @@ -/* - * purple - Jabber Protocol Plugin - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "xdata.h" - -typedef enum { - JABBER_X_DATA_IGNORE = 0, - JABBER_X_DATA_TEXT_SINGLE, - JABBER_X_DATA_TEXT_MULTI, - JABBER_X_DATA_LIST_SINGLE, - JABBER_X_DATA_LIST_MULTI, - JABBER_X_DATA_BOOLEAN, - JABBER_X_DATA_JID_SINGLE -} jabber_x_data_field_type; - -struct jabber_x_data_data { - GHashTable *fields; - GSList *values; - GCallback cb; - gpointer user_data; - JabberStream *js; - GList *actions; - PurpleRequestGroup *actiongroup; -}; - -static void -jabber_x_data_ok_cb(struct jabber_x_data_data *data, PurpleRequestPage *page) { - PurpleXmlNode *result = purple_xmlnode_new("x"); - GCallback cb = data->cb; - gpointer user_data = data->user_data; - JabberStream *js = data->js; - char *actionhandle = NULL; - gboolean hasActions = (data->actions != NULL); - guint n_groups; - - purple_xmlnode_set_namespace(result, "jabber:x:data"); - purple_xmlnode_set_attrib(result, "type", "submit"); - - n_groups = g_list_model_get_n_items(G_LIST_MODEL(page)); - for(guint group_index = 0; group_index < n_groups; group_index++) { - PurpleRequestGroup *group = NULL; - guint n_fields; - - group = g_list_model_get_item(G_LIST_MODEL(page), group_index); - n_fields = g_list_model_get_n_items(G_LIST_MODEL(group)); - - if(group == data->actiongroup) { - for(guint field_index = 0; field_index < n_fields; field_index++) { - PurpleRequestField *field = NULL; - PurpleRequestFieldChoice *choice = NULL; - const char *id = NULL; - int handleindex; - - field = g_list_model_get_item(G_LIST_MODEL(group), field_index); - choice = PURPLE_REQUEST_FIELD_CHOICE(field); - id = purple_request_field_get_id(field); - - if(!purple_strequal(id, "libpurple:jabber:xdata:actions")) { - g_object_unref(field); - continue; - } - - handleindex = GPOINTER_TO_INT(purple_request_field_choice_get_value(choice)); - actionhandle = g_strdup(g_list_nth_data(data->actions, handleindex)); - g_object_unref(field); - break; - } - - g_object_unref(group); - continue; - } - - for(guint field_index = 0; field_index < n_fields; field_index++) { - PurpleXmlNode *fieldnode, *valuenode; - PurpleRequestField *field = NULL; - const char *id = NULL; - jabber_x_data_field_type type; - - field = g_list_model_get_item(G_LIST_MODEL(group), field_index); - id = purple_request_field_get_id(field); - type = GPOINTER_TO_INT(g_hash_table_lookup(data->fields, id)); - - switch(type) { - case JABBER_X_DATA_TEXT_SINGLE: - case JABBER_X_DATA_JID_SINGLE: - { - PurpleRequestFieldString *sfield = PURPLE_REQUEST_FIELD_STRING(field); - const char *value = purple_request_field_string_get_value(sfield); - if (value == NULL) - break; - fieldnode = purple_xmlnode_new_child(result, "field"); - purple_xmlnode_set_attrib(fieldnode, "var", id); - valuenode = purple_xmlnode_new_child(fieldnode, "value"); - purple_xmlnode_insert_data(valuenode, value, -1); - break; - } - case JABBER_X_DATA_TEXT_MULTI: - { - PurpleRequestFieldString *sfield = PURPLE_REQUEST_FIELD_STRING(field); - const char *value = purple_request_field_string_get_value(sfield); - char **pieces, **p; - if (value == NULL) - break; - fieldnode = purple_xmlnode_new_child(result, "field"); - purple_xmlnode_set_attrib(fieldnode, "var", id); - - pieces = g_strsplit(value, "\n", -1); - for(p = pieces; *p != NULL; p++) { - valuenode = purple_xmlnode_new_child(fieldnode, "value"); - purple_xmlnode_insert_data(valuenode, *p, -1); - } - g_strfreev(pieces); - } - break; - case JABBER_X_DATA_LIST_SINGLE: - case JABBER_X_DATA_LIST_MULTI: - { - PurpleRequestFieldList *lfield = PURPLE_REQUEST_FIELD_LIST(field); - GList *selected = purple_request_field_list_get_selected(lfield); - char *value; - fieldnode = purple_xmlnode_new_child(result, "field"); - purple_xmlnode_set_attrib(fieldnode, "var", id); - - while(selected) { - value = purple_request_field_list_get_data(lfield, selected->data); - valuenode = purple_xmlnode_new_child(fieldnode, "value"); - if(value) - purple_xmlnode_insert_data(valuenode, value, -1); - selected = selected->next; - } - } - break; - case JABBER_X_DATA_BOOLEAN: - { - PurpleRequestFieldBool *bfield = PURPLE_REQUEST_FIELD_BOOL(field); - fieldnode = purple_xmlnode_new_child(result, "field"); - purple_xmlnode_set_attrib(fieldnode, "var", id); - valuenode = purple_xmlnode_new_child(fieldnode, "value"); - if(purple_request_field_bool_get_value(bfield)) { - purple_xmlnode_insert_data(valuenode, "1", -1); - } else { - purple_xmlnode_insert_data(valuenode, "0", -1); - } - } - break; - case JABBER_X_DATA_IGNORE: - break; - } - - g_object_unref(field); - } - - g_object_unref(group); - } - - g_hash_table_destroy(data->fields); - g_slist_free_full(data->values, g_free); - g_list_free_full(data->actions, g_free); - g_free(data); - - if(hasActions) { - ((jabber_x_data_action_cb)cb)(js, result, actionhandle, user_data); - } else { - ((jabber_x_data_cb)cb)(js, result, user_data); - } - - g_free(actionhandle); -} - -static void -jabber_x_data_cancel_cb(struct jabber_x_data_data *data, - G_GNUC_UNUSED PurpleRequestPage *page) -{ - PurpleXmlNode *result = purple_xmlnode_new("x"); - GCallback cb = data->cb; - gpointer user_data = data->user_data; - JabberStream *js = data->js; - gboolean hasActions = (data->actions != NULL); - g_hash_table_destroy(data->fields); - g_slist_free_full(data->values, g_free); - g_list_free_full(data->actions, g_free); - g_free(data); - - purple_xmlnode_set_namespace(result, "jabber:x:data"); - purple_xmlnode_set_attrib(result, "type", "cancel"); - - if(hasActions) { - ((jabber_x_data_action_cb)cb)(js, result, NULL, user_data); - } else { - ((jabber_x_data_cb)cb)(js, result, user_data); - } -} - -void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data) -{ - return jabber_x_data_request_with_actions(js, packet, NULL, 0, - (jabber_x_data_action_cb)(GCallback)cb, - user_data); -} - -void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data) -{ - void *handle; - PurpleXmlNode *fn, *x; - PurpleRequestPage *page; - PurpleRequestGroup *group; - PurpleRequestField *field = NULL; - - char *title = NULL; - char *instructions = NULL; - - struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1); - - data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - data->user_data = user_data; - data->cb = G_CALLBACK(cb); - data->js = js; - - page = purple_request_page_new(); - group = purple_request_group_new(NULL); - purple_request_page_add_group(page, group); - - for(fn = purple_xmlnode_get_child(packet, "field"); fn; fn = purple_xmlnode_get_next_twin(fn)) { - PurpleXmlNode *valuenode; - const char *type = purple_xmlnode_get_attrib(fn, "type"); - const char *label = purple_xmlnode_get_attrib(fn, "label"); - const char *var = purple_xmlnode_get_attrib(fn, "var"); - char *value = NULL; - - if(!type) - type = "text-single"; - - if(!var && !purple_strequal(type, "fixed")) - continue; - if(!label) - label = var; - - if(purple_strequal(type, "text-private")) { - if((valuenode = purple_xmlnode_get_child(fn, "value"))) - value = purple_xmlnode_get_data(valuenode); - - field = purple_request_field_string_new(var, label, - value ? value : "", FALSE); - purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field), - TRUE); - purple_request_group_add_field(group, field); - - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); - - g_free(value); - } else if(purple_strequal(type, "text-multi") || purple_strequal(type, "jid-multi")) { - GString *str = g_string_new(""); - - for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode; - valuenode = purple_xmlnode_get_next_twin(valuenode)) { - - if(!(value = purple_xmlnode_get_data(valuenode))) - continue; - - g_string_append_printf(str, "%s\n", value); - g_free(value); - } - - field = purple_request_field_string_new(var, label, - str->str, TRUE); - purple_request_group_add_field(group, field); - - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_MULTI)); - - g_string_free(str, TRUE); - } else if(purple_strequal(type, "list-single") || purple_strequal(type, "list-multi")) { - PurpleRequestFieldList *list_field = NULL; - PurpleXmlNode *optnode; - GList *selected = NULL; - - field = purple_request_field_list_new(var, label); - list_field = PURPLE_REQUEST_FIELD_LIST(field); - - if(purple_strequal(type, "list-multi")) { - purple_request_field_list_set_multi_select(list_field, TRUE); - g_hash_table_replace(data->fields, g_strdup(var), - GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI)); - } else { - g_hash_table_replace(data->fields, g_strdup(var), - GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE)); - } - - for(valuenode = purple_xmlnode_get_child(fn, "value"); valuenode; - valuenode = purple_xmlnode_get_next_twin(valuenode)) { - char *data = purple_xmlnode_get_data(valuenode); - if (data != NULL) { - selected = g_list_prepend(selected, data); - } - } - - for(optnode = purple_xmlnode_get_child(fn, "option"); optnode; - optnode = purple_xmlnode_get_next_twin(optnode)) { - const char *lbl; - - if(!(valuenode = purple_xmlnode_get_child(optnode, "value"))) - continue; - - if(!(value = purple_xmlnode_get_data(valuenode))) - continue; - - if(!(lbl = purple_xmlnode_get_attrib(optnode, "label"))) - lbl = value; - - data->values = g_slist_prepend(data->values, value); - - purple_request_field_list_add_icon(list_field, lbl, NULL, value); - if(g_list_find_custom(selected, value, (GCompareFunc)strcmp)) { - purple_request_field_list_add_selected(list_field, lbl); - } - } - purple_request_group_add_field(group, field); - - g_list_free_full(selected, g_free); - } else if(purple_strequal(type, "boolean")) { - gboolean def = FALSE; - - if((valuenode = purple_xmlnode_get_child(fn, "value"))) - value = purple_xmlnode_get_data(valuenode); - - if(value && (!g_ascii_strcasecmp(value, "yes") || - !g_ascii_strcasecmp(value, "true") || !g_ascii_strcasecmp(value, "1"))) - def = TRUE; - - field = purple_request_field_bool_new(var, label, def); - purple_request_group_add_field(group, field); - - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN)); - - g_free(value); - } else if(purple_strequal(type, "fixed")) { - if((valuenode = purple_xmlnode_get_child(fn, "value"))) - value = purple_xmlnode_get_data(valuenode); - - if(value != NULL) { - field = purple_request_field_label_new("", value); - purple_request_group_add_field(group, field); - - g_free(value); - } - } else if(purple_strequal(type, "hidden")) { - if((valuenode = purple_xmlnode_get_child(fn, "value"))) - value = purple_xmlnode_get_data(valuenode); - - field = purple_request_field_string_new(var, "", value ? value : "", - FALSE); - purple_request_field_set_visible(field, FALSE); - purple_request_group_add_field(group, field); - - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); - - g_free(value); - } else { /* text-single, jid-single, and the default */ - if((valuenode = purple_xmlnode_get_child(fn, "value"))) - value = purple_xmlnode_get_data(valuenode); - - field = purple_request_field_string_new(var, label, - value ? value : "", FALSE); - purple_request_group_add_field(group, field); - - if(purple_strequal(type, "jid-single")) { - purple_request_field_set_type_hint(field, "screenname"); - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_JID_SINGLE)); - } else { - g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); - } - - g_free(value); - } - - if(field && purple_xmlnode_get_child(fn, "required")) - purple_request_field_set_required(field,TRUE); - } - - if(actions != NULL) { - PurpleRequestField *field = NULL; - PurpleRequestFieldChoice *choice = NULL; - GList *action; - int i; - - data->actiongroup = group = purple_request_group_new(_("Actions")); - purple_request_page_add_group(page, group); - field = purple_request_field_choice_new("libpurple:jabber:xdata:actions", - _("Select an action"), - GINT_TO_POINTER(defaultaction)); - choice = PURPLE_REQUEST_FIELD_CHOICE(field); - - for(i = 0, action = actions; action; action = g_list_next(action), i++) { - JabberXDataAction *a = action->data; - - purple_request_field_choice_add(choice, a->name, GINT_TO_POINTER(i)); - data->actions = g_list_append(data->actions, g_strdup(a->handle)); - } - purple_request_field_set_required(field, TRUE); - purple_request_group_add_field(group, field); - } - - if((x = purple_xmlnode_get_child(packet, "title"))) - title = purple_xmlnode_get_data(x); - - if((x = purple_xmlnode_get_child(packet, "instructions"))) - instructions = purple_xmlnode_get_data(x); - - handle = purple_request_fields(js->gc, title, title, instructions, page, - _("OK"), G_CALLBACK(jabber_x_data_ok_cb), - _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb), - purple_request_cpar_from_connection(js->gc), - data); - - g_free(title); - g_free(instructions); - - return handle; -} - -gchar * -jabber_x_data_get_formtype(const PurpleXmlNode *form) -{ - PurpleXmlNode *field; - - g_return_val_if_fail(form != NULL, NULL); - - for (field = purple_xmlnode_get_child((PurpleXmlNode *)form, "field"); field; - field = purple_xmlnode_get_next_twin(field)) { - const char *var = purple_xmlnode_get_attrib(field, "var"); - if (purple_strequal(var, "FORM_TYPE")) { - PurpleXmlNode *value = purple_xmlnode_get_child(field, "value"); - if (value) - return purple_xmlnode_get_data(value); - else - /* An interesting corner case... Looking for a second - * FORM_TYPE would be more considerate, but I'm in favor - * of not helping broken clients. - */ - return NULL; - } - } - - /* Erm, none found :( */ - return NULL; -} -
--- a/libpurple/protocols/jabber/xdata.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * @file xdata.h utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_JABBER_XDATA_H -#define PURPLE_JABBER_XDATA_H - -#include "jabber.h" - -#include <purple.h> - -typedef struct { - char *name; - char *handle; -} JabberXDataAction; - -typedef void (*jabber_x_data_cb)(JabberStream *js, PurpleXmlNode *result, gpointer user_data); -typedef void (*jabber_x_data_action_cb)(JabberStream *js, PurpleXmlNode *result, const char *actionhandle, gpointer user_data); -void *jabber_x_data_request(JabberStream *js, PurpleXmlNode *packet, jabber_x_data_cb cb, gpointer user_data); -void *jabber_x_data_request_with_actions(JabberStream *js, PurpleXmlNode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data); - -/* - * Return the form type (the CDATA of the value child of the FORM_TYPE - * field entry. - * E.g., for the following, "http://jabber.org/protocol/muc#roominfo". - * <x xmlns='jabber:x:data' type='result'> - * <field var='FORM_TYPE' type='hidden'> - * <value>http://jabber.org/protocol/muc#roominfo</value> - * </field> - * </x> - * - * @param form The PurpleXmlNode for the form (the 'x' element) - * @returns The FORM_TYPE. Must be freed by caller. - */ -gchar *jabber_x_data_get_formtype(const PurpleXmlNode *form); - -#endif /* PURPLE_JABBER_XDATA_H */
--- a/libpurple/protocols/jabber/xmpp.c Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include <purpleconfig.h> - -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include "xmpp.h" - -struct _XMPPProtocol { - JabberProtocol parent; -}; - -static GList * -xmpp_protocol_get_account_options(G_GNUC_UNUSED PurpleProtocol *protocol) { - PurpleAccountOption *option; - PurpleKeyValuePair *kvp = NULL; - GList *opts = NULL, *encryption_values = NULL; - - /* build the list of encryption types we support */ - kvp = purple_key_value_pair_new(_("Require encryption"), "require_tls"); - encryption_values = g_list_append(encryption_values, kvp); - - kvp = purple_key_value_pair_new(_("Use encryption if available"), - "opportunistic_tls"); - encryption_values = g_list_append(encryption_values, kvp); - - kvp = purple_key_value_pair_new(_("Use old-style SSL"), "old_ssl"); - encryption_values = g_list_append(encryption_values, kvp); - - /* build all the options */ - option = purple_account_option_list_new(_("Connection security"), - "connection_security", - encryption_values); - opts = g_list_append(opts, option); - - option = purple_account_option_bool_new(_("Allow plaintext auth over " - "unencrypted streams"), - "auth_plain_in_clear", FALSE); - opts = g_list_append(opts, option); - - option = purple_account_option_int_new(_("Connect port"), "port", 5222); - opts = g_list_append(opts, option); - - option = purple_account_option_string_new(_("Connect server"), - "connect_server", NULL); - opts = g_list_append(opts, option); - - option = purple_account_option_string_new(_("File transfer proxies"), - "ft_proxies", NULL); - opts = g_list_append(opts, option); - - option = purple_account_option_string_new(_("BOSH URL"), "bosh_url", NULL); - opts = g_list_append(opts, option); - - return opts; -} - -static GList * -xmpp_protocol_get_user_splits(G_GNUC_UNUSED PurpleProtocol *protocol) { - GList *splits = NULL; - PurpleAccountUserSplit *split; - - /* Translators: 'domain' is used here in the context of Internet domains, - * e.g. pidgin.im. - */ - split = purple_account_user_split_new(_("Domain"), NULL, '@'); - purple_account_user_split_set_reverse(split, FALSE); - splits = g_list_append(splits, split); - - split = purple_account_user_split_new(_("Resource"), "", '/'); - purple_account_user_split_set_reverse(split, FALSE); - splits = g_list_append(splits, split); - - return splits; -} - -static void -xmpp_protocol_init(G_GNUC_UNUSED XMPPProtocol *self) { - purple_prefs_remove("/plugins/prpl/jabber"); -} - -static void -xmpp_protocol_class_init(XMPPProtocolClass *klass) { - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); - - protocol_class->get_account_options = xmpp_protocol_get_account_options; - protocol_class->get_user_splits = xmpp_protocol_get_user_splits; -} - -static void -xmpp_protocol_class_finalize(G_GNUC_UNUSED XMPPProtocolClass *klass) -{ -} - -G_DEFINE_DYNAMIC_TYPE_EXTENDED(XMPPProtocol, xmpp_protocol, - JABBER_TYPE_PROTOCOL, G_TYPE_FLAG_FINAL, {}) - -/* This exists solely because the above macro makes xmpp_protocol_register_type - * static. */ -void -xmpp_protocol_register(PurplePlugin *plugin) -{ - xmpp_protocol_register_type(G_TYPE_MODULE(plugin)); -} - -PurpleProtocol * -xmpp_protocol_new(void) { - PurpleProtocolOptions options; - - options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | - OPT_PROTO_MAIL_CHECK | OPT_PROTO_SLASH_COMMANDS_NATIVE | - OPT_PROTO_PASSWORD_OPTIONAL; - - return g_object_new( - XMPP_TYPE_PROTOCOL, - "id", XMPP_PROTOCOL_ID, - "name", "XMPP (Deprecated)", - "description", _("Extensible Messaging and Presence Protocol for IM, " - "voice, and video."), - "icon-name", "im-jabber", - "icon-resource-path", "/im/pidgin/libpurple/xmpp/icons", - "options", options, - NULL - ); -}
--- a/libpurple/protocols/jabber/xmpp.h Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_JABBER_XMPP_H -#define PURPLE_JABBER_XMPP_H - -#include "jabber.h" - -#define XMPP_PROTOCOL_ID "prpl-jabber" - -#define XMPP_TYPE_PROTOCOL (xmpp_protocol_get_type()) -G_DECLARE_FINAL_TYPE(XMPPProtocol, xmpp_protocol, XMPP, PROTOCOL, - JabberProtocol) - -/** - * Registers the XMPPProtocol type in the type system. - */ -G_GNUC_INTERNAL -void xmpp_protocol_register(PurplePlugin *plugin); - -G_GNUC_INTERNAL -PurpleProtocol *xmpp_protocol_new(void); - -/** - * Returns the GType for the XMPPProtocol object. - */ - -#endif /* PURPLE_JABBER_XMPP_H */
--- a/libpurple/protocols/meson.build Tue Apr 09 23:36:32 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -subdir('jabber')
--- a/meson.build Tue Apr 09 23:36:32 2024 -0500 +++ b/meson.build Wed Apr 10 00:05:21 2024 -0500 @@ -266,7 +266,7 @@ dependency('shoes', required : false) -DEFAULT_PRPLS = ['demo', 'ircv3', 'jabber', 'xmpp'] +DEFAULT_PRPLS = ['demo', 'ircv3', 'xmpp'] dynamic_list = get_option('dynamic-prpls').split(',') if dynamic_list == ['all'] @@ -285,7 +285,6 @@ DYNAMIC_DEMO = DYNAMIC_PRPLS.contains('demo') DYNAMIC_IRCV3 = DYNAMIC_PRPLS.contains('ircv3') -DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber') DYNAMIC_XMPP = DYNAMIC_PRPLS.contains('xmpp') add_project_arguments(
--- a/po/POTFILES.in Tue Apr 09 23:36:32 2024 -0500 +++ b/po/POTFILES.in Wed Apr 10 00:05:21 2024 -0500 @@ -62,45 +62,6 @@ libpurple/plugins/wincred/wincred.c libpurple/prefs.c libpurple/protocols.c -libpurple/protocols/jabber/adhoccommands.c -libpurple/protocols/jabber/auth.c -libpurple/protocols/jabber/auth_digest_md5.c -libpurple/protocols/jabber/auth_plain.c -libpurple/protocols/jabber/auth_scram.c -libpurple/protocols/jabber/auth_webex.c -libpurple/protocols/jabber/bosh.c -libpurple/protocols/jabber/buddy.c -libpurple/protocols/jabber/caps.c -libpurple/protocols/jabber/chat.c -libpurple/protocols/jabber/data.c -libpurple/protocols/jabber/disco.c -libpurple/protocols/jabber/ibb.c -libpurple/protocols/jabber/iq.c -libpurple/protocols/jabber/jabber.c -libpurple/protocols/jabber/jingle/content.c -libpurple/protocols/jabber/jingle/iceudp.c -libpurple/protocols/jabber/jingle/jingle.c -libpurple/protocols/jabber/jingle/rawudp.c -libpurple/protocols/jabber/jingle/rtp.c -libpurple/protocols/jabber/jingle/session.c -libpurple/protocols/jabber/jingle/transport.c -libpurple/protocols/jabber/jutil.c -libpurple/protocols/jabber/message.c -libpurple/protocols/jabber/oob.c -libpurple/protocols/jabber/parser.c -libpurple/protocols/jabber/pep.c -libpurple/protocols/jabber/ping.c -libpurple/protocols/jabber/presence.c -libpurple/protocols/jabber/roster.c -libpurple/protocols/jabber/si.c -libpurple/protocols/jabber/tests/test_jabber_caps.c -libpurple/protocols/jabber/tests/test_jabber_digest_md5.c -libpurple/protocols/jabber/tests/test_jabber_jutil.c -libpurple/protocols/jabber/tests/test_jabber_scram.c -libpurple/protocols/jabber/useravatar.c -libpurple/protocols/jabber/usernick.c -libpurple/protocols/jabber/xdata.c -libpurple/protocols/jabber/xmpp.c libpurple/proxy.c libpurple/purpleaccount.c libpurple/purpleaccountmanager.c