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