📄 peer.c
字号:
/* * Purple'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*//* * Functions dealing with peer connections. This includes the code * used to establish a peer connection for both Oscar File transfer * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to * as DirectIM and IM Image.) */#ifdef HAVE_CONFIG_H#include <config.h>#endif/* From the oscar PRPL */#include "oscar.h"#include "peer.h"/* From Purple */#include "conversation.h"#include "ft.h"#include "network.h"#include "notify.h"#include "request.h"#include "util.h"#ifndef _WIN32#include <stdio.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h> /* for inet_ntoa */#include <limits.h> /* for UINT_MAX */#endif#ifdef _WIN32#include "win32dep.h"#endif/* * I really want to switch all our networking code to using IPv6 only, * but that really isn't a good idea at all. Evan S. of Adium says * OS X sets all connections as "AF_INET6/PF_INET6," even if there is * nothing inherently IPv6 about them. And I feel like Linux kernel * 2.6.5 is doing the same thing. So we REALLY should accept * connections if they're showing up as IPv6. Old OSes (Solaris?) * that might not have full IPv6 support yet will fail if we try * to use PF_INET6 but it isn't defined. --Mark Doliner */#ifndef PF_INET6#define PF_INET6 PF_INET#endifPeerConnection *peer_connection_find_by_type(OscarData *od, const char *sn, OscarCapability type){ GSList *cur; PeerConnection *conn; for (cur = od->peer_connections; cur != NULL; cur = cur->next) { conn = cur->data; if ((conn->type == type) && !aim_sncmp(conn->sn, sn)) return conn; } return NULL;}/** * @param cookie This must be exactly 8 characters. */PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *sn, const guchar *cookie){ GSList *cur; PeerConnection *conn; for (cur = od->peer_connections; cur != NULL; cur = cur->next) { conn = cur->data; if (!memcmp(conn->cookie, cookie, 8) && !aim_sncmp(conn->sn, sn)) return conn; } return NULL;}PeerConnection *peer_connection_new(OscarData *od, OscarCapability type, const char *sn){ PeerConnection *conn; PurpleAccount *account; account = purple_connection_get_account(od->gc); conn = g_new0(PeerConnection, 1); conn->od = od; conn->type = type; conn->sn = g_strdup(sn); conn->buffer_outgoing = purple_circ_buffer_new(0); conn->listenerfd = -1; conn->fd = -1; conn->lastactivity = time(NULL); conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE); if (type == OSCAR_CAPABILITY_DIRECTIM) memcpy(conn->magic, "ODC2", 4); else if (type == OSCAR_CAPABILITY_SENDFILE) memcpy(conn->magic, "OFT2", 4); od->peer_connections = g_slist_prepend(od->peer_connections, conn); return conn;}static voidpeer_connection_close(PeerConnection *conn){ if (conn->type == OSCAR_CAPABILITY_DIRECTIM) peer_odc_close(conn); else if (conn->type == OSCAR_CAPABILITY_SENDFILE) peer_oft_close(conn); if (conn->verified_connect_data != NULL) { purple_proxy_connect_cancel(conn->verified_connect_data); conn->verified_connect_data = NULL; } if (conn->client_connect_data != NULL) { purple_proxy_connect_cancel(conn->client_connect_data); conn->client_connect_data = NULL; } if (conn->listen_data != NULL) { purple_network_listen_cancel(conn->listen_data); conn->listen_data = NULL; } if (conn->connect_timeout_timer != 0) { purple_timeout_remove(conn->connect_timeout_timer); conn->connect_timeout_timer = 0; } if (conn->watcher_incoming != 0) { purple_input_remove(conn->watcher_incoming); conn->watcher_incoming = 0; } if (conn->watcher_outgoing != 0) { purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; } if (conn->listenerfd >= 0) { close(conn->listenerfd); conn->listenerfd = -1; } if (conn->fd >= 0) { close(conn->fd); conn->fd = -1; } g_free(conn->buffer_incoming.data); conn->buffer_incoming.data = NULL; conn->buffer_incoming.len = 0; conn->buffer_incoming.offset = 0; purple_circ_buffer_destroy(conn->buffer_outgoing); conn->buffer_outgoing = purple_circ_buffer_new(0); conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;}static gbooleanpeer_connection_destroy_cb(gpointer data){ PeerConnection *conn; conn = data; purple_request_close_with_handle(conn); peer_connection_close(conn); if (conn->checksum_data != NULL) peer_oft_checksum_destroy(conn->checksum_data); if (conn->xfer != NULL) { PurpleXferStatusType status; conn->xfer->data = NULL; status = purple_xfer_get_status(conn->xfer); if ((status != PURPLE_XFER_STATUS_DONE) && (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) && (status != PURPLE_XFER_STATUS_CANCEL_REMOTE)) { if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) || (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)) purple_xfer_cancel_remote(conn->xfer); else purple_xfer_cancel_local(conn->xfer); } purple_xfer_unref(conn->xfer); conn->xfer = NULL; } g_free(conn->sn); g_free(conn->error_message); g_free(conn->proxyip); g_free(conn->clientip); g_free(conn->verifiedip); g_free(conn->xferdata.name); purple_circ_buffer_destroy(conn->buffer_outgoing); conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn); g_free(conn); return FALSE;}voidpeer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message){ if (conn->destroy_timeout != 0) purple_timeout_remove(conn->destroy_timeout); conn->disconnect_reason = reason; g_free(conn->error_message); conn->error_message = g_strdup(error_message); peer_connection_destroy_cb(conn);}voidpeer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message){ if (conn->destroy_timeout != 0) /* Already taken care of */ return; purple_debug_info("oscar", "Scheduling destruction of peer connection\n"); conn->disconnect_reason = reason; g_free(conn->error_message); conn->error_message = g_strdup(error_message); conn->destroy_timeout = purple_timeout_add(0, peer_connection_destroy_cb, conn);}/*******************************************************************//* Begin code for receiving data on a peer connection *//*******************************************************************//** * 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. */voidpeer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond){ PeerConnection *conn; ssize_t 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, 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -