File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / odc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Mon Jan 27 19:48:25 2025 UTC (6 months ago) by snw
Branches: MAIN, CoherentLogicDevelopment
CVS tags: test-tag, start, HEAD
Pidgin AIM Plugin for ChivaNet

    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>