rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / bonjour / jabber.c
blob47fda8d99516ab14a281050270cba2c39f459305
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"
24 #include <purple.h>
26 #ifndef _WIN32
27 #include <net/if.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #endif
33 #include <sys/types.h>
35 /* Solaris */
36 #if defined (__SVR4) && defined (__sun)
37 #include <sys/sockio.h>
38 #endif
40 #include <glib.h>
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #include <fcntl.h>
46 #ifdef HAVE_GETIFADDRS
47 #include <ifaddrs.h>
48 #endif
50 #include "jabber.h"
51 #include "parser.h"
52 #include "bonjour.h"
53 #include "buddy.h"
54 #include "bonjour_ft.h"
56 #ifdef _SIZEOF_ADDR_IFREQ
57 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
58 #else
59 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
60 #endif
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 {
68 NOT_SENT = 0,
69 PARTIALLY_SENT = 1,
70 FULLY_SENT = 2
73 static void
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;
84 bconv->pb = pb;
85 bconv->account = account;
86 bconv->ip = g_strdup(ip);
88 bonjour_parser_setup(bconv);
90 return bconv;
93 static const char *
94 _font_size_ichat_to_purple(int size)
96 if (size > 24) {
97 return "7";
98 } else if (size >= 21) {
99 return "6";
100 } else if (size >= 17) {
101 return "5";
102 } else if (size >= 14) {
103 return "4";
104 } else if (size >= 12) {
105 return "3";
106 } else if (size >= 10) {
107 return "2";
110 return "1";
113 static gchar *
114 get_xmlnode_contents(PurpleXmlNode *node)
116 gchar *contents;
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. */
123 if (contents) {
124 char *bodystart = strchr(contents, '>');
125 char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
126 if (bodystart && bodyend && (bodystart + 1) != bodyend) {
127 *bodyend = '\0';
128 memmove(contents, bodystart + 1, (bodyend - bodystart));
132 return contents;
135 static void
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));
140 gchar *body = NULL;
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");
147 return;
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 */
155 return;
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) {
169 gchar *html_body;
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");
191 if (font_face)
192 g_string_append_printf(str, " face='%s'", font_face);
193 if (font_size)
194 g_string_append_printf(str, " size='%s'", font_size);
195 if (font_color)
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);
205 g_free(html_body);
211 /* Compose the message */
212 if (body == NULL && body_node != NULL)
213 body = purple_xmlnode_get_data(body_node);
215 if (body == NULL) {
216 purple_debug_error("bonjour", "No html body or regular body found.\n");
217 return;
220 /* Send the message to the UI */
221 purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
223 g_free(body);
226 struct _match_buddies_by_address {
227 const char *address;
228 GSList *matched_buddies;
231 static void
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.
244 if (bb != NULL)
246 const char *ip;
247 GSList *tmp = bb->ips;
249 while(tmp) {
250 ip = tmp->data;
251 if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
252 mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
253 break;
255 tmp = tmp->next;
260 static void
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;
266 gsize writelen;
267 gssize ret;
268 GError *error = NULL;
270 writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
272 if (writelen == 0) {
273 g_source_remove(bconv->tx_handler);
274 bconv->tx_handler = 0;
275 return;
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);
285 return;
286 } else if (ret <= 0) {
287 PurpleConversation *conv = NULL;
288 PurpleAccount *account = NULL;
290 purple_debug_error(
291 "bonjour",
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));
299 if (conv != NULL)
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);
307 return;
310 purple_circular_buffer_mark_read(bconv->tx_buf, ret);
313 static gint
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);
319 gssize ret;
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) {
327 ret = -1;
328 g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
329 "Not yet ready to send.");
330 } else {
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) {
337 ret = 0;
338 g_clear_error(&error);
339 } else if (ret <= 0) {
340 PurpleConversation *conv;
341 PurpleAccount *account;
343 purple_debug_error(
344 "bonjour",
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));
352 if (conv != NULL)
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);
360 return -1;
363 if (ret < len) {
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) {
367 GSource *source =
368 g_pollable_output_stream_create_source(
369 G_POLLABLE_OUTPUT_STREAM(bconv->output),
370 bconv->cancellable);
371 g_source_set_callback(source,
372 (GSourceFunc)_send_data_write_cb,
373 pb, NULL);
374 bconv->tx_handler = g_source_attach(source, NULL);
376 purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
379 return 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);
391 else {
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);
410 if(bb)
411 bb->conversation = NULL;
414 static gboolean
415 _client_socket_handler(GObject *stream, gpointer data)
417 BonjourJabberConversation *bconv = data;
418 GError *error = NULL;
419 gssize len;
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);
426 if (len == -1) {
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(
431 "bonjour",
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);
439 if(bb != NULL)
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);
447 return FALSE;
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);
452 return FALSE;
455 message[len] = '\0';
457 purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
458 bonjour_parser_process(bconv, message, len);
460 return TRUE;
463 struct _stream_start_data {
464 char *msg;
467 static void
468 _start_stream(GObject *stream, gpointer data)
470 BonjourJabberConversation *bconv = data;
471 struct _stream_start_data *ss = bconv->stream_data;
472 GError *error = NULL;
473 gsize len;
474 gssize ret;
476 len = strlen(ss->msg);
478 /* Start Stream */
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);
485 return;
486 } else if (ret <= 0) {
487 PurpleConversation *conv;
488 const char *bname = bconv->buddy_name;
489 BonjourBuddy *bb = NULL;
491 if(bconv->pb) {
492 bb = purple_buddy_get_protocol_data(bconv->pb);
493 bname = purple_buddy_get_name(bconv->pb);
496 purple_debug_error(
497 "bonjour",
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));
503 if (conv != NULL)
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);
509 if(bb != NULL)
510 bb->conversation = NULL;
512 g_clear_error(&error);
513 return;
516 /* This is EXTREMELY unlikely to happen */
517 if (ret < len) {
518 char *tmp = g_strdup(ss->msg + ret);
519 g_free(ss->msg);
520 ss->msg = tmp;
521 return;
524 g_free(ss->msg);
525 g_free(ss);
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);
536 static gboolean
537 bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv,
538 GError **error)
540 gchar *stream_start;
541 gsize len;
542 gssize ret;
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. */
552 if (bname == NULL)
553 bname = "";
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) {
565 ret = 0;
566 g_clear_error(error);
567 } else if (ret <= 0) {
568 purple_debug_error(
569 "bonjour",
570 "Error starting stream with buddy %s at %s error: %s",
571 (*bname) ? bname : "(unknown)", bconv->ip,
572 *error ? (*error)->message : "(null)");
574 if (bconv->pb) {
575 PurpleConversation *conv;
576 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
577 if (conv != NULL)
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);
587 bconv->input = NULL;
588 bconv->output = NULL;
589 g_free(stream_start);
591 return FALSE;
594 /* This is unlikely to happen */
595 if (ret < len) {
596 GSource *source;
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),
603 bconv->cancellable);
604 g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
605 NULL);
606 bconv->tx_handler = g_source_attach(source, NULL);
607 } else {
608 bconv->sent_stream_start = FULLY_SENT;
611 g_free(stream_start);
613 return TRUE;
616 /* This gets called when we've successfully sent our <stream:stream />
617 * AND when we've received a <stream:stream /> */
618 void
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;
627 if (bconv->pb)
628 bname = purple_buddy_get_name(bconv->pb);
630 purple_debug_error(
631 "bonjour",
632 "Error starting stream with buddy %s at %s error: %s",
633 bname ? bname : "(unknown)", bconv->ip,
634 error ? error->message : "(null)");
636 if (bconv->pb) {
637 PurpleConversation *conv;
638 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
639 if (conv != NULL)
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);
650 bconv->input = NULL;
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);
658 return;
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),
668 bconv->cancellable);
669 g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
670 bconv->pb, NULL);
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
679 #endif
681 static void
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;
688 gchar *address_text;
689 struct _match_buddies_by_address *mbba;
690 BonjourJabberConversation *bconv;
691 GSList *buddies;
692 GSource *source;
694 their_addr = g_socket_connection_get_remote_address(connection, NULL);
695 if (their_addr == NULL) {
696 return;
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);
711 address_text = tmp;
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);
726 g_free(mbba);
727 return;
730 g_slist_free(mbba->matched_buddies);
731 g_free(mbba);
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));
741 bconv->output =
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,
746 bconv, NULL);
747 bconv->rx_handler = g_source_attach(source, NULL);
748 g_free(address_text);
751 gint
752 bonjour_jabber_start(BonjourJabber *jdata)
754 GError *error = NULL;
755 guint16 port;
757 purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
758 jdata->port);
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);
763 port = jdata->port;
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);
772 if (port == 0) {
773 purple_debug_error(
774 "bonjour", "Unable to create socket: %s",
775 error ? error->message : "(unknown)");
776 g_clear_error(&error);
777 return -1;
780 purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
781 jdata->port = port;
783 g_signal_connect(G_OBJECT(jdata->service), "incoming",
784 G_CALLBACK(_server_socket_handler), jdata);
786 return jdata->port;
789 static void
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;
795 GSource *rx_source;
796 GError *error = NULL;
798 conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
799 res, &error);
801 if (conn == NULL) {
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. */
808 g_error_free(error);
809 return;
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);
826 if (tmp)
827 tmp = g_slist_next(tmp);
829 account = purple_buddy_get_account(pb);
831 if (tmp != NULL) {
832 const gchar *ip;
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);
850 return;
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));
857 if (conv != NULL)
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;
864 return;
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 "
879 "%s:%d error: %s",
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));
887 if (conv != NULL)
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);
895 return;
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);
907 void
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))) {
917 const char *ip;
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 */
924 while(tmp) {
925 ip = tmp->data;
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);
941 bconv->pb = pb;
942 bb->conversation = bconv;
944 break;
946 tmp = tmp->next;
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);
960 void
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;
966 GSList *buddies;
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);
979 else {
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);
993 bconv->pb = pb;
994 bb->conversation = bconv;
996 } else
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);
1008 g_free(mbba);
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 */
1023 return NULL;
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,
1033 ip, bb->port_p2pj);
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).",
1040 to);
1041 return NULL;
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);
1052 return pb;
1056 bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
1058 PurpleXmlNode *message_node, *node, *node2;
1059 gchar *message, *xhtml;
1060 PurpleBuddy *pb;
1061 BonjourBuddy *bb;
1062 int ret;
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 */
1068 return -10000;
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));
1081 g_free(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));
1089 g_free(xhtml);
1090 g_free(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;
1102 g_free(message);
1104 return ret;
1107 static gboolean
1108 _async_bonjour_jabber_close_conversation_cb(gpointer data) {
1109 BonjourJabberConversation *bconv = data;
1110 bonjour_jabber_close_conversation(bconv);
1111 return FALSE;
1114 void
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);
1132 void
1133 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
1135 BonjourData *bd = NULL;
1136 PurpleConnection *pc = NULL;
1138 if (bconv == NULL) {
1139 return;
1142 pc = purple_account_get_connection(bconv->account);
1143 PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
1145 bd = purple_connection_get_protocol_data(pc);
1146 if (bd) {
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);
1166 xfers = tmp_next;
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;
1212 g_free(ss->msg);
1213 g_free(ss);
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);
1225 g_free(bconv->ip);
1226 g_free(bconv);
1229 void
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);
1263 XepIq *
1264 xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
1266 PurpleXmlNode *iq_node = NULL;
1267 XepIq *iq = 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);
1278 switch (type) {
1279 case XEP_IQ_SET:
1280 purple_xmlnode_set_attrib(iq_node, "type", "set");
1281 break;
1282 case XEP_IQ_GET:
1283 purple_xmlnode_set_attrib(iq_node, "type", "get");
1284 break;
1285 case XEP_IQ_RESULT:
1286 purple_xmlnode_set_attrib(iq_node, "type", "result");
1287 break;
1288 case XEP_IQ_ERROR:
1289 purple_xmlnode_set_attrib(iq_node, "type", "error");
1290 break;
1291 case XEP_IQ_NONE:
1292 default:
1293 purple_xmlnode_set_attrib(iq_node, "type", "none");
1294 break;
1297 iq = g_new0(XepIq, 1);
1298 iq->node = iq_node;
1299 iq->type = type;
1300 iq->data = ((BonjourData*)data)->jabber_data;
1301 iq->to = (char*)to;
1303 return iq;
1306 static gboolean
1307 check_if_blocked(PurpleBuddy *pb)
1309 gboolean blocked = FALSE;
1310 GSList *l = NULL;
1311 PurpleAccount *acc = purple_buddy_get_account(pb);
1313 if(acc == NULL)
1314 return FALSE;
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);
1324 blocked = TRUE;
1325 break;
1328 return blocked;
1331 static void
1332 xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
1334 PurpleAccount *account;
1335 PurpleConnection *gc;
1337 if(check_if_blocked(pb))
1338 return;
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);
1345 else
1346 xep_bytestreams_parse(gc, packet, pb);
1350 xep_iq_send_and_free(XepIq *iq)
1352 int ret = -1;
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 */
1358 if (pb != NULL) {
1359 /* Convert xml node into stream */
1360 gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
1361 ret = _send_data(pb, msg);
1362 g_free(msg);
1365 purple_xmlnode_free(iq->node);
1366 iq->node = NULL;
1367 g_free(iq);
1369 return (ret >= 0) ? 0 : -1;
1372 /* This returns a list containing all non-localhost IPs */
1373 GSList *
1374 bonjour_jabber_get_local_ips(int fd)
1376 GSList *ips = NULL;
1377 const char *address_text;
1378 int ret;
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);
1386 if (ret != 0) {
1387 const char *error = g_strerror(errno);
1388 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
1389 return 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)
1394 continue;
1396 memcpy(&addr, ifa->ifa_addr, sizeof(addr));
1397 address_text = NULL;
1398 switch (addr.sa.sa_family) {
1399 case AF_INET:
1400 address_text = inet_ntop(addr.sa.sa_family,
1401 &addr.in.sin_addr,
1402 addrstr, sizeof(addrstr));
1403 break;
1404 #ifdef PF_INET6
1405 case AF_INET6:
1406 address_text = inet_ntop(addr.sa.sa_family,
1407 &addr.in6.sin6_addr,
1408 addrstr, sizeof(addrstr));
1409 break;
1410 #endif
1413 if (address_text != NULL) {
1414 if (addr.sa.sa_family == AF_INET)
1415 ips = g_slist_append(ips, g_strdup(address_text));
1416 else
1417 ips = g_slist_prepend(ips, g_strdup(address_text));
1421 freeifaddrs(ifap);
1422 #else
1423 char *tmp;
1424 struct ifconf ifc;
1425 struct ifreq *ifr;
1426 char buffer[1024];
1427 struct sockaddr_in *sinptr;
1428 int source = fd;
1430 if (fd < 0)
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);
1437 if (fd < 0)
1438 close(source);
1440 if (ret < 0) {
1441 const char *error = g_strerror(errno);
1442 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
1443 return NULL;
1446 tmp = buffer;
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));
1459 #endif
1461 return ips;
1464 void
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)
1470 return;
1472 if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
1473 !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
1474 return;
1476 snprintf(ip + strlen(ip), len_remain, "%%%d",
1477 interface_param);