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.
/*
* purple - Bonjour 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 _WIN32
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<arpa/inet.h>
#else
#include
"libc_interface.h"
#endif
#include
<sys/types.h>
#include
<glib.h>
#include
<unistd.h>
#include
<fcntl.h>
#include
"internal.h"
#include
"network.h"
#include
"eventloop.h"
#include
"connection.h"
#include
"blist.h"
#include
"xmlnode.h"
#include
"debug.h"
#include
"notify.h"
#include
"util.h"
#include
"jabber.h"
#include
"parser.h"
#include
"bonjour.h"
#include
"buddy.h"
#define STREAM_END "</stream:stream>"
/* TODO: specify version='1.0' and send stream features */
#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http:
//etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
#if 0
/* this isn't used anywhere... */
static const char *
_font_size_purple_to_ichat(int size)
{
switch (size) {
case 1:
return "8";
case 2:
return "10";
case 3:
return "12";
case 4:
return "14";
case 5:
return "17";
case 6:
return "21";
case 7:
return "24";
}
return "12";
}
#endif
static
BonjourJabberConversation
*
bonjour_jabber_conv_new
()
{
BonjourJabberConversation
*
bconv
=
g_new0
(
BonjourJabberConversation
,
1
);
bconv
->
socket
=
-1
;
bconv
->
tx_buf
=
purple_circ_buffer_new
(
512
);
bconv
->
tx_handler
=
0
;
bconv
->
rx_handler
=
0
;
return
bconv
;
}
static
const
char
*
_font_size_ichat_to_purple
(
int
size
)
{
if
(
size
>
24
)
{
return
"7"
;
}
else
if
(
size
>=
21
)
{
return
"6"
;
}
else
if
(
size
>=
17
)
{
return
"5"
;
}
else
if
(
size
>=
14
)
{
return
"4"
;
}
else
if
(
size
>=
12
)
{
return
"3"
;
}
else
if
(
size
>=
10
)
{
return
"2"
;
}
return
"1"
;
}
static
void
_jabber_parse_and_write_message_to_ui
(
xmlnode
*
message_node
,
PurpleBuddy
*
pb
)
{
xmlnode
*
body_node
,
*
html_node
,
*
events_node
;
PurpleConnection
*
gc
=
pb
->
account
->
gc
;
char
*
body
,
*
html_body
=
NULL
;
const
char
*
ichat_balloon_color
=
NULL
;
const
char
*
ichat_text_color
=
NULL
;
const
char
*
font_face
=
NULL
;
const
char
*
font_size
=
NULL
;
const
char
*
font_color
=
NULL
;
gboolean
composing_event
=
FALSE
;
body_node
=
xmlnode_get_child
(
message_node
,
"body"
);
if
(
body_node
==
NULL
)
return
;
body
=
xmlnode_get_data
(
body_node
);
html_node
=
xmlnode_get_child
(
message_node
,
"html"
);
if
(
html_node
!=
NULL
)
{
xmlnode
*
html_body_node
;
html_body_node
=
xmlnode_get_child
(
html_node
,
"body"
);
if
(
html_body_node
!=
NULL
)
{
xmlnode
*
html_body_font_node
;
ichat_balloon_color
=
xmlnode_get_attrib
(
html_body_node
,
"ichatballooncolor"
);
ichat_text_color
=
xmlnode_get_attrib
(
html_body_node
,
"ichattextcolor"
);
html_body_font_node
=
xmlnode_get_child
(
html_body_node
,
"font"
);
if
(
html_body_font_node
!=
NULL
)
{
/* Types of messages sent by iChat */
font_face
=
xmlnode_get_attrib
(
html_body_font_node
,
"face"
);
/* The absolute iChat font sizes should be converted to 1..7 range */
font_size
=
xmlnode_get_attrib
(
html_body_font_node
,
"ABSZ"
);
if
(
font_size
!=
NULL
)
font_size
=
_font_size_ichat_to_purple
(
atoi
(
font_size
));
font_color
=
xmlnode_get_attrib
(
html_body_font_node
,
"color"
);
html_body
=
xmlnode_get_data
(
html_body_font_node
);
if
(
html_body
==
NULL
)
/* This is the kind of formated messages that Purple creates */
html_body
=
xmlnode_to_str
(
html_body_font_node
,
NULL
);
}
}
}
events_node
=
xmlnode_get_child_with_namespace
(
message_node
,
"x"
,
"jabber:x:event"
);
if
(
events_node
!=
NULL
)
{
if
(
xmlnode_get_child
(
events_node
,
"composing"
)
!=
NULL
)
composing_event
=
TRUE
;
if
(
xmlnode_get_child
(
events_node
,
"id"
)
!=
NULL
)
{
/* The user is just typing */
/* TODO: Deal with typing notification */
g_free
(
body
);
g_free
(
html_body
);
return
;
}
}
/* Compose the message */
if
(
html_body
!=
NULL
)
{
g_free
(
body
);
if
(
font_face
==
NULL
)
font_face
=
"Helvetica"
;
if
(
font_size
==
NULL
)
font_size
=
"3"
;
if
(
ichat_text_color
==
NULL
)
ichat_text_color
=
"#000000"
;
if
(
ichat_balloon_color
==
NULL
)
ichat_balloon_color
=
"#FFFFFF"
;
body
=
g_strdup_printf
(
"<font face='%s' size='%s' color='%s' back='%s'>%s</font>"
,
font_face
,
font_size
,
ichat_text_color
,
ichat_balloon_color
,
html_body
);
}
/* TODO: Should we do something with "composing_event" here? */
/* Send the message to the UI */
serv_got_im
(
gc
,
pb
->
name
,
body
,
0
,
time
(
NULL
));
g_free
(
body
);
g_free
(
html_body
);
}
struct
_check_buddy_by_address_t
{
const
char
*
address
;
PurpleBuddy
**
pb
;
BonjourJabber
*
bj
;
};
static
void
_check_buddy_by_address
(
gpointer
key
,
gpointer
value
,
gpointer
data
)
{
PurpleBuddy
*
pb
=
value
;
BonjourBuddy
*
bb
;
struct
_check_buddy_by_address_t
*
cbba
=
data
;
/*
* If the current PurpleBuddy's data is not null and the PurpleBuddy's account
* is the same as the account requesting the check then continue to determine
* whether the buddies IP matches the target IP.
*/
if
(
cbba
->
bj
->
account
==
pb
->
account
)
{
bb
=
pb
->
proto_data
;
if
((
bb
!=
NULL
)
&&
(
g_ascii_strcasecmp
(
bb
->
ip
,
cbba
->
address
)
==
0
))
*
(
cbba
->
pb
)
=
pb
;
}
}
static
void
_send_data_write_cb
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
PurpleBuddy
*
pb
=
data
;
BonjourBuddy
*
bb
=
pb
->
proto_data
;
BonjourJabberConversation
*
bconv
=
bb
->
conversation
;
int
ret
,
writelen
;
/* TODO: Make sure that the stream has been established before sending */
writelen
=
purple_circ_buffer_get_max_read
(
bconv
->
tx_buf
);
if
(
writelen
==
0
)
{
purple_input_remove
(
bconv
->
tx_handler
);
bconv
->
tx_handler
=
0
;
return
;
}
ret
=
send
(
bconv
->
socket
,
bconv
->
tx_buf
->
outptr
,
writelen
,
0
);
if
(
ret
<
0
&&
errno
==
EAGAIN
)
return
;
else
if
(
ret
<=
0
)
{
PurpleConversation
*
conv
;
const
char
*
error
=
strerror
(
errno
);
purple_debug_error
(
"bonjour"
,
"Error sending message to buddy %s error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
error
?
error
:
"(null)"
);
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
bb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
_
(
"Unable to send message."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
return
;
}
purple_circ_buffer_mark_read
(
bconv
->
tx_buf
,
ret
);
}
static
gint
_send_data
(
PurpleBuddy
*
pb
,
char
*
message
)
{
gint
ret
;
int
len
=
strlen
(
message
);
BonjourBuddy
*
bb
=
pb
->
proto_data
;
BonjourJabberConversation
*
bconv
=
bb
->
conversation
;
/* If we're not ready to actually send, append it to the buffer */
if
(
bconv
->
tx_handler
!=
0
||
bconv
->
connect_data
!=
NULL
||
!
bconv
->
sent_stream_start
||
!
bconv
->
recv_stream_start
||
purple_circ_buffer_get_max_read
(
bconv
->
tx_buf
)
>
0
)
{
ret
=
-1
;
errno
=
EAGAIN
;
}
else
{
ret
=
send
(
bconv
->
socket
,
message
,
len
,
0
);
}
if
(
ret
==
-1
&&
errno
==
EAGAIN
)
ret
=
0
;
else
if
(
ret
<=
0
)
{
PurpleConversation
*
conv
;
const
char
*
error
=
strerror
(
errno
);
purple_debug_error
(
"bonjour"
,
"Error sending message to buddy %s error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
error
?
error
:
"(null)"
);
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
bb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
_
(
"Unable to send message."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
return
-1
;
}
if
(
ret
<
len
)
{
if
(
bconv
->
tx_handler
==
0
)
bconv
->
tx_handler
=
purple_input_add
(
bconv
->
socket
,
PURPLE_INPUT_WRITE
,
_send_data_write_cb
,
pb
);
purple_circ_buffer_append
(
bconv
->
tx_buf
,
message
+
ret
,
len
-
ret
);
}
return
ret
;
}
void
bonjour_jabber_process_packet
(
PurpleBuddy
*
pb
,
xmlnode
*
packet
)
{
if
(
!
strcmp
(
packet
->
name
,
"message"
))
_jabber_parse_and_write_message_to_ui
(
packet
,
pb
);
else
purple_debug_warning
(
"bonjour"
,
"Unknown packet: %s
\n
"
,
packet
->
name
);
}
static
void
_client_socket_handler
(
gpointer
data
,
gint
socket
,
PurpleInputCondition
condition
)
{
PurpleBuddy
*
pb
=
data
;
gint
len
,
message_length
;
static
char
message
[
4096
];
/*TODO: use a static buffer */
/* Read the data from the socket */
if
((
len
=
recv
(
socket
,
message
,
sizeof
(
message
)
-
1
,
0
))
==
-1
)
{
/* There have been an error reading from the socket */
if
(
errno
!=
EAGAIN
)
{
BonjourBuddy
*
bb
=
pb
->
proto_data
;
purple_debug_warning
(
"bonjour"
,
"receive error: %s
\n
"
,
strerror
(
errno
));
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
/* I guess we really don't need to notify the user.
* If they try to send another message it'll reconnect */
}
return
;
}
else
if
(
len
==
0
)
{
/* The other end has closed the socket */
purple_debug_warning
(
"bonjour"
,
"Connection closed (without stream end) by %s.
\n
"
,
pb
->
name
);
bonjour_jabber_stream_ended
(
pb
);
return
;
}
else
{
message_length
=
len
;
message
[
message_length
]
=
'\0'
;
while
(
message_length
>
0
&&
g_ascii_iscntrl
(
message
[
message_length
-
1
]))
{
message
[
message_length
-
1
]
=
'\0'
;
message_length
--
;
}
}
purple_debug_info
(
"bonjour"
,
"Receive: -%s- %d bytes
\n
"
,
message
,
len
);
bonjour_parser_process
(
pb
,
message
,
message_length
);
}
void
bonjour_jabber_stream_ended
(
PurpleBuddy
*
pb
)
{
BonjourBuddy
*
bb
=
pb
->
proto_data
;
PurpleConversation
*
conv
;
purple_debug_info
(
"bonjour"
,
"Recieved conversation close notification from %s.
\n
"
,
pb
->
name
);
g_return_if_fail
(
bb
!=
NULL
);
/* Inform the user that the conversation has been closed */
if
(
bb
->
conversation
!=
NULL
)
{
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
pb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
{
char
*
tmp
=
g_strdup_printf
(
_
(
"%s has closed the conversation."
),
pb
->
name
);
purple_conversation_write
(
conv
,
NULL
,
tmp
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
tmp
);
}
/* Close the socket, clear the watcher and free memory */
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
}
}
void
bonjour_jabber_stream_started
(
PurpleBuddy
*
pb
)
{
BonjourBuddy
*
bb
=
pb
->
proto_data
;
BonjourJabberConversation
*
bconv
=
bb
->
conversation
;
/* If the stream has been completely started, we can start doing stuff */
if
(
bconv
->
sent_stream_start
&&
bconv
->
recv_stream_start
&&
purple_circ_buffer_get_max_read
(
bconv
->
tx_buf
)
>
0
)
{
/* Watch for when we can write the buffered messages */
bconv
->
tx_handler
=
purple_input_add
(
bconv
->
socket
,
PURPLE_INPUT_WRITE
,
_send_data_write_cb
,
pb
);
/* We can probably write the data right now. */
_send_data_write_cb
(
pb
,
bconv
->
socket
,
PURPLE_INPUT_WRITE
);
}
}
struct
_stream_start_data
{
char
*
msg
;
};
static
void
_start_stream
(
gpointer
data
,
gint
source
,
PurpleInputCondition
condition
)
{
PurpleBuddy
*
pb
=
data
;
BonjourBuddy
*
bb
=
pb
->
proto_data
;
BonjourJabberConversation
*
bconv
=
bb
->
conversation
;
struct
_stream_start_data
*
ss
=
bconv
->
stream_data
;
int
len
,
ret
;
len
=
strlen
(
ss
->
msg
);
/* Start Stream */
ret
=
send
(
source
,
ss
->
msg
,
len
,
0
);
if
(
ret
==
-1
&&
errno
==
EAGAIN
)
return
;
else
if
(
ret
<=
0
)
{
const
char
*
err
=
strerror
(
errno
);
PurpleConversation
*
conv
;
purple_debug_error
(
"bonjour"
,
"Error starting stream with buddy %s at %s:%d error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
bb
->
ip
?
bb
->
ip
:
"(null)"
,
bb
->
port_p2pj
,
err
?
err
:
"(null)"
);
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
bb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
_
(
"Unable to send the message, the conversation couldn't be started."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
bonjour_jabber_close_conversation
(
bconv
);
bb
->
conversation
=
NULL
;
return
;
}
/* This is EXTREMELY unlikely to happen */
if
(
ret
<
len
)
{
char
*
tmp
=
g_strdup
(
ss
->
msg
+
ret
);
g_free
(
ss
->
msg
);
ss
->
msg
=
tmp
;
return
;
}
g_free
(
ss
->
msg
);
g_free
(
ss
);
bconv
->
stream_data
=
NULL
;
/* Stream started; process the send buffer if there is one */
purple_input_remove
(
bconv
->
tx_handler
);
bconv
->
tx_handler
=
0
;
bconv
->
sent_stream_start
=
TRUE
;
bonjour_jabber_stream_started
(
pb
);
}
static
gboolean
bonjour_jabber_stream_init
(
PurpleBuddy
*
pb
,
int
client_socket
)
{
int
ret
,
len
;
char
*
stream_start
;
BonjourBuddy
*
bb
=
pb
->
proto_data
;
stream_start
=
g_strdup_printf
(
DOCTYPE
,
purple_account_get_username
(
pb
->
account
),
purple_buddy_get_name
(
pb
));
len
=
strlen
(
stream_start
);
/* Start the stream */
ret
=
send
(
client_socket
,
stream_start
,
len
,
0
);
if
(
ret
==
-1
&&
errno
==
EAGAIN
)
ret
=
0
;
else
if
(
ret
<=
0
)
{
const
char
*
err
=
strerror
(
errno
);
purple_debug_error
(
"bonjour"
,
"Error starting stream with buddy %s at %s:%d error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
bb
->
ip
?
bb
->
ip
:
"(null)"
,
bb
->
port_p2pj
,
err
?
err
:
"(null)"
);
close
(
client_socket
);
g_free
(
stream_start
);
return
FALSE
;
}
/* This is unlikely to happen */
if
(
ret
<
len
)
{
struct
_stream_start_data
*
ss
=
g_new
(
struct
_stream_start_data
,
1
);
ss
->
msg
=
g_strdup
(
stream_start
+
ret
);
bb
->
conversation
->
stream_data
=
ss
;
/* Finish sending the stream start */
bb
->
conversation
->
tx_handler
=
purple_input_add
(
client_socket
,
PURPLE_INPUT_WRITE
,
_start_stream
,
pb
);
}
else
bb
->
conversation
->
sent_stream_start
=
TRUE
;
g_free
(
stream_start
);
/* setup the parser fresh for each stream */
bonjour_parser_setup
(
bb
->
conversation
);
bb
->
conversation
->
socket
=
client_socket
;
bb
->
conversation
->
rx_handler
=
purple_input_add
(
client_socket
,
PURPLE_INPUT_READ
,
_client_socket_handler
,
pb
);
return
TRUE
;
}
static
void
_server_socket_handler
(
gpointer
data
,
int
server_socket
,
PurpleInputCondition
condition
)
{
PurpleBuddy
*
pb
=
NULL
;
struct
sockaddr_in
their_addr
;
/* connector's address information */
socklen_t
sin_size
=
sizeof
(
struct
sockaddr
);
int
client_socket
;
BonjourBuddy
*
bb
;
char
*
address_text
=
NULL
;
PurpleBuddyList
*
bl
=
purple_get_blist
();
struct
_check_buddy_by_address_t
*
cbba
;
/* Check that it is a read condition */
if
(
condition
!=
PURPLE_INPUT_READ
)
return
;
if
((
client_socket
=
accept
(
server_socket
,
(
struct
sockaddr
*
)
&
their_addr
,
&
sin_size
))
==
-1
)
return
;
fcntl
(
client_socket
,
F_SETFL
,
O_NONBLOCK
);
/* Look for the buddy that has opened the conversation and fill information */
address_text
=
inet_ntoa
(
their_addr
.
sin_addr
);
purple_debug_info
(
"bonjour"
,
"Received incoming connection from %s.
\n
"
,
address_text
);
cbba
=
g_new0
(
struct
_check_buddy_by_address_t
,
1
);
cbba
->
address
=
address_text
;
cbba
->
pb
=
&
pb
;
cbba
->
bj
=
data
;
g_hash_table_foreach
(
bl
->
buddies
,
_check_buddy_by_address
,
cbba
);
g_free
(
cbba
);
if
(
pb
==
NULL
)
{
purple_debug_info
(
"bonjour"
,
"We don't like invisible buddies, this is not a superheros comic
\n
"
);
close
(
client_socket
);
return
;
}
bb
=
pb
->
proto_data
;
/* Check if the conversation has been previously started */
if
(
bb
->
conversation
==
NULL
)
{
bb
->
conversation
=
bonjour_jabber_conv_new
();
if
(
!
bonjour_jabber_stream_init
(
pb
,
client_socket
))
{
close
(
client_socket
);
return
;
}
}
else
{
purple_debug_warning
(
"bonjour"
,
"Ignoring incoming connection because an existing connection exists.
\n
"
);
close
(
client_socket
);
}
}
gint
bonjour_jabber_start
(
BonjourJabber
*
data
)
{
struct
sockaddr_in
my_addr
;
int
yes
=
1
;
int
i
;
gboolean
bind_successful
;
/* Open a listening socket for incoming conversations */
if
((
data
->
socket
=
socket
(
PF_INET
,
SOCK_STREAM
,
0
))
<
0
)
{
purple_debug_error
(
"bonjour"
,
"Cannot open socket: %s
\n
"
,
strerror
(
errno
));
purple_connection_error_reason
(
data
->
account
->
gc
,
PURPLE_REASON_NETWORK_ERROR
,
_
(
"Cannot open socket"
));
return
-1
;
}
/* Make the socket reusable */
if
(
setsockopt
(
data
->
socket
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
yes
,
sizeof
(
int
))
!=
0
)
{
purple_debug_error
(
"bonjour"
,
"Error setting socket options: %s
\n
"
,
strerror
(
errno
));
purple_connection_error_reason
(
data
->
account
->
gc
,
PURPLE_REASON_NETWORK_ERROR
,
_
(
"Error setting socket options"
));
return
-1
;
}
memset
(
&
my_addr
,
0
,
sizeof
(
struct
sockaddr_in
));
my_addr
.
sin_family
=
PF_INET
;
/* Attempt to find a free port */
bind_successful
=
FALSE
;
for
(
i
=
0
;
i
<
10
;
i
++
)
{
my_addr
.
sin_port
=
htons
(
data
->
port
);
if
(
bind
(
data
->
socket
,
(
struct
sockaddr
*
)
&
my_addr
,
sizeof
(
struct
sockaddr
))
==
0
)
{
bind_successful
=
TRUE
;
break
;
}
data
->
port
++
;
}
/* On no! We tried 10 ports and could not bind to ANY of them */
if
(
!
bind_successful
)
{
purple_debug_error
(
"bonjour"
,
"Cannot bind socket: %s
\n
"
,
strerror
(
errno
));
purple_connection_error_reason
(
data
->
account
->
gc
,
PURPLE_REASON_NETWORK_ERROR
,
_
(
"Could not bind socket to port"
));
return
-1
;
}
/* Attempt to listen on the bound socket */
if
(
listen
(
data
->
socket
,
10
)
!=
0
)
{
purple_debug_error
(
"bonjour"
,
"Cannot listen on socket: %s
\n
"
,
strerror
(
errno
));
purple_connection_error_reason
(
data
->
account
->
gc
,
PURPLE_REASON_NETWORK_ERROR
,
_
(
"Could not listen on socket"
));
return
-1
;
}
#if 0
/* TODO: Why isn't this being used? */
data->socket = purple_network_listen(data->port, SOCK_STREAM);
if (data->socket == -1)
{
purple_debug_error("bonjour", "No se ha podido crear el socket\n");
}
#endif
/* Open a watcher in the socket we have just opened */
data
->
watcher_id
=
purple_input_add
(
data
->
socket
,
PURPLE_INPUT_READ
,
_server_socket_handler
,
data
);
return
data
->
port
;
}
static
void
_connected_to_buddy
(
gpointer
data
,
gint
source
,
const
gchar
*
error
)
{
PurpleBuddy
*
pb
=
data
;
BonjourBuddy
*
bb
=
pb
->
proto_data
;
bb
->
conversation
->
connect_data
=
NULL
;
if
(
source
<
0
)
{
PurpleConversation
*
conv
;
purple_debug_error
(
"bonjour"
,
"Error connecting to buddy %s at %s:%d error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
bb
->
ip
?
bb
->
ip
:
"(null)"
,
bb
->
port_p2pj
,
error
?
error
:
"(null)"
);
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
bb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
_
(
"Unable to send the message, the conversation couldn't be started."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
return
;
}
if
(
!
bonjour_jabber_stream_init
(
pb
,
source
))
{
const
char
*
err
=
strerror
(
errno
);
PurpleConversation
*
conv
;
purple_debug_error
(
"bonjour"
,
"Error starting stream with buddy %s at %s:%d error: %s
\n
"
,
purple_buddy_get_name
(
pb
),
bb
->
ip
?
bb
->
ip
:
"(null)"
,
bb
->
port_p2pj
,
err
?
err
:
"(null)"
);
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
bb
->
name
,
pb
->
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
_
(
"Unable to send the message, the conversation couldn't be started."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
close
(
source
);
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
return
;
}
}
int
bonjour_jabber_send_message
(
BonjourJabber
*
data
,
const
gchar
*
to
,
const
gchar
*
body
)
{
xmlnode
*
message_node
,
*
node
,
*
node2
;
gchar
*
message
;
PurpleBuddy
*
pb
;
BonjourBuddy
*
bb
;
int
ret
;
pb
=
purple_find_buddy
(
data
->
account
,
to
);
if
(
pb
==
NULL
)
{
purple_debug_info
(
"bonjour"
,
"Can't send a message to an offline buddy (%s).
\n
"
,
to
);
/* You can not send a message to an offline buddy */
return
-10000
;
}
bb
=
pb
->
proto_data
;
/* Check if there is a previously open conversation */
if
(
bb
->
conversation
==
NULL
)
{
PurpleProxyConnectData
*
connect_data
;
PurpleProxyInfo
*
proxy_info
;
/* Make sure that the account always has a proxy of "none".
* This is kind of dirty, but proxy_connect_none() isn't exposed. */
proxy_info
=
purple_account_get_proxy_info
(
data
->
account
);
if
(
proxy_info
==
NULL
)
{
proxy_info
=
purple_proxy_info_new
();
purple_account_set_proxy_info
(
data
->
account
,
proxy_info
);
}
purple_proxy_info_set_type
(
proxy_info
,
PURPLE_PROXY_NONE
);
connect_data
=
purple_proxy_connect
(
data
->
account
->
gc
,
data
->
account
,
bb
->
ip
,
bb
->
port_p2pj
,
_connected_to_buddy
,
pb
);
if
(
connect_data
==
NULL
)
{
purple_debug_error
(
"bonjour"
,
"Unable to connect to buddy (%s).
\n
"
,
to
);
return
-10001
;
}
bb
->
conversation
=
bonjour_jabber_conv_new
();
bb
->
conversation
->
connect_data
=
connect_data
;
/* We don't want _send_data() to register the tx_handler;
* that neeeds to wait until we're actually connected. */
bb
->
conversation
->
tx_handler
=
0
;
}
message_node
=
xmlnode_new
(
"message"
);
xmlnode_set_attrib
(
message_node
,
"to"
,
bb
->
name
);
xmlnode_set_attrib
(
message_node
,
"from"
,
purple_account_get_username
(
data
->
account
));
xmlnode_set_attrib
(
message_node
,
"type"
,
"chat"
);
/* Enclose the message from the UI within a "font" node */
node
=
xmlnode_new_child
(
message_node
,
"body"
);
message
=
purple_markup_strip_html
(
body
);
xmlnode_insert_data
(
node
,
message
,
strlen
(
message
));
g_free
(
message
);
node
=
xmlnode_new_child
(
message_node
,
"html"
);
xmlnode_set_namespace
(
node
,
"http://www.w3.org/1999/xhtml"
);
node
=
xmlnode_new_child
(
node
,
"body"
);
message
=
g_strdup_printf
(
"<font>%s</font>"
,
body
);
node2
=
xmlnode_from_str
(
message
,
strlen
(
message
));
g_free
(
message
);
xmlnode_insert_child
(
node
,
node2
);
node
=
xmlnode_new_child
(
message_node
,
"x"
);
xmlnode_set_namespace
(
node
,
"jabber:x:event"
);
xmlnode_insert_child
(
node
,
xmlnode_new
(
"composing"
));
message
=
xmlnode_to_str
(
message_node
,
NULL
);
xmlnode_free
(
message_node
);
ret
=
_send_data
(
pb
,
message
)
>=
0
;
g_free
(
message
);
return
ret
;
}
void
bonjour_jabber_close_conversation
(
BonjourJabberConversation
*
bconv
)
{
if
(
bconv
!=
NULL
)
{
/* Close the socket and remove the watcher */
if
(
bconv
->
socket
>=
0
)
{
/* Send the end of the stream to the other end of the conversation */
if
(
bconv
->
sent_stream_start
)
send
(
bconv
->
socket
,
STREAM_END
,
strlen
(
STREAM_END
),
0
);
/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
close
(
bconv
->
socket
);
}
if
(
bconv
->
rx_handler
!=
0
)
purple_input_remove
(
bconv
->
rx_handler
);
if
(
bconv
->
tx_handler
>
0
)
purple_input_remove
(
bconv
->
tx_handler
);
/* Free all the data related to the conversation */
purple_circ_buffer_destroy
(
bconv
->
tx_buf
);
if
(
bconv
->
connect_data
!=
NULL
)
purple_proxy_connect_cancel
(
bconv
->
connect_data
);
if
(
bconv
->
stream_data
!=
NULL
)
{
struct
_stream_start_data
*
ss
=
bconv
->
stream_data
;
g_free
(
ss
->
msg
);
g_free
(
ss
);
}
if
(
bconv
->
context
!=
NULL
)
bonjour_parser_setup
(
bconv
);
g_free
(
bconv
);
}
}
void
bonjour_jabber_stop
(
BonjourJabber
*
data
)
{
/* Close the server socket and remove the watcher */
if
(
data
->
socket
>=
0
)
close
(
data
->
socket
);
if
(
data
->
watcher_id
>
0
)
purple_input_remove
(
data
->
watcher_id
);
/* Close all the conversation sockets and remove all the watchers after sending end streams */
if
(
data
->
account
->
gc
!=
NULL
)
{
GSList
*
buddies
,
*
l
;
buddies
=
purple_find_buddies
(
data
->
account
,
purple_account_get_username
(
data
->
account
));
for
(
l
=
buddies
;
l
;
l
=
l
->
next
)
{
BonjourBuddy
*
bb
=
((
PurpleBuddy
*
)
l
->
data
)
->
proto_data
;
bonjour_jabber_close_conversation
(
bb
->
conversation
);
bb
->
conversation
=
NULL
;
}
g_slist_free
(
buddies
);
}
}