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
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <sys/types.h>
35 #if defined (__SVR4) && defined (__sun)
36 #include <sys/sockio.h>
45 #ifdef HAVE_GETIFADDRS
49 #include "buddylist.h"
50 #include "connection.h"
52 #include "eventloop.h"
55 #include "purple-gio.h"
63 #include "bonjour_ft.h"
65 #ifdef _SIZEOF_ADDR_IFREQ
66 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
68 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
71 #define STREAM_END "</stream:stream>"
72 /* TODO: specify version='1.0' and send stream features */
73 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
74 "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
76 enum sent_stream_start_types
{
83 xep_iq_parse(PurpleXmlNode
*packet
, PurpleBuddy
*pb
);
85 static BonjourJabberConversation
*
86 bonjour_jabber_conv_new(PurpleBuddy
*pb
, PurpleAccount
*account
, const char *ip
) {
88 BonjourJabberConversation
*bconv
= g_new0(BonjourJabberConversation
, 1);
89 bconv
->cancellable
= g_cancellable_new();
90 bconv
->tx_buf
= purple_circular_buffer_new(512);
91 bconv
->tx_handler
= 0;
92 bconv
->rx_handler
= 0;
94 bconv
->account
= account
;
95 bconv
->ip
= g_strdup(ip
);
97 bonjour_parser_setup(bconv
);
103 _font_size_ichat_to_purple(int size
)
107 } else if (size
>= 21) {
109 } else if (size
>= 17) {
111 } else if (size
>= 14) {
113 } else if (size
>= 12) {
115 } else if (size
>= 10) {
123 get_xmlnode_contents(PurpleXmlNode
*node
)
127 contents
= purple_xmlnode_to_str(node
, NULL
);
129 /* we just want the stuff inside <font></font>
130 * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
133 char *bodystart
= strchr(contents
, '>');
134 char *bodyend
= bodystart
? strrchr(bodystart
, '<') : NULL
;
135 if (bodystart
&& bodyend
&& (bodystart
+ 1) != bodyend
) {
137 memmove(contents
, bodystart
+ 1, (bodyend
- bodystart
));
145 _jabber_parse_and_write_message_to_ui(PurpleXmlNode
*message_node
, PurpleBuddy
*pb
)
147 PurpleXmlNode
*body_node
, *html_node
, *events_node
;
148 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(pb
));
151 body_node
= purple_xmlnode_get_child(message_node
, "body");
152 html_node
= purple_xmlnode_get_child(message_node
, "html");
154 if (body_node
== NULL
&& html_node
== NULL
) {
155 purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
159 events_node
= purple_xmlnode_get_child_with_namespace(message_node
, "x", "jabber:x:event");
160 if (events_node
!= NULL
) {
161 if (purple_xmlnode_get_child(events_node
, "id") != NULL
) {
162 /* The user is just typing */
163 /* TODO: Deal with typing notification */
168 if (html_node
!= NULL
) {
169 PurpleXmlNode
*html_body_node
;
171 html_body_node
= purple_xmlnode_get_child(html_node
, "body");
172 if (html_body_node
!= NULL
) {
173 PurpleXmlNode
*html_body_font_node
;
175 html_body_font_node
= purple_xmlnode_get_child(html_body_node
, "font");
176 /* Types of messages sent by iChat */
177 if (html_body_font_node
!= NULL
) {
179 const char *font_face
, *font_size
, *font_color
,
180 *ichat_balloon_color
, *ichat_text_color
;
182 font_face
= purple_xmlnode_get_attrib(html_body_font_node
, "face");
183 /* The absolute iChat font sizes should be converted to 1..7 range */
184 font_size
= purple_xmlnode_get_attrib(html_body_font_node
, "ABSZ");
185 if (font_size
!= NULL
)
186 font_size
= _font_size_ichat_to_purple(atoi(font_size
));
187 font_color
= purple_xmlnode_get_attrib(html_body_font_node
, "color");
188 ichat_balloon_color
= purple_xmlnode_get_attrib(html_body_node
, "ichatballooncolor");
189 ichat_text_color
= purple_xmlnode_get_attrib(html_body_node
, "ichattextcolor");
191 html_body
= get_xmlnode_contents(html_body_font_node
);
193 if (html_body
== NULL
)
194 /* This is the kind of formatted messages that Purple creates */
195 html_body
= purple_xmlnode_to_str(html_body_font_node
, NULL
);
197 if (html_body
!= NULL
) {
198 GString
*str
= g_string_new("<font");
201 g_string_append_printf(str
, " face='%s'", font_face
);
203 g_string_append_printf(str
, " size='%s'", font_size
);
205 g_string_append_printf(str
, " color='%s'", font_color
);
206 else if (ichat_text_color
)
207 g_string_append_printf(str
, " color='%s'", ichat_text_color
);
208 if (ichat_balloon_color
)
209 g_string_append_printf(str
, " back='%s'", ichat_balloon_color
);
210 g_string_append_printf(str
, ">%s</font>", html_body
);
212 body
= g_string_free(str
, FALSE
);
220 /* Compose the message */
221 if (body
== NULL
&& body_node
!= NULL
)
222 body
= purple_xmlnode_get_data(body_node
);
225 purple_debug_error("bonjour", "No html body or regular body found.\n");
229 /* Send the message to the UI */
230 purple_serv_got_im(gc
, purple_buddy_get_name(pb
), body
, 0, time(NULL
));
235 struct _match_buddies_by_address_t
{
237 GSList
*matched_buddies
;
241 _match_buddies_by_address(gpointer value
, gpointer data
)
243 PurpleBuddy
*pb
= value
;
244 BonjourBuddy
*bb
= NULL
;
245 struct _match_buddies_by_address_t
*mbba
= data
;
247 bb
= purple_buddy_get_protocol_data(pb
);
250 * If the current PurpleBuddy's data is not null, then continue to determine
251 * whether one of the buddies IPs matches the target IP.
256 GSList
*tmp
= bb
->ips
;
260 if (ip
!= NULL
&& g_ascii_strcasecmp(ip
, mbba
->address
) == 0) {
261 mbba
->matched_buddies
= g_slist_prepend(mbba
->matched_buddies
, pb
);
270 _send_data_write_cb(GObject
*stream
, gpointer data
)
272 PurpleBuddy
*pb
= data
;
273 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
274 BonjourJabberConversation
*bconv
= bb
->conversation
;
277 GError
*error
= NULL
;
279 writelen
= purple_circular_buffer_get_max_read(bconv
->tx_buf
);
282 g_source_remove(bconv
->tx_handler
);
283 bconv
->tx_handler
= 0;
287 ret
= g_pollable_output_stream_write_nonblocking(
288 G_POLLABLE_OUTPUT_STREAM(stream
),
289 purple_circular_buffer_get_output(bconv
->tx_buf
), writelen
,
290 bconv
->cancellable
, &error
);
292 if (ret
< 0 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
293 g_clear_error(&error
);
295 } else if (ret
<= 0) {
296 PurpleConversation
*conv
= NULL
;
297 PurpleAccount
*account
= NULL
;
301 "Error sending message to buddy %s error: %s",
302 purple_buddy_get_name(pb
),
303 error
? error
->message
: "(null)");
305 account
= purple_buddy_get_account(pb
);
307 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
309 purple_conversation_write_system_message(conv
,
310 _("Unable to send message."),
311 PURPLE_MESSAGE_ERROR
);
313 bonjour_jabber_close_conversation(bb
->conversation
);
314 bb
->conversation
= NULL
;
315 g_clear_error(&error
);
319 purple_circular_buffer_mark_read(bconv
->tx_buf
, ret
);
323 _send_data(PurpleBuddy
*pb
, char *message
)
325 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
326 BonjourJabberConversation
*bconv
= bb
->conversation
;
327 gsize len
= strlen(message
);
329 GError
*error
= NULL
;
331 /* If we're not ready to actually send, append it to the buffer */
332 if (bconv
->tx_handler
!= 0
333 || bconv
->sent_stream_start
!= FULLY_SENT
334 || !bconv
->recv_stream_start
335 || purple_circular_buffer_get_max_read(bconv
->tx_buf
) > 0) {
337 g_set_error_literal(&error
, G_IO_ERROR
, G_IO_ERROR_WOULD_BLOCK
,
338 "Not yet ready to send.");
340 ret
= g_pollable_output_stream_write_nonblocking(
341 G_POLLABLE_OUTPUT_STREAM(bconv
->output
), message
, len
,
342 bconv
->cancellable
, &error
);
345 if (ret
== -1 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
347 g_clear_error(&error
);
348 } else if (ret
<= 0) {
349 PurpleConversation
*conv
;
350 PurpleAccount
*account
;
354 "Error sending message to buddy %s error: %s",
355 purple_buddy_get_name(pb
),
356 error
? error
->message
: "(null)");
358 account
= purple_buddy_get_account(pb
);
360 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
362 purple_conversation_write_system_message(conv
,
363 _("Unable to send message."),
364 PURPLE_MESSAGE_ERROR
);
366 bonjour_jabber_close_conversation(bb
->conversation
);
367 bb
->conversation
= NULL
;
368 g_clear_error(&error
);
373 /* Don't interfere with the stream starting */
374 if (bconv
->sent_stream_start
== FULLY_SENT
&&
375 bconv
->recv_stream_start
&& bconv
->tx_handler
== 0) {
377 g_pollable_output_stream_create_source(
378 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
380 g_source_set_callback(source
,
381 (GSourceFunc
)_send_data_write_cb
,
383 bconv
->tx_handler
= g_source_attach(source
, NULL
);
385 purple_circular_buffer_append(bconv
->tx_buf
, message
+ ret
, len
- ret
);
391 void bonjour_jabber_process_packet(PurpleBuddy
*pb
, PurpleXmlNode
*packet
) {
393 g_return_if_fail(packet
!= NULL
);
394 g_return_if_fail(pb
!= NULL
);
396 if (purple_strequal(packet
->name
, "message"))
397 _jabber_parse_and_write_message_to_ui(packet
, pb
);
398 else if (purple_strequal(packet
->name
, "iq"))
399 xep_iq_parse(packet
, pb
);
401 purple_debug_warning("bonjour", "Unknown packet: %s\n",
402 packet
->name
? packet
->name
: "(null)");
406 static void bonjour_jabber_stream_ended(BonjourJabberConversation
*bconv
) {
408 /* Inform the user that the conversation has been closed */
409 BonjourBuddy
*bb
= NULL
;
410 const gchar
*name
= bconv
->pb
? purple_buddy_get_name(bconv
->pb
) : "(unknown)";
412 purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name
);
414 if(bconv
->pb
!= NULL
)
415 bb
= purple_buddy_get_protocol_data(bconv
->pb
);
417 /* Close the socket, clear the watcher and free memory */
418 bonjour_jabber_close_conversation(bconv
);
420 bb
->conversation
= NULL
;
424 _client_socket_handler(GObject
*stream
, gpointer data
)
426 BonjourJabberConversation
*bconv
= data
;
427 GError
*error
= NULL
;
429 static char message
[4096];
431 /* Read the data from the socket */
432 len
= g_pollable_input_stream_read_nonblocking(
433 G_POLLABLE_INPUT_STREAM(stream
), message
, sizeof(message
) - 1,
434 bconv
->cancellable
, &error
);
436 /* There has been an error reading from the socket */
437 if (error
== NULL
|| (error
->code
!= G_IO_ERROR_WOULD_BLOCK
&&
438 error
->code
!= G_IO_ERROR_CANCELLED
)) {
439 purple_debug_warning(
441 "receive of %" G_GSSIZE_FORMAT
" error: %s",
442 len
, error
? error
->message
: "(null)");
444 bonjour_jabber_close_conversation(bconv
);
445 if (bconv
->pb
!= NULL
) {
446 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(bconv
->pb
);
449 bb
->conversation
= NULL
;
452 /* I guess we really don't need to notify the user.
453 * If they try to send another message it'll reconnect */
455 g_clear_error(&error
);
457 } else if (len
== 0) { /* The other end has closed the socket */
458 const gchar
*name
= purple_buddy_get_name(bconv
->pb
);
459 purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name
) ? name
: "(unknown)");
460 bonjour_jabber_stream_ended(bconv
);
466 purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT
" bytes\n", message
, len
);
467 bonjour_parser_process(bconv
, message
, len
);
472 struct _stream_start_data
{
477 _start_stream(GObject
*stream
, gpointer data
)
479 BonjourJabberConversation
*bconv
= data
;
480 struct _stream_start_data
*ss
= bconv
->stream_data
;
481 GError
*error
= NULL
;
485 len
= strlen(ss
->msg
);
488 ret
= g_pollable_output_stream_write_nonblocking(
489 G_POLLABLE_OUTPUT_STREAM(stream
), ss
->msg
, len
,
490 bconv
->cancellable
, &error
);
492 if (ret
== -1 && error
->code
== G_IO_ERROR_WOULD_BLOCK
) {
493 g_clear_error(&error
);
495 } else if (ret
<= 0) {
496 PurpleConversation
*conv
;
497 const char *bname
= bconv
->buddy_name
;
498 BonjourBuddy
*bb
= NULL
;
501 bb
= purple_buddy_get_protocol_data(bconv
->pb
);
502 bname
= purple_buddy_get_name(bconv
->pb
);
507 "Error starting stream with buddy %s at %s error: %s",
508 bname
? bname
: "(unknown)", bconv
->ip
,
509 error
? error
->message
: "(null)");
511 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
513 purple_conversation_write_system_message(conv
,
514 _("Unable to send the message, the conversation couldn't be started."),
515 PURPLE_MESSAGE_ERROR
);
517 bonjour_jabber_close_conversation(bconv
);
519 bb
->conversation
= NULL
;
521 g_clear_error(&error
);
525 /* This is EXTREMELY unlikely to happen */
527 char *tmp
= g_strdup(ss
->msg
+ ret
);
535 bconv
->stream_data
= NULL
;
537 /* Stream started; process the send buffer if there is one */
538 g_source_remove(bconv
->tx_handler
);
539 bconv
->tx_handler
= 0;
540 bconv
->sent_stream_start
= FULLY_SENT
;
542 bonjour_jabber_stream_started(bconv
);
546 bonjour_jabber_send_stream_init(BonjourJabberConversation
*bconv
,
552 const char *bname
= bconv
->buddy_name
;
554 if (bconv
->pb
!= NULL
)
555 bname
= purple_buddy_get_name(bconv
->pb
);
557 /* If we have no idea who "to" is, use an empty string.
558 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
562 stream_start
= g_strdup_printf(DOCTYPE
, bonjour_get_jid(bconv
->account
), bname
);
563 len
= strlen(stream_start
);
565 bconv
->sent_stream_start
= PARTIALLY_SENT
;
567 /* Start the stream */
568 ret
= g_pollable_output_stream_write_nonblocking(
569 G_POLLABLE_OUTPUT_STREAM(bconv
->output
), stream_start
, len
,
570 bconv
->cancellable
, error
);
571 if (ret
== -1 && (*error
)->code
== G_IO_ERROR_WOULD_BLOCK
) {
573 g_clear_error(error
);
574 } else if (ret
<= 0) {
577 "Error starting stream with buddy %s at %s error: %s",
578 (*bname
) ? bname
: "(unknown)", bconv
->ip
,
579 *error
? (*error
)->message
: "(null)");
582 PurpleConversation
*conv
;
583 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
585 purple_conversation_write_system_message(conv
,
586 _("Unable to send the message, the conversation couldn't be started."),
587 PURPLE_MESSAGE_ERROR
);
590 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
591 G_INPUT_STREAM(bconv
->input
),
592 G_OUTPUT_STREAM(bconv
->output
));
593 g_clear_object(&bconv
->socket
);
595 bconv
->output
= NULL
;
596 g_free(stream_start
);
601 /* This is unlikely to happen */
604 struct _stream_start_data
*ss
= g_new(struct _stream_start_data
, 1);
605 ss
->msg
= g_strdup(stream_start
+ ret
);
606 bconv
->stream_data
= ss
;
607 /* Finish sending the stream start */
608 source
= g_pollable_output_stream_create_source(
609 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
611 g_source_set_callback(source
, (GSourceFunc
)_start_stream
, bconv
,
613 bconv
->tx_handler
= g_source_attach(source
, NULL
);
615 bconv
->sent_stream_start
= FULLY_SENT
;
618 g_free(stream_start
);
623 /* This gets called when we've successfully sent our <stream:stream />
624 * AND when we've received a <stream:stream /> */
626 bonjour_jabber_stream_started(BonjourJabberConversation
*bconv
)
628 GError
*error
= NULL
;
630 if (bconv
->sent_stream_start
== NOT_SENT
&&
631 !bonjour_jabber_send_stream_init(bconv
, &error
)) {
632 const char *bname
= bconv
->buddy_name
;
635 bname
= purple_buddy_get_name(bconv
->pb
);
639 "Error starting stream with buddy %s at %s error: %s",
640 bname
? bname
: "(unknown)", bconv
->ip
,
641 error
? error
->message
: "(null)");
644 PurpleConversation
*conv
;
645 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname
, bconv
->account
));
647 purple_conversation_write_system_message(conv
,
648 _("Unable to send the message, the conversation couldn't be started."),
649 PURPLE_MESSAGE_ERROR
);
652 /* We don't want to recieve anything else */
653 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
654 G_INPUT_STREAM(bconv
->input
),
655 G_OUTPUT_STREAM(bconv
->output
));
656 g_clear_object(&bconv
->socket
);
658 bconv
->output
= NULL
;
660 /* This must be asynchronous because it destroys the parser and we
661 * may be in the middle of parsing.
663 async_bonjour_jabber_close_conversation(bconv
);
664 g_clear_error(&error
);
668 /* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
669 /* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
670 if (bconv
->sent_stream_start
== FULLY_SENT
&& bconv
->recv_stream_start
671 && bconv
->pb
&& purple_circular_buffer_get_max_read(bconv
->tx_buf
) > 0) {
672 /* Watch for when we can write the buffered messages */
673 GSource
*source
= g_pollable_output_stream_create_source(
674 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
676 g_source_set_callback(source
, (GSourceFunc
)_send_data_write_cb
,
678 bconv
->tx_handler
= g_source_attach(source
, NULL
);
679 /* We can probably write the data right now. */
680 _send_data_write_cb(G_OBJECT(bconv
->output
), bconv
->pb
);
684 #ifndef INET6_ADDRSTRLEN
685 #define INET6_ADDRSTRLEN 46
689 _server_socket_handler(GSocketService
*service
, GSocketConnection
*connection
,
690 GObject
*source_object
, gpointer data
)
692 BonjourJabber
*jdata
= data
;
693 GSocketAddress
*their_addr
; /* connector's address information */
694 GInetAddress
*their_inet_addr
;
696 struct _match_buddies_by_address_t
*mbba
;
697 BonjourJabberConversation
*bconv
;
701 their_addr
= g_socket_connection_get_remote_address(connection
, NULL
);
702 if (their_addr
== NULL
) {
705 their_inet_addr
= g_inet_socket_address_get_address(
706 G_INET_SOCKET_ADDRESS(their_addr
));
708 /* Look for the buddy that has opened the conversation and fill information */
709 address_text
= g_inet_address_to_string(their_inet_addr
);
710 if (g_inet_address_get_family(their_inet_addr
) ==
711 G_SOCKET_FAMILY_IPV6
&&
712 g_inet_address_get_is_link_local(their_inet_addr
)) {
713 gchar
*tmp
= g_strdup_printf(
714 "%s%%%d", address_text
,
715 g_inet_socket_address_get_scope_id(
716 G_INET_SOCKET_ADDRESS(their_addr
)));
717 g_free(address_text
);
720 g_object_unref(their_addr
);
722 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text
);
723 mbba
= g_new0(struct _match_buddies_by_address_t
, 1);
724 mbba
->address
= address_text
;
726 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
727 g_slist_foreach(buddies
, _match_buddies_by_address
, mbba
);
728 g_slist_free(buddies
);
730 if (mbba
->matched_buddies
== NULL
) {
731 purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
732 g_free(address_text
);
737 g_slist_free(mbba
->matched_buddies
);
740 /* We've established that this *could* be from one of our buddies.
741 * Wait for the stream open to see if that matches too before assigning it.
743 bconv
= bonjour_jabber_conv_new(NULL
, jdata
->account
, address_text
);
745 /* We wait for the stream start before doing anything else */
746 bconv
->socket
= g_object_ref(connection
);
747 bconv
->input
= g_io_stream_get_input_stream(G_IO_STREAM(bconv
->socket
));
749 g_io_stream_get_output_stream(G_IO_STREAM(bconv
->socket
));
750 source
= g_pollable_input_stream_create_source(
751 G_POLLABLE_INPUT_STREAM(bconv
->input
), bconv
->cancellable
);
752 g_source_set_callback(source
, (GSourceFunc
)_client_socket_handler
,
754 bconv
->rx_handler
= g_source_attach(source
, NULL
);
755 g_free(address_text
);
759 bonjour_jabber_start(BonjourJabber
*jdata
)
761 GError
*error
= NULL
;
764 purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
767 /* Open a listening server for incoming conversations */
768 jdata
->service
= g_socket_service_new();
769 g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata
->service
), 10);
771 if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata
->service
),
772 port
, NULL
, &error
)) {
773 purple_debug_info("bonjour",
774 "Unable to bind to specified port %i: %s",
775 port
, error
? error
->message
: "(unknown)");
776 g_clear_error(&error
);
777 port
= g_socket_listener_add_any_inet_port(
778 G_SOCKET_LISTENER(jdata
->service
), NULL
, &error
);
781 "bonjour", "Unable to create socket: %s",
782 error
? error
->message
: "(unknown)");
783 g_clear_error(&error
);
787 purple_debug_info("bonjour", "Bound IP socket to port %u.", port
);
790 g_signal_connect(G_OBJECT(jdata
->service
), "incoming",
791 G_CALLBACK(_server_socket_handler
), jdata
);
797 _connected_to_buddy(GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
799 PurpleBuddy
*pb
= user_data
;
800 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
801 GSocketConnection
*conn
;
803 GError
*error
= NULL
;
805 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
809 PurpleConversation
*conv
= NULL
;
810 PurpleAccount
*account
= NULL
;
811 GSList
*tmp
= bb
->ips
;
813 if (error
&& error
->code
== G_IO_ERROR_CANCELLED
) {
814 /* This conversation was closed before it started. */
819 purple_debug_error("bonjour",
820 "Error connecting to buddy %s at %s:%d "
821 "(%s); Trying next IP address",
822 purple_buddy_get_name(pb
),
823 bb
->conversation
->ip
, bb
->port_p2pj
,
824 error
? error
->message
: "(unknown)");
825 g_clear_error(&error
);
827 /* There may be multiple entries for the same IP - one per
828 * presence recieved (e.g. multiple interfaces).
829 * We need to make sure that we find the previously used entry.
831 while (tmp
&& bb
->conversation
->ip_link
!= tmp
->data
)
832 tmp
= g_slist_next(tmp
);
834 tmp
= g_slist_next(tmp
);
836 account
= purple_buddy_get_account(pb
);
840 GSocketClient
*client
;
842 bb
->conversation
->ip_link
= ip
= tmp
->data
;
844 purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
845 purple_buddy_get_name(pb
), ip
, bb
->port_p2pj
);
847 /* Make sure to connect without a proxy. */
848 client
= g_socket_client_new();
849 if (client
!= NULL
) {
850 g_free(bb
->conversation
->ip
);
851 bb
->conversation
->ip
= g_strdup(ip
);
852 g_socket_client_connect_to_host_async(
853 client
, ip
, bb
->port_p2pj
,
854 bb
->conversation
->cancellable
,
855 _connected_to_buddy
, pb
);
856 g_object_unref(client
);
861 purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb
));
863 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
865 purple_conversation_write_system_message(conv
,
866 _("Unable to send the message, the conversation couldn't be started."),
867 PURPLE_MESSAGE_ERROR
);
869 bonjour_jabber_close_conversation(bb
->conversation
);
870 bb
->conversation
= NULL
;
874 bb
->conversation
->socket
= conn
;
875 bb
->conversation
->input
=
876 g_io_stream_get_input_stream(G_IO_STREAM(conn
));
877 bb
->conversation
->output
=
878 g_io_stream_get_output_stream(G_IO_STREAM(conn
));
880 if (!bonjour_jabber_send_stream_init(bb
->conversation
, &error
)) {
881 PurpleConversation
*conv
= NULL
;
882 PurpleAccount
*account
= NULL
;
884 purple_debug_error("bonjour",
885 "Error starting stream with buddy %s at "
887 purple_buddy_get_name(pb
),
888 bb
->conversation
->ip
, bb
->port_p2pj
,
889 error
? error
->message
: "(null)");
891 account
= purple_buddy_get_account(pb
);
893 conv
= PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb
->name
, account
));
895 purple_conversation_write_system_message(conv
,
896 _("Unable to send the message, the conversation couldn't be started."),
897 PURPLE_MESSAGE_ERROR
);
899 bonjour_jabber_close_conversation(bb
->conversation
);
900 bb
->conversation
= NULL
;
901 g_clear_error(&error
);
905 /* Start listening for the stream acknowledgement */
906 rx_source
= g_pollable_input_stream_create_source(
907 G_POLLABLE_INPUT_STREAM(bb
->conversation
->input
),
908 bb
->conversation
->cancellable
);
909 g_source_set_callback(rx_source
, (GSourceFunc
)_client_socket_handler
,
910 bb
->conversation
, NULL
);
911 bb
->conversation
->rx_handler
= g_source_attach(rx_source
, NULL
);
915 bonjour_jabber_conv_match_by_name(BonjourJabberConversation
*bconv
) {
916 PurpleBuddy
*pb
= NULL
;
917 BonjourBuddy
*bb
= NULL
;
919 g_return_if_fail(bconv
->ip
!= NULL
);
920 g_return_if_fail(bconv
->pb
== NULL
);
922 pb
= purple_blist_find_buddy(bconv
->account
, bconv
->buddy_name
);
923 if (pb
&& (bb
= purple_buddy_get_protocol_data(pb
))) {
925 GSList
*tmp
= bb
->ips
;
927 purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
928 purple_buddy_get_name(pb
));
930 /* Check that one of the buddy's IPs matches */
933 if (ip
!= NULL
&& g_ascii_strcasecmp(ip
, bconv
->ip
) == 0) {
934 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
935 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
936 BonjourJabber
*jdata
= bd
->jabber_data
;
938 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
939 purple_buddy_get_name(pb
), bconv
->ip
);
941 /* Attach conv. to buddy and remove from pending list */
942 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
944 /* Check if the buddy already has a conversation and, if so, replace it */
945 if(bb
->conversation
!= NULL
&& bb
->conversation
!= bconv
)
946 bonjour_jabber_close_conversation(bb
->conversation
);
949 bb
->conversation
= bconv
;
957 /* We've failed to match a buddy - give up */
958 if (bconv
->pb
== NULL
) {
959 /* This must be asynchronous because it destroys the parser and we
960 * may be in the middle of parsing.
962 async_bonjour_jabber_close_conversation(bconv
);
968 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation
*bconv
) {
969 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
970 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
971 BonjourJabber
*jdata
= bd
->jabber_data
;
972 struct _match_buddies_by_address_t
*mbba
;
975 mbba
= g_new0(struct _match_buddies_by_address_t
, 1);
976 mbba
->address
= bconv
->ip
;
978 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
979 g_slist_foreach(buddies
, _match_buddies_by_address
, mbba
);
980 g_slist_free(buddies
);
982 /* If there is exactly one match, use it */
983 if(mbba
->matched_buddies
!= NULL
) {
984 if(mbba
->matched_buddies
->next
!= NULL
)
985 purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv
->ip
);
987 PurpleBuddy
*pb
= mbba
->matched_buddies
->data
;
988 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(pb
);
990 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
991 purple_buddy_get_name(pb
), bconv
->ip
);
993 /* Attach conv. to buddy and remove from pending list */
994 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
996 /* Check if the buddy already has a conversation and, if so, replace it */
997 if (bb
->conversation
!= NULL
&& bb
->conversation
!= bconv
)
998 bonjour_jabber_close_conversation(bb
->conversation
);
1001 bb
->conversation
= bconv
;
1004 purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv
->ip
);
1006 /* We've failed to match a buddy - give up */
1007 if (bconv
->pb
== NULL
) {
1008 /* This must be asynchronous because it destroys the parser and we
1009 * may be in the middle of parsing.
1011 async_bonjour_jabber_close_conversation(bconv
);
1014 g_slist_free(mbba
->matched_buddies
);
1018 static PurpleBuddy
*
1019 _find_or_start_conversation(BonjourJabber
*jdata
, const gchar
*to
)
1021 PurpleBuddy
*pb
= NULL
;
1022 BonjourBuddy
*bb
= NULL
;
1024 g_return_val_if_fail(jdata
!= NULL
, NULL
);
1025 g_return_val_if_fail(to
!= NULL
, NULL
);
1027 pb
= purple_blist_find_buddy(jdata
->account
, to
);
1028 if (pb
== NULL
|| (bb
= purple_buddy_get_protocol_data(pb
)) == NULL
)
1029 /* You can not send a message to an offline buddy */
1032 /* Check if there is a previously open conversation */
1033 if (bb
->conversation
== NULL
) {
1034 GSocketClient
*client
;
1035 /* Start with the first IP address. */
1036 const gchar
*ip
= bb
->ips
->data
;
1038 purple_debug_info("bonjour",
1039 "Starting conversation with %s at %s:%d", to
,
1042 /* Make sure to connect without a proxy. */
1043 client
= g_socket_client_new();
1044 if (client
== NULL
) {
1045 purple_debug_error("bonjour",
1046 "Unable to connect to buddy (%s).",
1051 bb
->conversation
= bonjour_jabber_conv_new(pb
, jdata
->account
, ip
);
1052 bb
->conversation
->ip_link
= ip
;
1054 g_socket_client_connect_to_host_async(
1055 client
, ip
, bb
->port_p2pj
,
1056 bb
->conversation
->cancellable
, _connected_to_buddy
, pb
);
1057 g_object_unref(client
);
1063 bonjour_jabber_send_message(BonjourJabber
*jdata
, const gchar
*to
, const gchar
*body
)
1065 PurpleXmlNode
*message_node
, *node
, *node2
;
1066 gchar
*message
, *xhtml
;
1071 pb
= _find_or_start_conversation(jdata
, to
);
1072 if (pb
== NULL
|| (bb
= purple_buddy_get_protocol_data(pb
)) == NULL
) {
1073 purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to
);
1074 /* You can not send a message to an offline buddy */
1078 purple_markup_html_to_xhtml(body
, &xhtml
, &message
);
1080 message_node
= purple_xmlnode_new("message");
1081 purple_xmlnode_set_attrib(message_node
, "to", bb
->name
);
1082 purple_xmlnode_set_attrib(message_node
, "from", bonjour_get_jid(jdata
->account
));
1083 purple_xmlnode_set_attrib(message_node
, "type", "chat");
1085 /* Enclose the message from the UI within a "font" node */
1086 node
= purple_xmlnode_new_child(message_node
, "body");
1087 purple_xmlnode_insert_data(node
, message
, strlen(message
));
1090 node
= purple_xmlnode_new_child(message_node
, "html");
1091 purple_xmlnode_set_namespace(node
, "http://www.w3.org/1999/xhtml");
1093 node
= purple_xmlnode_new_child(node
, "body");
1094 message
= g_strdup_printf("<font>%s</font>", xhtml
);
1095 node2
= purple_xmlnode_from_str(message
, strlen(message
));
1098 purple_xmlnode_insert_child(node
, node2
);
1100 node
= purple_xmlnode_new_child(message_node
, "x");
1101 purple_xmlnode_set_namespace(node
, "jabber:x:event");
1102 purple_xmlnode_insert_child(node
, purple_xmlnode_new("composing"));
1104 message
= purple_xmlnode_to_str(message_node
, NULL
);
1105 purple_xmlnode_free(message_node
);
1107 ret
= _send_data(pb
, message
) >= 0;
1115 _async_bonjour_jabber_close_conversation_cb(gpointer data
) {
1116 BonjourJabberConversation
*bconv
= data
;
1117 bonjour_jabber_close_conversation(bconv
);
1122 async_bonjour_jabber_close_conversation(BonjourJabberConversation
*bconv
) {
1123 PurpleConnection
*pc
= purple_account_get_connection(bconv
->account
);
1124 BonjourData
*bd
= purple_connection_get_protocol_data(pc
);
1125 BonjourJabber
*jdata
= bd
->jabber_data
;
1127 jdata
->pending_conversations
= g_slist_remove(jdata
->pending_conversations
, bconv
);
1129 /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
1130 if(bconv
->pb
!= NULL
) {
1131 BonjourBuddy
*bb
= purple_buddy_get_protocol_data(bconv
->pb
);
1132 if (bb
->conversation
== bconv
)
1133 bb
->conversation
= NULL
;
1136 bconv
->close_timeout
= g_timeout_add(0, _async_bonjour_jabber_close_conversation_cb
, bconv
);
1140 bonjour_jabber_close_conversation(BonjourJabberConversation
*bconv
)
1142 BonjourData
*bd
= NULL
;
1143 PurpleConnection
*pc
= NULL
;
1145 if (bconv
== NULL
) {
1149 pc
= purple_account_get_connection(bconv
->account
);
1150 PURPLE_ASSERT_CONNECTION_IS_VALID(pc
);
1152 bd
= purple_connection_get_protocol_data(pc
);
1154 bd
->jabber_data
->pending_conversations
= g_slist_remove(
1155 bd
->jabber_data
->pending_conversations
, bconv
);
1158 /* Cancel any file transfers that are waiting to begin */
1159 /* There wont be any transfers if it hasn't been attached to a buddy */
1160 if (bconv
->pb
!= NULL
&& bd
!= NULL
) {
1161 GSList
*xfers
, *tmp_next
;
1162 xfers
= bd
->xfer_lists
;
1163 while (xfers
!= NULL
) {
1164 PurpleXfer
*xfer
= xfers
->data
;
1165 tmp_next
= xfers
->next
;
1166 /* We only need to cancel this if it hasn't actually started transferring. */
1167 /* This will change if we ever support IBB transfers. */
1168 if (purple_strequal(purple_xfer_get_remote_user(xfer
), purple_buddy_get_name(bconv
->pb
))
1169 && (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_NOT_STARTED
1170 || purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_UNKNOWN
)) {
1171 purple_xfer_cancel_remote(xfer
);
1177 /* Close the socket and remove the watcher */
1178 if (bconv
->socket
!= NULL
) {
1179 /* Send the end of the stream to the other end of the conversation */
1180 if (bconv
->sent_stream_start
== FULLY_SENT
) {
1181 size_t len
= strlen(STREAM_END
);
1182 if (g_pollable_output_stream_write_nonblocking(
1183 G_POLLABLE_OUTPUT_STREAM(bconv
->output
),
1184 STREAM_END
, len
, bconv
->cancellable
,
1185 NULL
) != (gssize
)len
) {
1186 purple_debug_error("bonjour",
1187 "bonjour_jabber_close_conversation: "
1188 "couldn't send data\n");
1191 /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
1192 purple_gio_graceful_close(G_IO_STREAM(bconv
->socket
),
1193 G_INPUT_STREAM(bconv
->input
),
1194 G_OUTPUT_STREAM(bconv
->output
));
1196 if (bconv
->rx_handler
!= 0) {
1197 g_source_remove(bconv
->rx_handler
);
1198 bconv
->rx_handler
= 0;
1200 if (bconv
->tx_handler
!= 0) {
1201 g_source_remove(bconv
->tx_handler
);
1202 bconv
->tx_handler
= 0;
1205 /* Cancel any pending operations. */
1206 if (bconv
->cancellable
!= NULL
) {
1207 g_cancellable_cancel(bconv
->cancellable
);
1208 g_clear_object(&bconv
->cancellable
);
1211 /* Free all the data related to the conversation */
1212 g_clear_object(&bconv
->socket
);
1213 bconv
->input
= NULL
;
1214 bconv
->output
= NULL
;
1216 g_object_unref(G_OBJECT(bconv
->tx_buf
));
1217 if (bconv
->stream_data
!= NULL
) {
1218 struct _stream_start_data
*ss
= bconv
->stream_data
;
1223 if (bconv
->context
!= NULL
) {
1224 bonjour_parser_setup(bconv
);
1227 if (bconv
->close_timeout
!= 0) {
1228 g_source_remove(bconv
->close_timeout
);
1231 g_free(bconv
->buddy_name
);
1237 bonjour_jabber_stop(BonjourJabber
*jdata
)
1239 /* Close the server socket and remove the watcher */
1240 if (jdata
->service
) {
1241 g_socket_service_stop(jdata
->service
);
1242 g_socket_listener_close(G_SOCKET_LISTENER(jdata
->service
));
1243 g_clear_object(&jdata
->service
);
1246 /* Close all the conversation sockets and remove all the watchers after sending end streams */
1247 if (!purple_account_is_disconnected(jdata
->account
)) {
1248 GSList
*buddies
, *l
;
1250 buddies
= purple_blist_find_buddies(jdata
->account
, NULL
);
1251 for (l
= buddies
; l
; l
= l
->next
) {
1252 BonjourBuddy
*bb
= purple_buddy_get_protocol_data((PurpleBuddy
*) l
->data
);
1253 if (bb
&& bb
->conversation
) {
1254 /* Any ongoing connection attempt is cancelled
1255 * when a connection is destroyed */
1256 bonjour_jabber_close_conversation(bb
->conversation
);
1257 bb
->conversation
= NULL
;
1261 g_slist_free(buddies
);
1264 while (jdata
->pending_conversations
!= NULL
) {
1265 bonjour_jabber_close_conversation(jdata
->pending_conversations
->data
);
1266 jdata
->pending_conversations
= g_slist_delete_link(jdata
->pending_conversations
, jdata
->pending_conversations
);
1271 xep_iq_new(void *data
, XepIqType type
, const char *to
, const char *from
, const char *id
)
1273 PurpleXmlNode
*iq_node
= NULL
;
1276 g_return_val_if_fail(data
!= NULL
, NULL
);
1277 g_return_val_if_fail(to
!= NULL
, NULL
);
1278 g_return_val_if_fail(id
!= NULL
, NULL
);
1280 iq_node
= purple_xmlnode_new("iq");
1282 purple_xmlnode_set_attrib(iq_node
, "to", to
);
1283 purple_xmlnode_set_attrib(iq_node
, "from", from
);
1284 purple_xmlnode_set_attrib(iq_node
, "id", id
);
1287 purple_xmlnode_set_attrib(iq_node
, "type", "set");
1290 purple_xmlnode_set_attrib(iq_node
, "type", "get");
1293 purple_xmlnode_set_attrib(iq_node
, "type", "result");
1296 purple_xmlnode_set_attrib(iq_node
, "type", "error");
1300 purple_xmlnode_set_attrib(iq_node
, "type", "none");
1304 iq
= g_new0(XepIq
, 1);
1307 iq
->data
= ((BonjourData
*)data
)->jabber_data
;
1314 check_if_blocked(PurpleBuddy
*pb
)
1316 gboolean blocked
= FALSE
;
1318 PurpleAccount
*acc
= purple_buddy_get_account(pb
);
1323 acc
= purple_buddy_get_account(pb
);
1325 for(l
= purple_account_privacy_get_denied(acc
); l
!= NULL
; l
= l
->next
) {
1326 const gchar
*name
= purple_buddy_get_name(pb
);
1327 const gchar
*username
= bonjour_get_jid(acc
);
1329 if(!purple_utf8_strcasecmp(name
, (char *)l
->data
)) {
1330 purple_debug_info("bonjour", "%s has been blocked by %s.\n", name
, username
);
1339 xep_iq_parse(PurpleXmlNode
*packet
, PurpleBuddy
*pb
)
1341 PurpleAccount
*account
;
1342 PurpleConnection
*gc
;
1344 if(check_if_blocked(pb
))
1347 account
= purple_buddy_get_account(pb
);
1348 gc
= purple_account_get_connection(account
);
1350 if (purple_xmlnode_get_child(packet
, "si") != NULL
|| purple_xmlnode_get_child(packet
, "error") != NULL
)
1351 xep_si_parse(gc
, packet
, pb
);
1353 xep_bytestreams_parse(gc
, packet
, pb
);
1357 xep_iq_send_and_free(XepIq
*iq
)
1360 PurpleBuddy
*pb
= NULL
;
1362 /* start the talk, reuse the message socket */
1363 pb
= _find_or_start_conversation((BonjourJabber
*) iq
->data
, iq
->to
);
1364 /* Send the message */
1366 /* Convert xml node into stream */
1367 gchar
*msg
= purple_xmlnode_to_str(iq
->node
, NULL
);
1368 ret
= _send_data(pb
, msg
);
1372 purple_xmlnode_free(iq
->node
);
1376 return (ret
>= 0) ? 0 : -1;
1379 /* This returns a list containing all non-localhost IPs */
1381 bonjour_jabber_get_local_ips(int fd
)
1384 const char *address_text
;
1387 #ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
1388 struct ifaddrs
*ifap
, *ifa
;
1389 common_sockaddr_t addr
;
1390 char addrstr
[INET6_ADDRSTRLEN
];
1392 ret
= getifaddrs(&ifap
);
1394 const char *error
= g_strerror(errno
);
1395 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error
? error
: "(null)");
1399 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1400 if (!(ifa
->ifa_flags
& IFF_RUNNING
) || (ifa
->ifa_flags
& IFF_LOOPBACK
) || ifa
->ifa_addr
== NULL
)
1403 memcpy(&addr
, ifa
->ifa_addr
, sizeof(addr
));
1404 address_text
= NULL
;
1405 switch (addr
.sa
.sa_family
) {
1407 address_text
= inet_ntop(addr
.sa
.sa_family
,
1409 addrstr
, sizeof(addrstr
));
1413 address_text
= inet_ntop(addr
.sa
.sa_family
,
1414 &addr
.in6
.sin6_addr
,
1415 addrstr
, sizeof(addrstr
));
1420 if (address_text
!= NULL
) {
1421 if (addr
.sa
.sa_family
== AF_INET
)
1422 ips
= g_slist_append(ips
, g_strdup(address_text
));
1424 ips
= g_slist_prepend(ips
, g_strdup(address_text
));
1434 struct sockaddr_in
*sinptr
;
1438 source
= socket(PF_INET
, SOCK_STREAM
, 0);
1440 ifc
.ifc_len
= sizeof(buffer
);
1441 ifc
.ifc_req
= (struct ifreq
*)buffer
;
1442 ret
= ioctl(source
, SIOCGIFCONF
, &ifc
);
1448 const char *error
= g_strerror(errno
);
1449 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error
? error
: "(null)");
1454 while (tmp
< buffer
+ ifc
.ifc_len
) {
1455 ifr
= (struct ifreq
*)tmp
;
1456 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
1458 if (ifr
->ifr_addr
.sa_family
== AF_INET
) {
1459 sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1460 if ((ntohl(sinptr
->sin_addr
.s_addr
) >> 24) != 127) {
1461 address_text
= inet_ntoa(sinptr
->sin_addr
);
1462 ips
= g_slist_prepend(ips
, g_strdup(address_text
));
1472 append_iface_if_linklocal(char *ip
, guint32 interface_param
) {
1473 struct in6_addr in6_addr
;
1474 int len_remain
= INET6_ADDRSTRLEN
- strlen(ip
);
1476 if (len_remain
<= 1)
1479 if (inet_pton(AF_INET6
, ip
, &in6_addr
) != 1 ||
1480 !IN6_IS_ADDR_LINKLOCAL(&in6_addr
))
1483 snprintf(ip
+ strlen(ip
), len_remain
, "%%%d",