Fri, 09 Sep 2005 12:34:27 +0000
[gaim-migrate @ 13719]
Commit the Bonjour code from oldstatus to HEAD. It hasn't been
updated for the new status code yet.
| 11477 | 1 | /* |
| 2 | * gaim - Bonjour Protocol Plugin | |
| 3 | * | |
| 4 | * Gaim 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. | |
| 7 | * | |
| 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. | |
| 12 | * | |
| 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. | |
| 17 | * | |
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 21 | */ | |
| 22 | ||
| 23 | #include <sys/socket.h> | |
| 24 | #include <sys/types.h> | |
| 25 | #include <netinet/in.h> | |
| 26 | #include <arpa/inet.h> | |
| 27 | #include <glib.h> | |
| 28 | #include <glib/gprintf.h> | |
| 29 | #include <unistd.h> | |
| 30 | #include <fcntl.h> | |
| 31 | ||
| 32 | #include "network.h" | |
| 33 | #include "eventloop.h" | |
| 34 | #include "connection.h" | |
| 35 | #include "blist.h" | |
| 36 | #include "xmlnode.h" | |
| 37 | #include "debug.h" | |
| 38 | #include "notify.h" | |
| 39 | #include "util.h" | |
| 40 | ||
| 41 | #include "jabber.h" | |
| 42 | #include "bonjour.h" | |
| 43 | #include "buddy.h" | |
| 44 | ||
| 45 | gint _connect_to_buddy(GaimBuddy* gb) | |
| 46 | { | |
| 47 | gint socket_fd; | |
| 48 | gint retorno = 0; | |
| 49 | struct sockaddr_in buddy_address; | |
| 50 | ||
| 51 | // Create a socket and make it non-blocking | |
| 52 | socket_fd = socket(PF_INET, SOCK_STREAM, 0); | |
| 53 | ||
| 54 | buddy_address.sin_family = PF_INET; | |
| 55 | buddy_address.sin_port = htons(((BonjourBuddy*)(gb->proto_data))->port_p2pj); | |
| 56 | inet_aton(((BonjourBuddy*)(gb->proto_data))->ip, &(buddy_address.sin_addr)); | |
| 57 | memset(&(buddy_address.sin_zero), '\0', 8); | |
| 58 | ||
| 59 | retorno = connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr)); | |
| 60 | if (retorno == -1) { | |
| 61 | perror("connect"); | |
| 62 | } | |
| 63 | fcntl(socket_fd, F_SETFL, O_NONBLOCK); | |
| 64 | ||
| 65 | return socket_fd; | |
| 66 | } | |
| 67 | ||
| 68 | char* _font_size_gaim_to_ichat(int size) | |
| 69 | { | |
| 70 | GString* result = NULL; | |
| 71 | ||
| 72 | switch(size){ | |
| 73 | case 1 : | |
| 74 | result = g_string_new("8"); | |
| 75 | break; | |
| 76 | case 2 : | |
| 77 | result = g_string_new("10"); | |
| 78 | break; | |
| 79 | case 3 : | |
| 80 | result = g_string_new("12"); | |
| 81 | break; | |
| 82 | case 4 : | |
| 83 | result = g_string_new("14"); | |
| 84 | break; | |
| 85 | case 5 : | |
| 86 | result = g_string_new("17"); | |
| 87 | break; | |
| 88 | case 6 : | |
| 89 | result = g_string_new("21"); | |
| 90 | break; | |
| 91 | case 7 : | |
| 92 | result = g_string_new("24"); | |
| 93 | break; | |
| 94 | default: | |
| 95 | result = g_string_new("12"); | |
| 96 | } | |
| 97 | ||
| 98 | return g_string_free(result, FALSE); | |
| 99 | } | |
| 100 | ||
| 101 | char* _font_size_ichat_to_gaim(int size) | |
| 102 | { | |
| 103 | GString* result = NULL; | |
| 104 | ||
| 105 | if (size > 24) { | |
| 106 | result = g_string_new("7"); | |
| 107 | } else if(size >= 21) { | |
| 108 | result = g_string_new("6"); | |
| 109 | } else if(size >= 17) { | |
| 110 | result = g_string_new("5"); | |
| 111 | } else if(size >= 14) { | |
| 112 | result = g_string_new("4"); | |
| 113 | } else if(size >= 12) { | |
| 114 | result = g_string_new("3"); | |
| 115 | } else if(size >= 10) { | |
| 116 | result = g_string_new("2"); | |
| 117 | } else { | |
| 118 | result = g_string_new("1"); | |
| 119 | } | |
| 120 | ||
| 121 | return g_string_free(result, FALSE); | |
| 122 | } | |
| 123 | void _jabber_parse_and_write_message_to_ui(char* message, GaimConnection* connection, GaimBuddy* gb) | |
| 124 | { | |
| 125 | xmlnode* body_node = NULL; | |
| 126 | char* body = NULL; | |
| 127 | xmlnode* html_node = NULL; | |
| 128 | gboolean isHTML = FALSE; | |
| 129 | xmlnode* html_body_node = NULL; | |
| 130 | char* ichat_balloon_color = NULL; | |
| 131 | char* ichat_text_color = NULL; | |
| 132 | xmlnode* html_body_font_node = NULL; | |
| 133 | char* font_face = NULL; | |
| 134 | char* font_size = NULL; | |
| 135 | char* font_color = NULL; | |
| 136 | char* html_body = NULL; | |
| 137 | xmlnode* events_node = NULL; | |
| 138 | gboolean composing_event = FALSE; | |
| 139 | gint garbage = -1; | |
| 140 | xmlnode* message_node = NULL; | |
| 141 | ||
| 142 | // Parsing of the message | |
| 143 | message_node = xmlnode_from_str(message, strlen(message)); | |
| 144 | if (message_node == NULL) { | |
| 145 | return; | |
| 146 | } | |
| 147 | ||
| 148 | body_node = xmlnode_get_child(message_node, "body"); | |
| 149 | if (body_node != NULL) { | |
| 150 | body = xmlnode_get_data(body_node); | |
| 151 | } else { | |
| 152 | return; | |
| 153 | } | |
| 154 | ||
| 155 | html_node = xmlnode_get_child(message_node, "html"); | |
| 156 | if (html_node != NULL) { | |
| 157 | isHTML = TRUE; | |
| 158 | html_body_node = xmlnode_get_child(html_node, "body"); | |
| 159 | if (html_body_node != NULL) { | |
| 160 | ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor"); | |
| 161 | ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor"); | |
| 162 | html_body_font_node = xmlnode_get_child(html_body_node, "font"); | |
| 163 | if (html_body_font_node != NULL) { // Types of messages sent by iChat | |
| 164 | font_face = xmlnode_get_attrib(html_body_font_node, "face"); | |
| 165 | // The absolute iChat font sizes should be converted to 1..7 range | |
| 166 | font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ"); | |
| 167 | if (font_size != NULL) { | |
| 168 | font_size = _font_size_ichat_to_gaim(atoi(font_size)); //<-- This call will probably leak memory | |
| 169 | } | |
| 170 | font_color = xmlnode_get_attrib(html_body_font_node, "color"); | |
| 171 | html_body = xmlnode_get_data(html_body_font_node); | |
| 172 | if (html_body == NULL) { // This is the kind of formated messages that Gaim creates | |
| 173 | html_body = xmlnode_to_str(html_body_font_node, &garbage); | |
| 174 | } | |
| 175 | } else { | |
| 176 | isHTML = FALSE; | |
| 177 | } | |
| 178 | } else { | |
| 179 | isHTML = FALSE; | |
| 180 | } | |
| 181 | ||
| 182 | } | |
| 183 | ||
| 184 | events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event"); | |
| 185 | if (events_node != NULL) { | |
| 186 | if (xmlnode_get_child(events_node, "composing") != NULL) { | |
| 187 | composing_event = TRUE; | |
| 188 | } | |
| 189 | if (xmlnode_get_child(events_node, "id") != NULL) { // The user is just typing | |
| 190 | xmlnode_free(message_node); | |
| 191 | g_free(body); | |
| 192 | g_free(html_body); | |
| 193 | return; | |
| 194 | } | |
| 195 | } | |
| 196 | ||
| 197 | // Compose the message | |
| 198 | if (isHTML) { | |
| 199 | if (font_face == NULL) font_face = "Helvetica"; | |
| 200 | if (font_size == NULL) font_size = "3"; | |
| 201 | if (ichat_text_color == NULL) ichat_text_color = "#000000"; | |
| 202 | if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF"; | |
| 203 | body = g_strconcat("<font face='", font_face, "' size='", font_size, "' color='", ichat_text_color, | |
| 204 | "' back='", ichat_balloon_color, "'>", html_body, "</font>", NULL); | |
| 205 | } | |
| 206 | ||
| 207 | // Send the message to the UI | |
| 208 | serv_got_im(connection, gb->name, body, 0, time(NULL)); | |
| 209 | ||
| 210 | // Free all the strings and nodes (the attributes are freed with their nodes) | |
| 211 | xmlnode_free(message_node); | |
| 212 | g_free(body); | |
| 213 | g_free(html_body); | |
| 214 | } | |
| 215 | ||
| 216 | gboolean _check_buddy_by_address(gpointer key, gpointer value, gpointer address) | |
| 217 | { | |
| 218 | GaimBuddy* gb = (GaimBuddy*)value; | |
| 219 | BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data; | |
| 220 | ||
| 221 | if (bb != NULL) { | |
| 222 | if (g_strcasecmp(bb->ip, (char*)address) == 0) { | |
| 223 | return TRUE; | |
| 224 | } else { | |
| 225 | return FALSE; | |
| 226 | } | |
| 227 | } else { | |
| 228 | return FALSE; | |
| 229 | } | |
| 230 | } | |
| 231 | ||
| 232 | gint _read_data(gint socket, char** message) | |
| 233 | { | |
| 234 | GString* data = g_string_new(""); | |
| 235 | char parcial_data[512]; | |
| 236 | gint total_message_length = 0; | |
| 237 | gint parcial_message_length = 0; | |
| 238 | ||
| 239 | // Read chunks of 512 bytes till the end of the data | |
| 240 | while ((parcial_message_length = recv(socket, parcial_data, 512, 0)) > 0) { | |
| 241 | g_string_append_len(data, parcial_data, parcial_message_length); | |
| 242 | total_message_length += parcial_message_length; | |
| 243 | } | |
| 244 | ||
| 245 | if (parcial_message_length == -1) { | |
| 246 | perror("recv"); | |
| 247 | if (total_message_length == 0) { | |
| 248 | return -1; | |
| 249 | } | |
| 250 | } | |
| 251 | ||
| 252 | *message = data->str; | |
| 253 | g_string_free(data, FALSE); | |
| 254 | if (total_message_length != 0) gaim_debug_info("bonjour", "Receive: -%s- %d bytes\n", *message, total_message_length); | |
| 255 | return total_message_length; | |
| 256 | } | |
| 257 | ||
| 258 | gint _send_data(gint socket, char* message) | |
| 259 | { | |
| 260 | gint message_len = strlen(message); | |
| 261 | gint parcial_sent = 0; | |
| 262 | gchar* parcial_message = message; | |
| 263 | ||
| 264 | while ((parcial_sent = send(socket, parcial_message, message_len, 0)) < message_len) { | |
| 265 | if (parcial_sent != -1) { | |
| 266 | parcial_message += parcial_sent; | |
| 267 | message_len -= parcial_sent; | |
| 268 | } else { | |
| 269 | return -1; | |
| 270 | } | |
| 271 | } | |
| 272 | ||
| 273 | return strlen(message); | |
| 274 | } | |
| 275 | ||
| 276 | void _client_socket_handler(gpointer data, gint socket, GaimInputCondition condition) | |
| 277 | { | |
| 278 | char* message = NULL; | |
| 279 | gint message_length; | |
| 280 | GaimBuddy* gb = (GaimBuddy*)data; | |
| 281 | GaimAccount* account = gb->account; | |
| 282 | GaimConversation* conversation; | |
| 283 | char* closed_conv_message; | |
| 284 | BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data; | |
| 285 | gboolean closed_conversation = FALSE; | |
| 286 | char* error_message; | |
| 287 | ||
| 288 | // Read the data from the socket | |
| 289 | if ((message_length = _read_data(socket, &message)) == -1) { | |
| 290 | // There have been an error reading from the socket | |
| 291 | return; | |
| 292 | } else if (message_length == 0) { // The other end has closed the socket | |
| 293 | closed_conversation = TRUE; | |
| 294 | } else { | |
| 295 | message[message_length] = '\0'; | |
| 296 | ||
| 297 | while (g_ascii_iscntrl(message[message_length - 1])) { | |
| 298 | message[message_length - 1] = '\0'; | |
| 299 | message_length--; | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | // Check if the start of the doctype has been received, if not check that the current | |
| 304 | // data is the doctype | |
| 305 | if (!(bb->conversation->start_step_one)) { | |
| 306 | if (g_str_has_prefix(message, DOCTYPE_DECLARATION)){ | |
| 307 | bb->conversation->start_step_one = TRUE; | |
| 308 | } | |
| 309 | } | |
| 310 | ||
| 311 | // Check if the start of the stream has been received, if not check that the current | |
| 312 | // data is the start of the stream | |
| 313 | if (!(bb->conversation->start_step_two)) { | |
| 314 | if (g_str_has_suffix(message, STREAM_START)) { | |
| 315 | bb->conversation->start_step_two = TRUE; | |
| 316 | ||
| 317 | // If we haven't done it yet, we have to sent the start of the stream to the other buddy | |
| 318 | if (!(bb->conversation->stream_started)) { | |
| 319 | if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1) { | |
| 320 | gaim_debug_error("bonjour", "Unable to start a conversation with %s\n", bb->name); | |
| 321 | } | |
| 322 | } | |
| 323 | } | |
| 324 | return; | |
| 325 | } | |
| 326 | ||
| 327 | // Check that this is not the end of the conversation | |
| 328 | if (g_str_has_prefix(message, STREAM_END) || (closed_conversation == TRUE)) { | |
| 329 | // Close the socket, clear the watcher and free memory | |
| 330 | if (bb->conversation != NULL) { | |
| 331 | close(bb->conversation->socket); | |
| 332 | gaim_input_remove(bb->conversation->watcher_id); | |
| 333 | g_free(bb->conversation->buddy_name); | |
| 334 | g_free(bb->conversation); | |
| 335 | bb->conversation = NULL; | |
| 336 | } | |
| 337 | ||
| 338 | // Inform the user that the conversation has been closed | |
| 339 | conversation = gaim_find_conversation_with_account(gb->name, account); | |
| 340 | closed_conv_message = g_strconcat(gb->name, " has closed the conversation.", NULL); | |
| 341 | gaim_conversation_write(conversation, NULL, closed_conv_message, GAIM_MESSAGE_SYSTEM, time(NULL)); | |
| 342 | } else { | |
| 343 | // Parse the message to get the data and send to the ui | |
| 344 | _jabber_parse_and_write_message_to_ui(message, account->gc, gb); | |
| 345 | } | |
| 346 | } | |
| 347 | ||
| 348 | void _server_socket_handler(gpointer data, int server_socket, GaimInputCondition condition) | |
| 349 | { | |
| 350 | GaimBuddy* gb = NULL; | |
| 351 | struct sockaddr_in their_addr; // connector's address information | |
| 352 | int sin_size = sizeof(struct sockaddr); | |
| 353 | int client_socket; | |
| 354 | BonjourBuddy* bb = NULL; | |
| 355 | char* address_text = NULL; | |
| 356 | GaimBuddyList* bl = gaim_get_blist(); | |
| 357 | ||
| 358 | //Check that it is a read condition | |
| 359 | if (condition != GAIM_INPUT_READ) { | |
| 360 | return; | |
| 361 | } | |
| 362 | ||
| 363 | if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) { | |
| 364 | return; | |
| 365 | } | |
| 366 | fcntl(client_socket, F_SETFL, O_NONBLOCK); | |
| 367 | ||
| 368 | // Look for the buddy that has open the conversation and fill information | |
| 369 | address_text = inet_ntoa(their_addr.sin_addr); | |
| 370 | gb = (GaimBuddy*)g_hash_table_find(bl->buddies, _check_buddy_by_address, address_text); | |
| 371 | if (gb == NULL) { | |
| 372 | gaim_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n"); | |
| 373 | close(client_socket); | |
| 374 | return; | |
| 375 | } | |
| 376 | bb = (BonjourBuddy*)gb->proto_data; | |
| 377 | ||
| 378 | // Check if the conversation has been previously started | |
| 379 | if (bb->conversation == NULL) { | |
| 380 | bb->conversation = g_new(BonjourJabberConversation, 1); | |
| 381 | bb->conversation->socket = client_socket; | |
| 382 | bb->conversation->start_step_one = FALSE; | |
| 383 | bb->conversation->start_step_two = FALSE; | |
| 384 | bb->conversation->stream_started = FALSE; | |
| 385 | bb->conversation->buddy_name = g_strdup(gb->name); | |
| 386 | bb->conversation->message_id = 1; | |
| 387 | ||
| 388 | if (bb->conversation->stream_started == FALSE) { | |
| 389 | // Start the stream | |
| 390 | send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0); | |
| 391 | bb->conversation->stream_started = TRUE; | |
| 392 | } | |
| 393 | ||
| 394 | // Open a watcher for the client socket | |
| 395 | bb->conversation->watcher_id = gaim_input_add(client_socket, GAIM_INPUT_READ, | |
| 396 | _client_socket_handler, gb); | |
| 397 | } else { | |
| 398 | close(client_socket); | |
| 399 | } | |
| 400 | } | |
| 401 | ||
| 402 | gint bonjour_jabber_start(BonjourJabber* data) | |
| 403 | { | |
| 404 | struct sockaddr_in my_addr; | |
| 405 | int yes = 1; | |
| 406 | char* error_message = NULL; | |
| 407 | ||
| 408 | ||
| 409 | // Open a listening socket for incoming conversations | |
| 410 | if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0) { | |
| 411 | gaim_debug_error("bonjour", "Cannot get socket\n"); | |
| 412 | error_message = strerror(errno); | |
| 413 | gaim_debug_error("bonjour", "%s\n", error_message); | |
| 414 | gaim_connection_error(data->account->gc, "Cannot open socket"); | |
| 415 | return -1; | |
| 416 | } | |
| 417 | ||
| 418 | // Make the socket reusable | |
| 419 | if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0) { | |
| 420 | gaim_debug_error("bonjour", "Cannot make socket reusable\n"); | |
| 421 | error_message = strerror(errno); | |
| 422 | gaim_debug_error("bonjour", "%s\n", error_message); | |
| 423 | gaim_connection_error(data->account->gc, "Error setting socket options"); | |
| 424 | return -1; | |
| 425 | } | |
| 426 | ||
| 427 | memset(&my_addr, 0, sizeof(struct sockaddr_in)); | |
| 428 | my_addr.sin_family = PF_INET; | |
| 429 | my_addr.sin_port = htons(data->port); | |
| 430 | ||
| 431 | if (bind(data->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) { | |
| 432 | gaim_debug_error("bonjour", "Cannot bind socket\n"); | |
| 433 | error_message = strerror(errno); | |
| 434 | gaim_debug_error("bonjour", "%s\n", error_message); | |
| 435 | gaim_connection_error(data->account->gc, "Cannot bind socket to port"); | |
| 436 | return -1; | |
| 437 | } | |
| 438 | ||
| 439 | if (listen(data->socket, 10) != 0) { | |
| 440 | gaim_debug_error("bonjour", "Cannot listen to socket\n"); | |
| 441 | error_message = strerror(errno); | |
| 442 | gaim_debug_error("bonjour", "%s\n", error_message); | |
| 443 | gaim_connection_error(data->account->gc, "Cannot listen to socket"); | |
| 444 | return -1; | |
| 445 | } | |
| 446 | ||
| 447 | //data->socket = gaim_network_listen(data->port); | |
| 448 | ||
| 449 | //if (data->socket == -1) { | |
| 450 | // gaim_debug_error("bonjour", "No se ha podido crear el socket\n"); | |
| 451 | //} | |
| 452 | ||
| 453 | // Open a watcher in the socket we have just opened | |
| 454 | data->watcher_id = gaim_input_add(data->socket, GAIM_INPUT_READ, _server_socket_handler, data); | |
| 455 | ||
| 456 | return 0; | |
| 457 | } | |
| 458 | ||
| 459 | void bonjour_jabber_send_message(BonjourJabber* data, const gchar* to, const gchar* body) | |
| 460 | { | |
| 461 | xmlnode* message_node = NULL; | |
| 462 | gchar* message = NULL; | |
| 463 | gint message_length = -1; | |
| 464 | xmlnode* message_body_node = NULL; | |
| 465 | xmlnode* message_html_node = NULL; | |
| 466 | xmlnode* message_html_body_node = NULL; | |
| 467 | xmlnode* message_html_body_font_node = NULL; | |
| 468 | xmlnode* message_x_node = NULL; | |
| 469 | GaimBuddy* gb = gaim_find_buddy(data->account, to); | |
| 470 | BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data; | |
| 471 | char* conv_message = NULL; | |
| 472 | GaimConversation* conversation = NULL; | |
| 473 | char* message_from_ui = NULL; | |
| 474 | char* stripped_message = NULL; | |
| 475 | ||
| 476 | // Enclose the message from the UI within a "font" node | |
| 477 | message_body_node = xmlnode_new("body"); | |
| 478 | stripped_message = gaim_markup_strip_html(body); | |
| 479 | xmlnode_insert_data(message_body_node, stripped_message, strlen(stripped_message)); | |
| 480 | ||
| 481 | message_from_ui = g_strconcat("<font>", body, "</font>", NULL); | |
| 482 | message_html_body_font_node = xmlnode_from_str(message_from_ui, strlen(message_from_ui)); | |
| 483 | ||
| 484 | message_html_body_node = xmlnode_new("body"); | |
| 485 | xmlnode_insert_child(message_html_body_node, message_html_body_font_node); | |
| 486 | ||
| 487 | message_html_node = xmlnode_new("html"); | |
| 488 | xmlnode_set_attrib(message_html_node, "xmlns", "http://www.w3.org/1999/xhtml"); | |
| 489 | xmlnode_insert_child(message_html_node, message_html_body_node); | |
| 490 | ||
| 491 | message_x_node = xmlnode_new("x"); | |
| 492 | xmlnode_set_attrib(message_x_node, "xmlns", "jabber:x:event"); | |
| 493 | xmlnode_insert_child(message_x_node, xmlnode_new("composing")); | |
| 494 | ||
| 495 | message_node = xmlnode_new("message"); | |
| 496 | xmlnode_set_attrib(message_node, "to", ((BonjourBuddy*)(gb->proto_data))->name); | |
| 497 | xmlnode_set_attrib(message_node, "type", "chat"); | |
| 498 | xmlnode_insert_child(message_node, message_body_node); | |
| 499 | xmlnode_insert_child(message_node, message_html_node); | |
| 500 | xmlnode_insert_child(message_node, message_x_node); | |
| 501 | ||
| 502 | message = xmlnode_to_str(message_node, &message_length); | |
| 503 | ||
| 504 | // Check if there is a previously open conversation | |
| 505 | if (bb->conversation == NULL) { | |
| 506 | bb->conversation = g_new(BonjourJabberConversation, 1); | |
| 507 | bb->conversation->socket = _connect_to_buddy(gb);; | |
| 508 | bb->conversation->start_step_one = FALSE; | |
| 509 | bb->conversation->start_step_two = FALSE; | |
| 510 | bb->conversation->stream_started = FALSE; | |
| 511 | bb->conversation->buddy_name = g_strdup(gb->name); | |
| 512 | bb->conversation->watcher_id = gaim_input_add(bb->conversation->socket, | |
| 513 | GAIM_INPUT_READ, _client_socket_handler, gb); | |
| 514 | } | |
| 515 | ||
| 516 | // Check if the stream for the conversation has been started | |
| 517 | if (bb->conversation->stream_started == FALSE) { | |
| 518 | // Start the stream | |
| 519 | if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1) { | |
| 520 | gaim_debug_error("bonjour", "Unable to start a conversation\n"); | |
| 521 | perror("send"); | |
| 522 | conv_message = g_strdup("Unable to send the message, the conversation couldn't be started."); | |
| 523 | conversation = gaim_find_conversation_with_account(bb->name, data->account); | |
| 524 | gaim_conversation_write(conversation, NULL, conv_message, GAIM_MESSAGE_SYSTEM, time(NULL)); | |
| 525 | close(bb->conversation->socket); | |
| 526 | gaim_input_remove(bb->conversation->watcher_id); | |
| 527 | ||
| 528 | // Free all the data related to the conversation | |
| 529 | g_free(bb->conversation->buddy_name); | |
| 530 | g_free(bb->conversation); | |
| 531 | bb->conversation = NULL; | |
| 532 | return; | |
| 533 | } | |
| 534 | ||
| 535 | bb->conversation->stream_started = TRUE; | |
| 536 | } | |
| 537 | ||
| 538 | // Send the message | |
| 539 | if (_send_data(bb->conversation->socket, message) == -1) { | |
| 540 | gaim_debug_error("bonjour", "Unable to send the message\n"); | |
| 541 | conv_message = g_strdup("Unable to send the message."); | |
| 542 | conversation = gaim_find_conversation_with_account(bb->name, data->account); | |
| 543 | gaim_conversation_write(conversation, NULL, conv_message, GAIM_MESSAGE_SYSTEM, time(NULL)); | |
| 544 | } | |
| 545 | } | |
| 546 | ||
| 547 | void bonjour_jabber_close_conversation(BonjourJabber* data, GaimBuddy* gb) | |
| 548 | { | |
| 549 | BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data; | |
| 550 | ||
| 551 | if (bb->conversation != NULL) { | |
| 552 | // Send the end of the stream to the other end of the conversation | |
| 553 | send(bb->conversation->socket, STREAM_END, strlen(STREAM_END), 0); | |
| 554 | ||
| 555 | // Close the socket and remove the watcher | |
| 556 | close(bb->conversation->socket); | |
| 557 | gaim_input_remove(bb->conversation->watcher_id); | |
| 558 | ||
| 559 | // Free all the data related to the conversation | |
| 560 | g_free(bb->conversation->buddy_name); | |
| 561 | g_free(bb->conversation); | |
| 562 | bb->conversation = NULL; | |
| 563 | } | |
| 564 | } | |
| 565 | ||
| 566 | void bonjour_jabber_stop(BonjourJabber* data) | |
| 567 | { | |
| 568 | GaimBuddy* gb = NULL; | |
| 569 | BonjourBuddy* bb = NULL; | |
| 570 | GSList* buddies; | |
| 571 | GSList* l; | |
| 572 | ||
| 573 | // Close the server socket and remove all the watcher | |
| 574 | close(data->socket); | |
| 575 | gaim_input_remove(data->watcher_id); | |
| 576 | ||
| 577 | // Close all the sockets and remove all the watchers after sending end streams | |
| 578 | if(data->account->gc != NULL){ | |
| 579 | buddies = gaim_find_buddies(data->account, data->account->username); | |
| 580 | for(l = buddies; l; l = l->next){ | |
| 581 | gb = (GaimBuddy*)l->data; | |
| 582 | bb = (BonjourBuddy*)gb->proto_data; | |
| 583 | if (bb->conversation != NULL) { | |
| 584 | send(bb->conversation->socket, STREAM_END, strlen(STREAM_END), 0); | |
| 585 | close(bb->conversation->socket); | |
| 586 | gaim_input_remove(bb->conversation->watcher_id); | |
| 587 | } | |
| 588 | } | |
| 589 | g_slist_free(buddies); | |
| 590 | } | |
| 591 | } |