diff -r d10dda2777a9 -r b63ebf84c42b src/protocols/oscar/odc.c
--- a/src/protocols/oscar/odc.c Sat Aug 19 00:24:14 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,598 +0,0 @@
-/*
- * Gaim's oscar protocol plugin
- * This file is the legal property of its developers.
- * Please see the AUTHORS file distributed alongside this file.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-/* From the oscar PRPL */
-#include "oscar.h"
-#include "peer.h"
-
-/* From Gaim */
-#include "conversation.h"
-#include "imgstore.h"
-#include "util.h"
-
-/**
- * Free any ODC related data and print a message to the conversation
- * window based on conn->disconnect_reason.
- */
-void
-peer_odc_close(PeerConnection *conn)
-{
- const gchar *tmp;
-
- if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
- tmp = _("The remote user has closed the connection.");
- else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)
- tmp = _("The remote user has declined your request.");
- else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
- tmp = _("Lost connection with the remote user for an unknown reason.");
- else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
- tmp = _("Received invalid data on connection with remote user.");
- else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
- tmp = _("Could not establish a connection with the remote user.");
- else
- /*
- * We shouldn't print a message for some disconnect_reasons.
- * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
- */
- tmp = NULL;
-
- if (tmp != NULL)
- {
- GaimAccount *account;
- GaimConversation *conv;
-
- account = gaim_connection_get_account(conn->od->gc);
- conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
- gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL));
- }
-
- if (conn->frame != NULL)
- {
- OdcFrame *frame;
- frame = conn->frame;
- g_free(frame->payload.data);
- g_free(frame);
- }
-}
-
-/**
- * Write the given OdcFrame to a ByteStream and send it out
- * on the established PeerConnection.
- */
-static void
-peer_odc_send(PeerConnection *conn, OdcFrame *frame)
-{
- GaimAccount *account;
- const char *username;
- size_t length;
- ByteStream bs;
-
- gaim_debug_info("oscar", "Outgoing ODC frame to %s with "
- "type=0x%04x, flags=0x%04x, payload length=%u\n",
- conn->sn, frame->type, frame->flags, frame->payload.len);
-
- account = gaim_connection_get_account(conn->od->gc);
- username = gaim_account_get_username(account);
- memcpy(frame->sn, username, strlen(username));
- memcpy(frame->cookie, conn->cookie, 8);
-
- length = 76;
- byte_stream_init(&bs, malloc(length + frame->payload.len),
- length + frame->payload.len);
- byte_stream_putraw(&bs, conn->magic, 4);
- byte_stream_put16(&bs, length);
- byte_stream_put16(&bs, frame->type);
- byte_stream_put16(&bs, frame->subtype);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_putraw(&bs, frame->cookie, 8);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put32(&bs, frame->payload.len);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, frame->encoding);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, frame->flags);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_put16(&bs, 0x0000);
- byte_stream_putraw(&bs, frame->sn, 32);
- byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
-
- peer_connection_send(conn, &bs);
-
- free(bs.data);
-}
-
-/**
- * Send a very basic ODC frame (which contains the cookie) so that the
- * remote user can verify that we are the person they were expecting.
- * If we made an outgoing connection to then remote user, then we send
- * this immediately. If the remote user connected to us, then we wait
- * for the other person to send this to us, then we send one to them.
- */
-void
-peer_odc_send_cookie(PeerConnection *conn)
-{
- OdcFrame frame;
-
- memset(&frame, 0, sizeof(OdcFrame));
- frame.type = 0x0001;
- frame.subtype = 0x0006;
- frame.flags = 0x0060; /* Maybe this means "we're sending the cookie"? */
-
- peer_odc_send(conn, &frame);
-}
-
-/**
- * Send client-to-client typing notification over an established direct connection.
- */
-void
-peer_odc_send_typing(PeerConnection *conn, GaimTypingState typing)
-{
- OdcFrame frame;
-
- memset(&frame, 0, sizeof(OdcFrame));
- frame.type = 0x0001;
- frame.subtype = 0x0006;
- if (typing == GAIM_TYPING)
- frame.flags = 0x0002 | 0x0008;
- else if (typing == GAIM_TYPED)
- frame.flags = 0x0002 | 0x0004;
- else
- frame.flags = 0x0002;
-
- peer_odc_send(conn, &frame);
-}
-
-/**
- * Send client-to-client IM over an established direct connection.
- * To send a direct IM, call this just like you would aim_send_im.
- *
- * @param conn The already-connected ODC connection.
- * @param msg Null-terminated string to send.
- * @param len The length of the message to send, including binary data.
- * @param encoding See the AIM_CHARSET_* defines in oscar.h
- * @param autoreply TRUE if this is any auto-reply.
- */
-void
-peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply)
-{
- OdcFrame frame;
-
- g_return_if_fail(msg != NULL);
- g_return_if_fail(len > 0);
-
- memset(&frame, 0, sizeof(OdcFrame));
- frame.type = 0x0001;
- frame.subtype = 0x0006;
- frame.payload.len = len;
- frame.encoding = encoding;
- frame.flags = autoreply;
- byte_stream_init(&frame.payload, malloc(len), len);
- byte_stream_putraw(&frame.payload, (guint8 *)msg, len);
-
- peer_odc_send(conn, &frame);
-
- g_free(frame.payload.data);
-}
-
-struct embedded_data
-{
- size_t size;
- const guint8 *data;
-};
-
-/**
- * This is called after a direct IM has been received in its entirety. This
- * function is passed a long chunk of data which contains the IM with any
- * data chunks (images) appended to it.
- *
- * This function rips out all the data chunks and creates an imgstore for
- * each one. In order to do this, it first goes through the IM and takes
- * out all the IMG tags. When doing so, it rewrites the original IMG tag
- * with one compatible with the imgstore Gaim core code. For each one, we
- * then read in chunks of data from the end of the message and actually
- * create the img store using the given data.
- *
- * For somewhat easy reference, here's a sample message
- * (with added whitespace):
- *
- *
- *
- * This is a really stupid picture:
- * 
- * Yeah it is
- * Here is another one:
- *
- *
- *
- *
- * datadatadatadata
- * datadatadatadata
- *
- */
-static void
-peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply)
-{
- GaimConnection *gc;
- GaimAccount *account;
- const char *msgend, *binary_start, *dataend;
- const char *tmp, *start, *end, *idstr, *src, *sizestr;
- GData *attributes;
- GHashTable *embedded_datas;
- struct embedded_data *embedded_data;
- GSList *images;
- gchar *utf8;
- GString *newmsg;
- GaimMessageFlags imflags;
-
- gc = conn->od->gc;
- account = gaim_connection_get_account(gc);
-
- dataend = msg + len;
-
- /*
- * Create a hash table containing references to each embedded
- * data chunk. The key is the "ID" and the value is an
- * embedded_data struct.
- */
- embedded_datas = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, g_free);
-
- /*
- * Create an index of any binary chunks. If we run into any
- * problems while parsing the binary data section then we stop
- * parsing it, and the local user will see broken image icons.
- */
- /* TODO: Use a length argument when looking for the tag! */
- binary_start = gaim_strcasestr(msg, "");
- if (binary_start == NULL)
- msgend = dataend;
- else
- {
- msgend = binary_start;
-
- /* Move our pointer to immediately after the tag */
- tmp = binary_start + 8;
-
- /* The embedded binary markup has a mimimum length of 29 bytes */
- /* TODO: Use a length argument when looking for the tag! */
- while ((tmp + 29 <= dataend) &&
- gaim_markup_find_tag("data", tmp, &start, &tmp, &attributes))
- {
- unsigned int id;
- size_t size;
-
- /* Move the binary pointer from ">" to the start of the data */
- tmp++;
-
- /* Get the ID */
- idstr = g_datalist_get_data(&attributes, "id");
- if (idstr == NULL)
- {
- g_datalist_clear(&attributes);
- break;
- }
- id = atoi(idstr);
-
- /* Get the size */
- sizestr = g_datalist_get_data(&attributes, "size");
- if (sizestr == NULL)
- {
- g_datalist_clear(&attributes);
- break;
- }
- size = atol(sizestr);
-
- g_datalist_clear(&attributes);
-
- if ((size > 0) && (tmp + size > dataend))
- break;
-
- embedded_data = g_new(struct embedded_data, 1);
- embedded_data->size = size;
- embedded_data->data = (const guint8 *)tmp;
- tmp += size;
-
- /* Skip past the closing tag */
- if (strncasecmp(tmp, "", 7))
- {
- g_free(embedded_data);
- break;
- }
- tmp += 7;
-
- g_hash_table_insert(embedded_datas,
- GINT_TO_POINTER(id), embedded_data);
- }
- }
-
- /*
- * Loop through the message, replacing OSCAR img tags with the
- * equivalent Gaim img tag.
- */
- images = NULL;
- newmsg = g_string_new("");
- tmp = msg;
- while (gaim_markup_find_tag("img", tmp, &start, &end, &attributes))
- {
- int imgid = 0;
-
- idstr = g_datalist_get_data(&attributes, "id");
- src = g_datalist_get_data(&attributes, "src");
- sizestr = g_datalist_get_data(&attributes, "datasize");
-
- if ((idstr != NULL) && (src != NULL) && (sizestr!= NULL))
- {
- unsigned int id;
- size_t size;
-
- id = atoi(idstr);
- size = atol(sizestr);
- embedded_data = g_hash_table_lookup(embedded_datas,
- GINT_TO_POINTER(id));
-
- if ((embedded_data != NULL) && (embedded_data->size == size))
- {
- imgid = gaim_imgstore_add(embedded_data->data, size, src);
-
- /* Record the image number */
- images = g_slist_append(images, GINT_TO_POINTER(imgid));
- }
- }
-
- /* Delete the attribute list */
- g_datalist_clear(&attributes);
-
- /* Append the message up to the tag */
- utf8 = gaim_plugin_oscar_decode_im_part(account, conn->sn,
- encoding, 0x0000, tmp, start - tmp);
- if (utf8 != NULL) {
- g_string_append(newmsg, utf8);
- g_free(utf8);
- }
-
- if (imgid != 0)
- {
- /* Write the new image tag */
- g_string_append_printf(newmsg, "
", imgid);
- }
-
- /* Continue from the end of the tag */
- tmp = end + 1;
- }
-
- /* Append any remaining message data */
- if (tmp <= msgend)
- {
- utf8 = gaim_plugin_oscar_decode_im_part(account, conn->sn,
- encoding, 0x0000, tmp, msgend - tmp);
- if (utf8 != NULL) {
- g_string_append(newmsg, utf8);
- g_free(utf8);
- }
- }
-
- /* Send the message */
- imflags = 0;
- if (images != NULL)
- imflags |= GAIM_MESSAGE_IMAGES;
- if (autoreply)
- imflags |= GAIM_MESSAGE_AUTO_RESP;
- serv_got_im(gc, conn->sn, newmsg->str, imflags, time(NULL));
- g_string_free(newmsg, TRUE);
-
- /* unref any images we allocated */
- if (images)
- {
- GSList *l;
- for (l = images; l != NULL; l = l->next)
- gaim_imgstore_unref(GPOINTER_TO_INT(l->data));
- g_slist_free(images);
- }
-
- /* Delete our list of pointers to embedded images */
- g_hash_table_destroy(embedded_datas);
-}
-
-/**
- * This is a gaim_input_add() watcher callback function for reading
- * direct IM payload data. "Payload data" is always an IM and
- * maybe some embedded images or files or something. The actual
- * ODC frame is read using peer_connection_recv_cb(). We temporarily
- * switch to this watcher callback ONLY to read the payload, and we
- * switch back once we're done.
- */
-static void
-peer_odc_recv_cb(gpointer data, gint source, GaimInputCondition cond)
-{
- PeerConnection *conn;
- OdcFrame *frame;
- ByteStream *bs;
- ssize_t read;
-
- conn = data;
- frame = conn->frame;
- bs = &frame->payload;
-
- /* Read data into the temporary buffer until it is complete */
- read = recv(conn->fd,
- &bs->data[bs->offset],
- bs->len - bs->offset,
- 0);
-
- /* Check if the remote user closed the connection */
- if (read == 0)
- {
- peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED);
- return;
- }
-
- if (read == -1)
- {
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- /* No worries */
- return;
-
- peer_connection_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION);
- return;
- }
-
- bs->offset += read;
- if (bs->offset < bs->len)
- /* Waiting for more data to arrive */
- return;
-
- /* We have a complete ODC/OFT frame! Handle it and continue reading */
- byte_stream_rewind(bs);
- peer_odc_handle_payload(conn, (const char *)bs->data,
- bs->len, frame->encoding, frame->flags & 0x0001);
- g_free(bs->data);
- bs->data = NULL;
- g_free(frame);
- conn->frame = NULL;
-
- gaim_input_remove(conn->watcher_incoming);
- conn->watcher_incoming = gaim_input_add(conn->fd,
- GAIM_INPUT_READ, peer_connection_recv_cb, conn);
-}
-
-/**
- * Handle an incoming OdcFrame. If there is a payload associated
- * with this frame, then we remove the old watcher and add the
- * ODC watcher to read in the payload.
- */
-void
-peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
-{
- GaimConnection *gc;
- OdcFrame *frame;
-
- gc = conn->od->gc;
-
- frame = g_new0(OdcFrame, 1);
- frame->type = byte_stream_get16(bs);
- frame->subtype = byte_stream_get16(bs);
- byte_stream_advance(bs, 2);
- byte_stream_getrawbuf(bs, frame->cookie, 8);
- byte_stream_advance(bs, 8);
- frame->payload.len = byte_stream_get32(bs);
- frame->encoding = byte_stream_get16(bs);
- byte_stream_advance(bs, 4);
- frame->flags = byte_stream_get16(bs);
- byte_stream_advance(bs, 4);
- byte_stream_getrawbuf(bs, frame->sn, 32);
-
- gaim_debug_info("oscar", "Incoming ODC frame from %s with "
- "type=0x%04x, flags=0x%04x, payload length=%u\n",
- frame->sn, frame->type, frame->flags, frame->payload.len);
-
- if (!conn->ready)
- {
- /*
- * We need to verify the cookie so that we know we are
- * connected to our friend and not a malicious middle man.
- */
-
- GaimAccount *account;
- GaimConversation *conv;
-
- if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)
- {
- if (memcmp(conn->cookie, frame->cookie, 8))
- {
- /*
- * Oh no! The user that connected to us did not send
- * the correct cookie! They are not our friend. Go try
- * to accept another connection?
- */
- gaim_debug_info("oscar", "Received an incorrect cookie. "
- "Closing connection.\n");
- peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA);
- g_free(frame);
- return;
- }
-
- /*
- * Ok, we know they are legit. Now be courteous and
- * send them our cookie. Note: This doesn't seem
- * to be necessary, but it also doesn't seem to hurt.
- */
- peer_odc_send_cookie(conn);
- }
-
- conn->ready = TRUE;
-
- /*
- * If they connected to us then close the listener socket
- * and send them our cookie.
- */
- if (conn->listenerfd != -1)
- {
- close(conn->listenerfd);
- conn->listenerfd = -1;
- }
-
- /* Tell the local user that we are connected */
- account = gaim_connection_get_account(gc);
- conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
- gaim_conversation_write(conv, NULL, _("Direct IM established"),
- GAIM_MESSAGE_SYSTEM, time(NULL));
- }
-
- if ((frame->type != 0x0001) && (frame->subtype != 0x0006))
- {
- gaim_debug_info("oscar", "Unknown ODC frame type 0x%04hx, "
- "subtype 0x%04hx.\n", frame->type, frame->subtype);
- return;
- }
-
- if (frame->flags & 0x0008)
- {
- /* I had to leave this. It's just too funny. It reminds me of my sister. */
- gaim_debug_info("oscar", "ohmigod! %s has started typing "
- "(DirectIM). He's going to send you a message! "
- "*squeal*\n", conn->sn);
- serv_got_typing(gc, conn->sn, 0, GAIM_TYPING);
- }
- else if (frame->flags & 0x0004)
- {
- serv_got_typing(gc, conn->sn, 0, GAIM_TYPED);
- }
- else
- {
- serv_got_typing_stopped(gc, conn->sn);
- }
-
- if (frame->payload.len > 0)
- {
- /* We have payload data! Switch to the ODC watcher to read it. */
- frame->payload.data = g_new(guint8, frame->payload.len);
- frame->payload.offset = 0;
- conn->frame = frame;
- gaim_input_remove(conn->watcher_incoming);
- conn->watcher_incoming = gaim_input_add(conn->fd,
- GAIM_INPUT_READ, peer_odc_recv_cb, conn);
- return;
- }
-
- g_free(frame);
-}