Annotation of ChivanetAimPidgin/oscarprpl/src/c/peer.c, revision 1.1.1.1
1.1 snw 1: /*
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19: */
20:
21: /*
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.)
26: */
27:
28: #ifdef HAVE_CONFIG_H
29: #include <config.h>
30: #endif
31:
32: /* From the oscar PRPL */
33: #include "oscar.h"
34: #include "peer.h"
35:
36: /* From Purple */
37: #include "conversation.h"
38: #include "ft.h"
39: #include "network.h"
40: #include "notify.h"
41: #include "request.h"
42: #include "util.h"
43:
44: #ifndef _WIN32
45: #include <stdio.h>
46: #include <netdb.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 */
51: #endif
52:
53: #ifdef _WIN32
54: #include "win32dep.h"
55: #endif
56:
57: /*
58: * I really want to switch all our networking code to using IPv6 only,
59: * but that really isn't a good idea at all. Evan S. of Adium says
60: * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
61: * nothing inherently IPv6 about them. And I feel like Linux kernel
62: * 2.6.5 is doing the same thing. So we REALLY should accept
63: * connections if they're showing up as IPv6. Old OSes (Solaris?)
64: * that might not have full IPv6 support yet will fail if we try
65: * to use PF_INET6 but it isn't defined. --Mark Doliner
66: */
67: #ifndef PF_INET6
68: #define PF_INET6 PF_INET
69: #endif
70:
71: PeerConnection *
72: peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type)
73: {
74: GSList *cur;
75: PeerConnection *conn;
76:
77: for (cur = od->peer_connections; cur != NULL; cur = cur->next)
78: {
79: conn = cur->data;
80: if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
81: return conn;
82: }
83:
84: return NULL;
85: }
86:
87: /**
88: * @param cookie This must be exactly 8 characters.
89: */
90: PeerConnection *
91: peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
92: {
93: GSList *cur;
94: PeerConnection *conn;
95:
96: for (cur = od->peer_connections; cur != NULL; cur = cur->next)
97: {
98: conn = cur->data;
99: if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
100: return conn;
101: }
102:
103: return NULL;
104: }
105:
106: PeerConnection *
107: peer_connection_new(OscarData *od, guint64 type, const char *bn)
108: {
109: PeerConnection *conn;
110: PurpleAccount *account;
111:
112: account = purple_connection_get_account(od->gc);
113:
114: conn = g_new0(PeerConnection, 1);
115: conn->od = od;
116: conn->type = type;
117: conn->bn = g_strdup(bn);
118: conn->buffer_outgoing = purple_circ_buffer_new(0);
119: conn->listenerfd = -1;
120: conn->fd = -1;
121: conn->lastactivity = time(NULL);
122: conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE);
123:
124: if (type == OSCAR_CAPABILITY_DIRECTIM)
125: memcpy(conn->magic, "ODC2", 4);
126: else if (type == OSCAR_CAPABILITY_SENDFILE)
127: memcpy(conn->magic, "OFT2", 4);
128:
129: od->peer_connections = g_slist_prepend(od->peer_connections, conn);
130:
131: return conn;
132: }
133:
134: static void
135: peer_connection_close(PeerConnection *conn)
136: {
137: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
138: peer_odc_close(conn);
139: else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
140: peer_oft_close(conn);
141:
142: if (conn->verified_connect_data != NULL)
143: {
144: purple_proxy_connect_cancel(conn->verified_connect_data);
145: conn->verified_connect_data = NULL;
146: }
147:
148: if (conn->client_connect_data != NULL)
149: {
150: purple_proxy_connect_cancel(conn->client_connect_data);
151: conn->client_connect_data = NULL;
152: }
153:
154: if (conn->listen_data != NULL)
155: {
156: purple_network_listen_cancel(conn->listen_data);
157: conn->listen_data = NULL;
158: }
159:
160: if (conn->connect_timeout_timer != 0)
161: {
162: purple_timeout_remove(conn->connect_timeout_timer);
163: conn->connect_timeout_timer = 0;
164: }
165:
166: if (conn->watcher_incoming != 0)
167: {
168: purple_input_remove(conn->watcher_incoming);
169: conn->watcher_incoming = 0;
170: }
171: if (conn->watcher_outgoing != 0)
172: {
173: purple_input_remove(conn->watcher_outgoing);
174: conn->watcher_outgoing = 0;
175: }
176: if (conn->listenerfd >= 0)
177: {
178: close(conn->listenerfd);
179: conn->listenerfd = -1;
180: }
181: if (conn->fd >= 0)
182: {
183: close(conn->fd);
184: conn->fd = -1;
185: }
186:
187: g_free(conn->buffer_incoming.data);
188: conn->buffer_incoming.data = NULL;
189: conn->buffer_incoming.len = 0;
190: conn->buffer_incoming.offset = 0;
191:
192: purple_circ_buffer_destroy(conn->buffer_outgoing);
193: conn->buffer_outgoing = purple_circ_buffer_new(0);
194:
195: conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
196: }
197:
198: static gboolean
199: peer_connection_destroy_cb(gpointer data)
200: {
201: PeerConnection *conn;
202:
203: conn = data;
204:
205: purple_request_close_with_handle(conn);
206:
207: peer_connection_close(conn);
208:
209: if (conn->checksum_data != NULL)
210: peer_oft_checksum_destroy(conn->checksum_data);
211:
212: if (conn->xfer != NULL)
213: {
214: PurpleXferStatusType status;
215: conn->xfer->data = NULL;
216: status = purple_xfer_get_status(conn->xfer);
217: if ((status != PURPLE_XFER_STATUS_DONE) &&
218: (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
219: (status != PURPLE_XFER_STATUS_CANCEL_REMOTE))
220: {
221: if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) ||
222: (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED))
223: purple_xfer_cancel_remote(conn->xfer);
224: else
225: purple_xfer_cancel_local(conn->xfer);
226: }
227: purple_xfer_unref(conn->xfer);
228: conn->xfer = NULL;
229: }
230:
231: g_free(conn->bn);
232: g_free(conn->error_message);
233: g_free(conn->proxyip);
234: g_free(conn->clientip);
235: g_free(conn->verifiedip);
236: g_free(conn->xferdata.name);
237: purple_circ_buffer_destroy(conn->buffer_outgoing);
238:
239: conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn);
240:
241: g_free(conn);
242:
243: return FALSE;
244: }
245:
246: void
247: peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
248: {
249: if (conn->destroy_timeout != 0)
250: purple_timeout_remove(conn->destroy_timeout);
251: conn->disconnect_reason = reason;
252: g_free(conn->error_message);
253: conn->error_message = g_strdup(error_message);
254: peer_connection_destroy_cb(conn);
255: }
256:
257: void
258: peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
259: {
260: if (conn->destroy_timeout != 0)
261: /* Already taken care of */
262: return;
263:
264: purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
265: conn->disconnect_reason = reason;
266: g_free(conn->error_message);
267: conn->error_message = g_strdup(error_message);
268: conn->destroy_timeout = purple_timeout_add(0, peer_connection_destroy_cb, conn);
269: }
270:
271: /*******************************************************************/
272: /* Begin code for receiving data on a peer connection */
273: /*******************************************************************/
274:
275: /**
276: * This should be used to read ODC and OFT framing info. It should
277: * NOT be used to read the payload sent across the connection (IMs,
278: * file data, etc), and it should NOT be used to read proxy negotiation
279: * headers.
280: *
281: * Unlike flap_connection_recv_cb(), this only reads one frame at a
282: * time. This is done so that the watcher can be changed during the
283: * handling of the frame. If the watcher is changed then this
284: * function will not read in any more data. This happens when
285: * reading the payload of a direct IM frame, or when we're
286: * receiving a file from the remote user. Once the data has been
287: * read, the watcher will be switched back to this function to
288: * continue reading the next frame.
289: */
290: void
291: peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
292: {
293: PeerConnection *conn;
294: gssize read;
295:
296: conn = data;
297:
298: /* Start reading a new ODC/OFT frame */
299: if (conn->buffer_incoming.data == NULL)
300: {
301: /* Read the first 6 bytes (magic string and frame length) */
302: read = recv(conn->fd, conn->header + conn->header_received,
303: 6 - conn->header_received, 0);
304:
305: /* Check if the remote user closed the connection */
306: if (read == 0)
307: {
308: peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
309: return;
310: }
311:
312: /* If there was an error then close the connection */
313: if (read < 0)
314: {
315: if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
316: /* No worries */
317: return;
318:
319: peer_connection_destroy(conn,
320: OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
321: return;
322: }
323:
324: conn->lastactivity = time(NULL);
325:
326: /* If we don't even have the first 6 bytes then do nothing */
327: conn->header_received += read;
328: if (conn->header_received < 6)
329: return;
330:
331: /* All ODC/OFT frames must start with a magic string */
332: if (memcmp(conn->magic, conn->header, 4))
333: {
334: purple_debug_warning("oscar", "Expecting magic string to "
335: "be %c%c%c%c but received magic string %c%c%c%c. "
336: "Closing connection.\n",
337: conn->magic[0], conn->magic[1], conn->magic[2],
338: conn->magic[3], conn->header[0], conn->header[1],
339: conn->header[2], conn->header[3]);
340: peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
341: return;
342: }
343:
344: /* Initialize a new temporary ByteStream for incoming data */
345: conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6;
346: conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len);
347: conn->buffer_incoming.offset = 0;
348: }
349:
350: /* Read data into the temporary buffer until it is complete */
351: read = recv(conn->fd,
352: &conn->buffer_incoming.data[conn->buffer_incoming.offset],
353: conn->buffer_incoming.len - conn->buffer_incoming.offset,
354: 0);
355:
356: /* Check if the remote user closed the connection */
357: if (read == 0)
358: {
359: peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
360: return;
361: }
362:
363: if (read < 0)
364: {
365: if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
366: /* No worries */
367: return;
368:
369: peer_connection_destroy(conn,
370: OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
371: return;
372: }
373:
374: conn->lastactivity = time(NULL);
375: conn->buffer_incoming.offset += read;
376: if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
377: /* Waiting for more data to arrive */
378: return;
379:
380: /* We have a complete ODC/OFT frame! Handle it and continue reading */
381: byte_stream_rewind(&conn->buffer_incoming);
382: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
383: {
384: peer_odc_recv_frame(conn, &conn->buffer_incoming);
385: }
386: else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
387: {
388: peer_oft_recv_frame(conn, &conn->buffer_incoming);
389: }
390:
391: g_free(conn->buffer_incoming.data);
392: conn->buffer_incoming.data = NULL;
393:
394: conn->header_received = 0;
395: }
396:
397: /*******************************************************************/
398: /* End code for receiving data on a peer connection */
399: /*******************************************************************/
400:
401: /*******************************************************************/
402: /* Begin code for sending data on a peer connection */
403: /*******************************************************************/
404:
405: static void
406: send_cb(gpointer data, gint source, PurpleInputCondition cond)
407: {
408: PeerConnection *conn;
409: gsize writelen;
410: gssize wrotelen;
411:
412: conn = data;
413: writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
414:
415: if (writelen == 0)
416: {
417: purple_input_remove(conn->watcher_outgoing);
418: conn->watcher_outgoing = 0;
419: /*
420: * The buffer is currently empty, so reset the current input
421: * and output positions to the start of the buffer. We do
422: * this so that the next chunk of data that we put into the
423: * buffer can be read back out of the buffer in one fell swoop.
424: * Otherwise it gets fragmented and we have to read from the
425: * second half of the buffer than go back and read the rest of
426: * the chunk from the first half.
427: *
428: * We're using TCP, which is a stream based protocol, so this
429: * isn't supposed to matter. However, experience has shown
430: * that at least the proxy file transfer code in AIM 6.1.41.2
431: * requires that the entire OFT frame arrive all at once. If
432: * the frame is fragmented then AIM freaks out and aborts the
433: * file transfer. Somebody should teach those guys how to
434: * write good TCP code.
435: */
436: conn->buffer_outgoing->inptr = conn->buffer_outgoing->buffer;
437: conn->buffer_outgoing->outptr = conn->buffer_outgoing->buffer;
438: return;
439: }
440:
441: wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
442: if (wrotelen <= 0)
443: {
444: if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
445: /* No worries */
446: return;
447:
448: if (conn->ready)
449: {
450: purple_input_remove(conn->watcher_outgoing);
451: conn->watcher_outgoing = 0;
452: close(conn->fd);
453: conn->fd = -1;
454: peer_connection_schedule_destroy(conn,
455: OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
456: }
457: else
458: {
459: /*
460: * This could happen when unable to send a negotiation
461: * frame to a peer proxy server.
462: */
463: peer_connection_trynext(conn);
464: }
465: return;
466: }
467:
468: purple_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
469: conn->lastactivity = time(NULL);
470: }
471:
472: /**
473: * This should be called by OFT/ODC code to send a standard OFT or ODC
474: * frame across the peer connection along with some payload data. Or
475: * maybe a file. Anything, really.
476: */
477: void
478: peer_connection_send(PeerConnection *conn, ByteStream *bs)
479: {
480: /* Add everything to our outgoing buffer */
481: purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
482:
483: /* If we haven't already started writing stuff, then start the cycle */
484: if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
485: {
486: conn->watcher_outgoing = purple_input_add(conn->fd,
487: PURPLE_INPUT_WRITE, send_cb, conn);
488: send_cb(conn, conn->fd, 0);
489: }
490: }
491:
492: /*******************************************************************/
493: /* End code for sending data on a peer connection */
494: /*******************************************************************/
495:
496: /*******************************************************************/
497: /* Begin code for establishing a peer connection */
498: /*******************************************************************/
499:
500: void
501: peer_connection_finalize_connection(PeerConnection *conn)
502: {
503: conn->watcher_incoming = purple_input_add(conn->fd,
504: PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
505:
506: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
507: {
508: /*
509: * If we are connecting to them then send our cookie so they
510: * can verify who we are. Note: This doesn't seem to be
511: * necessary, but it also doesn't seem to hurt.
512: */
513: if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
514: peer_odc_send_cookie(conn);
515: }
516: else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
517: {
518: if (purple_xfer_get_type(conn->xfer) == PURPLE_XFER_SEND)
519: {
520: peer_oft_send_prompt(conn);
521: }
522: }
523:
524: /*
525: * Tell the remote user that we're connected (which may also imply
526: * that we've accepted their request).
527: */
528: if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
529: aim_im_sendch2_connected(conn);
530: }
531:
532: /**
533: * We tried to make an outgoing connection to a remote user. It
534: * either connected or failed to connect.
535: */
536: static void
537: peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified)
538: {
539: PeerConnection *conn;
540:
541: conn = data;
542:
543: if (verified)
544: conn->verified_connect_data = NULL;
545: else
546: conn->client_connect_data = NULL;
547:
548: if (source < 0)
549: {
550: if ((conn->verified_connect_data == NULL) &&
551: (conn->client_connect_data == NULL))
552: {
553: /* Our parallel connection attemps have both failed. */
554: peer_connection_trynext(conn);
555: }
556: return;
557: }
558:
559: purple_timeout_remove(conn->connect_timeout_timer);
560: conn->connect_timeout_timer = 0;
561:
562: if (conn->client_connect_data != NULL)
563: {
564: purple_proxy_connect_cancel(conn->client_connect_data);
565: conn->client_connect_data = NULL;
566: }
567:
568: if (conn->verified_connect_data != NULL)
569: {
570: purple_proxy_connect_cancel(conn->verified_connect_data);
571: conn->verified_connect_data = NULL;
572: }
573:
574: conn->fd = source;
575:
576: peer_connection_finalize_connection(conn);
577: }
578:
579: static void
580: peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message)
581: {
582: peer_connection_common_established_cb(data, source, error_message, TRUE);
583: }
584:
585: static void
586: peer_connection_client_established_cb(gpointer data, gint source, const gchar *error_message)
587: {
588: peer_connection_common_established_cb(data, source, error_message, FALSE);
589: }
590:
591: /**
592: * This is the watcher callback for any listening socket that is
593: * waiting for a peer to connect. When a peer connects we set the
594: * input watcher to start reading data from the peer.
595: *
596: * To make sure that the connection is with the intended person and
597: * not with a malicious middle man, we don't send anything until we've
598: * received a peer frame from the remote user and have verified that
599: * the cookie in the peer frame matches the cookie that was exchanged
600: * in the channel 2 ICBM.
601: */
602: void
603: peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
604: {
605: PeerConnection *conn;
606: struct sockaddr addr;
607: socklen_t addrlen = sizeof(addr);
608:
609: conn = data;
610:
611: purple_debug_info("oscar", "Accepting connection on listener socket.\n");
612:
613: conn->fd = accept(conn->listenerfd, &addr, &addrlen);
614: if (conn->fd < 0)
615: {
616: if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
617: /* No connection yet--no worries */
618: /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
619: return;
620:
621: peer_connection_trynext(conn);
622: return;
623: }
624:
625: if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
626: {
627: /* Invalid connection type?! Continue waiting. */
628: close(conn->fd);
629: return;
630: }
631:
632: //_purple_network_set_common_socket_flags(conn->fd); // TODO: Not public? Check
633:
634: purple_input_remove(conn->watcher_incoming);
635:
636: peer_connection_finalize_connection(conn);
637: }
638:
639: /**
640: * We've just opened a listener socket, so we send the remote
641: * user an ICBM and ask them to connect to us.
642: */
643: static void
644: peer_connection_establish_listener_cb(int listenerfd, gpointer data)
645: {
646: PeerConnection *conn;
647: OscarData *od;
648: PurpleConnection *gc;
649: PurpleAccount *account;
650: PurpleConversation *conv;
651: char *tmp;
652: FlapConnection *bos_conn;
653: const char *listener_ip;
654: const guchar *ip_atoi;
655: unsigned short listener_port;
656:
657: conn = data;
658: conn->listen_data = NULL;
659:
660: if (listenerfd < 0)
661: {
662: /* Could not open listener socket */
663: peer_connection_trynext(conn);
664: return;
665: }
666:
667: od = conn->od;
668: gc = od->gc;
669: account = purple_connection_get_account(gc);
670: conn->listenerfd = listenerfd;
671:
672: /* Watch for new connections on our listener socket */
673: conn->watcher_incoming = purple_input_add(conn->listenerfd,
674: PURPLE_INPUT_READ, peer_connection_listen_cb, conn);
675:
676: /* Send the "please connect to me!" ICBM */
677: bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
678: if (bos_conn == NULL)
679: {
680: /* Not good */
681: peer_connection_trynext(conn);
682: return;
683: }
684:
685: if (bos_conn->gsc)
686: listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
687: else
688: listener_ip = purple_network_get_my_ip(bos_conn->fd);
689:
690: ip_atoi = purple_network_ip_atoi(listener_ip);
691: if (ip_atoi == NULL) {
692: /* Could not convert IP to 4 byte array--weird, but this does
693: happen for some users (#4829, Adium #15839). Maybe they're
694: connecting with IPv6...? Maybe through a proxy? */
695: purple_debug_error("oscar", "Can't ask peer to connect to us "
696: "because purple_network_ip_atoi(%s) returned NULL. "
697: "fd=%d. is_ssl=%d\n",
698: listener_ip ? listener_ip : "(null)",
699: bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
700: bos_conn->gsc ? 1 : 0);
701: peer_connection_trynext(conn);
702: return;
703: }
704:
705: listener_port = purple_network_get_port_from_fd(conn->listenerfd);
706:
707: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
708: {
709: aim_im_sendch2_odc_requestdirect(od,
710: conn->cookie, conn->bn, ip_atoi,
711: listener_port, ++conn->lastrequestnumber);
712:
713: /* Print a message to a local conversation window */
714: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
715: tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
716: "Direct IM."), conn->bn, listener_ip, listener_port);
717: purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
718: g_free(tmp);
719: }
720: else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
721: {
722: aim_im_sendch2_sendfile_requestdirect(od,
723: conn->cookie, conn->bn,
724: ip_atoi,
725: listener_port, ++conn->lastrequestnumber,
726: (const gchar *)conn->xferdata.name,
727: conn->xferdata.size, conn->xferdata.totfiles);
728: }
729: }
730:
731: /**
732: * This is a callback function used when we're connecting to a peer
733: * using either the client IP or the verified IP and the connection
734: * took longer than 5 seconds to complete. We do this because
735: * waiting for the OS to time out the connection attempt is not
736: * practical--the default timeout on many OSes can be 3 minutes or
737: * more, and users are impatient.
738: *
739: * Worst case scenario: the user is connected to the Internet using
740: * a modem with severe lag. The peer connections fail and Purple falls
741: * back to using a proxied connection. The lower bandwidth
742: * limitations imposed by the proxied connection won't matter because
743: * the user is using a modem.
744: *
745: * I suppose this line of thinking is discriminatory against people
746: * with very high lag but decent throughput who are transferring
747: * large files. But we don't care about those people.
748: *
749: * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
750: * too long for a user to wait to send a file. I'm also parallelizing
751: * requests when possible. The longest we should have to wait now is 10
752: * seconds. We shouldn't make it shorter than this.
753: */
754: static gboolean
755: peer_connection_tooktoolong(gpointer data)
756: {
757: PeerConnection *conn;
758:
759: conn = data;
760:
761: purple_debug_info("oscar", "Peer connection timed out after 5 seconds. "
762: "Trying next method...\n");
763:
764: peer_connection_trynext(conn);
765:
766: /* Cancel this timer. It'll be added again, if needed. */
767: return FALSE;
768: }
769:
770: /**
771: * Try to establish the given PeerConnection using a defined
772: * sequence of steps.
773: */
774: void
775: peer_connection_trynext(PeerConnection *conn)
776: {
777: PurpleAccount *account;
778:
779: account = purple_connection_get_account(conn->od->gc);
780:
781: /*
782: * Close any remnants of a previous failed connection attempt.
783: */
784: peer_connection_close(conn);
785:
786: /*
787: * 1. Attempt to connect to the remote user using their verifiedip and clientip.
788: * We try these at the same time and use whichever succeeds first, so we don't
789: * have to wait for a timeout.
790: */
791: if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) &&
792: (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy))
793: {
794: conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT;
795:
796: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
797: {
798: gchar *tmp;
799: PurpleConversation *conv;
800: tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
801: conn->verifiedip, conn->port);
802: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
803: purple_conversation_write(conv, NULL, tmp,
804: PURPLE_MESSAGE_SYSTEM, time(NULL));
805: g_free(tmp);
806: }
807:
808: conn->verified_connect_data = purple_proxy_connect(NULL, account,
809: conn->verifiedip, conn->port,
810: peer_connection_verified_established_cb, conn);
811:
812: if ((conn->verifiedip == NULL) ||
813: !purple_strequal(conn->verifiedip, conn->clientip))
814: {
815: conn->client_connect_data = purple_proxy_connect(NULL, account,
816: conn->clientip, conn->port,
817: peer_connection_client_established_cb, conn);
818: }
819:
820: if ((conn->verified_connect_data != NULL) ||
821: (conn->client_connect_data != NULL))
822: {
823: /* Connecting... */
824: conn->connect_timeout_timer = purple_timeout_add_seconds(5,
825: peer_connection_tooktoolong, conn);
826: return;
827: }
828: }
829:
830: /*
831: * 2. Attempt to have the remote user connect to us (using both
832: * our verifiedip and our clientip).
833: */
834: if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) &&
835: (!conn->use_proxy))
836: {
837: conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING;
838:
839: /*
840: * Remote user is connecting to us, so we'll need to verify
841: * that the user who connected is our friend.
842: */
843: conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
844:
845: conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM,
846: peer_connection_establish_listener_cb, conn);
847: if (conn->listen_data != NULL)
848: {
849: /* Opening listener socket... */
850: return;
851: }
852: }
853:
854: /*
855: * 3. Attempt to have both users connect to an intermediate proxy
856: * server.
857: */
858: if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY))
859: {
860: conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY;
861:
862: /*
863: * If we initiate the proxy connection, then the remote user
864: * could be anyone, so we need to verify that the user who
865: * connected is our friend.
866: */
867: if (!conn->use_proxy)
868: conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
869:
870: if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
871: {
872: gchar *tmp;
873: PurpleConversation *conv;
874: tmp = g_strdup(_("Attempting to connect via proxy server."));
875: conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
876: purple_conversation_write(conv, NULL, tmp,
877: PURPLE_MESSAGE_SYSTEM, time(NULL));
878: g_free(tmp);
879: }
880:
881: conn->verified_connect_data = purple_proxy_connect(NULL, account,
882: (conn->proxyip != NULL)
883: ? conn->proxyip
884: : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER),
885: PEER_PROXY_PORT,
886: peer_proxy_connection_established_cb, conn);
887: if (conn->verified_connect_data != NULL)
888: {
889: /* Connecting... */
890: return;
891: }
892: }
893:
894: /* Give up! */
895: peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL);
896: }
897:
898: /**
899: * Initiate a peer connection with someone.
900: */
901: void
902: peer_connection_propose(OscarData *od, guint64 type, const char *bn)
903: {
904: PeerConnection *conn;
905:
906: if (type == OSCAR_CAPABILITY_DIRECTIM)
907: {
908: conn = peer_connection_find_by_type(od, bn, type);
909: if (conn != NULL)
910: {
911: if (conn->ready)
912: {
913: PurpleAccount *account;
914: PurpleConversation *conv;
915:
916: purple_debug_info("oscar", "Already have a direct IM "
917: "session with %s.\n", bn);
918: account = purple_connection_get_account(od->gc);
919: conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
920: bn, account);
921: if (conv != NULL)
922: purple_conversation_present(conv);
923: return;
924: }
925:
926: /* Cancel the old connection and try again */
927: peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL);
928: }
929: }
930:
931: conn = peer_connection_new(od, type, bn);
932: conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
933: conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
934: aim_icbm_makecookie(conn->cookie);
935:
936: peer_connection_trynext(conn);
937: }
938:
939: /**
940: * Someone else wants to establish a peer connection with us,
941: * and we said yes.
942: */
943: static void
944: peer_connection_got_proposition_yes_cb(gpointer data, gint id)
945: {
946: PeerConnection *conn;
947:
948: conn = data;
949:
950: conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
951: peer_connection_trynext(conn);
952: }
953:
954: /**
955: * Someone else wants to establish a peer connection with us,
956: * and we said no.
957: *
958: * "Well, one time my friend asked me if I wanted to play the
959: * piccolo. But I said no."
960: */
961: static void
962: peer_connection_got_proposition_no_cb(gpointer data, gint id)
963: {
964: PeerConnection *conn;
965:
966: conn = data;
967:
968: aim_im_denytransfer(conn->od, conn->bn, conn->cookie,
969: AIM_TRANSFER_DENY_DECLINE);
970: peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
971: }
972:
973: /**
974: * Someone else wants to establish a peer connection with us.
975: */
976: void
977: peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
978: {
979: PurpleConnection *gc;
980: PurpleAccount *account;
981: PeerConnection *conn;
982: gchar *buf;
983:
984: gc = od->gc;
985: account = purple_connection_get_account(gc);
986:
987: /*
988: * If we have a connection with this same cookie then they are
989: * probably just telling us they weren't able to connect to us
990: * and we should try connecting to them, instead. Or they want
991: * to go through a proxy.
992: */
993: conn = peer_connection_find_by_cookie(od, bn, args->cookie);
994: if ((conn != NULL) && (conn->type == args->type))
995: {
996: purple_debug_info("oscar", "Remote user wants to try a "
997: "different connection method\n");
998: g_free(conn->proxyip);
999: g_free(conn->clientip);
1000: g_free(conn->verifiedip);
1001: if (args->use_proxy)
1002: conn->proxyip = g_strdup(args->proxyip);
1003: else
1004: conn->proxyip = NULL;
1005: conn->verifiedip = g_strdup(args->verifiedip);
1006: conn->clientip = g_strdup(args->clientip);
1007: conn->port = args->port;
1008: conn->use_proxy |= args->use_proxy;
1009: conn->lastrequestnumber++;
1010: peer_connection_trynext(conn);
1011: return;
1012: }
1013:
1014: /* If this is a direct IM, then close any existing session */
1015: if (args->type == OSCAR_CAPABILITY_DIRECTIM)
1016: {
1017: conn = peer_connection_find_by_type(od, bn, args->type);
1018: if (conn != NULL)
1019: {
1020: /* Close the old direct IM and start a new one */
1021: purple_debug_info("oscar", "Received new direct IM request "
1022: "from %s. Destroying old connection.\n", bn);
1023: peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
1024: }
1025: }
1026:
1027: /* Check for proper arguments */
1028: if (args->type == OSCAR_CAPABILITY_SENDFILE)
1029: {
1030: if ((args->info.sendfile.filename == NULL) ||
1031: (args->info.sendfile.totsize == 0) ||
1032: (args->info.sendfile.totfiles == 0))
1033: {
1034: purple_debug_warning("oscar",
1035: "%s tried to send you a file with incomplete "
1036: "information.\n", bn);
1037: return;
1038: }
1039: }
1040:
1041: conn = peer_connection_new(od, args->type, bn);
1042: memcpy(conn->cookie, args->cookie, 8);
1043: if (args->use_proxy)
1044: conn->proxyip = g_strdup(args->proxyip);
1045: conn->clientip = g_strdup(args->clientip);
1046: conn->verifiedip = g_strdup(args->verifiedip);
1047: conn->port = args->port;
1048: conn->use_proxy |= args->use_proxy;
1049: conn->lastrequestnumber++;
1050:
1051: if (args->type == OSCAR_CAPABILITY_DIRECTIM)
1052: {
1053: buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
1054: bn, purple_account_get_username(account));
1055:
1056: purple_request_action(conn, NULL, buf,
1057: _("This requires a direct connection between "
1058: "the two computers and is necessary for IM "
1059: "Images. Because your IP address will be "
1060: "revealed, this may be considered a privacy "
1061: "risk."),
1062: PURPLE_DEFAULT_ACTION_NONE,
1063: account, bn, NULL,
1064: conn, 2,
1065: _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
1066: _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
1067: }
1068: else if (args->type == OSCAR_CAPABILITY_SENDFILE)
1069: {
1070: gchar *filename;
1071:
1072: conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
1073: if (conn->xfer)
1074: {
1075: conn->xfer->data = conn;
1076: purple_xfer_ref(conn->xfer);
1077: purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
1078:
1079: /* Set the file name */
1080: if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
1081: filename = g_strdup(args->info.sendfile.filename);
1082: else
1083: filename = purple_utf8_salvage(args->info.sendfile.filename);
1084:
1085: if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
1086: {
1087: /*
1088: * If they are sending us a directory then the last character
1089: * of the file name will be an asterisk. We don't want to
1090: * save stuff to a directory named "*" so we remove the
1091: * asterisk from the file name.
1092: */
1093: char *tmp = strrchr(filename, '\\');
1094: if ((tmp != NULL) && (tmp[1] == '*'))
1095: tmp[0] = '\0';
1096: }
1097: purple_xfer_set_filename(conn->xfer, filename);
1098: g_free(filename);
1099:
1100: /*
1101: * Set the message, unless this is the dummy message from an
1102: * ICQ client or an empty message from an AIM client.
1103: * TODO: Maybe we should strip HTML and then see if strlen>0?
1104: */
1105: if ((message != NULL) &&
1106: (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
1107: (g_ascii_strcasecmp(message, "<HTML>") != 0))
1108: {
1109: purple_xfer_set_message(conn->xfer, message);
1110: }
1111:
1112: /* Setup our I/O op functions */
1113: purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
1114: purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
1115: purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
1116: purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
1117: purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);
1118:
1119: /* Now perform the request */
1120: purple_xfer_request(conn->xfer);
1121: }
1122: }
1123: }
1124:
1125: /*******************************************************************/
1126: /* End code for establishing a peer connection */
1127: /*******************************************************************/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>