Annotation of ChivanetAimPidgin/oscarprpl/src/c/odc.c, revision 1.1
1.1 ! snw 1: /*
! 2: * Purple's oscar protocol plugin
! 3: * This file is the legal property of its developers.
! 4: * Please see the AUTHORS file distributed alongside this file.
! 5: *
! 6: * This library is free software; you can redistribute it and/or
! 7: * modify it under the terms of the GNU Lesser General Public
! 8: * License as published by the Free Software Foundation; either
! 9: * version 2 of the License, or (at your option) any later version.
! 10: *
! 11: * This library is distributed in the hope that it will be useful,
! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 14: * Lesser General Public License for more details.
! 15: *
! 16: * You should have received a copy of the GNU Lesser General Public
! 17: * License along with this library; if not, write to the Free Software
! 18: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
! 19: */
! 20:
! 21: /* From the oscar PRPL */
! 22: #include "encoding.h"
! 23: #include "oscar.h"
! 24: #include "peer.h"
! 25:
! 26: /* From Purple */
! 27: #include "conversation.h"
! 28: #include "imgstore.h"
! 29: #include "util.h"
! 30:
! 31: #define DIRECTIM_MAX_FILESIZE 52428800
! 32:
! 33: /**
! 34: * Free any ODC related data and print a message to the conversation
! 35: * window based on conn->disconnect_reason.
! 36: */
! 37: void
! 38: peer_odc_close(PeerConnection *conn)
! 39: {
! 40: gchar *tmp;
! 41:
! 42: if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
! 43: tmp = g_strdup(_("The remote user has closed the connection."));
! 44: else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)
! 45: tmp = g_strdup(_("The remote user has declined your request."));
! 46: else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
! 47: tmp = g_strdup_printf(_("Lost connection with the remote user:<br>%s"),
! 48: conn->error_message);
! 49: else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
! 50: tmp = g_strdup(_("Received invalid data on connection with remote user."));
! 51: else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
! 52: tmp = g_strdup(_("Unable to establish a connection with the remote user."));
! 53: else
! 54: /*
! 55: * We shouldn't print a message for some disconnect_reasons.
! 56: * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
! 57: */
! 58: tmp = NULL;
! 59:
! 60: if (tmp != NULL)
! 61: {
! 62: PurpleAccount *account;
! 63: PurpleConversation *conv;
! 64:
! 65: account = purple_connection_get_account(conn->od->gc);
! 66: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
! 67: purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
! 68: g_free(tmp);
! 69: }
! 70:
! 71: if (conn->frame != NULL)
! 72: {
! 73: OdcFrame *frame;
! 74: frame = conn->frame;
! 75: g_free(frame->payload.data);
! 76: g_free(frame);
! 77: }
! 78: }
! 79:
! 80: /**
! 81: * Write the given OdcFrame to a ByteStream and send it out
! 82: * on the established PeerConnection.
! 83: */
! 84: static void
! 85: peer_odc_send(PeerConnection *conn, OdcFrame *frame)
! 86: {
! 87: PurpleAccount *account;
! 88: const char *username;
! 89: size_t length;
! 90: ByteStream bs;
! 91:
! 92: purple_debug_info("oscar", "Outgoing ODC frame to %s with "
! 93: "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
! 94: conn->bn, frame->type, frame->flags, frame->payload.len);
! 95:
! 96: account = purple_connection_get_account(conn->od->gc);
! 97: username = purple_account_get_username(account);
! 98: memcpy(frame->bn, username, strlen(username));
! 99: memcpy(frame->cookie, conn->cookie, 8);
! 100:
! 101: length = 76;
! 102: byte_stream_new(&bs, length + frame->payload.len);
! 103: byte_stream_putraw(&bs, conn->magic, 4);
! 104: byte_stream_put16(&bs, length);
! 105: byte_stream_put16(&bs, frame->type);
! 106: byte_stream_put16(&bs, frame->subtype);
! 107: byte_stream_put16(&bs, 0x0000);
! 108: byte_stream_putraw(&bs, frame->cookie, 8);
! 109: byte_stream_put16(&bs, 0x0000);
! 110: byte_stream_put16(&bs, 0x0000);
! 111: byte_stream_put16(&bs, 0x0000);
! 112: byte_stream_put16(&bs, 0x0000);
! 113: byte_stream_put32(&bs, frame->payload.len);
! 114: byte_stream_put16(&bs, frame->encoding);
! 115: byte_stream_put16(&bs, 0x0000);
! 116: byte_stream_put16(&bs, 0x0000);
! 117: byte_stream_put16(&bs, frame->flags);
! 118: byte_stream_put16(&bs, 0x0000);
! 119: byte_stream_put16(&bs, 0x0000);
! 120: byte_stream_putraw(&bs, frame->bn, 32);
! 121: byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
! 122:
! 123: peer_connection_send(conn, &bs);
! 124:
! 125: byte_stream_destroy(&bs);
! 126: }
! 127:
! 128: /**
! 129: * Send a very basic ODC frame (which contains the cookie) so that the
! 130: * remote user can verify that we are the person they were expecting.
! 131: * If we made an outgoing connection to then remote user, then we send
! 132: * this immediately. If the remote user connected to us, then we wait
! 133: * for the other person to send this to us, then we send one to them.
! 134: */
! 135: void
! 136: peer_odc_send_cookie(PeerConnection *conn)
! 137: {
! 138: OdcFrame frame;
! 139:
! 140: memset(&frame, 0, sizeof(OdcFrame));
! 141: frame.type = 0x0001;
! 142: frame.subtype = 0x0006;
! 143: frame.flags = 0x0060; /* Maybe this means "we're sending the cookie"? */
! 144:
! 145: peer_odc_send(conn, &frame);
! 146: }
! 147:
! 148: /**
! 149: * Send client-to-client typing notification over an established direct connection.
! 150: */
! 151: void
! 152: peer_odc_send_typing(PeerConnection *conn, PurpleTypingState typing)
! 153: {
! 154: OdcFrame frame;
! 155:
! 156: memset(&frame, 0, sizeof(OdcFrame));
! 157: frame.type = 0x0001;
! 158: frame.subtype = 0x0006;
! 159: if (typing == PURPLE_TYPING)
! 160: frame.flags = 0x0002 | 0x0008;
! 161: else if (typing == PURPLE_TYPED)
! 162: frame.flags = 0x0002 | 0x0004;
! 163: else
! 164: frame.flags = 0x0002;
! 165:
! 166: peer_odc_send(conn, &frame);
! 167: }
! 168:
! 169: /**
! 170: * Send client-to-client IM over an established direct connection.
! 171: * To send a direct IM, call this just like you would aim_send_im.
! 172: *
! 173: * @param conn The already-connected ODC connection.
! 174: * @param msg Null-terminated string to send.
! 175: * @param len The length of the message to send, including binary data.
! 176: * @param encoding See the AIM_CHARSET_* defines in oscar.h
! 177: * @param autoreply TRUE if this is any auto-reply.
! 178: */
! 179: void
! 180: peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply)
! 181: {
! 182: OdcFrame frame;
! 183:
! 184: g_return_if_fail(msg != NULL);
! 185: g_return_if_fail(len > 0);
! 186:
! 187: memset(&frame, 0, sizeof(OdcFrame));
! 188: frame.type = 0x0001;
! 189: frame.subtype = 0x0006;
! 190: frame.payload.len = len;
! 191: frame.encoding = encoding;
! 192: frame.flags = autoreply;
! 193: byte_stream_new(&frame.payload, len);
! 194: byte_stream_putraw(&frame.payload, (guint8 *)msg, len);
! 195:
! 196: peer_odc_send(conn, &frame);
! 197:
! 198: g_free(frame.payload.data);
! 199: }
! 200:
! 201: struct embedded_data
! 202: {
! 203: size_t size;
! 204: const guint8 *data;
! 205: };
! 206:
! 207: /**
! 208: * This is called after a direct IM has been received in its entirety. This
! 209: * function is passed a long chunk of data which contains the IM with any
! 210: * data chunks (images) appended to it.
! 211: *
! 212: * This function rips out all the data chunks and creates an imgstore for
! 213: * each one. In order to do this, it first goes through the IM and takes
! 214: * out all the IMG tags. When doing so, it rewrites the original IMG tag
! 215: * with one compatible with the imgstore Purple core code. For each one, we
! 216: * then read in chunks of data from the end of the message and actually
! 217: * create the img store using the given data.
! 218: *
! 219: * For somewhat easy reference, here's a sample message
! 220: * (with added whitespace):
! 221: *
! 222: * <HTML><BODY BGCOLOR="#ffffff">
! 223: * <FONT LANG="0">
! 224: * This is a really stupid picture:<BR>
! 225: * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
! 226: * Yeah it is<BR>
! 227: * Here is another one:<BR>
! 228: * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
! 229: * </FONT>
! 230: * </BODY></HTML>
! 231: * <BINARY>
! 232: * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
! 233: * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
! 234: * </BINARY>
! 235: */
! 236: static void
! 237: peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply)
! 238: {
! 239: PurpleConnection *gc;
! 240: PurpleAccount *account;
! 241: const char *msgend, *binary_start, *dataend;
! 242: const char *tmp, *start, *end, *idstr, *src, *sizestr;
! 243: GData *attributes;
! 244: GHashTable *embedded_datas;
! 245: struct embedded_data *embedded_data;
! 246: GSList *images;
! 247: gchar *utf8;
! 248: GString *newmsg;
! 249: PurpleMessageFlags imflags;
! 250:
! 251: gc = conn->od->gc;
! 252: account = purple_connection_get_account(gc);
! 253:
! 254: dataend = msg + len;
! 255:
! 256: /*
! 257: * Create a hash table containing references to each embedded
! 258: * data chunk. The key is the "ID" and the value is an
! 259: * embedded_data struct.
! 260: */
! 261: embedded_datas = g_hash_table_new_full(g_direct_hash,
! 262: g_direct_equal, NULL, g_free);
! 263:
! 264: /*
! 265: * Create an index of any binary chunks. If we run into any
! 266: * problems while parsing the binary data section then we stop
! 267: * parsing it, and the local user will see broken image icons.
! 268: */
! 269: binary_start = purple_strcasestr(msg, "<binary>");
! 270: if (binary_start == NULL)
! 271: msgend = dataend;
! 272: else
! 273: {
! 274: msgend = binary_start;
! 275:
! 276: /* Move our pointer to immediately after the <binary> tag */
! 277: tmp = binary_start + 8;
! 278:
! 279: /* The embedded binary markup has a mimimum length of 29 bytes */
! 280: while ((tmp + 29 <= dataend) &&
! 281: purple_markup_find_tag("data", tmp, &start, &tmp, &attributes))
! 282: {
! 283: unsigned int id;
! 284: size_t size;
! 285:
! 286: /* Move the binary pointer from ">" to the start of the data */
! 287: tmp++;
! 288:
! 289: /* Get the ID */
! 290: idstr = g_datalist_get_data(&attributes, "id");
! 291: if (idstr == NULL)
! 292: {
! 293: g_datalist_clear(&attributes);
! 294: break;
! 295: }
! 296: id = atoi(idstr);
! 297:
! 298: /* Get the size */
! 299: sizestr = g_datalist_get_data(&attributes, "size");
! 300: if (sizestr == NULL)
! 301: {
! 302: g_datalist_clear(&attributes);
! 303: break;
! 304: }
! 305: size = atol(sizestr);
! 306:
! 307: g_datalist_clear(&attributes);
! 308:
! 309: if ((size > 0) && (tmp + size > dataend))
! 310: break;
! 311:
! 312: embedded_data = g_new(struct embedded_data, 1);
! 313: embedded_data->size = size;
! 314: embedded_data->data = (const guint8 *)tmp;
! 315: tmp += size;
! 316:
! 317: /* Skip past the closing </data> tag */
! 318: if (g_ascii_strncasecmp(tmp, "</data>", 7))
! 319: {
! 320: g_free(embedded_data);
! 321: break;
! 322: }
! 323: tmp += 7;
! 324:
! 325: g_hash_table_insert(embedded_datas,
! 326: GINT_TO_POINTER(id), embedded_data);
! 327: }
! 328: }
! 329:
! 330: /*
! 331: * Loop through the message, replacing OSCAR img tags with the
! 332: * equivalent Purple img tag.
! 333: */
! 334: images = NULL;
! 335: newmsg = g_string_new("");
! 336: tmp = msg;
! 337: while (purple_markup_find_tag("img", tmp, &start, &end, &attributes))
! 338: {
! 339: int imgid = 0;
! 340:
! 341: idstr = g_datalist_get_data(&attributes, "id");
! 342: src = g_datalist_get_data(&attributes, "src");
! 343: sizestr = g_datalist_get_data(&attributes, "datasize");
! 344:
! 345: if ((idstr != NULL) && (src != NULL) && (sizestr!= NULL))
! 346: {
! 347: unsigned int id;
! 348: size_t size;
! 349:
! 350: id = atoi(idstr);
! 351: size = atol(sizestr);
! 352: embedded_data = g_hash_table_lookup(embedded_datas,
! 353: GINT_TO_POINTER(id));
! 354:
! 355: if ((embedded_data != NULL) && (embedded_data->size == size))
! 356: {
! 357: imgid = purple_imgstore_add_with_id(g_memdup(embedded_data->data, size), size, src);
! 358:
! 359: /* Record the image number */
! 360: images = g_slist_append(images, GINT_TO_POINTER(imgid));
! 361: }
! 362: }
! 363:
! 364: /* Delete the attribute list */
! 365: g_datalist_clear(&attributes);
! 366:
! 367: /* Append the message up to the tag */
! 368: utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp);
! 369: if (utf8 != NULL) {
! 370: g_string_append(newmsg, utf8);
! 371: g_free(utf8);
! 372: }
! 373:
! 374: if (imgid != 0)
! 375: {
! 376: /* Write the new image tag */
! 377: g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
! 378: }
! 379:
! 380: /* Continue from the end of the tag */
! 381: tmp = end + 1;
! 382: }
! 383:
! 384: /* Append any remaining message data */
! 385: if (tmp <= msgend)
! 386: {
! 387: utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
! 388: if (utf8 != NULL) {
! 389: g_string_append(newmsg, utf8);
! 390: g_free(utf8);
! 391: }
! 392: }
! 393:
! 394: /* Display the message we received */
! 395: imflags = 0;
! 396: if (images != NULL)
! 397: imflags |= PURPLE_MESSAGE_IMAGES;
! 398: if (autoreply)
! 399: imflags |= PURPLE_MESSAGE_AUTO_RESP;
! 400: serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
! 401: g_string_free(newmsg, TRUE);
! 402:
! 403: /* unref any images we allocated */
! 404: if (images)
! 405: {
! 406: GSList *l;
! 407: for (l = images; l != NULL; l = l->next)
! 408: purple_imgstore_unref_by_id(GPOINTER_TO_INT(l->data));
! 409: g_slist_free(images);
! 410: }
! 411:
! 412: /* Delete our list of pointers to embedded images */
! 413: g_hash_table_destroy(embedded_datas);
! 414: }
! 415:
! 416: /**
! 417: * This is a purple_input_add() watcher callback function for reading
! 418: * direct IM payload data. "Payload data" is always an IM and
! 419: * maybe some embedded images or files or something. The actual
! 420: * ODC frame is read using peer_connection_recv_cb(). We temporarily
! 421: * switch to this watcher callback ONLY to read the payload, and we
! 422: * switch back once we're done.
! 423: */
! 424: static void
! 425: peer_odc_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
! 426: {
! 427: PeerConnection *conn;
! 428: OdcFrame *frame;
! 429: ByteStream *bs;
! 430: gssize read;
! 431:
! 432: conn = data;
! 433: frame = conn->frame;
! 434: bs = &frame->payload;
! 435:
! 436: /* Read data into the temporary buffer until it is complete */
! 437: read = recv(conn->fd,
! 438: &bs->data[bs->offset],
! 439: bs->len - bs->offset,
! 440: 0);
! 441:
! 442: /* Check if the remote user closed the connection */
! 443: if (read == 0)
! 444: {
! 445: peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
! 446: return;
! 447: }
! 448:
! 449: if (read < 0)
! 450: {
! 451: if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
! 452: /* No worries */
! 453: return;
! 454:
! 455: peer_connection_destroy(conn,
! 456: OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
! 457: return;
! 458: }
! 459:
! 460: bs->offset += read;
! 461: if (bs->offset < bs->len)
! 462: /* Waiting for more data to arrive */
! 463: return;
! 464: /* TODO: Instead of null-terminating this, it would be better if we just
! 465: respected the length of the buffer when parsing it. But it doesn't
! 466: really matter and this is easy. */
! 467: bs->data[bs->len] = '\0';
! 468:
! 469: /* We have a complete ODC/OFT frame! Handle it and continue reading */
! 470: byte_stream_rewind(bs);
! 471: peer_odc_handle_payload(conn, (const char *)bs->data,
! 472: bs->len, frame->encoding, frame->flags & 0x0001);
! 473: g_free(bs->data);
! 474: bs->data = NULL;
! 475: g_free(frame);
! 476: conn->frame = NULL;
! 477:
! 478: purple_input_remove(conn->watcher_incoming);
! 479: conn->watcher_incoming = purple_input_add(conn->fd,
! 480: PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
! 481: }
! 482:
! 483: /**
! 484: * Handle an incoming OdcFrame. If there is a payload associated
! 485: * with this frame, then we remove the old watcher and add the
! 486: * ODC watcher to read in the payload.
! 487: */
! 488: void
! 489: peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
! 490: {
! 491: PurpleConnection *gc;
! 492: OdcFrame *frame;
! 493:
! 494: gc = conn->od->gc;
! 495:
! 496: frame = g_new0(OdcFrame, 1);
! 497: frame->type = byte_stream_get16(bs);
! 498: frame->subtype = byte_stream_get16(bs);
! 499: byte_stream_advance(bs, 2);
! 500: byte_stream_getrawbuf(bs, frame->cookie, 8);
! 501: byte_stream_advance(bs, 8);
! 502: frame->payload.len = byte_stream_get32(bs);
! 503: frame->encoding = byte_stream_get16(bs);
! 504: byte_stream_advance(bs, 4);
! 505: frame->flags = byte_stream_get16(bs);
! 506: byte_stream_advance(bs, 4);
! 507: byte_stream_getrawbuf(bs, frame->bn, 32);
! 508:
! 509: purple_debug_info("oscar", "Incoming ODC frame from %s with "
! 510: "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
! 511: frame->bn, frame->type, frame->flags, frame->payload.len);
! 512:
! 513: if (!conn->ready)
! 514: {
! 515: /*
! 516: * We need to verify the cookie so that we know we are
! 517: * connected to our friend and not a malicious middle man.
! 518: */
! 519:
! 520: PurpleAccount *account;
! 521: PurpleConversation *conv;
! 522:
! 523: if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)
! 524: {
! 525: if (memcmp(conn->cookie, frame->cookie, 8))
! 526: {
! 527: /*
! 528: * Oh no! The user that connected to us did not send
! 529: * the correct cookie! They are not our friend. Go try
! 530: * to accept another connection?
! 531: */
! 532: purple_debug_info("oscar", "Received an incorrect cookie. "
! 533: "Closing connection.\n");
! 534: peer_connection_destroy(conn,
! 535: OSCAR_DISCONNECT_INVALID_DATA, NULL);
! 536: g_free(frame);
! 537: return;
! 538: }
! 539:
! 540: /*
! 541: * Ok, we know they are legit. Now be courteous and
! 542: * send them our cookie. Note: This doesn't seem
! 543: * to be necessary, but it also doesn't seem to hurt.
! 544: */
! 545: peer_odc_send_cookie(conn);
! 546: }
! 547:
! 548: conn->ready = TRUE;
! 549:
! 550: /*
! 551: * If they connected to us then close the listener socket
! 552: * and send them our cookie.
! 553: */
! 554: if (conn->listenerfd != -1)
! 555: {
! 556: close(conn->listenerfd);
! 557: conn->listenerfd = -1;
! 558: }
! 559:
! 560: /* Tell the local user that we are connected */
! 561: account = purple_connection_get_account(gc);
! 562: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
! 563: purple_conversation_write(conv, NULL, _("Direct IM established"),
! 564: PURPLE_MESSAGE_SYSTEM, time(NULL));
! 565: }
! 566:
! 567: if ((frame->type != 0x0001) && (frame->subtype != 0x0006))
! 568: {
! 569: purple_debug_info("oscar", "Unknown ODC frame type 0x%04hx, "
! 570: "subtype 0x%04hx.\n", frame->type, frame->subtype);
! 571: g_free(frame);
! 572: return;
! 573: }
! 574:
! 575: if (frame->flags & 0x0008)
! 576: {
! 577: /* I had to leave this. It's just too funny. It reminds me of my sister. */
! 578: purple_debug_info("oscar", "ohmigod! %s has started typing "
! 579: "(DirectIM). He's going to send you a message! "
! 580: "*squeal*\n", conn->bn);
! 581: serv_got_typing(gc, conn->bn, 0, PURPLE_TYPING);
! 582: }
! 583: else if (frame->flags & 0x0004)
! 584: {
! 585: serv_got_typing(gc, conn->bn, 0, PURPLE_TYPED);
! 586: }
! 587: else
! 588: {
! 589: serv_got_typing_stopped(gc, conn->bn);
! 590: }
! 591:
! 592: if (frame->payload.len > 0)
! 593: {
! 594: if (frame->payload.len > DIRECTIM_MAX_FILESIZE)
! 595: {
! 596: gchar *tmp, *size1, *size2;
! 597: PurpleAccount *account;
! 598: PurpleConversation *conv;
! 599:
! 600: size1 = purple_str_size_to_units(frame->payload.len);
! 601: size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE);
! 602: tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->bn, size1, size2);
! 603: g_free(size1);
! 604: g_free(size2);
! 605:
! 606: account = purple_connection_get_account(conn->od->gc);
! 607: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
! 608: purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
! 609: g_free(tmp);
! 610:
! 611: peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
! 612: g_free(frame);
! 613: return;
! 614: }
! 615:
! 616: /* We have payload data! Switch to the ODC watcher to read it. */
! 617: frame->payload.data = g_new(guint8, frame->payload.len + 1);
! 618: frame->payload.offset = 0;
! 619: conn->frame = frame;
! 620: purple_input_remove(conn->watcher_incoming);
! 621: conn->watcher_incoming = purple_input_add(conn->fd,
! 622: PURPLE_INPUT_READ, peer_odc_recv_cb, conn);
! 623: return;
! 624: }
! 625:
! 626: g_free(frame);
! 627: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>