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 purple_timeout_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 purple_xfer_set_protocol_data(conn
->xfer
, NULL
);
212 status
= purple_xfer_get_status(conn
->xfer
);
213 if ((status
!= PURPLE_XFER_STATUS_DONE
) &&
214 (status
!= PURPLE_XFER_STATUS_CANCEL_LOCAL
) &&
215 (status
!= PURPLE_XFER_STATUS_CANCEL_REMOTE
))
217 if ((conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_CLOSED
) ||
218 (conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_REFUSED
))
219 purple_xfer_cancel_remote(conn
->xfer
);
221 purple_xfer_cancel_local(conn
->xfer
);
223 g_object_unref(conn
->xfer
);
228 g_free(conn
->error_message
);
229 g_free(conn
->proxyip
);
230 g_free(conn
->clientip
);
231 g_free(conn
->verifiedip
);
232 g_free(conn
->xferdata
.name
);
233 g_object_unref(G_OBJECT(conn
->buffer_outgoing
));
235 conn
->od
->peer_connections
= g_slist_remove(conn
->od
->peer_connections
, conn
);
243 peer_connection_destroy(PeerConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
245 if (conn
->destroy_timeout
!= 0)
246 purple_timeout_remove(conn
->destroy_timeout
);
247 conn
->disconnect_reason
= reason
;
248 g_free(conn
->error_message
);
249 conn
->error_message
= g_strdup(error_message
);
250 peer_connection_destroy_cb(conn
);
254 peer_connection_schedule_destroy(PeerConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
256 if (conn
->destroy_timeout
!= 0)
257 /* Already taken care of */
260 purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
261 conn
->disconnect_reason
= reason
;
262 g_free(conn
->error_message
);
263 conn
->error_message
= g_strdup(error_message
);
264 conn
->destroy_timeout
= purple_timeout_add(0, peer_connection_destroy_cb
, conn
);
267 /*******************************************************************/
268 /* Begin code for receiving data on a peer connection */
269 /*******************************************************************/
272 * This should be used to read ODC and OFT framing info. It should
273 * NOT be used to read the payload sent across the connection (IMs,
274 * file data, etc), and it should NOT be used to read proxy negotiation
277 * Unlike flap_connection_recv_cb(), this only reads one frame at a
278 * time. This is done so that the watcher can be changed during the
279 * handling of the frame. If the watcher is changed then this
280 * function will not read in any more data. This happens when
281 * reading the payload of a direct IM frame, or when we're
282 * receiving a file from the remote user. Once the data has been
283 * read, the watcher will be switched back to this function to
284 * continue reading the next frame.
287 peer_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
289 PeerConnection
*conn
;
294 /* Start reading a new ODC/OFT frame */
295 if (conn
->buffer_incoming
.data
== NULL
)
297 /* Read the first 6 bytes (magic string and frame length) */
298 read
= recv(conn
->fd
, conn
->header
+ conn
->header_received
,
299 6 - conn
->header_received
, 0);
301 /* Check if the remote user closed the connection */
304 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
308 /* If there was an error then close the connection */
311 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
315 peer_connection_destroy(conn
,
316 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
320 conn
->lastactivity
= time(NULL
);
322 /* If we don't even have the first 6 bytes then do nothing */
323 conn
->header_received
+= read
;
324 if (conn
->header_received
< 6)
327 /* All ODC/OFT frames must start with a magic string */
328 if (memcmp(conn
->magic
, conn
->header
, 4))
330 purple_debug_warning("oscar", "Expecting magic string to "
331 "be %c%c%c%c but received magic string %c%c%c%c. "
332 "Closing connection.\n",
333 conn
->magic
[0], conn
->magic
[1], conn
->magic
[2],
334 conn
->magic
[3], conn
->header
[0], conn
->header
[1],
335 conn
->header
[2], conn
->header
[3]);
336 peer_connection_destroy(conn
, OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
340 /* Initialize a new temporary ByteStream for incoming data */
341 conn
->buffer_incoming
.len
= aimutil_get16(&conn
->header
[4]) - 6;
342 conn
->buffer_incoming
.data
= g_new(guint8
, conn
->buffer_incoming
.len
);
343 conn
->buffer_incoming
.offset
= 0;
346 /* Read data into the temporary buffer until it is complete */
347 read
= recv(conn
->fd
,
348 &conn
->buffer_incoming
.data
[conn
->buffer_incoming
.offset
],
349 conn
->buffer_incoming
.len
- conn
->buffer_incoming
.offset
,
352 /* Check if the remote user closed the connection */
355 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
361 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
365 peer_connection_destroy(conn
,
366 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
370 conn
->lastactivity
= time(NULL
);
371 conn
->buffer_incoming
.offset
+= read
;
372 if (conn
->buffer_incoming
.offset
< conn
->buffer_incoming
.len
)
373 /* Waiting for more data to arrive */
376 /* We have a complete ODC/OFT frame! Handle it and continue reading */
377 byte_stream_rewind(&conn
->buffer_incoming
);
378 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
380 peer_odc_recv_frame(conn
, &conn
->buffer_incoming
);
382 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
384 peer_oft_recv_frame(conn
, &conn
->buffer_incoming
);
387 g_free(conn
->buffer_incoming
.data
);
388 conn
->buffer_incoming
.data
= NULL
;
390 conn
->header_received
= 0;
393 /*******************************************************************/
394 /* End code for receiving data on a peer connection */
395 /*******************************************************************/
397 /*******************************************************************/
398 /* Begin code for sending data on a peer connection */
399 /*******************************************************************/
402 send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
404 PeerConnection
*conn
;
407 const gchar
*output
= NULL
;
410 writelen
= purple_circular_buffer_get_max_read(conn
->buffer_outgoing
);
414 purple_input_remove(conn
->watcher_outgoing
);
415 conn
->watcher_outgoing
= 0;
417 * The buffer is currently empty, so reset the current input
418 * and output positions to the start of the buffer. We do
419 * this so that the next chunk of data that we put into the
420 * buffer can be read back out of the buffer in one fell swoop.
421 * Otherwise it gets fragmented and we have to read from the
422 * second half of the buffer than go back and read the rest of
423 * the chunk from the first half.
425 * We're using TCP, which is a stream based protocol, so this
426 * isn't supposed to matter. However, experience has shown
427 * that at least the proxy file transfer code in AIM 6.1.41.2
428 * requires that the entire OFT frame arrive all at once. If
429 * the frame is fragmented then AIM freaks out and aborts the
430 * file transfer. Somebody should teach those guys how to
431 * write good TCP code.
433 purple_circular_buffer_reset(conn
->buffer_outgoing
);
437 output
= purple_circular_buffer_get_output(conn
->buffer_outgoing
);
439 wrotelen
= send(conn
->fd
, output
, writelen
, 0);
442 if (wrotelen
< 0 && ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
)))
448 purple_input_remove(conn
->watcher_outgoing
);
449 conn
->watcher_outgoing
= 0;
452 peer_connection_schedule_destroy(conn
,
453 OSCAR_DISCONNECT_LOST_CONNECTION
, NULL
);
458 * This could happen when unable to send a negotiation
459 * frame to a peer proxy server.
461 peer_connection_trynext(conn
);
466 purple_circular_buffer_mark_read(conn
->buffer_outgoing
, wrotelen
);
467 conn
->lastactivity
= time(NULL
);
471 * This should be called by OFT/ODC code to send a standard OFT or ODC
472 * frame across the peer connection along with some payload data. Or
473 * maybe a file. Anything, really.
476 peer_connection_send(PeerConnection
*conn
, ByteStream
*bs
)
478 /* Add everything to our outgoing buffer */
479 purple_circular_buffer_append(conn
->buffer_outgoing
, bs
->data
, bs
->len
);
481 /* If we haven't already started writing stuff, then start the cycle */
482 if ((conn
->watcher_outgoing
== 0) && (conn
->fd
>= 0))
484 conn
->watcher_outgoing
= purple_input_add(conn
->fd
,
485 PURPLE_INPUT_WRITE
, send_cb
, conn
);
486 send_cb(conn
, conn
->fd
, 0);
490 /*******************************************************************/
491 /* End code for sending data on a peer connection */
492 /*******************************************************************/
494 /*******************************************************************/
495 /* Begin code for establishing a peer connection */
496 /*******************************************************************/
499 peer_connection_finalize_connection(PeerConnection
*conn
)
501 conn
->watcher_incoming
= purple_input_add(conn
->fd
,
502 PURPLE_INPUT_READ
, peer_connection_recv_cb
, conn
);
504 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
507 * If we are connecting to them then send our cookie so they
508 * can verify who we are. Note: This doesn't seem to be
509 * necessary, but it also doesn't seem to hurt.
511 if (!(conn
->flags
& PEER_CONNECTION_FLAG_IS_INCOMING
))
512 peer_odc_send_cookie(conn
);
514 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
516 if (purple_xfer_get_xfer_type(conn
->xfer
) == PURPLE_XFER_TYPE_SEND
)
518 peer_oft_send_prompt(conn
);
523 * Tell the remote user that we're connected (which may also imply
524 * that we've accepted their request).
526 if (!(conn
->flags
& PEER_CONNECTION_FLAG_IS_INCOMING
))
527 aim_im_sendch2_connected(conn
);
531 * We tried to make an outgoing connection to a remote user. It
532 * either connected or failed to connect.
535 peer_connection_common_established_cb(gpointer data
, gint source
, const gchar
*error_message
, gboolean verified
)
537 PeerConnection
*conn
;
542 conn
->verified_connect_data
= NULL
;
544 conn
->client_connect_data
= NULL
;
548 if ((conn
->verified_connect_data
== NULL
) &&
549 (conn
->client_connect_data
== NULL
))
551 /* Our parallel connection attemps have both failed. */
552 peer_connection_trynext(conn
);
557 purple_timeout_remove(conn
->connect_timeout_timer
);
558 conn
->connect_timeout_timer
= 0;
560 if (conn
->client_connect_data
!= NULL
)
562 purple_proxy_connect_cancel(conn
->client_connect_data
);
563 conn
->client_connect_data
= NULL
;
566 if (conn
->verified_connect_data
!= NULL
)
568 purple_proxy_connect_cancel(conn
->verified_connect_data
);
569 conn
->verified_connect_data
= NULL
;
574 peer_connection_finalize_connection(conn
);
578 peer_connection_verified_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
580 peer_connection_common_established_cb(data
, source
, error_message
, TRUE
);
584 peer_connection_client_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
586 peer_connection_common_established_cb(data
, source
, error_message
, FALSE
);
590 * This is the watcher callback for any listening socket that is
591 * waiting for a peer to connect. When a peer connects we set the
592 * input watcher to start reading data from the peer.
594 * To make sure that the connection is with the intended person and
595 * not with a malicious middle man, we don't send anything until we've
596 * received a peer frame from the remote user and have verified that
597 * the cookie in the peer frame matches the cookie that was exchanged
598 * in the channel 2 ICBM.
601 peer_connection_listen_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
603 PeerConnection
*conn
;
604 struct sockaddr addr
;
605 socklen_t addrlen
= sizeof(addr
);
609 purple_debug_info("oscar", "Accepting connection on listener socket.\n");
611 conn
->fd
= accept(conn
->listenerfd
, &addr
, &addrlen
);
614 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
615 /* No connection yet--no worries */
616 /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
619 peer_connection_trynext(conn
);
623 if ((addr
.sa_family
!= PF_INET
) && (addr
.sa_family
!= PF_INET6
))
625 /* Invalid connection type?! Continue waiting. */
630 _purple_network_set_common_socket_flags(conn
->fd
);
632 purple_input_remove(conn
->watcher_incoming
);
634 peer_connection_finalize_connection(conn
);
638 * Converts a dot-decimal IP address to an array of unsigned
639 * chars. For example, converts 192.168.0.1 to a 4 byte
640 * array containing 192, 168, 0 and 1.
642 * @param ip An IP address in dot-decimal notiation.
643 * @return An array of 4 bytes containing an IP addresses
644 * equivalent to the given parameter, or NULL if
645 * the given IP address is invalid. This value
646 * is statically allocated and should not be
649 static const unsigned char *
650 peer_ip_atoi(const char *ip
)
652 static unsigned char ret
[4];
653 gchar
*delimiter
= ".";
657 g_return_val_if_fail(ip
!= NULL
, NULL
);
659 split
= g_strsplit(ip
, delimiter
, 4);
660 for (i
= 0; split
[i
] != NULL
; i
++)
661 ret
[i
] = atoi(split
[i
]);
664 /* i should always be 4 */
672 * We've just opened a listener socket, so we send the remote
673 * user an ICBM and ask them to connect to us.
676 peer_connection_establish_listener_cb(int listenerfd
, gpointer data
)
678 PeerConnection
*conn
;
680 PurpleConnection
*gc
;
681 PurpleAccount
*account
;
682 PurpleIMConversation
*im
;
684 FlapConnection
*bos_conn
;
685 const char *listener_ip
;
686 const guchar
*ip_atoi
;
687 unsigned short listener_port
;
690 conn
->listen_data
= NULL
;
694 /* Could not open listener socket */
695 peer_connection_trynext(conn
);
701 account
= purple_connection_get_account(gc
);
702 conn
->listenerfd
= listenerfd
;
704 /* Watch for new connections on our listener socket */
705 conn
->watcher_incoming
= purple_input_add(conn
->listenerfd
,
706 PURPLE_INPUT_READ
, peer_connection_listen_cb
, conn
);
708 /* Send the "please connect to me!" ICBM */
709 bos_conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
);
710 if (bos_conn
== NULL
)
713 peer_connection_trynext(conn
);
718 listener_ip
= purple_network_get_my_ip(bos_conn
->gsc
->fd
);
720 listener_ip
= purple_network_get_my_ip(bos_conn
->fd
);
722 ip_atoi
= peer_ip_atoi(listener_ip
);
723 if (ip_atoi
== NULL
) {
724 /* Could not convert IP to 4 byte array--weird, but this does
725 happen for some users (#4829, Adium #15839). Maybe they're
726 connecting with IPv6...? Maybe through a proxy? */
727 purple_debug_error("oscar", "Can't ask peer to connect to us "
728 "because peer_ip_atoi(%s) returned NULL. "
729 "fd=%d. is_ssl=%d\n",
730 listener_ip
? listener_ip
: "(null)",
731 bos_conn
->gsc
? bos_conn
->gsc
->fd
: bos_conn
->fd
,
732 bos_conn
->gsc
? 1 : 0);
733 peer_connection_trynext(conn
);
737 listener_port
= purple_network_get_port_from_fd(conn
->listenerfd
);
739 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
741 aim_im_sendch2_odc_requestdirect(od
,
742 conn
->cookie
, conn
->bn
, ip_atoi
,
743 listener_port
, ++conn
->lastrequestnumber
);
745 /* Print a message to a local conversation window */
746 im
= purple_im_conversation_new(account
, conn
->bn
);
747 tmp
= g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
748 "Direct IM."), conn
->bn
, listener_ip
, listener_port
);
749 purple_conversation_write_system_message(
750 PURPLE_CONVERSATION(im
), tmp
, 0);
753 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
755 aim_im_sendch2_sendfile_requestdirect(od
,
756 conn
->cookie
, conn
->bn
,
758 listener_port
, ++conn
->lastrequestnumber
,
759 (const gchar
*)conn
->xferdata
.name
,
760 conn
->xferdata
.size
, conn
->xferdata
.totfiles
);
765 * This is a callback function used when we're connecting to a peer
766 * using either the client IP or the verified IP and the connection
767 * took longer than 5 seconds to complete. We do this because
768 * waiting for the OS to time out the connection attempt is not
769 * practical--the default timeout on many OSes can be 3 minutes or
770 * more, and users are impatient.
772 * Worst case scenario: the user is connected to the Internet using
773 * a modem with severe lag. The peer connections fail and Purple falls
774 * back to using a proxied connection. The lower bandwidth
775 * limitations imposed by the proxied connection won't matter because
776 * the user is using a modem.
778 * I suppose this line of thinking is discriminatory against people
779 * with very high lag but decent throughput who are transferring
780 * large files. But we don't care about those people.
782 * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
783 * too long for a user to wait to send a file. I'm also parallelizing
784 * requests when possible. The longest we should have to wait now is 10
785 * seconds. We shouldn't make it shorter than this.
788 peer_connection_tooktoolong(gpointer data
)
790 PeerConnection
*conn
;
794 purple_debug_info("oscar", "Peer connection timed out after 5 seconds. "
795 "Trying next method...\n");
797 peer_connection_trynext(conn
);
799 /* Cancel this timer. It'll be added again, if needed. */
804 * Try to establish the given PeerConnection using a defined
808 peer_connection_trynext(PeerConnection
*conn
)
810 PurpleAccount
*account
;
812 account
= purple_connection_get_account(conn
->od
->gc
);
815 * Close any remnants of a previous failed connection attempt.
817 peer_connection_close(conn
);
820 * 1. Attempt to connect to the remote user using their verifiedip and clientip.
821 * We try these at the same time and use whichever succeeds first, so we don't
822 * have to wait for a timeout.
824 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_DIRECT
) &&
825 (conn
->verifiedip
!= NULL
) && (conn
->port
!= 0) && (!conn
->use_proxy
))
827 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_DIRECT
;
829 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
832 PurpleIMConversation
*im
;
833 tmp
= g_strdup_printf(_("Attempting to connect to %s:%hu."),
834 conn
->verifiedip
, conn
->port
);
835 im
= purple_im_conversation_new(account
, conn
->bn
);
836 purple_conversation_write_system_message(
837 PURPLE_CONVERSATION(im
), tmp
, 0);
841 conn
->verified_connect_data
= purple_proxy_connect(NULL
, account
,
842 conn
->verifiedip
, conn
->port
,
843 peer_connection_verified_established_cb
, conn
);
845 if ((conn
->verifiedip
== NULL
) ||
846 !purple_strequal(conn
->verifiedip
, conn
->clientip
))
848 conn
->client_connect_data
= purple_proxy_connect(NULL
, account
,
849 conn
->clientip
, conn
->port
,
850 peer_connection_client_established_cb
, conn
);
853 if ((conn
->verified_connect_data
!= NULL
) ||
854 (conn
->client_connect_data
!= NULL
))
857 conn
->connect_timeout_timer
= purple_timeout_add_seconds(5,
858 peer_connection_tooktoolong
, conn
);
864 * 2. Attempt to have the remote user connect to us (using both
865 * our verifiedip and our clientip).
867 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_INCOMING
) &&
870 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_INCOMING
;
873 * Remote user is connecting to us, so we'll need to verify
874 * that the user who connected is our friend.
876 conn
->flags
|= PEER_CONNECTION_FLAG_IS_INCOMING
;
878 conn
->listen_data
= purple_network_listen_range(5190, 5290, AF_UNSPEC
, SOCK_STREAM
, TRUE
,
879 peer_connection_establish_listener_cb
, conn
);
880 if (conn
->listen_data
!= NULL
)
882 /* Opening listener socket... */
888 * 3. Attempt to have both users connect to an intermediate proxy
891 if (!(conn
->flags
& PEER_CONNECTION_FLAG_TRIED_PROXY
))
893 conn
->flags
|= PEER_CONNECTION_FLAG_TRIED_PROXY
;
896 * If we initiate the proxy connection, then the remote user
897 * could be anyone, so we need to verify that the user who
898 * connected is our friend.
900 if (!conn
->use_proxy
)
901 conn
->flags
|= PEER_CONNECTION_FLAG_IS_INCOMING
;
903 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
906 PurpleIMConversation
*im
;
907 tmp
= g_strdup(_("Attempting to connect via proxy server."));
908 im
= purple_im_conversation_new(account
, conn
->bn
);
909 purple_conversation_write_system_message(
910 PURPLE_CONVERSATION(im
), tmp
, 0);
914 conn
->verified_connect_data
= purple_proxy_connect(NULL
, account
,
915 (conn
->proxyip
!= NULL
)
917 : (conn
->od
->icq
? ICQ_PEER_PROXY_SERVER
: AIM_PEER_PROXY_SERVER
),
919 peer_proxy_connection_established_cb
, conn
);
920 if (conn
->verified_connect_data
!= NULL
)
928 peer_connection_destroy(conn
, OSCAR_DISCONNECT_COULD_NOT_CONNECT
, NULL
);
932 * Initiate a peer connection with someone.
935 peer_connection_propose(OscarData
*od
, guint64 type
, const char *bn
)
937 PeerConnection
*conn
;
939 if (type
== OSCAR_CAPABILITY_DIRECTIM
)
941 conn
= peer_connection_find_by_type(od
, bn
, type
);
946 PurpleAccount
*account
;
947 PurpleIMConversation
*im
;
949 purple_debug_info("oscar", "Already have a direct IM "
950 "session with %s.\n", bn
);
951 account
= purple_connection_get_account(od
->gc
);
952 im
= purple_conversations_find_im_with_account(bn
, account
);
954 purple_conversation_present(PURPLE_CONVERSATION(im
));
958 /* Cancel the old connection and try again */
959 peer_connection_destroy(conn
, OSCAR_DISCONNECT_RETRYING
, NULL
);
963 conn
= peer_connection_new(od
, type
, bn
);
964 conn
->flags
|= PEER_CONNECTION_FLAG_INITIATED_BY_ME
;
965 conn
->flags
|= PEER_CONNECTION_FLAG_APPROVED
;
966 aim_icbm_makecookie(conn
->cookie
);
968 peer_connection_trynext(conn
);
972 * Someone else wants to establish a peer connection with us,
976 peer_connection_got_proposition_yes_cb(gpointer data
, gint id
)
978 PeerConnection
*conn
;
982 conn
->flags
|= PEER_CONNECTION_FLAG_APPROVED
;
983 peer_connection_trynext(conn
);
987 * Someone else wants to establish a peer connection with us,
990 * "Well, one time my friend asked me if I wanted to play the
991 * piccolo. But I said no."
994 peer_connection_got_proposition_no_cb(gpointer data
, gint id
)
996 PeerConnection
*conn
;
1000 aim_im_denytransfer(conn
->od
, conn
->bn
, conn
->cookie
,
1001 AIM_TRANSFER_DENY_DECLINE
);
1002 peer_connection_destroy(conn
, OSCAR_DISCONNECT_LOCAL_CLOSED
, NULL
);
1006 * Someone else wants to establish a peer connection with us.
1009 peer_connection_got_proposition(OscarData
*od
, const gchar
*bn
, const gchar
*message
, IcbmArgsCh2
*args
)
1011 PurpleConnection
*gc
;
1012 PurpleAccount
*account
;
1013 PeerConnection
*conn
;
1017 account
= purple_connection_get_account(gc
);
1020 * If we have a connection with this same cookie then they are
1021 * probably just telling us they weren't able to connect to us
1022 * and we should try connecting to them, instead. Or they want
1023 * to go through a proxy.
1025 conn
= peer_connection_find_by_cookie(od
, bn
, args
->cookie
);
1026 if ((conn
!= NULL
) && (conn
->type
== args
->type
))
1028 purple_debug_info("oscar", "Remote user wants to try a "
1029 "different connection method\n");
1030 g_free(conn
->proxyip
);
1031 g_free(conn
->clientip
);
1032 g_free(conn
->verifiedip
);
1033 if (args
->use_proxy
)
1034 conn
->proxyip
= g_strdup(args
->proxyip
);
1036 conn
->proxyip
= NULL
;
1037 conn
->verifiedip
= g_strdup(args
->verifiedip
);
1038 conn
->clientip
= g_strdup(args
->clientip
);
1039 conn
->port
= args
->port
;
1040 conn
->use_proxy
|= args
->use_proxy
;
1041 conn
->lastrequestnumber
++;
1042 peer_connection_trynext(conn
);
1046 /* If this is a direct IM, then close any existing session */
1047 if (args
->type
== OSCAR_CAPABILITY_DIRECTIM
)
1049 conn
= peer_connection_find_by_type(od
, bn
, args
->type
);
1052 /* Close the old direct IM and start a new one */
1053 purple_debug_info("oscar", "Received new direct IM request "
1054 "from %s. Destroying old connection.\n", bn
);
1055 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
1059 /* Check for proper arguments */
1060 if (args
->type
== OSCAR_CAPABILITY_SENDFILE
)
1062 if ((args
->info
.sendfile
.filename
== NULL
) ||
1063 (args
->info
.sendfile
.totsize
== 0) ||
1064 (args
->info
.sendfile
.totfiles
== 0))
1066 purple_debug_warning("oscar",
1067 "%s tried to send you a file with incomplete "
1068 "information.\n", bn
);
1073 conn
= peer_connection_new(od
, args
->type
, bn
);
1074 memcpy(conn
->cookie
, args
->cookie
, 8);
1075 if (args
->use_proxy
)
1076 conn
->proxyip
= g_strdup(args
->proxyip
);
1077 conn
->clientip
= g_strdup(args
->clientip
);
1078 conn
->verifiedip
= g_strdup(args
->verifiedip
);
1079 conn
->port
= args
->port
;
1080 conn
->use_proxy
|= args
->use_proxy
;
1081 conn
->lastrequestnumber
++;
1083 if (args
->type
== OSCAR_CAPABILITY_DIRECTIM
)
1085 buf
= g_strdup_printf(_("%s has just asked to directly connect to %s"),
1086 bn
, purple_account_get_username(account
));
1088 purple_request_action(conn
, NULL
, buf
,
1089 _("This requires a direct connection between "
1090 "the two computers and is necessary for IM "
1091 "Images. Because your IP address will be "
1092 "revealed, this may be considered a privacy "
1094 PURPLE_DEFAULT_ACTION_NONE
,
1095 purple_request_cpar_from_account(account
),
1097 _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb
),
1098 _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb
));
1100 else if (args
->type
== OSCAR_CAPABILITY_SENDFILE
)
1104 conn
->xfer
= purple_xfer_new(account
, PURPLE_XFER_TYPE_RECEIVE
, bn
);
1107 purple_xfer_set_protocol_data(conn
->xfer
, conn
);
1108 purple_xfer_set_size(conn
->xfer
, args
->info
.sendfile
.totsize
);
1110 /* Set the file name */
1111 if (g_utf8_validate(args
->info
.sendfile
.filename
, -1, NULL
))
1112 filename
= g_strdup(args
->info
.sendfile
.filename
);
1114 filename
= purple_utf8_salvage(args
->info
.sendfile
.filename
);
1116 if (args
->info
.sendfile
.subtype
== AIM_OFT_SUBTYPE_SEND_DIR
)
1119 * If they are sending us a directory then the last character
1120 * of the file name will be an asterisk. We don't want to
1121 * save stuff to a directory named "*" so we remove the
1122 * asterisk from the file name.
1124 char *tmp
= strrchr(filename
, '\\');
1125 if ((tmp
!= NULL
) && (tmp
[1] == '*'))
1128 purple_xfer_set_filename(conn
->xfer
, filename
);
1132 * Set the message, unless this is the dummy message from an
1133 * ICQ client or an empty message from an AIM client.
1134 * TODO: Maybe we should strip HTML and then see if strlen>0?
1136 if ((message
!= NULL
) &&
1137 (g_ascii_strncasecmp(message
, "<ICQ_COOL_FT>", 13) != 0) &&
1138 (g_ascii_strcasecmp(message
, "<HTML>") != 0))
1140 purple_xfer_set_message(conn
->xfer
, message
);
1143 /* Setup our I/O op functions */
1144 purple_xfer_set_init_fnc(conn
->xfer
, peer_oft_recvcb_init
);
1145 purple_xfer_set_end_fnc(conn
->xfer
, peer_oft_recvcb_end
);
1146 purple_xfer_set_request_denied_fnc(conn
->xfer
, peer_oft_cb_generic_cancel
);
1147 purple_xfer_set_cancel_recv_fnc(conn
->xfer
, peer_oft_cb_generic_cancel
);
1148 purple_xfer_set_ack_fnc(conn
->xfer
, peer_oft_recvcb_ack_recv
);
1150 /* Now perform the request */
1151 purple_xfer_request(conn
->xfer
);
1156 /*******************************************************************/
1157 /* End code for establishing a peer connection */
1158 /*******************************************************************/