pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Use Meson summary() function.
2021-07-27, Elliott Sales de Andrade
cb640ea0f315
Use Meson summary() function.
Now that we require at least 0.52, we can use Meson's builtin summary printing to display the results of configuration.
Testing Done:
Configured with defaults, and with pixmaps disabled to trigger the warning: https://asciinema.org/a/mV2oxOoVCJNdmrPwgqqUJ3mkU?t=17
Reviewed at https://reviews.imfreedom.org/r/848/
/*
* Purple - XMPP Service Disco Browser
*
* 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
*
*/
/* TODO list (a little bit of a brain dump):
* Support more actions than "register" and "add" based on context.
- Subscribe to pubsub nodes (just...because?)
- Execute ad-hoc commands
- Change 'Register' to 'Unregister' if we're registered?
- Administer MUCs
* Enumerate pubsub node contents.
- PEP too? (useful development tool at times)
* See if we can better handle the ad-hoc commands that ejabberd returns
when disco'ing a server as an administrator:
from disco#items:
<item jid='darkrain42.org' node='announce' name='Announcements'/>
disco#info:
<iq from='darkrain42.org' type='result'>
<query xmlns='http://jabber.org/protocol/disco#info' node='announce'/>
</iq>
* For services that are a JID w/o a node, handle fetching ad-hoc commands?
*/
#include
<glib/gi18n-lib.h>
#include
<purple.h>
#include
<pidgin.h>
#include
"xmppdisco.h"
#include
"gtkdisco.h"
/* Variables */
PurplePlugin
*
my_plugin
=
NULL
;
static
GHashTable
*
iq_callbacks
=
NULL
;
static
gboolean
iq_listening
=
FALSE
;
typedef
void
(
*
XmppIqCallback
)(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
,
gpointer
data
);
struct
item_data
{
PidginDiscoList
*
list
;
XmppDiscoService
*
parent
;
char
*
name
;
char
*
node
;
/* disco#info replies don't always include the node */
};
struct
xmpp_iq_cb_data
{
/*
* Every IQ callback in this plugin uses the same structure for the
* callback data. It's a hack (it wouldn't scale), but it's used so that
* it's easy to clean up all the callbacks when the account disconnects
* (see remove_iq_callbacks_by_pc below).
*/
struct
item_data
*
context
;
PurpleConnection
*
pc
;
XmppIqCallback
cb
;
};
static
char
*
generate_next_id
()
{
static
guint32
index
=
0
;
if
(
index
==
0
)
{
do
{
index
=
g_random_int
();
}
while
(
index
==
0
);
}
return
g_strdup_printf
(
"purpledisco%x"
,
index
++
);
}
static
gboolean
remove_iq_callbacks_by_pc
(
gpointer
key
,
gpointer
value
,
gpointer
user_data
)
{
struct
xmpp_iq_cb_data
*
cb_data
=
value
;
if
(
cb_data
&&
cb_data
->
pc
==
user_data
)
{
struct
item_data
*
item_data
=
cb_data
->
context
;
if
(
item_data
)
{
pidgin_disco_list_unref
(
item_data
->
list
);
g_free
(
item_data
->
name
);
g_free
(
item_data
->
node
);
g_free
(
item_data
);
}
return
TRUE
;
}
else
return
FALSE
;
}
static
gboolean
xmpp_iq_received
(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
)
{
struct
xmpp_iq_cb_data
*
cb_data
;
cb_data
=
g_hash_table_lookup
(
iq_callbacks
,
id
);
if
(
!
cb_data
)
return
FALSE
;
cb_data
->
cb
(
cb_data
->
pc
,
type
,
id
,
from
,
iq
,
cb_data
->
context
);
g_hash_table_remove
(
iq_callbacks
,
id
);
if
(
g_hash_table_size
(
iq_callbacks
)
==
0
)
{
PurpleProtocol
*
protocol
=
purple_connection_get_protocol
(
pc
);
iq_listening
=
FALSE
;
purple_signal_disconnect
(
protocol
,
"jabber-receiving-iq"
,
my_plugin
,
PURPLE_CALLBACK
(
xmpp_iq_received
));
}
/* Om nom nom nom */
return
TRUE
;
}
static
void
xmpp_iq_register_callback
(
PurpleConnection
*
pc
,
gchar
*
id
,
gpointer
data
,
XmppIqCallback
cb
)
{
struct
xmpp_iq_cb_data
*
cbdata
=
g_new0
(
struct
xmpp_iq_cb_data
,
1
);
cbdata
->
context
=
data
;
cbdata
->
cb
=
cb
;
cbdata
->
pc
=
pc
;
g_hash_table_insert
(
iq_callbacks
,
id
,
cbdata
);
if
(
!
iq_listening
)
{
PurpleProtocol
*
protocol
=
NULL
;
PurpleProtocolManager
*
manager
=
purple_protocol_manager_get_default
();
protocol
=
purple_protocol_manager_find
(
manager
,
XMPP_PROTOCOL_ID
);
iq_listening
=
TRUE
;
purple_signal_connect
(
protocol
,
"jabber-receiving-iq"
,
my_plugin
,
PURPLE_CALLBACK
(
xmpp_iq_received
),
NULL
);
}
}
static
void
xmpp_disco_info_do
(
PurpleConnection
*
pc
,
gpointer
cbdata
,
const
char
*
jid
,
const
char
*
node
,
XmppIqCallback
cb
)
{
PurpleXmlNode
*
iq
,
*
query
;
char
*
id
=
generate_next_id
();
iq
=
purple_xmlnode_new
(
"iq"
);
purple_xmlnode_set_attrib
(
iq
,
"type"
,
"get"
);
purple_xmlnode_set_attrib
(
iq
,
"to"
,
jid
);
purple_xmlnode_set_attrib
(
iq
,
"id"
,
id
);
query
=
purple_xmlnode_new_child
(
iq
,
"query"
);
purple_xmlnode_set_namespace
(
query
,
NS_DISCO_INFO
);
if
(
node
)
purple_xmlnode_set_attrib
(
query
,
"node"
,
node
);
/* Steals id */
xmpp_iq_register_callback
(
pc
,
id
,
cbdata
,
cb
);
purple_signal_emit
(
purple_connection_get_protocol
(
pc
),
"jabber-sending-xmlnode"
,
pc
,
&
iq
);
if
(
iq
!=
NULL
)
purple_xmlnode_free
(
iq
);
}
static
void
xmpp_disco_items_do
(
PurpleConnection
*
pc
,
gpointer
cbdata
,
const
char
*
jid
,
const
char
*
node
,
XmppIqCallback
cb
)
{
PurpleXmlNode
*
iq
,
*
query
;
char
*
id
=
generate_next_id
();
iq
=
purple_xmlnode_new
(
"iq"
);
purple_xmlnode_set_attrib
(
iq
,
"type"
,
"get"
);
purple_xmlnode_set_attrib
(
iq
,
"to"
,
jid
);
purple_xmlnode_set_attrib
(
iq
,
"id"
,
id
);
query
=
purple_xmlnode_new_child
(
iq
,
"query"
);
purple_xmlnode_set_namespace
(
query
,
NS_DISCO_ITEMS
);
if
(
node
)
purple_xmlnode_set_attrib
(
query
,
"node"
,
node
);
/* Steals id */
xmpp_iq_register_callback
(
pc
,
id
,
cbdata
,
cb
);
purple_signal_emit
(
purple_connection_get_protocol
(
pc
),
"jabber-sending-xmlnode"
,
pc
,
&
iq
);
if
(
iq
!=
NULL
)
purple_xmlnode_free
(
iq
);
}
static
XmppDiscoServiceType
disco_service_type_from_identity
(
PurpleXmlNode
*
identity
)
{
const
char
*
category
,
*
type
;
if
(
!
identity
)
return
XMPP_DISCO_SERVICE_TYPE_OTHER
;
category
=
purple_xmlnode_get_attrib
(
identity
,
"category"
);
type
=
purple_xmlnode_get_attrib
(
identity
,
"type"
);
if
(
!
category
)
return
XMPP_DISCO_SERVICE_TYPE_OTHER
;
if
(
purple_strequal
(
category
,
"conference"
))
return
XMPP_DISCO_SERVICE_TYPE_CHAT
;
else
if
(
purple_strequal
(
category
,
"directory"
))
return
XMPP_DISCO_SERVICE_TYPE_DIRECTORY
;
else
if
(
purple_strequal
(
category
,
"gateway"
))
return
XMPP_DISCO_SERVICE_TYPE_GATEWAY
;
else
if
(
purple_strequal
(
category
,
"pubsub"
))
{
if
(
!
type
||
purple_strequal
(
type
,
"collection"
))
return
XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION
;
else
if
(
purple_strequal
(
type
,
"leaf"
))
return
XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF
;
else
if
(
purple_strequal
(
type
,
"service"
))
return
XMPP_DISCO_SERVICE_TYPE_OTHER
;
else
{
purple_debug_warning
(
"xmppdisco"
,
"Unknown pubsub type '%s'
\n
"
,
type
);
return
XMPP_DISCO_SERVICE_TYPE_OTHER
;
}
}
return
XMPP_DISCO_SERVICE_TYPE_OTHER
;
}
static
const
struct
{
const
char
*
from
;
const
char
*
to
;
}
disco_type_mappings
[]
=
{
{
"gadu-gadu"
,
"gadu-gadu"
},
/* the protocol is gg, but list_icon returns "gadu-gadu" */
{
"sametime"
,
"meanwhile"
},
{
"xmpp"
,
"jabber"
},
/* jabber (mentioned in case the protocol is renamed so this line will match) */
{
NULL
,
NULL
}
};
static
const
gchar
*
disco_type_from_string
(
const
gchar
*
str
)
{
int
i
=
0
;
g_return_val_if_fail
(
str
!=
NULL
,
""
);
for
(
;
disco_type_mappings
[
i
].
from
;
++
i
)
{
if
(
!
g_ascii_strcasecmp
(
str
,
disco_type_mappings
[
i
].
from
))
return
disco_type_mappings
[
i
].
to
;
}
/* fallback to the string itself */
return
str
;
}
static
void
got_info_cb
(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
,
gpointer
data
)
{
struct
item_data
*
item_data
=
data
;
PidginDiscoList
*
list
=
item_data
->
list
;
PurpleXmlNode
*
query
;
--
list
->
fetch_count
;
if
(
!
list
->
in_progress
)
goto
out
;
if
(
purple_strequal
(
type
,
"result"
)
&&
(
query
=
purple_xmlnode_get_child
(
iq
,
"query"
)))
{
PurpleXmlNode
*
identity
=
purple_xmlnode_get_child
(
query
,
"identity"
);
XmppDiscoService
*
service
;
PurpleXmlNode
*
feature
;
service
=
g_new0
(
XmppDiscoService
,
1
);
service
->
list
=
item_data
->
list
;
purple_debug_info
(
"xmppdisco"
,
"parent for %s is %p
\n
"
,
from
,
item_data
->
parent
);
service
->
parent
=
item_data
->
parent
;
service
->
flags
=
0
;
service
->
type
=
disco_service_type_from_identity
(
identity
);
if
(
item_data
->
node
)
{
if
(
item_data
->
name
)
{
service
->
name
=
item_data
->
name
;
item_data
->
name
=
NULL
;
}
else
service
->
name
=
g_strdup
(
item_data
->
node
);
service
->
node
=
item_data
->
node
;
item_data
->
node
=
NULL
;
if
(
service
->
type
==
XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION
)
service
->
flags
|=
XMPP_DISCO_BROWSE
;
}
else
service
->
name
=
g_strdup
(
from
);
if
(
!
service
->
node
)
/* Only support adding JIDs, not JID+node combos */
service
->
flags
|=
XMPP_DISCO_ADD
;
if
(
item_data
->
name
)
{
service
->
description
=
item_data
->
name
;
item_data
->
name
=
NULL
;
}
else
if
(
identity
)
service
->
description
=
g_strdup
(
purple_xmlnode_get_attrib
(
identity
,
"name"
));
/* TODO: Overlap with service->name a bit */
service
->
jid
=
g_strdup
(
from
);
for
(
feature
=
purple_xmlnode_get_child
(
query
,
"feature"
);
feature
;
feature
=
purple_xmlnode_get_next_twin
(
feature
))
{
const
char
*
var
;
if
(
!
(
var
=
purple_xmlnode_get_attrib
(
feature
,
"var"
)))
continue
;
if
(
purple_strequal
(
var
,
NS_REGISTER
))
service
->
flags
|=
XMPP_DISCO_REGISTER
;
else
if
(
purple_strequal
(
var
,
NS_DISCO_ITEMS
))
service
->
flags
|=
XMPP_DISCO_BROWSE
;
else
if
(
purple_strequal
(
var
,
NS_MUC
))
{
service
->
flags
|=
XMPP_DISCO_BROWSE
;
service
->
type
=
XMPP_DISCO_SERVICE_TYPE_CHAT
;
}
}
if
(
service
->
type
==
XMPP_DISCO_SERVICE_TYPE_GATEWAY
)
service
->
gateway_type
=
g_strdup
(
disco_type_from_string
(
purple_xmlnode_get_attrib
(
identity
,
"type"
)));
pidgin_disco_add_service
(
list
,
service
,
service
->
parent
);
}
out
:
if
(
list
->
fetch_count
==
0
)
pidgin_disco_list_set_in_progress
(
list
,
FALSE
);
g_free
(
item_data
->
name
);
g_free
(
item_data
->
node
);
g_free
(
item_data
);
pidgin_disco_list_unref
(
list
);
}
static
void
got_items_cb
(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
,
gpointer
data
)
{
struct
item_data
*
item_data
=
data
;
PidginDiscoList
*
list
=
item_data
->
list
;
PurpleXmlNode
*
query
;
gboolean
has_items
=
FALSE
;
--
list
->
fetch_count
;
if
(
!
list
->
in_progress
)
goto
out
;
if
(
purple_strequal
(
type
,
"result"
)
&&
(
query
=
purple_xmlnode_get_child
(
iq
,
"query"
)))
{
PurpleXmlNode
*
item
;
for
(
item
=
purple_xmlnode_get_child
(
query
,
"item"
);
item
;
item
=
purple_xmlnode_get_next_twin
(
item
))
{
const
char
*
jid
=
purple_xmlnode_get_attrib
(
item
,
"jid"
);
const
char
*
name
=
purple_xmlnode_get_attrib
(
item
,
"name"
);
const
char
*
node
=
purple_xmlnode_get_attrib
(
item
,
"node"
);
has_items
=
TRUE
;
if
(
item_data
->
parent
->
type
==
XMPP_DISCO_SERVICE_TYPE_CHAT
)
{
/* This is a hacky first-order approximation. Any MUC
* component that has a >1 level hierarchy (a Yahoo MUC
* transport component probably does) will violate this.
*
* On the other hand, this is better than querying all the
* chats at conference.jabber.org to enumerate them.
*/
XmppDiscoService
*
service
=
g_new0
(
XmppDiscoService
,
1
);
service
->
list
=
item_data
->
list
;
service
->
parent
=
item_data
->
parent
;
service
->
flags
=
XMPP_DISCO_ADD
;
service
->
type
=
XMPP_DISCO_SERVICE_TYPE_CHAT
;
service
->
name
=
g_strdup
(
name
);
service
->
jid
=
g_strdup
(
jid
);
service
->
node
=
g_strdup
(
node
);
pidgin_disco_add_service
(
list
,
service
,
item_data
->
parent
);
}
else
{
struct
item_data
*
item_data2
=
g_new0
(
struct
item_data
,
1
);
item_data2
->
list
=
item_data
->
list
;
item_data2
->
parent
=
item_data
->
parent
;
item_data2
->
name
=
g_strdup
(
name
);
item_data2
->
node
=
g_strdup
(
node
);
++
list
->
fetch_count
;
pidgin_disco_list_ref
(
list
);
xmpp_disco_info_do
(
pc
,
item_data2
,
jid
,
node
,
got_info_cb
);
}
}
}
if
(
!
has_items
)
pidgin_disco_add_service
(
list
,
NULL
,
item_data
->
parent
);
out
:
if
(
list
->
fetch_count
==
0
)
pidgin_disco_list_set_in_progress
(
list
,
FALSE
);
g_free
(
item_data
);
pidgin_disco_list_unref
(
list
);
}
static
void
server_items_cb
(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
,
gpointer
data
)
{
struct
item_data
*
cb_data
=
data
;
PidginDiscoList
*
list
=
cb_data
->
list
;
PurpleXmlNode
*
query
;
g_free
(
cb_data
);
--
list
->
fetch_count
;
if
(
purple_strequal
(
type
,
"result"
)
&&
(
query
=
purple_xmlnode_get_child
(
iq
,
"query"
)))
{
PurpleXmlNode
*
item
;
for
(
item
=
purple_xmlnode_get_child
(
query
,
"item"
);
item
;
item
=
purple_xmlnode_get_next_twin
(
item
))
{
const
char
*
jid
=
purple_xmlnode_get_attrib
(
item
,
"jid"
);
const
char
*
name
=
purple_xmlnode_get_attrib
(
item
,
"name"
);
const
char
*
node
=
purple_xmlnode_get_attrib
(
item
,
"node"
);
struct
item_data
*
item_data
;
if
(
!
jid
)
continue
;
item_data
=
g_new0
(
struct
item_data
,
1
);
item_data
->
list
=
list
;
item_data
->
name
=
g_strdup
(
name
);
item_data
->
node
=
g_strdup
(
node
);
++
list
->
fetch_count
;
pidgin_disco_list_ref
(
list
);
xmpp_disco_info_do
(
pc
,
item_data
,
jid
,
node
,
got_info_cb
);
}
}
if
(
list
->
fetch_count
==
0
)
pidgin_disco_list_set_in_progress
(
list
,
FALSE
);
pidgin_disco_list_unref
(
list
);
}
static
void
server_info_cb
(
PurpleConnection
*
pc
,
const
char
*
type
,
const
char
*
id
,
const
char
*
from
,
PurpleXmlNode
*
iq
,
gpointer
data
)
{
struct
item_data
*
cb_data
=
data
;
PidginDiscoList
*
list
=
cb_data
->
list
;
PurpleXmlNode
*
query
;
PurpleXmlNode
*
error
;
gboolean
items
=
FALSE
;
--
list
->
fetch_count
;
if
(
purple_strequal
(
type
,
"result"
)
&&
(
query
=
purple_xmlnode_get_child
(
iq
,
"query"
)))
{
PurpleXmlNode
*
feature
;
for
(
feature
=
purple_xmlnode_get_child
(
query
,
"feature"
);
feature
;
feature
=
purple_xmlnode_get_next_twin
(
feature
))
{
const
char
*
var
=
purple_xmlnode_get_attrib
(
feature
,
"var"
);
if
(
purple_strequal
(
var
,
NS_DISCO_ITEMS
))
{
items
=
TRUE
;
break
;
}
}
if
(
items
)
{
xmpp_disco_items_do
(
pc
,
cb_data
,
from
,
NULL
/* node */
,
server_items_cb
);
++
list
->
fetch_count
;
pidgin_disco_list_ref
(
list
);
}
else
{
pidgin_disco_list_set_in_progress
(
list
,
FALSE
);
g_free
(
cb_data
);
}
}
else
{
error
=
purple_xmlnode_get_child
(
iq
,
"error"
);
if
(
purple_xmlnode_get_child
(
error
,
"remote-server-not-found"
)
||
purple_xmlnode_get_child
(
error
,
"jid-malformed"
))
{
purple_notify_error
(
my_plugin
,
_
(
"Error"
),
_
(
"Server does not exist"
),
NULL
,
NULL
);
}
else
{
purple_notify_error
(
my_plugin
,
_
(
"Error"
),
_
(
"Server does not support service discovery"
),
NULL
,
NULL
);
}
pidgin_disco_list_set_in_progress
(
list
,
FALSE
);
g_free
(
cb_data
);
}
pidgin_disco_list_unref
(
list
);
}
void
xmpp_disco_start
(
PidginDiscoList
*
list
)
{
struct
item_data
*
cb_data
;
g_return_if_fail
(
list
!=
NULL
);
++
list
->
fetch_count
;
pidgin_disco_list_ref
(
list
);
cb_data
=
g_new0
(
struct
item_data
,
1
);
cb_data
->
list
=
list
;
xmpp_disco_info_do
(
list
->
pc
,
cb_data
,
list
->
server
,
NULL
,
server_info_cb
);
}
void
xmpp_disco_service_expand
(
XmppDiscoService
*
service
)
{
struct
item_data
*
item_data
;
g_return_if_fail
(
service
!=
NULL
);
if
(
service
->
expanded
)
return
;
item_data
=
g_new0
(
struct
item_data
,
1
);
item_data
->
list
=
service
->
list
;
item_data
->
parent
=
service
;
++
service
->
list
->
fetch_count
;
pidgin_disco_list_ref
(
service
->
list
);
pidgin_disco_list_set_in_progress
(
service
->
list
,
TRUE
);
xmpp_disco_items_do
(
service
->
list
->
pc
,
item_data
,
service
->
jid
,
service
->
node
,
got_items_cb
);
service
->
expanded
=
TRUE
;
}
void
xmpp_disco_service_register
(
XmppDiscoService
*
service
)
{
PurpleXmlNode
*
iq
,
*
query
;
char
*
id
=
generate_next_id
();
iq
=
purple_xmlnode_new
(
"iq"
);
purple_xmlnode_set_attrib
(
iq
,
"type"
,
"get"
);
purple_xmlnode_set_attrib
(
iq
,
"to"
,
service
->
jid
);
purple_xmlnode_set_attrib
(
iq
,
"id"
,
id
);
query
=
purple_xmlnode_new_child
(
iq
,
"query"
);
purple_xmlnode_set_namespace
(
query
,
NS_REGISTER
);
purple_signal_emit
(
purple_connection_get_protocol
(
service
->
list
->
pc
),
"jabber-sending-xmlnode"
,
service
->
list
->
pc
,
&
iq
);
if
(
iq
!=
NULL
)
purple_xmlnode_free
(
iq
);
g_free
(
id
);
}
static
void
create_dialog
(
PurplePluginAction
*
action
)
{
pidgin_disco_dialog_new
();
}
static
GList
*
actions
(
PurplePlugin
*
plugin
)
{
GList
*
l
=
NULL
;
PurplePluginAction
*
action
=
NULL
;
action
=
purple_plugin_action_new
(
_
(
"XMPP Service Discovery"
),
create_dialog
);
l
=
g_list_prepend
(
l
,
action
);
return
l
;
}
static
void
signed_off_cb
(
PurpleConnection
*
pc
,
gpointer
unused
)
{
/* Deal with any dialogs */
pidgin_disco_signed_off_cb
(
pc
);
/* Remove all the IQ callbacks for this connection */
g_hash_table_foreach_remove
(
iq_callbacks
,
remove_iq_callbacks_by_pc
,
pc
);
}
static
GPluginPluginInfo
*
xmpp_disco_query
(
GError
**
error
)
{
const
gchar
*
const
authors
[]
=
{
"Paul Aurich <paul@darkrain42.org>"
,
NULL
};
return
pidgin_plugin_info_new
(
"id"
,
PLUGIN_ID
,
"name"
,
N_
(
"XMPP Service Discovery"
),
"version"
,
DISPLAY_VERSION
,
"category"
,
N_
(
"Protocol utility"
),
"summary"
,
N_
(
"Allows browsing and registering services."
),
"description"
,
N_
(
"This plugin is useful for registering with legacy "
"transports or other XMPP services."
),
"authors"
,
authors
,
"website"
,
PURPLE_WEBSITE
,
"abi-version"
,
PURPLE_ABI_VERSION
,
"actions-cb"
,
actions
,
NULL
);
}
static
gboolean
xmpp_disco_load
(
GPluginPlugin
*
plugin
,
GError
**
error
)
{
PurpleProtocol
*
xmpp_protocol
;
PurpleProtocolManager
*
manager
;
my_plugin
=
plugin
;
manager
=
purple_protocol_manager_get_default
();
xmpp_protocol
=
purple_protocol_manager_find
(
manager
,
XMPP_PROTOCOL_ID
);
if
(
NULL
==
xmpp_protocol
)
{
g_set_error_literal
(
error
,
PLUGIN_DOMAIN
,
0
,
_
(
"XMPP protocol is not loaded."
));
return
FALSE
;
}
pidgin_disco_dialog_register
(
plugin
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signing-off"
,
plugin
,
PURPLE_CALLBACK
(
signed_off_cb
),
NULL
);
iq_callbacks
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
return
TRUE
;
}
static
gboolean
xmpp_disco_unload
(
GPluginPlugin
*
plugin
,
GError
**
error
)
{
g_hash_table_destroy
(
iq_callbacks
);
iq_callbacks
=
NULL
;
purple_signals_disconnect_by_handle
(
plugin
);
pidgin_disco_dialogs_destroy_all
();
return
TRUE
;
}
GPLUGIN_NATIVE_PLUGIN_DECLARE
(
xmpp_disco
)