qulogic/pidgin
Clone
Summary
Browse
Changes
Graph
Add disconnection reasons to oscar.
cpw.resiak.disconnectreason
2007-10-01, Will Thompson
a9fc6198b5c6
Add disconnection reasons to oscar.
/*
* 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 02111-1301, USA.
*/
#include
"internal.h"
#include
"debug.h"
#include
"buddy.h"
#include
"mdns_interface.h"
#include
"dns_sd_proxy.h"
#include
"dnsquery.h"
#include
"mdns_common.h"
/* data structure for the resolve callback */
typedef
struct
_ResolveCallbackArgs
{
DNSServiceRef
resolver
;
guint
resolver_handler
;
gchar
*
full_service_name
;
PurpleDnsQueryData
*
query
;
BonjourBuddy
*
buddy
;
}
ResolveCallbackArgs
;
/* data used by win32 bonjour implementation */
typedef
struct
_win32_session_impl_data
{
DNSServiceRef
presence_svc
;
DNSServiceRef
browser_svc
;
DNSRecordRef
buddy_icon_rec
;
guint
presence_handler
;
guint
browser_handler
;
}
Win32SessionImplData
;
typedef
struct
_win32_buddy_impl_data
{
DNSServiceRef
txt_query
;
guint
txt_query_handler
;
DNSServiceRef
null_query
;
guint
null_query_handler
;
}
Win32BuddyImplData
;
static
void
_mdns_handle_event
(
gpointer
data
,
gint
source
,
PurpleInputCondition
condition
)
{
DNSServiceProcessResult
((
DNSServiceRef
)
data
);
}
static
void
_mdns_parse_text_record
(
BonjourBuddy
*
buddy
,
const
char
*
record
,
uint16_t
record_len
)
{
const
char
*
txt_entry
;
uint8_t
txt_len
;
int
i
;
clear_bonjour_buddy_values
(
buddy
);
for
(
i
=
0
;
buddy_TXT_records
[
i
]
!=
NULL
;
i
++
)
{
txt_entry
=
TXTRecordGetValuePtr
(
record_len
,
record
,
buddy_TXT_records
[
i
],
&
txt_len
);
if
(
txt_entry
!=
NULL
)
set_bonjour_buddy_value
(
buddy
,
buddy_TXT_records
[
i
],
txt_entry
,
txt_len
);
}
}
static
void
DNSSD_API
_mdns_record_query_callback
(
DNSServiceRef
DNSServiceRef
,
DNSServiceFlags
flags
,
uint32_t
interfaceIndex
,
DNSServiceErrorType
errorCode
,
const
char
*
fullname
,
uint16_t
rrtype
,
uint16_t
rrclass
,
uint16_t
rdlen
,
const
void
*
rdata
,
uint32_t
ttl
,
void
*
context
)
{
if
(
kDNSServiceErr_NoError
!=
errorCode
)
{
purple_debug_error
(
"bonjour"
,
"record query - callback error.
\n
"
);
/* TODO: Probably should remove the buddy when this happens */
}
else
if
(
flags
&
kDNSServiceFlagsAdd
)
{
if
(
rrtype
==
kDNSServiceType_TXT
)
{
/* New Buddy */
BonjourBuddy
*
bb
=
(
BonjourBuddy
*
)
context
;
_mdns_parse_text_record
(
bb
,
rdata
,
rdlen
);
bonjour_buddy_add_to_purple
(
bb
,
NULL
);
}
else
if
(
rrtype
==
kDNSServiceType_NULL
)
{
/* Buddy Icon response */
BonjourBuddy
*
bb
=
(
BonjourBuddy
*
)
context
;
Win32BuddyImplData
*
idata
=
bb
->
mdns_impl_data
;
g_return_if_fail
(
idata
!=
NULL
);
bonjour_buddy_got_buddy_icon
(
bb
,
rdata
,
rdlen
);
/* We've got what we need; stop listening */
purple_input_remove
(
idata
->
null_query_handler
);
idata
->
null_query_handler
=
0
;
DNSServiceRefDeallocate
(
idata
->
null_query
);
idata
->
null_query
=
NULL
;
}
}
}
static
void
_mdns_resolve_host_callback
(
GSList
*
hosts
,
gpointer
data
,
const
char
*
error_message
)
{
ResolveCallbackArgs
*
args
=
(
ResolveCallbackArgs
*
)
data
;
BonjourBuddy
*
bb
=
args
->
buddy
;
if
(
!
hosts
||
!
hosts
->
data
)
{
purple_debug_error
(
"bonjour"
,
"host resolution - callback error.
\n
"
);
bonjour_buddy_delete
(
bb
);
}
else
{
struct
sockaddr_in
*
addr
=
(
struct
sockaddr_in
*
)
g_slist_nth_data
(
hosts
,
1
);
Win32BuddyImplData
*
idata
=
bb
->
mdns_impl_data
;
g_return_if_fail
(
idata
!=
NULL
);
g_free
(
bb
->
ip
);
bb
->
ip
=
g_strdup
(
inet_ntoa
(
addr
->
sin_addr
));
/* finally, set up the continuous txt record watcher, and add the buddy to purple */
if
(
kDNSServiceErr_NoError
==
DNSServiceQueryRecord
(
&
idata
->
txt_query
,
kDNSServiceFlagsLongLivedQuery
,
kDNSServiceInterfaceIndexAny
,
args
->
full_service_name
,
kDNSServiceType_TXT
,
kDNSServiceClass_IN
,
_mdns_record_query_callback
,
bb
))
{
purple_debug_info
(
"bonjour"
,
"Found buddy %s at %s:%d
\n
"
,
bb
->
name
,
bb
->
ip
,
bb
->
port_p2pj
);
idata
->
txt_query_handler
=
purple_input_add
(
DNSServiceRefSockFD
(
idata
->
txt_query
),
PURPLE_INPUT_READ
,
_mdns_handle_event
,
idata
->
txt_query
);
bonjour_buddy_add_to_purple
(
bb
,
NULL
);
}
else
bonjour_buddy_delete
(
bb
);
}
/* free the hosts list*/
g_slist_free
(
hosts
);
/* free the remaining args memory */
purple_dnsquery_destroy
(
args
->
query
);
g_free
(
args
->
full_service_name
);
g_free
(
args
);
}
static
void
DNSSD_API
_mdns_service_resolve_callback
(
DNSServiceRef
sdRef
,
DNSServiceFlags
flags
,
uint32_t
interfaceIndex
,
DNSServiceErrorType
errorCode
,
const
char
*
fullname
,
const
char
*
hosttarget
,
uint16_t
port
,
uint16_t
txtLen
,
const
char
*
txtRecord
,
void
*
context
)
{
ResolveCallbackArgs
*
args
=
(
ResolveCallbackArgs
*
)
context
;
/* remove the input fd and destroy the service ref */
purple_input_remove
(
args
->
resolver_handler
);
DNSServiceRefDeallocate
(
args
->
resolver
);
if
(
kDNSServiceErr_NoError
!=
errorCode
)
{
purple_debug_error
(
"bonjour"
,
"service resolver - callback error.
\n
"
);
bonjour_buddy_delete
(
args
->
buddy
);
g_free
(
args
);
}
else
{
args
->
buddy
->
port_p2pj
=
ntohs
(
port
);
/* parse the text record */
_mdns_parse_text_record
(
args
->
buddy
,
txtRecord
,
txtLen
);
/* set more arguments, and start the host resolver */
args
->
full_service_name
=
g_strdup
(
fullname
);
if
(
!
(
args
->
query
=
purple_dnsquery_a
(
hosttarget
,
port
,
_mdns_resolve_host_callback
,
args
)))
{
purple_debug_error
(
"bonjour"
,
"service resolver - host resolution failed.
\n
"
);
bonjour_buddy_delete
(
args
->
buddy
);
g_free
(
args
->
full_service_name
);
g_free
(
args
);
}
}
}
static
void
DNSSD_API
_mdns_service_register_callback
(
DNSServiceRef
sdRef
,
DNSServiceFlags
flags
,
DNSServiceErrorType
errorCode
,
const
char
*
name
,
const
char
*
regtype
,
const
char
*
domain
,
void
*
context
)
{
/* TODO: deal with collision */
if
(
kDNSServiceErr_NoError
!=
errorCode
)
purple_debug_error
(
"bonjour"
,
"service advertisement - callback error (%d).
\n
"
,
errorCode
);
else
purple_debug_info
(
"bonjour"
,
"service advertisement - callback.
\n
"
);
}
static
void
DNSSD_API
_mdns_service_browse_callback
(
DNSServiceRef
sdRef
,
DNSServiceFlags
flags
,
uint32_t
interfaceIndex
,
DNSServiceErrorType
errorCode
,
const
char
*
serviceName
,
const
char
*
regtype
,
const
char
*
replyDomain
,
void
*
context
)
{
PurpleAccount
*
account
=
(
PurpleAccount
*
)
context
;
PurpleBuddy
*
pb
=
NULL
;
if
(
kDNSServiceErr_NoError
!=
errorCode
)
purple_debug_error
(
"bonjour"
,
"service browser - callback error
\n
"
);
else
if
(
flags
&
kDNSServiceFlagsAdd
)
{
/* A presence service instance has been discovered... check it isn't us! */
if
(
purple_utf8_strcasecmp
(
serviceName
,
account
->
username
)
!=
0
)
{
/* OK, lets go ahead and resolve it to add to the buddy list */
ResolveCallbackArgs
*
args
=
g_new0
(
ResolveCallbackArgs
,
1
);
args
->
buddy
=
bonjour_buddy_new
(
serviceName
,
account
);
if
(
kDNSServiceErr_NoError
!=
DNSServiceResolve
(
&
args
->
resolver
,
0
,
0
,
serviceName
,
regtype
,
replyDomain
,
_mdns_service_resolve_callback
,
args
))
{
bonjour_buddy_delete
(
args
->
buddy
);
g_free
(
args
);
purple_debug_error
(
"bonjour"
,
"service browser - failed to resolve service.
\n
"
);
}
else
{
/* get a file descriptor for this service ref, and add it to the input list */
gint
fd
=
DNSServiceRefSockFD
(
args
->
resolver
);
args
->
resolver_handler
=
purple_input_add
(
fd
,
PURPLE_INPUT_READ
,
_mdns_handle_event
,
args
->
resolver
);
}
}
}
else
{
/* A peer has sent a goodbye packet, remove them from the buddy list */
purple_debug_info
(
"bonjour"
,
"service browser - remove notification
\n
"
);
pb
=
purple_find_buddy
(
account
,
serviceName
);
if
(
pb
!=
NULL
)
purple_blist_remove_buddy
(
pb
);
}
}
/****************************
* mdns_interface functions *
****************************/
gboolean
_mdns_init_session
(
BonjourDnsSd
*
data
)
{
data
->
mdns_impl_data
=
g_new0
(
Win32SessionImplData
,
1
);
return
TRUE
;
}
gboolean
_mdns_publish
(
BonjourDnsSd
*
data
,
PublishType
type
,
GSList
*
records
)
{
TXTRecordRef
dns_data
;
gboolean
ret
=
TRUE
;
DNSServiceErrorType
set_ret
=
kDNSServiceErr_NoError
;
Win32SessionImplData
*
idata
=
data
->
mdns_impl_data
;
g_return_val_if_fail
(
idata
!=
NULL
,
FALSE
);
TXTRecordCreate
(
&
dns_data
,
256
,
NULL
);
while
(
records
)
{
PurpleKeyValuePair
*
kvp
=
records
->
data
;
set_ret
=
TXTRecordSetValue
(
&
dns_data
,
kvp
->
key
,
strlen
(
kvp
->
value
),
kvp
->
value
);
if
(
set_ret
!=
kDNSServiceErr_NoError
)
break
;
records
=
records
->
next
;
}
if
(
set_ret
!=
kDNSServiceErr_NoError
)
{
purple_debug_error
(
"bonjour"
,
"Unable to allocate memory for text record.
\n
"
);
ret
=
FALSE
;
}
else
{
DNSServiceErrorType
err
=
kDNSServiceErr_NoError
;
/* OK, we're done constructing the text record, (re)publish the service */
switch
(
type
)
{
case
PUBLISH_START
:
purple_debug_info
(
"bonjour"
,
"Registering presence on port %d
\n
"
,
data
->
port_p2pj
);
err
=
DNSServiceRegister
(
&
idata
->
presence_svc
,
0
,
0
,
purple_account_get_username
(
data
->
account
),
ICHAT_SERVICE
,
NULL
,
NULL
,
htons
(
data
->
port_p2pj
),
TXTRecordGetLength
(
&
dns_data
),
TXTRecordGetBytesPtr
(
&
dns_data
),
_mdns_service_register_callback
,
NULL
);
break
;
case
PUBLISH_UPDATE
:
purple_debug_info
(
"bonjour"
,
"Updating presence.
\n
"
);
err
=
DNSServiceUpdateRecord
(
idata
->
presence_svc
,
NULL
,
0
,
TXTRecordGetLength
(
&
dns_data
),
TXTRecordGetBytesPtr
(
&
dns_data
),
0
);
break
;
}
if
(
err
!=
kDNSServiceErr_NoError
)
{
purple_debug_error
(
"bonjour"
,
"Failed to publish presence service.
\n
"
);
ret
=
FALSE
;
}
else
if
(
type
==
PUBLISH_START
)
{
/* We need to do this because according to the Apple docs:
* "the client is responsible for ensuring that DNSServiceProcessResult() is called
* whenever there is a reply from the daemon - the daemon may terminate its connection
* with a client that does not process the daemon's responses */
idata
->
presence_handler
=
purple_input_add
(
DNSServiceRefSockFD
(
idata
->
presence_svc
),
PURPLE_INPUT_READ
,
_mdns_handle_event
,
idata
->
presence_svc
);
}
}
/* Free the memory used by temp data */
TXTRecordDeallocate
(
&
dns_data
);
return
ret
;
}
gboolean
_mdns_browse
(
BonjourDnsSd
*
data
)
{
Win32SessionImplData
*
idata
=
data
->
mdns_impl_data
;
g_return_val_if_fail
(
idata
!=
NULL
,
FALSE
);
if
(
DNSServiceBrowse
(
&
idata
->
browser_svc
,
0
,
0
,
ICHAT_SERVICE
,
NULL
,
_mdns_service_browse_callback
,
data
->
account
)
==
kDNSServiceErr_NoError
)
{
idata
->
browser_handler
=
purple_input_add
(
DNSServiceRefSockFD
(
idata
->
browser_svc
),
PURPLE_INPUT_READ
,
_mdns_handle_event
,
idata
->
browser_svc
);
return
TRUE
;
}
return
FALSE
;
}
void
_mdns_stop
(
BonjourDnsSd
*
data
)
{
Win32SessionImplData
*
idata
=
data
->
mdns_impl_data
;
if
(
idata
==
NULL
)
return
;
if
(
idata
->
presence_svc
!=
NULL
)
{
purple_input_remove
(
idata
->
presence_handler
);
DNSServiceRefDeallocate
(
idata
->
presence_svc
);
}
if
(
idata
->
browser_svc
!=
NULL
)
{
purple_input_remove
(
idata
->
browser_handler
);
DNSServiceRefDeallocate
(
idata
->
browser_svc
);
}
g_free
(
idata
);
data
->
mdns_impl_data
=
NULL
;
}
gboolean
_mdns_set_buddy_icon_data
(
BonjourDnsSd
*
data
,
gconstpointer
avatar_data
,
gsize
avatar_len
)
{
Win32SessionImplData
*
idata
=
data
->
mdns_impl_data
;
DNSServiceErrorType
err
=
kDNSServiceErr_NoError
;
g_return_val_if_fail
(
idata
!=
NULL
,
FALSE
);
if
(
avatar_data
!=
NULL
&&
idata
->
buddy_icon_rec
==
NULL
)
{
purple_debug_info
(
"bonjour"
,
"Setting new buddy icon.
\n
"
);
err
=
DNSServiceAddRecord
(
idata
->
presence_svc
,
&
idata
->
buddy_icon_rec
,
0
,
kDNSServiceType_NULL
,
avatar_len
,
avatar_data
,
0
);
}
else
if
(
avatar_data
!=
NULL
)
{
purple_debug_info
(
"bonjour"
,
"Updating existing buddy icon.
\n
"
);
err
=
DNSServiceUpdateRecord
(
idata
->
presence_svc
,
idata
->
buddy_icon_rec
,
0
,
avatar_len
,
avatar_data
,
0
);
}
else
if
(
idata
->
buddy_icon_rec
!=
NULL
)
{
purple_debug_info
(
"bonjour"
,
"Removing existing buddy icon.
\n
"
);
DNSServiceRemoveRecord
(
idata
->
presence_svc
,
idata
->
buddy_icon_rec
,
0
);
idata
->
buddy_icon_rec
=
NULL
;
}
if
(
err
!=
kDNSServiceErr_NoError
)
purple_debug_error
(
"bonjour"
,
"Error (%d) setting buddy icon record.
\n
"
,
err
);
return
(
err
==
kDNSServiceErr_NoError
);
}
void
_mdns_init_buddy
(
BonjourBuddy
*
buddy
)
{
buddy
->
mdns_impl_data
=
g_new0
(
Win32BuddyImplData
,
1
);
}
void
_mdns_delete_buddy
(
BonjourBuddy
*
buddy
)
{
Win32BuddyImplData
*
idata
=
buddy
->
mdns_impl_data
;
g_return_if_fail
(
idata
!=
NULL
);
if
(
idata
->
txt_query
!=
NULL
)
{
purple_input_remove
(
idata
->
txt_query_handler
);
DNSServiceRefDeallocate
(
idata
->
txt_query
);
}
if
(
idata
->
null_query
!=
NULL
)
{
purple_input_remove
(
idata
->
null_query_handler
);
DNSServiceRefDeallocate
(
idata
->
null_query
);
}
g_free
(
idata
);
buddy
->
mdns_impl_data
=
NULL
;
}
void
_mdns_retrieve_buddy_icon
(
BonjourBuddy
*
buddy
)
{
Win32BuddyImplData
*
idata
=
buddy
->
mdns_impl_data
;
char
svc_name
[
kDNSServiceMaxDomainName
];
g_return_if_fail
(
idata
!=
NULL
);
/* Cancel any existing query */
if
(
idata
->
null_query
!=
NULL
)
{
purple_input_remove
(
idata
->
null_query_handler
);
idata
->
null_query_handler
=
0
;
DNSServiceRefDeallocate
(
idata
->
null_query
);
idata
->
null_query
=
NULL
;
}
DNSServiceConstructFullName
(
svc_name
,
buddy
->
name
,
ICHAT_SERVICE
,
"local"
);
if
(
kDNSServiceErr_NoError
==
DNSServiceQueryRecord
(
&
idata
->
null_query
,
0
,
kDNSServiceInterfaceIndexAny
,
svc_name
,
kDNSServiceType_NULL
,
kDNSServiceClass_IN
,
_mdns_record_query_callback
,
buddy
))
{
idata
->
null_query_handler
=
purple_input_add
(
DNSServiceRefSockFD
(
idata
->
null_query
),
PURPLE_INPUT_READ
,
_mdns_handle_event
,
idata
->
null_query
);
}
}