static void sendframe_flap(FlapConnection *conn, FlapFrame *frame) { ByteStream bs; int payloadlen, bslen; payloadlen = byte_stream_curpos(&frame->data); byte_stream_new(&bs, 6 + payloadlen); /* FLAP header */ byte_stream_put8(&bs, 0x2a); byte_stream_put8(&bs, frame->channel); byte_stream_put16(&bs, frame->seqnum); byte_stream_put16(&bs, payloadlen); /* Payload */ byte_stream_rewind(&frame->data); byte_stream_putbs(&bs, &frame->data, payloadlen); bslen = byte_stream_curpos(&bs); byte_stream_rewind(&bs); flap_connection_send_byte_stream(&bs, conn, bslen); byte_stream_destroy(&bs); }
/* Subtype 0x0006 */ static int userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_userinfo_t *userinfo, *userinfo2; GSList *tlvlist; aim_tlv_t *tlv = NULL; userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t)); aim_info_extract(od, bs, userinfo); tlvlist = aim_tlvlist_read(bs); /* Profile will be 1 and 2 */ userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { userinfo->info = (char *)g_malloc(tlv->length); memcpy(userinfo->info, tlv->value, tlv->length); userinfo->info_len = tlv->length; } /* Away message will be 3 and 4 */ userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { userinfo->away = (char *)g_malloc(tlv->length); memcpy(userinfo->away, tlv->value, tlv->length); userinfo->away_len = tlv->length; } /* Caps will be 5 */ if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { ByteStream cbs; PurpleAccount *account = purple_connection_get_account(od->gc); const char *mood; byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); byte_stream_rewind(&cbs); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; mood = aim_receive_custom_icon(od, &cbs, tlv->length); if (mood) purple_prpl_got_user_status(account, userinfo->bn, "mood", PURPLE_MOOD_NAME, mood, NULL); else purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood"); } aim_tlvlist_free(tlvlist); aim_locate_adduserinfo(od, userinfo); userinfo2 = aim_locate_finduserinfo(od, userinfo->bn); aim_info_free(userinfo); g_free(userinfo); /* Show the info to the user */ oscar_user_info_display_aim(od, userinfo2); return ret; }
/** * This should be used to read ODC and OFT framing info. It should * NOT be used to read the payload sent across the connection (IMs, * file data, etc), and it should NOT be used to read proxy negotiation * headers. * * Unlike flap_connection_recv_cb(), this only reads one frame at a * time. This is done so that the watcher can be changed during the * handling of the frame. If the watcher is changed then this * function will not read in any more data. This happens when * reading the payload of a direct IM frame, or when we're * receiving a file from the remote user. Once the data has been * read, the watcher will be switched back to this function to * continue reading the next frame. */ void peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) { PeerConnection *conn; gssize read; conn = data; /* Start reading a new ODC/OFT frame */ if (conn->buffer_incoming.data == NULL) { /* Read the first 6 bytes (magic string and frame length) */ read = recv(conn->fd, conn->header + conn->header_received, 6 - conn->header_received, 0); /* Check if the remote user closed the connection */ if (read == 0) { peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); return; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; peer_connection_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); return; } conn->lastactivity = time(NULL); /* If we don't even have the first 6 bytes then do nothing */ conn->header_received += read; if (conn->header_received < 6) return; /* All ODC/OFT frames must start with a magic string */ if (memcmp(conn->magic, conn->header, 4)) { purple_debug_warning("oscar", "Expecting magic string to " "be %c%c%c%c but received magic string %c%c%c%c. " "Closing connection.\n", conn->magic[0], conn->magic[1], conn->magic[2], conn->magic[3], conn->header[0], conn->header[1], conn->header[2], conn->header[3]); peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); return; } /* Initialize a new temporary ByteStream for incoming data */ conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6; conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len); conn->buffer_incoming.offset = 0; } /* Read data into the temporary buffer until it is complete */ read = recv(conn->fd, &conn->buffer_incoming.data[conn->buffer_incoming.offset], conn->buffer_incoming.len - conn->buffer_incoming.offset, 0); /* Check if the remote user closed the connection */ if (read == 0) { peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); return; } if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; peer_connection_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); return; } conn->lastactivity = time(NULL); conn->buffer_incoming.offset += read; if (conn->buffer_incoming.offset < conn->buffer_incoming.len) /* Waiting for more data to arrive */ return; /* We have a complete ODC/OFT frame! Handle it and continue reading */ byte_stream_rewind(&conn->buffer_incoming); if (conn->type == OSCAR_CAPABILITY_DIRECTIM) { peer_odc_recv_frame(conn, &conn->buffer_incoming); } else if (conn->type == OSCAR_CAPABILITY_SENDFILE) { peer_oft_recv_frame(conn, &conn->buffer_incoming); } g_free(conn->buffer_incoming.data); conn->buffer_incoming.data = NULL; conn->header_received = 0; }
static void peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) { PeerConnection *conn; gssize read; ProxyFrame *frame; conn = data; frame = conn->frame; /* Start reading a new proxy frame */ if (frame == NULL) { /* Read the first 12 bytes (frame length and header) */ read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received, 12 - conn->proxy_header_received, 0); /* Check if the proxy server closed the connection */ if (read == 0) { purple_debug_info("oscar", "Peer proxy server closed connection\n"); peer_connection_trynext(conn); return; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; purple_debug_info("oscar", "Lost connection with peer proxy server\n"); peer_connection_trynext(conn); return; } conn->lastactivity = time(NULL); /* If we don't even have the first 12 bytes then do nothing */ conn->proxy_header_received += read; if (conn->proxy_header_received < 12) return; /* We only support a specific version of the proxy protocol */ if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION) { purple_debug_warning("oscar", "Expected peer proxy protocol " "version %u but received version %u. Closing " "connection.\n", PEER_PROXY_PACKET_VERSION, aimutil_get16(&conn->proxy_header[2])); peer_connection_trynext(conn); return; } /* Initialize a new temporary ProxyFrame for incoming data */ frame = g_new0(ProxyFrame, 1); frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10; frame->version = aimutil_get16(&conn->proxy_header[2]); frame->type = aimutil_get16(&conn->proxy_header[4]); frame->unknown = aimutil_get16(&conn->proxy_header[6]); frame->flags = aimutil_get16(&conn->proxy_header[10]); if (frame->payload.len > 0) frame->payload.data = g_new(guint8, frame->payload.len); conn->frame = frame; } /* If this frame has a payload then attempt to read it */ if (frame->payload.len - frame->payload.offset > 0) { /* Read data into the temporary buffer until it is complete */ read = recv(conn->fd, &frame->payload.data[frame->payload.offset], frame->payload.len - frame->payload.offset, 0); /* Check if the proxy server closed the connection */ if (read == 0) { purple_debug_info("oscar", "Peer proxy server closed connection\n"); g_free(frame->payload.data); g_free(frame); conn->frame = NULL; peer_connection_trynext(conn); return; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; purple_debug_info("oscar", "Lost connection with peer proxy server\n"); g_free(frame->payload.data); g_free(frame); conn->frame = NULL; peer_connection_trynext(conn); return; } frame->payload.offset += read; } conn->lastactivity = time(NULL); if (frame->payload.offset < frame->payload.len) /* Waiting for more data to arrive */ return; /* We have a complete proxy frame! Handle it and continue reading */ conn->frame = NULL; byte_stream_rewind(&frame->payload); peer_proxy_recv_frame(conn, frame); g_free(frame->payload.data); g_free(frame); conn->proxy_header_received = 0; }
/** * 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; } }
/** * This sends a channel 2 FLAP containing a SNAC. The SNAC family and * subtype are looked up in the rate info for this connection, and if * sending this SNAC will induce rate limiting then we delay sending * of the SNAC by putting it into an outgoing holding queue. * * @param data The optional bytestream that makes up the data portion * of this SNAC. For empty SNACs this should be NULL. * @param high_priority If TRUE, the SNAC will be queued normally if * needed. If FALSE, it will be queued separately, to be sent * only if all high priority SNACs have been sent. */ void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority) { FlapFrame *frame; guint32 length; gboolean enqueue = FALSE; struct rateclass *rateclass; length = data != NULL ? data->offset : 0; frame = flap_frame_new(od, 0x02, 10 + length); aim_putsnac(&frame->data, family, subtype, snacid); if (length > 0) { byte_stream_rewind(data); byte_stream_putbs(&frame->data, data, length); } if (conn->queued_timeout != 0) enqueue = TRUE; else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL) { struct timeval now; guint32 new_current; gettimeofday(&now, NULL); new_current = rateclass_get_new_current(conn, rateclass, &now); if (rateclass->dropping_snacs || new_current <= rateclass->alert) { purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn, new_current, rateclass->alert); enqueue = TRUE; } else { rateclass->current = new_current; rateclass->last.tv_sec = now.tv_sec; rateclass->last.tv_usec = now.tv_usec; } } if (enqueue) { /* We've been sending too fast, so delay this message */ QueuedSnac *queued_snac; queued_snac = g_new(QueuedSnac, 1); queued_snac->family = family; queued_snac->subtype = subtype; queued_snac->frame = frame; if (high_priority) { if (!conn->queued_snacs) conn->queued_snacs = g_queue_new(); g_queue_push_tail(conn->queued_snacs, queued_snac); } else { if (!conn->queued_lowpriority_snacs) conn->queued_lowpriority_snacs = g_queue_new(); g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac); } if (conn->queued_timeout == 0) conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn); return; } flap_connection_send(conn, frame); }