mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / bonjour / jabber.c
blobe4060b123f3568603fe5eebd4edaae072eef358f
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 {
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 *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 g_return_val_if_fail(error != NULL, FALSE);
556 if (bconv->pb != NULL)
557 bname = purple_buddy_get_name(bconv->pb);
559 /* If we have no idea who "to" is, use an empty string.
560 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
561 if (bname == NULL)
562 bname = "";
564 stream_start = g_strdup_printf(DOCTYPE, bonjour_get_jid(bconv->account), bname);
565 len = strlen(stream_start);
567 bconv->sent_stream_start = PARTIALLY_SENT;
569 /* Start the stream */
570 ret = g_pollable_output_stream_write_nonblocking(
571 G_POLLABLE_OUTPUT_STREAM(bconv->output), stream_start, len,
572 bconv->cancellable, error);
573 if (ret == -1 && (*error)->code == G_IO_ERROR_WOULD_BLOCK) {
574 ret = 0;
575 g_clear_error(error);
576 } else if (ret <= 0) {
577 purple_debug_error(
578 "bonjour",
579 "Error starting stream with buddy %s at %s error: %s",
580 (*bname) ? bname : "(unknown)", bconv->ip,
581 *error ? (*error)->message : "(null)");
583 if (bconv->pb) {
584 PurpleConversation *conv;
585 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
586 if (conv != NULL)
587 purple_conversation_write_system_message(conv,
588 _("Unable to send the message, the conversation couldn't be started."),
589 PURPLE_MESSAGE_ERROR);
592 purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
593 G_INPUT_STREAM(bconv->input),
594 G_OUTPUT_STREAM(bconv->output));
595 g_clear_object(&bconv->socket);
596 bconv->input = NULL;
597 bconv->output = NULL;
598 g_free(stream_start);
600 return FALSE;
603 /* This is unlikely to happen */
604 if (ret < len) {
605 GSource *source;
606 struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
607 ss->msg = g_strdup(stream_start + ret);
608 bconv->stream_data = ss;
609 /* Finish sending the stream start */
610 source = g_pollable_output_stream_create_source(
611 G_POLLABLE_OUTPUT_STREAM(bconv->output),
612 bconv->cancellable);
613 g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
614 NULL);
615 bconv->tx_handler = g_source_attach(source, NULL);
616 } else {
617 bconv->sent_stream_start = FULLY_SENT;
620 g_free(stream_start);
622 return TRUE;
625 /* This gets called when we've successfully sent our <stream:stream />
626 * AND when we've received a <stream:stream /> */
627 void
628 bonjour_jabber_stream_started(BonjourJabberConversation *bconv)
630 GError *error = NULL;
632 if (bconv->sent_stream_start == NOT_SENT &&
633 !bonjour_jabber_send_stream_init(bconv, &error)) {
634 const char *bname = bconv->buddy_name;
636 if (bconv->pb)
637 bname = purple_buddy_get_name(bconv->pb);
639 purple_debug_error(
640 "bonjour",
641 "Error starting stream with buddy %s at %s error: %s",
642 bname ? bname : "(unknown)", bconv->ip,
643 error ? error->message : "(null)");
645 if (bconv->pb) {
646 PurpleConversation *conv;
647 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
648 if (conv != NULL)
649 purple_conversation_write_system_message(conv,
650 _("Unable to send the message, the conversation couldn't be started."),
651 PURPLE_MESSAGE_ERROR);
654 /* We don't want to recieve anything else */
655 purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
656 G_INPUT_STREAM(bconv->input),
657 G_OUTPUT_STREAM(bconv->output));
658 g_clear_object(&bconv->socket);
659 bconv->input = NULL;
660 bconv->output = NULL;
662 /* This must be asynchronous because it destroys the parser and we
663 * may be in the middle of parsing.
665 async_bonjour_jabber_close_conversation(bconv);
666 g_clear_error(&error);
667 return;
670 /* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
671 /* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
672 if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
673 && bconv->pb && purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
674 /* Watch for when we can write the buffered messages */
675 GSource *source = g_pollable_output_stream_create_source(
676 G_POLLABLE_OUTPUT_STREAM(bconv->output),
677 bconv->cancellable);
678 g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
679 bconv->pb, NULL);
680 bconv->tx_handler = g_source_attach(source, NULL);
681 /* We can probably write the data right now. */
682 _send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
686 #ifndef INET6_ADDRSTRLEN
687 #define INET6_ADDRSTRLEN 46
688 #endif
690 static void
691 _server_socket_handler(GSocketService *service, GSocketConnection *connection,
692 GObject *source_object, gpointer data)
694 BonjourJabber *jdata = data;
695 GSocketAddress *their_addr; /* connector's address information */
696 GInetAddress *their_inet_addr;
697 gchar *address_text;
698 struct _match_buddies_by_address *mbba;
699 BonjourJabberConversation *bconv;
700 GSList *buddies;
701 GSource *source;
703 their_addr = g_socket_connection_get_remote_address(connection, NULL);
704 if (their_addr == NULL) {
705 return;
707 their_inet_addr = g_inet_socket_address_get_address(
708 G_INET_SOCKET_ADDRESS(their_addr));
710 /* Look for the buddy that has opened the conversation and fill information */
711 address_text = g_inet_address_to_string(their_inet_addr);
712 if (g_inet_address_get_family(their_inet_addr) ==
713 G_SOCKET_FAMILY_IPV6 &&
714 g_inet_address_get_is_link_local(their_inet_addr)) {
715 gchar *tmp = g_strdup_printf(
716 "%s%%%d", address_text,
717 g_inet_socket_address_get_scope_id(
718 G_INET_SOCKET_ADDRESS(their_addr)));
719 g_free(address_text);
720 address_text = tmp;
722 g_object_unref(their_addr);
724 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
725 mbba = g_new0(struct _match_buddies_by_address, 1);
726 mbba->address = address_text;
728 buddies = purple_blist_find_buddies(jdata->account, NULL);
729 g_slist_foreach(buddies, _match_buddies_by_address, mbba);
730 g_slist_free(buddies);
732 if (mbba->matched_buddies == NULL) {
733 purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
734 g_free(address_text);
735 g_free(mbba);
736 return;
739 g_slist_free(mbba->matched_buddies);
740 g_free(mbba);
742 /* We've established that this *could* be from one of our buddies.
743 * Wait for the stream open to see if that matches too before assigning it.
745 bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
747 /* We wait for the stream start before doing anything else */
748 bconv->socket = g_object_ref(connection);
749 bconv->input = g_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
750 bconv->output =
751 g_io_stream_get_output_stream(G_IO_STREAM(bconv->socket));
752 source = g_pollable_input_stream_create_source(
753 G_POLLABLE_INPUT_STREAM(bconv->input), bconv->cancellable);
754 g_source_set_callback(source, (GSourceFunc)_client_socket_handler,
755 bconv, NULL);
756 bconv->rx_handler = g_source_attach(source, NULL);
757 g_free(address_text);
760 gint
761 bonjour_jabber_start(BonjourJabber *jdata)
763 GError *error = NULL;
764 guint16 port;
766 purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
767 jdata->port);
769 /* Open a listening server for incoming conversations */
770 jdata->service = g_socket_service_new();
771 g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata->service), 10);
772 port = jdata->port;
773 if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata->service),
774 port, NULL, &error)) {
775 purple_debug_info("bonjour",
776 "Unable to bind to specified port %i: %s",
777 port, error ? error->message : "(unknown)");
778 g_clear_error(&error);
779 port = g_socket_listener_add_any_inet_port(
780 G_SOCKET_LISTENER(jdata->service), NULL, &error);
781 if (port == 0) {
782 purple_debug_error(
783 "bonjour", "Unable to create socket: %s",
784 error ? error->message : "(unknown)");
785 g_clear_error(&error);
786 return -1;
789 purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
790 jdata->port = port;
792 g_signal_connect(G_OBJECT(jdata->service), "incoming",
793 G_CALLBACK(_server_socket_handler), jdata);
795 return jdata->port;
798 static void
799 _connected_to_buddy(GObject *source, GAsyncResult *res, gpointer user_data)
801 PurpleBuddy *pb = user_data;
802 BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
803 GSocketConnection *conn;
804 GSource *rx_source;
805 GError *error = NULL;
807 conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
808 res, &error);
810 if (conn == NULL) {
811 PurpleConversation *conv = NULL;
812 PurpleAccount *account = NULL;
813 GSList *tmp = bb->ips;
815 if (error && error->code == G_IO_ERROR_CANCELLED) {
816 /* This conversation was closed before it started. */
817 g_error_free(error);
818 return;
821 purple_debug_error("bonjour",
822 "Error connecting to buddy %s at %s:%d "
823 "(%s); Trying next IP address",
824 purple_buddy_get_name(pb),
825 bb->conversation->ip, bb->port_p2pj,
826 error ? error->message : "(unknown)");
827 g_clear_error(&error);
829 /* There may be multiple entries for the same IP - one per
830 * presence recieved (e.g. multiple interfaces).
831 * We need to make sure that we find the previously used entry.
833 while (tmp && bb->conversation->ip_link != tmp->data)
834 tmp = g_slist_next(tmp);
835 if (tmp)
836 tmp = g_slist_next(tmp);
838 account = purple_buddy_get_account(pb);
840 if (tmp != NULL) {
841 const gchar *ip;
842 GSocketClient *client;
844 bb->conversation->ip_link = ip = tmp->data;
846 purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
847 purple_buddy_get_name(pb), ip, bb->port_p2pj);
849 /* Make sure to connect without a proxy. */
850 client = g_socket_client_new();
851 if (client != NULL) {
852 g_free(bb->conversation->ip);
853 bb->conversation->ip = g_strdup(ip);
854 g_socket_client_connect_to_host_async(
855 client, ip, bb->port_p2pj,
856 bb->conversation->cancellable,
857 _connected_to_buddy, pb);
858 g_object_unref(client);
859 return;
863 purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
865 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
866 if (conv != NULL)
867 purple_conversation_write_system_message(conv,
868 _("Unable to send the message, the conversation couldn't be started."),
869 PURPLE_MESSAGE_ERROR);
871 bonjour_jabber_close_conversation(bb->conversation);
872 bb->conversation = NULL;
873 return;
876 bb->conversation->socket = conn;
877 bb->conversation->input =
878 g_io_stream_get_input_stream(G_IO_STREAM(conn));
879 bb->conversation->output =
880 g_io_stream_get_output_stream(G_IO_STREAM(conn));
882 if (!bonjour_jabber_send_stream_init(bb->conversation, &error)) {
883 PurpleConversation *conv = NULL;
884 PurpleAccount *account = NULL;
886 purple_debug_error("bonjour",
887 "Error starting stream with buddy %s at "
888 "%s:%d error: %s",
889 purple_buddy_get_name(pb),
890 bb->conversation->ip, bb->port_p2pj,
891 error ? error->message : "(null)");
893 account = purple_buddy_get_account(pb);
895 conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
896 if (conv != NULL)
897 purple_conversation_write_system_message(conv,
898 _("Unable to send the message, the conversation couldn't be started."),
899 PURPLE_MESSAGE_ERROR);
901 bonjour_jabber_close_conversation(bb->conversation);
902 bb->conversation = NULL;
903 g_clear_error(&error);
904 return;
907 /* Start listening for the stream acknowledgement */
908 rx_source = g_pollable_input_stream_create_source(
909 G_POLLABLE_INPUT_STREAM(bb->conversation->input),
910 bb->conversation->cancellable);
911 g_source_set_callback(rx_source, (GSourceFunc)_client_socket_handler,
912 bb->conversation, NULL);
913 bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
916 void
917 bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
918 PurpleBuddy *pb = NULL;
919 BonjourBuddy *bb = NULL;
921 g_return_if_fail(bconv->ip != NULL);
922 g_return_if_fail(bconv->pb == NULL);
924 pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
925 if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
926 const char *ip;
927 GSList *tmp = bb->ips;
929 purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
930 purple_buddy_get_name(pb));
932 /* Check that one of the buddy's IPs matches */
933 while(tmp) {
934 ip = tmp->data;
935 if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
936 PurpleConnection *pc = purple_account_get_connection(bconv->account);
937 BonjourData *bd = purple_connection_get_protocol_data(pc);
938 BonjourJabber *jdata = bd->jabber_data;
940 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
941 purple_buddy_get_name(pb), bconv->ip);
943 /* Attach conv. to buddy and remove from pending list */
944 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
946 /* Check if the buddy already has a conversation and, if so, replace it */
947 if(bb->conversation != NULL && bb->conversation != bconv)
948 bonjour_jabber_close_conversation(bb->conversation);
950 bconv->pb = pb;
951 bb->conversation = bconv;
953 break;
955 tmp = tmp->next;
959 /* We've failed to match a buddy - give up */
960 if (bconv->pb == NULL) {
961 /* This must be asynchronous because it destroys the parser and we
962 * may be in the middle of parsing.
964 async_bonjour_jabber_close_conversation(bconv);
969 void
970 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
971 PurpleConnection *pc = purple_account_get_connection(bconv->account);
972 BonjourData *bd = purple_connection_get_protocol_data(pc);
973 BonjourJabber *jdata = bd->jabber_data;
974 struct _match_buddies_by_address *mbba;
975 GSList *buddies;
977 mbba = g_new0(struct _match_buddies_by_address, 1);
978 mbba->address = bconv->ip;
980 buddies = purple_blist_find_buddies(jdata->account, NULL);
981 g_slist_foreach(buddies, _match_buddies_by_address, mbba);
982 g_slist_free(buddies);
984 /* If there is exactly one match, use it */
985 if(mbba->matched_buddies != NULL) {
986 if(mbba->matched_buddies->next != NULL)
987 purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
988 else {
989 PurpleBuddy *pb = mbba->matched_buddies->data;
990 BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
992 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
993 purple_buddy_get_name(pb), bconv->ip);
995 /* Attach conv. to buddy and remove from pending list */
996 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
998 /* Check if the buddy already has a conversation and, if so, replace it */
999 if (bb->conversation != NULL && bb->conversation != bconv)
1000 bonjour_jabber_close_conversation(bb->conversation);
1002 bconv->pb = pb;
1003 bb->conversation = bconv;
1005 } else
1006 purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
1008 /* We've failed to match a buddy - give up */
1009 if (bconv->pb == NULL) {
1010 /* This must be asynchronous because it destroys the parser and we
1011 * may be in the middle of parsing.
1013 async_bonjour_jabber_close_conversation(bconv);
1016 g_slist_free(mbba->matched_buddies);
1017 g_free(mbba);
1020 static PurpleBuddy *
1021 _find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
1023 PurpleBuddy *pb = NULL;
1024 BonjourBuddy *bb = NULL;
1026 g_return_val_if_fail(jdata != NULL, NULL);
1027 g_return_val_if_fail(to != NULL, NULL);
1029 pb = purple_blist_find_buddy(jdata->account, to);
1030 if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
1031 /* You can not send a message to an offline buddy */
1032 return NULL;
1034 /* Check if there is a previously open conversation */
1035 if (bb->conversation == NULL) {
1036 GSocketClient *client;
1037 /* Start with the first IP address. */
1038 const gchar *ip = bb->ips->data;
1040 purple_debug_info("bonjour",
1041 "Starting conversation with %s at %s:%d", to,
1042 ip, bb->port_p2pj);
1044 /* Make sure to connect without a proxy. */
1045 client = g_socket_client_new();
1046 if (client == NULL) {
1047 purple_debug_error("bonjour",
1048 "Unable to connect to buddy (%s).",
1049 to);
1050 return NULL;
1053 bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
1054 bb->conversation->ip_link = ip;
1056 g_socket_client_connect_to_host_async(
1057 client, ip, bb->port_p2pj,
1058 bb->conversation->cancellable, _connected_to_buddy, pb);
1059 g_object_unref(client);
1061 return pb;
1065 bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
1067 PurpleXmlNode *message_node, *node, *node2;
1068 gchar *message, *xhtml;
1069 PurpleBuddy *pb;
1070 BonjourBuddy *bb;
1071 int ret;
1073 pb = _find_or_start_conversation(jdata, to);
1074 if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
1075 purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
1076 /* You can not send a message to an offline buddy */
1077 return -10000;
1080 purple_markup_html_to_xhtml(body, &xhtml, &message);
1082 message_node = purple_xmlnode_new("message");
1083 purple_xmlnode_set_attrib(message_node, "to", bb->name);
1084 purple_xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
1085 purple_xmlnode_set_attrib(message_node, "type", "chat");
1087 /* Enclose the message from the UI within a "font" node */
1088 node = purple_xmlnode_new_child(message_node, "body");
1089 purple_xmlnode_insert_data(node, message, strlen(message));
1090 g_free(message);
1092 node = purple_xmlnode_new_child(message_node, "html");
1093 purple_xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
1095 node = purple_xmlnode_new_child(node, "body");
1096 message = g_strdup_printf("<font>%s</font>", xhtml);
1097 node2 = purple_xmlnode_from_str(message, strlen(message));
1098 g_free(xhtml);
1099 g_free(message);
1100 purple_xmlnode_insert_child(node, node2);
1102 node = purple_xmlnode_new_child(message_node, "x");
1103 purple_xmlnode_set_namespace(node, "jabber:x:event");
1104 purple_xmlnode_insert_child(node, purple_xmlnode_new("composing"));
1106 message = purple_xmlnode_to_str(message_node, NULL);
1107 purple_xmlnode_free(message_node);
1109 ret = _send_data(pb, message) >= 0;
1111 g_free(message);
1113 return ret;
1116 static gboolean
1117 _async_bonjour_jabber_close_conversation_cb(gpointer data) {
1118 BonjourJabberConversation *bconv = data;
1119 bonjour_jabber_close_conversation(bconv);
1120 return FALSE;
1123 void
1124 async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
1125 PurpleConnection *pc = purple_account_get_connection(bconv->account);
1126 BonjourData *bd = purple_connection_get_protocol_data(pc);
1127 BonjourJabber *jdata = bd->jabber_data;
1129 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
1131 /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
1132 if(bconv->pb != NULL) {
1133 BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
1134 if (bb->conversation == bconv)
1135 bb->conversation = NULL;
1138 bconv->close_timeout = g_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
1141 void
1142 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
1144 BonjourData *bd = NULL;
1145 PurpleConnection *pc = NULL;
1147 if (bconv == NULL) {
1148 return;
1151 pc = purple_account_get_connection(bconv->account);
1152 PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
1154 bd = purple_connection_get_protocol_data(pc);
1155 if (bd) {
1156 bd->jabber_data->pending_conversations = g_slist_remove(
1157 bd->jabber_data->pending_conversations, bconv);
1160 /* Cancel any file transfers that are waiting to begin */
1161 /* There wont be any transfers if it hasn't been attached to a buddy */
1162 if (bconv->pb != NULL && bd != NULL) {
1163 GSList *xfers, *tmp_next;
1164 xfers = bd->xfer_lists;
1165 while (xfers != NULL) {
1166 PurpleXfer *xfer = xfers->data;
1167 tmp_next = xfers->next;
1168 /* We only need to cancel this if it hasn't actually started transferring. */
1169 /* This will change if we ever support IBB transfers. */
1170 if (purple_strequal(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb))
1171 && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
1172 || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
1173 purple_xfer_cancel_remote(xfer);
1175 xfers = tmp_next;
1179 /* Close the socket and remove the watcher */
1180 if (bconv->socket != NULL) {
1181 /* Send the end of the stream to the other end of the conversation */
1182 if (bconv->sent_stream_start == FULLY_SENT) {
1183 size_t len = strlen(STREAM_END);
1184 if (g_pollable_output_stream_write_nonblocking(
1185 G_POLLABLE_OUTPUT_STREAM(bconv->output),
1186 STREAM_END, len, bconv->cancellable,
1187 NULL) != (gssize)len) {
1188 purple_debug_error("bonjour",
1189 "bonjour_jabber_close_conversation: "
1190 "couldn't send data\n");
1193 /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
1194 purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
1195 G_INPUT_STREAM(bconv->input),
1196 G_OUTPUT_STREAM(bconv->output));
1198 if (bconv->rx_handler != 0) {
1199 g_source_remove(bconv->rx_handler);
1200 bconv->rx_handler = 0;
1202 if (bconv->tx_handler != 0) {
1203 g_source_remove(bconv->tx_handler);
1204 bconv->tx_handler = 0;
1207 /* Cancel any pending operations. */
1208 if (bconv->cancellable != NULL) {
1209 g_cancellable_cancel(bconv->cancellable);
1210 g_clear_object(&bconv->cancellable);
1213 /* Free all the data related to the conversation */
1214 g_clear_object(&bconv->socket);
1215 bconv->input = NULL;
1216 bconv->output = NULL;
1218 g_object_unref(G_OBJECT(bconv->tx_buf));
1219 if (bconv->stream_data != NULL) {
1220 struct _stream_start_data *ss = bconv->stream_data;
1221 g_free(ss->msg);
1222 g_free(ss);
1225 if (bconv->context != NULL) {
1226 bonjour_parser_setup(bconv);
1229 if (bconv->close_timeout != 0) {
1230 g_source_remove(bconv->close_timeout);
1233 g_free(bconv->buddy_name);
1234 g_free(bconv->ip);
1235 g_free(bconv);
1238 void
1239 bonjour_jabber_stop(BonjourJabber *jdata)
1241 /* Close the server socket and remove the watcher */
1242 if (jdata->service) {
1243 g_socket_service_stop(jdata->service);
1244 g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
1245 g_clear_object(&jdata->service);
1248 /* Close all the conversation sockets and remove all the watchers after sending end streams */
1249 if (!purple_account_is_disconnected(jdata->account)) {
1250 GSList *buddies, *l;
1252 buddies = purple_blist_find_buddies(jdata->account, NULL);
1253 for (l = buddies; l; l = l->next) {
1254 BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
1255 if (bb && bb->conversation) {
1256 /* Any ongoing connection attempt is cancelled
1257 * when a connection is destroyed */
1258 bonjour_jabber_close_conversation(bb->conversation);
1259 bb->conversation = NULL;
1263 g_slist_free(buddies);
1266 while (jdata->pending_conversations != NULL) {
1267 bonjour_jabber_close_conversation(jdata->pending_conversations->data);
1268 jdata->pending_conversations = g_slist_delete_link(jdata->pending_conversations, jdata->pending_conversations);
1272 XepIq *
1273 xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
1275 PurpleXmlNode *iq_node = NULL;
1276 XepIq *iq = NULL;
1278 g_return_val_if_fail(data != NULL, NULL);
1279 g_return_val_if_fail(to != NULL, NULL);
1280 g_return_val_if_fail(id != NULL, NULL);
1282 iq_node = purple_xmlnode_new("iq");
1284 purple_xmlnode_set_attrib(iq_node, "to", to);
1285 purple_xmlnode_set_attrib(iq_node, "from", from);
1286 purple_xmlnode_set_attrib(iq_node, "id", id);
1287 switch (type) {
1288 case XEP_IQ_SET:
1289 purple_xmlnode_set_attrib(iq_node, "type", "set");
1290 break;
1291 case XEP_IQ_GET:
1292 purple_xmlnode_set_attrib(iq_node, "type", "get");
1293 break;
1294 case XEP_IQ_RESULT:
1295 purple_xmlnode_set_attrib(iq_node, "type", "result");
1296 break;
1297 case XEP_IQ_ERROR:
1298 purple_xmlnode_set_attrib(iq_node, "type", "error");
1299 break;
1300 case XEP_IQ_NONE:
1301 default:
1302 purple_xmlnode_set_attrib(iq_node, "type", "none");
1303 break;
1306 iq = g_new0(XepIq, 1);
1307 iq->node = iq_node;
1308 iq->type = type;
1309 iq->data = ((BonjourData*)data)->jabber_data;
1310 iq->to = (char*)to;
1312 return iq;
1315 static gboolean
1316 check_if_blocked(PurpleBuddy *pb)
1318 gboolean blocked = FALSE;
1319 GSList *l = NULL;
1320 PurpleAccount *acc = purple_buddy_get_account(pb);
1322 if(acc == NULL)
1323 return FALSE;
1325 acc = purple_buddy_get_account(pb);
1327 for(l = purple_account_privacy_get_denied(acc); l != NULL; l = l->next) {
1328 const gchar *name = purple_buddy_get_name(pb);
1329 const gchar *username = bonjour_get_jid(acc);
1331 if(!purple_utf8_strcasecmp(name, (char *)l->data)) {
1332 purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
1333 blocked = TRUE;
1334 break;
1337 return blocked;
1340 static void
1341 xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
1343 PurpleAccount *account;
1344 PurpleConnection *gc;
1346 if(check_if_blocked(pb))
1347 return;
1349 account = purple_buddy_get_account(pb);
1350 gc = purple_account_get_connection(account);
1352 if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
1353 xep_si_parse(gc, packet, pb);
1354 else
1355 xep_bytestreams_parse(gc, packet, pb);
1359 xep_iq_send_and_free(XepIq *iq)
1361 int ret = -1;
1362 PurpleBuddy *pb = NULL;
1364 /* start the talk, reuse the message socket */
1365 pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
1366 /* Send the message */
1367 if (pb != NULL) {
1368 /* Convert xml node into stream */
1369 gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
1370 ret = _send_data(pb, msg);
1371 g_free(msg);
1374 purple_xmlnode_free(iq->node);
1375 iq->node = NULL;
1376 g_free(iq);
1378 return (ret >= 0) ? 0 : -1;
1381 /* This returns a list containing all non-localhost IPs */
1382 GSList *
1383 bonjour_jabber_get_local_ips(int fd)
1385 GSList *ips = NULL;
1386 const char *address_text;
1387 int ret;
1389 #ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
1390 struct ifaddrs *ifap, *ifa;
1391 common_sockaddr_t addr;
1392 char addrstr[INET6_ADDRSTRLEN];
1394 ret = getifaddrs(&ifap);
1395 if (ret != 0) {
1396 const char *error = g_strerror(errno);
1397 purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
1398 return NULL;
1401 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1402 if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
1403 continue;
1405 memcpy(&addr, ifa->ifa_addr, sizeof(addr));
1406 address_text = NULL;
1407 switch (addr.sa.sa_family) {
1408 case AF_INET:
1409 address_text = inet_ntop(addr.sa.sa_family,
1410 &addr.in.sin_addr,
1411 addrstr, sizeof(addrstr));
1412 break;
1413 #ifdef PF_INET6
1414 case AF_INET6:
1415 address_text = inet_ntop(addr.sa.sa_family,
1416 &addr.in6.sin6_addr,
1417 addrstr, sizeof(addrstr));
1418 break;
1419 #endif
1422 if (address_text != NULL) {
1423 if (addr.sa.sa_family == AF_INET)
1424 ips = g_slist_append(ips, g_strdup(address_text));
1425 else
1426 ips = g_slist_prepend(ips, g_strdup(address_text));
1430 freeifaddrs(ifap);
1431 #else
1432 char *tmp;
1433 struct ifconf ifc;
1434 struct ifreq *ifr;
1435 char buffer[1024];
1436 struct sockaddr_in *sinptr;
1437 int source = fd;
1439 if (fd < 0)
1440 source = socket(PF_INET, SOCK_STREAM, 0);
1442 ifc.ifc_len = sizeof(buffer);
1443 ifc.ifc_req = (struct ifreq *)buffer;
1444 ret = ioctl(source, SIOCGIFCONF, &ifc);
1446 if (fd < 0)
1447 close(source);
1449 if (ret < 0) {
1450 const char *error = g_strerror(errno);
1451 purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
1452 return NULL;
1455 tmp = buffer;
1456 while (tmp < buffer + ifc.ifc_len) {
1457 ifr = (struct ifreq *)tmp;
1458 tmp += HX_SIZE_OF_IFREQ(*ifr);
1460 if (ifr->ifr_addr.sa_family == AF_INET) {
1461 sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
1462 if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
1463 address_text = inet_ntoa(sinptr->sin_addr);
1464 ips = g_slist_prepend(ips, g_strdup(address_text));
1468 #endif
1470 return ips;
1473 void
1474 append_iface_if_linklocal(char *ip, guint32 interface_param) {
1475 struct in6_addr in6_addr;
1476 int len_remain = INET6_ADDRSTRLEN - strlen(ip);
1478 if (len_remain <= 1)
1479 return;
1481 if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
1482 !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
1483 return;
1485 snprintf(ip + strlen(ip), len_remain, "%%%d",
1486 interface_param);