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
= g_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 g_slist_free_full(conn
->rateclasses
, g_free
);
505 g_hash_table_destroy(conn
->rateclass_members
);
507 if (conn
->queued_snacs
) {
508 while (!g_queue_is_empty(conn
->queued_snacs
))
510 QueuedSnac
*queued_snac
;
511 queued_snac
= g_queue_pop_head(conn
->queued_snacs
);
512 flap_frame_destroy(queued_snac
->frame
);
515 g_queue_free(conn
->queued_snacs
);
518 if (conn
->queued_lowpriority_snacs
) {
519 while (!g_queue_is_empty(conn
->queued_lowpriority_snacs
))
521 QueuedSnac
*queued_snac
;
522 queued_snac
= g_queue_pop_head(conn
->queued_lowpriority_snacs
);
523 flap_frame_destroy(queued_snac
->frame
);
526 g_queue_free(conn
->queued_lowpriority_snacs
);
529 if (conn
->queued_timeout
> 0)
530 g_source_remove(conn
->queued_timeout
);
538 * See the comments for the parameters of
539 * flap_connection_schedule_destroy().
542 flap_connection_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
544 if (conn
->destroy_timeout
!= 0)
545 g_source_remove(conn
->destroy_timeout
);
546 conn
->disconnect_reason
= reason
;
547 g_free(conn
->error_message
);
548 conn
->error_message
= g_strdup(error_message
);
549 flap_connection_destroy_cb(conn
);
553 * Schedule Purple to destroy the given FlapConnection as soon as we
554 * return control back to the program's main loop. We must do this
555 * if we want to destroy the connection but we are still using it
558 * @param reason The reason for the disconnection.
559 * @param error_message A brief error message that gives more detail
560 * regarding the reason for the disconnecting. This should
561 * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
562 * in which case it should contain the value of g_strerror(errno),
563 * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
564 * should contain the error_message passed back from the call
565 * to purple_proxy_connect().
568 flap_connection_schedule_destroy(FlapConnection
*conn
, OscarDisconnectReason reason
, const gchar
*error_message
)
570 if (conn
->destroy_timeout
!= 0)
571 /* Already taken care of */
574 purple_debug_info("oscar", "Scheduling destruction of FLAP "
575 "connection %p of type 0x%04hx\n", conn
, conn
->type
);
576 conn
->disconnect_reason
= reason
;
577 g_free(conn
->error_message
);
578 conn
->error_message
= g_strdup(error_message
);
579 conn
->destroy_timeout
= g_timeout_add(0, flap_connection_destroy_cb
, conn
);
583 * In OSCAR, every connection has a set of SNAC groups associated
584 * with it. These are the groups that you can send over this connection
585 * without being guaranteed a "Not supported" SNAC error.
587 * The grand theory of things says that these associations transcend
588 * what libfaim calls "connection types" (conn->type). You can probably
589 * see the elegance here, but since I want to revel in it for a bit, you
590 * get to hear it all spelled out.
592 * So let us say that you have your core BOS connection running. One
593 * of your modules has just given you a SNAC of the group 0x0004 to send
594 * you. Maybe an IM destined for some twit in Greenland. So you start
595 * at the top of your connection list, looking for a connection that
596 * claims to support group 0x0004. You find one. Why, that neat BOS
597 * connection of yours can do that. So you send it on its way.
599 * Now, say, that fellow from Greenland has friends and they all want to
600 * meet up with you in a lame chat room. This has landed you a SNAC
601 * in the family 0x000e and you have to admit you're a bit lost. You've
602 * searched your connection list for someone who wants to make your life
603 * easy and deliver this SNAC for you, but there isn't one there.
605 * Here comes the good bit. Without even letting anyone know, particularly
606 * the module that decided to send this SNAC, and definitely not that twit
607 * in Greenland, you send out a service request. In this request, you have
608 * marked the need for a connection supporting group 0x000e. A few seconds
609 * later, you receive a service redirect with an IP address and a cookie in
610 * it. Great, you say. Now I have something to do. Off you go, making
611 * that connection. One of the first things you get from this new server
612 * is a message saying that indeed it does support the group you were looking
613 * for. So you continue and send rate confirmation and all that.
615 * Then you remember you had that SNAC to send, and now you have a means to
616 * do it, and you do, and everyone is happy. Except the Greenlander, who is
617 * still stuck in the bitter cold.
619 * Oh, and this is useful for building the Migration SNACs, too. In the
620 * future, this may help convince me to implement rate limit mitigation
621 * for real. We'll see.
623 * Just to make me look better, I'll say that I've known about this great
624 * scheme for quite some time now. But I still haven't convinced myself
625 * to make libfaim work that way. It would take a fair amount of effort,
626 * and probably some client API changes as well. (Whenever I don't want
627 * to do something, I just say it would change the client API. Then I
628 * instantly have a couple of supporters of not doing it.)
630 * Generally, addgroup is only called by the internal handling of the
631 * server ready SNAC. So if you want to do something before that, you'll
632 * have to be more creative. That is done rather early, though, so I don't
633 * think you have to worry about it. Unless you're me. I care deeply
634 * about such inane things.
639 * Find a FlapConnection that supports the given oscar
643 flap_connection_findbygroup(OscarData
*od
, guint16 group
)
647 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
649 FlapConnection
*conn
;
653 if (g_slist_find(conn
->groups
, GUINT_TO_POINTER(group
)) != NULL
)
661 * Locates a connection of the specified type in the
664 * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
666 * @param od The session to search.
667 * @param type The type of connection to look for.
669 * @return Returns the first connection found of the given target type,
670 * or NULL if none could be found.
673 flap_connection_getbytype(OscarData
*od
, int type
)
677 for (cur
= od
->oscar_connections
; cur
!= NULL
; cur
= cur
->next
)
679 FlapConnection
*conn
;
681 if ((conn
->type
== type
) && (conn
->connected
))
689 flap_connection_getbytype_all(OscarData
*od
, int type
)
693 for (cur
= od
->oscar_connections
; cur
; cur
= cur
->next
)
695 FlapConnection
*conn
;
697 if (conn
->type
== type
)
705 * Allocate a new FLAP frame.
707 * @param channel The FLAP channel. This is almost always 2.
710 flap_frame_new(OscarData
*od
, guint16 channel
, int datalen
)
714 frame
= g_new0(FlapFrame
, 1);
715 frame
->channel
= channel
;
718 byte_stream_new(&frame
->data
, datalen
);
724 parse_snac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
729 if (byte_stream_bytes_left(&frame
->data
) < 10)
732 snac
.family
= byte_stream_get16(&frame
->data
);
733 snac
.subtype
= byte_stream_get16(&frame
->data
);
734 snac
.flags
= byte_stream_get16(&frame
->data
);
735 snac
.id
= byte_stream_get32(&frame
->data
);
737 /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
738 if (snac
.flags
& 0x0001) {
740 * This means the SNAC will be followed by another SNAC with
741 * related information. We don't need to do anything about
745 if (snac
.flags
& 0x8000) {
747 * This packet contains the version of the family that this SNAC is
748 * in. You get this when your SSI module is version 2 or higher.
749 * For now we have no need for this, but you could always save
750 * it as a part of aim_modnsac_t, or something. The format is...
751 * 2 byte length of total mini-header (which is 6 bytes), then TLV
752 * of type 0x0001, length 0x0002, value is the 2 byte version
755 byte_stream_advance(&frame
->data
, byte_stream_get16(&frame
->data
));
758 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
760 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
761 (cur
->family
!= snac
.family
))
764 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
770 parse_fakesnac(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
, guint16 family
, guint16 subtype
)
775 snac
.family
= family
;
776 snac
.subtype
= subtype
;
777 snac
.flags
= snac
.id
= 0;
779 for (cur
= (aim_module_t
*)od
->modlistv
; cur
; cur
= cur
->next
) {
781 if (!(cur
->flags
& AIM_MODFLAG_MULTIFAMILY
) &&
782 (cur
->family
!= snac
.family
))
785 if (cur
->snachandler(od
, conn
, cur
, frame
, &snac
, &frame
->data
))
791 parse_flap_ch4(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
796 if (byte_stream_bytes_left(&frame
->data
) == 0) {
797 /* XXX should do something with this */
801 /* An ICQ account is logging in */
802 if (conn
->type
== SNAC_FAMILY_AUTH
)
804 parse_fakesnac(od
, conn
, frame
, 0x0017, 0x0003);
808 tlvlist
= aim_tlvlist_read(&frame
->data
);
810 if (aim_tlv_gettlv(tlvlist
, 0x0009, 1))
811 conn
->disconnect_code
= aim_tlv_get16(tlvlist
, 0x0009, 1);
813 if (aim_tlv_gettlv(tlvlist
, 0x000b, 1))
814 msg
= aim_tlv_getstr(tlvlist
, 0x000b, 1);
817 * The server ended this FLAP connnection, so let's be nice and
818 * close the physical TCP connection
820 flap_connection_schedule_destroy(conn
,
821 OSCAR_DISCONNECT_REMOTE_CLOSED
, msg
);
823 aim_tlvlist_free(tlvlist
);
829 * Takes a new incoming FLAP frame and sends it to the appropriate
830 * handler function to be parsed.
833 parse_flap(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*frame
)
835 if (frame
->channel
== 0x01) {
836 guint32 flap_version
= byte_stream_get32(&frame
->data
);
837 if (flap_version
!= 0x00000001)
840 purple_debug_warning("oscar", "Expecting FLAP version "
841 "0x00000001 but received FLAP version %08x. Closing connection.\n",
843 flap_connection_schedule_destroy(conn
,
844 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
847 conn
->connected
= TRUE
;
849 } else if (frame
->channel
== 0x02) {
850 parse_snac(od
, conn
, frame
);
852 } else if (frame
->channel
== 0x04) {
853 parse_flap_ch4(od
, conn
, frame
);
855 } else if (frame
->channel
== 0x05) {
856 /* TODO: Reset our keepalive watchdog? */
862 * Read in all available data on the socket for a given connection.
863 * All complete FLAPs handled immedate after they're received.
864 * Incomplete FLAP data is stored locally and appended to the next
865 * time this callback is triggered.
867 * This is called by flap_connection_recv_cb and
868 * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
871 flap_connection_recv(FlapConnection
*conn
)
877 /* Read data until we run out of data and break out of the loop */
880 /* Start reading a new FLAP */
881 if (conn
->buffer_incoming
.data
.data
== NULL
)
883 buf
= conn
->header
+ conn
->header_received
;
884 buflen
= 6 - conn
->header_received
;
886 /* Read the first 6 bytes (the FLAP header) */
888 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
890 read
= recv(conn
->fd
, buf
, buflen
, 0);
892 /* Check if the FLAP server closed the connection */
895 flap_connection_schedule_destroy(conn
,
896 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
900 /* If there was an error then close the connection */
903 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
908 flap_connection_schedule_destroy(conn
,
909 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
912 purple_connection_update_last_received(conn
->od
->gc
);
914 /* If we don't even have a complete FLAP header then do nothing */
915 conn
->header_received
+= read
;
916 if (conn
->header_received
< 6)
919 /* All FLAP frames must start with the byte 0x2a */
920 if (aimutil_get8(&conn
->header
[0]) != 0x2a)
922 flap_connection_schedule_destroy(conn
,
923 OSCAR_DISCONNECT_INVALID_DATA
, NULL
);
927 /* Initialize a new temporary FlapFrame for incoming data */
928 conn
->buffer_incoming
.channel
= aimutil_get8(&conn
->header
[1]);
929 conn
->buffer_incoming
.seqnum
= aimutil_get16(&conn
->header
[2]);
930 conn
->buffer_incoming
.data
.len
= aimutil_get16(&conn
->header
[4]);
931 conn
->buffer_incoming
.data
.data
= g_new(guint8
, conn
->buffer_incoming
.data
.len
);
932 conn
->buffer_incoming
.data
.offset
= 0;
935 buflen
= conn
->buffer_incoming
.data
.len
- conn
->buffer_incoming
.data
.offset
;
938 buf
= &conn
->buffer_incoming
.data
.data
[conn
->buffer_incoming
.data
.offset
];
939 /* Read data into the temporary FlapFrame until it is complete */
941 read
= purple_ssl_read(conn
->gsc
, buf
, buflen
);
943 read
= recv(conn
->fd
, buf
, buflen
, 0);
945 /* Check if the FLAP server closed the connection */
948 flap_connection_schedule_destroy(conn
,
949 OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
955 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
960 flap_connection_schedule_destroy(conn
,
961 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
965 conn
->buffer_incoming
.data
.offset
+= read
;
966 if (conn
->buffer_incoming
.data
.offset
< conn
->buffer_incoming
.data
.len
)
967 /* Waiting for more data to arrive */
971 /* We have a complete FLAP! Handle it and continue reading */
972 byte_stream_rewind(&conn
->buffer_incoming
.data
);
973 parse_flap(conn
->od
, conn
, &conn
->buffer_incoming
);
974 conn
->lastactivity
= time(NULL
);
976 g_free(conn
->buffer_incoming
.data
.data
);
977 conn
->buffer_incoming
.data
.data
= NULL
;
979 conn
->header_received
= 0;
984 flap_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
986 FlapConnection
*conn
= data
;
988 flap_connection_recv(conn
);
992 flap_connection_recv_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
994 FlapConnection
*conn
= data
;
996 flap_connection_recv(conn
);
1000 * @param source When this function is called as a callback source is
1001 * set to the fd that triggered the callback. But this function
1002 * is also called directly from flap_connection_send_byte_stream(),
1003 * in which case source will be -1. So don't use source--use
1004 * conn->gsc or conn->fd instead.
1007 send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
1009 FlapConnection
*conn
;
1011 const gchar
*output
= NULL
;
1014 writelen
= purple_circular_buffer_get_max_read(conn
->buffer_outgoing
);
1015 output
= purple_circular_buffer_get_output(conn
->buffer_outgoing
);
1019 purple_input_remove(conn
->watcher_outgoing
);
1020 conn
->watcher_outgoing
= 0;
1025 ret
= purple_ssl_write(conn
->gsc
, output
, writelen
);
1027 ret
= send(conn
->fd
, output
, writelen
, 0);
1030 if (ret
< 0 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
))
1035 purple_input_remove(conn
->watcher_outgoing
);
1036 conn
->watcher_outgoing
= 0;
1038 purple_ssl_close(conn
->gsc
);
1044 flap_connection_schedule_destroy(conn
,
1045 OSCAR_DISCONNECT_LOST_CONNECTION
, g_strerror(errno
));
1049 purple_circular_buffer_mark_read(conn
->buffer_outgoing
, ret
);
1053 flap_connection_send_byte_stream(ByteStream
*bs
, FlapConnection
*conn
, size_t count
)
1058 /* Make sure we don't send past the end of the bs */
1059 if (count
> byte_stream_bytes_left(bs
))
1060 count
= byte_stream_bytes_left(bs
); /* truncate to remaining space */
1065 /* Add everything to our outgoing buffer */
1066 purple_circular_buffer_append(conn
->buffer_outgoing
, bs
->data
, count
);
1068 /* If we haven't already started writing stuff, then start the cycle */
1069 if (conn
->watcher_outgoing
== 0)
1072 conn
->watcher_outgoing
= purple_input_add(conn
->gsc
->fd
,
1073 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1074 send_cb(conn
, -1, 0);
1075 } else if (conn
->fd
>= 0) {
1076 conn
->watcher_outgoing
= purple_input_add(conn
->fd
,
1077 PURPLE_INPUT_WRITE
, send_cb
, conn
);
1078 send_cb(conn
, -1, 0);
1084 sendframe_flap(FlapConnection
*conn
, FlapFrame
*frame
)
1087 int payloadlen
, bslen
;
1089 payloadlen
= byte_stream_curpos(&frame
->data
);
1091 byte_stream_new(&bs
, 6 + payloadlen
);
1094 byte_stream_put8(&bs
, 0x2a);
1095 byte_stream_put8(&bs
, frame
->channel
);
1096 byte_stream_put16(&bs
, frame
->seqnum
);
1097 byte_stream_put16(&bs
, payloadlen
);
1100 byte_stream_rewind(&frame
->data
);
1101 byte_stream_putbs(&bs
, &frame
->data
, payloadlen
);
1103 bslen
= byte_stream_curpos(&bs
);
1104 byte_stream_rewind(&bs
);
1105 flap_connection_send_byte_stream(&bs
, conn
, bslen
);
1107 byte_stream_destroy(&bs
);
1111 flap_connection_send(FlapConnection
*conn
, FlapFrame
*frame
)
1113 frame
->seqnum
= ++(conn
->seqnum_out
);
1114 sendframe_flap(conn
, frame
);
1115 flap_frame_destroy(frame
);