Sun, 09 Apr 2006 18:29:27 +0000
[gaim-migrate @ 15985]
This fixes the bugs when receiving direct IMs containing multiple images.
Basically we were using gaim_strcasestr() to find data within a big
chunk of memory that contained binary data. The fix is to skip over
the binary data so that we start looking for the next <data> tag exactly
where it should occur.
| 13593 | 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 | ||
| 39 | if (conn->disconnect_reason == PEER_DISCONNECT_REMOTE_CLOSED) | |
| 40 | { | |
| 41 | tmp = _("Remote user closed the connection."); | |
| 42 | } | |
| 43 | else if (conn->disconnect_reason == PEER_DISCONNECT_REMOTE_REFUSED) | |
| 44 | { | |
| 45 | tmp = _("Remote user declined your request."); | |
| 46 | } | |
| 47 | else if (conn->disconnect_reason == PEER_DISCONNECT_LOST_CONNECTION) | |
| 48 | { | |
| 49 | tmp = _("Lost connection with remote user for an unknown reason."); | |
| 50 | } | |
| 51 | else if (conn->disconnect_reason == PEER_DISCONNECT_INVALID_DATA) | |
| 52 | { | |
| 53 | tmp = _("Received invalid data on connection."); | |
| 54 | } | |
| 55 | else if (conn->disconnect_reason == PEER_DISCONNECT_COULD_NOT_CONNECT) | |
| 56 | { | |
| 57 | tmp = _("Could not establish connection with remote user."); | |
| 58 | } | |
| 59 | else | |
| 60 | /* | |
| 61 | * We shouldn't print a message for some disconnect_reasons. | |
| 62 | * Like PEER_DISCONNECT_LOCAL_CLOSED. | |
| 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 | ||
| 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 Gaim 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 | * (without the whitespace and asterisks): | |
| 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 | * | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
236 | * TODO: This should be rewritten to parse all the binary data first |
|
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
237 | * and add each image, then go through the message afterwrod and |
|
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
238 | * substitute in the image tags. |
| 13593 | 239 | */ |
| 240 | static void | |
| 241 | peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply) | |
| 242 | { | |
| 243 | OscarData *od; | |
| 244 | GaimConnection *gc; | |
| 245 | GaimAccount *account; | |
| 246 | GaimMessageFlags imflags; | |
| 247 | gchar *utf8; | |
| 248 | GString *newmsg; | |
| 249 | GSList *images; | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
250 | const char *msgend, *binary_start, *binary; |
| 13593 | 251 | |
| 252 | od = conn->od; | |
| 253 | gc = od->gc; | |
| 254 | account = gaim_connection_get_account(gc); | |
| 255 | ||
| 256 | imflags = 0; | |
| 257 | newmsg = g_string_new(""); | |
| 258 | images = NULL; | |
| 259 | ||
| 260 | msgend = msg + len; | |
| 261 | ||
| 262 | if (autoreply) | |
| 263 | imflags |= GAIM_MESSAGE_AUTO_RESP; | |
| 264 | ||
| 265 | /* message has a binary trailer */ | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
266 | if ((binary_start = gaim_strcasestr(msg, "<binary>"))) |
| 13593 | 267 | { |
| 268 | GData *attribs; | |
| 269 | const char *tmp, *start, *end, *last = NULL; | |
| 270 | ||
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
271 | binary = binary_start; |
| 13593 | 272 | tmp = msg; |
| 273 | ||
| 274 | /* for each valid image tag... */ | |
| 275 | while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) | |
| 276 | { | |
| 277 | const char *id, *src, *datasize; | |
| 278 | const char *data = NULL; | |
| 279 | char *tag = NULL; | |
| 280 | size_t size; | |
| 281 | int imgid = 0; | |
| 282 | ||
| 283 | /* update the location of the last img tag */ | |
| 284 | last = end; | |
| 285 | ||
| 286 | /* grab attributes */ | |
| 287 | id = g_datalist_get_data(&attribs, "id"); | |
| 288 | src = g_datalist_get_data(&attribs, "src"); | |
| 289 | datasize = g_datalist_get_data(&attribs, "datasize"); | |
| 290 | ||
| 291 | /* if we have id & datasize, build the data tag */ | |
| 292 | if (id && datasize) | |
| 293 | tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize); | |
| 294 | ||
| 295 | /* if we have a tag, find the start of the data */ | |
| 296 | if (tag && (data = gaim_strcasestr(binary, tag))) | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
297 | { |
| 13593 | 298 | data += strlen(tag); |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
299 | binary = data + atoi(datasize) + 7; /* for </data> */ |
|
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
300 | } |
| 13593 | 301 | |
| 302 | g_free(tag); | |
| 303 | ||
| 304 | /* check the data is here and store it */ | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
305 | if (data && (data + (size = atoi(datasize)) <= msgend)) |
| 13593 | 306 | imgid = gaim_imgstore_add(data, size, src); |
| 307 | ||
| 308 | /* if we have a stored image... */ | |
| 309 | if (imgid) { | |
| 310 | /* append the message up to the tag */ | |
| 311 | utf8 = gaim_plugin_oscar_decode_im_part(account, conn->sn, | |
| 312 | encoding, 0x0000, tmp, start - tmp); | |
| 313 | if (utf8 != NULL) { | |
| 314 | newmsg = g_string_append(newmsg, utf8); | |
| 315 | g_free(utf8); | |
| 316 | } | |
| 317 | ||
| 318 | /* write the new image tag */ | |
| 319 | g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid); | |
| 320 | ||
| 321 | /* and record the image number */ | |
| 322 | images = g_slist_append(images, GINT_TO_POINTER(imgid)); | |
| 323 | } else { | |
| 324 | /* otherwise, copy up to the end of the tag */ | |
| 325 | utf8 = gaim_plugin_oscar_decode_im_part(account, conn->sn, | |
| 326 | encoding, 0x0000, tmp, (end + 1) - tmp); | |
| 327 | if (utf8 != NULL) { | |
| 328 | newmsg = g_string_append(newmsg, utf8); | |
| 329 | g_free(utf8); | |
| 330 | } | |
| 331 | } | |
| 332 | ||
| 333 | /* clear the attribute list */ | |
| 334 | g_datalist_clear(&attribs); | |
| 335 | ||
| 336 | /* continue from the end of the tag */ | |
| 337 | tmp = end + 1; | |
| 338 | } | |
| 339 | ||
| 340 | /* append any remaining message data (without the > :-)) */ | |
|
13600
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
341 | if (last++ && (last < binary_start)) |
|
4f436279a21c
[gaim-migrate @ 15985]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
342 | newmsg = g_string_append_len(newmsg, last, binary_start - last); |
| 13593 | 343 | |
| 344 | /* set the flag if we caught any images */ | |
| 345 | if (images) | |
| 346 | imflags |= GAIM_MESSAGE_IMAGES; | |
| 347 | } else { | |
| 348 | utf8 = gaim_plugin_oscar_decode_im_part(account, conn->sn, | |
| 349 | encoding, 0x0000, msg, len); | |
| 350 | if (utf8 != NULL) { | |
| 351 | g_string_append(newmsg, utf8); | |
| 352 | g_free(utf8); | |
| 353 | } | |
| 354 | } | |
| 355 | ||
| 356 | serv_got_im(gc, conn->sn, newmsg->str, imflags, time(NULL)); | |
| 357 | ||
| 358 | /* free the message */ | |
| 359 | g_string_free(newmsg, TRUE); | |
| 360 | ||
| 361 | /* unref any images we allocated */ | |
| 362 | if (images) { | |
| 363 | GSList *tmp; | |
| 364 | int id; | |
| 365 | ||
| 366 | for (tmp = images; tmp != NULL; tmp = tmp->next) { | |
| 367 | id = GPOINTER_TO_INT(tmp->data); | |
| 368 | gaim_imgstore_unref(id); | |
| 369 | } | |
| 370 | ||
| 371 | g_slist_free(images); | |
| 372 | } | |
| 373 | } | |
| 374 | ||
| 375 | /** | |
| 376 | * This is a gaim_input_add() watcher callback function for reading | |
| 377 | * direct IM payload data. "Payload data" is always an IM and | |
| 378 | * maybe some embedded images or files or something. The actual | |
| 379 | * ODC frame is read using peer_connection_recv_cb(). We temporarily | |
| 380 | * switch to this watcher callback ONLY to read the payload, and we | |
| 381 | * switch back once we're done. | |
| 382 | */ | |
| 383 | static void | |
| 384 | peer_odc_recv_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 385 | { | |
| 386 | PeerConnection *conn; | |
| 387 | OdcFrame *frame; | |
| 388 | ByteStream *bs; | |
| 389 | ssize_t read; | |
| 390 | ||
| 391 | conn = data; | |
| 392 | frame = conn->frame; | |
| 393 | bs = &frame->payload; | |
| 394 | ||
| 395 | /* Read data into the temporary buffer until it is complete */ | |
| 396 | read = recv(conn->fd, | |
| 397 | &bs->data[bs->offset], | |
| 398 | bs->len - bs->offset, | |
| 399 | 0); | |
| 400 | ||
| 401 | /* Check if the remote user closed the connection */ | |
| 402 | if (read == 0) | |
| 403 | { | |
| 404 | peer_connection_destroy(conn, PEER_DISCONNECT_REMOTE_CLOSED); | |
| 405 | return; | |
| 406 | } | |
| 407 | ||
| 408 | if (read == -1) | |
| 409 | { | |
| 410 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
| 411 | /* No worries */ | |
| 412 | return; | |
| 413 | ||
| 414 | peer_connection_destroy(conn, PEER_DISCONNECT_LOST_CONNECTION); | |
| 415 | return; | |
| 416 | } | |
| 417 | ||
| 418 | bs->offset += read; | |
| 419 | if (bs->offset < bs->len) | |
| 420 | /* Waiting for more data to arrive */ | |
| 421 | return; | |
| 422 | ||
| 423 | /* We have a complete ODC/OFT frame! Handle it and continue reading */ | |
| 424 | byte_stream_rewind(bs); | |
| 425 | peer_odc_handle_payload(conn, (const char *)bs->data, | |
| 426 | bs->len, frame->encoding, frame->flags & 0x0001); | |
| 427 | g_free(bs->data); | |
| 428 | bs->data = NULL; | |
| 429 | g_free(frame); | |
| 430 | conn->frame = NULL; | |
| 431 | ||
| 432 | gaim_input_remove(conn->watcher_incoming); | |
| 433 | conn->watcher_incoming = gaim_input_add(conn->fd, | |
| 434 | GAIM_INPUT_READ, peer_connection_recv_cb, conn); | |
| 435 | } | |
| 436 | ||
| 437 | /** | |
| 438 | * Handle an incoming OdcFrame. If there is a payload associated | |
| 439 | * with this frame, then we remove the old watcher and add the | |
| 440 | * ODC watcher to read in the payload. | |
| 441 | */ | |
| 442 | void | |
| 443 | peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs) | |
| 444 | { | |
| 445 | GaimConnection *gc; | |
| 446 | OdcFrame *frame; | |
| 447 | ||
| 448 | gc = conn->od->gc; | |
| 449 | ||
| 450 | frame = g_new0(OdcFrame, 1); | |
| 451 | frame->type = byte_stream_get16(bs); | |
| 452 | frame->subtype = byte_stream_get16(bs); | |
| 453 | byte_stream_advance(bs, 2); | |
| 454 | byte_stream_getrawbuf(bs, frame->cookie, 8); | |
| 455 | byte_stream_advance(bs, 8); | |
| 456 | frame->payload.len = byte_stream_get32(bs); | |
| 457 | frame->encoding = byte_stream_get16(bs); | |
| 458 | byte_stream_advance(bs, 4); | |
| 459 | frame->flags = byte_stream_get16(bs); | |
| 460 | byte_stream_advance(bs, 4); | |
| 461 | byte_stream_getrawbuf(bs, frame->sn, 32); | |
| 462 | ||
| 463 | gaim_debug_info("oscar", "Incoming ODC frame from %s with " | |
| 464 | "type=0x%04x, flags=0x%04x, payload length=%u\n", | |
| 465 | frame->sn, frame->type, frame->flags, frame->payload.len); | |
| 466 | ||
| 467 | if (!conn->ready) | |
| 468 | { | |
| 469 | /* | |
| 470 | * We need to verify the cookie so that we know we are | |
| 471 | * connected to our friend and not a malicious middle man. | |
| 472 | */ | |
| 473 | ||
| 474 | GaimAccount *account; | |
| 475 | GaimConversation *conv; | |
| 476 | ||
| 477 | if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING) | |
| 478 | { | |
| 479 | if (memcmp(conn->cookie, frame->cookie, 8)) | |
| 480 | { | |
| 481 | /* | |
| 482 | * Oh no! The user that connected to us did not send | |
| 483 | * the correct cookie! They are not our friend. Go try | |
| 484 | * to accept another connection? | |
| 485 | */ | |
| 486 | gaim_debug_info("oscar", "Received an incorrect cookie. " | |
| 487 | "Closing connection.\n"); | |
| 488 | peer_connection_destroy(conn, PEER_DISCONNECT_INVALID_DATA); | |
| 489 | g_free(frame); | |
| 490 | return; | |
| 491 | } | |
| 492 | ||
| 493 | /* | |
| 494 | * Ok, we know they are legit. Now be courteous and | |
| 495 | * send them our cookie. Note: This doesn't seem | |
| 496 | * to be necessary, but it also doesn't seem to hurt. | |
| 497 | */ | |
| 498 | peer_odc_send_cookie(conn); | |
| 499 | } | |
| 500 | ||
| 501 | conn->ready = TRUE; | |
| 502 | ||
| 503 | /* | |
| 504 | * If they connected to us then close the listener socket | |
| 505 | * and send them our cookie. | |
| 506 | */ | |
| 507 | if (conn->listenerfd != -1) | |
| 508 | { | |
| 509 | close(conn->listenerfd); | |
| 510 | conn->listenerfd = -1; | |
| 511 | } | |
| 512 | ||
| 513 | /* Tell the local user that we are connected */ | |
| 514 | account = gaim_connection_get_account(gc); | |
| 515 | conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn); | |
| 516 | gaim_conversation_write(conv, NULL, _("Direct IM established"), | |
| 517 | GAIM_MESSAGE_SYSTEM, time(NULL)); | |
| 518 | } | |
| 519 | ||
| 520 | if ((frame->type != 0x0001) && (frame->subtype != 0x0006)) | |
| 521 | { | |
| 522 | gaim_debug_info("oscar", "Unknown ODC frame type 0x%04hx, " | |
| 523 | "subtype 0x%04hx.\n", frame->type, frame->subtype); | |
| 524 | return; | |
| 525 | } | |
| 526 | ||
| 527 | if (frame->flags & 0x0008) | |
| 528 | { | |
| 529 | /* I had to leave this. It's just too funny. It reminds me of my sister. */ | |
| 530 | gaim_debug_info("oscar", "ohmigod! %s has started typing " | |
| 531 | "(DirectIM). He's going to send you a message! " | |
| 532 | "*squeal*\n", conn->sn); | |
| 533 | serv_got_typing(gc, conn->sn, 0, GAIM_TYPING); | |
| 534 | } | |
| 535 | else if (frame->flags & 0x0004) | |
| 536 | { | |
| 537 | serv_got_typing(gc, conn->sn, 0, GAIM_TYPED); | |
| 538 | } | |
| 539 | else | |
| 540 | { | |
| 541 | serv_got_typing_stopped(gc, conn->sn); | |
| 542 | } | |
| 543 | ||
| 544 | if (frame->payload.len > 0) | |
| 545 | { | |
| 546 | /* We have payload data! Switch to the ODC watcher to read it. */ | |
| 547 | frame->payload.data = g_new(guint8, frame->payload.len); | |
| 548 | frame->payload.offset = 0; | |
| 549 | conn->frame = frame; | |
| 550 | gaim_input_remove(conn->watcher_incoming); | |
| 551 | conn->watcher_incoming = gaim_input_add(conn->fd, | |
| 552 | GAIM_INPUT_READ, peer_odc_recv_cb, conn); | |
| 553 | return; | |
| 554 | } | |
| 555 | ||
| 556 | g_free(frame); | |
| 557 | } |