Mercurial > pidgin
diff libpurple/protocols/bonjour/jabber.c @ 18761:316be7e715c6
Update the Bonjour prpl to use libxml explicitly instead of the xml_node stuff. This allows us to deal with partial reads. I also fixed issues related to starting conversations with iChat and a couple other things. Fixes #2022,#1652
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Tue, 31 Jul 2007 23:23:25 +0000 |
parents | b981d3c39d0b |
children | 6e3cd5e80420 |
line wrap: on
line diff
--- a/libpurple/protocols/bonjour/jabber.c Tue Jul 31 23:20:56 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Tue Jul 31 23:23:25 2007 +0000 @@ -42,6 +42,7 @@ #include "util.h" #include "jabber.h" +#include "parser.h" #include "bonjour.h" #include "buddy.h" @@ -109,9 +110,10 @@ } static void -_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleConnection *connection, PurpleBuddy *pb) +_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb) { xmlnode *body_node, *html_node, *events_node; + PurpleConnection *gc = pb->account->gc; char *body, *html_body = NULL; const char *ichat_balloon_color = NULL; const char *ichat_text_color = NULL; @@ -186,7 +188,7 @@ /* TODO: Should we do something with "composing_event" here? */ /* Send the message to the UI */ - serv_got_im(connection, pb->name, body, 0, time(NULL)); + serv_got_im(gc, pb->name, body, 0, time(NULL)); g_free(body); g_free(html_body); @@ -218,37 +220,6 @@ } } -static gint -_read_data(gint socket, char **message) -{ - GString *data = g_string_new(""); - char partial_data[512]; - gint total_message_length = 0; - gint partial_message_length = 0; - - /* Read chunks of 512 bytes till the end of the data */ - while ((partial_message_length = recv(socket, partial_data, 512, 0)) > 0) - { - g_string_append_len(data, partial_data, partial_message_length); - total_message_length += partial_message_length; - } - - if (partial_message_length == -1) - { - if (errno != EAGAIN) - purple_debug_warning("bonjour", "receive error: %s\n", strerror(errno)); - if (total_message_length == 0) { - return -1; - } - } - - *message = g_string_free(data, FALSE); - if (total_message_length != 0) - purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", *message, total_message_length); - - return total_message_length; -} - static void _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond) { @@ -303,7 +274,8 @@ /* If we're not ready to actually send, append it to the buffer */ if (bconv->tx_handler != -1 || bconv->connect_data != NULL - || !bconv->stream_started + || !bconv->sent_stream_start + || !bconv->recv_stream_start || purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { ret = -1; errno = EAGAIN; @@ -341,20 +313,32 @@ return ret; } +void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) { + if (!strcmp(packet->name, "message")) + _jabber_parse_and_write_message_to_ui(packet, pb); + else + purple_debug_warning("bonjour", "Unknown packet: %s\n", + packet->name); +} + + static void _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition) { - char *message = NULL; - gint message_length; PurpleBuddy *pb = data; - PurpleAccount *account = pb->account; - BonjourBuddy *bb = pb->proto_data; - gboolean closed_conversation = FALSE; + gint len, message_length; + static char message[4096]; + + /*TODO: use a static buffer */ /* Read the data from the socket */ - if ((message_length = _read_data(socket, &message)) == -1) { + if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) { /* There have been an error reading from the socket */ if (errno != EAGAIN) { + BonjourBuddy *bb = pb->proto_data; + + purple_debug_warning("bonjour", "receive error: %s\n", strerror(errno)); + bonjour_jabber_close_conversation(bb->conversation); bb->conversation = NULL; @@ -362,65 +346,71 @@ * If they try to send another message it'll reconnect */ } return; - } else if (message_length == 0) { /* The other end has closed the socket */ - closed_conversation = TRUE; + } else if (len == 0) { /* The other end has closed the socket */ + purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", pb->name); + bonjour_jabber_stream_ended(pb); + return; } else { + message_length = len; message[message_length] = '\0'; - while (g_ascii_iscntrl(message[message_length - 1])) { + while (message_length > 0 && g_ascii_iscntrl(message[message_length - 1])) { message[message_length - 1] = '\0'; message_length--; } } - /* - * Check that this is not the end of the conversation. This is - * using a magic string, but xmlnode won't play nice when just - * parsing an end tag - */ - if (closed_conversation || purple_str_has_prefix(message, STREAM_END)) { - PurpleConversation *conv; + purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len); + + bonjour_parser_process(pb, message, message_length); +} - /* Close the socket, clear the watcher and free memory */ - bonjour_jabber_close_conversation(bb->conversation); - bb->conversation = NULL; +void bonjour_jabber_stream_ended(PurpleBuddy *pb) { + BonjourBuddy *bb = pb->proto_data; + PurpleConversation *conv; + + purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", pb->name); + + /* Close the socket, clear the watcher and free memory */ + bonjour_jabber_close_conversation(bb->conversation); + bb->conversation = NULL; - /* Inform the user that the conversation has been closed */ - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, account); - if (conv != NULL) { - char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name); - purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(tmp); - } - } else { - xmlnode *message_node; + /* Inform the user that the conversation has been closed */ + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, pb->account); + if (conv != NULL) { + char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name); + purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); + g_free(tmp); + } +} - /* Parse the message into an XMLnode for analysis */ - message_node = xmlnode_from_str(message, strlen(message)); +void bonjour_jabber_stream_started(PurpleBuddy *pb) { + BonjourBuddy *bb = pb->proto_data; + BonjourJabberConversation *bconv = bb->conversation; - if (message_node != NULL) { - /* Parse the message to get the data and send to the ui */ - _jabber_parse_and_write_message_to_ui(message_node, account->gc, pb); - xmlnode_free(message_node); - } else { - /* TODO: Deal with receiving only a partial message */ - } + /* If the stream has been completely started, we can start doing stuff */ + if (bconv->sent_stream_start && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { + /* Watch for when we can write the buffered messages */ + bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE, + _send_data_write_cb, pb); + /* We can probably write the data right now. */ + _send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE); } - g_free(message); } struct _stream_start_data { char *msg; - PurpleInputFunction tx_handler_cb; }; + static void _start_stream(gpointer data, gint source, PurpleInputCondition condition) { PurpleBuddy *pb = data; BonjourBuddy *bb = pb->proto_data; - struct _stream_start_data *ss = bb->conversation->stream_data; + BonjourJabberConversation *bconv = bb->conversation; + struct _stream_start_data *ss = bconv->stream_data; int len, ret; len = strlen(ss->msg); @@ -443,7 +433,7 @@ _("Unable to send the message, the conversation couldn't be started."), PURPLE_MESSAGE_SYSTEM, time(NULL)); - bonjour_jabber_close_conversation(bb->conversation); + bonjour_jabber_close_conversation(bconv); bb->conversation = NULL; return; @@ -457,22 +447,67 @@ return; } - /* Stream started; process the send buffer if there is one*/ - purple_input_remove(bb->conversation->tx_handler); - bb->conversation->tx_handler= -1; - - bb->conversation->stream_started = TRUE; - g_free(ss->msg); g_free(ss); - bb->conversation->stream_data = NULL; + bconv->stream_data = NULL; + + /* Stream started; process the send buffer if there is one */ + purple_input_remove(bconv->tx_handler); + bconv->tx_handler= -1; + bconv->sent_stream_start = TRUE; + + bonjour_jabber_stream_started(pb); + +} + +static gboolean bonjour_jabber_stream_init(PurpleBuddy *pb, int client_socket) +{ + int ret, len; + char *stream_start; + BonjourBuddy *bb = pb->proto_data; + + stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), + purple_buddy_get_name(pb)); + len = strlen(stream_start); + + /* Start the stream */ + ret = send(client_socket, stream_start, len, 0); + + if (ret == -1 && errno == EAGAIN) + ret = 0; + else if (ret <= 0) { + const char *err = strerror(errno); - if (ss->tx_handler_cb) { - bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, - ss->tx_handler_cb, pb); - /* We can probably write the data now. */ - (ss->tx_handler_cb)(pb, source, PURPLE_INPUT_WRITE); + purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", + purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)"); + + close(client_socket); + g_free(stream_start); + + return FALSE; } + + /* This is unlikely to happen */ + if (ret < len) { + struct _stream_start_data *ss = g_new(struct _stream_start_data, 1); + ss->msg = g_strdup(stream_start + ret); + bb->conversation->stream_data = ss; + /* Finish sending the stream start */ + bb->conversation->tx_handler = purple_input_add(client_socket, + PURPLE_INPUT_WRITE, _start_stream, pb); + } else + bb->conversation->sent_stream_start = TRUE; + + g_free(stream_start); + + /* setup the parser fresh for each stream */ + bonjour_parser_setup(bb->conversation); + + bb->conversation->socket = client_socket; + bb->conversation->rx_handler = purple_input_add(client_socket, + PURPLE_INPUT_READ, _client_socket_handler, pb); + + return TRUE; } static void @@ -498,6 +533,7 @@ /* Look for the buddy that has opened the conversation and fill information */ address_text = inet_ntoa(their_addr.sin_addr); + purple_debug_info("bonjour", "Received incoming connection from %s\n.", address_text); cbba = g_new0(struct _check_buddy_by_address_t, 1); cbba->address = address_text; cbba->pb = &pb; @@ -515,49 +551,15 @@ /* Check if the conversation has been previously started */ if (bb->conversation == NULL) { - int ret, len; - char *stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), - purple_buddy_get_name(pb)); - - len = strlen(stream_start); - - /* Start the stream */ - ret = send(client_socket, stream_start, len, 0); + bb->conversation = bonjour_jabber_conv_new(); - if (ret == -1 && errno == EAGAIN) - ret = 0; - else if (ret <= 0) { - const char *err = strerror(errno); - - purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", - purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)"); - + if (!bonjour_jabber_stream_init(pb, client_socket)) { close(client_socket); - g_free(stream_start); - return; } - bb->conversation = bonjour_jabber_conv_new(); - bb->conversation->socket = client_socket; - bb->conversation->rx_handler = purple_input_add(client_socket, - PURPLE_INPUT_READ, _client_socket_handler, pb); - - /* This is unlikely to happen */ - if (ret < len) { - struct _stream_start_data *ss = g_new(struct _stream_start_data, 1); - ss->msg = g_strdup(stream_start + ret); - ss->tx_handler_cb = NULL; /* We have nothing to write yet */ - bb->conversation->stream_data = ss; - /* Finish sending the stream start */ - bb->conversation->tx_handler = purple_input_add(client_socket, - PURPLE_INPUT_WRITE, _start_stream, pb); - } else { - bb->conversation->stream_started = TRUE; - } - - g_free(stream_start); } else { + purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n"); close(client_socket); } } @@ -639,8 +641,6 @@ { PurpleBuddy *pb = data; BonjourBuddy *bb = pb->proto_data; - int len, ret; - char *stream_start; bb->conversation->connect_data = NULL; @@ -661,15 +661,7 @@ return; } - stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), purple_buddy_get_name(pb)); - len = strlen(stream_start); - - /* Start the stream and send queued messages */ - ret = send(source, stream_start, len, 0); - - if (ret == -1 && errno == EAGAIN) - ret = 0; - else if (ret <= 0) { + if (!bonjour_jabber_stream_init(pb, source)) { const char *err = strerror(errno); PurpleConversation *conv; @@ -685,37 +677,8 @@ close(source); bonjour_jabber_close_conversation(bb->conversation); bb->conversation = NULL; - - g_free(stream_start); - return; } - - bb->conversation->socket = source; - bb->conversation->rx_handler = purple_input_add(source, - PURPLE_INPUT_READ, _client_socket_handler, pb); - - /* This is unlikely to happen */ - if (ret < len) { - struct _stream_start_data *ss = g_new(struct _stream_start_data, 1); - ss->msg = g_strdup(stream_start + ret); - ss->tx_handler_cb = _send_data_write_cb; - bb->conversation->stream_data = ss; - /* Finish sending the stream start */ - bb->conversation->tx_handler = purple_input_add(source, - PURPLE_INPUT_WRITE, _start_stream, pb); - } - /* Process the send buffer */ - else { - bb->conversation->stream_started = TRUE; - /* Watch for when we can write the buffered messages */ - bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, - _send_data_write_cb, pb); - /* We can probably write the data now. */ - _send_data_write_cb(pb, source, PURPLE_INPUT_WRITE); - } - - g_free(stream_start); } int @@ -809,7 +772,7 @@ /* Close the socket and remove the watcher */ if (bconv->socket >= 0) { /* Send the end of the stream to the other end of the conversation */ - if (bconv->stream_started) + if (bconv->sent_stream_start) send(bconv->socket, STREAM_END, strlen(STREAM_END), 0); /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */ close(bconv->socket); @@ -828,6 +791,10 @@ g_free(ss->msg); g_free(ss); } + + if (bconv->context != NULL) + bonjour_parser_setup(bconv); + g_free(bconv); } }