/** * Takes a new incoming FLAP frame and sends it to the appropriate * handler function to be parsed. */ static void parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame) { if (frame->channel == 0x01) { guint32 flap_version = byte_stream_get32(&frame->data); if (flap_version != 0x00000001) { /* Error! */ purple_debug_warning("oscar", "Expecting FLAP version " "0x00000001 but received FLAP version %08x. Closing connection.\n", flap_version); flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); } else conn->connected = TRUE; } else if (frame->channel == 0x02) { parse_snac(od, conn, frame); } else if (frame->channel == 0x04) { parse_flap_ch4(od, conn, frame); } else if (frame->channel == 0x05) { /* TODO: Reset our keepalive watchdog? */ } }
/** * @param source When this function is called as a callback source is * set to the fd that triggered the callback. But this function * is also called directly from flap_connection_send_byte_stream(), * in which case source will be -1. So don't use source--use * conn->gsc or conn->fd instead. */ static void send_cb(gpointer data, gint source, PurpleInputCondition cond) { FlapConnection *conn; int writelen, ret; const gchar *output = NULL; conn = data; writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing); output = purple_circular_buffer_get_output(conn->buffer_outgoing); if (writelen == 0) { purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; return; } if (conn->gsc) ret = purple_ssl_write(conn->gsc, output, writelen); else ret = send(conn->fd, output, writelen, 0); if (ret <= 0) { if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) /* No worries */ return; /* Error! */ purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; if (conn->gsc) { purple_ssl_close(conn->gsc); conn->gsc = NULL; } else { close(conn->fd); conn->fd = -1; } flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); return; } purple_circular_buffer_mark_read(conn->buffer_outgoing, ret); }
static void parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame) { GSList *tlvlist; char *msg = NULL; if (byte_stream_bytes_left(&frame->data) == 0) { /* XXX should do something with this */ return; } /* An ICQ account is logging in */ if (conn->type == SNAC_FAMILY_AUTH) { parse_fakesnac(od, conn, frame, 0x0017, 0x0003); return; } tlvlist = aim_tlvlist_read(&frame->data); if (aim_tlv_gettlv(tlvlist, 0x0009, 1)) conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1); if (aim_tlv_gettlv(tlvlist, 0x000b, 1)) msg = aim_tlv_getstr(tlvlist, 0x000b, 1); /* * The server ended this FLAP connnection, so let's be nice and * close the physical TCP connection */ flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, msg); aim_tlvlist_free(tlvlist); g_free(msg); }
/** * Read in all available data on the socket for a given connection. * All complete FLAPs handled immedate after they're received. * Incomplete FLAP data is stored locally and appended to the next * time this callback is triggered. * * This is called by flap_connection_recv_cb and * flap_connection_recv_cb_ssl for unencrypted/encrypted connections. */ static void flap_connection_recv(FlapConnection *conn) { gpointer buf; gsize buflen; gssize read; /* Read data until we run out of data and break out of the loop */ while (TRUE) { /* Start reading a new FLAP */ if (conn->buffer_incoming.data.data == NULL) { buf = conn->header + conn->header_received; buflen = 6 - conn->header_received; /* Read the first 6 bytes (the FLAP header) */ if (conn->gsc) read = purple_ssl_read(conn->gsc, buf, buflen); else read = recv(conn->fd, buf, buflen, 0); /* Check if the FLAP server closed the connection */ if (read == 0) { flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); break; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ break; /* Error! */ flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); break; } conn->od->gc->last_received = time(NULL); /* If we don't even have a complete FLAP header then do nothing */ conn->header_received += read; if (conn->header_received < 6) break; /* All FLAP frames must start with the byte 0x2a */ if (aimutil_get8(&conn->header[0]) != 0x2a) { flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); break; } /* Initialize a new temporary FlapFrame for incoming data */ conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]); conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]); conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]); conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len); conn->buffer_incoming.data.offset = 0; } buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset; if (buflen) { buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset]; /* Read data into the temporary FlapFrame until it is complete */ if (conn->gsc) read = purple_ssl_read(conn->gsc, buf, buflen); else read = recv(conn->fd, buf, buflen, 0); /* Check if the FLAP server closed the connection */ if (read == 0) { flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); break; } if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ break; /* Error! */ flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); break; } conn->buffer_incoming.data.offset += read; if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len) /* Waiting for more data to arrive */ break; } /* We have a complete FLAP! Handle it and continue reading */ byte_stream_rewind(&conn->buffer_incoming.data); parse_flap(conn->od, conn, &conn->buffer_incoming); conn->lastactivity = time(NULL); g_free(conn->buffer_incoming.data.data); conn->buffer_incoming.data.data = NULL; conn->header_received = 0; } }