2 * purple - Bonjour Protocol Plugin
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/types.h>
36 #if defined (__SVR4) && defined (__sun)
37 #include <sys/sockio.h>
46 #ifdef HAVE_GETIFADDRS
54 #include "bonjour_ft.h"
56 #ifdef _SIZEOF_ADDR_IFREQ
57 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
59 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
62 #define STREAM_END "</stream:stream>"
63 /* TODO: specify version='1.0' and send stream features */
64 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
65 "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
67 enum sent_stream_start_types
{
74 xep_iq_parse(PurpleXmlNode
*packet
, PurpleBuddy
*pb
);
76 static BonjourJabberConversation
*
77 bonjour_jabber_conv_new(PurpleBuddy
*pb
, PurpleAccount
*account
, const char *ip
) {
79 BonjourJabberConversation
*bconv
= g_new0(BonjourJabberConversation
, 1);
80 bconv
->cancellable
= g_cancellable_new();
81 bconv
->tx_buf
= purple_circular_buffer_new(512);
82 bconv
->tx_handler
= 0;
83 bconv
->rx_handler
= 0;
85 bconv
->account
= account
;
86 bconv
->ip
= g_strdup(ip
);
88 bonjour_parser_setup(bconv
);
94 _font_size_ichat_to_purple(int size
)
98 } else if (size
>= 21) {
100 } else if (size
>= 17) {
102 } else if (size
>= 14) {
104 } else if (size
>= 12) {
106 } else if (size
>= 10) {
114 get_xmlnode_contents(PurpleXmlNode
*node
)
118 contents
= purple_xmlnode_to_str(node
, NULL
);
120 /* we just want the stuff inside <font></font>
121 * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
124 char *bodystart
= strchr(contents
, '>');
125 char *bodyend
= bodystart
? strrchr(bodystart
, '<') : NULL
;
126 if (bodystart
&& bodyend
&& (bodystart
+ 1) != bodyend
) {
128 memmove(contents
, bodystart
+ 1, (bodyend
- bodystart
));
136 _jabber_parse_and_write_message_to_ui(PurpleXmlNode
*message_node
, PurpleBuddy
*pb
)
138 PurpleXmlNode
*body_node
, *html_node
, *events_node
;
139 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(pb
));
142 body_node
= purple_xmlnode_get_child(message_node
, "body");
143 html_node
= purple_xmlnode_get_child(message_node
, "html");
145 if (body_node
== NULL
&& html_node
== NULL
) {
146 purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
150 events_node
= purple_xmlnode_get_child_with_namespace(message_node
, "x", "jabber:x:event");
151 if (events_node
!= NULL
) {
152 if (purple_xmlnode_get_child(events_node
, "id") != NULL
) {
153 /* The user is just typing */
154 /* TODO: Deal with typing notification */
159 if (html_node
!= NULL
) {
160 PurpleXmlNode
*html_body_node
;
162 html_body_node
= purple_xmlnode_get_child(html_node
, "body");
163 if (html_body_node
!= NULL
) {
164 PurpleXmlNode
*html_body_font_node
;
166 html_body_font_node
= purple_xmlnode_get_child(html_body_node
, "font");
167 /* Types of messages sent by iChat */
168 if (html_body_font_node
!= NULL
) {
170 const char *font_face
, *font_size
, *font_color
,
171 *ichat_balloon_color
, *ichat_text_color
;
173 font_face
= purple_xmlnode_get_attrib(html_body_font_node
, "face");
174 /* The absolute iChat font sizes should be converted to 1..7 range */
175 font_size
= purple_xmlnode_get_attrib(html_body_font_node
, "ABSZ");
176 if (font_size
!= NULL
)
177 font_size
= _font_size_ichat_to_purple(atoi(font_size
));
178 font_color
= purple_xmlnode_get_attrib(html_body_font_node
, "color");
179 ichat_balloon_color
= purple_xmlnode_get_attrib(html_body_node
, "ichatballooncolor");
180 ichat_text_color
= purple_xmlnode_get_attrib(html_body_node
, "ichattextcolor");
182 html_body
= get_xmlnode_contents(html_body_font_node
);
184 if (html_body
== NULL
)
185 /* This is the kind of formatted messages that Purple creates */
186 html_body
= purple_xmlnode_to_str(html_body_font_node
, NULL
);
188 if (html_body
!= NULL
) {
189 GString
*str
= g_string_new("<font");
192 g_string_append_printf(str
, " face='%s'", font_face
);
194 g_string_append_printf(str
, " size='%s'", font_size
);
196 g_string_append_printf(str
, " color='%s'", font_color
);
197 else if (ichat_text_color
)
198 g_string_append_printf(str
, " color='%s'", ichat_text_color
);
199 if (ichat_balloon_color
)
200 g_string_append_printf(str
, " back='%s'", ichat_balloon_color
);
201 g_string_append_printf(str
, ">%s</font>", html_body
);
203 body
= g_string_free(str
, FALSE
);
211 /* Compose the message */
212 if (body
== NULL
&& body_node
!= NULL
)
213 body
= purple_xmlnode_get_data(body_node
);
216 purple_debug_error("bonjour", "No html body or regular body found.\n");
220 /* Send the message to the UI */
221 purple_serv_got_im(gc
, purple_buddy_get_name(pb
), body
, 0, time(NULL
));
226 struct _match_buddies_by_address
{
228 GSList
*matched_buddies
;
232 _match_buddies_by_address(gpointer value
, gpointer data
)
234 PurpleBuddy
*pb
= value
;
235 BonjourBuddy
*bb
= NULL
;
236 struct _match_buddies_by_address
*mbba
= data
;
238 bb
= purple_buddy_get_protocol_data(pb
);
241 * If the current PurpleBuddy's data is not null, then continue to determine
242 * whether one of the buddies IPs matches the target IP.
247 GSList
*tmp
= bb
->ips
;
251 if (ip
!= NULL
&& g_ascii_strcasecmp(ip
, mbba
->address
) == 0) {
252 mbba
->matched_buddies
= g_slist_prepend(mbba
->matched_buddies
, pb
);
261 _send_data_write_cb(GObject
*stream
, gpointer data
)
263 PurpleBuddy
*pb
= data
;
264 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
265 BonjourJabberConversation
*bconv
= bb
->conversation
;
268 GError
*error
= NULL
;
270 writelen
= purple_circular_buffer_get_max_read(bconv
->tx_buf
);
273 g_source_remove(bconv
->tx_handler
);
274 bconv
->tx_handler
= 0;
278 ret
= g_pollable_output_stream_write_nonblocking(
279 G_POLLABLE_OUTPUT_STREAM(stream
),
280 purple_circular_buffer_get_output(bconv
->tx_buf
), writelen
,
281 bconv
->cancellable
, &error
);
283 if (ret
< 0 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
284 g_clear_error(&error
);
286 } else if (ret
<= 0) {
287 PurpleConversation
*conv
= NULL
;
288 PurpleAccount
*account
= NULL
;
292 "Error sending message to buddy %s error: %s",
293 purple_buddy_get_name(pb
),
294 error
? error
->message
: "(null)");
296 account
= purple_buddy_get_account(pb
);
298 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
300 purple_conversation_write_system_message(conv
,
301 _("Unable to send message."),
302 PURPLE_MESSAGE_ERROR
);
304 bonjour_jabber_close_conversation(bb
->conversation
);
305 bb
->conversation
= NULL
;
306 g_clear_error(&error
);
310 purple_circular_buffer_mark_read(bconv
->tx_buf
, ret
);
314 _send_data(PurpleBuddy
*pb
, char *message
)
316 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
317 BonjourJabberConversation
*bconv
= bb
->conversation
;
318 gsize len
= strlen(message
);
320 GError
*error
= NULL
;
322 /* If we're not ready to actually send, append it to the buffer */
323 if (bconv
->tx_handler
!= 0
324 || bconv
->sent_stream_start
!= FULLY_SENT
325 || !bconv
->recv_stream_start
326 || purple_circular_buffer_get_max_read(bconv
->tx_buf
) > 0) {
328 g_set_error_literal(&error
, G_IO_ERROR
, G_IO_ERROR_WOULD_BLOCK
,
329 "Not yet ready to send.");
331 ret
= g_pollable_output_stream_write_nonblocking(
332 G_POLLABLE_OUTPUT_STREAM(bconv
->output
), message
, len
,
333 bconv
->cancellable
, &error
);
336 if (ret
== -1 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
338 g_clear_error(&error
);
339 } else if (ret
<= 0) {
340 PurpleConversation
*conv
;
341 PurpleAccount
*account
;
345 "Error sending message to buddy %s error: %s",
346 purple_buddy_get_name(pb
),
347 error
? error
->message
: "(null)");
349 account
= purple_buddy_get_account(pb
);
351 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
353 purple_conversation_write_system_message(conv
,
354 _("Unable to send message."),
355 PURPLE_MESSAGE_ERROR
);
357 bonjour_jabber_close_conversation(bb
->conversation
);
358 bb
->conversation
= NULL
;
359 g_clear_error(&error
);
364 /* Don't interfere with the stream starting */
365 if (bconv
->sent_stream_start
== FULLY_SENT
&&
366 bconv
->recv_stream_start
&& bconv
->tx_handler
== 0) {
368 g_pollable_output_stream_create_source(
369 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
371 g_source_set_callback(source
,
372 (GSourceFunc
)_send_data_write_cb
,
374 bconv
->tx_handler
= g_source_attach(source
, NULL
);
376 purple_circular_buffer_append(bconv
->tx_buf
, message
+ ret
, len
- ret
);
382 void bonjour_jabber_process_packet(PurpleBuddy
*pb
, PurpleXmlNode
*packet
) {
384 g_return_if_fail(packet
!= NULL
);
385 g_return_if_fail(pb
!= NULL
);
387 if (purple_strequal(packet
->name
, "message"))
388 _jabber_parse_and_write_message_to_ui(packet
, pb
);
389 else if (purple_strequal(packet
->name
, "iq"))
390 xep_iq_parse(packet
, pb
);
392 purple_debug_warning("bonjour", "Unknown packet: %s\n",
393 packet
->name
? packet
->name
: "(null)");
397 static void bonjour_jabber_stream_ended(BonjourJabberConversation
*bconv
) {
399 /* Inform the user that the conversation has been closed */
400 BonjourBuddy
*bb
= NULL
;
401 const gchar
*name
= bconv
->pb
? purple_buddy_get_name(bconv
->pb
) : "(unknown)";
403 purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name
);
405 if(bconv
->pb
!= NULL
)
406 bb
= purple_buddy_get_protocol_data(bconv
->pb
);
408 /* Close the socket, clear the watcher and free memory */
409 bonjour_jabber_close_conversation(bconv
);
411 bb
->conversation
= NULL
;
415 _client_socket_handler(GObject
*stream
, gpointer data
)
417 BonjourJabberConversation
*bconv
= data
;
418 GError
*error
= NULL
;
420 static char message
[4096];
422 /* Read the data from the socket */
423 len
= g_pollable_input_stream_read_nonblocking(
424 G_POLLABLE_INPUT_STREAM(stream
), message
, sizeof(message
) - 1,
425 bconv
->cancellable
, &error
);
427 /* There has been an error reading from the socket */
428 if (error
== NULL
|| (error
->code
!= G_IO_ERROR_WOULD_BLOCK
&&
429 error
->code
!= G_IO_ERROR_CANCELLED
)) {
430 purple_debug_warning(
432 "receive of %" G_GSSIZE_FORMAT
" error: %s",
433 len
, error
? error
->message
: "(null)");
435 bonjour_jabber_close_conversation(bconv
);
436 if (bconv
->pb
!= NULL
) {
437 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(bconv
->pb
);
440 bb
->conversation
= NULL
;
443 /* I guess we really don't need to notify the user.
444 * If they try to send another message it'll reconnect */
446 g_clear_error(&error
);
448 } else if (len
== 0) { /* The other end has closed the socket */
449 const gchar
*name
= purple_buddy_get_name(bconv
->pb
);
450 purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name
) ? name
: "(unknown)");
451 bonjour_jabber_stream_ended(bconv
);
457 purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT
" bytes\n", message
, len
);
458 bonjour_parser_process(bconv
, message
, len
);
463 struct _stream_start_data
{
468 _start_stream(GObject
*stream
, gpointer data
)
470 BonjourJabberConversation
*bconv
= data
;
471 struct _stream_start_data
*ss
= bconv
->stream_data
;
472 GError
*error
= NULL
;
476 len
= strlen(ss
->msg
);
479 ret
= g_pollable_output_stream_write_nonblocking(
480 G_POLLABLE_OUTPUT_STREAM(stream
), ss
->msg
, len
,
481 bconv
->cancellable
, &error
);
483 if (ret
== -1 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
484 g_clear_error(&error
);
486 } else if (ret
<= 0) {
487 PurpleConversation
*conv
;
488 const char *bname
= bconv
->buddy_name
;
489 BonjourBuddy
*bb
= NULL
;
492 bb
= purple_buddy_get_protocol_data(bconv
->pb
);
493 bname
= purple_buddy_get_name(bconv
->pb
);
498 "Error starting stream with buddy %s at %s error: %s",
499 bname
? bname
: "(unknown)", bconv
->ip
,
500 error
? error
->message
: "(null)");
502 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
504 purple_conversation_write_system_message(conv
,
505 _("Unable to send the message, the conversation couldn't be started."),
506 PURPLE_MESSAGE_ERROR
);
508 bonjour_jabber_close_conversation(bconv
);
510 bb
->conversation
= NULL
;
512 g_clear_error(&error
);
516 /* This is EXTREMELY unlikely to happen */
518 char *tmp
= g_strdup(ss
->msg
+ ret
);
526 bconv
->stream_data
= NULL
;
528 /* Stream started; process the send buffer if there is one */
529 g_source_remove(bconv
->tx_handler
);
530 bconv
->tx_handler
= 0;
531 bconv
->sent_stream_start
= FULLY_SENT
;
533 bonjour_jabber_stream_started(bconv
);
537 bonjour_jabber_send_stream_init(BonjourJabberConversation
*bconv
,
543 const char *bname
= bconv
->buddy_name
;
545 g_return_val_if_fail(error
!= NULL
, FALSE
);
547 if (bconv
->pb
!= NULL
)
548 bname
= purple_buddy_get_name(bconv
->pb
);
550 /* If we have no idea who "to" is, use an empty string.
551 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
555 stream_start
= g_strdup_printf(DOCTYPE
, bonjour_get_jid(bconv
->account
), bname
);
556 len
= strlen(stream_start
);
558 bconv
->sent_stream_start
= PARTIALLY_SENT
;
560 /* Start the stream */
561 ret
= g_pollable_output_stream_write_nonblocking(
562 G_POLLABLE_OUTPUT_STREAM(bconv
->output
), stream_start
, len
,
563 bconv
->cancellable
, error
);
564 if (ret
== -1 && (*error
)->code
== G_IO_ERROR_WOULD_BLOCK
) {
566 g_clear_error(error
);
567 } else if (ret
<= 0) {
570 "Error starting stream with buddy %s at %s error: %s",
571 (*bname
) ? bname
: "(unknown)", bconv
->ip
,
572 *error
? (*error
)->message
: "(null)");
575 PurpleConversation
*conv
;
576 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
578 purple_conversation_write_system_message(conv
,
579 _("Unable to send the message, the conversation couldn't be started."),
580 PURPLE_MESSAGE_ERROR
);
583 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
584 G_INPUT_STREAM(bconv
->input
),
585 G_OUTPUT_STREAM(bconv
->output
));
586 g_clear_object(&bconv
->socket
);
588 bconv
->output
= NULL
;
589 g_free(stream_start
);
594 /* This is unlikely to happen */
597 struct _stream_start_data
*ss
= g_new(struct _stream_start_data
, 1);
598 ss
->msg
= g_strdup(stream_start
+ ret
);
599 bconv
->stream_data
= ss
;
600 /* Finish sending the stream start */
601 source
= g_pollable_output_stream_create_source(
602 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
604 g_source_set_callback(source
, (GSourceFunc
)_start_stream
, bconv
,
606 bconv
->tx_handler
= g_source_attach(source
, NULL
);
608 bconv
->sent_stream_start
= FULLY_SENT
;
611 g_free(stream_start
);
616 /* This gets called when we've successfully sent our <stream:stream />
617 * AND when we've received a <stream:stream /> */
619 bonjour_jabber_stream_started(BonjourJabberConversation
*bconv
)
621 GError
*error
= NULL
;
623 if (bconv
->sent_stream_start
== NOT_SENT
&&
624 !bonjour_jabber_send_stream_init(bconv
, &error
)) {
625 const char *bname
= bconv
->buddy_name
;
628 bname
= purple_buddy_get_name(bconv
->pb
);
632 "Error starting stream with buddy %s at %s error: %s",
633 bname
? bname
: "(unknown)", bconv
->ip
,
634 error
? error
->message
: "(null)");
637 PurpleConversation
*conv
;
638 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
640 purple_conversation_write_system_message(conv
,
641 _("Unable to send the message, the conversation couldn't be started."),
642 PURPLE_MESSAGE_ERROR
);
645 /* We don't want to recieve anything else */
646 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
647 G_INPUT_STREAM(bconv
->input
),
648 G_OUTPUT_STREAM(bconv
->output
));
649 g_clear_object(&bconv
->socket
);
651 bconv
->output
= NULL
;
653 /* This must be asynchronous because it destroys the parser and we
654 * may be in the middle of parsing.
656 async_bonjour_jabber_close_conversation(bconv
);
657 g_clear_error(&error
);
661 /* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
662 /* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
663 if (bconv
->sent_stream_start
== FULLY_SENT
&& bconv
->recv_stream_start
664 && bconv
->pb
&& purple_circular_buffer_get_max_read(bconv
->tx_buf
) > 0) {
665 /* Watch for when we can write the buffered messages */
666 GSource
*source
= g_pollable_output_stream_create_source(
667 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
669 g_source_set_callback(source
, (GSourceFunc
)_send_data_write_cb
,
671 bconv
->tx_handler
= g_source_attach(source
, NULL
);
672 /* We can probably write the data right now. */
673 _send_data_write_cb(G_OBJECT(bconv
->output
), bconv
->pb
);
677 #ifndef INET6_ADDRSTRLEN
678 #define INET6_ADDRSTRLEN 46
682 _server_socket_handler(GSocketService
*service
, GSocketConnection
*connection
,
683 GObject
*source_object
, gpointer data
)
685 BonjourJabber
*jdata
= data
;
686 GSocketAddress
*their_addr
; /* connector's address information */
687 GInetAddress
*their_inet_addr
;
689 struct _match_buddies_by_address
*mbba
;
690 BonjourJabberConversation
*bconv
;
694 their_addr
= g_socket_connection_get_remote_address(connection
, NULL
);
695 if (their_addr
== NULL
) {
698 their_inet_addr
= g_inet_socket_address_get_address(
699 G_INET_SOCKET_ADDRESS(their_addr
));
701 /* Look for the buddy that has opened the conversation and fill information */
702 address_text
= g_inet_address_to_string(their_inet_addr
);
703 if (g_inet_address_get_family(their_inet_addr
) ==
704 G_SOCKET_FAMILY_IPV6
&&
705 g_inet_address_get_is_link_local(their_inet_addr
)) {
706 gchar
*tmp
= g_strdup_printf(
707 "%s%%%d", address_text
,
708 g_inet_socket_address_get_scope_id(
709 G_INET_SOCKET_ADDRESS(their_addr
)));
710 g_free(address_text
);
713 g_object_unref(their_addr
);
715 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text
);
716 mbba
= g_new0(struct _match_buddies_by_address
, 1);
717 mbba
->address
= address_text
;
719 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
720 g_slist_foreach(buddies
, _match_buddies_by_address
, mbba
);
721 g_slist_free(buddies
);
723 if (mbba
->matched_buddies
== NULL
) {
724 purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
725 g_free(address_text
);
730 g_slist_free(mbba
->matched_buddies
);
733 /* We've established that this *could* be from one of our buddies.
734 * Wait for the stream open to see if that matches too before assigning it.
736 bconv
= bonjour_jabber_conv_new(NULL
, jdata
->account
, address_text
);
738 /* We wait for the stream start before doing anything else */
739 bconv
->socket
= g_object_ref(connection
);
740 bconv
->input
= g_io_stream_get_input_stream(G_IO_STREAM(bconv
->socket
));
742 g_io_stream_get_output_stream(G_IO_STREAM(bconv
->socket
));
743 source
= g_pollable_input_stream_create_source(
744 G_POLLABLE_INPUT_STREAM(bconv
->input
), bconv
->cancellable
);
745 g_source_set_callback(source
, (GSourceFunc
)_client_socket_handler
,
747 bconv
->rx_handler
= g_source_attach(source
, NULL
);
748 g_free(address_text
);
752 bonjour_jabber_start(BonjourJabber
*jdata
)
754 GError
*error
= NULL
;
757 purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
760 /* Open a listening server for incoming conversations */
761 jdata
->service
= g_socket_service_new();
762 g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata
->service
), 10);
764 if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata
->service
),
765 port
, NULL
, &error
)) {
766 purple_debug_info("bonjour",
767 "Unable to bind to specified port %i: %s",
768 port
, error
? error
->message
: "(unknown)");
769 g_clear_error(&error
);
770 port
= g_socket_listener_add_any_inet_port(
771 G_SOCKET_LISTENER(jdata
->service
), NULL
, &error
);
774 "bonjour", "Unable to create socket: %s",
775 error
? error
->message
: "(unknown)");
776 g_clear_error(&error
);
780 purple_debug_info("bonjour", "Bound IP socket to port %u.", port
);
783 g_signal_connect(G_OBJECT(jdata
->service
), "incoming",
784 G_CALLBACK(_server_socket_handler
), jdata
);
790 _connected_to_buddy(GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
792 PurpleBuddy
*pb
= user_data
;
793 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
794 GSocketConnection
*conn
;
796 GError
*error
= NULL
;
798 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
802 PurpleConversation
*conv
= NULL
;
803 PurpleAccount
*account
= NULL
;
804 GSList
*tmp
= bb
->ips
;
806 if (error
&& error
->code
== G_IO_ERROR_CANCELLED
) {
807 /* This conversation was closed before it started. */
812 purple_debug_error("bonjour",
813 "Error connecting to buddy %s at %s:%d "
814 "(%s); Trying next IP address",
815 purple_buddy_get_name(pb
),
816 bb
->conversation
->ip
, bb
->port_p2pj
,
817 error
? error
->message
: "(unknown)");
818 g_clear_error(&error
);
820 /* There may be multiple entries for the same IP - one per
821 * presence recieved (e.g. multiple interfaces).
822 * We need to make sure that we find the previously used entry.
824 while (tmp
&& bb
->conversation
->ip_link
!= tmp
->data
)
825 tmp
= g_slist_next(tmp
);
827 tmp
= g_slist_next(tmp
);
829 account
= purple_buddy_get_account(pb
);
833 GSocketClient
*client
;
835 bb
->conversation
->ip_link
= ip
= tmp
->data
;
837 purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
838 purple_buddy_get_name(pb
), ip
, bb
->port_p2pj
);
840 /* Make sure to connect without a proxy. */
841 client
= g_socket_client_new();
842 if (client
!= NULL
) {
843 g_free(bb
->conversation
->ip
);
844 bb
->conversation
->ip
= g_strdup(ip
);
845 g_socket_client_connect_to_host_async(
846 client
, ip
, bb
->port_p2pj
,
847 bb
->conversation
->cancellable
,
848 _connected_to_buddy
, pb
);
849 g_object_unref(client
);
854 purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb
));
856 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
858 purple_conversation_write_system_message(conv
,
859 _("Unable to send the message, the conversation couldn't be started."),
860 PURPLE_MESSAGE_ERROR
);
862 bonjour_jabber_close_conversation(bb
->conversation
);
863 bb
->conversation
= NULL
;
867 bb
->conversation
->socket
= conn
;
868 bb
->conversation
->input
=
869 g_io_stream_get_input_stream(G_IO_STREAM(conn
));
870 bb
->conversation
->output
=
871 g_io_stream_get_output_stream(G_IO_STREAM(conn
));
873 if (!bonjour_jabber_send_stream_init(bb
->conversation
, &error
)) {
874 PurpleConversation
*conv
= NULL
;
875 PurpleAccount
*account
= NULL
;
877 purple_debug_error("bonjour",
878 "Error starting stream with buddy %s at "
880 purple_buddy_get_name(pb
),
881 bb
->conversation
->ip
, bb
->port_p2pj
,
882 error
? error
->message
: "(null)");
884 account
= purple_buddy_get_account(pb
);
886 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
888 purple_conversation_write_system_message(conv
,
889 _("Unable to send the message, the conversation couldn't be started."),
890 PURPLE_MESSAGE_ERROR
);
892 bonjour_jabber_close_conversation(bb
->conversation
);
893 bb
->conversation
= NULL
;
894 g_clear_error(&error
);
898 /* Start listening for the stream acknowledgement */
899 rx_source
= g_pollable_input_stream_create_source(
900 G_POLLABLE_INPUT_STREAM(bb
->conversation
->input
),
901 bb
->conversation
->cancellable
);
902 g_source_set_callback(rx_source
, (GSourceFunc
)_client_socket_handler
,
903 bb
->conversation
, NULL
);
904 bb
->conversation
->rx_handler
= g_source_attach(rx_source
, NULL
);
908 bonjour_jabber_conv_match_by_name(BonjourJabberConversation
*bconv
) {
909 PurpleBuddy
*pb
= NULL
;
910 BonjourBuddy
*bb
= NULL
;
912 g_return_if_fail(bconv
->ip
!= NULL
);
913 g_return_if_fail(bconv
->pb
== NULL
);
915 pb
= purple_blist_find_buddy(bconv
->account
, bconv
->buddy_name
);
916 if (pb
&& (bb
= purple_buddy_get_protocol_data(pb
))) {
918 GSList
*tmp
= bb
->ips
;
920 purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
921 purple_buddy_get_name(pb
));
923 /* Check that one of the buddy's IPs matches */
926 if (ip
!= NULL
&& g_ascii_strcasecmp(ip
, bconv
->ip
) == 0) {
927 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
928 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
929 BonjourJabber
*jdata
= bd
->jabber_data
;
931 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
932 purple_buddy_get_name(pb
), bconv
->ip
);
934 /* Attach conv. to buddy and remove from pending list */
935 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
937 /* Check if the buddy already has a conversation and, if so, replace it */
938 if(bb
->conversation
!= NULL
&& bb
->conversation
!= bconv
)
939 bonjour_jabber_close_conversation(bb
->conversation
);
942 bb
->conversation
= bconv
;
950 /* We've failed to match a buddy - give up */
951 if (bconv
->pb
== NULL
) {
952 /* This must be asynchronous because it destroys the parser and we
953 * may be in the middle of parsing.
955 async_bonjour_jabber_close_conversation(bconv
);
961 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation
*bconv
) {
962 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
963 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
964 BonjourJabber
*jdata
= bd
->jabber_data
;
965 struct _match_buddies_by_address
*mbba
;
968 mbba
= g_new0(struct _match_buddies_by_address
, 1);
969 mbba
->address
= bconv
->ip
;
971 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
972 g_slist_foreach(buddies
, _match_buddies_by_address
, mbba
);
973 g_slist_free(buddies
);
975 /* If there is exactly one match, use it */
976 if(mbba
->matched_buddies
!= NULL
) {
977 if(mbba
->matched_buddies
->next
!= NULL
)
978 purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv
->ip
);
980 PurpleBuddy
*pb
= mbba
->matched_buddies
->data
;
981 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
983 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
984 purple_buddy_get_name(pb
), bconv
->ip
);
986 /* Attach conv. to buddy and remove from pending list */
987 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
989 /* Check if the buddy already has a conversation and, if so, replace it */
990 if (bb
->conversation
!= NULL
&& bb
->conversation
!= bconv
)
991 bonjour_jabber_close_conversation(bb
->conversation
);
994 bb
->conversation
= bconv
;
997 purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv
->ip
);
999 /* We've failed to match a buddy - give up */
1000 if (bconv
->pb
== NULL
) {
1001 /* This must be asynchronous because it destroys the parser and we
1002 * may be in the middle of parsing.
1004 async_bonjour_jabber_close_conversation(bconv
);
1007 g_slist_free(mbba
->matched_buddies
);
1011 static PurpleBuddy
*
1012 _find_or_start_conversation(BonjourJabber
*jdata
, const gchar
*to
)
1014 PurpleBuddy
*pb
= NULL
;
1015 BonjourBuddy
*bb
= NULL
;
1017 g_return_val_if_fail(jdata
!= NULL
, NULL
);
1018 g_return_val_if_fail(to
!= NULL
, NULL
);
1020 pb
= purple_blist_find_buddy(jdata
->account
, to
);
1021 if (pb
== NULL
|| (bb
= purple_buddy_get_protocol_data(pb
)) == NULL
)
1022 /* You can not send a message to an offline buddy */
1025 /* Check if there is a previously open conversation */
1026 if (bb
->conversation
== NULL
) {
1027 GSocketClient
*client
;
1028 /* Start with the first IP address. */
1029 const gchar
*ip
= bb
->ips
->data
;
1031 purple_debug_info("bonjour",
1032 "Starting conversation with %s at %s:%d", to
,
1035 /* Make sure to connect without a proxy. */
1036 client
= g_socket_client_new();
1037 if (client
== NULL
) {
1038 purple_debug_error("bonjour",
1039 "Unable to connect to buddy (%s).",
1044 bb
->conversation
= bonjour_jabber_conv_new(pb
, jdata
->account
, ip
);
1045 bb
->conversation
->ip_link
= ip
;
1047 g_socket_client_connect_to_host_async(
1048 client
, ip
, bb
->port_p2pj
,
1049 bb
->conversation
->cancellable
, _connected_to_buddy
, pb
);
1050 g_object_unref(client
);
1056 bonjour_jabber_send_message(BonjourJabber
*jdata
, const gchar
*to
, const gchar
*body
)
1058 PurpleXmlNode
*message_node
, *node
, *node2
;
1059 gchar
*message
, *xhtml
;
1064 pb
= _find_or_start_conversation(jdata
, to
);
1065 if (pb
== NULL
|| (bb
= purple_buddy_get_protocol_data(pb
)) == NULL
) {
1066 purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to
);
1067 /* You can not send a message to an offline buddy */
1071 purple_markup_html_to_xhtml(body
, &xhtml
, &message
);
1073 message_node
= purple_xmlnode_new("message");
1074 purple_xmlnode_set_attrib(message_node
, "to", bb
->name
);
1075 purple_xmlnode_set_attrib(message_node
, "from", bonjour_get_jid(jdata
->account
));
1076 purple_xmlnode_set_attrib(message_node
, "type", "chat");
1078 /* Enclose the message from the UI within a "font" node */
1079 node
= purple_xmlnode_new_child(message_node
, "body");
1080 purple_xmlnode_insert_data(node
, message
, strlen(message
));
1083 node
= purple_xmlnode_new_child(message_node
, "html");
1084 purple_xmlnode_set_namespace(node
, "http://www.w3.org/1999/xhtml");
1086 node
= purple_xmlnode_new_child(node
, "body");
1087 message
= g_strdup_printf("<font>%s</font>", xhtml
);
1088 node2
= purple_xmlnode_from_str(message
, strlen(message
));
1091 purple_xmlnode_insert_child(node
, node2
);
1093 node
= purple_xmlnode_new_child(message_node
, "x");
1094 purple_xmlnode_set_namespace(node
, "jabber:x:event");
1095 purple_xmlnode_insert_child(node
, purple_xmlnode_new("composing"));
1097 message
= purple_xmlnode_to_str(message_node
, NULL
);
1098 purple_xmlnode_free(message_node
);
1100 ret
= _send_data(pb
, message
) >= 0;
1108 _async_bonjour_jabber_close_conversation_cb(gpointer data
) {
1109 BonjourJabberConversation
*bconv
= data
;
1110 bonjour_jabber_close_conversation(bconv
);
1115 async_bonjour_jabber_close_conversation(BonjourJabberConversation
*bconv
) {
1116 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
1117 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
1118 BonjourJabber
*jdata
= bd
->jabber_data
;
1120 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
1122 /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
1123 if(bconv
->pb
!= NULL
) {
1124 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(bconv
->pb
);
1125 if (bb
->conversation
== bconv
)
1126 bb
->conversation
= NULL
;
1129 bconv
->close_timeout
= g_timeout_add(0, _async_bonjour_jabber_close_conversation_cb
, bconv
);
1133 bonjour_jabber_close_conversation(BonjourJabberConversation
*bconv
)
1135 BonjourData
*bd
= NULL
;
1136 PurpleConnection
*pc
= NULL
;
1138 if (bconv
== NULL
) {
1142 pc
= purple_account_get_connection(bconv
->account
);
1143 PURPLE_ASSERT_CONNECTION_IS_VALID(pc
);
1145 bd
= purple_connection_get_protocol_data(pc
);
1147 bd
->jabber_data
->pending_conversations
= g_slist_remove(
1148 bd
->jabber_data
->pending_conversations
, bconv
);
1151 /* Cancel any file transfers that are waiting to begin */
1152 /* There wont be any transfers if it hasn't been attached to a buddy */
1153 if (bconv
->pb
!= NULL
&& bd
!= NULL
) {
1154 GSList
*xfers
, *tmp_next
;
1155 xfers
= bd
->xfer_lists
;
1156 while (xfers
!= NULL
) {
1157 PurpleXfer
*xfer
= xfers
->data
;
1158 tmp_next
= xfers
->next
;
1159 /* We only need to cancel this if it hasn't actually started transferring. */
1160 /* This will change if we ever support IBB transfers. */
1161 if (purple_strequal(purple_xfer_get_remote_user(xfer
), purple_buddy_get_name(bconv
->pb
))
1162 && (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_NOT_STARTED
1163 || purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_UNKNOWN
)) {
1164 purple_xfer_cancel_remote(xfer
);
1170 /* Close the socket and remove the watcher */
1171 if (bconv
->socket
!= NULL
) {
1172 /* Send the end of the stream to the other end of the conversation */
1173 if (bconv
->sent_stream_start
== FULLY_SENT
) {
1174 size_t len
= strlen(STREAM_END
);
1175 if (g_pollable_output_stream_write_nonblocking(
1176 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
1177 STREAM_END
, len
, bconv
->cancellable
,
1178 NULL
) != (gssize
)len
) {
1179 purple_debug_error("bonjour",
1180 "bonjour_jabber_close_conversation: "
1181 "couldn't send data\n");
1184 /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
1185 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
1186 G_INPUT_STREAM(bconv
->input
),
1187 G_OUTPUT_STREAM(bconv
->output
));
1189 if (bconv
->rx_handler
!= 0) {
1190 g_source_remove(bconv
->rx_handler
);
1191 bconv
->rx_handler
= 0;
1193 if (bconv
->tx_handler
!= 0) {
1194 g_source_remove(bconv
->tx_handler
);
1195 bconv
->tx_handler
= 0;
1198 /* Cancel any pending operations. */
1199 if (bconv
->cancellable
!= NULL
) {
1200 g_cancellable_cancel(bconv
->cancellable
);
1201 g_clear_object(&bconv
->cancellable
);
1204 /* Free all the data related to the conversation */
1205 g_clear_object(&bconv
->socket
);
1206 bconv
->input
= NULL
;
1207 bconv
->output
= NULL
;
1209 g_object_unref(G_OBJECT(bconv
->tx_buf
));
1210 if (bconv
->stream_data
!= NULL
) {
1211 struct _stream_start_data
*ss
= bconv
->stream_data
;
1216 if (bconv
->context
!= NULL
) {
1217 bonjour_parser_setup(bconv
);
1220 if (bconv
->close_timeout
!= 0) {
1221 g_source_remove(bconv
->close_timeout
);
1224 g_free(bconv
->buddy_name
);
1230 bonjour_jabber_stop(BonjourJabber
*jdata
)
1232 /* Close the server socket and remove the watcher */
1233 if (jdata
->service
) {
1234 g_socket_service_stop(jdata
->service
);
1235 g_socket_listener_close(G_SOCKET_LISTENER(jdata
->service
));
1236 g_clear_object(&jdata
->service
);
1239 /* Close all the conversation sockets and remove all the watchers after sending end streams */
1240 if (!purple_account_is_disconnected(jdata
->account
)) {
1241 GSList
*buddies
, *l
;
1243 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
1244 for (l
= buddies
; l
; l
= l
->next
) {
1245 BonjourBuddy
*bb
= purple_buddy_get_protocol_data((PurpleBuddy
*) l
->data
);
1246 if (bb
&& bb
->conversation
) {
1247 /* Any ongoing connection attempt is cancelled
1248 * when a connection is destroyed */
1249 bonjour_jabber_close_conversation(bb
->conversation
);
1250 bb
->conversation
= NULL
;
1254 g_slist_free(buddies
);
1257 while (jdata
->pending_conversations
!= NULL
) {
1258 bonjour_jabber_close_conversation(jdata
->pending_conversations
->data
);
1259 jdata
->pending_conversations
= g_slist_delete_link(jdata
->pending_conversations
, jdata
->pending_conversations
);
1264 xep_iq_new(void *data
, XepIqType type
, const char *to
, const char *from
, const char *id
)
1266 PurpleXmlNode
*iq_node
= NULL
;
1269 g_return_val_if_fail(data
!= NULL
, NULL
);
1270 g_return_val_if_fail(to
!= NULL
, NULL
);
1271 g_return_val_if_fail(id
!= NULL
, NULL
);
1273 iq_node
= purple_xmlnode_new("iq");
1275 purple_xmlnode_set_attrib(iq_node
, "to", to
);
1276 purple_xmlnode_set_attrib(iq_node
, "from", from
);
1277 purple_xmlnode_set_attrib(iq_node
, "id", id
);
1280 purple_xmlnode_set_attrib(iq_node
, "type", "set");
1283 purple_xmlnode_set_attrib(iq_node
, "type", "get");
1286 purple_xmlnode_set_attrib(iq_node
, "type", "result");
1289 purple_xmlnode_set_attrib(iq_node
, "type", "error");
1293 purple_xmlnode_set_attrib(iq_node
, "type", "none");
1297 iq
= g_new0(XepIq
, 1);
1300 iq
->data
= ((BonjourData
*)data
)->jabber_data
;
1307 check_if_blocked(PurpleBuddy
*pb
)
1309 gboolean blocked
= FALSE
;
1311 PurpleAccount
*acc
= purple_buddy_get_account(pb
);
1316 acc
= purple_buddy_get_account(pb
);
1318 for(l
= purple_account_privacy_get_denied(acc
); l
!= NULL
; l
= l
->next
) {
1319 const gchar
*name
= purple_buddy_get_name(pb
);
1320 const gchar
*username
= bonjour_get_jid(acc
);
1322 if(!purple_utf8_strcasecmp(name
, (char *)l
->data
)) {
1323 purple_debug_info("bonjour", "%s has been blocked by %s.\n", name
, username
);
1332 xep_iq_parse(PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
1334 PurpleAccount
*account
;
1335 PurpleConnection
*gc
;
1337 if(check_if_blocked(pb
))
1340 account
= purple_buddy_get_account(pb
);
1341 gc
= purple_account_get_connection(account
);
1343 if (purple_xmlnode_get_child(packet
, "si") != NULL
|| purple_xmlnode_get_child(packet
, "error") != NULL
)
1344 xep_si_parse(gc
, packet
, pb
);
1346 xep_bytestreams_parse(gc
, packet
, pb
);
1350 xep_iq_send_and_free(XepIq
*iq
)
1353 PurpleBuddy
*pb
= NULL
;
1355 /* start the talk, reuse the message socket */
1356 pb
= _find_or_start_conversation((BonjourJabber
*) iq
->data
, iq
->to
);
1357 /* Send the message */
1359 /* Convert xml node into stream */
1360 gchar
*msg
= purple_xmlnode_to_str(iq
->node
, NULL
);
1361 ret
= _send_data(pb
, msg
);
1365 purple_xmlnode_free(iq
->node
);
1369 return (ret
>= 0) ? 0 : -1;
1372 /* This returns a list containing all non-localhost IPs */
1374 bonjour_jabber_get_local_ips(int fd
)
1377 const char *address_text
;
1380 #ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
1381 struct ifaddrs
*ifap
, *ifa
;
1382 common_sockaddr_t addr
;
1383 char addrstr
[INET6_ADDRSTRLEN
];
1385 ret
= getifaddrs(&ifap
);
1387 const char *error
= g_strerror(errno
);
1388 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error
? error
: "(null)");
1392 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1393 if (!(ifa
->ifa_flags
& IFF_RUNNING
) || (ifa
->ifa_flags
& IFF_LOOPBACK
) || ifa
->ifa_addr
== NULL
)
1396 memcpy(&addr
, ifa
->ifa_addr
, sizeof(addr
));
1397 address_text
= NULL
;
1398 switch (addr
.sa
.sa_family
) {
1400 address_text
= inet_ntop(addr
.sa
.sa_family
,
1402 addrstr
, sizeof(addrstr
));
1406 address_text
= inet_ntop(addr
.sa
.sa_family
,
1407 &addr
.in6
.sin6_addr
,
1408 addrstr
, sizeof(addrstr
));
1413 if (address_text
!= NULL
) {
1414 if (addr
.sa
.sa_family
== AF_INET
)
1415 ips
= g_slist_append(ips
, g_strdup(address_text
));
1417 ips
= g_slist_prepend(ips
, g_strdup(address_text
));
1427 struct sockaddr_in
*sinptr
;
1431 source
= socket(PF_INET
, SOCK_STREAM
, 0);
1433 ifc
.ifc_len
= sizeof(buffer
);
1434 ifc
.ifc_req
= (struct ifreq
*)buffer
;
1435 ret
= ioctl(source
, SIOCGIFCONF
, &ifc
);
1441 const char *error
= g_strerror(errno
);
1442 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error
? error
: "(null)");
1447 while (tmp
< buffer
+ ifc
.ifc_len
) {
1448 ifr
= (struct ifreq
*)tmp
;
1449 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
1451 if (ifr
->ifr_addr
.sa_family
== AF_INET
) {
1452 sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1453 if ((ntohl(sinptr
->sin_addr
.s_addr
) >> 24) != 127) {
1454 address_text
= inet_ntoa(sinptr
->sin_addr
);
1455 ips
= g_slist_prepend(ips
, g_strdup(address_text
));
1465 append_iface_if_linklocal(char *ip
, guint32 interface_param
) {
1466 struct in6_addr in6_addr
;
1467 int len_remain
= INET6_ADDRSTRLEN
- strlen(ip
);
1469 if (len_remain
<= 1)
1472 if (inet_pton(AF_INET6
, ip
, &in6_addr
) != 1 ||
1473 !IN6_IS_ADDR_LINKLOCAL(&in6_addr
))
1476 snprintf(ip
+ strlen(ip
), len_remain
, "%%%d",