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.
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.
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.
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
22 * Functions dealing with peer connections. This includes the code
23 * used to establish a peer connection for both Oscar File transfer
24 * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to
25 * as DirectIM and IM Image.)
32 /* From the oscar PRPL */
37 #include "conversation.h"
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h> /* for inet_ntoa */
50 #include <limits.h> /* for UINT_MAX */
54 * I really want to switch all our networking code to using IPv6 only,
55 * but that really isn't a good idea at all. Evan S. of Adium says
56 * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
57 * nothing inherently IPv6 about them. And I feel like Linux kernel
58 * 2.6.5 is doing the same thing. So we REALLY should accept
59 * connections if they're showing up as IPv6. Old OSes (Solaris?)
60 * that might not have full IPv6 support yet will fail if we try
61 * to use PF_INET6 but it isn't defined. --Mark Doliner
64 #define PF_INET6 PF_INET
68 peer_connection_find_by_type(OscarData
*od
, const char *bn
, guint64 type
)
73 for (cur
= od
->peer_connections
; cur
!= NULL
; cur
= cur
->next
)
76 if ((conn
->type
== type
) && !oscar_util_name_compare(conn
->bn
, bn
))
84 * @param cookie This must be exactly 8 characters.
87 peer_connection_find_by_cookie(OscarData
*od
, const char *bn
, const guchar
*cookie
)
92 for (cur
= od
->peer_connections
; cur
!= NULL
; cur
= cur
->next
)
95 if (!memcmp(conn
->cookie
, cookie
, 8) && !oscar_util_name_compare(conn
->bn
, bn
))
103 peer_connection_new(OscarData
*od
, guint64 type
, const char *bn
)
105 PeerConnection
*conn
;
106 PurpleAccount
*account
;
108 account
= purple_connection_get_account(od
->gc
);
110 conn
= g_new0(PeerConnection
, 1);
113 conn
->bn
= g_strdup(bn
);
114 conn
->buffer_outgoing
= purple_circular_buffer_new(0);
115 conn
->listenerfd
= -1;
117 conn
->lastactivity
= time(NULL
);
118 conn
->use_proxy
|= purple_account_get_bool(account
, "always_use_rv_proxy", FALSE
);
120 if (type
== OSCAR_CAPABILITY_DIRECTIM
)
121 memcpy(conn
->magic
, "ODC2", 4);
122 else if (type
== OSCAR_CAPABILITY_SENDFILE
)
123 memcpy(conn
->magic
, "OFT2", 4);
125 od
->peer_connections
= g_slist_prepend(od
->peer_connections
, conn
);
131 peer_connection_close(PeerConnection
*conn
)
133 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
134 peer_odc_close(conn
);
135 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
136 peer_oft_close(conn
);
138 if (conn
->verified_connect_data
!= NULL
)
140 purple_proxy_connect_cancel(conn
->verified_connect_data
);
141 conn
->verified_connect_data
= NULL
;
144 if (conn
->client_connect_data
!= NULL
)
146 purple_proxy_connect_cancel(conn
->client_connect_data
);
147 conn
->client_connect_data
= NULL
;
150 if (conn
->listen_data
!= NULL
)
152 purple_network_listen_cancel(conn
->listen_data
);
153 conn
->listen_data
= NULL
;
156 if (conn
->connect_timeout_timer
!= 0)
158 g_source_remove(conn
->connect_timeout_timer
);
159 conn
->connect_timeout_timer
= 0;
162 if (conn
->watcher_incoming
!= 0)
164 purple_input_remove(conn
->watcher_incoming
);
165 conn
->watcher_incoming
= 0;
167 if (conn
->watcher_outgoing
!= 0)
169 purple_input_remove(conn
->watcher_outgoing
);
170 conn
->watcher_outgoing
= 0;
172 if (conn
->listenerfd
>= 0)
174 close(conn
->listenerfd
);
175 conn
->listenerfd
= -1;
183 g_free(conn
->buffer_incoming
.data
);
184 conn
->buffer_incoming
.data
= NULL
;
185 conn
->buffer_incoming
.len
= 0;
186 conn
->buffer_incoming
.offset
= 0;
188 g_object_unref(G_OBJECT(conn
->buffer_outgoing
));
189 conn
->buffer_outgoing
= purple_circular_buffer_new(0);
191 conn
->flags
&= ~PEER_CONNECTION_FLAG_IS_INCOMING
;
195 peer_connection_destroy_cb(gpointer data
)
197 PeerConnection
*conn
;
201 purple_request_close_with_handle(conn
);
203 peer_connection_close(conn
);
205 if (conn
->checksum_data
!= NULL
)
206 peer_oft_checksum_destroy(conn
->checksum_data
);
208 if (conn
->xfer
!= NULL
)
210 PurpleXferStatus status
;
211 status
= purple_xfer_get_status(conn
->xfer
);
212 if ((status
!= PURPLE_XFER_STATUS_DONE
) &&
213 (status
!= PURPLE_XFER_STATUS_CANCEL_LOCAL
) &&
214 (status
!= PURPLE_XFER_STATUS_CANCEL_REMOTE
))
216 if ((conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_CLOSED
) ||
217 (conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_REFUSED
))
218 purple_xfer_cancel_remote(conn
->xfer
);
220 purple_xfer_cancel_local(conn
->xfer
);
222 g_object_unref(conn
->xfer
);
227 g_free(conn
->error_message
);
228 g_free(conn
->proxyip
);
229 g_free(conn
->clientip
);
230 g_free(conn
->verifiedip
);
231 g_free(conn
->xferdata
.name
);
232 g_object_unref(G_OBJECT(conn
->buffer_outgoing
));
234 conn
->od
->peer_connections
= g_slist_remove(conn
->od
->peer_connections
, conn
);
242 peer_connection_destroy(PeerConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
244 if (conn
->destroy_timeout
!= 0)
245 g_source_remove(conn
->destroy_timeout
);
246 conn
->disconnect_reason
= reason
;
247 g_free(conn
->error_message
);
248 conn
->error_message
= g_strdup(error_message
);
249 peer_connection_destroy_cb(conn
);
253 peer_connection_schedule_destroy(PeerConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
255 if (conn
->destroy_timeout
!= 0)
256 /* Already taken care of */
259 purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
260 conn
->disconnect_reason
= reason
;
261 g_free(conn
->error_message
);
262 conn
->error_message
= g_strdup(error_message
);
263 conn
->destroy_timeout
= g_timeout_add(0, peer_connection_destroy_cb
, conn
);
266 /*******************************************************************/
267 /* Begin code for receiving data on a peer connection */
268 /*******************************************************************/
271 * This should be used to read ODC and OFT framing info. It should
272 * NOT be used to read the payload sent across the connection (IMs,
273 * file data, etc), and it should NOT be used to read proxy negotiation
276 * Unlike flap_connection_recv_cb(), this only reads one frame at a
277 * time. This is done so that the watcher can be changed during the
278 * handling of the frame. If the watcher is changed then this
279 * function will not read in any more data. This happens when
280 * reading the payload of a direct IM frame, or when we're
281 * receiving a file from the remote user. Once the data has been
282 * read, the watcher will be switched back to this function to
283 * continue reading the next frame.
286 peer_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
288 PeerConnection
*conn
;
293 /* Start reading a new ODC/OFT frame */
294 if (conn
->buffer_incoming
.data
== NULL
)
296 /* Read the first 6 bytes (magic string and frame length) */
297 read
= recv(conn
->fd
, conn
->header
+ conn
->header_received
,
298 6 - conn
->header_received
, 0);
300 /* Check if the remote user closed the connection */
303 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
307 /* If there was an error then close the connection */
310 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
314 peer_connection_destroy(conn
,
315 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
319 conn
->lastactivity
= time(NULL
);
321 /* If we don't even have the first 6 bytes then do nothing */
322 conn
->header_received
+= read
;
323 if (conn
->header_received
< 6)
326 /* All ODC/OFT frames must start with a magic string */
327 if (memcmp(conn
->magic
, conn
->header
, 4))
329 purple_debug_warning("oscar", "Expecting magic string to "
330 "be %c%c%c%c but received magic string %c%c%c%c. "
331 "Closing connection.\n",
332 conn
->magic
[0], conn
->magic
[1], conn
->magic
[2],
333 conn
->magic
[3], conn
->header
[0], conn
->header
[1],
334 conn
->header
[2], conn
->header
[3]);
335 peer_connection_destroy(conn
, OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
339 /* Initialize a new temporary ByteStream for incoming data */
340 conn
->buffer_incoming
.len
= aimutil_get16(&conn
->header
[4]) - 6;
341 conn
->buffer_incoming
.data
= g_new(guint8
, conn
->buffer_incoming
.len
);
342 conn
->buffer_incoming
.offset
= 0;
345 /* Read data into the temporary buffer until it is complete */
346 read
= recv(conn
->fd
,
347 &conn
->buffer_incoming
.data
[conn
->buffer_incoming
.offset
],
348 conn
->buffer_incoming
.len
- conn
->buffer_incoming
.offset
,
351 /* Check if the remote user closed the connection */
354 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
360 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
364 peer_connection_destroy(conn
,
365 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
369 conn
->lastactivity
= time(NULL
);
370 conn
->buffer_incoming
.offset
+= read
;
371 if (conn
->buffer_incoming
.offset
< conn
->buffer_incoming
.len
)
372 /* Waiting for more data to arrive */
375 /* We have a complete ODC/OFT frame! Handle it and continue reading */
376 byte_stream_rewind(&conn
->buffer_incoming
);
377 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
379 peer_odc_recv_frame(conn
, &conn
->buffer_incoming
);
381 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
383 peer_oft_recv_frame(conn
, &conn
->buffer_incoming
);
386 g_free(conn
->buffer_incoming
.data
);
387 conn
->buffer_incoming
.data
= NULL
;
389 conn
->header_received
= 0;
392 /*******************************************************************/
393 /* End code for receiving data on a peer connection */
394 /*******************************************************************/
396 /*******************************************************************/
397 /* Begin code for sending data on a peer connection */
398 /*******************************************************************/
401 send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
403 PeerConnection
*conn
;
406 const gchar
*output
= NULL
;
409 writelen
= purple_circular_buffer_get_max_read(conn
->buffer_outgoing
);
413 purple_input_remove(conn
->watcher_outgoing
);
414 conn
->watcher_outgoing
= 0;
416 * The buffer is currently empty, so reset the current input
417 * and output positions to the start of the buffer. We do
418 * this so that the next chunk of data that we put into the
419 * buffer can be read back out of the buffer in one fell swoop.
420 * Otherwise it gets fragmented and we have to read from the
421 * second half of the buffer than go back and read the rest of
422 * the chunk from the first half.
424 * We're using TCP, which is a stream based protocol, so this
425 * isn't supposed to matter. However, experience has shown
426 * that at least the proxy file transfer code in AIM 6.1.41.2
427 * requires that the entire OFT frame arrive all at once. If
428 * the frame is fragmented then AIM freaks out and aborts the
429 * file transfer. Somebody should teach those guys how to
430 * write good TCP code.
432 purple_circular_buffer_reset(conn
->buffer_outgoing
);
436 output
= purple_circular_buffer_get_output(conn
->buffer_outgoing
);
438 wrotelen
= send(conn
->fd
, output
, writelen
, 0);
441 if (wrotelen
< 0 && ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
)))
447 purple_input_remove(conn
->watcher_outgoing
);
448 conn
->watcher_outgoing
= 0;
451 peer_connection_schedule_destroy(conn
,
452 OSCAR_DISCONNECT_LOST_CONNECTION
, NULL
);
457 * This could happen when unable to send a negotiation
458 * frame to a peer proxy server.
460 peer_connection_trynext(conn
);
465 purple_circular_buffer_mark_read(conn
->buffer_outgoing
, wrotelen
);
466 conn
->lastactivity
= time(NULL
);
470 * This should be called by OFT/ODC code to send a standard OFT or ODC
471 * frame across the peer connection along with some payload data. Or
472 * maybe a file. Anything, really.
475 peer_connection_send(PeerConnection
*conn
, ByteStream
*bs
)
477 /* Add everything to our outgoing buffer */
478 purple_circular_buffer_append(conn
->buffer_outgoing
, bs
->data
, bs
->len
);
480 /* If we haven't already started writing stuff, then start the cycle */
481 if ((conn
->watcher_outgoing
== 0) && (conn
->fd
>= 0))
483 conn
->watcher_outgoing
= purple_input_add(conn
->fd
,
484 PURPLE_INPUT_WRITE
, send_cb
, conn
);
485 send_cb(conn
, conn
->fd
, 0);
489 /*******************************************************************/
490 /* End code for sending data on a peer connection */
491 /*******************************************************************/
493 /*******************************************************************/
494 /* Begin code for establishing a peer connection */
495 /*******************************************************************/
498 peer_connection_finalize_connection(PeerConnection
*conn
)
500 conn
->watcher_incoming
= purple_input_add(conn
->fd
,
501 PURPLE_INPUT_READ
, peer_connection_recv_cb
, conn
);
503 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
506 * If we are connecting to them then send our cookie so they
507 * can verify who we are. Note: This doesn't seem to be
508 * necessary, but it also doesn't seem to hurt.
510 if (!(conn
->flags
& PEER_CONNECTION_FLAG_IS_INCOMING
))
511 peer_odc_send_cookie(conn
);
513 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
515 if (purple_xfer_get_xfer_type(conn
->xfer
) == PURPLE_XFER_TYPE_SEND
)
517 peer_oft_send_prompt(conn
);
522 * Tell the remote user that we're connected (which may also imply
523 * that we've accepted their request).
525 if (!(conn
->flags
& PEER_CONNECTION_FLAG_IS_INCOMING
))
526 aim_im_sendch2_connected(conn
);
530 * We tried to make an outgoing connection to a remote user. It
531 * either connected or failed to connect.
534 peer_connection_common_established_cb(gpointer data
, gint source
, const gchar
*error_message
, gboolean verified
)
536 PeerConnection
*conn
;
541 conn
->verified_connect_data
= NULL
;
543 conn
->client_connect_data
= NULL
;
547 if ((conn
->verified_connect_data
== NULL
) &&
548 (conn
->client_connect_data
== NULL
))
550 /* Our parallel connection attemps have both failed. */
551 peer_connection_trynext(conn
);
556 g_source_remove(conn
->connect_timeout_timer
);
557 conn
->connect_timeout_timer
= 0;
559 if (conn
->client_connect_data
!= NULL
)
561 purple_proxy_connect_cancel(conn
->client_connect_data
);
562 conn
->client_connect_data
= NULL
;
565 if (conn
->verified_connect_data
!= NULL
)
567 purple_proxy_connect_cancel(conn
->verified_connect_data
);
568 conn
->verified_connect_data
= NULL
;
573 peer_connection_finalize_connection(conn
);
577 peer_connection_verified_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
579 peer_connection_common_established_cb(data
, source
, error_message
, TRUE
);
583 peer_connection_client_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
585 peer_connection_common_established_cb(data
, source
, error_message
, FALSE
);
589 * This is the watcher callback for any listening socket that is
590 * waiting for a peer to connect. When a peer connects we set the
591 * input watcher to start reading data from the peer.
593 * To make sure that the connection is with the intended person and
594 * not with a malicious middle man, we don't send anything until we've
595 * received a peer frame from the remote user and have verified that
596 * the cookie in the peer frame matches the cookie that was exchanged
597 * in the channel 2 ICBM.
600 peer_connection_listen_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
602 PeerConnection
*conn
;
603 struct sockaddr addr
;
604 socklen_t addrlen
= sizeof(addr
);
608 purple_debug_info("oscar", "Accepting connection on listener socket.\n");
610 conn
->fd
= accept(conn
->listenerfd
, &addr
, &addrlen
);
613 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
614 /* No connection yet--no worries */
615 /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
618 peer_connection_trynext(conn
);
622 if ((addr
.sa_family
!= PF_INET
) && (addr
.sa_family
!= PF_INET6
))
624 /* Invalid connection type?! Continue waiting. */
629 _purple_network_set_common_socket_flags(conn
->fd
);
631 purple_input_remove(conn
->watcher_incoming
);
633 peer_connection_finalize_connection(conn
);
637 * Converts a dot-decimal IP address to an array of unsigned
638 * chars. For example, converts 192.168.0.1 to a 4 byte
639 * array containing 192, 168, 0 and 1.
641 * @param ip An IP address in dot-decimal notiation.
642 * @return An array of 4 bytes containing an IP addresses
643 * equivalent to the given parameter, or NULL if
644 * the given IP address is invalid. This value
645 * is statically allocated and should not be
648 static const unsigned char *
649 peer_ip_atoi(const char *ip
)
651 static unsigned char ret
[4];
652 gchar
*delimiter
= ".";
656 g_return_val_if_fail(ip
!= NULL
, NULL
);
658 split
= g_strsplit(ip
, delimiter
, 4);
659 for (i
= 0; split
[i
] != NULL
; i
++)
660 ret
[i
] = atoi(split
[i
]);
663 /* i should always be 4 */
671 * We've just opened a listener socket, so we send the remote
672 * user an ICBM and ask them to connect to us.
675 peer_connection_establish_listener_cb(int listenerfd
, gpointer data
)
677 PeerConnection
*conn
;
679 PurpleConnection
*gc
;
680 PurpleAccount
*account
;
681 PurpleIMConversation
*im
;
683 FlapConnection
*bos_conn
;
684 const char *listener_ip
;
685 const guchar
*ip_atoi
;
686 unsigned short listener_port
;
689 conn
->listen_data
= NULL
;
693 /* Could not open listener socket */
694 peer_connection_trynext(conn
);
700 account
= purple_connection_get_account(gc
);
701 conn
->listenerfd
= listenerfd
;
703 /* Watch for new connections on our listener socket */
704 conn
->watcher_incoming
= purple_input_add(conn
->listenerfd
,
705 PURPLE_INPUT_READ
, peer_connection_listen_cb
, conn
);
707 /* Send the "please connect to me!" ICBM */
708 bos_conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
709 if (bos_conn
== NULL
)
712 peer_connection_trynext(conn
);
717 listener_ip
= purple_network_get_my_ip(bos_conn
->gsc
->fd
);
719 listener_ip
= purple_network_get_my_ip(bos_conn
->fd
);
721 ip_atoi
= peer_ip_atoi(listener_ip
);
722 if (ip_atoi
== NULL
) {
723 /* Could not convert IP to 4 byte array--weird, but this does
724 happen for some users (#4829, Adium #15839). Maybe they're
725 connecting with IPv6...? Maybe through a proxy? */
726 purple_debug_error("oscar", "Can't ask peer to connect to us "
727 "because peer_ip_atoi(%s) returned NULL. "
728 "fd=%d. is_ssl=%d\n",
729 listener_ip
? listener_ip
: "(null)",
730 bos_conn
->gsc
? bos_conn
->gsc
->fd
: bos_conn
->fd
,
731 bos_conn
->gsc
? 1 : 0);
732 peer_connection_trynext(conn
);
736 listener_port
= purple_network_get_port_from_fd(conn
->listenerfd
);
738 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
740 aim_im_sendch2_odc_requestdirect(od
,
741 conn
->cookie
, conn
->bn
, ip_atoi
,
742 listener_port
, ++conn
->lastrequestnumber
);
744 /* Print a message to a local conversation window */
745 im
= purple_im_conversation_new(account
, conn
->bn
);
746 tmp
= g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
747 "Direct IM."), conn
->bn
, listener_ip
, listener_port
);
748 purple_conversation_write_system_message(
749 PURPLE_CONVERSATION(im
), tmp
, 0);
752 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
754 aim_im_sendch2_sendfile_requestdirect(od
,
755 conn
->cookie
, conn
->bn
,
757 listener_port
, ++conn
->lastrequestnumber
,
758 (const gchar
*)conn
->xferdata
.name
,
759 conn
->xferdata
.size
, conn
->xferdata
.totfiles
);
764 * This is a callback function used when we're connecting to a peer
765 * using either the client IP or the verified IP and the connection
766 * took longer than 5 seconds to complete. We do this because
767 * waiting for the OS to time out the connection attempt is not
768 * practical--the default timeout on many OSes can be 3 minutes or
769 * more, and users are impatient.
771 * Worst case scenario: the user is connected to the Internet using
772 * a modem with severe lag. The peer connections fail and Purple falls
773 * back to using a proxied connection. The lower bandwidth
774 * limitations imposed by the proxied connection won't matter because
775 * the user is using a modem.
777 * I suppose this line of thinking is discriminatory against people
778 * with very high lag but decent throughput who are transferring
779 * large files. But we don't care about those people.
781 * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
782 * too long for a user to wait to send a file. I'm also parallelizing
783 * requests when possible. The longest we should have to wait now is 10
784 * seconds. We shouldn't make it shorter than this.
787 peer_connection_tooktoolong(gpointer data
)
789 PeerConnection
*conn
;
793 purple_debug_info("oscar", "Peer connection timed out after 5 seconds. "
794 "Trying next method...\n");
796 peer_connection_trynext(conn
);
798 /* Cancel this timer. It'll be added again, if needed. */
803 * Try to establish the given PeerConnection using a defined
807 peer_connection_trynext(PeerConnection
*conn
)
809 PurpleAccount
*account
;
811 account
= purple_connection_get_account(conn
->od
->gc
);
814 * Close any remnants of a previous failed connection attempt.
816 peer_connection_close(conn
);
819 * 1. Attempt to connect to the remote user using their verifiedip and clientip.
820 * We try these at the same time and use whichever succeeds first, so we don't
821 * have to wait for a timeout.
823 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_DIRECT
) &&
824 (conn
->verifiedip
!= NULL
) && (conn
->port
!= 0) && (!conn
->use_proxy
))
826 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_DIRECT
;
828 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
831 PurpleIMConversation
*im
;
832 tmp
= g_strdup_printf(_("Attempting to connect to %s:%hu."),
833 conn
->verifiedip
, conn
->port
);
834 im
= purple_im_conversation_new(account
, conn
->bn
);
835 purple_conversation_write_system_message(
836 PURPLE_CONVERSATION(im
), tmp
, 0);
840 conn
->verified_connect_data
= purple_proxy_connect(NULL
, account
,
841 conn
->verifiedip
, conn
->port
,
842 peer_connection_verified_established_cb
, conn
);
844 if ((conn
->verifiedip
== NULL
) ||
845 !purple_strequal(conn
->verifiedip
, conn
->clientip
))
847 conn
->client_connect_data
= purple_proxy_connect(NULL
, account
,
848 conn
->clientip
, conn
->port
,
849 peer_connection_client_established_cb
, conn
);
852 if ((conn
->verified_connect_data
!= NULL
) ||
853 (conn
->client_connect_data
!= NULL
))
856 conn
->connect_timeout_timer
= g_timeout_add_seconds(5,
857 peer_connection_tooktoolong
, conn
);
863 * 2. Attempt to have the remote user connect to us (using both
864 * our verifiedip and our clientip).
866 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_INCOMING
) &&
869 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_INCOMING
;
872 * Remote user is connecting to us, so we'll need to verify
873 * that the user who connected is our friend.
875 conn
->flags
|= PEER_CONNECTION_FLAG_IS_INCOMING
;
877 conn
->listen_data
= purple_network_listen_range(5190, 5290, AF_UNSPEC
, SOCK_STREAM
, TRUE
,
878 peer_connection_establish_listener_cb
, conn
);
879 if (conn
->listen_data
!= NULL
)
881 /* Opening listener socket... */
887 * 3. Attempt to have both users connect to an intermediate proxy
890 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_PROXY
))
892 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_PROXY
;
895 * If we initiate the proxy connection, then the remote user
896 * could be anyone, so we need to verify that the user who
897 * connected is our friend.
899 if (!conn
->use_proxy
)
900 conn
->flags
|= PEER_CONNECTION_FLAG_IS_INCOMING
;
902 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
905 PurpleIMConversation
*im
;
906 tmp
= g_strdup(_("Attempting to connect via proxy server."));
907 im
= purple_im_conversation_new(account
, conn
->bn
);
908 purple_conversation_write_system_message(
909 PURPLE_CONVERSATION(im
), tmp
, 0);
913 conn
->verified_connect_data
= purple_proxy_connect(NULL
, account
,
914 (conn
->proxyip
!= NULL
)
916 : (conn
->od
->icq
? ICQ_PEER_PROXY_SERVER
: AIM_PEER_PROXY_SERVER
),
918 peer_proxy_connection_established_cb
, conn
);
919 if (conn
->verified_connect_data
!= NULL
)
927 peer_connection_destroy(conn
, OSCAR_DISCONNECT_COULD_NOT_CONNECT
, NULL
);
931 * Initiate a peer connection with someone.
934 peer_connection_propose(OscarData
*od
, guint64 type
, const char *bn
)
936 PeerConnection
*conn
;
938 if (type
== OSCAR_CAPABILITY_DIRECTIM
)
940 conn
= peer_connection_find_by_type(od
, bn
, type
);
945 PurpleAccount
*account
;
946 PurpleIMConversation
*im
;
948 purple_debug_info("oscar", "Already have a direct IM "
949 "session with %s.\n", bn
);
950 account
= purple_connection_get_account(od
->gc
);
951 im
= purple_conversations_find_im_with_account(bn
, account
);
953 purple_conversation_present(PURPLE_CONVERSATION(im
));
957 /* Cancel the old connection and try again */
958 peer_connection_destroy(conn
, OSCAR_DISCONNECT_RETRYING
, NULL
);
962 conn
= peer_connection_new(od
, type
, bn
);
963 conn
->flags
|= PEER_CONNECTION_FLAG_INITIATED_BY_ME
;
964 conn
->flags
|= PEER_CONNECTION_FLAG_APPROVED
;
965 aim_icbm_makecookie(conn
->cookie
);
967 peer_connection_trynext(conn
);
971 * Someone else wants to establish a peer connection with us,
975 peer_connection_got_proposition_yes_cb(gpointer data
, gint id
)
977 PeerConnection
*conn
;
981 conn
->flags
|= PEER_CONNECTION_FLAG_APPROVED
;
982 peer_connection_trynext(conn
);
986 * Someone else wants to establish a peer connection with us,
989 * "Well, one time my friend asked me if I wanted to play the
990 * piccolo. But I said no."
993 peer_connection_got_proposition_no_cb(gpointer data
, gint id
)
995 PeerConnection
*conn
;
999 aim_im_denytransfer(conn
->od
, conn
->bn
, conn
->cookie
,
1000 AIM_TRANSFER_DENY_DECLINE
);
1001 peer_connection_destroy(conn
, OSCAR_DISCONNECT_LOCAL_CLOSED
, NULL
);
1005 * Someone else wants to establish a peer connection with us.
1008 peer_connection_got_proposition(OscarData
*od
, const gchar
*bn
, const gchar
*message
, IcbmArgsCh2
*args
)
1010 PurpleConnection
*gc
;
1011 PurpleAccount
*account
;
1012 PeerConnection
*conn
;
1016 account
= purple_connection_get_account(gc
);
1019 * If we have a connection with this same cookie then they are
1020 * probably just telling us they weren't able to connect to us
1021 * and we should try connecting to them, instead. Or they want
1022 * to go through a proxy.
1024 conn
= peer_connection_find_by_cookie(od
, bn
, args
->cookie
);
1025 if ((conn
!= NULL
) && (conn
->type
== args
->type
))
1027 purple_debug_info("oscar", "Remote user wants to try a "
1028 "different connection method\n");
1029 g_free(conn
->proxyip
);
1030 g_free(conn
->clientip
);
1031 g_free(conn
->verifiedip
);
1032 if (args
->use_proxy
)
1033 conn
->proxyip
= g_strdup(args
->proxyip
);
1035 conn
->proxyip
= NULL
;
1036 conn
->verifiedip
= g_strdup(args
->verifiedip
);
1037 conn
->clientip
= g_strdup(args
->clientip
);
1038 conn
->port
= args
->port
;
1039 conn
->use_proxy
|= args
->use_proxy
;
1040 conn
->lastrequestnumber
++;
1041 peer_connection_trynext(conn
);
1045 /* If this is a direct IM, then close any existing session */
1046 if (args
->type
== OSCAR_CAPABILITY_DIRECTIM
)
1048 conn
= peer_connection_find_by_type(od
, bn
, args
->type
);
1051 /* Close the old direct IM and start a new one */
1052 purple_debug_info("oscar", "Received new direct IM request "
1053 "from %s. Destroying old connection.\n", bn
);
1054 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
1058 /* Check for proper arguments */
1059 if (args
->type
== OSCAR_CAPABILITY_SENDFILE
)
1061 if ((args
->info
.sendfile
.filename
== NULL
) ||
1062 (args
->info
.sendfile
.totsize
== 0) ||
1063 (args
->info
.sendfile
.totfiles
== 0))
1065 purple_debug_warning("oscar",
1066 "%s tried to send you a file with incomplete "
1067 "information.\n", bn
);
1072 conn
= peer_connection_new(od
, args
->type
, bn
);
1073 memcpy(conn
->cookie
, args
->cookie
, 8);
1074 if (args
->use_proxy
)
1075 conn
->proxyip
= g_strdup(args
->proxyip
);
1076 conn
->clientip
= g_strdup(args
->clientip
);
1077 conn
->verifiedip
= g_strdup(args
->verifiedip
);
1078 conn
->port
= args
->port
;
1079 conn
->use_proxy
|= args
->use_proxy
;
1080 conn
->lastrequestnumber
++;
1082 if (args
->type
== OSCAR_CAPABILITY_DIRECTIM
)
1084 buf
= g_strdup_printf(_("%s has just asked to directly connect to %s"),
1085 bn
, purple_account_get_username(account
));
1087 purple_request_action(conn
, NULL
, buf
,
1088 _("This requires a direct connection between "
1089 "the two computers and is necessary for IM "
1090 "Images. Because your IP address will be "
1091 "revealed, this may be considered a privacy "
1093 PURPLE_DEFAULT_ACTION_NONE
,
1094 purple_request_cpar_from_account(account
),
1096 _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb
),
1097 _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb
));
1099 else if (args
->type
== OSCAR_CAPABILITY_SENDFILE
)
1103 conn
->xfer
= PURPLE_XFER(g_object_new(
1106 "type", PURPLE_XFER_TYPE_RECEIVE
,
1111 purple_xfer_set_size(conn
->xfer
, args
->info
.sendfile
.totsize
);
1113 /* Set the file name */
1114 if (g_utf8_validate(args
->info
.sendfile
.filename
, -1, NULL
))
1115 filename
= g_strdup(args
->info
.sendfile
.filename
);
1117 filename
= purple_utf8_salvage(args
->info
.sendfile
.filename
);
1119 if (args
->info
.sendfile
.subtype
== AIM_OFT_SUBTYPE_SEND_DIR
)
1122 * If they are sending us a directory then the last character
1123 * of the file name will be an asterisk. We don't want to
1124 * save stuff to a directory named "*" so we remove the
1125 * asterisk from the file name.
1127 char *tmp
= strrchr(filename
, '\\');
1128 if ((tmp
!= NULL
) && (tmp
[1] == '*'))
1131 purple_xfer_set_filename(conn
->xfer
, filename
);
1135 * Set the message, unless this is the dummy message from an
1136 * ICQ client or an empty message from an AIM client.
1137 * TODO: Maybe we should strip HTML and then see if strlen>0?
1139 if ((message
!= NULL
) &&
1140 (g_ascii_strncasecmp(message
, "<ICQ_COOL_FT>", 13) != 0) &&
1141 (g_ascii_strcasecmp(message
, "<HTML>") != 0))
1143 purple_xfer_set_message(conn
->xfer
, message
);
1146 /* Now perform the request */
1147 purple_xfer_request(conn
->xfer
);
1151 /*******************************************************************/
1152 /* End code for establishing a peer connection */
1153 /*******************************************************************/
1155 G_DEFINE_DYNAMIC_TYPE(OscarXfer
, oscar_xfer
, PURPLE_TYPE_XFER
);
1158 oscar_xfer_init_xfer(PurpleXfer
*xfer
) {
1159 PurpleXferType type
= purple_xfer_get_xfer_type(xfer
);
1161 if(type
== PURPLE_XFER_TYPE_SEND
) {
1162 peer_oft_sendcb_init(xfer
);
1163 } else if(type
== PURPLE_XFER_TYPE_RECEIVE
) {
1164 peer_oft_recvcb_init(xfer
);
1169 oscar_xfer_ack(PurpleXfer
*xfer
, const guchar
*buffer
, size_t size
) {
1170 PurpleXferType type
= purple_xfer_get_xfer_type(xfer
);
1172 if(type
== PURPLE_XFER_TYPE_SEND
) {
1173 peer_oft_sendcb_ack(xfer
, buffer
, size
);
1174 } else if(type
== PURPLE_XFER_TYPE_RECEIVE
) {
1175 peer_oft_recvcb_ack_recv(xfer
, buffer
, size
);
1181 oscar_xfer_init(OscarXfer
*xfer
) {
1186 oscar_xfer_class_finalize(OscarXferClass
*klass
) {
1191 oscar_xfer_class_init(OscarXferClass
*klass
) {
1192 PurpleXferClass
*xfer_class
= PURPLE_XFER_CLASS(klass
);
1194 xfer_class
->init
= oscar_xfer_init_xfer
;
1195 xfer_class
->end
= peer_oft_recvcb_end
;
1196 xfer_class
->cancel_send
= peer_oft_cb_generic_cancel
;
1197 xfer_class
->cancel_recv
= peer_oft_cb_generic_cancel
;
1198 xfer_class
->request_denied
= peer_oft_cb_generic_cancel
;
1199 xfer_class
->ack
= oscar_xfer_ack
;
1203 oscar_xfer_register(GTypeModule
*module
) {
1204 oscar_xfer_register_type(module
);
1208 oscar_xfer_get_peer_connection(OscarXfer
*xfer
) {
1209 g_return_val_if_fail(OSCAR_IS_XFER(xfer
), NULL
);