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>
37 * This sends a channel 1 SNAC containing the FLAP version.
38 * The FLAP version is sent by itself at the beginning of every
39 * connection to a FLAP server. It is always the very first
40 * packet sent by both the server and the client after the SYN,
41 * SYN/ACK, ACK handshake.
44 flap_connection_send_version(OscarData
*od
, FlapConnection
*conn
)
48 frame
= flap_frame_new(od
, 0x01, 4);
49 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
50 flap_connection_send(conn
, frame
);
54 * This sends a channel 1 FLAP containing the FLAP version and
55 * the authentication cookie. This is sent when connecting to
56 * any FLAP server after the initial connection to the auth
57 * server. It is always the very first packet sent by both the
58 * server and the client after the SYN, SYN/ACK, ACK handshake.
61 flap_connection_send_version_with_cookie(OscarData
*od
, FlapConnection
*conn
, guint16 length
, const guint8
*chipsahoy
)
64 GSList
*tlvlist
= NULL
;
66 frame
= flap_frame_new(od
, 0x01, 4 + 2 + 2 + length
);
67 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
68 aim_tlvlist_add_raw(&tlvlist
, 0x0006, length
, chipsahoy
);
69 aim_tlvlist_write(&frame
->data
, &tlvlist
);
70 aim_tlvlist_free(tlvlist
);
72 flap_connection_send(conn
, frame
);
76 flap_connection_send_version_with_cookie_and_clientinfo(OscarData
*od
, FlapConnection
*conn
, guint16 length
, const guint8
*chipsahoy
, ClientInfo
*ci
, gboolean allow_multiple_logins
)
79 GSList
*tlvlist
= NULL
;
81 frame
= flap_frame_new(od
, 0x01, 1152 + length
);
83 byte_stream_put32(&frame
->data
, 0x00000001); /* FLAP Version */
84 aim_tlvlist_add_raw(&tlvlist
, 0x0006, length
, chipsahoy
);
86 if (ci
->clientstring
!= NULL
)
87 aim_tlvlist_add_str(&tlvlist
, 0x0003, ci
->clientstring
);
89 gchar
*clientstring
= oscar_get_clientstring();
90 aim_tlvlist_add_str(&tlvlist
, 0x0003, clientstring
);
93 aim_tlvlist_add_16(&tlvlist
, 0x0017, (guint16
)ci
->major
);
94 aim_tlvlist_add_16(&tlvlist
, 0x0018, (guint16
)ci
->minor
);
95 aim_tlvlist_add_16(&tlvlist
, 0x0019, (guint16
)ci
->point
);
96 aim_tlvlist_add_16(&tlvlist
, 0x001a, (guint16
)ci
->build
);
97 aim_tlvlist_add_8(&tlvlist
, 0x004a, (allow_multiple_logins
? 0x01 : 0x03));
99 aim_tlvlist_write(&frame
->data
, &tlvlist
);
101 aim_tlvlist_free(tlvlist
);
103 flap_connection_send(conn
, frame
);
106 static struct rateclass
*
107 flap_connection_get_rateclass(FlapConnection
*conn
, guint16 family
, guint16 subtype
)
112 key
= GUINT_TO_POINTER((family
<< 16) + subtype
);
113 rateclass
= g_hash_table_lookup(conn
->rateclass_members
, key
);
114 if (rateclass
!= NULL
)
117 return conn
->default_rateclass
;
121 * Attempt to calculate what our new current average would be if we
122 * were to send a SNAC in this rateclass at the given time.
125 rateclass_get_new_current(FlapConnection
*conn
, struct rateclass
*rateclass
, struct timeval
*now
)
127 unsigned long timediff
; /* In milliseconds */
130 /* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
131 timediff
= (now
->tv_sec
- rateclass
->last
.tv_sec
) * 1000 + (now
->tv_usec
- rateclass
->last
.tv_usec
) / 1000;
132 current
= ((rateclass
->current
* (rateclass
->windowsize
- 1)) + timediff
) / rateclass
->windowsize
;
134 return MIN(current
, rateclass
->max
);
138 * Attempt to send the contents of a given queue
140 * @return TRUE if the queue was completely emptied or was initially
141 * empty; FALSE if rate limiting prevented it from being
144 static gboolean
flap_connection_send_snac_queue(FlapConnection
*conn
, struct timeval now
, GQueue
*queue
)
146 while (!g_queue_is_empty(queue
))
148 QueuedSnac
*queued_snac
;
149 struct rateclass
*rateclass
;
151 queued_snac
= g_queue_peek_head(queue
);
153 rateclass
= flap_connection_get_rateclass(conn
, queued_snac
->family
, queued_snac
->subtype
);
154 if (rateclass
!= NULL
)
158 new_current
= rateclass_get_new_current(conn
, rateclass
, &now
);
160 if (rateclass
->dropping_snacs
|| new_current
<= rateclass
->alert
)
161 /* Not ready to send this SNAC yet--keep waiting. */
164 rateclass
->current
= new_current
;
165 rateclass
->last
.tv_sec
= now
.tv_sec
;
166 rateclass
->last
.tv_usec
= now
.tv_usec
;
169 flap_connection_send(conn
, queued_snac
->frame
);
171 g_queue_pop_head(queue
);
174 /* We emptied the queue */
178 static gboolean
flap_connection_send_queued(gpointer data
)
180 FlapConnection
*conn
;
184 gettimeofday(&now
, NULL
);
186 purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n",
187 (conn
->queued_snacs
? conn
->queued_snacs
->length
: 0),
188 (conn
->queued_lowpriority_snacs
? conn
->queued_lowpriority_snacs
->length
: 0),
190 if (!conn
->queued_snacs
|| flap_connection_send_snac_queue(conn
, now
, conn
->queued_snacs
)) {
191 if (!conn
->queued_lowpriority_snacs
|| flap_connection_send_snac_queue(conn
, now
, conn
->queued_lowpriority_snacs
)) {
192 /* Both queues emptied. */
193 conn
->queued_timeout
= 0;
198 /* We couldn't send all our SNACs. Keep trying */
203 * This sends a channel 2 FLAP containing a SNAC. The SNAC family and
204 * subtype are looked up in the rate info for this connection, and if
205 * sending this SNAC will induce rate limiting then we delay sending
206 * of the SNAC by putting it into an outgoing holding queue.
208 * @param data The optional bytestream that makes up the data portion
209 * of this SNAC. For empty SNACs this should be NULL.
210 * @param high_priority If TRUE, the SNAC will be queued normally if
211 * needed. If FALSE, it will be queued separately, to be sent
212 * only if all high priority SNACs have been sent.
215 flap_connection_send_snac_with_priority(OscarData
*od
, FlapConnection
*conn
, guint16 family
, const guint16 subtype
, aim_snacid_t snacid
, ByteStream
*data
, gboolean high_priority
)
219 gboolean enqueue
= FALSE
;
220 struct rateclass
*rateclass
;
222 length
= data
!= NULL
? data
->offset
: 0;
224 frame
= flap_frame_new(od
, 0x02, 10 + length
);
225 aim_putsnac(&frame
->data
, family
, subtype
, snacid
);
229 byte_stream_rewind(data
);
230 byte_stream_putbs(&frame
->data
, data
, length
);
233 if (conn
->queued_timeout
!= 0)
235 else if ((rateclass
= flap_connection_get_rateclass(conn
, family
, subtype
)) != NULL
)
240 gettimeofday(&now
, NULL
);
241 new_current
= rateclass_get_new_current(conn
, rateclass
, &now
);
243 if (rateclass
->dropping_snacs
|| new_current
<= rateclass
->alert
)
245 purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn
, new_current
, rateclass
->alert
);
251 rateclass
->current
= new_current
;
252 rateclass
->last
.tv_sec
= now
.tv_sec
;
253 rateclass
->last
.tv_usec
= now
.tv_usec
;
259 /* We've been sending too fast, so delay this message */
260 QueuedSnac
*queued_snac
;
262 queued_snac
= g_new(QueuedSnac
, 1);
263 queued_snac
->family
= family
;
264 queued_snac
->subtype
= subtype
;
265 queued_snac
->frame
= frame
;
268 if (!conn
->queued_snacs
)
269 conn
->queued_snacs
= g_queue_new();
270 g_queue_push_tail(conn
->queued_snacs
, queued_snac
);
272 if (!conn
->queued_lowpriority_snacs
)
273 conn
->queued_lowpriority_snacs
= g_queue_new();
274 g_queue_push_tail(conn
->queued_lowpriority_snacs
, queued_snac
);
277 if (conn
->queued_timeout
== 0)
278 conn
->queued_timeout
= purple_timeout_add(500, flap_connection_send_queued
, conn
);
283 flap_connection_send(conn
, frame
);
287 flap_connection_send_snac(OscarData
*od
, FlapConnection
*conn
, guint16 family
, const guint16 subtype
, aim_snacid_t snacid
, ByteStream
*data
)
289 flap_connection_send_snac_with_priority(od
, conn
, family
, subtype
, snacid
, data
, TRUE
);
293 * This sends an empty channel 4 FLAP. This is sent to signify
294 * that we're logging off. This shouldn't really be necessary--
295 * usually the AIM server will detect that the TCP connection has
296 * been destroyed--but it's good practice.
299 flap_connection_send_close(OscarData
*od
, FlapConnection
*conn
)
303 frame
= flap_frame_new(od
, 0x04, 0);
304 flap_connection_send(conn
, frame
);
308 * This sends an empty channel 5 FLAP. This is used as a keepalive
309 * packet in FLAP connections. WinAIM 4.x and higher send these
310 * _every minute_ to keep the connection alive.
313 flap_connection_send_keepalive(OscarData
*od
, FlapConnection
*conn
)
317 frame
= flap_frame_new(od
, 0x05, 0);
318 flap_connection_send(conn
, frame
);
320 /* clean out SNACs over 60sec old */
321 aim_cleansnacs(od
, 60);
325 * Allocate a new empty connection structure.
327 * @param od The oscar session associated with this connection.
328 * @param type Type of connection to create
330 * @return Returns the new connection structure.
333 flap_connection_new(OscarData
*od
, int type
)
335 FlapConnection
*conn
;
337 conn
= g_new0(FlapConnection
, 1);
339 conn
->buffer_outgoing
= purple_circ_buffer_new(0);
343 conn
->rateclass_members
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
345 od
->oscar_connections
= g_slist_prepend(od
->oscar_connections
, conn
);
351 * Close (but not free) a connection.
353 * This cancels any currently pending connection attempt,
354 * closes any open fd and frees the auth cookie.
356 * @param conn The connection to close.
359 flap_connection_close(OscarData
*od
, FlapConnection
*conn
)
361 if (conn
->connect_data
!= NULL
)
363 purple_proxy_connect_cancel(conn
->connect_data
);
364 conn
->connect_data
= NULL
;
367 if (conn
->gsc
!= NULL
&& conn
->gsc
->connect_data
!= NULL
)
369 purple_ssl_close(conn
->gsc
);
373 if (conn
->new_conn_data
!= NULL
)
375 if (conn
->type
== SNAC_FAMILY_CHAT
)
377 oscar_chat_destroy(conn
->new_conn_data
);
378 conn
->new_conn_data
= NULL
;
382 if ((conn
->fd
>= 0 || conn
->gsc
!= NULL
)
383 && conn
->type
== SNAC_FAMILY_LOCATE
)
384 flap_connection_send_close(od
, conn
);
386 if (conn
->watcher_incoming
!= 0)
388 purple_input_remove(conn
->watcher_incoming
);
389 conn
->watcher_incoming
= 0;
392 if (conn
->watcher_outgoing
!= 0)
394 purple_input_remove(conn
->watcher_outgoing
);
395 conn
->watcher_outgoing
= 0;
404 if (conn
->gsc
!= NULL
)
406 purple_ssl_close(conn
->gsc
);
410 g_free(conn
->buffer_incoming
.data
.data
);
411 conn
->buffer_incoming
.data
.data
= NULL
;
413 purple_circ_buffer_destroy(conn
->buffer_outgoing
);
414 conn
->buffer_outgoing
= NULL
;
420 * @param frame The frame to free.
423 flap_frame_destroy(FlapFrame
*frame
)
425 g_free(frame
->data
.data
);
430 flap_connection_destroy_cb(gpointer data
)
432 FlapConnection
*conn
;
434 PurpleAccount
*account
;
435 aim_rxcallback_t userfunc
;
438 /* Explicitly added for debugging #5927. Don't re-order this, only
439 * consider removing it.
441 purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn
);
444 account
= purple_connection_get_account(od
->gc
);
446 purple_debug_info("oscar", "Destroying oscar connection (%p) of "
447 "type 0x%04hx. Disconnect reason is %d\n", conn
,
448 conn
->type
, conn
->disconnect_reason
);
450 od
->oscar_connections
= g_slist_remove(od
->oscar_connections
, conn
);
452 if ((userfunc
= aim_callhandler(od
, AIM_CB_FAM_SPECIAL
, AIM_CB_SPECIAL_CONNERR
)))
453 userfunc(od
, conn
, NULL
, conn
->disconnect_code
, conn
->error_message
);
456 * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
457 * we should try to request one instead of disconnecting.
459 if (!account
->disconnecting
&& ((od
->oscar_connections
== NULL
)
460 || (!flap_connection_getbytype(od
, SNAC_FAMILY_LOCATE
))))
462 /* No more FLAP connections! Sign off this PurpleConnection! */
464 PurpleConnectionError reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
466 if (conn
->disconnect_code
== 0x0001) {
467 reason
= PURPLE_CONNECTION_ERROR_NAME_IN_USE
;
468 tmp
= g_strdup(_("You have signed on from another location"));
469 if (!purple_account_get_remember_password(account
))
470 purple_account_set_password(account
, NULL
);
471 } else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_REMOTE_CLOSED
)
472 tmp
= g_strdup(_("Server closed the connection"));
473 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_LOST_CONNECTION
)
474 tmp
= g_strdup_printf(_("Lost connection with server: %s"),
475 conn
->error_message
);
476 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_INVALID_DATA
)
477 tmp
= g_strdup(_("Received invalid data on connection with server"));
478 else if (conn
->disconnect_reason
== OSCAR_DISCONNECT_COULD_NOT_CONNECT
)
479 tmp
= g_strdup_printf(_("Unable to connect: %s"),
480 conn
->error_message
);
483 * We shouldn't print a message for some disconnect_reasons.
484 * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
490 purple_connection_error_reason(od
->gc
, reason
, tmp
);
495 flap_connection_close(od
, conn
);
497 g_free(conn
->error_message
);
498 g_free(conn
->cookie
);
501 * Free conn->internal, if necessary
503 if (conn
->type
== SNAC_FAMILY_CHAT
)
504 flap_connection_destroy_chat(od
, conn
);
506 g_slist_free(conn
->groups
);
507 while (conn
->rateclasses
!= NULL
)
509 g_free(conn
->rateclasses
->data
);
510 conn
->rateclasses
= g_slist_delete_link(conn
->rateclasses
, conn
->rateclasses
);
513 g_hash_table_destroy(conn
->rateclass_members
);
515 if (conn
->queued_snacs
) {
516 while (!g_queue_is_empty(conn
->queued_snacs
))
518 QueuedSnac
*queued_snac
;
519 queued_snac
= g_queue_pop_head(conn
->queued_snacs
);
520 flap_frame_destroy(queued_snac
->frame
);
523 g_queue_free(conn
->queued_snacs
);
526 if (conn
->queued_lowpriority_snacs
) {
527 while (!g_queue_is_empty(conn
->queued_lowpriority_snacs
))
529 QueuedSnac
*queued_snac
;
530 queued_snac
= g_queue_pop_head(conn
->queued_lowpriority_snacs
);
531 flap_frame_destroy(queued_snac
->frame
);
534 g_queue_free(conn
->queued_lowpriority_snacs
);
537 if (conn
->queued_timeout
> 0)
538 purple_timeout_remove(conn
->queued_timeout
);
546 * See the comments for the parameters of
547 * flap_connection_schedule_destroy().
550 flap_connection_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
552 if (conn
->destroy_timeout
!= 0)
553 purple_timeout_remove(conn
->destroy_timeout
);
554 conn
->disconnect_reason
= reason
;
555 g_free(conn
->error_message
);
556 conn
->error_message
= g_strdup(error_message
);
557 flap_connection_destroy_cb(conn
);
561 * Schedule Purple to destroy the given FlapConnection as soon as we
562 * return control back to the program's main loop. We must do this
563 * if we want to destroy the connection but we are still using it
566 * @param reason The reason for the disconnection.
567 * @param error_message A brief error message that gives more detail
568 * regarding the reason for the disconnecting. This should
569 * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
570 * in which case it should contain the value of g_strerror(errno),
571 * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
572 * should contain the error_message passed back from the call
573 * to purple_proxy_connect().
576 flap_connection_schedule_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
578 if (conn
->destroy_timeout
!= 0)
579 /* Already taken care of */
582 purple_debug_info("oscar", "Scheduling destruction of FLAP "
583 "connection %p of type 0x%04hx\n", conn
, conn
->type
);
584 conn
->disconnect_reason
= reason
;
585 g_free(conn
->error_message
);
586 conn
->error_message
= g_strdup(error_message
);
587 conn
->destroy_timeout
= purple_timeout_add(0, flap_connection_destroy_cb
, conn
);
591 * In OSCAR, every connection has a set of SNAC groups associated
592 * with it. These are the groups that you can send over this connection
593 * without being guaranteed a "Not supported" SNAC error.
595 * The grand theory of things says that these associations transcend
596 * what libfaim calls "connection types" (conn->type). You can probably
597 * see the elegance here, but since I want to revel in it for a bit, you
598 * get to hear it all spelled out.
600 * So let us say that you have your core BOS connection running. One
601 * of your modules has just given you a SNAC of the group 0x0004 to send
602 * you. Maybe an IM destined for some twit in Greenland. So you start
603 * at the top of your connection list, looking for a connection that
604 * claims to support group 0x0004. You find one. Why, that neat BOS
605 * connection of yours can do that. So you send it on its way.
607 * Now, say, that fellow from Greenland has friends and they all want to
608 * meet up with you in a lame chat room. This has landed you a SNAC
609 * in the family 0x000e and you have to admit you're a bit lost. You've
610 * searched your connection list for someone who wants to make your life
611 * easy and deliver this SNAC for you, but there isn't one there.
613 * Here comes the good bit. Without even letting anyone know, particularly
614 * the module that decided to send this SNAC, and definitely not that twit
615 * in Greenland, you send out a service request. In this request, you have
616 * marked the need for a connection supporting group 0x000e. A few seconds
617 * later, you receive a service redirect with an IP address and a cookie in
618 * it. Great, you say. Now I have something to do. Off you go, making
619 * that connection. One of the first things you get from this new server
620 * is a message saying that indeed it does support the group you were looking
621 * for. So you continue and send rate confirmation and all that.
623 * Then you remember you had that SNAC to send, and now you have a means to
624 * do it, and you do, and everyone is happy. Except the Greenlander, who is
625 * still stuck in the bitter cold.
627 * Oh, and this is useful for building the Migration SNACs, too. In the
628 * future, this may help convince me to implement rate limit mitigation
629 * for real. We'll see.
631 * Just to make me look better, I'll say that I've known about this great
632 * scheme for quite some time now. But I still haven't convinced myself
633 * to make libfaim work that way. It would take a fair amount of effort,
634 * and probably some client API changes as well. (Whenever I don't want
635 * to do something, I just say it would change the client API. Then I
636 * instantly have a couple of supporters of not doing it.)
638 * Generally, addgroup is only called by the internal handling of the
639 * server ready SNAC. So if you want to do something before that, you'll
640 * have to be more creative. That is done rather early, though, so I don't
641 * think you have to worry about it. Unless you're me. I care deeply
642 * about such inane things.
647 * Find a FlapConnection that supports the given oscar
651 flap_connection_findbygroup(OscarData
*od
, guint16 group
)
655 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
657 FlapConnection
*conn
;
662 for (l
= conn
->groups
; l
!= NULL
; l
= l
->next
)
664 if (GPOINTER_TO_UINT(l
->data
) == group
)
673 * Locates a connection of the specified type in the
676 * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
678 * @param od The session to search.
679 * @param type The type of connection to look for.
681 * @return Returns the first connection found of the given target type,
682 * or NULL if none could be found.
685 flap_connection_getbytype(OscarData
*od
, int type
)
689 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
691 FlapConnection
*conn
;
693 if ((conn
->type
== type
) && (conn
->connected
))
701 flap_connection_getbytype_all(OscarData
*od
, int type
)
705 for (cur
= od
->oscar_connections
; cur
; cur
= cur
->next
)
707 FlapConnection
*conn
;
709 if (conn
->type
== type
)
717 * Allocate a new FLAP frame.
719 * @param channel The FLAP channel. This is almost always 2.
722 flap_frame_new(OscarData
*od
, guint16 channel
, int datalen
)
726 frame
= g_new0(FlapFrame
, 1);
727 frame
->channel
= channel
;
730 byte_stream_new(&frame
->data
, datalen
);
736 parse_snac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
741 if (byte_stream_bytes_left(&frame
->data
) < 10)
744 snac
.family
= byte_stream_get16(&frame
->data
);
745 snac
.subtype
= byte_stream_get16(&frame
->data
);
746 snac
.flags
= byte_stream_get16(&frame
->data
);
747 snac
.id
= byte_stream_get32(&frame
->data
);
749 /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
750 if (snac
.flags
& 0x0001) {
752 * This means the SNAC will be followed by another SNAC with
753 * related information. We don't need to do anything about
757 if (snac
.flags
& 0x8000) {
759 * This packet contains the version of the family that this SNAC is
760 * in. You get this when your SSI module is version 2 or higher.
761 * For now we have no need for this, but you could always save
762 * it as a part of aim_modnsac_t, or something. The format is...
763 * 2 byte length of total mini-header (which is 6 bytes), then TLV
764 * of type 0x0001, length 0x0002, value is the 2 byte version
767 byte_stream_advance(&frame
->data
, byte_stream_get16(&frame
->data
));
770 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
772 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
773 (cur
->family
!= snac
.family
))
776 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
782 parse_fakesnac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
, guint16 family
, guint16 subtype
)
787 snac
.family
= family
;
788 snac
.subtype
= subtype
;
789 snac
.flags
= snac
.id
= 0;
791 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
793 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
794 (cur
->family
!= snac
.family
))
797 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
803 parse_flap_ch4(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
808 if (byte_stream_bytes_left(&frame
->data
) == 0) {
809 /* XXX should do something with this */
813 /* An ICQ account is logging in */
814 if (conn
->type
== SNAC_FAMILY_AUTH
)
816 parse_fakesnac(od
, conn
, frame
, 0x0017, 0x0003);
820 tlvlist
= aim_tlvlist_read(&frame
->data
);
822 if (aim_tlv_gettlv(tlvlist
, 0x0009, 1))
823 conn
->disconnect_code
= aim_tlv_get16(tlvlist
, 0x0009, 1);
825 if (aim_tlv_gettlv(tlvlist
, 0x000b, 1))
826 msg
= aim_tlv_getstr(tlvlist
, 0x000b, 1);
829 * The server ended this FLAP connnection, so let's be nice and
830 * close the physical TCP connection
832 flap_connection_schedule_destroy(conn
,
833 OSCAR_DISCONNECT_REMOTE_CLOSED
, msg
);
835 aim_tlvlist_free(tlvlist
);
841 * Takes a new incoming FLAP frame and sends it to the appropriate
842 * handler function to be parsed.
845 parse_flap(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
847 if (frame
->channel
== 0x01) {
848 guint32 flap_version
= byte_stream_get32(&frame
->data
);
849 if (flap_version
!= 0x00000001)
852 purple_debug_warning("oscar", "Expecting FLAP version "
853 "0x00000001 but received FLAP version %08x. Closing connection.\n",
855 flap_connection_schedule_destroy(conn
,
856 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
859 conn
->connected
= TRUE
;
861 } else if (frame
->channel
== 0x02) {
862 parse_snac(od
, conn
, frame
);
864 } else if (frame
->channel
== 0x04) {
865 parse_flap_ch4(od
, conn
, frame
);
867 } else if (frame
->channel
== 0x05) {
868 /* TODO: Reset our keepalive watchdog? */
874 * Read in all available data on the socket for a given connection.
875 * All complete FLAPs handled immedate after they're received.
876 * Incomplete FLAP data is stored locally and appended to the next
877 * time this callback is triggered.
879 * This is called by flap_connection_recv_cb and
880 * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
883 flap_connection_recv(FlapConnection
*conn
)
889 /* Read data until we run out of data and break out of the loop */
892 /* Start reading a new FLAP */
893 if (conn
->buffer_incoming
.data
.data
== NULL
)
895 buf
= conn
->header
+ conn
->header_received
;
896 buflen
= 6 - conn
->header_received
;
898 /* Read the first 6 bytes (the FLAP header) */
900 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
902 read
= recv(conn
->fd
, buf
, buflen
, 0);
904 /* Check if the FLAP server closed the connection */
907 flap_connection_schedule_destroy(conn
,
908 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
912 /* If there was an error then close the connection */
915 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
920 flap_connection_schedule_destroy(conn
,
921 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
924 conn
->od
->gc
->last_received
= time(NULL
);
926 /* If we don't even have a complete FLAP header then do nothing */
927 conn
->header_received
+= read
;
928 if (conn
->header_received
< 6)
931 /* All FLAP frames must start with the byte 0x2a */
932 if (aimutil_get8(&conn
->header
[0]) != 0x2a)
934 flap_connection_schedule_destroy(conn
,
935 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
939 /* Initialize a new temporary FlapFrame for incoming data */
940 conn
->buffer_incoming
.channel
= aimutil_get8(&conn
->header
[1]);
941 conn
->buffer_incoming
.seqnum
= aimutil_get16(&conn
->header
[2]);
942 conn
->buffer_incoming
.data
.len
= aimutil_get16(&conn
->header
[4]);
943 conn
->buffer_incoming
.data
.data
= g_new(guint8
, conn
->buffer_incoming
.data
.len
);
944 conn
->buffer_incoming
.data
.offset
= 0;
947 buflen
= conn
->buffer_incoming
.data
.len
- conn
->buffer_incoming
.data
.offset
;
950 buf
= &conn
->buffer_incoming
.data
.data
[conn
->buffer_incoming
.data
.offset
];
951 /* Read data into the temporary FlapFrame until it is complete */
953 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
955 read
= recv(conn
->fd
, buf
, buflen
, 0);
957 /* Check if the FLAP server closed the connection */
960 flap_connection_schedule_destroy(conn
,
961 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
967 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
972 flap_connection_schedule_destroy(conn
,
973 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
977 conn
->buffer_incoming
.data
.offset
+= read
;
978 if (conn
->buffer_incoming
.data
.offset
< conn
->buffer_incoming
.data
.len
)
979 /* Waiting for more data to arrive */
983 /* We have a complete FLAP! Handle it and continue reading */
984 byte_stream_rewind(&conn
->buffer_incoming
.data
);
985 parse_flap(conn
->od
, conn
, &conn
->buffer_incoming
);
986 conn
->lastactivity
= time(NULL
);
988 g_free(conn
->buffer_incoming
.data
.data
);
989 conn
->buffer_incoming
.data
.data
= NULL
;
991 conn
->header_received
= 0;
996 flap_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
998 FlapConnection
*conn
= data
;
1000 flap_connection_recv(conn
);
1004 flap_connection_recv_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
1006 FlapConnection
*conn
= data
;
1008 flap_connection_recv(conn
);
1012 * @param source When this function is called as a callback source is
1013 * set to the fd that triggered the callback. But this function
1014 * is also called directly from flap_connection_send_byte_stream(),
1015 * in which case source will be -1. So don't use source--use
1016 * conn->gsc or conn->fd instead.
1019 send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
1021 FlapConnection
*conn
;
1025 writelen
= purple_circ_buffer_get_max_read(conn
->buffer_outgoing
);
1029 purple_input_remove(conn
->watcher_outgoing
);
1030 conn
->watcher_outgoing
= 0;
1035 ret
= purple_ssl_write(conn
->gsc
, conn
->buffer_outgoing
->outptr
,
1038 ret
= send(conn
->fd
, conn
->buffer_outgoing
->outptr
, writelen
, 0);
1041 if (ret
< 0 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
))
1046 purple_input_remove(conn
->watcher_outgoing
);
1047 conn
->watcher_outgoing
= 0;
1049 purple_ssl_close(conn
->gsc
);
1055 flap_connection_schedule_destroy(conn
,
1056 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
1060 purple_circ_buffer_mark_read(conn
->buffer_outgoing
, ret
);
1064 flap_connection_send_byte_stream(ByteStream
*bs
, FlapConnection
*conn
, size_t count
)
1069 /* Make sure we don't send past the end of the bs */
1070 if (count
> byte_stream_bytes_left(bs
))
1071 count
= byte_stream_bytes_left(bs
); /* truncate to remaining space */
1076 /* Add everything to our outgoing buffer */
1077 purple_circ_buffer_append(conn
->buffer_outgoing
, bs
->data
, count
);
1079 /* If we haven't already started writing stuff, then start the cycle */
1080 if (conn
->watcher_outgoing
== 0)
1083 conn
->watcher_outgoing
= purple_input_add(conn
->gsc
->fd
,
1084 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1085 send_cb(conn
, -1, 0);
1086 } else if (conn
->fd
>= 0) {
1087 conn
->watcher_outgoing
= purple_input_add(conn
->fd
,
1088 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1089 send_cb(conn
, -1, 0);
1095 sendframe_flap(FlapConnection
*conn
, FlapFrame
*frame
)
1098 int payloadlen
, bslen
;
1100 payloadlen
= byte_stream_curpos(&frame
->data
);
1102 byte_stream_new(&bs
, 6 + payloadlen
);
1105 byte_stream_put8(&bs
, 0x2a);
1106 byte_stream_put8(&bs
, frame
->channel
);
1107 byte_stream_put16(&bs
, frame
->seqnum
);
1108 byte_stream_put16(&bs
, payloadlen
);
1111 byte_stream_rewind(&frame
->data
);
1112 byte_stream_putbs(&bs
, &frame
->data
, payloadlen
);
1114 bslen
= byte_stream_curpos(&bs
);
1115 byte_stream_rewind(&bs
);
1116 flap_connection_send_byte_stream(&bs
, conn
, bslen
);
1118 byte_stream_destroy(&bs
);
1122 flap_connection_send(FlapConnection
*conn
, FlapFrame
*frame
)
1124 frame
->seqnum
= ++(conn
->seqnum_out
);
1125 sendframe_flap(conn
, frame
);
1126 flap_frame_destroy(frame
);