Remove unused Bonjour connect data.
[pidgin-git.git] / libpurple / protocols / bonjour / jabber.c
blob087f6f1502b5579786ed7f8c2990ff9e97c667f0
1 /*
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
6 * source distribution.
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
23 #include "internal.h"
25 #ifndef _WIN32
26 #include <net/if.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #endif
32 #include <sys/types.h>
34 /* Solaris */
35 #if defined (__SVR4) && defined (__sun)
36 #include <sys/sockio.h>
37 #endif
39 #include <glib.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <fcntl.h>
45 #ifdef HAVE_GETIFADDRS
46 #include <ifaddrs.h>
47 #endif
49 #include "buddylist.h"
50 #include "connection.h"
51 #include "debug.h"
52 #include "eventloop.h"
53 #include "network.h"
54 #include "notify.h"
55 #include "purple-gio.h"
56 #include "util.h"
57 #include "xmlnode.h"
59 #include "jabber.h"
60 #include "parser.h"
61 #include "bonjour.h"
62 #include "buddy.h"
63 #include "bonjour_ft.h"
65 #ifdef _SIZEOF_ADDR_IFREQ
66 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
67 #else
68 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
69 #endif
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 {
77 NOT_SENT = 0,
78 PARTIALLY_SENT = 1,
79 FULLY_SENT = 2
82 static void
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;
93 bconv->pb = pb;
94 bconv->account = account;
95 bconv->ip = g_strdup(ip);
97 bonjour_parser_setup(bconv);
99 return bconv;
102 static const char *
103 _font_size_ichat_to_purple(int size)
105 if (size > 24) {
106 return "7";
107 } else if (size >= 21) {
108 return "6";
109 } else if (size >= 17) {
110 return "5";
111 } else if (size >= 14) {
112 return "4";
113 } else if (size >= 12) {
114 return "3";
115 } else if (size >= 10) {
116 return "2";
119 return "1";
122 static gchar *
123 get_xmlnode_contents(PurpleXmlNode *node)
125 gchar *contents;
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. */
132 if (contents) {
133 char *bodystart = strchr(contents, '>');
134 char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
135 if (bodystart && bodyend && (bodystart + 1) != bodyend) {
136 *bodyend = '\0';
137 memmove(contents, bodystart + 1, (bodyend - bodystart));
141 return contents;
144 static void
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));
149 gchar *body = NULL;
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");
156 return;
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 */
164 return;
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) {
178 gchar *html_body;
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");
200 if (font_face)
201 g_string_append_printf(str, " face='%s'", font_face);
202 if (font_size)
203 g_string_append_printf(str, " size='%s'", font_size);
204 if (font_color)
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);
214 g_free(html_body);
220 /* Compose the message */
221 if (body == NULL && body_node != NULL)
222 body = purple_xmlnode_get_data(body_node);
224 if (body == NULL) {
225 purple_debug_error("bonjour", "No html body or regular body found.\n");
226 return;
229 /* Send the message to the UI */
230 purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
232 g_free(body);
235 struct _match_buddies_by_address_t {
236 const char *address;
237 GSList *matched_buddies;
240 static void
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.
253 if (bb != NULL)
255 const char *ip;
256 GSList *tmp = bb->ips;
258 while(tmp) {
259 ip = tmp->data;
260 if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
261 mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
262 break;
264 tmp = tmp->next;
269 static void
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;
275 gsize writelen;
276 gssize ret;
277 GError *error = NULL;
279 writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
281 if (writelen == 0) {
282 g_source_remove(bconv->tx_handler);
283 bconv->tx_handler = 0;
284 return;
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);
294 return;
295 } else if (ret <= 0) {
296 PurpleConversation *conv = NULL;
297 PurpleAccount *account = NULL;
299 purple_debug_error(
300 "bonjour",
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));
308 if (conv != NULL)
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);
316 return;
319 purple_circular_buffer_mark_read(bconv->tx_buf, ret);
322 static gint
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);
328 gssize ret;
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) {
336 ret = -1;
337 g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
338 "Not yet ready to send.");
339 } else {
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) {
346 ret = 0;
347 g_clear_error(&error);
348 } else if (ret <= 0) {
349 PurpleConversation *conv;
350 PurpleAccount *account;
352 purple_debug_error(
353 "bonjour",
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));
361 if (conv != NULL)
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);
369 return -1;
372 if (ret < len) {
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) {
376 GSource *source =
377 g_pollable_output_stream_create_source(
378 G_POLLABLE_OUTPUT_STREAM(bconv->output),
379 bconv->cancellable);
380 g_source_set_callback(source,
381 (GSourceFunc)_send_data_write_cb,
382 pb, NULL);
383 bconv->tx_handler = g_source_attach(source, NULL);
385 purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
388 return 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);
400 else {
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);
419 if(bb)
420 bb->conversation = NULL;
423 static gboolean
424 _client_socket_handler(GObject *stream, gpointer data)
426 BonjourJabberConversation *bconv = data;
427 GError *error = NULL;
428 gssize len;
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);
435 if (len == -1) {
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(
440 "bonjour",
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);
448 if(bb != NULL)
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);
456 return FALSE;
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);
461 return FALSE;
464 message[len] = '\0';
466 purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
467 bonjour_parser_process(bconv, message, len);
469 return TRUE;
472 struct _stream_start_data {
473 char *msg;
476 static void
477 _start_stream(GObject *stream, gpointer data)
479 BonjourJabberConversation *bconv = data;
480 struct _stream_start_data *ss = bconv->stream_data;
481 GError *error = NULL;
482 gsize len;
483 gssize ret;
485 len = strlen(ss->msg);
487 /* Start Stream */
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);
494 return;
495 } else if (ret <= 0) {
496 PurpleConversation *conv;
497 const char *bname = bconv->buddy_name;
498 BonjourBuddy *bb = NULL;
500 if(bconv->pb) {
501 bb = purple_buddy_get_protocol_data(bconv->pb);
502 bname = purple_buddy_get_name(bconv->pb);
505 purple_debug_error(
506 "bonjour",
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));
512 if (conv != NULL)
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);
518 if(bb != NULL)
519 bb->conversation = NULL;
521 g_clear_error(&error);
522 return;
525 /* This is EXTREMELY unlikely to happen */
526 if (ret < len) {
527 char *tmp = g_strdup(ss->msg + ret);
528 g_free(ss->msg);
529 ss->msg = tmp;
530 return;
533 g_free(ss->msg);
534 g_free(ss);
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);
545 static gboolean
546 bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv,
547 GError **error)
549 gchar *stream_start;
550 gsize len;
551 gssize ret;
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. */
559 if (bname == NULL)
560 bname = "";
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) {
572 ret = 0;
573 g_clear_error(error);
574 } else if (ret <= 0) {
575 purple_debug_error(
576 "bonjour",
577 "Error starting stream with buddy %s at %s error: %s",
578 (*bname) ? bname : "(unknown)", bconv->ip,
579 *error ? (*error)->message : "(null)");
581 if (bconv->pb) {
582 PurpleConversation *conv;
583 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
584 if (conv != NULL)
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);
594 bconv->input = NULL;
595 bconv->output = NULL;
596 g_free(stream_start);
598 return FALSE;
601 /* This is unlikely to happen */
602 if (ret < len) {
603 GSource *source;
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),
610 bconv->cancellable);
611 g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
612 NULL);
613 bconv->tx_handler = g_source_attach(source, NULL);
614 } else {
615 bconv->sent_stream_start = FULLY_SENT;
618 g_free(stream_start);
620 return TRUE;
623 /* This gets called when we've successfully sent our <stream:stream />
624 * AND when we've received a <stream:stream /> */
625 void
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;
634 if (bconv->pb)
635 bname = purple_buddy_get_name(bconv->pb);
637 purple_debug_error(
638 "bonjour",
639 "Error starting stream with buddy %s at %s error: %s",
640 bname ? bname : "(unknown)", bconv->ip,
641 error ? error->message : "(null)");
643 if (bconv->pb) {
644 PurpleConversation *conv;
645 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
646 if (conv != NULL)
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);
657 bconv->input = NULL;
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);
665 return;
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),
675 bconv->cancellable);
676 g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
677 bconv->pb, NULL);
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
686 #endif
688 static void
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;
695 gchar *address_text;
696 struct _match_buddies_by_address_t *mbba;
697 BonjourJabberConversation *bconv;
698 GSList *buddies;
699 GSource *source;
701 their_addr = g_socket_connection_get_remote_address(connection, NULL);
702 if (their_addr == NULL) {
703 return;
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);
718 address_text = tmp;
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);
733 g_free(mbba);
734 return;
737 g_slist_free(mbba->matched_buddies);
738 g_free(mbba);
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));
748 bconv->output =
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,
753 bconv, NULL);
754 bconv->rx_handler = g_source_attach(source, NULL);
755 g_free(address_text);
758 gint
759 bonjour_jabber_start(BonjourJabber *jdata)
761 GError *error = NULL;
762 guint16 port;
764 purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
765 jdata->port);
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);
770 port = jdata->port;
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);
779 if (port == 0) {
780 purple_debug_error(
781 "bonjour", "Unable to create socket: %s",
782 error ? error->message : "(unknown)");
783 g_clear_error(&error);
784 return -1;
787 purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
788 jdata->port = port;
790 g_signal_connect(G_OBJECT(jdata->service), "incoming",
791 G_CALLBACK(_server_socket_handler), jdata);
793 return jdata->port;
796 static void
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;
802 GSource *rx_source;
803 GError *error = NULL;
805 conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
806 res, &error);
808 if (conn == NULL) {
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. */
815 g_error_free(error);
816 return;
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);
833 if (tmp)
834 tmp = g_slist_next(tmp);
836 account = purple_buddy_get_account(pb);
838 if (tmp != NULL) {
839 const gchar *ip;
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);
857 return;
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));
864 if (conv != NULL)
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;
871 return;
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 "
886 "%s:%d error: %s",
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));
894 if (conv != NULL)
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);
902 return;
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);
914 void
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))) {
924 const char *ip;
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 */
931 while(tmp) {
932 ip = tmp->data;
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);
948 bconv->pb = pb;
949 bb->conversation = bconv;
951 break;
953 tmp = tmp->next;
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);
967 void
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;
973 GSList *buddies;
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);
986 else {
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);
1000 bconv->pb = pb;
1001 bb->conversation = bconv;
1003 } else
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);
1015 g_free(mbba);
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 */
1030 return NULL;
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,
1040 ip, bb->port_p2pj);
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).",
1047 to);
1048 return NULL;
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);
1059 return pb;
1063 bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
1065 PurpleXmlNode *message_node, *node, *node2;
1066 gchar *message, *xhtml;
1067 PurpleBuddy *pb;
1068 BonjourBuddy *bb;
1069 int ret;
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 */
1075 return -10000;
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));
1088 g_free(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));
1096 g_free(xhtml);
1097 g_free(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;
1109 g_free(message);
1111 return ret;
1114 static gboolean
1115 _async_bonjour_jabber_close_conversation_cb(gpointer data) {
1116 BonjourJabberConversation *bconv = data;
1117 bonjour_jabber_close_conversation(bconv);
1118 return FALSE;
1121 void
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);
1139 void
1140 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
1142 BonjourData *bd = NULL;
1143 PurpleConnection *pc = NULL;
1145 if (bconv == NULL) {
1146 return;
1149 pc = purple_account_get_connection(bconv->account);
1150 PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
1152 bd = purple_connection_get_protocol_data(pc);
1153 if (bd) {
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);
1173 xfers = tmp_next;
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;
1219 g_free(ss->msg);
1220 g_free(ss);
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);
1232 g_free(bconv->ip);
1233 g_free(bconv);
1236 void
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);
1270 XepIq *
1271 xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
1273 PurpleXmlNode *iq_node = NULL;
1274 XepIq *iq = 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);
1285 switch (type) {
1286 case XEP_IQ_SET:
1287 purple_xmlnode_set_attrib(iq_node, "type", "set");
1288 break;
1289 case XEP_IQ_GET:
1290 purple_xmlnode_set_attrib(iq_node, "type", "get");
1291 break;
1292 case XEP_IQ_RESULT:
1293 purple_xmlnode_set_attrib(iq_node, "type", "result");
1294 break;
1295 case XEP_IQ_ERROR:
1296 purple_xmlnode_set_attrib(iq_node, "type", "error");
1297 break;
1298 case XEP_IQ_NONE:
1299 default:
1300 purple_xmlnode_set_attrib(iq_node, "type", "none");
1301 break;
1304 iq = g_new0(XepIq, 1);
1305 iq->node = iq_node;
1306 iq->type = type;
1307 iq->data = ((BonjourData*)data)->jabber_data;
1308 iq->to = (char*)to;
1310 return iq;
1313 static gboolean
1314 check_if_blocked(PurpleBuddy *pb)
1316 gboolean blocked = FALSE;
1317 GSList *l = NULL;
1318 PurpleAccount *acc = purple_buddy_get_account(pb);
1320 if(acc == NULL)
1321 return FALSE;
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);
1331 blocked = TRUE;
1332 break;
1335 return blocked;
1338 static void
1339 xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
1341 PurpleAccount *account;
1342 PurpleConnection *gc;
1344 if(check_if_blocked(pb))
1345 return;
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);
1352 else
1353 xep_bytestreams_parse(gc, packet, pb);
1357 xep_iq_send_and_free(XepIq *iq)
1359 int ret = -1;
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 */
1365 if (pb != NULL) {
1366 /* Convert xml node into stream */
1367 gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
1368 ret = _send_data(pb, msg);
1369 g_free(msg);
1372 purple_xmlnode_free(iq->node);
1373 iq->node = NULL;
1374 g_free(iq);
1376 return (ret >= 0) ? 0 : -1;
1379 /* This returns a list containing all non-localhost IPs */
1380 GSList *
1381 bonjour_jabber_get_local_ips(int fd)
1383 GSList *ips = NULL;
1384 const char *address_text;
1385 int ret;
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);
1393 if (ret != 0) {
1394 const char *error = g_strerror(errno);
1395 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
1396 return 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)
1401 continue;
1403 memcpy(&addr, ifa->ifa_addr, sizeof(addr));
1404 address_text = NULL;
1405 switch (addr.sa.sa_family) {
1406 case AF_INET:
1407 address_text = inet_ntop(addr.sa.sa_family,
1408 &addr.in.sin_addr,
1409 addrstr, sizeof(addrstr));
1410 break;
1411 #ifdef PF_INET6
1412 case AF_INET6:
1413 address_text = inet_ntop(addr.sa.sa_family,
1414 &addr.in6.sin6_addr,
1415 addrstr, sizeof(addrstr));
1416 break;
1417 #endif
1420 if (address_text != NULL) {
1421 if (addr.sa.sa_family == AF_INET)
1422 ips = g_slist_append(ips, g_strdup(address_text));
1423 else
1424 ips = g_slist_prepend(ips, g_strdup(address_text));
1428 freeifaddrs(ifap);
1429 #else
1430 char *tmp;
1431 struct ifconf ifc;
1432 struct ifreq *ifr;
1433 char buffer[1024];
1434 struct sockaddr_in *sinptr;
1435 int source = fd;
1437 if (fd < 0)
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);
1444 if (fd < 0)
1445 close(source);
1447 if (ret < 0) {
1448 const char *error = g_strerror(errno);
1449 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
1450 return NULL;
1453 tmp = buffer;
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));
1466 #endif
1468 return ips;
1471 void
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)
1477 return;
1479 if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
1480 !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
1481 return;
1483 snprintf(ip + strlen(ip), len_remain, "%%%d",
1484 interface_param);