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
23 #include "eventloop.h"
28 #include <sys/socket.h>
29 #include <netinet/in.h>
33 * This sends a channel 1 SNAC containing the FLAP version.
34 * The FLAP version is sent by itself at the beginning of every
35 * connection to a FLAP server. It is always the very first
36 * packet sent by both the server and the client after the SYN,
37 * SYN/ACK, ACK handshake.
40 flap_connection_send_version(OscarData
*od
, FlapConnection
*conn
)
44 frame
= flap_frame_new(od
, 0x01, 4);
45 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
46 flap_connection_send(conn
, frame
);
50 * This sends a channel 1 FLAP containing the FLAP version and
51 * the authentication cookie. This is sent when connecting to
52 * any FLAP server after the initial connection to the auth
53 * server. It is always the very first packet sent by both the
54 * server and the client after the SYN, SYN/ACK, ACK handshake.
57 flap_connection_send_version_with_cookie(OscarData
*od
, FlapConnection
*conn
, guint16 length
, const guint8
*chipsahoy
)
60 GSList
*tlvlist
= NULL
;
62 frame
= flap_frame_new(od
, 0x01, 4 + 2 + 2 + length
);
63 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
64 aim_tlvlist_add_raw(&tlvlist
, 0x0006, length
, chipsahoy
);
65 aim_tlvlist_write(&frame
->data
, &tlvlist
);
66 aim_tlvlist_free(tlvlist
);
68 flap_connection_send(conn
, frame
);
72 flap_connection_send_version_with_cookie_and_clientinfo(OscarData
*od
, FlapConnection
*conn
, guint16 length
, const guint8
*chipsahoy
, ClientInfo
*ci
, gboolean allow_multiple_logins
)
75 GSList
*tlvlist
= NULL
;
77 frame
= flap_frame_new(od
, 0x01, 1152 + length
);
79 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
80 aim_tlvlist_add_raw(&tlvlist
, 0x0006, length
, chipsahoy
);
82 if (ci
->clientstring
!= NULL
)
83 aim_tlvlist_add_str(&tlvlist
, 0x0003, ci
->clientstring
);
85 gchar
*clientstring
= oscar_get_clientstring();
86 aim_tlvlist_add_str(&tlvlist
, 0x0003, clientstring
);
89 aim_tlvlist_add_16(&tlvlist
, 0x0017, (guint16
)ci
->major
);
90 aim_tlvlist_add_16(&tlvlist
, 0x0018, (guint16
)ci
->minor
);
91 aim_tlvlist_add_16(&tlvlist
, 0x0019, (guint16
)ci
->point
);
92 aim_tlvlist_add_16(&tlvlist
, 0x001a, (guint16
)ci
->build
);
93 aim_tlvlist_add_8(&tlvlist
, 0x004a, (allow_multiple_logins
? 0x01 : 0x03));
95 aim_tlvlist_write(&frame
->data
, &tlvlist
);
97 aim_tlvlist_free(tlvlist
);
99 flap_connection_send(conn
, frame
);
102 static struct rateclass
*
103 flap_connection_get_rateclass(FlapConnection
*conn
, guint16 family
, guint16 subtype
)
108 key
= GUINT_TO_POINTER((family
<< 16) + subtype
);
109 rateclass
= g_hash_table_lookup(conn
->rateclass_members
, key
);
110 if (rateclass
!= NULL
)
113 return conn
->default_rateclass
;
117 * Attempt to calculate what our new current average would be if we
118 * were to send a SNAC in this rateclass at the given time.
121 rateclass_get_new_current(FlapConnection
*conn
, struct rateclass
*rateclass
, struct timeval
*now
)
123 unsigned long timediff
; /* In milliseconds */
126 /* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
127 timediff
= (now
->tv_sec
- rateclass
->last
.tv_sec
) * 1000 + (now
->tv_usec
- rateclass
->last
.tv_usec
) / 1000;
128 current
= ((rateclass
->current
* (rateclass
->windowsize
- 1)) + timediff
) / rateclass
->windowsize
;
130 return MIN(current
, rateclass
->max
);
134 * Attempt to send the contents of a given queue
136 * @return TRUE if the queue was completely emptied or was initially
137 * empty; FALSE if rate limiting prevented it from being
140 static gboolean
flap_connection_send_snac_queue(FlapConnection
*conn
, struct timeval now
, GQueue
*queue
)
142 while (!g_queue_is_empty(queue
))
144 QueuedSnac
*queued_snac
;
145 struct rateclass
*rateclass
;
147 queued_snac
= g_queue_peek_head(queue
);
149 rateclass
= flap_connection_get_rateclass(conn
, queued_snac
->family
, queued_snac
->subtype
);
150 if (rateclass
!= NULL
)
154 new_current
= rateclass_get_new_current(conn
, rateclass
, &now
);
156 if (rateclass
->dropping_snacs
|| new_current
<= rateclass
->alert
)
157 /* Not ready to send this SNAC yet--keep waiting. */
160 rateclass
->current
= new_current
;
161 rateclass
->last
.tv_sec
= now
.tv_sec
;
162 rateclass
->last
.tv_usec
= now
.tv_usec
;
165 flap_connection_send(conn
, queued_snac
->frame
);
167 g_queue_pop_head(queue
);
170 /* We emptied the queue */
174 static gboolean
flap_connection_send_queued(gpointer data
)
176 FlapConnection
*conn
;
180 gettimeofday(&now
, NULL
);
182 purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n",
183 (conn
->queued_snacs
? conn
->queued_snacs
->length
: 0),
184 (conn
->queued_lowpriority_snacs
? conn
->queued_lowpriority_snacs
->length
: 0),
186 if (!conn
->queued_snacs
|| flap_connection_send_snac_queue(conn
, now
, conn
->queued_snacs
)) {
187 if (!conn
->queued_lowpriority_snacs
|| flap_connection_send_snac_queue(conn
, now
, conn
->queued_lowpriority_snacs
)) {
188 /* Both queues emptied. */
189 conn
->queued_timeout
= 0;
194 /* We couldn't send all our SNACs. Keep trying */
199 * This sends a channel 2 FLAP containing a SNAC. The SNAC family and
200 * subtype are looked up in the rate info for this connection, and if
201 * sending this SNAC will induce rate limiting then we delay sending
202 * of the SNAC by putting it into an outgoing holding queue.
204 * @param data The optional bytestream that makes up the data portion
205 * of this SNAC. For empty SNACs this should be NULL.
206 * @param high_priority If TRUE, the SNAC will be queued normally if
207 * needed. If FALSE, it will be queued separately, to be sent
208 * only if all high priority SNACs have been sent.
211 flap_connection_send_snac_with_priority(OscarData
*od
, FlapConnection
*conn
, guint16 family
, const guint16 subtype
, aim_snacid_t snacid
, ByteStream
*data
, gboolean high_priority
)
215 gboolean enqueue
= FALSE
;
216 struct rateclass
*rateclass
;
218 length
= data
!= NULL
? data
->offset
: 0;
220 frame
= flap_frame_new(od
, 0x02, 10 + length
);
221 aim_putsnac(&frame
->data
, family
, subtype
, snacid
);
225 byte_stream_rewind(data
);
226 byte_stream_putbs(&frame
->data
, data
, length
);
229 if (conn
->queued_timeout
!= 0)
231 else if ((rateclass
= flap_connection_get_rateclass(conn
, family
, subtype
)) != NULL
)
236 gettimeofday(&now
, NULL
);
237 new_current
= rateclass_get_new_current(conn
, rateclass
, &now
);
239 if (rateclass
->dropping_snacs
|| new_current
<= rateclass
->alert
)
241 purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn
, new_current
, rateclass
->alert
);
247 rateclass
->current
= new_current
;
248 rateclass
->last
.tv_sec
= now
.tv_sec
;
249 rateclass
->last
.tv_usec
= now
.tv_usec
;
255 /* We've been sending too fast, so delay this message */
256 QueuedSnac
*queued_snac
;
258 queued_snac
= g_new(QueuedSnac
, 1);
259 queued_snac
->family
= family
;
260 queued_snac
->subtype
= subtype
;
261 queued_snac
->frame
= frame
;
264 if (!conn
->queued_snacs
)
265 conn
->queued_snacs
= g_queue_new();
266 g_queue_push_tail(conn
->queued_snacs
, queued_snac
);
268 if (!conn
->queued_lowpriority_snacs
)
269 conn
->queued_lowpriority_snacs
= g_queue_new();
270 g_queue_push_tail(conn
->queued_lowpriority_snacs
, queued_snac
);
273 if (conn
->queued_timeout
== 0)
274 conn
->queued_timeout
= purple_timeout_add(500, flap_connection_send_queued
, conn
);
279 flap_connection_send(conn
, frame
);
283 flap_connection_send_snac(OscarData
*od
, FlapConnection
*conn
, guint16 family
, const guint16 subtype
, aim_snacid_t snacid
, ByteStream
*data
)
285 flap_connection_send_snac_with_priority(od
, conn
, family
, subtype
, snacid
, data
, TRUE
);
289 * This sends an empty channel 4 FLAP. This is sent to signify
290 * that we're logging off. This shouldn't really be necessary--
291 * usually the AIM server will detect that the TCP connection has
292 * been destroyed--but it's good practice.
295 flap_connection_send_close(OscarData
*od
, FlapConnection
*conn
)
299 frame
= flap_frame_new(od
, 0x04, 0);
300 flap_connection_send(conn
, frame
);
304 * This sends an empty channel 5 FLAP. This is used as a keepalive
305 * packet in FLAP connections. WinAIM 4.x and higher send these
306 * _every minute_ to keep the connection alive.
309 flap_connection_send_keepalive(OscarData
*od
, FlapConnection
*conn
)
313 frame
= flap_frame_new(od
, 0x05, 0);
314 flap_connection_send(conn
, frame
);
316 /* clean out SNACs over 60sec old */
317 aim_cleansnacs(od
, 60);
321 * Allocate a new empty connection structure.
323 * @param od The oscar session associated with this connection.
324 * @param type Type of connection to create
326 * @return Returns the new connection structure.
329 flap_connection_new(OscarData
*od
, int type
)
331 FlapConnection
*conn
;
333 conn
= g_new0(FlapConnection
, 1);
335 conn
->buffer_outgoing
= purple_circular_buffer_new(0);
339 conn
->rateclass_members
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
341 od
->oscar_connections
= g_slist_prepend(od
->oscar_connections
, conn
);
347 * Close (but not free) a connection.
349 * This cancels any currently pending connection attempt,
350 * closes any open fd and frees the auth cookie.
352 * @param conn The connection to close.
355 flap_connection_close(OscarData
*od
, FlapConnection
*conn
)
357 if (conn
->connect_data
!= NULL
)
359 purple_proxy_connect_cancel(conn
->connect_data
);
360 conn
->connect_data
= NULL
;
363 if (conn
->gsc
!= NULL
&& conn
->gsc
->connect_data
!= NULL
)
365 purple_ssl_close(conn
->gsc
);
369 if (conn
->new_conn_data
!= NULL
)
371 if (conn
->type
== SNAC_FAMILY_CHAT
)
373 oscar_chat_destroy(conn
->new_conn_data
);
374 conn
->new_conn_data
= NULL
;
378 if ((conn
->fd
>= 0 || conn
->gsc
!= NULL
)
379 && conn
->type
== SNAC_FAMILY_LOCATE
)
380 flap_connection_send_close(od
, conn
);
382 if (conn
->watcher_incoming
!= 0)
384 purple_input_remove(conn
->watcher_incoming
);
385 conn
->watcher_incoming
= 0;
388 if (conn
->watcher_outgoing
!= 0)
390 purple_input_remove(conn
->watcher_outgoing
);
391 conn
->watcher_outgoing
= 0;
400 if (conn
->gsc
!= NULL
)
402 purple_ssl_close(conn
->gsc
);
406 g_free(conn
->buffer_incoming
.data
.data
);
407 conn
->buffer_incoming
.data
.data
= NULL
;
409 g_object_unref(G_OBJECT(conn
->buffer_outgoing
));
410 conn
->buffer_outgoing
= NULL
;
416 * @param frame The frame to free.
419 flap_frame_destroy(FlapFrame
*frame
)
421 g_free(frame
->data
.data
);
426 flap_connection_destroy_cb(gpointer data
)
428 FlapConnection
*conn
;
430 PurpleAccount
*account
;
431 aim_rxcallback_t userfunc
;
434 /* Explicitly added for debugging #5927. Don't re-order this, only
435 * consider removing it.
437 purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn
);
440 account
= purple_connection_get_account(od
->gc
);
442 purple_debug_info("oscar", "Destroying oscar connection (%p) of "
443 "type 0x%04hx. Disconnect reason is %d\n", conn
,
444 conn
->type
, conn
->disconnect_reason
);
446 od
->oscar_connections
= g_slist_remove(od
->oscar_connections
, conn
);
448 if ((userfunc
= aim_callhandler(od
, AIM_CB_FAM_SPECIAL
, AIM_CB_SPECIAL_CONNERR
)))
449 userfunc(od
, conn
, NULL
, conn
->disconnect_code
, conn
->error_message
);
452 * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
453 * we should try to request one instead of disconnecting.
455 if (!purple_account_is_disconnecting(account
) && ((od
->oscar_connections
== NULL
)
456 || (!flap_connection_getbytype(od
, SNAC_FAMILY_LOCATE
))))
458 /* No more FLAP connections! Sign off this PurpleConnection! */
460 PurpleConnectionError reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
462 if (conn
->disconnect_code
== 0x0001) {
463 reason
= PURPLE_CONNECTION_ERROR_NAME_IN_USE
;
464 tmp
= g_strdup(_("You have signed on from another location"));
465 if (!purple_account_get_remember_password(account
))
466 purple_account_set_password(account
, NULL
, NULL
, NULL
);
467 } else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_CLOSED
)
468 tmp
= g_strdup(_("Server closed the connection"));
469 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_LOST_CONNECTION
)
470 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
471 conn
->error_message
);
472 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_INVALID_DATA
)
473 tmp
= g_strdup(_("Received invalid data on connection with server"));
474 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_COULD_NOT_CONNECT
)
475 tmp
= g_strdup_printf(_("Unable to connect: %s"),
476 conn
->error_message
);
479 * We shouldn't print a message for some disconnect_reasons.
480 * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
486 purple_connection_error(od
->gc
, reason
, tmp
);
491 flap_connection_close(od
, conn
);
493 g_free(conn
->error_message
);
494 g_free(conn
->cookie
);
497 * Free conn->internal, if necessary
499 if (conn
->type
== SNAC_FAMILY_CHAT
)
500 flap_connection_destroy_chat(od
, conn
);
502 g_slist_free(conn
->groups
);
503 while (conn
->rateclasses
!= NULL
)
505 g_free(conn
->rateclasses
->data
);
506 conn
->rateclasses
= g_slist_delete_link(conn
->rateclasses
, conn
->rateclasses
);
509 g_hash_table_destroy(conn
->rateclass_members
);
511 if (conn
->queued_snacs
) {
512 while (!g_queue_is_empty(conn
->queued_snacs
))
514 QueuedSnac
*queued_snac
;
515 queued_snac
= g_queue_pop_head(conn
->queued_snacs
);
516 flap_frame_destroy(queued_snac
->frame
);
519 g_queue_free(conn
->queued_snacs
);
522 if (conn
->queued_lowpriority_snacs
) {
523 while (!g_queue_is_empty(conn
->queued_lowpriority_snacs
))
525 QueuedSnac
*queued_snac
;
526 queued_snac
= g_queue_pop_head(conn
->queued_lowpriority_snacs
);
527 flap_frame_destroy(queued_snac
->frame
);
530 g_queue_free(conn
->queued_lowpriority_snacs
);
533 if (conn
->queued_timeout
> 0)
534 purple_timeout_remove(conn
->queued_timeout
);
542 * See the comments for the parameters of
543 * flap_connection_schedule_destroy().
546 flap_connection_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
548 if (conn
->destroy_timeout
!= 0)
549 purple_timeout_remove(conn
->destroy_timeout
);
550 conn
->disconnect_reason
= reason
;
551 g_free(conn
->error_message
);
552 conn
->error_message
= g_strdup(error_message
);
553 flap_connection_destroy_cb(conn
);
557 * Schedule Purple to destroy the given FlapConnection as soon as we
558 * return control back to the program's main loop. We must do this
559 * if we want to destroy the connection but we are still using it
562 * @param reason The reason for the disconnection.
563 * @param error_message A brief error message that gives more detail
564 * regarding the reason for the disconnecting. This should
565 * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
566 * in which case it should contain the value of g_strerror(errno),
567 * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
568 * should contain the error_message passed back from the call
569 * to purple_proxy_connect().
572 flap_connection_schedule_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
574 if (conn
->destroy_timeout
!= 0)
575 /* Already taken care of */
578 purple_debug_info("oscar", "Scheduling destruction of FLAP "
579 "connection %p of type 0x%04hx\n", conn
, conn
->type
);
580 conn
->disconnect_reason
= reason
;
581 g_free(conn
->error_message
);
582 conn
->error_message
= g_strdup(error_message
);
583 conn
->destroy_timeout
= purple_timeout_add(0, flap_connection_destroy_cb
, conn
);
587 * In OSCAR, every connection has a set of SNAC groups associated
588 * with it. These are the groups that you can send over this connection
589 * without being guaranteed a "Not supported" SNAC error.
591 * The grand theory of things says that these associations transcend
592 * what libfaim calls "connection types" (conn->type). You can probably
593 * see the elegance here, but since I want to revel in it for a bit, you
594 * get to hear it all spelled out.
596 * So let us say that you have your core BOS connection running. One
597 * of your modules has just given you a SNAC of the group 0x0004 to send
598 * you. Maybe an IM destined for some twit in Greenland. So you start
599 * at the top of your connection list, looking for a connection that
600 * claims to support group 0x0004. You find one. Why, that neat BOS
601 * connection of yours can do that. So you send it on its way.
603 * Now, say, that fellow from Greenland has friends and they all want to
604 * meet up with you in a lame chat room. This has landed you a SNAC
605 * in the family 0x000e and you have to admit you're a bit lost. You've
606 * searched your connection list for someone who wants to make your life
607 * easy and deliver this SNAC for you, but there isn't one there.
609 * Here comes the good bit. Without even letting anyone know, particularly
610 * the module that decided to send this SNAC, and definitely not that twit
611 * in Greenland, you send out a service request. In this request, you have
612 * marked the need for a connection supporting group 0x000e. A few seconds
613 * later, you receive a service redirect with an IP address and a cookie in
614 * it. Great, you say. Now I have something to do. Off you go, making
615 * that connection. One of the first things you get from this new server
616 * is a message saying that indeed it does support the group you were looking
617 * for. So you continue and send rate confirmation and all that.
619 * Then you remember you had that SNAC to send, and now you have a means to
620 * do it, and you do, and everyone is happy. Except the Greenlander, who is
621 * still stuck in the bitter cold.
623 * Oh, and this is useful for building the Migration SNACs, too. In the
624 * future, this may help convince me to implement rate limit mitigation
625 * for real. We'll see.
627 * Just to make me look better, I'll say that I've known about this great
628 * scheme for quite some time now. But I still haven't convinced myself
629 * to make libfaim work that way. It would take a fair amount of effort,
630 * and probably some client API changes as well. (Whenever I don't want
631 * to do something, I just say it would change the client API. Then I
632 * instantly have a couple of supporters of not doing it.)
634 * Generally, addgroup is only called by the internal handling of the
635 * server ready SNAC. So if you want to do something before that, you'll
636 * have to be more creative. That is done rather early, though, so I don't
637 * think you have to worry about it. Unless you're me. I care deeply
638 * about such inane things.
643 * Find a FlapConnection that supports the given oscar
647 flap_connection_findbygroup(OscarData
*od
, guint16 group
)
651 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
653 FlapConnection
*conn
;
658 for (l
= conn
->groups
; l
!= NULL
; l
= l
->next
)
660 if (GPOINTER_TO_UINT(l
->data
) == group
)
669 * Locates a connection of the specified type in the
672 * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
674 * @param od The session to search.
675 * @param type The type of connection to look for.
677 * @return Returns the first connection found of the given target type,
678 * or NULL if none could be found.
681 flap_connection_getbytype(OscarData
*od
, int type
)
685 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
687 FlapConnection
*conn
;
689 if ((conn
->type
== type
) && (conn
->connected
))
697 flap_connection_getbytype_all(OscarData
*od
, int type
)
701 for (cur
= od
->oscar_connections
; cur
; cur
= cur
->next
)
703 FlapConnection
*conn
;
705 if (conn
->type
== type
)
713 * Allocate a new FLAP frame.
715 * @param channel The FLAP channel. This is almost always 2.
718 flap_frame_new(OscarData
*od
, guint16 channel
, int datalen
)
722 frame
= g_new0(FlapFrame
, 1);
723 frame
->channel
= channel
;
726 byte_stream_new(&frame
->data
, datalen
);
732 parse_snac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
737 if (byte_stream_bytes_left(&frame
->data
) < 10)
740 snac
.family
= byte_stream_get16(&frame
->data
);
741 snac
.subtype
= byte_stream_get16(&frame
->data
);
742 snac
.flags
= byte_stream_get16(&frame
->data
);
743 snac
.id
= byte_stream_get32(&frame
->data
);
745 /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
746 if (snac
.flags
& 0x0001) {
748 * This means the SNAC will be followed by another SNAC with
749 * related information. We don't need to do anything about
753 if (snac
.flags
& 0x8000) {
755 * This packet contains the version of the family that this SNAC is
756 * in. You get this when your SSI module is version 2 or higher.
757 * For now we have no need for this, but you could always save
758 * it as a part of aim_modnsac_t, or something. The format is...
759 * 2 byte length of total mini-header (which is 6 bytes), then TLV
760 * of type 0x0001, length 0x0002, value is the 2 byte version
763 byte_stream_advance(&frame
->data
, byte_stream_get16(&frame
->data
));
766 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
768 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
769 (cur
->family
!= snac
.family
))
772 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
778 parse_fakesnac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
, guint16 family
, guint16 subtype
)
783 snac
.family
= family
;
784 snac
.subtype
= subtype
;
785 snac
.flags
= snac
.id
= 0;
787 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
789 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
790 (cur
->family
!= snac
.family
))
793 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
799 parse_flap_ch4(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
804 if (byte_stream_bytes_left(&frame
->data
) == 0) {
805 /* XXX should do something with this */
809 /* An ICQ account is logging in */
810 if (conn
->type
== SNAC_FAMILY_AUTH
)
812 parse_fakesnac(od
, conn
, frame
, 0x0017, 0x0003);
816 tlvlist
= aim_tlvlist_read(&frame
->data
);
818 if (aim_tlv_gettlv(tlvlist
, 0x0009, 1))
819 conn
->disconnect_code
= aim_tlv_get16(tlvlist
, 0x0009, 1);
821 if (aim_tlv_gettlv(tlvlist
, 0x000b, 1))
822 msg
= aim_tlv_getstr(tlvlist
, 0x000b, 1);
825 * The server ended this FLAP connnection, so let's be nice and
826 * close the physical TCP connection
828 flap_connection_schedule_destroy(conn
,
829 OSCAR_DISCONNECT_REMOTE_CLOSED
, msg
);
831 aim_tlvlist_free(tlvlist
);
837 * Takes a new incoming FLAP frame and sends it to the appropriate
838 * handler function to be parsed.
841 parse_flap(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
843 if (frame
->channel
== 0x01) {
844 guint32 flap_version
= byte_stream_get32(&frame
->data
);
845 if (flap_version
!= 0x00000001)
848 purple_debug_warning("oscar", "Expecting FLAP version "
849 "0x00000001 but received FLAP version %08x. Closing connection.\n",
851 flap_connection_schedule_destroy(conn
,
852 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
855 conn
->connected
= TRUE
;
857 } else if (frame
->channel
== 0x02) {
858 parse_snac(od
, conn
, frame
);
860 } else if (frame
->channel
== 0x04) {
861 parse_flap_ch4(od
, conn
, frame
);
863 } else if (frame
->channel
== 0x05) {
864 /* TODO: Reset our keepalive watchdog? */
870 * Read in all available data on the socket for a given connection.
871 * All complete FLAPs handled immedate after they're received.
872 * Incomplete FLAP data is stored locally and appended to the next
873 * time this callback is triggered.
875 * This is called by flap_connection_recv_cb and
876 * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
879 flap_connection_recv(FlapConnection
*conn
)
885 /* Read data until we run out of data and break out of the loop */
888 /* Start reading a new FLAP */
889 if (conn
->buffer_incoming
.data
.data
== NULL
)
891 buf
= conn
->header
+ conn
->header_received
;
892 buflen
= 6 - conn
->header_received
;
894 /* Read the first 6 bytes (the FLAP header) */
896 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
898 read
= recv(conn
->fd
, buf
, buflen
, 0);
900 /* Check if the FLAP server closed the connection */
903 flap_connection_schedule_destroy(conn
,
904 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
908 /* If there was an error then close the connection */
911 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
916 flap_connection_schedule_destroy(conn
,
917 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
920 purple_connection_update_last_received(conn
->od
->gc
);
922 /* If we don't even have a complete FLAP header then do nothing */
923 conn
->header_received
+= read
;
924 if (conn
->header_received
< 6)
927 /* All FLAP frames must start with the byte 0x2a */
928 if (aimutil_get8(&conn
->header
[0]) != 0x2a)
930 flap_connection_schedule_destroy(conn
,
931 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
935 /* Initialize a new temporary FlapFrame for incoming data */
936 conn
->buffer_incoming
.channel
= aimutil_get8(&conn
->header
[1]);
937 conn
->buffer_incoming
.seqnum
= aimutil_get16(&conn
->header
[2]);
938 conn
->buffer_incoming
.data
.len
= aimutil_get16(&conn
->header
[4]);
939 conn
->buffer_incoming
.data
.data
= g_new(guint8
, conn
->buffer_incoming
.data
.len
);
940 conn
->buffer_incoming
.data
.offset
= 0;
943 buflen
= conn
->buffer_incoming
.data
.len
- conn
->buffer_incoming
.data
.offset
;
946 buf
= &conn
->buffer_incoming
.data
.data
[conn
->buffer_incoming
.data
.offset
];
947 /* Read data into the temporary FlapFrame until it is complete */
949 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
951 read
= recv(conn
->fd
, buf
, buflen
, 0);
953 /* Check if the FLAP server closed the connection */
956 flap_connection_schedule_destroy(conn
,
957 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
963 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
968 flap_connection_schedule_destroy(conn
,
969 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
973 conn
->buffer_incoming
.data
.offset
+= read
;
974 if (conn
->buffer_incoming
.data
.offset
< conn
->buffer_incoming
.data
.len
)
975 /* Waiting for more data to arrive */
979 /* We have a complete FLAP! Handle it and continue reading */
980 byte_stream_rewind(&conn
->buffer_incoming
.data
);
981 parse_flap(conn
->od
, conn
, &conn
->buffer_incoming
);
982 conn
->lastactivity
= time(NULL
);
984 g_free(conn
->buffer_incoming
.data
.data
);
985 conn
->buffer_incoming
.data
.data
= NULL
;
987 conn
->header_received
= 0;
992 flap_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
994 FlapConnection
*conn
= data
;
996 flap_connection_recv(conn
);
1000 flap_connection_recv_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
1002 FlapConnection
*conn
= data
;
1004 flap_connection_recv(conn
);
1008 * @param source When this function is called as a callback source is
1009 * set to the fd that triggered the callback. But this function
1010 * is also called directly from flap_connection_send_byte_stream(),
1011 * in which case source will be -1. So don't use source--use
1012 * conn->gsc or conn->fd instead.
1015 send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
1017 FlapConnection
*conn
;
1019 const gchar
*output
= NULL
;
1022 writelen
= purple_circular_buffer_get_max_read(conn
->buffer_outgoing
);
1023 output
= purple_circular_buffer_get_output(conn
->buffer_outgoing
);
1027 purple_input_remove(conn
->watcher_outgoing
);
1028 conn
->watcher_outgoing
= 0;
1033 ret
= purple_ssl_write(conn
->gsc
, output
, writelen
);
1035 ret
= send(conn
->fd
, output
, writelen
, 0);
1038 if (ret
< 0 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
))
1043 purple_input_remove(conn
->watcher_outgoing
);
1044 conn
->watcher_outgoing
= 0;
1046 purple_ssl_close(conn
->gsc
);
1052 flap_connection_schedule_destroy(conn
,
1053 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
1057 purple_circular_buffer_mark_read(conn
->buffer_outgoing
, ret
);
1061 flap_connection_send_byte_stream(ByteStream
*bs
, FlapConnection
*conn
, size_t count
)
1066 /* Make sure we don't send past the end of the bs */
1067 if (count
> byte_stream_bytes_left(bs
))
1068 count
= byte_stream_bytes_left(bs
); /* truncate to remaining space */
1073 /* Add everything to our outgoing buffer */
1074 purple_circular_buffer_append(conn
->buffer_outgoing
, bs
->data
, count
);
1076 /* If we haven't already started writing stuff, then start the cycle */
1077 if (conn
->watcher_outgoing
== 0)
1080 conn
->watcher_outgoing
= purple_input_add(conn
->gsc
->fd
,
1081 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1082 send_cb(conn
, -1, 0);
1083 } else if (conn
->fd
>= 0) {
1084 conn
->watcher_outgoing
= purple_input_add(conn
->fd
,
1085 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1086 send_cb(conn
, -1, 0);
1092 sendframe_flap(FlapConnection
*conn
, FlapFrame
*frame
)
1095 int payloadlen
, bslen
;
1097 payloadlen
= byte_stream_curpos(&frame
->data
);
1099 byte_stream_new(&bs
, 6 + payloadlen
);
1102 byte_stream_put8(&bs
, 0x2a);
1103 byte_stream_put8(&bs
, frame
->channel
);
1104 byte_stream_put16(&bs
, frame
->seqnum
);
1105 byte_stream_put16(&bs
, payloadlen
);
1108 byte_stream_rewind(&frame
->data
);
1109 byte_stream_putbs(&bs
, &frame
->data
, payloadlen
);
1111 bslen
= byte_stream_curpos(&bs
);
1112 byte_stream_rewind(&bs
);
1113 flap_connection_send_byte_stream(&bs
, conn
, bslen
);
1115 byte_stream_destroy(&bs
);
1119 flap_connection_send(FlapConnection
*conn
, FlapFrame
*frame
)
1121 frame
->seqnum
= ++(conn
->seqnum_out
);
1122 sendframe_flap(conn
, frame
);
1123 flap_frame_destroy(frame
);