/** * Logoff and deallocate a session. * * @param od Session to kill */ void oscar_data_destroy(OscarData *od) { aim_cleansnacs(od, -1); while (od->requesticon) { gchar *sn = od->requesticon->data; od->requesticon = g_slist_remove(od->requesticon, sn); g_free(sn); } g_free(od->email); g_free(od->newp); g_free(od->oldp); if (od->icontimer > 0) gaim_timeout_remove(od->icontimer); if (od->getblisttimer > 0) gaim_timeout_remove(od->getblisttimer); if (od->getinfotimer > 0) gaim_timeout_remove(od->getinfotimer); while (od->oscar_connections != NULL) flap_connection_destroy(od->oscar_connections->data, OSCAR_DISCONNECT_DONE, NULL); while (od->peer_connections != NULL) peer_connection_destroy(od->peer_connections->data, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); aim__shutdownmodules(od); g_hash_table_destroy(od->buddyinfo); g_hash_table_destroy(od->handlerlist); g_free(od); }
/** * Logoff and deallocate a session. * * @param od Session to kill */ void oscar_data_destroy(OscarData *od) { aim_cleansnacs(od, -1); /* Only used when connecting with clientLogin */ if (od->url_data != NULL) purple_util_fetch_url_cancel(od->url_data); while (od->requesticon) { g_free(od->requesticon->data); od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon); } g_free(od->email); g_free(od->newp); g_free(od->oldp); if (od->getblisttimer > 0) purple_timeout_remove(od->getblisttimer); while (od->oscar_connections != NULL) flap_connection_destroy(od->oscar_connections->data, OSCAR_DISCONNECT_DONE, NULL); while (od->peer_connections != NULL) peer_connection_destroy(od->peer_connections->data, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); aim__shutdownmodules(od); g_hash_table_destroy(od->buddyinfo); g_hash_table_destroy(od->handlerlist); g_free(od); }
/** * Someone else wants to establish a peer connection with us, * and we said no. * * "Well, one time my friend asked me if I wanted to play the * piccolo. But I said no." */ static void peer_connection_got_proposition_no_cb(gpointer data, gint id) { PeerConnection *conn; conn = data; aim_im_denytransfer(conn->od, conn->bn, conn->cookie, AIM_TRANSFER_DENY_DECLINE); peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); }
/** * Initiate a peer connection with someone. */ void peer_connection_propose(OscarData *od, guint64 type, const char *bn) { PeerConnection *conn; if (type == OSCAR_CAPABILITY_DIRECTIM) { conn = peer_connection_find_by_type(od, bn, type); if (conn != NULL) { if (conn->ready) { PurpleAccount *account; PurpleConversation *conv; purple_debug_info("oscar", "Already have a direct IM " "session with %s.\n", bn); account = purple_connection_get_account(od->gc); conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bn, account); if (conv != NULL) purple_conversation_present(conv); return; } /* Cancel the old connection and try again */ peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL); } } conn = peer_connection_new(od, type, bn); conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME; conn->flags |= PEER_CONNECTION_FLAG_APPROVED; aim_icbm_makecookie(conn->cookie); peer_connection_trynext(conn); }
/** * Someone else wants to establish a peer connection with us. */ void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args) { PurpleConnection *gc; PurpleAccount *account; PeerConnection *conn; gchar *buf; gc = od->gc; account = purple_connection_get_account(gc); /* * If we have a connection with this same cookie then they are * probably just telling us they weren't able to connect to us * and we should try connecting to them, instead. Or they want * to go through a proxy. */ conn = peer_connection_find_by_cookie(od, bn, args->cookie); if ((conn != NULL) && (conn->type == args->type)) { purple_debug_info("oscar", "Remote user wants to try a " "different connection method\n"); g_free(conn->proxyip); g_free(conn->clientip); g_free(conn->verifiedip); if (args->use_proxy) conn->proxyip = g_strdup(args->proxyip); else conn->proxyip = NULL; conn->verifiedip = g_strdup(args->verifiedip); conn->clientip = g_strdup(args->clientip); conn->port = args->port; conn->use_proxy |= args->use_proxy; conn->lastrequestnumber++; peer_connection_trynext(conn); return; } /* If this is a direct IM, then close any existing session */ if (args->type == OSCAR_CAPABILITY_DIRECTIM) { conn = peer_connection_find_by_type(od, bn, args->type); if (conn != NULL) { /* Close the old direct IM and start a new one */ purple_debug_info("oscar", "Received new direct IM request " "from %s. Destroying old connection.\n", bn); peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); } } /* Check for proper arguments */ if (args->type == OSCAR_CAPABILITY_SENDFILE) { if ((args->info.sendfile.filename == NULL) || (args->info.sendfile.totsize == 0) || (args->info.sendfile.totfiles == 0)) { purple_debug_warning("oscar", "%s tried to send you a file with incomplete " "information.\n", bn); return; } } conn = peer_connection_new(od, args->type, bn); memcpy(conn->cookie, args->cookie, 8); if (args->use_proxy) conn->proxyip = g_strdup(args->proxyip); conn->clientip = g_strdup(args->clientip); conn->verifiedip = g_strdup(args->verifiedip); conn->port = args->port; conn->use_proxy |= args->use_proxy; conn->lastrequestnumber++; if (args->type == OSCAR_CAPABILITY_DIRECTIM) { buf = g_strdup_printf(_("%s has just asked to directly connect to %s"), bn, purple_account_get_username(account)); purple_request_action(conn, NULL, buf, _("This requires a direct connection between " "the two computers and is necessary for IM " "Images. Because your IP address will be " "revealed, this may be considered a privacy " "risk."), PURPLE_DEFAULT_ACTION_NONE, account, bn, NULL, conn, 2, _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb), _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb)); } else if (args->type == OSCAR_CAPABILITY_SENDFILE) { gchar *filename; conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn); if (conn->xfer) { conn->xfer->data = conn; purple_xfer_ref(conn->xfer); purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize); /* Set the file name */ if (g_utf8_validate(args->info.sendfile.filename, -1, NULL)) filename = g_strdup(args->info.sendfile.filename); else filename = purple_utf8_salvage(args->info.sendfile.filename); if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) { /* * If they are sending us a directory then the last character * of the file name will be an asterisk. We don't want to * save stuff to a directory named "*" so we remove the * asterisk from the file name. */ char *tmp = strrchr(filename, '\\'); if ((tmp != NULL) && (tmp[1] == '*')) tmp[0] = '\0'; } purple_xfer_set_filename(conn->xfer, filename); g_free(filename); /* * Set the message, unless this is the dummy message from an * ICQ client or an empty message from an AIM client. * TODO: Maybe we should strip HTML and then see if strlen>0? */ if ((message != NULL) && (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) && (g_ascii_strcasecmp(message, "<HTML>") != 0)) { purple_xfer_set_message(conn->xfer, message); } /* Setup our I/O op functions */ purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init); purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end); purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel); purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel); purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv); /* Now perform the request */ purple_xfer_request(conn->xfer); } } }
/** * Try to establish the given PeerConnection using a defined * sequence of steps. */ void peer_connection_trynext(PeerConnection *conn) { PurpleAccount *account; account = purple_connection_get_account(conn->od->gc); /* * Close any remnants of a previous failed connection attempt. */ peer_connection_close(conn); /* * 1. Attempt to connect to the remote user using their verifiedip and clientip. * We try these at the same time and use whichever succeeds first, so we don't * have to wait for a timeout. */ if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) && (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy)) { conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT; if (conn->type == OSCAR_CAPABILITY_DIRECTIM) { gchar *tmp; PurpleConversation *conv; tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."), conn->verifiedip, conn->port); conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn); purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); g_free(tmp); } conn->verified_connect_data = purple_proxy_connect(NULL, account, conn->verifiedip, conn->port, peer_connection_verified_established_cb, conn); if ((conn->verifiedip == NULL) || strcmp(conn->verifiedip, conn->clientip)) { conn->client_connect_data = purple_proxy_connect(NULL, account, conn->clientip, conn->port, peer_connection_client_established_cb, conn); } if ((conn->verified_connect_data != NULL) || (conn->client_connect_data != NULL)) { /* Connecting... */ conn->connect_timeout_timer = purple_timeout_add_seconds(5, peer_connection_tooktoolong, conn); return; } } /* * 2. Attempt to have the remote user connect to us (using both * our verifiedip and our clientip). */ if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) && (!conn->use_proxy)) { conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING; /* * Remote user is connecting to us, so we'll need to verify * that the user who connected is our friend. */ conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING; conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM, peer_connection_establish_listener_cb, conn); if (conn->listen_data != NULL) { /* Opening listener socket... */ return; } } /* * 3. Attempt to have both users connect to an intermediate proxy * server. */ if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY)) { conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY; /* * If we initiate the proxy connection, then the remote user * could be anyone, so we need to verify that the user who * connected is our friend. */ if (!conn->use_proxy) conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING; if (conn->type == OSCAR_CAPABILITY_DIRECTIM) { gchar *tmp; PurpleConversation *conv; tmp = g_strdup(_("Attempting to connect via proxy server.")); conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn); purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); g_free(tmp); } conn->verified_connect_data = purple_proxy_connect(NULL, account, (conn->proxyip != NULL) ? conn->proxyip : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER), PEER_PROXY_PORT, peer_proxy_connection_established_cb, conn); if (conn->verified_connect_data != NULL) { /* Connecting... */ return; } } /* Give up! */ peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL); }
/** * 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; }